|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <el-row :gutter="0" class="overview-row">
|
|
|
+ <el-row :gutter="0" class="overview-row">
|
|
|
<el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
|
|
|
<div class="overview-left">
|
|
|
<div class="overview-left-title">
|
|
|
@@ -8,10 +8,10 @@
|
|
|
<div class="overview-left-content">
|
|
|
<ul v-loading="alarm_loading">
|
|
|
<li v-for="(item, index) in alertData" :key="index">
|
|
|
- <div class="content-item" :class="getColor(item.switch_type)">
|
|
|
+ <div class="content-item" :class="getColor(item.switch_type??'')">
|
|
|
<IconAlarm />
|
|
|
<span style="width:120px; margin-left: 15px;">{{ item.crane_name }}</span>
|
|
|
- <span style="width: 2px;height: 60%;margin: 0px 30px;" :class="getColorSpan(item.switch_type)" />
|
|
|
+ <span style="width: 2px;height: 60%;margin: 0px 30px;" :class="getColorSpan(item.switch_type??'')" />
|
|
|
<span>{{ item.msg }}</span>
|
|
|
</div>
|
|
|
</li>
|
|
|
@@ -21,7 +21,6 @@
|
|
|
<span>当前无数据</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
</div>
|
|
|
</el-col>
|
|
|
<el-col :xs="16" :sm="16" :md="16" :lg="16" :xl="16">
|
|
|
@@ -32,15 +31,15 @@
|
|
|
<div class="overview-right-content">
|
|
|
<pro-table :height="tabHeight" :loading="tab_loading" :data="craneData" :config="tableConfig">
|
|
|
<template #default="{ row, label }">
|
|
|
- <div v-if="label === '操作'" style="width: 100%;height: 100%;display: flex;justify-content: center;">
|
|
|
+ <div v-if="label === '操作'" style="width:100%;height:100%;display: flex;justify-content: center;">
|
|
|
<div class="button-container" @click="handleClick(row)">
|
|
|
查看
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-if="label === '在线状态'">
|
|
|
- <el-tag effect="dark" v-if="row.Status != '在线' && row.Status != '离线'" type="warning">连接中</el-tag>
|
|
|
- <el-tag effect="dark" v-if="row.Status === '在线'" type="success">在线</el-tag>
|
|
|
- <el-tag effect="dark" v-if="row.Status === '离线'" type="danger">离线</el-tag>
|
|
|
+ <el-tag effect="dark" v-if="row.online_status != '在线' && row.online_status != '离线'" type="warning">连接中</el-tag>
|
|
|
+ <el-tag effect="dark" v-if="row.online_status === '在线'" type="success">在线</el-tag>
|
|
|
+ <el-tag effect="dark" v-if="row.online_status === '离线'" type="danger">离线</el-tag>
|
|
|
</div>
|
|
|
</template>
|
|
|
</pro-table>
|
|
|
@@ -49,21 +48,50 @@
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</template>
|
|
|
-
|
|
|
+
|
|
|
<script lang="ts" setup>
|
|
|
-import { useDictStore } from "@/store";
|
|
|
-import VraneAPI from "@/api/module_business/crane";
|
|
|
-import BizCraneAPI, { BizCranePageQuery } from '@/api/module_business/crane'
|
|
|
+import BizCraneAPI, { BizCranePageQuery, BizCraneTable } from '@/api/module_business/crane'
|
|
|
+import emptybgUrl from '@/assets/images/empty-bg.png';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+import { onMounted, onUnmounted, ref, reactive, inject } from 'vue';
|
|
|
+
|
|
|
+interface alertData {
|
|
|
+ switch_type?: string;
|
|
|
+ crane_name?: string;
|
|
|
+ msg?: string;
|
|
|
+}
|
|
|
|
|
|
+// 路由 & 全局注入
|
|
|
+const router = useRouter()
|
|
|
+const receiveData = inject<(data: { craneName: string; isShowHomeButton: boolean }) => void>('receiveData');
|
|
|
+
|
|
|
+// 静态资源 & 样式相关
|
|
|
+const emptybg = ref(emptybgUrl)
|
|
|
+const tabHeight = ref('calc(100vh - 70px - 60px - 40px - 42px)')
|
|
|
+
|
|
|
+// 数据加载状态
|
|
|
const alarm_loading = ref(true);
|
|
|
-const alertData = ref([])
|
|
|
const tab_loading = ref(true)
|
|
|
-const craneData = ref([])
|
|
|
+
|
|
|
+// 业务数据
|
|
|
+const alertData = ref<alertData[]>([])
|
|
|
+const craneData = ref<BizCraneTable[]>([]);
|
|
|
+
|
|
|
+// WS 配置(两个独立的WS地址)
|
|
|
+const ALERT_WS_URL = import.meta.env.VITE_APP_WS_ENDPOINT + "/api/v1/business/vardict/ws/alert";
|
|
|
+const ONLINE_WS_URL = import.meta.env.VITE_APP_WS_ENDPOINT + "/api/v1/business/crane/ws/online";
|
|
|
+
|
|
|
+// WS 实例 & 状态管理(两个独立实例)
|
|
|
+let alertWs: WebSocket | null = null; // 报警WS
|
|
|
+let onlineWs: WebSocket | null = null; // 在线状态WS
|
|
|
+const alertConnectionStatus = ref<"connected" | "connecting" | "disconnected">("disconnected");
|
|
|
+const onlineConnectionStatus = ref<"connected" | "connecting" | "disconnected">("disconnected");
|
|
|
+
|
|
|
+// 表格配置
|
|
|
const tableConfig = ref([
|
|
|
{
|
|
|
prop: 'crane_no',
|
|
|
- label: '编号',
|
|
|
- width: 210
|
|
|
+ label: '编号'
|
|
|
},
|
|
|
{
|
|
|
prop: 'crane_name',
|
|
|
@@ -78,7 +106,7 @@ const tableConfig = ref([
|
|
|
label: 'IP地址'
|
|
|
},
|
|
|
{
|
|
|
- prop: 'status',
|
|
|
+ prop: 'online_status',
|
|
|
label: '在线状态',
|
|
|
slot: true
|
|
|
},
|
|
|
@@ -91,7 +119,7 @@ const tableConfig = ref([
|
|
|
// 分页查询参数
|
|
|
const queryFormData = reactive<BizCranePageQuery>({
|
|
|
page_no: 1,
|
|
|
- page_size: 10,
|
|
|
+ page_size: 100,
|
|
|
crane_name: undefined,
|
|
|
crane_no: undefined,
|
|
|
crane_model: undefined,
|
|
|
@@ -103,49 +131,176 @@ const queryFormData = reactive<BizCranePageQuery>({
|
|
|
updated_id: undefined,
|
|
|
});
|
|
|
|
|
|
-const dictStore = useDictStore();
|
|
|
-
|
|
|
-// 字典数据
|
|
|
-const getOptions = async () => {
|
|
|
- return await dictStore.getDict(["sys_user_sex"]);
|
|
|
-};
|
|
|
+// 操作按钮点击事件
|
|
|
+const handleClick = (item: BizCraneTable) => {
|
|
|
+ if (receiveData) {
|
|
|
+ receiveData({ craneName: item.crane_name ?? '', isShowHomeButton: true });
|
|
|
+ }
|
|
|
+ localStorage.setItem('craneInfo', JSON.stringify(item))
|
|
|
+ router.push('/crane');
|
|
|
+}
|
|
|
|
|
|
+// 获取起重机列表数据
|
|
|
const getCraneListData = async () => {
|
|
|
try {
|
|
|
tab_loading.value = true
|
|
|
const response = await BizCraneAPI.listBizCrane(queryFormData);
|
|
|
craneData.value = response.data.data.items;
|
|
|
- console.log(craneData.value)
|
|
|
} finally {
|
|
|
tab_loading.value = false
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// 颜色样式处理
|
|
|
+const getColor = (type: string) => {
|
|
|
+ switch (type) {
|
|
|
+ case '2':
|
|
|
+ return 'content-item-yellow';
|
|
|
+ case '3':
|
|
|
+ return 'content-item-orange';
|
|
|
+ case '4':
|
|
|
+ return 'content-item-red';
|
|
|
+ }
|
|
|
+}
|
|
|
+const getColorSpan = (type: string) => {
|
|
|
+ switch (type) {
|
|
|
+ case '2':
|
|
|
+ return 'content-item-span-yellow';
|
|
|
+ case '3':
|
|
|
+ return 'content-item-span-orange';
|
|
|
+ case '4':
|
|
|
+ return 'content-item-span-red';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化业务数据
|
|
|
const getData = () => {
|
|
|
-getCraneListData()
|
|
|
-// mqttService.connect();
|
|
|
-// topics.forEach(topic => {
|
|
|
-// mqttService.subscribe(topic, (message) => {
|
|
|
-// message = JSON.parse(message.toString());
|
|
|
-// if (topic === 'gc/alert') {
|
|
|
-// alertData.value = message.data;
|
|
|
-// alarm_loading.value = false
|
|
|
-// }
|
|
|
-// if (topic === 'gc/crane_status') {
|
|
|
-// message.forEach(item => {
|
|
|
-// craneData.value.forEach(craneItem => {
|
|
|
-// if (craneItem.CraneNo === item.crane_no) {
|
|
|
-// craneItem.Status = item.is_online ? '在线' : '离线';
|
|
|
-// }
|
|
|
-// });
|
|
|
-// });
|
|
|
-// }
|
|
|
-// });
|
|
|
-// });
|
|
|
+ getCraneListData()
|
|
|
}
|
|
|
|
|
|
+// ======================== 核心改造:WS 连接管理(两个独立WS) ========================
|
|
|
+/**
|
|
|
+ * 通用 WS 连接函数
|
|
|
+ * @param url WS地址
|
|
|
+ * @param name WS名称(用于日志/提示)
|
|
|
+ * @param onMessage 消息处理回调
|
|
|
+ * @param statusRef 连接状态Ref
|
|
|
+ * @returns WS实例
|
|
|
+ */
|
|
|
+ const connectWS = (
|
|
|
+ url: string,
|
|
|
+ name: string,
|
|
|
+ onMessage: (data: any) => void,
|
|
|
+ statusRef: Ref<"connected" | "connecting" | "disconnected">
|
|
|
+): WebSocket | null => {
|
|
|
+ // 初始状态设为 connecting
|
|
|
+ statusRef.value = "connecting";
|
|
|
+ console.log(`[${name}] 开始连接WS: ${url}`);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 在此处创建 WebSocket 实例
|
|
|
+ const ws = new WebSocket(url);
|
|
|
+
|
|
|
+ // 连接成功
|
|
|
+ ws.onopen = () => {
|
|
|
+ console.log(`[${name}] WS连接已建立`);
|
|
|
+ statusRef.value = "connected";
|
|
|
+ ElMessage.success(`${name}系统连接成功`);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 接收消息
|
|
|
+ ws.onmessage = (event) => {
|
|
|
+ console.log(`[${name}] 收到WS消息:`, event.data);
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(event.data);
|
|
|
+ onMessage(data);
|
|
|
+ } catch (err) {
|
|
|
+ console.error(`[${name}] 解析WS消息失败:`, err);
|
|
|
+ onMessage({ content: event.data });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 连接关闭(自动重连)
|
|
|
+ ws.onclose = (event) => {
|
|
|
+ console.log(`[${name}] WS连接已关闭`, event.code, event.reason);
|
|
|
+ statusRef.value = "disconnected";
|
|
|
+ // 非主动关闭(code!==1000)则3秒后重连
|
|
|
+ if (event.code !== 1000) {
|
|
|
+ setTimeout(() => {
|
|
|
+ console.log(`[${name}] 尝试重连WS...`);
|
|
|
+ connectWS(url, name, onMessage, statusRef);
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 连接错误
|
|
|
+ ws.onerror = (error) => {
|
|
|
+ console.error(`[${name}] WS连接错误:`, error);
|
|
|
+ statusRef.value = "disconnected";
|
|
|
+ ElMessage.error(`${name}连接失败,请检查服务器状态`);
|
|
|
+ };
|
|
|
+
|
|
|
+ return ws;
|
|
|
+ } catch (err) {
|
|
|
+ console.error(`[${name}] 创建WS连接失败:`, err);
|
|
|
+ statusRef.value = "disconnected";
|
|
|
+ ElMessage.error(`${name}连接初始化失败`);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 报警WS消息处理
|
|
|
+const handleAlertMessage = (data: any) => {
|
|
|
+ console.log('起重机报警消息:', data);
|
|
|
+ alarm_loading.value = false;
|
|
|
+ alertData.value = data.data || []; // 赋值给报警列表
|
|
|
+};
|
|
|
+
|
|
|
+// 在线状态WS消息处理
|
|
|
+const handleOnlineMessage = (data: any) => {
|
|
|
+ console.log('起重机在线状态消息:', data);
|
|
|
+ if (Array.isArray(data.data)) {
|
|
|
+ data.data.forEach((item: any) => {
|
|
|
+ craneData.value.forEach((craneItem) => {
|
|
|
+ if (craneItem.crane_no === item.crane_no) {
|
|
|
+ craneItem.online_status = item.is_online ? '在线' : '离线';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 断开所有WS连接
|
|
|
+const disconnectAllWS = () => {
|
|
|
+ // 断开报警WS
|
|
|
+ if (alertWs) {
|
|
|
+ alertWs.close(1000, "用户主动断开");
|
|
|
+ alertWs = null;
|
|
|
+ }
|
|
|
+ // 断开在线状态WS
|
|
|
+ if (onlineWs) {
|
|
|
+ onlineWs.close(1000, "用户主动断开");
|
|
|
+ onlineWs = null;
|
|
|
+ }
|
|
|
+ alertConnectionStatus.value = "disconnected";
|
|
|
+ onlineConnectionStatus.value = "disconnected";
|
|
|
+};
|
|
|
+
|
|
|
+// ======================== 生命周期 ========================
|
|
|
onMounted(async () => {
|
|
|
+ // 初始化业务数据
|
|
|
getData()
|
|
|
- await getOptions();
|
|
|
+ if (receiveData) {
|
|
|
+ receiveData({ craneName: '', isShowHomeButton: false });
|
|
|
+ }
|
|
|
+ // 连接两个WS
|
|
|
+ alertWs = connectWS(ALERT_WS_URL, "报警", handleAlertMessage, alertConnectionStatus);
|
|
|
+ onlineWs = connectWS(ONLINE_WS_URL, "在线状态", handleOnlineMessage, onlineConnectionStatus);
|
|
|
+});
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ // 页面销毁时断开所有WS
|
|
|
+ disconnectAllWS();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
@@ -156,7 +311,6 @@ onMounted(async () => {
|
|
|
|
|
|
.overview-left {
|
|
|
height: calc(100vh - 70px - 40px);
|
|
|
- ;
|
|
|
margin-right: 20px;
|
|
|
background: linear-gradient(to bottom, #14428C 0%, #121F34 12%);
|
|
|
border: 1px solid #284988;
|
|
|
@@ -169,7 +323,7 @@ onMounted(async () => {
|
|
|
.overview-left-title {
|
|
|
width: 400px;
|
|
|
height: 60px;
|
|
|
- background-image: url('../../assets/img/overview-left-title.png');
|
|
|
+ background-image: url('../../../assets/images/overview-left-title.png');
|
|
|
background-size: cover;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
@@ -231,7 +385,6 @@ onMounted(async () => {
|
|
|
|
|
|
.overview-right {
|
|
|
height: calc(100vh - 70px - 40px);
|
|
|
- ;
|
|
|
background: linear-gradient(to bottom, #14428C 0%, #121F34 12%);
|
|
|
border: 1px solid #284988;
|
|
|
display: flex;
|
|
|
@@ -243,7 +396,7 @@ onMounted(async () => {
|
|
|
.overview-right-title {
|
|
|
width: 640px;
|
|
|
height: 60px;
|
|
|
- background-image: url('../../assets/img/overview-right-title.png');
|
|
|
+ background-image: url('../../../assets/images/overview-right-title.png');
|
|
|
background-size: cover;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
@@ -275,7 +428,23 @@ onMounted(async () => {
|
|
|
line-height: 35px;
|
|
|
border-radius: 5px;
|
|
|
font-size: 16px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.el-table-empty {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ color: #8ECAFF;
|
|
|
+
|
|
|
+ img {
|
|
|
+ width: 200px;
|
|
|
+ height: auto;
|
|
|
+ margin-bottom: 20px;
|
|
|
}
|
|
|
}
|
|
|
-</style>
|
|
|
-
|
|
|
+</style>
|