openyida 2026.9.10 → 2026.9.11-2
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 +4 -2
- package/bin/yida.js +1 -0
- package/lib/app/create-form/batch.js +12 -0
- package/lib/app/update-app.js +17 -2
- package/lib/core/command-manifest.js +11 -4
- package/lib/core/locales/en.js +6 -2
- package/lib/core/locales/zh.js +6 -2
- package/lib/process/services/process-actions.js +145 -0
- package/lib/process/services/process-compiler.js +4 -30
- package/lib/process/services/process-view-verifier.js +31 -0
- package/package.json +1 -1
- package/yida-skills/SKILL.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +21 -1
- package/yida-skills/skills/yida-create-app/SKILL.md +4 -4
- package/yida-skills/skills/yida-create-process/SKILL.md +2 -0
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +1 -1
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +1 -1
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +1 -1
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
- package/yida-skills/skills/yida-process-rule/SKILL.md +6 -1
- package/yida-skills/skills/yida-process-rule/references/approval-actions.md +48 -0
- package/yida-skills/skills/yida-requirement-analysis/SKILL.md +1 -1
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +22 -13
package/README.md
CHANGED
|
@@ -210,6 +210,8 @@ openyida data create form APP_XXX FORM_XXX --expect-form-name 客户 --expect-fo
|
|
|
210
210
|
openyida get-permission APP_XXX FORM_XXX
|
|
211
211
|
```
|
|
212
212
|
|
|
213
|
+
Append and forward permissions are configured in the process definition through `nodes[].actions.normalActions/appendActions`. Both `create-process` and `configure-process` compile these settings into matching designer and runtime properties. See [approval action configuration](yida-skills/skills/yida-process-rule/references/approval-actions.md) for the JSON example, defaults, and verification limits.
|
|
214
|
+
|
|
213
215
|
`configure-process` 的流程 JSON 中,审批人可配置为发起人、指定成员、指定角色、部门主管或直属主管,例如:
|
|
214
216
|
|
|
215
217
|
```json
|
|
@@ -454,8 +456,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
454
456
|
|
|
455
457
|
| Command | Description |
|
|
456
458
|
|---------|-------------|
|
|
457
|
-
| `openyida configure-process <appType> <formUuid> <definition> [processCode] [--replace]` | Configure and publish process rules |
|
|
458
|
-
| `openyida create-process <appType> ... [--replace]` | Create process form (all-in-one) |
|
|
459
|
+
| `openyida configure-process <appType> <formUuid> <definition> [processCode] [--replace]` | Configure and publish process rules; Supports append/forward via JSON nodes[].actions.normalActions/appendActions |
|
|
460
|
+
| `openyida create-process <appType> ... [--replace]` | Create process form (all-in-one); Supports append/forward via JSON nodes[].actions.normalActions/appendActions |
|
|
459
461
|
| `openyida ai-form-setting <get\|fields\|models\|enable\|disable\|save> <appType> ...` | Manage process form AI approval prompts |
|
|
460
462
|
| `openyida process preview <appType> ...` | Preview process instance (visual flowchart) |
|
|
461
463
|
|
package/bin/yida.js
CHANGED
|
@@ -496,6 +496,7 @@ const MANIFEST_HELP_PATHS = Object.freeze({
|
|
|
496
496
|
data: ['data'],
|
|
497
497
|
report: ['report'],
|
|
498
498
|
'create-process': ['create-process'],
|
|
499
|
+
'configure-process': ['configure-process'],
|
|
499
500
|
'create-report': ['create-report'],
|
|
500
501
|
'append-chart': ['append-chart'],
|
|
501
502
|
'save-share-config': ['save-share-config'],
|
|
@@ -246,6 +246,9 @@ async function schedule(forms, concurrency, results, worker, save) {
|
|
|
246
246
|
return !result || result.status === 'blocked' || (result.status === 'failed' && result.formUuid);
|
|
247
247
|
}).map(form => form.key));
|
|
248
248
|
const active = new Map();
|
|
249
|
+
const recoveryAttempted = new Set(forms.filter(form =>
|
|
250
|
+
form.formUuid || (results[form.key]?.status === 'failed' && results[form.key]?.formUuid)
|
|
251
|
+
).map(form => form.key));
|
|
249
252
|
try {
|
|
250
253
|
while (pending.size || active.size) {
|
|
251
254
|
const pendingBefore = pending.size;
|
|
@@ -257,6 +260,8 @@ async function schedule(forms, concurrency, results, worker, save) {
|
|
|
257
260
|
}
|
|
258
261
|
if (active.size >= concurrency || deps.some(status => status !== 'success')) { continue; }
|
|
259
262
|
pending.delete(form.key);
|
|
263
|
+
const hadKnownFormUuid = Boolean(form.formUuid || results[form.key]?.formUuid);
|
|
264
|
+
if (hadKnownFormUuid) { recoveryAttempted.add(form.key); }
|
|
260
265
|
results[form.key] = { ...results[form.key], status: 'running' };
|
|
261
266
|
save(); // Record intent before the request; an interrupted create is never retried automatically.
|
|
262
267
|
const task = Promise.resolve().then(() => worker(form)).then(output => {
|
|
@@ -264,6 +269,13 @@ async function schedule(forms, concurrency, results, worker, save) {
|
|
|
264
269
|
}, error => {
|
|
265
270
|
results[form.key] = { ...results[form.key], status: 'failed', error: error.message,
|
|
266
271
|
...((error.output?.formUuid || error.output?.details?.formUuid) ? { formUuid: error.output.formUuid || error.output.details.formUuid } : {}) };
|
|
272
|
+
// A create may persist the form before a later schema/readback stage fails.
|
|
273
|
+
// Retry that known UUID once inside the same authoritative batch so its
|
|
274
|
+
// dependents remain pending and can run after conservative resume.
|
|
275
|
+
if (!hadKnownFormUuid && results[form.key].formUuid && !recoveryAttempted.has(form.key)) {
|
|
276
|
+
recoveryAttempted.add(form.key);
|
|
277
|
+
pending.add(form.key);
|
|
278
|
+
}
|
|
267
279
|
}).then(() => { save(); }).finally(() => active.delete(form.key));
|
|
268
280
|
active.set(form.key, task);
|
|
269
281
|
}
|
package/lib/app/update-app.js
CHANGED
|
@@ -221,6 +221,16 @@ function normalizeLayoutDirection(layoutDirection) {
|
|
|
221
221
|
return normalized;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/** 与 yc-utils 的应用配置归一化一致;不应用详情页 Shell 的 top_fold 覆盖。 */
|
|
225
|
+
function normalizeStoredLayoutDirection(layoutDirection, navType) {
|
|
226
|
+
if (SUPPORTED_LAYOUT_DIRECTIONS.includes(layoutDirection)) {return layoutDirection;}
|
|
227
|
+
if (layoutDirection === 'hoz') {return navType === 'top_side' ? 'l_shape' : 'top';}
|
|
228
|
+
if (layoutDirection === 'ver') {return 'side';}
|
|
229
|
+
if (navType === 'top_fold') {return 'top';}
|
|
230
|
+
if (navType === 'top_side') {return 'l_shape';}
|
|
231
|
+
return 'side';
|
|
232
|
+
}
|
|
233
|
+
|
|
224
234
|
function pickAppField(currentApp, fieldName, configName) {
|
|
225
235
|
if (currentApp[fieldName] !== undefined && currentApp[fieldName] !== null) {
|
|
226
236
|
return currentApp[fieldName];
|
|
@@ -296,6 +306,8 @@ function buildUpdateAppPostData(params, currentApp = {}, authRef) {
|
|
|
296
306
|
const currentCustomThemeStyle = pickAppField(currentApp, 'customThemeStyle', 'CUSTOM_THEME_STYLE');
|
|
297
307
|
const addWaterMark = pickAppField(currentApp, 'addWaterMark', 'ADDWATERMARK');
|
|
298
308
|
const sentryMode = pickAppField(currentApp, 'sentryMode', 'SENTRY_MODE');
|
|
309
|
+
const currentNavType = currentApp.navType || (currentApp.config && currentApp.config.NAVTYPE);
|
|
310
|
+
const currentLayoutDirection = currentApp.layoutDirection || (currentApp.config && currentApp.config.LAY_OUT_DIRECTION);
|
|
299
311
|
if (params.colour === 'custom' && !params.themeColor && !currentThemeColor) {
|
|
300
312
|
throw new Error(t('update_app.custom_theme_color_required'));
|
|
301
313
|
}
|
|
@@ -319,9 +331,10 @@ function buildUpdateAppPostData(params, currentApp = {}, authRef) {
|
|
|
319
331
|
mode: currentApp.mode || (currentApp.config && currentApp.config.APPMODE) || 'normal',
|
|
320
332
|
type: currentApp.type || (currentApp.config && currentApp.config.APPTYPE) || 'single',
|
|
321
333
|
navTheme: params.navTheme || currentApp.navTheme || (currentApp.config && currentApp.config.NAV_THEME) || 'light',
|
|
322
|
-
navType: currentApp.navType || (currentApp.config && currentApp.config.NAVTYPE) || 'top_side',
|
|
323
334
|
navLayout: currentApp.navLayout || (currentApp.config && currentApp.config.NAVLAYOUT) || 'auto',
|
|
324
|
-
layoutDirection: params.layoutDirection
|
|
335
|
+
layoutDirection: params.layoutDirection
|
|
336
|
+
? normalizeLayoutDirection(params.layoutDirection)
|
|
337
|
+
: normalizeStoredLayoutDirection(currentLayoutDirection, currentNavType),
|
|
325
338
|
homepageLogo: currentApp.homepageLogo || (currentApp.config && currentApp.config.HOMEPAGELOGO) || '',
|
|
326
339
|
logoSource: params.logoSource || currentApp.logoSource || (currentApp.config && currentApp.config.LOGO_SOURCE) || 'appIcon',
|
|
327
340
|
logoLink: currentApp.logoLink || (currentApp.config && currentApp.config.LOGOLINK) || '',
|
|
@@ -346,6 +359,8 @@ function buildUpdateAppPostData(params, currentApp = {}, authRef) {
|
|
|
346
359
|
pageFooter: currentApp.pageFooter || '',
|
|
347
360
|
};
|
|
348
361
|
|
|
362
|
+
// ThemeNavSetting 保存现有 navType;新布局由 layoutDirection 控制,不反写或补造旧字段。
|
|
363
|
+
if (currentNavType) {postDataObj.navType = currentNavType;}
|
|
349
364
|
if (addWaterMark !== undefined && addWaterMark !== null) {
|
|
350
365
|
postDataObj.addWaterMark = addWaterMark;
|
|
351
366
|
}
|
|
@@ -1292,8 +1292,15 @@ const COMMAND_GROUPS = [
|
|
|
1292
1292
|
id: 'process',
|
|
1293
1293
|
titleKey: 'help.group_process',
|
|
1294
1294
|
commands: [
|
|
1295
|
-
command('configure-process', ['configure-process'], 'configure-process <appType> <formUuid> <definition> [processCode] [--replace]', 'help.cmd_configure_process'
|
|
1296
|
-
|
|
1295
|
+
command('configure-process', ['configure-process'], 'configure-process <appType> <formUuid> <definition> [processCode] [--replace]', 'help.cmd_configure_process', {
|
|
1296
|
+
examples: ['openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process-with-actions.json'],
|
|
1297
|
+
}),
|
|
1298
|
+
command('create-process', ['create-process'], 'create-process <appType> ... [--replace]', 'help.cmd_create_process', {
|
|
1299
|
+
examples: [
|
|
1300
|
+
'openyida create-process APP_XXX "Approval Form" .cache/openyida/process/fields.json .cache/openyida/process/process-with-actions.json',
|
|
1301
|
+
'openyida create-process APP_XXX --formUuid FORM_XXX .cache/openyida/process/process-with-actions.json',
|
|
1302
|
+
],
|
|
1303
|
+
}),
|
|
1297
1304
|
command('ai-form-setting', ['ai-form-setting'], 'ai-form-setting <get|fields|models|enable|disable|save> <appType> ...', 'help.cmd_ai_form_setting', {
|
|
1298
1305
|
output: 'json',
|
|
1299
1306
|
aliases: ['ai-approve', 'aiFormSetting'],
|
|
@@ -1555,8 +1562,8 @@ function summarizeLocalizedCommands(commands) {
|
|
|
1555
1562
|
plan_command_ids: ['design-plan.init', 'design-plan.preview', 'design-plan.materialize', 'design-plan.patch'],
|
|
1556
1563
|
theme_command_ids: ['sample', 'create-app', 'update-app'],
|
|
1557
1564
|
navigation_command_ids: { platform: ['update-app', 'nav-group'], custom: ['update-app', 'update-form-config', 'get-form-config'] },
|
|
1558
|
-
navigation_policy: 'Before PRD planning, reuse explicit navigation choices. During first-time intake, ask only when unknown: platform
|
|
1559
|
-
design_mode_policy: 'Analyze requirements first. First-time builds include new apps and existing apps without business pages. Reuse detailed supplied plans; if the mode is unspecified, ask whether to prepare a PRD for confirmation (Plan, more detailed and slower) or build from the supplied requirements (Fast). Without detailed requirements, offer Fast and Plan neutrally. Confirm unresolved navigation
|
|
1565
|
+
navigation_policy: 'Before PRD planning, reuse explicit navigation choices. During first-time intake, ask only when ownership is unknown and offer exactly two neutral options with no preselection: "宜搭原生导航" (使用宜搭自带的导航菜单。) and "自定义导航" (在自定义页面里定制菜单的样式和操作方式,替代宜搭自带的导航菜单。). Native means platform navigation. The agent chooses layout from module count, hierarchy, switching frequency, content width and target device; do not ask layout or presentation-style questions. Prefer top for few shallow modules and wide content, side for many frequently accessed modules, L-shaped/mixed for business domains with child modules, and custom dock for lightweight mobile entries. Preserve user-specified layouts. Store resolved type/source/reason/variant in the brief, distinguishing user ownership from agent layout reasoning. Platform navigation uses update-app --layout top|side|l_shape --show-app-nav, which writes layoutDirection=top|side|l_shape and hideAppNav=n. Preserve stored navType unchanged; do not derive or invent it and do not add a --nav-type option. Without --layout, normalize stored legacy layoutDirection and navType using the yc-utils application rules (hoz plus top_side means l_shape; missing layout uses top_fold/top_side for top/l_shape). Shell page-level top_fold/none overrides do not belong in app settings. Custom side/top/mixed/dock is a page layout and requires app and per-page navigation hiding with readback. Verify persisted layoutDirection and hideAppNav; updatedFields and themeVerification do not prove navigation persistence. Navigation tone is separate from layout.',
|
|
1566
|
+
design_mode_policy: 'Analyze requirements first. First-time builds include new apps and existing apps without business pages. Reuse detailed supplied plans; if the mode is unspecified, ask whether to prepare a PRD for confirmation (Plan, more detailed and slower) or build from the supplied requirements (Fast). Without detailed requirements, offer Fast and Plan neutrally. Confirm unresolved navigation ownership, visual style and page scope before planning or resource creation; the agent determines navigation layout from the business context without a layout question. Existing business apps only clarify the current change. A named resource-only request with explicitScope.allowInferredResources=false skips unrelated navigation and visual questions. Standard Plan uses design-plan init parallelTasks, then executes the returned materialize.command exactly once with business-file and visual-file; preview and --from-preview are only for explicitly incremental large plans. Confirm the displayed revision before building, and do not materialize or patch again after confirmation.',
|
|
1560
1567
|
product_design_policy: 'yida-requirement-analysis owns shared facts and first-time intake. yida-prd owns business planning; yida-design owns visual design. Reuse the same confirmed brief and supplied details. Prepare business and base visuals concurrently, then bind visuals to settled page tasks. yida-app merges and checks the artifacts before creating resources. Plan uses the compact authoring contract and selected theme context; the CLI reads full templates and renders all artifacts.',
|
|
1561
1568
|
ui_guidance_policy: 'Page implementation consumes yida-prd prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, business-specific checks, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it consumes yida-design design.md for app custom theme CSS delivery, themeColor/navTheme, visual states, visualScaffold, surface material, rounded rules, density rules, components, and state styling. prd.md and design.md are the only design sources of truth. Page implementation may extract page-spec.json as a derived implementation handoff from prd.md + design.md; conflicts are resolved by sending business conflicts to yida-prd and visual conflicts to yida-design before regenerating the spec. Core normal forms default to 1-3 business sample records before page implementation, followed by query readback. An explicit opt-out, configuration dictionary, sensitive data, or lack of safely constructible values requires a recorded skip reason. Screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
|
|
1562
1569
|
default_nav_order_policy: 'For custom navigation, implement PRD navigation order in the custom shell and verify app/page navigation hiding; skip platform nav-group ordering. For platform navigation: after the primary page is successfully published, perform exactly one navigation order operation. If the PRD names a navigation order, publish without --auto-nav-order and then call openyida nav-group order <appType> <items...>. If the PRD only gives broad groups or is missing navigation order, use openyida publish ... --auto-nav-order and do not call nav-group order or auto-order afterward. Explicit and automatic ordering are mutually exclusive; never generate per-item move loops. The fallback priority is portal/home/workbench entry > business handling > data management > business analytics > system configuration.',
|
package/lib/core/locales/en.js
CHANGED
|
@@ -64,8 +64,8 @@ module.exports = {
|
|
|
64
64
|
cmd_corp_manager: 'Manage platform admins and address book permissions',
|
|
65
65
|
cmd_agent_center: 'Manage process and departure delegation',
|
|
66
66
|
group_process: 'Process',
|
|
67
|
-
cmd_configure_process: 'Configure and publish process rules',
|
|
68
|
-
cmd_create_process: 'Create process form (all-in-one)',
|
|
67
|
+
cmd_configure_process: 'Configure and publish process rules; Supports append/forward via JSON nodes[].actions.normalActions/appendActions',
|
|
68
|
+
cmd_create_process: 'Create process form (all-in-one); Supports append/forward via JSON nodes[].actions.normalActions/appendActions',
|
|
69
69
|
cmd_ai_form_setting: 'Manage process form AI approval prompts',
|
|
70
70
|
cmd_process_preview: 'Preview process instance (visual flowchart)',
|
|
71
71
|
group_share: 'Page Config & Sharing',
|
|
@@ -2070,3 +2070,7 @@ module.exports.connector_e2e = {
|
|
|
2070
2070
|
test_contract_unverified: 'Connector testing did not prove the controlled fixture, marker, ownership, and auth runtime contract.',
|
|
2071
2071
|
action_mutated: 'Connector testing mutated the persisted action definition.',
|
|
2072
2072
|
};
|
|
2073
|
+
|
|
2074
|
+
Object.assign(module.exports.process_errors || (module.exports.process_errors = {}), {
|
|
2075
|
+
action_config_invalid: 'Invalid approval action configuration for node {0}: {1}',
|
|
2076
|
+
});
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -64,8 +64,8 @@ module.exports = {
|
|
|
64
64
|
cmd_corp_manager: '管理平台管理员与通讯录权限',
|
|
65
65
|
cmd_agent_center: '管理流程代理和离职代理',
|
|
66
66
|
group_process: '流程',
|
|
67
|
-
cmd_configure_process: '
|
|
68
|
-
cmd_create_process: '
|
|
67
|
+
cmd_configure_process: '配置并发布流程规则; 支持加签/转交,配置位于 JSON nodes[].actions.normalActions/appendActions',
|
|
68
|
+
cmd_create_process: '创建流程表单(一体化); 支持加签/转交,配置位于 JSON nodes[].actions.normalActions/appendActions',
|
|
69
69
|
cmd_ai_form_setting: '管理流程表单 AI 审批提示',
|
|
70
70
|
cmd_process_preview: '预览流程实例(可视化流程图)',
|
|
71
71
|
group_share: '页面配置 & 分享',
|
|
@@ -2024,3 +2024,7 @@ module.exports.connector_e2e = {
|
|
|
2024
2024
|
test_contract_unverified: '连接器测试未能证明受控 fixture、标记、归属和鉴权运行时契约。',
|
|
2025
2025
|
action_mutated: '连接器测试改变了持久化动作定义。',
|
|
2026
2026
|
};
|
|
2027
|
+
|
|
2028
|
+
Object.assign(module.exports.process_errors || (module.exports.process_errors = {}), {
|
|
2029
|
+
action_config_invalid: '节点 {0} 的审批动作配置无效:{1}',
|
|
2030
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { buildYidaI18n } = require('../../core/yida-i18n');
|
|
4
|
+
const { t } = require('../../core/i18n');
|
|
5
|
+
|
|
6
|
+
function buildActions() {
|
|
7
|
+
const actionDefs = [
|
|
8
|
+
{ action: 'agree', zh: '同意', en: 'Agree', ja: '同意', hidden: false },
|
|
9
|
+
{ action: 'disagree', zh: '拒绝', en: 'Disagree', ja: '拒否', hidden: false },
|
|
10
|
+
{ action: 'save', zh: '保存', en: 'Save', ja: '保存', hidden: true },
|
|
11
|
+
{ action: 'forward', zh: '转交', en: 'Forward', ja: '転送', hidden: true },
|
|
12
|
+
{ action: 'append', zh: '加签', en: 'Append', ja: '承認者を追加', hidden: true },
|
|
13
|
+
{ action: 'return', zh: '退回', en: 'Return', ja: '差し戻し', hidden: true },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
return actionDefs.map(function (def) {
|
|
17
|
+
return {
|
|
18
|
+
hidden: def.hidden,
|
|
19
|
+
name: buildYidaI18n(def.zh, { en_US: def.en, ja_JP: def.ja }),
|
|
20
|
+
action: def.action,
|
|
21
|
+
text: buildYidaI18n(def.zh, { en_US: def.en, ja_JP: def.ja }),
|
|
22
|
+
alias: buildYidaI18n(def.zh, { en_US: def.en, ja_JP: def.ja }),
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function invalid(nodeName, field) {
|
|
28
|
+
const error = new TypeError(t('process_errors.action_config_invalid', nodeName, field));
|
|
29
|
+
error.code = 'PROCESS_COMPILE_ACTION_CONFIG_INVALID';
|
|
30
|
+
error.details = { nodeName, field };
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isObject(value) {
|
|
35
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function clone(value) {
|
|
39
|
+
return JSON.parse(JSON.stringify(value));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Merge partial button settings without changing unspecified permissions. */
|
|
43
|
+
function normalizeActions(value, field, nodeName, complete = false) {
|
|
44
|
+
const defaults = buildActions();
|
|
45
|
+
if (value === undefined) { return defaults; }
|
|
46
|
+
if (!Array.isArray(value)) { invalid(nodeName, field); }
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
const overrides = value.map(action => {
|
|
49
|
+
if (!isObject(action) || typeof action.action !== 'string' || !action.action.trim()
|
|
50
|
+
|| (!complete && !['agree', 'disagree', 'save', 'forward', 'append', 'return', 'recall'].includes(action.action))
|
|
51
|
+
|| seen.has(action.action) || (action.hidden !== undefined && typeof action.hidden !== 'boolean')) {
|
|
52
|
+
invalid(nodeName, field);
|
|
53
|
+
}
|
|
54
|
+
seen.add(action.action);
|
|
55
|
+
return clone(action);
|
|
56
|
+
});
|
|
57
|
+
const merge = (base, override) => {
|
|
58
|
+
const result = { ...base, ...override };
|
|
59
|
+
if (override && override.name !== undefined && override.alias === undefined) { result.alias = clone(override.name); }
|
|
60
|
+
if (override && override.alias !== undefined && override.name === undefined) { result.name = clone(override.alias); }
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
// Raw processProps arrays are complete lists, not partial overrides. Do not add
|
|
64
|
+
// agree/disagree permissions to a legacy list that intentionally omitted them.
|
|
65
|
+
if (complete) {
|
|
66
|
+
// NewProcessAdapter treats an omitted hidden field in a raw action as visible.
|
|
67
|
+
return overrides.map(item => merge({ ...defaults.find(action => action.action === item.action), hidden: false }, item));
|
|
68
|
+
}
|
|
69
|
+
const result = defaults.map(action => merge(action, overrides.find(item => item.action === action.action)));
|
|
70
|
+
// Native versions may include the additional recall button.
|
|
71
|
+
overrides.filter(item => item.action === 'recall').forEach(item => result.push({ hidden: true, ...item }));
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Compile one shared action contract into designer and engine representations.
|
|
77
|
+
* Native node.actions takes precedence; legacy processProps lists remain accepted.
|
|
78
|
+
* Missing append settings use the same defaults as ActionSetter; invalid explicit
|
|
79
|
+
* settings fail before publishing. No config retains historical hidden defaults.
|
|
80
|
+
* @param {object|undefined} config native normalActions / appendActions overrides
|
|
81
|
+
* @param {object} rawProps legacy approver process properties
|
|
82
|
+
* @param {string} nodeName diagnostic node name
|
|
83
|
+
* @returns {{viewActions: object, processProps: object}} independent JSON values
|
|
84
|
+
*/
|
|
85
|
+
function buildNodeActions(config, rawProps = {}, nodeName = '') {
|
|
86
|
+
if (config !== undefined && (!isObject(config)
|
|
87
|
+
|| Object.keys(config).some(key => !['normalActions', 'appendActions'].includes(key)))) {
|
|
88
|
+
invalid(nodeName, 'actions');
|
|
89
|
+
}
|
|
90
|
+
const input = config || {};
|
|
91
|
+
const normalActions = normalizeActions(input.normalActions === undefined ? rawProps.actions : input.normalActions,
|
|
92
|
+
'actions.normalActions', nodeName, input.normalActions === undefined);
|
|
93
|
+
const appendActions = normalizeActions(input.appendActions === undefined ? rawProps.appendActions : input.appendActions,
|
|
94
|
+
'actions.appendActions', nodeName, input.appendActions === undefined);
|
|
95
|
+
const append = normalActions.find(item => item.action === 'append') || { hidden: true };
|
|
96
|
+
const appendedAppend = appendActions.find(item => item.action === 'append') || { hidden: true };
|
|
97
|
+
const processProps = {};
|
|
98
|
+
|
|
99
|
+
// These engine fields are node-wide: the designer derives them from normalActions.
|
|
100
|
+
if (!appendedAppend.hidden && append.hidden) {
|
|
101
|
+
invalid(nodeName, 'actions.normalActions.append.hidden');
|
|
102
|
+
}
|
|
103
|
+
if (!append.hidden) {
|
|
104
|
+
if (append.appendPosition === undefined) {
|
|
105
|
+
append.appendPosition = rawProps.moldList === undefined ? ['BEFORE_APPEND'] : clone(rawProps.moldList);
|
|
106
|
+
}
|
|
107
|
+
if (append.appendResult === undefined) {
|
|
108
|
+
if (rawProps.isConsiderAppendedAction !== undefined && typeof rawProps.isConsiderAppendedAction !== 'boolean') {
|
|
109
|
+
invalid(nodeName, 'processProps.isConsiderAppendedAction');
|
|
110
|
+
}
|
|
111
|
+
append.appendResult = rawProps.isConsiderAppendedAction === undefined
|
|
112
|
+
? 'valid' : (rawProps.isConsiderAppendedAction === true ? 'valid' : 'invalid');
|
|
113
|
+
}
|
|
114
|
+
if (!Array.isArray(append.appendPosition) || append.appendPosition.length === 0
|
|
115
|
+
|| append.appendPosition.some(position => !['BEFORE_APPEND', 'AFTER_APPEND'].includes(position))
|
|
116
|
+
|| new Set(append.appendPosition).size !== append.appendPosition.length) {
|
|
117
|
+
invalid(nodeName, 'actions.normalActions.append.appendPosition');
|
|
118
|
+
}
|
|
119
|
+
if (!['valid', 'invalid'].includes(append.appendResult)) {
|
|
120
|
+
invalid(nodeName, 'actions.normalActions.append.appendResult');
|
|
121
|
+
}
|
|
122
|
+
Object.assign(processProps, {
|
|
123
|
+
allowTaskAppend: true,
|
|
124
|
+
moldList: clone(append.appendPosition),
|
|
125
|
+
isConsiderAppendedAction: append.appendResult === 'valid',
|
|
126
|
+
isNeedEndTaskGroupChain: append.appendResult === 'valid',
|
|
127
|
+
});
|
|
128
|
+
} else if (['allowTaskAppend', 'moldList', 'isConsiderAppendedAction', 'isNeedEndTaskGroupChain']
|
|
129
|
+
.some(key => rawProps[key] !== undefined)) {
|
|
130
|
+
// Clear stale engine flags when the action is explicitly disabled.
|
|
131
|
+
Object.assign(processProps, {
|
|
132
|
+
allowTaskAppend: false, moldList: [], isConsiderAppendedAction: false, isNeedEndTaskGroupChain: false,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const viewActions = { normalActions, appendActions };
|
|
136
|
+
// Retain existing return settings in both representations.
|
|
137
|
+
['realBackOriginator', 'triggerRule', 'backScope'].forEach(key => {
|
|
138
|
+
if (rawProps[key] !== undefined) { viewActions[key] = clone(rawProps[key]); }
|
|
139
|
+
});
|
|
140
|
+
processProps.actions = clone(normalActions);
|
|
141
|
+
processProps.appendActions = clone(appendActions);
|
|
142
|
+
return { viewActions, processProps };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = { buildNodeActions };
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const { buildYidaI18n } = require('../../core/yida-i18n');
|
|
10
10
|
const { t } = require('../../core/i18n');
|
|
11
|
+
const { buildNodeActions } = require('./process-actions');
|
|
11
12
|
|
|
12
13
|
const KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
13
14
|
|
|
@@ -649,29 +650,6 @@ function buildFriendlyApproverConfig(node, approver) {
|
|
|
649
650
|
return null;
|
|
650
651
|
}
|
|
651
652
|
|
|
652
|
-
// ── 构建审批/抄送动作列表 ────────────────────────────
|
|
653
|
-
|
|
654
|
-
function buildActions() {
|
|
655
|
-
const actionDefs = [
|
|
656
|
-
{ action: 'agree', zh: '同意', en: 'Agree', ja: '同意', hidden: false },
|
|
657
|
-
{ action: 'disagree', zh: '拒绝', en: 'Disagree', ja: '拒否', hidden: false },
|
|
658
|
-
{ action: 'save', zh: '保存', en: 'Save', ja: '保存', hidden: true },
|
|
659
|
-
{ action: 'forward', zh: '转交', en: 'Forward', ja: '転送', hidden: true },
|
|
660
|
-
{ action: 'append', zh: '加签', en: 'Append', ja: '承認者を追加', hidden: true },
|
|
661
|
-
{ action: 'return', zh: '退回', en: 'Return', ja: '差し戻し', hidden: true },
|
|
662
|
-
];
|
|
663
|
-
|
|
664
|
-
return actionDefs.map(function (def) {
|
|
665
|
-
return {
|
|
666
|
-
hidden: def.hidden,
|
|
667
|
-
name: i18n(def.zh, def.en, def.ja),
|
|
668
|
-
action: def.action,
|
|
669
|
-
text: i18n(def.zh, def.en, def.ja),
|
|
670
|
-
alias: i18n(def.zh, def.en, def.ja),
|
|
671
|
-
};
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
|
|
675
653
|
// ── 构建条件规则(严格匹配宜搭真实格式)─────────────
|
|
676
654
|
|
|
677
655
|
function buildConditionRules(rules, logic) {
|
|
@@ -1162,10 +1140,9 @@ function buildApprovalNode(node, nextNodeId, nameToIdMap, nodeType) {
|
|
|
1162
1140
|
formConfig = { behaviorList: behaviorList };
|
|
1163
1141
|
}
|
|
1164
1142
|
|
|
1143
|
+
const nodeActions = buildNodeActions(node.actions, approverConfig.processProps, displayName);
|
|
1165
1144
|
const processNodeProps = {
|
|
1166
1145
|
conditionalMode: 'conditional',
|
|
1167
|
-
actions: buildActions(),
|
|
1168
|
-
appendActions: buildActions(),
|
|
1169
1146
|
openDigitalSign: false,
|
|
1170
1147
|
noActionersType: 'stopProcess',
|
|
1171
1148
|
routeRule: routeRule,
|
|
@@ -1173,7 +1150,7 @@ function buildApprovalNode(node, nextNodeId, nameToIdMap, nodeType) {
|
|
|
1173
1150
|
if (approverConfig.approvals !== undefined) {
|
|
1174
1151
|
processNodeProps.approvals = approverConfig.approvals;
|
|
1175
1152
|
}
|
|
1176
|
-
Object.assign(processNodeProps, approverConfig.processProps);
|
|
1153
|
+
Object.assign(processNodeProps, approverConfig.processProps, nodeActions.processProps);
|
|
1177
1154
|
let multiApproverRules;
|
|
1178
1155
|
if (approvalNodeType === 'multiApproval') {
|
|
1179
1156
|
const multiMode = node.approvalMode
|
|
@@ -1220,10 +1197,7 @@ function buildApprovalNode(node, nextNodeId, nameToIdMap, nodeType) {
|
|
|
1220
1197
|
nodeName: componentName,
|
|
1221
1198
|
name: i18n(displayName, titleText[1], displayName),
|
|
1222
1199
|
description: nodeDescription,
|
|
1223
|
-
actions:
|
|
1224
|
-
normalActions: buildActions(),
|
|
1225
|
-
appendActions: buildActions(),
|
|
1226
|
-
},
|
|
1200
|
+
actions: nodeActions.viewActions,
|
|
1227
1201
|
routeRule: routeRule,
|
|
1228
1202
|
};
|
|
1229
1203
|
if (multiApproverRules) {
|
|
@@ -125,6 +125,7 @@ function verifyPlatformView(response, expectedViewJson, expectedFormUuid) {
|
|
|
125
125
|
actual: actualNodes,
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
|
+
verifyActionSettings(expectedViewJson.schema.children, actualView.schema.children, errors);
|
|
128
129
|
return {
|
|
129
130
|
verificationLevel: errors.length === 0 ? 'PLATFORM_VIEW_VERIFIED' : 'PUBLISHED_UNVERIFIED',
|
|
130
131
|
valid: errors.length === 0,
|
|
@@ -133,6 +134,36 @@ function verifyPlatformView(response, expectedViewJson, expectedFormUuid) {
|
|
|
133
134
|
};
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
/** Compare permissions and append semantics, tolerating platform-added metadata. */
|
|
138
|
+
function verifyActionSettings(expectedNodes, actualNodes, errors, parentPath = '') {
|
|
139
|
+
(expectedNodes || []).forEach((node, index) => {
|
|
140
|
+
const actual = (actualNodes || [])[index];
|
|
141
|
+
const path = parentPath + '/' + index;
|
|
142
|
+
const expectedActions = node.props && node.props.actions;
|
|
143
|
+
const actualActions = actual && actual.props && actual.props.actions;
|
|
144
|
+
if (isPlainObject(expectedActions)) {
|
|
145
|
+
['normalActions', 'appendActions'].forEach(group => {
|
|
146
|
+
if (!Array.isArray(expectedActions[group])) { return; }
|
|
147
|
+
expectedActions[group].forEach(action => {
|
|
148
|
+
const matches = actualActions && Array.isArray(actualActions[group])
|
|
149
|
+
? actualActions[group].filter(item => item && item.action === action.action) : [];
|
|
150
|
+
const found = matches[0];
|
|
151
|
+
const keys = ['hidden'];
|
|
152
|
+
if (group === 'normalActions' && action.action === 'append' && !action.hidden) {
|
|
153
|
+
keys.push('appendPosition', 'appendResult');
|
|
154
|
+
}
|
|
155
|
+
if (matches.length !== 1 || keys.some(key => JSON.stringify(action[key]) !== JSON.stringify(found[key]))) {
|
|
156
|
+
errors.push({ code: 'PROCESS_PLATFORM_VIEW_ACTION_MISMATCH', path, group, action: action.action });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (Array.isArray(node.children)) {
|
|
162
|
+
verifyActionSettings(node.children, actual && actual.children, errors, path);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
136
167
|
module.exports = {
|
|
137
168
|
extractPlatformView,
|
|
138
169
|
verifyPlatformView,
|
package/package.json
CHANGED
package/yida-skills/SKILL.md
CHANGED
|
@@ -128,7 +128,7 @@ description: >
|
|
|
128
128
|
| 创建自定义展示页资源 | `yida-create-page`,之后交给 `yida-canvas-custom-page` 编写页面源码,再交给 `yida-publish-page` 发布 |
|
|
129
129
|
| 开发表单字段结构 / 增删改字段 | 使用 `yida-create-form-page` 落地字段结构 |
|
|
130
130
|
| 创建带审批的流程表单 | `yida-create-process` |
|
|
131
|
-
|
|
|
131
|
+
| 修改已有流程节点/分支/字段权限,配置加签/转交操作权限 | `yida-process-rule`;加签/转交先读其 `references/approval-actions.md` |
|
|
132
132
|
| 查字段 ID / 保存 Schema 证据 | `yida-get-schema`;凡涉及 fieldId 的数据、流程、公式、页面代码先取证 |
|
|
133
133
|
| 改表单数据记录 | `yida-data-management`,不是 `yida-create-form-page` |
|
|
134
134
|
| 配字段默认值、计算、校验 | `yida-formula`;静态检查用 `yida-formula-evaluate` |
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## 2.0 先分析并确认需求
|
|
10
10
|
|
|
11
|
-
调用 `yida-requirement-analysis`,按 [需求分析与首次搭建确认](../../yida-requirement-analysis/workflow/prepare-brief.md) 整理来源、复用资源及用户已有计划,在同一轮一次性确认尚未明确的 Fast / Plan
|
|
11
|
+
调用 `yida-requirement-analysis`,按 [需求分析与首次搭建确认](../../yida-requirement-analysis/workflow/prepare-brief.md) 整理来源、复用资源及用户已有计划,在同一轮一次性确认尚未明确的 Fast / Plan、业务模块、页面与表单、导航归属及风格。导航 `ask_human` 仅提供“宜搭原生导航”和“自定义导航”两个选项,原生导航就是平台导航,并按 [导航选项说明](../../yida-requirement-analysis/workflow/prepare-brief.md#导航选项说明) 备注实现方式,不标记“推荐”。顶部、侧边、L 型等布局在用户所选归属内根据场景确定,不询问布局或样式;用户已明确的要求优先,后续阶段复用归属选择和布局依据。首次搭建在必要回答写回、`intake.confirmed=true` 后继续。
|
|
12
12
|
|
|
13
13
|
回答齐全后直接保存内部需求记录并进入 2.1;不把“生成需求简报”列为独立任务,不再扩写或展示简报请用户确认。已有确认记录且需求未变化时直接复用。记录粒度、保存和校验规则统一遵守上述需求分析流程。
|
|
14
14
|
|
|
@@ -40,7 +40,27 @@
|
|
|
40
40
|
|
|
41
41
|
## 应用导航配置
|
|
42
42
|
|
|
43
|
-
按 PRD 的 [导航类型](../../yida-prd/workflow/output-prd.md
|
|
43
|
+
按 PRD 的 [导航类型](../../yida-prd/workflow/output-prd.md#agent-导航实施映射) 设置布局。已有应用切换平台导航时执行 `openyida update-app <appType> --layout <l_shape|top|side> --show-app-nav`;自定义导航执行 `openyida update-app <appType> --hide-app-nav`,并将逐页隐藏清单交给 Step 4 与 Step 6,在各页面创建后立即配置,Step 8 只做发布后核对。新建应用也在主题文件生成后的同一次 `update-app --theme-file` 中传入布局和 `--hide-app-nav` / `--show-app-nav`,不要提前单独更新导航或在创建时传入布局。用户已确认的应用导航隐藏随这次设置更新一起生效,不等待自定义页面创建、代码开发或发布。
|
|
44
|
+
|
|
45
|
+
### 平台导航参数
|
|
46
|
+
|
|
47
|
+
与 `yida-next` 的 ThemeNavSetting 一致:`updateApp.json` 接收的字段名是 **`layoutDirection`**。CLI 使用 `--layout` 传入,按下表逐项对应:
|
|
48
|
+
|
|
49
|
+
| PRD 导航方案 | CLI 参数 | 请求中的 `layoutDirection` | 请求中的 `hideAppNav` |
|
|
50
|
+
| --- | --- | --- | --- |
|
|
51
|
+
| 平台顶部导航 | `--layout top --show-app-nav` | `top` | `n` |
|
|
52
|
+
| 平台侧边导航 | `--layout side --show-app-nav` | `side` | `n` |
|
|
53
|
+
| 平台 L 型导航 | `--layout l_shape --show-app-nav` | `l_shape` | `n` |
|
|
54
|
+
|
|
55
|
+
例如,仅将已有应用切换为平台顶部导航:`openyida update-app <appType> --layout top --show-app-nav`。联合更新主题时,将这两个参数合并到同一次 `update-app --theme-file` 调用。
|
|
56
|
+
|
|
57
|
+
Agent 必须显式传入场景选择对应的布局,不依赖 CLI 默认值。`navigationType=platform-top` 是计划内部标识,不能作为 `--layout` 的值;也不能用旧 Shell 的 `navType`、`hoz/ver/slide` 或 `top_fold/top_side/side_only` 代替上述参数。`navTheme` 只控制导航配色,不控制布局。自定义导航的 `variant=top/side/mixed/dock` 描述页面内菜单,不用于设置平台布局;自定义导航仍使用 `--hide-app-nav`。
|
|
58
|
+
|
|
59
|
+
`navType` 仍是兼容字段:与 `yida-next` 一致,CLI 查询应用后将已有值原样带回;缺失时不补造,也不根据新布局改写。没有新增 `--nav-type` 参数。未传 `--layout` 时,CLI 按 `yc-utils` 的应用配置规则保留原布局:现代 `side/top/l_shape` 优先;旧 `hoz + top_side` 为 L 型,其他 `hoz` 为顶部,`ver` 为侧边;布局缺失时用 `navType=top_fold/top_side` 分别恢复顶部/L 型,其余回退侧边。
|
|
60
|
+
|
|
61
|
+
Shell 渲染阶段才把顶部、侧边、L 型转换为 `top_fold`、`side_only`、`top_side`。详情/提交页的 `top_fold` 强制顶部、`none` 保持无导航属于页面运行态规则,不能反向写入应用配置。
|
|
62
|
+
|
|
63
|
+
保存后核对应用详情中的 `layoutDirection` 与 `hideAppNav` 是否等于目标值,可通过应用设置页或 `/<appType>/query/app/getAppIncludingAecpInfo.json` 回读。CLI 输出的 `updatedFields` 是提交值,`themeVerification` 只验证主题资源,两者都不能单独证明导航配置已生效。发布后再刷新应用,检查实际布局;未完成回读时明确标记未验证。
|
|
44
64
|
|
|
45
65
|
## 产出
|
|
46
66
|
|
|
@@ -63,7 +63,7 @@ openyida update-app <appType> --theme-file <app-theme.css> --nav-theme light --l
|
|
|
63
63
|
|---------|------|
|
|
64
64
|
| `--colour` / `--theme` | 平台主题 key,例如 `podBlue`、`podGreen`、`podOrange`、`black`、`custom`;禁止填写 HEX、RGB 或自造名称 |
|
|
65
65
|
| `--nav-theme` | `light` / `dark` / `white` / `gray` |
|
|
66
|
-
| `--layout` |
|
|
66
|
+
| `--layout` | 顶部传 `top`、侧边传 `side`、L 型传 `l_shape`;CLI 写入接口字段 `layoutDirection`,使用平台导航时同时传 `--show-app-nav` |
|
|
67
67
|
| `--theme-file` | 上传主题 CSS,保存 `customThemeStyle` 资源及从 CSS 提取的 `themeColor` |
|
|
68
68
|
| `--theme-color` | 仅更新应用主色;与主题文件同传时以 CSS 主色为准 |
|
|
69
69
|
| `--logo-source` | `appIcon` / `customImage`;后者要求应用已有 `homepageLogo` |
|
|
@@ -77,7 +77,7 @@ openyida update-app <appType> --theme-file <app-theme.css> --nav-theme light --l
|
|
|
77
77
|
|
|
78
78
|
创建命令只提交名称、描述、图标和必要的创建标记,不推断或提交应用主题与导航配置。创建完整应用、使用 PRD/design.md 或用户要求配置主题时,主题更新命令默认传入主题文件;只有用户明确只创建空壳或暂不配置主题时才跳过主题更新。搭建流程必须采用先 `create-app`、再 `update-app --theme-file` 的两个步骤。`create-app` 不接受 `--theme-file` 和 `--logo-source`,也不接受 `--colour`、`--theme`、`--nav-theme`、`--layout` 或旧位置参数里的主题与布局,不隐式执行应用设置更新。
|
|
79
79
|
|
|
80
|
-
CLI 始终按“显式 `--icon` → 行业推断 → 随机系统图标”的顺序选择图标,只有未显式指定且未命中行业时才随机,与后续是否更新主题文件无关。主题文件生成后按 PRD 在 `update-app --layout`
|
|
80
|
+
CLI 始终按“显式 `--icon` → 行业推断 → 随机系统图标”的顺序选择图标,只有未显式指定且未命中行业时才随机,与后续是否更新主题文件无关。主题文件生成后按 PRD 在 `update-app --layout` 中显式设置布局;用户未指定布局时按业务场景选择,不固定使用 `l_shape`。参数映射与保存后核对遵守 [平台导航参数](../yida-app/workflow/step-3-create-or-reuse-app.md#平台导航参数)。在 `update-app --theme-file` 步骤中校验 CSS 并将图标颜色统一为 `--color-brand1-6` 转换后的 HEX;导航配置沿用 PRD,普通浅色方案显式传 `--nav-theme light --logo-source appIcon`。
|
|
81
81
|
|
|
82
82
|
**应用主题(colour)口径**:
|
|
83
83
|
|
|
@@ -93,8 +93,8 @@ openyida sample yida-design app-theme --output .cache/openyida/<项目名>/app-t
|
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
95
|
openyida create-app --name "<应用名>" --desc "<描述>"
|
|
96
|
-
# 从创建结果提取 appType
|
|
97
|
-
openyida update-app <appType> --theme-file <app-theme.css> --nav-theme light --logo-source appIcon --layout
|
|
96
|
+
# 从创建结果提取 appType 后执行;以下为 PRD 选择平台顶部导航的示例
|
|
97
|
+
openyida update-app <appType> --theme-file <app-theme.css> --nav-theme light --logo-source appIcon --layout top --show-app-nav
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
CLI 会先校验主题文件完整声明平台实际生成的 `--color-brand1-1/2/3/5/6/9/10`,并允许不存在 `--color-brand1-4/7/8`。`update-app --theme-file` 上传 CSS,再调用应用基础设置的 `updateApp` 接口联合保存从 `--color-brand1-6` 提取的 `themeColor`、`customThemeStyle`、`navTheme`、`logoSource` 和 `layoutDirection`。更新主题时,系统应用图标会同步保存为 `iconName%%主题色HEX`;外链或上传图片图标保持原值。
|
|
@@ -188,6 +188,7 @@ openyida create-process "APP_XXX" --formUuid "FORM-YYY" .cache/openyida/order/pr
|
|
|
188
188
|
1. **🔐 字段权限**:当字段 ≥ 3 且审批节点 ≥ 2 时,每个节点只允许编辑相关字段
|
|
189
189
|
2. **🔄 跳转规则**:存在回退/循环语义时,自动配置 `routeRules`
|
|
190
190
|
3. **🔀 并行/办理/高级组件**:需要并行会审、办理节点或连接器/数据/消息等官方组件节点时,流程定义格式直接参考 `yida-process-rule`
|
|
191
|
+
4. **加签/转交操作权限**:用户要求开启时,按 [操作权限配置](../yida-process-rule/references/approval-actions.md) 生成 `nodes[].actions.normalActions/appendActions`;未要求时保持默认关闭。`create-process` 与 `configure-process` 共用编译器,无需新增命令或手写运行字段。禁止仅修改 `hidden` 后提交原始运行 JSON。
|
|
191
192
|
|
|
192
193
|
需要流程规则细节时,调用 `use_skill("yida-process-rule", "配置已有流程节点、分支和字段权限")`。
|
|
193
194
|
|
|
@@ -198,6 +199,7 @@ openyida create-process "APP_XXX" --formUuid "FORM-YYY" .cache/openyida/order/pr
|
|
|
198
199
|
| 命令返回失败 | 检查 appType 和 formUuid 是否正确,确认登录态有效 |
|
|
199
200
|
| processCode 获取失败 | 确认表单已成功转为流程表单类型,重新执行 |
|
|
200
201
|
| 流程定义 JSON 格式错误 | 加载 `yida-process-rule` 子技能,按其中的 JSON 格式说明修正 |
|
|
202
|
+
| `PROCESS_COMPILE_ACTION_CONFIG_INVALID` | 本地编译已拦截无效操作权限,修正报错节点的加签位置、结果或按钮配置后再执行;不要通过删除校验或手动补运行 JSON 绕过 |
|
|
201
203
|
| 返回 JSON 中无 processCode | 不要猜测 processCode,重新执行命令获取 |
|
|
202
204
|
| 流程发布失败 | 检查流程定义中的 fieldId 是否为真实 ID(先 get-schema 获取) |
|
|
203
205
|
| 登录态失效 | 执行 `openyida login` 重新登录后再试 |
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
1. 进度消息使用业务结果和下一步动作。结构化问题紧随其后时,不额外发送内部分析结论;多个问题确需共同背景时,只在整组交互的引导中说明一次,不写入每个问题的 `title` 或 `prompt`。
|
|
61
61
|
2. `title`、`prompt`、`label`、`description` 和普通对话只使用用户能直接理解的业务词。
|
|
62
62
|
3. `interactionId`、`questionType`、`value`、`writeBackPath`、`reason`、状态字段和文件路径仅供内部执行,不进入用户可见内容。
|
|
63
|
-
4. `title` 只写确认主题,`prompt`
|
|
63
|
+
4. `title` 只写确认主题,`prompt` 只写用户当前需要回答的问题;搭建模式、导航归属保持中性,不标记“推荐”或“默认”,不预选、不按偏好排序,也不在引导或描述中推荐。导航布局和呈现样式根据场景确定,不进入提问选项;用户明确的布局和样式优先。其他问题存在合理默认值时可标记。只有某个选择会产生重要且难以撤销的影响时,才在对应选项的 `description` 中说明影响。
|
|
64
64
|
5. 用户已经提供的信息直接写入内部事实,不重复总结成“缺口分析”或再次追问。
|
|
65
65
|
6. PRD 的业务说明、HTML、进度消息和交付总结使用功能、体验和验收结果描述配置。接口参数、配置键值、内部 ID 和 CLI 选项保留在 Agent 实施交接中;访问链接使用业务名称,链接目标保留必需参数。用户明确询问实现细节时,再解释对应技术内容。
|
|
66
66
|
7. 规划写预期行为,进度写正在进行的动作,完成总结依据实际验证结果。导航方案写“采用自定义导航,支持各业务页面间切换”;完成配置并验证跳转后写“已启用自定义导航,可在各业务页面间切换”。
|
|
@@ -100,7 +100,7 @@ Fast / Plan 是面向用户的模式名称,可以展示。已有详细计划
|
|
|
100
100
|
| ---------------- | ---------------------------------------------- | ---------------------------- | ----------------------------- |
|
|
101
101
|
| 搭建方式 | `single_choice` | 按需求详细程度提供 PRD 是/否或 Fast/Plan | brief 的 `intake.designMode` |
|
|
102
102
|
| 业务模块与页面范围 | `multi_choice` | 具体业务模块、对应页面、表单及用途,可合为一题 | brief 的 `coreFunctions/businessObjects/pageScenes/explicitScope` |
|
|
103
|
-
|
|
|
103
|
+
| 导航归属 | `single_choice` | 仅“宜搭原生导航”和“自定义导航”两个选项;原生导航就是平台导航,布局根据场景确定 | brief 的 `navigation`(按场景补齐 `type`,自定义含 `variant`,`reason` 区分用户归属选择与布局依据) |
|
|
104
104
|
| 应用设计风格 | `single_choice` | 面向业务的风格选项与自定义偏好 | brief 的 `visualSelection` |
|
|
105
105
|
| 来源补齐 | `free_text` | 可读正文、链接或附件 | `meta.source` |
|
|
106
106
|
| 最新搭建计划确认 | `confirm` | 当前版本、确认生成、继续调整 | `meta.planState` |
|
|
@@ -124,9 +124,9 @@ Fast / Plan 是面向用户的模式名称,可以展示。已有详细计划
|
|
|
124
124
|
|
|
125
125
|
## 首次搭建确认
|
|
126
126
|
|
|
127
|
-
先完成需求分析,再按 [首次搭建确认表](../../yida-requirement-analysis/workflow/prepare-brief.md#2-确认首次搭建的未决事项)
|
|
127
|
+
先完成需求分析,再按 [首次搭建确认表](../../yida-requirement-analysis/workflow/prepare-brief.md#2-确认首次搭建的未决事项) 询问尚未明确的事项。该表统一维护详细计划复用、搭建方式、业务模块、导航归属、风格和页面范围;已有业务应用的局部增改按本次疑问澄清。
|
|
128
128
|
|
|
129
|
-
首次搭建的全部未决事项在一次结构化提问调用中呈现并一次性作答,Fast / Plan
|
|
129
|
+
首次搭建的全部未决事项在一次结构化提问调用中呈现并一次性作答,Fast / Plan、导航归属、业务模块和页面范围不得分轮收集。导航仅提供“宜搭原生导航”和“自定义导航”两个选项,按 [导航选项说明](../../yida-requirement-analysis/workflow/prepare-brief.md#导航选项说明) 附带实现方式说明;宜搭原生导航就是平台导航。支持 `description` 时写入该字段,否则在选项后括号备注。顶部、侧边、L 型、浮导、底部菜单等布局或样式不进入 `ask_human`;Agent 在已选归属内根据场景确定,记录布局依据并交给后续规划,不补问位置。用户已明确的归属、布局或样式直接沿用。工具限制问题数量时合并为复合问题,多选能力不足时使用同组自由输入;没有可用结构化工具时,在一条消息中展示全部问题。具体合并方式遵守首次搭建确认表,全部必要回答写回后才开始后续规划。
|
|
130
130
|
|
|
131
131
|
视觉映射统一写入 brief 的 `visualSelection`,由后续计划沿用 visualDirection、selectedTheme、colorStrategy、navigationStyle;未选候选保留在会话中。
|
|
132
132
|
|
package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md
CHANGED
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
|
|
143
143
|
约束:
|
|
144
144
|
|
|
145
|
-
-
|
|
145
|
+
- 计划生成前先确定应用范围与导航类型,再选择视觉方向。范围和导航归属不明时可合并询问,导航选项仅为“宜搭原生导航”和“自定义导航”;原生导航即平台导航,顶部、侧边、L 型等布局由 Agent 按场景确定,不进入 `ask_human`。回答后生成遵守导航决策的视觉候选;每个问题必须有 `interactionId` 和 `writeBackPath`。
|
|
146
146
|
- `questionType` 只取 `single_choice`、`multi_choice`、`free_text` 或最终计划使用的 `confirm`,不设置 AI 代填项中间确认类型。
|
|
147
147
|
- 只有宽泛应用名称且缺少模块、场景、任务、流程和可读需求时才问应用范围。
|
|
148
148
|
- 审批、角色权限、字段、首页、看板、页面和常规流程细节不进入 `ask_human`;用户已提供时承接,未提供时基于业务推断并在最终计划统一呈现。
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## 自定义导航设计
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
沿用需求阶段用户选择的导航归属,以及根据场景确定或用户指定的布局;不再询问顶部、侧边等布局选项。参考 [导航壳形态目录](../../yida-nav-shell/references/nav-shell-patterns.md) 设计位置、比例、留白与选中态,代码示例只按需参考。顶部默认浮导;侧导及顶部+侧边布局写清折叠/展开、恢复宽度、拖拽边界、内容区联动和移动端收起方式。已有满意的导航保留外观,只补缺失交互。
|
|
17
17
|
|
|
18
18
|
区分三种操作:本页视图切换、保留导航并更新主内容 iframe、当前标签跨页跳转。管理入口使用 workbench,办理入口使用 submission;不要把应用级办理导航设计为每次弹抽屉。页面内新增/详情按钮才采用下面的抽屉规则。设计结果写入当前 `design.md`;Plan 模式先更新计划源事实再物化。
|
|
19
19
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
| 悬浮胶囊 / Dock | 沉浸展示、轻量门户,减少常驻导航占位 | 3–6 项 | 底部胶囊或可收起菜单 |
|
|
15
15
|
| 标签页 | 同一模块的同级视图,不替代应用主导航 | 2–8 项 | 横向滚动 |
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
数量是布局参考,不是增删业务模块的依据。用户选择自定义导航后,由 Agent 按场景确定形态,用户已指定的形态优先;不把形态拆成导航提问选项,也不为这些样式再发起一轮提问。
|
|
18
18
|
|
|
19
19
|
## 通用设计要点
|
|
20
20
|
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
|
|
25
25
|
| 配置项 | 值 |
|
|
26
26
|
| --- | --- |
|
|
27
|
-
| 导航类型 | <平台L型导航 / 平台顶部导航 / 平台侧边导航 /
|
|
27
|
+
| 导航类型 | <平台L型导航 / 平台顶部导航 / 平台侧边导航 / 自定义导航;按用户选择的归属和场景确定具体布局> |
|
|
28
28
|
| 是否使用平台应用导航 | <前三种为是,自定义导航为否> |
|
|
29
29
|
| 页面导航配置 | <自定义导航:列出本轮全部表单、流程表单、自定义页面及需配置的其他页面,统一隐藏平台页面导航;平台导航:保留页面设置,明确独立入口例外> |
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
用户仅选择宜搭原生导航(即平台导航)或自定义导航;上表记录可执行方案,平台顶部、侧边、L 型以及自定义布局由 Agent 根据场景确定,用户已指定的布局优先。导航方案区分用户选择的归属与布局判断依据,说明页面入口和跨页切换方式;导航配色单独说明深色或浅色。
|
|
32
32
|
|
|
33
33
|
## 3. 数据结构(业务语义,不含细节 ID)
|
|
34
34
|
|
|
@@ -34,7 +34,7 @@ description: 配置已有流程表单审批规则。
|
|
|
34
34
|
- 配置前先用 `yida-get-schema` 获取所有字段 ID
|
|
35
35
|
- 流程定义 JSON 必须用结构化文件写入工具创建到 `<projectRoot>/.cache/openyida/<项目名或任务名>/`,不要在仓库根目录、系统临时目录或 `.cache/` 顶层生成 `process-definition.json` 等临时文件
|
|
36
36
|
- 必须以表单 binding 只读结果证明 `processCode` 属于目标 `formUuid`;`CONFIGURE_PROCESS_OWNERSHIP_UNVERIFIED` 时停止,不得换一个 processCode 猜测重试
|
|
37
|
-
- 命令成功必须同时返回 `verificationLevel: "PLATFORM_VIEW_VERIFIED"` 和 `platformViewVerified: true`;这只证明平台可见 view
|
|
37
|
+
- 命令成功必须同时返回 `verificationLevel: "PLATFORM_VIEW_VERIFIED"` 和 `platformViewVerified: true`;这只证明平台可见 view 的节点、组件、名称、顺序、审批模式及按钮权限和加签参数,不代表平台返回了可独立验证的 `processJson`
|
|
38
38
|
|
|
39
39
|
## 适用场景
|
|
40
40
|
|
|
@@ -173,6 +173,7 @@ OpenYida 会自动兼容常见别名:
|
|
|
173
173
|
| `approver` | String/Object | 是 | 审批人。`"originator"` 表示发起人;Object 支持 `user`、`role`、`deptLeader`、`directLeader`,也可传入宜搭流程设计器的原始审批人配置 |
|
|
174
174
|
| `description` | String | 否 | 节点描述 |
|
|
175
175
|
| `formConfig` | Object | 否 | 字段权限配置 |
|
|
176
|
+
| `actions` | Object | 否 | 操作权限,包含 `normalActions` 和 `appendActions`,见下文 |
|
|
176
177
|
| `routeRules` | Array | 否 | 跳转规则 |
|
|
177
178
|
|
|
178
179
|
### 办理 / 填写节点(operator)
|
|
@@ -281,6 +282,10 @@ OpenYida 会自动兼容常见别名:
|
|
|
281
282
|
| `In` | 属于 | SelectField, RadioField |
|
|
282
283
|
| `NotIn` | 不属于 | SelectField, RadioField |
|
|
283
284
|
|
|
285
|
+
### 加签和转交(actions)
|
|
286
|
+
|
|
287
|
+
CLI 支持通过 `node.actions.normalActions/appendActions` 同时生成按钮和完整加签参数。需要开启加签或转交时,先阅读 [操作权限配置](references/approval-actions.md);不要仅修改原始 JSON 的 `hidden`。未配置时保持历史默认权限。
|
|
288
|
+
|
|
284
289
|
### 字段权限配置(formConfig)
|
|
285
290
|
|
|
286
291
|
```json
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 加签和转交(actions)
|
|
2
|
+
|
|
3
|
+
适用于 `approval`、`operator` 和 `multiApproval` 节点。加签和转交可以通过 CLI 配置;必须由编译器同时生成设计器配置和运行配置,不能只改 `hidden` 后直接提交原始流程 JSON。
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"type": "approval",
|
|
8
|
+
"key": "purchase_approval",
|
|
9
|
+
"name": "采购审批",
|
|
10
|
+
"approver": "originator",
|
|
11
|
+
"actions": {
|
|
12
|
+
"normalActions": [
|
|
13
|
+
{ "action": "forward", "hidden": false },
|
|
14
|
+
{
|
|
15
|
+
"action": "append",
|
|
16
|
+
"hidden": false,
|
|
17
|
+
"appendPosition": ["BEFORE_APPEND", "AFTER_APPEND"],
|
|
18
|
+
"appendResult": "valid"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"appendActions": [
|
|
22
|
+
{ "action": "forward", "hidden": false },
|
|
23
|
+
{ "action": "append", "hidden": false }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- `normalActions` 是普通审批人的按钮;`appendActions` 是被加签人的按钮,独立配置。数组按 `action` 部分覆盖默认值,不配置时保持历史默认:同意、拒绝显示,保存、转交、加签、退回隐藏。
|
|
30
|
+
- 加签位置和结果是节点级规则,在 `normalActions` 的 `append` 动作上设置。`appendPosition` 必须为非空数组,支持 `BEFORE_APPEND`(前加签)、`AFTER_APPEND`(后加签);省略时默认前加签。`appendResult` 支持 `valid`(参与审批)、`invalid`(不参与审批);省略时默认 `valid`,与设计器开启加签时一致。
|
|
31
|
+
- 编译器同步生成运行配置 `allowTaskAppend`、`moldList`、`isConsiderAppendedAction`、`isNeedEndTaskGroupChain`。转交使用 `forward.hidden`,无需虚构额外的转交范围配置。
|
|
32
|
+
- 开启被加签人的再次加签时,也须开启普通审批人的加签,以确保存在节点级加签规则;无效配置会在发布前报错。
|
|
33
|
+
- 兼容已有 `approver.processProps.actions/appendActions` 完整数组,并同步到设计器;`node.actions` 对对应数组优先。旧运行字段 `moldList` 和布尔类型 `isConsiderAppendedAction` 可用于补齐省略的加签参数。不要同时提供互相矛盾的两套配置。
|
|
34
|
+
- 发布后的回读会核对按钮是否隐藏,以及开启加签时的位置和结果设置。`PLATFORM_VIEW_VERIFIED` 仍不等于真实审批人已完成加签/转交验收;需要在获授权的测试流程中分别验证普通审批人、被加签人和移动端。
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## CLI 入口
|
|
38
|
+
|
|
39
|
+
将以上节点放入完整流程定义的 `nodes` 数组,由同一编译器处理:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process-with-actions.json
|
|
43
|
+
openyida create-process APP_XXX --formUuid FORM_XXX .cache/openyida/process/process-with-actions.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
已有流程使用第一条;普通表单首次转流程可使用第二条。配置命令会替换整张流程图,必须保留完整节点与分支;发现已有版本时需按主技能的替换要求使用 `--replace`。本功能不提供 `--append`、`--forward` 等独立开关,也不用于执行某一待办实例的加签或转交操作。
|
|
47
|
+
|
|
48
|
+
用 `openyida configure-process --help`、`openyida create-process --help` 查看入口;命令发现 JSON 也会返回加签/转交配置提示。发布后若返回 `PROCESS_PLATFORM_VIEW_ACTION_MISMATCH` 诊断,不得将已发布但未验证的流程当作成功,也不要直接重试写入。
|
|
@@ -32,7 +32,7 @@ description: 识别并读取需求来源,理解和澄清用户需求,输出
|
|
|
32
32
|
| `pageScenes` | 已确认的页面与表单范围,数组;记录稳定 key、name、kind、purpose 及已有细项 |
|
|
33
33
|
| `intake` | 首次搭建判断、来源详细程度、搭建方式和需求确认状态 |
|
|
34
34
|
| `visualSelection` | 已确认风格及其主题、主色、导航明暗映射;Plan 初始化前必须含非空 `themeId` |
|
|
35
|
-
| `navigation` |
|
|
35
|
+
| `navigation` | 用户仅选择宜搭原生导航(即平台导航)或自定义导航;Agent 按场景确定布局并在 `reason` 区分选择与推断。应用导航决策:`type` 只能是 `platform-l-shape/platform-top/platform-side/custom`,并记录 `source/reason`;自定义导航增加 `variant: side/top/mixed/dock`;未决时 type 为 null,规划前补齐 |
|
|
36
36
|
| `resourceContext` | 已确认可复用的 app/page/form/process 业务上下文,不写猜测 ID |
|
|
37
37
|
| `explicitScope` | 用户明确指定的页面、表单、流程、报表、导航项和本轮交付;明确窄范围时写对应数组及 `allowInferredResources:false`,没有时为 `null` |
|
|
38
38
|
| `brandHints` / `colorHints` | 明确的品牌、参考页面、已有主题、偏好色与避用色 |
|
|
@@ -14,37 +14,46 @@
|
|
|
14
14
|
|
|
15
15
|
## 2. 确认首次搭建的未决事项
|
|
16
16
|
|
|
17
|
-
先分析再提问。用户已明确的信息直接采用;同一次搭建已回答的问题直接复用。首次提问必须在同一轮一次性收集所有尚未明确的搭建方式(Fast / Plan
|
|
17
|
+
先分析再提问。用户已明确的信息直接采用;同一次搭建已回答的问题直接复用。首次提问必须在同一轮一次性收集所有尚未明确的搭建方式(Fast / Plan)、业务模块、页面与表单范围、导航归属(宜搭原生导航或自定义导航)和设计风格。不要先问模块、导航和风格,收到回答后再另起一轮补问模式、导航位置或页面。
|
|
18
18
|
|
|
19
19
|
先锁定用户已经明确的搭建方式:用户写明 `Plan`、先出 PRD/方案并确认后搭建时,立即把草稿的 `intake.designMode` 设为 `plan`;用户写明 `Fast` 或直接快速搭建时设为 `fast`。已锁定的搭建方式不再进入提问选项。后续 `ask_human` 只补齐其他未决事项,合并回答时必须保留已有 `intake.designMode`;不能因为回答里没有重复提到模式、结构化问题被合并、上下文压缩或重新生成 brief 而回退到 Fast。只有用户明确说“改用 Fast/Plan”时才能切换,并以最后一次明确选择为准。
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
将整组问题放在一次结构化提问调用中。宿主限制问题数量时,合并为“搭建方式”“导航归属”“业务模块、页面与风格”等复合问题;选项数量或多选能力不足时,用允许自由输入的同组问题列出完整选择。不得因为工具数量限制而拆成多轮。只有回答遗漏、互相冲突或产生新的关键业务疑问时才针对性追问。
|
|
22
22
|
|
|
23
23
|
| 事项 | 何时询问 | 用户可见问题与选项 |
|
|
24
24
|
| --- | --- | --- |
|
|
25
25
|
| 搭建方式 | 用户未选择方式,且已提供详细计划 | “是否根据你提供的需求,整理一份应用 PRD 计划供你确认?” 是:计划更清晰详细,耗时更长;否:按现有需求快速搭建 |
|
|
26
26
|
| 搭建方式 | 用户未选择方式,且需求细节不足 | “希望用哪种方式搭建?” Fast(快速搭建);Plan(先生成 PRD,确认后再搭建,耗时更长) |
|
|
27
|
-
|
|
|
27
|
+
| 导航归属 | 用户未明确宜搭原生导航或自定义导航 | “你希望应用使用哪种导航菜单?” 仅提供“宜搭原生导航”和“自定义导航”两个选项,并附下表说明。宜搭原生导航就是平台导航;归属已明确时不再询问,顶部、侧边、L 型等布局根据场景选择,不进入 `ask_human` |
|
|
28
28
|
| 业务模块 | 用户未明确业务范围 | “应用需要包含哪些业务模块?” 根据业务列出具体模块及用途,支持多选或自行补充;不要只用“标准4-5模块”代替具体模块清单 |
|
|
29
29
|
| 应用设计风格 | 用户未提及 | 根据业务提供简短的配色与界面风格选项,并允许描述自己的偏好;已有品牌或参考图时沿用 |
|
|
30
30
|
| 页面范围 | 用户未明确页面与表单 | “这次需要哪些页面和表单?” 同轮按候选业务模块列出页面和表单及用途,支持多选或自行补充,例如“工作台:汇总待办”“活动报名表:填写报名信息”;可与业务模块合成一题,不等模块回答后再问,也不要只用“标准4页”代替页面清单 |
|
|
31
31
|
|
|
32
32
|
### 导航选项说明
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
导航 `ask_human` 仅有以下两个选项,不把布局或呈现样式拆成选项,也不追加布局问题。选项名称与说明一起呈现;支持 `description` 时写入该字段,否则在选项名称后用括号备注。
|
|
35
35
|
|
|
36
36
|
| 导航选项 | 必须附带的说明 |
|
|
37
37
|
| --- | --- |
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
| 平台顶部导航 | 使用宜搭原生导航,菜单位于顶部 |
|
|
41
|
-
| 自定义导航 | 在自定义页面中实现,不使用宜搭原生导航 |
|
|
38
|
+
| 宜搭原生导航 | 使用宜搭自带的导航菜单。 |
|
|
39
|
+
| 自定义导航 | 在自定义页面里定制菜单的样式和操作方式,替代宜搭自带的导航菜单。 |
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
需要解释布局时,统一使用:“菜单放在顶部、左侧,还是顶部加左侧,会根据应用场景安排,你不用再选。” 面向用户不使用“导航归属”“布局枚举”等内部术语。
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
模式和导航归属选项平等说明,不推荐、不预选,不添加“(推荐)”或“默认”。详细计划问题的“是”对应 Plan,“否”对应 Fast;两者都复用已有需求。用户明确要求代为决定时,记录选择和依据。
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
### 根据场景确定导航布局
|
|
46
|
+
|
|
47
|
+
用户只选择导航归属。Agent 在该归属内,结合业务模块数量、层级、切换频率、内容宽度和目标设备确定布局;用户已明确布局或提供参考时优先沿用。用户只说“平台导航”视为已选“宜搭原生导航”;只指定顶部、侧边或 L 型但未明确归属时,保留布局要求,仅询问上述两个归属选项。
|
|
48
|
+
|
|
49
|
+
| 场景 | 宜搭原生导航(平台导航) | 自定义导航 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| 模块少、层级浅,表格或看板需要较宽内容区 | 顶部:`platform-top` | 顶部:`custom` + `variant: top` |
|
|
52
|
+
| 模块或分组多,需要频繁跨模块操作 | 侧边:`platform-side` | 左侧:`custom` + `variant: side` |
|
|
53
|
+
| 业务域与域内模块形成两级导航 | L 型:`platform-l-shape` | 顶部+侧边:`custom` + `variant: mixed` |
|
|
54
|
+
| 少量高频入口的移动端轻量门户或沉浸展示 | 按实际层级选顶部或侧边 | 可选底部悬浮菜单:`custom` + `variant: dock` |
|
|
55
|
+
|
|
56
|
+
这些是布局判断依据,不是固定模板或用户问卷;不得为适配布局增删业务模块,也不能因布局偏好改变已选导航归属。自定义形态细化参考 [导航壳形态目录](../../yida-nav-shell/references/nav-shell-patterns.md)。自定义顶部未指定样式时默认浮导;侧边及顶部+侧边在 PRD/design 中写入折叠/展开和拖拽调宽,不另行提问。
|
|
48
57
|
|
|
49
58
|
额外只补问影响搭建的关键疑问:给谁使用、主要解决什么问题、创建还是复用应用、数据可见范围、关键审批规则、必要的外部数据来源。能够从需求和资源上下文确定的内容直接记录;一般字段和布局细节交给后续规划。
|
|
50
59
|
|
|
@@ -63,7 +72,7 @@
|
|
|
63
72
|
- 已有确认记录且需求未变化时直接复用;后续只补充已确定的视觉映射或更新用户变更涉及的字段,不因阶段切换重写全文或重新生成页面 key。
|
|
64
73
|
|
|
65
74
|
- `intake` 记录 `firstBuild`、`sourceDetail`(`detailed/brief`)、`designMode`(`fast/plan`)、`confirmed`。未决事项处理完毕后才将 confirmed 设为 true。
|
|
66
|
-
- `navigation` 记录 `type/source/reason
|
|
75
|
+
- `navigation` 记录 `type/source/reason`;归属由用户选择时 `source` 为 `user_selected`,`reason` 分别写清用户选择的归属、Agent 根据场景确定的布局及依据,不将推断布局记成用户指定。归属也由用户授权代选时标记 `ai_default`。保存 confirmed brief 前按上表补齐可执行的布局枚举,不把中文选项名或笼统的 `platform` 写入 `type`,不因缺少布局再次提问;自定义导航的 `variant` 为 `side/top/mixed/dock`。顶部浮导使用 `top`,默认浮导或用户指定的通栏样式写入 `reason`,PRD 与视觉设计共同沿用;`dock` 表示底部悬浮胶囊。导航明暗由视觉选择记录。
|
|
67
76
|
- 业务模块答案写入 `coreFunctions/businessObjects/explicitScope`;与页面范围合问时,分别保存模块事实与 `pageScenes`,不要只保留模块或页面数量。
|
|
68
77
|
- `targetUsers/businessGoals/coreFunctions/businessObjects/pageScenes` 一律保持数组类型;单个目标也写成单元素数组。Plan 的 `visualSelection.themeId` 在保存 confirmed brief 前补齐;导航 type 使用 `platform-l-shape/platform-top/platform-side/custom` 精确枚举。
|
|
69
78
|
- 用户先用“应用/系统”描述背景、后续又明确“只完成/随后完成一个”具体资源并交付时,以具体资源作为本轮执行边界。`explicitScope` 写对应的 forms/processes/reports/pages/delivery 数组并设置 `allowInferredResources:false`;不把“应用”自动扩展为示例数据、工作台、自定义列表或其他未点名资源。
|
|
@@ -79,7 +88,7 @@ Plan 的视觉选择直接使用下面的对象结构,不另写 `styleDescript
|
|
|
79
88
|
"themeId": "airy-structured-clarity",
|
|
80
89
|
"visualDirection": {"label": "轻盈结构", "description": "清晰、简洁、适合持续业务操作", "source": "requirement"},
|
|
81
90
|
"colorStrategy": {"primaryColor": "#1677FF", "primaryColorName": "专业蓝", "source": "requirement", "usage": "主操作与选中态", "surfaceTone": "brand-tinted"},
|
|
82
|
-
"navigationStyle": {"structure": "side", "tone": "light", "source": "
|
|
91
|
+
"navigationStyle": {"structure": "side", "tone": "light", "source": "ai_default", "selectionReason": "用户选择宜搭原生导航;模块分组较多,采用平台侧边布局与浅色导航"}
|
|
83
92
|
}
|
|
84
93
|
```
|
|
85
94
|
|