You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

424 lines
11 KiB

<template>
<div class="fault-rule-drawer">
<el-drawer v-model="isShowDrawer" :title="title" direction="rtl" size="90%"
modal-class="model-dev-opn"
:before-close="handleBeforeClose" @opened="onDrawerOpened">
<main class="wh-full flex">
<EdfsWrap title="点位组" class="p-r-4 h-full border-r-1 border-solid border-r-#e4e7ed">
<PointGroupTree v-if="isShowDrawer" :data="pointGroup" @device-select="onGroupChange"
:groupChangeLoading="groupChangeLoading" @onchangePoints="onchangePoints"
:devicePoints="pointList"
:loadingPoints="loadingPoints" :isTransfer="props.isTransfer"
:siteInfo="props.siteInfo"
ref="pointGroupTreeRef"/>
</EdfsWrap>
<div class="flex-1 p-4 h-full overflow-hidden relative"
>
<div class="absolute w-full h-full bg-[#ffffffe5] z-99" v-if="loadingChart">
<div class="w-full h-full flex flex-col justify-center items-center">
<el-progress
style="width: 70%"
:text-inside="true"
:stroke-width="18"
:percentage="progress"
status="success"
/>
<div class="text-xl">
<div>当前加载数据量较大请稍等。。。</div>
</div>
</div>
</div>
<el-button class="mb-4" type="primary" @click="loadChardData">查询数据</el-button>
<NewDataChart v-if="isShowChart" :chart-datas="chartDatas" :legends="legends"
:axis-data="Array.from(axisData)" ref="chartRef"/>
</div>
</main>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import NewDataChart from './newDataChart.vue'
import PointGroupTree from './PointGroupTree.vue'
import { type ManualAction, } from '@/utils/zmq'
import ZMQWorker from '@/composables/useZMQJsonWorker'
import type { IMyPoint, IOfflineDevice, IOnlineDevice } from '../../type'
import {
getDeviceDetails,
getPointGroup,
getPoints,
type IGetDeviceDataParams,
type IPointGroupOV,
type IPointGroupParams,
type IPointsParams,
type ISite,
} from '@/api/module/transfer'
import { useMessage } from '@/composables/useMessage'
import dayjs from 'dayjs'
import { nextTick } from "vue";
const env = import.meta.env
const worker = ZMQWorker.getInstance()
const isShowDrawer = defineModel<boolean>()
const title = computed(() => (props.isTransfer ? '数据详情' : `已迁移数据详情`))
const message = useMessage()
const pointGroupTreeRef = ref<InstanceType<typeof PointGroupTree>>()
const props = defineProps<{
siteInfo: ISite | null
isTransfer: boolean
}>()
const emit = defineEmits(['on-save'])
const pubIdWithDevice = new Map<
string,
{ device: IOfflineDevice | IOnlineDevice; action: ManualAction }
>()
const pointList = ref<IMyPoint[]>([])
const curDevice = ref<IOfflineDevice | IOnlineDevice>()
async function open(device: IOfflineDevice | IOnlineDevice) {
curDevice.value = device
await loadPointGroup()
.then(async () => {
// if (env.VITE_APP_ENV === 'local') {
// props.isTransfer ? await loadDeviceDetails() : zmqImport(device as IOfflineDevice)
// } else {
await loadDeviceDetails()
// }
})
.catch(() => {
message.error('获取点位组数据失败')
fullscreenLoading.value?.close()
})
}
const loadingPoints = ref(false)
const columsParams = computed(() => ['ts', ...checkPointList.value.map(i => i.addr)])
async function loadPoints() {
if (!curGroup.value) {
message.error('请先选择点位组')
return
}
const params: IPointsParams = {
type: curGroup.value,
}
if (props.isTransfer) {
const onlineDevice = curDevice.value as IOnlineDevice
params.isLocal = false
params.host = onlineDevice.clientIp
} else {
const offlineDevice = curDevice.value as IOfflineDevice
params.sn = offlineDevice.sn
params.site = props.siteInfo!.name
params.isLocal = true
}
loadingPoints.value = true
const res = await getPoints(params)
if (res.code === 0) {
const data = Array.isArray(res?.data) ? res.data : []
pointList.value = data.map((i: any) => ({
label: i.cnName,
addr: i.addr,
unit: i.unit || '',
}))
}
loadingPoints.value = false
return res
}
const pointData = ref<any[]>([])
async function loadDeviceDetails() {
if (!fullscreenLoading.value) {
openFullScreen()
}
chartDatas.clear()
axisData.clear()
legends.value = []
const poinsRes = await loadPoints()
if (!poinsRes || poinsRes.code !== 0) {
message.error('获取点位数据失败')
pointList.value = []
chartDatas.clear()
axisData.clear()
fullscreenLoading.value?.close()
return
}
isShowDrawer.value = true
groupChangeLoading.value = false
fullscreenLoading.value?.close()
}
const chartDatas = reactive(new Map<string, any[]>())
const axisData = new Set<string>()
const legends = ref<{ addr: string; label: string, unit: string }[]>([])
const loadingChart = ref(false)
const chartAllTotal = ref(0)
const chartLimit = ref(1000)
const chartOffset = ref(0)
const progress = ref(0)
async function loadChardData() {
if (!columsParams.value.filter(i => i !== 'ts').length) {
message.error('请选择点位')
return
}
clearData()
const limit = chartLimit.value
const offset = chartOffset.value
const options = {
columns: columsParams.value,
isLocal: props.isTransfer ? false : true,
host: props.isTransfer ? (curDevice.value as IOnlineDevice).clientIp : '',
name: curGroupName.value as string,
}
const params: IGetDeviceDataParams = {
...options,
limit,
offset
}
if (env.VITE_APP_ENV !== 'local' || !props.isTransfer) {
params.site_id = props.siteInfo!.name || ''
params.device_id = curDevice.value?.sn || ''
}
loadingChart.value = true
isShowChart.value = false
const res = await getDeviceDetails(params)
if (res.code !== 0) {
resetChartStatus()
message.error('获取设备数据失败')
return
}
chartAllTotal.value = res?.data?.total ?? 0
if (chartAllTotal.value === 0) {
resetChartStatus()
message.info('暂无数据')
return
}
const pointData = Array.isArray(res.data.results) ? res.data.results : []
setChartData(pointData)
const pageCount = Math.ceil(chartAllTotal.value / Number(limit))
if (pageCount <= 1) {
progress.value = 100
setTimeout(() => {
resetChartStatus()
})
return
}
for (let i = 1; i < pageCount; i++) {
const params: IGetDeviceDataParams = {
...options,
limit,
offset: i * Number(limit)
}
const res = await getDeviceDetails(params)
if (res.code !== 0) {
clearData()
resetChartStatus()
message.error('获取设备数据失败')
return
}
const pointData = Array.isArray(res.data.results) ? res.data.results : []
setChartData(pointData)
progress.value = Math.min(100, Math.floor(((i + 1) / pageCount) * 100))
}
resetChartStatus()
}
function setChartData(pointData: any[]) {
for (const addr of columsParams.value.filter(i => i !== 'ts')) {
const label = pointList.value.find(i => i.addr === addr)?.label || addr
const find = legends.value.find(i => i.addr === addr)
const unit = pointList.value.find(i => i.addr === addr)?.unit || ''
if (!find) {
legends.value.push({ addr, label, unit })
}
}
pointData.forEach((data: any[]) => {
const [ts, val, addr] = data
if (checkPointList.value.some(i => i.addr === addr)) {
const time = dayjs(Number(ts)).format('YYYY-MM-DD HH:mm:ss')
if (addr) {
const colData = chartDatas.get(addr)
if (colData) {
colData.push([time, val])
} else {
chartDatas.set(addr, [[time, val]])
}
}
if (ts) {
axisData.add(time)
}
}
})
}
// function zmqImport(device: IOfflineDevice) {
// if (!device.sn || !props.siteInfo!.name) {
// message.error('未找到站点或设备')
// return
// }
// const msg = getPubInitData<'import'>(
// 'import',
// ['', '', '', '', '', '', `${props.siteInfo!.name}/${device.sn}`],
// 'yes'
// )
// pubIdWithDevice.set(msg.id, { device, action: 'import' })
// worker.publish(postTransferTopic, msg, true, zmqTimeoutCb)
// worker.subscribe(getTransferTopic, zmqImportCb, msg.id)
// }
// function zmqImportCb(msg: PubMsgData) {
// const { id, result } = msg
// if (result !== 'progress') {
// const { device, action } = pubIdWithDevice.get(id)!
// if (result === 'success' && device && action === 'import') {
// loadDeviceDetails()
// pubIdWithDevice.delete(id)
// } else {
// message.error(`设备数据获取失败`)
// fullscreenLoading.value?.close()
// pubIdWithDevice.delete(id)
// }
// }
// }
// function zmqTimeoutCb(msg: TimeoutMsg) {
// const { device, action } = pubIdWithDevice.get(msg.timeoutId)!
// if (device && action === 'import') {
// message.error(`设备${device.sn},查询信息超时,请稍后重试`)
// pubIdWithDevice.delete(msg.timeoutId)
// fullscreenLoading.value?.close()
// }
// }
function handleBeforeClose(done: () => void) {
isShowDrawer.value = false
clearData()
fullscreenLoading.value = null
done()
}
function clearData() {
chartLimit.value = 1000
chartOffset.value = 0
chartAllTotal.value = 0
progress.value = 0
legends.value = []
chartDatas.clear()
axisData.clear()
}
function resetChartStatus() {
loadingChart.value = false
nextTick(() => {
isShowChart.value = true
})
}
const chartRef = ref<InstanceType<typeof NewDataChart>>()
const isShowChart = ref(false)
function onDrawerOpened() {
nextTick(() => {
isShowChart.value = true
})
}
const fullscreenLoading = ref<any>(null)
const openFullScreen = () => {
fullscreenLoading.value = ElLoading.service({
lock: true,
text: '数据加载中,请稍后...',
background: 'rgba(255, 255, 255, 0.8)',
})
}
const pointGroup = ref<IPointGroupOV[]>([])
const curGroup = ref<string>()
const curGroupName = ref<string>()
async function loadPointGroup() {
const params: IPointGroupParams = {}
if (props.isTransfer) {
const onlineDevice = curDevice.value as IOnlineDevice
params.isLocal = false
params.host = onlineDevice.clientIp
} else {
const offlineDevice = curDevice.value as IOfflineDevice
params.sn = offlineDevice.sn
params.site = props.siteInfo!.name
params.isLocal = true
}
const res = await getPointGroup(params)
if (res.code === 0) {
curGroup.value = res.data[0]?.type
curGroupName.value = res.data[0]?.name
pointGroup.value = Array.isArray(res?.data) ? res.data : []
return Promise.resolve()
} else {
return Promise.reject()
}
}
const groupChangeLoading = ref<boolean>(false)
function onGroupChange(item: IPointGroupOV) {
if (!item?.type) {
return
}
groupChangeLoading.value = true
curGroup.value = item.type
curGroupName.value = item.name
loadDeviceDetails()
}
const checkPointList = ref<IMyPoint[]>([])
function onchangePoints(checkPoints: IMyPoint[]) {
checkPointList.value = []
checkPointList.value = checkPoints
}
defineExpose({
open,
openFullScreen,
})
</script>
<style scoped lang="scss">
.fault-rule-drawer {
font-size: 16px;
:deep(.edfs-wrap) {
width: auto;
}
:deep(.el-drawer__header) {
color: var(--text-color);
}
}
</style>