feat(mobile): 登录修复 + 人名字典 + 留言板FAB + 流转算法重构

- login: catch补齐toast,修复密码错误静默失败
- detail: formatUserName渲染当前位置,dictVersion驱动响应式重绘
- detail: 留言板改为悬浮按钮+底部抽屉,未读徽标逻辑
- TaskSwipeCards: lanes终极递归算法,SPAWN不限层级开辟新Tab,先序push修复Tab顺序
- TreeCanvas: 父子相对坐标延伸算法,替代旧版奇偶距离递增
This commit is contained in:
2026-08-11 10:02:12 +08:00
parent d3def15bb3
commit 6755dfeae3
4 changed files with 102 additions and 114 deletions

View File

@ -134,40 +134,35 @@ export default {
if (!this.product || !this.product.task_tree) return result;
const mainLane = { _key: 'main', label: '主分支', cards: [], _cardIdx: 0 };
const isMainFn = (t) => !t.parent_task_id || t.task_type === 'TRANSFER' || t.task_type === 'RECOVERY';
const sortTasks = (tasks) => {
if (!tasks) return [];
return [...tasks].sort((a, b) => {
const aMain = isMainFn(a) ? -1 : 1;
const bMain = isMainFn(b) ? -1 : 1;
if (aMain !== bMain) return aMain - bMain;
return new Date(a.created_at) - new Date(b.created_at);
});
return [...tasks].sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
};
// 🚀 终极递归收集算法
const followMain = (taskList, lane) => {
if (!taskList || !taskList.length) return;
const sorted = sortTasks(taskList);
for (const t of sorted) {
if (isMainFn(t)) {
lane.cards.push({ ...t, _key: t.id + '_' + lane._key, _isMain: lane._key === 'main' });
if (t.child_tasks && t.child_tasks.length) {
const nextMain = t.child_tasks.filter(c =>
c.task_type === 'TRANSFER' || c.task_type === 'RECOVERY');
followMain(nextMain, lane);
// SPAWN 子节点 → 新分支(含嵌套 spawn)
const spawns = t.child_tasks.filter(c => c.task_type === 'SPAWN');
for (const sc of spawns) {
const blabel = this.branchLabelMap[sc.id] || '分支';
const branchLane = { _key: 'branch_' + sc.id, label: blabel, cards: [], _cardIdx: 0 };
branchLane.cards.push({ ...sc, _key: sc.id + '_' + branchLane._key, _isMain: false });
if (sc.child_tasks && sc.child_tasks.length) {
followMain(sc.child_tasks, branchLane);
}
result.push(branchLane);
}
// 1. 无条件入列:既然进到了这个 lane,就属于这个 lane 的卡片
lane.cards.push({ ...t, _key: t.id + '_' + lane._key, _isMain: lane._key === 'main' });
if (t.child_tasks && t.child_tasks.length) {
// 1. 同一分支线性延续
const nextOnThisLane = t.child_tasks.filter(c =>
c.task_type === 'TRANSFER' || c.task_type === 'RECOVERY'
);
followMain(nextOnThisLane, lane);
// 2. 凡是 SPAWN,必定开辟新分支 (不限层级)
const spawns = t.child_tasks.filter(c => c.task_type === 'SPAWN');
for (const sc of spawns) {
const blabel = (this.branchLabelMap && this.branchLabelMap[sc.id]) || '协助分支';
const branchLane = { _key: 'branch_' + sc.id, label: blabel, cards: [], _cardIdx: 0 };
result.push(branchLane); // 先占坑:父分支排在前面
followMain([sc], branchLane); // 再递归:孙子分支自然排在后面
}
}
}