点击放大样式
This commit is contained in:
@@ -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
|
||||
@@ -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; // 标记:这次关闭来自“确定”
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user