216 lines
7.8 KiB
Vue
216 lines
7.8 KiB
Vue
<script setup lang="ts">
|
|
import type { NdmNvrResultVO, NdmRecordCheck, RecordItem, Station } from '@/apis';
|
|
import { getChannelListApi, getRecordCheckApi as getRecordCheckByParentIdApi, reloadAllRecordCheckApi as reloadAllRecordCheckApi, reloadRecordCheckApi as reloadRecordCheckByGbIdApi } from '@/apis';
|
|
import { exportRecordDiagCsv, transformRecordChecks } from '@/helper';
|
|
import { useStationStore } from '@/stores';
|
|
import { useMutation } from '@tanstack/vue-query';
|
|
import { DownloadOutlined } from '@vicons/antd';
|
|
import { RefreshOutline } from '@vicons/ionicons5';
|
|
import dayjs from 'dayjs';
|
|
import { NButton, NCard, NFlex, NIcon, NPagination, NPopconfirm, NPopover, NRadioButton, NRadioGroup, NTooltip, useThemeVars } from 'naive-ui';
|
|
import { computed, onMounted, ref, toRefs } from 'vue';
|
|
|
|
const props = defineProps<{
|
|
stationCode: Station['code'];
|
|
ndmNvr: NdmNvrResultVO;
|
|
}>();
|
|
|
|
const { stationCode, ndmNvr } = toRefs(props);
|
|
|
|
const recordChecks = ref<NdmRecordCheck[]>([]);
|
|
|
|
const lossInput = ref<number>(0);
|
|
|
|
const recordDiags = computed(() => {
|
|
return transformRecordChecks(recordChecks.value).filter((recordDiag) => {
|
|
if (lossInput.value === 0) {
|
|
return true;
|
|
} else if (lossInput.value === 1) {
|
|
return recordDiag.lostChunks.length > 0;
|
|
} else if (lossInput.value === 2) {
|
|
return recordDiag.lostChunks.length === 0;
|
|
}
|
|
return false;
|
|
});
|
|
});
|
|
|
|
const { mutate: getRecordCheckByParentId, isPending: loading } = useMutation({
|
|
mutationFn: async () => {
|
|
const checks = await getRecordCheckByParentIdApi(ndmNvr.value, 90, [], { stationCode: stationCode.value });
|
|
return checks;
|
|
},
|
|
onSuccess: (checks) => {
|
|
recordChecks.value = checks;
|
|
},
|
|
onError: (error) => {
|
|
console.error(error);
|
|
window.$message.error(error.message);
|
|
},
|
|
});
|
|
|
|
const { mutate: reloadAllRecordCheck, isPending: reloading } = useMutation({
|
|
mutationFn: async () => {
|
|
await reloadAllRecordCheckApi(90, { stationCode: stationCode.value });
|
|
},
|
|
onSuccess: () => {
|
|
window.$message.success('正在逐步刷新中,请稍后点击刷新按钮查看');
|
|
},
|
|
onError: (error) => {
|
|
console.error(error);
|
|
window.$message.error(error.message);
|
|
},
|
|
});
|
|
|
|
const onExportRecordCheck = () => {
|
|
const stationStore = useStationStore();
|
|
const stationName = stationStore.stationList.find((station) => station.code === stationCode.value)?.name ?? '';
|
|
exportRecordDiagCsv(recordDiags.value, stationName);
|
|
};
|
|
|
|
const page = ref(1);
|
|
const pageSize = ref(10);
|
|
|
|
const pagedRecordDiags = computed(() => {
|
|
const startIndex = (page.value - 1) * pageSize.value;
|
|
const endIndex = page.value * pageSize.value;
|
|
return recordDiags.value.slice(startIndex, endIndex);
|
|
});
|
|
|
|
const getLostChunkDOMStyle = (lostChunk: RecordItem, duration: RecordItem) => {
|
|
const chunk = dayjs(lostChunk.endTime).diff(dayjs(lostChunk.startTime));
|
|
const offset = dayjs(lostChunk.startTime).diff(dayjs(duration.startTime));
|
|
const total = dayjs(duration.endTime).diff(dayjs(duration.startTime));
|
|
return {
|
|
left: `${(offset / total) * 100}%`,
|
|
width: `${(chunk / total) * 100}%`,
|
|
};
|
|
};
|
|
|
|
const { mutate: reloadRecordCheckByGbId } = useMutation({
|
|
mutationFn: async (params: { gbCode: string }) => {
|
|
const channelList = await getChannelListApi(ndmNvr.value, { stationCode: stationCode.value });
|
|
const channel = channelList.find((channel) => channel.code === params.gbCode);
|
|
if (!channel) throw new Error('通道不存在');
|
|
window.$message.loading('刷新耗时较长, 请不要多次刷新, 并耐心等待...', {
|
|
duration: 1000 * 60 * 60 * 24 * 300,
|
|
});
|
|
const isSuccess = await reloadRecordCheckByGbIdApi(channel, 90, { stationCode: stationCode.value });
|
|
window.$message.destroyAll();
|
|
if (isSuccess) {
|
|
window.$message.success('刷新成功');
|
|
} else {
|
|
window.$message.error('刷新失败');
|
|
}
|
|
},
|
|
onSuccess: () => {
|
|
getRecordCheckByParentId();
|
|
},
|
|
onError: (error) => {
|
|
console.error(error);
|
|
window.$message.error(error.message);
|
|
},
|
|
});
|
|
|
|
onMounted(() => {
|
|
getRecordCheckByParentId();
|
|
});
|
|
|
|
const themeVars = useThemeVars();
|
|
</script>
|
|
|
|
<template>
|
|
<NCard size="small" hoverable>
|
|
<template #header>
|
|
<NFlex align="center" :size="24">
|
|
<div>录像诊断</div>
|
|
<NPopconfirm @positive-click="() => reloadAllRecordCheck()">
|
|
<template #trigger>
|
|
<NButton secondary size="small" :loading="reloading">更新所有通道录像诊断</NButton>
|
|
</template>
|
|
<template #default>
|
|
<span>确认更新所有通道录像诊断吗?</span>
|
|
</template>
|
|
</NPopconfirm>
|
|
</NFlex>
|
|
</template>
|
|
<template #header-extra>
|
|
<NFlex>
|
|
<NTooltip trigger="hover">
|
|
<template #trigger>
|
|
<NButton size="small" quaternary circle :loading="loading" @click="() => getRecordCheckByParentId()">
|
|
<template #icon>
|
|
<NIcon>
|
|
<RefreshOutline />
|
|
</NIcon>
|
|
</template>
|
|
</NButton>
|
|
</template>
|
|
<template #default>
|
|
<span>刷新数据</span>
|
|
</template>
|
|
</NTooltip>
|
|
<NTooltip trigger="hover">
|
|
<template #trigger>
|
|
<NButton size="small" quaternary circle @click="onExportRecordCheck">
|
|
<template #icon>
|
|
<NIcon>
|
|
<DownloadOutlined />
|
|
</NIcon>
|
|
</template>
|
|
</NButton>
|
|
</template>
|
|
<template #default>
|
|
<span>导出录像诊断</span>
|
|
</template>
|
|
</NTooltip>
|
|
</NFlex>
|
|
</template>
|
|
<template #default>
|
|
<NFlex justify="flex-end" style="margin-bottom: 6px">
|
|
<NRadioGroup size="small" v-model:value="lossInput">
|
|
<NRadioButton label="全部" :value="0" />
|
|
<NRadioButton label="有缺失" :value="1" />
|
|
<NRadioButton label="无缺失" :value="2" />
|
|
</NRadioGroup>
|
|
</NFlex>
|
|
<template v-for="{ gbCode, channelName, recordDuration, lostChunks } in pagedRecordDiags" :key="gbCode">
|
|
<div style="display: flex; justify-content: space-between">
|
|
<div>
|
|
<span>{{ channelName }}</span>
|
|
<span>{{ '\u3000' }}</span>
|
|
<span>{{ recordDuration.startTime }} - {{ recordDuration.endTime }}</span>
|
|
</div>
|
|
<NPopconfirm trigger="click" @positive-click="() => reloadRecordCheckByGbId({ gbCode })">
|
|
<template #trigger>
|
|
<NButton ghost size="tiny" type="info">刷新</NButton>
|
|
</template>
|
|
<template #default>
|
|
<span>是否确认刷新?</span>
|
|
</template>
|
|
</NPopconfirm>
|
|
</div>
|
|
<div style="position: relative; height: 24px; margin: 2px 0" :style="{ backgroundColor: lostChunks.length > 0 ? themeVars.infoColor : themeVars.successColor }">
|
|
<template v-for="{ startTime, endTime } in lostChunks" :key="`${startTime}-${endTime}`">
|
|
<NPopover trigger="hover">
|
|
<template #trigger>
|
|
<div style="position: absolute; height: 100%; cursor: pointer; background-color: #eee" :style="getLostChunkDOMStyle({ startTime, endTime }, recordDuration)" />
|
|
</template>
|
|
<template #default>
|
|
<div>开始时间:{{ dayjs(startTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
|
|
<div>结束时间:{{ dayjs(endTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
|
|
</template>
|
|
</NPopover>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
<template #action>
|
|
<NFlex justify="flex-end">
|
|
<NPagination size="small" :page="page" :page-size="pageSize" :page-count="Math.ceil(recordDiags.length / pageSize)" @update:page="(p) => (page = p)" />
|
|
</NFlex>
|
|
</template>
|
|
</NCard>
|
|
</template>
|
|
|
|
<style scoped lang="scss"></style>
|