diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 16517f5..08ce433 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -16,6 +16,7 @@ export default function Login() { const [linuxdoEnabled, setLinuxdoEnabled] = useState(false); const [form] = Form.useForm(); const [showAnnouncement, setShowAnnouncement] = useState(false); + const [activeLoginMethod, setActiveLoginMethod] = useState<'local' | 'linuxdo'>('local'); // 检查是否已登录和获取认证配置 useEffect(() => { @@ -215,6 +216,12 @@ export default function Login() { localStorage.setItem('announcement_hide_forever', 'true'); }; + const currentLoginMethod = localAuthEnabled && linuxdoEnabled + ? activeLoginMethod + : localAuthEnabled + ? 'local' + : 'linuxdo'; + return ( <> setActiveLoginMethod(key as 'local' | 'linuxdo')} centered items={[ { @@ -353,9 +361,19 @@ export default function Login() { marginBottom: 0, lineHeight: 1.6, }}> - 🎉 首次登录将自动创建账号 -
- 🔒 每个用户拥有独立的数据空间 + {currentLoginMethod === 'linuxdo' ? ( + <> + 🎉 首次登录将自动创建账号 +
+ 🔒 每个用户拥有独立的数据空间 + + ) : ( + <> + 🧪 默认账号:admin / admin123 +
+ 🔒 每个用户拥有独立的数据空间 + + )} diff --git a/frontend/src/pages/UserManagement.tsx b/frontend/src/pages/UserManagement.tsx index a74d683..9097387 100644 --- a/frontend/src/pages/UserManagement.tsx +++ b/frontend/src/pages/UserManagement.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { Table, @@ -43,6 +43,17 @@ interface UserWithStatus extends User { is_active?: boolean; } +type SortField = + | 'username' + | 'display_name' + | 'is_active' + | 'is_admin' + | 'trust_level' + | 'created_at' + | 'last_login'; + +type SortOrder = 'ascend' | 'descend' | null; + export default function UserManagement() { const navigate = useNavigate(); const [users, setUsers] = useState([]); @@ -55,6 +66,8 @@ export default function UserManagement() { const [pageSize, setPageSize] = useState(20); const [currentPage, setCurrentPage] = useState(1); const [searchText, setSearchText] = useState(''); + const [sortField, setSortField] = useState('created_at'); + const [sortOrder, setSortOrder] = useState('descend'); const [form] = Form.useForm(); const [editForm] = Form.useForm(); @@ -71,6 +84,61 @@ export default function UserManagement() { ); }); + // 排序后的用户列表 + const sortedUsers = useMemo(() => { + if (!sortField || !sortOrder) { + return filteredUsers; + } + + const compareValues = ( + a: string | number | boolean | null | undefined, + b: string | number | boolean | null | undefined + ) => { + // 空值始终置底 + if (a == null && b == null) return 0; + if (a == null) return 1; + if (b == null) return -1; + + if (typeof a === 'string' && typeof b === 'string') { + return a.localeCompare(b, 'zh-CN'); + } + + if (typeof a === 'boolean' && typeof b === 'boolean') { + return Number(a) - Number(b); + } + + return Number(a) - Number(b); + }; + + const getSortValue = (user: UserWithStatus) => { + switch (sortField) { + case 'username': + return user.username ?? null; + case 'display_name': + return user.display_name ?? null; + case 'is_active': + return user.is_active !== false; + case 'is_admin': + return user.is_admin; + case 'trust_level': + return user.trust_level ?? null; + case 'created_at': + return user.created_at ? new Date(user.created_at).getTime() : null; + case 'last_login': + return user.last_login ? new Date(user.last_login).getTime() : null; + default: + return null; + } + }; + + const sorted = [...filteredUsers].sort((a, b) => { + const result = compareValues(getSortValue(a), getSortValue(b)); + return sortOrder === 'ascend' ? result : -result; + }); + + return sorted; + }, [filteredUsers, sortField, sortOrder]); + // 加载用户列表 const loadUsers = async () => { setLoading(true); @@ -240,6 +308,8 @@ export default function UserManagement() { dataIndex: 'username', key: 'username', width: 150, + sorter: true, + sortOrder: sortField === 'username' ? sortOrder : null, render: (text: string) => ( @@ -252,12 +322,16 @@ export default function UserManagement() { dataIndex: 'display_name', key: 'display_name', width: 150, + sorter: true, + sortOrder: sortField === 'display_name' ? sortOrder : null, }, { title: '状态', dataIndex: 'is_active', key: 'is_active', width: 100, + sorter: true, + sortOrder: sortField === 'is_active' ? sortOrder : null, render: (isActive: boolean) => ( ( {isAdmin ? '👑 管理员' : '普通用户'} @@ -281,6 +357,8 @@ export default function UserManagement() { dataIndex: 'trust_level', key: 'trust_level', width: 100, + sorter: true, + sortOrder: sortField === 'trust_level' ? sortOrder : null, render: (level: number) => ( = 5 ? 'green' : 'blue'}> {level === -1 ? '已禁用' : `Level ${level}`} @@ -292,6 +370,8 @@ export default function UserManagement() { dataIndex: 'created_at', key: 'created_at', width: 180, + sorter: true, + sortOrder: sortField === 'created_at' ? sortOrder : null, render: (date: string) => date ? new Date(date).toLocaleString('zh-CN') : '-', }, { @@ -299,6 +379,8 @@ export default function UserManagement() { dataIndex: 'last_login', key: 'last_login', width: 180, + sorter: true, + sortOrder: sortField === 'last_login' ? sortOrder : null, render: (date: string) => date ? new Date(date).toLocaleString('zh-CN') : '从未登录', }, { @@ -568,7 +650,7 @@ export default function UserManagement() { }}> { + const currentSorter = Array.isArray(sorter) ? sorter[0] : sorter; + setCurrentPage(1); + + if (currentSorter && currentSorter.field && currentSorter.order) { + setSortField(currentSorter.field as SortField); + setSortOrder(currentSorter.order as SortOrder); + } else { + setSortField(null); + setSortOrder(null); + } + }} />