feat(iot):重构运行状态和设备功能模块适配感知平台
- 重新设计功能标签页结构,调整设备信息与功能模块的显示顺序 - 调整运行状态页面适配感知平台 - 适配新websocket格式 - 优化功能参数输入界面,支持多种表单控件类型(数字、滑块、开关、时间选择器等) - 实现功能执行前的安全预检查机制(用户密码、固定密码、短信验证) - 改进执行结果展示方式,使用pre标签格式化JSON数据显示 - 重构表单数据初始化逻辑,支持更精确的默认值设置 - 增强参数类型转换处理,确保数据格式正确性 - 优化代码结构和组件间通信方式,提升整体性能和可维护性
This commit is contained in:
parent
8d6a326d5f
commit
ef18be7b31
|
@ -54,6 +54,11 @@
|
|||
></device-run-starts-wrap>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="设备信息" name="info">
|
||||
<div class="tabs-body">
|
||||
<info-wrap :infoData="infoData" @updateInfo="updateInfo($event)" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="事件" name="eventLog">
|
||||
<div class="tabs-body">
|
||||
<event-log
|
||||
|
@ -64,9 +69,9 @@
|
|||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="设备信息" name="info">
|
||||
<el-tab-pane label="功能" name="function">
|
||||
<div class="tabs-body">
|
||||
<info-wrap :infoData="infoData" @updateInfo="updateInfo($event)" />
|
||||
<function-wrap v-if="activeName === 'function'" :deviceInfo="infoData" :sourceId="deviceId"></function-wrap>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
|
@ -105,11 +110,7 @@
|
|||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="功能" name="function">
|
||||
<div class="tabs-body">
|
||||
<function-wrap v-if="activeName === 'function'" :deviceInfo="infoData" :sourceId="deviceId"></function-wrap>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,91 +2,38 @@
|
|||
<div class="device-run-starts-wrap">
|
||||
<div class="cmd-list">
|
||||
<div class="cmd-title-wrap">
|
||||
<!-- <svg-icon-->
|
||||
<!-- icon-class="A_product1"-->
|
||||
<!-- style="margin-right: 2px; height: 20px; width: 20px"-->
|
||||
<!-- />-->
|
||||
分组名称:
|
||||
<el-select v-model="activeCmd" placeholder="请选择" @change="changeCmd">
|
||||
<el-option
|
||||
v-for="item in cmdList"
|
||||
:key="item.cmdKey"
|
||||
:label="item.cmdName"
|
||||
:value="item.cmdKey"
|
||||
>
|
||||
<el-select v-model="selectedGroup" placeholder="请选择">
|
||||
<el-option v-for="item in groupOptions" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-if="filterProdObj.properties.length <= 0" style="width:100%; padding: 100px;text-align: center">
|
||||
<div v-if="filteredProperties.length <= 0" style="width:100%; padding: 100px;text-align: center">
|
||||
<i class="el-icon-s-order" style="font-size: 50px;color: #999"></i>
|
||||
<div style="color: #909399;margin-top: 10px">暂无数据</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(item, indexs) in filterProdObj.properties"
|
||||
:key="indexs"
|
||||
class="param-item2"
|
||||
>
|
||||
<div v-for="(item, index) in filteredProperties" :key="index" class="param-item2">
|
||||
<div class="title-top">
|
||||
<span class="name-wr">{{ item.funName }}</span>
|
||||
<span class="name-wr">{{ item.name }}</span>
|
||||
<span class="type-wr" @click="handleShowData(item)">查看</span>
|
||||
</div>
|
||||
<div class="value-info">
|
||||
<div class="value-wrap">
|
||||
<span
|
||||
v-if="item.dataFormatType == 'ENUM'"
|
||||
class="val-span"
|
||||
v-text="item.dataFormatObj[item.lastValue] || item.lastValue"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-else-if="item.dataFormatType == 'TIME'"
|
||||
class="val-span"
|
||||
v-text="formatTime(item.lastValue)"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="val-span"
|
||||
v-text="
|
||||
item.lastValue === null || item.lastValue === undefined
|
||||
? '--'
|
||||
: item.lastValue
|
||||
"
|
||||
>
|
||||
<span class="val-span">
|
||||
{{ formatValue(item) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-w">
|
||||
<span class="time-warp">{{ item.unitName }}</span>
|
||||
<span
|
||||
class="time"
|
||||
v-text="item.lastTime ? parseTime(item.lastTime) : '--'"
|
||||
></span>
|
||||
<span class="time-warp">{{ item.valueParams.unit || '' }}</span>
|
||||
<span class="time">{{ item.timestamp }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="dialogShow"
|
||||
append-to-body
|
||||
class="device-run-state-dailog"
|
||||
title="查看数据"
|
||||
width="700px"
|
||||
@close="dialogCloseCell"
|
||||
@opened="dialogOpen"
|
||||
>
|
||||
<run-state-table
|
||||
ref="showChart"
|
||||
:deviceId="sourceId"
|
||||
:deviceKey="deviceInfo.deviceKey"
|
||||
:dialogData="dialogData"
|
||||
:dialogShow="dialogShow"
|
||||
:pro_type="dialogData.funDataType"
|
||||
:prodId="prodId"
|
||||
/>
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="dialogShow" append-to-body class="device-run-state-dailog"
|
||||
title="查看数据" width="700px" @close="dialogCloseCell" @opened="dialogOpen">
|
||||
<run-state-table ref="showChart" :deviceId="sourceId" :deviceKey="deviceInfo.deviceKey" :dialogData="dialogData"
|
||||
:dialogShow="dialogShow" :pro_type="dialogData.funDataType" :prodId="prodId" />
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button size="small" @click="dialogShow = false">关 闭</el-button>
|
||||
</div>
|
||||
|
@ -127,16 +74,59 @@ export default {
|
|||
properties: [],
|
||||
events: []
|
||||
},
|
||||
activeCmd: ""
|
||||
runtimeProperties: [],
|
||||
selectedGroup: "all",
|
||||
metadata:{
|
||||
properties: [],
|
||||
propertyGroups: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
groupOptions() {
|
||||
|
||||
const groups = this.metadata?.propertyGroups || [];
|
||||
return [
|
||||
{ label: '全部', value: 'all' },
|
||||
...groups.map((group) => ({ label: group.name, value: group.id })),
|
||||
];
|
||||
},
|
||||
filteredProperties() {
|
||||
let properties = [...this.runtimeProperties];
|
||||
|
||||
if (this.selectedGroup !== 'all') {
|
||||
const group = (this.metadata?.propertyGroups || []).find(
|
||||
(g) => g.id === this.selectedGroup,
|
||||
);
|
||||
if (group?.properties) {
|
||||
const ids = new Set(group.properties.map((p) => p.id));
|
||||
properties = properties.filter((p) => ids.has(p.id));
|
||||
}
|
||||
}
|
||||
|
||||
// properties = properties.filter((p) => {
|
||||
// const type = p.expands?.type || 'R';
|
||||
// return selectedTypes.value.includes(type);
|
||||
// });
|
||||
|
||||
return properties;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getMetaDate();
|
||||
this.getProdObj();
|
||||
this.getCmdList();
|
||||
// this.getCmdList();
|
||||
|
||||
// this.connection();
|
||||
},
|
||||
methods: {
|
||||
getMetaDate(){
|
||||
try {
|
||||
this.metadata = JSON.parse(this.deviceInfo.metadata);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
changeCmd(e) {
|
||||
if (e != "") {
|
||||
const filterObj =
|
||||
|
@ -150,42 +140,57 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
getProdObj() {
|
||||
let prodJson = {
|
||||
properties: [],
|
||||
events: []
|
||||
};
|
||||
try {
|
||||
prodJson = JSON.parse(this.deviceInfo.prodJson);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
formatValue(property) {
|
||||
const { value, valueParams } = property;
|
||||
if (value === undefined || value === null) return '--';
|
||||
|
||||
let displayValue = value;
|
||||
|
||||
if (valueParams?.formType === 'input' && valueParams?.length) {
|
||||
displayValue = value.slice(0, valueParams.length);
|
||||
}
|
||||
if (prodJson.properties) {
|
||||
let list = prodJson.properties.filter(item => item.show === true) || [];
|
||||
let properties = list.map(item => {
|
||||
if (item.dataFormat) {
|
||||
let dataFormat = item.dataFormat;
|
||||
item.dataFormatType = dataFormat.type;
|
||||
if (dataFormat.list && dataFormat.type === "ENUM") {
|
||||
try {
|
||||
item.dataFormatObj = JSON.parse(
|
||||
dataFormat.list.replace(/\\/g, "")
|
||||
);
|
||||
} catch (e) {
|
||||
item.dataFormatObj = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.prodObj.properties = properties;
|
||||
|
||||
if (valueParams?.formType === 'switch') {
|
||||
return value.toString() === valueParams.trueValue
|
||||
? valueParams.trueText
|
||||
: valueParams.falseText;
|
||||
}
|
||||
|
||||
if (valueParams?.formType === 'select') {
|
||||
return valueParams.enumConf.find((item) => item.value === value)?.text;
|
||||
}
|
||||
|
||||
if (valueParams?.formType === 'time' && valueParams?.dataType !== 'string') {
|
||||
return moment(value).format(valueParams.format);
|
||||
}
|
||||
|
||||
if (
|
||||
valueParams?.formType === 'number' ||
|
||||
valueParams?.formType === 'progress'
|
||||
) {
|
||||
if (valueParams?.scale) return value.toFixed(valueParams.scale);
|
||||
return value;
|
||||
}
|
||||
|
||||
return displayValue;
|
||||
},
|
||||
getProdObj() {
|
||||
if (this.metadata.properties) {
|
||||
let list = this.metadata.properties.filter((item) => item.show === true) || [];
|
||||
let properties = list.map(item => ({
|
||||
...item,
|
||||
value: null,
|
||||
timestamp: moment().format('YYYY-MM-DD HH:mm:ss')
|
||||
}));
|
||||
this.runtimeProperties = properties;
|
||||
this.$nextTick(() => {
|
||||
this.$set(this.filterProdObj, "properties", properties);
|
||||
this.$set(this, "runtimeProperties", properties);
|
||||
|
||||
});
|
||||
} else {
|
||||
this.prodObj.properties = [];
|
||||
this.runtimeProperties = [];
|
||||
}
|
||||
this.prodObj.events = prodJson.events || [];
|
||||
this.connection();
|
||||
},
|
||||
formatTime(date) {
|
||||
// 检查时间戳长度
|
||||
|
@ -242,32 +247,58 @@ export default {
|
|||
this.$forceUpdate();
|
||||
},
|
||||
recursionSet(list, result) {
|
||||
if (result.type === "properties") {
|
||||
if (result.tags.device_key === this.deviceInfo.deviceKey) {
|
||||
this.prodObj.properties = this.prodObj.properties.map(item => ({
|
||||
...item,
|
||||
...(item.funKey in result.properties && {
|
||||
lastValue: result.properties[item.funKey],
|
||||
lastTime: result.properties.timestamp
|
||||
})
|
||||
}));
|
||||
this.filterProdObj.properties = this.filterProdObj.properties.map(
|
||||
item => ({
|
||||
...item,
|
||||
...(item.funKey in result.properties && {
|
||||
lastValue: result.properties[item.funKey],
|
||||
lastTime: result.properties.timestamp
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
if(result.type === "state"){
|
||||
this.$emit("updateDeviceState",{
|
||||
deviceKey: this.deviceInfo.deviceKey,
|
||||
state: result.state
|
||||
if (result.type === "result" && result.topic.endsWith('/report') && result.payload) {
|
||||
// 更函数式的写法,避免直接修改
|
||||
this.runtimeProperties = this.runtimeProperties.map(property => {
|
||||
const propValue = result.payload.value[property.id];
|
||||
if (propValue !== undefined) {
|
||||
return {
|
||||
...property,
|
||||
value: propValue,
|
||||
timestamp: moment(result.payload.timeString).format('YYYY-MM-DD HH:mm:ss')
|
||||
};
|
||||
}
|
||||
return property;
|
||||
});
|
||||
// 更新实时属性数据
|
||||
// Object.entries(result.payload).forEach(([propId, propValue]) => {
|
||||
// const property = this.runtimeProperties.find(
|
||||
// (p) => p.id === propId,
|
||||
// );
|
||||
// if (property) {
|
||||
// property.value = propValue;
|
||||
// property.timestamp = moment(data.payload.timeString).format(
|
||||
// 'YYYY-MM-DD HH:mm:ss',
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
}
|
||||
// if (result.type === "properties") {
|
||||
// if (result.tags.device_key === this.deviceInfo.deviceKey) {
|
||||
// this.prodObj.properties = this.prodObj.properties.map(item => ({
|
||||
// ...item,
|
||||
// ...(item.funKey in result.properties && {
|
||||
// lastValue: result.properties[item.funKey],
|
||||
// lastTime: result.properties.timestamp
|
||||
// })
|
||||
// }));
|
||||
// this.filterProdObj.properties = this.filterProdObj.properties.map(
|
||||
// item => ({
|
||||
// ...item,
|
||||
// ...(item.funKey in result.properties && {
|
||||
// lastValue: result.properties[item.funKey],
|
||||
// lastTime: result.properties.timestamp
|
||||
// })
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// if (result.type === "state") {
|
||||
// this.$emit("updateDeviceState", {
|
||||
// deviceKey: this.deviceInfo.deviceKey,
|
||||
// state: result.state
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
// for (var i = 0; i < list.length; i++) {
|
||||
|
@ -309,7 +340,7 @@ export default {
|
|||
if (this.socket_flag) {
|
||||
this.socket_flag = false;
|
||||
let self = this;
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
self.socket_flag = true;
|
||||
self.connection();
|
||||
}, 10000);
|
||||
|
@ -375,7 +406,7 @@ export default {
|
|||
];
|
||||
}
|
||||
this.connection();
|
||||
}).catch(error=>{
|
||||
}).catch(error => {
|
||||
this.connection();
|
||||
});
|
||||
},
|
||||
|
@ -415,7 +446,7 @@ export default {
|
|||
// }
|
||||
// },
|
||||
realTimeData: {
|
||||
handler: function() {
|
||||
handler: function () {
|
||||
this.setListData(this.realTimeData);
|
||||
},
|
||||
deep: true
|
||||
|
@ -429,11 +460,13 @@ export default {
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
|
||||
//padding: 10px;
|
||||
.el-button--medium {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.cmd-list {
|
||||
width: 100%;
|
||||
/* height: auto; */
|
||||
|
@ -441,9 +474,11 @@ export default {
|
|||
flex-wrap: wrap;
|
||||
cursor: default;
|
||||
padding: 10px;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.cmd-title-wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -451,6 +486,7 @@ export default {
|
|||
font-size: 16px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.cmd-title {
|
||||
font-size: 14px;
|
||||
color: #a9a6a6;
|
||||
|
@ -459,6 +495,7 @@ export default {
|
|||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.param-item {
|
||||
height: 130px;
|
||||
// border: 1px solid #777474;
|
||||
|
@ -475,6 +512,7 @@ export default {
|
|||
margin-left: 15px;
|
||||
margin-top: 15px;
|
||||
box-shadow: 0px 0px 3px 0px #b7b4b4;
|
||||
|
||||
.title-top {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
|
@ -483,23 +521,27 @@ export default {
|
|||
border-bottom: 1px dotted #c5c3c3;
|
||||
padding-bottom: 3px;
|
||||
justify-content: space-between;
|
||||
|
||||
.name-wr {
|
||||
font-size: 18px;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.type-wr {
|
||||
font-size: 14px;
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
.value-info {
|
||||
height: 55px;
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.value-wrap {
|
||||
}
|
||||
|
||||
.value-wrap {}
|
||||
|
||||
.val-span {
|
||||
color: #03a9f4;
|
||||
font-size: 20px;
|
||||
|
@ -520,6 +562,7 @@ export default {
|
|||
color: #908c8c;
|
||||
}
|
||||
}
|
||||
|
||||
.param-item2 {
|
||||
height: 150px;
|
||||
width: 300px;
|
||||
|
@ -534,6 +577,7 @@ export default {
|
|||
margin-left: -1px;
|
||||
margin-top: -1px;
|
||||
border-color: #e0e0e0;
|
||||
|
||||
.title-top {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
|
@ -542,28 +586,33 @@ export default {
|
|||
border-bottom: 1px dotted #c5c3c3;
|
||||
padding-bottom: 3px;
|
||||
justify-content: space-between;
|
||||
|
||||
.name-wr {
|
||||
font-size: 18px;
|
||||
// color: #1890ff;
|
||||
}
|
||||
|
||||
.type-wr {
|
||||
font-size: 14px;
|
||||
// color: #606266;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.type-wr:hover {
|
||||
color: #1890ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.value-info {
|
||||
height: 55px;
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.value-wrap {
|
||||
}
|
||||
|
||||
.value-wrap {}
|
||||
|
||||
.val-span {
|
||||
color: #03a9f4;
|
||||
font-size: 20px;
|
||||
|
@ -586,10 +635,12 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.device-run-state-dailog {
|
||||
.el-dialog__header {
|
||||
border-bottom: 1px solid #b6b6b6;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
border-top: 1px solid #b6b6b6;
|
||||
padding-bottom: 10px;
|
||||
|
|
Loading…
Reference in New Issue