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:
1 parent
ec3a03cf38
commit
8b921c6c83
33 files changed
+1734
-2006
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user