显卡逻辑完善
This commit is contained in:
parent
ec7e9ff6b9
commit
c470b9074e
@ -76,10 +76,10 @@
|
|||||||
:disabled="!basicSettings.screenEnabled">
|
:disabled="!basicSettings.screenEnabled">
|
||||||
</el-slider>
|
</el-slider>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<!-- <el-form-item>
|
||||||
<el-button type="danger" @click="handleClearScreen"
|
<el-button type="danger" @click="handleClearScreen"
|
||||||
:disabled="!basicSettings.screenEnabled">清除屏幕</el-button>
|
:disabled="!basicSettings.screenEnabled">清除屏幕</el-button>
|
||||||
</el-form-item>
|
</el-form-item> -->
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
@ -110,7 +110,7 @@
|
|||||||
<el-input-number v-model="screenParams.height" :min="1" :max="10000"></el-input-number>
|
<el-input-number v-model="screenParams.height" :min="1" :max="10000"></el-input-number>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="屏幕角度">
|
<el-form-item label="屏幕角度">
|
||||||
<el-radio-group v-model="screenParams.angle">
|
<el-radio-group v-model="screenParams.angle" @change="drawAddProgramPreview">
|
||||||
<el-radio :label="0">0°</el-radio>
|
<el-radio :label="0">0°</el-radio>
|
||||||
<el-radio :label="90">90°</el-radio>
|
<el-radio :label="90">90°</el-radio>
|
||||||
<el-radio :label="180">180°</el-radio>
|
<el-radio :label="180">180°</el-radio>
|
||||||
@ -356,11 +356,10 @@
|
|||||||
<!-- 添加/编辑自定义节目对话框 -->
|
<!-- 添加/编辑自定义节目对话框 -->
|
||||||
<el-dialog :title="isEditProgram ? '编辑自定义节目' : '添加自定义节目'" :visible.sync="addProgramDialogVisible" width="700px"
|
<el-dialog :title="isEditProgram ? '编辑自定义节目' : '添加自定义节目'" :visible.sync="addProgramDialogVisible" width="700px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<!-- <div
|
<div class="sticky-canvas-preview">
|
||||||
style="position:sticky;top:0;z-index:10;background:#fff;padding-top:24px;padding-bottom:32px;box-shadow:0 2px 8px #e0e0e0;text-align:center;margin-bottom:36px;width:600px;margin-left:auto;margin-right:auto;border-radius:16px;"> -->
|
<canvas ref="addProgramPreviewCanvas" width="640px" height="240px"
|
||||||
<canvas ref="addProgramPreviewCanvas" width="640px" height="240px"
|
style="width:100%;height:240px;border:1px solid #e0e0e0;border-radius:16px;box-shadow:0 2px 8px #e0e0e0;background:#FFFFFF00; margin-bottom: 20px;"></canvas>
|
||||||
style="width:100%;height:240px;border:1px solid #e0e0e0;border-radius:16px;box-shadow:0 2px 8px #e0e0e0;background:#FFFFFF00; margin-bottom: 20px;"></canvas>
|
</div>
|
||||||
<!-- </div> -->
|
|
||||||
<el-form :model="addProgramForm" ref="addProgramFormRef" label-width="60px" size="mini"
|
<el-form :model="addProgramForm" ref="addProgramFormRef" label-width="60px" size="mini"
|
||||||
style="margin-bottom:0;">
|
style="margin-bottom:0;">
|
||||||
<el-form-item label="节目模式" label-width="70px">
|
<el-form-item label="节目模式" label-width="70px">
|
||||||
@ -442,6 +441,14 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="7">
|
||||||
|
<el-form-item label="字体(英)" label-width="70px">
|
||||||
|
<el-select v-model="zone.fontEn" placeholder="字体(英)" style="width:100%">
|
||||||
|
<el-option v-for="(item, idx) in addProgramFontEns" :key="idx" :label="item"
|
||||||
|
:value="idx" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-form-item label="字号" label-width="42px">
|
<el-form-item label="字号" label-width="42px">
|
||||||
<el-select v-model="zone.fontSize" placeholder="字号" style="width:100%">
|
<el-select v-model="zone.fontSize" placeholder="字号" style="width:100%">
|
||||||
@ -522,22 +529,26 @@
|
|||||||
<el-row :gutter="8">
|
<el-row :gutter="8">
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-form-item label="X" label-width="20px">
|
<el-form-item label="X" label-width="20px">
|
||||||
<el-input-number v-model="zone.x" :min="0" style="width:100%" />
|
<el-input-number :value="zoneDisplay(zIdx).x" :min="0" style="width:100%"
|
||||||
|
@input="setZoneDisplay(zIdx, 'x', $event)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-form-item label="宽" label-width="24px">
|
<el-form-item label="宽" label-width="24px">
|
||||||
<el-input-number v-model="zone.width" :min="1" style="width:100%" />
|
<el-input-number :value="zoneDisplay(zIdx).width" :min="1" style="width:100%"
|
||||||
|
@input="setZoneDisplay(zIdx, 'width', $event)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-form-item label="Y" label-width="20px">
|
<el-form-item label="Y" label-width="20px">
|
||||||
<el-input-number v-model="zone.y" :min="0" style="width:100%" />
|
<el-input-number :value="zoneDisplay(zIdx).y" :min="0" style="width:100%"
|
||||||
|
@input="setZoneDisplay(zIdx, 'y', $event)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-form-item label="高" label-width="24px">
|
<el-form-item label="高" label-width="24px">
|
||||||
<el-input-number v-model="zone.height" :min="1" style="width:100%" />
|
<el-input-number :value="zoneDisplay(zIdx).height" :min="1" style="width:100%"
|
||||||
|
@input="setZoneDisplay(zIdx, 'height', $event)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -726,14 +737,15 @@ export default {
|
|||||||
commonPhrase: 0,
|
commonPhrase: 0,
|
||||||
displayText: '',
|
displayText: '',
|
||||||
font: 0,
|
font: 0,
|
||||||
|
fontEn: 0, // 新增英文字体
|
||||||
fontShape: 0,
|
fontShape: 0,
|
||||||
fontSize: 0,
|
fontSize: 0,
|
||||||
fontColor: 0,
|
fontColor: 0, // 红色(默认)
|
||||||
fontBold: 0,
|
fontBold: 0,
|
||||||
fontStretch: 0,
|
fontStretch: 0,
|
||||||
image: '',
|
image: '',
|
||||||
imageSize: 1, // 32x32
|
imageSize: 1, // 32x32
|
||||||
imageColor: 0, // Red
|
imageColor: 0, // 红色(默认)
|
||||||
effect: 0,
|
effect: 0,
|
||||||
speed: 4,
|
speed: 4,
|
||||||
stayTime: 5,
|
stayTime: 5,
|
||||||
@ -767,7 +779,7 @@ export default {
|
|||||||
addProgramFonts: ['宋体(中)', '黑体(中)', '楷体(中)'],
|
addProgramFonts: ['宋体(中)', '黑体(中)', '楷体(中)'],
|
||||||
addProgramFontShapes: ['圆角(英)', '直角(英)'],
|
addProgramFontShapes: ['圆角(英)', '直角(英)'],
|
||||||
addProgramFontSizes: ['16px', '24px', '32px'],
|
addProgramFontSizes: ['16px', '24px', '32px'],
|
||||||
addProgramFontColors: ['黑色', '红色', '绿色', '黄色'],
|
addProgramFontColors: ['红色', '绿色', '黄色'],
|
||||||
addProgramFontBold: ['不加粗', '加粗'],
|
addProgramFontBold: ['不加粗', '加粗'],
|
||||||
addProgramFontStretch: ['不拉伸', '横向拉伸', '纵向拉伸'],
|
addProgramFontStretch: ['不拉伸', '横向拉伸', '纵向拉伸'],
|
||||||
addProgramEffects: ['立即显示', '左移', '右移', '上移', '下移', '连续左移', '闪烁换页'],
|
addProgramEffects: ['立即显示', '左移', '右移', '上移', '下移', '连续左移', '闪烁换页'],
|
||||||
@ -776,7 +788,10 @@ export default {
|
|||||||
addProgramHorizontalAlign: ['居左对齐', '居中对齐', '居右对齐'],
|
addProgramHorizontalAlign: ['居左对齐', '居中对齐', '居右对齐'],
|
||||||
addProgramVerticalAlign: ['顶部对齐', '居中对齐', '底部对齐'],
|
addProgramVerticalAlign: ['顶部对齐', '居中对齐', '底部对齐'],
|
||||||
addProgramImageSizes: ['16x16', '32x32', '48x48', '64x64'],
|
addProgramImageSizes: ['16x16', '32x32', '48x48', '64x64'],
|
||||||
addProgramImageColors: ['黑色', '红色', '绿色', '黄色'],
|
addProgramImageColors: ['红色', '绿色', '黄色'],
|
||||||
|
addProgramFontEns: [
|
||||||
|
'打字(英)', '长黑(英)', '白斜(英)', '线形(英)', '方斜(英)', '歌德(英)', '黑正(英)', '美术(英)', '手写(英)', '正圆(英)', '时钟(英)圆角(英)'
|
||||||
|
],
|
||||||
// 自动分页相关
|
// 自动分页相关
|
||||||
addProgramPreviewPage: [0], // 每个分区当前页
|
addProgramPreviewPage: [0], // 每个分区当前页
|
||||||
addProgramPreviewTimer: [], // 每个分区定时器
|
addProgramPreviewTimer: [], // 每个分区定时器
|
||||||
@ -961,13 +976,8 @@ export default {
|
|||||||
const progListModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progList');
|
const progListModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progList');
|
||||||
if (progListModel) {
|
if (progListModel) {
|
||||||
try {
|
try {
|
||||||
const jsonStr = progListModel.shadow.replace('JSON=', '');
|
// 只发送删除指令,不带上整个节目列表
|
||||||
const data = JSON.parse(jsonStr);
|
progListModel.shadow = 'JSON=' + JSON.stringify({ del_prog: program.order });
|
||||||
|
|
||||||
// 删除指定序号的节目
|
|
||||||
data.del_prog = program.order;
|
|
||||||
|
|
||||||
progListModel.shadow = 'JSON=' + JSON.stringify(data);
|
|
||||||
this.mqttPublish(this.deviceInfo, progListModel).then(() => {
|
this.mqttPublish(this.deviceInfo, progListModel).then(() => {
|
||||||
this.$message.success('删除指令已发送');
|
this.$message.success('删除指令已发送');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -1014,28 +1024,37 @@ export default {
|
|||||||
if (programData.aLst && programData.aLst.length > 0) {
|
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 => {
|
this.addProgramForm.zones = programData.aLst.map(area => {
|
||||||
|
let x = area.size ? area.size.x : 0;
|
||||||
|
let y = area.size ? area.size.y : 0;
|
||||||
|
let w = area.size ? area.size.l : 32;
|
||||||
|
let h = area.size ? area.size.h : 64;
|
||||||
|
if (this.screenParams.angle === 90 || this.screenParams.angle === 270) {
|
||||||
|
[x, y] = [y, x];
|
||||||
|
[w, h] = [h, w];
|
||||||
|
}
|
||||||
const zone = {
|
const zone = {
|
||||||
playType: 0,
|
playType: 0,
|
||||||
commonPhrase: 0,
|
commonPhrase: 0,
|
||||||
displayText: '',
|
displayText: '',
|
||||||
font: 0,
|
font: 0,
|
||||||
|
fontEn: 0, // 新增英文字体
|
||||||
fontShape: 0,
|
fontShape: 0,
|
||||||
fontSize: 0,
|
fontSize: 0,
|
||||||
fontColor: 0,
|
fontColor: 0, // 红色(默认)
|
||||||
fontBold: 0,
|
fontBold: 0,
|
||||||
fontStretch: 0,
|
fontStretch: 0,
|
||||||
image: '',
|
image: '',
|
||||||
imageSize: 1,
|
imageSize: 1,
|
||||||
imageColor: 0,
|
imageColor: 0, // 红色(默认)
|
||||||
effect: 0,
|
effect: 0,
|
||||||
speed: 4,
|
speed: 4,
|
||||||
stayTime: 5,
|
stayTime: 5,
|
||||||
hAlign: 1,
|
hAlign: 1,
|
||||||
vAlign: 1,
|
vAlign: 1,
|
||||||
x: area.size ? area.size.x : 0,
|
x: x,
|
||||||
y: area.size ? area.size.y : 0,
|
y: y,
|
||||||
width: area.size ? area.size.l : 32,
|
width: w,
|
||||||
height: area.size ? area.size.h : 64
|
height: h
|
||||||
};
|
};
|
||||||
|
|
||||||
// 解析播放项
|
// 解析播放项
|
||||||
@ -1046,12 +1065,13 @@ export default {
|
|||||||
zone.playType = 0;
|
zone.playType = 0;
|
||||||
zone.displayText = item.txt.str ? item.txt.str.replace(/\0/g, '') : '';
|
zone.displayText = item.txt.str ? item.txt.str.replace(/\0/g, '') : '';
|
||||||
zone.font = item.txt.fCn ? item.txt.fCn - 1 : 0; // 中文字体索引
|
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.fontSize = item.txt.fS ? this.getFontSizeIndex(item.txt.fS) : 0; // 字体大小索引
|
||||||
zone.fontColor = item.txt.col || 0; // 文本颜色
|
zone.fontColor = item.txt.col || 0; // 文本颜色
|
||||||
zone.fontBold = item.txt.fW || 0; // 字体加粗
|
zone.fontBold = item.txt.fW || 0; // 字体加粗
|
||||||
zone.fontStretch = item.txt.stch || 0; // 拉伸方向
|
zone.fontStretch = item.txt.stch || 0; // 拉伸方向
|
||||||
zone.hAlign = item.txt.hPos || 1; // 水平对齐
|
zone.hAlign = typeof item.txt.hPos === 'number' ? Math.max(item.txt.hPos - 1, 0) : 1; // 水平对齐
|
||||||
zone.vAlign = item.txt.vPos || 1; // 垂直对齐
|
zone.vAlign = typeof item.txt.vPos === 'number' ? Math.max(item.txt.vPos - 1, 0) : 1; // 垂直对齐
|
||||||
} else if (item.typ === 1 && item.img) {
|
} else if (item.typ === 1 && item.img) {
|
||||||
// 图片类型
|
// 图片类型
|
||||||
zone.playType = 1;
|
zone.playType = 1;
|
||||||
@ -1077,14 +1097,15 @@ export default {
|
|||||||
commonPhrase: 0,
|
commonPhrase: 0,
|
||||||
displayText: '',
|
displayText: '',
|
||||||
font: 0,
|
font: 0,
|
||||||
|
fontEn: 0, // 新增英文字体
|
||||||
fontShape: 0,
|
fontShape: 0,
|
||||||
fontSize: 0,
|
fontSize: 0,
|
||||||
fontColor: 0,
|
fontColor: 0, // 红色(默认)
|
||||||
fontBold: 0,
|
fontBold: 0,
|
||||||
fontStretch: 0,
|
fontStretch: 0,
|
||||||
image: '',
|
image: '',
|
||||||
imageSize: 1,
|
imageSize: 1,
|
||||||
imageColor: 0,
|
imageColor: 0, // 红色(默认)
|
||||||
effect: 0,
|
effect: 0,
|
||||||
speed: 4,
|
speed: 4,
|
||||||
stayTime: 5,
|
stayTime: 5,
|
||||||
@ -1099,6 +1120,7 @@ export default {
|
|||||||
|
|
||||||
// 更新预览
|
// 更新预览
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
this.onAddProgramModeChange(this.addProgramForm.mode); // 保证分区布局和模式同步
|
||||||
this.drawAddProgramPreview();
|
this.drawAddProgramPreview();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -2087,6 +2109,7 @@ export default {
|
|||||||
this.$set(this.screenParams, 'width', params.w);
|
this.$set(this.screenParams, 'width', params.w);
|
||||||
this.$set(this.screenParams, 'height', params.h);
|
this.$set(this.screenParams, 'height', params.h);
|
||||||
this.$set(this.screenParams, 'angle', params.angle);
|
this.$set(this.screenParams, 'angle', params.angle);
|
||||||
|
this.drawAddProgramPreview(); // 切换模板时强制刷新预览
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleOePolarityChange(val) {
|
handleOePolarityChange(val) {
|
||||||
@ -2187,14 +2210,15 @@ export default {
|
|||||||
commonPhrase: 0,
|
commonPhrase: 0,
|
||||||
displayText: '',
|
displayText: '',
|
||||||
font: 0,
|
font: 0,
|
||||||
|
fontEn: 0, // 新增英文字体
|
||||||
fontShape: 0,
|
fontShape: 0,
|
||||||
fontSize: 0,
|
fontSize: 0,
|
||||||
fontColor: 0,
|
fontColor: 0, // 红色(默认)
|
||||||
fontBold: 0,
|
fontBold: 0,
|
||||||
fontStretch: 0,
|
fontStretch: 0,
|
||||||
image: '',
|
image: '',
|
||||||
imageSize: 1,
|
imageSize: 1,
|
||||||
imageColor: 0,
|
imageColor: 0, // 红色(默认)
|
||||||
effect: 0,
|
effect: 0,
|
||||||
speed: 4,
|
speed: 4,
|
||||||
stayTime: 5,
|
stayTime: 5,
|
||||||
@ -2222,14 +2246,15 @@ export default {
|
|||||||
commonPhrase: 0,
|
commonPhrase: 0,
|
||||||
displayText: '',
|
displayText: '',
|
||||||
font: 0,
|
font: 0,
|
||||||
|
fontEn: 0, // 新增英文字体
|
||||||
fontShape: 0,
|
fontShape: 0,
|
||||||
fontSize: 0,
|
fontSize: 0,
|
||||||
fontColor: 0,
|
fontColor: 0, // 红色(默认)
|
||||||
fontBold: 0,
|
fontBold: 0,
|
||||||
fontStretch: 0,
|
fontStretch: 0,
|
||||||
image: '',
|
image: '',
|
||||||
imageSize: 1,
|
imageSize: 1,
|
||||||
imageColor: 0,
|
imageColor: 0, // 红色(默认)
|
||||||
effect: 0,
|
effect: 0,
|
||||||
speed: 4,
|
speed: 4,
|
||||||
stayTime: 5,
|
stayTime: 5,
|
||||||
@ -2282,77 +2307,97 @@ export default {
|
|||||||
const progListModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progList');
|
const progListModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progList');
|
||||||
if (progListModel) {
|
if (progListModel) {
|
||||||
try {
|
try {
|
||||||
// 构建符合显卡接口规范的节目数据
|
// 计算 order
|
||||||
const programData = {
|
let order;
|
||||||
del_prog: 0, // 不删除节目
|
if (this.isEditProgram && this.editingProgram) {
|
||||||
prog_list: [{
|
order = this.editingProgram.order;
|
||||||
order: this.isEditProgram ? this.editingProgram.order : 0, // 编辑模式使用原序号
|
} else {
|
||||||
rem: (this.addProgramForm.remark || `自定义节目${Date.now()}`) + '\0', // 节目备注,需要包含\0
|
// 新建,找未用的最小 order
|
||||||
dur: this.addProgramForm.duration, // 播放时长(秒)
|
const usedOrders = this.programList.map(p => p.order);
|
||||||
areaM: this.addProgramForm.mode, // 分区模式
|
order = 0;
|
||||||
aLst: this.addProgramForm.zones.map(zone => {
|
while (usedOrders.includes(order) && order < 10) {
|
||||||
// 构建分区数据
|
order++;
|
||||||
const area = {
|
}
|
||||||
size: {
|
}
|
||||||
x: zone.x, // 左下角X坐标
|
|
||||||
y: zone.y, // 左下角Y坐标
|
// 构建节目对象
|
||||||
l: zone.width, // 宽度
|
const programObj = {
|
||||||
h: zone.height // 高度
|
order: order,
|
||||||
|
rem: (this.addProgramForm.remark || `自定义节目${Date.now()}`) + '\0', // 节目备注,需要包含\0
|
||||||
|
dur: this.addProgramForm.duration, // 播放时长(秒)
|
||||||
|
areaM: this.addProgramForm.mode, // 分区模式
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
const area = {
|
||||||
|
size: {
|
||||||
|
x: x, // 左下角X坐标
|
||||||
|
y: y, // 左下角Y坐标
|
||||||
|
l: w, // 宽度
|
||||||
|
h: h // 高度
|
||||||
|
},
|
||||||
|
pLst: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据播放类型构建播放项
|
||||||
|
if (zone.playType === 0) {
|
||||||
|
// 文本类型
|
||||||
|
const textItem = {
|
||||||
|
typ: 0,
|
||||||
|
anim: {
|
||||||
|
typ: zone.effect + 1, // 动画类型映射
|
||||||
|
spd: zone.speed, // 动画速度
|
||||||
|
pauseT: this.getPauseTime(zone.stayTime), // 暂停时间
|
||||||
|
playT: 2000 // 播放时间
|
||||||
},
|
},
|
||||||
pLst: []
|
txt: {
|
||||||
|
str: zone.displayText + '\0', // 文本内容,需要包含\0
|
||||||
|
col: zone.fontColor + 1, // 文本颜色
|
||||||
|
fS: this.getFontSize(zone.fontSize), // 字体大小
|
||||||
|
fCn: zone.font + 1, // 中文字体
|
||||||
|
fEn: zone.fontEn + 1, // 英文字体
|
||||||
|
fW: zone.fontBold, // 字体加粗
|
||||||
|
stch: zone.fontStretch, // 拉伸方向
|
||||||
|
hPos: zone.hAlign - 1, // 水平对齐
|
||||||
|
vPos: zone.vAlign - 1 // 垂直对齐
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
area.pLst.push(textItem);
|
||||||
|
} else if (zone.playType === 1) {
|
||||||
|
// 图片类型
|
||||||
|
const imageItem = {
|
||||||
|
typ: 1,
|
||||||
|
anim: {
|
||||||
|
typ: zone.effect + 1,
|
||||||
|
spd: zone.speed,
|
||||||
|
pauseT: this.getPauseTime(zone.stayTime),
|
||||||
|
playT: 2000
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
num: this.getImageNumber(zone.image), // 图片编号
|
||||||
|
col: zone.imageColor, // 图片颜色
|
||||||
|
data: '', // 预留字段
|
||||||
|
w: this.getImageSize(zone.imageSize), // 图片宽度
|
||||||
|
h: this.getImageSize(zone.imageSize) // 图片高度
|
||||||
|
}
|
||||||
|
};
|
||||||
|
area.pLst.push(imageItem);
|
||||||
|
}
|
||||||
|
|
||||||
// 根据播放类型构建播放项
|
return area;
|
||||||
if (zone.playType === 0) {
|
})
|
||||||
// 文本类型
|
};
|
||||||
const textItem = {
|
|
||||||
typ: 0,
|
// 只下发当前节目
|
||||||
anim: {
|
const programData = {
|
||||||
typ: zone.effect + 1, // 动画类型映射
|
del_prog: 0, // 不删除节目
|
||||||
spd: zone.speed, // 动画速度
|
prog_list: [programObj]
|
||||||
pauseT: this.getPauseTime(zone.stayTime), // 暂停时间
|
|
||||||
playT: 2000 // 播放时间
|
|
||||||
},
|
|
||||||
txt: {
|
|
||||||
str: zone.displayText + '\0', // 文本内容,需要包含\0
|
|
||||||
col: zone.fontColor, // 文本颜色
|
|
||||||
fS: this.getFontSize(zone.fontSize), // 字体大小
|
|
||||||
fCn: zone.font + 1, // 中文字体
|
|
||||||
fEn: 7, // 英文字体默认值
|
|
||||||
fW: zone.fontBold, // 字体加粗
|
|
||||||
stch: zone.fontStretch, // 拉伸方向
|
|
||||||
hPos: zone.hAlign, // 水平对齐
|
|
||||||
vPos: zone.vAlign // 垂直对齐
|
|
||||||
}
|
|
||||||
};
|
|
||||||
area.pLst.push(textItem);
|
|
||||||
} else if (zone.playType === 1) {
|
|
||||||
// 图片类型
|
|
||||||
const imageItem = {
|
|
||||||
typ: 1,
|
|
||||||
anim: {
|
|
||||||
typ: zone.effect + 1,
|
|
||||||
spd: zone.speed,
|
|
||||||
pauseT: this.getPauseTime(zone.stayTime),
|
|
||||||
playT: 2000
|
|
||||||
},
|
|
||||||
img: {
|
|
||||||
num: this.getImageNumber(zone.image), // 图片编号
|
|
||||||
col: zone.imageColor, // 图片颜色
|
|
||||||
data: '', // 预留字段
|
|
||||||
w: this.getImageSize(zone.imageSize), // 图片宽度
|
|
||||||
h: this.getImageSize(zone.imageSize) // 图片高度
|
|
||||||
}
|
|
||||||
};
|
|
||||||
area.pLst.push(imageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return area;
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送节目数据
|
|
||||||
progListModel.shadow = 'JSON=' + JSON.stringify(programData);
|
progListModel.shadow = 'JSON=' + JSON.stringify(programData);
|
||||||
this.mqttPublish(this.deviceInfo, progListModel).then(() => {
|
this.mqttPublish(this.deviceInfo, progListModel).then(() => {
|
||||||
this.addProgramDialogVisible = false;
|
this.addProgramDialogVisible = false;
|
||||||
@ -2376,8 +2421,14 @@ export default {
|
|||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
// 获取当前屏幕参数
|
// 获取当前屏幕参数
|
||||||
const screenW = this.screenParams.width || 32;
|
let screenW = this.screenParams.width || 32;
|
||||||
const screenH = this.screenParams.height || 64;
|
let screenH = this.screenParams.height || 64;
|
||||||
|
// 如果角度为90或270,渲染时宽高互换
|
||||||
|
const angle = this.screenParams.angle;
|
||||||
|
let swapWH = angle === 90 || angle === 270;
|
||||||
|
if (swapWH) {
|
||||||
|
[screenW, screenH] = [screenH, screenW];
|
||||||
|
}
|
||||||
|
|
||||||
// 获取实际渲染尺寸
|
// 获取实际渲染尺寸
|
||||||
const renderWidth = 640; // 调大预览宽度
|
const renderWidth = 640; // 调大预览宽度
|
||||||
@ -2414,11 +2465,19 @@ export default {
|
|||||||
const asset = this.addProgramPreviewAssets[zIdx];
|
const asset = this.addProgramPreviewAssets[zIdx];
|
||||||
if (!asset) return;
|
if (!asset) return;
|
||||||
|
|
||||||
// 计算分区在画布中的位置
|
// 计算分区在画布中的位置(角度为90或270时,x/y/width/height互换)
|
||||||
const zoneX = offsetX + zone.x * scale;
|
let zoneX, zoneY, zoneWidth, zoneHeight;
|
||||||
const zoneY = offsetY + zone.y * scale;
|
if (swapWH) {
|
||||||
const zoneWidth = zone.width * scale;
|
zoneX = offsetX + (zone.y || 0) * scale;
|
||||||
const zoneHeight = zone.height * scale;
|
zoneY = offsetY + (zone.x || 0) * scale;
|
||||||
|
zoneWidth = (zone.height || 0) * scale;
|
||||||
|
zoneHeight = (zone.width || 0) * scale;
|
||||||
|
} else {
|
||||||
|
zoneX = offsetX + (zone.x || 0) * scale;
|
||||||
|
zoneY = offsetY + (zone.y || 0) * scale;
|
||||||
|
zoneWidth = (zone.width || 0) * scale;
|
||||||
|
zoneHeight = (zone.height || 0) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
// 绘制分区边框(蓝色虚线)
|
// 绘制分区边框(蓝色虚线)
|
||||||
ctx.save();
|
ctx.save();
|
||||||
@ -2443,10 +2502,19 @@ export default {
|
|||||||
const pageLines = asset.pages[page] || [];
|
const pageLines = asset.pages[page] || [];
|
||||||
|
|
||||||
// 设置字体样式
|
// 设置字体样式
|
||||||
const colors = ['#000000', '#FF0000', '#00FF00', '#FFFF00'];
|
const colors = ['#FF0000', '#00FF00', '#FFFF00'];
|
||||||
ctx.fillStyle = colors[zone.fontColor] || '#000000';
|
ctx.fillStyle = colors[zone.fontColor] || '#000000';
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
ctx.font = `${asset.fontWeight} ${asset.scaledFontSize}px ${asset.fontFamily}`;
|
// 这里根据zone.fontEn选择英文字体
|
||||||
|
let fontFamily = ['SimSun', 'SimHei', 'KaiTi'][zone.font] || 'SimSun';
|
||||||
|
if (zone.fontEn !== undefined && zone.fontEn !== null) {
|
||||||
|
// 你可以根据实际需求映射到具体英文字体
|
||||||
|
const enFontMap = [
|
||||||
|
'Courier New', 'Arial Black', 'Arial Italic', 'Lucida Console', 'Impact', 'Gothic', 'Arial Narrow', 'Comic Sans MS', 'Brush Script MT', 'Century Gothic', 'Times New Roman'
|
||||||
|
];
|
||||||
|
fontFamily = enFontMap[zone.fontEn] || fontFamily;
|
||||||
|
}
|
||||||
|
ctx.font = `${asset.fontWeight} ${asset.scaledFontSize}px ${fontFamily}`;
|
||||||
|
|
||||||
// 计算行高
|
// 计算行高
|
||||||
const lineHeight = asset.scaledFontSize * 1.2;
|
const lineHeight = asset.scaledFontSize * 1.2;
|
||||||
@ -2570,8 +2638,14 @@ export default {
|
|||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
||||||
// 动态获取屏幕参数
|
// 动态获取屏幕参数
|
||||||
const screenW = this.screenParams.width || 32;
|
let screenW = this.screenParams.width || 32;
|
||||||
const screenH = this.screenParams.height || 64;
|
let screenH = this.screenParams.height || 64;
|
||||||
|
// 如果角度为90或270,渲染时宽高互换
|
||||||
|
const angle = this.screenParams.angle;
|
||||||
|
const swapWH = angle === 90 || angle === 270;
|
||||||
|
if (swapWH) {
|
||||||
|
[screenW, screenH] = [screenH, screenW];
|
||||||
|
}
|
||||||
const renderWidth = 640; // 与实际渲染宽度匹配
|
const renderWidth = 640; // 与实际渲染宽度匹配
|
||||||
const scale = Math.min(renderWidth / screenW, 240 / screenH);
|
const scale = Math.min(renderWidth / screenW, 240 / screenH);
|
||||||
|
|
||||||
@ -2610,7 +2684,8 @@ export default {
|
|||||||
measureCtx.font = `${fontWeight} ${scaledFontSize}px ${fontFamily}`;
|
measureCtx.font = `${fontWeight} ${scaledFontSize}px ${fontFamily}`;
|
||||||
|
|
||||||
// 分行逻辑
|
// 分行逻辑
|
||||||
const maxWidth = zone.width * scale - 4; // 预留4px边距
|
const zoneRenderWidth = swapWH ? zone.height : zone.width;
|
||||||
|
const maxWidth = zoneRenderWidth * scale - 4; // 预留4px边距
|
||||||
let currentLine = '';
|
let currentLine = '';
|
||||||
const lines = [];
|
const lines = [];
|
||||||
|
|
||||||
@ -2629,8 +2704,9 @@ export default {
|
|||||||
if (currentLine) lines.push(currentLine);
|
if (currentLine) lines.push(currentLine);
|
||||||
|
|
||||||
// 计算每页最大行数
|
// 计算每页最大行数
|
||||||
|
const zoneRenderHeight = swapWH ? zone.width : zone.height;
|
||||||
const lineHeight = scaledFontSize * 1.2;
|
const lineHeight = scaledFontSize * 1.2;
|
||||||
const maxLines = Math.max(1, Math.floor((zone.height * scale - 4) / lineHeight));
|
const maxLines = Math.max(1, Math.floor((zoneRenderHeight * scale - 4) / lineHeight));
|
||||||
|
|
||||||
// 分页
|
// 分页
|
||||||
const pages = [];
|
const pages = [];
|
||||||
@ -2901,6 +2977,40 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
// ================= 显卡物模型适配方法 END =================
|
// ================= 显卡物模型适配方法 END =================
|
||||||
|
// 获取分区显示用的坐标和尺寸,index为分区下标
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 设置分区的物理坐标,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;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -3321,4 +3431,17 @@ export default {
|
|||||||
background: #a8a8a8;
|
background: #a8a8a8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 弹窗内canvas预览区固定顶部 */
|
||||||
|
.sticky-canvas-preview {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
background: #fff;
|
||||||
|
padding-top: 24px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
box-shadow: 0 2px 8px #e0e0e0;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -76,20 +76,21 @@
|
|||||||
<!-- 子设备标题 -->
|
<!-- 子设备标题 -->
|
||||||
|
|
||||||
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
|
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
|
||||||
<el-tab-pane label="显示屏控制" name="display" style="line-height: 42px; height: 42px;">
|
<el-tab-pane v-if="availableTabs.includes('display')" label="显示屏控制" name="display"
|
||||||
|
style="line-height: 42px; height: 42px;">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<display-component :device="device" ref="displayComponent" v-if="activeTab === 'display'">
|
<display-component :device="device" ref="displayComponent" v-if="activeTab === 'display'">
|
||||||
</display-component>
|
</display-component>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="声卡控制" name="voicecard">
|
<el-tab-pane v-if="availableTabs.includes('voicecard')" label="声卡控制" name="voicecard">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<voicecard-component :device="device" ref="voicecardComponent" v-if="activeTab === 'voicecard'">
|
<voicecard-component :device="device" ref="voicecardComponent" v-if="activeTab === 'voicecard'">
|
||||||
</voicecard-component>
|
</voicecard-component>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="网关设置" name="gatewaySet">
|
<el-tab-pane v-if="availableTabs.includes('gatewaySet')" label="网关设置" name="gatewaySet">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<gatewaySet :device="device" ref="gatewaySet" v-if="activeTab === 'gatewaySet'">
|
<gatewaySet :device="device" ref="gatewaySet" v-if="activeTab === 'gatewaySet'">
|
||||||
</gatewaySet>
|
</gatewaySet>
|
||||||
@ -145,17 +146,17 @@
|
|||||||
$t('device.running-status.866086-12') }}</el-link>
|
$t('device.running-status.866086-12') }}</el-link>
|
||||||
</template>
|
</template>
|
||||||
<el-descriptions-item :label="$t('device.running-status.866086-13')">{{ firmware.firmwareName
|
<el-descriptions-item :label="$t('device.running-status.866086-13')">{{ firmware.firmwareName
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('device.device-edit.148398-4')">{{ firmware.productName
|
<el-descriptions-item :label="$t('device.device-edit.148398-4')">{{ firmware.productName
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('device.device-edit.148398-12')">Version {{ firmware.version
|
<el-descriptions-item :label="$t('device.device-edit.148398-12')">Version {{ firmware.version
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('device.running-status.866086-16')">
|
<el-descriptions-item :label="$t('device.running-status.866086-16')">
|
||||||
<el-link :href="getDownloadUrl(firmware.filePath)" :underline="false" type="primary">{{
|
<el-link :href="getDownloadUrl(firmware.filePath)" :underline="false" type="primary">{{
|
||||||
getDownloadUrl(firmware.filePath) }}</el-link>
|
getDownloadUrl(firmware.filePath) }}</el-link>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('device.running-status.866086-17')">{{ firmware.remark
|
<el-descriptions-item :label="$t('device.running-status.866086-17')">{{ firmware.remark
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button type="success" @click="otaUpgrade"
|
<el-button type="success" @click="otaUpgrade"
|
||||||
@ -175,6 +176,14 @@ import { getOrderControl } from '@/api/iot/control';
|
|||||||
import DisplayComponent from './display.vue';
|
import DisplayComponent from './display.vue';
|
||||||
import VoicecardComponent from './voicecard.vue';
|
import VoicecardComponent from './voicecard.vue';
|
||||||
import gatewaySet from './gateway-set.vue';
|
import gatewaySet from './gateway-set.vue';
|
||||||
|
|
||||||
|
// 子产品类型映射
|
||||||
|
const PRODUCT_TYPE_MAP = {
|
||||||
|
102: 'display', // 显卡
|
||||||
|
103: 'voicecard', // 声卡
|
||||||
|
1: 'gatewaySet', // 网关卡
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'gateway',
|
name: 'gateway',
|
||||||
components: {
|
components: {
|
||||||
@ -242,6 +251,14 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
// 计算当前设备支持的子产品 Tab
|
||||||
|
availableTabs() {
|
||||||
|
// deviceInfo.product 可能为 undefined/null
|
||||||
|
const arr = (this.deviceInfo.product || []).slice(1); // 跳过第一个
|
||||||
|
return arr.map(type => PRODUCT_TYPE_MAP[type]).filter(Boolean);
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
device: {
|
device: {
|
||||||
handler(newVal) {
|
handler(newVal) {
|
||||||
@ -256,18 +273,60 @@ export default {
|
|||||||
if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) {
|
if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) {
|
||||||
this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order);
|
this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 调用解析函数
|
||||||
|
this.parseProductFromThingsModels();
|
||||||
}
|
}
|
||||||
|
// activeTab 不在 availableTabs 里时,切换到第一个可用 Tab
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.availableTabs.includes(this.activeTab) && this.availableTabs.length > 0) {
|
||||||
|
this.activeTab = this.availableTabs[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 监听 availableTabs 变化,保证 activeTab 合理
|
||||||
|
availableTabs(newTabs) {
|
||||||
|
if (!newTabs.includes(this.activeTab) && newTabs.length > 0) {
|
||||||
|
this.activeTab = newTabs[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.device && this.device.deviceId) {
|
if (this.device && this.device.deviceId) {
|
||||||
this.handleDeviceChange(this.device);
|
this.handleDeviceChange(this.device);
|
||||||
this.initDataStatus();
|
this.initDataStatus();
|
||||||
this.initData();
|
this.initData();
|
||||||
|
// mounted 时也解析一次
|
||||||
|
this.parseProductFromThingsModels();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 解析 thingsModels 里的 1#productpram,提取 product 数组
|
||||||
|
parseProductFromThingsModels() {
|
||||||
|
const productModel = (this.deviceInfo.thingsModels || []).find(
|
||||||
|
item => item.id === '1#productpram'
|
||||||
|
);
|
||||||
|
if (productModel) {
|
||||||
|
let valueStr = productModel.value || productModel.shadow || '';
|
||||||
|
let match = valueStr.match(/JSON=(.*)/);
|
||||||
|
if (match) {
|
||||||
|
try {
|
||||||
|
let json = JSON.parse(match[1]);
|
||||||
|
if (json.product) {
|
||||||
|
this.deviceInfo.product = json.product;
|
||||||
|
// 打印调试
|
||||||
|
console.log('解析到的 product:', this.deviceInfo.product);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解析 product JSON 失败:', e, valueStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('未找到 1#productpram 物模型');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Tab切换处理
|
// Tab切换处理
|
||||||
handleTabClick(tab) {
|
handleTabClick(tab) {
|
||||||
console.log('切换到:', tab.name);
|
console.log('切换到:', tab.name);
|
||||||
|
@ -12,12 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="status-label">信号强度:</span>
|
<span class="status-label">信号强度:</span>
|
||||||
<el-rate
|
<el-rate v-model="signalStrength" disabled :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max="3" />
|
||||||
v-model="signalStrength"
|
|
||||||
disabled
|
|
||||||
:colors="['#99A9BF', '#F7BA2A', '#FF9900']"
|
|
||||||
:max="3"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -27,20 +22,10 @@
|
|||||||
<h3 class="section-title">
|
<h3 class="section-title">
|
||||||
<i class="el-icon-s-platform"></i> 产品选择
|
<i class="el-icon-s-platform"></i> 产品选择
|
||||||
</h3>
|
</h3>
|
||||||
<el-select
|
<el-select v-model="selectedProduct" placeholder="请选择产品" class="enhanced-select" size="medium" filterable
|
||||||
v-model="selectedProduct"
|
@focus="loadProductList">
|
||||||
placeholder="请选择产品"
|
<el-option v-for="product in productModels" :key="product.id" :label="`${product.name} (${product.id})`"
|
||||||
class="enhanced-select"
|
:value="product.id" />
|
||||||
size="medium"
|
|
||||||
filterable
|
|
||||||
@focus="loadProductList"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="product in productModels"
|
|
||||||
:key="product.id"
|
|
||||||
:label="`${product.name} (${product.id})`"
|
|
||||||
:value="product.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -49,13 +34,8 @@
|
|||||||
<i class="el-icon-menu"></i> 功能选择
|
<i class="el-icon-menu"></i> 功能选择
|
||||||
</h3>
|
</h3>
|
||||||
<div class="page-control-grid">
|
<div class="page-control-grid">
|
||||||
<div
|
<div v-for="(page, index) in pageList" :key="index" class="page-item"
|
||||||
v-for="(page, index) in pageList"
|
:class="{ 'active': selectedPages[index] }" @click="togglePage(index)">
|
||||||
:key="index"
|
|
||||||
class="page-item"
|
|
||||||
:class="{ 'active': selectedPages[index] }"
|
|
||||||
@click="togglePage(index)"
|
|
||||||
>
|
|
||||||
<div class="page-icon">
|
<div class="page-icon">
|
||||||
<i :class="page.icon"></i>
|
<i :class="page.icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@ -88,22 +68,11 @@
|
|||||||
|
|
||||||
<!-- 操作按钮区 -->
|
<!-- 操作按钮区 -->
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<el-button
|
<el-button type="primary" icon="el-icon-s-promotion" @click="sendControlCommand" class="action-button"
|
||||||
type="primary"
|
:loading="sending" :disabled="!selectedProduct || !deviceInfo.deviceId">
|
||||||
icon="el-icon-s-promotion"
|
|
||||||
@click="sendControlCommand"
|
|
||||||
class="action-button"
|
|
||||||
:loading="sending"
|
|
||||||
:disabled="!selectedProduct || !deviceInfo.deviceId"
|
|
||||||
>
|
|
||||||
发送指令
|
发送指令
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button type="info" icon="el-icon-refresh" @click="resetSettings" class="action-button">
|
||||||
type="info"
|
|
||||||
icon="el-icon-refresh"
|
|
||||||
@click="resetSettings"
|
|
||||||
class="action-button"
|
|
||||||
>
|
|
||||||
重置选择
|
重置选择
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -139,14 +108,14 @@ export default {
|
|||||||
signalStrength: 2,
|
signalStrength: 2,
|
||||||
productModels: [],
|
productModels: [],
|
||||||
pageList: [
|
pageList: [
|
||||||
{ name: '显示设置', icon: 'el-icon-data-line' },
|
{ name: '显示单元', icon: 'el-icon-data-line', code: 102 },
|
||||||
{ name: '声卡设置', icon: 'el-icon-headset' },
|
{ name: '声卡设置', icon: 'el-icon-headset', code: 103 },
|
||||||
{ name: '车牌识别', icon: 'el-icon-camera' },
|
// { name: '车牌识别', icon: 'el-icon-camera' },
|
||||||
{ name: '预警设置', icon: 'el-icon-warning' },
|
// { name: '预警设置', icon: 'el-icon-warning' },
|
||||||
{ name: '远程喊话', icon: 'el-icon-microphone' },
|
// { name: '远程喊话', icon: 'el-icon-microphone' },
|
||||||
{ name: '网关设置', icon: 'el-icon-connection' },
|
{ name: '网关设置', icon: 'el-icon-connection', code: 1 },
|
||||||
{ name: '路锥控制', icon: 'el-icon-place' },
|
{ name: '路锥控制', icon: 'el-icon-place', code: 104 },
|
||||||
{ name: '翻转屏', icon: 'el-icon-monitor' }
|
{ name: '激光单元', icon: 'el-icon-monitor', code: 105 }
|
||||||
],
|
],
|
||||||
loadingProducts: false,
|
loadingProducts: false,
|
||||||
deviceInfo: {
|
deviceInfo: {
|
||||||
@ -188,6 +157,15 @@ export default {
|
|||||||
if (!this.selectedProduct) return '未选择';
|
if (!this.selectedProduct) return '未选择';
|
||||||
const product = this.productModels.find(p => p.id === this.selectedProduct);
|
const product = this.productModels.find(p => p.id === this.selectedProduct);
|
||||||
return product ? `${product.name} (${product.id})` : this.selectedProduct;
|
return product ? `${product.name} (${product.id})` : this.selectedProduct;
|
||||||
|
},
|
||||||
|
productArray() {
|
||||||
|
const arr = [Number(this.selectedProduct)];
|
||||||
|
this.selectedPages.forEach((selected, idx) => {
|
||||||
|
if (selected && this.pageList[idx] && this.pageList[idx].code) {
|
||||||
|
arr.push(this.pageList[idx].code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -205,7 +183,7 @@ export default {
|
|||||||
isShadow: newVal.isShadow,
|
isShadow: newVal.isShadow,
|
||||||
protocolCode: newVal.protocolCode
|
protocolCode: newVal.protocolCode
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化distribute模型的shadow值
|
// 初始化distribute模型的shadow值
|
||||||
const distributeModel = this.deviceInfo.thingsModels.find(
|
const distributeModel = this.deviceInfo.thingsModels.find(
|
||||||
model => model.id === 'distribute'
|
model => model.id === 'distribute'
|
||||||
@ -225,7 +203,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async loadProductList() {
|
async loadProductList() {
|
||||||
if (this.productModels.length > 0 || this.loadingProducts) return;
|
if (this.productModels.length > 0 || this.loadingProducts) return;
|
||||||
|
|
||||||
this.loadingProducts = true;
|
this.loadingProducts = true;
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
@ -243,13 +221,13 @@ export default {
|
|||||||
this.loadingProducts = false;
|
this.loadingProducts = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
togglePage(index) {
|
togglePage(index) {
|
||||||
this.$set(this.selectedPages, index, this.selectedPages[index] ? 0 : 1);
|
this.$set(this.selectedPages, index, this.selectedPages[index] ? 0 : 1);
|
||||||
// 更新distribute模型的shadow值
|
// 更新distribute模型的shadow值
|
||||||
this.updateDistributeModel();
|
this.updateDistributeModel();
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDistributeModel() {
|
updateDistributeModel() {
|
||||||
const distributeModel = this.deviceInfo.thingsModels.find(
|
const distributeModel = this.deviceInfo.thingsModels.find(
|
||||||
model => model.id === 'distribute'
|
model => model.id === 'distribute'
|
||||||
@ -258,7 +236,8 @@ export default {
|
|||||||
distributeModel.shadow = this.fullCommand;
|
distributeModel.shadow = this.fullCommand;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
async sendControlCommand() {
|
async sendControlCommand() {
|
||||||
if (!this.selectedProduct) {
|
if (!this.selectedProduct) {
|
||||||
this.$message.warning('请选择产品');
|
this.$message.warning('请选择产品');
|
||||||
@ -268,80 +247,97 @@ export default {
|
|||||||
this.$message.warning('设备信息未初始化');
|
this.$message.warning('设备信息未初始化');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sending = true;
|
this.sending = true;
|
||||||
|
const payload = { product: this.productArray };
|
||||||
try {
|
try {
|
||||||
const distributeModel = this.deviceInfo.thingsModels.find(
|
// 发送102#model指令,值为1
|
||||||
model => model.id === 'productpram'
|
const model = this.deviceInfo.thingsModels.find(m => m.id === '1#productpram');
|
||||||
);
|
if (model) {
|
||||||
|
model.shadow = 'JSON=' + JSON.stringify(payload);
|
||||||
if (!distributeModel) {
|
await this.mqttPublish(this.deviceInfo, model);
|
||||||
throw new Error('未找到分发控制模型');
|
this.$message.success('已发送刷新指令,编译信息将自动更新');
|
||||||
}
|
this.sending = false;
|
||||||
|
|
||||||
// 确保shadow值是最新的
|
|
||||||
distributeModel.shadow = this.fullCommand;
|
|
||||||
|
|
||||||
await this.mqttPublish(this.deviceInfo, distributeModel);
|
|
||||||
|
|
||||||
this.$message.success('控制指令发送成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('发送控制指令失败:', error);
|
|
||||||
this.$message.error(`控制指令发送失败: ${error.message}`);
|
|
||||||
} finally {
|
|
||||||
this.sending = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async mqttPublish(device, model) {
|
|
||||||
try {
|
|
||||||
if (!device || !device.deviceId) {
|
|
||||||
throw new Error('无效的设备信息');
|
|
||||||
}
|
|
||||||
if (!model || !model.id) {
|
|
||||||
throw new Error('无效的模型信息');
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = {};
|
|
||||||
command[model.id] = model.shadow;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
deviceId: device.deviceId,
|
|
||||||
modelId: model.modelId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await getOrderControl(params);
|
|
||||||
if (response.code != 200) {
|
|
||||||
throw new Error(response.msg || '获取控制指令失败');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
serialNumber: device.serialNumber,
|
|
||||||
productId: device.productId,
|
|
||||||
remoteCommand: command,
|
|
||||||
identifier: model.id,
|
|
||||||
modelName: model.name,
|
|
||||||
isShadow: device.status != 3,
|
|
||||||
type: model.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (device.status !== 3 && device.isShadow !== 1) {
|
|
||||||
throw new Error('设备不在线且未启用影子模式');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((device.protocolCode === 'MODBUS-TCP' || device.protocolCode === 'MODBUS-RTU') && device.status === 3) {
|
|
||||||
await serviceInvokeReply(data);
|
|
||||||
} else {
|
} else {
|
||||||
await serviceInvoke(data);
|
this.$message.warning('未找到102#model物模型');
|
||||||
|
this.sending = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('MQTT发布失败:', error);
|
console.error('发送刷新指令失败:', error);
|
||||||
throw error;
|
this.sending = false;
|
||||||
|
|
||||||
|
this.$message.error('发送刷新指令失败');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//发送指令
|
||||||
|
async mqttPublish(device, model) {
|
||||||
|
const command = {};
|
||||||
|
command[model.id] = model.shadow;
|
||||||
|
const params = {
|
||||||
|
deviceId: device.deviceId,
|
||||||
|
modelId: model.modelId,
|
||||||
|
};
|
||||||
|
const response = await getOrderControl(params);
|
||||||
|
if (response.code != 200) {
|
||||||
|
this.$message({
|
||||||
|
type: 'warning',
|
||||||
|
message: response.msg,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
serialNumber: device.serialNumber,
|
||||||
|
productId: device.productId,
|
||||||
|
remoteCommand: command,
|
||||||
|
identifier: model.id,
|
||||||
|
modelName: model.name,
|
||||||
|
isShadow: device.status != 3,
|
||||||
|
type: model.type,
|
||||||
|
};
|
||||||
|
//设备在线状态判断
|
||||||
|
if (this.device.status !== 3 && this.device.isShadow !== 1) {
|
||||||
|
let title = '';
|
||||||
|
if (this.device.status === 1) {
|
||||||
|
title = this.$t('device.device-variable.930930-0');
|
||||||
|
} else if (this.device.status === 2) {
|
||||||
|
title = this.$t('device.device-variable.930930-1');
|
||||||
|
} else {
|
||||||
|
title = this.$t('device.device-variable.930930-2');
|
||||||
|
}
|
||||||
|
this.$message({
|
||||||
|
type: 'warning',
|
||||||
|
message: title,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((this.deviceInfo.protocolCode === 'MODBUS-TCP' || this.deviceInfo.protocolCode === 'MODBUS-RTU') && this.device.status === 3) {
|
||||||
|
await serviceInvokeReply(data).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.$message({
|
||||||
|
type: 'success',
|
||||||
|
message: this.$t('device.running-status.866086-25'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$message.error(response.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await serviceInvoke(data).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.$message({
|
||||||
|
type: 'success',
|
||||||
|
message: this.$t('device.running-status.866086-25'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$message.error(response.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
resetSettings() {
|
resetSettings() {
|
||||||
this.selectedPages = [0, 0, 0, 0, 0, 0, 0, 0];
|
this.selectedPages = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
this.updateDistributeModel();
|
this.updateDistributeModel();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user