|
|
@@ -0,0 +1,588 @@
|
|
|
+import { useState } from 'react';
|
|
|
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
|
+import { Plus, Edit, Trash2, RefreshCw, Search } from 'lucide-react';
|
|
|
+import { useForm } from 'react-hook-form';
|
|
|
+import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
+import { z } from 'zod';
|
|
|
+import { format } from 'date-fns';
|
|
|
+import { zhCN } from 'date-fns/locale';
|
|
|
+
|
|
|
+import { Button } from '@/client/components/ui/button';
|
|
|
+import { Input } from '@/client/components/ui/input';
|
|
|
+import { Label } from '@/client/components/ui/label';
|
|
|
+import { Switch } from '@/client/components/ui/switch';
|
|
|
+import {
|
|
|
+ Dialog,
|
|
|
+ DialogContent,
|
|
|
+ DialogDescription,
|
|
|
+ DialogFooter,
|
|
|
+ DialogHeader,
|
|
|
+ DialogTitle
|
|
|
+} from '@/client/components/ui/dialog';
|
|
|
+import {
|
|
|
+ Form,
|
|
|
+ FormControl,
|
|
|
+ FormDescription,
|
|
|
+ FormField,
|
|
|
+ FormItem,
|
|
|
+ FormLabel,
|
|
|
+ FormMessage,
|
|
|
+} from '@/client/components/ui/form';
|
|
|
+import {
|
|
|
+ Select,
|
|
|
+ SelectContent,
|
|
|
+ SelectItem,
|
|
|
+ SelectTrigger,
|
|
|
+ SelectValue,
|
|
|
+} from '@/client/components/ui/select';
|
|
|
+import {
|
|
|
+ Table,
|
|
|
+ TableBody,
|
|
|
+ TableCell,
|
|
|
+ TableHead,
|
|
|
+ TableHeader,
|
|
|
+ TableRow,
|
|
|
+} from '@/client/components/ui/table';
|
|
|
+import {
|
|
|
+ Pagination,
|
|
|
+ PaginationContent,
|
|
|
+ PaginationEllipsis,
|
|
|
+ PaginationItem,
|
|
|
+ PaginationLink,
|
|
|
+ PaginationNext,
|
|
|
+ PaginationPrevious,
|
|
|
+} from '@/client/components/ui/pagination';
|
|
|
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
|
|
|
+import { Badge } from '@/client/components/ui/badge';
|
|
|
+import { couponLogClient } from '@/client/api';
|
|
|
+
|
|
|
+import {
|
|
|
+ CreateCouponLogDto,
|
|
|
+ UpdateCouponLogDto,
|
|
|
+ CouponLogSchema
|
|
|
+} from '@/server/modules/coupon-logs/coupon-log.schema';
|
|
|
+import type { InferResponseType, InferRequestType } from 'hono/client';
|
|
|
+import { toast } from 'sonner';
|
|
|
+
|
|
|
+// 类型定义
|
|
|
+type CouponLog = InferResponseType<typeof couponLogClient.$get, 200>['data'][0];
|
|
|
+type CreateCouponLogRequest = InferRequestType<typeof couponLogClient.$post>['json'];
|
|
|
+type UpdateCouponLogRequest = InferRequestType<typeof couponLogClient[':id']['$put']>['json'];
|
|
|
+
|
|
|
+// 创建和更新表单验证
|
|
|
+const createSchema = CreateCouponLogDto;
|
|
|
+const updateSchema = UpdateCouponLogDto;
|
|
|
+
|
|
|
+export default function CouponLogs() {
|
|
|
+ const queryClient = useQueryClient();
|
|
|
+ const [isModalOpen, setIsModalOpen] = useState(false);
|
|
|
+ const [isCreateForm, setIsCreateForm] = useState(true);
|
|
|
+ const [editingData, setEditingData] = useState<CouponLog | null>(null);
|
|
|
+ const [page, setPage] = useState(1);
|
|
|
+ const [pageSize, setPageSize] = useState(10);
|
|
|
+ const [keyword, setKeyword] = useState('');
|
|
|
+
|
|
|
+ // 查询券日志列表
|
|
|
+ const { data: couponLogs, isLoading, refetch } = useQuery({
|
|
|
+ queryKey: ['coupon-logs', page, pageSize, keyword],
|
|
|
+ queryFn: async () => {
|
|
|
+ const response = await couponLogClient.$get({
|
|
|
+ query: { page, pageSize, keyword }
|
|
|
+ });
|
|
|
+ if (response.status !== 200) {
|
|
|
+ throw new Error('获取领券日志失败');
|
|
|
+ }
|
|
|
+ return response.json();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 创建表单
|
|
|
+ const createForm = useForm<CreateCouponLogRequest>({
|
|
|
+ resolver: zodResolver(createSchema),
|
|
|
+ defaultValues: {
|
|
|
+ couponId: '',
|
|
|
+ batchId: '',
|
|
|
+ batchCategoryId: 1,
|
|
|
+ exchangeCodeId: 1,
|
|
|
+ userId: 1,
|
|
|
+ result: 1,
|
|
|
+ failReason: null
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新表单
|
|
|
+ const updateForm = useForm<UpdateCouponLogRequest>({
|
|
|
+ resolver: zodResolver(updateSchema),
|
|
|
+ defaultValues: {}
|
|
|
+ });
|
|
|
+
|
|
|
+ // 创建券日志
|
|
|
+ const createMutation = useMutation({
|
|
|
+ mutationFn: async (data: CreateCouponLogRequest) => {
|
|
|
+ const response = await couponLogClient.$post({ json: data });
|
|
|
+ if (response.status !== 201) {
|
|
|
+ throw new Error('创建失败');
|
|
|
+ }
|
|
|
+ return response.json();
|
|
|
+ },
|
|
|
+ onSuccess: () => {
|
|
|
+ toast({ title: '创建成功' });
|
|
|
+ setIsModalOpen(false);
|
|
|
+ refetch();
|
|
|
+ createForm.reset();
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ toast({
|
|
|
+ title: '创建失败',
|
|
|
+ description: error.message,
|
|
|
+ variant: 'destructive'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新券日志
|
|
|
+ const updateMutation = useMutation({
|
|
|
+ mutationFn: async ({ id, data }: { id: number; data: UpdateCouponLogRequest }) => {
|
|
|
+ const response = await couponLogClient[':id']['$put']({
|
|
|
+ param: { id: id.toString() },
|
|
|
+ json: data
|
|
|
+ });
|
|
|
+ if (response.status !== 200) {
|
|
|
+ throw new Error('更新失败');
|
|
|
+ }
|
|
|
+ return response.json();
|
|
|
+ },
|
|
|
+ onSuccess: () => {
|
|
|
+ toast({ title: '更新成功' });
|
|
|
+ setIsModalOpen(false);
|
|
|
+ refetch();
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ toast({
|
|
|
+ title: '更新失败',
|
|
|
+ description: error.message,
|
|
|
+ variant: 'destructive'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 删除券日志
|
|
|
+ const deleteMutation = useMutation({
|
|
|
+ mutationFn: async (id: number) => {
|
|
|
+ const response = await couponLogClient[':id']['$delete']({
|
|
|
+ param: { id: id.toString() }
|
|
|
+ });
|
|
|
+ if (response.status !== 200) {
|
|
|
+ throw new Error('删除失败');
|
|
|
+ }
|
|
|
+ return response.json();
|
|
|
+ },
|
|
|
+ onSuccess: () => {
|
|
|
+ toast({ title: '删除成功' });
|
|
|
+ refetch();
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ toast({
|
|
|
+ title: '删除失败',
|
|
|
+ description: error.message,
|
|
|
+ variant: 'destructive'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 打开创建对话框
|
|
|
+ const handleOpenCreate = () => {
|
|
|
+ setIsCreateForm(true);
|
|
|
+ setEditingData(null);
|
|
|
+ createForm.reset();
|
|
|
+ setIsModalOpen(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 打开编辑对话框
|
|
|
+ const handleOpenEdit = (log: CouponLog) => {
|
|
|
+ setIsCreateForm(false);
|
|
|
+ setEditingData(log);
|
|
|
+ updateForm.reset({
|
|
|
+ couponId: log.couponId,
|
|
|
+ batchId: log.batchId,
|
|
|
+ batchCategoryId: log.batchCategoryId,
|
|
|
+ exchangeCodeId: log.exchangeCodeId,
|
|
|
+ userId: log.userId,
|
|
|
+ result: log.result,
|
|
|
+ failReason: log.failReason
|
|
|
+ });
|
|
|
+ setIsModalOpen(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理创建提交
|
|
|
+ const handleCreateSubmit = (data: CreateCouponLogRequest) => {
|
|
|
+ createMutation.mutate(data);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理更新提交
|
|
|
+ const handleUpdateSubmit = (data: UpdateCouponLogRequest) => {
|
|
|
+ if (editingData) {
|
|
|
+ updateMutation.mutate({ id: editingData.id, data });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理删除
|
|
|
+ const handleDelete = (id: number) => {
|
|
|
+ if (window.confirm('确定要删除这条领券日志吗?')) {
|
|
|
+ deleteMutation.mutate(id);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 格式化日期
|
|
|
+ const formatDate = (date: string) => {
|
|
|
+ return format(new Date(date), 'yyyy-MM-dd HH:mm:ss', { locale: zhCN });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取结果状态文本
|
|
|
+ const getResultText = (result: number) => {
|
|
|
+ return result === 1 ? '成功' : '失败';
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取结果状态样式
|
|
|
+ const getResultStyle = (result: number) => {
|
|
|
+ return result === 1
|
|
|
+ ? 'bg-green-100 text-green-800'
|
|
|
+ : 'bg-red-100 text-red-800';
|
|
|
+ };
|
|
|
+
|
|
|
+ const totalPages = Math.ceil((couponLogs?.pagination?.total || 0) / pageSize);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="p-6 space-y-6">
|
|
|
+ <div className="flex justify-between items-center">
|
|
|
+ <div>
|
|
|
+ <h1 className="text-3xl font-bold tracking-tight">领券日志管理</h1>
|
|
|
+ <p className="text-muted-foreground">管理用户的领券记录和状态</p>
|
|
|
+ </div>
|
|
|
+ <div className="flex gap-2">
|
|
|
+ <Button onClick={handleOpenCreate}>
|
|
|
+ <Plus className="w-4 h-4 mr-2" />
|
|
|
+ 创建日志
|
|
|
+ </Button>
|
|
|
+ <Button variant="outline" onClick={() => refetch()}>
|
|
|
+ <RefreshCw className="w-4 h-4 mr-2" />
|
|
|
+ 刷新
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Card>
|
|
|
+ <CardHeader>
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
+ <div>
|
|
|
+ <CardTitle>领券日志列表</CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 显示所有用户的领券记录,包括成功和失败的情况
|
|
|
+ </CardDescription>
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center space-x-2">
|
|
|
+ <div className="relative">
|
|
|
+ <Search className="absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
|
+ <Input
|
|
|
+ placeholder="搜索券ID、批次号..."
|
|
|
+ value={keyword}
|
|
|
+ onChange={(e) => setKeyword(e.target.value)}
|
|
|
+ className="pl-8 w-64"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <div className="rounded-md border">
|
|
|
+ <Table>
|
|
|
+ <TableHeader>
|
|
|
+ <TableRow>
|
|
|
+ <TableHead>ID</TableHead>
|
|
|
+ <TableHead>券ID</TableHead>
|
|
|
+ <TableHead>批次号</TableHead>
|
|
|
+ <TableHead>分类ID</TableHead>
|
|
|
+ <TableHead>用户ID</TableHead>
|
|
|
+ <TableHead>结果</TableHead>
|
|
|
+ <TableHead>失败原因</TableHead>
|
|
|
+ <TableHead>创建时间</TableHead>
|
|
|
+ <TableHead className="text-right">操作</TableHead>
|
|
|
+ </TableRow>
|
|
|
+ </TableHeader>
|
|
|
+ <TableBody>
|
|
|
+ {isLoading ? (
|
|
|
+ <TableRow>
|
|
|
+ <TableCell colSpan={9} className="text-center">
|
|
|
+ 加载中...
|
|
|
+ </TableCell>
|
|
|
+ </TableRow>
|
|
|
+ ) : couponLogs?.data?.length === 0 ? (
|
|
|
+ <TableRow>
|
|
|
+ <TableCell colSpan={9} className="text-center">
|
|
|
+ 暂无数据
|
|
|
+ </TableCell>
|
|
|
+ </TableRow>
|
|
|
+ ) : (
|
|
|
+ couponLogs?.data?.map((log) => (
|
|
|
+ <TableRow key={log.id}>
|
|
|
+ <TableCell>{log.id}</TableCell>
|
|
|
+ <TableCell className="font-mono text-sm">{log.couponId}</TableCell>
|
|
|
+ <TableCell className="font-mono text-sm">{log.batchId}</TableCell>
|
|
|
+ <TableCell>{log.batchCategoryId}</TableCell>
|
|
|
+ <TableCell>{log.userId}</TableCell>
|
|
|
+ <TableCell>
|
|
|
+ <Badge className={getResultStyle(log.result)}>
|
|
|
+ {getResultText(log.result)}
|
|
|
+ </Badge>
|
|
|
+ </TableCell>
|
|
|
+ <TableCell>
|
|
|
+ {log.failReason || '-'}
|
|
|
+ </TableCell>
|
|
|
+ <TableCell>{formatDate(log.createdAt)}</TableCell>
|
|
|
+ <TableCell className="text-right">
|
|
|
+ <div className="flex justify-end space-x-2">
|
|
|
+ <Button
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleOpenEdit(log)}
|
|
|
+ >
|
|
|
+ <Edit className="w-4 h-4" />
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleDelete(log.id)}
|
|
|
+ >
|
|
|
+ <Trash2 className="w-4 h-4" />
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </TableCell>
|
|
|
+ </TableRow>
|
|
|
+ ))
|
|
|
+ )}
|
|
|
+ </TableBody>
|
|
|
+ </Table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 分页 */}
|
|
|
+ <div className="flex items-center justify-between mt-4">
|
|
|
+ <div className="text-sm text-muted-foreground">
|
|
|
+ 共 {couponLogs?.pagination?.total || 0} 条记录
|
|
|
+ </div>
|
|
|
+ <div className="space-x-2">
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => setPage(Math.max(1, page - 1))}
|
|
|
+ disabled={page <= 1}
|
|
|
+ >
|
|
|
+ 上一页
|
|
|
+ </Button>
|
|
|
+ <span className="text-sm">
|
|
|
+ 第 {page} 页 / 共 {totalPages} 页
|
|
|
+ </span>
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => setPage(Math.min(totalPages, page + 1))}
|
|
|
+ disabled={page >= totalPages}
|
|
|
+ >
|
|
|
+ 下一页
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 创建/编辑对话框 */}
|
|
|
+ <Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
|
|
|
+ <DialogContent className="sm:max-w-[600px]">
|
|
|
+ <DialogHeader>
|
|
|
+ <DialogTitle>{isCreateForm ? '创建领券日志' : '编辑领券日志'}</DialogTitle>
|
|
|
+ <DialogDescription>
|
|
|
+ {isCreateForm ? '创建新的领券日志记录' : '编辑现有的领券日志信息'}
|
|
|
+ </DialogDescription>
|
|
|
+ </DialogHeader>
|
|
|
+
|
|
|
+ <Form {...(isCreateForm ? createForm : updateForm)}>
|
|
|
+ <form
|
|
|
+ onSubmit={(isCreateForm ? createForm : updateForm).handleSubmit(
|
|
|
+ isCreateForm ? handleCreateSubmit : handleUpdateSubmit
|
|
|
+ )}
|
|
|
+ className="space-y-4"
|
|
|
+ >
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="couponId"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel className="flex items-center">
|
|
|
+ 券ID
|
|
|
+ <span className="text-red-500 ml-1">*</span>
|
|
|
+ </FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入券ID" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="batchId"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel className="flex items-center">
|
|
|
+ 批次号
|
|
|
+ <span className="text-red-500 ml-1">*</span>
|
|
|
+ </FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入批次号" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <div className="grid grid-cols-2 gap-4">
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="batchCategoryId"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel className="flex items-center">
|
|
|
+ 批次分类ID
|
|
|
+ <span className="text-red-500 ml-1">*</span>
|
|
|
+ </FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ placeholder="请输入批次分类ID"
|
|
|
+ {...field}
|
|
|
+ onChange={(e) => field.onChange(parseInt(e.target.value))}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="exchangeCodeId"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel className="flex items-center">
|
|
|
+ 兑换码ID
|
|
|
+ <span className="text-red-500 ml-1">*</span>
|
|
|
+ </FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ placeholder="请输入兑换码ID"
|
|
|
+ {...field}
|
|
|
+ onChange={(e) => field.onChange(parseInt(e.target.value))}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="userId"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel className="flex items-center">
|
|
|
+ 用户ID
|
|
|
+ <span className="text-red-500 ml-1">*</span>
|
|
|
+ </FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ placeholder="请输入用户ID"
|
|
|
+ {...field}
|
|
|
+ onChange={(e) => field.onChange(parseInt(e.target.value))}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="result"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
|
|
+ <div className="space-y-0.5">
|
|
|
+ <FormLabel className="text-base">领取结果</FormLabel>
|
|
|
+ <FormDescription>
|
|
|
+ 设置领取结果为成功或失败
|
|
|
+ </FormDescription>
|
|
|
+ </div>
|
|
|
+ <FormControl>
|
|
|
+ <Select
|
|
|
+ value={field.value.toString()}
|
|
|
+ onValueChange={(value) => field.onChange(parseInt(value))}
|
|
|
+ >
|
|
|
+ <SelectTrigger className="w-[180px]">
|
|
|
+ <SelectValue />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ <SelectItem value="0">失败</SelectItem>
|
|
|
+ <SelectItem value="1">成功</SelectItem>
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
+ </FormControl>
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FormField
|
|
|
+ control={(isCreateForm ? createForm : updateForm).control}
|
|
|
+ name="failReason"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>失败原因</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ placeholder="请输入失败原因(可选)"
|
|
|
+ {...field}
|
|
|
+ value={field.value || ''}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <DialogFooter>
|
|
|
+ <Button
|
|
|
+ type="button"
|
|
|
+ variant="outline"
|
|
|
+ onClick={() => setIsModalOpen(false)}
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="submit"
|
|
|
+ disabled={
|
|
|
+ isCreateForm
|
|
|
+ ? createMutation.isPending
|
|
|
+ : updateMutation.isPending
|
|
|
+ }
|
|
|
+ >
|
|
|
+ {isCreateForm ? '创建' : '更新'}
|
|
|
+ </Button>
|
|
|
+ </DialogFooter>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </DialogContent>
|
|
|
+ </Dialog>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|