175 lines
6.1 KiB
TypeScript
175 lines
6.1 KiB
TypeScript
|
|
import { useNavigate } from 'react-router-dom'
|
||
|
|
import { useUserStore } from '@/stores/user'
|
||
|
|
import { useTranslation } from 'react-i18next'
|
||
|
|
import { showLanguageSwitcher } from '@/components/LanguageSwitcher'
|
||
|
|
import styles from './dashboard.module.css'
|
||
|
|
|
||
|
|
interface QuickAction {
|
||
|
|
id: string
|
||
|
|
icon: string
|
||
|
|
label: string
|
||
|
|
path: string
|
||
|
|
color: string
|
||
|
|
}
|
||
|
|
|
||
|
|
const quickActions: QuickAction[] = [
|
||
|
|
{ id: '1', icon: '✈️', label: '出差申请', path: '/business-trip/apply', color: '#1677ff' },
|
||
|
|
{ id: '2', icon: '📝', label: '采购申请', path: '/purchase/apply', color: '#52c41a' },
|
||
|
|
{ id: '3', icon: '🏠', label: '会议室', path: '/meeting-room', color: '#faad14' },
|
||
|
|
{ id: '4', icon: '📅', label: '日程管理', path: '/schedule', color: '#722ed1' },
|
||
|
|
{ id: '5', icon: '💰', label: '报销申请', path: '/expense/apply', color: '#eb2f96' },
|
||
|
|
{ id: '6', icon: '📋', label: '请假申请', path: '/leave/apply', color: '#13c2c2' },
|
||
|
|
]
|
||
|
|
|
||
|
|
interface TodoItem {
|
||
|
|
id: string
|
||
|
|
title: string
|
||
|
|
type: 'approval' | 'notice' | 'task'
|
||
|
|
time: string
|
||
|
|
}
|
||
|
|
|
||
|
|
const mockTodos: TodoItem[] = [
|
||
|
|
{ id: '1', title: '张三的出差申请待审批', type: 'approval', time: '10分钟前' },
|
||
|
|
{ id: '2', title: '本周部门会议', type: 'task', time: '今天 14:00' },
|
||
|
|
]
|
||
|
|
|
||
|
|
interface StatCard {
|
||
|
|
title: string
|
||
|
|
value: number
|
||
|
|
unit: string
|
||
|
|
color: string
|
||
|
|
}
|
||
|
|
|
||
|
|
const mockStats: StatCard[] = [
|
||
|
|
{ title: '待审批', value: 5, unit: '条', color: '#ff4d4f' },
|
||
|
|
{ title: '已审批', value: 12, unit: '条', color: '#52c41a' },
|
||
|
|
{ title: '我发起', value: 8, unit: '条', color: '#1677ff' },
|
||
|
|
]
|
||
|
|
|
||
|
|
export default function Dashboard() {
|
||
|
|
const navigate = useNavigate()
|
||
|
|
const { user } = useUserStore()
|
||
|
|
const { t } = useTranslation()
|
||
|
|
|
||
|
|
const handleQuickAction = (action: QuickAction) => {
|
||
|
|
navigate(action.path)
|
||
|
|
}
|
||
|
|
|
||
|
|
const getCurrentLanguage = () => {
|
||
|
|
const lang = localStorage.getItem('i18nextLng') || 'zh'
|
||
|
|
const langMap: Record<string, string> = {
|
||
|
|
zh: '🇨🇳',
|
||
|
|
en: '🇬🇧',
|
||
|
|
ru: '🇷🇺',
|
||
|
|
es: '🇪🇸',
|
||
|
|
fr: '🇫🇷',
|
||
|
|
}
|
||
|
|
return langMap[lang] || '🇨🇳'
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={styles.dashboard}>
|
||
|
|
{/* 顶部欢迎区 */}
|
||
|
|
<div className={styles.welcome}>
|
||
|
|
<div className={styles.userInfo}>
|
||
|
|
<div className={styles.avatar}>{user?.name?.[0] || 'U'}</div>
|
||
|
|
<div className={styles.greetingText}>
|
||
|
|
<h2 className={styles.greeting}>{t('dashboard.greeting', { name: user?.name || t('login.username') })}</h2>
|
||
|
|
<p className={styles.date}>{new Date().toLocaleDateString('zh-CN', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<button className={styles.langBtn} onClick={() => showLanguageSwitcher()}>
|
||
|
|
{getCurrentLanguage()}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 数据统计卡片 */}
|
||
|
|
<div className={styles.section}>
|
||
|
|
<h3 className={styles.sectionTitle}>数据概览</h3>
|
||
|
|
<div className={styles.statsGrid}>
|
||
|
|
{mockStats.map((stat) => (
|
||
|
|
<div key={stat.title} className={styles.statCard} style={{ borderTop: `3px solid ${stat.color}` }}>
|
||
|
|
<div className={styles.statValue}>{stat.value}</div>
|
||
|
|
<div className={styles.statTitle}>{stat.title}</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 待办事项 */}
|
||
|
|
<div className={styles.section}>
|
||
|
|
<div className={styles.sectionHeader}>
|
||
|
|
<h3 className={styles.sectionTitle}>待办事项</h3>
|
||
|
|
<span className={styles.more}>查看全部 {'>'}</span>
|
||
|
|
</div>
|
||
|
|
{mockTodos.length > 0 ? (
|
||
|
|
<div className={styles.todoList}>
|
||
|
|
{mockTodos.map((todo) => (
|
||
|
|
<div key={todo.id} className={styles.todoItem} onClick={() => navigate('/approval/pending')}>
|
||
|
|
<div className={styles.todoIcon}>
|
||
|
|
{todo.type === 'approval' && '📋'}
|
||
|
|
{todo.type === 'notice' && '🔔'}
|
||
|
|
{todo.type === 'task' && '✓'}
|
||
|
|
</div>
|
||
|
|
<div className={styles.todoContent}>
|
||
|
|
<div className={styles.todoTitle}>{todo.title}</div>
|
||
|
|
<div className={styles.todoTime}>{todo.time}</div>
|
||
|
|
</div>
|
||
|
|
<div className={styles.todoArrow}>{'>'}</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div className={styles.empty}>暂无待办事项</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 快捷入口 */}
|
||
|
|
<div className={styles.section}>
|
||
|
|
<h3 className={styles.sectionTitle}>快捷入口</h3>
|
||
|
|
<div className={styles.actionGrid}>
|
||
|
|
{quickActions.map((action) => (
|
||
|
|
<div
|
||
|
|
key={action.id}
|
||
|
|
className={styles.actionCard}
|
||
|
|
onClick={() => handleQuickAction(action)}
|
||
|
|
>
|
||
|
|
<div className={styles.actionIcon} style={{ backgroundColor: `${action.color}15` }}>
|
||
|
|
<span style={{ fontSize: 28 }}>{action.icon}</span>
|
||
|
|
</div>
|
||
|
|
<span className={styles.actionLabel}>{action.label}</span>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 最新动态 */}
|
||
|
|
<div className={styles.section}>
|
||
|
|
<div className={styles.sectionHeader}>
|
||
|
|
<h3 className={styles.sectionTitle}>最新动态</h3>
|
||
|
|
<span className={styles.more}>更多 {'>'}</span>
|
||
|
|
</div>
|
||
|
|
<div className={styles.activityList}>
|
||
|
|
<div className={styles.activityItem}>
|
||
|
|
<div className={styles.activityDot}></div>
|
||
|
|
<div className={styles.activityContent}>
|
||
|
|
<div>张三 提交了出差申请</div>
|
||
|
|
<div className={styles.activityTime}>10分钟前</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className={styles.activityItem}>
|
||
|
|
<div className={styles.activityDot}></div>
|
||
|
|
<div className={styles.activityContent}>
|
||
|
|
<div>李四 审批通过了采购申请</div>
|
||
|
|
<div className={styles.activityTime}>1小时前</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 底部安全区域 */}
|
||
|
|
<div className={styles.safeArea} />
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|