import React, { useState } from 'react'; import { useQuery, useMutation } from '@tanstack/react-query'; import { Plus, Edit, Trash2, Search } from 'lucide-react'; import { format } from 'date-fns'; import { Input } from '@d8d/shared-ui-components/components/ui/input'; import { Button } from '@d8d/shared-ui-components/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@d8d/shared-ui-components/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@d8d/shared-ui-components/components/ui/table'; import { Badge } from '@d8d/shared-ui-components/components/ui/badge'; import { Skeleton } from '@d8d/shared-ui-components/components/ui/skeleton'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@d8d/shared-ui-components/components/ui/dialog'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@d8d/shared-ui-components/components/ui/form'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { toast } from 'sonner'; import { DataTablePagination } from '@d8d/shared-ui-components/components/admin/DataTablePagination'; import { FileSelector } from '@d8d/file-management-ui'; import { AdvertisementTypeSelector } from '@d8d/advertisement-type-management-ui'; import { advertisementClientManager } from '../api/advertisementClient'; import { CreateAdvertisementDto, UpdateAdvertisementDto } from '@d8d/advertisements-module/schemas'; import type { AdvertisementSearchParams, CreateAdvertisementRequest, UpdateAdvertisementRequest, AdvertisementResponse } from '../types'; type CreateRequest = CreateAdvertisementRequest; type UpdateRequest = UpdateAdvertisementRequest; const createFormSchema = CreateAdvertisementDto; const updateFormSchema = UpdateAdvertisementDto; export const AdvertisementManagement: React.FC = () => { const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, search: '' }); const [isModalOpen, setIsModalOpen] = useState(false); const [editingAdvertisement, setEditingAdvertisement] = useState(null); const [isCreateForm, setIsCreateForm] = useState(true); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [advertisementToDelete, setAdvertisementToDelete] = useState(null); // 表单实例 const createForm = useForm({ resolver: zodResolver(createFormSchema), defaultValues: { title: '', typeId: 1, code: '', url: '', imageFileId: undefined, sort: 0, status: 1, actionType: 1 } }); const updateForm = useForm({ resolver: zodResolver(updateFormSchema), defaultValues: {} }); // 数据查询 const { data, isLoading, refetch } = useQuery({ queryKey: ['advertisements', searchParams], queryFn: async () => { const res = await advertisementClientManager.get().index.$get({ query: { page: searchParams.page, pageSize: searchParams.limit, keyword: searchParams.search } }); if (res.status !== 200) throw new Error('获取广告列表失败'); return await res.json(); } }); // 创建广告 const createMutation = useMutation({ mutationFn: async (data: CreateRequest) => { const res = await advertisementClientManager.get().index.$post({ json: data }); if (res.status !== 201) throw new Error('创建广告失败'); return await res.json(); }, onSuccess: () => { toast.success('广告创建成功'); setIsModalOpen(false); createForm.reset(); refetch(); }, onError: (error) => { toast.error(error instanceof Error ? error.message : '创建广告失败'); } }); // 更新广告 const updateMutation = useMutation({ mutationFn: async ({ id, data }: { id: number; data: UpdateRequest }) => { const res = await advertisementClientManager.get()[':id']['$put']({ param: { id }, json: data }); if (res.status !== 200) throw new Error('更新广告失败'); return await res.json(); }, onSuccess: () => { toast.success('广告更新成功'); setIsModalOpen(false); setEditingAdvertisement(null); refetch(); }, onError: (error) => { toast.error(error instanceof Error ? error.message : '更新广告失败'); } }); // 删除广告 const deleteMutation = useMutation({ mutationFn: async (id: number) => { const res = await advertisementClientManager.get()[':id']['$delete']({ param: { id } }); if (res.status !== 204) throw new Error('删除广告失败'); return await res.json(); }, onSuccess: () => { toast.success('广告删除成功'); setDeleteDialogOpen(false); setAdvertisementToDelete(null); refetch(); }, onError: (error) => { toast.error(error instanceof Error ? error.message : '删除广告失败'); } }); // 处理搜索 const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setSearchParams(prev => ({ ...prev, page: 1 })); refetch(); }; // 处理创建广告 const handleCreateAdvertisement = () => { setIsCreateForm(true); setEditingAdvertisement(null); createForm.reset(); setIsModalOpen(true); }; // 处理编辑广告 const handleEditAdvertisement = (advertisement: AdvertisementResponse) => { setIsCreateForm(false); setEditingAdvertisement(advertisement); updateForm.reset({ title: advertisement.title || undefined, typeId: advertisement.typeId || undefined, code: advertisement.code || undefined, url: advertisement.url || undefined, imageFileId: advertisement.imageFileId || undefined, sort: advertisement.sort || undefined, status: advertisement.status || undefined, actionType: advertisement.actionType || undefined }); setIsModalOpen(true); }; // 处理删除广告 const handleDeleteAdvertisement = (id: number) => { setAdvertisementToDelete(id); setDeleteDialogOpen(true); }; // 确认删除 const confirmDelete = () => { if (advertisementToDelete) { deleteMutation.mutate(advertisementToDelete); } }; // 处理创建表单提交 const handleCreateSubmit = async (data: CreateRequest) => { try { await createMutation.mutateAsync(data); } catch (error) { throw error; } }; // 处理编辑表单提交 const handleUpdateSubmit = async (data: UpdateRequest) => { if (!editingAdvertisement) return; try { await updateMutation.mutateAsync({ id: editingAdvertisement.id, data }); } catch (error) { throw error; } }; return (

