Browse Source

🗑️ feat(advertisement): remove advertisement types feature

- 删除菜单中的广告类型选项
- 删除广告类型管理页面组件
- 移除路由配置中的广告类型页面
- 清理相关的页面引用和依赖
yourname 3 months ago
parent
commit
de587af816

+ 0 - 7
src/client/admin-shadcn/menu.tsx

@@ -110,13 +110,6 @@ export const useMenu = () => {
       path: '/admin/advertisements',
       permission: 'advertisement:manage'
     },
-    {
-      key: 'advertisement-types',
-      label: '广告类型',
-      icon: <Tag className="h-4 w-4" />,
-      path: '/admin/advertisement-types',
-      permission: 'advertisement:manage'
-    },
     {
       key: 'settings',
       label: '系统设置',

+ 0 - 437
src/client/admin-shadcn/pages/AdvertisementTypes.tsx

@@ -1,437 +0,0 @@
-import React, { useState } from 'react';
-import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
-import { Plus, Edit, Trash2, Search } from 'lucide-react';
-import { Input } from '@/client/components/ui/input';
-import { Button } from '@/client/components/ui/button';
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
-import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/client/components/ui/table';
-import { Badge } from '@/client/components/ui/badge';
-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 { useForm } from 'react-hook-form';
-import { zodResolver } from '@hookform/resolvers/zod';
-import { toast } from 'sonner';
-import { DataTablePagination } from '@/client/admin-shadcn/components/DataTablePagination';
-import { advertisementClient } from '@/client/api';
-import type { InferRequestType, InferResponseType } from 'hono/client';
-import { CreateAdvertisementTypeDto, UpdateAdvertisementTypeDto } from '@/server/modules/advertisements/advertisement-type.schema';
-
-type CreateRequest = InferRequestType<typeof advertisementClient.types.$post>['json'];
-type UpdateRequest = InferRequestType<typeof advertisementClient.types[':id']['$put']>['json'];
-type AdvertisementTypeResponse = InferResponseType<typeof advertisementClient.types.$get, 200>['data'][0];
-
-const createFormSchema = CreateAdvertisementTypeDto;
-const updateFormSchema = UpdateAdvertisementTypeDto;
-
-export const AdvertisementTypesPage = () => {
-  const queryClient = useQueryClient();
-  const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, search: '' });
-  const [isModalOpen, setIsModalOpen] = useState(false);
-  const [editingType, setEditingType] = useState<AdvertisementTypeResponse | null>(null);
-  const [isCreateForm, setIsCreateForm] = useState(true);
-  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
-  const [typeToDelete, setTypeToDelete] = useState<number | null>(null);
-
-  // 表单实例
-  const createForm = useForm<CreateRequest>({
-    resolver: zodResolver(createFormSchema),
-    defaultValues: {
-      name: '',
-      code: '',
-      remark: '',
-      status: 1
-    }
-  });
-
-  const updateForm = useForm<UpdateRequest>({
-    resolver: zodResolver(updateFormSchema),
-    defaultValues: {}
-  });
-
-  // 数据查询
-  const { data, isLoading, refetch } = useQuery({
-    queryKey: ['advertisement-types', searchParams],
-    queryFn: async () => {
-      const res = await advertisementClient.types.$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 advertisementClient.types.$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 advertisementClient.types[':id'].$put({ 
-        param: { id: id.toString() },
-        json: data 
-      });
-      if (res.status !== 200) throw new Error('更新广告类型失败');
-      return await res.json();
-    },
-    onSuccess: () => {
-      toast.success('广告类型更新成功');
-      setIsModalOpen(false);
-      setEditingType(null);
-      refetch();
-    },
-    onError: (error) => {
-      toast.error(error instanceof Error ? error.message : '更新广告类型失败');
-    }
-  });
-
-  // 删除广告类型
-  const deleteMutation = useMutation({
-    mutationFn: async (id: number) => {
-      const res = await advertisementClient.types[':id'].$delete({ 
-        param: { id: id.toString() } 
-      });
-      if (res.status !== 200) throw new Error('删除广告类型失败');
-      return await res.json();
-    },
-    onSuccess: () => {
-      toast.success('广告类型删除成功');
-      setDeleteDialogOpen(false);
-      setTypeToDelete(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 handleCreateType = () => {
-    setIsCreateForm(true);
-    setEditingType(null);
-    createForm.reset();
-    setIsModalOpen(true);
-  };
-
-  // 处理编辑广告类型
-  const handleEditType = (type: AdvertisementTypeResponse) => {
-    setIsCreateForm(false);
-    setEditingType(type);
-    updateForm.reset({
-      name: type.name,
-      code: type.code,
-      remark: type.remark || undefined,
-      status: type.status
-    });
-    setIsModalOpen(true);
-  };
-
-  // 处理删除广告类型
-  const handleDeleteType = (id: number) => {
-    setTypeToDelete(id);
-    setDeleteDialogOpen(true);
-  };
-
-  // 确认删除
-  const confirmDelete = () => {
-    if (typeToDelete) {
-      deleteMutation.mutate(typeToDelete);
-    }
-  };
-
-  // 处理表单提交
-  const handleSubmit = (data: CreateRequest | UpdateRequest) => {
-    if (isCreateForm) {
-      createMutation.mutate(data as CreateRequest);
-    } else if (editingType) {
-      updateMutation.mutate({ 
-        id: editingType.id, 
-        data: data as UpdateRequest 
-      });
-    }
-  };
-
-  // 渲染加载骨架
-  if (isLoading) {
-    return (
-      <div className="space-y-4">
-        <div className="flex justify-between items-center">
-          <h1 className="text-2xl font-bold">广告类型管理</h1>
-          <Button disabled>
-            <Plus className="mr-2 h-4 w-4" />
-            创建类型
-          </Button>
-        </div>
-        
-        <Card>
-          <CardHeader>
-            <Skeleton className="h-6 w-1/4" />
-          </CardHeader>
-          <CardContent>
-            <Skeleton className="h-32 w-full" />
-          </CardContent>
-        </Card>
-      </div>
-    );
-  }
-
-  return (
-    <div className="space-y-4">
-      <div className="flex justify-between items-center">
-        <h1 className="text-2xl font-bold">广告类型管理</h1>
-        <Button onClick={handleCreateType}>
-          <Plus className="mr-2 h-4 w-4" />
-          创建类型
-        </Button>
-      </div>
-
-      <Card>
-        <CardHeader>
-          <CardTitle>广告类型列表</CardTitle>
-          <CardDescription>管理广告的类型分类</CardDescription>
-        </CardHeader>
-        <CardContent>
-          <div className="mb-4">
-            <form onSubmit={handleSearch} className="flex gap-2">
-              <div className="relative flex-1 max-w-sm">
-                <Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
-                <Input
-                  placeholder="搜索类型名称或别名..."
-                  value={searchParams.search}
-                  onChange={(e) => setSearchParams(prev => ({ ...prev, search: e.target.value }))}
-                  className="pl-8"
-                />
-              </div>
-              <Button type="submit" variant="outline">
-                搜索
-              </Button>
-            </form>
-          </div>
-
-          <div className="rounded-md border">
-            <Table>
-              <TableHeader>
-                <TableRow>
-                  <TableHead>ID</TableHead>
-                  <TableHead>类型名称</TableHead>
-                  <TableHead>调用别名</TableHead>
-                  <TableHead>备注</TableHead>
-                  <TableHead>状态</TableHead>
-                  <TableHead>创建时间</TableHead>
-                  <TableHead className="text-right">操作</TableHead>
-                </TableRow>
-              </TableHeader>
-              <TableBody>
-                {data?.data.map((type) => (
-                  <TableRow key={type.id}>
-                    <TableCell>{type.id}</TableCell>
-                    <TableCell className="font-medium">{type.name}</TableCell>
-                    <TableCell>
-                      <code className="text-xs bg-muted px-1 rounded">{type.code}</code>
-                    </TableCell>
-                    <TableCell>
-                      {type.remark ? (
-                        <span className="text-sm">{type.remark}</span>
-                      ) : (
-                        <span className="text-muted-foreground text-xs">无备注</span>
-                      )}
-                    </TableCell>
-                    <TableCell>
-                      <Badge variant={type.status === 1 ? 'default' : 'secondary'}>
-                        {type.status === 1 ? '启用' : '禁用'}
-                      </Badge>
-                    </TableCell>
-                    <TableCell>
-                      {type.createTime ? new Date(type.createTime * 1000).toLocaleDateString() : '-'}
-                    </TableCell>
-                    <TableCell className="text-right">
-                      <div className="flex justify-end gap-2">
-                        <Button 
-                          variant="ghost" 
-                          size="icon" 
-                          onClick={() => handleEditType(type)}
-                        >
-                          <Edit className="h-4 w-4" />
-                        </Button>
-                        <Button 
-                          variant="ghost" 
-                          size="icon" 
-                          onClick={() => handleDeleteType(type.id)}
-                        >
-                          <Trash2 className="h-4 w-4" />
-                        </Button>
-                      </div>
-                    </TableCell>
-                  </TableRow>
-                ))}
-              </TableBody>
-            </Table>
-          </div>
-
-          {data?.data.length === 0 && !isLoading && (
-            <div className="text-center py-8">
-              <p className="text-muted-foreground">暂无广告类型数据</p>
-            </div>
-          )}
-
-          <DataTablePagination
-            current={searchParams.page}
-            pageSize={searchParams.limit}
-            total={data?.pagination.total || 0}
-            onChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
-          />
-        </CardContent>
-      </Card>
-
-      {/* 创建/编辑对话框 */}
-      <Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
-        <DialogContent className="sm:max-w-[500px] max-h-[90vh] overflow-y-auto">
-          <DialogHeader>
-            <DialogTitle>{isCreateForm ? '创建广告类型' : '编辑广告类型'}</DialogTitle>
-            <DialogDescription>
-              {isCreateForm ? '创建一个新的广告类型' : '编辑现有广告类型信息'}
-            </DialogDescription>
-          </DialogHeader>
-          
-          <Form {...(isCreateForm ? createForm : updateForm)}>
-            <form onSubmit={(isCreateForm ? createForm : updateForm).handleSubmit(handleSubmit)} className="space-y-4">
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="name"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel className="flex items-center">
-                      类型名称 <span className="text-red-500 ml-1">*</span>
-                    </FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入类型名称" {...field} />
-                    </FormControl>
-                    <FormDescription>广告类型的显示名称,最多50个字符</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="code"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel className="flex items-center">
-                      调用别名 <span className="text-red-500 ml-1">*</span>
-                    </FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入调用别名" {...field} />
-                    </FormControl>
-                    <FormDescription>用于程序调用的唯一标识,最多20个字符</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="remark"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel>备注</FormLabel>
-                    <FormControl>
-                      <Input placeholder="请输入备注" {...field} />
-                    </FormControl>
-                    <FormDescription>广告类型的备注说明,最多100个字符</FormDescription>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <FormField
-                control={(isCreateForm ? createForm : updateForm).control}
-                name="status"
-                render={({ field }) => (
-                  <FormItem>
-                    <FormLabel>状态</FormLabel>
-                    <FormControl>
-                      <select 
-                        {...field} 
-                        className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
-                        value={field.value || 1}
-                        onChange={(e) => field.onChange(parseInt(e.target.value))}
-                      >
-                        <option value={1}>启用</option>
-                        <option value={0}>禁用</option>
-                      </select>
-                    </FormControl>
-                    <FormMessage />
-                  </FormItem>
-                )}
-              />
-
-              <DialogFooter>
-                <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
-                  取消
-                </Button>
-                <Button type="submit" disabled={(isCreateForm ? createMutation : updateMutation).isPending}>
-                  {isCreateForm ? '创建' : '更新'}
-                </Button>
-              </DialogFooter>
-            </form>
-          </Form>
-        </DialogContent>
-      </Dialog>
-
-      {/* 删除确认对话框 */}
-      <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
-        <DialogContent>
-          <DialogHeader>
-            <DialogTitle>确认删除</DialogTitle>
-            <DialogDescription>
-              确定要删除这个广告类型吗?此操作无法撤销。
-            </DialogDescription>
-          </DialogHeader>
-          <DialogFooter>
-            <Button variant="outline" onClick={() => setDeleteDialogOpen(false)}>
-              取消
-            </Button>
-            <Button 
-              variant="destructive" 
-              onClick={confirmDelete}
-              disabled={deleteMutation.isPending}
-            >
-              {deleteMutation.isPending ? '删除中...' : '删除'}
-            </Button>
-          </DialogFooter>
-        </DialogContent>
-      </Dialog>
-    </div>
-  );
-};
-
-// 简单的骨架屏组件
-const Skeleton = ({ className }: { className?: string }) => (
-  <div className={`animate-pulse rounded-md bg-muted ${className}`} />
-);

+ 0 - 6
src/client/admin-shadcn/routes.tsx

@@ -9,7 +9,6 @@ import { UsersPage } from './pages/Users';
 import { LoginPage } from './pages/Login';
 import { FilesPage } from './pages/Files';
 import { AdvertisementsPage } from './pages/Advertisements';
-import { AdvertisementTypesPage } from './pages/AdvertisementTypes';
 
 export const router = createBrowserRouter([
   {
@@ -52,11 +51,6 @@ export const router = createBrowserRouter([
         element: <AdvertisementsPage />,
         errorElement: <ErrorPage />
       },
-      {
-        path: 'advertisement-types',
-        element: <AdvertisementTypesPage />,
-        errorElement: <ErrorPage />
-      },
       {
         path: '*',
         element: <NotFoundPage />,