Browse Source

feat: 在线设备根据站点区分

main
betaqi 1 week ago
parent
commit
8210c143e8
  1. 2
      global.types/components.d.ts
  2. 109
      src/stores/transferData.ts
  3. 151
      src/views/stationData/index.vue
  4. 2
      src/views/stationData/transfer/index.vue

2
global.types/components.d.ts vendored

@ -34,6 +34,8 @@ declare module 'vue' { @@ -34,6 +34,8 @@ declare module 'vue' {
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']

109
src/stores/transferData.ts

@ -1,10 +1,16 @@ @@ -1,10 +1,16 @@
import { ref, computed } from 'vue'
import { ref, computed, reactive } from 'vue'
import { defineStore } from 'pinia'
import type { IOnlineDevice, IUpFirmwareStatus } from '@/views/stationData/type'
import ZMQWorker from '@/composables/useZMQJsonWorker'
import { getSubTopic, type SubMsgData } from '@/utils/zmq'
import { getDeviceTopic } from '@/views/stationData/utils'
import dayjs from "dayjs";
export interface SiteInfo {
id: string
onlineCount: number
offlineCount: number
devices: Map<string, IOnlineDevice>
}
export const useTransferDataStore = defineStore('transfer', () => {
const subDevices = getSubTopic('client', 'status', 'transfer')
@ -13,20 +19,52 @@ export const useTransferDataStore = defineStore('transfer', () => { @@ -13,20 +19,52 @@ export const useTransferDataStore = defineStore('transfer', () => {
const connectSite = ref<any>(null)
async function initConnectSite() {
if (connectSite.value) return
connectSite.value = {
title: '未命名站点',
edit: true,
editTitle: '未命名站点',
}
}
const checkDeviceStatusInterval = ref<NodeJS.Timeout>()
const siteMap = reactive(new Map<string, SiteInfo>())
const devicesMap = reactive(new Map<string, IOnlineDevice>())
const checkDeviceStatusInterval = ref<NodeJS.Timeout>()
function checkDeviceStatus() {
checkDeviceStatusInterval.value = setInterval(checkDeviceStatusFn, 100);
// =========== mock =================
// let i = 0
//
// function mockSubDeviceMsg() {
// // 随机生成 SN
// const sn = `SN-${Math.floor(Math.random() * 1000)}`
// // const siteId = `${i % 2 === 0 ? 'site1' : 'site2'}`
// const siteId = `siteId-${Math.floor(Math.random() * 1000)}`
// const clientIp = `192.168.0.${Math.floor(Math.random() * 255)}`
// const version = `v${(Math.random() * 2 + 1).toFixed(2)}`
// const footprint = (Math.random() * 50000).toFixed(2) // KB
//
// // const sn = 123
// // const siteId = 123123
// // const clientIp = `192.168.0.${Math.floor(Math.random() * 255)}`
// // const version = `v${(Math.random() * 2 + 1).toFixed(2)}`
// // const footprint = (Math.random() * 50000).toFixed(2) // KB
//
// const msg: any = {
// feedback: [clientIp, sn, siteId, version, footprint],
// }
//
// getSubDevicesCb(msg)
// }
//
//
// let a = setInterval(() => {
// i++
// mockSubDeviceMsg()
// }, 1000)
// setTimeout(() => {
// clearInterval(a)
// }, 8000)
// =========== mock end =================
function startCheckStatus() {
clearInterval(checkDeviceStatusInterval.value)
checkDeviceStatusInterval.value = setInterval(checkDeviceStatusFn, 1000);
}
const checkDeviceStatusFn = () => {
@ -37,6 +75,7 @@ export const useTransferDataStore = defineStore('transfer', () => { @@ -37,6 +75,7 @@ export const useTransferDataStore = defineStore('transfer', () => {
device.status = '离线';
}
});
reassignDevicesToSites()
}
function formatSizeFromKB(num: number): string {
@ -77,8 +116,34 @@ export const useTransferDataStore = defineStore('transfer', () => { @@ -77,8 +116,34 @@ export const useTransferDataStore = defineStore('transfer', () => {
}
devicesMap.set(sn, device)
}
}
function reassignDevicesToSites() {
devicesMap.forEach((device) => {
const siteId = device.site_id
if (!siteId) return
// 若该站点还未创建,则初始化
if (!siteMap.has(siteId)) {
siteMap.set(siteId, {
id: siteId,
onlineCount: 0,
offlineCount: 0,
devices: reactive(new Map<string, IOnlineDevice>()),
})
}
// 获取站点信息并更新
const siteInfo = siteMap.get(siteId)!
siteInfo.devices.set(device.sn, device)
})
isConnected.value = devicesMap.size > 0
// 更新site 在线数量和离线数量
siteMap.forEach((siteInfo) => {
const devices = Array.from(siteInfo.devices.values())
siteInfo.onlineCount = devices.filter(item => item.status === '在线').length
siteInfo.offlineCount = devices.filter(item => item.status === '离线').length
})
}
const onlineCount = computed(() => {
@ -93,8 +158,8 @@ export const useTransferDataStore = defineStore('transfer', () => { @@ -93,8 +158,8 @@ export const useTransferDataStore = defineStore('transfer', () => {
onMounted(() => {
worker.subscribe(getDeviceTopic, getSubDevicesCb)
checkDeviceStatus()
document.addEventListener('visibilitychange', checkDeviceStatusFn)
startCheckStatus()
document.addEventListener('visibilitychange', handleVisibilityChange)
})
const route = useRoute()
@ -102,11 +167,18 @@ export const useTransferDataStore = defineStore('transfer', () => { @@ -102,11 +167,18 @@ export const useTransferDataStore = defineStore('transfer', () => {
watch(() => route.path, (val) => {
if (!['/station/data-transfer', '/station'].includes(val)) {
clearInterval(checkDeviceStatusInterval.value)
document.removeEventListener('visibilitychange', checkDeviceStatusFn)
document.removeEventListener('visibilitychange', handleVisibilityChange)
worker.unsubscribe(subDevices)
}
})
function handleVisibilityChange() {
if (document.hidden) {
clearInterval(checkDeviceStatusInterval.value)
} else {
startCheckStatus()
}
}
function upFirmwareStatus(sn: string, feedback: any[]) {
const device = devicesMap.get(sn)
@ -181,14 +253,13 @@ export const useTransferDataStore = defineStore('transfer', () => { @@ -181,14 +253,13 @@ export const useTransferDataStore = defineStore('transfer', () => {
return {
siteMap,
isConnected,
devicesMap,
connectSite,
checkDeviceStatusInterval,
onlineCount,
offlineCount,
checkDeviceStatus,
initConnectSite,
upFirmwarePending,
upFirmwareReset,
upFirmwareStatus,

151
src/views/stationData/index.vue

@ -1,97 +1,108 @@ @@ -1,97 +1,108 @@
<template>
<div class="flex-col gap-16 wh-full">
<template v-if="env.VITE_APP_ENV == 'local'">
<EdfsWrap title="当前连接站点" v-if="connectSite?.title" shape="circle" shapeColor="#4B9E5F"
class="h-auto">
<div class="station-list-flow">
<div class="station-item">
<div class="title bg-[#4B9E5F]">
<div>{{ connectSite.title }}</div>
<EdfsWrap title="当前连接站点" shape="circle" shapeColor="#4B9E5F"
class="h-auto max-h[462px]">
<el-scrollbar>
<div class="station-list-flow">
<div class="wh-full flex-center items-center"
v-if="!Array.from(siteMap.values()).length">
<el-empty style="height: 180px;" :image-size="130"/>
<!-- :description=""-->
</div>
<div class="body">
<div class="info">
<div class="info-item">
<div class="info-item-label">在线设备</div>
<div class="info-item-value">
<div class="i-octicon:cloud-16 color-[#4B9E5F] text-16px"></div>
{{ onlineCount }}
<template v-else v-for=" site in Array.from(siteMap.values())">
<div class="station-item">
<div class="title bg-[#4B9E5F]">
<div>{{ site.id }}</div>
</div>
<div class="body">
<div class="info">
<div class="info-item">
<div class="info-item-label">在线设备</div>
<div class="info-item-value">
<div class="i-octicon:cloud-16 color-[#4B9E5F] text-16px"></div>
{{ site.onlineCount }}
</div>
</div>
<div class="info-item">
<div class="info-item-label">离线设备</div>
<div class="info-item-value">
<div class="i-octicon:cloud-offline-16 color-[#F44336] text-16px"></div>
{{ site.offlineCount }}
</div>
</div>
</div>
</div>
<div class="info-item">
<div class="info-item-label">离线设备</div>
<div class="info-item-value">
<div class="i-octicon:cloud-offline-16 color-[#F44336] text-16px"></div>
{{ offlineCount }}
<div class="footer row-end-0">
<div class="m-l-auto p-b-8">
<el-button type="primary" style="height: 28px; padding: 0 12px" color="#4B9E5F"
@click="onTransferData(site)">设备详情
</el-button>
</div>
</div>
</div>
</div>
<div class="footer row-end-0">
<div class="m-l-auto p-b-8">
<el-button type="primary" style="height: 28px; padding: 0 12px" color="#4B9E5F"
@click="onTransferData">设备详情
</el-button>
</div>
</div>
</template>
</div>
</div>
</el-scrollbar>
</EdfsWrap>
</template>
<EdfsWrap :title="`${env.VITE_APP_ENV == 'local' ? '数据迁移历史' : '数据导入历史'}`"
shape="circle" shapeColor="#F1BF63"
class="flex-1" useScrollBar>
<div class="station-list-flow">
<div class="station-item" v-for="item in siteList" :key="item.id">
<div class="title bg-[#F1BF63]">
{{ item.name }}
<div class="flex items-center gap-col-2">
<el-tooltip content="详情">
<div
class="i-material-symbols:info-outline :hover:color-[#ddd] color-[#FFFFFF] cursor-pointer text-20px"
@click="onSiteDetails(item)"></div>
</el-tooltip>
<el-scrollbar>
<div class="station-list-flow">
<div class="station-item" v-for="item in siteList" :key="item.id">
<div class="title bg-[#F1BF63]">
{{ item.name }}
<div class="flex items-center gap-col-2">
<el-tooltip content="详情">
<div
class="i-material-symbols:info-outline :hover:color-[#ddd] color-[#FFFFFF] cursor-pointer text-20px"
@click="onSiteDetails(item)"></div>
</el-tooltip>
</div>
</div>
</div>
<template v-if="env.VITE_APP_ENV == 'local'">
<div class="body">
<div class="info">
<div class="info-item">
<div class="info-item-label">导出路径</div>
<el-tooltip :content="item.export_root_path">
<div class="info-item-path">{{ item.export_root_path }}</div>
</el-tooltip>
<template v-if="env.VITE_APP_ENV == 'local'">
<div class="body">
<div class="info">
<div class="info-item">
<div class="info-item-label">导出路径</div>
<el-tooltip :content="item.export_root_path">
<div class="info-item-path">{{ item.export_root_path }}</div>
</el-tooltip>
</div>
</div>
</div>
</div>
<div class="footer">
<div class="item">
<div class="label">迁移时间:</div>
<div class="value">
{{ item.create_time }}
<div class="footer">
<div class="item">
<div class="label">迁移时间:</div>
<div class="value">
{{ item.create_time }}
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div class="body">
<div class="info">
<div class="info-item">
<div class="info-item-label">创建时间</div>
<div class="info-item-value">{{ item.create_time }}</div>
</template>
<template v-else>
<div class="body">
<div class="info">
<div class="info-item">
<div class="info-item-label">创建时间</div>
<div class="info-item-value">{{ item.create_time }}</div>
</div>
</div>
</div>
</div>
</template>
</template>
</div>
</div>
</div>
</el-scrollbar>
</EdfsWrap>
</div>
</template>
<script setup lang="ts">
import { useTransferDataStore } from '@/stores/transferData'
import { type SiteInfo, useTransferDataStore } from '@/stores/transferData'
import { storeToRefs } from 'pinia'
import { getSiteList, type ISite } from '@/api/module/transfer'
import EdfsWrap from "@/components/Edfs-wrap.vue";
@ -101,20 +112,15 @@ const env = import.meta.env @@ -101,20 +112,15 @@ const env = import.meta.env
const router = useRouter()
const transferDataStore = useTransferDataStore()
const { isConnected, connectSite, onlineCount, offlineCount } =
const { connectSite, siteMap, onlineCount, offlineCount } =
storeToRefs(transferDataStore)
const { initConnectSite } = transferDataStore
watch(isConnected, val => {
val ? initConnectSite() : (connectSite.value = null)
})
function onTransferData() {
function onTransferData(site: SiteInfo) {
router.push({
path: '/station/data-transfer',
query: {
type: 'export',
site: JSON.stringify(site),
},
})
}
@ -139,7 +145,6 @@ async function loadSiteList() { @@ -139,7 +145,6 @@ async function loadSiteList() {
}
onMounted(() => {
initConnectSite()
loadSiteList()
})
</script>

2
src/views/stationData/transfer/index.vue

@ -181,7 +181,7 @@ const transferStatus = ref< @@ -181,7 +181,7 @@ const transferStatus = ref<
const devices = computed(() => {
return isonLineTransfer.value
? Array.from(devicesMap.value.values())
? Array.from(devicesMap.value.values()).filter(r => r.site_id === siteInfo.value.id)
: offLineDeviceList.value
}) as Ref<any[]>

Loading…
Cancel
Save