|
|
@@ -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>
|