fix(mobile): 修复全览按钮点击后页面卡死的响应式循环bug
treeNodes 计算属性中直接修改了 product.task_tree 原始响应式对象
(设置 x/y/_isMain/_blabel),触发 Vue 2 深度响应式的无限渲染循环。
修复方案:对所有 task 对象进行浅拷贝 { ...t },在副本上设置坐标
和标签属性,切断响应式依赖循环。
This commit is contained in:
@ -83,12 +83,22 @@ export default {
|
|||||||
const CARD_W = 160, CARD_H = 80, GAP_X = 24, GAP_Y = 24;
|
const CARD_W = 160, CARD_H = 80, GAP_X = 24, GAP_Y = 24;
|
||||||
const CENTER_X = 0; // 中央主干 X
|
const CENTER_X = 0; // 中央主干 X
|
||||||
|
|
||||||
// 拍平 + 深拷贝
|
// 🔧 浅拷贝所有 task 对象,避免修改响应式原始数据导致无限渲染循环
|
||||||
const flatMap = {};
|
const flatMap = {};
|
||||||
const allTasks = [];
|
const allTasks = [];
|
||||||
const walk = (tasks) => { if (!tasks) return; tasks.forEach(t => { allTasks.push(t); flatMap[t.id] = t; walk(t.child_tasks); }); };
|
const walk = (tasks) => { if (!tasks) return; tasks.forEach(t => { const copy = { ...t }; allTasks.push(copy); flatMap[copy.id] = copy; walk(t.child_tasks); }); };
|
||||||
walk(this.product.task_tree);
|
walk(this.product.task_tree);
|
||||||
|
|
||||||
|
// 重建父子引用(child_tasks 指向副本)
|
||||||
|
allTasks.forEach(t => {
|
||||||
|
if (t.child_tasks && t.child_tasks.length) {
|
||||||
|
t.child_tasks = t.child_tasks.map(c => flatMap[c.id]).filter(Boolean);
|
||||||
|
}
|
||||||
|
if (t.parent_task_id) {
|
||||||
|
t.parent = flatMap[t.parent_task_id] || null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 分支编号
|
// 分支编号
|
||||||
const branchLabelMap = {};
|
const branchLabelMap = {};
|
||||||
const traverse = (tasks, prefix) => {
|
const traverse = (tasks, prefix) => {
|
||||||
@ -101,7 +111,9 @@ export default {
|
|||||||
else { si++; const n = prefix ? prefix + '.' + si : '' + si; branchLabelMap[t.id] = '分支 ' + n; traverse(t.child_tasks, n); }
|
else { si++; const n = prefix ? prefix + '.' + si : '' + si; branchLabelMap[t.id] = '分支 ' + n; traverse(t.child_tasks, n); }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
this.product.task_tree.forEach(t => { branchLabelMap[t.id] = '主分支'; traverse(t.child_tasks, ''); });
|
// 遍历顶层时使用副本引用
|
||||||
|
const rootCopies = this.product.task_tree.map(t => flatMap[t.id]).filter(Boolean);
|
||||||
|
rootCopies.forEach(t => { branchLabelMap[t.id] = '主分支'; traverse(t.child_tasks, ''); });
|
||||||
|
|
||||||
// 主管分类
|
// 主管分类
|
||||||
const mains = []; const subs = []; const subByParent = {};
|
const mains = []; const subs = []; const subByParent = {};
|
||||||
@ -117,25 +129,27 @@ export default {
|
|||||||
const nodes = [];
|
const nodes = [];
|
||||||
const childSeen = new Set();
|
const childSeen = new Set();
|
||||||
mains.forEach((m, mi) => {
|
mains.forEach((m, mi) => {
|
||||||
m.x = CENTER_X; m.y = mi * (CARD_H + GAP_Y) * 2;
|
const node = { ...m, x: CENTER_X, y: mi * (CARD_H + GAP_Y) * 2, _isMain: true, _blabel: '主分支' };
|
||||||
m._isMain = true; m._blabel = '主分支';
|
nodes.push(node);
|
||||||
nodes.push(m);
|
// 更新 flatMap 引用以便 lines/arrows 计算使用正确的坐标
|
||||||
|
flatMap[node.id] = node;
|
||||||
|
|
||||||
const children = subByParent[m.id] || [];
|
const children = subByParent[m.id] || [];
|
||||||
children.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
children.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
||||||
children.forEach((c, ci) => {
|
children.forEach((c, ci) => {
|
||||||
const isLeft = ci % 2 === 0;
|
const isLeft = ci % 2 === 0;
|
||||||
const dist = Math.floor(ci / 2) + 1;
|
const dist = Math.floor(ci / 2) + 1;
|
||||||
c.y = m.y;
|
const childNode = { ...c, y: node.y,
|
||||||
c.x = isLeft ? CENTER_X - dist * (CARD_W + GAP_X) : CENTER_X + dist * (CARD_W + GAP_X);
|
x: isLeft ? CENTER_X - dist * (CARD_W + GAP_X) : CENTER_X + dist * (CARD_W + GAP_X),
|
||||||
c._isMain = false; c._blabel = branchLabelMap[c.id] || '分支';
|
_isMain: false, _blabel: branchLabelMap[c.id] || '分支' };
|
||||||
nodes.push(c);
|
nodes.push(childNode);
|
||||||
|
flatMap[childNode.id] = childNode;
|
||||||
childSeen.add(c.id);
|
childSeen.add(c.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 遗留分支(父任务不在 mains 中的)
|
// 遗留分支(父任务不在 mains 中的)
|
||||||
subs.forEach(s => { if (!childSeen.has(s.id)) { s.x = 200; s.y = nodes.length ? nodes[nodes.length - 1].y + CARD_H + GAP_Y : 0; s._isMain = false; s._blabel = '分支'; nodes.push(s); } });
|
subs.forEach(s => { if (!childSeen.has(s.id)) { const legacyNode = { ...s, x: 200, y: nodes.length ? nodes[nodes.length - 1].y + CARD_H + GAP_Y : 0, _isMain: false, _blabel: '分支' }; nodes.push(legacyNode); flatMap[legacyNode.id] = legacyNode; } });
|
||||||
|
|
||||||
// 加上全局偏移
|
// 加上全局偏移
|
||||||
const padding = 40;
|
const padding = 40;
|
||||||
|
|||||||
Reference in New Issue
Block a user