2e5f570b40
- 完善Token过期/不存在时跳转登录重新认证 - 实现正常的账号密码登录验证(邮箱/手机号+密码) - 用户初始化密码设为工号(sn) - 添加修改密码功能(API+前端页面) - 修复扫码登录后跳转问题(zustand persist格式) - 创建飞书通知服务 FeishuNotificationService - 设置页面添加修改密码入口 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
5.5 KiB
TypeScript
125 lines
5.5 KiB
TypeScript
import { useNavigate } from 'react-router-dom'
|
|
import { useUserStore } from '@/stores/user'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { Toast } from 'antd-mobile'
|
|
import { showLanguageSwitcher } from '@/components/LanguageSwitcher'
|
|
import styles from './dashboard.module.css'
|
|
|
|
interface QuickAction {
|
|
id: string
|
|
icon: string
|
|
label: string
|
|
path: string
|
|
color: string
|
|
description?: string
|
|
}
|
|
|
|
// 从应用页面迁移的完整应用列表
|
|
const quickActions: QuickAction[] = [
|
|
{ id: '1', icon: '✈️', label: '出差申请', path: '/business-trip/apply', color: '#1677ff', description: '提交出差申请' },
|
|
{ id: '2', icon: '📝', label: '采购申请', path: '/purchase/apply', color: '#52c41a', description: '物品采购审批' },
|
|
{ id: '3', icon: '📦', label: '备货录入', path: '/stock/entry', color: '#faad14', description: '备货信息录入' },
|
|
{ id: '4', icon: '📅', label: '日程管理', path: '/schedule', color: '#722ed1', description: '个人日程安排' },
|
|
{ id: '5', icon: '💰', label: '报销申请', path: '/expense/apply', color: '#eb2f96', description: '费用报销审批' },
|
|
{ id: '6', icon: '📋', label: '请假申请', path: '/leave/apply', color: '#13c2c2', description: '请假审批' },
|
|
{ id: '7', icon: '🚗', label: '用车申请', path: '/car/apply', color: '#f759ab', description: '公车使用申请' },
|
|
{ id: '8', icon: '🎁', label: '礼品申请', path: '/gift/apply', color: '#ff7a45', description: '礼品采购申请' },
|
|
{ id: '9', icon: '📊', label: '数据报表', path: '/reports', color: '#1890ff', description: '业务数据统计' },
|
|
{ id: '10', icon: '👥', label: '通讯录', path: '/contacts', color: '#096dd9', description: '内部通讯录' },
|
|
{ id: '11', icon: '📁', label: '文件中心', path: '/files', color: '#595959', description: '文件共享管理' },
|
|
{ id: '12', icon: '⚙️', label: '系统设置', path: '/settings', color: '#8c8c8c', description: '系统偏好设置' },
|
|
]
|
|
|
|
export default function Dashboard() {
|
|
const navigate = useNavigate()
|
|
const { user } = useUserStore()
|
|
const { t } = useTranslation()
|
|
|
|
const handleQuickAction = (action: QuickAction) => {
|
|
// 允许的应用:出差申请、审批、系统设置
|
|
const allowedPaths = ['/business-trip/apply', '/business-trip', '/settings']
|
|
if (allowedPaths.includes(action.path)) {
|
|
navigate(action.path)
|
|
} else {
|
|
Toast.show({ content: '功能开发中...', duration: 1500 })
|
|
}
|
|
}
|
|
|
|
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.actionGrid}>
|
|
{quickActions.slice(0, 3).map((action) => (
|
|
<div
|
|
key={action.id}
|
|
className={styles.actionCard}
|
|
onClick={() => handleQuickAction(action)}
|
|
>
|
|
<div className={styles.actionIcon} style={{ backgroundColor: `${action.color}15` }}>
|
|
<span style={{ fontSize: 32 }}>{action.icon}</span>
|
|
</div>
|
|
<span className={styles.actionLabel}>{action.label}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 所有应用 */}
|
|
<div className={styles.section}>
|
|
<h3 className={styles.sectionTitle}>所有应用</h3>
|
|
<div className={styles.actionGrid}>
|
|
<div className={styles.actionCard} onClick={() => navigate('/approval')}>
|
|
<div className={styles.actionIcon} style={{ backgroundColor: '#1677ff15' }}>
|
|
<span style={{ fontSize: 32 }}>✍️</span>
|
|
</div>
|
|
<span className={styles.actionLabel}>审批</span>
|
|
</div>
|
|
<div className={styles.actionCard} onClick={() => handleQuickAction(quickActions[8])}>
|
|
<div className={styles.actionIcon} style={{ backgroundColor: `${quickActions[8].color}15` }}>
|
|
<span style={{ fontSize: 32 }}>{quickActions[8].icon}</span>
|
|
</div>
|
|
<span className={styles.actionLabel}>{quickActions[8].label}</span>
|
|
</div>
|
|
<div className={styles.actionCard} onClick={() => handleQuickAction(quickActions[11])}>
|
|
<div className={styles.actionIcon} style={{ backgroundColor: `${quickActions[11].color}15` }}>
|
|
<span style={{ fontSize: 32 }}>{quickActions[11].icon}</span>
|
|
</div>
|
|
<span className={styles.actionLabel}>{quickActions[11].label}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 底部安全区域 */}
|
|
<div className={styles.safeArea} />
|
|
</div>
|
|
)
|
|
}
|