Procházet zdrojové kódy

♻️ refactor(admin): 重构提交记录页面使用现代UI组件

- 将antd替换为shadcn/ui组件系统,提升用户体验
- 集成react-hook-form和zod实现更好的表单验证
- 使用@tanstack/react-query替换手动数据获取逻辑
- 添加加载骨架屏和空状态处理
- 优化搜索、分页和CRUD操作的交互体验
yourname před 6 měsíci
rodič
revize
e080e04567
1 změnil soubory, kde provedl 807 přidání a 398 odebrání
  1. 807 398
      src/client/admin/pages/SubmissionRecordsPage.tsx

+ 807 - 398
src/client/admin/pages/SubmissionRecordsPage.tsx

@@ -1,447 +1,856 @@
-import React, { useState, useEffect } from 'react';
-import { Table, Button, Modal, Form, Input, DatePicker, InputNumber, Space, Typography, message } from 'antd';
-import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined } from '@ant-design/icons';
+import React, { useState } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { format } from 'date-fns';
+import { zhCN } from 'date-fns/locale';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useForm } from 'react-hook-form';
+import { toast } from 'sonner';
+import { Plus, Search, Edit, Trash2 } from 'lucide-react';
+
 import { submissionRecordsClient } from '@/client/api';
 import type { InferResponseType, InferRequestType } from 'hono/client';
-import { App } from 'antd';
+import { CreateSubmissionRecordsDto, UpdateSubmissionRecordsDto } from '@/server/modules/submission/submission-records.schema';
+
+import { Button } from '@/client/components/ui/button';
+import { Input } from '@/client/components/ui/input';
+import { Card, CardContent, CardHeader, CardTitle } from '@/client/components/ui/card';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/client/components/ui/table';
+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 { Skeleton } from '@/client/components/ui/skeleton';
+import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/client/components/ui/alert-dialog';
 
-const { Title } = Typography;
+import { DataTablePagination } from '@/client/admin/components/DataTablePagination';
 
-// 定义类型
 type SubmissionRecordsListResponse = InferResponseType<typeof submissionRecordsClient.$get, 200>;
 type SubmissionRecordsItem = SubmissionRecordsListResponse['data'][0];
 type CreateSubmissionRecordsRequest = InferRequestType<typeof submissionRecordsClient.$post>['json'];
 type UpdateSubmissionRecordsRequest = InferRequestType<typeof submissionRecordsClient[':id']['$put']>['json'];
 
