Archived
fix(engine): 打不回的验收卡不摆那颗钮 —— 全 auto 的放行步没有模型能接住那句意见
用户:「评审放行与落账确认这一步的验收卡不应该有重做的选项,因为这里都没有接入 ai」。 W05 的 T_release 整个由配方跑完(guide 是空串,不问模型),件又是只读的 —— 打回之后没有任何东西能接住那句意见:配方只会原样再提交一次,撞在「打回之后台账没动过 就不许再提交」那道闸上,连撞两拍落成一张「连续撞在同一条判据上」的兜底卡。 一颗按下去只会把单子推进死胡同的钮,不如不摆(墙要配路;没有路就别立那扇门)。 · 契约:AcceptanceWorkOrder.redoable(不写 = 打得回,老卡老行为); · 装配层按放行那一步的声明现算 —— releaseRedoable 纯函数(有 auto 且 guide 空 → 打不回; 半 auto 与模型驱动照旧打得回,那儿真有模型会读意见),与 decisionCardFace / executionCardVariant 同一种做法、同一个测试位; · 卡面收起理由框与那颗钮,换一句真路:「这一步只落账,退不回上一步 —— 有异议就别确认, 去终止这一单重开」; · **服务端才是门**:redoable:false 的卡收到非 pass 的回传当场拒(redo-unavailable), 直调 API 绕不过;拒回把真路指出来。 S3 结论审核那张审核卡不受影响 —— 它的重做真会带着意见重判(昨夜那一轮)。 测:card-rules 3 条 + releaseRedoable 4 条,两处变异验证;e2e w05 5/5 released、被拒 0。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -1834,6 +1834,18 @@
|
||||
.dossiers 三列网格(1fr·76px·1fr),中间列是实体动画道 —— ⇄ 徽呼吸 + 双点绕徽轨道
|
||||
对转(sw-orbit),总线虚线在道里连两卡;窄屏单列收道。浏览器两帧相位实证。
|
||||
|
||||
- **打不回的验收卡就别摆那颗钮**(2026-09-01 用户定:「评审放行与落账确认这一步的验收卡不应该
|
||||
有重做的选项,因为这里都没有接入 ai」):W05 的 T_release 整个由配方跑完(`guide` 是空串,
|
||||
不问模型),件又是只读的 —— 打回之后**没有任何东西能接住那句意见**:配方只会原样再提交一次,
|
||||
撞在「打回之后台账没动过就不许再提交」那道闸上,连撞两拍落成一张「连续撞在同一条判据上」的
|
||||
兜底卡。**一颗按下去只会把单子推进死胡同的钮,不如不摆**(墙要配路;没有路就别立那扇门)。
|
||||
契约加 `AcceptanceWorkOrder.redoable`(不写 = 打得回,老卡老行为);装配层按放行那一步的声明现算
|
||||
(`releaseRedoable` 纯函数:有 `auto` 且 `guide` 是空串 → 打不回;半 auto 与模型驱动照旧打得回);
|
||||
卡面收起理由框与那颗钮,换一句**真路**:「这一步只落账,退不回上一步 —— 有异议就别确认,
|
||||
去终止这一单重开」;**服务端才是门**:`redoable === false` 的卡收到非 pass 的回传当场拒
|
||||
(`redo-unavailable`),直调 API 也绕不过。两处变异验证;S3 结论审核那张审核卡不受影响 ——
|
||||
它的重做现在真会带着意见重判(见上一条)。
|
||||
|
||||
- **答得太快就没人再叫醒它 —— stale 那条守卫补最后一下**(2026-09-01,一夜之内撞停两次,
|
||||
第二次直接把我自己的验证卡住 15 分钟):卡发出 **270ms** 后人就答了,而**把 `lastProgressAt`
|
||||
往前挪的那一拍,正是发出这张卡的那一拍** —— 它不可能消费一条当时还不存在的回传,
|
||||
|
||||
@@ -28,6 +28,8 @@ export type SubmitForReleaseInput = {
|
||||
readonly criteria: readonly CriterionCheck[];
|
||||
/** 人工验收清单(剧本声明,固定):卡上一条一枚开关,全部达标才能通过 */
|
||||
readonly checklist?: ReadonlyArray<string>;
|
||||
/** 这张卡打回得了吗 —— `false` = 打回之后没人接得住(见契约 `AcceptanceWorkOrder.redoable`) */
|
||||
readonly redoable?: boolean;
|
||||
/** 报告可修订:AI 拟值已填的表单字段 */
|
||||
readonly fields?: ReadonlyArray<Record<string, unknown>>;
|
||||
/**
|
||||
|
||||
@@ -93,6 +93,8 @@ export type CardGateway = {
|
||||
}>;
|
||||
/** 人工验收清单 —— 原样带上卡 */
|
||||
checklist?: ReadonlyArray<string> | undefined;
|
||||
/** 这张卡打回得了吗(`false` = 那颗钮不摆)—— 端口不声明它,值就在装配层被丢掉,这行不能省 */
|
||||
redoable?: boolean | undefined;
|
||||
/**
|
||||
* 可修订字段 —— 验收报告「AI 已填好、可编辑」改的就是它们(带 `ai` 拟值与 `ledger` 落回地址)。
|
||||
* **端口不声明它,值就在装配层被丢掉**:上游一路传下来,而 TypeScript 对条件展开
|
||||
|
||||
@@ -1225,6 +1225,7 @@ export declare const taskDetailSchema: z.ZodObject<{
|
||||
because: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
passLabel: z.ZodOptional<z.ZodString>;
|
||||
redoable: z.ZodOptional<z.ZodBoolean>;
|
||||
id: z.core.$ZodBranded<z.ZodString, "WorkOrderId", "out">;
|
||||
at: z.ZodISODateTime;
|
||||
title: z.ZodString;
|
||||
@@ -1819,6 +1820,7 @@ export declare const taskDetailSchema: z.ZodObject<{
|
||||
because: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
passLabel: z.ZodOptional<z.ZodString>;
|
||||
redoable: z.ZodOptional<z.ZodBoolean>;
|
||||
id: z.core.$ZodBranded<z.ZodString, "WorkOrderId", "out">;
|
||||
at: z.ZodISODateTime;
|
||||
title: z.ZodString;
|
||||
@@ -3155,6 +3157,7 @@ export declare const taskDetailRoute: {
|
||||
because: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
passLabel: z.ZodOptional<z.ZodString>;
|
||||
redoable: z.ZodOptional<z.ZodBoolean>;
|
||||
id: z.core.$ZodBranded<z.ZodString, "WorkOrderId", "out">;
|
||||
at: z.ZodISODateTime;
|
||||
title: z.ZodString;
|
||||
@@ -3749,6 +3752,7 @@ export declare const taskDetailRoute: {
|
||||
because: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
passLabel: z.ZodOptional<z.ZodString>;
|
||||
redoable: z.ZodOptional<z.ZodBoolean>;
|
||||
id: z.core.$ZodBranded<z.ZodString, "WorkOrderId", "out">;
|
||||
at: z.ZodISODateTime;
|
||||
title: z.ZodString;
|
||||
|
||||
+2
@@ -1208,6 +1208,7 @@ export declare const acceptanceWorkOrderSchema: z.ZodObject<{
|
||||
because: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
passLabel: z.ZodOptional<z.ZodString>;
|
||||
redoable: z.ZodOptional<z.ZodBoolean>;
|
||||
id: z.core.$ZodBranded<z.ZodString, "WorkOrderId", "out">;
|
||||
at: z.ZodISODateTime;
|
||||
title: z.ZodString;
|
||||
@@ -1803,6 +1804,7 @@ export declare const workOrderSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||
because: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
passLabel: z.ZodOptional<z.ZodString>;
|
||||
redoable: z.ZodOptional<z.ZodBoolean>;
|
||||
id: z.core.$ZodBranded<z.ZodString, "WorkOrderId", "out">;
|
||||
at: z.ZodISODateTime;
|
||||
title: z.ZodString;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* **打不回的放行卡**(2026-09-01 用户定:「评审放行与落账确认这一步的验收卡不应该有重做的选项,
|
||||
* 因为这里都没有接入 ai」)。
|
||||
*
|
||||
* 判据只有一条:这一步整个由配方跑完、一个字的 `guide` 都没有 —— 那时打回的意见没有模型会去读,
|
||||
* 配方只会原样再提交一次,撞在「打回之后台账没动过就不许再提交」那道闸上,最后落成一张
|
||||
* 「连续撞在同一条判据上」的兜底卡。**一颗按下去只会把单子推进死胡同的钮,不如不摆。**
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { releaseRedoable } from '../wiring/engines.ts';
|
||||
|
||||
describe('放行卡打回得了吗', () => {
|
||||
it('全 auto(有配方、guide 是空串)→ 打不回', () => {
|
||||
expect(releaseRedoable({ auto: { do: [] }, guide: '' })).toBe(false);
|
||||
expect(releaseRedoable({ auto: { do: [] }, guide: ' ' })).toBe(false);
|
||||
});
|
||||
|
||||
it('半 auto(guide 非空)→ 照旧打得回:那儿真有模型会读那句意见', () => {
|
||||
expect(releaseRedoable({ auto: { do: [] }, guide: '按意见修订台账,改完重新提交' })).toBe(true);
|
||||
});
|
||||
|
||||
it('模型驱动(压根没声明配方)→ 照旧打得回', () => {
|
||||
expect(releaseRedoable({ guide: '把这一签下去会发生什么摆清楚' })).toBe(true);
|
||||
});
|
||||
|
||||
it('认不出那一步(没声明人工验收清单的老活动)→ 老行为', () => {
|
||||
expect(releaseRedoable(undefined)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -256,6 +256,22 @@ export function createOnlyViolation(
|
||||
* 执行工单」)—— 配方递下来的 `accept` 在这儿被静默丢掉,对话区照旧摆「执行工单」。
|
||||
* 配方那一头与前端那一头都有测,唯独**真发卡这条路**上没有:面孔要在这里断言。
|
||||
*/
|
||||
/**
|
||||
* **这张放行卡打回得了吗**(2026-09-01 用户定:「评审放行与落账确认这一步的验收卡不应该有
|
||||
* 重做的选项,因为这里都没有接入 ai」)。
|
||||
*
|
||||
* 打不回的判据只有一条:**这一步整个由配方跑完、一个字的 `guide` 都没有**(全 auto = 不问模型)。
|
||||
* 那时打回的意见没有模型会去读,配方只会原样再提交一次,撞在「打回之后台账没动过就不许再提交」
|
||||
* 那道闸上,连撞两拍之后落成一张「连续撞在同一条判据上」的兜底卡 —— 一颗按下去只会把单子
|
||||
* 推进死胡同的钮,不如不摆(墙要配路;没有路就别立那扇门)。有异议的人还有真路:别签,终止重开。
|
||||
*
|
||||
* 半 auto(`guide` 非空)与模型驱动的放行步照旧打得回 —— 那儿真有模型会读那句意见。
|
||||
* 认不出那一步(没声明人工验收清单的老活动)也照旧 —— 不写这面旗就是老行为。
|
||||
*/
|
||||
export function releaseRedoable(step: { readonly auto?: unknown; readonly guide?: string } | undefined): boolean {
|
||||
return !(step?.auto !== undefined && (step.guide ?? '').trim() === '');
|
||||
}
|
||||
|
||||
export function executionCardVariant(fromCard: unknown, fromStep: unknown): 'data' | 'accept' | undefined {
|
||||
for (const value of [fromStep, fromCard]) {
|
||||
if (value === 'data' || value === 'accept') return value;
|
||||
@@ -1826,14 +1842,27 @@ export function wireEngines(deps: EngineWiringDeps): EngineWiring {
|
||||
* 终局验收卡的人工验收清单:剧本里声明了 `acceptanceChecklist` 且不是审核卡那一步的(放行那一步)。
|
||||
* 人核这份工单要走的步骤是固定的,由活动声明、这里带上卡,不靠模型抄。
|
||||
*/
|
||||
const releaseChecklist = manifest.assets.businessModel.steps?.find(
|
||||
const releaseStepDecl = manifest.assets.businessModel.steps?.find(
|
||||
(st) => st.acceptanceChecklist !== undefined && st.acceptanceChecklist.length > 0 && st.cardVariant !== 'review',
|
||||
)?.acceptanceChecklist;
|
||||
);
|
||||
const releaseChecklist = releaseStepDecl?.acceptanceChecklist;
|
||||
/*
|
||||
* **打回之后没人接得住的,就别摆那颗钮**(2026-09-01 用户定:「评审放行与落账确认这一步的
|
||||
* 验收卡不应该有重做的选项,因为这里都没有接入 ai」)。
|
||||
*
|
||||
* 判据是这一步**整个由配方跑完、一个字的 `guide` 都没有**(全 auto = 不问模型):
|
||||
* 那时打回的意见没有模型会去读,配方只会原样再提交一次,撞在「打回之后台账没动过就不许
|
||||
* 再提交」那道闸上,连撞两拍之后落成一张「连续撞在同一条判据上」的兜底卡 —— 一颗按下去
|
||||
* 只会把单子推进死胡同的钮,不如不摆(墙要配路;没有路就别立那扇门)。
|
||||
* 半 auto(`guide` 非空)与模型驱动的放行步照旧摆着 —— 那儿真有模型会读那句意见。
|
||||
*/
|
||||
const redoable = releaseRedoable(releaseStepDecl);
|
||||
const submitted = await deps.release.submitForRelease({
|
||||
taskId: input.taskId,
|
||||
ownerId: input.ownerId,
|
||||
humanCallCap: input.humanCallCap,
|
||||
...(releaseChecklist === undefined ? {} : { checklist: releaseChecklist }),
|
||||
...(redoable ? {} : { redoable: false }),
|
||||
deliverableId: String(input.card['deliverable'] ?? manifest.contract.requiredDeliverables[0]?.key ?? 'deliverable'),
|
||||
title,
|
||||
basis: basis.length > 0 ? basis : [{ k: '来源', v: '案例模型收口' }],
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
- **调人预算环外强制**:超过契约声明的打扰上限即 `HumanCallBudgetExceededError`,
|
||||
活动代码无从自查自守
|
||||
- **落地失败卡是执行工单的 preset**,不构成第五种卡
|
||||
- **打不回的验收卡**(2026-09-01 用户定):`AcceptanceWorkOrder.redoable === false` 时,`verdict !== 'pass'` 的回传当场拒(`redo-unavailable`)。什么时候是 false:那一步整个由配方跑完、一个字的 `guide` 都没有 —— 打回之后没有模型会读那句意见、件也只读,配方只会原样重提并撞在「打回之后台账没动过」那道闸上。卡面把钮收起来只是给人看的,**这里才是门**;拒回把真路指出来(别签,去终止重开)。判断在装配层 `releaseRedoable`(纯函数,有测)。
|
||||
- **人工验收清单点全才能通过**(2026-08-27 用户定):验收工单 / 审核卡带 `checklist`(剧本 `acceptanceChecklist`,
|
||||
每份工单固定)时,「通过」的回传必须带 `checked ⊇ checklist`,否则 `checklist-incomplete` 拒回;驳回不看清单。
|
||||
审核卡只拦「通过」那一个选项(`reviewApproveValue`:值叫 approve 的 / 推荐的 / 第一个)。点过的条目落进
|
||||
|
||||
@@ -95,6 +95,30 @@ describe('工单形态机检', () => {
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* **打不回的验收卡**(2026-09-01 用户定:「评审放行与落账确认这一步的验收卡不应该有重做的选项,
|
||||
* 因为这里都没有接入 ai」)。卡面把钮收起来只是给人看的,这里才是门 —— 直调 API 交「重做」,
|
||||
* 单子会被推进死胡同:没有模型读那句意见,配方只会原样重提,撞在「打回之后台账没动过」那道闸上。
|
||||
*/
|
||||
describe('打不回的验收卡', () => {
|
||||
const redo: WorkOrderReply = { mode: 'acceptance', workOrderId: OID, verdict: 'redo', values: {}, comment: '再看看', selectedPorts: {} };
|
||||
|
||||
it('redoable:false → 交「重做」当场拒,拒回把真路指出来', () => {
|
||||
const v = checkWorkOrderReply(acceptance({ redoable: false }), redo);
|
||||
expect(v.map((i) => i.code)).toContain('redo-unavailable');
|
||||
expect(v.find((i) => i.code === 'redo-unavailable')?.message).toContain('终止');
|
||||
});
|
||||
|
||||
it('同一张卡「通过」照收 —— 拦的是打回,不是这张卡', () => {
|
||||
const pass: WorkOrderReply = { mode: 'acceptance', workOrderId: OID, verdict: 'pass', values: {}, selectedPorts: {} };
|
||||
expect(checkWorkOrderReply(acceptance({ redoable: false }), pass).map((i) => i.code)).not.toContain('redo-unavailable');
|
||||
});
|
||||
|
||||
it('没写这面旗(老卡)照旧打得回', () => {
|
||||
expect(checkWorkOrderReply(acceptance(), redo).map((i) => i.code)).not.toContain('redo-unavailable');
|
||||
});
|
||||
});
|
||||
|
||||
describe('到达面两路', () => {
|
||||
it('负责人的卡进任务对话流', () => {
|
||||
expect(arrivalOf('s1', 's1')).toBe('task-chat');
|
||||
|
||||
@@ -133,6 +133,18 @@ export function checkWorkOrderReply(order: WorkOrder, reply: WorkOrderReply): Ca
|
||||
if (rejectionNeedsComment(reply)) {
|
||||
out.push({ code: 'comment-required', message: '驳回必附意见(OS 05)' });
|
||||
}
|
||||
/*
|
||||
* **打不回的卡就是打不回**(2026-09-01 用户定:「评审放行与落账确认这一步的验收卡不应该有
|
||||
* 重做的选项,因为这里都没有接入 ai」)。卡面把那颗钮收起来只是给人看的,**这里才是门** ——
|
||||
* 直调 API 把「重做」交回来,单子会被推进死胡同(没有模型读那句意见,配方只会原样重提,
|
||||
* 撞在「打回之后台账没动过」那道闸上)。拒回把真路指出来:别签、去终止重开。
|
||||
*/
|
||||
if (reply.verdict !== 'pass' && (order as AcceptanceWorkOrder).redoable === false) {
|
||||
out.push({
|
||||
code: 'redo-unavailable',
|
||||
message: '这一步打不回:它整个由配方跑完,没有模型会读你的意见,件也只读 —— 有异议就别确认,去终止这一单重开。',
|
||||
});
|
||||
}
|
||||
/*
|
||||
* **恰选一口,随放行一并确认**(AM 1.2)—— 与「驳回必附意见」同族的收单规矩。
|
||||
* 工单投影自己声明了选择组(portChoice),放行回传就必须带组内恰一口:
|
||||
|
||||
@@ -50,6 +50,8 @@ export type SubmitForReleaseInput = {
|
||||
readonly criteria: readonly CriterionCheck[];
|
||||
/** 人工验收清单(剧本声明,固定):卡上一条一枚开关,全部达标才能通过 */
|
||||
readonly checklist?: ReadonlyArray<string>;
|
||||
/** 这张卡打回得了吗 —— `false` = 打回之后没人接得住(见契约 `AcceptanceWorkOrder.redoable`) */
|
||||
readonly redoable?: boolean;
|
||||
/** 报告可修订:AI 拟值已填的表单字段 */
|
||||
readonly fields?: ReadonlyArray<Record<string, unknown>>;
|
||||
/**
|
||||
|
||||
@@ -144,6 +144,8 @@ export function createReleaseService(deps: ReleaseServiceDeps): ReleaseService {
|
||||
actions: report.verified.map((v) => ({ label: v.label, effect: v.effect, detail: v.detail })),
|
||||
// 人工验收清单:人核这份工单要走的步骤(剧本声明,固定)
|
||||
...(input.checklist === undefined || input.checklist.length === 0 ? {} : { checklist: input.checklist }),
|
||||
/* 打回得了吗 —— 只在明确说「打不回」时才带下去,老调用方一个字不用改 */
|
||||
...(input.redoable === false ? { redoable: false } : {}),
|
||||
/*
|
||||
* 可修订字段 —— 验收报告「AI 已填好、可编辑」改的就是它们。
|
||||
*
|
||||
|
||||
@@ -80,6 +80,8 @@ export type CardGateway = {
|
||||
actions: ReadonlyArray<{ label: string; effect: 'read' | 'write'; detail: string }>;
|
||||
/** 人工验收清单 —— 原样带上卡 */
|
||||
checklist?: ReadonlyArray<string> | undefined;
|
||||
/** 这张卡打回得了吗(`false` = 那颗钮不摆)—— 端口不声明它,值就在装配层被丢掉,这行不能省 */
|
||||
redoable?: boolean | undefined;
|
||||
/**
|
||||
* 可修订字段 —— 验收报告「AI 已填好、可编辑」改的就是它们(带 `ai` 拟值与 `ledger` 落回地址)。
|
||||
* **端口不声明它,值就在装配层被丢掉**:上游一路传下来,而 TypeScript 对条件展开
|
||||
|
||||
@@ -1130,6 +1130,8 @@ function AcceptForm({
|
||||
});
|
||||
const unchecked = checklistMissing(checklist, checked);
|
||||
const passN = order.criteria.filter((c) => c.pass).length;
|
||||
/* 不写 = 打得回(老卡照旧);写了 false 才收起那颗钮 —— 见契约 `AcceptanceWorkOrder.redoable` */
|
||||
const redoable = order.redoable !== false;
|
||||
return (
|
||||
<div className="jc-fc t-acc">
|
||||
<div className="jc-fc-h">
|
||||
@@ -1213,32 +1215,48 @@ function AcceptForm({
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<textarea
|
||||
className="jc-note"
|
||||
placeholder="要重做?写明原因(也可以在下面对话里聊)"
|
||||
value={why}
|
||||
onChange={(e) => setWhy(e.target.value)}
|
||||
/>
|
||||
{/*
|
||||
* **打不回的卡不摆理由框、也不摆那颗钮**(2026-09-01 用户定:「评审放行与落账确认这一步的
|
||||
* 验收卡不应该有重做的选项,因为这里都没有接入 ai」)。这一步整个由配方跑完,打回之后
|
||||
* 没有模型会读那句意见、件也只读 —— 那颗钮按下去只会把单子推进死胡同。
|
||||
* 有异议的人还有真路可走:别签,去终止这一单重开 —— 这句话得写在他眼前,
|
||||
* 不然收起一颗钮就成了「少了个选项,不知道该怎么办」。
|
||||
*/}
|
||||
{redoable ? (
|
||||
<textarea
|
||||
className="jc-note"
|
||||
placeholder="要重做?写明原因(也可以在下面对话里聊)"
|
||||
value={why}
|
||||
onChange={(e) => setWhy(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<p className="jc-fc-note">
|
||||
<Ico name="info" s={13} />
|
||||
<span>这一步只落账,退不回上一步 —— 有异议就别确认,去终止这一单重开。</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="jc-fc-ft">
|
||||
<Btn
|
||||
className="jc-btn jc-btn-bad"
|
||||
disabled={why.trim() === ''}
|
||||
busy={busy}
|
||||
onClick={() =>
|
||||
onReplyOrder({
|
||||
mode: 'acceptance',
|
||||
workOrderId: order.id,
|
||||
verdict: 'redo',
|
||||
// 驳回也把改过的值带上:人常常一边改一边发现问题,改的痕不该丢
|
||||
values: { ...(orderValues ?? {}) },
|
||||
comment: why.trim(),
|
||||
selectedPorts: {},
|
||||
})
|
||||
}
|
||||
>
|
||||
重做
|
||||
</Btn>
|
||||
{redoable && (
|
||||
<Btn
|
||||
className="jc-btn jc-btn-bad"
|
||||
disabled={why.trim() === ''}
|
||||
busy={busy}
|
||||
onClick={() =>
|
||||
onReplyOrder({
|
||||
mode: 'acceptance',
|
||||
workOrderId: order.id,
|
||||
verdict: 'redo',
|
||||
// 驳回也把改过的值带上:人常常一边改一边发现问题,改的痕不该丢
|
||||
values: { ...(orderValues ?? {}) },
|
||||
comment: why.trim(),
|
||||
selectedPorts: {},
|
||||
})
|
||||
}
|
||||
>
|
||||
重做
|
||||
</Btn>
|
||||
)}
|
||||
<span className="jc-fc-sp" />
|
||||
<Btn
|
||||
className="jc-btn jc-btn-ok"
|
||||
|
||||
@@ -516,6 +516,18 @@ export const acceptanceWorkOrderSchema = z.object({
|
||||
})
|
||||
.optional(),
|
||||
passLabel: z.string().optional(),
|
||||
/**
|
||||
* **这张卡打回得了吗**(2026-09-01 用户定:「评审放行与落账确认这一步的验收卡不应该有重做的选项,
|
||||
* 因为这里都没有接入 ai」)。不写 = 打得回(老行为);写 `false` = **打回之后没有任何东西能接住它**,
|
||||
* 那颗钮就不该摆在那儿。
|
||||
*
|
||||
* 什么时候是 `false`:这一步整个由配方跑完、一个字的 `guide` 都没有(不问模型),而件又是只读的 ——
|
||||
* 打回之后既没有模型会去读那句意见,人也没有格子可改,配方只会原样再提交一次、撞在
|
||||
* 「打回之后台账没动过就不许再提交」那道闸上,最后落成一张「连续撞在同一条判据上」的兜底卡。
|
||||
* **一颗按下去只会把单子推进死胡同的钮,不如不摆**(墙要配路,没有路就别立那扇门)。
|
||||
* 有异议的人还有真路可走:别签,去把这一单终止掉重开。
|
||||
*/
|
||||
redoable: z.boolean().optional(),
|
||||
});
|
||||
export type AcceptanceWorkOrder = z.infer<typeof acceptanceWorkOrderSchema>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user