Răsfoiți Sursa

迁移了admin XunlianCodes.tsx → pages_xunlian_codes.tsx

yourname 6 luni în urmă
părinte
comite
5435cd0474

+ 2 - 1
client/admin/api/index.ts

@@ -24,4 +24,5 @@ export * from './sys.ts';
 export * from './know_info.ts';
 export * from './maps.ts';
 export * from './classroom_data.ts';
-export * from './submission_records.ts';
+export * from './submission_records.ts';
+export * from './xunlian_codes.ts';

+ 22 - 0
client/admin/api/xunlian_codes.ts

@@ -0,0 +1,22 @@
+import axios from 'axios';
+import type { XunlianCode, XunlianCodeListResponse } from '../../share/types.ts';
+
+export const XunlianCodeAPI = {
+  getXunlianCodes: (params: {
+    page?: number;
+    pageSize?: number;
+    code?: string;
+    stock_name?: string;
+  }) => axios.get<XunlianCodeListResponse>('/xunlian-codes', { params }),
+
+  getXunlianCode: (id: number) => axios.get<XunlianCode>(`/xunlian-codes/${id}`),
+
+  createXunlianCode: (data: Partial<XunlianCode>) =>
+    axios.post<XunlianCode>('/xunlian-codes', data),
+
+  updateXunlianCode: (id: number, data: Partial<XunlianCode>) =>
+    axios.put<XunlianCode>(`/xunlian-codes/${id}`, data),
+
+  deleteXunlianCode: (id: number) =>
+    axios.delete<void>(`/xunlian-codes/${id}`),
+};

+ 317 - 0
client/admin/pages_xunlian_codes.tsx

