|
|
@@ -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>
|
|
|
+ );
|
|
|
+};
|