Files
track/track-uniapp/src/pages/scan/components/TreeCanvas.vue
duxingchen 25ad5156f8 style: 移动端流转树主干道对齐与连线箭头
- 主线(含 TRANSFER 子任务)左边缘对齐,消除缩进形成笔直主干道
- 主线卡片之间加向下连线箭头(↓)
- 分支(SPAWN 协助)保留缩进 + 左边线
2026-09-01 14:10:40 +08:00

122 lines
4.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="tree-fullscreen">
<view class="tc-toolbar">
<view class="tc-toolbar-left">
<view class="tc-back-btn" @tap="$emit('back')"> 返回</view>
<text class="tc-title">🌳 流转树</text>
</view>
<view class="tc-toolbar-right">
<view class="tc-zoom-btn tc-mode-btn" @tap="$emit('swipe')">📇 卡片</view>
</view>
</view>
<view v-if="!product.task_tree || !product.task_tree.length" class="zero-task">
<text class="zero-icon">📋</text><text class="zero-text">该产品暂无流转记录</text>
</view>
<scroll-view v-else scroll-y class="tree-scroll" :style="{ height: scrollHeight + 'px' }">
<view class="tree-root">
<!-- 递归纵向流转树根级主线之间用 连线分支由 FlowTree 内部缩进 -->
<template v-for="(t, i) in product.task_tree">
<view :key="'arrow-' + i" v-if="i > 0" class="ft-flow-arrow"></view>
<FlowTree
:key="t.id"
:node="t"
:current-user="currentUser"
:current-user-id="currentUserId"
:current-username="currentUsername"
@view-records="$emit('viewRecords', $event)"
/>
</template>
</view>
<!-- 图例 -->
<view class="tree-legend">
<text class="legend-item legend-main"> 主线</text>
<text class="legend-item legend-sub"> 分支</text>
<text class="legend-item">📥 在库</text>
<text class="legend-item">📦 已入库</text>
</view>
</scroll-view>
</view>
</template>
<script>
import FlowTree from "./FlowTree.vue";
export default {
name: "TreeCanvas",
components: { FlowTree },
props: {
product: { type: Object, required: true },
currentUser: { type: Object, default: null },
currentUserId: { type: [String, Number], default: "" },
currentUsername: { type: String, default: "" },
},
emits: ["viewRecords", "back", "swipe"],
data() {
return { scrollHeight: 500 };
},
mounted() {
try {
const info = uni.getSystemInfoSync();
// 工具条高度 + 底部安全区预留约 70px剩余给滚动区
this.scrollHeight = Math.max((info.windowHeight || 600) - 70, 300);
} catch (e) {}
},
};
</script>
<style scoped>
.tree-fullscreen {
height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fa;
}
.tc-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
padding-top: calc(10px + env(safe-area-inset-top));
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid #e5e7eb;
flex-shrink: 0;
z-index: 999;
}
.tc-toolbar-left { display: flex; align-items: center; gap: 12px; }
.tc-back-btn { font-size: 14px; font-weight: 700; color: #2563eb; padding: 6px 12px; background: #eff6ff; border-radius: 8px; }
.tc-title { font-size: 16px; font-weight: 800; color: #1f2937; }
.tc-toolbar-right { display: flex; align-items: center; gap: 8px; }
.tc-zoom-btn { display: flex; align-items: center; justify-content: center; }
.tc-mode-btn { width: auto; padding: 0 10px; font-size: 12px; background: #ede9fe; color: #7c3aed; border-radius: 8px; height: 30px; }
.zero-task { display: flex; flex-direction: column; align-items: center; padding: 60px 0; }
.zero-icon { font-size: 40px; margin-bottom: 10px; }
.zero-text { font-size: 14px; color: #9ca3af; }
.tree-scroll { flex-shrink: 0; }
.tree-root { padding: 16px 12px 20px; }
/* 根级主线之间的向下连线箭头 */
.ft-flow-arrow {
text-align: center;
color: #9ca3af;
font-size: 16px;
font-weight: 700;
line-height: 1;
margin: 2px 0;
}
.tree-legend {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 10px 16px 20px;
font-size: 11px;
color: #6b7280;
}
.legend-item { padding: 2px 8px; background: #fff; border-radius: 6px; border: 1px solid #e5e7eb; }
.legend-main { color: #2563eb; font-weight: 700; }
.legend-sub { color: #7c3aed; font-weight: 700; }
</style>