| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import React from 'react';
- import { useForm } from 'react-hook-form';
- import { zodResolver } from '@hookform/resolvers/zod';
- import { Button } from '@/client/components/ui/button';
- import { Input } from '@/client/components/ui/input';
- 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>;
- onCancel: () => void;
- isLoading?: boolean;
- }
- export const ActivityForm: React.FC<ActivityFormProps> = ({
- initialData,
- onSubmit,
- onCancel,
- isLoading = false
- }) => {
- const isEditing = !!initialData?.id;
- const form = useForm<CreateActivityInput | UpdateActivityInput>({
- resolver: zodResolver(isEditing ? updateActivitySchema : createActivitySchema),
- defaultValues: initialData ? {
- name: initialData.name || '',
- description: initialData.description || '',
- type: initialData.type || ActivityType.DEPARTURE,
- startDate: initialData.startDate ? formatDateTimeForInput(initialData.startDate) : '',
- endDate: initialData.endDate ? formatDateTimeForInput(initialData.endDate) : '',
- isDisabled: initialData.isDisabled
- } : {
- name: '',
- description: '',
- type: ActivityType.DEPARTURE,
- startDate: '',
- endDate: '',
- }
- });
- const handleSubmit = async (data: CreateActivityInput | UpdateActivityInput) => {
- try {
- await onSubmit(data);
- } catch (error) {
- console.error('表单提交失败:', error);
- }
- };
- return (
- <Form {...form}>
- <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
- {/* 活动名称 */}
- <FormField
- control={form.control}
- name="name"
- render={({ field }) => (
- <FormItem>
- <FormLabel>活动名称 *</FormLabel>
- <FormControl>
- <Input
- placeholder="请输入活动名称"
- {...field}
- />
- </FormControl>
- <FormDescription>
- 活动的显示名称,最多255个字符
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- {/* 活动类型 */}
- <FormField
- control={form.control}
- name="type"
- render={({ field }) => (
- <FormItem>
- <FormLabel>活动类型 *</FormLabel>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
- <FormControl>
- <SelectTrigger data-testid="activity-type-select">
- <SelectValue placeholder="选择活动类型" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- <SelectItem value={ActivityType.DEPARTURE}>
- <div className="flex items-center gap-2">
- <Calendar className="h-4 w-4 text-blue-500" />
- <span>去程活动</span>
- </div>
- </SelectItem>
- <SelectItem value={ActivityType.RETURN}>
- <div className="flex items-center gap-2">
- <Calendar className="h-4 w-4 text-green-500" />
- <span>返程活动</span>
- </div>
- </SelectItem>
- </SelectContent>
- </Select>
- <FormDescription>
- 选择活动的类型:去程或返程
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
- {/* 活动描述 */}
- <FormField
- control={form.control}
- name="description"
- render={({ field }) => (
- <FormItem>
- <FormLabel>活动描述</FormLabel>
- <FormControl>
- <Textarea
- placeholder="请输入活动描述(可选)"
- className="min-h-[100px]"
- {...field}
- value={field.value || ''}
- />
- </FormControl>
- <FormDescription>
- 活动的详细描述,最多1000个字符
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
- {/* 开始日期 */}
- <FormField
- control={form.control}
- name="startDate"
- render={({ field }) => (
- <FormItem>
- <FormLabel>开始日期 *</FormLabel>
- <FormControl>
- <Input
- type="datetime-local"
- {...field}
- />
- </FormControl>
- <FormDescription>
- 活动的开始时间
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- {/* 结束日期 */}
- <FormField
- control={form.control}
- name="endDate"
- render={({ field }) => (
- <FormItem>
- <FormLabel>结束日期 *</FormLabel>
- <FormControl>
- <Input
- type="datetime-local"
- {...field}
- />
- </FormControl>
- <FormDescription>
- 活动的结束时间
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
- {/* 状态选择(仅在编辑时显示) */}
- {isEditing && (
- <FormField
- control={form.control}
- name="isDisabled"
- render={({ field }) => (
- <FormItem>
- <FormLabel>活动状态</FormLabel>
- <Select onValueChange={(value) => field.onChange(parseInt(value))} defaultValue={field.value?.toString()}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="选择活动状态" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- <SelectItem value="0">启用</SelectItem>
- <SelectItem value="1">禁用</SelectItem>
- </SelectContent>
- </Select>
- <FormDescription>
- 控制活动是否对用户可见
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- )}
- {/* 操作按钮 */}
- <div className="flex justify-end gap-4 pt-6">
- <Button
- type="button"
- variant="outline"
- onClick={onCancel}
- disabled={isLoading}
- >
- 取消
- </Button>
- <Button
- type="submit"
- disabled={isLoading}
- >
- {isLoading ? '保存中...' : isEditing ? '更新活动' : '创建活动'}
- </Button>
- </div>
- </form>
- </Form>
- );
- };
|