工艺节点树: 新增结束分支(终止)接口 + 前端🏁结束分支按钮 + WIP可无下游完结
This commit is contained in:
@ -102,6 +102,24 @@ async def complete_task_endpoint(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 核心业务 0:结束分支(终止当前节点,不创建下游)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@router.post("/{task_id}/end", response_model=TaskResponse)
|
||||||
|
async def end_task_endpoint(
|
||||||
|
task_id: str,
|
||||||
|
operator_id: str | None = Query(None),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
**结束当前分支:标记任务为 COMPLETED,不创建下游任务。**
|
||||||
|
|
||||||
|
用于工人认为工序已完结、无需转交下一人的场景。
|
||||||
|
"""
|
||||||
|
return await task_service.end_task(db, uuid.UUID(task_id), operator_id)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 核心业务 1:确认接收 (PENDING → WIP)
|
# 核心业务 1:确认接收 (PENDING → WIP)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|||||||
@ -223,6 +223,36 @@ async def get_all_tasks(
|
|||||||
return TaskListResponse(tasks=all_tasks, total=len(all_tasks))
|
return TaskListResponse(tasks=all_tasks, total=len(all_tasks))
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 核心业务 0:结束分支(终止当前节点)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
async def end_task(
|
||||||
|
db: AsyncSession, task_id: uuid.UUID, operator_id: str | None = None
|
||||||
|
) -> TaskResponse:
|
||||||
|
"""
|
||||||
|
结束当前分支:标记任务为 COMPLETED,不创建下游任务。
|
||||||
|
用于工序已完结、无需转交下一人的场景。
|
||||||
|
"""
|
||||||
|
task = await _get_task_or_404(db, task_id)
|
||||||
|
|
||||||
|
if task.status == TASK_STATUS_COMPLETED:
|
||||||
|
raise HTTPException(status_code=409, detail="此分支已经结束")
|
||||||
|
if task.status == TASK_STATUS_PENDING:
|
||||||
|
raise HTTPException(status_code=409, detail="请先接收任务再结束分支")
|
||||||
|
|
||||||
|
now = get_beijing_time()
|
||||||
|
task.status = TASK_STATUS_COMPLETED
|
||||||
|
task.completed_at = now
|
||||||
|
|
||||||
|
await _create_task_log(db, task_id, action_type="end",
|
||||||
|
operator_id=operator_id, remark=f"分支「{task.task_name}」已终止(无下游)")
|
||||||
|
|
||||||
|
await db.commit()
|
||||||
|
await db.refresh(task)
|
||||||
|
return _to_response(task)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 核心业务 1:确认接收 (PENDING → WIP)
|
# 核心业务 1:确认接收 (PENDING → WIP)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|||||||
@ -140,6 +140,10 @@
|
|||||||
@tap="handleTaskAction({ task: currentTask, type: 'record' })">
|
@tap="handleTaskAction({ task: currentTask, type: 'record' })">
|
||||||
📝 记录/拍照
|
📝 记录/拍照
|
||||||
</button>
|
</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"
|
<button v-if="currentTask.status === 'PENDING'" class="footer-btn footer-receive"
|
||||||
@tap="handleTaskAction({ task: currentTask, type: 'receive' })">
|
@tap="handleTaskAction({ task: currentTask, type: 'receive' })">
|
||||||
✅ 接收任务
|
✅ 接收任务
|
||||||
@ -725,6 +729,7 @@ export default {
|
|||||||
async handleTaskAction({ task, type, record }) {
|
async handleTaskAction({ task, type, record }) {
|
||||||
if (type === "record") { this.openRecordPopup(task); return; }
|
if (type === "record") { this.openRecordPopup(task); return; }
|
||||||
if (type === "deleteRecord") { this.doDeleteRecord(record); return; }
|
if (type === "deleteRecord") { this.doDeleteRecord(record); return; }
|
||||||
|
if (type === "end") { this.confirmEndBranch(task); return; }
|
||||||
|
|
||||||
// 转交时异步拉取真实工序和用户数据
|
// 转交时异步拉取真实工序和用户数据
|
||||||
if (type === "transfer") {
|
if (type === "transfer") {
|
||||||
@ -747,6 +752,23 @@ export default {
|
|||||||
handleViewRecords(task) {
|
handleViewRecords(task) {
|
||||||
uni.navigateTo({ url: `/pages/scan/records?taskId=${task.id}` });
|
uni.navigateTo({ url: `/pages/scan/records?taskId=${task.id}` });
|
||||||
},
|
},
|
||||||
|
confirmEndBranch(task) {
|
||||||
|
uni.showModal({
|
||||||
|
title: "结束当前分支",
|
||||||
|
content: `确定结束「${task.task_name}」吗?\n此分支将标记为已完成,不会创建下游任务。`,
|
||||||
|
confirmText: "确定结束",
|
||||||
|
confirmColor: "#f59e0b",
|
||||||
|
success: (res) => { if (res.confirm) this.doEndBranch(task); },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async doEndBranch(task) {
|
||||||
|
try {
|
||||||
|
const opId = this.currentUsername || this.currentUserId;
|
||||||
|
await post(`/tasks/${task.id}/end?operator_id=${encodeURIComponent(opId)}`);
|
||||||
|
uni.showToast({ title: "分支已结束", icon: "success" });
|
||||||
|
this.doQuery(this.product.serial_number);
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
onSwiperChange(e) {
|
onSwiperChange(e) {
|
||||||
this.swiperCurrent = e.detail.current;
|
this.swiperCurrent = e.detail.current;
|
||||||
},
|
},
|
||||||
@ -996,6 +1018,7 @@ export default {
|
|||||||
.footer-record { background: #eff6ff; color: #2563eb; }
|
.footer-record { background: #eff6ff; color: #2563eb; }
|
||||||
.footer-receive { background: #dbeafe; color: #1d4ed8; }
|
.footer-receive { background: #dbeafe; color: #1d4ed8; }
|
||||||
.footer-reject { background: #fce4ec; color: #dc2626; }
|
.footer-reject { background: #fce4ec; color: #dc2626; }
|
||||||
|
.footer-end { background: #fef3c7; color: #b45309; }
|
||||||
|
|
||||||
/* Picker 选择器 */
|
/* Picker 选择器 */
|
||||||
.form-item { margin: 10px 0; }
|
.form-item { margin: 10px 0; }
|
||||||
|
|||||||
Reference in New Issue
Block a user