由于本次代码变更内容为空,无法生成有效的提交信息。请提供具体的代码变更内容以便生成合适的提交信息。

This commit is contained in:
2025-09-10 20:55:32 +08:00
parent 72f254e6ba
commit 0197a903f1
6 changed files with 365 additions and 160 deletions

View File

@@ -12,9 +12,11 @@
},
"dependencies": {
"@ant-design/icons-vue": "^6.1.0",
"@types/echarts": "^4.9.22",
"ant-design-vue": "^4.0.0",
"axios": "^1.4.0",
"dayjs": "^1.11.0",
"echarts": "^6.0.0",
"lodash-es": "^4.17.21",
"pinia": "^2.1.0",
"vue": "^3.3.0",

View File

@@ -188,6 +188,17 @@ export const mockSystemAPI = {
todayUserCount: 5,
todayOrderCount: 3
})
},
getSystemMonitorData: async () => {
await delay(500)
return createSuccessResponse({
cpuUsage: Math.floor(Math.random() * 100),
memoryUsage: Math.floor(Math.random() * 100),
networkTraffic: Math.floor(Math.random() * 1000),
diskUsage: Math.floor(Math.random() * 100),
timestamp: new Date().toISOString()
})
}
}

View File

@@ -105,9 +105,29 @@ export interface SystemStats {
export const getSystemStats = () =>
request.get<{ success: boolean; code: number; message: string; data: SystemStats }>('/admin/system/stats')
// 获取系统日志
export const getSystemLogs = (params?: { page?: number; limit?: number; level?: string }) =>
request.get<{ success: boolean; code: number; message: string; data: { logs: any[]; pagination: any } }>('/admin/system/logs', { params })
// 定义系统日志相关类型
export interface SystemLog {
id: string
level: 'info' | 'warn' | 'error' | 'debug'
message: string
timestamp: string
module: string
userId?: string
ip?: string
}
export interface SystemLogQueryParams {
page?: number
limit?: number
level?: string
module?: string
startDate?: string
endDate?: string
}
// 获取系统日志列表
export const getSystemLogs = (params?: SystemLogQueryParams) =>
request.get<{ success: boolean; code: number; message: string; data: { logs: SystemLog[]; pagination: any } }>('/admin/system/logs', { params })
// 获取系统设置
export const getSystemSettings = () =>
@@ -117,6 +137,19 @@ export const getSystemSettings = () =>
export const updateSystemSettings = (data: any) =>
request.put<{ success: boolean; code: number; message: string }>(`/admin/system/settings`, data)
// 定义系统监控数据类型
export interface SystemMonitorData {
cpuUsage: number
memoryUsage: number
networkTraffic: number
diskUsage: number
timestamp: string
}
// 获取系统监控数据
export const getSystemMonitorData = () =>
request.get<{ success: boolean; code: number; message: string; data: SystemMonitorData }>('/admin/system/monitor')
// 开发环境使用模拟数据
const systemAPI = createMockWrapper({
getServices,
@@ -129,9 +162,10 @@ const systemAPI = createMockWrapper({
getSystemConfigs,
updateSystemConfig,
getSystemStats,
getSystemLogs,
getSystemLogs, // 添加日志API
getSystemSettings,
updateSystemSettings
updateSystemSettings,
getSystemMonitorData
}, mockSystemAPI)
export default systemAPI

View File

@@ -20,7 +20,7 @@
<a-card>
<a-statistic
title="总用户数"
:value="dashboardData.userCount"
:value="dashboardData.stats.userCount"
:precision="0"
suffix="人"
>
@@ -34,7 +34,7 @@
<a-card>
<a-statistic
title="商家数量"
:value="dashboardData.merchantCount"
:value="dashboardData.stats.merchantCount"
:precision="0"
suffix="家"
>
@@ -48,7 +48,7 @@
<a-card>
<a-statistic
title="旅行计划"
:value="dashboardData.travelCount"
:value="dashboardData.stats.travelCount"
:precision="0"
suffix="个"
>
@@ -62,7 +62,7 @@
<a-card>
<a-statistic
title="动物认领"
:value="dashboardData.animalCount"
:value="dashboardData.stats.animalCount"
:precision="0"
suffix="只"
>
@@ -77,18 +77,12 @@
<a-row :gutter="16" class="chart-row">
<a-col :span="12">
<a-card title="用户增长趋势" class="chart-card">
<div class="chart-placeholder">
<BarChartOutlined />
<p>用户增长图表</p>
</div>
<div ref="userGrowthChart" style="height: 300px;"></div>
</a-card>
</a-col>
<a-col :span="12">
<a-card title="订单统计" class="chart-card">
<div class="chart-placeholder">
<PieChartOutlined />
<p>订单分布图表</p>
</div>
<div ref="orderStatsChart" style="height: 300px;"></div>
</a-card>
</a-col>
</a-row>
@@ -96,7 +90,7 @@
<a-row :gutter="16" class="activity-row">
<a-col :span="16">
<a-card title="最近活动" class="activity-card">
<a-list item-layout="horizontal" :data-source="recentActivities">
<a-list item-layout="horizontal" :data-source="dashboardData.activities">
<template #renderItem="{ item }">
<a-list-item>
<a-list-item-meta
@@ -138,24 +132,42 @@
</template>
<script setup lang="ts">
import { UserOutlined, ShopOutlined, CompassOutlined, HeartOutlined, BarChartOutlined, PieChartOutlined } from '@ant-design/icons-vue'
import { ref, onMounted } from 'vue'
import { UserOutlined, ShopOutlined, CompassOutlined, HeartOutlined } from '@ant-design/icons-vue'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { useAppStore } from '@/stores/app'
import { getSystemStats } from '@/api/system'
import { getUsers } from '@/api/user'
import { getMerchants } from '@/api/merchant'
import { getTravels } from '@/api/travel'
import { getAnimals } from '@/api/animal'
import * as echarts from 'echarts'
import {
getDashboardData,
getUserGrowthData,
getOrderStatsData
} from '@/api/dashboard'
// 定义仪表板数据结构
interface DashboardData {
userCount: number
merchantCount: number
travelCount: number
animalCount: number
orderCount: number
todayUserCount: number
todayOrderCount: number
stats: {
userCount: number
newUserCount: number
activeUserCount: number
merchantCount: number
newMerchantCount: number
activeMerchantCount: number
travelCount: number
newTravelCount: number
animalCount: number
newAnimalCount: number
orderCount: number
newOrderCount: number
orderAmount: number
onlineUserCount: number
systemLoad: number
}
activities: Array<{
id: number
title: string
description: string
avatar: string
time: string
}>
}
const appStore = useAppStore()
@@ -166,42 +178,31 @@ const startupTime = new Date().toLocaleString()
// 仪表板数据
const dashboardData = ref<DashboardData>({
userCount: 0,
merchantCount: 0,
travelCount: 0,
animalCount: 0,
orderCount: 0,
todayUserCount: 0,
todayOrderCount: 0
stats: {
userCount: 0,
newUserCount: 0,
activeUserCount: 0,
merchantCount: 0,
newMerchantCount: 0,
activeMerchantCount: 0,
travelCount: 0,
newTravelCount: 0,
animalCount: 0,
newAnimalCount: 0,
orderCount: 0,
newOrderCount: 0,
orderAmount: 0,
onlineUserCount: 0,
systemLoad: 0
},
activities: []
})
// 最近活动(暂时保持静态数据)
const recentActivities = [
{
title: '新用户注册',
description: '用户"旅行爱好者"完成了注册',
avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=1',
time: '2分钟前'
},
{
title: '旅行计划创建',
description: '用户"探险家"发布了西藏旅行计划',
avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2',
time: '5分钟前'
},
{
title: '动物认领',
description: '用户"动物之友"认领了一只羊驼',
avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=3',
time: '10分钟前'
},
{
title: '订单完成',
description: '花店"鲜花坊"完成了一笔鲜花订单',
avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=4',
time: '15分钟前'
}
]
// 图表实例
const userGrowthChart = ref<HTMLDivElement | null>(null)
const orderStatsChart = ref<HTMLDivElement | null>(null)
let userGrowthChartInstance: echarts.ECharts | null = null
let orderStatsChartInstance: echarts.ECharts | null = null
// 加载仪表板数据
const loadDashboardData = async () => {
@@ -218,54 +219,139 @@ const loadDashboardData = async () => {
}
}
// 获取系统统计信息
const statsResponse = await getSystemStats()
if (statsResponse.success) {
// 从statsResponse.data中获取统计数据
const data = statsResponse.data
dashboardData.value.userCount = data.userCount
dashboardData.value.merchantCount = data.merchantCount
dashboardData.value.travelCount = data.travelCount
dashboardData.value.animalCount = data.animalCount
dashboardData.value.orderCount = data.orderCount
dashboardData.value.todayUserCount = data.todayUserCount
dashboardData.value.todayOrderCount = data.todayOrderCount
// 获取仪表板数据
const response = await getDashboardData()
if (response.success) {
dashboardData.value = response.data
}
// 如果系统统计API不可用则使用独立的API调用来获取各类统计数据
if (!statsResponse.success) {
// 获取用户总数
const userResponse = await getUsers({ page: 1, pageSize: 1 })
if (userResponse.data.success) {
dashboardData.value.userCount = userResponse.data.pagination?.total || 0
}
// 获取商家总数
const merchantResponse = await getMerchants({ page: 1, limit: 1 })
if (merchantResponse.data.success) {
dashboardData.value.merchantCount = merchantResponse.data.pagination?.total || 0
}
// 获取旅行计划总数
const travelResponse = await getTravels({ page: 1, limit: 1 })
if (travelResponse.data.success) {
dashboardData.value.travelCount = travelResponse.data.pagination?.total || 0
}
// 获取动物总数
const animalResponse = await getAnimals({ page: 1, limit: 1 })
if (animalResponse.data.success) {
dashboardData.value.animalCount = animalResponse.data.pagination?.total || 0
}
}
// 获取图表数据并更新图表
await updateUserGrowthChart()
await updateOrderStatsChart()
} catch (error) {
console.error('加载仪表板数据失败:', error)
}
}
// 更新用户增长图表
const updateUserGrowthChart = async () => {
try {
const response = await getUserGrowthData(7)
if (response.success && userGrowthChart.value) {
// 初始化图表实例
if (!userGrowthChartInstance) {
userGrowthChartInstance = echarts.init(userGrowthChart.value)
}
// 配置图表选项
const option = {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: response.data.map((item: any) => item.date)
},
yAxis: {
type: 'value'
},
series: [{
data: response.data.map((item: any) => item.count),
type: 'line',
smooth: true,
areaStyle: {}
}]
}
// 设置图表选项
userGrowthChartInstance.setOption(option)
}
} catch (error) {
console.error('加载用户增长数据失败:', error)
}
}
// 更新订单统计图表
const updateOrderStatsChart = async () => {
try {
const response = await getOrderStatsData(7)
if (response.success && orderStatsChart.value) {
// 初始化图表实例
if (!orderStatsChartInstance) {
orderStatsChartInstance = echarts.init(orderStatsChart.value)
}
// 配置图表选项
const option = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['订单数量', '订单金额']
},
xAxis: {
type: 'category',
data: response.data.map((item: any) => item.date)
},
yAxis: [
{
type: 'value',
name: '订单数量'
},
{
type: 'value',
name: '订单金额',
position: 'right'
}
],
series: [
{
name: '订单数量',
data: response.data.map((item: any) => item.count),
type: 'bar'
},
{
name: '订单金额',
data: response.data.map((item: any) => item.amount),
type: 'line',
yAxisIndex: 1
}
]
}
// 设置图表选项
orderStatsChartInstance.setOption(option)
}
} catch (error) {
console.error('加载订单统计数据失败:', error)
}
}
// 窗口大小改变时重置图表大小
const handleResize = () => {
if (userGrowthChartInstance) {
userGrowthChartInstance.resize()
}
if (orderStatsChartInstance) {
orderStatsChartInstance.resize()
}
}
// 组件挂载时加载数据
onMounted(() => {
loadDashboardData()
window.addEventListener('resize', handleResize)
})
// 组件卸载前清理资源
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
if (userGrowthChartInstance) {
userGrowthChartInstance.dispose()
}
if (orderStatsChartInstance) {
orderStatsChartInstance.dispose()
}
})
</script>

