fix(frontend): 流转蓝图 + 聊天室 + 卡片布局 + 打印功能修复
1. TreeCanvas.vue - Y轴坐标算法重构:
- calcSubtreeHeight 改为兄弟累加 (reduce) 替代 Math.max,彻底杜绝垂直重叠
- childNode.y = parentNode.y + CARD_H + VERTICAL_GAP 严格向下延伸
- 同级子节点按子树高度累加 startY,各占独立 Y 区域
- 加宽画布默认尺寸 (400→800, padding +300/+400)
2. detail.vue - onShow 生命周期修复:
- 新增 onShow() 钩子调用 fetchMessages(),解决聊天室退回不刷新问题
3. TaskSwipeCards.vue + detail.vue - CSS 防跳动:
- .ss-swiper-h / .ss-card-wrapper 添加 min-height: 220px
- .card 添加 min-height: 120px
4. detail.vue - 移动端打印标签:
- 新增「🖨️ 打印标签」按钮
- 双方案 ActionSheet: 网络打印机(后端API) / 蓝牙打印机(ESC/POS)
- 对接现有 /print/execute 端点
This commit is contained in:
@ -74,11 +74,11 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
// ============================================================
|
||||
// 🚀 父子相对坐标延伸算法
|
||||
// 🚀 父子相对坐标延伸算法(v3 — 无重叠 Y 轴栈式布局)
|
||||
// ============================================================
|
||||
treeNodes() {
|
||||
if (!this.product || !this.product.task_tree) return [];
|
||||
const CARD_W = 160, CARD_H = 80, GAP_X = 24, GAP_Y = 24;
|
||||
const CARD_W = 160, CARD_H = 80, GAP_X = 24, VERTICAL_GAP = 28, MAIN_GAP = 40;
|
||||
const CENTER_X = 0;
|
||||
|
||||
// 拍平 + 浅拷贝
|
||||
@ -121,25 +121,39 @@ export default {
|
||||
allTasks.forEach(t => { if (isMainFn(t)) mains.push(t); });
|
||||
mains.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
||||
|
||||
// 🚀 预计算每个节点的子树总高度(兄弟累加,用于防重叠占位)
|
||||
const calcSubtreeHeight = (nodeId) => {
|
||||
const children = childMap[nodeId] || [];
|
||||
if (children.length === 0) return CARD_H + VERTICAL_GAP;
|
||||
const totalChildrenHeight = children.reduce((sum, c) => sum + calcSubtreeHeight(c.id), 0);
|
||||
return CARD_H + VERTICAL_GAP + totalChildrenHeight;
|
||||
};
|
||||
|
||||
const nodes = [];
|
||||
|
||||
// 🚀 全新递归放置算法(相对坐标系)
|
||||
// 🚀 递归放置算法:childNode.y = parentNode.y + CARD_H + VERTICAL_GAP
|
||||
// 同级子节点按 index 错开 Y 轴,各占其子树高度防止重叠
|
||||
const placeChildren = (parentNode, currentSide) => {
|
||||
const children = childMap[parentNode.id] || [];
|
||||
// 🚀 首个子节点 Y 起始:parent.y 正下方
|
||||
let startY = parentNode.y + CARD_H + VERTICAL_GAP;
|
||||
|
||||
children.forEach((child, index) => {
|
||||
const subtreeH = calcSubtreeHeight(child.id);
|
||||
|
||||
// 主干的第一层协助分支:均衡分发左右
|
||||
let side = currentSide;
|
||||
if (parentNode._isMain) {
|
||||
side = index % 2 === 0 ? 'right' : 'left';
|
||||
}
|
||||
|
||||
// 🚀 核心:永远基于真实父亲 (parentNode) 的坐标向外延伸!
|
||||
// 🚀 X 轴:永远基于真实父亲向外延伸
|
||||
const childX = side === 'right'
|
||||
? parentNode.x + CARD_W + GAP_X
|
||||
: parentNode.x - CARD_W - GAP_X;
|
||||
|
||||
// 同级多子节点略微错开 Y 轴防重叠
|
||||
const childY = parentNode.y + (index * (CARD_H + 20));
|
||||
// 🚀 Y 轴:严格基于父节点向下延伸,同级按 index 错开
|
||||
const childY = startY;
|
||||
|
||||
const childNode = {
|
||||
...child,
|
||||
@ -151,24 +165,30 @@ export default {
|
||||
nodes.push(childNode);
|
||||
flatMap[childNode.id] = childNode;
|
||||
|
||||
// 带着当前的方向(side)继续递归,保证孙子永远顺着儿子的方向长
|
||||
// 递归放置孙子节点(沿相同方向)
|
||||
placeChildren(childNode, side);
|
||||
|
||||
// 🚀 下一个兄弟节点跳到当前子树高度之后,严禁 Y 坐标重叠
|
||||
startY += subtreeH + VERTICAL_GAP;
|
||||
});
|
||||
};
|
||||
|
||||
// 🚀 渲染入口
|
||||
// 🚀 渲染入口:主干节点也按子树深度排布
|
||||
let mainStartY = 0;
|
||||
mains.forEach((m, mi) => {
|
||||
const rootY = mi * (CARD_H + GAP_Y) * 2;
|
||||
const mainNode = { ...m, x: CENTER_X, y: rootY, _isMain: true, _blabel: '主分支', _rootMainId: m.id };
|
||||
const subtreeH = calcSubtreeHeight(m.id);
|
||||
const mainNode = { ...m, x: CENTER_X, y: mainStartY, _isMain: true, _blabel: '主分支', _rootMainId: m.id };
|
||||
nodes.push(mainNode);
|
||||
flatMap[mainNode.id] = mainNode;
|
||||
placeChildren(mainNode, null);
|
||||
mainStartY += subtreeH + MAIN_GAP;
|
||||
});
|
||||
|
||||
// 全局偏移
|
||||
// 全局偏移(给左侧分支预留空间)
|
||||
const minX = Math.min(...nodes.map(n => n.x));
|
||||
const minY = Math.min(...nodes.map(n => n.y));
|
||||
const padding = 40;
|
||||
nodes.forEach(n => { n.x += Math.abs(minX) + CARD_W * 2 + padding; n.y += padding; });
|
||||
nodes.forEach(n => { n.x += Math.abs(minX) + CARD_W * 2 + padding; n.y += padding - Math.min(0, minY); });
|
||||
return nodes;
|
||||
},
|
||||
// ============================================================
|
||||
@ -215,8 +235,8 @@ export default {
|
||||
return result;
|
||||
},
|
||||
// ============================================================
|
||||
canvasWidth() { const ns = this.treeNodes; if (!ns.length) return 400; return Math.max(...ns.map(n => n.x)) + 200; },
|
||||
canvasHeight() { const ns = this.treeNodes; if (!ns.length) return 400; return Math.max(...ns.map(n => n.y)) + 200; },
|
||||
canvasWidth() { const ns = this.treeNodes; if (!ns.length) return 800; return Math.max(...ns.map(n => n.x)) + 400; },
|
||||
canvasHeight() { const ns = this.treeNodes; if (!ns.length) return 800; return Math.max(...ns.map(n => n.y)) + 300; },
|
||||
},
|
||||
methods: {
|
||||
formatUserName,
|
||||
|
||||
Reference in New Issue
Block a user