2
0
Просмотр исходного кода

♻️ refactor(date-notes): 重构日期备注管理页面

- 迁移UI组件库,从Ant Design替换为自定义组件系统
- 引入React Query进行数据获取和缓存管理
- 集成react-hook-form和zod实现表单验证
- 优化状态管理,使用函数式组件和hooks
- 改进用户体验,添加骨架屏加载状态
- 调整页面布局结构,提高代码可维护性

✨ feat(date-notes): 增强日期备注管理功能

- 添加删除确认对话框,防止误操作
- 优化搜索功能,支持表单提交方式
- 改进分页组件,提供更好的导航体验
- 实现创建/编辑表单分离,提高用户体验
- 添加状态标签显示,区分原始数据和已更新数据

💄 style(date-notes): 更新日期备注页面样式

- 统一图标使用Lucide图标库
- 优化表格样式和响应式布局
- 改进模态框样式和交互效果
- 调整颜色方案,提升视觉层次感
- 优化表单元素样式,提高一致性
yourname 6 месяцев назад
Родитель
Сommit
1b2b436b0b
2 измененных файлов с 412 добавлено и 256 удалено
  1. 409 253
      src/client/admin/pages/DateNotesPage.tsx
  2. 3 3
      src/server/modules/stock/date-notes.schema.ts

+ 409 - 253
src/client/admin/pages/DateNotesPage.tsx

@@ -1,12 +1,26 @@
-import dayjs from 'dayjs';
-import React, { useState, useEffect } from 'react';
-import { Table, Button, Modal, Form, Input, DatePicker, Space, Typography, message, Tag } from 'antd';
-import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined } from '@ant-design/icons';
+import React, { useState } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { format } from 'date-fns';
+import { zhCN } from 'date-fns/locale';
+import { z } from 'zod';
+import { toast } from 'sonner';
+import { Plus, Search, Edit, Trash2, Calendar } from 'lucide-react';
+
 import { dateNotesClient } from '@/client/api';
 import type { InferResponseType, InferRequestType } from 'hono/client';
-import { App } from 'antd';
+import { CreateDateNotesDto, UpdateDateNotesDto } from '@/server/modules/stock/date-notes.schema';
 
