Sfoglia il codice sorgente

迁移了admin date_notes

yourname 6 mesi fa
parent
commit
cd5342a5ed
3 ha cambiato i file con 441 aggiunte e 0 eliminazioni
  1. 84 0
      client/admin/api/date_notes.ts
  2. 325 0
      client/admin/pages_date_notes.tsx
  3. 32 0
      client/share/types.ts

+ 84 - 0
client/admin/api/date_notes.ts

@@ -0,0 +1,84 @@
+import axios from 'axios';
+import type {
+  DateNote,
+  DateNoteListResponse
+} from '../../share/types.ts';
+
+interface DateNoteResponse {
+  data: DateNote;
+  message?: string;
+}
+
+interface DateNoteCreateResponse {
+  message: string;
+  data: DateNote;
+}
+
+interface DateNoteUpdateResponse {
+  message: string;
+  data: DateNote;
+}
+
+interface DateNoteDeleteResponse {
+  message: string;
+  id: number;
+}
+
+// 日期备注API
+export const DateNotesAPI = {
+  // 获取日期备注列表
+  getDateNotes: async (params?: {
+    page?: number;
+    pageSize?: number;
+    code?: string;
+    start_date?: string;
+    end_date?: string;
+  }): Promise<DateNoteListResponse> => {
+    try {
+      const response = await axios.get('/date-notes', { params });
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 获取单个日期备注
+  getDateNote: async (id: number): Promise<DateNoteResponse> => {
+    try {
+      const response = await axios.get(`/date-notes/${id}`);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 创建日期备注
+  createDateNote: async (data: Partial<DateNote>): Promise<DateNoteCreateResponse> => {
+    try {
+      const response = await axios.post('/date-notes', data);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 更新日期备注
+  updateDateNote: async (id: number, data: Partial<DateNote>): Promise<DateNoteUpdateResponse> => {
+    try {
+      const response = await axios.put(`/date-notes/${id}`, data);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 删除日期备注
+  deleteDateNote: async (id: number): Promise<DateNoteDeleteResponse> => {
+    try {
+      const response = await axios.delete(`/date-notes/${id}`);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  }
+};

+ 325 - 0
client/admin/pages_date_notes.tsx

@@ -0,0 +1,325 @@
+import React, { useState } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
+import {
+  Button, Table, Space,
+  Form, Input, DatePicker, 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 { 
+  DateNote
+} from '../share/types.ts';
+
+import { DateNotesAPI } from './api/date_notes.ts';
+
+// 配置 dayjs 插件
+dayjs.extend(weekday);
+dayjs.extend(localeData);
+
+// 设置 dayjs 语言
+dayjs.locale('zh-cn');
+
+export const DateNotesPage = () => {
+  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: '',
+    start_date: '',
+    end_date: '',
+    page: 1,
+    limit: 10,
+  });
+  
+  // 使用React Query获取日期备注列表
+  const { data: notesData, isLoading: isListLoading, refetch } = useQuery({
+    queryKey: ['dateNotes', searchParams],
+    queryFn: () => DateNotesAPI.getDateNotes({
+      page: searchParams.page,
+      pageSize: searchParams.limit,
+      code: searchParams.code,
+      start_date: searchParams.start_date,
+      end_date: searchParams.end_date
+    }),
+    placeholderData: {
+      data: [],
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        total: 0,
+        totalPages: 1
+      }
+    }
+  });
+  
+  const notes = React.useMemo(() => notesData?.data || [], [notesData]);
+  const pagination = React.useMemo(() => ({
+    current: notesData?.pagination?.current || 1,
+    pageSize: notesData?.pagination?.pageSize || 10,
+    total: notesData?.pagination?.total || 0,
+    totalPages: notesData?.pagination?.totalPages || 1
+  }), [notesData]);
+  
+  // 获取单个日期备注
+  const fetchNote = async (id: number) => {
+    try {
+      const response = await DateNotesAPI.getDateNote(id);
+      return response.data;
+    } catch (error) {
+      message.error('获取日期备注详情失败');
+      return null;
+    }
+  };
+  
+  // 处理表单提交
+  const handleSubmit = async (values: Partial<DateNote>) => {
+    try {
+      const response = formMode === 'create'
+        ? await DateNotesAPI.createDateNote(values)
+        : await DateNotesAPI.updateDateNote(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 note = await fetchNote(id);
+    if (note) {
+      setFormMode('edit');
+      setEditingId(id);
+      form.setFieldsValue({
+        ...note,
+        note_date: note.note_date ? dayjs(note.note_date) : null
+      });
+      setModalVisible(true);
+    }
+  };
+  
+  // 处理删除
+  const handleDelete = async (id: number) => {
+    try {
+      await DateNotesAPI.deleteDateNote(id);
+      message.success('删除日期备注成功');
+      refetch();
+    } catch (error) {
+      message.error((error as Error).message);
+    }
+  };
+  
+  // 处理搜索
+  const handleSearch = async (values: any) => {
+    try {
+      queryClient.removeQueries({ queryKey: ['dateNotes'] });
+      setSearchParams({
+        code: values.code || '',
+        start_date: values.dateRange?.[0]?.format('YYYY-MM-DD') || '',
+        end_date: values.dateRange?.[1]?.format('YYYY-MM-DD') || '',
+        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: 'note_date',
+      key: 'note_date',
+      render: (date: string) => dayjs(date).format('YYYY-MM-DD'),
+    },
+    {
+      title: '备注内容',
+      dataIndex: 'note',
+      key: 'note',
+      ellipsis: true,
+    },
+    {
+      title: '更新时间',
+      dataIndex: 'updated_at',
+      key: 'updated_at',
+      render: (date: string) => dayjs(date).format('YYYY-MM-DD HH:mm:ss'),
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (_: any, record: DateNote) => (
+        <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 name="dateRange" label="日期范围">
+            <DatePicker.RangePicker />
+          </Form.Item>
+          
+          <Form.Item>
+            <Space>
+              <Button type="primary" htmlType="submit">
+                搜索
+              </Button>
+              <Button htmlType="reset" onClick={() => {
+                searchForm.resetFields();
+                setSearchParams({
+                  code: '',
+                  start_date: '',
+                  end_date: '',
+                  page: 1,
+                  limit: 10,
+                });
+              }}>
+                重置
+              </Button>
+              <Button type="primary" onClick={handleAdd}>
+                添加备注
+              </Button>
+            </Space>
+          </Form.Item>
+        </Form>
+        
+        <Table
+          columns={columns}
+          dataSource={notes}
+          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,
+                note_date: values.note_date?.format('YYYY-MM-DD')
+              });
+            })
+            .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="note_date"
+                label="日期"
+                rules={[{ required: true, message: '请选择日期' }]}
+              >
+                <DatePicker style={{ width: '100%' }} />
+              </Form.Item>
+            </Col>
+          </Row>
+          
+          <Form.Item
+            name="note"
+            label="备注内容"
+            rules={[{ required: true, message: '请输入备注内容' }]}
+          >
+            <Input.TextArea rows={8} placeholder="请输入备注内容" />
+          </Form.Item>
+        </Form>
+      </Modal>
+    </div>
+  );
+};

+ 32 - 0
client/share/types.ts

@@ -600,3 +600,35 @@ export interface ClassroomDataListResponse {
     totalPages: number;
   };
 }
+
+// 日期备注接口
+export interface DateNote {
+  /** 主键ID */
+  id: number;
+  
+  /** 股票代码 */
+  code: string;
+  
+  /** 备注日期 */
+  note_date: string;
+  
+  /** 备注内容 */
+  note: string;
+  
+  /** 创建时间 */
+  created_at: string;
+  
+  /** 更新时间 */
+  updated_at: string;
+}
+
+// 日期备注列表响应
+export interface DateNoteListResponse {
+  data: DateNote[];
+  pagination: {
+    current: number;
+    pageSize: number;
+    total: number;
+    totalPages: number;
+  };
+}