diff --git a/frontend/src/pages/MkhBomData/index.jsx b/frontend/src/pages/MkhBomData/index.jsx index e74f650..46ebc7a 100644 --- a/frontend/src/pages/MkhBomData/index.jsx +++ b/frontend/src/pages/MkhBomData/index.jsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { - Alert, Button, Card, Collapse, Dropdown, Input, Modal, Segmented, Select, Space, Spin, Switch, + Alert, Button, Card, Collapse, Dropdown, Input, Modal, Progress, Segmented, Select, Space, Spin, Switch, Table, Tooltip, Typography, message, } from 'antd'; import { @@ -37,6 +37,19 @@ const EMPTY_FILTERS = { keyword: '', scope: 'warning' }; // 会合并为每个缓存周期一次业务库读取,不会每 2 分钟重复扫远程大表。 const AUTO_REFRESH_MS = 2 * 60 * 1000; +const getRefreshProgress = (elapsedSeconds) => { + if (elapsedSeconds < 3) { + return { percent: 12 + elapsedSeconds * 8, stage: '正在读取实时库存、箱表和加工单…' }; + } + if (elapsedSeconds < 15) { + return { percent: 36 + (elapsedSeconds - 3) * 3, stage: '正在核对待发货订单和近15天销量…' }; + } + return { + percent: Math.min(95, 72 + Math.floor((elapsedSeconds - 15) / 2)), + stage: '正在合并最新数据与 BOM 配件结果…', + }; +}; + // 预警等级 → 标签配色 const WARNING_TONE = { 缺货: 'danger', @@ -266,6 +279,7 @@ const MkhBomData = () => { // 导出(当前筛选条件下的全部数据) const [exporting, setExporting] = useState(false); const [syncing, setSyncing] = useState(false); + const [refreshProgress, setRefreshProgress] = useState({ open: false, elapsedSeconds: 0, title: '' }); // 补货预警推送设置 const [alertOpen, setAlertOpen] = useState(false); @@ -280,10 +294,35 @@ const MkhBomData = () => { const tableWrapRef = useRef(null); const listRequestGateRef = useRef(createRequestGate()); const detailGenerationRef = useRef(0); + const refreshProgressGenerationRef = useRef(0); const lastListRefreshAtRef = useRef(0); const autoRefreshPendingRef = useRef(false); const [bodyHeight, setBodyHeight] = useState(420); + const beginRefreshProgress = useCallback((title) => { + refreshProgressGenerationRef.current += 1; + const generation = refreshProgressGenerationRef.current; + setRefreshProgress({ open: true, elapsedSeconds: 0, title, generation }); + return generation; + }, []); + + const endRefreshProgress = useCallback((generation) => { + if (generation !== refreshProgressGenerationRef.current) return; + setRefreshProgress((current) => ({ ...current, open: false })); + }, []); + + useEffect(() => { + if (!refreshProgress.open) return undefined; + const startedAt = Date.now(); + const timer = window.setInterval(() => { + setRefreshProgress((current) => ({ + ...current, + elapsedSeconds: Math.max(0, Math.floor((Date.now() - startedAt) / 1000)), + })); + }, 1000); + return () => window.clearInterval(timer); + }, [refreshProgress.open]); + useEffect(() => { const el = tableWrapRef.current; if (!el) return undefined; @@ -320,12 +359,16 @@ const MkhBomData = () => { const load = useCallback(async ({ background = false } = {}) => { const requestGeneration = listRequestGateRef.current.begin(); + let showProgress = false; + let progressGeneration = null; if (!background) setLoading(true); if (!background) setError(''); try { const now = Date.now(); const forceFresh = !lastListRefreshAtRef.current || now - lastListRefreshAtRef.current >= AUTO_REFRESH_MS; + showProgress = forceFresh && !background; + if (showProgress) progressGeneration = beginRefreshProgress('正在更新最新业务数据'); // 首次打开/F5 和定时轮询才重读云库;筛选、分页只复用刚刷新的内存索引。 // 后端仍会合并多用户的并发刷新,不会对业务云库造成重复全量查询。 const result = await getMkhBomList({ @@ -347,8 +390,9 @@ const MkhBomData = () => { setError(requestError?.response?.data?.error || requestError?.message || '加载 MKH-BOM 资料失败'); } finally { if (!background && listRequestGateRef.current.isCurrent(requestGeneration)) setLoading(false); + if (showProgress) endRefreshProgress(progressGeneration); } - }, [query]); + }, [beginRefreshProgress, endRefreshProgress, query]); useEffect(() => { load(); }, [load]); @@ -850,6 +894,7 @@ const MkhBomData = () => { const handleSync = async () => { setSyncing(true); setLoading(true); + const progressGeneration = beginRefreshProgress('正在强制同步全部最新数据'); // 立即废弃同步前发出的列表/明细请求;它们即使稍后返回,也不能覆盖新快照。 listRequestGateRef.current.invalidate(); detailGenerationRef.current += 1; @@ -874,11 +919,31 @@ const MkhBomData = () => { } finally { if (listRequestGateRef.current.isCurrent(syncGeneration)) setLoading(false); setSyncing(false); + endRefreshProgress(progressGeneration); } }; + const progressView = getRefreshProgress(refreshProgress.elapsedSeconds); + return ( + + + + {progressView.stage} + 已用时 {refreshProgress.elapsedSeconds} 秒,完成后页面会自动显示新数据。 + 进度为查询阶段估算,实际完成以服务端返回为准。 + +
MKH-BOM 资料