openyida 2026.8.12 → 2026.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -641,7 +641,9 @@ Latest contributors: [DDlixin1](https://github.com/DDlixin1), [fcloud](https://g
641
641
  <p>
642
642
  <a href="https://github.com/yize"><img src="https://github.com/yize.png?size=48" width="48" height="48" alt="九神" title="九神" /></a>
643
643
  <a href="https://github.com/alex-mm"><img src="https://github.com/alex-mm.png?size=48" width="48" height="48" alt="天晟" title="天晟" /></a>
644
+ <a href="https://github.com/fryaninc"><img src="https://github.com/fryaninc.png?size=48" width="48" height="48" alt="New Core architecture, Core development, user issue support, skill maintenance and Help & Show Case Center 构建者" title="New Core architecture, Core development, user issue support, skill maintenance and Help & Show Case Center 构建者" /></a>
644
645
  <a href="https://github.com/DDlixin1"><img src="https://github.com/DDlixin1.png?size=48" width="48" height="48" alt="DDlixin1" title="DDlixin1" /></a>
646
+ <a href="https://github.com/xhy-ali"><img src="https://github.com/xhy-ali.png?size=48" width="48" height="48" alt="登录态开发者" title="登录态开发者" /></a>
645
647
  <a href="https://github.com/fcloud"><img src="https://github.com/fcloud.png?size=48" width="48" height="48" alt="Aiden Wu (fcloud)" title="Aiden Wu (fcloud)" /></a>
646
648
  <a href="https://github.com/nicky1108"><img src="https://github.com/nicky1108.png?size=48" width="48" height="48" alt="nicky1108" title="nicky1108" /></a>
647
649
  <a href="https://github.com/angelinheys"><img src="https://github.com/angelinheys.png?size=48" width="48" height="48" alt="angelinheys" title="angelinheys" /></a>
@@ -658,6 +660,8 @@ Latest contributors: [DDlixin1](https://github.com/DDlixin1), [fcloud](https://g
658
660
  <a href="https://github.com/key-668"><img src="https://github.com/key-668.png?size=48" width="48" height="48" alt="再不喝汽水" title="再不喝汽水" /></a>
659
661
  <a href="https://github.com/dongbeixiaohuo"><img src="https://github.com/dongbeixiaohuo.png?size=48" width="48" height="48" alt="dongbeixiaohuo" title="dongbeixiaohuo" /></a>
660
662
  <a href="https://github.com/nandanadileep"><img src="https://github.com/nandanadileep.png?size=48" width="48" height="48" alt="nandanadileep" title="nandanadileep" /></a>
663
+ <a href="https://github.com/hj837990198"><img src="https://github.com/hj837990198.png?size=48" width="48" height="48" alt="skill 测试" title="skill 测试" /></a>
664
+ <a href="https://github.com/chengminchao-create"><img src="https://github.com/chengminchao-create.png?size=48" width="48" height="48" alt="skill共建" title="skill共建" /></a>
661
665
  </p>
662
666
 
663
667
  <!-- openyida-contributors:end -->
@@ -46,10 +46,6 @@ const {
46
46
  findFormDetailLinkIssues,
47
47
  formatFormDetailLinkMessage,
48
48
  } = require('./form-detail-link-guard');
49
- const {
50
- findPageRichnessIssues,
51
- formatPageRichnessMessage,
52
- } = require('./page-richness-guard');
53
49
 
54
50
  /**
55
51
  * 依赖 → window 别名白名单,用于把 import 改写到运行时全局对象。
@@ -621,21 +617,6 @@ function compileCanvasLocal(source, options = {}) {
621
617
  },
622
618
  });
623
619
  }
624
- const richnessIssues = findPageRichnessIssues(source);
625
- if (richnessIssues.length > 0) {
626
- const firstIssue = richnessIssues[0];
627
- throw new CliError(formatPageRichnessMessage(firstIssue), {
628
- code: 'OPENYIDA_CANVAS_CONTENT_BLOCKS_TOO_FEW',
629
- details: {
630
- stage: 'canvas_compile',
631
- sourcePath: options.sourcePath || '',
632
- line: firstIssue.line,
633
- scene: firstIssue.scene,
634
- count: firstIssue.count,
635
- min: firstIssue.min,
636
- },
637
- });
638
- }
639
620
  assertNoBareDependencyGlobals(source, options);
640
621
  assertNoManualDependencyGlobals(source, options);
641
622
 
@@ -269,6 +269,8 @@ function buildCreateAppPayload(params, auth, contentLocale, openExclusive, openP
269
269
  openIsolationDatabase: 'n',
270
270
  openExclusiveUnit: 'n',
271
271
  group: 'ALL',
272
+ fromBuilderAi: 'y',
273
+ builderAiSource: 'local',
272
274
  };
273
275
  if (navTheme) {
274
276
  payload.navTheme = navTheme;
@@ -40,6 +40,8 @@ async function createApp(appName, authRef) {
40
40
  openIsolationDatabase: 'n',
41
41
  openExclusiveUnit: 'n',
42
42
  group: 'ALL',
43
+ fromBuilderAi: 'y',
44
+ builderAiSource: 'local',
43
45
  });
44
46
  return httpPost(auth.baseUrl, '/query/app/registerApp.json', postData, auth.cookies);
45
47
  }, authRef);
@@ -9,7 +9,6 @@ const {
9
9
  const { findBareCjkJsxTextIdentifiers } = require('./jsx-text-guard');
10
10
  const { findDirectFormOpenIssues } = require('./form-open-guard');
11
11
  const { findFormDetailLinkIssues } = require('./form-detail-link-guard');
12
- const { findPageRichnessIssues } = require('./page-richness-guard');
13
12
  const Babel = require('@babel/standalone');
14
13
 
15
14
  const THEN_CALLBACK_LINE_LIMIT = 50;
@@ -866,10 +865,6 @@ function lintYidaSource(sourceCode, filePath) {
866
865
  findFormDetailLinkIssues(sourceCode, { parserOptions: PARSER_OPTIONS }).forEach((issue) => {
867
866
  pushIssue(errors, issue.line, 'form-detail-link', t('publish.lint_form_detail_link'), disableMap);
868
867
  });
869
- findPageRichnessIssues(sourceCode).forEach((issue) => {
870
- pushIssue(errors, issue.line, 'content-blocks-too-few', t('publish.lint_content_blocks_too_few', issue.count, issue.min), disableMap);
871
- });
872
-
873
868
  return { errors, warnings };
874
869
  }
875
870
 
@@ -1,12 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('crypto');
4
+ const fs = require('fs');
4
5
  const path = require('path');
5
6
  const { version } = require('../../package.json');
6
7
  const { t } = require('./i18n');
7
8
  const { buildCommandManifest } = require('./command-manifest');
8
9
  const { buildEnvironmentSnapshot } = require('./env');
9
- const { getAuthStatus } = require('./utils');
10
+ const { findProjectRoot, getAuthStatus } = require('./utils');
10
11
 
11
12
  function redactLogin(login) {
12
13
  const redacted = { ...login };
@@ -35,6 +36,21 @@ function hasEnvTokenCredential(env = process.env) {
35
36
  );
36
37
  }
37
38
 
39
+ function hasRuntimeProvisionedAccessToken(env = process.env) {
40
+ return isTruthyEnv(env.YIDA_AUTH_ENABLED) &&
41
+ !!String(env.OPENYIDA_ACCESS_TOKEN || '').trim();
42
+ }
43
+
44
+ function buildRuntimeProvisionedLoginStatus() {
45
+ return {
46
+ ok: true,
47
+ auth_mode: 'token',
48
+ auth_source: 'env',
49
+ status: 'ok',
50
+ can_auto_use: true,
51
+ };
52
+ }
53
+
38
54
  const BUILDER_CORE_COMMAND_IDS = Object.freeze([
39
55
  'agent-capabilities',
40
56
  'commands',
@@ -84,8 +100,10 @@ function buildAuthPath(login, env = process.env) {
84
100
  const envTokenPresent = hasEnvTokenCredential(env);
85
101
  const authSource = login.auth_source || (hostInjectedTokenMode || envTokenPresent ? 'env' : 'token_session');
86
102
  const hostTokenEnvDetected = hostInjectedTokenMode || envTokenPresent || authSource === 'env';
87
-
88
- return {
103
+ const runtimeAuthProvisioned = hasRuntimeProvisionedAccessToken(env) &&
104
+ authSource === 'env' &&
105
+ login.status === 'ok';
106
+ const authPath = {
89
107
  mode: login.auth_mode || 'token',
90
108
  source: authSource,
91
109
  can_auto_use: login.can_auto_use === true,
@@ -109,6 +127,10 @@ function buildAuthPath(login, env = process.env) {
109
127
  ? 'STOP_AND_REQUEST_HOST_TOKEN'
110
128
  : 'RUN_OPENYIDA_LOGIN_IF_USER_APPROVES',
111
129
  };
130
+ if (runtimeAuthProvisioned) {
131
+ authPath.runtime_auth_provisioned = true;
132
+ }
133
+ return authPath;
112
134
  }
113
135
 
114
136
  function buildBuilderPath(login, projectRoot, manifest, env = process.env) {
@@ -218,26 +240,30 @@ function compactBuilderPath(builderPath) {
218
240
  const commandContract = builderPath.command_contract;
219
241
  const resourceContext = builderPath.resource_context_resolution;
220
242
  const paths = builderPath.paths;
243
+ const auth = {
244
+ mode: builderPath.auth.mode,
245
+ source: builderPath.auth.source,
246
+ can_auto_use: builderPath.auth.can_auto_use,
247
+ host_injected_token_mode: builderPath.auth.host_injected_token_mode,
248
+ host_token_env_detected: builderPath.auth.host_token_env_detected,
249
+ env_token_present: builderPath.auth.env_token_present,
250
+ interactive_login_allowed: builderPath.auth.interactive_login_allowed,
251
+ browser_session_auth_allowed: builderPath.auth.browser_session_auth_allowed,
252
+ auth_runtime: builderPath.auth.auth_runtime,
253
+ cookie_auth_supported: builderPath.auth.cookie_auth_supported,
254
+ cookie_check_required: builderPath.auth.cookie_check_required,
255
+ playwright_cookie_check_required: builderPath.auth.playwright_cookie_check_required,
256
+ qr_login_required: builderPath.auth.qr_login_required,
257
+ missing_token_action: builderPath.auth.missing_token_action,
258
+ };
259
+ if (builderPath.auth.runtime_auth_provisioned === true) {
260
+ auth.runtime_auth_provisioned = true;
261
+ }
221
262
 
222
263
  return {
223
264
  schema_version: builderPath.schema_version,
224
265
  preflight: builderPath.preflight,
225
- auth: {
226
- mode: builderPath.auth.mode,
227
- source: builderPath.auth.source,
228
- can_auto_use: builderPath.auth.can_auto_use,
229
- host_injected_token_mode: builderPath.auth.host_injected_token_mode,
230
- host_token_env_detected: builderPath.auth.host_token_env_detected,
231
- env_token_present: builderPath.auth.env_token_present,
232
- interactive_login_allowed: builderPath.auth.interactive_login_allowed,
233
- browser_session_auth_allowed: builderPath.auth.browser_session_auth_allowed,
234
- auth_runtime: builderPath.auth.auth_runtime,
235
- cookie_auth_supported: builderPath.auth.cookie_auth_supported,
236
- cookie_check_required: builderPath.auth.cookie_check_required,
237
- playwright_cookie_check_required: builderPath.auth.playwright_cookie_check_required,
238
- qr_login_required: builderPath.auth.qr_login_required,
239
- missing_token_action: builderPath.auth.missing_token_action,
240
- },
266
+ auth,
241
267
  environment_check_simplification: {
242
268
  minimal_probe_commands: environment.minimal_probe_commands,
243
269
  can_skip_default_exploration_when_summary_ok: environment.can_skip_default_exploration_when_summary_ok,
@@ -319,8 +345,37 @@ function buildCommandManifestDigest(manifest) {
319
345
  }
320
346
 
321
347
  function buildAgentCapabilitiesSummary() {
322
- const envSnapshot = buildEnvironmentSnapshot();
323
348
  const manifest = buildCommandManifest({ t, version });
349
+
350
+ if (hasRuntimeProvisionedAccessToken(process.env)) {
351
+ const projectRoot = findProjectRoot();
352
+ const loginStatus = buildRuntimeProvisionedLoginStatus();
353
+ const builderPath = compactBuilderPath(
354
+ buildBuilderPath(loginStatus, projectRoot, manifest)
355
+ );
356
+
357
+ return {
358
+ schema_version: 1,
359
+ name: 'openyida-agent-capabilities-summary',
360
+ version,
361
+ login: compactLogin(loginStatus),
362
+ precheck: {
363
+ skipped: true,
364
+ reason: 'runtime_auth_provisioned',
365
+ },
366
+ workdir: projectRoot,
367
+ workdir_exists: fs.existsSync(projectRoot),
368
+ cache_dir: path.join(projectRoot, '.cache'),
369
+ openyida_task_cache_dir: path.join(projectRoot, '.cache', 'openyida'),
370
+ command_manifest_digest: buildCommandManifestDigest(manifest),
371
+ command_manifest_digest_algorithm: 'sha256',
372
+ command_count: manifest.summary.command_count,
373
+ full_capabilities_command: 'openyida agent-capabilities --json',
374
+ builder_path: builderPath,
375
+ };
376
+ }
377
+
378
+ const envSnapshot = buildEnvironmentSnapshot();
324
379
  const projectRoot = envSnapshot.active.projectRoot;
325
380
  const loginStatus = getAuthStatus({ projectRoot, includeSecrets: false });
326
381
  const builderPath = compactBuilderPath(
@@ -1149,7 +1149,6 @@ Examples:
1149
1149
  lint_iframe_self_navigation: 'Yida custom pages run in an iframe. Use target="_top"/target="_blank" or window.top.location for Yida page navigation to avoid nested pages.',
1150
1150
  lint_form_open_container: 'Opening Yida form submission/detail pages from a custom page must use FormOpenContainer: a 50vw drawer iframe on desktop, and full/new page only on mobile. Button handlers should call openForm({ type: "submission" | "detail", ... }).',
1151
1151
  lint_form_detail_link: 'Yida form detail pages must use a real formInstId: read row.formInstId first, and disable or warn when the instance id is missing instead of opening a formDetail link with an empty formInstId.',
1152
- lint_content_blocks_too_few: 'Display pages must declare @openyida-content-blocks and include at least {1} business-purpose content blocks; KPI groups, quick-entry groups, and list groups each count as one block. Current count: {0}.',
1153
1152
  lint_page_size_limit: 'pageSize={0} exceeds the Yida API limit of 100; use 100 or less',
1154
1153
  lint_page_size_recommend: 'pageSize={0} is large; the recommended value is 50 (best performance, max 100). Prefer 10/20/50',
1155
1154
  lint_searchformdata_http_path: 'A direct searchFormDatas.json call must use /dingtalk/web/<appType>/v1/form/searchFormDatas.json; /query/form/searchFormDatas.json is not a valid form data endpoint',
@@ -1143,7 +1143,6 @@ openyida - 宜搭命令行工具
1143
1143
  lint_iframe_self_navigation: '宜搭自定义页面运行在 iframe 中,跳转宜搭页面时请使用 target="_top"/target="_blank" 或 window.top.location,避免页面套娃',
1144
1144
  lint_form_open_container: '自定义页内打开表单提交/详情只能使用 FormOpenContainer:PC 端用 50vw 抽屉 iframe,移动端才整页或新页打开。按钮事件请调用 openForm({ type: "submission" | "detail", ... })。',
1145
1145
  lint_form_detail_link: '表单详情页必须使用真实 formInstId:优先读取 row.formInstId,缺少实例 ID 时禁用详情入口或提示,不能打开空 formInstId 的 formDetail 链接。',
1146
- lint_content_blocks_too_few: '展示型页面必须声明 @openyida-content-blocks,且至少包含 {1} 个有业务目的的内容区块;KPI 组、快捷入口组、列表组各只算 1 个区块,当前只有 {0} 个。',
1147
1146
  lint_page_size_limit: 'pageSize={0} 超过宜搭接口上限 100,请改为 100 或更小',
1148
1147
  lint_page_size_recommend: 'pageSize={0} 偏大,推荐使用 50(性能最佳,最大 100),优先选择 10/20/50',
1149
1148
  lint_searchformdata_http_path: '直连 searchFormDatas.json 必须使用 /dingtalk/web/<appType>/v1/form/searchFormDatas.json;/query/form/searchFormDatas.json 不是有效表单数据端点',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.8.12",
3
+ "version": "2026.8.13",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -55,9 +55,9 @@ description: >
55
55
 
56
56
  ## 默认执行路径
57
57
 
58
- OpenYida builder 默认使用 `create-app / create-form / create-page / publish` 等常规命令链路。页面实现先消费 `prd.md` `design.md`;只有走生成器或需要稳定交接时才派生 `page-spec.json`,再选择页面生成器或手写源码。CLI 内部负责读取必要的 schema、定位字段、输出 compact diff/evidence、readback 和 bindings,模型不要为了简单字段更新先拉取大 schema,也不要把新建命令当作默认动作。
58
+ 完整应用搭建加载 `yida-app`,按`yida-app`去执行对应步骤;默认使用 `create-app / create-form / create-page / publish` 这条 CLI 能力链,但必须由 `yida-app` 编排,不直接手动拼接子命令。
59
59
 
60
- `.cache/<项目名>-schema.json` 只是本地 ID 映射,不等于远端真相。路径不明确时先只读确认或询问用户;不要通过新建同类资源规避不确定性。
60
+ 默认阶段心智模型:`resolve_resource_context → yida-design → create/reuse app → resolve forms/processes → seed records → reserve main page → 发布 + 轻量导航排序 → final`。发布后的轻量导航自动排序、seed records 和表单详情页 formDetail CSS 注入是默认收尾;PRD 写明导航顺序时执行 `openyida nav-group order <appType> <页面/表单...>`,未写明时使用 `--auto-nav-order` / `nav-group auto-order` 兜底。新建表单拿到真实 `formUuid` 后默认注入 formDetail CSS;字段级命令内置解析,普通字段更新优先交给 `create-form update/add-option/bind-datasource/validation/rule`。
61
61
 
62
62
  ---
63
63
 
@@ -105,7 +105,7 @@ OpenYida builder 默认使用 `create-app / create-form / create-page / publish`
105
105
  - 已解析到目标自定义页面 URL / `formUuid` / bound page 时,默认写源码并发布到该页面,不执行 `yida-create-page`;只有缺少目标 display page 且本次意图允许新增页面时才创建。
106
106
  - 已解析到目标表单 `formUuid` 时,字段结构诉求默认走 `yida-create-form-page` 的 update/patch/rule/bind-datasource 模式,不创建同名或同类表单。
107
107
  - 已解析到目标流程表单 / `processCode` 时,默认走 `yida-process-rule` 配置/更新流程,不从零执行 `yida-create-process`。
108
- - 完整应用统一编排也遵守本规则:先由 `yida-design` 输出 `prd.md` 与 `design.md`,再按 PRD 创建或复用应用;表单/流程先于自定义页面,页面实现消费 PRD 的业务结构、design.md 的视觉契约和真实资源 ID。
108
+ - 完整应用搭建使用 `yida-app` 技能, 按`yida-app` 技能执行。
109
109
 
110
110
  验收心智模型:
111
111
 
@@ -136,23 +136,7 @@ OpenYida builder 默认使用 `create-app / create-form / create-page / publish`
136
136
  ## 完整开发流程(完整搭建 / 补齐)
137
137
 
138
138
  > 📌 仅当第二步判定为「完整搭建 / 补齐」时进入;单一/增量任务请跳「技能路由」。
139
- > 加载子技能 `yida-app`,由它负责完整应用 workflow、阶段子技能加载、关键 ID 流转、PRD 与 schema cache 约束。
140
- > 用户说“按默认方案 / 不要追问 / 直接创建 / 尽快搭建”时,`yida-app` 使用统一编排:先解析本轮资源上下文,再由 `yida-design` 完成需求分析和产品设计,输出 `prd/<项目名>/prd.md` 与 `prd/<项目名>/design.md`,随后按 PRD 创建或复用应用/表单/流程/页面,按 design.md 实现页面视觉,发布主页面后立刻做一次轻量导航排序;最终先输出 2-3 句业务交付总结,再给一个主入口链接,不默认输出表格或资源 ID 摘要。
141
- > `yida-app` 使用常规 OpenYida 命令编排。
142
-
143
- **默认链路**:完整应用必须只做 `resolve context → yida-design prd.md + design.md → create/reuse app → resolve forms/processes → seed records → reserve main page → 编写/更新主页面源码 → 发布 + 轻量导航排序 → 返回 2-3 句业务交付总结 + 一个主入口链接`。资源创建顺序按 PRD 执行:应用先落位,表单/流程先于自定义页面;页面实现读取真实表单 URL、字段语义、数据来源和 design.md 视觉契约。只有走生成器时才派生 `page-spec.json`。发布主页面成功后,PRD 写明导航顺序时执行 `openyida nav-group order <appType> <页面/表单...>`;PRD 只写宽泛分组或缺少导航顺序时,执行 `openyida nav-group auto-order <appType>` 或 `openyida publish ... --auto-nav-order` 兜底,兜底顺序为门户/首页/工作台入口、业务办理、数据管理、经营分析、系统配置。完整应用默认写入 1-3 条核心普通表单示例记录;用户明确要求公开访问、截图验收、报表/大屏、数据桥深度接入或精细导航分组时,再追加对应技能。
144
-
145
- **默认加载边界**:只加载 `yida-app` 和当前阶段必需的子技能。`yida-design` 负责需求分析、产品定位、页面/表单/流程蓝图、主题色、各页面布局、资源创建顺序、页面实现交付顺序、导航顺序和验收标准,并输出 `prd/<项目名>/prd.md` 与 `prd/<项目名>/design.md`;`yida-create-app`、`yida-create-page`、`yida-create-form-page` 只有在目标资源缺失且本次意图允许创建时才加载;已有资源时进入对应 update / publish 分支。主页面实现读取 PRD 的业务输入,并直接读取 design.md 的主题、布局、材质、圆角、密度、组件和状态规则;走生成器时再派生 `page-spec.json`,并标记 `sourceOfTruth.prdFile/designFile`。页面默认走 Code Canvas;当用户明确要求普通自定义页面 JSX/Jsx 组件链路,或页面强依赖普通自定义页实例桥(`this.$(fieldId)` / `this.utils.yida.*` / `this.dataSourceMap` / 表单提交或字段双向绑定深度耦合)时,选择 `yida-custom-page`。数据源深接、数据管理和原生报表只在用户明确要求或 PRD 验收标准命中时追加。
146
-
147
- **Canvas 数据边界**:完整应用/真实交付页如果展示列表、看板或详情记录,必须优先把本轮真实 `appType/formUuid/fieldId` 写入 `page-spec.json` 的 `dataBinding.mode=form`;完整应用默认先写入 1-3 条核心普通表单记录再读取。未接真实表单且未写入 demo records 时,页面展示空态/入口,不用前端 seedRows 冒充业务数据。
148
-
149
- **字段级命令内置解析**:简单字段属性更新(例如“把备注字段改为必填”)直接调用 `openyida create-form update <appType> <formUuid> '[{"action":"update","label":"备注","changes":{"required":true}}]'`;CLI 会内部读取当前 schema,按 label/fieldId/tableLabel 定位字段,并在 JSON 中输出 resolved/updated evidence。`add-option`、`bind-datasource`、`validation`、`rule` 等字段级命令也可直接按 label 或已知 fieldId 操作,成功返回 compact `resolved`,失败返回 compact `diagnostics[].candidates`。只有字段解析仍歧义、需要底层 patch path,或页面代码/数据查询/流程/公式等确实需要多个 `fieldId` 时,才对目标表单执行一次 `openyida get-schema <appType> <formUuid> --field-map-json` 并合并到 `.cache/<项目名>-schema.json`。不要用 `head`/`tail`/`grep` 截断 get-schema stdout 作为字段证据,也不要因此对同一表单重复拉取多轮 schema。
150
-
151
- **Canvas 实现路径二选一**:走页面生成器时,先写业务化 `page-spec.json` 再生成可编译骨架,后续只读 manifest/摘要并小范围 Edit;不要立即 Read 大段源码再全量 Write 覆盖同一路径。若已经明确最终页面结构,直接 Write 最终 `.canvas.jsx`。
152
-
153
- **doneWhen**:`yida-app` 发布主页面成功、轻量导航自动排序已执行或给出明确 warning,并先输出 2-3 句业务交付总结、再给可访问主入口链接。到这里默认完成;不要发布后继续 TaskCreate、重复读技能或继续规划。
154
-
155
- **optionalAfterDone**:精细导航整理、公开访问、截图验证、数据源/连接器深度接入、报表/大屏,只在用户明确要求或 PRD 验收标准命中时执行;发布后的轻量导航自动排序、seed records 和表单详情页 formDetail CSS 注入是默认收尾,不算可选后置。表单页开发默认加载 `yida-form-detail` 做表单视觉引导,并把 Divider 分割线语义分组合并进字段 JSON;拿到真实 `formUuid` 后默认注入 formDetail CSS。
139
+ 完整应用 workflow 由`yida-app`负责、按阶段加载子技能完成应用的首轮开发&页面的开发。
156
140
 
157
141
  ---
158
142
 
@@ -45,14 +45,14 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
45
45
 
46
46
  ## 设计职责边界
47
47
 
48
- 完整应用只走统一编排。需求分析、产品定位、页面/表单/流程蓝图、主题色、各页面布局、交互状态和验收标准统一交给 `yida-design` 完成。
48
+ 完整应用只走统一编排。需求分析、产品定位、页面/表单/流程蓝图、主题色、各页面布局、交互状态和验收标准统一交给 `yida-design`;把 `yida-design` 输出的 PRD 写入 `prd/<项目名>/prd.md`,把所有页面共同遵守的 UI 视觉规则写入 `prd/<项目名>/design.md`;后续阶段消费 PRD 中的资源创建顺序、页面实现交付顺序、导航顺序和验收标准。具体产物边界见 `yida-design`。
49
49
 
50
50
  `yida-app` 只负责执行编排:
51
51
 
52
52
  - 解析并复用已有 app/page/form/process;
53
- - `yida-design` 输出的 PRD 写入 `prd/<项目名>/prd.md`,把所有页面共同遵守的 UI 视觉规则写入 `prd/<项目名>/design.md`;
53
+ - 加载 `yida-design` 并消费它输出的 `prd/<项目名>/prd.md` `prd/<项目名>/design.md`;
54
54
  - 按 PRD 的资源创建顺序创建缺失且允许创建的应用、表单、流程和主页面;其中表单/流程先于自定义页面;
55
- - 读取 PRD 的页面与功能设计、资源关系和页面实现交付顺序,读取 design.md 的主题、布局、材质、圆角、密度、组件和状态规则;只有走页面生成器或需要稳定交接时才派生 `page-spec.json`;
55
+ - 只有走页面生成器或需要稳定交接时才派生 `page-spec.json`;
56
56
  - 调用页面技能实现并发布;
57
57
  - 发布后按 PRD 的导航顺序执行轻量导航排序;
58
58
  - 只输出 2-3 句业务交付总结和一个主入口链接,业务总结在前、链接在后。
@@ -72,7 +72,7 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
72
72
  [Step 1] 解析资源上下文 → 合并本轮显式资源、workspace 配置、缓存和会话历史
73
73
 
74
74
  [Step 2] 产品设计 → use_skill("yida-design", "完整应用产品设计")
75
- ↓ yida-design 输出 prd/<项目名>/prd.md 和 prd/<项目名>/design.md;PRD 覆盖业务、资源、页面、顺序和验收,design.md 覆盖全局视觉系统
75
+ ↓ yida-design 输出 prd/<项目名>/prd.md 和 prd/<项目名>/design.md;产物职责见 yida-design
76
76
 
77
77
  [Step 3] 创建/复用应用 → use_skill("yida-create-app", "按 PRD 创建应用并获取 appType") → openyida create-app
78
78
  ↓ 已有 appType 时直接复用;缺少 app 且 allowCreate=true 时按 PRD 创建应用
@@ -90,7 +90,7 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
90
90
 
91
91
  [Step 7] 编写自定义页面代码 → use_skill("yida-canvas-custom-page", "生成 Code Canvas 主页面")
92
92
  ↓ 按 PRD 的页面实现交付顺序逐页实现;每个页面实现前必须读取 prd.md 与 design.md
93
- 先读取 PRD 中的业务对象、页面区块、数据来源和主操作,再读取 design.md 的主题、visualScaffold、材质、圆角、密度、组件和状态规则;需要生成器时派生 page-spec.json,可直接手写时跳过 spec 写最终 .canvas.jsx
93
+ 读取 yida-design 产物中的当前页面输入;需要生成器时派生 page-spec.json,可直接手写时跳过 spec 写最终 .canvas.jsx
94
94
  ↓ 字段映射优先来自 create/update 命令输出和 `.cache/<项目名>-schema.json`;同一表单不要重复 get-schema,除非页面/数据链路确实需要 fieldId 且缓存不完整
95
95
  ↓ 本轮已创建/解析业务表单且页面需要列表/看板/详情数据时,必须在 spec.dataBinding 写 mode=form + 真实 appType/formUuid/fieldId;深度接入再加载 yida-canvas-data-binding
96
96
  ↓ 明确要求普通自定义页面 JSX/Jsx 组件链路,或强依赖 this.$ / this.utils.yida.* / this.dataSourceMap 等实例桥时选择 yida-custom-page
@@ -105,21 +105,9 @@ description: 宜搭完整应用开发编排技能。对普通 OpenYida 应用做
105
105
 
106
106
  ## UI/体验集成点
107
107
 
108
- UI/体验不是 `yida-app` 内部模式,而是由 `yida-design` 在 Step 2 一次性完成:
108
+ UI/体验不是 `yida-app` 内部模式,由 `yida-design` 在 Step 2 一次性完成。`yida-app` 只读取 `yida-design` 产物,并把主题、页面、导航和状态要求交给后续创建、页面实现和发布技能。
109
109
 
110
- | 设计产物 | `yida-app` 如何消费 |
111
- |------|----------------|
112
- | PRD | 写入 `prd/<项目名>/prd.md`,作为表单、流程、页面、验收的业务依据 |
113
- | design.md | 写入 `prd/<项目名>/design.md`,作为所有页面共同遵守的视觉、主题、布局、组件和状态契约 |
114
- | 资源蓝图 + 资源创建顺序 | 决定 app、普通表单、流程表单、自定义页面和报表的创建/复用/更新顺序;表单/流程先于自定义页面 |
115
- | 主题色配置 | 平台预置主题才传给 `create-app/update-app --theme`;自定义色盘不传 theme,token 和注入方式写入 design.md;page-spec 只保存主题摘要和 designRefs |
116
- | 页面与功能设计 + 页面实现交付顺序 | 作为页面实现输入;走生成器时派生 `page-spec.json`,手写时直接按 PRD + design.md 写 Code Canvas 页面 |
117
- | 业务逻辑与交互状态 | 决定原生表单入口、抽屉、详情下钻、空/载/错态 |
118
- | 导航顺序 | 发布后执行轻量导航排序;导航顺序是用户看到的顺序,不等同于页面实现交付顺序 |
119
-
120
- 主题补充:主题先由 `yida-design` 按业务气质判断。`podBlue`、`podGreen`、`podOrange` 等只是平台预置候选;只有 PRD 摘要和 design.md 都写明 `shouldPassCreateAppTheme=true` 且 `themePresetKey` 命中平台 key,`yida-app` 才把它传给 `create-app/update-app --theme`。若 PRD 给的是自定义品牌色、渐变色盘或非预置主题名,创建应用时不显式传 `theme/colour`,tokens 必须写在 design.md,由页面实现读取 design.md 后注入 `style#yida-global-theme` / `customThemeStyle.tokens`。
121
-
122
- 首次生成面向用户的主页面时,先执行 `use_skill("yida-design", "完整应用产品设计")` 产出 `prd/<项目名>/prd.md` 和 `prd/<项目名>/design.md`,再由 `yida-app` 从 PRD 提炼业务 spec,并在实现阶段直接读取 design.md 的主题、布局、材质、圆角、密度、组件和状态规则,交给 `yida-canvas-custom-page` 或 `yida-custom-page` 落地。用户明确要求“好看 / 去 AI 味 / 高级视觉 / 品牌化 / 多页面体验”时,仍然在 `yida-design` 内补足 design.md 设计细节,不在 `yida-app` 内另分模式。
110
+ 主题 key 是否传给 `create-app/update-app --theme`,只消费 `yida-design` 产物中的明确结论;只有 PRD 摘要和 design.md 都写明 `shouldPassCreateAppTheme=true` 且 `themePresetKey` 命中平台 key 时才传主题,没有命中平台预置主题 key 时不传主题。
123
111
 
124
112
  ## 页面链路原则
125
113
 
@@ -140,11 +128,11 @@ UI/体验不是 `yida-app` 内部模式,而是由 `yida-design` 在 Step 2 一
140
128
 
141
129
  ## 页面规格优先
142
130
 
143
- 完整应用页面先消费 `yida-design` 的 PRD 与 design.md,再决定用生成器入口还是直接手写页面。`prd.md` 和 `design.md` 是唯一设计事实源;`page-spec.json` 只是从两者派生的实现 handoff / 生成器输入,不是第三份设计文件。实现阶段不读取内置页面源码来决定页面内容、布局或视觉风格。
131
+ 完整应用页面先消费 `yida-design` 产物,再决定用生成器入口还是直接手写页面。`prd.md` 和 `design.md` 是唯一设计事实源;`page-spec.json` 只是派生的实现 handoff / 生成器输入,不是第三份设计文件。实现阶段不读取内置页面源码来决定页面内容、布局或视觉风格。
144
132
 
145
133
  使用生成器入口时,必须把当前页面从 `prd.md + design.md` 派生成业务化 `page-spec.json`,至少覆盖:
146
134
 
147
- - PRD 中已有 `pageSpecHandoff` 时,优先从 `pageSpecHandoff` 提取 `pageStructure`、`scene`、`contentBlocks`、`themeSummary`、`designFile`、`designRefs`、`dataBinding` 和 `primaryAction`;`page-spec.json` 只保存这些业务参数和 design 指针。实现页面时再读取 `designFile` 中对应章节,获取 `visualScaffold`、`backgroundLayer`、`surfaceMaterial`、`colorRoles`、`depthRule`、`roundedRule`、`densityRule`、组件和状态规则。
135
+ - PRD 中已有 `pageSpecHandoff` 时,优先从 `pageSpecHandoff` 提取 `pageStructure`、`scene`、`contentBlocks`、`themeSummary`、`designFile`、`designRefs`、`dataBinding` 和 `primaryAction`;`page-spec.json` 只保存这些参数和 design 指针。
148
136
  - `sourceOfTruth`:写明 `prdFile`、`designFile`、`designRefs` 和 `conflictPolicy: "prd-design-win"`。若 `page-spec.json` 与 PRD/design.md 不一致,丢弃或重生成 spec;不得用 spec 覆盖 PRD/design.md。
149
137
  - `brandName` / `tagline` / `heroText`:使用当前应用的业务名称、角色和问题域,不沿用模板默认标题。
150
138
  - `features`:写真实业务对象、模块入口或处理事项,不写“统一入口 / 状态跟进 / 流程闭环”这类通用模板卖点。
@@ -154,9 +142,9 @@ UI/体验不是 `yida-app` 内部模式,而是由 `yida-design` 在 Step 2 一
154
142
  - `themeSummary`:只写应用主题色和风格摘要,并保持与 `design.md` 一致;不写 token、surface、layout、componentRecipe 等 UI 设计规则。
155
143
  - 官网/品牌页还必须写 `assets` 或明确素材缺口;看板/列表/详情页优先写 `dataBinding`、字段映射或表单链接。
156
144
 
157
- UI 设计规则必须来自 `yida-design` 的 design.md;页面区块、文案、指标、图片、表单入口和状态说明都来自 PRD 当前业务。
145
+ 页面实现必须消费 `yida-design` 的当前产物,不从模板或内置示例反推业务和视觉。
158
146
 
159
- 生成后检查命令输出和 `.openyida-page.json` 里的 `domainFidelity.status`:只有 `domain-ready` 才能作为真实业务页面交付。修复路径按事实源分流:
147
+ 生成后检查命令输出和 `.openyida-page.json` 里的 `domainFidelity.status`:只有 `domain-ready` 才能作为真实业务页面交付。修复路径按事实源分流;业务或视觉事实源缺失时先回写 `prd.md` / `design.md` 并重生成 spec,只有实现偏差才小范围改源码。
160
148
 
161
149
  | 问题类型 | 必须修改哪里 | 不允许的做法 |
162
150
  | --- | --- | --- |
@@ -169,8 +157,8 @@ UI 设计规则必须来自 `yida-design` 的 design.md;页面区块、文案
169
157
 
170
158
  页面实现路径二选一:
171
159
 
172
- - **生成器入口**:页面结构已在 PRD 写清后,用页面生成器读取 `<page-spec.json>` 生成可编译骨架。生成后只读取 `.openyida-page.json` / CLI 摘要判断 `domainFidelity` 和 dataBinding 状态;业务或视觉事实源缺失时先回写 `prd.md` / `design.md` 并重生成 spec,只有实现偏差才基于生成文件做小范围 Edit/patch。
173
- - **手写实现**:如果 PRD 已经明确页面结构和数据桥,且 design.md 已经写清视觉规则,直接 Write 最终 `.canvas.jsx`,再做本地快检和 publish。
160
+ - **生成器入口**:页面结构已明确时,用页面生成器读取 `<page-spec.json>` 生成可编译骨架。生成后只读取 `.openyida-page.json` / CLI 摘要判断 `domainFidelity` 和 dataBinding 状态;产物事实缺失时先回写 `prd.md` / `design.md` 并重生成 spec,只有实现偏差才基于生成文件做小范围 Edit/patch。
161
+ - **手写实现**:如果 `yida-design` 产物已经明确页面结构、数据桥和视觉规则,直接 Write 最终 `.canvas.jsx`,再做本地快检和 publish。
174
162
  - **JSX 文案安全**:中文业务文案只能写成纯文本 `所有级别` 或带引号字符串 `{'所有级别'}`;不能写 `{所有级别}`、`{处理中}` 这类裸中文表达式,否则 JSX 会按变量求值并在运行时报 `所有级别 is not defined`。
175
163
  - **emoji 硬门禁**:表单字段 JSON、`page-spec.json`、`.canvas.jsx` / `.oyd.jsx` 源码、发布 Schema 和产物文件路径都不能包含 emoji。OpenYida 报 emoji 错误时修改字段文案、spec、源码或路径;不要用 `--skip-lint`、重复 create/publish 或全量 rewrite 试图绕过。若 emoji 原本是操作、状态、导航或空态图标,Code Canvas 按 `design.md.iconSystem` 改成 `lucide-react` 或 `@ant-design/icons` 标准 import;普通 JSX 不支持 import,只能使用已验证运行时脚本/global 加载这两类图标库,加载条件不满足时切到 Code Canvas。不得退成 CSS 图形、字母占位或临时 SVG。
176
164
 
@@ -179,10 +167,10 @@ UI 设计规则必须来自 `yida-design` 的 design.md;页面区块、文案
179
167
  1. 先从 PRD 和 design.md 派生当前业务自己的 `page-spec.json`,只写 design.md 指针和主题摘要;不要把 design.md 的视觉规则复制进 spec;
180
168
  2. 再执行页面生成器生成 Code Canvas 骨架;
181
169
  3. 读取 manifest / CLI 摘要的 `domainFidelity`,若仍是草稿或业务化不足,按修复路径先回写 `prd.md` 或 `design.md`,再重生成 spec;只有实现偏差才小范围改源码;
182
- 4. 按 PRD 扩展交互和真实数据,按 design.md 打磨视觉;
170
+ 4. 按 `yida-design` 产物扩展交互、真实数据和视觉;
183
171
  5. 验证所有参数名称与 CLI 一致。
184
172
 
185
- 表单页开发默认加载 `use_skill("yida-form-detail", "表单视觉引导与详情页样式默认注入")`,将填写路径、字段密度和 Divider 分割线语义分组合并进 `yida-create-form-page` 的字段 JSON。表单详情页 CSS 优化不走 `openyida publish`;拿到真实 `formUuid` 后默认由 `yida-form-detail` / `openyida form-detail-style apply` 写入表单 Schema JS,在 `openyidaThemeDidMount` 中统一注入全局主题并按 formDetail 条件注入详情页样式,重复执行必须幂等。
173
+ 表单页开发默认加载 `use_skill("yida-form-detail", "表单视觉引导与详情页样式默认注入")`,将填写路径、字段密度和 Divider 分割线语义分组合并进 `yida-create-form-page` 的字段 JSON,确保字段结构有 Divider 分组。表单详情页 CSS 优化不走 `openyida publish`;拿到真实 `formUuid` 后默认由 `yida-form-detail` / `openyida form-detail-style apply` 写入表单 Schema JS,在 `openyidaThemeDidMount` 中统一注入全局主题并按 formDetail 条件注入详情页样式,重复执行必须幂等;doneWhen 需要看到 formDetail CSS 已注入或有明确阻塞原因。
186
174
 
187
175
  ## 完整应用统一编排阶段
188
176
 
@@ -190,12 +178,12 @@ UI 设计规则必须来自 `yida-design` 的 design.md;页面区块、文案
190
178
  |------|--------|----------|----------|
191
179
  | 0. 解析资源上下文 | 无 | 合并本轮显式资源、已绑定资源上下文、workspace 配置/缓存、会话历史;本轮显式目标覆盖已绑定上下文;判定 app/page/form/process 的 `source` 和 `allowCreate` | 明确复用、创建缺口或需要 ask_human |
192
180
  | 1. 设计前上下文 | 无 | 合并本轮显式资源、workspace 配置/缓存、会话历史,确认已有 `appType` 或 `allowCreate=true`;不在本阶段创建资源 | PRD 所需的目标组织、应用名称候选、资源复用边界明确 |
193
- | 2. 产品设计 | `yida-design` | 输出 `prd/<项目名>/prd.md` 与 `prd/<项目名>/design.md`;PRD 必须包含应用基本信息、应用配置、数据结构、页面与功能、业务逻辑、交互状态、资源蓝图、资源创建顺序、页面实现交付顺序、导航顺序和验收标准;design.md 必须包含主题色、视觉 DNA、各页面场景配方、布局密度、圆角规则、组件规则和状态规则。写/更新 `.cache/<项目名>-schema.json` 本地 ID 映射位置 | 业务语义、视觉契约、页面布局、三种顺序、资源蓝图和 ID 存储位置明确 |
181
+ | 2. 产品设计 | `yida-design` | 输出 `prd/<项目名>/prd.md` 与 `prd/<项目名>/design.md`;产物职责和输出格式见 `yida-design`。写/更新 `.cache/<项目名>-schema.json` 本地 ID 映射位置 | `yida-design` 产物可被后续阶段直接消费,ID 存储位置明确 |
194
182
  | 3. create/reuse app | `yida-create-app` 仅在 app 缺失且允许创建时加载;不自动修改应用名称 | 已有 `appType`/应用 URL/已绑定 app 时直接复用;否则按 PRD 创建应用并提取真实 `appType` | 拿到真实目标 `appType`,且不会重复创建同类 app |
195
183
  | 4. resolve forms/processes | `yida-form-detail` 视觉引导与详情页样式默认注入,再 `yida-create-form-page`;PRD 命中审批/流程时加载 `yida-create-process` | 已有目标表单时 update/patch/rule/bind-datasource;创建或更新字段结构前先用 `yida-form-detail` 确定表单视觉引导、填写路径和 Divider 分割线语义分组,再由 `yida-create-form-page` 写字段 JSON;PRD 包含流程表单时在自定义页面之前创建流程表单;简单字段属性更新直接用 compact changes 让 CLI 内部按 label 读 schema/定位字段并输出 resolved evidence;缺少支撑 MVP 的核心表单且允许创建时才 create;字段配置文件写入 `.cache/openyida/<项目名>/`;页面/数据/流程/公式确需多字段映射时,对每个目标表单最多一次性获取完整 `--field-map-json` 并合并写回 `.cache/<项目名>-schema.json`;拿到或确认真实 `formUuid` 后默认执行或补齐 formDetail CSS 注入 | 拿到或确认表单/流程表单 `formUuid`,字段结构有 Divider 分组,formDetail CSS 已注入或有明确阻塞原因,必要时拿到真实 `fieldId` |
196
184
  | 5. seed records | `yida-data-management` | 完整应用默认给本轮新建或页面数据源依赖的核心普通表单写入 1-3 条业务化 seed records。先 `openyida get-schema <appType> <formUuid> --field-map-json` 获取真实字段 ID,生成字段值时遵守字段类型和日期毫秒时间戳规则,再逐条执行 `openyida data create form <appType> <formUuid> --data-file ...` 或 `--data-json ...`,最后 `openyida data query form` 抽查至少 1 条。用户明确不要造数、表单是配置字典/权限表、或字段缺少可安全构造值时可跳过并说明原因 | 核心表单有 1-3 条真实表单记录,或有明确跳过原因和空态方案 |
197
185
  | 6. reserve main page | `yida-create-page` 仅在主页面缺失且允许创建时加载 | 已有页面 URL / `formUuid` / 已绑定页面时直接作为主页面;若需要首页/工作台/智能助手/门户门面且缺少主页面,在表单/流程创建和 seed records 完成后创建空 display page 占位,暂不写最终源码 | 拿到真实主页面 `formUuid`,且不会重复创建页面 |
198
- | 7. 编写/更新页面 | 默认 `yida-canvas-custom-page`;明确要求 JSX/Jsx 组件链路或实例桥强依赖时选择 `yida-custom-page` | 按 PRD 的页面实现交付顺序读取页面场景、业务区块、素材策略、原生表单入口和业务化自检;同时读取 design.md themeProfile、visualScaffold、材质、圆角、密度、组件和状态规则,再生成或修改页面源码。实现 PRD 里的核心首屏和核心操作。展示业务列表/看板/详情记录时,必须接本轮真实表单 `dataBinding.mode=form`;默认读取 Step 5 写入的 1-3 条 demo records,没写入成功才展示空态和登记入口 | 本地源码通过对应页面技能的基础校验;未执行 publish 时仍是“源码已修改,尚未发布” |
186
+ | 7. 编写/更新页面 | 默认 `yida-canvas-custom-page`;明确要求 JSX/Jsx 组件链路或实例桥强依赖时选择 `yida-custom-page` | 按 `yida-design` 产物和页面实现交付顺序生成或修改页面源码。展示业务列表/看板/详情记录时,必须接本轮真实表单 `dataBinding.mode=form`;默认读取 Step 5 写入的 1-3 条 demo records,没写入成功才展示空态和登记入口 | 本地源码通过对应页面技能的基础校验;未执行 publish 时仍是“源码已修改,尚未发布” |
199
187
  | 8. 发布页面 | `yida-publish-page` | 按页面链路校验后发布到已解析主页面:Canvas `.canvas.jsx` 使用 `openyida publish` 的 Canvas 编译阶段或 `compileCanvasLocal` 快检;普通自定义页面 `.oyd.jsx` / `.jsx` 跑 `check-page` / `compile`;再执行 `openyida publish <source> <appType> <displayPageFormUuid> --auto-nav-order` 发布主页面。发布成功后,PRD 写明导航顺序时执行 `openyida nav-group order <appType> <页面/表单...>`;PRD 缺少明确页面清单时用 `--auto-nav-order` / `nav-group auto-order` 兜底 | 发布成功、获得可访问 URL,且 PRD 导航顺序已执行,或兜底自动排序已执行/给出明确 warning |
200
188
  | 9. 输出结果 | 无 | 先写 2-3 句业务交付总结,再给一个主入口链接。若本轮意图是新增/修改/发布某个具体页面,主入口是当前页面 URL;其他完整应用、建表单、建流程、权限、主题、导航或批量资源场景,主入口是应用首页 `{base_url}/{appType}/workbench`。不要输出表格、资源 ID 清单或长列表,除非用户明确要排障 ID | 用户先理解完成了哪些业务能力,再打开唯一主入口 |
201
189
 
@@ -24,7 +24,7 @@
24
24
  - 禁止用 160px 以上的大空态白卡显示“暂无数据”;空态应是薄行、列表内空态或右侧提示条,并带登记/发布/刷新等下一步动作。
25
25
  - 快捷入口不要做孤立图标大卡片阵列;高频动作放按钮组、工具条或 40-56px 的紧凑入口,低频动作折叠到更多。
26
26
  - 首屏必须至少有一个任务/动态/最近记录/待处理列表承接真实工作流;若当前没有记录,显示可执行空态,而不是把空白面积留给装饰。
27
- - 页面整体至少包含 10 个有业务目的的区块以上;这些区块应通过密度、主次、分栏和列表节奏形成丰富度,不通过重复大卡片形成面积。计数按区块组算,KPI 子项、快捷入口子项和列表行不能分别计数。
27
+ - 页面整体推荐包含 8-10 个有业务目的的区块以上;这些区块应通过密度、主次、分栏和列表节奏形成丰富度,不通过重复大卡片形成面积。区块数量不是实现准出硬门槛,窄场景或用户要求精简时可以更少。计数按区块组算,KPI 子项、快捷入口子项和列表行不能分别计数。
28
28
 
29
29
  ## 默认圆润高密与呼吸感落地
30
30
 
@@ -47,7 +47,7 @@ Code Canvas 必须把 `design.md` 的 `roundedRule`、`densityRule` 和 `breathi
47
47
  3. `sectionRhythm` / `breathingRule`:确定首屏主次、区块间距、阅读顺序、组内/组间节奏和移动端折叠间距。
48
48
  4. `densityRule`:控制卡片高度、列表行高、按钮尺寸和信息密度。
49
49
  5. `componentRecipe`:统一按钮、入口、标签、图标、列表、图表、空态和弹层。
50
- 6. `acceptanceChecks`:逐项检查 10+ contentBlocks、无大空白卡、主色跟随应用主题、KPI/快捷入口子项不计数、移动端不挤压。
50
+ 6. `acceptanceChecks`:逐项检查 contentBlocks 是否支撑业务目标、无大空白卡、主色跟随应用主题、KPI/快捷入口子项不计数、移动端不挤压;区块数量不作为硬门槛。
51
51
 
52
52
  ## 背景层实现规则
53
53
 
@@ -20,7 +20,7 @@ PRD 写有 `pageSpecHandoff` 时,可以把 `pageSpecHandoff` 转成 `page-spec
20
20
  - spec 与 PRD/design.md 冲突时,以 PRD/design.md 为准,重新生成 spec;不要修改 PRD/design.md 来迎合旧 spec。
21
21
  - 手写页面且结构清楚时可以跳过 `page-spec.json`,但源码实现备注必须能说明已读取 `prd.md` 和 `design.md`。
22
22
 
23
- 实现阶段不再从 PRD 里反推视觉,也不直接读取 `references/style-designs/`。该目录只在 yida-design 阶段提供 `design.md` 结构模板;Code Canvas 只遵守当前项目的 `design.md`。工作台/业务首页通常需要圆润紧凑状态摘要、高频动作、待办/动态/最近记录和右侧上下文;实现阶段用这些结构替代“4 个等宽大 KPI 白卡 + 图标快捷卡 + 大空态白卡”。列表/管理页通常需要顶部视觉区、搜索筛选区、左侧列表或表格、右侧详情预览、错误/空态下一步动作;实现阶段用这些结构替代单个渐变标题、单个指标卡和大块空白提示。工作台、首页、门户、看板、展示页和业务入口页至少落地 10 个有业务目的的区块以上,区块可以紧凑组合,不能用重复 KPI 卡、重复快捷入口或大空白卡凑数;KPI 子项、快捷入口子项和列表行不计入区块数量。
23
+ 实现阶段不再从 PRD 里反推视觉,也不直接读取 `references/style-designs/`。该目录只在 yida-design 阶段提供 `design.md` 结构模板;Code Canvas 只遵守当前项目的 `design.md`。工作台/业务首页通常需要圆润紧凑状态摘要、高频动作、待办/动态/最近记录和右侧上下文;实现阶段用这些结构替代“4 个等宽大 KPI 白卡 + 图标快捷卡 + 大空态白卡”。列表/管理页通常需要顶部视觉区、搜索筛选区、左侧列表或表格、右侧详情预览、错误/空态下一步动作;实现阶段用这些结构替代单个渐变标题、单个指标卡和大块空白提示。工作台、首页、门户、看板、展示页和业务入口页推荐落地 8-10 个有业务目的的区块以上;区块可以紧凑组合,不能用重复 KPI 卡、重复快捷入口或大空白卡凑数;KPI 子项、快捷入口子项和列表行不计入区块数量。窄场景或用户要求精简时可以更少,不应因此阻塞实现。
24
24
 
25
25
  如果当前 `design.md` 缺少 `roundedRule`、`densityRule` 或 `breathingRule`,先回写设计文件再实现。默认业务页应写清卡片 padding >20px、卡片 gap <20px、卡片圆角 0-32px;状态摘要、任务列表、动作条和空态保持紧凑,不得用额外 margin、超宽空状态框或空白高度制造“高级感”。
26
26
 
@@ -149,7 +149,7 @@ PRD 给出品牌色、色值、独立品牌/活动页诉求,或明确要求做
149
149
  | `designFile` | 当前项目设计契约路径 | 来自 `yida-design` Step 5 |
150
150
  | `designRefs` | 当前页面引用的 design.md 章节 ID | 来自 PRD 的 pageSpecHandoff |
151
151
  | `themeSummary` | 应用主题色、风格关键词、themeScope 摘要 | 来自 PRD 摘要,必须与 design.md 一致 |
152
- | `contentBlocks` | 页面区块清单,工作台/首页/门户/看板/展示页/业务入口页不少于 10 个有业务目的的区块;KPI 组、快捷入口组、列表组各只算 1 个区块 | 来自 `yida-design` Step 4 |
152
+ | `contentBlocks` | 页面区块清单,工作台/首页/门户/看板/展示页/业务入口页推荐 8-10 个有业务目的的区块以上,但不作为硬门槛;KPI 组、快捷入口组、列表组各只算 1 个区块 | 来自 `yida-design` Step 4 |
153
153
  | `domainFidelity` | 实现后由 CLI 回填,标记业务化程度 | 无需手写 |
154
154
 
155
155
  示例:
@@ -211,9 +211,9 @@ PRD 给出品牌色、色值、独立品牌/活动页诉求,或明确要求做
211
211
  | `official-homepage` | Real-scene hero、Product/service visual、Process/space story、Visit/service section、CTA |
212
212
  | `data-screen` | Command map、Metric grid、Rank panel、Screen insight header |
213
213
 
214
- 工作台的状态摘要必须是 64-88px 圆润紧凑状态条,不是 180px 高的大白卡,也不是横跨整页但内容稀疏的空矩形;快捷入口必须有分组和主次,不能平铺成图标卡阵列;待办、动态、最近记录、洞察、提醒和右侧上下文至少组合成 10 个业务目的区块。空数据也用薄空态行 + 主操作入口,不渲染大块空白卡片。
214
+ 工作台的状态摘要必须是 64-88px 圆润紧凑状态条,不是 180px 高的大白卡,也不是横跨整页但内容稀疏的空矩形;快捷入口必须有分组和主次,不能平铺成图标卡阵列;待办、动态、最近记录、洞察、提醒和右侧上下文推荐组合成 8-10 个业务目的区块以上,但不作为硬门槛。空数据也用薄空态行 + 主操作入口,不渲染大块空白卡片。
215
215
 
216
- 展示型 Canvas 页面验收时检查 `contentBlocks` 或源码结构:工作台、首页、门户、看板、展示页和业务入口页至少有 10 个有业务目的的区块以上;每个区块承担不同任务,例如判断状态、发起动作、筛选、处理待办、查看动态、看洞察、看异常、进入详情、处理空态或补充上下文。若 PRD 只写“`KPI 卡片: 学生总数, 课程总数, 本月出勤率, 平均分`、`快捷入口: 录入学生/登记成绩/记录考勤/管理课程`、`最近成绩列表`、`最近考勤记录`”,实现前必须退回补齐 `contentBlocks`,因为这只构成 4 个聚合区块。
216
+ 展示型 Canvas 页面验收时检查 `contentBlocks` 或源码结构:工作台、首页、门户、看板、展示页和业务入口页应有足够多有业务目的的区块;每个区块承担不同任务,例如判断状态、发起动作、筛选、处理待办、查看动态、看洞察、看异常、进入详情、处理空态或补充上下文。区块数量不作为阻塞实现的硬门槛。若 PRD 只写“`KPI 卡片: 学生总数, 课程总数, 本月出勤率, 平均分`、`快捷入口: 录入学生/登记成绩/记录考勤/管理课程`、`最近成绩列表`、`最近考勤记录`”,实现前建议补充 `contentBlocks`;若业务确实是窄场景,可以继续实现并说明取舍。
217
217
 
218
218
  所有展示型页面都按当前项目 `design.md` 的 `visualScaffold` 实现。若 PRD 只有业务区块、design.md 只有视觉形容词,没有明确 `layoutRecipe` / `surfaceMap` / `componentRecipe`,先回到 `prd/<项目名>/design.md` 补齐:
219
219
 
@@ -59,7 +59,7 @@ description: >
59
59
  10. **默认主题先做业务判断**:工作台、门户、列表、详情、普通看板和数据大屏默认都是浅底 / light 模式,但主色不固定为 `podBlue` 或 #1677ff;先根据行业、品牌、业务情绪和视觉目标做创意色彩判断,选择最贴合当前业务的色彩关系,禁止套用“科技=蓝、宠物=橙、法律=蓝”这类刻板配色。若命中平台预置主题 key,再传给 `create-app/update-app --theme`;若是任意自定义色盘,创建应用时不显式传 `theme/colour`,PRD 只写主题色和风格摘要,`style#yida-global-theme` / `customThemeStyle.tokens` 注入方案写入 design.md。只有用户明确说暗色/深色/夜间/高对比时才用深色沉浸。
60
60
  默认浅底业务屏,只有用户明确说暗色/深色/夜间/高对比时才用深色沉浸。
61
61
  11. **页面布局要到可实现粒度**:每个页面至少写清顶部/左侧/主体/右侧/底部区域、核心组件、信息密度、主操作位置、PC/移动端差异和空/载/错态。
62
- 12. **页面丰富度保底**:工作台、首页、门户、看板、展示页和业务入口页至少规划 10 个有业务目的的区块以上,例如上下文标题、状态摘要、主操作、筛选、任务列表、最近记录、动态流、洞察、提醒、空态行动、右侧上下文和底部辅助信息。计数按“区块组”算,不按子项算:`KPI 卡片: 学生总数, 课程总数, 出勤率, 平均分` 只能算 1 个状态摘要区块,`快捷入口: 录入学生/登记成绩/记录考勤/管理课程` 只能算 1 个动作区块;不能用重复 KPI 卡、重复快捷入口或大空白卡凑数量。
62
+ 12. **页面丰富度建议**:工作台、首页、门户、看板、展示页和业务入口页推荐规划 8-10 个有业务目的的区块以上,例如上下文标题、状态摘要、主操作、筛选、任务列表、最近记录、动态流、洞察、提醒、空态行动、右侧上下文和底部辅助信息。区块数量不是硬门槛,窄场景、单任务页面或用户明确要求精简时可以更少,但要写清每个区块的业务目的和取舍原因。计数按“区块组”算,不按子项算:`KPI 卡片: 学生总数, 课程总数, 出勤率, 平均分` 只能算 1 个状态摘要区块,`快捷入口: 录入学生/登记成绩/记录考勤/管理课程` 只能算 1 个动作区块;不能用重复 KPI 卡、重复快捷入口或大空白卡凑数量。
63
63
  13. **工作台禁低密大卡片模板**:工作台 / 业务首页不能用“标题 + 4 个等宽大 KPI 白卡 + 图标快捷卡 + 大空态白卡”撑首屏。默认改成紧凑状态摘要条、任务/动态列表、最近记录、右侧上下文面板和高频动作;没有真实数据时也展示薄空态行 + 登记入口,不铺大块空白卡片。
64
64
  14. **默认圆润高密且有呼吸感**:业务工具页默认使用圆润形状、紧凑信息密度和清晰呼吸节奏。`design.md` 必须写清 `roundedRule`、`densityRule` 和 `breathingRule`:卡片 padding 必须大于 20px(默认 22-28px),卡片与卡片的 gap 必须小于 20px(默认 12-18px),卡片圆角范围 0-32px(业务卡片默认 20-24px),控件 10-14px,状态摘要 64-88px,动作条 40-56px,列表行 44-56px,空态 88-120px 内;页面边距、卡片 gap 和卡片 padding 要形成可扫读的分组节奏。呼吸感来自对齐、分组、层级和节奏,不来自额外 margin、超宽空 KPI 框或空白卡撑页面。
65
65
  15. **背景与卡片必须有层次对比**:默认业务页背景保持浅色调、清爽但不能与卡片相近或相同。`design.md` 必须写清 `surfaceContrast`:白色/浅色背景配有边框卡片;浅灰背景(如 `#F3F4F6`)配白色无边框卡片;浅彩色背景(如浅蓝、浅暖灰)配白色无边框卡片;渐变背景配玻璃感卡片。禁止浅底白卡无边框、同色背景同色卡片或只有阴影没有色差/边框的层次。
@@ -2,12 +2,12 @@
2
2
 
3
3
  本文件用于 Step 4、Step 5 和 Step 6 输出前自检。页面设计必须先通过这些门禁,再交给实现阶段。PRD 负责业务结构、应用主题色和风格摘要,`design.md` 负责完整 UI 设计规则;不要把 `design.md` 内容复制进 PRD。
4
4
 
5
- ## 1. 区块数量门禁
5
+ ## 1. 区块丰富度建议
6
6
 
7
- - 工作台、首页、门户、看板、展示页和业务入口页至少有 10 个有业务目的的 `contentBlocks`。
7
+ - 工作台、首页、门户、看板、展示页和业务入口页推荐有 8-10 个有业务目的的 `contentBlocks` 以上,但不作为准出硬门槛。
8
8
  - 计数按区块组算:KPI 组、快捷入口组、列表组、图表组各只算 1 个区块。
9
9
  - 每个区块必须写清目的、数据来源、主操作和状态。
10
- - 重复指标、重复入口、列表行、图表点、装饰块、空白容器不计数。
10
+ - 重复指标、重复入口、列表行、图表点、装饰块、空白容器不计数;窄场景可以少于参考数量,并说明业务取舍。
11
11
 
12
12
  ## 2. 源码槽位门禁
13
13
 
@@ -110,7 +110,7 @@
110
110
  - pageSpecHandoff:
111
111
  - pageStructure:<workbench / dashboard-overview / business-list / detail-profile / split-pane-detail / portal-shell-home / official-homepage / data-screen>
112
112
  - scene:<workbench / dashboard / list / detail / landing / screen>
113
- - contentBlocks:<10+ 区块;KPI/快捷入口/列表/图表子项不分别计数>
113
+ - contentBlocks:<推荐 8-10 个区块以上;KPI/快捷入口/列表/图表子项不分别计数>
114
114
  - themeSummary:<应用主题色 / 风格关键词 / themeScope 摘要;必须与 design.md 一致>
115
115
  - designFile:<prd/<项目名>/design.md>
116
116
  - designRefs:<themeProfile / sceneRecipes.<scene> / components.<name> / states.<name>>
@@ -5,7 +5,7 @@
5
5
  ## 使用规则
6
6
 
7
7
  1. 先按页面场景选择一套配方。
8
- 2. 把 `contentBlocks` 映射到配方槽位,少于 10 个区块先回到 Step 4 补齐。
8
+ 2. 把 `contentBlocks` 映射到配方槽位;工作台、首页、门户、看板、展示页和业务入口页推荐 8-10 个区块以上。如果业务是窄场景或用户要求精简,可以减少区块,并在 Step 4 说明取舍。
9
9
  3. 把配方写入 `design.md` 的 `visualScaffold`:`layoutRecipe`、`surfaceMap`、`sectionRhythm`、`densityRule`、`breathingRule`、`componentRecipe`、`emptyStateRecipe`、`responsiveSlots`、`acceptanceChecks`。PRD 只引用 `designFile/designRefs` 和主题风格摘要。
10
10
  4. 实现阶段逐项消费,不从“高级 / 简洁 / 好看”等形容词直接写 CSS。
11
11
  5. 默认业务页采用圆润高密且有呼吸感的规则:卡片 padding 默认 22-28px 且必须大于 20px,卡片与卡片的 gap 默认 12-18px 且必须小于 20px,卡片圆角范围 0-32px,控件 10-14px;状态摘要、动作条、列表和空态必须紧凑,间距服务分组和扫读,不用额外 margin 或空白高度撑面积。
@@ -116,7 +116,7 @@ KPI 子项、图表点、列表行不分别计数。
116
116
  ### acceptanceChecks
117
117
 
118
118
  - 三栏比例存在,不退化为四张等宽 KPI 卡。
119
- - 至少 10 个有业务目的的 `contentBlocks`。
119
+ - `contentBlocks` 推荐 8-10 个区块以上,不作为硬门槛。
120
120
  - 首屏有主图、风险、事件流和动作入口。
121
121
  - 左侧指标轨不是四个大白卡。
122
122
  - 右侧事件流不是空白卡。
@@ -71,7 +71,7 @@
71
71
  - 详情页:<原生 formDetail / 自定义详情页 / 抽屉详情>
72
72
  - 需要设计的区块:
73
73
  - <区块名称>:<区块目的;数据来源;主操作;状态>
74
- - 工作台、首页、门户、看板、展示页和业务入口页必须逐条列出至少 10 个 `contentBlocks`;KPI 组、快捷入口组、列表组各只算 1 个区块,不能用子项或列表行凑数。
74
+ - 工作台、首页、门户、看板、展示页和业务入口页推荐逐条列出 8-10 个 `contentBlocks` 以上,但这不是硬门槛。KPI 组、快捷入口组、列表组各只算 1 个区块,不能用子项或列表行凑数;窄场景可以更少,并说明取舍理由。
75
75
  - 自定义页面需要逐个写清首屏、筛选、列表/卡片、图表、表单入口、详情抽屉、空态等区块。
76
76
  - 布局骨架:<顶部概览 / 筛选区 / 表格 / 卡片列表 / 图表区 / 右侧详情等>
77
77
  - 核心组件:<KPI / 快捷入口 / 表格 / 图表 / 表单入口 / 状态标签等>
@@ -80,7 +80,7 @@
80
80
  - pageSpecHandoff:
81
81
  - pageStructure:<workbench / dashboard-overview / business-list / detail-profile / split-pane-detail / portal-shell-home / official-homepage / data-screen>
82
82
  - scene:<workbench / dashboard / list / detail / landing / screen>
83
- - contentBlocks:<10+ 区块;KPI/快捷入口/列表/图表子项不分别计数>
83
+ - contentBlocks:<推荐列出 8-10 个业务区块以上;KPI/快捷入口/列表/图表子项不分别计数>
84
84
  - themeSummary:<应用主题色 / 风格关键词 / themeScope 摘要;必须与 design.md 一致,不写 token 和视觉规则>
85
85
  - designFile:<prd/<项目名>/design.md>
86
86
  - designRefs:<themeProfile / sceneRecipes.<scene> / components.<name> / states.<name>>
@@ -117,7 +117,7 @@
117
117
 
118
118
  | 资源 | 类型 | 用途 | 关键字段 / 功能 | 创建策略 |
119
119
  | --- | --- | --- | --- | --- |
120
- | <主页面> | display-page / main | <入口和概览> | <10+ contentBlocks:标题上下文、筛选、摘要、主操作、待办、最近记录、动态、提醒、右侧上下文、空态行动等> | <复用 / 创建> |
120
+ | <主页面> | display-page / main | <入口和概览> | <contentBlocks:推荐 8-10 个区块以上,如标题上下文、筛选、摘要、主操作、待办、最近记录、动态、提醒、右侧上下文、空态行动等;不作为硬门槛> | <复用 / 创建> |
121
121
  | <业务表单> | normal-form | <数据录入和管理> | <核心字段> | <复用 / 创建 / 更新> |
122
122
  | <审批表单> | process-form | <流程闭环> | <节点和条件> | <复用 / 创建 / 更新> |
123
123
  | <报表> | report | <汇总分析> | <指标口径> | <复用 / 创建 / 更新> |
@@ -5,7 +5,7 @@
5
5
  ## 确定应用入口
6
6
 
7
7
  1. 首页/入口页:官网首页、工作台、经营驾驶舱或其他入口。
8
- 2. 页面清单:每个页面写清 scene、目标用户、主任务和需要设计的区块;工作台、首页、门户、看板、展示页和业务入口页必须显式列出 10 个以上 `contentBlocks`。
8
+ 2. 页面清单:每个页面写清 scene、目标用户、主任务和需要设计的区块;工作台、首页、门户、看板、展示页和业务入口页推荐显式列出 8-10 `contentBlocks` 以上,但不作为硬性门槛。
9
9
  3. 导航分组:按角色路径和业务主次分组,门面页靠前,数据录入/配置类页面靠后。
10
10
  4. 表单/流程关系:录入、审批、权限、校验交给原生表单和流程。
11
11
 
@@ -61,7 +61,7 @@
61
61
  - appBlueprint:<应用目标、角色、页面清单、导航分组>
62
62
  - resourceBlueprint:<pages: name/resourceType/scene/purpose;forms: name/formKind/fields/process>
63
63
  - 页面场景:<scene + 判定依据>
64
- - 页面区块 / contentBlocks:<工作台、首页、门户、看板、展示页和业务入口页逐条列出至少 10 个区块;KPI 组和快捷入口组各只算 1 个区块>
64
+ - 页面区块 / contentBlocks:<工作台、首页、门户、看板、展示页和业务入口页推荐逐条列出 8-10 个区块以上;KPI 组和快捷入口组各只算 1 个区块>
65
65
  - 页面关系:<上一层入口、下钻目标、原生表单/流程关系>
66
66
  ```
67
67
 
@@ -13,11 +13,11 @@
13
13
 
14
14
  ## 列内容区块
15
15
 
16
- 开始拆区块前读取 [页面质量门禁](../references/page-quality-gates.md),按区块数量门禁和低密大卡片门禁检查。
16
+ 开始拆区块前读取 [页面质量门禁](../references/page-quality-gates.md),按区块丰富度建议和低密大卡片门禁检查。
17
17
 
18
- 工作台、首页、门户、看板、展示页和业务入口页默认至少拆成 10 个有业务目的的区块以上;区块可以是紧凑标题/状态/操作/筛选/列表/动态/洞察/提醒/上下文/空态行动,不要求都做成卡片。纯表单、窄详情或单一列表页也要在同一页面内拆出工具栏、筛选、主列表、详情抽屉、批量动作、状态反馈等子区块。计数按区块组算,不按子项算:KPI 组只能算 1 个,快捷入口组只能算 1 个,列表组只能算 1 个。禁止用重复 KPI 卡、重复快捷入口或大空白卡凑数。
18
+ 工作台、首页、门户、看板、展示页和业务入口页推荐拆成 8-10 个有业务目的的区块以上;区块可以是紧凑标题/状态/操作/筛选/列表/动态/洞察/提醒/上下文/空态行动,不要求都做成卡片。纯表单、窄详情或单一列表页可以更精简,但仍建议在同一页面内拆出工具栏、筛选、主列表、详情抽屉、批量动作、状态反馈等必要区块。计数按区块组算,不按子项算:KPI 组只能算 1 个,快捷入口组只能算 1 个,列表组只能算 1 个。禁止用重复 KPI 卡、重复快捷入口或大空白卡凑数。
19
19
 
20
- 错误示例:`KPI 卡片: 学生总数, 课程总数, 本月出勤率, 平均分` + `快捷入口: 录入学生/登记成绩/记录考勤/管理课程` + `最近成绩列表` + `最近考勤记录`,视觉上只有 4 个聚合区块,不满足 10+。正确写法要逐条列出 `contentBlocks`,例如:标题上下文、班级/学期筛选、主状态摘要、风险提醒、主操作条、待处理学生列表、最近成绩、最近考勤、课程进度、班级动态、右侧班主任上下文、空态行动。
20
+ 错误示例:`KPI 卡片: 学生总数, 课程总数, 本月出勤率, 平均分` + `快捷入口: 录入学生/登记成绩/记录考勤/管理课程` + `最近成绩列表` + `最近考勤记录`,视觉上只有几个聚合区块,复杂首页通常会显得单薄。更好的写法是按真实任务逐条列出 `contentBlocks`,例如:标题上下文、班级/学期筛选、主状态摘要、风险提醒、主操作条、待处理学生列表、最近成绩、最近考勤、课程进度、班级动态、右侧班主任上下文、空态行动;若业务确实是窄场景,可以少写并说明取舍。
21
21
 
22
22
  每个区块写清:
23
23
 
@@ -39,7 +39,7 @@
39
39
 
40
40
  ```markdown
41
41
  - 布局骨架:<上下/左右/卡片流/列表/主从/大屏>
42
- - 内容区块:<至少 10 个区块列表 + 目的;若少于 10 个,说明为什么是窄场景>
42
+ - 内容区块:<推荐 8-10 个区块以上 + 目的;若较少,说明为什么是窄场景>
43
43
  - 主操作:<入口、按钮、下钻、提交、返回>
44
44
  - 功能契约:<保留的数据源/字段映射/按钮动作/筛选逻辑/提交 URL/权限/状态>
45
45
  - 响应式策略:<PC / 移动端差异>
@@ -58,7 +58,7 @@
58
58
  - 不规则背景只作用于页面壳、顶部首屏、功能引导卡或空状态安全区;内容布局继续使用规则栅格、稳定分栏和清晰对齐,做到“背景自由、内容严谨”。
59
59
  - `flowLight`、流动线条或光影动效必须低饱和、低对比、低速,并写 `prefers-reduced-motion` 静态降级;禁止离散装饰圆球、bokeh 或干扰文字阅读的背景动效。
60
60
  - 这些字段要能直接指导实现,而不是形容词;实现者应能按 `design.md` 写出根容器、分栏、背景层、表面材质、按钮状态和空态。
61
- - `acceptanceChecks` 至少包含:10+ `contentBlocks`、KPI/快捷入口子项不计数、首屏至少两层信息、没有大空白卡、主色跟随应用主题、背景或装饰层不干扰前景对比度。
61
+ - `acceptanceChecks` 建议包含:`contentBlocks` 推荐 8-10 个区块以上、KPI/快捷入口子项不计数、首屏至少两层信息、没有大空白卡、主色跟随应用主题、背景或装饰层不干扰前景对比度。区块数量不作为准出硬门槛。
62
62
 
63
63
  ## 3. 写组件和状态规则
64
64
 
@@ -90,7 +90,7 @@
90
90
  9. 首屏是否有视觉锚点、主操作、关键状态和至少两个信息层。
91
91
  10. 自定义展示页是否在 PRD 中引用 `designFile` 和 `designRefs`,且 `design.md` 已写清视觉 DNA 和页面场景规则。
92
92
  11. 工作台 / 业务首页是否避开“4 个等宽大 KPI 卡 + 图标快捷卡 + 大空态白卡”的低密模板;空数据是否用薄空态行和主操作入口承接。
93
- 12. 工作台、首页、门户、看板、展示页和业务入口页是否至少有 10 个有业务目的的区块以上,并且不是靠重复卡片或空白容器凑数;KPI 子项、快捷入口子项和列表行不能分别计数。
93
+ 12. 工作台、首页、门户、看板、展示页和业务入口页是否推荐有 8-10 个有业务目的的区块以上,并且不是靠重复卡片或空白容器凑数;窄场景可以更少并说明取舍。KPI 子项、快捷入口子项和列表行不能分别计数。
94
94
  13. 页面是否已考虑 `backgroundLayer`:优先选择淡色背景、顶部不规则色块、柔和光洗、低速流光、微噪点、细线装饰、插图或局部渐变之一;近白画布可以保留,但不能呈现为未设计的空白底。
95
95
  14. 不规则背景是否只服务氛围和视觉焦点,内容区是否仍保持规则栅格、稳定对齐、可读对比度和 reduced motion 静态降级。
96
96
  15. 页面背景与卡片背景是否形成明显层次对比,并按白色/浅色背景配边框、浅灰/浅彩背景配白色无边框、渐变背景配玻璃卡片的方案落地。
@@ -38,7 +38,7 @@
38
38
  | 页面结构配方 | 中性槽位、`visualScaffold`、`surfaceMap`、`componentRecipe` |
39
39
  | 状态与交互 / 响应式 / 可访问性 | loading、empty、error、mobile、reduced motion、焦点和对比度 |
40
40
  | 实现适配 | CSS 变量、Yida / Code Canvas 容器重置、`Yida Global Theme Runtime Contract`、Code Canvas / 普通 JSX helper 使用规则 |
41
- | 必须包含 / 禁止项 / 错误 vs 正确 / Agent 使用提示 / 交付自检 | 保护视觉 DNA、10+ contentBlocks、禁大白卡、自定义色 token 注入、实现前读取双文件 |
41
+ | 包含项 / 禁止项 / 错误 vs 正确 / Agent 使用提示 / 交付自检 | 保护视觉 DNA、contentBlocks 推荐 8-10 个区块以上、禁大白卡、自定义色 token 注入、实现前读取双文件 |
42
42
 
43
43
  ## 写文件前检查
44
44
 
@@ -1,108 +0,0 @@
1
- 'use strict';
2
-
3
- const MIN_CONTENT_BLOCKS = 10;
4
- const RICH_SCENES = ['workbench', 'dashboard', 'landing', 'screen'];
5
-
6
- function lineNumberAt(source, index) {
7
- return String(source || '').slice(0, index).split(/\r?\n/).length;
8
- }
9
-
10
- function isTemplateToken(value) {
11
- return /^\{\{[A-Z0-9_]+\}\}$/.test(String(value || '').trim());
12
- }
13
-
14
- function readAnnotation(source, name) {
15
- const text = String(source || '');
16
- const pattern = new RegExp(`@openyida-${name}\\s+([^\\r\\n*]+)`, 'i');
17
- const match = text.match(pattern);
18
- if (!match) {
19
- return null;
20
- }
21
- return {
22
- value: match[1].trim(),
23
- line: lineNumberAt(text, match.index),
24
- };
25
- }
26
-
27
- function parseList(value) {
28
- const raw = String(value || '').trim();
29
- if (!raw || isTemplateToken(raw)) {
30
- return null;
31
- }
32
- return raw
33
- .split(',')
34
- .map((item) => item.trim())
35
- .filter(Boolean);
36
- }
37
-
38
- function inferScene(source) {
39
- const scene = readAnnotation(source, 'scene');
40
- if (scene && scene.value) {
41
- return scene.value;
42
- }
43
- const text = String(source || '');
44
- if (/工作台|业务首页|系统首页|运营台|任务中心|workbench/i.test(text)) {
45
- return 'workbench';
46
- }
47
- if (/数据看板|经营看板|驾驶舱|dashboard/i.test(text)) {
48
- return 'dashboard';
49
- }
50
- if (/官网|落地页|品牌首页|landing/i.test(text)) {
51
- return 'landing';
52
- }
53
- if (/大屏|态势|实时监控|screen/i.test(text)) {
54
- return 'screen';
55
- }
56
- return '';
57
- }
58
-
59
- function findPageRichnessIssues(source) {
60
- const text = String(source || '');
61
- const scene = inferScene(text);
62
- if (!RICH_SCENES.includes(scene)) {
63
- return [];
64
- }
65
-
66
- const annotation = readAnnotation(text, 'content-blocks');
67
- if (!annotation) {
68
- return [{
69
- line: 1,
70
- type: 'missing',
71
- count: 0,
72
- min: MIN_CONTENT_BLOCKS,
73
- scene,
74
- }];
75
- }
76
-
77
- const blocks = parseList(annotation.value);
78
- if (blocks === null) {
79
- return [];
80
- }
81
- if (blocks.length >= MIN_CONTENT_BLOCKS) {
82
- return [];
83
- }
84
-
85
- return [{
86
- line: annotation.line,
87
- type: 'too-few',
88
- count: blocks.length,
89
- min: MIN_CONTENT_BLOCKS,
90
- scene,
91
- }];
92
- }
93
-
94
- function formatPageRichnessMessage(issue) {
95
- const count = issue && Number.isFinite(issue.count) ? issue.count : 0;
96
- const min = issue && Number.isFinite(issue.min) ? issue.min : MIN_CONTENT_BLOCKS;
97
- if (issue && issue.type === 'missing') {
98
- return `展示型页面必须声明 @openyida-content-blocks,且至少包含 ${min} 个有业务目的的内容区块;KPI 组、快捷入口组、列表组各只算 1 个区块。`;
99
- }
100
- return `展示型页面至少需要 ${min} 个有业务目的的内容区块;KPI 组、快捷入口组、列表组各只算 1 个区块,当前只有 ${count} 个。`;
101
- }
102
-
103
- module.exports = {
104
- MIN_CONTENT_BLOCKS,
105
- RICH_SCENES,
106
- findPageRichnessIssues,
107
- formatPageRichnessMessage,
108
- };