防呆重构: 废除swiper→任务列表+锁定工作区 + remark醒目显示 + 树视图减少缩进+分支闭环标记
This commit is contained in:
@ -22,8 +22,12 @@
|
||||
<text class="remark-text">{{ task.remark || task.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 状态标签 -->
|
||||
<text :class="['badge', statusColor(task.status)]">{{ statusLabel(task.status) }}</text>
|
||||
<!-- 状态标签 + 分支闭环标记 -->
|
||||
<view style="display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex-shrink:0;margin-left:8px;">
|
||||
<text :class="['badge', statusColor(task.status)]">{{ statusLabel(task.status) }}</text>
|
||||
<text v-if="task.status === 'COMPLETED' && (!task.child_tasks || !task.child_tasks.length)" class="end-marker">🏁 已终止</text>
|
||||
<text v-if="task.status === 'ARCHIVED'" class="end-marker end-warehouse">📦 已入库</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 第二层:递归子任务树 -->
|
||||
@ -154,7 +158,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tnode { position: relative; padding-left: 24px; margin-bottom: 4px; }
|
||||
.tnode { position: relative; padding-left: 12px; margin-bottom: 6px; }
|
||||
.tree-line-vertical { position: absolute; left: 8px; top: 28px; bottom: 0; width: 2px; background: #e5e7eb; }
|
||||
|
||||
.tnode-card {
|
||||
@ -211,9 +215,11 @@ export default {
|
||||
|
||||
.badge {
|
||||
font-size: 10px; padding: 2px 8px; border-radius: 20px; font-weight: 600;
|
||||
white-space: nowrap; flex-shrink: 0; margin-left: 8px;
|
||||
white-space: nowrap; flex-shrink: 0;
|
||||
background: #f3f4f6; color: #6b7280;
|
||||
}
|
||||
.end-marker { font-size: 9px; padding: 1px 6px; border-radius: 8px; font-weight: 600; white-space: nowrap; background: #dcfce7; color: #16a34a; }
|
||||
.end-warehouse { background: #ede9fe; color: #7c3aed; }
|
||||
.s-yellow .badge { background: #fef3c7; color: #b45309; }
|
||||
.s-blue .badge { background: #dbeafe; color: #1d4ed8; }
|
||||
.s-green .badge { background: #dcfce7; color: #15803d; }
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
<!-- ================================================================ -->
|
||||
<!-- 🛠️ 个人工作区 (workspace) -->
|
||||
<!-- ================================================================ -->
|
||||
<view v-if="currentMode === 'workspace'" class="swiper-area">
|
||||
<view v-if="currentMode === 'workspace'" class="workspace-area">
|
||||
<!-- 0任务 -->
|
||||
<view v-if="!product.task_tree || !product.task_tree.length" class="zero-task">
|
||||
<text class="zero-icon">📋</text>
|
||||
@ -67,91 +67,87 @@
|
||||
<button class="btn-start" @tap="openCreateFirstTask">🚀 发起首道工序</button>
|
||||
</view>
|
||||
|
||||
<!-- 无本人任务但有其他任务 -->
|
||||
<!-- 无本人任务 -->
|
||||
<view v-else-if="!focusTasks.length" class="zero-task">
|
||||
<text class="zero-icon">🔒</text>
|
||||
<text class="zero-text">当前没有需要你处理的任务</text>
|
||||
<text class="task-count">共 {{ countTasks(product.task_tree) }} 个任务</text>
|
||||
</view>
|
||||
|
||||
<!-- Swiper 卡片 -->
|
||||
<!-- ====== 任务列表(未锁定) ====== -->
|
||||
<template v-else-if="!lockedTaskId">
|
||||
<view class="list-header">
|
||||
<text class="list-title">📋 待处理任务 ({{ focusTasks.length }})</text>
|
||||
<text class="list-hint">点击任务进入工作区</text>
|
||||
</view>
|
||||
<view class="task-list">
|
||||
<view v-for="t in focusTasks" :key="t.id" class="task-list-item"
|
||||
:class="statusColor(t.status)" @tap="lockTask(t.id)">
|
||||
<view class="tli-left">
|
||||
<text class="tli-name">{{ t.task_name }}</text>
|
||||
<text v-if="t.remark" class="tli-remark">{{ t.remark }}</text>
|
||||
</view>
|
||||
<view class="tli-right">
|
||||
<text :class="['tli-badge', statusColor(t.status)]">{{ statusLabel(t.status) }}</text>
|
||||
<text v-if="t.is_rework" class="tag tag-rework-sm" style="margin-top:2px;">⚠返工</text>
|
||||
<text class="tli-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- ====== 锁定工作区(单任务详情) ====== -->
|
||||
<template v-else>
|
||||
<view class="swipe-nav">
|
||||
<text class="swipe-info">{{ swiperCurrent + 1 }} / {{ focusTasks.length }}</text>
|
||||
<text class="swipe-hint">← 左右滑动切换任务 →</text>
|
||||
<view class="lock-bar">
|
||||
<text class="lock-back" @tap="lockedTaskId = null">← 返回列表</text>
|
||||
<text class="lock-title">🔒 {{ lockedTask.task_name }}</text>
|
||||
</view>
|
||||
|
||||
<swiper
|
||||
class="task-swiper"
|
||||
:current="swiperCurrent"
|
||||
@change="onSwiperChange"
|
||||
:indicator-dots="false"
|
||||
:circular="false"
|
||||
>
|
||||
<swiper-item v-for="(t, i) in focusTasks" :key="t.id">
|
||||
<view class="focus-card" :class="statusColor(t.status)">
|
||||
<!-- 卡片头 -->
|
||||
<view class="fc-header">
|
||||
<text class="fc-status">{{ statusLabel(t.status) }}</text>
|
||||
<text v-if="t.is_rework" class="tag tag-rework-sm">⚠返工</text>
|
||||
</view>
|
||||
<view class="focus-card" :class="statusColor(lockedTask.status)">
|
||||
<view class="fc-header">
|
||||
<text class="fc-status">{{ statusLabel(lockedTask.status) }}</text>
|
||||
<text v-if="lockedTask.is_rework" class="tag tag-rework-sm">⚠返工</text>
|
||||
</view>
|
||||
<text class="fc-name">{{ lockedTask.task_name }}</text>
|
||||
|
||||
<!-- 任务名 -->
|
||||
<text class="fc-name">{{ t.task_name }}</text>
|
||||
<!-- 📌 交接/接收备注 -->
|
||||
<view v-if="lockedTask.remark" class="fc-remark">
|
||||
<text class="fc-remark-label">📌 交接/接收备注:</text>
|
||||
<text class="fc-remark-text">{{ lockedTask.remark }}</text>
|
||||
</view>
|
||||
|
||||
<!-- remark初始说明 -->
|
||||
<view v-if="t.remark" class="fc-remark">
|
||||
<text class="fc-remark-label">📌</text>
|
||||
<text class="fc-remark-text">{{ t.remark }}</text>
|
||||
</view>
|
||||
<view v-if="lockedTask.parent_task_id && parentTaskOf(lockedTask)" class="fc-link fc-up">
|
||||
<text class="fc-link-label">⬆ 上游工序</text>
|
||||
<text class="fc-link-name">{{ parentTaskOf(lockedTask).task_name }} → {{ parentTaskOf(lockedTask).assignee_id || '—' }}</text>
|
||||
</view>
|
||||
<view v-if="lockedTask.child_tasks && lockedTask.child_tasks.length" class="fc-link fc-down">
|
||||
<text class="fc-link-label">⬇ 下游分支 ({{ lockedTask.child_tasks.length }})</text>
|
||||
<text v-for="c in lockedTask.child_tasks" :key="c.id" class="fc-link-name">
|
||||
· {{ c.task_name }} → {{ c.assignee_id || '—' }}
|
||||
<text v-if="c.status==='COMPLETED'" class="branch-done">✓已完成</text>
|
||||
<text v-else-if="c.status==='ARCHIVED'" class="branch-done">📦已入库</text>
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 来源/去向 -->
|
||||
<view class="fc-chain">
|
||||
<view v-if="t.parent_task_id && parentTaskOf(t)" class="fc-link fc-up">
|
||||
<text class="fc-link-label">⬆ 上一步</text>
|
||||
<text class="fc-link-name">{{ parentTaskOf(t).task_name }}</text>
|
||||
<text class="fc-link-who">→ {{ parentTaskOf(t).assignee_id || '—' }}</text>
|
||||
</view>
|
||||
<view v-if="t.child_tasks && t.child_tasks.length" class="fc-link fc-down">
|
||||
<text class="fc-link-label">⬇ 下一步 ({{ t.child_tasks.length }})</text>
|
||||
<text v-for="c in t.child_tasks" :key="c.id" class="fc-link-name">· {{ c.task_name }} → {{ c.assignee_id || '—' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="lockedTask.records && lockedTask.records.length" class="fc-records-bar" @tap="handleViewRecords(lockedTask)">
|
||||
📋 {{ lockedTask.records.length }} 条干活记录 ›
|
||||
</view>
|
||||
<view class="fc-time">{{ formatTaskTime(lockedTask) }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 最新记录 -->
|
||||
<view v-if="t.records && t.records.length" class="fc-records-bar" @tap="handleViewRecords(t)">
|
||||
📋 {{ t.records.length }} 条记录 ›
|
||||
</view>
|
||||
|
||||
<!-- 时间 -->
|
||||
<view class="fc-time">{{ formatTaskTime(t) }}</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="footer-actions">
|
||||
<button v-if="lockedTask.status === 'WIP'" class="footer-btn footer-transfer"
|
||||
@tap="handleTaskAction({ task: lockedTask, type: 'transfer' })">🔄 完工转交</button>
|
||||
<button v-if="lockedTask.status === 'WIP'" class="footer-btn footer-record"
|
||||
@tap="handleTaskAction({ task: lockedTask, type: 'record' })">📝 记录/拍照</button>
|
||||
<button v-if="lockedTask.status === 'WIP'" class="footer-btn footer-end"
|
||||
@tap="handleTaskAction({ task: lockedTask, type: 'end' })">🏁 结束分支</button>
|
||||
<button v-if="lockedTask.status === 'PENDING'" class="footer-btn footer-receive"
|
||||
@tap="handleTaskAction({ task: lockedTask, type: 'receive' })">✅ 接收任务</button>
|
||||
<button v-if="lockedTask.status === 'PENDING'" class="footer-btn footer-reject"
|
||||
@tap="handleTaskAction({ task: lockedTask, type: 'reject' })">❌ 驳回任务</button>
|
||||
</view>
|
||||
</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>
|
||||
|
||||
<!-- ================================================================ -->
|
||||
@ -418,8 +414,8 @@ export default {
|
||||
currentUser: null,
|
||||
currentUserId: "",
|
||||
currentUsername: "",
|
||||
swiperCurrent: 0,
|
||||
currentMode: "tree",
|
||||
lockedTaskId: null,
|
||||
// Picker 数据源
|
||||
processOptions: [],
|
||||
userOptions: [],
|
||||
@ -473,8 +469,8 @@ export default {
|
||||
if (this.product) walk(this.product.task_tree);
|
||||
return result;
|
||||
},
|
||||
currentTask() {
|
||||
return this.focusTasks[this.swiperCurrent] || null;
|
||||
lockedTask() {
|
||||
return this.focusTasks.find(t => t.id === this.lockedTaskId) || null;
|
||||
},
|
||||
hasMyActiveTask() {
|
||||
return this.focusTasks.length > 0;
|
||||
@ -508,14 +504,17 @@ export default {
|
||||
// 智能分流:有本人活跃任务 → workspace,否则 → tree
|
||||
this.$nextTick(() => {
|
||||
this.currentMode = this.hasMyActiveTask ? 'workspace' : 'tree';
|
||||
this.swiperCurrent = 0;
|
||||
this.lockedTaskId = null;
|
||||
});
|
||||
} 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;
|
||||
if (this.currentMode === 'workspace') this.lockedTaskId = null;
|
||||
},
|
||||
lockTask(taskId) {
|
||||
this.lockedTaskId = taskId;
|
||||
},
|
||||
|
||||
// ---- 状态定调 ----
|
||||
@ -804,9 +803,6 @@ export default {
|
||||
this.doQuery(this.product.serial_number);
|
||||
} catch {}
|
||||
},
|
||||
onSwiperChange(e) {
|
||||
this.swiperCurrent = e.detail.current;
|
||||
},
|
||||
parentTaskOf(t) {
|
||||
if (!t || !t.parent_task_id) return null;
|
||||
return this.taskMap[t.parent_task_id] || null;
|
||||
@ -910,12 +906,30 @@ export default {
|
||||
|
||||
/* 卡片 */
|
||||
.card { background: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); flex-shrink: 0; }
|
||||
/* Swiper 卡片看板 */
|
||||
.swiper-area { flex: 1; display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
|
||||
.swipe-nav { display: flex; align-items: center; justify-content: space-between; padding: 8px 4px; flex-shrink: 0; }
|
||||
.swipe-info { font-size: 12px; font-weight: 700; color: #2563eb; }
|
||||
.swipe-hint { font-size: 11px; color: #9ca3af; }
|
||||
.task-swiper { flex: 1; min-height: 0; }
|
||||
/* 工作区 */
|
||||
.workspace-area { flex: 1; display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
|
||||
.list-header { display: flex; align-items: baseline; justify-content: space-between; padding: 8px 4px; flex-shrink: 0; }
|
||||
.list-title { font-size: 14px; font-weight: 700; color: #1f2937; }
|
||||
.list-hint { font-size: 11px; color: #9ca3af; }
|
||||
.task-list { flex: 1; overflow-y: auto; min-height: 0; }
|
||||
.task-list-item { display: flex; align-items: flex-start; justify-content: space-between; background: #fff; border-radius: 12px; padding: 14px; margin-bottom: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); border-left: 4px solid transparent; }
|
||||
.task-list-item.s-yellow { border-left-color: #f59e0b; }
|
||||
.task-list-item.s-blue { border-left-color: #3b82f6; }
|
||||
.tli-left { flex: 1; min-width: 0; }
|
||||
.tli-name { font-size: 15px; font-weight: 700; color: #1f2937; display: block; }
|
||||
.tli-remark { font-size: 12px; color: #6b7280; margin-top: 4px; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.tli-right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex-shrink: 0; margin-left: 8px; }
|
||||
.tli-badge { font-size: 11px; padding: 2px 10px; border-radius: 12px; font-weight: 600; }
|
||||
.tli-badge.s-yellow { background: #fef3c7; color: #b45309; }
|
||||
.tli-badge.s-blue { background: #dbeafe; color: #1d4ed8; }
|
||||
.tli-arrow { font-size: 18px; color: #d1d5db; }
|
||||
|
||||
.lock-bar { display: flex; align-items: center; gap: 12px; padding: 8px 0; flex-shrink: 0; }
|
||||
.lock-back { font-size: 13px; color: #2563eb; font-weight: 600; }
|
||||
.lock-title { font-size: 14px; font-weight: 700; color: #1f2937; }
|
||||
|
||||
.branch-done { font-size: 11px; font-weight: 600; color: #16a34a; }
|
||||
.branch-active { font-size: 11px; font-weight: 600; color: #2563eb; }
|
||||
.task-count { font-size: 12px; color: #9ca3af; display: block; margin-top: 4px; }
|
||||
|
||||
.focus-card {
|
||||
|
||||
Reference in New Issue
Block a user