重构: PENDING恢复驳回按钮 + 转交弹窗升级多分支裂变(无限加分支)

This commit is contained in:
2026-08-05 15:57:20 +08:00
parent c2db89d9d5
commit 30d35d7fa2

View File

@ -103,6 +103,10 @@
@tap="handleTaskAction({ task: activeTask, type: 'receive' })">
接收任务
</button>
<button v-if="activeTask.status === 'PENDING'" class="footer-btn footer-reject"
@tap="handleTaskAction({ task: activeTask, type: 'reject' })">
驳回任务
</button>
</view>
</template>
@ -235,50 +239,69 @@
<button class="btn-danger" :disabled="actionLoading || !rejectReason.trim()" @tap="doReject">确认驳回</button>
</view>
</template>
<!-- 转交 -->
<!-- 转交支持多分支裂变 -->
<template v-if="actionPopup.type === 'transfer'">
<text class="popup-title">完工转交</text>
<text class="popup-title">完工转交 · 裂变派发</text>
<!-- 工序选择 (Picker) -->
<view class="form-item">
<text class="form-label">下一道工序 <text class="required">*</text></text>
<picker mode="selector" :range="processOptions" @change="onProcessChange">
<view class="picker-value">
<text :class="transferForm.next_task_name ? '' : 'picker-placeholder'">
{{ transferForm.next_task_name || '请选择下一道工序' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
<!-- 分支列表 -->
<view v-for="(branch, idx) in transferForm.branches" :key="idx" class="branch-item">
<view class="branch-header">
<text class="branch-label">分支 {{ idx + 1 }}</text>
<text v-if="transferForm.branches.length > 1" class="branch-del"
@tap="removeBranch(idx)">🗑 删除</text>
</view>
<!-- 工序选择 -->
<view class="form-item">
<text class="form-label">工序 <text class="required">*</text></text>
<picker mode="selector" :range="processOptions" @change="(e) => onBranchProcessChange(idx, e)">
<view class="picker-value">
<text :class="branch.task_name ? '' : 'picker-placeholder'">
{{ branch.task_name || '请选择工序' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
</view>
<!-- 接收人入库分支不需要 -->
<view v-if="!isBranchWarehouse(idx)" class="form-item">
<text class="form-label">接收人 <text class="required">*</text></text>
<picker mode="selector" :range="userOptions" range-key="name" @change="(e) => onBranchUserChange(idx, e)">
<view class="picker-value">
<text :class="branchUserName(idx) ? '' : 'picker-placeholder'">
{{ branchUserName(idx) || '请选择接收人' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
</view>
<view v-if="isBranchWarehouse(idx)" class="warehouse-hint">
📦 入库分支 直接归档无需指定接收人
</view>
</view>
<!-- 接收人选择仅非入库时显示 -->
<view v-if="!isWarehouse" class="form-item">
<text class="form-label">接收人 <text class="required">*</text></text>
<picker mode="selector" :range="userOptions" range-key="name" @change="onUserChange">
<view class="picker-value">
<text :class="selectedUserName ? '' : 'picker-placeholder'">
{{ selectedUserName || '请选择接收人' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
<!-- 添加分支 -->
<button class="btn-add-branch" @tap="addBranch"> 添加并行分支</button>
<!-- 备注 -->
<input v-model="transferForm.note" class="popup-input" placeholder="交接备注(选填)" style="margin-top:12px;" />
<!-- 预览 -->
<view v-if="transferForm.branches.some(b => b.task_name)" class="preview-hint">
将创建 {{ transferForm.branches.length }} 个并行任务
<text v-for="(b, i) in transferForm.branches" :key="i" style="display:block;margin-top:2px;">
{{ i + 1 }}. {{ b.task_name || '' }} {{ branchUserName(i) || (isBranchWarehouse(i) ? '仓库' : '未选') }}
</text>
</view>
<!-- 入库提示 -->
<view v-if="isWarehouse" class="warehouse-hint">
📦 该任务将直接入库任务状态变为已入库
</view>
<input v-model="transferForm.note" class="popup-input" placeholder="交接备注(选填)" />
<view v-if="transferForm.next_task_name" class="preview-hint">
{{ isWarehouse ? '产品将入库并从个人待办中移除' : '将创建 1 ' + transferForm.next_task_name + '任务指派给 ' + (selectedUserName || '') }}
</view>
<view class="popup-btns">
<button class="btn-cancel" @tap="closeActionPopup">取消</button>
<button class="btn-primary" :disabled="actionLoading || !transferForm.next_task_name || (!isWarehouse && !transferForm.assignees.length)" @tap="doTransfer">
{{ isWarehouse ? '📦 确认入库' : '确认转交' }}
<button class="btn-primary"
:disabled="actionLoading || !transferForm.branches.some(b => b.task_name) || !canSubmitTransfer"
@tap="doTransfer">
{{ actionLoading ? '提交中...' : '确认转交 (' + transferForm.branches.length + '分支)' }}
</button>
</view>
</template>
@ -334,7 +357,7 @@ export default {
actionPopup: { visible: false, type: "", task: null },
actionLoading: false,
rejectReason: "",
transferForm: { next_task_name: "", assignees: [], selectedUserId: "", warehouse: false, note: "" },
transferForm: { branches: [{ task_name: "", assignee_id: "" }], note: "" },
};
},
computed: {
@ -346,12 +369,12 @@ export default {
if (!this.currentUser) return true;
return this.currentUser.username === this.recordPopup.task.assignee_id;
},
selectedUserName() {
const u = this.userOptions.find(u => u.id === this.transferForm.selectedUserId);
return u ? u.name : "";
},
isWarehouse() {
return this.transferForm.next_task_name === "🏭 入库 (virtual_warehouse)";
canSubmitTransfer() {
return this.transferForm.branches.every(b => {
if (!b.task_name) return false;
if (b.task_name === "🏭 入库 (virtual_warehouse)") return true;
return !!b.assignee_id;
});
},
activeTask() {
// 查找树中第一个 WIP 或 PENDING 任务作为底部 Footer 的操作目标
@ -658,7 +681,7 @@ export default {
this.actionPopup = { visible: true, type, task };
this.rejectReason = "";
this.transferForm = { next_task_name: "", assignees: [], selectedUserId: "", warehouse: false, note: "" };
this.transferForm = { branches: [{ task_name: "", assignee_id: "" }], note: "" };
},
handleViewRecords(task) {
uni.navigateTo({ url: `/pages/scan/records?taskId=${task.id}` });
@ -683,28 +706,46 @@ export default {
this.doQuery(this.product.serial_number);
} catch {} finally { this.actionLoading = false; }
},
onProcessChange(e) {
this.transferForm.next_task_name = this.processOptions[e.detail.value];
},
onUserChange(e) {
const user = this.userOptions[e.detail.value];
if (user) {
this.transferForm.selectedUserId = user.id;
this.transferForm.assignees = [user.id];
// ---- 多分支操作 ----
addBranch() {
if (this.transferForm.branches.length >= 5) {
uni.showToast({ title: "最多 5 个并行分支", icon: "none" }); return;
}
this.transferForm.branches.push({ task_name: "", assignee_id: "" });
},
removeBranch(idx) {
if (this.transferForm.branches.length <= 1) return;
this.transferForm.branches.splice(idx, 1);
},
onBranchProcessChange(idx, e) {
this.transferForm.branches[idx].task_name = this.processOptions[e.detail.value];
// 切换工序时清空接收人
this.transferForm.branches[idx].assignee_id = "";
},
onBranchUserChange(idx, e) {
const user = this.userOptions[e.detail.value];
if (user) this.transferForm.branches[idx].assignee_id = user.id;
},
isBranchWarehouse(idx) {
return this.transferForm.branches[idx].task_name === "🏭 入库 (virtual_warehouse)";
},
branchUserName(idx) {
const u = this.userOptions.find(u => u.id === this.transferForm.branches[idx].assignee_id);
return u ? u.name : "";
},
async doTransfer() {
this.actionLoading = true;
const finalAssignees = this.isWarehouse
? ["virtual_warehouse"]
: [...this.transferForm.assignees];
const branches = this.transferForm.branches.filter(b => b.task_name);
try {
await post(`/tasks/${this.actionPopup.task.id}/transfer`, {
next_assignees: finalAssignees,
next_task_name: this.transferForm.next_task_name.trim(),
next_tasks: branches.map(b => ({
task_name: b.task_name,
assignees: b.task_name === "🏭 入库 (virtual_warehouse)"
? ["virtual_warehouse"] : [b.assignee_id],
})),
note: this.transferForm.note.trim() || undefined,
});
uni.showToast({ title: this.isWarehouse ? "已入库" : "转交成功", icon: "success" });
uni.showToast({ title: `已派发 ${branches.length} 个分支`, icon: "success" });
this.closeActionPopup();
this.doQuery(this.product.serial_number);
} catch {} finally { this.actionLoading = false; }
@ -793,6 +834,14 @@ export default {
.warehouse-hint { font-size: 13px; background: #ede9fe; color: #7c3aed; padding: 10px 14px; border-radius: 10px; margin: 8px 0; text-align: center; }
.preview-hint { font-size: 12px; background: #f0fdf4; color: #16a34a; padding: 8px 10px; border-radius: 8px; margin: 6px 0; }
/* 多分支裂变 */
.branch-item { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 10px; padding: 12px; margin-bottom: 10px; }
.branch-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.branch-label { font-size: 13px; font-weight: 700; color: #374151; }
.branch-del { font-size: 12px; color: #ef4444; font-weight: 600; padding: 2px 8px; }
.btn-add-branch { width: 100%; height: 40px; border: 2px dashed #93c5fd; border-radius: 10px; background: #eff6ff; color: #2563eb; font-size: 14px; font-weight: 700; line-height: 40px; margin: 4px 0; }
.btn-add-branch::after { border: none; }
/* Picker & 表单 */
.field-label { font-size: 14px; font-weight: 600; color: #374151; margin-top: 10px; margin-bottom: 4px; }
.required { color: #ef4444; }
@ -826,6 +875,7 @@ export default {
.footer-transfer { background: #dcfce7; color: #16a34a; }
.footer-record { background: #eff6ff; color: #2563eb; }
.footer-receive { background: #dbeafe; color: #1d4ed8; }
.footer-reject { background: #fce4ec; color: #dc2626; }
/* Picker 选择器 */
.form-item { margin: 10px 0; }