20260126:模型排序&高德地图

This commit is contained in:
George 2026-01-26 19:17:47 +08:00
parent 652a32b797
commit b770da12e3
13 changed files with 5348 additions and 5153 deletions

View File

@ -102,7 +102,7 @@ spring:
redis:
host: localhost # 地址
port: 6379 # 端口,默认为6379
database: 3 # 数据库索引
database: 0 # 数据库索引
password: fastbee # 密码
timeout: 10s # 连接超时时间
lettuce:

View File

@ -50,8 +50,11 @@ public class TCPBootStrap {
.setPort(port)
//设置报文最大长度 modbus-rtu
.setMaxFrameLength(1024)
// 此处就是ASCII和注册包粘包了,无需strip掉 “分隔符” 即可
.setDelimiters(new Delimiter(new byte[]{0x7e}, false))
/*
* 此处就是ASCII和注册包粘包了,无需strip掉 “分隔符” 即可
* 最原始.setDelimiters(new Delimiter(new byte[]{0x7e}, true))
*/
.setDelimiters(new Delimiter(new byte[]{0x0A}, true))
.setDecoder(messageAdapter)
.setEncoder(messageAdapter)
.setHandlerMapping(handlerMapping)

View File

@ -125,13 +125,12 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
if (minDelim != null) {
int minDelimLength = minDelim.value.length;
ByteBuf frame = null;
boolean keepDelimiterForNextFrame = shouldKeepDelimiterForNextFrame(minDelim) && minFrameLength > 0;
if (discardingTooLongFrame) {
// 如果true,将长度不符合报文丢弃
// 初始化原来的值
discardingTooLongFrame = false;
buffer.skipBytes(minFrameLength + (keepDelimiterForNextFrame ? 0 : minDelimLength));
buffer.skipBytes(minFrameLength + minDelimLength);
int tooLongFrameLength = this.tooLongFrameLength;
this.tooLongFrameLength = 0;
@ -143,16 +142,10 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
/*小于最小长度帧处理*/
if (minFrameLength > maxFrameLength) {
//放弃读取帧
buffer.skipBytes(minFrameLength + (keepDelimiterForNextFrame ? 0 : minDelimLength));
buffer.skipBytes(minFrameLength + minDelimLength);
fail(minFrameLength);
return null;
}
if (keepDelimiterForNextFrame) {
frame = buffer.readRetainedSlice(minFrameLength);
return frame;
}
/*是否需要跳过某字节*/
if (minDelim.strip) {
//忽略长度等于0的报文
@ -273,9 +266,6 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
return -1;
}
// Modbus RTU:为了避免把请求帧(常见固定8字节)误当响应帧(第3字节是byteCount)导致截断,
// 这里采用"候选长度 + CRC校验"方式确定帧边界,兼容:请求/回显/响应/异常响应。
// 异常响应:地址(1) + 功能码(1|0x80) + 异常码(1) + CRC(2) = 5字节
if ((functionCode & 0x80) != 0) {
int len = 5;
@ -309,8 +299,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
}
int byteCount = buffer.getByte(readerIndex + 2) & 0xFF;
int respLen = 3 + byteCount + 2;
log.debug("[{}] Modbus功能码{}: byteCount={}, 预期帧长={}, 可读={}",
deviceId, String.format("%02X", functionCode), byteCount, respLen, readable);
log.debug("[{}] Modbus功能码{}: byteCount={}, 预期帧长={}, 可读={}", deviceId, String.format("%02X", functionCode), byteCount, respLen, readable);
if (respLen > maxFrameLength) {
log.warn("[{}] Modbus帧长度{}超过最大限制{}", deviceId, respLen, maxFrameLength);
return -1;
@ -368,13 +357,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
byte[] crc = CRC16Utils.getCrc16Byte(data); // [low, high]
int crcLo = buffer.getByte(offset + len - 2) & 0xFF;
int crcHi = buffer.getByte(offset + len - 1) & 0xFF;
boolean valid = ((crc[0] & 0xFF) == crcLo) && ((crc[1] & 0xFF) == crcHi);
if (!valid && log.isDebugEnabled()) {
log.debug("[{}] CRC校验: 计算值={}{}, 实际值={}{}", deviceId,
String.format("%02X", crc[0] & 0xFF), String.format("%02X", crc[1] & 0xFF),
String.format("%02X", crcLo), String.format("%02X", crcHi));
}
return valid;
return ((crc[0] & 0xFF) == crcLo) && ((crc[1] & 0xFF) == crcHi);
}
private void fail(long frameLength) {

View File

@ -114,4 +114,10 @@ public class ThingsModelValueItem {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date ts;
/**
* 创建时间(用于排序)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

View File

@ -599,6 +599,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
arrayObjectThingsModel.setIsReadonly(param.getIsReadonly());
arrayObjectThingsModel.setDatatype(param.getDatatype());
arrayObjectThingsModel.setModelOrder(thingsModel.getModelOrder());
arrayObjectThingsModel.setCreateTime(thingsModel.getCreateTime());
resultList.add(arrayObjectThingsModel);
}
}
@ -617,6 +618,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
arrayThingsModel.setIsReadonly(thingsModel.getIsReadonly());
arrayThingsModel.setDatatype(datatype);
arrayThingsModel.setModelOrder(thingsModel.getModelOrder());
arrayThingsModel.setCreateTime(thingsModel.getCreateTime());
resultList.add(arrayThingsModel);
}
} else if ("object".equals(datatype.getType())) {
@ -630,6 +632,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
newObjectThingsModel.setIsReadonly(objectThingsModel.getIsReadonly());
newObjectThingsModel.setDatatype(objectThingsModel.getDatatype());
newObjectThingsModel.setModelOrder(thingsModel.getModelOrder());
newObjectThingsModel.setCreateTime(thingsModel.getCreateTime());
resultList.add(newObjectThingsModel);
}
} else {
@ -642,6 +645,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
otherThingsModel.setIsReadonly(thingsModel.getIsReadonly());
otherThingsModel.setDatatype(datatype);
otherThingsModel.setModelOrder(thingsModel.getModelOrder());
otherThingsModel.setCreateTime(thingsModel.getCreateTime());
resultList.add(otherThingsModel);
}
return resultList;
@ -686,24 +690,31 @@ public class ThingsModelServiceImpl implements IThingsModelService {
if (null == productId) {
return new ArrayList<>();
}
// 1、获取产品物模型
// 排序器:按 modelOrder 降序,createTime 降序(与数据库 SQL 一致)
Comparator<ThingsModelDTO> modelComparator = Comparator
.comparing(ThingsModelDTO::getModelOrder, Comparator.reverseOrder())
.thenComparing(ThingsModelDTO::getCreateTime, Comparator.nullsLast(Comparator.reverseOrder()));
// 1、获取产品物模型并排序
Map<String, ThingsModelValueItem> map = itslCache.getCacheThMapByProductId(productId);
List<ThingsModelDTO> thingsModelDTOList = new ArrayList<>();
for (Map.Entry<String, ThingsModelValueItem> entry : map.entrySet()) {
ThingsModel thingsModel = this.propertyDtoChangeThingsModel(entry.getValue());
thingsModelDTOList.addAll(this.changeObjectOrArray(thingsModel));
}
thingsModelDTOList.sort(modelComparator);
// 2. 获取设备级计算物模型并合并
if (StringUtils.isNotEmpty(serialNumber)) {
Map<String, ThingsModelValueItem> deviceCalculatedMap = itslCache.getDeviceCalculatedModelMap(serialNumber);
List<ThingsModelDTO> deviceModelList = new ArrayList<>();
for (Map.Entry<String, ThingsModelValueItem> entry : deviceCalculatedMap.entrySet()) {
ThingsModel thingsModel = this.propertyDtoChangeThingsModel(entry.getValue());
thingsModelDTOList.addAll(this.changeObjectOrArray(thingsModel));
deviceModelList.addAll(this.changeObjectOrArray(thingsModel));
}
deviceModelList.sort(modelComparator);
thingsModelDTOList.addAll(deviceModelList);
}
thingsModelDTOList.sort(Comparator.comparing(ThingsModelDTO::getModelOrder));
if (StringUtils.isEmpty(serialNumber)) {
return thingsModelDTOList;
}
@ -767,6 +778,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
thingsModel.setSpecs(JSON.toJSONString(valueItem.getDatatype()));
thingsModel.setType(valueItem.getType());
thingsModel.setModelId(valueItem.getModelId());
thingsModel.setCreateTime(valueItem.getCreateTime());
return thingsModel;
}

14
vue/package-lock.json generated
View File

@ -5751,6 +5751,15 @@
"zrender": "5.6.1"
}
},
"echarts-extension-leaflet": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/echarts-extension-leaflet/-/echarts-extension-leaflet-1.2.2.tgz",
"integrity": "sha512-EKZmIQDRqxnf6DtYWqujn2PRqHmEAq3yr0gsZAwmOT9rSy0Rwt9FlXKPjK3d+EVLqT6se6IQweyJTsqMg537Gg==",
"requires": {
"echarts": "^5.5.0",
"leaflet": "^1.9.4"
}
},
"echarts-liquidfill": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-3.1.0.tgz",
@ -8974,6 +8983,11 @@
"launch-editor": "^2.12.0"
}
},
"leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmmirror.com/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/leven/-/leven-2.1.0.tgz",

View File

@ -46,6 +46,7 @@
"codemirror": "^5.65.2",
"core-js": "3.25.3",
"echarts": "^5.4.0",
"echarts-extension-leaflet": "^1.2.2",
"echarts-liquidfill": "^3.0.6",
"element-china-area-data": "^4.1.1",
"element-ui": "2.15.10",

View File

@ -1,28 +1,66 @@
export function loadBMap() {
// 使用OpenStreetMap和Leaflet替代百度地图
export function loadMap() {
return new Promise(function(resolve, reject) {
if (typeof BMap !== 'undefined') {
resolve(BMap)
return true
// eslint-disable-next-line no-undef
if (typeof L !== 'undefined') {
// eslint-disable-next-line no-undef
resolve(L);
return true;
}
window.onBMapCallback = function() {
resolve(BMap)
// 加载Leaflet CSS
if (!document.querySelector('link[href*="leaflet.css"]')) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
link.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
link.crossOrigin = '';
document.head.appendChild(link);
}
// 使用https协议需要添加一下meta标签
var protocolStr = document.location.protocol;
if(protocolStr == "https:")
{
let meta = document.createElement('meta')
meta.httpEquiv = 'Content-Security-Policy'
meta.content ='upgrade-insecure-requests'
meta.onerror = reject
document.head.appendChild(meta)
}
// 引入百度地图
let script = document.createElement('script')
script.type = 'text/javascript'
script.src =
'http://api.map.baidu.com/api?v=2.0&ak='+ process.env.VUE_APP_BAI_DU_AK +'&s=1&__ec_v__=20190126&callback=onBMapCallback'
script.onerror = reject
document.head.appendChild(script)
})
// 加载Leaflet JS
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
script.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=';
script.crossOrigin = '';
script.onload = function() {
// eslint-disable-next-line no-undef
resolve(L);
};
script.onerror = reject;
document.head.appendChild(script);
});
}
// export function loadBMap() {
// return new Promise(function(resolve, reject) {
// if (typeof BMap !== 'undefined') {
// resolve(BMap)
// return true
// }
// window.onBMapCallback = function() {
// resolve(BMap)
// }
// // 使用https协议需要添加一下meta标签
// var protocolStr = document.location.protocol;
// if(protocolStr == "https:")
// {
// let meta = document.createElement('meta')
// meta.httpEquiv = 'Content-Security-Policy'
// meta.content ='upgrade-insecure-requests'
// meta.onerror = reject
// document.head.appendChild(meta)
// }
// // 引入百度地图
// let script = document.createElement('script')
// script.type = 'text/javascript'
// script.src =
// 'http://api.map.baidu.com/api?v=2.0&ak='+ process.env.VUE_APP_BAI_DU_AK +'&s=1&__ec_v__=20190126&callback=onBMapCallback'
// script.onerror = reject
// document.head.appendChild(script)
// })
// }

View File

@ -15,7 +15,7 @@
<script>
import {
loadBMap
loadMap
} from '@/utils/map.js'
//安装的是echarts完整包,里面包含百度地图扩展,路径为 echarts/extension/bmap/bmap.js,将其引入
//ECharts的百度地图扩展,可以在百度地图上展现点图,线图,热力图等可视化
@ -65,7 +65,7 @@ export default {
},
/**加载地图*/
loadMap() {
loadBMap().then(() => {
loadMap().then(() => {
this.getmap();
});
},

View File

@ -212,7 +212,7 @@
</el-card>
</el-col>
<!-- 应飞哥要求隐藏 2026-01-01 -->
<!-- 应飞哥要求隐藏 2026-01-01 -->
<!-- <el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<!- mqtt状态数据 ->
<div class="card-container">
@ -319,10 +319,9 @@ import axios from 'axios';
import { getDeviceStatistic } from '@/api/iot/device';
import { listNotice, getNotice } from '@/api/system/notice';
import CountTo from 'vue-count-to';
import { loadBMap } from '@/utils/map.js';
//安装的是echarts完整包,里面包含百度地图扩展,路径为 echarts/extension/bmap/bmap.js,将其引入
//ECharts的百度地图扩展,可以在百度地图上展现点图,线图,热力图等可视化
// 注意:bmap扩展需要在BMap加载后再引入
import 'leaflet/dist/leaflet.css';
import 'echarts-extension-leaflet';
//import coordtransform from 'coordtransform';
import { getServer } from '@/api/monitor/server';
import { listAllDeviceShort } from '@/api/iot/device';
import 'echarts-liquidfill';
@ -1157,23 +1156,10 @@ export default {
/**加载地图*/
loadMap() {
this.$nextTick(() => {
loadBMap().then(() => {
if (typeof BMap === 'undefined') {
this.$message.error('百度地图API未正确加载');
return;
}
// 确保bmap扩展已加载
try {
require('echarts/extension/bmap/bmap');
} catch (e) {
// 忽略扩展加载错误
}
// 使用 echarts-extension-leaflet,已在 import 时加载
setTimeout(() => {
this.getmap();
}, 500);
}).catch((error) => {
this.$message.error('百度地图加载失败,请检查API Key配置');
});
}, 300);
});
},
/** 查询服务器信息 */
@ -1229,13 +1215,14 @@ export default {
/** 地图 */
getmap() {
if (typeof BMap === 'undefined') {
this.$message.error('百度地图API未正确加载,请刷新页面重试');
if (!this.$refs.map) {
return;
}
if (!this.$refs.map) {
return;
// 销毁旧的ECharts实例,避免多个实例叠加
var existingChart = this.$echarts.getInstanceByDom(this.$refs.map);
if (existingChart) {
existingChart.dispose();
}
var myChart = this.$echarts.init(this.$refs.map);
@ -1258,8 +1245,14 @@ export default {
let convertData = function (data, status) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = [data[i].longitude, data[i].latitude];
if (geoCoord && data[i].status == status) {
// 验证经纬度有效性
var lng = parseFloat(data[i].longitude);
var lat = parseFloat(data[i].latitude);
if (isNaN(lng) || isNaN(lat) || lng === 0 || lat === 0) {
continue;
}
var geoCoord = [lng, lat];
if (data[i].status == status) {
res.push({
name: data[i].deviceName,
value: geoCoord,
@ -1320,14 +1313,9 @@ export default {
return { center: [centerLng, centerLat], zoom: zoom };
};
// 使用长沙作为默认中心点,如果有设备数据则使用计算的中心点
let mapConfig = { center: [112.867037, 28.11857], zoom: 12 };
if (this.deviceList && this.deviceList.length > 0) {
const calculated = calculateMapCenter(this.deviceList);
if (calculated.center && calculated.center[0] > 0) {
mapConfig = calculated;
}
}
// 使用中国长沙作为地图中心点
// 长沙坐标:经度 112.94, 纬度 28.23
let mapConfig = { center: [112.94, 28.23], zoom: 5 };
option = {
title: {
@ -1382,21 +1370,27 @@ export default {
return htmlStr;
},
},
bmap: {
center: mapConfig.center,
zoom: mapConfig.zoom,
leaflet: {
// echarts-extension-leaflet center 使用 [lng, lat] 格式
// 长沙坐标:经度 112.94, 纬度 28.23
center: [112.94, 28.23],
zoom: 5,
roam: true,
// 确保地图正确初始化
enableMapClick: true,
// 暂时不使用自定义样式,确保地图能正常显示
// mapStyle: {
// styleJson: [...]
// },
tiles: [
{
// 使用高德地图瓦片(国内访问稳定)
urlTemplate: 'https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
options: {
subdomains: ['1', '2', '3', '4'],
attribution: '&copy; 高德地图',
},
},
],
},
series: [
{
type: 'scatter',
coordinateSystem: 'bmap',
coordinateSystem: 'leaflet',
data: convertData(this.deviceList, 1),
symbolSize: 15,
itemStyle: {
@ -1405,7 +1399,7 @@ export default {
},
{
type: 'scatter',
coordinateSystem: 'bmap',
coordinateSystem: 'leaflet',
data: convertData(this.deviceList, 2),
symbolSize: 15,
itemStyle: {
@ -1414,7 +1408,7 @@ export default {
},
{
type: 'scatter',
coordinateSystem: 'bmap',
coordinateSystem: 'leaflet',
data: convertData(this.deviceList, 4),
symbolSize: 15,
itemStyle: {
@ -1423,7 +1417,7 @@ export default {
},
{
type: 'effectScatter',
coordinateSystem: 'bmap',
coordinateSystem: 'leaflet',
data: convertData(this.deviceList, 3),
symbolSize: 15,
showEffectOn: 'render',
@ -1465,35 +1459,10 @@ export default {
// 设置地图配置
myChart.setOption(option, true);
// 等待地图初始化后调整大小和刷新
setTimeout(() => {
// 等待地图初始化完成
this.$nextTick(() => {
myChart.resize();
const bmapModel = myChart.getModel().getComponent('bmap');
if (bmapModel && bmapModel.getBMap) {
const bmapInstance = bmapModel.getBMap();
if (bmapInstance) {
// 启用地图功能
bmapInstance.enableScrollWheelZoom(true);
bmapInstance.enableDoubleClickZoom(true);
bmapInstance.enableDragging(true);
// 设置中心点和缩放级别
const centerPoint = new BMap.Point(mapConfig.center[0], mapConfig.center[1]);
bmapInstance.setCenter(centerPoint);
bmapInstance.setZoom(mapConfig.zoom);
// 刷新地图确保瓦片加载
setTimeout(() => {
myChart.resize();
bmapInstance.reset();
bmapInstance.setCenter(centerPoint);
bmapInstance.setZoom(mapConfig.zoom);
myChart.resize();
}, 500);
}
}
}, 800);
});
} catch (error) {
this.$message.error('地图初始化失败: ' + error.message);
}
@ -2292,7 +2261,7 @@ export default {
color: 'rgba(72,216,191, 0.3)',
borderWidth: 10,
/*shadowColor: 'rgba(72,216,191, 0.3)',
shadowBlur: 100,*/
shadowBlur: 100,*/
borderColor: '#A9F387',
},
smooth: true,

View File

@ -1,6 +1,7 @@
<template>
<el-card style="margin: 6px; padding-bottom: 100px">
<el-tabs id="deviceDetailTab" v-model="activeName" tab-position="left" @tab-click="tabChange" style="padding: 10px; min-height: 400px" lazy>
<el-tabs id="deviceDetailTab" v-model="activeName" tab-position="left" @tab-click="tabChange"
style="padding: 10px; min-height: 400px" lazy>
<el-tab-pane name="basic">
<span slot="label">{{ $t('device.device-edit.148398-0') }}</span>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
@ -8,7 +9,9 @@
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
<el-form-item :label="$t('device.device-edit.148398-1')" prop="deviceName">
<el-input v-model="form.deviceName" :placeholder="$t('device.device-edit.148398-2')">
<el-button slot="append" @click="openSummaryDialog" v-if="form.deviceId != 0">{{ $t('device.device-edit.148398-3') }}</el-button>
<el-button slot="append" @click="openSummaryDialog" v-if="form.deviceId != 0">
{{ $t('device.device-edit.148398-3') }}
</el-button>
</el-input>
</el-form-item>
<el-form-item label="" prop="productName">
@ -16,8 +19,11 @@
<span style="color: red">*</span>
{{ $t('device.device-edit.148398-4') }}
</template>
<el-input readonly v-model="form.productName" :placeholder="$t('device.device-edit.148398-5')" :disabled="form.status != 1">
<el-button slot="append" @click="selectProduct()" :disabled="form.status != 1">{{ $t('device.device-edit.148398-6') }}</el-button>
<el-input readonly v-model="form.productName" :placeholder="$t('device.device-edit.148398-5')"
:disabled="form.status != 1">
<el-button slot="append" @click="selectProduct()" :disabled="form.status != 1">
{{ $t('device.device-edit.148398-6') }}
</el-button>
</el-input>
</el-form-item>
<el-form-item label="" prop="serialNumber">
@ -25,11 +31,14 @@
<span style="color: red">*</span>
{{ $t('device.device-edit.148398-7') }}
</template>
<el-input v-model="form.serialNumber" :placeholder="$t('device.device-edit.148398-8')" :disabled="form.status != 1" maxlength="32">
<el-button v-if="form.deviceType !== 3" slot="append" @click="generateNum" :loading="genDisabled" :disabled="form.status != 1" v-hasPermi="['iot:device:add']">
<el-input v-model="form.serialNumber" :placeholder="$t('device.device-edit.148398-8')"
:disabled="form.status != 1" maxlength="32">
<el-button v-if="form.deviceType !== 3" slot="append" @click="generateNum" :loading="genDisabled"
:disabled="form.status != 1" v-hasPermi="['iot:device:add']">
{{ $t('device.device-edit.148398-9') }}
</el-button>
<el-button v-if="form.deviceType === 3" slot="append" @click="genSipID()" :disabled="form.status != 1" v-hasPermi="['iot:device:add']">
<el-button v-if="form.deviceType === 3" slot="append" @click="genSipID()" :disabled="form.status != 1"
v-hasPermi="['iot:device:add']">
{{ $t('device.device-edit.148398-9') }}
</el-button>
</el-input>
@ -45,13 +54,15 @@
</template>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-12')" prop="firmwareVersion">
<el-input v-model="form.firmwareVersion" :placeholder="$t('device.device-edit.148398-13')" type="number" step="0.1" :disabled="form.status != 1 || form.deviceType === 3">
<el-input v-model="form.firmwareVersion" :placeholder="$t('device.device-edit.148398-13')" type="number"
step="0.1" :disabled="form.status != 1 || form.deviceType === 3">
<template slot="prepend">Version</template>
</el-input>
</el-form-item>
<!-- 设备影子 -->
<el-form-item :label="$t('device.device-edit.148398-15')" prop="isShadow">
<el-switch v-model="form.isShadow" active-text="" inactive-text="" :active-value="1" :inactive-value="0" :disabled="form.deviceType === 3"></el-switch>
<el-switch v-model="form.isShadow" active-text="" inactive-text="" :active-value="1" :inactive-value="0"
:disabled="form.deviceType === 3"></el-switch>
</el-form-item>
<el-form-item v-if="form.deviceType != 4" :label="$t('device.device-edit.148398-16')" prop="deviceStatus">
@ -66,49 +77,61 @@
></el-switch>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-17')" prop="remark">
<el-input v-model="form.remark" type="textarea" :placeholder="$t('device.device-edit.148398-18')" rows="1" />
<el-input v-model="form.remark" type="textarea" :placeholder="$t('device.device-edit.148398-18')"
rows="1"/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
<el-form-item :label="$t('device.device-edit.148398-19')" prop="locationWay">
<el-select v-model="form.locationWay" :placeholder="$t('device.device-edit.148398-20')" clearable size="small" style="width: 100%" :disabled="form.deviceType === 3">
<el-option v-for="dict in dict.type.iot_location_way" :key="dict.value" :label="dict.label" :value="Number(dict.value)" />
<el-select v-model="form.locationWay" :placeholder="$t('device.device-edit.148398-20')" clearable
size="small" style="width: 100%" :disabled="form.deviceType === 3">
<el-option v-for="dict in dict.type.iot_location_way" :key="dict.value" :label="dict.label"
:value="Number(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-21')" prop="longitude">
<el-input v-model="form.longitude" :placeholder="$t('device.device-edit.148398-22')" type="number" :disabled="form.locationWay != 3">
<el-link slot="append" :underline="false" href="https://api.map.baidu.com/lbsapi/getpoint/index.html" target="_blank" :disabled="form.locationWay != 3">
<el-input v-model="form.longitude" :placeholder="$t('device.device-edit.148398-22')" type="number"
:disabled="form.locationWay != 3">
<el-link slot="append" :underline="false" href="https://api.map.baidu.com/lbsapi/getpoint/index.html"
target="_blank" :disabled="form.locationWay != 3">
{{ $t('device.device-edit.148398-23') }}
</el-link>
</el-input>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-24')" prop="latitude">
<el-input v-model="form.latitude" :placeholder="$t('device.device-edit.148398-25')" type="number" :disabled="form.locationWay != 3">
<el-link slot="append" :underline="false" href="https://api.map.baidu.com/lbsapi/getpoint/index.html" target="_blank" :disabled="form.locationWay != 3">
<el-input v-model="form.latitude" :placeholder="$t('device.device-edit.148398-25')" type="number"
:disabled="form.locationWay != 3">
<el-link slot="append" :underline="false" href="https://api.map.baidu.com/lbsapi/getpoint/index.html"
target="_blank" :disabled="form.locationWay != 3">
{{ $t('device.device-edit.148398-23') }}
</el-link>
</el-input>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-26')" prop="networkAddress">
<el-input v-model="form.networkAddress" :placeholder="$t('device.device-edit.148398-27')" :disabled="form.locationWay != 3" />
<el-input v-model="form.networkAddress" :placeholder="$t('device.device-edit.148398-27')"
:disabled="form.locationWay != 3"/>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-28')" prop="networkIp">
<el-input v-model="form.networkIp" :placeholder="$t('device.device-edit.148398-29')" disabled />
<el-input v-model="form.networkIp" :placeholder="$t('device.device-edit.148398-29')" disabled/>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-30')" prop="activeTime">
<el-date-picker clearable v-model="form.activeTime" type="date" value-format="yyyy-MM-dd" :placeholder="$t('device.device-edit.148398-31')" disabled style="width: 100%"></el-date-picker>
<el-date-picker clearable v-model="form.activeTime" type="date" value-format="yyyy-MM-dd"
:placeholder="$t('device.device-edit.148398-31')" disabled
style="width: 100%"></el-date-picker>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-32')" prop="rssi">
<el-input v-model="form.rssi" :placeholder="$t('device.device-edit.148398-33')" disabled />
<el-input v-model="form.rssi" :placeholder="$t('device.device-edit.148398-33')" disabled/>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-34')" prop="remark" v-if="form.deviceId != 0">
<dict-tag :options="dict.type.iot_device_status" :value="form.status" style="display: inline-block; margin-right: 8px" />
<dict-tag :options="dict.type.iot_device_status" :value="form.status"
style="display: inline-block; margin-right: 8px"/>
<el-button size="small" @click="handleViewMqtt()">{{ $t('device.device-edit.148398-35') }}</el-button>
<el-button size="small" @click="openCodeDialog()">{{ $t('device.device-edit.148398-36') }}</el-button>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8" v-if="form.deviceId != 0">
<div style="border: 1px solid #dfe4ed; border-radius: 5px; padding: 5px; text-align: center; line-height: 400px">
<div
style="border: 1px solid #dfe4ed; border-radius: 5px; padding: 5px; text-align: center; line-height: 400px">
<div id="map" style="height: 435px; width: 100%">{{ $t('device.device-edit.148398-37') }}</div>
</div>
</el-col>
@ -126,20 +149,20 @@
</el-form-item>
</el-form>
<!-- 选择产品 -->
<product-list ref="productList" :productId="form.productId" @productEvent="getProductData($event)" />
<sipid ref="sipidGen" :product="form" @addGenEvent="getSipIDData($event)" />
<product-list ref="productList" :productId="form.productId" @productEvent="getProductData($event)"/>
<sipid ref="sipidGen" :product="form" @addGenEvent="getSipIDData($event)"/>
</el-tab-pane>
<el-tab-pane name="runningStatus" v-if="form.deviceType !== 3">
<span slot="label">{{ $t('device.device-edit.148398-42') }}</span>
<!-- <real-time-status ref="realTimeStatus" :device="form" @statusEvent="getDeviceStatusData($event)" /> -->
<running-status ref="runningStatus" :device="form" @statusEvent="getDeviceStatusData($event)" />
<running-status ref="runningStatus" :device="form" @statusEvent="getDeviceStatusData($event)"/>
</el-tab-pane>
<!-- 设备级别物模型 -->
<el-tab-pane name="calculatedModel" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label"><span style="color: red">* </span>{{$t('device.device-edit.148398-83')}}</span>
<device-things-model ref="deviceThingsModel" :device="form" />
<span slot="label"><span style="color: red">* </span>{{ $t('device.device-edit.148398-83') }}</span>
<device-things-model ref="deviceThingsModel" :device="form"/>
</el-tab-pane>
<el-tab-pane
@ -149,97 +172,97 @@
lazy
>
<span slot="label">{{ $t('device.device-edit.148398-43') }}</span>
<device-sub ref="deviceSub" :device="form" />
<device-sub ref="deviceSub" :device="form"/>
</el-tab-pane>
<el-tab-pane name="sipChannel" :disabled="form.deviceId == 0" v-if="form.deviceType === 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-44') }}</span>
<channel ref="Channel" :device="form" @playerEvent="getPlayerData($event)" />
<channel ref="Channel" :device="form" @playerEvent="getPlayerData($event)"/>
</el-tab-pane>
<el-tab-pane name="sipPlayer" :disabled="form.deviceId == 0" v-if="form.deviceType === 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-45') }}</span>
<device-live-stream ref="deviceLiveStream" :device="form" />
<device-live-stream ref="deviceLiveStream" :device="form"/>
</el-tab-pane>
<el-tab-pane name="sipVideo" :disabled="form.deviceId == 0" v-if="form.deviceType === 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-46') }}</span>
<deviceVideo ref="deviceVideo" :device="form" />
<deviceVideo ref="deviceVideo" :device="form"/>
</el-tab-pane>
<el-tab-pane name="ossRecord" :disabled="form.deviceId == 0" v-if="form.deviceType === 3">
<span slot="label">{{ $t('device.device-edit.148398-79') }}</span>
<OssRecord ref="OssRecord" :device="form" />
<OssRecord ref="OssRecord" :device="form"/>
</el-tab-pane>
<el-tab-pane name="sipTalk" :disabled="form.deviceId == 0" v-if="form.deviceType === 3">
<span slot="label">{{ $t('device.device-edit.148398-82') }}</span>
<siptalk ref="siptalk" :device="form" />
<siptalk ref="siptalk" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceTimer" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-47') }}</span>
<device-timer ref="deviceTimer" :device="form" />
<device-timer ref="deviceTimer" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceUser" :disabled="form.deviceId == 0" lazy>
<span slot="label">{{ $t('device.device-edit.148398-48') }}</span>
<device-user ref="deviceUser" :device="form" @userEvent="getUserData($event)" />
<device-user ref="deviceUser" :device="form" @userEvent="getUserData($event)"/>
</el-tab-pane>
<el-tab-pane name="deviceLog" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-49') }}</span>
<device-log ref="deviceLog" :device="form" />
<device-log ref="deviceLog" :device="form"/>
</el-tab-pane>
<el-tab-pane name="alertUser" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3">
<span slot="label" v-hasPermi="['iot:device:alert:user:list']">{{ $t('device.device-edit.148398-80') }}</span>
<alert-user ref="alertUser" :device="form" />
<alert-user ref="alertUser" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceAlert" :disabled="form.deviceId == 0">
<span slot="label" v-hasPermi="['iot:alertLog:list']">{{ $t('device.device-edit.148398-81') }}</span>
<device-alert ref="deviceAlert" :device="form" />
<device-alert ref="deviceAlert" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceFuncLog" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-50') }}</span>
<device-func ref="deviceFuncLog" :device="form" />
<device-func ref="deviceFuncLog" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceMonitor" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3">
<span slot="label">{{ $t('device.device-edit.148398-51') }}</span>
<device-monitor ref="deviceMonitor" :device="form" />
<device-monitor ref="deviceMonitor" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceStastic" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3">
<span slot="label">{{ $t('device.device-edit.148398-52') }}</span>
<device-statistic ref="deviceStatistic" :device="form" />
<device-statistic ref="deviceStatistic" :device="form"/>
</el-tab-pane>
<!-- 轮询任务 -->
<el-tab-pane name="deviceModbusTask" :disabled="form.deviceId == 0" v-if="form.canConfigPoll === true" lazy>
<span slot="label">{{ $t('device.device-edit.148398-77') }}</span>
<device-modbus-task ref="deviceModbusTask" :device="form" />
<device-modbus-task ref="deviceModbusTask" :device="form"/>
</el-tab-pane>
<el-tab-pane name="instructionParsing" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-76') }}</span>
<instruction-parsing ref="instructionParsing" :device="form" />
<instruction-parsing ref="instructionParsing" :device="form"/>
</el-tab-pane>
<el-tab-pane name="scada" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3 && isShowScada == true" lazy>
<span slot="label">{{ $t('device.device-edit.148398-73') }}</span>
<device-scada ref="deviceScada" :device="form" />
<device-scada ref="deviceScada" :device="form"/>
</el-tab-pane>
<el-tab-pane name="variable" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-74') }}</span>
<device-variable ref="deviceVariable" :device="form" />
<device-variable ref="deviceVariable" :device="form"/>
</el-tab-pane>
<el-tab-pane name="inlineVideo" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-75') }}</span>
<device-inline-video ref="deviceInlineVideo" :sipRelationList="form.sipRelationList" />
<device-inline-video ref="deviceInlineVideo" :sipRelationList="form.sipRelationList"/>
</el-tab-pane>
<!-- 用于设置间距 -->
@ -258,7 +281,9 @@
</el-tab-pane> -->
<el-tab-pane name="deviceReturn" disabled>
<span slot="label">
<el-button type="info" size="mini" @click="goBack()">{{ $t('device.device-edit.148398-53') }}</el-button>
<el-button type="info" size="mini" @click="goBack()">{{
$t('device.device-edit.148398-53')
}}</el-button>
</span>
</el-tab-pane>
</el-tabs>
@ -293,10 +318,10 @@
<el-dialog :title="$t('device.device-edit.148398-58')" :visible.sync="openViewMqtt" width="600px" append-to-body>
<el-form ref="listQuery" :model="listQuery" :rules="rules" label-width="150px">
<el-form-item label="clientId" prop="clientId" v-if="this.form.transport == 'MQTT'">
<el-input v-model="listQuery.clientId" disabled style="width: 80%" />
<el-input v-model="listQuery.clientId" disabled style="width: 80%"/>
</el-form-item>
<el-form-item label="username" prop="username" v-if="this.form.transport == 'MQTT'">
<el-input v-model="listQuery.username" disabled style="width: 80%" />
<el-input v-model="listQuery.username" disabled style="width: 80%"/>
</el-form-item>
<el-form-item label="passwd" prop="passwd" v-if="this.form.transport == 'MQTT'">
<el-input clearable v-model="listQuery.passwd" disabled style="width: 80%"></el-input>
@ -351,13 +376,20 @@ import deviceFuncLog from './device-functionlog';
import deviceSub from './device-sub';
import deviceThingsModel from './device-things-model.vue';
import vueQr from 'vue-qr';
import { loadBMap } from '@/utils/map.js';
import { deviceSynchronization, getDevice, addDevice, updateDevice, generatorDeviceNum, getMqttConnect } from '@/api/iot/device';
import { getDeviceRunningStatus } from '@/api/iot/device';
import { cacheJsonThingsModel } from '@/api/iot/model';
import {loadMap} from '@/utils/map.js';
import {
deviceSynchronization,
getDevice,
addDevice,
updateDevice,
generatorDeviceNum,
getMqttConnect
} from '@/api/iot/device';
import {getDeviceRunningStatus} from '@/api/iot/device';
import {cacheJsonThingsModel} from '@/api/iot/model';
import DeviceFunc from '@/views/iot/device/device-functionlog';
import RealTimeStatus from '@/views/iot/device/realTime-status';
import { clientOut } from '@/api/iot/netty';
import {clientOut} from '@/api/iot/netty';
import defaultSettings from '@/settings';
export default {
@ -875,7 +907,7 @@ export default {
/**加载地图*/
loadMap() {
this.$nextTick(() => {
loadBMap().then(() => {
loadMap().then(() => {
this.getmap();
});
});
@ -953,8 +985,9 @@ export default {
this.$router.go(0);
//是否设备设置为禁用状态,则踢出设备
if (this.form.status === 2) {
const params = { clientId: this.form.serialNumber };
clientOut(params).then((res) => {});
const params = {clientId: this.form.serialNumber};
clientOut(params).then((res) => {
});
}
}
});
@ -1033,7 +1066,8 @@ export default {
},
// 获取选中的用户
getUserData(user) {},
getUserData(user) {
},
/**关闭物模型 */
openSummaryDialog() {
let json = {
@ -1088,21 +1122,44 @@ export default {
},
// 地图定位
getmap() {
this.map = new BMap.Map('map');
let point = null;
if (this.form.longitude != null && this.form.longitude != '' && this.form.latitude != null && this.form.latitude != '') {
point = new BMap.Point(this.form.longitude, this.form.latitude);
} else {
point = new BMap.Point(116.404, 39.915);
// 清除旧地图实例
if (this.map) {
this.map.remove();
this.map = null;
}
this.map.centerAndZoom(point, 19);
this.map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
this.map.addControl(new BMap.NavigationControl());
// 标注设备位置
this.mk = new BMap.Marker(point);
this.map.addOverlay(this.mk);
this.map.panTo(point);
// 使用Leaflet和高德地图
let lat, lng;
let parsedLat = parseFloat(this.form.latitude);
let parsedLng = parseFloat(this.form.longitude);
// 验证坐标有效性
if (!isNaN(parsedLat) && !isNaN(parsedLng) && parsedLat !== 0 && parsedLng !== 0) {
lat = parsedLat;
lng = parsedLng;
} else {
// 默认使用中国长沙坐标
lat = 28.23;
lng = 112.94;
}
// 创建地图
this.map = L.map('map').setView([lat, lng], 15);
// 添加高德地图图层(国内访问稳定)
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: ['1', '2', '3', '4'],
attribution: '&copy; 高德地图',
maxZoom: 18
}).addTo(this.map);
// 添加标记点
this.mk = L.marker([lat, lng]).addTo(this.map);
// 添加popup提示
if (this.form.deviceName) {
this.mk.bindPopup(`<b>${this.form.deviceName}</b><br>${this.form.networkAddress || ''}`).openPopup();
}
},
// 生成随机字母和数字
generateNum() {
@ -1111,7 +1168,7 @@ export default {
return;
}
this.genDisabled = true;
const params = { type: this.serverType };
const params = {type: this.serverType};
generatorDeviceNum(params).then((response) => {
this.form.serialNumber = response.data;
this.genDisabled = false;

View File

@ -4,7 +4,8 @@
<el-col :span="5" :xs="24">
<el-card style="margin: 20px 0 20px 20px">
<div class="menu-wrap">
<el-input v-model="deptName" :placeholder="$t('device.device-linkage.188958-0')" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px; margin-right: 10px" />
<el-input v-model="deptName" :placeholder="$t('device.device-linkage.188958-0')" clearable size="small"
prefix-icon="el-icon-search" style="margin-bottom: 20px; margin-right: 10px"/>
<el-tree
:data="deptOptions"
:props="defaultProps"
@ -34,7 +35,8 @@
style="margin-bottom: 20px; margin-right: 10px"
@keydown.enter.native="getDeviceList"
/>
<el-menu class="menu-wrap" :default-active="deviceList[0] && deviceList[0].devcieId" :default-openeds="defaultOpeneds" @open="handleOpen" style="margin-right: 10px">
<el-menu class="menu-wrap" :default-active="deviceList[0] && deviceList[0].devcieId"
:default-openeds="defaultOpeneds" @open="handleOpen" style="margin-right: 10px">
<el-submenu index="1" style="margin-left: -5px">
<template slot="title">
<i class="el-icon-menu"></i>
@ -60,7 +62,8 @@
<el-col :span="19" :xs="24">
<el-card style="margin: 20px 20px 20px 0">
<div class="head-container">
<el-tabs v-model="activeName" tab-position="top" @tab-click="tabChange" style="min-height: 400px" v-if="devTotal > 0">
<el-tabs v-model="activeName" tab-position="top" @tab-click="tabChange" style="min-height: 400px"
v-if="devTotal > 0">
<el-tab-pane name="basic">
<span slot="label">{{ $t('device.device-edit.148398-0') }}</span>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
@ -68,7 +71,9 @@
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
<el-form-item :label="$t('device.device-edit.148398-1')" prop="deviceName">
<el-input v-model="form.deviceName" :placeholder="$t('device.device-edit.148398-2')">
<el-button slot="append" @click="openSummaryDialog" v-if="form.deviceId != 0">{{ $t('device.device-edit.148398-3') }}</el-button>
<el-button slot="append" @click="openSummaryDialog" v-if="form.deviceId != 0">
{{ $t('device.device-edit.148398-3') }}
</el-button>
</el-input>
</el-form-item>
<el-form-item label="" prop="productName">
@ -76,8 +81,12 @@
<span style="color: red">*</span>
{{ $t('device.device-edit.148398-4') }}
</template>
<el-input readonly v-model="form.productName" :placeholder="$t('device.allot-import-dialog.060657-1')" disabled>
<el-button slot="append" @click="selectProduct()" disabled>{{ $t('device.device-edit.148398-6') }}</el-button>
<el-input readonly v-model="form.productName"
:placeholder="$t('device.allot-import-dialog.060657-1')" disabled>
<el-button slot="append" @click="selectProduct()" disabled>{{
$t('device.device-edit.148398-6')
}}
</el-button>
</el-input>
</el-form-item>
<el-form-item label="" prop="serialNumber">
@ -85,11 +94,15 @@
<span style="color: red">*</span>
{{ $t('device.device-edit.148398-7') }}
</template>
<el-input v-model="form.serialNumber" :placeholder="$t('device.device-edit.148398-8')" disabled maxlength="32">
<el-button v-if="form.deviceType !== 3" slot="append" @click="generateNum" :loading="genDisabled" disabled v-hasPermi="['iot:device:add']">
<el-input v-model="form.serialNumber" :placeholder="$t('device.device-edit.148398-8')" disabled
maxlength="32">
<el-button v-if="form.deviceType !== 3" slot="append" @click="generateNum"
:loading="genDisabled" disabled v-hasPermi="['iot:device:add']">
{{ $t('device.device-edit.148398-9') }}
</el-button>
<el-button v-if="form.deviceType === 3" slot="append" @click="genSipID()" disabled v-hasPermi="['iot:device:add']">{{ $t('device.device-edit.148398-9') }}</el-button>
<el-button v-if="form.deviceType === 3" slot="append" @click="genSipID()" disabled
v-hasPermi="['iot:device:add']">{{ $t('device.device-edit.148398-9') }}
</el-button>
</el-input>
</el-form-item>
<el-form-item v-if="openServerTip">
@ -103,48 +116,61 @@
</template>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-12')" prop="firmwareVersion">
<el-input v-model="form.firmwareVersion" :placeholder="$t('device.device-edit.148398-13')" type="number" step="0.1" :disabled="form.status != 1 || form.deviceType === 3">
<el-input v-model="form.firmwareVersion" :placeholder="$t('device.device-edit.148398-13')"
type="number" step="0.1" :disabled="form.status != 1 || form.deviceType === 3">
<template slot="prepend">Version</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-14')" prop="isSimulate">
<el-switch v-model="form.isSimulate" active-text="" inactive-text="" :active-value="1" :inactive-value="0" disabled></el-switch>
<el-switch v-model="form.isSimulate" active-text="" inactive-text="" :active-value="1"
:inactive-value="0" disabled></el-switch>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-15')" prop="isShadow">
<el-switch v-model="form.isShadow" active-text="" inactive-text="" :active-value="1" :inactive-value="0" disabled></el-switch>
<el-switch v-model="form.isShadow" active-text="" inactive-text="" :active-value="1"
:inactive-value="0" disabled></el-switch>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-16')" prop="deviceStatus">
<el-switch v-model="deviceStatus" active-text="" inactive-text="" disabled :active-value="1" :inactive-value="0" active-color="#F56C6C"></el-switch>
<el-switch v-model="deviceStatus" active-text="" inactive-text="" disabled :active-value="1"
:inactive-value="0" active-color="#F56C6C"></el-switch>
</el-form-item>
<el-form-item :label="$t('device.device-edit.148398-17')" prop="remark">
<el-input v-model="form.remark" type="textarea" :placeholder="$t('plzInput')" rows="1" />
<el-input v-model="form.remark" type="textarea" :placeholder="$t('plzInput')" rows="1"/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
<el-form-item :label="$t('device.device-edit.148398-19')" prop="locationWay">
<el-select v-model="form.locationWay" :placeholder="$t('device.device-linkage.188958-25')" clearable size="small" style="width: 100%" :disabled="form.deviceType === 3">
<el-option v-for="dict in dict.type.iot_location_way" :key="dict.value" :label="dict.label" :value="Number(dict.value)" />
<el-select v-model="form.locationWay" :placeholder="$t('device.device-linkage.188958-25')"
clearable size="small" style="width: 100%" :disabled="form.deviceType === 3">
<el-option v-for="dict in dict.type.iot_location_way" :key="dict.value" :label="dict.label"
:value="Number(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-26')" prop="longitude">
<el-input v-model="form.longitude" :placeholder="$t('device.device-linkage.188958-27')" type="number" disabled>
<el-link slot="append" :underline="false" href="https://api.map.baidu.com/lbsapi/getpoint/index.html" target="_blank" :disabled="form.locationWay != 3">
<el-input v-model="form.longitude" :placeholder="$t('device.device-linkage.188958-27')"
type="number" disabled>
<el-link slot="append" :underline="false"
href="https://api.map.baidu.com/lbsapi/getpoint/index.html" target="_blank"
:disabled="form.locationWay != 3">
{{ $t('device.device-linkage.188958-28') }}
</el-link>
</el-input>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-29')" prop="latitude">
<el-input v-model="form.latitude" :placeholder="$t('device.device-linkage.188958-30')" type="number" disabled>
<el-link slot="append" :underline="false" href="https://api.map.baidu.com/lbsapi/getpoint/index.html" target="_blank" :disabled="form.locationWay != 3">
<el-input v-model="form.latitude" :placeholder="$t('device.device-linkage.188958-30')"
type="number" disabled>
<el-link slot="append" :underline="false"
href="https://api.map.baidu.com/lbsapi/getpoint/index.html" target="_blank"
:disabled="form.locationWay != 3">
{{ $t('device.device-linkage.188958-28') }}
</el-link>
</el-input>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-31')" prop="networkAddress">
<el-input v-model="form.networkAddress" :placeholder="$t('device.device-linkage.188958-32')" disabled />
<el-input v-model="form.networkAddress" :placeholder="$t('device.device-linkage.188958-32')"
disabled/>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-33')" prop="networkIp">
<el-input v-model="form.networkIp" :placeholder="$t('device.device-linkage.188958-34')" disabled />
<el-input v-model="form.networkIp" :placeholder="$t('device.device-linkage.188958-34')" disabled/>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-35')" prop="activeTime">
<el-date-picker
@ -158,31 +184,40 @@
></el-date-picker>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-37')" prop="rssi">
<el-input v-model="form.rssi" :placeholder="$t('device.device-linkage.188958-38')" disabled />
<el-input v-model="form.rssi" :placeholder="$t('device.device-linkage.188958-38')" disabled/>
</el-form-item>
<el-form-item :label="$t('device.device-linkage.188958-39')" prop="remark" v-if="form.deviceId != 0">
<dict-tag :options="dict.type.iot_device_status" :value="form.status" style="display: inline-block; margin-right: 8px" />
<el-button size="small" @click="handleViewMqtt()">{{ $t('device.device-linkage.188958-40') }}</el-button>
<el-button size="small" @click="openCodeDialog()">{{ $t('device.device-linkage.188958-41') }}</el-button>
<el-form-item :label="$t('device.device-linkage.188958-39')" prop="remark"
v-if="form.deviceId != 0">
<dict-tag :options="dict.type.iot_device_status" :value="form.status"
style="display: inline-block; margin-right: 8px"/>
<el-button size="small" @click="handleViewMqtt()">{{
$t('device.device-linkage.188958-40')
}}
</el-button>
<el-button size="small" @click="openCodeDialog()">{{
$t('device.device-linkage.188958-41')
}}
</el-button>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8" v-if="form.deviceId != 0">
<div style="border: 1px solid #dfe4ed; border-radius: 5px; padding: 5px; text-align: center; line-height: 400px">
<div
style="border: 1px solid #dfe4ed; border-radius: 5px; padding: 5px; text-align: center; line-height: 400px">
<div id="map" style="height: 435px; width: 100%">{{ $t('device.device-linkage.188958-42') }}</div>
</div>
</el-col>
</el-row>
</el-form>
<!-- 选择产品 -->
<product-list ref="productList" :productId="form.productId" @productEvent="getProductData($event)" />
<product-list ref="productList" :productId="form.productId" @productEvent="getProductData($event)"/>
<sipid ref="sipidGen" :product="form" @addGenEvent="getSipIDData($event)" />
<sipid ref="sipidGen" :product="form" @addGenEvent="getSipIDData($event)"/>
</el-tab-pane>
<el-tab-pane name="runningStatus" v-if="form.deviceType !== 3">
<span slot="label">{{ $t('device.device-edit.148398-42') }}</span>
<!-- <real-time-status ref="realTimeStatus" :device="form" @statusEvent="getDeviceStatusData($event)" /> -->
<running-status ref="runningStatus" :device="form" @statusEvent="getDeviceStatusData($event)" />
<running-status ref="runningStatus" :device="form" @statusEvent="getDeviceStatusData($event)"/>
</el-tab-pane>
<el-tab-pane
name="deviceSub"
@ -191,66 +226,68 @@
lazy
>
<span slot="label">{{ $t('device.device-edit.148398-43') }}</span>
<device-sub ref="deviceSub" :device="form" />
<device-sub ref="deviceSub" :device="form"/>
</el-tab-pane>
<el-tab-pane name="sipChannel" :disabled="form.deviceId == 0" v-if="form.deviceType === 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-44') }}</span>
<channel ref="Channel" :device="form" @playerEvent="getPlayerData($event)" />
<channel ref="Channel" :device="form" @playerEvent="getPlayerData($event)"/>
</el-tab-pane>
<el-tab-pane :disabled="form.deviceId == 0" v-if="form.deviceType === 3" name="sipPlayer" lazy>
<span slot="label">{{ $t('device.device-edit.148398-45') }}</span>
<device-live-stream ref="deviceLiveStream" :device="form" />
<device-live-stream ref="deviceLiveStream" :device="form"/>
</el-tab-pane>
<el-tab-pane :disabled="form.deviceId == 0" v-if="form.deviceType === 3" name="sipVideo" lazy>
<span slot="label">{{ $t('device.device-edit.148398-46') }}</span>
<deviceVideo ref="deviceVideo" :device="form" />
<deviceVideo ref="deviceVideo" :device="form"/>
</el-tab-pane>
<el-tab-pane name="ossRecord" :disabled="form.deviceId == 0" v-if="form.deviceType === 3">
<span slot="label">{{ $t('device.device-edit.148398-79') }}</span>
<OssRecord ref="OssRecord" :device="form" />
<OssRecord ref="OssRecord" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceTimer" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-47') }}</span>
<device-timer ref="deviceTimer" :device="form" />
<device-timer ref="deviceTimer" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceUser" :disabled="form.deviceId == 0" lazy>
<span slot="label">{{ $t('device.device-edit.148398-48') }}</span>
<device-user ref="deviceUser" :device="form" @userEvent="getUserData($event)" />
<device-user ref="deviceUser" :device="form" @userEvent="getUserData($event)"/>
</el-tab-pane>
<el-tab-pane name="deviceLog" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-49') }}</span>
<device-log ref="deviceLog" :device="form" />
<device-log ref="deviceLog" :device="form"/>
</el-tab-pane>
<el-tab-pane name="alertUser" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3">
<span slot="label" v-hasPermi="['iot:device:alert:user:list']">{{ $t('device.device-edit.148398-80') }}</span>
<alert-user ref="alertUser" :device="form" />
<span slot="label" v-hasPermi="['iot:device:alert:user:list']">{{
$t('device.device-edit.148398-80')
}}</span>
<alert-user ref="alertUser" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceAlert" :disabled="form.deviceId == 0">
<span slot="label" v-hasPermi="['iot:alertLog:list']">{{ $t('device.device-edit.148398-81') }}</span>
<device-alert ref="deviceAlert" :device="form" />
<device-alert ref="deviceAlert" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceFuncLog" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-50') }}</span>
<device-func ref="deviceFuncLog" :device="form" />
<device-func ref="deviceFuncLog" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceMonitor" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3">
<span slot="label">{{ $t('device.device-edit.148398-51') }}</span>
<device-monitor ref="deviceMonitor" :device="form" />
<device-monitor ref="deviceMonitor" :device="form"/>
</el-tab-pane>
<el-tab-pane name="deviceStastic" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3">
<span slot="label">{{ $t('device.device-edit.148398-52') }}</span>
<device-statistic ref="deviceStatistic" :device="form" />
<device-statistic ref="deviceStatistic" :device="form"/>
</el-tab-pane>
<!-- 轮询任务 -->
@ -261,27 +298,28 @@
lazy
>
<span slot="label">{{ $t('device.device-edit.148398-77') }}</span>
<device-modbus-task ref="deviceModbusTask" :device="form" />
<device-modbus-task ref="deviceModbusTask" :device="form"/>
</el-tab-pane>
<el-tab-pane name="instructionParsing" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-76') }}</span>
<instruction-parsing ref="instructionParsing" :device="form" />
<instruction-parsing ref="instructionParsing" :device="form"/>
</el-tab-pane>
<el-tab-pane name="scada" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3 && isShowScada == true" lazy>
<el-tab-pane name="scada" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3 && isShowScada == true"
lazy>
<span slot="label">{{ $t('device.device-edit.148398-73') }}</span>
<device-scada ref="deviceScada" :device="form" />
<device-scada ref="deviceScada" :device="form"/>
</el-tab-pane>
<el-tab-pane name="variable" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-74') }}</span>
<device-variable ref="deviceVariable" :device="form" />
<device-variable ref="deviceVariable" :device="form"/>
</el-tab-pane>
<el-tab-pane name="inlineVideo" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
<span slot="label">{{ $t('device.device-edit.148398-75') }}</span>
<device-inline-video ref="deviceInlineVideo" :sipRelationList="form.sipRelationList" />
<device-inline-video ref="deviceInlineVideo" :sipRelationList="form.sipRelationList"/>
</el-tab-pane>
<!-- <el-tab-pane name="device04" v-if="form.deviceType !== 3" disabled>
@ -294,7 +332,8 @@
</el-tabs>
<el-empty :description="$t('device.index.105953-41')" v-if="devTotal == 0"></el-empty>
<!-- 设备配置JSON -->
<el-dialog :title="$t('device.device-linkage.188958-54')" :visible.sync="openSummary" width="700px" append-to-body>
<el-dialog :title="$t('device.device-linkage.188958-54')" :visible.sync="openSummary" width="700px"
append-to-body>
<el-row :gutter="20">
<el-col :span="14">
<div style="border: 1px solid #ccc; margin-top: -15px; height: 350px; width: 360px; overflow: scroll">
@ -304,7 +343,8 @@
</div>
</el-col>
<el-col :span="10">
<div style="border: 1px solid #ccc; width: 200px; text-align: center; margin-left: 20px; margin-top: -10px">
<div
style="border: 1px solid #ccc; width: 200px; text-align: center; margin-left: 20px; margin-top: -10px">
<vue-qr :text="qrText" :size="200"></vue-qr>
<div style="padding-bottom: 10px">{{ $t('device.device-linkage.188958-56') }}</div>
</div>
@ -321,13 +361,14 @@
<div style="padding-bottom: 10px">{{ $t('device.device-linkage.188958-56') }}</div>
</div>
</el-dialog>
<el-dialog :title="$t('device.device-linkage.188958-58')" :visible.sync="openViewMqtt" width="600px" append-to-body>
<el-dialog :title="$t('device.device-linkage.188958-58')" :visible.sync="openViewMqtt" width="600px"
append-to-body>
<el-form ref="listQuery" :model="listQuery" :rules="rules" label-width="150px">
<el-form-item label="clientId" prop="clientId">
<el-input v-model="listQuery.clientId" disabled style="width: 80%" />
<el-input v-model="listQuery.clientId" disabled style="width: 80%"/>
</el-form-item>
<el-form-item label="username" prop="username">
<el-input v-model="listQuery.username" disabled style="width: 80%" />
<el-input v-model="listQuery.username" disabled style="width: 80%"/>
</el-form-item>
<el-form-item label="passwd" prop="passwd">
<el-input clearable v-model="listQuery.passwd" disabled style="width: 80%"></el-input>
@ -337,7 +378,10 @@
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button class="btns" type="primary" @click="doCopy(2)">{{ $t('device.device-linkage.188958-59') }}</el-button>
<el-button class="btns" type="primary" @click="doCopy(2)">{{
$t('device.device-linkage.188958-59')
}}
</el-button>
<el-button @click="closeSummaryDialog">{{ $t('device.device-linkage.188958-57') }}</el-button>
</div>
</el-dialog>
@ -366,18 +410,25 @@ import deviceFuncLog from './device-functionlog';
import deviceSub from './device-sub';
import alertUser from './alert-user';
import vueQr from 'vue-qr';
import { loadBMap } from '@/utils/map.js';
import { deviceSynchronization, getDevice, addDevice, updateDevice, generatorDeviceNum, getMqttConnect } from '@/api/iot/device';
import { getDeviceUser } from '@/api/iot/deviceuser';
import { getUserId } from '@/utils/auth';
import { getDeviceRunningStatus } from '@/api/iot/device';
import { cacheJsonThingsModel } from '@/api/iot/model';
import {loadMap} from '@/utils/map.js';
import {
deviceSynchronization,
getDevice,
addDevice,
updateDevice,
generatorDeviceNum,
getMqttConnect
} from '@/api/iot/device';
import {getDeviceUser} from '@/api/iot/deviceuser';
import {getUserId} from '@/utils/auth';
import {getDeviceRunningStatus} from '@/api/iot/device';
import {cacheJsonThingsModel} from '@/api/iot/model';
import DeviceFunc from '@/views/iot/device/device-functionlog';
import { getDeviceTemp } from '@/api/iot/temp';
import {getDeviceTemp} from '@/api/iot/temp';
import RealTimeStatus from '@/views/iot/device/realTime-status';
import { clientOut } from '@/api/iot/netty';
import { deptsTreeSelect } from '@/api/system/user';
import { listDeviceShort } from '@/api/iot/device';
import {clientOut} from '@/api/iot/netty';
import {deptsTreeSelect} from '@/api/system/user';
import {listDeviceShort} from '@/api/iot/device';
import OssRecord from '@/views/iot/record/record-oss.vue';
import defaultSettings from '@/settings';
import instructionParsing from './instruction-parsing';
@ -980,7 +1031,7 @@ export default {
/**加载地图*/
loadMap() {
this.$nextTick(() => {
loadBMap().then(() => {
loadMap().then(() => {
this.getmap();
});
});
@ -1056,8 +1107,9 @@ export default {
this.loadMap();
//是否设备设置为禁用状态,则踢出设备
if (this.form.status === 2) {
const params = { clientId: this.form.serialNumber };
clientOut(params).then((res) => {});
const params = {clientId: this.form.serialNumber};
clientOut(params).then((res) => {
});
}
}
});
@ -1145,7 +1197,8 @@ export default {
});
},
// 获取选中的用户
getUserData(user) {},
getUserData(user) {
},
/**关闭物模型 */
openSummaryDialog() {
let json = {
@ -1185,20 +1238,40 @@ export default {
},
// 地图定位
getmap() {
this.map = new BMap.Map('map');
let point = null;
if (this.form.longitude != null && this.form.longitude != '' && this.form.latitude != null && this.form.latitude != '') {
point = new BMap.Point(this.form.longitude, this.form.latitude);
} else {
point = new BMap.Point(116.404, 39.915);
// 清除旧地图实例
if (this.map) {
this.map.remove();
this.map = null;
}
// 使用Leaflet和OpenStreetMap
let lat, lng;
if (this.form.latitude != null && this.form.latitude != '' && this.form.longitude != null && this.form.longitude != '') {
lat = parseFloat(this.form.latitude);
lng = parseFloat(this.form.longitude);
} else {
// 默认使用北京天安门坐标
lat = 39.915;
lng = 116.404;
}
// 创建地图
this.map = L.map('map').setView([lat, lng], 15);
// 添加高德地图图层(国内访问稳定)
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: ['1', '2', '3', '4'],
attribution: '&copy; 高德地图',
maxZoom: 18
}).addTo(this.map);
// 添加标记点
this.mk = L.marker([lat, lng]).addTo(this.map);
// 添加popup提示
if (this.form.deviceName) {
this.mk.bindPopup(`<b>${this.form.deviceName}</b><br>${this.form.networkAddress || ''}`).openPopup();
}
this.map.centerAndZoom(point, 19);
this.map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
this.map.addControl(new BMap.NavigationControl());
// 标注设备位置
this.mk = new BMap.Marker(point);
this.map.addOverlay(this.mk);
this.map.panTo(point);
},
// 生成随机字母和数字
generateNum() {
@ -1207,7 +1280,7 @@ export default {
return;
}
this.genDisabled = true;
const params = { type: this.serverType };
const params = {type: this.serverType};
generatorDeviceNum(params).then((response) => {
this.form.serialNumber = response.data;
this.genDisabled = false;
@ -1256,6 +1329,7 @@ export default {
margin-top: -10px;
margin-left: -15px;
}
.tabs-container {
display: flex;
justify-content: space-around;

View File

@ -31,19 +31,25 @@
</div>
<div class="item-content">
<div class="content-value">
<!-- 根据format选择显示HEX或ASCII格式 -->
<div v-if="format === 'Hex' && item.value" class="data-format-item">
<span class="format-label">HEX:</span>
<span class="hex-value">{{ item.value }}</span>
</div>
<div v-if="format === 'ASCII' && item.valueAscii" class="data-format-item ascii-item">
<span class="format-label">ASCII:</span>
<pre class="ascii-value">{{ item.valueAscii }}</pre>
</div>
<div v-if="format === 'JSON'" class="data-format-item">
<!-- 自动识别并展示所有可用格式 -->
<!-- JSON格式优先展示(如果是有效JSON) -->
<div v-if="isValidJson(item.valueAscii || item.value)" class="data-format-item json-item">
<span class="format-label">JSON:</span>
<pre class="json-value">{{ formatJsonValue(item.value, item.valueAscii) }}</pre>
</div>
<!-- 非JSON时展示HEX和ASCII -->
<template v-else>
<!-- HEX格式 -->
<div v-if="item.value" class="data-format-item">
<span class="format-label">HEX:</span>
<span class="hex-value">{{ item.value }}</span>
</div>
<!-- ASCII格式(如果有可读内容) -->
<div v-if="item.valueAscii && hasReadableAscii(item.valueAscii)" class="data-format-item ascii-item">
<span class="format-label">ASCII:</span>
<pre class="ascii-value">{{ item.valueAscii }}</pre>
</div>
</template>
</div>
</div>
<div class="analysis-data" v-if="item.type != 0 && item.analysis">
@ -564,6 +570,24 @@ export default {
}
// 如果都不是JSON,返回提示信息
return '数据不是有效的JSON格式';
},
/**判断是否为有效的JSON */
isValidJson(value) {
if (!value) return false;
try {
const parsed = JSON.parse(value);
// 确保是对象或数组,而不是简单值
return typeof parsed === 'object' && parsed !== null;
} catch (e) {
return false;
}
},
/**判断ASCII是否包含可读内容 */
hasReadableAscii(asciiValue) {
if (!asciiValue) return false;
// 检查是否有可打印字符(排除纯控制字符)
const printableRegex = /[\x20-\x7E]/;
return printableRegex.test(asciiValue) && asciiValue.trim().length > 0;
},
/**打开编辑框 */
openEdit() {
@ -990,6 +1014,11 @@ $right-btn-color: #1890ff;
}
.right {
display: flex;
flex-direction: column;
// 高度 = 头部36px + 内容700px + 底部64px
height: 800px;
.right-head {
font-size: 14px;
text-align: center;
@ -997,19 +1026,28 @@ $right-btn-color: #1890ff;
justify-content: center;
}
// 覆盖通用 .content 样式
.content.beautify-scroll-def {
flex: 1;
height: auto;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 0;
}
.quick-item {
width: 80%;
margin: auto;
margin: 0 auto 12px auto;
text-align: center;
line-height: 36px;
margin-top: 12px;
border: 1px solid $right-btn-color;
color: $right-btn-color;
cursor: pointer;
border-radius: 4px;
&:last-child {
margin-bottom: 12px;
&:first-child {
margin-top: 12px;
}
}