import { useState, useEffect, useRef } from 'react'; import { Table, Button, Input, Select, Space, Card, Form, Row, Col, App as AntdApp, Tag, DatePicker, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { SearchOutlined, ReloadOutlined } from '@ant-design/icons'; import { stockDailyPriceService } from '@/services/stock-daily-price'; import type { StockDailyPrice, QueryStockDailyPriceRequest } from '@/types/stock-daily-price'; import { MARKET_OPTIONS, getMarketText } from '@/types/stock-daily-price'; import dayjs from 'dayjs'; import './StockDailyPricePage.css'; const { Option } = Select; const { RangePicker } = DatePicker; const StockDailyPricePage = () => { const { message: messageApi } = AntdApp.useApp(); const [prices, setPrices] = useState([]); const [loading, setLoading] = useState(false); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); const [form] = Form.useForm(); const formRef = useRef({}); // 初始化:默认查询最近7天 useEffect(() => { const endDate = dayjs(); const startDate = endDate.subtract(6, 'day'); // 最近7天(包含今天) form.setFieldsValue({ dateRange: [startDate, endDate], }); formRef.current = { startDate: startDate.format('YYYY-MM-DD'), endDate: endDate.format('YYYY-MM-DD'), }; loadData(formRef.current, true); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 加载数据 const loadData = async (params?: QueryStockDailyPriceRequest, resetPage = false) => { setLoading(true); try { const currentPage = resetPage ? 1 : pagination.current; const pageSize = pagination.pageSize; const queryParams: QueryStockDailyPriceRequest = { page: currentPage, limit: pageSize, sortBy: 'tradeDate', sortOrder: 'DESC', ...formRef.current, ...params, }; const response = await stockDailyPriceService.getStockDailyPriceList(queryParams); setPrices(response.list); setPagination((prev) => ({ ...prev, current: response.pagination.current_page, pageSize: response.pagination.page_size, total: response.pagination.total, })); } catch (error: any) { messageApi.error(error.message || '加载股票价格列表失败'); } finally { setLoading(false); } }; // 当分页改变时,重新加载数据 useEffect(() => { if (pagination.current > 0 && pagination.pageSize > 0) { loadData(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [pagination.current, pagination.pageSize]); // 查询 const handleSearch = () => { const values = form.getFieldsValue(); const queryParams: QueryStockDailyPriceRequest = { stockCode: values.stockCode || undefined, stockName: values.stockName || undefined, market: values.market || undefined, }; // 处理日期范围 if (values.dateRange && values.dateRange.length === 2) { queryParams.startDate = values.dateRange[0].format('YYYY-MM-DD'); queryParams.endDate = values.dateRange[1].format('YYYY-MM-DD'); } formRef.current = queryParams; loadData(queryParams, true); }; // 重置 const handleReset = () => { form.resetFields(); // 重置为默认的最近7天 const endDate = dayjs(); const startDate = endDate.subtract(6, 'day'); form.setFieldsValue({ dateRange: [startDate, endDate], }); formRef.current = { startDate: startDate.format('YYYY-MM-DD'), endDate: endDate.format('YYYY-MM-DD'), }; loadData(formRef.current, true); }; // 格式化价格 const formatPrice = (price?: number) => { if (price === null || price === undefined) return '-'; return price.toFixed(2); }; // 格式化金额 const formatAmount = (amount?: number) => { if (amount === null || amount === undefined) return '-'; if (amount >= 100000000) { return `${(amount / 100000000).toFixed(2)}亿`; } if (amount >= 10000) { return `${(amount / 10000).toFixed(2)}万`; } return amount.toFixed(2); }; // 格式化成交量 const formatVolume = (volume?: number) => { if (volume === null || volume === undefined) return '-'; if (volume >= 10000) { return `${(volume / 10000).toFixed(2)}万手`; } return `${volume}手`; }; // 表格列定义 const columns: ColumnsType = [ { title: '股票代码', dataIndex: 'stockCode', key: 'stockCode', width: 120, fixed: 'left', }, { title: '股票名称', dataIndex: 'stockName', key: 'stockName', width: 150, fixed: 'left', }, { title: '市场', dataIndex: 'market', key: 'market', width: 100, render: (market: string) => {getMarketText(market)}, }, { title: '交易日期', dataIndex: 'tradeDate', key: 'tradeDate', width: 120, render: (date: Date) => dayjs(date).format('YYYY-MM-DD'), }, { title: '开盘价', dataIndex: 'openPrice', key: 'openPrice', width: 100, align: 'right', render: formatPrice, }, { title: '收盘价', dataIndex: 'closePrice', key: 'closePrice', width: 100, align: 'right', render: formatPrice, }, { title: '最高价', dataIndex: 'highPrice', key: 'highPrice', width: 100, align: 'right', render: formatPrice, }, { title: '最低价', dataIndex: 'lowPrice', key: 'lowPrice', width: 100, align: 'right', render: formatPrice, }, { title: '涨跌额', dataIndex: 'changeAmount', key: 'changeAmount', width: 100, align: 'right', render: (amount?: number) => { if (amount === null || amount === undefined) return '-'; const color = amount >= 0 ? '#ff4d4f' : '#52c41a'; return {formatPrice(amount)}; }, }, { title: '涨跌幅', dataIndex: 'changePercent', key: 'changePercent', width: 100, align: 'right', render: (percent?: number) => { if (percent === null || percent === undefined) return '-'; const color = percent >= 0 ? '#ff4d4f' : '#52c41a'; return ( {percent >= 0 ? '+' : ''} {percent.toFixed(2)}% ); }, }, { title: '成交量', dataIndex: 'volume', key: 'volume', width: 120, align: 'right', render: formatVolume, }, { title: '成交额', dataIndex: 'amount', key: 'amount', width: 150, align: 'right', render: formatAmount, }, { title: '换手率', dataIndex: 'turnoverRate', key: 'turnoverRate', width: 100, align: 'right', render: (rate?: number) => { if (rate === null || rate === undefined) return '-'; return `${rate.toFixed(2)}%`; }, }, { title: '市盈率', dataIndex: 'peRatio', key: 'peRatio', width: 100, align: 'right', render: formatPrice, }, { title: '市净率', dataIndex: 'pbRatio', key: 'pbRatio', width: 100, align: 'right', render: formatPrice, }, ]; return (
{/* 查询表单 */}
{/* 表格 */} `共 ${total} 条`, onChange: (page, pageSize) => { setPagination((prev) => ({ ...prev, current: page, pageSize: pageSize || 10, })); }, }} scroll={{ x: 1600 }} /> ); }; export default StockDailyPricePage;