From ead8ea216bdbd041e203e24487b10e5b8806bb01 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Tue, 13 Oct 2026 21:13:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=9B=AE=E5=BD=95=E8=B5=B7?= =?UTF-8?q?=E8=8D=89=EF=BC=88=E5=89=8D=E7=AB=AF=EF=BC=89=EF=BC=9A=E8=A1=A8?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E9=87=8C=E5=B7=B2=E7=BB=8F=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E7=9A=84=E8=A1=A8=EF=BC=88=E5=8F=AA=E5=89=A9=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=EF=BC=89=E4=B8=8D=E5=86=8D=E7=BB=99=E3=80=8C=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E8=B5=B7=E8=8D=89=E8=BF=99=E5=BC=A0=E8=A1=A8=E3=80=8D=E3=80=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E8=B5=B7=E8=8D=89=E8=A6=81=E6=B1=82=E3=80=8D?= =?UTF-8?q?=E2=80=94=E2=80=94=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=8F=AA=E4=BC=9A?= =?UTF-8?q?=E6=8A=8A=E5=AE=83=E8=AE=B0=E6=88=90=E5=B7=B2=E8=B7=B3=E8=BF=87?= =?UTF-8?q?=E3=80=82=E8=A1=A8=E8=AF=A6=E6=83=85=E9=A1=B6=E9=83=A8=E7=85=A7?= =?UTF-8?q?=E5=86=99=E4=B8=8A=E6=AC=A1=E8=B5=B7=E8=8D=89=E7=9A=84=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=EF=BC=88=E6=B2=A1=E5=86=99=E5=85=A8=E7=9A=84=E5=8E=9F?= =?UTF-8?q?=E5=9B=A0=E3=80=81=E7=95=99=E7=A9=BA=E4=BA=86=E5=87=A0=E5=88=97?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=BB=BA=E8=AE=AE=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E8=B5=B7=E8=8D=89=E8=A6=81=E6=B1=82=E5=90=8E=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E8=B5=B7=E8=8D=89=EF=BC=8C=E5=86=99=E6=98=8E=E3=80=8C?= =?UTF-8?q?=E8=A1=A8=E7=BB=93=E6=9E=84=E4=B8=AD=E5=B7=B2=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E8=BF=99=E5=BC=A0=E8=A1=A8=EF=BC=8C=E4=B8=8D=E8=83=BD=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E8=B5=B7=E8=8D=89=E3=80=8D=EF=BC=9B=E6=B8=85=E5=8D=95?= =?UTF-8?q?=E4=B8=8A=E7=95=99=E7=A9=BA=E5=88=97=E7=9A=84=E8=BD=BB=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E7=85=A7=E6=97=A7=EF=BC=8C=E6=82=AC=E5=81=9C=E9=87=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=BB=BA=E8=AE=AE=E9=87=8D=E6=96=B0=E8=B5=B7?= =?UTF-8?q?=E8=8D=89=E3=80=82=E8=B5=B7=E8=8D=89=E8=AE=BE=E7=BD=AE=E6=94=B6?= =?UTF-8?q?=E5=88=B0=E7=9A=84=E9=A2=84=E5=85=88=E9=80=89=E5=A5=BD=E7=9A=84?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=EF=BC=88=E8=A1=A8=E8=AF=A6=E6=83=85=E7=9A=84?= =?UTF-8?q?=E3=80=8C=E8=A1=A5=E5=85=85=E8=B5=B7=E8=8D=89=E8=A6=81=E6=B1=82?= =?UTF-8?q?=E3=80=8D=E3=80=81=E7=BB=93=E6=9E=9C=E9=87=8C=E7=9A=84=E3=80=8C?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=B5=B7=E8=8D=89=E8=BF=99=E5=87=A0=E5=BC=A0?= =?UTF-8?q?=E8=A1=A8=E3=80=8D=EF=BC=89=E9=87=8C=E7=9A=84=E8=A1=A8=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E4=B8=8D=E5=9C=A8=E8=A1=A8=E7=BB=93=E6=9E=84=E9=87=8C?= =?UTF-8?q?=E6=97=B6=E4=B8=8D=E5=86=8D=E9=80=80=E5=9B=9E=E5=88=AB=E7=9A=84?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=EF=BC=9A=E9=82=A3=E4=B8=80=E9=A1=B9=E7=85=A7?= =?UTF-8?q?=E6=97=A7=E9=80=89=E7=9D=80=EF=BC=8C=E5=86=99=E6=98=8E=E3=80=8C?= =?UTF-8?q?=E8=A1=A8=E7=BB=93=E6=9E=84=E4=B8=AD=E5=B7=B2=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E8=BF=99=E5=BC=A0=E8=A1=A8=EF=BC=8C=E4=B8=8D=E8=83=BD=E8=B5=B7?= =?UTF-8?q?=E8=8D=89=E3=80=8D=EF=BC=88=E5=8F=AA=E5=B0=91=E4=BA=86=E5=87=A0?= =?UTF-8?q?=E5=BC=A0=E6=97=B6=E5=86=99=E5=85=B6=E4=B8=AD=E5=87=A0=E5=BC=A0?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E8=B5=B7=E8=8D=89=EF=BC=89=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E5=BC=80=E5=A7=8B=EF=BC=8C=E4=B9=9F=E4=B8=8D=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=8B=BE=E7=94=A8=E6=A8=A1=E5=9E=8B=E3=80=81=E4=B8=8D?= =?UTF-8?q?=E6=8A=8A=E7=84=A6=E7=82=B9=E6=94=BE=E8=BF=9B=E8=B5=B7=E8=8D=89?= =?UTF-8?q?=E8=A6=81=E6=B1=82=E2=80=94=E2=80=94=E4=BB=A5=E5=89=8D=E9=80=80?= =?UTF-8?q?=E5=88=B0=E3=80=8C=E4=BD=BF=E7=94=A8=E6=AC=A1=E6=95=B0=E6=9C=80?= =?UTF-8?q?=E5=A4=9A=E7=9A=84=2020=20=E5=BC=A0=E8=A1=A8=E3=80=8D=E8=BF=98?= =?UTF-8?q?=E5=8B=BE=E7=9D=80=E7=94=A8=E6=A8=A1=E5=9E=8B=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E4=B8=BA=E5=8F=AA=E8=B5=B7=E8=8D=89=E8=BF=99=E4=B8=80=E5=BC=A0?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E9=99=85=E6=8C=89=E5=88=AB=E7=9A=84=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E8=B5=B7=E8=8D=89=E4=BA=86=EF=BC=9B=E9=80=89=E7=9A=84?= =?UTF-8?q?=E9=82=A3=E4=B8=80=E9=A1=B9=E4=B8=8D=E5=9C=A8=E4=BA=86=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=8D=95=E9=80=89=E6=A1=86=E7=85=A7=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E8=B5=B7=E8=8D=89=E7=9A=84=E9=82=A3=E4=B8=80=E9=A1=B9=E5=8B=BE?= =?UTF-8?q?=E3=80=82check-catalog=20=E6=96=B0=E5=8A=A0=E4=B8=80=E8=8A=82?= =?UTF-8?q?=EF=BC=9A=E5=8F=AA=E5=89=A9=E7=9B=AE=E5=BD=95=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E7=9A=84=E6=B8=85=E5=8D=95=E6=8F=90=E7=A4=BA=E4=B8=8E=E8=AF=A6?= =?UTF-8?q?=E6=83=85=EF=BC=9B=E6=89=93=E5=BC=80=E8=AF=A6=E6=83=85=E4=BB=A5?= =?UTF-8?q?=E5=90=8E=E8=A1=A8=E6=89=8D=E6=B2=A1=E4=BA=86=EF=BC=88=E6=B8=85?= =?UTF-8?q?=E5=8D=95=E9=87=8D=E5=8F=96=E4=BA=86=E3=80=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E6=B2=A1=E9=87=8D=E6=96=B0=E8=BD=BD=E5=85=A5=EF=BC=89=E6=97=B6?= =?UTF-8?q?=E3=80=8C=E8=A1=A5=E5=85=85=E8=B5=B7=E8=8D=89=E8=A6=81=E6=B1=82?= =?UTF-8?q?=E3=80=8D=E6=89=93=E5=BC=80=E7=9A=84=E8=AE=BE=E7=BD=AE=EF=BC=9B?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E6=89=93=E5=BC=80=E5=90=8E=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E7=BB=99=E4=B8=A4=E4=B8=AA=E6=8C=89=E9=92=AE=E3=80=82check-all?= =?UTF-8?q?=20=E9=87=8C=E6=95=B0=E6=8D=AE=E7=9B=AE=E5=BD=95=E7=9A=84?= =?UTF-8?q?=E8=80=97=E6=97=B6=E6=8C=89=E5=AE=9E=E6=B5=8B=E6=94=B9=E6=88=90?= =?UTF-8?q?=20140=20=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/lib/terms.ts | 9 +++ frontend/src/pages/catalog/DraftDialog.tsx | 41 +++++++---- frontend/src/pages/catalog/TableDetail.tsx | 15 ++-- frontend/src/pages/catalog/TableIndex.tsx | 6 +- scripts/check-all.mjs | 2 +- scripts/check-catalog.mjs | 84 ++++++++++++++++++++++ 6 files changed, 137 insertions(+), 20 deletions(-) diff --git a/frontend/src/lib/terms.ts b/frontend/src/lib/terms.ts index b409f578..663b6329 100644 --- a/frontend/src/lib/terms.ts +++ b/frontend/src/lib/terms.ts @@ -2434,6 +2434,13 @@ export const CATALOG_DRAFT_TEXT = { // ---- 从结果、表详情回到设置:范围就是那几张表 scopeBlank: (n: number) => `有留空列的 ${formatNumber(n)} 张表`, scopeTable: (name: string) => `这张表:${name}`, + /** + * 预先选好的范围里有表已经不在表结构里(打开表详情以后别处重新探查了结构):写在那一项下面。一张都不剩时那一项 + * 照旧选着、不能开始,不退回别的范围 + */ + presetMissing: (gone: number, total: number) => (gone < total + ? `其中 ${formatNumber(gone)} 张在表结构中已没有,不会起草` + : total === 1 ? '表结构中已没有这张表,不能起草' : `表结构中已没有这 ${formatNumber(total)} 张表,不能起草`), // ---- 进行中 queued: '排队中,等待开始…', running: '正在起草…', @@ -2538,6 +2545,8 @@ export const CATALOG_DRAFT_TEXT = { redraftHint: '用模型重新起草这张表(不带补充说明,带上数据源的起草要求);人工确认、驳回过的项不会被改动', redraftBusy: '已有起草任务在进行,结束后再重新起草这张表', redraftEditing: '正在编辑这张表,保存或取消后再重新起草', + /** 表结构里已经没有的表(只剩目录):上次起草的结果照写,不给重新起草(服务端只会把它记成已跳过) */ + redraftMissing: '表结构中已没有这张表,不能重新起草', } /** 设置页「偏好设置」里「起草模型」一节(服务端 api/settings.py 的 catalog_draft 一组) */ diff --git a/frontend/src/pages/catalog/DraftDialog.tsx b/frontend/src/pages/catalog/DraftDialog.tsx index 4dafc323..3a7547fc 100644 --- a/frontend/src/pages/catalog/DraftDialog.tsx +++ b/frontend/src/pages/catalog/DraftDialog.tsx @@ -25,7 +25,8 @@ import type { DraftPreset, DraftStartBody } from './draftJob' // // 留空的列(模型列出了、中文名和含义都没给)不算没写全,表照样是已完成;结果里写合计和办法——在起草要求里补充缩写和 // 术语的含义,再重新起草这几张表。「重新起草这几张表」回到设置:范围就是那几张、勾着用模型、焦点在起草要求, -// 补完再开始。表详情里的「补充起草要求」也是这样打开(范围是那一张,见 preset)。 +// 补完再开始。表详情里的「补充起草要求」也是这样打开(范围是那一张,见 preset)。预先选好的范围里的表已经不在表结构 +// 里时不退回别的范围:那一项照旧选着、写明原因,不能开始。 // =========================================================================== type Scope = 'preset' | 'selected' | 'filtered' | 'top' | 'all' @@ -66,8 +67,9 @@ export function DraftDialog({ /** 打开时显示的任务是撞上已有任务(409)转过来的:写明 */ initialBusy?: boolean /** - * 打开设置时预先选好的范围(表详情里「补充起草要求」:只有那张表)。给了就勾上用模型、焦点放在起草要求上: - * 打开它就是为了先补起草要求再起草 + * 打开设置时预先选好的范围(表详情里「补充起草要求」:只有那张表)。给了就选它;里面有可起草的表时勾上用模型、 + * 焦点放在起草要求上(打开它就是为了先补起草要求再起草)。一张可起草的都没有(已不在表结构里)时照旧选着它、 + * 写明原因、不能开始,也不勾用模型——不退回别的范围 */ preset?: DraftPreset | null /** 点了停止、服务端还没停下来 */ @@ -88,9 +90,13 @@ export function DraftDialog({ /** 预先选好的范围:打开时给的,或者结果里点了「重新起草这几张表」 */ const [preset, setPreset] = useState(initialPreset) const scopes = useMemo(() => { - const out: { key: Scope; label: string; tables: string[] }[] = [] - const pre = preset?.tables.filter((t) => liveSet.has(t)) ?? [] - if (preset && pre.length) out.push({ key: 'preset', label: preset.label, tables: pre }) + const out: { key: Scope; label: string; tables: string[]; gone?: number }[] = [] + // 预先选好的范围总在第一项:里面的表已经不在表结构里的(打开表详情以后别处重新探查了结构)不起草、写明有几张; + // 一张都不剩也留着这一项,不退回别的范围——以为只起草那一张,实际按别的范围用模型起草了 + if (preset) { + const pre = preset.tables.filter((t) => liveSet.has(t)) + out.push({ key: 'preset', label: preset.label, tables: pre, gone: preset.tables.length - pre.length }) + } const sel = selected.filter((t) => liveSet.has(t)) if (sel.length) out.push({ key: 'selected', label: CT.scopeSelected(sel.length), tables: sel }) const vis = visible.filter((r) => r.in_schema).map((r) => r.table_name) @@ -100,11 +106,15 @@ export function DraftDialog({ return out }, [preset, selected, visible, filtered, live, liveSet]) const [scope, setScope] = useState(() => scopes[0].key) - const tables = scopes.find((s) => s.key === scope)?.tables ?? scopes[0].tables + /** 起草的就是这一项:选的那一项不在了(所选、筛选变了)就是第一项,单选框也照它勾,看得见起草的是哪些 */ + const current = scopes.find((s) => s.key === scope) ?? scopes[0] + const tables = current.tables const tooMany = tables.length > DRAFT_MAX_TABLES - const [useModel, setUseModel] = useState(!!initialPreset) + /** 打开时给的范围里有可起草的表:勾上用模型、焦点放进起草要求(打开它就是为了先补起草要求);一张都没有就都不做 */ + const presetLive = !!initialPreset && initialPreset.tables.some((t) => liveSet.has(t)) + const [useModel, setUseModel] = useState(presetLive) /** 回到设置以后把焦点放进起草要求(预先选好范围的两种入口都是为了先补起草要求) */ - const [focusGuidance, setFocusGuidance] = useState(!!initialPreset) + const [focusGuidance, setFocusGuidance] = useState(presetLive) const [guidanceText, setGuidanceText] = useState(guidance) const [note, setNote] = useState('') const guidanceLen = charCount(guidanceText) @@ -284,9 +294,16 @@ export function DraftDialog({
{scopes.map((s) => ( ))}
diff --git a/frontend/src/pages/catalog/TableDetail.tsx b/frontend/src/pages/catalog/TableDetail.tsx index 049ce6c3..61783e69 100644 --- a/frontend/src/pages/catalog/TableDetail.tsx +++ b/frontend/src/pages/catalog/TableDetail.tsx @@ -77,9 +77,12 @@ export function TableDetail({ onDetail: (d: CatalogDetail) => void /** 这个数据源有排队、进行中的起草任务:「重新起草这张表」等它结束 */ draftBusy?: boolean - /** 上次起草失败、部分完成或有留空列时的「重新起草这张表」:建只含这张表的起草任务(用模型、不带补充说明) */ + /** + * 上次起草失败、部分完成或有留空列时的「重新起草这张表」:建只含这张表的起草任务(用模型、不带补充说明)。 + * 表结构里已经没有的表不给 + */ onRedraft?: (table: string) => Promise - /** 上次起草有留空列时的「补充起草要求」:打开起草设置,范围只有这张表,先补起草要求再开始 */ + /** 上次起草有留空列时的「补充起草要求」:打开起草设置,范围只有这张表,先补起草要求再开始。表结构里已经没有的表不给 */ onDraftGuidance?: (table: string) => void }) { const [detail, setDetail] = useState(null) @@ -465,7 +468,8 @@ export function TableDetail({ // 上次起草没写全:写明原因;有留空的列:写几列、怎么办(在起草要求里补充缩写和术语的含义)。只有留空的列时是 // 提示(表照样写完了),不是失败。给「重新起草这张表」(只含这张表、用模型);有留空的列再给「补充起草要求」 // (打开起草设置,范围是这张表)。有起草任务在进行、正在编辑这张表时先不给点:起草会写进这张表,编辑中的保存 - // 就会撞上版本冲突 + // 就会撞上版本冲突。表结构里已经没有这张表(只剩目录)时上次的结果照写,不给这两个按钮、不建议重新起草, + // 写明为什么:服务端只会把它记成「已跳过」 {lastBad &&

{lastBad.status === 'failed' ? DT.lastFailed : DT.lastPartial}

} {lastBad && taskReason(lastBad) && ( @@ -474,10 +478,11 @@ export function TableDetail({ {lastBlank > 0 && ( <>

{DT.lastBlank(lastBlank)}

-

{DT.lastBlankAdvice}

+ {detail.in_schema &&

{DT.lastBlankAdvice}

} )} - {onRedraft && ( + {!detail.in_schema &&

{DT.redraftMissing}

} + {detail.in_schema && onRedraft && ( <>