From 1d759f3e8cef3eac8a375335d61a93a9eb47d246 Mon Sep 17 00:00:00 2001 From: duxingchen Date: Wed, 5 Aug 2026 16:58:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=81=E8=BD=AC=E6=A0=912D=E7=94=BB=E5=B8=83?= =?UTF-8?q?:=20scroll-view=E6=97=A0=E9=99=90=E6=8B=96=E6=8B=BD=20+=20?= =?UTF-8?q?=E6=B0=B4=E5=B9=B3=E5=88=86=E6=94=AF=E5=B9=B6=E6=8E=92=20+=20?= =?UTF-8?q?=E5=9E=82=E7=9B=B4=E6=97=B6=E9=97=B4=E8=BD=B4=20+=20=E8=BF=9E?= =?UTF-8?q?=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- track-uniapp/src/pages/scan/detail.vue | 125 ++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 15 deletions(-) diff --git a/track-uniapp/src/pages/scan/detail.vue b/track-uniapp/src/pages/scan/detail.vue index d50a59e..aae142c 100644 --- a/track-uniapp/src/pages/scan/detail.vue +++ b/track-uniapp/src/pages/scan/detail.vue @@ -151,25 +151,35 @@ - + 📋 该产品暂无流转任务 - + 🖐 上下滑动看流程 · 左右滑动看分支 + + + + + {{ node.task_name }} + {{ node.assignee_id || '—' }} + {{ statusLabel(node.status) }} + {{ node.remark }} + 🏁 终止 + 📦 入库 + 📋{{ node.records.length }} + + + + + + @@ -475,6 +485,59 @@ export default { hasMyActiveTask() { return this.focusTasks.length > 0; }, + // 2D 画布布局 + treeNodes() { + const nodes = []; + const CARD_W = 180, CARD_H = 120, GAP_X = 40, GAP_Y = 30; + const layout = (tasks, depth, startX) => { + if (!tasks || !tasks.length) return startX; + const y = 20 + depth * (CARD_H + GAP_Y); + let x = startX; + const childXs = []; + for (const t of tasks) { + nodes.push({ ...t, x, y }); + const childStartX = x; + if (t.child_tasks && t.child_tasks.length) { + x = layout(t.child_tasks, depth + 1, x); + } else { + x += CARD_W + GAP_X; + } + childXs.push({ id: t.id, cx: childStartX + CARD_W / 2, cy: y + CARD_H, children: t.child_tasks || [] }); + } + // Store parent center for line drawing + nodes._lines = nodes._lines || []; + for (const cx of childXs) { + if (cx.children.length) { + const midX = cx.cx; + for (const c of cx.children) { + const childNode = nodes.find(n => n.id === c.id); + if (childNode) { + nodes._lines.push({ key: cx.id + '-' + c.id, x1: midX, y1: cx.cy, x2: childNode.x + CARD_W / 2, y2: childNode.y }); + } + } + } + } + return x; + }; + if (this.product && this.product.task_tree) { + nodes._lines = []; + layout(this.product.task_tree, 0, 20); + } + return nodes; + }, + treeLines() { + return this.treeNodes._lines || []; + }, + canvasWidth() { + const nodes = this.treeNodes; + if (!nodes.length) return 400; + return Math.max(400, Math.max(...nodes.map(n => n.x)) + 200); + }, + canvasHeight() { + const nodes = this.treeNodes; + if (!nodes.length) return 400; + return Math.max(400, Math.max(...nodes.map(n => n.y)) + 140); + }, }, onLoad(options) { this.loadUsers(); @@ -516,6 +579,19 @@ export default { lockTask(taskId) { this.lockedTaskId = taskId; }, + lineStyle(line) { + const dx = line.x2 - line.x1; + const dy = line.y2 - line.y1; + const len = Math.sqrt(dx * dx + dy * dy); + const angle = Math.atan2(dy, dx) * 180 / Math.PI; + return { + left: line.x1 + 'px', + top: line.y1 + 'px', + width: len + 'px', + transform: `rotate(${angle}deg)`, + transformOrigin: '0 0', + }; + }, // ---- 状态定调 ---- async handleSetOverallStatus(status) { @@ -965,8 +1041,27 @@ export default { .fc-records-bar { padding: 8px 12px; background: linear-gradient(135deg,#eff6ff,#dbeafe); border-radius: 8px; font-size: 13px; font-weight: 700; color: #2563eb; margin-bottom: 8px; } .fc-time { font-size: 11px; color: #9ca3af; margin-top: auto; padding-top: 8px; border-top: 1px solid #f3f4f6; } -/* 流转树视图(只读) */ -.tree-area { flex: 1; overflow-y: auto; min-height: 0; padding: 4px 0; -webkit-overflow-scrolling: touch; } +/* 流转树 2D 画布 */ +.tree-area { flex: 1; overflow: hidden; min-height: 0; position: relative; } +.canvas-hint { font-size: 11px; color: #9ca3af; text-align: center; padding: 4px 0; flex-shrink: 0; } +.tree-canvas { flex: 1; min-height: 0; } +.canvas-inner { position: relative; } +.canvas-node { position: absolute; width: 180px; min-height: 100px; background: #fff; border-radius: 10px; padding: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); border-top: 4px solid #3b82f6; } +.canvas-node.s-yellow { border-top-color: #f59e0b; } +.canvas-node.s-blue { border-top-color: #3b82f6; } +.canvas-node.s-green { border-top-color: #22c55e; } +.canvas-node.s-red { border-top-color: #ef4444; } +.cn-name { font-size: 13px; font-weight: 700; color: #1f2937; display: block; } +.cn-assignee { font-size: 10px; color: #9ca3af; display: block; margin-top: 2px; } +.cn-badge { font-size: 9px; padding: 1px 6px; border-radius: 8px; font-weight: 600; margin-top: 4px; display: inline-block; } +.cn-badge.s-yellow { background: #fef3c7; color: #b45309; } +.cn-badge.s-blue { background: #dbeafe; color: #1d4ed8; } +.cn-badge.s-green { background: #dcfce7; color: #15803d; } +.cn-remark { font-size: 10px; color: #6b7280; margin-top: 4px; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.cn-end { font-size: 9px; font-weight: 600; color: #16a34a; margin-top: 2px; } +.cn-end-wh { color: #7c3aed; } +.cn-records { font-size: 10px; color: #2563eb; font-weight: 600; margin-top: 2px; } +.canvas-line { position: absolute; height: 2px; background: #d1d5db; } .card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; } .card-header-right { display: flex; align-items: center; gap: 8px; } .card-title { font-size: 15px; font-weight: 700; }