feat(pages): call-log-page

This commit is contained in:
yangsy
2025-11-18 20:22:26 +08:00
parent 799a5af857
commit 0c3e9c24f9
7 changed files with 288 additions and 21 deletions

View File

@@ -7,6 +7,7 @@ import type { NdmKeyboardVO } from './video/ndm-keyboard';
import type { NdmMediaServerVO } from './video/ndm-media-server';
import type { NdmVideoServerVO } from './video/ndm-video-server';
export * from './log/ndm-call-log';
export * from './log/ndm-device-alarm-log';
export * from './log/ndm-icmp-log';
export * from './log/ndm-snmp-log';

View File

@@ -0,0 +1,17 @@
import type { BaseModel, ReduceForPageQuery, ReduceForSaveVO, ReduceForUpdateVO } from '../../base';
export interface NdmCallLogVO extends BaseModel {
sourceGbId: string;
targetGbId: string;
method: string;
messageType: string;
cmdType: string;
}
export type NdmCallLogResultVO = Partial<NdmCallLogVO>;
export type NdmCallLogSaveVO = Partial<Omit<NdmCallLogVO, ReduceForSaveVO>>;
export type NdmCallLogUpdateVO = Partial<Omit<NdmCallLogVO, ReduceForUpdateVO>>;
export type NdmCallLogPageQuery = Partial<Omit<NdmCallLogVO, ReduceForPageQuery>>;

View File

@@ -1,5 +1,6 @@
export * from './export/ndm-icmp-export';
export * from './log/ndm-call-log';
export * from './log/ndm-device-alarm-log';
export * from './log/ndm-icmp-log';
export * from './log/ndm-snmp-log';

View File

@@ -0,0 +1,25 @@
import { ndmClient } from '@/apis/client';
import type { NdmCallLogPageQuery, NdmCallLogResultVO, PageParams, PageResult } from '@/apis/models';
export const postNdmCallLogPage = async (stationCode: string, pageQuery: PageParams<NdmCallLogPageQuery>) => {
const prefix = stationCode ? `/${stationCode}` : '';
const resp = await ndmClient.post<PageResult<NdmCallLogResultVO>>(`${prefix}/api/ndm/ndmCallLog/page`, pageQuery);
const [err, callLogData] = resp;
if (err || !callLogData) {
throw err;
}
return callLogData;
};
export const ndmCallLogDefaultExportByTemplate = async (stationCode: string, pageQuery: PageParams<NdmCallLogPageQuery>) => {
const endpoint = '/api/ndm/ndmCallLog/defaultExportByTemplate';
if (!stationCode) {
throw new Error('请选择车站');
}
const resp = await ndmClient.post<Blob>(`/${stationCode}${endpoint}`, pageQuery, { responseType: 'blob', retRaw: true });
const [err, data] = resp;
if (err || !data) {
throw err;
}
return data;
};

View File

