From 2e751f8536afd00b5e9cf7a297a434fc0d7c3de7 Mon Sep 17 00:00:00 2001 From: Pine Date: Sat, 5 Sep 2026 11:18:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5=20Steps=20=E6=8C=89?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E7=94=9F=E6=88=90=E9=98=B6=E6=AE=B5=E5=BA=8F?= =?UTF-8?q?=E5=88=97=EF=BC=9B=E6=96=B0=E5=A2=9E=E7=AD=BE=E7=BA=A6/?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E6=8E=A5=E5=8F=97/=E5=A9=89=E6=8B=92?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=EF=BC=9BMyTasks=20=E9=80=82=E9=85=8D?= =?UTF-8?q?=E5=BE=85=E7=A1=AE=E8=AE=A4/=E5=B7=B2=E7=AD=BE=E7=BA=A6?= =?UTF-8?q?=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- console/src/api/modules/hall.ts | 5 + console/src/pages/portal/Opc/MyTasks.tsx | 9 +- .../src/pages/portal/Opc/TaskDetailPage.tsx | 119 +++++++++++++++--- 3 files changed, 111 insertions(+), 22 deletions(-) diff --git a/console/src/api/modules/hall.ts b/console/src/api/modules/hall.ts index 67fc83e..8766fed 100644 --- a/console/src/api/modules/hall.ts +++ b/console/src/api/modules/hall.ts @@ -214,6 +214,11 @@ export const hallApi = { bidTask: (id: string, body: { quote: number; plan: string; material?: string; deliverDays?: number; team?: unknown[]; cases?: unknown[]; commit?: Record }) => request<{ ok: boolean; item?: TaskBidRow }>(`/hall/tasks/${encodeURIComponent(id)}/bid`, { method: "POST", body: JSON.stringify(body) }), doingTask: (id: string) => request<{ ok: boolean }>(`/hall/tasks/${encodeURIComponent(id)}/doing`, { method: "POST" }), + /** v3.5:签约托管(发布方 claimed → signed)与确认接受(接单方 assigned → claimed)(0060) */ + signTask: (id: string, body?: { payType?: string }) => + request<{ ok: boolean; item?: HallTask }>(`/hall/tasks/${encodeURIComponent(id)}/sign`, { method: "POST", body: JSON.stringify(body ?? {}) }), + acceptInviteTask: (id: string) => + request<{ ok: boolean; item?: HallTask }>(`/hall/tasks/${encodeURIComponent(id)}/accept-invite`, { method: "POST" }), completeTask: (id: string) => request<{ ok: boolean }>(`/hall/tasks/${encodeURIComponent(id)}/complete`, { method: "POST" }), deliverTask: (id: string, body?: { attachments?: string[]; note?: string; milestoneId?: string }) => request<{ ok: boolean; item?: HallTask }>(`/hall/tasks/${encodeURIComponent(id)}/deliver`, { method: "POST", body: JSON.stringify(body ?? {}) }), diff --git a/console/src/pages/portal/Opc/MyTasks.tsx b/console/src/pages/portal/Opc/MyTasks.tsx index 78a9119..b13d3a2 100644 --- a/console/src/pages/portal/Opc/MyTasks.tsx +++ b/console/src/pages/portal/Opc/MyTasks.tsx @@ -13,10 +13,11 @@ const TASK_STATUS: Record = { claimed: "已接单", doing: "履约中", delivered: "待验收", completed: "已完成", cancelled: "已取消", }; const CLAIM_STATUS: Record = { - claimed: { text: "已接单", color: "orange" }, + claimed: { text: "待签约", color: "orange" }, + signed: { text: "已签约", color: "purple" }, doing: { text: "进行中", color: "orange" }, delivered: { text: "已交付·待验收", color: "blue" }, - assigned: { text: "已指派", color: "blue" }, + assigned: { text: "待确认", color: "blue" }, registered: { text: "已报名", color: "purple" }, recommended: { text: "被推荐", color: "cyan" }, completed: { text: "已完成", color: "green" }, @@ -72,6 +73,10 @@ function OpcMyTasksBody() { className={styles.item} actions={[ , + task.claimStatus === "assigned" && , + task.claimStatus === "assigned" && act(task.id, () => hallApi.withdrawTask(task.id), "已婉拒任务")}> + + , task.claimStatus === "claimed" && , task.claimStatus === "claimed" && act(task.id, () => hallApi.withdrawTask(task.id), "已退出任务")}> diff --git a/console/src/pages/portal/Opc/TaskDetailPage.tsx b/console/src/pages/portal/Opc/TaskDetailPage.tsx index 546b7e4..58ffb3b 100644 --- a/console/src/pages/portal/Opc/TaskDetailPage.tsx +++ b/console/src/pages/portal/Opc/TaskDetailPage.tsx @@ -25,16 +25,65 @@ const STATUS_TAG: Record = { pending: { text: "待审核", color: "gold" }, review: { text: "审核中", color: "gold" }, published: { text: "招募中", color: "green" }, - claimed: { text: "已接单", color: "orange" }, + assigned: { text: "待确认", color: "geekblue" }, + claimed: { text: "待签约", color: "orange" }, + signed: { text: "已签约", color: "purple" }, doing: { text: "履约中", color: "processing" }, delivered: { text: "待验收", color: "blue" }, completed: { text: "已完成", color: "success" }, cancelled: { text: "已取消", color: "default" }, }; const TL_COLOR: Record = { - publish: "blue", claim: "orange", doing: "green", deliver: "cyan", + publish: "blue", claim: "orange", sign: "purple", doing: "green", deliver: "cyan", accept: "green", reject: "red", settle: "purple", invoice: "gold", cancel: "red", }; + +/** 按任务模式的阶段序列(领取/确定方式不同) */ +function modeSteps(mode?: string): { key: string; title: string }[] { + switch (mode) { + case "bid": + return [ + { key: "published", title: "发布" }, + { key: "bidding", title: "投标" }, + { key: "claimed", title: "定标" }, + { key: "signed", title: "签约" }, + { key: "doing", title: "开始" }, + { key: "delivered", title: "交付" }, + { key: "completed", title: "验收" }, + ]; + case "register": + return [ + { key: "published", title: "发布" }, + { key: "registering", title: "报名" }, + { key: "claimed", title: "选定" }, + { key: "signed", title: "签约" }, + { key: "doing", title: "开始" }, + { key: "delivered", title: "交付" }, + { key: "completed", title: "验收" }, + ]; + case "assign": + case "recommend": + return [ + { key: "published", title: "发布" }, + { key: "assigned", title: mode === "assign" ? "指派" : "邀约" }, + { key: "claimed", title: "确认" }, + { key: "signed", title: "签约" }, + { key: "doing", title: "开始" }, + { key: "delivered", title: "交付" }, + { key: "completed", title: "验收" }, + ]; + case "grab": + default: + return [ + { key: "published", title: "发布" }, + { key: "claimed", title: "抢单" }, + { key: "signed", title: "签约" }, + { key: "doing", title: "开始" }, + { key: "delivered", title: "交付" }, + { key: "completed", title: "验收" }, + ]; + } +} const PRICE: Record = { fixed: "一口价", hourly: "按小时", unit: "按件", revshare: "分成" }; const PAY: Record = { escrow: "平台托管", stage: "阶段托管", offline: "线下支付" }; @@ -391,6 +440,13 @@ function RecruitManageView({ task, claims, bids, busy, onSelect, onWin }: { )} + {(mode === "assign" || mode === "recommend") && ( + + + + )} ); } @@ -414,11 +470,11 @@ function ServiceFlowView({ task, busy, onAct, onOpenDeliver, onOpenReject }: { const invoices = (task as { invoices?: Array<{ id: string; amount: number; status: string; created_at: string; issuer_name: string }> }).invoices ?? []; const participants = (task as { participants?: { publisher?: { id: string; name: string }; claimer?: { id: string; name: string } | null } }).participants; const claimer = participants?.claimer ?? null; - const stepIndex = ["published", "claimed", "doing", "delivered", "completed"].indexOf(status); + const stepIndex = modeSteps(task.mode).findIndex((s) => s.key === status); return ( <> - {/* 阶段进度 */} + {/* 阶段进度(按模式) */} {status === "cancelled" ? (
任务已取消,流程终止
@@ -427,13 +483,21 @@ function ServiceFlowView({ task, busy, onAct, onOpenDeliver, onOpenReject }: { size="small" current={Math.max(stepIndex - 1, 0)} status={status === "completed" ? "finish" : "process"} - items={[ - { title: "发布", description: task.created_at ? task.created_at.slice(0, 16).replace("T", " ") : "" }, - { title: "接单", description: task.claimed_at ? task.claimed_at.slice(0, 16).replace("T", " ") : "等待接单" }, - { title: "履约", description: task.doing_at ? task.doing_at.slice(0, 16).replace("T", " ") : "—" }, - { title: "交付", description: task.deliver_at ? task.deliver_at.slice(0, 16).replace("T", " ") : "—" }, - { title: "完成", description: task.accepted_at ? task.accepted_at.slice(0, 16).replace("T", " ") : "—" }, - ]} + items={modeSteps(task.mode).map((s, i) => { + const times: Record = { + published: task.created_at || "", + claimed: task.claimed_at || "", + signed: task.signed_at || "", + doing: task.doing_at || "", + delivered: task.deliver_at || "", + completed: task.accepted_at || "", + assigned: task.assigned_at || "", + }; + return { + title: s.title, + description: times[s.key] ? times[s.key].slice(0, 16).replace("T", " ") : (i === 0 ? "—" : "待进行"), + }; + })} /> )}
@@ -490,14 +554,34 @@ function ServiceFlowView({ task, busy, onAct, onOpenDeliver, onOpenReject }: { {/* 操作区 */}
- {myRole === "claimer" && status === "claimed" && ( + {myRole === "claimer" && status === "assigned" && ( <> - - onAct(() => hallApi.withdrawTask(task.id), "已退出任务")}> - + + onAct(() => hallApi.withdrawTask(task.id), "已婉拒任务")}> + )} + {myRole === "publisher" && status === "assigned" && ( + 已发出{task.mode === "assign" ? "指派" : "邀约"},等待对方确认 + )} + {myRole === "publisher" && status === "claimed" && ( + <> + + onAct(() => hallApi.taskCancel(task.id), "任务已取消")}> + + + + )} + {myRole === "claimer" && status === "claimed" && ( + 任务已锁定,等待发包方签约托管 + )} + {myRole === "claimer" && status === "signed" && ( + + )} + {myRole === "publisher" && status === "signed" && ( + 已签约托管,等待接单方开始 + )} {myRole === "claimer" && status === "doing" && ( )} @@ -516,11 +600,6 @@ function ServiceFlowView({ task, busy, onAct, onOpenDeliver, onOpenReject }: { {myRole === "publisher" && status === "doing" && ( 履约中,等待接单方交付 )} - {myRole === "publisher" && ["claimed", "doing"].includes(status) && ( - onAct(() => hallApi.taskCancel(task.id), "任务已取消")}> - - - )}