- 更新 DashboardLayout 组件,统一使用演示模式布局 - 实现仪表盘页面的完整演示数据和功能 - 完成用户管理页面的演示模式,包含搜索、过滤、分页等功能 - 实现通话记录页面的演示数据和录音播放功能 - 完成翻译员管理页面的演示模式 - 实现订单管理页面的完整功能 - 完成发票管理页面的演示数据 - 更新文档管理页面 - 添加 utils.ts 工具函数库 - 完善 API 路由和数据库结构 - 修复各种 TypeScript 类型错误 - 统一界面风格和用户体验
373 lines
15 KiB
TypeScript
373 lines
15 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import DashboardLayout from '../../components/Layout/DashboardLayout';
|
||
import { getDemoData } from '../../lib/demo-data';
|
||
import {
|
||
UsersIcon,
|
||
PhoneIcon,
|
||
DocumentTextIcon,
|
||
CurrencyDollarIcon,
|
||
CheckCircleIcon,
|
||
ClockIcon,
|
||
ExclamationTriangleIcon,
|
||
ArrowUpIcon,
|
||
ArrowDownIcon,
|
||
EyeIcon
|
||
} from '@heroicons/react/24/outline';
|
||
|
||
interface DashboardStats {
|
||
totalUsers: number;
|
||
activeUsers: number;
|
||
totalCalls: number;
|
||
activeCalls: number;
|
||
totalOrders: number;
|
||
pendingOrders: number;
|
||
completedOrders: number;
|
||
totalRevenue: number;
|
||
monthlyRevenue: number;
|
||
activeInterpreters: number;
|
||
}
|
||
|
||
interface RecentActivity {
|
||
id: string;
|
||
type: 'call' | 'order' | 'user' | 'system';
|
||
title: string;
|
||
description: string;
|
||
time: string;
|
||
status: 'success' | 'warning' | 'error' | 'info';
|
||
}
|
||
|
||
export default function Dashboard() {
|
||
const [stats, setStats] = useState<DashboardStats | null>(null);
|
||
const [activities, setActivities] = useState<RecentActivity[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
const loadDashboardData = async () => {
|
||
try {
|
||
setLoading(true);
|
||
|
||
// 模拟加载延迟
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
|
||
// 使用演示数据
|
||
const mockStats: DashboardStats = {
|
||
totalUsers: 1248,
|
||
activeUsers: 856,
|
||
totalCalls: 3456,
|
||
activeCalls: 12,
|
||
totalOrders: 2789,
|
||
pendingOrders: 45,
|
||
completedOrders: 2654,
|
||
totalRevenue: 125000,
|
||
monthlyRevenue: 15600,
|
||
activeInterpreters: 23
|
||
};
|
||
|
||
const mockActivities: RecentActivity[] = [
|
||
{
|
||
id: '1',
|
||
type: 'call',
|
||
title: '新通话开始',
|
||
description: '张三开始了中英互译通话',
|
||
time: '2分钟前',
|
||
status: 'success'
|
||
},
|
||
{
|
||
id: '2',
|
||
type: 'order',
|
||
title: '订单完成',
|
||
description: '订单ORD-2024-001已完成,费用¥180',
|
||
time: '5分钟前',
|
||
status: 'success'
|
||
},
|
||
{
|
||
id: '3',
|
||
type: 'user',
|
||
title: '新用户注册',
|
||
description: 'ABC公司注册了企业账户',
|
||
time: '10分钟前',
|
||
status: 'info'
|
||
},
|
||
{
|
||
id: '4',
|
||
type: 'system',
|
||
title: '系统维护',
|
||
description: '系统将在今晚22:00-23:00进行维护',
|
||
time: '30分钟前',
|
||
status: 'warning'
|
||
},
|
||
{
|
||
id: '5',
|
||
type: 'call',
|
||
title: '通话异常',
|
||
description: '通话CALL-2024-003出现连接问题',
|
||
time: '1小时前',
|
||
status: 'error'
|
||
}
|
||
];
|
||
|
||
setStats(mockStats);
|
||
setActivities(mockActivities);
|
||
} catch (error) {
|
||
console.error('Failed to load dashboard data:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
loadDashboardData();
|
||
}, []);
|
||
|
||
const getStatusColor = (status: string) => {
|
||
switch (status) {
|
||
case 'success':
|
||
return 'text-green-600 bg-green-100';
|
||
case 'warning':
|
||
return 'text-yellow-600 bg-yellow-100';
|
||
case 'error':
|
||
return 'text-red-600 bg-red-100';
|
||
default:
|
||
return 'text-blue-600 bg-blue-100';
|
||
}
|
||
};
|
||
|
||
const getStatusIcon = (status: string) => {
|
||
switch (status) {
|
||
case 'success':
|
||
return <CheckCircleIcon className="h-5 w-5 text-green-500" />;
|
||
case 'warning':
|
||
return <ExclamationTriangleIcon className="h-5 w-5 text-yellow-500" />;
|
||
case 'error':
|
||
return <ExclamationTriangleIcon className="h-5 w-5 text-red-500" />;
|
||
default:
|
||
return <ClockIcon className="h-5 w-5 text-blue-500" />;
|
||
}
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<DashboardLayout title="仪表盘">
|
||
<div className="flex items-center justify-center h-64">
|
||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||
</div>
|
||
</DashboardLayout>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<DashboardLayout title="仪表盘">
|
||
<div className="space-y-6">
|
||
{/* 欢迎区域 */}
|
||
<div className="bg-white shadow rounded-lg p-6">
|
||
<h1 className="text-2xl font-bold text-gray-900">欢迎回来!</h1>
|
||
<p className="mt-1 text-sm text-gray-600">
|
||
这里是您的管理仪表板,查看最新的业务数据和活动。
|
||
</p>
|
||
</div>
|
||
|
||
{/* 统计卡片 */}
|
||
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
|
||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
||
<div className="p-5">
|
||
<div className="flex items-center">
|
||
<div className="flex-shrink-0">
|
||
<UsersIcon className="h-6 w-6 text-blue-400" />
|
||
</div>
|
||
<div className="ml-5 w-0 flex-1">
|
||
<dl>
|
||
<dt className="text-sm font-medium text-gray-500 truncate">总用户数</dt>
|
||
<dd className="flex items-baseline">
|
||
<div className="text-2xl font-semibold text-gray-900">{stats?.totalUsers || 0}</div>
|
||
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
|
||
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
|
||
<span className="sr-only">增加了</span>
|
||
12%
|
||
</div>
|
||
</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-gray-50 px-5 py-3">
|
||
<div className="text-sm">
|
||
<span className="font-medium text-gray-500">活跃用户: </span>
|
||
<span className="text-gray-900">{stats?.activeUsers || 0}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
||
<div className="p-5">
|
||
<div className="flex items-center">
|
||
<div className="flex-shrink-0">
|
||
<PhoneIcon className="h-6 w-6 text-green-400" />
|
||
</div>
|
||
<div className="ml-5 w-0 flex-1">
|
||
<dl>
|
||
<dt className="text-sm font-medium text-gray-500 truncate">总通话数</dt>
|
||
<dd className="flex items-baseline">
|
||
<div className="text-2xl font-semibold text-gray-900">{stats?.totalCalls || 0}</div>
|
||
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
|
||
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
|
||
<span className="sr-only">增加了</span>
|
||
8%
|
||
</div>
|
||
</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-gray-50 px-5 py-3">
|
||
<div className="text-sm">
|
||
<span className="font-medium text-gray-500">进行中: </span>
|
||
<span className="text-gray-900">{stats?.activeCalls || 0}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
||
<div className="p-5">
|
||
<div className="flex items-center">
|
||
<div className="flex-shrink-0">
|
||
<DocumentTextIcon className="h-6 w-6 text-yellow-400" />
|
||
</div>
|
||
<div className="ml-5 w-0 flex-1">
|
||
<dl>
|
||
<dt className="text-sm font-medium text-gray-500 truncate">总订单数</dt>
|
||
<dd className="flex items-baseline">
|
||
<div className="text-2xl font-semibold text-gray-900">{stats?.totalOrders || 0}</div>
|
||
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
|
||
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
|
||
<span className="sr-only">增加了</span>
|
||
15%
|
||
</div>
|
||
</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-gray-50 px-5 py-3">
|
||
<div className="text-sm">
|
||
<span className="font-medium text-gray-500">待处理: </span>
|
||
<span className="text-gray-900">{stats?.pendingOrders || 0}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
||
<div className="p-5">
|
||
<div className="flex items-center">
|
||
<div className="flex-shrink-0">
|
||
<CurrencyDollarIcon className="h-6 w-6 text-purple-400" />
|
||
</div>
|
||
<div className="ml-5 w-0 flex-1">
|
||
<dl>
|
||
<dt className="text-sm font-medium text-gray-500 truncate">总收入</dt>
|
||
<dd className="flex items-baseline">
|
||
<div className="text-2xl font-semibold text-gray-900">¥{stats?.totalRevenue?.toLocaleString() || 0}</div>
|
||
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
|
||
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
|
||
<span className="sr-only">增加了</span>
|
||
22%
|
||
</div>
|
||
</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-gray-50 px-5 py-3">
|
||
<div className="text-sm">
|
||
<span className="font-medium text-gray-500">本月: </span>
|
||
<span className="text-gray-900">¥{stats?.monthlyRevenue?.toLocaleString() || 0}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 最近活动和快速操作 */}
|
||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||
{/* 最近活动 */}
|
||
<div className="bg-white shadow rounded-lg">
|
||
<div className="px-4 py-5 sm:p-6">
|
||
<h3 className="text-lg leading-6 font-medium text-gray-900 mb-4">最近活动</h3>
|
||
<div className="space-y-4">
|
||
{activities.map((activity) => (
|
||
<div key={activity.id} className="flex items-start space-x-3">
|
||
<div className="flex-shrink-0">
|
||
{getStatusIcon(activity.status)}
|
||
</div>
|
||
<div className="min-w-0 flex-1">
|
||
<div className="text-sm font-medium text-gray-900">{activity.title}</div>
|
||
<div className="text-sm text-gray-500">{activity.description}</div>
|
||
<div className="text-xs text-gray-400 mt-1">{activity.time}</div>
|
||
</div>
|
||
<div className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(activity.status)}`}>
|
||
{activity.status === 'success' && '成功'}
|
||
{activity.status === 'warning' && '警告'}
|
||
{activity.status === 'error' && '错误'}
|
||
{activity.status === 'info' && '信息'}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="mt-6">
|
||
<button className="w-full bg-gray-50 border border-gray-300 rounded-md py-2 px-4 inline-flex justify-center items-center text-sm font-medium text-gray-700 hover:bg-gray-100">
|
||
<EyeIcon className="h-4 w-4 mr-2" />
|
||
查看所有活动
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 快速操作 */}
|
||
<div className="bg-white shadow rounded-lg">
|
||
<div className="px-4 py-5 sm:p-6">
|
||
<h3 className="text-lg leading-6 font-medium text-gray-900 mb-4">快速操作</h3>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<button className="bg-blue-50 border border-blue-200 rounded-lg p-4 text-left hover:bg-blue-100 transition-colors">
|
||
<div className="flex items-center">
|
||
<UsersIcon className="h-8 w-8 text-blue-600" />
|
||
<div className="ml-3">
|
||
<div className="text-sm font-medium text-blue-900">用户管理</div>
|
||
<div className="text-xs text-blue-700">管理用户账户</div>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
<button className="bg-green-50 border border-green-200 rounded-lg p-4 text-left hover:bg-green-100 transition-colors">
|
||
<div className="flex items-center">
|
||
<PhoneIcon className="h-8 w-8 text-green-600" />
|
||
<div className="ml-3">
|
||
<div className="text-sm font-medium text-green-900">通话监控</div>
|
||
<div className="text-xs text-green-700">实时通话状态</div>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
<button className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 text-left hover:bg-yellow-100 transition-colors">
|
||
<div className="flex items-center">
|
||
<DocumentTextIcon className="h-8 w-8 text-yellow-600" />
|
||
<div className="ml-3">
|
||
<div className="text-sm font-medium text-yellow-900">订单管理</div>
|
||
<div className="text-xs text-yellow-700">处理订单请求</div>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
<button className="bg-purple-50 border border-purple-200 rounded-lg p-4 text-left hover:bg-purple-100 transition-colors">
|
||
<div className="flex items-center">
|
||
<CurrencyDollarIcon className="h-8 w-8 text-purple-600" />
|
||
<div className="ml-3">
|
||
<div className="text-sm font-medium text-purple-900">财务报表</div>
|
||
<div className="text-xs text-purple-700">查看收入统计</div>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</DashboardLayout>
|
||
);
|
||
}
|