feat(iot): 实现基于 Websocket 的设备管理系统
- 更新系统标题为"基于MQTT的IOT设备管理系统" - 移除旧的SSE连接,改为使用WebSocket连接 - 新增设备状态、产品状态和报警状态的WebSocket连接及处理逻辑 - 更新面包屑导航和页面标题 - 修改用户编辑页面的标题 - 更新登录页面的标题
This commit is contained in:
parent
7328db1a23
commit
69a0144ebd
@ -8,7 +8,7 @@
|
|||||||
href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico"
|
href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico"
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>物联网网关系统</title>
|
<title>基于MQTT的IOT设备管理系统</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
@ -79,7 +79,8 @@ export function addAttachments(data: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除附件
|
|
||||||
export function deleteAttachment(id: string) {
|
// 删除设备预览图或图标
|
||||||
return axios.delete(`/api/rest/attachment/delete/${id}`);
|
export function deleteDeviceAttachment(id: string) {
|
||||||
|
return axios.delete(`/api/rest/device/attachment/${id}`);
|
||||||
}
|
}
|
@ -12,7 +12,7 @@
|
|||||||
:heading="5"
|
:heading="5"
|
||||||
@click="$router.push({ name: 'Workplace' })"
|
@click="$router.push({ name: 'Workplace' })"
|
||||||
>
|
>
|
||||||
物联网网关系统
|
IOT设备管理系统
|
||||||
</a-typography-title>
|
</a-typography-title>
|
||||||
<icon-menu-fold
|
<icon-menu-fold
|
||||||
v-if="!topMenu && appStore.device === 'mobile'"
|
v-if="!topMenu && appStore.device === 'mobile'"
|
||||||
|
@ -83,7 +83,7 @@ const SYSTEM: AppRouteRecordRaw = {
|
|||||||
name: 'Log',
|
name: 'Log',
|
||||||
component: () => import('@/views/system/log/index.vue'),
|
component: () => import('@/views/system/log/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '操作日志管理',
|
title: '操作日志',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
permissions: ['system:menu'],
|
permissions: ['system:menu'],
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="left-side">
|
<div class="left-side">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<Banner />
|
<Banner />
|
||||||
<DataPanel :deviceInfo="computedDeviceInfo" :alarmInfo='alarmInfo' :productInfo='productInfo' />
|
<DataPanel v-if="computedDeviceInfo && alarmInfo && productInfo" :deviceInfo="computedDeviceInfo" :alarmInfo='alarmInfo' :productInfo='productInfo' />
|
||||||
<ContentPublishingSource v-if="computedDeviceInfo && alarmInfo && productInfo" :deviceInfo="computedDeviceInfo" :alarmInfo='alarmInfo' :productInfo='productInfo' />
|
<ContentPublishingSource v-if="computedDeviceInfo && alarmInfo && productInfo" :deviceInfo="computedDeviceInfo" :alarmInfo='alarmInfo' :productInfo='productInfo' />
|
||||||
</div>
|
</div>
|
||||||
<a-grid :cols="24" :col-gap="16" :row-gap="16" style="margin-top: 16px">
|
<a-grid :cols="24" :col-gap="16" :row-gap="16" style="margin-top: 16px">
|
||||||
@ -22,8 +22,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { getAlarmInfo, getDeviceInfo, getProductInfo } from '@/api/dashboard';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { EventSourcePolyfill } from 'event-source-polyfill';
|
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
|
|
||||||
import Banner from './components/banner.vue';
|
import Banner from './components/banner.vue';
|
||||||
@ -37,55 +36,91 @@
|
|||||||
const alarmInfo = ref<any>();
|
const alarmInfo = ref<any>();
|
||||||
const productInfo = ref<any>();
|
const productInfo = ref<any>();
|
||||||
|
|
||||||
// const fetchData = async () => {
|
let deviceWebSocket: WebSocket | null = null;
|
||||||
// const res = await getDeviceInfo();
|
let productWebSocket: WebSocket | null = null;
|
||||||
// deviceInfo.value = res.data;
|
let alarmWebSocket: WebSocket | null = null;
|
||||||
// const res1 = await getAlarmInfo();
|
|
||||||
// alarmInfo.value = res1.data;
|
|
||||||
// const res2 = await getProductInfo();
|
|
||||||
// productInfo.value = res2.data;
|
|
||||||
// }
|
|
||||||
// onMounted(() => {
|
|
||||||
// fetchData();
|
|
||||||
// })
|
|
||||||
|
|
||||||
let eventSource: EventSourcePolyfill | null = null;
|
const startWebSockets = () => {
|
||||||
const token = getToken();
|
// 创建设备状态 WebSocket 连接
|
||||||
const startSSE = () => {
|
deviceWebSocket = new WebSocket('ws://127.0.0.1:8081/api/rest/ws/device/status');
|
||||||
// 创建 EventSourcePolyfill 对象并连接到 SSE 接口
|
deviceWebSocket.onopen = () => {
|
||||||
eventSource = new EventSourcePolyfill(`http://127.0.0.1:8081/api/rest/device/sse/status`, {
|
console.log('Device WebSocket connected');
|
||||||
headers: {
|
};
|
||||||
'X-CSRF-TOKEN': token
|
deviceWebSocket.onmessage = (event) => {
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听消息事件
|
|
||||||
eventSource.onmessage = (event:any) => {
|
|
||||||
const data = JSON.parse(event.data); // 假设服务器发送的是 JSON 数据
|
const data = JSON.parse(event.data); // 假设服务器发送的是 JSON 数据
|
||||||
console.log('Received data:', data);
|
console.log('Received device data:', data);
|
||||||
// 在这里处理接收到的数据
|
deviceInfo.value = data; // 更新设备信息
|
||||||
|
};
|
||||||
|
deviceWebSocket.onerror = (error) => {
|
||||||
|
console.error('Device WebSocket error:', error);
|
||||||
|
Message.error({
|
||||||
|
content: '设备状态 WebSocket 连接错误',
|
||||||
|
duration: 5 * 1000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
deviceWebSocket.onclose = () => {
|
||||||
|
console.log('Device WebSocket closed');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听错误事件
|
// 创建产品状态 WebSocket 连接
|
||||||
eventSource.onerror = (error:any) => {
|
productWebSocket = new WebSocket('ws://127.0.0.1:8081/api/rest/ws/product/status');
|
||||||
console.error('SSE error:', error);
|
productWebSocket.onopen = () => {
|
||||||
// 在这里处理错误
|
console.log('Product WebSocket connected');
|
||||||
|
};
|
||||||
|
productWebSocket.onmessage = (event) => {
|
||||||
|
const data = JSON.parse(event.data); // 假设服务器发送的是 JSON 数据
|
||||||
|
console.log('Received product data:', data);
|
||||||
|
productInfo.value = data; // 更新产品信息
|
||||||
|
};
|
||||||
|
productWebSocket.onerror = (error) => {
|
||||||
|
console.error('Product WebSocket error:', error);
|
||||||
|
Message.error({
|
||||||
|
content: '产品状态 WebSocket 连接错误',
|
||||||
|
duration: 5 * 1000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
productWebSocket.onclose = () => {
|
||||||
|
console.log('Product WebSocket closed');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建报警状态 WebSocket 连接
|
||||||
|
alarmWebSocket = new WebSocket('ws://127.0.0.1:8081/api/rest/ws/record/status');
|
||||||
|
alarmWebSocket.onopen = () => {
|
||||||
|
console.log('Alarm WebSocket connected');
|
||||||
|
};
|
||||||
|
alarmWebSocket.onmessage = (event) => {
|
||||||
|
const data = JSON.parse(event.data); // 假设服务器发送的是 JSON 数据
|
||||||
|
console.log('Received alarm data:', data);
|
||||||
|
alarmInfo.value = data; // 更新报警信息
|
||||||
|
};
|
||||||
|
alarmWebSocket.onerror = (error) => {
|
||||||
|
console.error('Alarm WebSocket error:', error);
|
||||||
|
Message.error({
|
||||||
|
content: '报警状态 WebSocket 连接错误',
|
||||||
|
duration: 5 * 1000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
alarmWebSocket.onclose = () => {
|
||||||
|
console.log('Alarm WebSocket closed');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
startSSE();
|
startWebSockets();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 组件卸载时关闭 SSE 连接
|
// 组件卸载时关闭 WebSocket 连接
|
||||||
if (eventSource) {
|
if (deviceWebSocket) {
|
||||||
eventSource.close();
|
deviceWebSocket.close();
|
||||||
|
}
|
||||||
|
if (productWebSocket) {
|
||||||
|
productWebSocket.close();
|
||||||
|
}
|
||||||
|
if (alarmWebSocket) {
|
||||||
|
alarmWebSocket.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Breadcrumb :items="['系统管理', '公告设置']" />
|
<Breadcrumb :items="['系统管理', '设备管理']" />
|
||||||
<a-card class="general-card" title=" ">
|
<a-card class="general-card" title=" ">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :flex="1">
|
<a-col :flex="1">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Breadcrumb :items="['系统管理', '公告设置']" />
|
<Breadcrumb :items="['系统管理', '设备管理']" />
|
||||||
<a-card class="general-card" title=" ">
|
<a-card class="general-card" title=" ">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :flex="1">
|
<a-col :flex="1">
|
||||||
|
@ -164,7 +164,7 @@
|
|||||||
ProductCreateRecord,
|
ProductCreateRecord,
|
||||||
queryProductDetail,
|
queryProductDetail,
|
||||||
updateProduct,
|
updateProduct,
|
||||||
addAttachments, deleteAttachment
|
addAttachments, deleteDeviceAttachment
|
||||||
} from '@/api/product';
|
} from '@/api/product';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -296,7 +296,7 @@
|
|||||||
Message.error('无法获取图片 ID');
|
Message.error('无法获取图片 ID');
|
||||||
}
|
}
|
||||||
// 调用删除接口
|
// 调用删除接口
|
||||||
const res = await deleteAttachment(fileId);
|
const res = await deleteDeviceAttachment(fileId);
|
||||||
if (res.data === true) {
|
if (res.data === true) {
|
||||||
// 删除成功后
|
// 删除成功后
|
||||||
Message.success('删除成功');
|
Message.success('删除成功');
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<div class="logo-text">Hello Ticket!</div>
|
<div class="logo-text">Hello</div>
|
||||||
</div>
|
</div>
|
||||||
<LoginBanner />
|
<LoginBanner />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
'login.form.title': '票据管理系统',
|
'login.form.title': 'IOT设备管理系统',
|
||||||
'login.form.userName.errMsg': '账号不能为空',
|
'login.form.userName.errMsg': '账号不能为空',
|
||||||
'login.form.password.errMsg': '密码不能为空',
|
'login.form.password.errMsg': '密码不能为空',
|
||||||
'login.form.login.errMsg': '登录出错,请刷新重试',
|
'login.form.login.errMsg': '登录出错,请刷新重试',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Breadcrumb :items="['通知管理', '消息设置']" />
|
<Breadcrumb :items="['通知管理', '消息管理']" />
|
||||||
<a-card class="general-card" title=" ">
|
<a-card class="general-card" title=" ">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :flex="1">
|
<a-col :flex="1">
|
||||||
|
@ -147,7 +147,7 @@
|
|||||||
});
|
});
|
||||||
const emit = defineEmits(['refresh']);
|
const emit = defineEmits(['refresh']);
|
||||||
const modalTitle = computed(() => {
|
const modalTitle = computed(() => {
|
||||||
return props.isCreate ? '新建用户' : '编辑用户';
|
return props.isCreate ? '新增用户' : '编辑用户';
|
||||||
});
|
});
|
||||||
const { visible, setVisible } = useVisible(false);
|
const { visible, setVisible } = useVisible(false);
|
||||||
const checkKeys = ref<number[]>([]);
|
const checkKeys = ref<number[]>([]);
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<a-col :span="9">
|
<a-col :span="10">
|
||||||
<a-form-item field="phone" label="电话号码">
|
<a-form-item field="phone" label="电话号码">
|
||||||
<a-input
|
<a-input
|
||||||
v-model="formModel.phone"
|
v-model="formModel.phone"
|
||||||
|
Loading…
Reference in New Issue
Block a user