回归检查单
-
[落地页 a11y]
PROD_SMOKE_SKIP_BUILD=1 yarn check:prod-smoke中/ landing a11y: has main landmark and high-contrast red links通过;Lighthouse 不再报对比度与 main landmark ✅ 2026-08-29 -
[llms.txt 规范]
npx @dualnova/llms-txt validate对website/static/llms.txt与frontend/public/llms.txt均通过;_headers中/llms.txt返回text/plain; charset=utf-8;website构建后所有页面 head 含rel="describedby"✅ 2026-08-29 -
[CI e2e 不打公网 API]
frontend/.env不在git ls-files;CI=true ./env.local.sh写出的API_URL为空;e2e-smoke job 显式API_URL=✅ 2026-08-30 -
[登录页 hero src] smoke 断言
/landing-hero/(含-800.jpg响应式 src)✅ 2026-08-30 -
[Frontend CI SSG]
frontend-ci.ymlBuild 步为yarn build:prod;prod-smoke 语言切换覆盖静态<select>与 antdSelect✅ 2026-08-30
规则来源:
.cursor/rules/change-points-as-tests.mdc—— 每个改动点必须登记为可验证的检查点。 自动化覆盖的标注 ✅自动;其余为手工项,涉及对应模块时必查。
MCP / Agent 增长楔子(2026-08-28)
- [PAT 铸造即安装闭环] 铸造成功弹层同屏显示六客户端;Cursor / VS Code install URI 不含本次 PAT;Cursor/Cline/Devin/VS Code 已填 JSON 与 Claude Code 已填命令均含本次 PAT;Claude Desktop 明示静态 Bearer 不可用、OAuth 待后续 ✅ E2E + Playwright 2026-08-30
- [远程 MCP 一 URL 六客户端]
/cursor-mcp/中英切换后 URL 均为https://api.erdonline.com/mcp;Cursor deeplink 解码后仅含url、不含 PAT;六张卡分别锁定 Cursorurl、Claude Desktop Connectors、Claude Codetype:http、ClinestreamableHttp、Devin~/.codeium/mcp_config.json+serverUrl、VS Code 顶层servers✅ Playwright 2026-08-30 - [文档 MCP 页 SEO]
cd website && yarn build && yarn test:seo→ 中/英/docs/guide/api-and-mcp/title 含 MCP,正文含mcpServers✅ 2026-08-28 - [落地页 MCP 链]
yarn test:e2e --project=chromium tests/e2e/landing.spec.ts --grep "顶栏与 Hero"→landing-mcp-docs指向https://doc.erdonline.com/docs/guide/api-and-mcp/;对照表 Agent 格不再是「路线图中」 ✅ 2026-08-28 - [稿 #13 打包]
node scripts/growth/build-package.mjs cursor-mcp-read-and-suggest-version产出掘金 + 小红书包 ✅ 2026-08-28 - [小红书草稿] 创作台长文编辑器已填标题+正文并自动保存;未点公开发布(需封面/人审)。粘贴稿:
content/articles/cursor-mcp-read-and-suggest-version.xhs.md - [小红书 #13 已点发布] 创作台笔记管理「审核中」;explore
https://www.xiaohongshu.com/explore/6a90682300000000290346fd;禁止再点发布 ✅ 2026-08-28 00:38 - [小红书 #13 过审] 审核中 Tab 空;已发布浏览 5(2026-08-28 01:23);仍禁止再点发布
- [两周评审提前] GSC 3mo 2/103/1.9%/62.7;网页无 doc.erdonline.com;www
/home无 P0 泄漏 ✅ 2026-08-28 - [稿 #15 dunk+demo]
node scripts/growth/build-package.mjs cursor-reads-erd-drawio-cannot→ 主 CTA demo +utm_source=juejin;汉字 ≥ 800 ✅ 2026-08-28 - [稿 #15 小红书长文草稿] 梁工造物写长文已填标题+正文,字数 1358,自动保存于 01:32;未点一键排版、未点发布 ✅ 2026-08-28
- [稿 #16 小红书长文草稿] 梁工造物写长文已填标题+开头/中间/结尾,字数 1385,自动保存于 02:05;未点一键排版、未点发布 ✅ 2026-08-28
- [对照页 draw.io]
/compare+ 落地摘要表含 draw.io 列与「关系语义 / 外键」;对照 title 含 draw.io;www H1/SERP 未改 ✅ 2026-08-28 - [catalog 独立 SEO]
catalog.spec.ts「列表 SEO」title 含「ER 图模板」且不含 Draw ER Diagram Online;i18n.spec.ts/en/catalog含 ER diagram templates;www H1/SERP 未改 ✅ 2026-08-28 - [掘金粘贴包]
content/articles/cursor-mcp-read-and-suggest-version.juejin.md主 CTA 为文档 MCP +utm_source=juejin✅ 2026-08-28 - [掘金发 #13] Chrome 登录掘金后粘贴上述稿 → 公开发布 URL(本机 2026-08-28 为验证码墙,未发;切片 6 跳过)
- [稿 #14 CI schema-lint]
node scripts/growth/build-package.mjs ci-rest-projectjson-schema-lint→ 主 CTA 文档 MCP 页 +utm_source=juejin;汉字 ≥ 800 ✅ 2026-08-28 - [projectJSON schema lint]
node scripts/validate-projectjson.mjs正例过、负例非零 ✅ 2026-08-28 - [www SERP] 落地页 H1 仍 Git + Figma;静态 title 仍 Draw ER Diagram Online ✅ 未改 title;landing E2E 仍断言 draw-ERD
- [Home MCP 次入口]
yarn test:e2e --project=chromium tests/e2e/home-mcp.spec.ts✅ 2026-08-28 - [切片 7 MCP 文档 live] 中/英
/docs/guide/api-and-mcp/200、canonical 尾斜杠、sitemap loc 尾斜杠;无斜杠 308 ✅ curl 2026-08-28 - [切片 7 GSC] 两 URL 未知 → 已请求编入索引;GSC 补交
en/sitemap.xml成功(66 URL)✅ 2026-08-28 - [GSC catalog/compare]
/catalog/compare/en/compare已收录;/en/catalog已发现尚未编入索引 → 已请求编入索引;四 URL curl 200、未检查 301、未发 XHS ✅ 2026-08-28 - [公开路径 prerender 壳]
yarn test:seo-static:/catalog/compare/en/en/catalog/en/compare首屏 canonical 为该路径,不是首页;_redirects无这些精确路径的200反代 ✅ 2026-08-28 - [catalog 详情 hydrate SEO]
npx tsx src/utils/catalogSeoPath.test.ts+catalog.spec.ts「详情 SEO」:canonical 为/catalog/demo-authz不是列表;title 含模板名 ✅ 2026-08-28 - [营销路径 JSON-LD]
yarn test:seo-static:/WebApplication;/catalogCollectionPage;官方详情 ItemPage;其余 WebPage;url 对齐该路径 ✅ 2026-08-28
英文漏斗 i18n / SEO(2026-08-12)
- [i18n 键对齐 CI]
cd frontend && yarn check:i18n-keys→ 4 locale 文件、952 键、zh/en 零 diff、占位符一致 ✅ 2026-08-12 - [硬编码中文棘轮 CI]
cd frontend && yarn check:i18n-cjk→ baseline 1570 CJK 字符,只减不增 ✅ 2026-08-12 - [LocaleRoute 路由 flatten]
cd frontend && yarn check:routesPASS;node scripts/check-routes.mjs --self-testbad fixture FAIL / good PASS ✅ 2026-08-12 - [生产 boot smoke]
cd frontend && PROD_SMOKE_SKIP_BUILD=1 yarn check:prod-smoke→ 6 公开 URL 无 pageerror、#root有内容 ✅ 2026-08-12 - [catalog 英文 UI]
localStorage umi_locale=en-US→/catalog顶栏与筛选为英文(Template catalog / Trending…),无catalog.*裸键 ✅ Playwright 2026-08-12 - [docsUrl 单测]
cd frontend && npx tsx src/utils/docsUrl.test.ts路径带尾斜杠 ✅ 2026-08-27 - [文档站 SEO 尾斜杠]
cd website && yarn build && yarn test:seo:sitemap loc 全带/、无/search、_redirects目标带/✅ 构建门禁 - [落地页 Docs 分流] 英文态页脚 Docs →
https://doc.erdonline.com/en/;中文态 →https://doc.erdonline.com/(无/en/)✅ Playwright 生产 2026-08-27 - [分享页 SEO]
/s/public-demo英文态document.title含 demo 文案;meta[name=description]非空;html[lang=en]
性能 umi.js 瘦身(2026-08-28)
- [crypto-js 按需加载] 导出
.erd.json能正常生成加密文件;导入.erd.json能正确解密并加载(手工:设计页 → 导入 → 选择 .erd.json) - [jsondiffpatch 按需加载] 设计页「保存版本」能正常产生 diff;版本列表能正常显示 diff;协作远程同步能正常 patch(手工:创建版本 → 打开版本对比;或两人在设计页同时操作)
落地页静态化(2026-08-28)
- [
/静态页] 访问https://www.erdonline.com/时,DevTools Network 不出现framework.*.js/preload_helper.*.js/umi.*.js请求;FCP 与 hero 图快速出现 - [CTA 链接] 落地页「在线试用」「打开演示」「Compare」等 CTA 跳转后,Umi SPA 正常启动并进入对应页面
- [语言切换] 静态页与 SPA 页顶部的语言切换都保持下拉形态;切换后正确跳转
/或/en - [登录态] 已登录状态下刷新
/,顶栏和主 CTA 显示 "Open workspace" 并跳转/home - [SPA 路由]
/home、/demo、/login、/design/*、/project/*等仍正常 200 并提供 SPA 壳 - [登录/注册]
/login、/register在浏览器中打开是正常 HTML 页面,不会变成文件下载 - [登录态] 已登录状态下刷新
/,顶栏和主 CTA 显示「进入工作台」(英文Workspace)并跳转/home - [分享页] 任意
/s/<token>分享链接仍能正常打开
主站 SEO 索引(2026-08-12)
- [构建产物]
cd frontend && yarn build→dist/sitemap.xml+robots.txt+_redirects+404.html;xmllint --noout dist/sitemap.xml✅ 2026-08-12 - [线上 sitemap] 部署后
curl -sI https://www.erdonline.com/sitemap.xml→content-type含 xml,非 HTML ✅ 2026-08-27 - [线上真 404] 部署后
curl -sI https://www.erdonline.com/__seo_health_nonexistent_path__→ HTTP 404 - [线上 SPA 仍 200] 部署后
/compare、/catalog、/demo、/login、/s/public-demo仍 200
会话 JWT TTL(2026-08-11)
- [登录 expires_in]
POST /auth/login响应expires_in为604800(7 天;或部署覆盖的JWT_EXPIRES_IN) ✅ curl 2026-08-11
字段库 / ADR-0032(2026-08-09)
- [平台种子 apply]
POST /dataDict/dd-field-gender/apply返回 fields+enums 且带dictRef✅DataDictServiceImplApplyTest - [scope ACL] platform 只读;group 成员可写;user 本人可写 ✅
DataDictAclTest - [E2E 写入] 表设计 JExcel 工具栏「从字段库写入」→ 追加性别 →
gender列落盘 ✅field-library-insert.spec.ts - [E2E 覆盖] 选中行 → 从字段库写入 → 覆盖为
gender✅field-library-insert.spec.ts - [E2E 菜单露出] 项目菜单 → 类型字典 / 字段库页可达;表单新建条目(不手写 JSON)✅
field-library-manage.spec.ts - [手工] 团队项目:字段库 scope=group 条目仅成员可见/可编辑
- [手工] 设置页
/design/table/setting/fieldLibrary新建个人条目 CRUD - [手工] 表字段空态「从字段库写入」→ Modal → 管理字段库深链
数据源凭证落库加密 / R-DATA-06 · ADR-0024(2026-08-05)
- [加解密 roundtrip] 明文→密文→明文一致;IV 随机不重复;篡改/错密钥抛异常 ✅
DataSourceCredentialCipherTest - [幂等加密] 已加密值再
encrypt()不二次包裹 ✅DataSourceCredentialCipherTest#encrypt_isIdempotent_doesNotDoubleEncrypt - [存量明文透传] 无
enc:v1:前缀原样返回,不抛异常 ✅DataSourceCredentialCipherTest#decrypt_legacyPlaintext_passesThroughUnchanged - [prod fail-fast] 空密钥 / 仍用仓库默认值 → 启动拒绝 ✅
DataSourceCredentialCipherTest#prodProfile_* - [落库为密文]
POST /ncnb/dataSources后直查 MySQLpassword为enc:v1:...✅ curl +docker exec mysql2026-08-05 - [API 仍收发明文]
GET /ncnb/dataSources/{id}与分页列表password/username为明文,与改动前一致 ✅ curl 2026-08-05 - [渐进迁移] 手工插入明文行 →
GET可读明文 →PATCH重新保存 → MySQL 变为密文 ✅ curl 2026-08-05 - [ACL 路 径同覆盖]
DataSourceAcl.requireOwned直查 mapper 路径同样解密,ConnectorCredentialResolver建连仍拿明文 ✅ConnectorCredentialResolverTest(mock 层)+ 手工建连验证需求未变 - [手工 dogfood] 设计器内新建/编辑数据库连接、测试连接、批量删除,UI 无回归(DatabaseConfigForm/index.tsx 未改动,仅后端加解密透明层)——建议下轮 Playwright UX 走查覆盖
联邦登录解绑重登 / ADR-0021(2026-08-05)
- [解绑物理删除]
unlink走physicalDeleteById而非deleteById✅FederateUserServiceTest#unlink_isPhysicalDelete_notLogicalDeleteById - [解绑后重登不撞已存在] 无邮箱身份按约定用户名重新挂接 ✅
FederateUserServiceTest#resolveForLogin_relinksOrphanedAccount_byConventionUsername_whenLinkMissingAndNoEmail;有邮箱身份仍走邮箱重新挂接 ✅resolveForLogin_relinksOrphanedAccount_byEmail_whenUsernameNotConventionBased - [防劫持] 候选账号已挂别的 subject 时不重新挂接 ✅
resolveForLogin_doesNotRelink_whenCandidateAlreadyLinkedToDifferentSubject - [错误码] 极端并发撞库转 409
FederateException,不再裸 500 ✅resolveForLogin_translatesDuplicateKeyExceptionTo409 - [存量数据] Flyway
V15清掉历史软删行 ✅./backend/dev-ensure.sh --restart日志确认迁移成功 +SELECT COUNT(*) FROM user_identity_link WHERE del_flag<>'0'=0 - [Railway 手工] 真实 Google 账号:登录建号 → 账号设置解绑 → 再次 Google 登录 → 秒登成功(原地复用旧账号,无「已存在」错误、无需重新创建项目)——需真实 IdP 回调,无法自动化,redeploy 后人工走一遍
公开 API / ADR-0013
- [PAT 哈希] 铸造后库内仅
token_hash/token_hint,无明文 ✅PatTokenCodecTest+PersonalAccessTokenAuthTest - [scope 门禁] 默认只读;写 scope 须显式铸造 ✅
PatScopesTest - [限流骨架] 超配额拒绝 ✅
PublicApiRateLimiterTest - [Redis 集群限流] Redisson
RRateLimiter;超限 DENY;Redis 异常 UNAVAILABLE(fail-closed) ✅PublicApiRateLimiterTest - [手工 dogfood] 登录铸造 →
GET /api/v1/me200;无 token / 坏 token → 401;JWT 调/api/v1/me→ 401 ✅ 2026-08-04 - [projects 列表] PAT +
projects:read→GET /api/v1/projects仅成员项目 ✅PublicApiProjectServiceTest+ curl - [projects 详情] 非成员 403;
projectJSON.profile.dbs空列表 ✅PublicApiProjectServiceTest+ProjectShareSanitizeTest - [手工 dogfood] 铸造 → projects 列表/详情 200;无 PAT / JWT → 401 ✅ 2026-08-04
- [versions 列表] PAT +
versions:read+ 成员 →GET /api/v1/projects/{id}/versions✅PublicApiVersionServiceTest+ curl - [versions 详情] 跨项目 versionId → 404;
projectJSON.profile.dbs空列表 ✅PublicApiVersionServiceTest+ curl - [手工 dogfood] 铸造 → versions 列表/详情 200;缺
versions:read→ 403 ✅ 2026-08-04 - [MCP 只读骨架]
cd mcp && yarn dogfood:tools 五只读 + REST;无写 tool ✅ 2026-08-04(切片 4) - [写 scope 铸造]
projects:write/versions:write可铸造;默认仍只读 ✅PatScopesTest - [POST versions]
versions:write+ 成员 → 创建版本;清profile.dbs;无写 scope → 403 ✅PublicApiVersionServiceTest+ dogfood - [PATCH project]
projects:write+ 成员 → 改projectName;无写 scope → 403 ✅PublicApiProjectServiceTest+ curl - [PUT projectJSON] 写入前清
profile.dbs;非成员 403 ✅PublicApiProjectServiceTest+ curl - [MCP create_version]
yarn dogfood:REST 写 + MCPcreate_version✅ 2026-08-04 - [MCP projects:write]
yarn dogfood:MCPupdate_project+put_project_json;只读 PAT → 403 ✅ 2026-08-04 - [OAuth client 哈希] 注册后库内仅
client_secret_hash/hint,无明文 ✅OAuthClientCodecTest - [OAuth client_credentials] 有效凭证 →
erd_oat_;坏 secret →invalid_client;scope ⊆ 客户端 ✅OAuthClientCodecTest+ curl - [OAuth /api/v1] OAT Bearer →
GET /api/v1/me200;与 PAT 同链;会话 JWT → 401 ✅ curl 2026-08-04 - [OAuth ignore]
/oauth/token/auth/oauth/token在 ignore-urls;无 CORS 放宽 ✅DeadSecurityConfigContractTest - [OAuth PKCE] S256 round-trip;redirect 精确匹配;拒 http 非 localhost ✅
OAuthClientCodecTest - [OAuth authorize] 会话 JWT → 302
code+state;未注册 redirect → JSON 不 302;无 JWT → 401 ✅ curl 2026-08-04 - [OAuth auth_code 换票] public:code+verifier→
erd_oat_;坏 verifier / 重放 code →invalid_grant;code 库仅哈希 ✅ curl 2026-08-04 - [OAuth public 禁 M2M] public client
client_credentials→unauthorized_client✅ curl 2026-08-04 - [OAuth confidential code] 无 secret →
invalid_client;有 secret+PKCE → OAT ✅ curl 2026-08-04 - [OAuth client 管理 UI]
/account/settings?selectKey=oauthClients注册→secret 揭示→复制 ID→吊销 ✅oauth-clients.spec.ts - [PAT 管理 UI]
/account/settings?selectKey=personalAccessTokens铸造→明文揭示→复制→吊销 ✅personal-access-tokens.spec.ts - [PAT 揭示弹层 mcp.json] 铸造后见
pat-mcp-json(含mcpServers+ 明文 PAT)+ 复制按钮 ✅personal-access-tokens.spec.ts2026-08-28 - [文档 MCP 无截图] 中/英
guide/api-and-mcp用可复制 JSON,不含mcp-pat-reveal/mcp-json/mcp-agent-tools图 ✅ 2026-08-30 - [文档站 HTTP MCP]
https://doc.erdonline.com/docs/guide/api-and-mcp/标题含 MCP、正文含https://api.erdonline.com/mcp与mcpServersJSON、无截图、无erdonline-mcp-0.1.0.tgz主路径 → 待 push 后 docs-site 部署复验
图本身可读可分享 / ADR-0016(续)
- [几何择柄] 竖叠同列表 FK →
data-port=same;截图diagram-port-same-side.png✅relation.spec.ts「PK/FK 与边样式」 - [字段行再压一档]
.erd-field-rowmin-height 22 / line-height 16 / pad 2;FIELD_ROW_H=26✅graphLayout.test.ts+relation.spec.ts「表节点视觉」 - [表节点密表再压] 表头 pad ≤6;字段行 minH 20 / lh 15 / pad 1;
FIELD_ROW_H=24;标题/徽章层次不动;截图diagram-table-node-density.png/demo-table-node-density.png✅graphLayout.test.ts+relation.spec「PK/FK」+demo.spec - [表节点底栏/空表井碎距] 表头/字段行已 密不改;添加 margin≤6 + minH≥22;打开表设计 margin≤6×4 + btn minH≥22;空表井 pad≤6 / gap≤4 / marginT≤4;
NODE_FOOTER_H=28;截图diagram-table-fields-empty-dense.png✅relation「PK/FK」+table-field-empty「画布空表」 - [导入后首屏] 空态「导入 DBML」→ 直开关系图 + 节点落入画布可视区;截图
diagram-import-first-screen.png✅dbml-import.spec.ts「空态导入 DBML」
已自动化
- 工作台壳外井次密:HomeLayout shell/body ≤12×16;GroupLayout content/body ≤12×16;列表空态 ≤12;截图
workspace-shell-dense.png/group-shell-dense.png✅layout-outlet.spec.ts - 模型树「表/关系」默认展开:不点 switcher 即见三层 +
tree-open-relation✅model-design-ux.spec.ts - 树虚拟滚动:
.ant-tree-list-holder承载(100+ 表不卡)✅model-design-ux.spec.ts - 左树行高密度:treenode ≤24(目标 ~22)/ font ≤13;截图
diagram-left-tree-dense.png✅model-design-ux.spec.ts - 左树工具条/次密距:工具条 ≤32(目标 ~28)/ 新建·搜索控件 ∈24–28;图标不 clip;sider padX ≤20;新建 Tab focus-visible ✅
model-design-ux.spec.ts - CommonTabs / 表设计签头密度:签栏 ≤26(目标 ~24)+ 签头 ≤28;签头 padX≤8 / gap≤4;内签 gutter/marginR≤2;不 clip 标签/关闭;Tab focus-visible + Cmd+1/2/3;截图
diagram-common-tabs-dense.png✅model-design-ux.spec.ts「表设计三签」「表设计内签」 - CommonTabs 签头键盘:←/→ 移焦 + Enter 激活;关闭「关闭
{表名}」;关签焦点归还;内签同构 ✅common-tabs-keyboard.spec.ts - 审批/工单 SQL 明细:
Modal.info首焦「知道了」;Esc/OK 归还「查看SQL」;Tab trap ✅sql-detail-keyboard.spec.ts - 导入跳过校验:二次导入全跳过 → 首焦「知道了」;Esc/OK 归还「解析并导入」;Tab trap ✅
import-skip-warning-keyboard.spec.ts - 工作台 databaseConfig Drawer:新建/编辑首焦「连接名称」;Esc 归还触发器;Tab trap ✅
database-config-drawer-keyboard.spec.ts - JExcel Escape 退格 / 快捷操作:Esc 归还网格;快捷操作首焦「知道了」;Esc 归还;Tab trap ✅
jexcel-grid-keyboard.spec.ts - 版本同步结果弹层:成功/失败首焦「知道了」;Esc 归还「同步」;Tab trap ✅
version-sync-result-keyboard.spec.ts - 版本列表行密度:行 pad-block ≤10 / 标题 ≤14 / 顶栏 ≤32;截图
diagram-version-list-dense.png✅version.spec.ts - 版本工具条二次密度/碎色:控件 ∈24–28;图标不 clip;增删摘要·hint 色 ≡
--erd-success/--erd-brand/--erd-ink-600;新增钮 Tab focus-visible ✅version.spec.ts - 工单/审批列表密度:标题栏 ≤32(目标 ~24)/ 行 pad-block ≤10 / 动作钮 ∈22–28;图标不 clip;动作钮 focus-visible;截图
approval-list-dense.png/order-list-dense.png✅approval.spec.ts - 设计器次屏 JExcel 密度:工具栏 ≤32 / 表头·行 pad-block ≤10 / 行高 ≤32;图标不 clip;撤销钮 Tab focus-visible;截图
diagram-jexcel-dense.png✅model-design-ux.spec.ts - 元数据应用子签密度:CodeTab/DbTab 栏 ≤26(目标 ~24);不 clip 标签;子签 Tab focus-visible;Cmd+1/2/3 不回归;截图
diagram-code-tabs-dense.png✅model-design-ux.spec.ts - 表设计内签密度:字段/索引/元数据栏 ≤26(目标 ~24);不 clip 标签;内签 Tab focus-visible;Cmd+1/2/3 不回归;截图
diagram-inner-tabs-dense.png✅model-design-ux.spec.ts - 表设计签体内容次密距:侧 pad ≤16 / 底 pad ≤6;unique-hint pad-block ≤10 / 高 ≤32;JExcel 工具栏不 clip;元数据 tip ≤32 + CodeTab padY ≤4;截图
diagram-tab-body-dense.png✅model-design-ux.spec.ts - 设计器空态次密距:字段/索引 Empty margin ≤8(禁 100 / antd XL);空态 pad 贴 tab-body;保留 CTA;截图
diagram-pane-empty-dense.png✅model-design-ux.spec.ts - 欢迎空态次密距:
.erd-welcome-empty__innerpadY≤20 / padX≤16;标题 mt∈[8,12] / 字∈[16,18] / lh≈22;hero ≤180;逆向链 + 左树新增模型;截图diagram-welcome-empty-dense.png✅model-design-ux.spec.ts - AuthBrandShell 次密距:品牌/表单 pad ≤20×16;gap ∈[8,12];门头 mb ∈[8,12];表单 Title mt≤8;项 mb∈[8,16];控件 ∈[24,32];标题 ≥24;登录/
share-invalid/注册同源;键盘 Skip/Tab 不回归 ✅smoke+share+session - LandingChrome /
/compare次密距:次屏 section pad ≤52;对照行 pad ≤12;nav ≤20;footer ≤36;compare hero ≤36;hero 品牌字 ≥36 + 全幅;键盘 Skip/Tab 不回归 ✅landing.spec+compare.spec - 分享成功态 meta / 表清单次密:meta 高 ≤60 / gap ≤2 / stage pad ≤6;表清单 pad ≤6 / 标题 ≤12 / 行 ∈20–26;弹层 body ≤8 / hint mb ≤8;键盘 Esc/Tab trap 不回归 ✅
demo.spec+share-project-keyboard - Home hero CTA 簇次密:actions gap ≤8;secondary 钮 pad ≤4×10;hero gap ≤24 / mb·pb ≤16;主 CTA ≥40;问候 ≥28;Skip→主区→CTA/二级入口/项目卡 + focus-visible 不回归 ✅
home-keyboard - Home 空态/公告次密:空态 pad ≤24×12;二级入口 mb ≤16;项目区 mb ≤20;公告 pt ≤4 / 行 pad ≤4·gap ≤10 / 标题 ≤13;空态 CTA +「更多公告」保留;键盘不回归 ✅
home-keyboard - 设计器次屏碎密度:逆向/DDL Steps ≤10/12;设置 hint mb≤8;SyncConfig
.erd-io-modal;ReverseTable meta 次密 ✅designer-secondary-pane - 导入/导出弹层 Steps 对齐:
.erd-io-modal__stepsmt/mb ≤10/12 · 标题 ≤13;与次屏同阶;键盘 Esc/Tab 不回归;截图diagram-import-steps-dense.png✅reverse-database-keyboard+export-ddl-keyboard - 右键/树操作菜单密度:项高 ∈26–30(目标 ~28)/ padY≤2 / border-box / font ≤13;图标·文案不 clip;
role=menuitem+ ArrowDown/Esc;截图diagram-context-menu-dense.png✅model-design-ux.spec.ts - [字段签空态 CTA] 清字段→字段签见「还没有字段」+「添加第一个字段」→ 网格 + 画布
id;空态消失 ✅table-field-empty.spec.ts - [画布空表字段 CTA] 清字段见
canvas-fields-empty+「添加第一个字段」→ 内联建 NAME → 空态消失 +「添 加字段」 ✅table-field-empty.spec.ts - 版本 diff 次屏密度:组头/行 ~24 pad 4×8 + token 色(success/brand/warning);不回归可视化 diff ✅
version.spec.ts - 导入/导出弹层密度:标题 ≤14 / 头·身·脚 pad ≤8×12(禁头 10×14×8 / 脚 8×14)/ OK ≥28≤32;截图
diagram-import-modal-dense.png/diagram-export-modal-dense.png✅dbml-import/dbml-export - 普通导出页密度:页标题 ≤14 / 卡片 padY ≤20;图标
currentColor→--erd-brand;截图diagram-export-common-dense.png✅export.spec.ts - Home 主导航图标 fill ≡
--erd-brand(erdColors.brand,非组件硬编码)✅layout-outlet.spec.ts「三壳同语言」 - dataTypeDomains 树图标 fill ≡
erdColors.brand(禁裸#DE2910);设置页/design/table/setting/dataType挂载 CRUD ✅dataTypeDomainsSlice.test.ts+datatype-domains-failure - 设置页 chrome 密度:标题 ≤14 / 输入·保存钮 ≤32 / 表单项 mb ≤16;截图
diagram-setting-page-dense.png✅default-field.spec.ts - 数据库配置页密度:标题 ≤14 / 工具条钮 ≤32 / 抽屉输入·保存钮 ≤32;截图
database-config-page-dense.png✅database-config.spec.ts - 账号设置 / Home 项目卡密度:标题 ≤14 / 输入·保存钮 ≤32 / 安全行 padY ≤16;卡 padY ≤28;截图
account-settings-page-dense.png/home-project-cards-dense.png✅account-settings+layout-outlet - 账号 BaseView 左右列次密:
.baseViewgap ≤16(窄屏 ≤12);禁 24;截图同account-settings-page-dense.png✅account-settingsdensify - 顶栏
erd-chrome-actions次密:Home/Group/分享 gap ≤12;Design ≤8;禁 16;截图chrome-actions-dense.png✅layout-outletdensify - 顶栏
erd-chrome-header次密:Home padX ≤16 + brand–nav gap ≤12;Design gap ≤8;禁 padX20 / gap16;截图chrome-header-dense.png✅layout-outletdensify - Home 水平导航 Menu 项次密:padX ∈[8,12];项高 64;命中宽 ≥44;
testid=home-layout-menu;截图home-nav-menu-dense.png✅layout-outletdensify +home-keyboard回归 - Group 侧栏 nav 行距次密:项高 ∈[28,32];padX ∈[8,12];marginY ≤4;
testid=group-layout-sider-menu;截图group-sider-nav-dense.png✅layout-outletdensify +group-keyboard回归 - 设计器侧栏 nav 行距次密:项高 ∈[28,32];padX ∈[8,12];marginY ≤4;
testid=design-layout-sider-menu;截图design-sider-nav-dense.png✅layout-outletdensify + 侧栏键盘 - 版本列表空态井次密:padY≤12 / padX≤8;禁 16×12;保留「保存第一个版本」;截图
version-empty-dense.png✅version.spec「无数据源也可新增版本」 - 项目列表工具条碎距:Space gap ∈[8,12];搜索高 ≤28;工具条高 ≤32;
testid=project-list-toolbar;截图project-*-list-dense.png✅project-surfacedensify +project-list-keyboard回归 - 团队成员工具条碎距:Space gap ∈[8,12];搜索高 ≤28;工具条高 ≤32 / mb≤8;钮 padX∈[8,12];
testid=group-user-toolbar;截图group-user-toolbar-dense.png✅group-layout-navdensify +group-keyboard/add-user-keyboard回归 - Group 用户组 Title/左角色签碎距:标题 ≤14/lh≤24/mb≤8;左签 padX∈[8,12]·高∈[28,32];
testid=group-setting-page;截图group-setting-page-dense.png✅group-layout-navdensify +group-keyboard/add-user-keyboard回归 - Group 基本设置页头碎距:标题 ≤14/lh≤24/mb≤8/mt≤4;
testid=basic-setting-page;截图group-basic-setting-dense.png✅group-basic-settingdensify +group-layout-nav/group-keyboard回归 - Group 基本设置 Form 碎距:项 mb≤16(目标12);Input/Select/钮高≤32(目标28);label≤13;禁默认 24/32 ✅
group-basic-settingdensify +group-layout-nav/group-keyboard回归 - Group 基本设置删区碎片:Divider≤16;body gap≤12;次文≤13/lh≤20;标题 mb≤2;
testid=basic-setting-delete-zone;确认/aria 不弱化 ✅group-basic-settingdensify +group-project-delete-keyboard - 个人/最近/团队/公告列表行密度:行 pad-block ≤10 / 标题 ≤14 / 打开钮 ≤32;截图
project-person-list-dense.png/project-recent-list-dense.png/project-group-list-dense.png/project-notice-list-dense.png✅project-surface.spec.ts+project-notice.spec.ts - 用户手动折叠模块不被默认展开回顶 ✅
model-design-ux.spec.ts - 表设计三签:签头表名/模型层级 + 字段/索引/元数据应用切换 ✅
model-design-ux.spec.ts - 画布「索引」→ 表设计索引签(无死 affordance;再入仍落索引)✅
relation.spec.ts「画布打开索引签」 - [索引签空态 CTA] 画布→索引见「还没有索引」+「添加第一个索引」→ toast「索引更新成功」+ 表头「索引名*」+
T_TABLE_1_IDX1;空态消失 ✅relation.spec.ts「索引签空态 CTA」 - [索引签再加一行 CTA] 首条后见「+ 再添加一条索引」→ toast +
T_TABLE_1_IDX2;index-add-row仍可见 ✅relation.spec.ts「索引签再加一行 CTA」 - [索引签删除二次确认] 「删除索引
{name}」→ 取消保留;确认 toast「索引更新成功」+ 空态「添加第一个索引」;index-delete-list消失 ✅relation.spec.ts「索引签删除二次确认」 - [JExcel 工具栏删除二次确认] 字段签选中行→「删除选中行」→ 取消保留;确认后网格与画布无该字段 ✅
relation.spec.ts「JExcel 工具栏删除二次确认」+ 键盘 ✅jexcel-toolbar-delete-keyboard.spec.ts - [表设计字段签 Tab 焦点序] hint→撤销→重做→末尾增加一行;Enter 增行;网格 Shift+Tab 回「快捷操作」 ✅
relation.spec.ts「工具栏 Tab 可达」 - [设计器 Skip + 焦点环] 首项 Tab「跳到模型树」→
erd-design-tree→ Tab 入搜索;「跳到主工作区」→erd-design-workspace→ Tab 离地标(无 trap) ✅relation.spec.ts「设计器 Skip」 - [画布字段浏览器 Tab 环] 选中表 Tab 穿字段→添加字段→开表设计后可脱出;未选中表
tabIndex=-1✅relation.spec.ts「字段浏览器 Tab 环」 - [画布 chrome Tab 序] Controls 四钮 → 工具栏;MiniMap svg
tabindex=-1;Controls:focus-visiblebrand 环;Shift+Tab 回 Controls 无 trap ✅relation.spec.ts「画布 chrome Tab 序」 - [左树键盘漫游] Skip→↓入树;active
data-tree-kb-activebrand 环;Enter 定位表 + 开关系;Skip→Tab 进搜索无 trap ✅relation.spec.ts「左树键盘漫游」 - [画布节点级 Tab] 无选中无 RF node wrapper/
erd-edge-label进序;选中边 chip Enter 开基数;Frame Enter 重命名 ✅relation.spec.ts「画布节点级 Tab」 - [分享壳键盘]
/demo首项 Tab Skip「跳到关系图」→ stage → Tab 离地标;Controls 三钮进序;MiniMaptabindex=-1;适应画布 focus-visible brand 环 ✅share.spec.ts「分享壳键盘」 - [登录壳键盘]
/login首项 Tab Skip「跳到登录表单」→#auth-form-anchor→ Tab 入用户名;密码 Enter 错误凭证 toast;登录钮 focus-visible brand 环;无 trap ✅session.spec.ts「登录壳键盘」 - [注册壳键盘]
/registerSkip「跳到注册表单」→#auth-form-anchor→ 字段 Tab 序(tip 出序);末字段 Enter 密码不一致 toast;注册钮 focus-visible brand 环;无 trap ✅session.spec.ts「注册壳键盘」 - [落地页键盘]
/首项 Tab Skip「跳到主操作」→#landing-main-cta→ Tab 入「在线试用」;试用→注册→登录可逆;主 CTA focus-visible surface 环;无 trap ✅landing.spec.ts「落地页键盘」 - [竞品对照页键盘]
/compare首项 Tab Skip「跳到主操作」→#landing-main-cta→「打开演示」→「自部署指南」→「返回产品首页」可逆;surface focus-visible;无 trap ✅compare.spec.ts「竞品对照页键盘」 - [Home 工作台键盘]
/home首项 Tab Skip「跳到主内容」→#home-main-content→继续建模→新建→示例→二级入口→项目卡可逆;brand focus-visible;无 trap ✅home-keyboard.spec.ts「Home 键盘」 - [GroupLayout 壳键盘]
/project/group/setting/basic首项 Tab Skip「跳到主内容」→#group-main-content→项目名→标签→项目描述可逆;侧栏链 brand focus-visible;无 trap ✅group-keyboard.spec.ts「Group 键盘」 - [项目列表行键盘] 个人/最近/团队 stretched link 消死卡;Enter 开设计器;Tab 行内动作可逆;行
:hasbrand focus-visible;无 trap ✅project-list-keyboard.spec.ts - [账号设置壳键盘]
/account/settings首项 Tab Skip「跳到主表单」→#account-settings-form→邮箱→电话→更新基本信息可逆;brand focus-visible;无 trap ✅account-settings-keyboard.spec.ts - [项目动作弹窗键盘] 新建首焦类型 / 修改首焦项目名 / 删除首焦「是」;Esc 关窗归还触发器;Tab trap 在 dialog ✅
project-action-modals-keyboard.spec.ts - [导入导出弹层键盘] 空态导入首焦 DBML文本 / 菜单导出首焦模型 Select;Esc 归还 CTA/项目菜单;Tab trap ✅
import-export-keyboard.spec.ts - [版本动作弹窗键盘] 新增/编辑首焦版本号(非最新编辑首焦描述)/ 删除·回滚首焦「是」;Esc 关窗归还触发器;Tab trap 在 dialog ✅
version-action-modals-keyboard.spec.ts - [版本对比/详情 diff 键盘] 比对首焦「初始版本」/ 详情首焦「导出变更清单」;Esc 关窗归还触发器;Tab trap 在 dialog ✅
version-diff-keyboard.spec.ts - [同步配置/重建版本键盘] 同步配置首焦「字段增量」/ 重建版本首焦「版本号」;Esc 关窗归还触发器;Tab trap 在 dialog ✅
version-sync-rebuild-keyboard.spec.ts - [重建基线确认键盘] 重建版本表单提交→「重建基线」确认首焦「重建」;Esc 关确认不落盘、归还重建钮;Tab trap ✅
version-rebuild-confirm-keyboard.spec.ts - [初始化基线键盘] 有数据源且未建基线时首焦「版本号」;Esc 关窗归还触发器;Tab trap 在 dialog ✅
version-init-keyboard.spec.ts - [复刻弹层键盘] 版本行「复刻」首焦「项目名」;Esc 关窗归还触发器;Tab trap 在 dialog ✅
project-copy-keyboard.spec.ts - [数据源设置键盘] 项目菜单→数据源设置首焦「新增数据源」;Esc 关窗归还「项目菜单」;Tab trap 在 dialog ✅
database-setup-keyboard.spec.ts - [默认项设置键盘] 项目菜单→默认项设置首焦「默认字段」;Esc 关窗归还「项目菜单」;Tab trap 在 dialog ✅
default-setup-keyboard.spec.ts - [数据源逆向解析键盘] 项目菜单→数据源逆向解析首焦「数据源」;Esc 关窗归还「项目菜单」;Tab trap 在 dialog ✅
reverse-database-keyboard.spec.ts - [导出DDL键盘] 项目菜单→导出DDL首焦「数据源」;Esc 关窗归还「项目菜单」;Tab trap 在 dialog ✅
export-ddl-keyboard.spec.ts - [解析ERD文件键盘] 项目菜单→解析ERD文件首焦上传区「选择ERD文件」;Esc 关窗归还「项目菜单」;Tab trap 在 dialog ✅
reverse-erd-keyboard.spec.ts - [解析PdMan文件键盘] 项目菜单→解析PdMan文件首焦上传区「选择PdMan文件」;Esc 关窗归还「项目菜单」;Tab trap 在 dialog ✅
reverse-pdman-keyboard.spec.ts - [修改密码键盘]
/account/settings?selectKey=security→「修改密码」首焦「密码」;Esc 关窗归还触发器;Tab trap 在 dialog ✅reset-password-keyboard.spec.ts - [修改密码失败不关窗] mock
settings/update业务码 → toast「模拟更新密码拒绝」+ 窗仍开 → 二次成功关窗+「更新密码信息成功」 ✅reset-password-failure.spec.ts - [同步配置失败不关窗] mock
project/save(upgradeType=rebuild)业务码 → toast「模拟同步配置拒绝」+ 窗仍开 → 二次成功关窗+「设置成功」 ✅sync-config-failure.spec.ts - [默认项设置失败不关窗] mock
project/save(sqlConfig 标记)业务码 → toast「模拟默认项设置拒绝」+ 窗仍开 → 二次成功关窗+「设置成功」 ✅default-setup-failure.spec.ts - [数据源设置确定失败不关窗] mock PUT
dataSources(url 标记)业务码 → toast「模拟数据源保存拒绝」+ 窗仍开 → 二次成功关窗+「保存成功!」 ✅database-setup-failure.spec.ts - [EntityModal 落盘失败不关窗] mock
project/save(modules 含新模型名)业务码 → toast「模拟模型保存拒绝」+ 窗仍开 → 二次成功关窗+「模型添加成功」;键盘闭环不回归 ✅entity-modal-failure.spec.ts+entity-modal-keyboard.spec.ts - [画布关系图弹层落盘失败不关窗] mock
project/save(diagrams 含新图名)业务码 → toast「模拟关系图保存拒绝」+ 窗仍开 → 二次成功关窗+「已新建关系图」+ switcher ✅diagram-modal-failure.spec.ts - [画布关系图弹层键盘] 工具栏「新建关系图」→ 首焦「关系图名称」;Tab trap;Esc 关窗归还触发器 ✅
diagram-modal-keyboard.spec.ts - [数据类型字典弹层键盘] 设置页「新增字段类型」→ 首焦「类型名称」;Tab trap;Esc 关窗归还触发器 ✅
datatype-domains-keyboard.spec.ts - [数据类型字典枚举域 UX] 「新增枚举」→ kind=enum + values[] 落盘;列表种类/取值;编辑追加取值;定位 role/aria/testid ✅
datatype-enum-ux.spec.ts - [字段类型下拉区分枚举] 字典建枚举 → 画布 type select 有 optgroup「枚举」→ 选 code 落盘 + 浏览态徽章;定位 combobox/testid;勿扫
.ant-*✅field-type-enum-picker.spec.ts - [逻辑类型 apply 方言映射]
/setting/dataType新增/编辑逻辑类型 → 填 MYSQL/PG/… 物理类型 → save 含apply[code].type;枚举弹层无 apply 编辑器;定位 role/aria/testid ✅datatype-apply-ux.spec.ts - [画布表头改名落盘失败不退出编辑] mock
project/save(entities 含新表名)业务码 → toast「模拟表改名保存拒绝」+ 编辑态仍开(草稿保留、旧节点 id)→ 二次成功改节点 id ✅table-rename-failure.spec.ts - [画布建表/行内加字段落盘失败可重试] mock save(entities 含 T_TABLE_1 / fields 含新字段)→ toast + 无节点或仍编辑草稿 → 重试成功上图/落字段;空字段 CTA / 空名 toast 不回归 ✅
canvas-create-field-failure.spec.ts - [画布字段改名/删字段落盘失败可重试] mock save(改名 fields 含新名 / 删缺 NAME)→ toast + 仍编辑或行仍在+确认可再删 → 重试成功;空名 toast / 删二次确认不回归 ✅
canvas-field-rename-delete-failure.spec.ts - [画布字段 meta 落盘失败可重试] mock save(PK / Integer 类型 / relationNoShow / 浏览态 PK)→ toast + 勾选/类型回滚或行仍可见 → 重试成功;happy 路径 relation 不回归 ✅
canvas-field-meta-failure.spec.ts - [表设计 JExcel 字段 meta 落盘失败可重试] mock save(字段签 PK / 隐藏)→ toast + 勾选回滚 → 重试成功 + 画布对齐;工具栏删行/半成品/空态 CTA 不回归 ✅
jexcel-field-meta-failure.spec.ts - [表设计索引签落盘失败可重试] mock save(添加第一个索引 / 勾是否唯一)→ toast + 空态或勾选回滚 → 重试成功 + 画布 UK;空态 CTA/再加/删除/键盘不回归 ✅
jexcel-index-failure.spec.ts - [默认字段落盘失败可重试] mock save(改默认主键英文名)→ toast + 网格回滚仍为 id → 重试成功 + 新表带重命名字段;既有成功路径不回归 ✅
default-field-failure.spec.ts/default-field.spec.ts - [画布删表落盘失败可重试] mock save(全 modules 缺 T_TABLE_1)→ toast「模拟删表保存拒绝」+ 节点仍在+确认可再删(无「表删除成功」)→ 重试移出;二次确认/键盘不回归 ✅
canvas-delete-table-failure.spec.ts/relation「画布删表/删边二次确认」 - [左树删模型/关系图落盘失败可重试] mock save(缺关系图名 / 缺模块名)→ toast 模拟拒绝 + 树/表仍在+确认可再删(无成功 toast)→ 重试移出;二次确认不回归 ✅
tree-delete-module-diagram-failure.spec.ts/multi-diagram「左树删除关系图/模型二次确认」 - [画布删边/删分组落盘失败可重试] mock save(缺 T_ORDER→T_TABLE_1 关联 / groups 空)→ toast 模拟拒绝 + 边/框仍在+确认可再删(无成功 toast)→ 重试移出;二次确认/键盘不回归 ✅
canvas-delete-edge-frame-failure.spec.ts/canvas-delete-edge-frame-keyboard.spec.ts/diagram-frame「删除分组二次确认」 - [左树剪切/粘贴表落盘失败可重试] mock save(含 T_TABLE_1副本 / 缺 T_TABLE_1)→ toast 模拟拒绝 + 无副本或表仍在(无成功 toast / 无「剪切成功」)→ 重试粘贴出现副本 / 剪切移出;定位 role/aria(勿扫
.ant-*) ✅tree-cut-paste-failure.spec.ts - [画布拖表坐标落盘失败可回滚] mock save(layout 位移 T_TABLE_1)→ toast「模拟布局保存拒绝」+ transform 回滚(无已保存伪装)→ 重试拖动成功;定位
rfNode/save-status(勿扫.ant-*) ✅canvas-drag-reposition-failure.spec.ts - [画布对齐/自动布局落盘失败可回滚] mock save(左齐两表同 x / 自动布局位移)→ toast 拒绝 + transform 回滚 → 重试成功;定位
align-left/aria-label=自动布局/rfNode(勿扫.ant-*) ✅canvas-align-layout-failure.spec.ts - [Frame 改名/适应成员落盘失败可回滚] mock save → toast「模拟分组保存拒绝」+ 改名草稿保留/适应成员 RF+store 回滚(无「已适应成员」)→ 重试成功;定位
frame-rename-*/diagram-frame/ 「适应成员」(勿扫.ant-*) ✅canvas-frame-rename-bounds-failure.spec.ts - [Frame 新建/成员加减落盘失败可回滚] mock save → toast「模拟分组保存拒绝」+ 新建不上图/加入成员仍 0(无成功 toast)→ 重试成功;定位「新建分组」/「加入分组」/
diagram-frame(勿扫.ant-*) ✅canvas-frame-members-failure.spec.ts - [画布连线建关联落盘失败可重试] mock save → toast「模拟连线保存拒绝」+ 不上边(无
erd-edge-label)→ 重试成功上边;定位rfNode/data-handleid/erd-edge-label(勿扫.ant-*) ✅canvas-connect-edge-failure.spec.ts - [画布改边基数落盘失败可重试] mock save → toast「模拟基数保存拒绝」+ chip 仍
n:1→ 重试成功1:1;定位erd-edge-label/erd-edge-cardinality/role=option