Преглед изворни кода

[Feat 0000]喷淋控制开发

bobo04052021@163.com пре 2 дана
родитељ
комит
34983e59be
2 измењених фајлова са 83 додато и 79 уклоњено
  1. 19 0
      src/api/vent/index.ts
  2. 64 79
      src/views/vent/home/configurable/components/belt/SprayControl.vue

+ 19 - 0
src/api/vent/index.ts

@@ -4,6 +4,7 @@ import { loginCipher } from '/@/settings/encryptionSetting';
 
 enum Api {
   DEVICE_CONTROL = '/safety/ventanalyMonitorData/devicecontrol', // 开关控制
+  SPRAY_CONTROL = '/ventanaly-device/safety/ventanalyMonitorData/batchDevicecontrol', // 皮带三级防灭火批量控制
 }
 
 // Get personal center-basic settings
@@ -14,3 +15,21 @@ export const deviceControlApi = (data) => {
   data.password = encryption.encryptByAES(data.password);
   return defHttp.put({ url: Api.DEVICE_CONTROL, data });
 };
+// 皮带三级防灭火批量控制
+export const sprayControlApi = (data) => {
+  const encryption = new AesEncryption({ key: loginCipher.key, iv: loginCipher.iv });
+  // 判断传入是数组还是单个对象
+  if (Array.isArray(data)) {
+    // 批量:遍历每一条加密password
+    const encryptList = data.map((item) => {
+      return {
+        ...item,
+        password: encryption.encryptByAES(item.password),
+      };
+    });
+    return defHttp.put({ url: Api.SPRAY_CONTROL, data: encryptList });
+  } else {
+    data.password = encryption.encryptByAES(data.password);
+    return defHttp.put({ url: Api.SPRAY_CONTROL, data });
+  }
+};

+ 64 - 79
src/views/vent/home/configurable/components/belt/SprayControl.vue

@@ -4,8 +4,8 @@
     <div class="sensor-list">
       <!-- 按钮 -->
       <div class="control-bar" v-if="config.deviceType != 'dust'">
-        <a-button class="control-btn" @click="handleSpary()">启动喷淋</a-button>
-        <a-button class="control-btn" @click="handleSpary()">停止喷淋</a-button>
+        <a-button class="control-btn" @click="handleSpary('启动喷淋', 'CtrlStart')">启动喷淋</a-button>
+        <a-button class="control-btn" @click="handleSpary('停止喷淋', 'CtrlStop')">停止喷淋</a-button>
         <div class="switch-wrapper">
           <span class="text1">自动</span>
           <div class="toggle-switch" :class="{ 'is-on': isOn }" @click="toggleSwitch">
@@ -16,19 +16,18 @@
       </div>
       <!-- 循环渲染分组 -->
       <div v-for="(beltData, index) in data" :key="index" class="block-item">
-        <!-- 这里应该由data渲染,因为data数量是动态不固定的,config.config是静态的 -->
         <div v-for="(group, index) in config.config" :key="index" class="sensor-group">
-          <!-- 组标题 -->
+          <!-- 组标题复选框 多选 -->
           <div class="group-title" v-if="config.deviceType != 'dust'">
             <a-checkbox
               class="check-btn"
               :checked="selectedSids.includes(beltData.deviceID)"
-              @change="(e) => handleCheckChange(e.target.checked, beltData, beltData.deviceType)"
+              @change="(e) => handleCheckChange(e.target.checked, beltData.deviceID, beltData.deviceType)"
             >
               控制勾选
             </a-checkbox>
           </div>
-          <!-- 循环渲染组内项 -->
+          <!-- 顶部数据项 -->
           <div v-for="(item, itemIndex) in group.contentTop" :key="itemIndex" class="sensor-item" :class="getBgClass(itemIndex)">
             <div class="item-icon" :class="getBgClass(itemIndex)"></div>
             <div class="item-status">
@@ -71,7 +70,7 @@ import { computed, onMounted, ref, inject } from 'vue';
 import { getFormattedText } from '../../hooks/helper';
 import HandleModal from '/@/views/vent/monitorManager/gateMonitor/modal.vue';
 import { message } from 'ant-design-vue';
