feat(iot): 增加设备在线状态展示(运营端)及定时心跳功能

- 在设备详情页面添加在线状态展示
- 实现定时向设备发送心跳信号以保持连接
- 优化设备状态更新逻辑
- 新增前置校验功能
This commit is contained in:
fhysy 2025-07-01 14:32:36 +08:00
parent d25b3e1e6b
commit 55c93e3ac3
7 changed files with 221 additions and 37 deletions

View File

@ -18,3 +18,13 @@ export const eventLevel = {
warn:'警告', warn:'警告',
urgent:'紧急', urgent:'紧急',
} }
export const preCheckTypeList = [
{
value: '',
label: '无校验'
},
{
value: 'userPassword',
label: '用户密码校验'
}
]

View File

@ -5,6 +5,22 @@
<span>设备名称{{infoData.deviceName}}</span> <span>设备名称{{infoData.deviceName}}</span>
<span>设备key{{infoData.deviceKey}}</span> <span>设备key{{infoData.deviceKey}}</span>
<span>所属型号{{infoData.modelName}}</span> <span>所属型号{{infoData.modelName}}</span>
<div
:class="
infoData['deviceState'] == 'ONLINE'
? 'dev-info-state'
: 'dev-info-state off-line'
"
>在线状态
<i
v-if="infoData['deviceState'] == 'ONLINE'"
class="iconfont iconSYS_STA_1"
/>
<i v-else class="iconfont iconlixian" />
<span style="margin-left: 1px" class="dev-info-state-txt">{{
infoData["deviceState"] == "ONLINE" ? "在线" : "离线"
}}</span>
</div>
</div> </div>
<div class="link-to-list"> <div class="link-to-list">
<el-button icon="el-icon-d-arrow-left" size="small" style="margin-left: 10px;" title="返回列表" type="primary" @click="toTableClick" <el-button icon="el-icon-d-arrow-left" size="small" style="margin-left: 10px;" title="返回列表" type="primary" @click="toTableClick"
@ -34,6 +50,7 @@
:prodId="infoData.prodKey" :prodId="infoData.prodKey"
:sourceId="deviceId" :sourceId="deviceId"
:wsUrl="iotWebSocketBaseUrl" :wsUrl="iotWebSocketBaseUrl"
@updateDeviceState="changeDeviceState"
></device-run-starts-wrap> ></device-run-starts-wrap>
</div> </div>
</el-tab-pane> </el-tab-pane>
@ -108,6 +125,7 @@ import { iotWebSocketBaseUrl } from "@/config/env";
import TriggerWrap from "@/views/profile/DeviceTrigger/index"; import TriggerWrap from "@/views/profile/DeviceTrigger/index";
import EDeviceScene from '@/views/profile/EDeviceScene/indexView' import EDeviceScene from '@/views/profile/EDeviceScene/indexView'
import functionWrap from "@/views/iot/device/profile/functionWrap" import functionWrap from "@/views/iot/device/profile/functionWrap"
import { setFunctionControl } from '../../../../api/iot/device'
export default { export default {
name: "DetailsWrap", name: "DetailsWrap",
props: ["sourceId"], props: ["sourceId"],
@ -129,7 +147,8 @@ export default {
breadcrumbList: [], breadcrumbList: [],
tempType: "bs", tempType: "bs",
deviceId: "", deviceId: "",
devudeRunState: false devudeRunState: false,
timingPingWs_flag: null,
}; };
}, },
created() { created() {
@ -153,6 +172,10 @@ export default {
this.devudeRunState = false; this.devudeRunState = false;
this.deviceInfo(); this.deviceInfo();
}, },
changeDeviceState(e){
console.log("更新设备状态",e)
this.infoData.deviceState = e.state;
},
// //
deviceInfo() { deviceInfo() {
getDevice(this.deviceId).then(response => { getDevice(this.deviceId).then(response => {
@ -160,6 +183,9 @@ export default {
// response.data.deviceLabel.shift(); // response.data.deviceLabel.shift();
// } // }
this.infoData = response.data; this.infoData = response.data;
if(response.data && response.data.deviceKey && (response.data.deviceType === '10001' || response.data.deviceType === '10002' || response.data.deviceType === '10013')){
this.timingPingWs(response.data)
}
if (this.breadcrumbList.length <= 0) { if (this.breadcrumbList.length <= 0) {
this.breadcrumbList.push(this.infoData); this.breadcrumbList.push(this.infoData);
} }
@ -175,6 +201,31 @@ export default {
this.devudeRunState = true; this.devudeRunState = true;
}); });
}, },
// ping ws
timingPingWs(row) {
this.extenSubmit(row);
clearInterval(this.timingPingWs_flag);
const _this = this;
this.timingPingWs_flag = setInterval(function() {
_this.extenSubmit(row);
}, 110000);
},
//
extenSubmit(row) {
let params = {
input: {
ltime: 15, // (0.1)
dtime: 1200 //(0.1)
},
deviceKey: row.deviceKey,
funcId: "set_live_time"
};
//使
setFunctionControl(params).then(res => {
console.log(res);
});
},
toNewDevice(data) { toNewDevice(data) {
this.tempType = "children"; this.tempType = "children";
this.deviceId = data.deviceId; this.deviceId = data.deviceId;
@ -211,10 +262,33 @@ export default {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.device-info-left{ .device-info-left{
display: flex;
align-items: center;
font-size: 14px;
span{ span{
margin-right: 20px; margin-right: 20px;
font-size: 14px;
} }
.dev-info-state {
display: flex;
align-items: center;
justify-content: flex-start;
.iconfont{
color: #00c805;
margin-right: 2px;
}
.dev-info-state-txt{
color: #00c805;
}
&.off-line {
.iconfont{
color: #da2710;
}
.dev-info-state-txt{
color: #da2710;
}
}
}
} }
} }

View File

@ -360,6 +360,7 @@ import {
} from "@/config/env"; } from "@/config/env";
import SignalIntensity from "./signalIntensity"; import SignalIntensity from "./signalIntensity";
import EWebSocket from "@/components/EWebSocket/src/basic/index"; import EWebSocket from "@/components/EWebSocket/src/basic/index";
import { setFunctionControl } from '../../../api/iot/device'
export default { export default {
name: "DeviceSelectNav", name: "DeviceSelectNav",
components: { components: {
@ -458,29 +459,42 @@ export default {
},${str}`; },${str}`;
}, },
wsMessage(e) { wsMessage(e) {
if ( console.log("wsMessage", e);
e["deviceState"] || // if (
(e["switch"] !== null && e["switch"] !== undefined) // e["deviceState"] ||
) { // (e["switch"] !== null && e["switch"] !== undefined)
if(e["switch"] == "1"){ // ) {
e["switch"] = 1 // if(e["switch"] == "1"){
}else if(e["switch"] == "0"){ // e["switch"] = 1
e["switch"] = 0 // }else if(e["switch"] == "0"){
} // e["switch"] = 0
this.handleDeviceInfo(e); // }
} // this.handleDeviceInfo(e);
// }
this.handleDeviceInfo(e);
}, },
// socket // socket
handleDeviceInfo(param) { handleDeviceInfo(param) {
if (param.deviceKey === this.deviceInfo.deviceKey) { if (param.tags.device_key === this.deviceInfo.deviceKey) {
this.deviceInfo = Object.assign(this.deviceInfo, param); if(param.type === "state"){
this.$nextTick(() => {
this.$set(this.deviceInfo, 'deviceState',param.state);
});
}
// this.deviceInfo = Object.assign(this.deviceInfo, param);
} else { } else {
if (this.childDeviceList && this.childDeviceList.length > 0) { if (this.childDeviceList && this.childDeviceList.length > 0) {
this.childDeviceList = this.childDeviceList.map(v => { this.childDeviceList = this.childDeviceList.map(item => {
if (v["deviceKey"] === param["deviceKey"]) { if (item["deviceKey"] === param.tags.device_key) {
return Object.assign(v, param); if(param.type === "state"){
item.deviceState = param.state;
}
if(param.type === "extra" && param.extra.type === "switch"){
item.switch = param.extra.switch;
}
return item;
} else { } else {
return v; return item;
} }
}); });
this.$forceUpdate(); this.$forceUpdate();
@ -740,20 +754,35 @@ export default {
}, 110000); }, 110000);
}, },
extenSubmit(row) { extenSubmit(row) {
let params = { // let params = {
data: { // data: {
params: { // params: {
ltime: 15, // (0.1) // ltime: 15, // (0.1)
dtime: 1200 //(0.1) // dtime: 1200 //(0.1)
} // }
}, // },
deviceId: row.deviceId // deviceId: row.deviceId
}; // };
// //
// setLivetimeControl(params).then(res => { // setLivetimeControl(params).then(res => {
// console.log(res); // console.log(res);
// // this.msgSuccess(""); // // this.msgSuccess("");
// }); // });
let params = {
input: {
ltime: 15, // (0.1)
dtime: 1200 //(0.1)
},
deviceKey: row.deviceKey,
funcId: "set_live_time"
};
//使
setFunctionControl(params).then(res => {
console.log(res);
// this.msgSuccess("");
});
}, },
// 线 // 线
submitChildStatus(type, row) { submitChildStatus(type, row) {

View File

@ -262,6 +262,13 @@ export default {
); );
} }
} }
if(result.type === "state"){
this.$emit("updateDeviceState",{
deviceKey: this.deviceInfo.deviceKey,
state: result.state
});
}
// for (var i = 0; i < list.length; i++) { // for (var i = 0; i < list.length; i++) {
// // if (this.firstWsMassage) { // // if (this.firstWsMassage) {

View File

@ -95,10 +95,33 @@
>新增输入参数</el-button >新增输入参数</el-button
> >
</el-form-item> </el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否异步:" prop="async">
<el-switch v-model="form.async" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="前置校验:" prop="preCheck">
<el-select
v-model="form.expands.preCheck"
placeholder="请选择前置校验"
style="width: 100%"
>
<el-option
v-for="(item, index) in preCheckTypeList"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="是否异步:" prop="async"> <!-- <el-form-item label="是否异步:" prop="async">-->
<el-switch v-model="form.async" /> <!-- <el-switch v-model="form.async" />-->
</el-form-item> <!-- </el-form-item>-->
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button> <el-button type="primary" @click="submitForm"> </el-button>
@ -220,7 +243,7 @@
</div> </div>
</template> </template>
<script> <script>
import { dataTypeOption } from "@/basedata/physicalModel"; import { dataTypeOption,preCheckTypeList } from "@/basedata/physicalModel";
const defaultenumForm = { const defaultenumForm = {
value: "", value: "",
@ -271,11 +294,14 @@ export default {
data() { data() {
return { return {
dataTypeOption, dataTypeOption,
preCheckTypeList,
inputParamOpen: false, inputParamOpen: false,
form: { form: {
id: "", id: "",
name: "", name: "",
expands: {}, expands: {
preCheck: ""
},
async: false, async: false,
inputs: [], inputs: [],
output: {} output: {}
@ -322,7 +348,9 @@ export default {
this.form = { this.form = {
id: "", id: "",
name: "", name: "",
expands: {}, expands: {
preCheck: ""
},
async: true, async: true,
inputs: [], inputs: [],
output: {} output: {}
@ -341,7 +369,9 @@ export default {
this.form = { this.form = {
id: "", id: "",
name: "", name: "",
expands: {}, expands: {
preCheck: ""
},
async: true, async: true,
inputs: [], inputs: [],
output: {} output: {}

View File

@ -116,7 +116,9 @@ export default {
functionForm: { functionForm: {
id: "", id: "",
name: "", name: "",
expands: {}, expands: {
preCheck: ""
},
async: false, async: false,
inputs: [], inputs: [],
output: {} output: {}
@ -201,7 +203,9 @@ export default {
functionForm: { functionForm: {
id: "", id: "",
name: "", name: "",
expands: {}, expands: {
preCheck: ""
},
async: false, async: false,
inputs: [], inputs: [],
output: {} output: {}

View File

@ -96,6 +96,7 @@ import DeviceRunStartsWrap from "@/views/profile/DeviceRunStarts/index";
import { iotWebSocketBaseUrl } from "@/config/env"; import { iotWebSocketBaseUrl } from "@/config/env";
import TriggerWrap from "@/views/profile/DeviceTrigger/index"; import TriggerWrap from "@/views/profile/DeviceTrigger/index";
import functionWrap from "@/views/iot/device/profile/functionWrap" import functionWrap from "@/views/iot/device/profile/functionWrap"
import { setFunctionControl } from '../../../../api/iot/device'
export default { export default {
name: "DetailsWrap", name: "DetailsWrap",
props: ["sourceId"], props: ["sourceId"],
@ -116,6 +117,7 @@ export default {
tempType: "bs", tempType: "bs",
deviceId: "", deviceId: "",
devudeRunState: false, devudeRunState: false,
timingPingWs_flag: null,
iotWebSocketBaseUrl iotWebSocketBaseUrl
}; };
}, },
@ -147,6 +149,9 @@ export default {
// response.data.deviceLabel.shift(); // response.data.deviceLabel.shift();
// } // }
this.infoData = response.data; this.infoData = response.data;
if(response.data && response.data.deviceKey && (response.data.deviceType === '10001' || response.data.deviceType === '10002' || response.data.deviceType === '10013')){
this.timingPingWs(response.data)
}
if (this.breadcrumbList.length <= 0) { if (this.breadcrumbList.length <= 0) {
this.breadcrumbList.push(this.infoData); this.breadcrumbList.push(this.infoData);
} }
@ -162,6 +167,31 @@ export default {
this.devudeRunState = true; this.devudeRunState = true;
}); });
}, },
// ping ws
timingPingWs(row) {
this.extenSubmit(row);
clearInterval(this.timingPingWs_flag);
const _this = this;
this.timingPingWs_flag = setInterval(function() {
_this.extenSubmit(row);
}, 110000);
},
//
extenSubmit(row) {
let params = {
input: {
ltime: 15, // (0.1)
dtime: 1200 //(0.1)
},
deviceKey: row.deviceKey,
funcId: "set_live_time"
};
//使
setFunctionControl(params).then(res => {
console.log(res);
});
},
toNewDevice(data) { toNewDevice(data) {
this.tempType = "children"; this.tempType = "children";
this.deviceId = data.deviceId; this.deviceId = data.deviceId;