广告管理

广告列表 管理网站的所有广告内容
setSearchParams(prev => ({ ...prev, search: e.target.value }))} className="pl-8" data-testid="search-input" />
ID 标题 类型 别名 图片 状态 排序 创建时间 操作 {isLoading ? ( Array.from({ length: 5 }).map((_, index) => (
)) ) : data?.data && data.data.length > 0 ? ( data.data.map((advertisement) => ( {advertisement.id} {advertisement.title || '-'} {advertisement.advertisementType?.name || '-'} {advertisement.code || '-'} {advertisement.imageFile?.fullUrl ? ( {advertisement.title { e.currentTarget.src = '/placeholder.png'; }} /> ) : ( 无图片 )} {advertisement.status === 1 ? '启用' : '禁用'} {advertisement.sort} {advertisement.createdAt ? format(new Date(advertisement.createdAt), 'yyyy-MM-dd HH:mm') : '-'}
)) ) : (

暂无广告数据

)}
setSearchParams(prev => ({ ...prev, page, limit }))} />
{/* 创建/编辑对话框 */} {isCreateForm ? '创建广告' : '编辑广告'} {isCreateForm ? '创建一个新的广告' : '编辑现有广告信息'} {isCreateForm ? ( // 创建表单(独立渲染)
( 标题 * 广告显示的标题文本,最多30个字符 )} /> ( 广告类型 * )} /> ( 调用别名 * 用于程序调用的唯一标识,最多20个字符 )} /> ( 广告图片 推荐尺寸:1200x400px,支持jpg、png格式 )} /> ( 跳转链接 点击广告后跳转的URL地址 )} />
( 跳转类型 )} /> ( 排序值 field.onChange(parseInt(e.target.value))} data-testid="sort-input" /> 数值越大排序越靠前 )} />
( 状态 )} /> ) : ( // 编辑表单(独立渲染)
( 标题 * 广告显示的标题文本,最多30个字符 )} /> ( 广告类型 * )} /> ( 调用别名 * 用于程序调用的唯一标识,最多20个字符 )} /> ( 广告图片 推荐尺寸:1200x400px,支持jpg、png格式 )} /> ( 跳转链接 点击广告后跳转的URL地址 )} />
( 跳转类型 )} /> ( 排序值 field.onChange(parseInt(e.target.value))} data-testid="sort-input" /> 数值越大排序越靠前 )} />
( 状态 )} /> )}
{/* 删除确认对话框 */} 确认删除 确定要删除这个广告吗?此操作无法撤销。
); }; export default AdvertisementManagement;