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 @@
- {{ title }}
-
+
+ {{ 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 }}
+
+ {{ 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 @@
- {{ title }}
-
- 打印物模型
-
+
+ {{ 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 @@
-
+