View File

@@ -110,34 +110,7 @@
<a-col :span="12">
<a-card title="系统监控" size="small">
<div style="height: 200px;">
<div style="display: flex; flex-direction: column; height: 100%; justify-content: space-around;">
<div>
<div style="display: flex; justify-content: space-between;">
<span>CPU使用率</span>
<span>{{ mockChartData.cpu }}%</span>
</div>
<a-progress :percent="mockChartData.cpu" size="small" />
</div>
<div>
<div style="display: flex; justify-content: space-between;">
<span>内存使用率</span>
<span>{{ mockChartData.memory }}%</span>
</div>
<a-progress :percent="mockChartData.memory" size="small" status="active" />
</div>
<div>
<div style="display: flex; justify-content: space-between;">
<span>网络流量</span>
<span>{{ mockChartData.network }} KB/s</span>
</div>
<a-progress :percent="Math.min(mockChartData.network / 10, 100)" size="small" status="success" />
</div>
<div style="text-align: center; font-size: 12px; color: #666;">
更新时间: {{ mockChartData.time }}
</div>
</div>
</div>
<div style="height: 200px;" ref="monitorChartContainer"></div>
</a-card>
</a-col>
</a-row>
@@ -246,6 +219,7 @@
<script setup lang="ts">
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { message, Modal } from 'ant-design-vue'
import * as echarts from 'echarts'
import {
ReloadOutlined,
DatabaseOutlined,
@@ -262,7 +236,8 @@ import {
getDatabaseStatus,
getCacheStatus,
getSystemSettings,
updateSystemSettings
updateSystemSettings,
getSystemMonitorData
} from '@/api/system'
import type { Service, SystemInfo, DatabaseStatus, CacheStatus, SystemSettings } from '@/api/system'
@@ -304,46 +279,95 @@ const systemSettings = reactive<SystemSettings>({
enableSwagger: true
})
// 添加监控数据状态
const mockChartData = reactive({
time: new Date().toLocaleTimeString(),
cpu: 0,
memory: 0,
network: 0
})
// 图表容器引用
const monitorChartContainer = ref<HTMLDivElement | null>(null)
let monitorChartInstance: echarts.ECharts | null = null
let monitorInterval: number | null = null
let chartInterval: number | null = null
// 模拟系统监控数据
const generateMockChartData = () => {
return {
time: new Date().toLocaleTimeString(),
cpu: Math.floor(Math.random() * 100),
memory: Math.floor(Math.random() * 100),
network: Math.floor(Math.random() * 1000)
// 初始化监控图表
const initMonitorChart = () => {
if (monitorChartContainer.value) {
monitorChartInstance = echarts.init(monitorChartContainer.value)
updateMonitorChart()
}
}
// 更新监控图表
const updateChart = () => {
const data = generateMockChartData()
mockChartData.time = data.time
mockChartData.cpu = data.cpu
mockChartData.memory = data.memory
mockChartData.network = data.network
const updateMonitorChart = async () => {
try {
const response = await getSystemMonitorData()
if (response.success && monitorChartInstance) {
const data = response.data
const option = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['CPU使用率', '内存使用率', '网络流量']
},
xAxis: {
type: 'category',
data: [new Date(data.timestamp).toLocaleTimeString()]
},
yAxis: [
{
type: 'value',
name: '使用率(%)',
min: 0,
max: 100
},
{
type: 'value',
name: '流量(KB/s)',
min: 0
}
],
series: [
{
name: 'CPU使用率',
data: [data.cpuUsage],
type: 'line',
smooth: true
},
{
name: '内存使用率',
data: [data.memoryUsage],
type: 'line',
smooth: true
},
{
name: '网络流量',
data: [data.networkTraffic],
type: 'line',
smooth: true,
yAxisIndex: 1
}
]
}
monitorChartInstance.setOption(option)
}
} catch (error) {
console.error('获取监控数据失败:', error)
}
}
// 开始监控
const startMonitoring = () => {
updateChart()
chartInterval = window.setInterval(updateChart, 5000)
initMonitorChart()
monitorInterval = window.setInterval(updateMonitorChart, 5000)
}
// 停止监控
const stopMonitoring = () => {
if (chartInterval) {
clearInterval(chartInterval)
chartInterval = null
if (monitorInterval) {
clearInterval(monitorInterval)
monitorInterval = null
}
if (monitorChartInstance) {
monitorChartInstance.dispose()
monitorChartInstance = null
}
}