点击放大样式

This commit is contained in:
2025-09-08 23:12:00 +08:00
parent 517345471a
commit b2bccb622a
5 changed files with 300 additions and 24 deletions

View File

@@ -1,9 +1,10 @@
# iOS 控制服务
VUE_APP_BASE_LOCAL=http://127.0.0.1:34567/
# VUE_APP_BASE_LOCAL=http://192.168.1.209:34567/
# 业务后端(开发用内网地址)
VUE_APP_BASE_REMOTE=https://crawlclient.api.yolozs.com
# VUE_APP_BASE_REMOTE=http://192.168.1.144:8101/
# VUE_APP_BASE_REMOTE=https://crawlclient.api.yolozs.com
VUE_APP_BASE_REMOTE=http://192.168.1.144:8101/
# AI 服务
VUE_APP_BASE_SPECIAL=http://ai.zhukeping.com

View File

@@ -54,9 +54,9 @@ function parseLines() {
}
function handleConfirm() {
if (!rawText.value) {
return; // 空内容直接忽略
};
// if (!rawText.value) {
// return; // 空内容直接忽略
// };
const items = parseLines();
emit('confirm', items, props.title, props.index, value1.value);
closingByConfirm.value = true; // 标记:这次关闭来自“确定”

View File

@@ -41,6 +41,12 @@ body {
/* 关键grid items 在 block 轴不拉伸 */
justify-items: center;
/* 可选:让每个卡片水平居中 */
row-gap: 24px;
/* 上下行之间 24px 间距 */
column-gap: 16px;
/* 左右列之间 16px 间距(可按需调整或删掉) */
overflow: visible;
/* 放大时允许覆盖 */
}
.right {
@@ -250,4 +256,229 @@ video {
flex-direction: column;
align-items: center;
// justify-content: center;
}
/* === STEP 1: 放大覆盖,但不改变网格布局尺寸 === */
/* 1) 网格行高稳定(可选) */
.content {
/* 确保每个自动行的基准高度,等于缩略高度 432px */
grid-auto-rows: 432px;
overflow: visible;
/* 放大时允许溢出覆盖 */
}
/* 2) 每个卡片的占位固定为缩略尺寸320×720 的 0.6 倍 = 192×432 */
.video-container {
position: relative;
width: 192px;
/* 固定缩略占位宽度 */
height: 432px;
/* 固定缩略占位高度 */
overflow: visible;
/* 放大时从容器溢出,不挤压布局 */
align-self: start;
margin: 0;
}
/* 3) 真正的手机画面用绝对定位 + 视觉缩放(默认缩略 0.6 */
.video-container .video-canvas {
position: absolute;
top: 0;
left: 0;
/* 原始尺寸320×720。用 !important 覆盖模板里的 :style 宽高,避免改变布局 */
width: 320px !important;
height: 720px !important;
transform: scale(0.6);
transform-origin: top center;
/* 默认以上方为原点(下一步会针对上下行细调方向) */
transition: transform .18s ease, box-shadow .18s ease, z-index .18s ease;
z-index: 1;
/* 未选中时层级较低 */
line-height: 0;
cursor: pointer;
}
/* 4) 选中时:仅视觉放大,不改变外层占位尺寸 */
.video-container .video-canvas.active {
transform: scale(1);
z-index: 20;
/* 盖住其它缩略卡片 */
box-shadow: 0 8px 22px rgba(0, 0, 0, .25);
}
/* 5) 图像与画布尺寸固定为原始尺寸,覆盖在 video-canvas 内 */
.video-container .stream,
.video-container .overlay {
position: absolute;
top: 0;
left: 0;
width: 320px !important;
height: 720px !important;
display: block;
}
.video-container .stream {
user-select: none;
pointer-events: none;
/* 事件交给上层 overlay/canvas */
z-index: 1;
}
.video-container .overlay {
z-index: 2;
cursor: crosshair;
}
/* —— 修正容器与画布的对齐与偏移 —— */
/* 网格:行距回到紧凑,且不再让子项有奇怪的基线对齐 */
.content {
row-gap: 12px;
/* 行间距按需改 */
column-gap: 16px;
align-items: start;
/* 已有,强调一下 */
justify-items: center;
/* 已有 */
overflow: visible;
/* 放大可溢出覆盖 */
}
/* 让每个格子“像块级元素”占位,避免 flex 造成的基线/对齐偏差 */
.video-container {
display: block !important;
/* 避免作为 flex 容器引入的对齐偏差 */
position: relative;
width: 192px;
/* 固定缩略占位320×720 的 0.6 倍 */
height: 432px;
margin: 0 !important;
/* gap 负责间距 */
padding: 0 !important;
border: 0;
overflow: visible;
align-self: start;
/* 和网格对齐一致 */
}
/* 核心:子元素绝对定位在容器(0,0);缩放从左上角开始,杜绝“左/上漂移” */
.video-container .video-canvas {
position: absolute;
top: 0;
left: 0;
width: 320px !important;
height: 720px !important;
transform: scale(0.6);
transform-origin: top left !important;
/* 关键:从左上角缩放,边界贴紧容器左上 */
margin: 0 !important;
box-sizing: border-box;
/* 如果你需要边框,避免边框把可视尺寸再挤偏 */
z-index: 1;
line-height: 0;
cursor: pointer;
/* 如之前有边框导致“看起来外扩”,建议先关掉调试 */
border: none !important;
/* 需要边框再开,但建议用 outline不参与尺寸 */
}
/* 选中放大:遮挡覆盖但不挤压 */
.video-container .video-canvas.active {
transform: scale(1);
z-index: 20;
box-shadow: 0 8px 22px rgba(0, 0, 0, .25);
}
/* 图片与操作层铺满 video-canvas 的原始尺寸 */
.video-container .stream,
.video-container .overlay {
position: absolute;
top: 0;
left: 0;
width: 320px !important;
height: 720px !important;
display: block;
}
.video-container .stream {
user-select: none;
pointer-events: none;
z-index: 1;
}
.video-container .overlay {
z-index: 2;
}
// /* 第二行“选中时”向上放大(默认仍从左上缩放,只有选中才改为 bottom-left */
// .content .video-container:nth-child(n + 4) .video-canvas.active {
// transform-origin: bottom left !important;
// /* 只在选中时往上长 */
// }
/* 网格允许覆盖,不挤压布局 */
.content {
overflow: visible;
}
/* 每个卡片只占缩略尺寸320x720 的 0.6 倍 = 192x432 */
.video-container {
position: relative;
width: 192px !important;
height: 432px !important;
overflow: visible;
margin: 0 !important;
/* 行/列间距用 gap 控即可 */
padding: 0 !important;
}
/* 画面容器固定原始尺寸,绝对定位到卡片左上角 */
.video-canvas {
position: absolute;
top: 0;
left: 0;
width: 320px !important;
height: 720px !important;
transition: transform .18s ease, box-shadow .18s ease;
will-change: transform;
z-index: 1;
}
/* 选中时仅提升层级、阴影(缩放由 getCanvasStyle 控制) */
.video-canvas.active {
z-index: 20;
box-shadow: 0 8px 22px rgba(0, 0, 0, .25);
}
/* 图像与遮罩铺满 320x720 */
.video-canvas .stream,
.video-canvas .overlay {
position: absolute;
top: 0;
left: 0;
width: 320px !important;
height: 720px !important;
display: block;
}
.video-canvas .stream {
user-select: none;
pointer-events: none;
/* 让鼠标事件落在 overlay 上 */
z-index: 1;
}
.video-canvas .overlay {
z-index: 2;
}

View File

@@ -74,7 +74,7 @@ export function addToHostList(newItem) {
// 用于获取私信信息
export function getContentpriList() {
const arr = JSON.parse(localStorage.getItem('Contentpri'))
return [arr, arr, arr, arr, arr, arr, arr, arr];
return arr;
}
// 用于设置私信信息
export function setContentpriList(data) {

View File

@@ -18,16 +18,13 @@
<!-- 中间手机区域 -->
<div class="content" @click.self="selectedDevice = 999">
<div v-if="isImg" class="video-container" v-for="(device, index) in deviceInformation" :key="device.deviceId">
<div class="video-canvas" :class="{ active: selectedDevice === index }" :style="imgWH(index)"
<div class="video-canvas" :class="{ active: selectedDevice === index }" :style="getCanvasStyle(index)"
@click="selectDevice(index)">
<img class="stream" :style="imgWH(index)" :src="'http://localhost:' + device.screenPort" />
<!-- 选中时显示把down/up都放这里 -->
<canvas v-show="selectedDevice === index" class="overlay" :style="imgWH(index)"
<img class="stream" :src="'http://localhost:' + device.screenPort" />
<canvas v-show="selectedDevice === index" class="overlay"
@mousedown.stop="(e) => onCanvasDown(device.deviceId, e, index)"
@mouseup.stop="(e) => onCanvasUp(device.deviceId, e, index)"
@mousemove.stop="(e) => onCanvasMove(device.deviceId, e, index)">
</canvas>
@mousemove.stop="(e) => onCanvasMove(device.deviceId, e, index)" />
</div>
<div class="input-info" v-show="selectedDevice == index">
@@ -109,7 +106,7 @@
</template>
<script setup>
import { ref, onMounted, onUnmounted, onBeforeUnmount, watch, inject } from "vue";
import { ref, onMounted, onUnmounted, onBeforeUnmount, watch, inject, computed } from "vue";
import { useRouter } from 'vue-router';
import {
setphoneXYinfo, getphoneXYinfo, getUser,
@@ -376,7 +373,7 @@ let scheduleState = (() => {
let scheduleTimer = null // 轮询定时器句柄
const scheduleTickMs = 30_000 // 每 30s 检查一次是否到切换点
let scheduleEnabled = ref(true) // 需要时可手动关闭调度(例如“全部停止”)
let scheduleEnabled = ref(false) // 需要时可手动关闭调度(例如“全部停止”)
// 弹窗
const showScheduleDlg = ref(false)
@@ -400,7 +397,7 @@ function openScheduleDialog() {
// 保存:校验=60 分钟 → 更新 schedulePlan → 持久化 → 重启轮询
function saveSchedule() {
scheduleEnabled.value = true
const total = schedAMin.value + schedBMin.value
if (total !== 60) {
ElMessage.error('两个片段相加必须等于 60 分钟')
@@ -421,7 +418,7 @@ function saveSchedule() {
localStorage.setItem('SCHEDULE_STATE', JSON.stringify(scheduleState))
// 若启用则重启轮询
if (scheduleEnabled.value) startScheduleLoop()
startScheduleLoop()
showScheduleDlg.value = false
ElMessage.success('已保存定时调度')
@@ -430,10 +427,36 @@ function saveSchedule() {
const selectedDevice = ref(null)
// —— 显示尺寸固定为 320x720未选中缩略为 THUMB_SCALE 倍 ——
// 尺寸与排布
const BASE_W = 320
const BASE_H = 720
const THUMB_SCALE = 0.6
const PER_ROW = 3
// 底行上移的位移量720*(1-0.6)=288
const BOTTOM_SHIFT = Math.round(BASE_H * (1 - THUMB_SCALE)) // 288
// 是否至少有两行
const hasTwoRows = computed(() => deviceInformation.value.length > PER_ROW)
// 真正的“底行”判定:必须有两行以上才成立
const isBottomRow = (index) => {
if (!hasTwoRows.value) return false
const lastRow = Math.floor((deviceInformation.value.length - 1) / PER_ROW)
return Math.floor(index / PER_ROW) === lastRow
}
// 统一给 .video-canvas 返回 transform缩略/放大/底行上移)
function getCanvasStyle(index) {
const isSelected = selectedDevice.value === index
if (!isSelected) {
return { transform: `scale(${THUMB_SCALE})` }
}
// 选中:默认正常放大;若在底行且至少两行 -> 先上移再放大
return isBottomRow(index)
? { transform: `translateY(-${BOTTOM_SHIFT}px) scale(1)` }
: { transform: 'scale(1)' }
}
// 当前选中的卡片:选中=1倍未选中=缩略比例
const imgWH = (index) => {
const scale = (selectedDevice.value === index) ? 1 : THUMB_SCALE
@@ -787,7 +810,7 @@ onUnmounted(() => {
})
const getDeviceListFun = () => {
getDeviceList().then((res) => {
// console.log('返回', res.length)
console.log('返回', res.length)
if (res && res.length > 0 && deviceInformation.value.length !== res.length) {
console.log("设备变更")
deviceInformation.value = res
@@ -798,6 +821,7 @@ const getDeviceListFun = () => {
deviceInformation.value = []
reloadImg()
}
// deviceInformation.value = ['', '', '', '', '', '',]
}).catch((err) => {
ElMessage.error(`IOSAI服务错误`)
@@ -852,16 +876,33 @@ async function uploadLogFile() {
}
}
function runTask(key) {
if (!scheduleEnabled.value) return
console.log('[schedule] 切换到任务:', key, printCurrentTime())
forceActivate(key, () => {
if (key === 'follow') {
runType.value = 'follow'
// 这三行保持你现有的唤起流程弹“主播ID”输入等
// showDialog.value = true
// dialogTitle.value = '主播ID'
// selectedDevice.value = 999
if (scheduleEnabled.value) {
stopAll()
setTimeout(() => {
runType.value = 'follow'
passAnchorData(
{
deviceList: deviceInformation.value.map(item => item.deviceId),
anchorList: [],
prologueList: getContentpriList(),
needReply: false
}
).then((res) => {
hostList = []
})
}, 1000)
return
}
scheduleEnabled.value = true
if (hostList.length <= 0) {
dialogTitle.value = '主播ID';
} else {
@@ -874,6 +915,7 @@ function runTask(key) {
stopAll()
setTimeout(() => {
scheduleEnabled.value = true
runType.value = 'like'
deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId }))
}, 1000)
@@ -882,6 +924,7 @@ function runTask(key) {
stopAll()
setTimeout(() => {
scheduleEnabled.value = true
runType.value = 'brushLive'
deviceInformation.value.forEach((item) => watchLiveForGrowth({ udid: item.deviceId }))
}, 1000)
@@ -891,6 +934,7 @@ function runTask(key) {
setTimeout(() => {
runType.value = 'listen'
scheduleEnabled.value = true
isMonitorOn.value = true
deviceInformation.value.forEach((item) => monitorMessages({ udid: item.deviceId }))
}, 1000)