FenXiNspBackControl/src/views/appmana/deploy/components/ControlModal.vue

403 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<template>
<a-modal title="球阀控制" :width="width" :body-style="bodystyle" :visible="visible" @ok="handleOk" @cancel="handleCancel" footer="">
<a-descriptions
title="运行状态"
layout="vertical"
bordered
:labelStyle="{ fontWeight: 'bold', textAlign: 'center' }"
:contentStyle="{ textAlign: 'center' }"
>
<a-descriptions-item :label="item.runName" v-for="item in formState.runList" :key="item.id">
<span style="color: red" v-if="item.value == '0'">停止</span>
<span style="color: green" v-else-if="item.value == '1'">运行</span>
<span style="color: orange" v-else>待同步</span>
</a-descriptions-item>
</a-descriptions>
<a-descriptions title="" layout="vertical" bordered style="margin-top: 20px">
<a-descriptions-item label="控制项">
<a-form :model="formState" :label-col="labelCol" :wrapper-col="wrapperCol">
<!-- <a-form-item label="上报时间">-->
<!-- <a-input v-model:value="formState.dataTime" disabled="true" />-->
<!-- </a-form-item>-->
<div class="card-container">
<a-row :gutter="16" type="flex">
<a-col :span="8" v-for="group in formState.moduleList" :key="group.id" class="card-col">
<a-card :title="group.groupName" :bordered="true">
<!-- <template #extra><a href="#">一键启动</a></template>-->
<a-form-item :label="item.relayName" v-for="item in group.relayList" :key="item.id">
<a-switch
v-model:checked="item.value"
checked-children="打开"
un-checked-children="关闭"
:checked-value="'1'"
:un-checked-value="'0'"
@change="(checked) => switchChange(checked, item)"
/>
</a-form-item>
</a-card>
</a-col>
</a-row>
</div>
</a-form>
</a-descriptions-item>
</a-descriptions>
</a-modal>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, reactive, nextTick, defineExpose } from 'vue';
import type { UnwrapRef } from 'vue';
import { getValveStatus, sendDeviceCmd, getRelayList } from '../SurvDeviceDeploy.api';
import { message } from 'ant-design-vue';
import { usePageMqtt } from '/@/components/mqtt/usePageMqtt';
// MQTT 配置
const mqttOptions = {
brokerUrl: import.meta.env.VITE_MQTT_BROKER_URL || 'ws://localhost:8083/mqtt',
username: import.meta.env.VITE_MQTT_USERNAME || 'admin',
password: import.meta.env.VITE_MQTT_PASSWORD || 'admin123',
keepalive: 60,
reconnectPeriod: 5000,
};
// 使用页面级 MQTT
const { isConnected, isConnecting, error, connectAndSubscribe, publish, disconnect } = usePageMqtt(mqttOptions);
// 设备数据
const deviceData = reactive({
temperature: 0,
humidity: 0,
status: 'offline',
lastUpdate: 0,
});
// 消息日志
interface LogEntry {
id: number;
topic: string;
message: string;
timestamp: number;
}
const logs = ref<LogEntry[]>([]);
let logId = 0;
// 格式化时间
const formatTime = (timestamp: number) => {
if (!timestamp) return '--:--:--';
return new Date(timestamp).toLocaleTimeString();
};
// 添加日志
const addLog = (topic: string, message: string) => {
logs.value.unshift({
id: logId++,
topic,
message: typeof message === 'object' ? JSON.stringify(message) : message,
timestamp: Date.now(),
});
// 保留最近100条日志
if (logs.value.length > 100) {
logs.value.pop();
}
};
// 处理接收到的消息
const handleMessage = (payload: string, topic: string) => {
// console.log('处理消息:', topic, payload);
try {
const data = JSON.parse(payload);
// 添加日志
addLog(topic, payload);
if (data.rw_prot !== undefined) {
if (data.rw_prot.r_data !== undefined) {
// 先构建源数据的 Map
const sourceMap = data.rw_prot.r_data.reduce((map, item) => {
map[item.name] = item.value;
return map;
}, {});
// 然后遍历目标数组赋值
// 1.状态部分
formState.runList.forEach((target) => {
const source = sourceMap[target.runKey];
if (source) {
Object.assign(target, {
value: source,
});
}
});
// 2.控制项部分
formState.moduleList.forEach((target) => {
if (target.relayList) {
// 遍历内层数组
target.relayList.forEach((member) => {
const source = sourceMap[member.relayKey];
if (source) {
Object.assign(member, {
value: source,
});
}
});
}
});
}
console.log('check-----', formState.moduleList);
deviceData.temperature = data.temperature;
}
} catch (e) {
// 非 JSON 格式,直接作为文本处理
addLog(topic, payload);
}
};
// 发送设备控制指令
const sendCommand = async (topic: string, command: string) => {
if (!isConnected.value) {
addLog('system', 'MQTT未连接无法发送指令');
return;
}
// const payload = {
// command,
// timestamp: Date.now(),
// requestId: `${Date.now()}-${Math.random().toString(36).substring(2, 10)}`,
// };
const payload = command;
console.log('sendCommand===', topic, command);
try {
await publish(topic, payload);
addLog(topic, `发送指令: ${command}`);
} catch (err) {
console.error('发送指令失败:', err);
addLog(topic, `指令发送失败: ${command}`);
}
};
const bodystyle = {
height: '1000px',
margin: '20px',
};
interface FormState {
deployCode: string;
delivery1: boolean;
delivery2: boolean;
delivery3: boolean;
dataTime: string;
moduleList: [];
runList: [];
deployInfo: {};
}
const formState: UnwrapRef<FormState> = reactive({
deployCode: '',
delivery1: false,
delivery2: false,
dataTime: '',
moduleList: [],
runList: [],
deployInfo: {},
});
const labelCol = { style: { width: '150px' } };
const wrapperCol = { span: 14 };
const title = ref<string>('');
const width = ref<number>(1000);
const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const registerForm = ref();
const emit = defineEmits(['register', 'success']);
const key = 'updatable';
const openMessage = () => {
message.loading({ content: '发送指令中', key, duration: 7 });
};
// 页面进入时连接 MQTT
onMounted(() => {});
// 页面关闭时断开连接
onUnmounted(() => {
console.log('页面卸载,断开 MQTT...');
disconnect();
});
function switchChange(checked, relay) {
openMessage();
let ops = checked;
// if (checked === true) {
// ops = '1';
// } else if (checked === false) {
// ops = '0';
// }
// let parmas = { relayId: relay.id, ops: ops };
// sendDeviceCmd(parmas).then((res) => {
// if (res.code == 500) {
// //操作失败
// formState[relay.id] = false;
// message.error({ content: res.message, key, duration: 2 });
// }
// });
if ('1' == ops) {
sendCommand(formState.deployInfo.sendTopic, relay.registerCmdOn);
} else if ('0' == ops) {
sendCommand(formState.deployInfo.sendTopic, relay.registerCmdOff);
}
}
function on1SwitchChange(checked) {
openMessage();
if (checked === true) {
sendDeviceCmd({ deployCode: formState.deployCode, os: '01', valveCode: '01' }).then((res) => {
if (res.code == 500) {
//操作失败
formState.delivery1 = false;
message.error({ content: res.message, key, duration: 2 });
}
});
} else if (checked === false) {
sendDeviceCmd({ deployCode: formState.deployCode, os: '00', valveCode: '01' }).then((res) => {
if (res.code == 500) {
//操作失败
formState.delivery1 = true;
message.error({ content: res.message, key, duration: 2 });
}
});
}
}
function on2SwitchChange(checked) {
openMessage();
if (checked === true) {
sendDeviceCmd({ deployCode: formState.deployCode, os: '01', valveCode: '02' }).then((res) => {
if (res.code == 500) {
formState.delivery2 = false;
message.error({ content: res.message, key, duration: 2 });
}
});
} else if (checked === false) {
sendDeviceCmd({ deployCode: formState.deployCode, os: '00', valveCode: '02' }).then((res) => {
if (res.code == 500) {
formState.delivery2 = true;
message.error({ content: res.message, key, duration: 2 });
}
});
}
}
/**
* 控制
*/
function showup(record) {
title.value = '球阀控制';
visible.value = true;
formState.moduleList = [];
formState.runList = [];
formState.dataTime = '';
formState.deployInfo = record;
console.log(record);
console.log('页面加载,连接 MQTT...', formState.deployInfo.receiveTopic);
// 需要订阅的主题
const subscribeTopic = formState.deployInfo.receiveTopic; // 传感器数据主题
// 连接并订阅
connectAndSubscribe(subscribeTopic, handleMessage);
getRelayList({ deployId: record.deployId }).then((res) => {
if (res.code == 200) {
formState.moduleList = res.result.moduleList;
formState.runList = res.result.runStatus;
formState.dataTime = res.result.dataTime;
} else {
}
});
}
// getValveStatus({ deployCode: record.deployCode }).then((res) => {
// if (res.code == 200) {
// formState.dataTime=res.result.dataTime
// formState.deployCode = record.deployCode
// if(res.result.firstValveStatus == "01"){
// formState.delivery1=true
// }else{
// formState.delivery1=false
// }
// if(res.result.secondValveStatus == "01"){
// formState.delivery2=true
// }else{
// formState.delivery2=false
// }
// } else {
// }
// })
/**
* 编辑
* @param record
*/
function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑';
visible.value = true;
nextTick(() => {
registerForm.value.edit(record);
});
}
/**
* 确定按钮点击事件
*/
function handleOk() {
registerForm.value.submitForm();
}
/**
* form保存回调事件
*/
function submitCallback() {
handleCancel();
emit('success');
}
/**
* 取消按钮回调事件
*/
function handleCancel() {
visible.value = false;
disconnect();
}
defineExpose({
showup,
edit,
disableSubmit,
});
</script>
<style>
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
.card-container {
width: 100%;
}
.card-col {
flex: 0 0 33.333333%; /* 每个卡片占1/3宽度 */
max-width: 33.333333%; /* 最大宽度也是1/3 */
margin-bottom: 16px; /* 行间距 */
}
/* 可选:固定卡片高度统一 */
:deep(.ant-card) {
height: 100%;
min-height: 180px;
}
</style>