diff --git a/frontend/src/api/users.ts b/frontend/src/api/users.ts index b56ff2a..e9015c8 100644 --- a/frontend/src/api/users.ts +++ b/frontend/src/api/users.ts @@ -15,6 +15,7 @@ export interface UserListItem { } export interface UserDetail extends UserListItem { + canEditDocument?: boolean; employeeId?: number | null; permissionCategoryIds: number[]; updatedBy?: string | null; @@ -46,7 +47,7 @@ export interface CreateUserReq { userType: 'NORMAL' | 'SUPER_ADMIN'; language: 'zh-CN' | 'en-US' | 'zh-TW'; canEditDocument: boolean; - employeeId?: number; + employeeId?: number | null; permissionCategoryIds?: number[]; } @@ -55,7 +56,7 @@ export interface UpdateUserReq { userType?: 'NORMAL' | 'SUPER_ADMIN'; language?: 'zh-CN' | 'en-US' | 'zh-TW'; canEditDocument?: boolean; - employeeId?: number; + employeeId?: number | null; isDeleted?: boolean; permissionCategoryIds?: number[]; } diff --git a/frontend/src/pages/users/UserFormPage.test.tsx b/frontend/src/pages/users/UserFormPage.test.tsx index 8a78fba..3dbbedb 100644 --- a/frontend/src/pages/users/UserFormPage.test.tsx +++ b/frontend/src/pages/users/UserFormPage.test.tsx @@ -84,6 +84,15 @@ describe('UserFormPage (edit)', () => { }); }); + it('prefills canEditDocument from backend (not hardcoded false)', async () => { + renderForm('edit', '/users/1'); + // backend mock 返回 canEditDocument=true;UI 应反映为 checkbox.checked=true + await waitFor(() => { + const checkbox = document.querySelector('input[type="checkbox"]') as HTMLInputElement; + expect(checkbox?.checked).toBe(true); + }); + }); + it('unknown userId (40401) shows 404 result', async () => { renderForm('edit', '/users/99999'); await waitFor(() => expect(screen.getByTestId('user-not-found')).toBeInTheDocument()); diff --git a/frontend/src/pages/users/UserFormPage.tsx b/frontend/src/pages/users/UserFormPage.tsx index 0e63bff..d141963 100644 --- a/frontend/src/pages/users/UserFormPage.tsx +++ b/frontend/src/pages/users/UserFormPage.tsx @@ -47,7 +47,7 @@ export default function UserFormPage({ mode }: Props) { userCode: detail.userCode, userType: detail.userType, language: detail.language as FormValues['language'], - canEditDocument: false, // detail VO 当前不返回,默认 false + canEditDocument: detail.canEditDocument ?? false, employeeId: detail.employeeId ?? undefined, }); setPermissionCategoryIds(detail.permissionCategoryIds ?? []); @@ -69,6 +69,13 @@ export default function UserFormPage({ mode }: Props) { }; }, [mode, userId, form]); + // spec § 八三态 employeeId 映射: + // - 新增模式:values.employeeId === undefined 或 0 → 不传 / 显式 null;正整数 → 传 ID + // - 编辑模式:undefined → 不变(PATCH 缺省 = 保留原值);0 → 显式发 0(解除关联约定);正整数 → 传 ID + const toCreateEmployeeId = (v: number | undefined): number | null | undefined => + v == null || v === 0 ? null : v; + const toUpdateEmployeeId = (v: number | undefined): number | undefined => v; + const handleSubmit = async (values: FormValues) => { setSubmitting(true); setErrorMessage(null); @@ -85,7 +92,7 @@ export default function UserFormPage({ mode }: Props) { userType: values.userType!, language: values.language!, canEditDocument: !!values.canEditDocument, - employeeId: values.employeeId, + employeeId: toCreateEmployeeId(values.employeeId), permissionCategoryIds, }); message.success('新增用户成功'); @@ -95,7 +102,7 @@ export default function UserFormPage({ mode }: Props) { userType: values.userType, language: values.language, canEditDocument: values.canEditDocument, - employeeId: values.employeeId, + employeeId: toUpdateEmployeeId(values.employeeId), permissionCategoryIds, }; await usersApi.update(userId, patch); diff --git a/frontend/src/pages/users/UserPermissionPanel.tsx b/frontend/src/pages/users/UserPermissionPanel.tsx index 1acfb1d..8d291c8 100644 --- a/frontend/src/pages/users/UserPermissionPanel.tsx +++ b/frontend/src/pages/users/UserPermissionPanel.tsx @@ -28,6 +28,8 @@ export default function UserPermissionPanel({ value, onChange, disabled = false { key: 'customer', label: '客户查看权限', disabled: true, children: null }, { key: 'supplier', label: '供应商查看权限', disabled: true, children: null }, { key: 'person', label: '人员查看权限', disabled: true, children: null }, + { key: 'process', label: '工序查看权限', disabled: true, children: null }, + { key: 'driver', label: '司机查看权限', disabled: true, children: null }, ]} /> diff --git a/frontend/src/pages/users/UsersListPage.tsx b/frontend/src/pages/users/UsersListPage.tsx index a5796d6..a61aa81 100644 --- a/frontend/src/pages/users/UsersListPage.tsx +++ b/frontend/src/pages/users/UsersListPage.tsx @@ -12,7 +12,12 @@ import UsersTable from './UsersTable'; export default function UsersListPage() { const navigate = useNavigate(); - const [query, setQuery] = useState({ page: 1, size: 20 }); + const [query, setQuery] = useState({ + page: 1, + size: 20, + sortField: 'tCreateDate', + sortOrder: 'desc', + }); const [records, setRecords] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); diff --git a/frontend/src/pages/users/UsersTable.tsx b/frontend/src/pages/users/UsersTable.tsx index 2a98563..3178a91 100644 --- a/frontend/src/pages/users/UsersTable.tsx +++ b/frontend/src/pages/users/UsersTable.tsx @@ -1,4 +1,4 @@ -import { Table, Tag } from 'antd'; +import { Table, Tag, Button } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import type { UserListItem } from '../../api/users'; @@ -28,9 +28,9 @@ export default function UsersTable({ width: 60, render: (_: unknown, __: unknown, idx: number) => (page - 1) * size + idx + 1, }, - { title: '用户名', dataIndex: 'username', key: 'username' }, + { title: '用户名', dataIndex: 'username', key: 'username', sorter: true }, { title: '员工名', dataIndex: 'employeeName', key: 'employeeName' }, - { title: '用户号', dataIndex: 'userCode', key: 'userCode' }, + { title: '用户号', dataIndex: 'userCode', key: 'userCode', sorter: true }, { title: '部门', dataIndex: 'departmentName', key: 'departmentName' }, { title: '用户类型', @@ -46,9 +46,27 @@ export default function UsersTable({ render: (v: boolean) => v ? 作废 : 启用, }, - { title: '登录日期', dataIndex: 'lastLoginDate', key: 'lastLoginDate' }, + { title: '登录日期', dataIndex: 'lastLoginDate', key: 'lastLoginDate', sorter: true }, { title: '制单人', dataIndex: 'createdBy', key: 'createdBy' }, - { title: '制单日期', dataIndex: 'createdDate', key: 'createdDate' }, + { title: '制单日期', dataIndex: 'createdDate', key: 'createdDate', sorter: true }, + { + title: '操作', + key: 'action', + width: 80, + render: (_: unknown, row: UserListItem) => ( + + ), + }, ]; return ( @@ -59,7 +77,16 @@ export default function UsersTable({ loading={loading} onRow={(row) => ({ onClick: () => onRowClick(row), + onKeyDown: (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onRowClick(row); + } + }, style: { cursor: 'pointer' }, + tabIndex: 0, + role: 'button', + 'aria-label': `查看用户 ${row.username}`, 'data-testid': `user-row-${row.userId}`, })} pagination={{ diff --git a/frontend/src/pages/users/usersConstants.ts b/frontend/src/pages/users/usersConstants.ts index 3f5b919..ec03b30 100644 --- a/frontend/src/pages/users/usersConstants.ts +++ b/frontend/src/pages/users/usersConstants.ts @@ -16,6 +16,7 @@ export const QUERY_FIELD_OPTIONS = [ { value: 'departmentName', label: '部门' }, { value: 'userType', label: '用户类型' }, { value: 'isDeleted', label: '作废' }, + { value: 'lastLoginDate', label: '登录日期' }, { value: 'createdBy', label: '制单人' }, ] as const; diff --git a/frontend/src/test-utils/msw-handlers.ts b/frontend/src/test-utils/msw-handlers.ts index b7c3de0..34b70b6 100644 --- a/frontend/src/test-utils/msw-handlers.ts +++ b/frontend/src/test-utils/msw-handlers.ts @@ -148,6 +148,7 @@ export const handlers = [ language: 'zh-CN', isDeleted: false, lastLoginDate: '2026-05-15T08:00:00', + canEditDocument: true, employeeId: 1, permissionCategoryIds: [1, 2], createdBy: 'admin',