-import { deviceControlApi } from '/@/api/vent/index';
+import { sprayControlApi } from '/@/api/vent/index';
 
 const props = defineProps<{
   config: Array<{
@@ -93,72 +92,85 @@ const props = defineProps<{
     [key: string]: any;
   };
 }>();
-const modalIsShow = ref<boolean>(false); // 是否显示模态框
-const modalTitle = ref(''); // 模态框标题显示内容,根据设备操作类型决定
-const modalType = ref(''); // 模态框内容显示类型,设备操作类型
-const paramcode = ref(''); // 模态框操作代码
+const modalIsShow = ref<boolean>(false);
+const modalTitle = ref('');
+const modalType = ref('');
 const globalConfig = inject<any>('globalConfig');
-const selectType = ref('');
-// 存储选中id
+// 存储选中设备ID数组 支持多选
 const selectedSids = ref<string[]>([]);
+// 缓存选中设备类型
+const selectType = ref('');
+// 缓存当前操作指令
+const currentHandlerState = ref('');
 const isOn = ref(false);
-const openGateControlModal = (customTitle?: string) => {
-  modalIsShow.value = true;
-};
 
-// 处理勾选变化
-const handleCheckChange = (checked: boolean, data, deviceType: string) => {
+// 多选勾选逻辑
+const handleCheckChange = (checked: boolean, deviceId: string, deviceType: string) => {
   if (checked) {
-    // 勾选
-    if (!selectedSids.value.includes(data.deviceID)) {
-      selectedSids.value.push(data.deviceID);
+    if (!selectedSids.value.includes(deviceId)) {
+      selectedSids.value.push(deviceId);
       selectType.value = deviceType;
     }
   } else {
-    // 取消勾选
-    const index = selectedSids.value.indexOf(data.deviceID);
-    if (index > -1) {
-      selectedSids.value.splice(index, 1);
-      selectType.value = '';
-    }
+    const idx = selectedSids.value.indexOf(deviceId);
+    if (idx > -1) selectedSids.value.splice(idx, 1);
+    selectType.value = '';
   }
 };
+
 const getBgClass = (index) => {
   return index % 2 === 0 ? 'bg-1' : 'bg-2';
 };
 
-const handleSpary = () => {
-  openGateControlModal();
+// 打开弹窗,不再传sid,统一读取selectedSids
+const handleSpary = (title: string, flag: string) => {
+  if (selectedSids.value.length === 0) {
+    message.warning('请至少勾选一台设备');
+    return;
+  }
+  modalType.value = flag;
+  modalTitle.value = title;
+  currentHandlerState.value = flag;
+  modalIsShow.value = true;
 };
-function handleOK(passWord, handlerState, value?) {
+
+// 批量生成数组参数循环调用接口
+async function handleOK(passWord: string, handlerState: string, value?: any) {
   console.log('handleOK', passWord, handlerState, value);
   if (!passWord && !globalConfig?.simulatedPassword) {
     message.warning('请输入密码');
     return;
   }
-  let data = {
-    deviceid: selectedSids.value.join(','),
-    devicetype: selectType.value,
-    paramcode: handlerState,
-    password: passWord || globalConfig?.simulatedPassword,
-    value: value ? value : null,
-  };
-  deviceControlApi(data)
-    .then((res) => {
-      if (res.success) {
-        message.success('指令已下发成功!');
-      } else {
-        message.error(res.message);
-      }
-    })
-    .finally(() => {
-      handleCancel();
-    });
+  // 批量组装对象数组
+  const reqList = selectedSids.value.map((deviceid) => {
+    return {
+      deviceid,
+      paramcode: handlerState,
+      password: passWord || globalConfig?.simulatedPassword,
+      devicetype: selectType.value || '',
+      value: value ?? null,
+    };
+  });
+
+  try {
+    const res = await sprayControlApi(reqList);
+    if (res.success === true) {
+      message.success(res.message || '全部设备指令下发成功!');
+    } else {
+      message.error(res.message || '批量下发指令失败');
+    }
+    handleCancel();
+  } catch (error) {
+    message.error('设备批量控制请求失败,请重试');
+    handleCancel();
+  }
 }
+
 function handleCancel() {
   modalIsShow.value = false;
   modalTitle.value = '';
   modalType.value = '';
+  currentHandlerState.value = '';
 }
 
 function toggleSwitch() {
@@ -168,7 +180,7 @@ onMounted(() => {});
 </script>
 
 <style scoped lang="less">
-/* 全局面板样式 */
+/* 样式完全不变,和你原有代码一致 */
 .fire-safety-panel {
   border-radius: 8px;
   padding: 3px;
@@ -176,13 +188,11 @@ onMounted(() => {});
   font-family: 'Microsoft YaHei', sans-serif;
   padding-top: 10px;
 }
-/* 列表容器 */
 .sensor-list {
   display: flex;
   flex-direction: column;
-  gap: 15px; /* 组间距 */
+  gap: 15px;
 }
-/** 按钮 **/
 .control-bar {
   background: url('@/assets/images/beltFire/yjkf/1-2title.png');
   background-size: 100% 100%;
@@ -206,14 +216,10 @@ onMounted(() => {});
   align-items: center;
   justify-content: center;
 }
-
-/* 分组卡片 */
 .sensor-group {
   background: url('@/assets/images/beltFire/fireMonitor/2-1.png') no-repeat;
   background-size: 100% 100%;
 }
-
-/* 组标题 */
 .group-title {
   background: url('@/assets/images/beltFire/fireMonitor/2-2.png') no-repeat;
   background-size: 35% 100%;
@@ -229,8 +235,6 @@ onMounted(() => {});
     font-size: 12px;
   }
 }
-
-/* 列表项 */
 .sensor-item {
   height: 30px;
   background-size: 100% 100%;
@@ -257,7 +261,6 @@ onMounted(() => {});
 .item-icon.bg-1 {
   background: url('@/assets/images/beltFire/yjkf/1-3area.svg') no-repeat;
 }
-
 .item-icon.bg-2 {
   background: url('@/assets/images/beltFire/yjkf/1-4wz.svg') no-repeat;
 }
@@ -274,19 +277,16 @@ onMounted(() => {});
   width: calc(50% - 5px);
   color: #c0d0e0;
 }
-
 .dataInfo-item:nth-child(1),
 .dataInfo-item:nth-child(4) {
   background: url('@/assets/images/beltFire/yjkf/1-7.png') no-repeat;
   background-size: 100% 100%;
 }
-
 .dataInfo-item:nth-child(2),
 .dataInfo-item:nth-child(3) {
   background: url('@/assets/images/beltFire/yjkf/1-8.png') no-repeat;
   background-size: 100% 100%;
 }
-
 .item-status {
   width: 100%;
   display: flex;
@@ -307,7 +307,6 @@ onMounted(() => {});
     font-family: 'douyuFont';
   }
 }
-
 .item-content {
   flex: 1;
   display: flex;
@@ -316,7 +315,6 @@ onMounted(() => {});
     color: #fff;
   }
 }
-
 .item-value {
   margin-right: 10px;
   font-size: 12px;
@@ -326,8 +324,6 @@ onMounted(() => {});
   color: #ffff;
   font-size: 11px;
 }
-
-/* 状态指示灯 */
 .status-dot {
   display: inline-block;
   width: 10px;
@@ -337,23 +333,17 @@ onMounted(() => {});
   margin-top: 10px;
   animation: pulse 2s infinite;
   background: #3a3a3a;
-  // position: absolute;
-  // left: 0;
-  // top: 50%;
   transform: translateY(-50%);
   box-shadow: 0 0 6px 2px rgba(90, 90, 90, 0.6);
   &.status-normal {
     background-color: #00ff00;
     box-shadow: 0 0 6px 2px rgba(104, 255, 45, 0.6);
   }
-
   &.status-danger {
     background-color: #ff4d4d;
     box-shadow: 0 0 6px 2px rgb(255, 0, 0);
   }
 }
-
-/* 动画效果 */
 @keyframes pulse {
   0% {
     transform: scale(1);
@@ -368,7 +358,6 @@ onMounted(() => {});
     opacity: 1;
   }
 }
-
 @keyframes flash {
   0% {
     opacity: 1;
@@ -393,8 +382,7 @@ onMounted(() => {});
   border: 1px solid #15567f;
   box-sizing: border-box;
   margin-top: 3px;
-  background: #0f2840; /* 加背景更美观 */
-
+  background: #0f2840;
   .slider {
     position: absolute;
     top: 3px;
@@ -404,11 +392,8 @@ onMounted(() => {});
     background-color: #ffffff;
     transition: all 0.3s ease;
     z-index: 1;
-
-    /* 默认关闭:左边 */
     left: 3px;
   }
-  /* 开启:滑块去右边 */
   &.is-on {
     .slider {
       left: calc(100% - 15px);
@@ -429,4 +414,4 @@ onMounted(() => {});
   justify-content: center;
   gap: 8px;
 }
-</style>
+</style>