@@ -118,15 +118,19 @@ const menuOptions = ref<MenuOption[]>([
// icon: renderIcon(AreaChartOutlined),
// },
{
label: () => h(RouterLink, { to: '/log/vimp-log' }, { default: () => '视频平台日志' }), // '系统日志记录'
key: '/log/vimp-log',
label: '系统日志',
key: '/log',
icon: renderIcon(FileTextFilled),
// children: [
// {
// label: () => h(RouterLink, { to: '/log/vimp-log' }, { default: () => '视频平台日志' }),
// key: '/log/vimp-log',
// },
// ],
children: [
{
label: () => h(RouterLink, { to: '/log/vimp-log' }, { default: () => '视频平台日志' }),
key: '/log/vimp-log',
},
{
label: () => h(RouterLink, { to: '/log/call-log' }, { default: () => '上级调用日志' }),
key: '/log/call-log',
},
],
},
{
label: () => h(RouterLink, { to: '/debug' }, { default: () => '调试' }),

218
src/pages/call-log-page.vue Normal file
View File

@@ -0,0 +1,218 @@
<script setup lang="ts">
import type { NdmCallLogResultVO } from '@/apis/models';
import { ndmCallLogDefaultExportByTemplate, postNdmCallLogPage } from '@/apis/requests';
import { useStationStore } from '@/stores/station';
import { downloadByData } from '@/utils/download';
import { useMutation } from '@tanstack/vue-query';
import dayjs from 'dayjs';
import {
NButton,
NDataTable,
NDatePicker,
NForm,
NFormItemGi,
NGrid,
NGridItem,
NSelect,
NSpace,
NTag,
type DataTableColumns,
type DataTableRowData,
type PaginationProps,
type SelectOption,
} from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, h, reactive, ref, watch, watchEffect } from 'vue';
const stationStore = useStationStore();
const { stationList, onlineStationList } = storeToRefs(stationStore);
const stationSelectOptions = computed(() => {
return stationList.value.map<SelectOption>((station) => ({
label: station.name,
value: station.code,
disabled: !station.online,
}));
});
const searchFields = reactive({
stationCode: undefined as string | undefined,
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')] as [string, string],
});
const resetSearchFields = () => {
searchFields.stationCode = stationList.value.find((station) => station.online)?.code;
searchFields.createdTime = [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')];
};
const getExtraFields = () => {
const createdTime_precisest = searchFields.createdTime[0];
const createdTime_preciseed = searchFields.createdTime[1];
return {
createdTime_precisest,
createdTime_preciseed,
};
};
const searchFieldsChanged = ref(false);
watch(searchFields, () => {
searchFieldsChanged.value = true;
});
const tableColumns: DataTableColumns<NdmCallLogResultVO> = [
{ title: '时间', key: 'createdTime' },
{ title: '调用者国标码', key: 'sourceGbId' },
{ title: '被调用设备国标码', key: 'targetGbId' },
{ title: '调用方法', key: 'method' },
{ title: '消息类型', key: 'messageType' },
{ title: '操作类型', key: 'cmdType' },
];
const tableData = ref<DataTableRowData[]>([]);
const tablePagination = reactive<PaginationProps>({
showSizePicker: true,
page: 1,
pageSize: 10,
pageSizes: [5, 10, 20, 50, 80, 100],
itemCount: 0,
prefix: ({ itemCount }) => {
return h('div', {}, { default: () => `${itemCount}` });
},
onUpdatePage: (page: number) => {
tablePagination.page = page;
getCallLogList();
},
onUpdatePageSize: (pageSize: number) => {
tablePagination.pageSize = pageSize;
tablePagination.page = 1;
getCallLogList();
},
});
const { mutate: getCallLogList, isPending: tableLoading } = useMutation({
mutationFn: async () => {
if (!searchFields.stationCode) throw Error('请选择车站');
const res = await postNdmCallLogPage(searchFields.stationCode, {
model: {},
extra: getExtraFields(),
current: tablePagination.page ?? 1,
size: tablePagination.pageSize ?? 10,
order: 'descending',
sort: 'id',
});
return res;
},
onSuccess: (res) => {
const { records, size, total } = res;
tablePagination.pageSize = parseInt(size);
tablePagination.itemCount = parseInt(total);
tableData.value = records;
},
onError: (error) => {
console.error(error);
window.$message.error(error.message);
},
});
const onClickReset = () => {
resetSearchFields();
tablePagination.page = 1;
tablePagination.pageSize = 10;
tablePagination.itemCount = 0;
getCallLogList();
};
const onClickQuery = () => {
if (searchFieldsChanged.value) {
tablePagination.page = 1;
tablePagination.pageSize = 10;
searchFieldsChanged.value = false;
}
getCallLogList();
};
const { mutate: exportTableData, isPending: exporting } = useMutation({
mutationFn: async () => {
if (!searchFields.stationCode) throw Error('请选择车站');
const data = await ndmCallLogDefaultExportByTemplate(searchFields.stationCode, {
model: {},
extra: getExtraFields(),
current: tablePagination.page ?? 1,
size: tablePagination.pageSize ?? 10,
order: 'descending',
sort: 'id',
});
return data;
},
onSuccess: (data) => {
const time = dayjs().format('YYYY-MM-DD_HH-mm-ss');
downloadByData(data, `上级调用日志_${time}.xlsx`);
},
onError: (error) => {
console.error(error);
window.$message.error(error.message);
},
});
const defaultStation = computed(() => onlineStationList.value.at(0));
watchEffect(() => {
if (defaultStation.value?.code && !searchFields.stationCode) {
searchFields.stationCode = defaultStation.value.code;
getCallLogList();
}
});
</script>
<template>
<!-- 容器上下布局表格自适应剩余高度 -->
<div style="height: 100%; display: flex; flex-direction: column">
<!-- 查询面板 -->
<div style="flex: 0 0 auto; padding: 8px">
<NForm>
<NGrid :cols="3" :x-gap="24">
<NFormItemGi :span="1" label="车站" label-placement="left">
<NSelect
v-model:value="searchFields.stationCode"
:options="stationSelectOptions"
:render-label="
(option: SelectOption) => {
return [
h(NTag, { type: option.disabled ? 'error' : 'success', size: 'tiny' }, { default: () => (option.disabled ? '离线' : '在线') }),
h('span', {}, { default: () => `${option.label}` }),
];
}
"
:multiple="false"
clearable
/>
</NFormItemGi>
<NFormItemGi :span="1" label="时间" label-placement="left">
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" @update:value="undefined" />
</NFormItemGi>
</NGrid>
<!-- 按钮 -->
<NGrid :cols="1">
<NGridItem>
<NSpace>
<NButton @click="onClickReset">重置</NButton>
<NButton type="primary" :loading="tableLoading" @click="onClickQuery">查询</NButton>
</NSpace>
</NGridItem>
</NGrid>
</NForm>
</div>
<!-- 工具栏:横向、右对齐按钮) -->
<div style="flex: 0 0 auto; display: flex; align-items: center; padding: 8px">
<div style="font-size: medium">视频平台日志</div>
<NSpace style="margin-left: auto">
<NButton type="primary" :loading="exporting" @click="() => exportTableData()">导出</NButton>
</NSpace>
</div>
<!-- 表格区域:填满剩余空间 -->
<div style="flex: 1 1 auto; min-height: 0; padding: 8px">
<NDataTable remote :columns="tableColumns" :data="tableData" :pagination="tablePagination" :loading="tableLoading" :single-line="false" flex-height style="height: 100%" />
</div>
</div>
</template>
<style scoped lang="scss"></style>

View File

@@ -31,12 +31,15 @@ const router = createRouter({
},
{
path: 'log',
redirect: '/log/vimp-log',
children: [
{
path: 'vimp-log',
component: () => import('@/pages/vimp-log-page.vue'),
},
{
path: 'call-log',
component: () => import('@/pages/call-log-page.vue'),
},
],
},
{
@@ -62,32 +65,30 @@ const router = createRouter({
const whiteList = ['/debug'];
router.beforeEach((to, from, next) => {
router.beforeEach((to) => {
const userStore = useUserStore();
const isAuthed = !!userStore.userLoginResult?.token;
// 放行白名单
const inWhiteList = whiteList.some((path) => to.path.startsWith(path));
if (inWhiteList) {
next();
return;
return true;
}
// 已登录用户不允许进入登录页(手动访问 /login 会重定向到首页)
if (to.path === '/login') {
if (isAuthed) {
next({ path: '/' });
return { path: '/' };
} else {
next();
return true;
}
return;
}
// 其它路由按登录态控制
if (!isAuthed) {
next('/login');
} else {
next();
// 其它路由按登录态控制
if (!isAuthed) {
return { path: '/login' };
} else {
return true;
}
}
});