Przeglądaj źródła

✨ feat(activities): 优化日期时间处理逻辑

- 添加日期格式化工具函数formatDateTimeForInput,将ISO日期转换为datetime-local输入框格式
- 在活动表单中使用格式化函数处理startDate和endDate的初始值

✨ feat(routes): 优化日期时间处理逻辑

- 在路线表单中使用格式化函数处理departureTime的初始值

♻️ refactor(schemas): 更新日期字段类型定义

- 将活动和路线schema中的日期字段从string类型改为date类型
- 统一日期时间处理方式,提高数据类型一致性
yourname 4 miesięcy temu
rodzic
commit
3c73b94499

+ 10 - 2
src/client/admin/components/ActivityForm.tsx

@@ -7,10 +7,18 @@ import { Textarea } from '@/client/components/ui/textarea';
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
 import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
 import { Calendar } from 'lucide-react';
+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';
 
+// 将ISO日期时间格式化为 datetime-local 输入框需要的格式
+const formatDateTimeForInput = (dateString: string): string => {
+  const date = new Date(dateString);
+  // 使用 date-fns 转换为 YYYY-MM-DDTHH:mm 格式
+  return format(date, "yyyy-MM-dd'T'HH:mm");
+};
+
 interface ActivityFormProps {
   initialData?: UpdateActivityInput & { id?: number };
   onSubmit: (data: CreateActivityInput | UpdateActivityInput) => Promise<void>;
@@ -32,8 +40,8 @@ export const ActivityForm: React.FC<ActivityFormProps> = ({
       name: initialData.name || '',
       description: initialData.description || '',
       type: initialData.type || ActivityType.DEPARTURE,
-      startDate: initialData.startDate || '',
-      endDate: initialData.endDate || '',
+      startDate: initialData.startDate ? formatDateTimeForInput(initialData.startDate) : '',
+      endDate: initialData.endDate ? formatDateTimeForInput(initialData.endDate) : '',
       isDisabled: initialData.isDisabled
     } : {
       name: '',

+ 9 - 1
src/client/admin/components/RouteForm.tsx

@@ -7,10 +7,18 @@ import { Textarea } from '@/client/components/ui/textarea';
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
 import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
 import { MapPin, DollarSign, Users, Car } from 'lucide-react';
+import { format } from 'date-fns';
 import { createRouteSchema, updateRouteSchema } from '@/server/modules/routes/route.schema';
 import type { CreateRouteInput, UpdateRouteInput } from '@/server/modules/routes/route.schema';
 import { ActivitySelect } from './ActivitySelect';
 
+// 将ISO日期时间格式化为 datetime-local 输入框需要的格式
+const formatDateTimeForInput = (dateString: string): string => {
+  const date = new Date(dateString);
+  // 使用 date-fns 转换为 YYYY-MM-DDTHH:mm 格式
+  return format(date, "yyyy-MM-dd'T'HH:mm");
+};
+
 interface RouteFormProps {
   initialData?: UpdateRouteInput & { id?: number };
   onSubmit: (data: CreateRouteInput | UpdateRouteInput) => Promise<void>;
@@ -35,7 +43,7 @@ export const RouteForm: React.FC<RouteFormProps> = ({
       endPoint: initialData.endPoint || '',
       pickupPoint: initialData.pickupPoint || '',
       dropoffPoint: initialData.dropoffPoint || '',
-      departureTime: initialData.departureTime || '',
+      departureTime: initialData.departureTime ? formatDateTimeForInput(initialData.departureTime) : '',
       vehicleType: initialData.vehicleType || 'bus',
       price: initialData.price || 0,
       seatCount: initialData.seatCount || 1,

+ 4 - 4
src/server/modules/activities/activity.schema.ts

@@ -60,11 +60,11 @@ export const activityListResponseSchema = z.object({
   type: z.nativeEnum(ActivityType, {
     message: '活动类型必须是departure(去程)或return(返程)'
   }),
-  startDate: z.coerce.string(),
-  endDate: z.coerce.string(),
+  startDate: z.coerce.date(),
+  endDate: z.coerce.date(),
   isDisabled: z.nativeEnum(DisabledStatus),
-  createdAt: z.coerce.string(),
-  updatedAt: z.coerce.string(),
+  createdAt: z.coerce.date(),
+  updatedAt: z.coerce.date(),
   createdBy: z.number().int().optional(),
   updatedBy: z.number().int().optional(),
 });

+ 3 - 3
src/server/modules/routes/route.schema.ts

@@ -63,15 +63,15 @@ export const routeListResponseSchema = z.object({
   endPoint: z.string().min(1, '目的地不能为空').max(255, '目的地不能超过255个字符'),
   pickupPoint: z.string().min(1, '上车点不能为空').max(255, '上车点不能超过255个字符'),
   dropoffPoint: z.string().min(1, '下车点不能为空').max(255, '下车点不能超过255个字符'),
-  departureTime: z.coerce.string(),
+  departureTime: z.coerce.date(),
   vehicleType: z.string().min(1, '车型不能为空').max(50, '车型不能超过50个字符'),
   price: z.coerce.number().min(0, '价格不能为负数').max(99999999.99, '价格不能超过99999999.99'),
   seatCount: z.number().int().min(1, '座位数至少为1').max(1000, '座位数不能超过1000'),
   availableSeats: z.number().int().min(0, '可用座位数不能为负数').max(1000, '可用座位数不能超过1000'),
   activityId: z.number().int().positive('活动ID必须为正整数'),
   isDisabled: z.nativeEnum(DisabledStatus),
-  createdAt: z.coerce.string(),
-  updatedAt: z.coerce.string(),
+  createdAt: z.coerce.date(),
+  updatedAt: z.coerce.date(),
   createdBy: z.number().int().optional(),
   updatedBy: z.number().int().optional(),
   activity: z.object({