基于后端due对象的进度条进度计算实现方案咨询
进度条组件的进度百分比计算逻辑完善
需求背景
已完成进度条组件开发,需基于后端返回的due对象实现进度百分比计算逻辑,用于单元摘要生成的实时进度跟踪。
后端返回的due对象结构
due: { coref: 1, extract: 4, keyconcepts: 1, question-text-cmas: 1, question-text-fibq: 1, question-text-mcq: 1, rank: 1, summary-1: 1, text: 1, topic: 1 }
字段规则说明
- 属性处理完成后会被后端从
due对象中移除 - 属性值为
1表示待处理,大于1表示处理中
现有初始实现代码
const propertiesRef = useRef({ extract: { curr: 0, max: 15 }, keyconcepts: { curr: 0, max: 20 }, question: { cmas: { curr: 0, max: 10 }, fibq: { crr: 0, max: 10 }, // 拼写错误:crr -> curr mcq: { curr: 0, max: 10 }, }, rank: { curr: 0, max: 5 }, summary: { curr: 0, max: 15 }, text: { curr: 0, max: 10 }, topic: { curr: 0, max: 5 }, allOver: 0, }); // 存储进度数据 const getProcess = async () => { let conditionLoop = true; do { setTimeout(async () => { await axios .get(`Route`, { withCredentials: true }) // 获取due对象 .then((res) => { conditionLoop = res.data.due; if (res?.data?.due) { for (let key in propertiesRef.current) { if (res.data.due.hasOwn(key)) { console.log(key, res.data.due[key]); } if (res.data.due.hasOwn("question-text-cmas")) { console.log(res.data.due); } if (res.data.due.hasOwn("question-text-fibq")) { console.log(res.data.due); } if (res.data.due.hasOwn("question-text-mcq")) { console.log(res.data.due); } } } else { propertiesRef.current.allOver = 1; conditionloop=false; // 拼写错误:conditionloop -> conditionLoop } console.log(propertiesRef.current); }); }, 2000); } while (conditionLoop); };
完善后的实现逻辑与代码
核心改进方向
- 修复原代码拼写错误,避免逻辑异常
- 建立
due字段与本地进度项的映射关系,统一处理普通字段和嵌套字段 - 实现基于
due状态的进度更新逻辑(待处理/处理中/已完成) - 计算总进度百分比,适配进度条组件
- 优化轮询逻辑,避免定时器堆积,增加错误处理
完整代码
const propertiesRef = useRef({ extract: { curr: 0, max: 15 }, keyconcepts: { curr: 0, max: 20 }, question: { cmas: { curr: 0, max: 10 }, fibq: { curr: 0, max: 10 }, // 修正拼写错误 mcq: { curr: 0, max: 10 }, }, rank: { curr: 0, max: 5 }, summary: { curr: 0, max: 15 }, text: { curr: 0, max: 10 }, topic: { curr: 0, max: 5 }, allOver: 0, }); // 预计算所有进度项的总max值 const totalMax = useMemo(() => { const { question, ...rest } = propertiesRef.current; const questionMax = Object.values(question).reduce((sum, item) => sum + item.max, 0); const restMax = Object.values(rest) .filter(item => typeof item === 'object') .reduce((sum, item) => sum + item.max, 0); return questionMax + restMax; }, []); // 建立due字段与本地进度项的映射 const fieldMapping = { extract: 'extract', keyconcepts: 'keyconcepts', 'question-text-cmas': ['question', 'cmas'], 'question-text-fibq': ['question', 'fibq'], 'question-text-mcq': ['question', 'mcq'], rank: 'rank', 'summary-1': 'summary', text: 'text', topic: 'topic', coref: null, // coref无对应进度项,忽略 }; const getProcess = async () => { let conditionLoop = true; let timerId; // 进度更新逻辑 const updateProgress = (due) => { Object.keys(fieldMapping).forEach(field => { const target = fieldMapping[field]; if (!target) return; // 获取对应进度项 let progressItem; if (Array.isArray(target)) { progressItem = propertiesRef.current[target[0]][target[1]]; } else { progressItem = propertiesRef.current[target]; } if (due.hasOwnProperty(field)) { const status = due[field]; if (status === 1) { // 待处理:进度重置为0 progressItem.curr = 0; } else { // 处理中:按步长递增进度,直到达到max if (progressItem.curr < progressItem.max) { progressItem.curr += 1; // 可根据实际情况调整步长 } } } else { // 已完成:进度直接设为max progressItem.curr = progressItem.max; } }); // 计算总进度百分比 const { question, ...rest } = propertiesRef.current; const questionCurr = Object.values(question).reduce((sum, item) => sum + item.curr, 0); const restCurr = Object.values(rest) .filter(item => typeof item === 'object') .reduce((sum, item) => sum + item.curr, 0); const totalCurr = questionCurr + restCurr; const progressPercent = Math.round((totalCurr / totalMax) * 100); // 检查是否全部完成 if (progressPercent === 100) { propertiesRef.current.allOver = 1; conditionLoop = false; clearTimeout(timerId); } }; // 轮询逻辑 while (conditionLoop) { try { const res = await axios.get(`Route`, { withCredentials: true }); const due = res.data.due; if (due) { updateProgress(due); } else { // 后端无返回due,直接标记全部完成 propertiesRef.current.allOver = 1; conditionLoop = false; } // 等待2秒后发起下一次请求 await new Promise(resolve => timerId = setTimeout(resolve, 2000)); } catch (error) { console.error('获取进度失败:', error); // 出错后停止轮询 conditionLoop = false; clearTimeout(timerId); } } };
关键优化点
- 修正原代码中的拼写错误,避免逻辑异常
- 使用
while循环结合Promise实现正确轮询,避免定时器堆积 - 通过字段映射统一处理嵌套字段(如
question-text-cmas对应question.cmas) - 基于
due字段的状态(1/大于1/不存在)分别更新进度 - 自动计算总进度百分比,直接适配进度条组件
- 添加错误处理,避免请求失败后无限循环
内容的提问来源于stack exchange,提问作者Yovel Hadad
相关产品推荐
相关产品推荐

