import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Alert, Button, Modal, Space, Spin, Table, Tooltip, Typography } from 'antd'; import { InfoCircleOutlined, ReloadOutlined } from '@ant-design/icons'; import { SycmPage } from '../../components/Common'; import { getDailyInventory } from '../../api/dailyInventory'; import { DAILY_INVENTORY_COLUMNS, COLUMN_SOURCE, FORMULA_DESCRIPTION, } from '../../constants/dailyInventory.js'; import './style.css'; const { Title, Text } = Typography; const TipTitle = ({ tip, children }) => ( {children} ); const int = (value) => { if (value === null || value === undefined || value === '') return '—'; return Number(value).toLocaleString('zh-CN', { maximumFractionDigits: 0 }); }; const decimal = (value, digits = 1) => { if (value === null || value === undefined || value === '') return '—'; return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: digits, maximumFractionDigits: digits, }); }; /** 负数标红、正数标黑:出入库列以负数表示流出,便于一眼分辨方向 */ const renderSigned = (value) => { if (value === null || value === undefined || value === '') return '—'; const n = Number(value); if (!Number.isFinite(n) || n === 0) return 0; return {int(n)}; }; const DailyInventory = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [data, setData] = useState(null); const [helpOpen, setHelpOpen] = useState(false); const load = useCallback(async () => { setLoading(true); setError(''); try { const res = await getDailyInventory(); setData(res?.data ?? null); } catch (err) { setError(err?.response?.data?.error || err?.message || '每日进销存数据加载失败'); setData(null); } finally { setLoading(false); } }, []); useEffect(() => { load(); }, [load]); const items = useMemo(() => data?.items ?? [], [data]); const columns = useMemo(() => DAILY_INVENTORY_COLUMNS.map((col) => { const isRuntime = col.source === COLUMN_SOURCE.runtime; return { title: {col.label}, dataIndex: col.key, key: col.key, align: col.key === 'warehouse' ? 'left' : 'right', width: col.key === 'warehouse' ? 240 : 130, fixed: col.key === 'warehouse' ? 'left' : undefined, className: isRuntime ? 'daily-inventory-col--runtime' : undefined, render: (value, record) => { if (record?.isTotal) { // 合计行:周转天数不累加,显示「—」 if (col.key === 'turnoverDays') return '—'; return int(value); } if (col.key === 'warehouse') return {value}; if (col.key === 'closingQty') return int(value); if (col.key === 'turnoverDays') return decimal(value, 1); return renderSigned(value); }, }; }), []); const tableRows = useMemo(() => { if (!data?.totals) return items; return [...items, { ...data.totals, key: '__total__' }]; }, [items, data]); const noGradeCount = useMemo(() => items.filter((it) => !it.hasGrade).length, [items]); return (
每日进销存
基准日期:{data?.baselineDate || '—'} 库存快照日:{data?.latestBizDate || '—'} 分级基准时刻:{data?.asOf || '—'} {data?.items?.[0]?.closingTakenAt ? ( 库存取数时间:{new Date(data.items[0].closingTakenAt).toLocaleString('zh-CN', { hour12: false })} ) : null}
期初数量与 12 个流水列取自 {data?.baselineDate || '2026-09-22'} 聚水潭进销存导出值(业务库缺少调拨/盘点/加工/其它出入库单据表,无法还原); 期末数量与右侧 5 列为运行时实时取数。悬浮表头可看每列口径。 {noGradeCount > 0 ? ` 其中 ${noGradeCount} 个仓在库存快照中无数据,周转天数与四级库存量显示「—」。` : ''} } style={{ marginBottom: 12 }} /> {error ? : null} record.key || record.warehouse} size="small" bordered pagination={false} scroll={{ x: 'max-content', y: 'calc(100vh - 300px)' }} rowClassName={(record) => (record?.isTotal ? 'daily-inventory-row--total' : '')} /> 共 {items.length} 个仓库;蓝色底纹列为运行时取数,其余为基准值。 setHelpOpen(false)} footer={null} width={720} >
{FORMULA_DESCRIPTION.map((line, i) => (

{line}

))}
); }; export default DailyInventory;