Browse Source

♻️ refactor(admin): 重构活动和路线表单组件

- 统一时间处理逻辑,添加类型定义和转换函数
- 优化表单数据流转,实现清晰的类型转换层
- 改进表单字段绑定方式,提高类型安全性

♻️ refactor(route-form): 更新路线表单字段映射

- 将startPoint/endPoint替换为startLocationId/endLocationId
- 添加表单数据转换函数,统一处理时间字段转换
- 优化表单默认值设置,提高初始数据处理可靠性

♻️ refactor(activity-form): 优化活动表单数据处理

- 定义ActivityFormData接口,明确表单数据类型
- 添加transformFormDataToSubmit函数处理数据转换
- 优化表单字段绑定方式,提高代码可维护性
yourname 4 months ago
parent
commit
3e32440e36

+ 48 - 8
src/client/admin/components/ActivityForm.tsx

@@ -11,14 +11,19 @@ import { format } from 'date-fns';
 import { createActivitySchema, updateActivitySchema } from '@/server/modules/activities/activity.schema';
 import type { CreateActivityInput, UpdateActivityInput } from '@/server/modules/activities/activity.schema';
 import { ActivityType } from '@/server/modules/activities/activity.entity';
+import { DisabledStatus } from '@/share/types';
 
-// 将ISO日期时间格式化为 datetime-local 输入框需要的格式
-const formatDateTimeForInput = (dateString: string): string => {
-  const date = new Date(dateString);
+// 将Date对象格式化为 datetime-local 输入框需要的格式
+const formatDateTimeForInput = (date: Date): string => {
   // 使用 date-fns 转换为 YYYY-MM-DDTHH:mm 格式
   return format(date, "yyyy-MM-dd'T'HH:mm");
 };
 
+// 将 datetime-local 输入框的值转换为Date对象
+const parseDateTimeFromInput = (dateString: string): Date => {
+  return new Date(dateString);
+};
+
 interface ActivityFormProps {
   initialData?: UpdateActivityInput & { id?: number };
   onSubmit: (data: CreateActivityInput | UpdateActivityInput) => Promise<void>;
@@ -26,6 +31,17 @@ interface ActivityFormProps {
   isLoading?: boolean;
 }
 
+// 表单数据类型 - 使用字符串表示时间字段
+interface ActivityFormData {
+  name: string;
+  description: string;
+  type: ActivityType;
+  startDate: string;
+  endDate: string;
+  venueLocationId?: number;
+  isDisabled?: DisabledStatus;
+}
+
 export const ActivityForm: React.FC<ActivityFormProps> = ({
   initialData,
   onSubmit,
@@ -34,7 +50,22 @@ export const ActivityForm: React.FC<ActivityFormProps> = ({
 }) => {
   const isEditing = !!initialData?.id;
 
-  const form = useForm<CreateActivityInput | UpdateActivityInput>({
+  // 创建表单数据转换函数
+  const transformFormDataToSubmit = (formData: any): CreateActivityInput | UpdateActivityInput => {
+    const submitData: any = { ...formData };
+
+    // 转换时间字段为Date对象
+    if (formData.startDate) {
+      submitData.startDate = parseDateTimeFromInput(formData.startDate);
+    }
+    if (formData.endDate) {
+      submitData.endDate = parseDateTimeFromInput(formData.endDate);
+    }
+
+    return submitData;
+  };
+
+  const form = useForm({
     resolver: zodResolver(isEditing ? updateActivitySchema : createActivitySchema),
     defaultValues: initialData ? {
       name: initialData.name || '',
@@ -42,6 +73,7 @@ export const ActivityForm: React.FC<ActivityFormProps> = ({
       type: initialData.type || ActivityType.DEPARTURE,
       startDate: initialData.startDate ? formatDateTimeForInput(initialData.startDate) : '',
       endDate: initialData.endDate ? formatDateTimeForInput(initialData.endDate) : '',
+      venueLocationId: initialData.venueLocationId,
       isDisabled: initialData.isDisabled
     } : {
       name: '',
@@ -49,12 +81,14 @@ export const ActivityForm: React.FC<ActivityFormProps> = ({
       type: ActivityType.DEPARTURE,
       startDate: '',
       endDate: '',
+      venueLocationId: undefined,
     }
   });
 
-  const handleSubmit = async (data: CreateActivityInput | UpdateActivityInput) => {
+  const handleSubmit = async (formData: any) => {
     try {
-      await onSubmit(data);
+      const submitData = transformFormDataToSubmit(formData);
+      await onSubmit(submitData);
     } catch (error) {
       console.error('表单提交失败:', error);
     }
@@ -156,7 +190,10 @@ export const ActivityForm: React.FC<ActivityFormProps> = ({
                 <FormControl>
                   <Input
                     type="datetime-local"
-                    {...field}
+                    value={field.value as string}
+                    onChange={field.onChange}
+                    onBlur={field.onBlur}
+                    ref={field.ref}
                   />
                 </FormControl>
                 <FormDescription>
@@ -177,7 +214,10 @@ export const ActivityForm: React.FC<ActivityFormProps> = ({
                 <FormControl>
                   <Input
                     type="datetime-local"
-                    {...field}
+                    value={field.value as string}
+                    onChange={field.onChange}
+                    onBlur={field.onBlur}
+                    ref={field.ref}
                   />
                 </FormControl>
                 <FormDescription>

+ 32 - 11
src/client/admin/components/RouteForm.tsx

@@ -11,14 +11,19 @@ import { format } from 'date-fns';
 import { createRouteSchema, updateRouteSchema, VehicleType } from '@/server/modules/routes/route.schema';
 import type { CreateRouteInput, UpdateRouteInput } from '@/server/modules/routes/route.schema';
 import { ActivitySelect } from './ActivitySelect';
+import { DisabledStatus } from '@/share/types';
 
-// 将ISO日期时间格式化为 datetime-local 输入框需要的格式
-const formatDateTimeForInput = (dateString: string): string => {
-  const date = new Date(dateString);
+// 将Date对象格式化为 datetime-local 输入框需要的格式
+const formatDateTimeForInput = (date: Date): string => {
   // 使用 date-fns 转换为 YYYY-MM-DDTHH:mm 格式
   return format(date, "yyyy-MM-dd'T'HH:mm");
 };
 
+// 将 datetime-local 输入框的值转换为Date对象
+const parseDateTimeFromInput = (dateString: string): Date => {
+  return new Date(dateString);
+};
+
 interface RouteFormProps {
   initialData?: UpdateRouteInput & { id?: number };
   onSubmit: (data: CreateRouteInput | UpdateRouteInput) => Promise<void>;
@@ -34,13 +39,25 @@ export const RouteForm: React.FC<RouteFormProps> = ({
 }) => {
   const isEditing = !!initialData?.id;
 
-  const form = useForm<CreateRouteInput | UpdateRouteInput>({
+  // 创建表单数据转换函数
+  const transformFormDataToSubmit = (formData: any): CreateRouteInput | UpdateRouteInput => {
+    const submitData: any = { ...formData };
+
+    // 转换时间字段为Date对象
+    if (formData.departureTime) {
+      submitData.departureTime = parseDateTimeFromInput(formData.departureTime);
+    }
+
+    return submitData;
+  };
+
+  const form = useForm({
     resolver: zodResolver(isEditing ? updateRouteSchema : createRouteSchema),
     defaultValues: initialData ? {
       name: initialData.name || '',
       description: initialData.description || '',
-      startPoint: initialData.startPoint || '',
-      endPoint: initialData.endPoint || '',
+      startLocationId: initialData.startLocationId || 0,
+      endLocationId: initialData.endLocationId || 0,
       pickupPoint: initialData.pickupPoint || '',
       dropoffPoint: initialData.dropoffPoint || '',
       departureTime: initialData.departureTime ? formatDateTimeForInput(initialData.departureTime) : '',
@@ -53,8 +70,8 @@ export const RouteForm: React.FC<RouteFormProps> = ({
     } : {
       name: '',
       description: '',
-      startPoint: '',
-      endPoint: '',
+      startLocationId: 0,
+      endLocationId: 0,
       pickupPoint: '',
       dropoffPoint: '',
       departureTime: '',
@@ -66,9 +83,10 @@ export const RouteForm: React.FC<RouteFormProps> = ({
     }
   });
 
-  const handleSubmit = async (data: CreateRouteInput | UpdateRouteInput) => {
+  const handleSubmit = async (formData: any) => {
     try {
-      await onSubmit(data);
+      const submitData = transformFormDataToSubmit(formData);
+      await onSubmit(submitData);
     } catch (error) {
       console.error('表单提交失败:', error);
     }
@@ -286,7 +304,10 @@ export const RouteForm: React.FC<RouteFormProps> = ({
                   <Input
                     type="datetime-local"
                     data-testid="departure-time-input"
-                    {...field}
+                    value={field.value as string}
+                    onChange={field.onChange}
+                    onBlur={field.onBlur}
+                    ref={field.ref}
                   />
                 </FormControl>
                 <FormDescription>