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.

487 lines
14 KiB

8 months ago
<template>
<div class="fault-rule-drawer">
<el-drawer v-model="isShowDrawer" :title="title" direction="rtl" size="100%"
modal-class="model-dev-opn"
:before-close="handleBeforeClose" @opened="onDrawerOpened">
<main class="wh-full flex">
<EdfsWrap title="点位组" isCollapse :collapsed="collapsed" @collapse="onCollapse"
:style="{width: `${collapsed ? 0 : 600}px`}">
3 months ago
<PointGroupTree
v-if="isShowDrawer"
:data="pointGroup"
@device-select="onGroupChange"
:groupChangeLoading="groupChangeLoading"
@onchangePoints="onchangePoints"
:pointList="pointList"
:loadingPoints="loadingPoints"
:isTransfer="props.isTransfer"
:siteInfo="props.siteInfo"
ref="pointGroupTreeRef"/>
</EdfsWrap>
<div class="flex-1 p-4 h-full overflow-hidden relative" v-loading="loading"
element-loading-text="数据加载中请耐心等待..."
>
<div
class="absolute inset-0 z-[99] flex items-center justify-center bg-white/80 backdrop-blur-sm"
v-if="isFetching"
>
<div class="w-full max-w-[600px] space-y-10 p-6">
<el-scrollbar height="70vh" max-height="600px">
<div
v-for="deviceInfo in pointInfoData"
:key="deviceInfo.id"
class="space-y-3 text-center bg-white/90 p-5 rounded-xl shadow"
>
<div class="font-semibold text-gray-700 text-lg">
{{ chartGroupMap?.get(deviceInfo.id)?.cnName }} 数据加载中
</div>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="deviceInfo.progress"
/>
<div class="text-gray-600 text-base">已查询
<span class="text-green-600 font-semibold">
{{ deviceInfo.fetchLimit }}
</span>/<span class="text-blue-600 font-semibold">{{
deviceInfo.total
}}</span>剩余<span
class="text-red-500 font-semibold">{{
deviceInfo.total - deviceInfo.fetchLimit
}}</span>
</div>
</div>
</el-scrollbar>
</div>
</div>
<div class="flex w-full items-center gap-16">
<div class="w-400px">
<el-date-picker
v-model="time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator="到"
:default-value="getBeforeMonth"
start-placeholder="开始时间"
end-placeholder="结束时间"
:disabled-date="disabledDate"
/>
</div>
<el-button type="primary" @click="loadChardData2">查询数据</el-button>
查询最近
<el-input-number :key="refreshKey" v-model="num" :min="1" :max="diffHours"
@input="handleInput"/>
小时
</div>
3 months ago
<NewDataChart v-if="isShowChart" :chart-datas="chartData" :legends="legends"
:axis-data="Array.from(axisData)" ref="chartRef"/>
</div>
8 months ago
</main>
</el-drawer>
</div>
</template>
<script setup lang="ts">
3 months ago
import NewDataChart from '../../component/newDataChart.vue'
import PointGroupTree from './PointGroupTree.vue'
import type { IMyPoint, IOfflineDevice, IOnlineDevice } from '../../type'
8 months ago
import {
getDeviceDetails,
getPointGroup,
8 months ago
getPoints,
type IGetDeviceDataParams,
type IPointGroupOV,
type IPointGroupParams,
8 months ago
type IPointsParams,
type ISite,
8 months ago
} from '@/api/module/transfer'
import { useMessage } from '@/composables/useMessage'
import dayjs from 'dayjs'
3 months ago
import EdfsWrap from "@/components/Edfs-wrap.vue";
import { bms_cellRewriteName } from "@/views/stationData/utils";
import { debounce } from 'lodash-es'
import {
type DeviceFetchInfo,
useFetcher
} from "@/views/stationData/transfer/components/useFetcher";
type ChartParamsMap = {
name: string
columns: IMyPoint[]
}
const time = ref<[string, string]>()
const env = import.meta.env
const num = ref<number>()
const diffHours = ref<number>()
const refreshKey = ref(0)
const handleInput = debounce(async (val: number) => {
if (diffHours.value && val > diffHours.value) {
num.value = diffHours.value
refreshKey.value++
await nextTick()
} else if (val < 1) {
num.value = 1
refreshKey.value++
await nextTick()
} else {
num.value = val
}
const device = curDevice.value as IOfflineDevice
const endTime =
device.end_time ?
dayjs(device.end_time).valueOf() : dayjs().valueOf()
const startTime = dayjs(endTime).subtract(num.value, 'hour').valueOf()
time.value = [dayjs(startTime).format('YYYY-MM-DD HH:mm:ss'), dayjs(endTime).format('YYYY-MM-DD HH:mm:ss')]
}, 300)
8 months ago
const isShowDrawer = defineModel<boolean>()
const title = computed(() =>
(
props.isTransfer ?
'数据详情' : env.VITE_APP_ENV === 'local'
? `已导出数据详情` : '已导入数据详情'
)
)
8 months ago
const message = useMessage()
const pointGroupTreeRef = ref<InstanceType<typeof PointGroupTree>>()
8 months ago
const props = defineProps<{
siteInfo: ISite | null
8 months ago
isTransfer: boolean
}>()
const emit = defineEmits(['on-save'])
const pointList = ref<IMyPoint[]>([])
8 months ago
const curDevice = ref<IOfflineDevice | IOnlineDevice>()
async function open(device: IOfflineDevice | IOnlineDevice) {
8 months ago
curDevice.value = device
onCollapse(false)
await loadPointGroup()
.then(async () => {
await loadDeviceDetails()
})
.catch(() => {
message.error('获取点位组数据失败')
fullscreenLoading.value?.close()
})
const deviceOff = curDevice.value as IOfflineDevice
if (!deviceOff.start_time || !deviceOff.end_time) {
diffHours.value = 168 // 默认一周时间
return
}
const start = dayjs(deviceOff.start_time)
const end = dayjs(deviceOff.end_time)
diffHours.value = end.diff(start, "hour") + 1;
8 months ago
}
const loadingPoints = ref(false)
8 months ago
async function loadPoints() {
if (!curGroup.value?.type) {
message.error('请先选择点位组')
return
}
const params: IPointsParams = {
type: curGroup.value.type,
}
8 months ago
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
8 months ago
params.isLocal = true
}
loadingPoints.value = true
8 months ago
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,
6 months ago
unit: i.unit || '',
parentName: curGroupName.value,
8 months ago
}))
}
loadingPoints.value = false
8 months ago
return res
}
8 months ago
async function loadDeviceDetails() {
if (!fullscreenLoading.value) {
openFullScreen()
8 months ago
}
3 months ago
const pointsRes = await loadPoints()
if (!pointsRes || pointsRes.code !== 0) {
8 months ago
message.error('获取点位数据失败')
fullscreenLoading.value?.close()
8 months ago
return
}
isShowDrawer.value = true
groupChangeLoading.value = false
fullscreenLoading.value?.close()
}
3 months ago
const chartData = reactive(new Map<string, any[]>())
const axisData = new Set<string>()
6 months ago
const legends = ref<{ addr: string; label: string, unit: string }[]>([])
const loadingChart = ref(false)
const loading = ref(false)
async function loadPointChartData(params: IGetDeviceDataParams, abort?: AbortController) {
return await getDeviceDetails(params, abort)
}
function buildOptions(groupId: string): Omit<IGetDeviceDataParams, 'limit' | 'offset'> {
const timeArr = time.value as [string, string]
const currentGroup = chartParamsMap.get(groupId)
if (!currentGroup) {
return {} as Omit<IGetDeviceDataParams, 'limit' | 'offset'>
}
const options: Omit<IGetDeviceDataParams, 'limit' | 'offset'> = {
columns: ['ts', ...currentGroup.columns.map(column => column.addr)],
name: groupId,
startTime: timeArr[0],
endTime: timeArr[1],
3 months ago
isLocal: !props.isTransfer,
host: props.isTransfer ? (curDevice.value as IOnlineDevice).clientIp : '',
}
if (env.VITE_APP_ENV !== 'local' || !props.isTransfer) {
options.site_id = props.siteInfo!.name || ''
options.device_id = curDevice.value?.sn || ''
}
return options
}
const pointInfoData = ref<DeviceFetchInfo[]>([])
const { devices, isFetching, startFetching, abortAll } = useFetcher(loadPointChartData)
watch(devices, (newValue) => {
if (!newValue) return
pointInfoData.value = newValue
loading.value = false
}, { deep: true })
async function loadChardData2() {
loading.value = true
clearData()
const groupIds = Array.from(chartParamsMap.keys())
if (!groupIds.length) {
loading.value = false
message.error('请先选择点位')
return
}
await startFetching(groupIds, buildOptions, 1000)
if (!devices.value || !devices.value.length) {
loading.value = false
clearData()
message.error('获取设备数据失败')
return
}
for (const device of devices.value) {
setChartData2(device.id, device.data)
}
}
function setChartData2(groupName: string, data: any[]) {
const chartParams = chartParamsMap.get(groupName)
if (!chartParams) return
for (const column of chartParams.columns) {
const groupInfo = chartGroupMap.get(groupName)
if (!groupInfo) continue
legends.value.push({
addr: `${groupName}-${column.addr}`,
label: `${groupInfo.cnName}-${column.label}`,
unit: column.unit || ''
})
}
data.forEach((data: any[]) => {
const [ts, val, addr] = data
const time = dayjs(Number(ts)).format('YYYY-MM-DD HH:mm:ss.SSS')
if (addr) {
const colData = chartData.get(`${groupName}-${addr}`)
const value = Number(Number(val).toFixed(5))
if (colData) {
colData.push([time, value])
} else {
chartData.set(`${groupName}-${addr}`, [[time, value]])
}
}
})
8 months ago
}
function handleBeforeClose(done: () => void) {
ElMessageBox.confirm('你确定要关闭吗?')
.then(() => {
if (isFetching) {
abortAll()
}
time.value = undefined
isShowDrawer.value = false
clearData()
fullscreenLoading.value = null
chartParamsMap.clear()
chartGroupMap.clear()
done()
})
.catch(() => {
})
8 months ago
}
function clearData() {
legends.value = []
pointInfoData.value = []
3 months ago
chartData.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<IPointGroupOV>()
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]
curGroupName.value = res.data[0]?.name
pointGroup.value = Array.isArray(res?.data) ? bms_cellRewriteName(res.data) : []
return Promise.resolve()
} else {
return Promise.reject()
}
}
const groupChangeLoading = ref<boolean>(false)
function onGroupChange(item: IPointGroupOV) {
console.log(item)
if (!item?.type) {
return
}
groupChangeLoading.value = true
curGroup.value = item
curGroupName.value = item.name
loadDeviceDetails()
}
const chartParamsMap = new Map<string, ChartParamsMap>()
const chartGroupMap = new Map<string, IPointGroupOV>()
function onchangePoints(checkPoints: IMyPoint[]) {
if (!curGroup?.value || !curGroupName?.value) return message.error('请选择设备')
const currentCheckPoints = checkPoints.filter((r: any) => r.parentName === curGroupName.value)
chartParamsMap.set(curGroupName.value, {
name: curGroupName.value,
columns: currentCheckPoints
})
chartGroupMap.set(curGroupName.value, curGroup.value)
if (!currentCheckPoints.length) {
chartParamsMap.delete(curGroupName.value)
chartGroupMap.delete(curGroupName.value)
}
}
const disabledDate = (time: any) => {
const current = dayjs(time)
if (props.isTransfer) return current.isAfter(dayjs(), 'day')
try {
const deviceOff = curDevice.value as IOfflineDevice
const start = dayjs(deviceOff.start_time).format('YYYY-MM-DD')
const end = dayjs(deviceOff.end_time).format('YYYY-MM-DD')
return current.isBefore(start, 'day') || current.isAfter(end, 'day')
} catch (e) {
console.log('disableDate error', e)
return current.isAfter(dayjs(), 'day')
}
}
const collapsed = ref(false)
function onCollapse(value?: boolean) {
collapsed.value = value ?? !collapsed.value
}
const getBeforeMonth = dayjs().startOf('month').subtract(1, 'month').startOf('month').toDate()
8 months ago
defineExpose({
open,
openFullScreen,
8 months ago
})
</script>
<style scoped lang="scss">
.fault-rule-drawer {
font-size: 16px;
:deep(.edfs-wrap) {
width: auto;
8 months ago
}
:deep(.el-drawer__header) {
color: var(--text-color);
8 months ago
}
}
</style>