@@ -0,0 +1,317 @@
+import React, { useState } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
+import {
+  Button, Table, Space,
+  Form, Input, message, Modal,
+  Card, Row, Col,
+  Popconfirm, Tag
+} from 'antd';
+import { 
+  useQuery,
+} from '@tanstack/react-query';
+import dayjs from 'dayjs';
+import weekday from 'dayjs/plugin/weekday';
+import localeData from 'dayjs/plugin/localeData';
+import 'dayjs/locale/zh-cn';
+import type { 
+  XunlianCode,
+  XunlianCodeListResponse
+} from '../share/types.ts';
+
+import { getEnumOptions } from './utils.ts';
+
+import { 
+  XunlianCodeAPI
+} from './api/xunlian_codes.ts';
+
+// 配置 dayjs 插件
+dayjs.extend(weekday);
+dayjs.extend(localeData);
+
+// 设置 dayjs 语言
+dayjs.locale('zh-cn');
+
+// 训练代码管理页面组件
+export const XunlianCodePage = () => {
+  const queryClient = useQueryClient();
+  const [modalVisible, setModalVisible] = useState(false);
+  const [formMode, setFormMode] = useState<'create' | 'edit'>('create');
+  const [editingId, setEditingId] = useState<number | null>(null);
+  const [form] = Form.useForm();
+  const [searchForm] = Form.useForm();
+  const [searchParams, setSearchParams] = useState({
+    code: '',
+    page: 1,
+    limit: 10,
+  });
+  
+  // 使用React Query获取训练代码列表
+  const { data: codesData, isLoading: isListLoading, refetch } = useQuery<XunlianCodeListResponse>({
+    queryKey: ['xunlianCodes', searchParams],
+    queryFn: async () => {
+      const response = await XunlianCodeAPI.getXunlianCodes({
+        page: searchParams.page,
+        pageSize: searchParams.limit,
+        code: searchParams.code,
+      });
+      return response.data;
+    },
+    placeholderData: {
+      data: [],
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        total: 0,
+        totalPages: 1
+      }
+    }
+  });
+  
+  const codes = React.useMemo(() => (codesData as XunlianCodeListResponse)?.data || [], [codesData]);
+  const pagination = React.useMemo(() => ({
+    current: (codesData as XunlianCodeListResponse)?.pagination?.current || 1,
+    pageSize: (codesData as XunlianCodeListResponse)?.pagination?.pageSize || 10,
+    total: (codesData as XunlianCodeListResponse)?.pagination?.total || 0,
+    totalPages: (codesData as XunlianCodeListResponse)?.pagination?.totalPages || 1
+  }), [codesData]);
+  
+  // 获取单个训练代码
+  const fetchCode = async (id: number) => {
+    try {
+      const response = await XunlianCodeAPI.getXunlianCode(id);
+      return response.data;
+    } catch (error) {
+      message.error('获取训练代码详情失败');
+      return null;
+    }
+  };
+  
+  // 处理表单提交
+  const handleSubmit = async (values: Partial<XunlianCode>) => {
+    try {
+      const response = formMode === 'create'
+        ? await XunlianCodeAPI.createXunlianCode(values)
+        : await XunlianCodeAPI.updateXunlianCode(editingId!, values);
+      
+      message.success(formMode === 'create' ? '创建训练代码成功' : '更新训练代码成功');
+      setModalVisible(false);
+      form.resetFields();
+      refetch();
+    } catch (error) {
+      message.error((error as Error).message);
+    }
+  };
+  
+  // 处理编辑
+  const handleEdit = async (id: number) => {
+    const code = await fetchCode(id);
+    if (code) {
+      setFormMode('edit');
+      setEditingId(id);
+      form.setFieldsValue(code);
+      setModalVisible(true);
+    }
+  };
+  
+  // 处理删除
+  const handleDelete = async (id: number) => {
+    try {
+      await XunlianCodeAPI.deleteXunlianCode(id);
+      
+      message.success('删除训练代码成功');
+      refetch();
+    } catch (error) {
+      message.error((error as Error).message);
+    }
+  };
+  
+  // 处理搜索
+  const handleSearch = async (values: any) => {
+    try {
+      queryClient.removeQueries({ queryKey: ['xunlianCodes'] });
+      setSearchParams({
+        code: values.code || '',
+        page: 1,
+        limit: searchParams.limit,
+      });
+    } catch (error) {
+      message.error('搜索失败');
+    }
+  };
+  
+  // 处理分页
+  const handlePageChange = (page: number, pageSize?: number) => {
+    setSearchParams(prev => ({
+      ...prev,
+      page,
+      limit: pageSize || prev.limit,
+    }));
+  };
+  
+  // 处理添加
+  const handleAdd = () => {
+    setFormMode('create');
+    setEditingId(null);
+    form.resetFields();
+    setModalVisible(true);
+  };
+  
+  // 表格列定义
+  const columns = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      key: 'id',
+      width: 80,
+    },
+    {
+      title: '股票代码',
+      dataIndex: 'code',
+      key: 'code',
+    },
+    {
+      title: '训练日期',
+      dataIndex: 'training_date',
+      key: 'training_date',
+      render: (date: string) => dayjs(date).format('YYYY-MM-DD'),
+    },
+    {
+      title: '提交用户',
+      dataIndex: 'nickname',
+      key: 'nickname',
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'created_at',
+      key: 'created_at',
+      render: (date: string) => dayjs(date).format('YYYY-MM-DD HH:mm:ss'),
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (_: any, record: XunlianCode) => (
+        <Space size="middle">
+          <Button type="link" onClick={() => handleEdit(record.id)}>编辑</Button>
+          <Popconfirm
+            title="确定要删除这条训练代码吗?"
+            onConfirm={() => handleDelete(record.id)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <Button type="link" danger>删除</Button>
+          </Popconfirm>
+        </Space>
+      ),
+    },
+  ];
+  
+  return (
+    <div>
+      <Card title="训练代码管理" className="mb-4">
+        <Form
+          form={searchForm}
+          layout="inline"
+          onFinish={handleSearch}
+          style={{ marginBottom: '16px' }}
+        >
+          <Form.Item name="code" label="股票代码">
+            <Input placeholder="要搜索的股票代码" />
+          </Form.Item>
+          
+          <Form.Item>
+            <Space>
+              <Button type="primary" htmlType="submit">
+                搜索
+              </Button>
+              <Button htmlType="reset" onClick={() => {
+                searchForm.resetFields();
+                setSearchParams({
+                  code: '',
+                  page: 1,
+                  limit: 10,
+                });
+              }}>
+                重置
+              </Button>
+              <Button type="primary" onClick={handleAdd}>
+                添加代码
+              </Button>
+            </Space>
+          </Form.Item>
+        </Form>
+        
+        <Table
+          columns={columns}
+          dataSource={codes}
+          rowKey="id"
+          loading={{
+            spinning: isListLoading,
+            tip: '正在加载数据...',
+          }}
+          pagination={{
+            current: pagination.current,
+            pageSize: pagination.pageSize,
+            total: pagination.total,
+            onChange: handlePageChange,
+            showSizeChanger: true,
+            showTotal: (total) => `共 ${total} 条`,
+          }}
+        />
+      </Card>
+      
+      <Modal
+        title={formMode === 'create' ? '添加训练代码' : '编辑训练代码'}
+        open={modalVisible}
+        onOk={() => {
+          form.validateFields()
+            .then(values => {
+              handleSubmit(values);
+            })
+            .catch(info => {
+              console.log('表单验证失败:', info);
+            });
+        }}
+        onCancel={() => setModalVisible(false)}
+        width={800}
+        okText="确定"
+        cancelText="取消"
+        destroyOnClose
+      >
+        <Form
+          form={form}
+          layout="vertical"
+        >
+          <Row gutter={16}>
+            <Col span={12}>
+              <Form.Item
+                name="code"
+                label="股票代码"
+                rules={[{ required: true, message: '请输入股票代码' }]}
+              >
+                <Input placeholder="请输入股票代码" />
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item
+                name="training_date"
+                label="训练日期"
+                rules={[{ required: true, message: '请选择训练日期' }]}
+              >
+                <Input placeholder="请输入训练日期,格式:YYYY-MM-DD" />
+              </Form.Item>
+            </Col>
+          </Row>
+          
+          <Form.Item
+            name="content"
+            label="代码内容"
+            rules={[{ required: true, message: '请输入代码内容' }]}
+          >
+            <Input.TextArea rows={15} placeholder="请输入代码内容" />
+          </Form.Item>
+          
+        </Form>
+      </Modal>
+    </div>
+  );
+};

+ 38 - 0
client/share/types.ts

@@ -690,3 +690,41 @@ export interface SubmissionRecordListResponse {
     totalPages: number;
   };
 }
+
+// 训练代码实体
+export interface XunlianCode {
+  /** 主键ID */
+  id: number;
+  
+  /** 股票代码 */
+  code: string;
+  
+  /** 训练日期 */
+  training_date: string;
+  
+  /** 代码内容 */
+  content: string;
+  
+  /** 提交用户ID */
+  user_id: number;
+  
+  /** 用户昵称 */
+  nickname: string;
+  
+  /** 创建时间 */
+  created_at: string;
+  
+  /** 更新时间 */
+  updated_at: string;
+}
+
+// 训练代码列表响应
+export interface XunlianCodeListResponse {
+  data: XunlianCode[];
+  pagination: {
+    current: number;
+    pageSize: number;
+    total: number;
+    totalPages: number;
+  };
+}