双模态详情页: workspace(swiper+footer) / tree(只读流转树) 智能分流+一键切换

This commit is contained in:
2026-08-05 16:43:32 +08:00
parent 21aa608db3
commit 78559db1fa
2 changed files with 66 additions and 24 deletions

View File

@ -35,6 +35,7 @@
:currentUser="currentUser"
:currentUserId="currentUserId"
:currentUsername="currentUsername"
:readonly="readonly"
:isLast="idx === task.child_tasks.length - 1"
@action="(e) => $emit('action', e)"
@editRecord="(e) => $emit('editRecord', e)"
@ -85,6 +86,7 @@ export default {
currentUser: { type: Object, default: null },
currentUserId: { type: [String, Number], default: "" },
currentUsername: { type: String, default: "" },
readonly: { type: Boolean, default: false },
},
emits: ["action", "editRecord", "viewRecords"],
mounted() {
@ -94,6 +96,7 @@ export default {
},
computed: {
canEditRecord() {
if (this.readonly) return false;
if (this.task.assignee_id == this.currentUserId) return true;
if (this.task.assignee_id == this.currentUsername) return true;
if (this.currentUser && this.currentUser.id == this.task.assignee_id) return true;

View File

@ -24,6 +24,9 @@
<text class="card-title">📦 产品信息</text>
<view class="card-header-right">
<text :class="['badge', statusColor(product.status)]">{{ statusLabel(product.status) }}</text>
<text class="mode-toggle" @tap="toggleMode">
{{ currentMode === 'workspace' ? '🌳 流转树' : '🛠 工作区' }}
</text>
<text class="edit-btn" @tap="openEditProduct"></text>
</view>
</view>
@ -54,9 +57,9 @@
</view>
<!-- ================================================================ -->
<!-- 任务卡片看板 (专注模式 + 左右滑动) -->
<!-- 🛠 个人工作区 (workspace) -->
<!-- ================================================================ -->
<view class="swiper-area">
<view v-if="currentMode === 'workspace'" class="swiper-area">
<!-- 0任务 -->
<view v-if="!product.task_tree || !product.task_tree.length" class="zero-task">
<text class="zero-icon">📋</text>
@ -126,32 +129,51 @@
</swiper-item>
</swiper>
</template>
<!-- 底部操作栏 workspace 模式 -->
<view v-if="currentTask" class="footer-actions">
<button v-if="currentTask.status === 'WIP'" class="footer-btn footer-transfer"
@tap="handleTaskAction({ task: currentTask, type: 'transfer' })">
🔄 完工转交
</button>
<button v-if="currentTask.status === 'WIP'" class="footer-btn footer-record"
@tap="handleTaskAction({ task: currentTask, type: 'record' })">
📝 记录/拍照
</button>
<button v-if="currentTask.status === 'WIP'" class="footer-btn footer-end"
@tap="handleTaskAction({ task: currentTask, type: 'end' })">
🏁 结束分支
</button>
<button v-if="currentTask.status === 'PENDING'" class="footer-btn footer-receive"
@tap="handleTaskAction({ task: currentTask, type: 'receive' })">
接收任务
</button>
<button v-if="currentTask.status === 'PENDING'" class="footer-btn footer-reject"
@tap="handleTaskAction({ task: currentTask, type: 'reject' })">
驳回任务
</button>
</view>
</view>
<!-- ================================================================ -->
<!-- 底部操作栏钉死不随滚动 -->
<!-- 🌳 全局流转树 (tree 模式 只读) -->
<!-- ================================================================ -->
<view v-if="currentTask" class="footer-actions">
<button v-if="currentTask.status === 'WIP'" class="footer-btn footer-transfer"
@tap="handleTaskAction({ task: currentTask, type: 'transfer' })">
🔄 完工转交
</button>
<button v-if="currentTask.status === 'WIP'" class="footer-btn footer-record"
@tap="handleTaskAction({ task: currentTask, type: 'record' })">
📝 记录/拍照
</button>
<button v-if="currentTask.status === 'WIP'" class="footer-btn footer-end"
@tap="handleTaskAction({ task: currentTask, type: 'end' })">
🏁 结束分支
</button>
<button v-if="currentTask.status === 'PENDING'" class="footer-btn footer-receive"
@tap="handleTaskAction({ task: currentTask, type: 'receive' })">
接收任务
</button>
<button v-if="currentTask.status === 'PENDING'" class="footer-btn footer-reject"
@tap="handleTaskAction({ task: currentTask, type: 'reject' })">
驳回任务
</button>
<view v-if="currentMode === 'tree'" class="tree-area">
<view v-if="!product.task_tree || !product.task_tree.length" class="zero-task">
<text class="zero-icon">📋</text>
<text class="zero-text">该产品暂无流转任务</text>
</view>
<TaskTreeNode
v-for="(task, idx) in (product.task_tree || [])"
:key="task.id"
:task="task"
:currentUser="currentUser"
:currentUserId="currentUserId"
:currentUsername="currentUsername"
:hasChildren="task.child_tasks && task.child_tasks.length > 0"
:isLast="idx === (product.task_tree || []).length - 1"
:readonly="true"
@viewRecords="handleViewRecords"
/>
</view>
</template>
@ -397,6 +419,7 @@ export default {
currentUserId: "",
currentUsername: "",
swiperCurrent: 0,
currentMode: "tree",
// Picker 数据源
processOptions: [],
userOptions: [],
@ -453,6 +476,9 @@ export default {
currentTask() {
return this.focusTasks[this.swiperCurrent] || null;
},
hasMyActiveTask() {
return this.focusTasks.length > 0;
},
},
onLoad(options) {
this.loadUsers();
@ -479,9 +505,18 @@ export default {
try {
this.product = await get(`/products/scan/${sn}`);
if (!this.product.overall_status) this.showStatusPicker = true;
// 智能分流:有本人活跃任务 → workspace否则 → tree
this.$nextTick(() => {
this.currentMode = this.hasMyActiveTask ? 'workspace' : 'tree';
this.swiperCurrent = 0;
});
} catch (e) { this.error = e?.data?.detail || "查询失败"; }
finally { this.loading = false; }
},
toggleMode() {
this.currentMode = this.currentMode === 'workspace' ? 'tree' : 'workspace';
if (this.currentMode === 'workspace') this.swiperCurrent = 0;
},
// ---- 状态定调 ----
async handleSetOverallStatus(status) {
@ -915,10 +950,14 @@ 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; }
.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; }
.edit-btn { font-size: 18px; padding: 2px 6px; }
.mode-toggle { font-size: 13px; font-weight: 700; padding: 4px 10px; border-radius: 8px; background: #eff6ff; color: #2563eb; }
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.label { font-size: 12px; color: #9ca3af; }
.value { font-size: 14px; color: #1f2937; font-weight: 600; word-break: break-all; }