import { useEffect, useState } from 'react' import { useNavigate, useSearchParams, useLocation } from 'react-router-dom' import { useAppDispatch } from '../../store/hooks' import { openTab, updateTabPath } from '../../store/slices/tabsSlice' import { PermButton } from '../../components/PermButton' import { getUserList } from '../../api/usr' import type { PageVO, UserListItemVO } from '../../api/usr' const QUERY_FIELDS = [ { value: 'username', label: '用户名' }, { value: 'staffName', label: '员工名' }, { value: 'userCode', label: '用户号' }, { value: 'department', label: '部门' }, { value: 'userType', label: '用户类型' }, { value: 'disabled', label: '作废' }, { value: 'lastLoginDate', label: '登录日期' }, { value: 'creator', label: '制单人' }, ] const MATCH_TYPES = [ { value: 'contains', label: '包含' }, { value: 'notContains', label: '不包含' }, { value: 'equals', label: '等于' }, ] // REQ-USR-003: 查询用户 // REQ-USR-001: 新增入口 (navigate to /usr/users/new) // REQ-USR-002: 修改入口 (double-click row → /usr/users/:id) export default function UserListPage() { const navigate = useNavigate() const dispatch = useAppDispatch() const location = useLocation() const [searchParams, setSearchParams] = useSearchParams() const [queryField, setQueryField] = useState(searchParams.get('queryField') ?? 'username') const [matchType, setMatchType] = useState(searchParams.get('matchType') ?? 'contains') const [queryValue, setQueryValue] = useState(searchParams.get('queryValue') ?? '') const [currentPage, setCurrentPage] = useState(Number(searchParams.get('page') ?? '1')) const [data, setData] = useState | null>(null) const [selectedId, setSelectedId] = useState(null) useEffect(() => { dispatch(openTab({ id: 'userlist', title: '用户列表', path: location.pathname + location.search, closable: true })) doLoad(currentPage, queryField, matchType, queryValue) }, []) function doLoad(pg: number, field: string, match: string, val: string) { setCurrentPage(pg) getUserList({ queryField: field, matchType: match, queryValue: val, page: pg, pageSize: 20 }).then(setData) } function handleSearch() { const newPath = `/usr/users?queryField=${queryField}&matchType=${matchType}&queryValue=${encodeURIComponent(queryValue)}` setSearchParams({ queryField, matchType, queryValue, page: '1' }) dispatch(updateTabPath({ id: 'userlist', path: newPath })) doLoad(1, queryField, matchType, queryValue) } function handleClear() { setQueryField('username'); setMatchType('contains'); setQueryValue('') setSearchParams({}) dispatch(updateTabPath({ id: 'userlist', path: '/usr/users' })) doLoad(1, 'username', 'contains', '') } const totalPages = data ? Math.ceil(data.total / 20) : 1 return (
{/* Toolbar */}
navigate('/usr/users/new')} style={tbBtn}> 新增
{/* Filter bar */}
setQueryValue(e.target.value)} style={fIn} />
{/* Table */}
{['', '序号', '用户名', '员工名', '用户号', '部门', '用户类型', '语言', '作废', '登录日期', '制单人', '制单日期'].map((h, i) => ( ))} {(data?.list ?? []).map((row, idx) => ( setSelectedId(row.sId)} onDoubleClick={() => navigate(`/usr/users/${row.sId}`, { state: row })} style={{ background: row.sId === selectedId ? 'var(--color-table-row-bg-selected)' : idx % 2 === 1 ? 'var(--color-table-row-alt)' : '#fff', cursor: 'pointer' }} > ))}
{h}{i > 1 && ⇅ ⌕}
{idx + 1} {row.sUsername} {row.sStaffName ?? ''} {row.sUserCode} {row.sDepartment ?? ''} {row.sUserType} {row.sLanguage} {row.bIsDisabled ? '是' : ''} {row.tLastLoginDate ?? ''} {row.sCreatorUsername ?? ''} {row.tCreateDate}
{/* Pager */}
当前显示 共{data?.total ?? 0}条记录 {currentPage}
) } const tbBtn: React.CSSProperties = { display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', color: 'var(--color-toolbar-text)', cursor: 'pointer', fontSize: 13, borderRadius: 2, border: 'none', background: 'transparent', fontFamily: 'inherit' } const fSel: React.CSSProperties = { height: 30, border: '1px solid #d5d8de', borderRadius: 2, padding: '0 28px 0 10px', background: '#fff', minWidth: 100 } const fIn: React.CSSProperties = { height: 30, border: '1px solid #d5d8de', borderRadius: 2, padding: '0 10px', minWidth: 140, fontSize: 13, fontFamily: 'inherit' } const fBtn: React.CSSProperties = { height: 30, padding: '0 14px', borderRadius: 2, border: '1px solid var(--color-primary)', background: 'var(--color-primary)', color: '#fff', display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 13, cursor: 'pointer', fontFamily: 'inherit' } const td: React.CSSProperties = { padding: '7px 10px', textAlign: 'left', whiteSpace: 'nowrap', border: '1px solid var(--color-table-border)' } const pgBtn: React.CSSProperties = { width: 28, height: 28, border: '1px solid #d5d8de', background: '#fff', borderRadius: 2, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: '#666', fontFamily: 'inherit' }