|
|
@@ -79,15 +79,7 @@ export const UserManagement = () => {
|
|
|
|
|
|
const updateForm = useForm<UpdateUserFormData>({
|
|
|
resolver: zodResolver(updateUserFormSchema),
|
|
|
- defaultValues: {
|
|
|
- username: '',
|
|
|
- nickname: null,
|
|
|
- email: null,
|
|
|
- phone: null,
|
|
|
- name: null,
|
|
|
- password: '',
|
|
|
- isDisabled: 0,
|
|
|
- },
|
|
|
+ defaultValues: {}, // 初始为空,编辑时动态设置
|
|
|
});
|
|
|
|
|
|
const { data: usersData, isLoading, refetch } = useQuery({
|
|
|
@@ -212,32 +204,74 @@ export const UserManagement = () => {
|
|
|
const handleEditUser = (user: UserResponse) => {
|
|
|
setEditingUser(user);
|
|
|
setIsCreateForm(false);
|
|
|
- updateForm.reset({
|
|
|
- username: user.username,
|
|
|
- nickname: user.nickname,
|
|
|
- email: user.email,
|
|
|
- phone: user.phone,
|
|
|
- name: user.name,
|
|
|
- avatarFileId: user.avatarFileId,
|
|
|
- isDisabled: user.isDisabled,
|
|
|
- });
|
|
|
+
|
|
|
+ // 准备表单值,将null转换为undefined
|
|
|
+ const formValues: Partial<UpdateUserFormData> = {};
|
|
|
+
|
|
|
+ // 用户名必须设置
|
|
|
+ if (user.username !== undefined && user.username !== null) {
|
|
|
+ formValues.username = user.username;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他字段:如果为null,设置为undefined(不设置)
|
|
|
+ if (user.nickname !== undefined && user.nickname !== null) {
|
|
|
+ formValues.nickname = user.nickname;
|
|
|
+ }
|
|
|
+ if (user.email !== undefined && user.email !== null) {
|
|
|
+ formValues.email = user.email;
|
|
|
+ }
|
|
|
+ if (user.phone !== undefined && user.phone !== null) {
|
|
|
+ formValues.phone = user.phone;
|
|
|
+ }
|
|
|
+ if (user.name !== undefined && user.name !== null) {
|
|
|
+ formValues.name = user.name;
|
|
|
+ }
|
|
|
+ if (user.avatarFileId !== undefined && user.avatarFileId !== null) {
|
|
|
+ formValues.avatarFileId = user.avatarFileId;
|
|
|
+ }
|
|
|
+ if (user.isDisabled !== undefined && user.isDisabled !== null) {
|
|
|
+ formValues.isDisabled = user.isDisabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.debug('设置编辑表单值:', formValues);
|
|
|
+ updateForm.reset(formValues);
|
|
|
setIsModalOpen(true);
|
|
|
};
|
|
|
|
|
|
// 处理创建表单提交
|
|
|
const handleCreateSubmit = async (data: CreateUserFormData) => {
|
|
|
try {
|
|
|
+ console.debug('开始创建用户:', data);
|
|
|
const res = await userClientManager.get().index.$post({
|
|
|
json: data
|
|
|
});
|
|
|
+
|
|
|
+ console.debug('创建用户响应:', { status: res.status });
|
|
|
+
|
|
|
if (res.status !== 201) {
|
|
|
- throw new Error('创建用户失败');
|
|
|
+ // 尝试获取错误信息
|
|
|
+ let errorMessage = '创建用户失败';
|
|
|
+ try {
|
|
|
+ const errorData = await res.json();
|
|
|
+ console.debug('创建用户错误响应数据:', errorData);
|
|
|
+ errorMessage = errorData.message || errorMessage;
|
|
|
+ } catch (jsonError) {
|
|
|
+ console.debug('无法解析错误响应JSON:', jsonError);
|
|
|
+ }
|
|
|
+ throw new Error(errorMessage);
|
|
|
}
|
|
|
+
|
|
|
+ // 获取成功响应数据
|
|
|
+ const result = await res.json();
|
|
|
+ console.debug('创建用户成功:', result);
|
|
|
+
|
|
|
toast.success('用户创建成功');
|
|
|
setIsModalOpen(false);
|
|
|
refetch();
|
|
|
- } catch {
|
|
|
- toast.error('创建失败,请重试');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('创建用户失败:', error);
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '创建失败,请重试';
|
|
|
+ toast.error(`创建失败: ${errorMessage}`);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -246,18 +280,178 @@ export const UserManagement = () => {
|
|
|
if (!editingUser) return;
|
|
|
|
|
|
try {
|
|
|
+ console.debug('开始更新用户,原始数据:', {
|
|
|
+ userId: editingUser.id,
|
|
|
+ data: data,
|
|
|
+ dataKeys: Object.keys(data),
|
|
|
+ passwordValue: data.password,
|
|
|
+ passwordType: typeof data.password,
|
|
|
+ passwordLength: data.password ? data.password.length : 0
|
|
|
+ });
|
|
|
+
|
|
|
+ // 处理提交数据:修剪字符串字段并移除空值
|
|
|
+ const submitData: Record<string, any> = {};
|
|
|
+
|
|
|
+ // 首先处理密码字段
|
|
|
+ if (data.password !== undefined && data.password !== null && data.password !== '') {
|
|
|
+ if (typeof data.password === 'string') {
|
|
|
+ const trimmedPassword = data.password.trim();
|
|
|
+ if (trimmedPassword !== '') {
|
|
|
+ if (trimmedPassword.length < 6) {
|
|
|
+ toast.error('密码长度至少6位');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ submitData.password = trimmedPassword;
|
|
|
+ }
|
|
|
+ // 如果trimmedPassword === '',不设置submitData.password字段
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果data.password是undefined/null/空字符串,不设置submitData.password字段
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 处理其他字段
|
|
|
+ for (const [key, value] of Object.entries(data)) {
|
|
|
+ // 跳过密码字段,已经处理过了
|
|
|
+ if (key === 'password') continue;
|
|
|
+
|
|
|
+ if (value === undefined || value === null) {
|
|
|
+ // 跳过undefined和null
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof value === 'string') {
|
|
|
+ const trimmedValue = value.trim();
|
|
|
+ if (trimmedValue === '') {
|
|
|
+ // 空字符串,跳过(不包含在提交数据中)
|
|
|
+ console.debug(`字段 ${key} 为空字符串,已跳过`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 特殊处理用户名字段
|
|
|
+ if (key === 'username' && trimmedValue.length < 3) {
|
|
|
+ // 用户名至少3个字符
|
|
|
+ toast.error('用户名至少3个字符');
|
|
|
+ return; // 直接返回,不提交
|
|
|
+ } else if (key === 'email') {
|
|
|
+ // 邮箱格式验证(简单验证)
|
|
|
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
+ if (!emailRegex.test(trimmedValue)) {
|
|
|
+ toast.error('邮箱格式不正确');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ submitData[key] = trimmedValue;
|
|
|
+ } else {
|
|
|
+ submitData[key] = trimmedValue;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 非字符串字段
|
|
|
+ if (key === 'isDisabled') {
|
|
|
+ // 确保 isDisabled 是数字 0 或 1
|
|
|
+ const numValue = Number(value);
|
|
|
+ if (numValue === 0 || numValue === 1) {
|
|
|
+ submitData[key] = numValue;
|
|
|
+ } else {
|
|
|
+ console.warn(`isDisabled 值无效: ${value},应为 0 或 1`);
|
|
|
+ // 跳过无效值
|
|
|
+ }
|
|
|
+ } else if (key === 'avatarFileId') {
|
|
|
+ // avatarFileId 应该是正数或null
|
|
|
+ if (value !== null && value !== undefined) {
|
|
|
+ const numValue = Number(value);
|
|
|
+ if (numValue > 0) {
|
|
|
+ submitData[key] = numValue;
|
|
|
+ } else {
|
|
|
+ console.warn(`avatarFileId 值无效: ${value},应为正数`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 其他非字符串字段直接传递
|
|
|
+ submitData[key] = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('处理提交数据时出错:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有实际修改的字段
|
|
|
+ if (Object.keys(submitData).length === 0) {
|
|
|
+ toast.info('没有修改任何内容');
|
|
|
+ setIsModalOpen(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 使用schema验证数据格式
|
|
|
+ try {
|
|
|
+ const validatedData = updateUserFormSchema.parse(submitData);
|
|
|
+ console.debug('数据验证通过:', validatedData);
|
|
|
+ console.debug('验证后的数据密码字段:', {
|
|
|
+ hasPassword: 'password' in validatedData,
|
|
|
+ passwordValue: validatedData.password ? `[长度:${validatedData.password.length}]` : '[未设置]'
|
|
|
+ });
|
|
|
+ } catch (validationError: any) {
|
|
|
+ console.error('数据验证失败:', validationError);
|
|
|
+ console.error('验证失败的数据:', submitData);
|
|
|
+
|
|
|
+ // 提取具体的验证错误信息
|
|
|
+ let errorMessage = '数据格式错误,请检查输入';
|
|
|
+ if (validationError?.errors?.length > 0) {
|
|
|
+ const errors = validationError.errors.map((err: any) =>
|
|
|
+ `${err.path?.join('.') || '字段'}: ${err.message}`
|
|
|
+ ).join('; ');
|
|
|
+ errorMessage = `验证错误: ${errors}`;
|
|
|
+ console.error('详细验证错误:', errors);
|
|
|
+ }
|
|
|
+
|
|
|
+ toast.error(errorMessage);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 直接使用表单数据,react-hook-form已经通过zodResolver验证过了
|
|
|
const res = await userClientManager.get()[':id']['$put']({
|
|
|
param: { id: editingUser.id },
|
|
|
- json: data
|
|
|
+ json: submitData
|
|
|
});
|
|
|
+
|
|
|
+ console.debug('更新用户响应:', { status: res.status });
|
|
|
+
|
|
|
if (res.status !== 200) {
|
|
|
- throw new Error('更新用户失败');
|
|
|
+ // 尝试获取错误信息
|
|
|
+ let errorMessage = '更新用户失败';
|
|
|
+ let errorDetails: any = null;
|
|
|
+ try {
|
|
|
+ const errorData = await res.json();
|
|
|
+ console.debug('更新用户错误响应数据:', errorData);
|
|
|
+ errorMessage = errorData.message || errorMessage;
|
|
|
+ errorDetails = errorData;
|
|
|
+ } catch (jsonError) {
|
|
|
+ console.debug('无法解析错误响应JSON:', jsonError);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是参数错误,显示更详细的信息
|
|
|
+ if (res.status === 400 && errorDetails?.errors) {
|
|
|
+ const validationErrors = errorDetails.errors.map((err: any) =>
|
|
|
+ `${err.path?.join('.') || '字段'}: ${err.message}`
|
|
|
+ ).join('; ');
|
|
|
+ errorMessage = `参数验证失败: ${validationErrors}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new Error(errorMessage);
|
|
|
}
|
|
|
+
|
|
|
+ // 获取成功响应数据
|
|
|
+ const result = await res.json();
|
|
|
+ console.debug('更新用户成功:', result);
|
|
|
+
|
|
|
toast.success('用户更新成功');
|
|
|
setIsModalOpen(false);
|
|
|
refetch();
|
|
|
- } catch {
|
|
|
- toast.error('更新失败,请重试');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新用户失败:', error);
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '更新失败,请重试';
|
|
|
+ toast.error(`更新失败: ${errorMessage}`);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -271,16 +465,33 @@ export const UserManagement = () => {
|
|
|
if (!userToDelete) return;
|
|
|
|
|
|
try {
|
|
|
+ console.debug('开始删除用户:', { userId: userToDelete });
|
|
|
const res = await userClientManager.get()[':id']['$delete']({
|
|
|
param: { id: userToDelete }
|
|
|
});
|
|
|
+
|
|
|
+ console.debug('删除用户响应:', { status: res.status });
|
|
|
+
|
|
|
if (res.status !== 204) {
|
|
|
- throw new Error('删除用户失败');
|
|
|
+ // 尝试获取错误信息
|
|
|
+ let errorMessage = '删除用户失败';
|
|
|
+ try {
|
|
|
+ const errorData = await res.json();
|
|
|
+ console.debug('删除用户错误响应数据:', errorData);
|
|
|
+ errorMessage = errorData.message || errorMessage;
|
|
|
+ } catch (jsonError) {
|
|
|
+ console.debug('无法解析错误响应JSON:', jsonError);
|
|
|
+ }
|
|
|
+ throw new Error(errorMessage);
|
|
|
}
|
|
|
+
|
|
|
+ console.debug('删除用户成功');
|
|
|
toast.success('用户删除成功');
|
|
|
refetch();
|
|
|
- } catch {
|
|
|
- toast.error('删除失败,请重试');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除用户失败:', error);
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '删除失败,请重试';
|
|
|
+ toast.error(`删除失败: ${errorMessage}`);
|
|
|
} finally {
|
|
|
setDeleteDialogOpen(false);
|
|
|
setUserToDelete(null);
|