(null);
+
+ useEffect(() => {
+ if (mode !== 'edit' || userId == null) return;
+ let cancelled = false;
+ (async () => {
+ try {
+ const detail = await usersApi.get(userId);
+ if (cancelled) return;
+ setOriginalDetail(detail);
+ form.setFieldsValue({
+ username: detail.username,
+ userCode: detail.userCode,
+ userType: detail.userType,
+ language: detail.language as FormValues['language'],
+ canEditDocument: false, // detail VO 当前不返回,默认 false
+ employeeId: detail.employeeId ?? undefined,
+ });
+ setPermissionCategoryIds(detail.permissionCategoryIds ?? []);
+ } catch (e) {
+ if (cancelled) return;
+ if (isBizError(e) && e.code === 40401) {
+ setNotFound(true);
+ } else if (isBizError(e)) {
+ setErrorMessage(e.message || (ERROR_MESSAGES.UNKNOWN as string));
+ } else {
+ setErrorMessage(ERROR_MESSAGES.UNKNOWN as string);
+ }
+ } finally {
+ if (!cancelled) setLoadingInitial(false);
+ }
+ })();
+ return () => {
+ cancelled = true;
+ };
+ }, [mode, userId, form]);
+
+ const handleSubmit = async (values: FormValues) => {
+ setSubmitting(true);
+ setErrorMessage(null);
+ form.setFields([
+ { name: 'username', errors: [] },
+ { name: 'userCode', errors: [] },
+ ]);
+
+ try {
+ if (mode === 'create') {
+ await usersApi.create({
+ username: values.username!,
+ userCode: values.userCode!,
+ userType: values.userType!,
+ language: values.language!,
+ canEditDocument: !!values.canEditDocument,
+ employeeId: values.employeeId,
+ permissionCategoryIds,
+ });
+ message.success('新增用户成功');
+ } else if (userId != null) {
+ const patch: UpdateUserReq = {
+ userCode: values.userCode,
+ userType: values.userType,
+ language: values.language,
+ canEditDocument: values.canEditDocument,
+ employeeId: values.employeeId,
+ permissionCategoryIds,
+ };
+ await usersApi.update(userId, patch);
+ message.success('保存成功');
+ }
+ navigate('/users');
+ } catch (e) {
+ handleBizError(e);
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ const handleBizError = (e: unknown) => {
+ if (!isBizError(e)) {
+ setErrorMessage(ERROR_MESSAGES.UNKNOWN as string);
+ return;
+ }
+ const be = e as BizError;
+ if (be.code === 40901) {
+ form.setFields([{ name: 'username', errors: [ERROR_MESSAGES[40901] as string] }]);
+ } else if (be.code === 40902) {
+ form.setFields([{ name: 'userCode', errors: [ERROR_MESSAGES[40902] as string] }]);
+ } else if (be.code === 40004) {
+ setErrorMessage(ERROR_MESSAGES[40004] as string);
+ } else if (be.code === 40401) {
+ setNotFound(true);
+ } else if (be.code === -1) {
+ setErrorMessage(ERROR_MESSAGES.NETWORK as string);
+ } else {
+ setErrorMessage(be.message || (ERROR_MESSAGES.UNKNOWN as string));
+ }
+ };
+
+ if (notFound) {
+ return (
+
+ navigate('/users')}>
+ 返回列表
+
+ }
+ />
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {errorMessage && (
+
+ )}
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/users/UserPermissionPanel.tsx b/frontend/src/pages/users/UserPermissionPanel.tsx
new file mode 100644
index 0000000..1acfb1d
--- /dev/null
+++ b/frontend/src/pages/users/UserPermissionPanel.tsx
@@ -0,0 +1,35 @@
+import { Tabs, Checkbox } from 'antd';
+import { PERMISSION_CATEGORY_OPTIONS } from './usersConstants';
+
+interface Props {
+ value: number[];
+ onChange: (ids: number[]) => void;
+ disabled?: boolean;
+}
+
+export default function UserPermissionPanel({ value, onChange, disabled = false }: Props) {
+ return (
+
+ onChange(checked as number[])}
+ disabled={disabled}
+ data-testid="permission-category-group"
+ />
+ ),
+ },
+ { key: 'customer', label: '客户查看权限', disabled: true, children: null },
+ { key: 'supplier', label: '供应商查看权限', disabled: true, children: null },
+ { key: 'person', label: '人员查看权限', disabled: true, children: null },
+ ]}
+ />
+
+ );
+}
diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx
index 22c3125..3ca96bd 100644
--- a/frontend/src/router/index.tsx
+++ b/frontend/src/router/index.tsx
@@ -1,19 +1,33 @@
import { createBrowserRouter, Navigate } from 'react-router-dom';
import LoginPage from '../pages/login/LoginPage';
-import RequireAuth from './RequireAuth';
-
-function UsersPlaceholder() {
- return users placeholder
;
-}
+import UsersListPage from '../pages/users/UsersListPage';
+import UserFormPage from '../pages/users/UserFormPage';
+import RequireSuperAdmin from './RequireSuperAdmin';
export const router = createBrowserRouter([
{ path: '/login', element: },
{
path: '/users',
element: (
-
-
-
+
+
+
+ ),
+ },
+ {
+ path: '/users/new',
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: '/users/:userId',
+ element: (
+
+
+
),
},
{ path: '*', element: },