From f61004ba1244ffd85a074777e090965caacc934e Mon Sep 17 00:00:00 2001 From: Frudrax Cheng Date: Tue, 26 May 2026 11:04:25 +0800 Subject: [PATCH] Show aftersales stats and recent orders on dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adds a second row of cards (total / pending / closed / rejected) - New "最近售后工单" table linking to detail pages - DashboardStats type extended; dashboardApi maps backend overview Co-Authored-By: Claude Opus 4.7 (1M context) --- src/pages/Dashboard.tsx | 140 ++++++++++++++++++++++++++++++++++++++-- src/services/api.ts | 5 ++ src/types/index.ts | 15 +++++ 3 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 55e213b..05b9b0b 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,10 +1,44 @@ import { useEffect, useState } from 'react'; import { Card, Row, Col, Statistic, Table, Spin, message, Tag } from 'antd'; -import { TeamOutlined, KeyOutlined, CheckCircleOutlined, UserOutlined } from '@ant-design/icons'; +import { + TeamOutlined, + KeyOutlined, + CheckCircleOutlined, + UserOutlined, + ToolOutlined, + ClockCircleOutlined, + ExclamationCircleOutlined, +} from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; import { dashboardApi } from '@/services/api'; -import type { DashboardStats } from '@/types'; +import type { + DashboardStats, + AftersalesServiceType, + AftersalesWorkOrderStatus, +} from '@/types'; + +const SERVICE_TYPE_LABEL: Record = { + software: '软件', + hardware: '硬件', + other: '其他', +}; + +const WORK_ORDER_STATUS_LABEL: Record = { + created: '待处理', + pending_confirmation: '待客户确认', + closed: '已完成', + rejected: '已退回', +}; + +const WORK_ORDER_STATUS_COLOR: Record = { + created: 'default', + pending_confirmation: 'processing', + closed: 'success', + rejected: 'warning', +}; function DashboardPage() { + const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [stats, setStats] = useState(null); @@ -26,7 +60,14 @@ function DashboardPage() { if (loading) { return ( -
+
); @@ -77,6 +118,49 @@ function DashboardPage() { + + + + } + valueStyle={{ color: '#165DFF' }} + /> + + + + + } + valueStyle={{ color: '#faad14' }} + /> + + + + + } + valueStyle={{ color: '#52c41a' }} + /> + + + + + } + valueStyle={{ color: '#ff4d4f' }} + /> + + + + + + +
( + navigate(`/admin/aftersales/${sn}`)} + > + {sn} + + ), + }, + { title: '客户公司', dataIndex: 'companyName', key: 'companyName' }, + { + title: '服务类型', + dataIndex: 'serviceType', + key: 'serviceType', + render: (type: AftersalesServiceType) => + SERVICE_TYPE_LABEL[type] || type, + }, + { title: '处理人', dataIndex: 'technicianName', key: 'technicianName' }, + { + title: '状态', + dataIndex: 'workOrderStatus', + key: 'workOrderStatus', + render: (status: AftersalesWorkOrderStatus) => ( + + {WORK_ORDER_STATUS_LABEL[status]} + + ), + }, + { + title: '创建时间', + dataIndex: 'createdAt', + key: 'createdAt', + render: (date: string) => new Date(date).toLocaleString('zh-CN'), + }, + ]} + dataSource={stats?.recentAftersales || []} + rowKey="serialNumber" + pagination={false} + /> + ); } -export default DashboardPage; \ No newline at end of file +export default DashboardPage; diff --git a/src/services/api.ts b/src/services/api.ts index b9910ee..6e1b021 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -212,6 +212,10 @@ export const dashboardApi = { totalEmployeeSerials: data.overview?.totalEmployeeSerials || 0, activeSerials: data.overview?.activeSerials || 0, inactiveSerials: data.overview?.inactiveSerials || 0, + totalAftersales: data.overview?.totalAftersales || 0, + pendingConfirmation: data.overview?.pendingConfirmation || 0, + closedAftersales: data.overview?.closedAftersales || 0, + rejectedAftersales: data.overview?.rejectedAftersales || 0, monthlyData: data.monthlyStats || [], recentCompanies: data.recentCompanies?.map((c: any) => ({ id: c.companyName, @@ -227,6 +231,7 @@ export const dashboardApi = { createdAt: s.createdAt, type: s.type, })) || [], + recentAftersales: data.recentAftersales || [], }; } throw new Error('获取统计数据失败'); diff --git a/src/types/index.ts b/src/types/index.ts index 78c9958..9dca46c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -99,12 +99,26 @@ export interface ApiResponse { error?: string; } +export interface DashboardRecentAftersales { + serialNumber: string; + companyName: string; + serviceType: AftersalesServiceType; + workOrderStatus: AftersalesWorkOrderStatus; + authorizationStatus: AftersalesAuthorizationStatus; + technicianName: string; + createdAt: string; +} + export interface DashboardStats { totalCompanies: number; totalSerials: number; totalEmployeeSerials: number; activeSerials: number; inactiveSerials: number; + totalAftersales: number; + pendingConfirmation: number; + closedAftersales: number; + rejectedAftersales: number; monthlyData: Array<{ month: string; companies: number; @@ -112,6 +126,7 @@ export interface DashboardStats { }>; recentCompanies: Company[]; recentSerials: Serial[]; + recentAftersales: DashboardRecentAftersales[]; } export interface CompanyFilter {