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:'警告',
urgent:'紧急',
}
export const preCheckTypeList = [
{
value: '',
label: '无校验'
},
{
value: 'userPassword',
label: '用户密码校验'
}
]

View File

@ -5,6 +5,22 @@
<span>设备名称{{infoData.deviceName}}</span>
<span>设备key{{infoData.deviceKey}}</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 class="link-to-list">
<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"
:sourceId="deviceId"
:wsUrl="iotWebSocketBaseUrl"
@updateDeviceState="changeDeviceState"
></device-run-starts-wrap>
</div>
</el-tab-pane>
@ -108,6 +125,7 @@ import { iotWebSocketBaseUrl } from "@/config/env";
import TriggerWrap from "@/views/profile/DeviceTrigger/index";
import EDeviceScene from '@/views/profile/EDeviceScene/indexView'
import functionWrap from "@/views/iot/device/profile/functionWrap"
import { setFunctionControl } from '../../../../api/iot/device'
export default {
name: "DetailsWrap",
props: ["sourceId"],
@ -129,7 +147,8 @@ export default {
breadcrumbList: [],
tempType: "bs",
deviceId: "",
devudeRunState: false
devudeRunState: false,
timingPingWs_flag: null,
};
},
created() {
@ -153,6 +172,10 @@ export default {
this.devudeRunState = false;
this.deviceInfo();
},
changeDeviceState(e){
console.log("更新设备状态",e)
this.infoData.deviceState = e.state;
},
//
deviceInfo() {
getDevice(this.deviceId).then(response => {
@ -160,6 +183,9 @@ export default {
// response.data.deviceLabel.shift();
// }
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) {
this.breadcrumbList.push(this.infoData);
}
@ -175,6 +201,31 @@ export default {
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) {
this.tempType = "children";
this.deviceId = data.deviceId;
@ -211,10 +262,33 @@ export default {
justify-content: space-between;
align-items: center;
.device-info-left{
display: flex;
align-items: center;
font-size: 14px;
span{
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";
import SignalIntensity from "./signalIntensity";
import EWebSocket from "@/components/EWebSocket/src/basic/index";
import { setFunctionControl } from '../../../api/iot/device'
export default {
name: "DeviceSelectNav",
components: {
@ -458,29 +459,42 @@ export default {
},${str}`;
},
wsMessage(e) {
if (
e["deviceState"] ||
(e["switch"] !== null && e["switch"] !== undefined)
) {
if(e["switch"] == "1"){
e["switch"] = 1
}else if(e["switch"] == "0"){
e["switch"] = 0
}
console.log("wsMessage", e);
// if (
// e["deviceState"] ||
// (e["switch"] !== null && e["switch"] !== undefined)
// ) {
// if(e["switch"] == "1"){
// e["switch"] = 1
// }else if(e["switch"] == "0"){
// e["switch"] = 0
// }
// this.handleDeviceInfo(e);
// }
this.handleDeviceInfo(e);
}
},
// socket
handleDeviceInfo(param) {
if (param.deviceKey === this.deviceInfo.deviceKey) {
this.deviceInfo = Object.assign(this.deviceInfo, param);
if (param.tags.device_key === this.deviceInfo.deviceKey) {
if(param.type === "state"){
this.$nextTick(() => {
this.$set(this.deviceInfo, 'deviceState',param.state);
});
}
// this.deviceInfo = Object.assign(this.deviceInfo, param);
} else {
if (this.childDeviceList && this.childDeviceList.length > 0) {
this.childDeviceList = this.childDeviceList.map(v => {
if (v["deviceKey"] === param["deviceKey"]) {
return Object.assign(v, param);
this.childDeviceList = this.childDeviceList.map(item => {
if (item["deviceKey"] === param.tags.device_key) {
if(param.type === "state"){
item.deviceState = param.state;
}
if(param.type === "extra" && param.extra.type === "switch"){
item.switch = param.extra.switch;
}
return item;
} else {
return v;
return item;
}
});
this.$forceUpdate();
@ -740,20 +754,35 @@ export default {
}, 110000);
},
extenSubmit(row) {
let params = {
data: {
params: {
ltime: 15, // (0.1)
dtime: 1200 //(0.1)
}
},
deviceId: row.deviceId
};
// let params = {
// data: {
// params: {
// ltime: 15, // (0.1)
// dtime: 1200 //(0.1)
// }
// },
// deviceId: row.deviceId
// };
//
// setLivetimeControl(params).then(res => {
// console.log(res);
// // 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) {

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++) {
// // if (this.firstWsMassage) {

View File

@ -95,10 +95,33 @@
>新增输入参数</el-button
>
</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-switch v-model="form.async" />-->
<!-- </el-form-item>-->
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
@ -220,7 +243,7 @@
</div>
</template>
<script>
import { dataTypeOption } from "@/basedata/physicalModel";
import { dataTypeOption,preCheckTypeList } from "@/basedata/physicalModel";
const defaultenumForm = {
value: "",
@ -271,11 +294,14 @@ export default {
data() {
return {
dataTypeOption,
preCheckTypeList,
inputParamOpen: false,
form: {
id: "",
name: "",
expands: {},
expands: {
preCheck: ""
},
async: false,
inputs: [],
output: {}
@ -322,7 +348,9 @@ export default {
this.form = {
id: "",
name: "",
expands: {},
expands: {
preCheck: ""
},
async: true,
inputs: [],
output: {}
@ -341,7 +369,9 @@ export default {
this.form = {
id: "",
name: "",
expands: {},
expands: {
preCheck: ""
},
async: true,
inputs: [],
output: {}

View File

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

View File

@ -96,6 +96,7 @@ import DeviceRunStartsWrap from "@/views/profile/DeviceRunStarts/index";
import { iotWebSocketBaseUrl } from "@/config/env";
import TriggerWrap from "@/views/profile/DeviceTrigger/index";
import functionWrap from "@/views/iot/device/profile/functionWrap"
import { setFunctionControl } from '../../../../api/iot/device'
export default {
name: "DetailsWrap",
props: ["sourceId"],
@ -116,6 +117,7 @@ export default {
tempType: "bs",
deviceId: "",
devudeRunState: false,
timingPingWs_flag: null,
iotWebSocketBaseUrl
};
},
@ -147,6 +149,9 @@ export default {
// response.data.deviceLabel.shift();
// }
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) {
this.breadcrumbList.push(this.infoData);
}
@ -162,6 +167,31 @@ export default {
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) {
this.tempType = "children";
this.deviceId = data.deviceId;