refactor: mock清理+安全配置+通用组件+菜单优化

P0安全: SECRET_KEY/ADMIN_PASSWORD/CORS环境变量化, seed仅admin

P1Mock: mock.ts 301->88行, 删8数据mock+5generateMock, net-1300行

P2组件: useToast/useFetchList+ConfirmDialog/FormDialog

P3菜单: menuSections/menuItems->constants/menu.ts

P4API: NotificationsPage/WorkflowPage重写+RolesPage演示

测试: 1050passed, tsc clean
This commit is contained in:
logan committed 2026-05-24 02:42:29 +08:00
1 parent ec3a03cf38
commit 8b921c6c83
33 files changed
+1734 -2006

No files matched your search

+3 -41
View File
@@ -5,7 +5,6 @@ import {
UserCheck, UserX, Filter,
} from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { users as mockUsers } from '@/lib/mock';
import { userApi } from '@/api/user';
/* ───────── Types ───────── */
@@ -91,26 +90,6 @@ function validateUserForm(data: { username: string; password: string; email: str
return errors;
}
/* ───────── Mock Activities ───────── */
function generateMockActivities(userId: number): UserActivity[] {
const actions = [
{ action: '登录系统', target: 'Web Console' },
{ action: '执行查询', target: 'users 表' },
{ action: '提交工单', target: 'TK-20240115-001' },
{ action: '审批工单', target: 'TK-20240115-002' },
{ action: '导出数据', target: 'orders 表' },
{ action: '修改密码', target: '账户安全' },
];
return actions.map((a, i) => ({
id: `act-${userId}-${i}`,
action: a.action,
target: a.target,
created_at: new Date(Date.now() - i * 3600000 * 2).toISOString(),
status: Math.random() > 0.2 ? 'success' : 'failed',
}));
}
/* ───────── CSV Export Helper ───────── */
function exportUsersToCSV(users: UserItem[]) {
@@ -155,12 +134,7 @@ export default function UsersPage() {
try {
const res = await userApi.list();
const data = (res as any)?.items || res;
// Merge with mock user data to enrich with last_login
const merged = (Array.isArray(data) ? data : []).map((u: UserItem) => {
const mockU = mockUsers.find(m => m.id === u.id);
return { ...u, last_login: mockU?.lastLogin || u.created_at };
});
setUsers(merged);
setUsers(Array.isArray(data) ? data : []);
} catch {
setToast({ message: '获取用户列表失败', type: 'error' });
} finally {
@@ -792,7 +766,6 @@ function ResetPasswordModal({ user, onClose, setToast }: { user: UserItem; onClo
/* ───────── User Detail Modal ───────── */
function UserDetailModal({ user, onClose }: { user: UserItem; onClose: () => void }) {
const activities = useMemo(() => generateMockActivities(user.id), [user.id]);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
@@ -847,19 +820,8 @@ function UserDetailModal({ user, onClose }: { user: UserItem; onClose: () => voi
<Clock className="w-4 h-4 text-[var(--dg-text-muted)]" />
最近活动
</h3>
<div className="space-y-2 max-h-[300px] overflow-y-auto">
{activities.map(act => (
<div key={act.id} className="flex items-center gap-3 p-2.5 rounded-lg hover:bg-[var(--dg-bg-hover)] transition-colors">
<div className={`w-2 h-2 rounded-full ${act.status === 'success' ? 'bg-green-400' : 'bg-red-400'}`} />
<div className="flex-1 min-w-0">
<div className="text-sm">{act.action}</div>
<div className="text-xs text-[var(--dg-text-muted)]">{act.target}</div>
</div>
<div className="text-xs text-[var(--dg-text-muted)] whitespace-nowrap">
{new Date(act.created_at).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })}
</div>
</div>
))}
<div className="text-sm text-[var(--dg-text-muted)] text-center py-6">
暂无活动记录
</div>
</div>
</div>