Archived
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的: 件上就地改 · 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名, 于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 —— 而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。 · **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。 删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)—— 「没回传就是删了」会把一次不完整的回传变成一次删表。 · **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。 · 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。 判据与门 · 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来, 人填好的目标价它照样判「客户未提供」的毛病由此消掉。 · 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。 · 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开, 于是一单没打标就进了放行门。三个数全由基座现数。 · 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。 **指向两扇打不开的门,比不指还糟。** 界面不撒谎 · 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。 · 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、 往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。 · 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。 · 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。 · 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。 A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。 五道门禁全绿(971 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
import { Card, Chip, Clear, Entry, NList, NRow, Note, PageHead, QueryGate, Sec } from '@web-shared/gov/primitives.tsx';
|
||||
import type { Nav } from '@web-shared/gov/gov-route.ts';
|
||||
import { Ico } from '@web-shared/Ico.tsx';
|
||||
import { useGovLedger, useGovObjects } from '@web-shared/gov/gov-api.ts';
|
||||
import { domainShort, sortDomains } from '@web-shared/gov/domains.ts';
|
||||
|
||||
@@ -24,40 +25,100 @@ export type HomeProps = { tab: string; nav: Nav };
|
||||
* 只呈现**结构**:域分组 → 对象 → 列与主键。
|
||||
* 行数与行浏览入口不在这里 —— 那在运营中心的台账。
|
||||
*/
|
||||
/**
|
||||
* 三类的呈现次序与说辞 —— **按「要不要管它」排**,不按字母。
|
||||
*
|
||||
* 主数据摆最前:一屏里真正需要治理的就是它(谁能改、依据是什么、多久算过期)。
|
||||
* 单据量最大但不需要「管」—— 查得到、回放得出就够了,所以摆中间。
|
||||
* 派生摆最后,并且明写「不该手写」:手写的派生值会和它的来源打架,而没人会发现。
|
||||
*
|
||||
* `unknown` 是**库里有表、却没有哪个在装的活动声明它**——多半是退役活动留下的空壳。
|
||||
* 它不是一种性质,是一种状况,所以单列一档、说清楚,不混进上面三类假装正常。
|
||||
*/
|
||||
const KIND_ORDER = ['master', 'record', 'derived', 'unknown'] as const;
|
||||
type KindKey = (typeof KIND_ORDER)[number];
|
||||
|
||||
const KIND_VIEW: Readonly<Record<KindKey, { title: string; ico: string; desc: string; tone: string }>> = {
|
||||
master: {
|
||||
title: '主数据',
|
||||
ico: 'library',
|
||||
tone: 'm',
|
||||
desc: '跨单积累的经营资产 —— 会被反复更新,下一单读它做判断。治理都在这一类上:谁能改、依据是什么、怎么去重、多久算过期。',
|
||||
},
|
||||
record: {
|
||||
title: '单据',
|
||||
ico: 'file-text',
|
||||
tone: 'r',
|
||||
desc: '一单一份的过程与结论 —— 放行即定稿、只增不改。它不需要「管理」,需要的是查得到、回放得出;主键第一列就是那一单的标识。',
|
||||
},
|
||||
derived: {
|
||||
title: '派生',
|
||||
ico: 'trending-up',
|
||||
tone: 'd',
|
||||
desc: '能由单据与主数据算出来的指标 —— 本不该手写:手写的派生值会和它的来源打架,而没人会发现。标出来,至少让它可被重算校验。',
|
||||
},
|
||||
unknown: {
|
||||
title: '无人声明',
|
||||
ico: 'help-circle',
|
||||
tone: 'u',
|
||||
desc: '库里有这张表,却没有哪个在装的活动声明它 —— 多半是退役活动留下的空壳。台账「只增不破坏」,清空壳表是一次性的人工决定。',
|
||||
},
|
||||
};
|
||||
|
||||
export function DataHome({ nav }: HomeProps) {
|
||||
const q = useGovObjects();
|
||||
const objects = q.data?.objects ?? [];
|
||||
const domains = sortDomains([...new Set(objects.map((o) => o.domain))]);
|
||||
|
||||
return (
|
||||
<QueryGate query={q}>
|
||||
<>
|
||||
{domains.map((d) => {
|
||||
const inDomain = objects.filter((o) => o.domain === d);
|
||||
{KIND_ORDER.map((kind) => {
|
||||
const inKind = objects.filter((o) => (o.kind ?? 'unknown') === kind);
|
||||
if (inKind.length === 0) return null;
|
||||
const view = KIND_VIEW[kind];
|
||||
const domains = sortDomains([...new Set(inKind.map((o) => o.domain))]);
|
||||
return (
|
||||
<Card
|
||||
key={d}
|
||||
ico="database"
|
||||
title={domainShort(d)}
|
||||
extra={
|
||||
<span className="g-mono">
|
||||
{d} · {inDomain.length} 对象
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="g-entries">
|
||||
{inDomain.map((o) => (
|
||||
<Entry
|
||||
key={o.object}
|
||||
ico="table"
|
||||
// 人读名优先;没登记 label 才退回对象键 —— 治理台不该拿 `intake_record` 给人看
|
||||
title={o.label ?? o.object}
|
||||
desc={`${o.domain}.${o.object}`}
|
||||
onClick={() => nav.push({ k: 'obj', id: `${o.domain}.${o.object}` })}
|
||||
/>
|
||||
))}
|
||||
<section key={kind} className={'g-kind t-' + view.tone}>
|
||||
<div className="g-kind-h">
|
||||
<Ico name={view.ico} s={15} />
|
||||
<b>{view.title}</b>
|
||||
<span className="n">{inKind.length} 张表</span>
|
||||
<p>{view.desc}</p>
|
||||
</div>
|
||||
</Card>
|
||||
{domains.map((d) => {
|
||||
const inDomain = inKind.filter((o) => o.domain === d);
|
||||
return (
|
||||
<Card
|
||||
key={d}
|
||||
ico="database"
|
||||
title={domainShort(d)}
|
||||
extra={
|
||||
<span className="g-mono">
|
||||
{d} · {inDomain.length} 对象
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="g-entries">
|
||||
{inDomain.map((o) => (
|
||||
<Entry
|
||||
key={o.object}
|
||||
ico="table"
|
||||
// 人读名优先;没登记 label 才退回对象键 —— 治理台不该拿 `intake_record` 给人看
|
||||
title={o.label ?? o.object}
|
||||
/* 单据把**按什么分单**摆出来 —— 那是它和主数据最要紧的差别 */
|
||||
desc={
|
||||
o.caseKey === undefined
|
||||
? `${o.domain}.${o.object}`
|
||||
: `${o.domain}.${o.object} · 按 ${o.caseKey} 分单`
|
||||
}
|
||||
onClick={() => nav.push({ k: 'obj', id: `${o.domain}.${o.object}` })}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
{objects.length === 0 && (
|
||||
|
||||
Reference in New Issue
Block a user