feat: 增加计数功能

This commit is contained in:
foxhui
2026-01-11 19:25:34 +08:00
Unverified
parent 3183f7bdc5
commit aa730e7f22
29 changed files with 446 additions and 30 deletions
+18 -1
View File
@@ -10,7 +10,8 @@ import {
LineChartOutlined,
SyncOutlined,
ExclamationCircleOutlined,
CheckCircleOutlined
CheckCircleOutlined,
CloseCircleOutlined
} from '@ant-design/icons-vue';
const systemStore = useSystemStore();
@@ -205,6 +206,22 @@ onUnmounted(() => {
</a-statistic>
</a-col>
</a-row>
<a-row :gutter="16" style="margin-top: 16px">
<a-col :span="12">
<a-statistic title="今日成功" :value="systemStore.stats.success || 0">
<template #prefix>
<CheckCircleOutlined style="color: #52c41a" />
</template>
</a-statistic>
</a-col>
<a-col :span="12">
<a-statistic title="今日失败" :value="systemStore.stats.failed || 0">
<template #prefix>
<CloseCircleOutlined style="color: #ff4d4f" />
</template>
</a-statistic>
</a-col>
</a-row>
</a-card>
</a-col>
</a-row>
+171 -2
View File
@@ -9,7 +9,8 @@ import {
WarningOutlined,
CloseCircleOutlined,
InfoCircleOutlined,
BugOutlined
BugOutlined,
CheckCircleOutlined
} from '@ant-design/icons-vue';
import { message, Modal } from 'ant-design-vue';
@@ -23,6 +24,11 @@ const refreshInterval = ref(null);
const searchText = ref('');
const levelFilter = ref('all');
// 统计查询相关
const dateRange = ref([]);
const rangeStats = ref({ success: 0, failed: 0, days: 0 });
const statsLoading = ref(false);
// 日志级别配置
const levelConfig = {
'INFO': { color: '#1890ff', icon: InfoCircleOutlined },
@@ -141,6 +147,61 @@ const toggleAutoRefresh = (newState) => {
}
};
// 查询日期范围统计
const fetchRangeStats = async () => {
if (!dateRange.value || dateRange.value.length !== 2) {
rangeStats.value = { success: 0, failed: 0, days: 0 };
return;
}
statsLoading.value = true;
try {
const [start, end] = dateRange.value;
const res = await fetch(
`/admin/stats/range?start=${start.format('YYYY-MM-DD')}&end=${end.format('YYYY-MM-DD')}`,
{ headers: settingsStore.getHeaders() }
);
if (res.ok) {
rangeStats.value = await res.json();
}
} catch (e) {
message.error('获取统计失败');
} finally {
statsLoading.value = false;
}
};
// 删除选定范围的统计数据
const clearRangeStats = () => {
if (!dateRange.value || dateRange.value.length !== 2) {
message.warning('请先选择日期范围');
return;
}
Modal.confirm({
title: '确认删除',
content: `确定要删除 ${dateRange.value[0].format('YYYY-MM-DD')}${dateRange.value[1].format('YYYY-MM-DD')} 的统计数据吗?`,
okText: '删除',
okType: 'danger',
cancelText: '取消',
async onOk() {
try {
const [start, end] = dateRange.value;
const res = await fetch(
`/admin/stats/range?start=${start.format('YYYY-MM-DD')}&end=${end.format('YYYY-MM-DD')}`,
{ method: 'DELETE', headers: settingsStore.getHeaders() }
);
if (res.ok) {
message.success('统计数据已删除');
rangeStats.value = { success: 0, failed: 0, days: 0 };
}
} catch (e) {
message.error('删除失败');
}
}
});
};
onMounted(() => {
fetchLogs();
});
@@ -153,7 +214,47 @@ onUnmounted(() => {
</script>
<template>
<a-card title="系统日志" :bordered="false">
<!-- 统计查询面板 -->
<a-card title="请求统计" :bordered="false">
<template #extra>
<a-button type="link" danger size="small" @click="clearRangeStats"
:disabled="!dateRange || dateRange.length !== 2">
<template #icon>
<DeleteOutlined />
</template>
删除统计
</a-button>
</template>
<div class="stats-content">
<a-range-picker v-model:value="dateRange" :format="'YYYY-MM-DD'" :placeholder="['开始日期', '结束日期']"
size="small" style="width: 240px" @change="fetchRangeStats" />
<a-divider type="vertical" style="height: 32px; margin: 0 16px" />
<a-spin :spinning="statsLoading" size="small">
<div class="stats-numbers">
<div class="stat-item success">
<CheckCircleOutlined />
<span class="stat-value">{{ rangeStats.success }}</span>
<span class="stat-label">成功</span>
</div>
<div class="stat-item error">
<CloseCircleOutlined />
<span class="stat-value">{{ rangeStats.failed }}</span>
<span class="stat-label">失败</span>
</div>
<div class="stat-item neutral">
<span class="stat-value">{{ rangeStats.days }}</span>
<span class="stat-label"></span>
</div>
</div>
</a-spin>
</div>
</a-card>
<!-- 系统日志 -->
<a-card title="系统日志" :bordered="false" style="margin-top: 24px">
<!-- 工具栏 -->
<div class="toolbar">
<!-- 第一行级别筛选和操作按钮 -->
@@ -305,4 +406,72 @@ onUnmounted(() => {
max-width: 300px;
}
}
/* 统计内容样式 */
.stats-content {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.stats-numbers {
display: flex;
align-items: center;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
background: #fafafa;
border-radius: 6px;
transition: all 0.2s;
}
.stat-item:hover {
background: #f0f0f0;
}
.stat-item.success {
color: #52c41a;
}
.stat-item.error {
color: #ff4d4f;
}
.stat-item.neutral {
color: #8c8c8c;
}
.stat-value {
font-size: 18px;
font-weight: 600;
font-family: 'SF Mono', 'Monaco', monospace;
}
.stat-label {
font-size: 12px;
color: #8c8c8c;
}
/* 响应式:小屏幕统计面板垂直布局 */
@media (max-width: 576px) {
.stats-content {
flex-direction: column;
align-items: flex-start;
}
.stats-content .ant-divider {
display: none;
}
.stats-numbers {
margin-top: 8px;
}
}
</style>
+3 -1
View File
@@ -28,7 +28,9 @@ export const useSystemStore = defineStore('system', {
successRate: 0,
activeWorkers: 0,
totalWorkers: 0,
avgResponseTime: 0
avgResponseTime: 0,
success: 0,
failed: 0
}
}),