-export const SubmissionRecordsPage: React.FC = () => {
-  const [data, setData] = useState<SubmissionRecordsItem[]>([]);
-  const [loading, setLoading] = useState<boolean>(true);
-  const [pagination, setPagination] = useState({
-    current: 1,
-    pageSize: 10,
-    total: 0,
+// 表单schema
+const createFormSchema = CreateSubmissionRecordsDto;
+const updateFormSchema = UpdateSubmissionRecordsDto;
+
+// 提交记录管理页面
+export const SubmissionRecordsPage = () => {
+  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<SubmissionRecordsItem | 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 [editingRecord, setEditingRecord] = useState<any>(null);
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+  const [recordToDelete, setRecordToDelete] = useState<number | null>(null);
+
+  // 创建表单
+  const createForm = useForm<CreateSubmissionRecordsRequest>({
+    resolver: zodResolver(createFormSchema),
+    defaultValues: {
+      classroomNo: null,
+      userId: null,
+      nickname: null,
+      score: null,
+      code: null,
+      trainingDate: null,
+      mark: null,
+      status: null,
+      holdingStock: null,
+      holdingCash: null,
+      price: null,
+      profitAmount: null,
+      profitPercent: null,
+      totalProfitAmount: null,
+      totalProfitPercent: null
+    }
+  });
+
+  // 更新表单
+  const updateForm = useForm<UpdateSubmissionRecordsRequest>({
+    resolver: zodResolver(updateFormSchema),
+    defaultValues: {
+      classroomNo: null,
+      userId: null,
+      nickname: null,
+      score: null,
+      code: null,
+      trainingDate: null,
+      mark: null,
+      status: null,
+      holdingStock: null,
+      holdingCash: null,
+      price: null,
+      profitAmount: null,
+      profitPercent: null,
+      totalProfitAmount: null,
+      totalProfitPercent: null
+    }
+  });
+
+  // 获取提交记录列表
+  const { data: recordsData, isLoading, refetch } = useQuery({
+    queryKey: ['submission-records', searchParams],
+    queryFn: async () => {
       const res = await submissionRecordsClient.$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('获取数据失败');
+      if (res.status !== 200) {
+        throw new Error('获取提交记录列表失败');
       }
-      
-      const result = await res.json() as SubmissionRecordsListResponse;
-      setData(result.data);
-      setPagination(prev => ({
-        ...prev,
-        total: result.pagination.total,
-      }));
-    } catch (error) {
-      console.error('获取提交记录数据失败:', error);
-      antMessage.error('获取数据失败,请重试');
-    } finally {
-      setLoading(false);
+      return await res.json();
     }
-  };
+  });
 
-  // 初始加载和分页、搜索变化时重新获取数据
-  useEffect(() => {
-    fetchData();
-  }, [pagination.current, pagination.pageSize]);
+  const records = recordsData?.data || [];
+  const totalCount = recordsData?.pagination?.total || 0;
 
-  // 搜索功能
-  const handleSearch = () => {
-    setPagination(prev => ({ ...prev, current: 1 }));
-    fetchData();
+  // 处理搜索
+  const handleSearch = (e: React.FormEvent) => {
+    e.preventDefault();
+    setSearchParams(prev => ({ ...prev, page: 1 }));
   };
 
-  // 显示创建模态框
+  // 打开创建记录对话
   const showCreateModal = () => {
-    setIsEditing(false);
-    setCurrentItem(null);
-    form.resetFields();
-    setIsModalVisible(true);
+    setIsCreateForm(true);
+    setEditingRecord(null);
+    createForm.reset();
+    setIsModalOpen(true);
   };
 
-  // 显示编辑模态
-  const showEditModal = (record: SubmissionRecordsItem) => {
-    setIsEditing(true);
-    setCurrentItem(record);
-    form.setFieldsValue({
-      classroomNo: record.classroomNo || undefined,
-      userId: record.userId || undefined,
-      nickname: record.nickname || undefined,
-      score: record.score || undefined,
-      code: record.code || undefined,
+  // 打开编辑记录对话
+  const showEditModal = (record: any) => {
+    setIsCreateForm(false);
+    setEditingRecord(record);
+    updateForm.reset({
+      classroomNo: record.classroomNo || null,
+      userId: record.userId || null,
+      nickname: record.nickname || null,
+      score: record.score || null,
+      code: record.code || null,
       trainingDate: record.trainingDate ? new Date(record.trainingDate) : null,
-      mark: record.mark || undefined,
-      status: record.status || undefined,
-      holdingStock: record.holdingStock || undefined,
-      holdingCash: record.holdingCash || undefined,
-      price: record.price || undefined,
-      profitAmount: record.profitAmount || undefined,
-      profitPercent: record.profitPercent || undefined,
-      totalProfitAmount: record.totalProfitAmount || undefined,
-      totalProfitPercent: record.totalProfitPercent || undefined,
+      mark: record.mark || null,
+      status: record.status || null,
+      holdingStock: record.holdingStock || null,
+      holdingCash: record.holdingCash || null,
+      price: record.price || null,
+      profitAmount: record.profitAmount || null,
+      profitPercent: record.profitPercent || null,
+      totalProfitAmount: record.totalProfitAmount || null,
+      totalProfitPercent: record.totalProfitPercent || null
     });
-    setIsModalVisible(true);
+    setIsModalOpen(true);
   };
 
-  // 处理表单提交
-  const handleSubmit = async () => {
+  // 处理创建记录
+  const handleCreateSubmit = async (data: CreateSubmissionRecordsRequest) => {
     try {
-      const values = await form.validateFields();
-      
-      if (isEditing && currentItem) {
-        // 更新数据
-        const res = await submissionRecordsClient[':id'].$put({
-          param: { id: currentItem.id },
-          json: values as UpdateSubmissionRecordsRequest,
-        });
-        
-        if (!res.ok) {
-          throw new Error('更新失败');
-        }
-        antMessage.success('更新成功');
-      } else {
-        // 创建新数据
-        const res = await submissionRecordsClient.$post({
-          json: values as CreateSubmissionRecordsRequest,
-        });
-        
-        if (!res.ok) {
-          throw new Error('创建失败');
-        }
-        antMessage.success('创建成功');
+      const res = await submissionRecordsClient.$post({ json: data });
+      if (res.status !== 201) {
+        const error = await res.json();
+        throw new Error(error.message || '创建提交记录失败');
       }
-      
-      setIsModalVisible(false);
-      fetchData();
+      toast.success('提交记录创建成功');
+      setIsModalOpen(false);
+      createForm.reset();
+      refetch();
     } catch (error) {
-      console.error('提交表单失败:', error);
-      antMessage.error(isEditing ? '更新失败,请重试' : '创建失败,请重试');
+      toast.error(error instanceof Error ? error.message : '创建失败,请重试');
     }
   };
 
-  // 删除数据
-  const handleDelete = async (id: number) => {
+  // 处理更新记录
+  const handleUpdateSubmit = async (data: UpdateSubmissionRecordsRequest) => {
+    if (!editingRecord) return;
+    
     try {
-      const res = await submissionRecordsClient[':id'].$delete({
-        param: { id },
+      const res = await submissionRecordsClient[':id']['$put']({
+        param: { id: editingRecord.id.toString() },
+        json: data
       });
-      
-      if (!res.ok) {
-        throw new Error('删除失败');
+      if (res.status !== 200) {
+        const error = await res.json();
+        throw new Error(error.message || '更新提交记录失败');
       }
-      
-      antMessage.success('删除成功');
-      fetchData();
+      toast.success('提交记录更新成功');
+      setIsModalOpen(false);
+      updateForm.reset();
+      refetch();
     } catch (error) {
-      console.error('删除数据失败:', error);
-      antMessage.error('删除失败,请重试');
+      toast.error(error instanceof Error ? error.message : '更新失败,请重试');
     }
   };
 
-  // 表格列定义
-  const columns = [
-    {
-      title: 'ID',
-      dataIndex: 'id',
-      key: 'id',
-      width: 80,
-    },
-    {
-      title: '教室号',
-      dataIndex: 'classroomNo',
-      key: 'classroomNo',
-    },
-    {
-      title: '用户ID',
-      dataIndex: 'userId',
-      key: 'userId',
-    },
-    {
-      title: '昵称',
-      dataIndex: 'nickname',
-      key: 'nickname',
-    },
-    {
-      title: '成绩',
-      dataIndex: 'score',
-      key: 'score',
-    },
-    {
-      title: '代码',
-      dataIndex: 'code',
-      key: 'code',
-    },
-    {
-      title: '训练日期',
-      dataIndex: 'trainingDate',
-      key: 'trainingDate',
-      render: (date: string) => date ? new Date(date).toLocaleString() : '-',
-    },
-    {
-      title: '状态',
-      dataIndex: 'status',
-      key: 'status',
-    },
-    {
-      title: '收益率',
-      dataIndex: 'profitPercent',
-      key: 'profitPercent',
-      render: (percent: number) => percent ? `${percent}%` : '-',
-    },
-    {
-      title: '累计收益率',
-      dataIndex: 'totalProfitPercent',
-      key: 'totalProfitPercent',
-      render: (percent: number) => percent ? `${percent}%` : '-',
-    },
-    {
-      title: '操作',
-      key: 'action',
-      render: (_: any, record: SubmissionRecordsItem) => (
-        <Space size="small">
-          <Button 
-            type="text" 
-            icon={<EditOutlined />} 
-            onClick={() => showEditModal(record)}
-          >
-            编辑
-          </Button>
-          <Button 
-            type="text" 
-            danger 
-            icon={<DeleteOutlined />} 
-            onClick={() => handleDelete(record.id)}
-          >
-            删除
-          </Button>
-        </Space>
-      ),
-    },
-  ];
+  // 打开删除确认对话框
+  const showDeleteDialog = (recordId: number) => {
+    setRecordToDelete(recordId);
+    setDeleteDialogOpen(true);
+  };
+
+  // 处理删除记录
+  const handleDelete = async () => {
+    if (!recordToDelete) return;
+    
+    try {
+      const res = await submissionRecordsClient[':id']['$delete']({
+        param: { id: recordToDelete.toString() }
+      });
+      if (res.status !== 204) {
+        throw new Error('删除提交记录失败');
+      }
+      toast.success('提交记录删除成功');
+      setDeleteDialogOpen(false);
+      setRecordToDelete(null);
+      refetch();
+    } catch (error) {
+      toast.error('删除失败,请重试');
+    }
+  };
+
+  // 加载状态
+  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>
+            <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="搜索教室号、用户ID或代码"
-          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 })}
-        bordered
-        scroll={{ x: 'max-content' }}
-        headerCellStyle={{ backgroundColor: '#f9fafb' }}
-        rowClassName={(record, index) => index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}
-        rowKey="id"
-      />
-      
-      <Modal
-        title={isEditing ? "编辑提交记录" : "添加提交记录"}
-        open={isModalVisible}
-        onOk={handleSubmit}
-        onCancel={() => setIsModalVisible(false)}
-        destroyOnClose
-        maskClosable={false}
-        width={700}
-      >
-        <Form
-          form={form}
-          layout="vertical"
-          name="submission_records_form"
-        >
-          <Form.Item
-            name="classroomNo"
-            label="教室号"
-            rules={[{ max: 255, message: '教室号不能超过255个字符' }]}
-          >
-            <Input placeholder="请输入教室号" />
-          </Form.Item>
-          
-          <Form.Item
-            name="userId"
-            label="用户ID"
-            rules={[{ max: 255, message: '用户ID不能超过255个字符' }]}
-          >
-            <Input placeholder="请输入用户ID" />
-          </Form.Item>
-          
-          <Form.Item
-            name="nickname"
-            label="昵称"
-            rules={[{ max: 255, message: '昵称不能超过255个字符' }]}
-          >
-            <Input placeholder="请输入昵称" />
-          </Form.Item>
-          
-          <Form.Item
-            name="score"
-            label="成绩"
-          >
-            <InputNumber 
-              placeholder="请输入成绩" 
-              style={{ width: '100%' }} 
-              formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
-              parser={value => value!.replace(/\$\s?|(,*)/g, '')}
-              precision={2}
-            />
-          </Form.Item>
-          
-          <Form.Item
-            name="code"
-            label="代码"
-            rules={[{ max: 255, message: '代码不能超过255个字符' }]}
-          >
-            <Input placeholder="请输入代码" />
-          </Form.Item>
-          
-          <Form.Item
-            name="trainingDate"
-            label="训练日期"
-          >
-            <DatePicker showTime placeholder="请选择训练日期" style={{ width: '100%' }} />
-          </Form.Item>
-          
-          <Form.Item
-            name="mark"
-            label="标记"
-            rules={[{ max: 255, message: '标记不能超过255个字符' }]}
-          >
-            <Input placeholder="请输入标记" />
-          </Form.Item>
-          
-          <Form.Item
-            name="status"
-            label="状态"
-          >
-            <InputNumber placeholder="请输入状态" style={{ width: '100%' }} />
-          </Form.Item>
-          
-          <div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
-            <Form.Item
-              name="holdingStock"
-              label="持股"
-              rules={[{ max: 255, message: '持股信息不能超过255个字符' }]}
-              style={{ flex: 1 }}
-            >
-              <Input placeholder="请输入持股信息" />
-            </Form.Item>
-            
-            <Form.Item
-              name="holdingCash"
-              label="持币"
-              rules={[{ max: 255, message: '持币信息不能超过255个字符' }]}
-              style={{ flex: 1 }}
-            >
-              <Input placeholder="请输入持币信息" />
-            </Form.Item>
-          </div>
-          
-          <div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
-            <Form.Item
-              name="price"
-              label="价格"
-              style={{ flex: 1 }}
-            >
-              <InputNumber 
-                placeholder="请输入价格" 
-                style={{ width: '100%' }} 
-                precision={2}
-              />
-            </Form.Item>
-            
-            <Form.Item
-              name="profitAmount"
-              label="收益金额"
-              style={{ flex: 1 }}
-            >
-              <InputNumber 
-                placeholder="请输入收益金额" 
-                style={{ width: '100%' }} 
-                precision={2}
-              />
-            </Form.Item>
-          </div>
-          
-          <div style={{ display: 'flex', gap: 16 }}>
-            <Form.Item
-              name="profitPercent"
-              label="收益率(%)"
-              style={{ flex: 1 }}
-            >
-              <InputNumber 
-                placeholder="请输入收益率" 
-                style={{ width: '100%' }} 
-                precision={2}
-              />
-            </Form.Item>
-            
-            <Form.Item
-              name="totalProfitPercent"
-              label="累计收益率(%)"
-              style={{ flex: 1 }}
-            >
-              <InputNumber 
-                placeholder="请输入累计收益率" 
-                style={{ width: '100%' }} 
-                precision={2}
-              />
-            </Form.Item>
-            
-            <Form.Item
-              name="totalProfitAmount"
-              label="累计收益金额"
-              style={{ flex: 1 }}
-            >
-              <InputNumber 
-                placeholder="请输入累计收益金额" 
-                style={{ width: '100%' }} 
-                precision={2}
+
+      {/* 搜索区域 */}
+      <Card>
+        <CardHeader>
+          <CardTitle>提交记录列表</CardTitle>
+        </CardHeader>
+        <CardContent>
+          <form onSubmit={handleSearch} className="flex gap-2 mb-4">
+            <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="搜索教室号、用户ID或代码..."
+                value={searchParams.search}
+                onChange={(e) => setSearchParams(prev => ({ ...prev, search: e.target.value }))}
+                className="pl-8"
               />
-            </Form.Item>
+            </div>
+            <Button type="submit" variant="outline">
+              搜索
+            </Button>
+          </form>
+
+          {/* 提交记录表格 */}
+          <div className="rounded-md border">
+            <Table>
+              <TableHeader>
+                <TableRow>
+                  <TableHead>ID</TableHead>
+                  <TableHead>教室号</TableHead>
+                  <TableHead>用户ID</TableHead>
+                  <TableHead>昵称</TableHead>
+                  <TableHead>成绩</TableHead>
+                  <TableHead>代码</TableHead>
+                  <TableHead>训练日期</TableHead>
+                  <TableHead>收益率</TableHead>
+                  <TableHead>累计收益率</TableHead>
+                  <TableHead className="text-right">操作</TableHead>
+                </TableRow>
+              </TableHeader>
+              <TableBody>
+                {records.map((record) => (
+                  <TableRow key={record.id}>
+                    <TableCell>{record.id}</TableCell>
+                    <TableCell>{record.classroomNo || '-'}</TableCell>
+                    <TableCell>{record.userId || '-'}</TableCell>
+                    <TableCell>{record.nickname || '-'}</TableCell>
+                    <TableCell>{record.score || '-'}</TableCell>
+                    <TableCell>{record.code || '-'}</TableCell>
+                    <TableCell>
+                      {record.trainingDate ? format(new Date(record.trainingDate), 'yyyy-MM-dd HH:mm', { locale: zhCN }) : '-'}
+                    </TableCell>
+                    <TableCell>{record.profitPercent ? `${record.profitPercent}%` : '-'}</TableCell>
+                    <TableCell>{record.totalProfitPercent ? `${record.totalProfitPercent}%` : '-'}</TableCell>
+                    <TableCell className="text-right">
+                      <div className="flex justify-end gap-2">
+                        <Button
+                          variant="ghost"
+                          size="sm"
+                          onClick={() => showEditModal(record)}
+                        >
+                          <Edit className="h-4 w-4" />
+                        </Button>
+                        <Button
+                          variant="ghost"
+                          size="sm"
+                          onClick={() => showDeleteDialog(record.id)}
+                          className="text-destructive hover:text-destructive"
+                        >
+                          <Trash2 className="h-4 w-4" />
+                        </Button>
+                      </div>
+                    </TableCell>
+                  </TableRow>
+                ))}
+              </TableBody>
+            </Table>
           </div>
-        </Form>
-      </Modal>
+
+          {records.length === 0 && (
+            <div className="text-center py-8">
+              <p className="text-muted-foreground">暂无数据</p>
+            </div>
+          )}
+
+          {/* 分页 */}
+          <DataTablePagination
+            currentPage={searchParams.page}
+            pageSize={searchParams.limit}
+            totalCount={totalCount}
+            onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
+          />
+        </CardContent>
+      </Card>
+
+      {/* 创建/编辑记录对话框 */}
+      <Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
+        <DialogContent className="sm:max-w-[600px] 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">
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="classroomNo"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>教室号</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入教室号" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="userId"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>用户ID</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入用户ID" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="nickname"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>昵称</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入昵称" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="score"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>成绩</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入成绩" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="code"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>代码</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入代码" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="trainingDate"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>训练日期</FormLabel>
+                        <FormControl>
+                          <Input 
+                            type="datetime-local" 
+                            {...field} 
+                            value={field.value ? format(new Date(field.value), "yyyy-MM-dd'T'HH:mm") : ''}
+                            onChange={(e) => field.onChange(e.target.value ? new Date(e.target.value) : null)}
+                          />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={createForm.control}
+                  name="mark"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>标记</FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入标记" {...field} value={field.value || ''} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={createForm.control}
+                  name="status"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>状态</FormLabel>
+                      <FormControl>
+                        <Input type="number" placeholder="请输入状态" {...field} value={field.value || ''} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="holdingStock"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>持股</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入持股信息" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="holdingCash"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>持币</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入持币信息" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                <FormField
+                    control={createForm.control}
+                    name="price"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>价格</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入价格" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="profitAmount"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>收益金额</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入收益金额" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="profitPercent"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>收益率(%)</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入收益率" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="totalProfitPercent"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>累计收益率(%)</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入累计收益率" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="totalProfitAmount"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>累计收益金额</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入累计收益金额" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <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">
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="classroomNo"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>教室号</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入教室号" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="userId"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>用户ID</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入用户ID" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="nickname"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>昵称</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入昵称" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="score"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>成绩</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入成绩" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="code"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>代码</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入代码" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="trainingDate"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>训练日期</FormLabel>
+                        <FormControl>
+                          <Input 
+                            type="datetime-local" 
+                            {...field} 
+                            value={field.value ? format(new Date(field.value), "yyyy-MM-dd'T'HH:mm") : ''}
+                            onChange={(e) => field.onChange(e.target.value ? new Date(e.target.value) : null)}
+                          />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={updateForm.control}
+                  name="mark"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>标记</FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入标记" {...field} value={field.value || ''} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="status"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>状态</FormLabel>
+                      <FormControl>
+                        <Input type="number" placeholder="请输入状态" {...field} value={field.value || ''} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="holdingStock"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>持股</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入持股信息" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="holdingCash"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>持币</FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入持币信息" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="price"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>价格</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入价格" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="profitAmount"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>收益金额</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入收益金额" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="profitPercent"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>收益率(%)</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入收益率" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="totalProfitPercent"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>累计收益率(%)</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入累计收益率" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="totalProfitAmount"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>累计收益金额</FormLabel>
+                        <FormControl>
+                          <Input type="number" step="0.01" placeholder="请输入累计收益金额" {...field} value={field.value || ''} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit">更新</Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          )}
+        </DialogContent>
+      </Dialog>
+
+      {/* 删除确认对话框 */}
+      <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
+        <AlertDialogContent>
+          <AlertDialogHeader>
+            <AlertDialogTitle>确认删除</AlertDialogTitle>
+            <AlertDialogDescription>
+              确定要删除这个提交记录吗?此操作无法撤销。
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>取消</AlertDialogCancel>
+            <AlertDialogAction onClick={handleDelete}>删除</AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
     </div>
   );
 };