双模态详情页: workspace(swiper+footer) / tree(只读流转树) 智能分流+一键切换
This commit is contained in:
@ -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;
|
||||
|
||||
@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user