diff --git a/.env.development b/.env.development index ff83b16..ccdee5d 100644 --- a/.env.development +++ b/.env.development @@ -1,5 +1,5 @@ # 页面标题 -VUE_APP_TITLE = 信安智能 +VUE_APP_TITLE = 浙江信安智能科技有限公司 # 开发环境配置 ENV = 'development' diff --git a/.env.production b/.env.production index 286f7ca..4cf5787 100644 --- a/.env.production +++ b/.env.production @@ -1,5 +1,5 @@ # 页面标题 -VUE_APP_TITLE = 信安智能 +VUE_APP_TITLE = 浙江信安智能科技有限公司 # 生产环境配置 ENV = 'production' diff --git a/.env.staging b/.env.staging index fad5015..7170816 100644 --- a/.env.staging +++ b/.env.staging @@ -1,5 +1,5 @@ # 页面标题 -VUE_APP_TITLE = 信安智能 +VUE_APP_TITLE = 浙江信安智能科技有限公司 NODE_ENV = production diff --git a/dist.zip b/dist.zip index 81493f4..5e6f8d2 100644 Binary files a/dist.zip and b/dist.zip differ diff --git a/package.json b/package.json index bd0853b..ce040c5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fastbee", "version": "2.5.2", - "description": "信安智能", + "description": "浙江信安智能科技有限公司", "author": "kerwincui", "license": "AGPL3.0", "scripts": { diff --git a/src/views/iot/device/display.vue b/src/views/iot/device/display.vue index 77e3b42..9560af5 100644 --- a/src/views/iot/device/display.vue +++ b/src/views/iot/device/display.vue @@ -9,13 +9,18 @@
- {{ $t('device.running-status.866086-0') }} + + + +
- {{ title }} - + +
@@ -585,7 +590,7 @@ export default { this.$nextTick(function () { this.MonitorChart(); }); - console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); + // console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) { this.deviceInfo.thingsModels = this.device.thingsModels.sort((a, b) => b.order - a.order); @@ -813,6 +818,7 @@ export default { programList: [], // 节目使能状态数组 programEnableList: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // 默认全部开启 + boardOnline: false, }; }, mounted() { @@ -1010,7 +1016,7 @@ export default { fillProgramForm(programData) { if (!programData) return; - console.log('填充节目表单数据:', JSON.stringify(programData)); + // console.log('填充节目表单数据:', JSON.stringify(programData)); // 重置表单 this.addProgramForm = { @@ -1022,7 +1028,7 @@ export default { // 解析分区数据 if (programData.aLst && programData.aLst.length > 0) { - console.log('解析分区数据:', JSON.stringify(programData.aLst)); + // console.log('解析分区数据:', JSON.stringify(programData.aLst)); this.addProgramForm.zones = programData.aLst.map(area => { let x = area.size ? area.size.x : 0; let y = area.size ? area.size.y : 0; @@ -1067,14 +1073,15 @@ export default { zone.font = item.txt.fCn ? item.txt.fCn - 1 : 0; // 中文字体索引 zone.fontEn = item.txt.fEn ? item.txt.fEn - 1 : 0; // 英文字体索引 zone.fontSize = item.txt.fS ? this.getFontSizeIndex(item.txt.fS) : 0; // 字体大小索引 - zone.fontColor = item.txt.col || 0; // 文本颜色 + zone.fontColor = item.txt.col - 1 || 0; // 文本颜色 zone.fontBold = item.txt.fW || 0; // 字体加粗 zone.fontStretch = item.txt.stch || 0; // 拉伸方向 // 调试输出 - console.log('编辑节目item.txt.hPos:', item.txt.hPos, 'item.txt.vPos:', item.txt.vPos); - zone.hAlign = typeof item.txt.hPos === 'number' ? (item.txt.hPos + 1) : 0; // 水平对齐(1左,2中,3右) - zone.vAlign = typeof item.txt.vPos === 'number' ? (item.txt.vPos + 1) : 0; // 垂直对齐(1顶,2中,3底) - console.log('zone.hAlign:', zone.hAlign, 'zone.vAlign:', zone.vAlign); + // console.log('编辑节目item.txt.hPos:', item.txt.hPos, 'item.txt.vPos:', item.txt.vPos); + // 修正:物模型hPos/vPos为1/2/3,UI下拉框为0/1/2 + zone.hAlign = typeof item.txt.hPos === 'number' ? (item.txt.hPos - 1) : 0; // 水平对齐(1左,2中,3右) + zone.vAlign = typeof item.txt.vPos === 'number' ? (item.txt.vPos - 1) : 0; // 垂直对齐(1顶,2中,3底) + // console.log('zone.hAlign:', zone.hAlign, 'zone.vAlign:', zone.vAlign); } else if (item.typ === 1 && item.img) { // 图片类型 zone.playType = 1; @@ -1230,7 +1237,7 @@ export default { // 刷新编译信息 async refreshBuildInfo() { - console.log('programList', JSON.stringify(this.programList)) + // console.log('programList', JSON.stringify(this.programList)) try { // 发送102#model指令,值为1 const model = this.deviceInfo.thingsModels.find(m => m.id === '102#model'); @@ -1371,7 +1378,10 @@ export default { // 更新基础设置 updateBasicSettings() { + + if (!this.deviceInfo.thingsModels) return; + this.updateBoardOnlineStatus(); // 屏幕开关 const screenEnModel = this.deviceInfo.thingsModels.find(model => model.id === '102#screenEn'); @@ -1431,7 +1441,7 @@ export default { // 更新节目列表(如果有102#progList物模型) const progListModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progList'); - console.log("progListModel", JSON.stringify(progListModel)) + // console.log("progListModel", JSON.stringify(progListModel)) if (progListModel && progListModel.shadow) { try { const jsonStr = progListModel.shadow.replace('JSON=', ''); @@ -1451,7 +1461,7 @@ export default { raw: program }; }); - console.log('解析后的节目列表:', this.programList); + // console.log('解析后的节目列表:', this.programList); } else { this.programList = []; } @@ -1480,16 +1490,14 @@ export default { this.programList.forEach((program, index) => { program.enabled = this.programEnableList[index] || 0; }); - console.log('节目使能状态:', this.programEnableList); + // console.log('节目使能状态:', this.programEnableList); } } catch (error) { console.error('解析节目使能状态失败:', error); // 如果解析失败,初始化为10个0 this.programEnableList = new Array(10).fill(0); } - } else { - // 如果没有物模型数据,初始化为10个0 - this.programEnableList = new Array(10).fill(0); + //在线离线 } }, @@ -2237,8 +2245,16 @@ export default { }, onAddProgramModeChange(val) { // 根据模式调整分区数量和布局 + const isSwap = this.screenParams.angle === 90 || this.screenParams.angle === 270; + let actualMode = val; + //神经逻辑,反人类逻辑 + if (isSwap) { + if (val === 1) actualMode = 2; // 上下变左右 + else if (val === 2) actualMode = 1; // 左右变上下 + else if (val === 3) actualMode = 2; // 上中下变左右 + } const zoneCounts = [1, 2, 2, 3, 1, 1]; - const count = zoneCounts[val] || 1; + const count = zoneCounts[actualMode] || 1; // 使用当前屏幕参数 const screenW = this.screenParams.width || 32; const screenH = this.screenParams.height || 64; @@ -2268,7 +2284,7 @@ export default { width: screenW, height: screenH }; - switch (val) { + switch (actualMode) { case 1: // 上下 templateZone.height = Math.floor(screenH / count); templateZone.y = i * templateZone.height; @@ -2332,10 +2348,10 @@ export default { aLst: this.addProgramForm.zones.map(zone => { // 构建分区数据 let x = zone.x, y = zone.y, w = zone.width, h = zone.height; - if (this.screenParams.angle === 90 || this.screenParams.angle === 270) { - [x, y] = [y, x]; - [w, h] = [h, w]; - } + // if (this.screenParams.angle === 90 || this.screenParams.angle === 270) { + // [x, y] = [y, x]; + // [w, h] = [h, w]; + // } const area = { size: { x: x, // 左下角X坐标 @@ -2597,7 +2613,12 @@ export default { // 获取像素数据并应用颜色 const imageData = tempCtx.getImageData(0, 0, newWidth, newHeight); const data = imageData.data; - const colors = [[0, 0, 0], [255, 0, 0], [0, 255, 0], [255, 255, 0]]; // Black, Red, Green, Yellow + // 修正颜色顺序:0=红色, 1=绿色, 2=黄色 + const colors = [ + [255, 0, 0], // 红色 + [0, 255, 0], // 绿色 + [255, 255, 0] // 黄色 + ]; const selectedColor = colors[zone.imageColor]; if (selectedColor) { for (let i = 0; i < data.length; i += 4) { @@ -2664,9 +2685,10 @@ export default { this.addProgramPreviewAssets = this.addProgramForm.zones.map(zone => { if (zone.playType === 1 && zone.image) { + console.log('加载图片', zone.image); // 图片类型,异步加载图片 const img = new window.Image(); - img.crossOrigin = 'Anonymous'; + // img.crossOrigin = 'Anonymous'; const asset = { img, isImage: true, loaded: false }; img.onload = () => { asset.loaded = true; @@ -2747,13 +2769,20 @@ export default { if (pages.length === 0) pages.push([]); + // 新增:计算总宽高和区域宽高,供动画用 + const totalWidth = Math.max(...lines.map(line => measureCtx.measureText(line).width), 0); + const totalHeight = lines.length * lineHeight; return { pages, isText: true, scaledFontSize, fontFamily: fontFamily2, fontWeight, - lineHeight + lineHeight, + totalWidth, + totalHeight, + zoneRenderWidth: zoneRenderWidth * scale, + zoneRenderHeight: zoneRenderHeight * scale }; }); }, @@ -2855,66 +2884,286 @@ export default { // 动画主循环,更新每个分区的动画状态 const renderWidth = 640; const renderHeight = 240; - const scale = Math.min(renderWidth / 32, renderHeight / 64); + // const scale = Math.min(renderWidth / 32, renderHeight / 64); + // 使用与 prepareAddProgramPreviewAssets 中相同的 scale 计算方式 + let screenW = this.screenParams.width || 32; + let screenH = this.screenParams.height || 64; + const angle = this.screenParams.angle; + const swapWH = angle === 90 || angle === 270; + if (swapWH) { + [screenW, screenH] = [screenH, screenW]; + } + const scale = Math.min(renderWidth / screenW, renderHeight / screenH); + + this.addProgramForm.zones.forEach((zone, idx) => { const asset = this.addProgramPreviewAssets[idx]; const state = this.addProgramPreviewAnimState[idx]; + if (!asset || !state || !asset.isText) return; + const effect = zone.effect; const page = this.addProgramPreviewPage[idx] || 0; const pageLines = asset.pages[page] || []; - const lineHeight = asset.scaledFontSize * 1.2; - const totalHeight = pageLines.length * lineHeight; - const totalWidth = Math.max(...pageLines.map(line => this.measureTextWidth(line, asset))); + const lineHeight = asset.lineHeight; + // const totalHeight = pageLines.length * lineHeight; // 原始计算 + // const totalWidth = Math.max(...pageLines.map(line => measureCtx.measureText(line).width), 0); // 原始计算 + // 使用 prepareAddProgramPreviewAssets 中预先计算好的值 + const totalHeight = asset.totalHeight; + const totalWidth = asset.totalWidth; + + // 动画速度 const speedMap = [1, 2, 3, 4, 5]; - const animSpeed = (speedMap[zone.speed] || 3) * scale * 0.5; - // 区域宽高 - const zoneWidth = zone.width * scale; - const zoneHeight = zone.height * scale; - // 动画逻辑 + const animSpeed = (speedMap[zone.speed] || 3) * scale * 0.5; // 像素/帧 + + // 区域宽高 (使用预先计算并缩放的值) + const zoneWidth = asset.zoneRenderWidth; + const zoneHeight = asset.zoneRenderHeight; + + // 暂停时间 (毫秒) + const pauseMs = this.getPauseTime(zone.stayTime); + + // 确保初始状态值存在 + if (typeof state.isPausing !== 'boolean') state.isPausing = false; + if (typeof state.pauseStart !== 'number') state.pauseStart = 0; + if (typeof state.pausePhase !== 'string') state.pausePhase = 'start'; // 'start' | 'move' + + switch (effect) { case 1: // 左移 - if (state.currentX > -totalWidth) { - state.currentX -= animSpeed; - } else { - state.currentX = zoneWidth; + // 确保初始 state.currentX 值存在 + if (typeof state.currentX !== 'number') state.currentX = 0; + // 确保初始 pausePhase 值存在 + if (!state.pausePhase) state.pausePhase = 'start'; // 'start' | 'move' + + // 初始位置停留阶段 + if (!state.isPausing && state.pausePhase === 'start') { + state.isPausing = true; + state.pauseStart = Date.now(); } + + // 检查初始停留是否结束 + if (state.isPausing && state.pausePhase === 'start') { + if (Date.now() - state.pauseStart >= pauseMs) { + state.isPausing = false; + state.pausePhase = 'move'; + } + // 移动阶段 + } else if (!state.isPausing && state.pausePhase === 'move') { + // --- 强制多移动的逻辑 --- + // !!! 核心修改点 !!! + // 原判断: if (state.currentX + totalWidth > 0) + // 新判断: 计算从起始位置(0)到当前位置已经移动的距离 (-state.currentX) + // 并与一个我们认为足够"移出"的距离进行比较 + const pixelsMoved = -state.currentX; // 从 X=0 开始,向左移动了多远 + const distanceToTriggerReset = zoneWidth + 100; // <--- 关键:移出区域宽度 + 额外距离 + // 例如:区域宽 32 + 额外 100 = 132 + // 你可以调整这个 100 为你觉得合适的值 + + if (pixelsMoved < distanceToTriggerReset) { + // 移动距离还不够,继续移动 + state.currentX -= animSpeed; + } else { + // 移动距离足够了(强制认为已经移出),立即重置 + // console.log(`Zone ${idx} - Left Move: Forcefully reset after moving ${pixelsMoved.toFixed(2)} pixels.`); // 调试信息 + state.currentX = 0; // 回到起始位置 + state.pausePhase = 'start'; // 回到初始停留阶段 + } + // --- 强制多移动逻辑结束 --- + } + // Y轴不移动 + state.currentY = 0; break; + + case 2: // 右移 - if (state.currentX < zoneWidth) { - state.currentX += animSpeed; - } else { - state.currentX = -totalWidth; + // 确保初始 state.currentX 值存在 + if (typeof state.currentX !== 'number') state.currentX = 0; // 通常从左侧开始向右移 + // 确保初始 pausePhase 值存在 + if (!state.pausePhase) state.pausePhase = 'start'; // 'start' | 'move' + + // 初始位置停留阶段 + if (!state.isPausing && state.pausePhase === 'start') { + state.isPausing = true; + state.pauseStart = Date.now(); } + + // 检查初始停留是否结束 + if (state.isPausing && state.pausePhase === 'start') { + if (Date.now() - state.pauseStart >= pauseMs) { + state.isPausing = false; + state.pausePhase = 'move'; + } + // 移动阶段 + } else if (!state.isPausing && state.pausePhase === 'move') { + // --- 修正后的逻辑 --- + // 判断是否还可以继续向右移动 + // 条件:当前文本左边缘距离完全移出右边界还有距离 (currentX < zoneWidth) + if (state.currentX < zoneWidth) { + state.currentX += animSpeed; + // 可选:边界检查 + // if (state.currentX > zoneWidth) { + // state.currentX = zoneWidth; // 精确设置到边界 + // } + } else { + // 内容完全移出右侧,立即重置,准备下一轮 + // 不再进入 'end' 停留阶段 + state.currentX = 0; // 回到左侧起始位置 + state.pausePhase = 'start'; // 回到初始停留阶段 + } + // --- 修正结束 --- + } + // Y轴不移动 + state.currentY = 0; break; + case 3: // 上移 - if (state.currentY > -totalHeight) { - state.currentY -= animSpeed; - } else { - state.currentY = zoneHeight; + // 确保初始 state.currentY 值存在 + if (typeof state.currentY !== 'number') state.currentY = 0; + // 确保初始 pausePhase 值存在 + if (!state.pausePhase) state.pausePhase = 'start'; // 'start' | 'move' + + // 初始位置停留阶段 + if (!state.isPausing && state.pausePhase === 'start') { + state.isPausing = true; + state.pauseStart = Date.now(); } + + // 检查初始停留是否结束 + if (state.isPausing && state.pausePhase === 'start') { + if (Date.now() - state.pauseStart >= pauseMs) { + state.isPausing = false; + state.pausePhase = 'move'; + } + // 移动阶段 + } else if (!state.isPausing && state.pausePhase === 'move') { + // --- 修正后的逻辑 --- + // 判断是否还可以继续向上移动 + // 条件:当前文本底边距离完全移出顶边还有距离 (currentY + totalHeight > 0) + if (state.currentY + totalHeight > 0) { + state.currentY -= animSpeed; + // 可选:边界检查 + // if (state.currentY + totalHeight < 0) { + // state.currentY = -totalHeight; + // } + } else { + // 内容完全移出顶部,立即重置,准备下一轮 + // 不再进入 'end' 停留阶段 + state.currentY = 0; // 回到顶部起始位置 + state.pausePhase = 'start'; // 回到初始停留阶段 + } + // --- 修正结束 --- + } + // X轴不移动 + state.currentX = 0; break; + case 4: // 下移 - if (state.currentY < zoneHeight) { - state.currentY += animSpeed; - } else { - state.currentY = -totalHeight; + // 确保初始 state.currentY 值存在 + if (typeof state.currentY !== 'number') state.currentY = 0; // 通常从顶部开始向下移 + // 确保初始 pausePhase 值存在 + if (!state.pausePhase) state.pausePhase = 'start'; // 'start' | 'move' + + // 初始位置停留阶段 + if (!state.isPausing && state.pausePhase === 'start') { + state.isPausing = true; + state.pauseStart = Date.now(); } - break; - case 5: // 连续左移 - if (state.currentX > -totalWidth) { - state.currentX -= animSpeed; - } else { - state.currentX = 0; + + // 检查初始停留是否结束 + if (state.isPausing && state.pausePhase === 'start') { + if (Date.now() - state.pauseStart >= pauseMs) { + state.isPausing = false; + state.pausePhase = 'move'; + } + // 移动阶段 + } else if (!state.isPausing && state.pausePhase === 'move') { + // --- 修正后的逻辑 --- + // 判断是否还可以继续向下移动 + // 条件:当前文本顶边距离完全移出底边还有距离 (currentY < zoneHeight) + if (state.currentY < zoneHeight) { + state.currentY += animSpeed; + // 可选:边界检查 + // if (state.currentY > zoneHeight) { + // state.currentY = zoneHeight; + // } + } else { + // 内容完全移出底部,立即重置,准备下一轮 + // 不再进入 'end' 停留阶段 + state.currentY = 0; // 回到顶部起始位置 + state.pausePhase = 'start'; // 回到初始停留阶段 + } + // --- 修正结束 --- } + // X轴不移动 + state.currentX = 0; break; + + case 5: // 连续左移 (立即显示 -> 左移 -> 立即显示 -> ...) + // 确保初始 state.currentX 值存在 + if (typeof state.currentX !== 'number') state.currentX = 0; + // 确保初始 pausePhase 值存在 + if (!state.pausePhase) state.pausePhase = 'start'; // 'start' | 'move' + + // 初始立即显示阶段 (或一轮结束后的显示阶段) + if (!state.isPausing && state.pausePhase === 'start') { + // 立即显示阶段,不移动,停留 pauseMs 时间 + state.isPausing = true; + state.pauseStart = Date.now(); + } + + // 检查立即显示阶段是否结束 + if (state.isPausing && state.pausePhase === 'start') { + if (Date.now() - state.pauseStart >= pauseMs) { + state.isPausing = false; + state.pausePhase = 'move'; // 进入移动阶段 + } + // 移动阶段 (左移) + } else if (!state.isPausing && state.pausePhase === 'move') { + // --- 修正后的逻辑 (连续左移的移动逻辑与普通左移一致) --- + // 判断是否还可以继续向左移动 + if (state.currentX > -totalWidth) { + state.currentX -= animSpeed; + // 可选边界检查 + // if (state.currentX < -totalWidth) { + // state.currentX = -totalWidth; + // } + } else { + // 内容完全移出,进入下一个立即显示阶段 + // 连续左移的效果是在这里停留 pauseMs 后再显示下一轮 + state.isPausing = true; + state.pauseStart = Date.now(); + state.currentX = 0; // 回到起始位置显示 + state.pausePhase = 'start'; // 回到 'start' 阶段进行显示停留 + } + // --- 修正结束 --- + } + state.currentY = 0; // Y轴不移动 + break; + case 6: // 闪烁换页 - // 由自动翻页定时器控制 - break; - default: + // 由自动翻页定时器控制,此处不处理动画位移 state.currentX = 0; state.currentY = 0; + break; + + default: // 立即显示 (effect === 0) 或其他未知效果 + state.currentX = 0; + state.currentY = 0; + // 可以考虑添加立即显示的停留逻辑,如果需要的话 + // if (!state.isPausing && state.pausePhase === 'start') { + // state.isPausing = true; + // state.pauseStart = Date.now(); + // } + // if (state.isPausing && state.pausePhase === 'start') { + // if (Date.now() - state.pauseStart >= pauseMs) { + // state.isPausing = false; + // state.pausePhase = 'end'; // 或者重置为 'start' + // } + // } + // state.pausePhase = 'start'; // 重置状态以便下次循环 + break; } }); }, @@ -3016,36 +3265,47 @@ export default { zoneDisplay(zIdx) { const zone = this.addProgramForm.zones[zIdx]; if (!zone) return { x: 0, y: 0, width: 0, height: 0 }; - if (this.screenParams.angle === 90 || this.screenParams.angle === 270) { - return { - x: zone.y, - y: zone.x, - width: zone.height, - height: zone.width - }; - } else { - return { - x: zone.x, - y: zone.y, - width: zone.width, - height: zone.height - }; - } + // 已去除角度为90或270时的xy/宽高对调逻辑,UI显示与实际参数一致 + return { + x: zone.x, + y: zone.y, + width: zone.width, + height: zone.height + }; }, - // 设置分区的物理坐标,UI输入时自动对调回来 + // 设置分区的物理坐标,UI输入时不再自动对调 setZoneDisplay(zIdx, key, value) { const zone = this.addProgramForm.zones[zIdx]; if (!zone) return; - if (this.screenParams.angle === 90 || this.screenParams.angle === 270) { - // 对调 - if (key === 'x') zone.y = value; - else if (key === 'y') zone.x = value; - else if (key === 'width') zone.height = value; - else if (key === 'height') zone.width = value; - } else { - zone[key] = value; - } + // 已去除角度为90或270时的xy/宽高对调逻辑,UI输入与实际参数一致 + zone[key] = value; }, + updateBoardOnlineStatus() { + if (!this.deviceInfo || !this.deviceInfo.thingsModels) { + this.boardOnline = false; + return; + } + const onlineModel = this.deviceInfo.thingsModels.find(m => m.id === '1#onlineBoard'); + if (!onlineModel || !(onlineModel.shadow || onlineModel.value)) { // 先检查 shadow + this.boardOnline = false; + return; + } + try { + const valueStr = onlineModel.shadow || onlineModel.value; // 优先使用 shadow + const jsonStr = valueStr.replace(/^JSON=/, ''); + const data = JSON.parse(jsonStr); + const onlineArr = data.online || []; + this.boardOnline = onlineArr.includes(102); // 显卡编号 + } catch (e) { + this.boardOnline = false; + } + // console.log("测试是否受到消息", JSON.stringify(this.boardOnline)); + // console.log("数据来源:", onlineModel.shadow ? "shadow" : "value"); + + } + }, + computed: { + // ...原有computed }, }; @@ -3479,4 +3739,25 @@ export default { border-radius: 16px; margin-bottom: 20px; } + +.board-status-single { + margin-top: 6px; + font-size: 15px; + + span { + font-weight: 600; + + &.online { + color: #67C23A; + } + + &.offline { + color: #bbb; + } + } +} + +.title.offline { + color: #bbb; +} \ No newline at end of file diff --git a/src/views/iot/device/gateway-set.vue b/src/views/iot/device/gateway-set.vue index 27bfa1f..f7997ca 100644 --- a/src/views/iot/device/gateway-set.vue +++ b/src/views/iot/device/gateway-set.vue @@ -8,10 +8,23 @@
- 设备模式 + + + +
- {{ title }} + + + @@ -205,6 +218,42 @@ @@ -654,4 +888,25 @@ export default { } } } + +.board-status-single { + margin-top: 6px; + font-size: 15px; + + span { + font-weight: 600; + + &.online { + color: #67C23A; + } + + &.offline { + color: #bbb; + } + } +} + +.title.offline { + color: #bbb; +} \ No newline at end of file diff --git a/src/views/iot/device/gateway.vue b/src/views/iot/device/gateway.vue index 613173a..b502f5e 100644 --- a/src/views/iot/device/gateway.vue +++ b/src/views/iot/device/gateway.vue @@ -67,6 +67,15 @@ 子设备管理
+ + @@ -257,6 +266,24 @@ export default { // deviceInfo.product 可能为 undefined/null const arr = (this.deviceInfo.product || []).slice(1); // 跳过第一个 return arr.map(type => PRODUCT_TYPE_MAP[type]).filter(Boolean); + }, + // 板卡在线状态 + boardOnlineStatus() { + const model = this.deviceInfo.thingsModels?.find(m => m.id === '1#onlineBoard'); + if (!model || !(model.value || model.shadow)) return {}; + try { + const valueStr = model.value || model.shadow; + const jsonStr = valueStr.replace(/^JSON=/, ''); + const data = JSON.parse(jsonStr); + const onlineArr = data.online || []; + return { + gateway: onlineArr.includes(1), + display: onlineArr.includes(102), + sound: onlineArr.includes(103) + }; + } catch (e) { + return {}; + } } }, watch: { @@ -1031,4 +1058,23 @@ export default { } } } + +.board-status { + margin-top: 8px; + font-size: 15px; + + span { + display: inline-block; + min-width: 40px; + font-weight: 600; + + &.online { + color: #67C23A; + } + + &.offline { + color: #bbb; + } + } +} \ No newline at end of file diff --git a/src/views/iot/device/voicecard.vue b/src/views/iot/device/voicecard.vue index d707f5c..b7d0dc9 100644 --- a/src/views/iot/device/voicecard.vue +++ b/src/views/iot/device/voicecard.vue @@ -9,13 +9,18 @@
- {{ $t('device.running-status.866086-0') }} + + + +
- {{ title }} - - 打印物模型 - + +
@@ -49,10 +54,12 @@
- + - {{ basicSettings.volume }} + {{ + basicSettings.volume }}
@@ -294,19 +301,22 @@
- + {{ newAudio.spd }}
- + {{ newAudio.pit }}
- + {{ newAudio.vol }}
@@ -444,6 +454,7 @@ export default { }, firmware: {}, openFirmware: false, + boardOnline: false, loading: true, deviceInfo: { deviceId: 0, @@ -706,7 +717,7 @@ export default { if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) { this.deviceInfo.thingsModels = this.deviceInfo.thingsModels.sort((a, b) => b.order - a.order); this.updateBasicSettings(); // 更新基础设置 - this.printThingsModels(); + // this.printThingsModels(); } if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) { this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order); @@ -716,8 +727,9 @@ export default { // 更新基础设置 updateBasicSettings() { + // this.isBoardOnline(); if (!this.deviceInfo.thingsModels) return; - + this.updateBoardOnlineStatus(); // 更新音频开关状态 const playEnModel = this.deviceInfo.thingsModels.find(model => model.id === '103#playEn'); if (playEnModel) { @@ -791,6 +803,7 @@ export default { console.error('解析播放列表失败:', error); } } + }, printThingsModels() { @@ -800,7 +813,30 @@ export default { type: 'success' }); }, + //online or offline + updateBoardOnlineStatus() { + if (!this.deviceInfo || !this.deviceInfo.thingsModels) { + this.boardOnline = false; + return; + } + const onlineModel = this.deviceInfo.thingsModels.find(m => m.id === '1#onlineBoard'); + if (!onlineModel || !(onlineModel.shadow || onlineModel.value)) { // 先检查 shadow + this.boardOnline = false; + return; + } + try { + const valueStr = onlineModel.shadow || onlineModel.value; // 优先使用 shadow + const jsonStr = valueStr.replace(/^JSON=/, ''); + const data = JSON.parse(jsonStr); + const onlineArr = data.online || []; + this.boardOnline = onlineArr.includes(103); // 显卡编号 + } catch (e) { + this.boardOnline = false; + } + // console.log("测试声卡否受到消息", JSON.stringify(this.boardOnline)); + // console.log("数据来源:", onlineModel.shadow ? "shadow" : "value"); + }, // 声卡特有方法 formatVolume(val) { return val + '%'; @@ -841,130 +877,10 @@ export default { // }, - //更新参数值 - updateParam(params) { - let { serialNumber, productId, data } = params; - let isComplete = false; - data = data.message; - if (data) { - for (let j = 0; j < data.length; j++) { - for (let k = 0; k < this.deviceInfo.thingsModels.length && !isComplete; k++) { - if (this.deviceInfo.thingsModels[k].id == data[j].id) { - const variable = this.deviceInfo.thingsModels[k]; - // 普通类型(小数/整数/字符串/布尔/枚举) - if (this.deviceInfo.thingsModels[k].datatype.type == 'decimal' || this.deviceInfo.thingsModels[k].datatype.type == 'integer') { - variable.shadow = Number(data[j].value); - } else { - variable.shadow = data[j].value; - } - } - if (this.deviceInfo.thingsModels[k].datatype.type == 'object') { - // 对象类型 - for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.params.length; n++) { - if (this.deviceInfo.thingsModels[k].datatype.params[n].id == data[j].id) { - this.deviceInfo.thingsModels[k].datatype.params[n].shadow = data[j].value; - isComplete = true; - break; - } - } - } else if (this.deviceInfo.thingsModels[k].datatype.type == 'array') { - // 数组类型 - if (this.deviceInfo.thingsModels[k].datatype.arrayType == 'object') { - // 1.对象类型数组,id为数组中一个元素,例如:array_01_gateway_temperature - if (String(data[j].id).indexOf('array_') == 0) { - for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) { - for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) { - if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == data[j].id) { - this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value; - isComplete = true; - break; - } - } - if (isComplete) { - break; - } - } - } else { - // 2.对象类型数组,例如:gateway_temperature,消息ID添加前缀后匹配 - for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) { - for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) { - let index = n > 9 ? String(n) : '0' + k; - let prefix = 'array_' + index + '_'; - if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == prefix + data[j].id) { - this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value; - } - } - } - } - } else { - // 整数、小数和字符串类型数组 - for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayModel.length; n++) { - if (this.deviceInfo.thingsModels[k].datatype.arrayModel[n].id == data[j].id) { - this.deviceInfo.thingsModels[k].datatype.arrayModel[n].shadow = data[j].value; - break; - } - } - } - } - } - // 图表数据 - for (let k = 0; k < this.deviceInfo.chartList.length; k++) { - if (this.deviceInfo.chartList[k].id.indexOf('array_') == 0) { - // 数组类型匹配,例如:array_00_gateway_temperature - if (this.deviceInfo.chartList[k].id == data[j].id) { - this.deviceInfo.chartList[k].shadow = data[j].value; - // 更新图表 - for (let m = 0; m < this.monitorChart.length; m++) { - if (data[j].id == this.monitorChart[m].data.id) { - let data = [ - { - value: this.deviceInfo.chartList[k].shadow, - name: this.monitorChart[m].data.name, - }, - ]; - this.monitorChart[m].chart.setOption({ - series: [ - { - data: data, - }, - ], - }); - break; - } - } - } - } else { - // 普通类型匹配 - if (this.deviceInfo.chartList[k].id == data[j].id) { - this.deviceInfo.chartList[k].shadow = data[j].value; - // 更新图表 - for (let m = 0; m < this.monitorChart.length; m++) { - if (data[j].id == this.monitorChart[m].data.id) { - let data = [ - { - value: this.deviceInfo.chartList[k].shadow, - name: this.monitorChart[m].data.name, - }, - ]; - this.monitorChart[m].chart.setOption({ - series: [ - { - data: data, - }, - ], - }); - break; - } - } - } - } - if (isComplete) { - break; - } - } - } - } - this.updateBasicSettings(); + initData() { + this.$busEvent.$on('updateData', (params) => { + this.updateParam(params); + }); }, @@ -1830,6 +1746,10 @@ export default { this.editingPlaylistIndex = null; }, }, + computed: { + + // ...原有computed + }, }; @@ -2209,4 +2129,25 @@ export default { background: #a8a8a8; } } + +.board-status-single { + margin-top: 6px; + font-size: 15px; + + span { + font-weight: 600; + + &.online { + color: #67C23A; + } + + &.offline { + color: #bbb; + } + } +} + +.title.offline { + color: #bbb; +} \ No newline at end of file diff --git a/src/views/login.vue b/src/views/login.vue index cf6bc4d..995333e 100644 --- a/src/views/login.vue +++ b/src/views/login.vue @@ -263,14 +263,10 @@ - +