Xazn-app/pagesA/home/device/status/gatewayrunning-status.vue
2025-06-06 06:23:49 +08:00

1242 lines
67 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="running-status">
<el-row :gutter="90">
<el-col :xs="24" :sm="24" :md="24" :lg="14" :xl="10" class="status-col">
<el-descriptions :column="1" border>
<!-- 设备模式-->
<el-descriptions-item :labelStyle="statusColor">
<template slot="label">
<i class="el-icon-menu"></i>
{{ $t('device.running-status.866086-0') }}
</template>
<span class="title">{{ title }}</span>
</el-descriptions-item>
<!-- 设备升级-->
<el-descriptions-item :labelStyle="statusColor">
<template slot="label">
<svg-icon icon-class="ota" />
{{ $t('device.running-status.866086-1') }}
</template>
<el-button type="primary" size="mini" :plain="true" @click="viewVersion()">{{
$t('device.running-status.866086-44')
}}</el-button>
<el-button type="primary" size="mini" :plain="true" @click="sendDataToIframe()">{{
$t('device.running-status.866086-44')
}}</el-button>
<el-button type="primary" size="mini" :plain="true" @click="sendProductParamOnce()">{{
$t('device.running-status.866086-44')
}}</el-button>
</el-descriptions-item>
<!-- 设备物模型-->
<el-descriptions-item v-for="(item, index) in deviceInfo.thingsModels" :key="index"
:labelStyle="statusColor">
<template slot="label">
<i class="el-icon-open"></i>
{{ item.name }}
</template>
<div v-if="item.datatype.type == 'bool'">
<el-switch v-model="item.shadow" @change="mqttPublish(deviceInfo, item)" active-text=""
inactive-text="" active-value="1" inactive-value="0" style="min-width: 100px"
:disabled="shadowUnEnable || item.isReadonly == 1" />
</div>
<div v-if="item.datatype.type == 'enum'" class="emum-wrap">
<div v-if="item.datatype.showWay && item.datatype.showWay == 'button'">
<el-button class="btn" size="mini"
@click="enumButtonClick(deviceInfo, item, subItem.value)"
v-for="subItem in item.datatype.enumList" :key="subItem.value"
:disabled="shadowUnEnable || item.isReadonly == 1"
:class="{ 'is-active-btn': subItem.value === item.shadow }">
{{ subItem.text }}
</el-button>
</div>
<el-select v-else v-model="item.shadow" :placeholder="$t('device.running-status.866086-3')"
@change="mqttPublish(deviceInfo, item)"
:disabled="shadowUnEnable || item.isReadonly == 1">
<el-option v-for="subItem in item.datatype.enumList" :key="subItem.value"
:label="subItem.text" :value="subItem.value" />
</el-select>
</div>
<div v-if="item.datatype.type == 'string'">
<el-input v-model="item.shadow"
:placeholder="item.datatype.unit ? $t('device.running-status.866086-5', [item.datatype.unit]) : $t('device.running-status.866086-4')"
:disabled="shadowUnEnable || item.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, item)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && item.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="item.datatype.type == 'decimal'">
<div style="width: 80%; float: left">
<el-slider v-model="item.shadow" :min="item.datatype.min" :max="item.datatype.max"
:step="item.datatype.step" :format-tooltip="(x) => x + ' ' + item.datatype.unit"
:disabled="shadowUnEnable || item.isReadonly == 1"></el-slider>
</div>
<div style="width: 20%; float: left">
<el-button icon="el-icon-s-promotion" type="info" @click="mqttPublish(deviceInfo, item)"
style="font-size: 16px; padding: 1px 8px; margin: 2px 0 0 5px; border-radius: 3px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && item.isReadonly == 0"></el-button>
</div>
</div>
<div v-if="item.datatype.type == 'integer'">
<div style="width: 80%; float: left">
<el-slider style="margin-left: 10px" v-model="item.shadow" :min="item.datatype.min"
:max="item.datatype.max" :step="item.datatype.step"
:format-tooltip="(x) => x + ' ' + item.datatype.unit"
:disabled="shadowUnEnable || item.isReadonly == 1"></el-slider>
</div>
<div style="width: 20%; float: left">
<el-button icon="el-icon-s-promotion" type="info" @click="mqttPublish(deviceInfo, item)"
style="font-size: 16px; padding: 1px 8px; margin: 4px 0 0 10px; border-radius: 3px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && item.isReadonly == 0"></el-button>
</div>
</div>
<div v-if="item.datatype.type == 'object'">
<el-descriptions :column="1" size="mini" border>
<el-descriptions-item v-for="(param, index) in item.datatype.params" :key="index"
:label="param.name">
<div v-if="param.datatype.type == 'bool'">
<el-switch v-model="param.shadow" @change="mqttPublish(deviceInfo, param)"
active-text="" inactive-text="" active-value="1" inactive-value="0"
style="min-width: 100px"
:disabled="shadowUnEnable || param.isReadonly == 1" />
</div>
<div v-if="param.datatype.type == 'enum'">
<div v-if="param.datatype.showWay && param.datatype.showWay == 'button'">
<el-button style="margin: 5px" size="mini"
@click="enumButtonClick(deviceInfo, param, subItem.value)"
v-for="subItem in param.datatype.enumList" :key="subItem.value"
:disabled="shadowUnEnable || param.isReadonly == 1"
:class="{ 'is-active-btn': subItem.value === param.shadow }">
{{ subItem.text }}
</el-button>
</div>
<el-select size="small" v-else v-model="param.shadow"
:placeholder="$t('device.running-status.866086-3')"
@change="mqttPublish(deviceInfo, param)"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-option v-for="subItem in param.datatype.enumList" :key="subItem.value"
:label="subItem.text" :value="subItem.value" />
</el-select>
</div>
<div v-if="param.datatype.type == 'string'">
<el-input v-model="param.shadow"
:placeholder="$t('device.running-status.866086-4')"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, param)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && param.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="param.datatype.type == 'decimal'">
<el-input v-model="param.shadow" type="number"
:placeholder="$t('device.running-status.866086-7')"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, param)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && param.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="param.datatype.type == 'integer'">
<el-input v-model="param.shadow" type="integer"
:placeholder="$t('device.running-status.866086-8')"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, param)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && param.isReadonly == 0"></el-button>
</el-input>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
<div v-if="item.datatype.type == 'array'">
<el-descriptions :column="1" size="mini" border v-if="item.datatype.arrayType != 'object'">
<el-descriptions-item v-for="(model, index) in item.datatype.arrayModel" :key="index"
:label="item.name + (index + 1)">
<div v-if="item.datatype.arrayType == 'string'">
<el-input :placeholder="$t('device.running-status.866086-4')" size="mini"
v-model="model.shadow" :disabled="shadowUnEnable || item.isReadonly == 1"
@input="arrayItemChange($event, item)">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, model)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable || item.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="item.datatype.arrayType == 'decimal'">
<el-input type="number" :placeholder="$t('device.running-status.866086-7')"
size="mini" v-model="model.shadow"
:disabled="shadowUnEnable || item.isReadonly == 1"
@input="arrayItemChange($event, item)">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, model)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable || item.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="item.datatype.arrayType == 'integer'">
<el-input type="integer" :placeholder="$t('device.running-status.866086-8')"
size="mini" v-model="model.shadow"
:disabled="shadowUnEnable || item.isReadonly == 1"
@input="arrayItemChange($event, item)">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, model)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable || item.isReadonly == 0"></el-button>
</el-input>
</div>
</el-descriptions-item>
</el-descriptions>
<el-collapse v-if="item.datatype.arrayType == 'object'">
<el-collapse-item v-for="(arrayParam, index) in item.datatype.arrayParams" :key="index">
<template slot="title">
<span style="color: #666">
<i class="el-icon-tickets"></i>
{{ item.name + (index + 1) }}
</span>
</template>
<el-descriptions :column="1" size="mini" border>
<el-descriptions-item v-for="(param, index) in arrayParam" :key="index"
:label="param.name">
<div v-if="param.datatype.type == 'bool'">
<el-switch v-model="param.shadow"
@change="mqttPublish(deviceInfo, param)" active-text=""
inactive-text="" active-value="1" inactive-value="0"
style="min-width: 100px"
:disabled="shadowUnEnable || param.isReadonly == 1" />
</div>
<div v-if="param.datatype.type == 'enum'">
<div
v-if="param.datatype.showWay && param.datatype.showWay == 'button'">
<el-button style="margin: 5px" size="mini"
@click="enumButtonClick(deviceInfo, param, subItem.value)"
v-for="subItem in param.datatype.enumList" :key="subItem.value"
:disabled="shadowUnEnable || param.isReadonly == 1"
:class="{ 'is-active-btn': subItem.value === param.shadow }">
{{ subItem.text }}
</el-button>
</div>
<el-select v-else v-model="param.shadow"
:placeholder="$t('device.running-status.866086-3')" size="small"
@change="mqttPublish(deviceInfo, param)"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-option v-for="subItem in param.datatype.enumList"
:key="subItem.value" :label="subItem.text"
:value="subItem.value" />
</el-select>
</div>
<div v-if="param.datatype.type == 'string'">
<el-input v-model="param.shadow"
:placeholder="$t('device.running-status.866086-4')"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, param)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && param.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="param.datatype.type == 'decimal'">
<el-input v-model="param.shadow" type="number"
:placeholder="$t('device.running-status.866086-7')"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, param)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && param.isReadonly == 0"></el-button>
</el-input>
</div>
<div v-if="param.datatype.type == 'integer'">
<el-input v-model="param.shadow" type="integer"
:placeholder="$t('device.running-status.866086-8')"
:disabled="shadowUnEnable || param.isReadonly == 1">
<el-button slot="append" icon="el-icon-s-promotion"
@click="mqttPublish(deviceInfo, param)" style="font-size: 20px"
:title="$t('device.running-status.866086-6')"
v-if="!shadowUnEnable && param.isReadonly == 0"></el-button>
</el-input>
</div>
</el-descriptions-item>
</el-descriptions>
</el-collapse-item>
</el-collapse>
</div>
</el-descriptions-item>
</el-descriptions>
<!---设备状态(影子模式value值不会更新)-->
<el-descriptions style="margin-top: 30px" :column="1" border
v-if="deviceInfo.isShadow == 1 && deviceInfo.status != 3">
<template slot="title" v-if="deviceInfo.thingsModels.length > 0">
<span style="font-size: 14px; color: #606266">{{ $t('device.running-status.866086-9') }}</span>
</template>
<!-- 设备物模型-->
<el-descriptions-item v-for="(item, index) in deviceInfo.thingsModels" :key="index"
:labelStyle="{ minWidth: '100px' }">
<template slot="label">
<i class="el-icon-open"></i>
{{ item.name }}
</template>
<div v-if="item.datatype.type == 'bool'">
<el-switch v-model="item.value" @change="mqttPublish(deviceInfo, item)" active-text=""
inactive-text="" active-value="1" inactive-value="0" style="min-width: 100px"
disabled />
</div>
<div v-if="item.datatype.type == 'enum'">
<div v-if="item.datatype.showWay && item.datatype.showWay == 'button'">
<el-button style="margin: 5px" size="mini" disabled
v-for="subItem in item.datatype.enumList" :key="subItem.value">{{ subItem.text
}}</el-button>
</div>
<el-select v-else v-model="item.value" :placeholder="$t('device.running-status.866086-3')"
@change="mqttPublish(deviceInfo, item)" disabled>
<el-option v-for="subItem in item.datatype.enumList" :key="subItem.value"
:label="subItem.text" :value="subItem.value" />
</el-select>
</div>
<div v-if="item.datatype.type == 'string'">
<el-input v-model="item.value" :placeholder="$t('device.running-status.866086-4')"
disabled></el-input>
</div>
<div v-if="item.datatype.type == 'decimal'">
<el-input v-model="item.value" type="number"
:placeholder="$t('device.running-status.866086-7')" disabled></el-input>
</div>
<div v-if="item.datatype.type == 'integer'">
<el-input v-model="item.value" type="integer"
:placeholder="$t('device.running-status.866086-8')" disabled></el-input>
</div>
<div v-if="item.datatype.type == 'object'">
<el-descriptions :column="1" size="mini" border>
<el-descriptions-item v-for="(param, index) in item.datatype.params" :key="index"
:label="param.name">
<div v-if="param.datatype.type == 'bool'">
<el-switch v-model="param.value" size="mini"
@change="mqttPublish(deviceInfo, param)" active-text="" inactive-text=""
active-value="1" inactive-value="0" style="min-width: 100px" disabled />
</div>
<div v-if="param.datatype.type == 'enum'">
<el-select v-model="param.value"
:placeholder="$t('device.running-status.866086-3')"
@change="mqttPublish(deviceInfo, param)" disabled size="mini">
<el-option v-for="subItem in param.datatype.enumList" :key="subItem.value"
:label="subItem.text" :value="subItem.value" />
</el-select>
</div>
<div v-if="param.datatype.type == 'string'">
<el-input v-model="param.value"
:placeholder="$t('device.running-status.866086-4')" disabled
size="mini"></el-input>
</div>
<div v-if="param.datatype.type == 'decimal'">
<el-input v-model="param.value" type="number"
:placeholder="$t('device.running-status.866086-7')" disabled
size="mini"></el-input>
</div>
<div v-if="param.datatype.type == 'integer'">
<el-input v-model="param.value" type="integer"
:placeholder="$t('device.running-status.866086-8')" disabled
size="mini"></el-input>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
<div v-if="item.datatype.type == 'array'">
<el-descriptions :column="1" size="mini" border v-if="item.datatype.arrayType != 'object'">
<el-descriptions-item v-for="(model, index) in item.datatype.arrayModel" :key="index"
:label="item.name + (index + 1)">
<div v-if="item.datatype.arrayType == 'string'">
<el-input v-model="model.value"
:placeholder="$t('device.running-status.866086-4')" size="mini"
disabled></el-input>
</div>
<div v-if="item.datatype.arrayType == 'decimal'">
<el-input v-model="model.value" type="number"
:placeholder="$t('device.running-status.866086-7')" size="mini"
disabled></el-input>
</div>
<div v-if="item.datatype.arrayType == 'integer'">
<el-input v-model="model.value" type="integer"
:placeholder="$t('device.running-status.866086-8')" size="mini"
disabled></el-input>
</div>
</el-descriptions-item>
</el-descriptions>
<el-collapse v-if="item.datatype.arrayType == 'object'">
<el-collapse-item v-for="(arrayParam, index) in item.datatype.arrayParams" :key="index">
<template slot="title">
<span style="color: #666">
<i class="el-icon-tickets"></i>
{{ item.name + (index + 1) }}
</span>
</template>
<el-descriptions :column="1" size="mini" border>
<el-descriptions-item v-for="(param, index) in arrayParam" :key="index"
:label="param.name">
<div v-if="param.datatype.type == 'bool'">
<el-switch v-model="param.value"
@change="mqttPublish(deviceInfo, param)" active-text=""
inactive-text="" active-value="1" inactive-value="0"
style="min-width: 100px" disabled />
</div>
<div v-if="param.datatype.type == 'enum'">
<el-select v-model="param.value"
:placeholder="$t('device.running-status.866086-3')"
@change="mqttPublish(deviceInfo, param)" disabled size="mini">
<el-option v-for="subItem in param.datatype.enumList"
:key="subItem.value" :label="subItem.text"
:value="subItem.value" />
</el-select>
</div>
<div v-if="param.datatype.type == 'string'">
<el-input v-model="param.value"
:placeholder="$t('device.running-status.866086-4')" disabled
size="mini"></el-input>
</div>
<div v-if="param.datatype.type == 'decimal'">
<el-input v-model="param.value" type="number"
:placeholder="$t('device.running-status.866086-7')" disabled
size="mini"></el-input>
</div>
<div v-if="param.datatype.type == 'integer'">
<el-input v-model="param.value" type="integer"
:placeholder="$t('device.running-status.866086-8')" disabled
size="mini"></el-input>
</div>
</el-descriptions-item>
</el-descriptions>
</el-collapse-item>
</el-collapse>
</div>
</el-descriptions-item>
</el-descriptions>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="10" :xl="14">
<!-- 设备监测图表-->
<el-row :gutter="20" v-if="deviceInfo.chartList.length > 0">
<el-col :xs="24" :sm="12" :md="12" :lg="24" :xl="8" v-for="(item, index) in deviceInfo.chartList"
:key="index">
<el-card shadow="hover" style="border-radius: 8px; margin-bottom: 20px">
<div ref="map" style="height: 230px; width: 185px; margin: 0 auto; margin-bottom: 15px">
</div>
</el-card>
</el-col>
</el-row>
</el-col>
<iframe ref="childFrame" src="https://iot-xcwl.cn/h5/gateway/index.html#/" width="23%" height="800px"
style="border: none;"></iframe>
<!-- <iframe ref="childFrame" src="http://localhost:81/" width="23%" height="800px"
style="border: none;"></iframe> -->
</el-row>
<!-- 固件版本查看对话框 -->
<el-dialog :title="$t('device.running-status.866086-10')" :visible.sync="openVersion" width="550px"
append-to-body>
<el-form ref="firmwareForm" label-width="100px" :model="firmwareParams" :inline="true" :rules="rules">
<el-form-item :label="$t('device.running-status.866086-38')" prop="firmwareType">
<el-select v-model="deviceInfo.firmwareType" :placeholder="$t('firmware.index.222541-51')"
@change="handleVersionInputChange" style="width: 350px" disabled>
<el-option v-for="item in firmwareTypeList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('device.running-status.866086-39')" prop="">
<el-input :placeholder="$t('device.running-status.866086-40')" v-model="deviceInfo.firmwareVersion"
style="width: 350px" disabled>
<template slot="prepend">Version</template>
</el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-tooltip effect="dark" :content="$t('device.running-status.866086-41')" placement="top-start">
<el-button type="primary" @click="getLatestFirmware" :disabled="device.status !== 3">{{
$t('device.running-status.866086-42') }}</el-button>
</el-tooltip>
<el-button @click="cancel1">{{ $t('cancel') }}</el-button>
</div>
</el-dialog>
<!-- 添加或修改产品固件对话框 -->
<el-dialog :title="$t('device.running-status.866086-10')" :visible.sync="openFirmware" width="600px"
append-to-body>
<div v-if="firmware == null" style="text-align: center; font-size: 16px">
<i class="el-icon-success" style="color: #67c23a"></i>
{{ $t('device.running-status.866086-11') }}
</div>
<el-descriptions :column="1" border size="large"
v-if="firmware != null && deviceInfo.firmwareVersion < firmware.version"
:labelStyle="{ width: '150px', 'font-weight': 'bold' }">
<template slot="title">
<el-link icon="el-icon-success" type="success" :underline="false">{{
$t('device.running-status.866086-12') }}</el-link>
</template>
<el-descriptions-item :label="$t('device.running-status.866086-13')">{{ firmware.firmwareName
}}</el-descriptions-item>
<el-descriptions-item :label="$t('device.device-edit.148398-4')">{{ firmware.productName
}}</el-descriptions-item>
<el-descriptions-item :label="$t('device.device-edit.148398-12')">Version {{ firmware.version
}}</el-descriptions-item>
<el-descriptions-item :label="$t('device.running-status.866086-16')">
<el-link :href="getDownloadUrl(firmware.filePath)" :underline="false" type="primary">{{
getDownloadUrl(firmware.filePath) }}</el-link>
</el-descriptions-item>
<el-descriptions-item :label="$t('device.running-status.866086-17')">{{ firmware.remark
}}</el-descriptions-item>
</el-descriptions>
<div slot="footer" class="dialog-footer">
<el-button type="success" @click="otaUpgrade"
v-if="firmware != null && deviceInfo.firmwareVersion < firmware.version">{{
$t('device.running-status.866086-18') }}</el-button>
<el-button @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getLatestFirmware } from '@/api/iot/firmware';
import { serviceInvoke, serviceInvokeReply } from '@/api/iot/runstatus';
import { getOrderControl } from '@/api/iot/control';
export default {
name: 'running-status',
props: {
device: {
type: Object,
default: null,
},
},
watch: {
// 获取到父组件传递的device后刷新列表
device: {
handler(newVal) {
if (newVal && newVal.deviceId != 0) {
if (newVal && newVal.deviceId != 0) {
this.deviceInfo = newVal;
this.updateDeviceStatus(this.deviceInfo);
this.$nextTick(function () {
this.MonitorChart();
});
//物模型排序
if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) {
this.deviceInfo.thingsModels = this.device.thingsModels.sort((a, b) => b.order - a.order);
}
if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) {
this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order);
}
// 添加message事件监听器
// this.mqttCallback();
}
}
},
},
},
data() {
return {
// 控制模块标题
title: '设备控制 ',
// 未启用设备影子
shadowUnEnable: false,
// 控制项标题背景
statusColor: {
background: '#67C23A',
color: '#fff',
minWidth: '100px',
},
// 最新固件信息
firmware: {},
// 打开固件对话框
openFirmware: false,
// 遮罩层
loading: true,
// 设备信息
deviceInfo: {
boolList: [],
enumList: [],
stringList: [],
integerList: [],
decimalList: [],
arrayList: [],
thingsModels: [],
chartList: [],
},
firmwareParams: {
firmwareType: '',
versionInput: '',
},
// 监测图表
monitorChart: [
{
chart: {},
data: {
id: '',
name: '',
value: '',
},
},
],
openVersion: false,
firmwareTypeList: [
{
label: this.$t('firmware.index.222541-52'),
value: 1,
},
{
label: 'HTTP',
value: 2,
},
],
remoteCommand: {},
// 表单校验
rules: {
firmwareType: [
{
required: true,
message: this.$t('device.running-status.866086-43'),
trigger: 'blur',
},
],
},
};
},
mounted() {
this.handleDeviceChange(this.device);
const { deviceId, serialNumber } = this.device;
if (deviceId) {
this.initDataStatus();
this.initData();
}
// this.mqttCallback();
// 只在mounted时发送一次productpram数据
this.$nextTick(() => {
this.waitForIframeLoad();
});
// 添加消息监听器
window.addEventListener('message', (event) => {
// 确保消息来源是可信的
// 找到 id 为 distribute 的模型
const distributeModel = this.deviceInfo.thingsModels.find(model => model.id === 'distribute');
if (distributeModel) {
// 更新模型的 shadow 值,直接使用内部数据
distributeModel.shadow = JSON.stringify(event.data.data);
console.log('收到来自子页面的消息:', JSON.stringify(event.data.data));
// 调用 mqttPublish 下发数据
this.mqttPublish(this.deviceInfo, distributeModel);
}
});
},
methods: {
// 新增方法等待iframe加载完成
waitForIframeLoad() {
const maxAttempts = 5; // 最大尝试次数
let attempts = 0;
const checkInterval = setInterval(() => {
const iframe = this.$refs.childFrame;
// 1. 检查iframe是否存在且已加载
if (iframe && iframe.contentWindow) {
// 2. 可选:检查子页面是否已准备好接收消息(需要子页面配合)
// iframe.contentWindow.postMessage({ type: 'ping' }, '*');
clearInterval(checkInterval);
this.sendProductParamOnce();
return;
}
// 3. 超过最大尝试次数则停止
if (++attempts >= maxAttempts) {
clearInterval(checkInterval);
console.warn('Iframe not ready after multiple attempts');
return;
}
}, 500); // 每500ms检查一次
},
// 发送productpram数据保持不变
sendProductParamOnce() {
const iframe = this.$refs.childFrame;
if (!iframe || !iframe.contentWindow) {
console.error('Iframe not ready');
return;
}
const productParamModel = this.deviceInfo.thingsModels.find(model => model.id === 'productpram');
if (!productParamModel) {
console.warn('productpram model not found');
return;
}
let paramData = productParamModel.shadow;
if (typeof paramData === 'string' && paramData.startsWith('JSON=')) {
paramData = paramData.substring(5);
}
const paramDataString = typeof paramData === 'string' ? paramData : JSON.stringify(paramData);
iframe.contentWindow.postMessage(
paramDataString,
'https://iot-xcwl.cn/h5/gateway/index.html#/'
);
console.log('Initial productpram data sent:', paramDataString);
},
//监听设备状态(设备联动)
handleDeviceChange(device) {
if (device && device.deviceId != 0 && this.device.thingsModels) {
const { firmwareVersion, wirelessVersion, firmwareType, ...res } = device;
const data = {
version: firmwareType === 1 ? firmwareVersion : wirelessVersion,
firmwareType,
...res,
};
this.deviceInfo = data;
this.updateDeviceStatus(this.deviceInfo);
this.$nextTick(() => {
this.MonitorChart();
// 向iframe发送数据
// this.sendDataToIframe();
});
//物模型排序
this.deviceInfo.thingsModels = this.deviceInfo.thingsModels.sort((a, b) => b.order - a.order);
this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order);
}
},
initData() {
// 监听值的实时更新
this.$busEvent.$on('updateData', (params) => {
this.updateParam(params);
});
},
initDataStatus() {
// 监听值的实时更新
this.$busEvent.$on('updateStatus', (status) => {
this.updateStatus(status);
});
},
updateStatus(status) {
let { serialNumber, productId, data } = status;
if (data) {
if (this.deviceInfo.serialNumber == serialNumber) {
this.deviceInfo.status = data.status;
this.deviceInfo.isShadow = data.isShadow;
this.deviceInfo.rssi = data.rssi;
this.updateDeviceStatus(this.deviceInfo);
}
}
},
//更新参数值
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;
}
}
}
}
// 在数据更新后发送到iframe
this.$nextTick(() => {
this.sendDataToIframe();
});
},
//发送指令
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,
};
// console.log('下发指令:', JSON.stringify(data));
//设备在线状态判断
if (this.device.status !== 3 && this.device.isShadow !== 1) {
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);
}
});
}
},
/**
* Mqtt发布消息
* @device 设备
* @model 物模型(id/name/type/name/isReadonly/value/shadow)type 类型(1=属性2=功能3=OTA升级4=实时监测)
* */
// mqttPublish(device, model) {
// let topic = "";
// let message = ""
// if (model.type == 1) {
// if (device.status == 3) {
// // 属性,在线模式
// topic = "/" + device.productId + "/" + device.serialNumber + "/property-online/get";
// } else if (device.isShadow) {
// // 属性,离线模式
// topic = "/" + device.productId + "/" + device.serialNumber + "/property-offline/post";
// }
// message = '[{"id":"' + model.id + '","value":"' + model.shadow + '"}]';
// } else if (model.type == 2) {
// if (device.status == 3) {
// // 功能,在线模式
// topic = "/" + device.productId + "/" + device.serialNumber + "/function-online/get";
//
// } else if (device.isShadow) {
// // 功能,离线模式
// topic = "/" + device.productId + "/" + device.serialNumber + "/function-offline/post";
// }
// message = '[{"id":"' + model.id + '","value":"' + model.shadow + '"}]';
// } else if (model.type == 3) {
// // OTA升级
// topic = "/" + device.productId + "/" + device.serialNumber + "/ota/get";
// message = '{"version":' + this.firmware.version + ',"downloadUrl":"' + this.getDownloadUrl(this.firmware.filePath) + '"}';
// } else {
// return;
// }
// if (topic != "") {
// // 发布
// this.$mqttTool.publish(topic, message, model.name).then(res => {
// this.$modal.notifySuccess(res);
// }).catch(res => {
// this.$modal.notifyError(res);
// });
// }
// },
/** 枚举类型按钮单击 */
enumButtonClick(device, model, value) {
model.shadow = value;
this.mqttPublish(device, model);
},
viewVersion() {
this.openVersion = true;
this.firmwareParams.firmwareType = 1;
this.firmwareParams.versionInput = '';
this.handleVersionInputChange();
},
//选择改变
handleVersionInputChange() {
if (this.firmwareParams.firmwareType == 1) {
this.firmwareParams.versionInput = 'Version' + this.device.firmwareVersion;
} else {
this.firmwareParams.versionInput = 'Version' + this.device.wirelessVersion;
}
},
// 取消按钮
cancel1() {
this.openVersion = false;
},
/** 更新设备状态 */
updateDeviceStatus(device) {
if (device.status == 3) {
this.statusColor.background = '#12d09f';
this.title = this.$t('device.running-status.866086-26');
this.shadowUnEnable = false;
} else {
if (device.isShadow == 1) {
this.statusColor.background = '#486FF2';
this.title = this.$t('device.running-status.866086-27');
this.shadowUnEnable = false;
} else {
this.statusColor.background = '#909399';
this.title = this.$t('device.running-status.866086-28');
this.shadowUnEnable = true;
}
}
this.$emit('statusEvent', this.deviceInfo.status);
},
/** 物模型数组元素值改变事件 */
arrayItemChange(value, thingsModel) {
let shadow = '';
for (let i = 0; i < thingsModel.datatype.arrayCount; i++) {
shadow += thingsModel.datatype.arrayModel[i].shadow + ',';
}
shadow = shadow.substring(0, shadow.length - 1);
thingsModel.shadow = shadow;
},
/** 物模型中数组值改变事件 */
arrayInputChange(value, thingsModel) {
let arrayModels = value.split(',');
if (arrayModels.length != thingsModel.datatype.arrayCount) {
this.$modal.alertWarning(this.$t('device.running-status.866086-29') + thingsModel.datatype.arrayCount + this.$t('device.running-status.866086-30'));
} else {
for (let i = 0; i < thingsModel.datatype.arrayCount; i++) {
thingsModel.datatype.arrayModel[i].shadow = arrayModels[i];
}
}
},
/**用户是否拥有分享设备权限*/
// hasShrarePerm(permission) {
// if (this.deviceInfo.isOwner == 0) {
// // 分享设备权限
// if (this.deviceInfo.userPerms.indexOf(permission) == -1) {
// return false;
// }
// }
// return true;
// },
/** 设备升级 */
otaUpgrade() {
// OTA升级
let topic = '/' + this.deviceInfo.productId + '/' + this.deviceInfo.serialNumber + '/ota/get';
let message = '{"version":' + this.firmware.version + ',"downloadUrl":"' + this.getDownloadUrl(this.firmware.filePath) + '"}';
// 发布
this.$mqttTool
.publish(topic, message, this.$t('device.running-status.866086-31'))
.then((res) => {
this.$modal.notifySuccess(res);
})
.catch((res) => {
this.$modal.notifyError(res);
});
this.openFirmware = false;
},
/** 获取最新固件 */
getLatestFirmware() {
const { deviceId, firmwareType } = this.deviceInfo;
getLatestFirmware(deviceId, firmwareType).then((response) => {
if (response.code === 200) {
this.firmware = response.data;
this.openFirmware = true;
}
});
},
// 取消按钮
cancel() {
this.openFirmware = false;
},
// 获取下载路径前缀
getDownloadUrl(path) {
return window.location.origin + process.env.VUE_APP_BASE_API + path;
},
/**图表展示*/
MonitorChart() {
for (let i = 0; i < this.deviceInfo.chartList.length; i++) {
this.monitorChart[i] = {
chart: this.$echarts.init(this.$refs.map[i]),
data: {
id: this.deviceInfo.chartList[i].id,
name: this.deviceInfo.chartList[i].name,
value: this.deviceInfo.chartList[i].shadow ? this.deviceInfo.chartList[i].shadow : this.deviceInfo.chartList[i].datatype.min,
},
};
var option;
option = {
tooltip: {
formatter: ' {b} <br/> {c}' + this.deviceInfo.chartList[i].datatype.unit,
},
series: [
{
name: this.deviceInfo.chartList[i].datatype.type,
type: 'gauge',
min: this.deviceInfo.chartList[i].datatype.min,
max: this.deviceInfo.chartList[i].datatype.max,
colorBy: 'data',
splitNumber: 10,
radius: '100%',
// 分割线
splitLine: {
distance: 4,
},
axisLabel: {
fontSize: 10,
distance: 10,
},
// 刻度线
axisTick: {
distance: 4,
},
// 仪表盘轴线
axisLine: {
lineStyle: {
width: 8,
color: [
[0.2, '#409EFF'], // 0~20%
[0.8, '#12d09f'], // 40~60%
[1, '#F56C6C'], // 80~100%
],
opacity: 0.3,
},
},
pointer: {
icon: 'triangle',
length: '60%',
width: 7,
},
progress: {
show: true,
width: 8,
},
detail: {
valueAnimation: true,
formatter: '{value}' + ' ' + this.deviceInfo.chartList[i].datatype.unit,
offsetCenter: [0, '80%'],
fontSize: 20,
},
data: [
{
value: this.deviceInfo.chartList[i].shadow ? this.deviceInfo.chartList[i].shadow : this.deviceInfo.chartList[i].datatype.min,
name: this.deviceInfo.chartList[i].name,
},
],
title: {
offsetCenter: [0, '115%'],
fontSize: 16,
},
},
],
};
option && this.monitorChart[i].chart.setOption(option);
}
},
// 保留原有方法但不再用于productpram
sendDataToIframe() {
const iframe = this.$refs.childFrame;
if (iframe && iframe.contentWindow) {
// 这里只处理upload数据如果需要
const uploadModel = this.deviceInfo.thingsModels.find(model => model.id === 'upload');
if (uploadModel) {
let shadowData = uploadModel.shadow;
if (typeof shadowData === 'string' && shadowData.startsWith('JSON=')) {
shadowData = shadowData.substring(5);
}
const uploadDataString = typeof shadowData === 'string' ? shadowData : JSON.stringify(shadowData);
iframe.contentWindow.postMessage(
uploadDataString,
'https://iot-xcwl.cn/h5/gateway/index.html#/'
);
console.log('Sent upload data:', uploadDataString);
}
}
}
},
};
</script>
<style lang="scss" scoped>
.running-status {
padding-bottom: 20px;
.status-col {
.title {
line-height: 28px;
font-size: 16px;
}
.emum-wrap {
.btn {
margin: 5px 10px 5px 0;
}
}
::v-deep .el-slider__bar {
height: 18px;
}
::v-deep .el-slider__runway {
height: 18px;
margin: 5px 0;
}
::v-deep .el-slider__button {
height: 18px;
width: 18px;
border-radius: 10%;
}
::v-deep .el-slider__button-wrapper {
top: -9px;
}
.is-active-btn {
color: #1890ff;
border-color: #badeff;
background-color: #e8f4ff;
}
}
}
</style>