diff --git a/public/config.js b/public/config.js index 901178a..5119a4c 100644 --- a/public/config.js +++ b/public/config.js @@ -3,4 +3,17 @@ * @Date: 2025-09-26 16:33:30 * @Description: */ -window.CustomMqttUrl = 'ws://111.229.30.246:4199/mqtt' \ No newline at end of file +window.CustomMqttUrl = 'ws://111.229.30.246:4199/mqtt' + +// 区域配置 +window.ShiYanRegionConfig = [ + { area: 1, x: 0, y: 420, name: "9栋3单元", code: '0101' }, + { area: 2, x: 0, y: 220, name: "1栋2单元", code: '0102' }, + { area: 3, x: 450, y: 410, name: "2栋1单元", code: '0201' }, + { area: 4, x: 450, y: 200, name: "2栋2单元", code: '0202' }, + { area: 5, x: 1100, y: 430, name: "3栋1单元", code: '0301' }, + { area: 6, x: 1100, y: 220, name: "3栋2单元", code: '0302' }, + { area: 7, x: 100, y: 0, name: "4栋1单元", code: '0401' }, + { area: 8, x: 800, y: 0, name: "4栋2单元", code: '0402' }, + { area: 9, x: 200, y: 600, name: "广场", code: 'ground' }, +] \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index f25df1f..1333e19 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -602,48 +602,48 @@ export const constantRoutes = [ // ] // }, - { - name: "Security", - alwaysShow: true, - component: Layout, - hidden: false, - name: "Security", - path: "/Security", - redirect: "noRedirect", - meta: { - icon: "afxt", - link: null, - noCache: false, - title: "安防系统", - }, - children: [ - { - component: () => import('@/views/Security/videoSurveillance'), - hidden: false, - name: "videoSurveillance", - path: "videoSurveillance", - meta: { - icon: "spjk", - link: null, - noCache: false, - title: "视频监控", - }, - }, - { - component: () => import('@/views/Security/doorControl'), - hidden: false, - name: "doorControl", - path: "doorControl", - meta: { - icon: "afmj", - link: null, - noCache: false, - title: "门禁", - }, - }, - ] + // { + // name: "Security", + // alwaysShow: true, + // component: Layout, + // hidden: false, + // name: "Security", + // path: "/Security", + // redirect: "noRedirect", + // meta: { + // icon: "afxt", + // link: null, + // noCache: false, + // title: "安防系统", + // }, + // children: [ + // { + // component: () => import('@/views/Security/videoSurveillance'), + // hidden: false, + // name: "videoSurveillance", + // path: "videoSurveillance", + // meta: { + // icon: "spjk", + // link: null, + // noCache: false, + // title: "视频监控", + // }, + // }, + // { + // component: () => import('@/views/Security/doorControl'), + // hidden: false, + // name: "doorControl", + // path: "doorControl", + // meta: { + // icon: "afmj", + // link: null, + // noCache: false, + // title: "门禁", + // }, + // }, + // ] - }, + // }, // { // path: '', // component: Layout, diff --git a/src/views/components/dialogEle.vue b/src/views/components/dialogEle.vue index b3c11a8..0cb7964 100644 --- a/src/views/components/dialogEle.vue +++ b/src/views/components/dialogEle.vue @@ -68,9 +68,32 @@
{{ item.IotAgreementHostPoint.point_name }}:
- {{ formatStatusValue(item.IotAgreementHostPoint.status, props.TypeId) }} {{ item.IotAgreementHostPoint.unit }} + + {{ formatStatusValue(item.IotAgreementHostPoint.status, props.TypeId) }} {{ item.IotAgreementHostPoint.unit }} +
+
+
+ + + 设定 + +
+
@@ -139,30 +162,6 @@ 停止 -
-
{{ item.IotAgreementHostPoint.point_name }}:
-
- - - 设定 - -
-
@@ -233,22 +232,57 @@ const loading = ref(false); let timmer = null; const toggle = ref(false); +// 存储输入框的独立值,避免被 MQTT 更新影响 +// 使用 point_id 作为 key,存储每个输入框的值 +const temperatureInputValues = reactive({}); + watch(dialogVisible, (newVal, oldVal) => { if (newVal) { nextTick(() => { getDeviceInfo(props.id); + // 对话框打开时,初始化输入框的值 + if (EquipmentData && EquipmentData.length > 0) { + initTemperatureInputValues(EquipmentData); + } }); } else { clearTimeout(timmer); } }); +// 初始化输入框的值(从初始数据中提取) +const initTemperatureInputValues = (equipmentData) => { + if (!equipmentData || !Array.isArray(equipmentData)) return; + + equipmentData.forEach((equipment) => { + if (equipment.Data && Array.isArray(equipment.Data)) { + equipment.Data.forEach((item) => { + const point = item.IotAgreementHostPoint; + // 如果是设定温度的点位,初始化输入框的值 + if (point && + props.TypeId === 82 && + point.point_name && + point.point_name.includes('设定温度') && + point.register_type === '保持寄存器') { + const pointId = point.id; + // 只在第一次初始化时设置值,避免覆盖用户已输入的值 + if (!(pointId in temperatureInputValues)) { + temperatureInputValues[pointId] = point.status || 0; + } + } + }); + } + }); +}; + // 监听 props.extra 的变化,确保数据实时更新 watch(() => props.extra, (newExtra) => { if (newExtra && dialogVisible.value) { // 如果 EquipmentData 为空或结构不匹配,重新初始化 if (EquipmentData.length === 0 || !EquipmentData[0] || EquipmentData[0].DeviceId !== newExtra.DeviceId) { EquipmentData = [newExtra]; + // 初始化输入框的值 + initTemperatureInputValues(EquipmentData); } else { // 同步更新 Data 数组中的数据 if (newExtra.Data && Array.isArray(newExtra.Data) && EquipmentData[0].Data) { @@ -309,6 +343,8 @@ onMounted(async () => { nextTick(() => { EquipmentData = [props.extra]; console.log(EquipmentData, "===> EquipmentData"); + // 初始化输入框的值 + initTemperatureInputValues(EquipmentData); isRefresh.value = true; }); }); @@ -437,6 +473,20 @@ const getControlTopic = () => { return window.getControlTopic ? window.getControlTopic() : 'shiyan/control'; }; +// 获取本地缓存的 userId +const getUserId = () => { + try { + const userInfo = localStorage.getItem('APP_USER_INFO'); + if (userInfo) { + const user = JSON.parse(userInfo); + return user?.userId || user?.user_id || user?.id || ''; + } + } catch (err) { + console.warn('读取用户信息失败', err); + } + return ''; +}; + // topicControl 方法:通过 MQTT 发送控制指令 const topicControl = async (pointData = null, Value = true) => { const client = getMqttClient(); @@ -448,11 +498,15 @@ const topicControl = async (pointData = null, Value = true) => { // 生成 GUID const guid = generateGuid(); + // 获取 userId + const userId = getUserId(); + // 发送控制指令,如果未传递参数,使用默认值 const controlData = { id: guid, point_id: pointData.IotAgreementHostPoint.id, value: Value, //线圈寄存器 true false 保持寄存器 1 0 + userId: userId, }; // 发布消息到 control_topic @@ -479,11 +533,15 @@ const spTopicControl = async (pointData = null) => { // 生成 GUID(两次发送使用同一个 GUID) const guid = generateGuid(); + // 获取 userId + const userId = getUserId(); + // 第一次发送 true const controlDataTrue = { id: guid, point_id: pointId, value: true, + userId: userId, }; await client.mqttPublish(controlTopic, controlDataTrue, { qos: 1 }); @@ -502,6 +560,7 @@ const spTopicControl = async (pointData = null) => { id: guid, point_id: pointId, value: false, + userId: userId, }; await clientAgain.mqttPublish(controlTopic, controlDataFalse, { qos: 1 }); diff --git a/src/views/equipment/wastewater.vue b/src/views/equipment/wastewater.vue index 980193c..67f148e 100644 --- a/src/views/equipment/wastewater.vue +++ b/src/views/equipment/wastewater.vue @@ -831,6 +831,20 @@ export default { : "shiyan_control"; }; + // 获取本地缓存的 userId + const getUserId = () => { + try { + const userInfo = localStorage.getItem('APP_USER_INFO'); + if (userInfo) { + const user = JSON.parse(userInfo); + return user?.userId || user?.user_id || user?.id || ''; + } + } catch (err) { + console.warn('读取用户信息失败', err); + } + return ''; + }; + // topicControl 方法:通过 MQTT 发送控制指令 const topicControl = async (pointData = null, Value = true, skipConfirm = false) => { console.log(Value,"Value"); @@ -855,11 +869,15 @@ export default { // 生成 GUID const guid = generateGuid(); + // 获取 userId + const userId = getUserId(); + // 发送控制指令 const controlData = { id: guid, point_id: pointData.IotAgreementHostPoint.id, value: Value, //线圈寄存器 true false 保持寄存器 1 0 或数值 + userId: userId, }; // 发布消息到 control_topic @@ -986,12 +1004,16 @@ export default { // 生成 GUID(两次发送使用同一个 GUID) const guid = generateGuid(); - + + // 获取 userId + const userId = getUserId(); + // 第一次发送 true const controlDataTrue = { id: guid, point_id: pointId, value: true, + userId: userId, }; await client.mqttPublish(controlTopic, controlDataTrue, { qos: 1 }); @@ -1010,6 +1032,7 @@ export default { id: guid, point_id: pointId, value: false, + userId: userId, }; await clientAgain.mqttPublish(controlTopic, controlDataFalse, { diff --git a/src/views/linkConfig/sensorConfig.vue b/src/views/linkConfig/sensorConfig.vue index 9ee5fdc..b20dacd 100644 --- a/src/views/linkConfig/sensorConfig.vue +++ b/src/views/linkConfig/sensorConfig.vue @@ -22,11 +22,11 @@ :model="sensor" :rules="thresholdRules" ref="formRefs" - label-width="100px" + label-width="80px" class="threshold-form" > - + - + + + + + + @@ -302,4 +321,8 @@ onMounted(() => { background-color: #66b1ff; border-color: #66b1ff; } + +::v-deep .sp-label .el-form-item__label { + width: 120px !important; +} diff --git a/src/views/plan/shiyan.vue b/src/views/plan/shiyan.vue index 621f787..ab89f9c 100644 --- a/src/views/plan/shiyan.vue +++ b/src/views/plan/shiyan.vue @@ -148,7 +148,7 @@ import { getArea } from "@/api/area.js"; import { getDeviceType, getDevices, updateDevice } from "@/api/device.js"; import { ElMessage, ElNotification } from "element-plus"; import dialogEle from "./../components/dialogEle.vue"; -import { ref, onMounted, onUnmounted, reactive, nextTick, provide } from "vue"; +import { ref, onMounted, onUnmounted, reactive, nextTick, provide, watch } from "vue"; import { useRoute } from "vue-router"; import { shiyan_typesDictionary } from "../../utils/equipmentType"; //设备类型字典 import videoEle from "./../components/videoEle.vue"; @@ -301,17 +301,20 @@ const deviceList = ref([]); let StandardC = 948; //设置标准系数 初始化数据电脑屏幕的高度 let BL = ref(0); //标准系数 / 当前屏幕高度 计算出的系数比例 保证在各高度设备上展示正常 -const region = ref([ - { area: 1, x: 0, x_b: 0, y: 450, name: "1栋1单元" }, - { area: 2, x: 0, x_b: 0, y: 240, name: "1栋2单元" }, - { area: 3, x: 450, x_b: 0, y: 410, name: "2栋1单元" }, - { area: 4, x: 450, x_b: 0, y: 200, name: "2栋2单元" }, - { area: 5, x: 1100, x_b: 0, y: 430, name: "3栋1单元" }, - { area: 6, x: 1100, x_b: 0, y: 220, name: "3栋2单元" }, - { area: 7, x: 100, x_b: 0, y: 0, name: "4栋1单元" }, - { area: 8, x: 800, x_b: 0, y: 0, name: "4栋2单元" }, - { area: 9, x: 200, x_b: 0, y: 600, name: "广场" }, -]); +// 从配置文件中读取区域配置,如果没有则使用默认值 +const region = ref( + window.ShiYanRegionConfig || [ + { area: 1, x: 0, y: 450, name: "1栋1单元", code: '0101' }, + { area: 2, x: 0, y: 240, name: "1栋2单元", code: '0102' }, + { area: 3, x: 450, y: 410, name: "2栋1单元", code: '0201' }, + { area: 4, x: 450, y: 200, name: "2栋2单元", code: '0202' }, + { area: 5, x: 1100, y: 430, name: "3栋1单元", code: '0301' }, + { area: 6, x: 1100, y: 220, name: "3栋2单元", code: '0302' }, + { area: 7, x: 100, y: 0, name: "4栋1单元", code: '0401' }, + { area: 8, x: 800, y: 0, name: "4栋2单元", code: '0402' }, + { area: 9, x: 200, y: 600, name: "广场", code: 'ground' }, + ] +); // 背景图信息(保持不变) const imgAspectRatio = ref(1); @@ -699,17 +702,23 @@ const editFun = () => { } }; +// 根据 code 从 region 配置中获取 label 的辅助函数 +const getLabelByCode = (code) => { + const matchedRegion = region.value.find((r) => r.code === code); + return matchedRegion ? matchedRegion.name : ''; +}; + const rects = reactive([ { code: "0401", y: 10, - label: "4栋1单元", + label: getLabelByCode("0401"), height: 220, }, { code: "0402", y: 10, - label: "4栋2单元", + label: getLabelByCode("0402"), height: 220, }, ]); @@ -718,17 +727,17 @@ const rects1 = reactive([ { code: "0102", y: 240, - label: "1栋2单元", + label: getLabelByCode("0102"), }, { code: "0202", y: 240, - label: "2栋2单元", + label: getLabelByCode("0202"), }, { code: "0302", y: 240, - label: "3栋2单元", + label: getLabelByCode("0302"), }, ]); @@ -736,17 +745,17 @@ const rects2 = reactive([ { code: "0101", y: 430, - label: "1栋1单元", + label: getLabelByCode("0101"), }, { code: "0201", y: 430, - label: "2栋1单元", + label: getLabelByCode("0201"), }, { code: "0301", y: 430, - label: "3栋1单元", + label: getLabelByCode("0301"), }, ]); @@ -754,10 +763,35 @@ const rect_ground = reactive([ { code: "ground", y: 620, - label: "广场", + label: getLabelByCode("ground"), }, ]); +// 监听 region 变化,更新所有 rects 的 label +const updateRectsLabels = () => { + rects.forEach((rect) => { + rect.label = getLabelByCode(rect.code); + }); + rects1.forEach((rect) => { + rect.label = getLabelByCode(rect.code); + }); + rects2.forEach((rect) => { + rect.label = getLabelByCode(rect.code); + }); + rect_ground.forEach((rect) => { + rect.label = getLabelByCode(rect.code); + }); +}; + +// 监听 region 变化 +watch( + () => region.value, + () => { + updateRectsLabels(); + }, + { deep: true, immediate: true } +); + let s_w = 0.8; const getDeviceTypeFun = async () => { @@ -791,21 +825,17 @@ const getDevicesFun = async () => { devicesByRegion[regionName].push(item); }); - // 2) 映射设备列表,根据 region_name 匹配 region 并设置坐标 deviceList.value = list.map((item) => { const regionName = item.region_name || ""; - // 在 region 中查找匹配的 name const matchedRegion = region.value.find((r) => r.name === regionName); let finalX = item.x ?? 300; let finalY = item.y ?? 300; if (matchedRegion) { - // 找到匹配的 region,使用 region 的 x 和 y 作为基础坐标 finalX = matchedRegion.x; finalY = matchedRegion.y + 50; - // 计算当前设备在同一 region 内的索引 const regionDevices = devicesByRegion[regionName] || []; const deviceIndex = regionDevices.findIndex((d) => { // 使用唯一标识来匹配,优先使用 DeviceId,如果没有则使用其他唯一字段 @@ -1743,19 +1773,19 @@ const handleMouseMove = (e) => { let newActiveMarker = { target: "" }; points.forEach((point) => { - if (!point.path) { - point.path = new Path2D(); - point.path.arc( - point.x * BL.value, - point.y * BL.value, - pointSize.value * BL.value, - 0, - Math.PI * 2 + // 使用距离计算而不是路径检测,更精确且不受 scale 影响 + if (point.TypeId == SELECT_ACTION_TYPE.value || SELECT_ACTION_TYPE.value == "") { + const pointX = point.x * BL.value; + const pointY = point.y * BL.value; + // 计算检测半径(与绘制时保持一致:pointSize * 0.2 * BL) + // 绘制时半径是 (pointSize * scale * 0.2 * BL) / scale = pointSize * 0.2 * BL + const detectRadius = pointSize.value * 0.2 * BL.value; + const distance = Math.sqrt( + Math.pow(originalX - pointX, 2) + Math.pow(originalY - pointY, 2) ); - } - - if (ctx.value.isPointInPath(point.path, originalX, originalY)) { - newActiveMarker = point; + if (distance <= detectRadius) { + newActiveMarker = point; + } } }); @@ -1788,25 +1818,23 @@ const startDrag = (e) => { if (isEdit.value) { points.forEach((point) => { - if (!point.path) { - point.path = new Path2D(); - point.path.arc( - point.x * BL.value, - point.y * BL.value, - pointSize.value * BL.value, - 0, - Math.PI * 2 + if (point.TypeId == SELECT_ACTION_TYPE.value || SELECT_ACTION_TYPE.value == "") { + const pointX = point.x * BL.value; + const pointY = point.y * BL.value; + // 计算检测半径(与绘制时保持一致:pointSize * 0.2 * BL) + const detectRadius = pointSize.value * 0.2 * BL.value; + const distance = Math.sqrt( + Math.pow(originalX - pointX, 2) + Math.pow(originalY - pointY, 2) ); - } - - if (ctx.value.isPointInPath(point.path, originalX, originalY)) { - dragging.value = "point"; - currentPoint = point; - startPointX.value = point.x * BL.value; - startPointY.value = point.y * BL.value; - startDragX.value = mouseX; - startDragY.value = mouseY; - return; + if (distance <= detectRadius) { + dragging.value = "point"; + currentPoint = point; + startPointX.value = point.x * BL.value; + startPointY.value = point.y * BL.value; + startDragX.value = mouseX; + startDragY.value = mouseY; + return; + } } }); } @@ -1846,8 +1874,9 @@ const endDrag = (type) => { currentPoint.y * BL.value, pointSize.value * BL.value, 0, - Math.PI * 2 + 2 * Math.PI ); + currentPoint.path.closePath(); } }; @@ -1915,26 +1944,24 @@ const handleCanvasClick = (e) => { let clickedPoint = false; points.forEach((point) => { - if (!point.path) { - point.path = new Path2D(); - point.path.arc( - point.x * BL.value, - point.y * BL.value, - pointSize.value * BL.value, - 0, - Math.PI * 2 + if (point.TypeId == SELECT_ACTION_TYPE.value || SELECT_ACTION_TYPE.value == "") { + const pointX = point.x * BL.value; + const pointY = point.y * BL.value; + // 计算检测半径(与绘制时保持一致:pointSize * 0.2 * BL) + const detectRadius = pointSize.value * 0.2 * BL.value; + const distance = Math.sqrt( + Math.pow(originalX - pointX, 2) + Math.pow(originalY - pointY, 2) ); - } - - if (ctx.value.isPointInPath(point.path, originalX, originalY)) { - clickedPoint = true; - if (point.target == "camera") { - selectPoint(point); - selectedPointId.value = null; - } else { - // currentPointX.value = point.x; - // currentPointY.value = point.y; - selectPoint(point); + if (distance <= detectRadius) { + clickedPoint = true; + if (point.target == "camera") { + selectPoint(point); + selectedPointId.value = null; + } else { + // currentPointX.value = point.x; + // currentPointY.value = point.y; + selectPoint(point); + } } } }); diff --git a/vite.config.js b/vite.config.js index 0272c3c..17fa5b0 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,7 +7,7 @@ import { defineConfig, loadEnv } from 'vite' import path from 'path' import createVitePlugins from './vite/plugins' -const baseUrl = 'http://325ccefb.r3.cpolar.top' +const baseUrl = 'http://172.16.1.165:18080' // https://vitejs.dev/config/