-const { Title } = Typography;
+import { Button } from '@/client/components/ui/button';
+import { Input } from '@/client/components/ui/input';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
+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 { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/client/components/ui/table';
+import { Badge } from '@/client/components/ui/badge';
+import { DataTablePagination } from '@/client/admin/components/DataTablePagination';
+import { Skeleton } from '@/client/components/ui/skeleton';
 
 // 定义类型
 type DateNotesListResponse = InferResponseType<typeof dateNotesClient.$get, 200>;
@@ -14,294 +28,436 @@ type DateNotesItem = DateNotesListResponse['data'][0];
 type CreateDateNotesRequest = InferRequestType<typeof dateNotesClient.$post>['json'];
 type UpdateDateNotesRequest = InferRequestType<typeof dateNotesClient[':id']['$put']>['json'];
 
-export const DateNotesPage: React.FC = () => {
-  const [data, setData] = useState<DateNotesItem[]>([]);
-  const [loading, setLoading] = useState<boolean>(true);
-  const [pagination, setPagination] = useState({
-    current: 1,
-    pageSize: 10,
-    total: 0,
+// 表单Schema直接使用后端定义
+const createFormSchema = CreateDateNotesDto;
+const updateFormSchema = UpdateDateNotesDto;
+
+export const DateNotesPage = () => {
+  const [searchParams, setSearchParams] = useState({
+    page: 1,
+    limit: 10,
+    search: '',
   });
-  const [searchText, setSearchText] = useState('');
-  const [isModalVisible, setIsModalVisible] = useState(false);
-  const [isEditing, setIsEditing] = useState(false);
-  const [currentItem, setCurrentItem] = useState<DateNotesItem | null>(null);
-  const [form] = Form.useForm();
-  const { message: antMessage } = App.useApp();
-
-  // 获取数据列表
-  const fetchData = async () => {
-    try {
-      setLoading(true);
+  const [isModalOpen, setIsModalOpen] = useState(false);
+  const [isCreateForm, setIsCreateForm] = useState(true);
+  const [editingEntity, setEditingEntity] = useState<DateNotesItem | null>(null);
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+  const [entityToDelete, setEntityToDelete] = useState<number | null>(null);
+
+  // 表单实例
+  const createForm = useForm<CreateDateNotesRequest>({
+    resolver: zodResolver(createFormSchema),
+    defaultValues: {
+      code: '',
+      noteDate: new Date() as any,
+      note: '',
+    },
+  });
+
+  const updateForm = useForm<UpdateDateNotesRequest>({
+    resolver: zodResolver(updateFormSchema),
+  });
+
+  // 数据查询
+  const { data, isLoading, refetch } = useQuery({
+    queryKey: ['date-notes', searchParams],
+    queryFn: async () => {
       const res = await dateNotesClient.$get({
         query: {
-          page: pagination.current,
-          pageSize: pagination.pageSize,
-          keyword: searchText,
+          page: searchParams.page,
+          pageSize: searchParams.limit,
+          keyword: searchParams.search,
         },
       });
-      
-      if (!res.ok) {
-        throw new Error('获取数据失败');
-      }
-      
-      const result = await res.json() as DateNotesListResponse;
-      setData(result.data);
-      setPagination(prev => ({
-        ...prev,
-        total: result.pagination.total,
-      }));
-    } catch (error) {
-      console.error('获取日期备注数据失败:', error);
-      antMessage.error('获取数据失败,请重试');
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  // 初始加载和分页、搜索变化时重新获取数据
-  useEffect(() => {
-    fetchData();
-  }, [pagination.current, pagination.pageSize]);
+      if (res.status !== 200) throw new Error('获取列表失败');
+      return await res.json();
+    },
+  });
 
   // 搜索功能
   const handleSearch = () => {
-    setPagination(prev => ({ ...prev, current: 1 }));
-    fetchData();
+    setSearchParams(prev => ({ ...prev, page: 1 }));
   };
 
   // 显示创建模态框
   const showCreateModal = () => {
-    setIsEditing(false);
-    setCurrentItem(null);
-    form.resetFields();
-    setIsModalVisible(true);
+    setIsCreateForm(true);
+    setEditingEntity(null);
+    createForm.reset();
+    setIsModalOpen(true);
   };
 
   // 显示编辑模态框
   const showEditModal = (record: DateNotesItem) => {
-    setIsEditing(true);
-    setCurrentItem(record);
-    form.setFieldsValue({
+    setIsCreateForm(false);
+    setEditingEntity(record);
+    updateForm.reset({
       code: record.code,
-      noteDate: record.noteDate ? dayjs(record.noteDate) : null,
+      noteDate: record.noteDate as any,
       note: record.note,
     });
-    setIsModalVisible(true);
+    setIsModalOpen(true);
   };
 
-  // 处理表单提交
-  const handleSubmit = async () => {
+  // 处理创建提交
+  const handleCreateSubmit = async (data: CreateDateNotesRequest) => {
     try {
-      const values = await form.validateFields();
+      const res = await dateNotesClient.$post({ json: data });
+      if (res.status !== 201) throw new Error('创建失败');
       
-      if (isEditing && currentItem) {
-        // 更新数据
-        const res = await dateNotesClient[':id'].$put({
-          param: { id: currentItem.id },
-          json: values as UpdateDateNotesRequest,
-        });
-        
-        if (!res.ok) {
-          throw new Error('更新失败');
-        }
-        antMessage.success('更新成功');
-      } else {
-        // 创建新数据
-        const res = await dateNotesClient.$post({
-          json: values as CreateDateNotesRequest,
-        });
-        
-        if (!res.ok) {
-          throw new Error('创建失败');
-        }
-        antMessage.success('创建成功');
-      }
+      toast.success('创建成功');
+      setIsModalOpen(false);
+      refetch();
+    } catch (error) {
+      console.error('创建失败:', error);
+      toast.error('创建失败,请重试');
+    }
+  };
+
+  // 处理更新提交
+  const handleUpdateSubmit = async (data: UpdateDateNotesRequest) => {
+    if (!editingEntity) return;
+
+    try {
+      const res = await dateNotesClient[':id'].$put({
+        param: { id: editingEntity.id.toString() },
+        json: data,
+      });
+      if (res.status !== 200) throw new Error('更新失败');
       
-      setIsModalVisible(false);
-      fetchData();
+      toast.success('更新成功');
+      setIsModalOpen(false);
+      refetch();
     } catch (error) {
-      console.error('提交表单失败:', error);
-      antMessage.error(isEditing ? '更新失败,请重试' : '创建失败,请重试');
+      console.error('更新失败:', error);
+      toast.error('更新失败,请重试');
     }
   };
 
-  // 删除数据
-  const handleDelete = async (id: number) => {
+  // 显示删除确认
+  const showDeleteDialog = (id: number) => {
+    setEntityToDelete(id);
+    setDeleteDialogOpen(true);
+  };
+
+  // 确认删除
+  const confirmDelete = async () => {
+    if (!entityToDelete) return;
+
     try {
       const res = await dateNotesClient[':id'].$delete({
-        param: { id },
+        param: { id: entityToDelete.toString() },
       });
       
-      if (!res.ok) {
-        throw new Error('删除失败');
-      }
+      if (res.status !== 204) throw new Error('删除失败');
       
-      antMessage.success('删除成功');
-      fetchData();
+      toast.success('删除成功');
+      setDeleteDialogOpen(false);
+      refetch();
     } catch (error) {
-      console.error('删除数据失败:', error);
-      antMessage.error('删除失败,请重试');
+      console.error('删除失败:', error);
+      toast.error('删除失败,请重试');
     }
   };
 
-  // 表格列定义
-  const columns = [
-    {
-      title: 'ID',
-      dataIndex: 'id',
-      key: 'id',
-      width: 80,
-    },
-    {
-      title: '股票代码',
-      dataIndex: 'code',
-      key: 'code',
-      filters: [
-        ...Array.from(new Set(data.map(item => item.code))).map(code => ({
-          text: code,
-          value: code,
-        }))
-      ],
-      onFilter: (value: string, record: DateNotesItem) => record.code === value,
-    },
-    {
-      title: '备注日期',
-      dataIndex: 'noteDate',
-      key: 'noteDate',
-      render: (date: string) => date ? new Date(date).toLocaleString() : '-',
-    },
-    {
-      title: '备注内容',
-      dataIndex: 'note',
-      key: 'note',
-    },
-    {
-      title: '创建时间',
-      dataIndex: 'createdAt',
-      key: 'createdAt',
-      render: (date: string) => new Date(date).toLocaleString(),
-    },
-    {
-      title: '状态',
-      key: 'status',
-      render: (_, record: DateNotesItem) => (
-        <Tag color={new Date(record.updatedAt) > new Date(record.createdAt) ? 'blue' : 'green'}>
-          {new Date(record.updatedAt) > new Date(record.createdAt) ? '已更新' : '原始数据'}
-        </Tag>
-      ),
-    },
-    {
-      title: '操作',
-      key: 'action',
-      render: (_: any, record: DateNotesItem) => (
-        <Space size="small">
-          <Button 
-            type="text" 
-            icon={<EditOutlined />} 
-            onClick={() => showEditModal(record)}
-          >
-            编辑
-          </Button>
-          <Button 
-            type="text" 
-            danger 
-            icon={<DeleteOutlined />} 
-            onClick={() => handleDelete(record.id)}
-          >
-            删除
-          </Button>
-        </Space>
-      ),
-    },
-  ];
+  // 骨架屏加载状态
+  if (isLoading) {
+    return (
+      <div className="space-y-4">
+        <div className="flex justify-between items-center">
+          <Skeleton className="h-8 w-48" />
+          <Skeleton className="h-10 w-32" />
+        </div>
+        
+        <Card>
+          <CardHeader>
+            <Skeleton className="h-6 w-1/4" />
+          </CardHeader>
+          <CardContent>
+            <Skeleton className="h-10 w-full max-w-sm" />
+          </CardContent>
+        </Card>
+
+        <Card>
+          <CardContent className="pt-6">
+            <div className="space-y-3">
+              {[...Array(5)].map((_, i) => (
+                <div key={i} className="flex gap-4">
+                  <Skeleton className="h-10 flex-1" />
+                  <Skeleton className="h-10 flex-1" />
+                  <Skeleton className="h-10 flex-1" />
+                  <Skeleton className="h-10 w-20" />
+                </div>
+              ))}
+            </div>
+          </CardContent>
+        </Card>
+      </div>
+    );
+  }
 
   return (
-    <div className="page-container">
-      <div className="page-header" style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
-        <Title level={2}>日期备注管理</Title>
-        <Button type="primary" icon={<PlusOutlined />} onClick={showCreateModal}>
+    <div className="space-y-4">
+      {/* 页面标题和按钮 */}
+      <div className="flex justify-between items-center">
+        <h1 className="text-2xl font-bold">日期备注管理</h1>
+        <Button onClick={showCreateModal}>
+          <Plus className="mr-2 h-4 w-4" />
           添加备注
         </Button>
       </div>
-      
-      <div className="search-container" style={{ marginBottom: 16 }}>
-        <Input
-          placeholder="搜索股票代码或备注内容"
-          value={searchText}
-          onChange={(e) => setSearchText(e.target.value)}
-          onPressEnter={handleSearch}
-          style={{ width: 300 }}
-          suffix={<SearchOutlined onClick={handleSearch} />}
-        />
-      </div>
-      
-      <Table
-        columns={columns}
-        dataSource={data.map(item => ({ ...item, key: item.id }))}
-        loading={loading}
-        pagination={{
-          current: pagination.current,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          showTotal: (total) => `共 ${total} 条记录`,
-        }}
-        onChange={(p) => setPagination({ ...pagination, current: p.current || 1, pageSize: p.pageSize || 10 })}
-        rowKey="id"
-        bordered
-        scroll={{ x: 'max-content' }}
-        headerCellStyle={{ backgroundColor: '#f9fafb' }}
-        rowClassName={(record, index) => index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}
-      />
-      
-      <Modal
-        title={isEditing ? "编辑日期备注" : "添加日期备注"}
-        open={isModalVisible}
-        onOk={handleSubmit}
-        onCancel={() => setIsModalVisible(false)}
-        destroyOnClose
-        maskClosable={false}
-        width={600}
-      >
-        <Form
-          form={form}
-          layout="vertical"
-          name="date_notes_form"
-        >
-          <Form.Item
-            name="code"
-            label="股票代码"
-            rules={[
-              { required: true, message: '请输入股票代码' },
-              { max: 255, message: '股票代码不能超过255个字符' }
-            ]}
-          >
-            <Input placeholder="请输入股票代码" />
-          </Form.Item>
-          
-          <Form.Item
-            name="noteDate"
-            label="备注日期"
-            rules={[{ required: true, message: '请选择备注日期' }]}
-          >
-            <DatePicker showTime placeholder="请选择备注日期" style={{ width: '100%' }} />
-          </Form.Item>
-          
-          <Form.Item
-            name="note"
-            label="备注内容"
-            rules={[
-              { required: true, message: '请输入备注内容' },
-              { max: 255, message: '备注内容不能超过255个字符' }
-            ]}
-          >
-            <Input.TextArea rows={4} placeholder="请输入备注内容" />
-          </Form.Item>
-        </Form>
-      </Modal>
+
+      {/* 搜索区域 */}
+      <Card>
+        <CardHeader>
+          <CardTitle>日期备注列表</CardTitle>
+          <CardDescription>管理股票日期备注信息</CardDescription>
+        </CardHeader>
+        <CardContent>
+          <div className="mb-4">
+            <form onSubmit={(e) => { e.preventDefault(); 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 className="w-[80px]">ID</TableHead>
+                  <TableHead>股票代码</TableHead>
+                  <TableHead>备注日期</TableHead>
+                  <TableHead>备注内容</TableHead>
+                  <TableHead>创建时间</TableHead>
+                  <TableHead>状态</TableHead>
+                  <TableHead className="text-right">操作</TableHead>
+                </TableRow>
+              </TableHeader>
+              <TableBody>
+                {data?.data.map((item) => (
+                  <TableRow key={item.id}>
+                    <TableCell className="font-medium">{item.id}</TableCell>
+                    <TableCell>{item.code}</TableCell>
+                    <TableCell>
+                      {item.noteDate 
+                        ? format(new Date(item.noteDate), 'yyyy-MM-dd HH:mm', { locale: zhCN })
+                        : '-'}
+                    </TableCell>
+                    <TableCell>{item.note}</TableCell>
+                    <TableCell>
+                      {item.createdAt 
+                        ? format(new Date(item.createdAt), 'yyyy-MM-dd HH:mm', { locale: zhCN })
+                        : '-'}
+                    </TableCell>
+                    <TableCell>
+                      <Badge 
+                        variant={new Date(item.updatedAt) > new Date(item.createdAt) ? 'default' : 'secondary'}
+                      >
+                        {new Date(item.updatedAt) > new Date(item.createdAt) ? '已更新' : '原始数据'}
+                      </Badge>
+                    </TableCell>
+                    <TableCell className="text-right">
+                      <div className="flex justify-end gap-2">
+                        <Button 
+                          variant="ghost" 
+                          size="icon" 
+                          onClick={() => showEditModal(item)}
+                        >
+                          <Edit className="h-4 w-4" />
+                        </Button>
+                        <Button 
+                          variant="ghost" 
+                          size="icon" 
+                          onClick={() => showDeleteDialog(item.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
+            currentPage={searchParams.page}
+            pageSize={searchParams.limit}
+            totalCount={data?.pagination.total || 0}
+            onPageChange={(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>
+
+          {isCreateForm ? (
+            // 创建表单
+            <Form {...createForm}>
+              <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+                <FormField
+                  control={createForm.control}
+                  name="code"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>股票代码 <span className="text-red-500">*</span></FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入股票代码" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="noteDate"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>备注日期 <span className="text-red-500">*</span></FormLabel>
+                      <FormControl>
+                        <Input 
+                          type="datetime-local" 
+                          {...field}
+                          value={field.value ? new Date(field.value).toISOString().slice(0, 16) : ''}
+                          onChange={(e) => field.onChange(new Date(e.target.value))}
+                        />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="note"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>备注内容 <span className="text-red-500">*</span></FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入备注内容" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit">创建</Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          ) : (
+            // 编辑表单
+            <Form {...updateForm}>
+              <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+                <FormField
+                  control={updateForm.control}
+                  name="code"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>股票代码</FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入股票代码" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="noteDate"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>备注日期</FormLabel>
+                      <FormControl>
+                        <Input 
+                          type="datetime-local" 
+                          {...field}
+                          value={field.value ? new Date(field.value).toISOString().slice(0, 16) : ''}
+                          onChange={(e) => field.onChange(new Date(e.target.value))}
+                        />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="note"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>备注内容</FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入备注内容" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit">更新</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}>
+              删除
+            </Button>
+          </DialogFooter>
+        </DialogContent>
+      </Dialog>
     </div>
   );
-};
-
-export default DateNotesPage;
+};

+ 3 - 3
src/server/modules/stock/date-notes.schema.ts

@@ -9,7 +9,7 @@ export const DateNotesSchema = z.object({
     description: '股票代码',
     example: '600000'
   }),
-  noteDate: z.coerce.date('备注日期格式不正确').openapi({
+  noteDate: z.coerce.date<Date>('备注日期格式不正确').openapi({
     description: '备注日期',
     example: '2024-01-15T00:00:00Z'
   }),
@@ -32,7 +32,7 @@ export const CreateDateNotesDto = z.object({
     description: '股票代码',
     example: '600000'
   }),
-  noteDate: z.coerce.date('备注日期格式不正确').openapi({
+  noteDate: z.coerce.date<Date>('备注日期格式不正确').openapi({
     description: '备注日期',
     example: '2024-01-15T00:00:00Z'
   }),
@@ -47,7 +47,7 @@ export const UpdateDateNotesDto = z.object({
     description: '股票代码',
     example: '600000'
   }),
-  noteDate: z.coerce.date('备注日期格式不正确').optional().openapi({
+  noteDate: z.coerce.date<Date>('备注日期格式不正确').optional().openapi({
     description: '备注日期',
     example: '2024-01-15T00:00:00Z'
   }),