openyida 2026.9.8 → 2026.9.9
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 -1
- package/lib/app/create-form/args.js +36 -2
- package/lib/app/create-form/batch.js +24 -7
- package/lib/app/create-form/commands.js +2 -0
- package/lib/app/create-form.js +292 -0
- package/lib/app/services/canvas-page-schema-builder.js +3 -3
- package/lib/core/command-manifest.js +4 -1
- package/lib/core/locales/en.js +9 -1
- package/lib/core/locales/zh.js +9 -1
- package/lib/core/sample.js +13 -1
- package/lib/design-plan/materialize.js +6 -10
- package/lib/process/configure-process.js +18 -1
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +107 -23
- package/lib/samples/openyida-scaffold/canvas-nav/content.jsx +42 -0
- package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +1 -1
- package/lib/samples/yida-canvas-table-form/table-form-batch-submit.canvas.jsx +84 -17
- package/lib/samples/yida-rechart/trend-combo.canvas.jsx +97 -18
- package/package.json +1 -1
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +3 -3
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +15 -3
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +4 -1
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +2 -0
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +27 -7
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-authoring-examples.md +2 -41
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +93 -65
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +89 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/component-library-guide.md +2 -2
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +11 -3
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +6 -2
- package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +122 -0
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +102 -0
- package/yida-skills/skills/yida-canvas-table-form/SKILL.md +2 -2
- package/yida-skills/skills/yida-create-form-page/SKILL.md +17 -3
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +18 -4
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +2 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-media-grid.md +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-modular-clarity.md +3 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-structured-clarity.md +3 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ambient-halo-layered.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-focus-layered.md +3 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-luminous-modular.md +7 -7
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/deep-stage-duotone.md +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/hairline-runway-clarity.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/high-contrast-modular.md +3 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/interlocked-vivid-modules.md +5 -5
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/matrix-grid-focus.md +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/media-rail-inspector.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mist-layered-signal.md +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/modular-rail-signal.md +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mono-grid-signal.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ribbon-ledger-lift.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/segmented-meter-clarity.md +5 -5
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/split-rail-precision.md +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/status-framed-media-grid.md +3 -3
- package/yida-skills/skills/yida-design/workflow/output-design.md +1 -1
- package/yida-skills/skills/yida-nav-shell/SKILL.md +7 -2
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +84 -3
package/README.md
CHANGED
|
@@ -174,6 +174,8 @@ openyida update-app APP_XXX --theme-file .cache/openyida/crm/app-theme.css --nav
|
|
|
174
174
|
openyida corp-efficiency
|
|
175
175
|
openyida create-form create APP_XXX "Customer" .cache/openyida/forms/customer-fields.json
|
|
176
176
|
openyida create-form update APP_XXX FORM_XXX .cache/openyida/forms/customer-changes.json
|
|
177
|
+
openyida create-form update APP_XXX FORM_XXX --data-file .cache/openyida/forms/customer-changes.json
|
|
178
|
+
openyida create-form resume APP_XXX FORM_XXX .cache/openyida/forms/customer-fields.json --json
|
|
177
179
|
openyida sample openyida-page-template form-fields --output .cache/openyida/forms/customer-fields.json
|
|
178
180
|
openyida sample openyida-page-template canvas-form-drawer --output project/pages/src/customer-entry.canvas.jsx --var APP_TYPE=APP_XXX --var FORM_UUID=FORM_XXX
|
|
179
181
|
openyida get-schema APP_XXX FORM_XXX
|
|
@@ -413,7 +415,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
413
415
|
| `openyida create-form create <appType> "<formTitle>" <fieldsJsonFile> [--icon auto\|<iconName>] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
|
|
414
416
|
| `openyida create-form icons [--json]` | List available form navigation icons |
|
|
415
417
|
| `openyida create-form validate-fields <fieldsJsonOrFile> [--json]` | Validate form field JSON locally |
|
|
416
|
-
| `openyida create-form update <appType>
|
|
418
|
+
| `openyida create-form update <appType> <formUuid> (<changesJsonOrFile> \| --data-file <changesJsonOrFile>) [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
|
|
419
|
+
| `openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> [--json]` | Update a form page |
|
|
417
420
|
| `openyida create-form patch <appType> <formUuid> <patchJsonOrFile> [--open\|--no-open]` | Update a form page |
|
|
418
421
|
| `openyida create-form rule <appType> <formUuid> <rulesJsonOrFile> [--open\|--no-open]` | Update a form page |
|
|
419
422
|
| `openyida create-form validation <appType> <formUuid> <validationsJsonOrFile> [--open\|--no-open]` | Update a form page |
|
|
@@ -89,6 +89,7 @@ function createParseArgs(dependencies) {
|
|
|
89
89
|
contentLocale: null,
|
|
90
90
|
browserOpenMode: openOption.mode,
|
|
91
91
|
};
|
|
92
|
+
let dataFileInput;
|
|
92
93
|
|
|
93
94
|
const args = [...rawArgs];
|
|
94
95
|
|
|
@@ -111,6 +112,14 @@ function createParseArgs(dependencies) {
|
|
|
111
112
|
i--;
|
|
112
113
|
} else if (args[i] === '--icon') {
|
|
113
114
|
throwCreateFormError('--icon requires a value', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
115
|
+
} else if (args[i] === '--data-file') {
|
|
116
|
+
const next = args[i + 1];
|
|
117
|
+
if (dataFileInput !== undefined || !next || next.startsWith('--')) {
|
|
118
|
+
throwCreateFormError('--data-file requires exactly one value', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
119
|
+
}
|
|
120
|
+
dataFileInput = next;
|
|
121
|
+
args.splice(i, 2);
|
|
122
|
+
i--;
|
|
114
123
|
} else if ((args[i] === '--locale' || args[i] === '--content-locale' || args[i] === '--lang') && i + 1 < args.length) {
|
|
115
124
|
options.contentLocale = args[i + 1];
|
|
116
125
|
if (!normalizeYidaLocale(options.contentLocale)) {
|
|
@@ -145,6 +154,10 @@ function createParseArgs(dependencies) {
|
|
|
145
154
|
|
|
146
155
|
const mode = args[0];
|
|
147
156
|
|
|
157
|
+
if (dataFileInput !== undefined && mode !== 'update') {
|
|
158
|
+
throwCreateFormError('--data-file is only supported by create-form update', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
159
|
+
}
|
|
160
|
+
|
|
148
161
|
if (mode === 'icons' || mode === 'list-icons') {
|
|
149
162
|
return {
|
|
150
163
|
mode: 'icons',
|
|
@@ -182,7 +195,12 @@ function createParseArgs(dependencies) {
|
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
if (mode === 'update') {
|
|
185
|
-
|
|
198
|
+
const positionalInput = args[3];
|
|
199
|
+
if (args.length > 4 || (positionalInput && dataFileInput !== undefined)) {
|
|
200
|
+
throwCreateFormError('Use either <changesJsonOrFile> or --data-file, not both', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
201
|
+
}
|
|
202
|
+
const changesJsonOrFile = dataFileInput !== undefined ? dataFileInput : positionalInput;
|
|
203
|
+
if (args.length < 3 || !changesJsonOrFile) {
|
|
186
204
|
usage(t('create_form.usage_update'), t('create_form.example_update'));
|
|
187
205
|
throwCreateFormError(t('create_form.usage_update'), 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
188
206
|
}
|
|
@@ -190,7 +208,23 @@ function createParseArgs(dependencies) {
|
|
|
190
208
|
mode: 'update',
|
|
191
209
|
appType: args[1],
|
|
192
210
|
formUuid: args[2],
|
|
193
|
-
changesJsonOrFile
|
|
211
|
+
changesJsonOrFile,
|
|
212
|
+
...options
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (mode === 'resume') {
|
|
217
|
+
if (args.length !== 4) {
|
|
218
|
+
throwCreateFormError(
|
|
219
|
+
'openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> --json',
|
|
220
|
+
'CREATE_FORM_INVALID_ARGUMENTS'
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
mode: 'resume',
|
|
225
|
+
appType: args[1],
|
|
226
|
+
formUuid: args[2],
|
|
227
|
+
fieldsJsonOrFile: args[3],
|
|
194
228
|
...options
|
|
195
229
|
};
|
|
196
230
|
}
|
|
@@ -101,7 +101,10 @@ function execute(args) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
async function schedule(forms, concurrency, results, worker, save) {
|
|
104
|
-
const pending = new Set(forms.filter(form =>
|
|
104
|
+
const pending = new Set(forms.filter(form => {
|
|
105
|
+
const result = results[form.key];
|
|
106
|
+
return !result || result.status === 'blocked' || (result.status === 'failed' && result.formUuid);
|
|
107
|
+
}).map(form => form.key));
|
|
105
108
|
const active = new Map();
|
|
106
109
|
try {
|
|
107
110
|
while (pending.size || active.size) {
|
|
@@ -114,7 +117,7 @@ async function schedule(forms, concurrency, results, worker, save) {
|
|
|
114
117
|
}
|
|
115
118
|
if (active.size >= concurrency || deps.some(status => status !== 'success')) { continue; }
|
|
116
119
|
pending.delete(form.key);
|
|
117
|
-
results[form.key] = { status: 'running' };
|
|
120
|
+
results[form.key] = { ...results[form.key], status: 'running' };
|
|
118
121
|
save(); // Record intent before the request; an interrupted create is never retried automatically.
|
|
119
122
|
const task = Promise.resolve().then(() => worker(form)).then(output => {
|
|
120
123
|
results[form.key] = { ...output, status: 'success' };
|
|
@@ -178,16 +181,30 @@ async function run(args, dependencies = {}) {
|
|
|
178
181
|
return matches[0].fieldId;
|
|
179
182
|
};
|
|
180
183
|
await schedule(forms, options.concurrency, state.results, async form => {
|
|
181
|
-
let formUuid = form.formUuid;
|
|
184
|
+
let formUuid = form.formUuid || state.results[form.key]?.formUuid;
|
|
185
|
+
let shouldResume = Boolean(!form.formUuid && formUuid);
|
|
186
|
+
const resolvedFields = mapReferences(form.fields, resolve);
|
|
182
187
|
if (!formUuid) {
|
|
183
|
-
const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(
|
|
188
|
+
const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(resolvedFields), '--no-open'];
|
|
184
189
|
for (const key of ['icon', 'locale']) { if (form[key]) { argv.push(`--${key}`, form[key]); } }
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
try {
|
|
191
|
+
const created = await call(argv);
|
|
192
|
+
if (typeof created.formUuid !== 'string' || !created.formUuid.startsWith('FORM')) { invalid(`create result: ${form.key}`); }
|
|
193
|
+
formUuid = created.formUuid;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const createdFormUuid = error.output?.formUuid || error.output?.details?.formUuid;
|
|
196
|
+
if (typeof createdFormUuid !== 'string' || !createdFormUuid.startsWith('FORM')) {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
formUuid = createdFormUuid;
|
|
200
|
+
shouldResume = true;
|
|
201
|
+
}
|
|
188
202
|
}
|
|
189
203
|
state.results[form.key].formUuid = formUuid;
|
|
190
204
|
save(state);
|
|
205
|
+
if (shouldResume) {
|
|
206
|
+
await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
|
|
207
|
+
}
|
|
191
208
|
const schema = await call(['get-schema', options.appType, formUuid, '--field-map-json']);
|
|
192
209
|
if (schema.formUuid !== formUuid || !Array.isArray(schema.fields)) { invalid(`schema: ${form.key}`); }
|
|
193
210
|
return { formUuid, fields: schema.fields };
|
|
@@ -6,6 +6,8 @@ function dispatchCreateFormCommand(parsedArgs, authContext, handlers) {
|
|
|
6
6
|
switch (parsedArgs.mode) {
|
|
7
7
|
case 'update':
|
|
8
8
|
return handlers.update(...args);
|
|
9
|
+
case 'resume':
|
|
10
|
+
return handlers.resume(...args);
|
|
9
11
|
case 'patch':
|
|
10
12
|
return handlers.patch(...args);
|
|
11
13
|
case 'rule':
|
package/lib/app/create-form.js
CHANGED
|
@@ -5911,6 +5911,297 @@ async function mainValidation(parsedArgs, authRef) {
|
|
|
5911
5911
|
|
|
5912
5912
|
// ── update 模式主流程 ─────────────────────────────────
|
|
5913
5913
|
|
|
5914
|
+
function collectResumeFieldEvidence(fields, output) {
|
|
5915
|
+
const evidence = output || [];
|
|
5916
|
+
(fields || []).forEach(function (field) {
|
|
5917
|
+
if (!field || typeof field !== 'object') {
|
|
5918
|
+
return;
|
|
5919
|
+
}
|
|
5920
|
+
const labelText = extractLabelText(field);
|
|
5921
|
+
if (labelText && !isFormPresentationComponent(field.componentName)) {
|
|
5922
|
+
evidence.push({
|
|
5923
|
+
label: labelText,
|
|
5924
|
+
componentName: field.componentName,
|
|
5925
|
+
fieldId: field.props && field.props.fieldId ? field.props.fieldId : null,
|
|
5926
|
+
});
|
|
5927
|
+
}
|
|
5928
|
+
if (Array.isArray(field.children)) {
|
|
5929
|
+
collectResumeFieldEvidence(field.children.flat(), evidence);
|
|
5930
|
+
}
|
|
5931
|
+
});
|
|
5932
|
+
return evidence;
|
|
5933
|
+
}
|
|
5934
|
+
|
|
5935
|
+
function desiredResumeFieldIdentity(field) {
|
|
5936
|
+
const labelValue = field && field.label;
|
|
5937
|
+
const label = typeof labelValue === 'string'
|
|
5938
|
+
? labelValue
|
|
5939
|
+
: labelValue && (labelValue.zh_CN || labelValue.ja_JP || labelValue.en_US || labelValue.pureEn_US);
|
|
5940
|
+
return {
|
|
5941
|
+
label: String(label || '').trim(),
|
|
5942
|
+
componentName: normalizeFormDefinitionType(field),
|
|
5943
|
+
};
|
|
5944
|
+
}
|
|
5945
|
+
|
|
5946
|
+
function collectResumeDesiredFields(fields, options) {
|
|
5947
|
+
const output = [];
|
|
5948
|
+
const verifyNestedFields = Boolean(options && options.verifyNestedFields);
|
|
5949
|
+
|
|
5950
|
+
function visit(value) {
|
|
5951
|
+
if (Array.isArray(value)) {
|
|
5952
|
+
value.forEach(visit);
|
|
5953
|
+
return;
|
|
5954
|
+
}
|
|
5955
|
+
if (!value || typeof value !== 'object') {
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
const componentName = normalizeFormDefinitionType(value);
|
|
5959
|
+
if (isSupportedBusinessFieldType(componentName)) {
|
|
5960
|
+
output.push(value);
|
|
5961
|
+
if (!verifyNestedFields) {
|
|
5962
|
+
return;
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
if (Array.isArray(value.children)) {
|
|
5966
|
+
value.children.forEach(visit);
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
|
|
5970
|
+
visit(fields);
|
|
5971
|
+
return output;
|
|
5972
|
+
}
|
|
5973
|
+
|
|
5974
|
+
function isRecoverableBlankResumeSchema(schema) {
|
|
5975
|
+
const allowedShellComponents = new Set(['Page', 'RootHeader', 'RootContent', 'RootFooter', 'FooterYida']);
|
|
5976
|
+
const componentsTree = schema && schema.pages && schema.pages[0] && schema.pages[0].componentsTree;
|
|
5977
|
+
if (!Array.isArray(componentsTree) || componentsTree.length === 0) {
|
|
5978
|
+
return true;
|
|
5979
|
+
}
|
|
5980
|
+
let safe = true;
|
|
5981
|
+
function visit(node) {
|
|
5982
|
+
if (!node || typeof node !== 'object' || !safe) {
|
|
5983
|
+
return;
|
|
5984
|
+
}
|
|
5985
|
+
if (node.componentName && !allowedShellComponents.has(node.componentName)) {
|
|
5986
|
+
safe = false;
|
|
5987
|
+
return;
|
|
5988
|
+
}
|
|
5989
|
+
if (Array.isArray(node.children)) {
|
|
5990
|
+
node.children.forEach(visit);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
componentsTree.forEach(visit);
|
|
5994
|
+
return safe;
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
function buildResumeChanges(existingFields, desiredFields) {
|
|
5998
|
+
const existingByLabel = new Map();
|
|
5999
|
+
existingFields.forEach(function (field) {
|
|
6000
|
+
const bucket = existingByLabel.get(field.label) || [];
|
|
6001
|
+
bucket.push(field);
|
|
6002
|
+
existingByLabel.set(field.label, bucket);
|
|
6003
|
+
});
|
|
6004
|
+
|
|
6005
|
+
const desiredLabels = new Set();
|
|
6006
|
+
const missing = [];
|
|
6007
|
+
const existing = [];
|
|
6008
|
+
const conflicts = [];
|
|
6009
|
+
desiredFields.forEach(function (field) {
|
|
6010
|
+
const identity = desiredResumeFieldIdentity(field);
|
|
6011
|
+
if (!identity.label || desiredLabels.has(identity.label)) {
|
|
6012
|
+
conflicts.push({
|
|
6013
|
+
code: identity.label ? 'CREATE_FORM_RESUME_DUPLICATE_DESIRED_FIELD' : 'CREATE_FORM_RESUME_FIELD_LABEL_REQUIRED',
|
|
6014
|
+
label: identity.label || null,
|
|
6015
|
+
});
|
|
6016
|
+
return;
|
|
6017
|
+
}
|
|
6018
|
+
desiredLabels.add(identity.label);
|
|
6019
|
+
const matches = existingByLabel.get(identity.label) || [];
|
|
6020
|
+
if (matches.length === 0) {
|
|
6021
|
+
missing.push(field);
|
|
6022
|
+
return;
|
|
6023
|
+
}
|
|
6024
|
+
if (matches.length !== 1 || matches[0].componentName !== identity.componentName) {
|
|
6025
|
+
conflicts.push({
|
|
6026
|
+
code: 'CREATE_FORM_RESUME_FIELD_CONFLICT',
|
|
6027
|
+
label: identity.label,
|
|
6028
|
+
requestedType: identity.componentName,
|
|
6029
|
+
observedTypes: matches.map(function (item) { return item.componentName; }),
|
|
6030
|
+
});
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6033
|
+
existing.push({
|
|
6034
|
+
label: identity.label,
|
|
6035
|
+
componentName: identity.componentName,
|
|
6036
|
+
fieldId: matches[0].fieldId,
|
|
6037
|
+
});
|
|
6038
|
+
});
|
|
6039
|
+
return { missing, existing, conflicts };
|
|
6040
|
+
}
|
|
6041
|
+
|
|
6042
|
+
async function readResumeSchema(authRef, appType, formUuid) {
|
|
6043
|
+
const schemaResult = await requestWithAutoLogin(function (auth) {
|
|
6044
|
+
return sendGetRequest(
|
|
6045
|
+
auth.baseUrl,
|
|
6046
|
+
buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
|
|
6047
|
+
{ formUuid: formUuid, schemaVersion: 'V5' }
|
|
6048
|
+
);
|
|
6049
|
+
}, authRef);
|
|
6050
|
+
if (!schemaResult || schemaResult.success === false || schemaResult.__needLogin || schemaResult.__csrfExpired) {
|
|
6051
|
+
throwCreateFormError(t('create_form.resume_readback_failed'), 'CREATE_FORM_RESUME_READBACK_FAILED', {
|
|
6052
|
+
appType,
|
|
6053
|
+
formUuid,
|
|
6054
|
+
result: sanitizeFailureResult(schemaResult),
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
6057
|
+
if (schemaResult.appType && schemaResult.appType !== appType) {
|
|
6058
|
+
throwCreateFormError(t('create_form.resume_ownership_unverified'), 'CREATE_FORM_RESUME_OWNERSHIP_UNVERIFIED', {
|
|
6059
|
+
appType,
|
|
6060
|
+
formUuid,
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
if (schemaResult.formUuid && schemaResult.formUuid !== formUuid) {
|
|
6064
|
+
throwCreateFormError(t('create_form.resume_identity_mismatch'), 'CREATE_FORM_RESUME_OWNERSHIP_UNVERIFIED', {
|
|
6065
|
+
appType,
|
|
6066
|
+
formUuid,
|
|
6067
|
+
});
|
|
6068
|
+
}
|
|
6069
|
+
const schema = schemaResult.content
|
|
6070
|
+
? (typeof schemaResult.content === 'string' ? JSON.parse(schemaResult.content) : schemaResult.content)
|
|
6071
|
+
: schemaResult.pages ? schemaResult : null;
|
|
6072
|
+
if (!schema || !Array.isArray(schema.pages) || schema.pages.length === 0) {
|
|
6073
|
+
throwCreateFormError(t('create_form.resume_schema_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
|
|
6074
|
+
appType,
|
|
6075
|
+
formUuid,
|
|
6076
|
+
});
|
|
6077
|
+
}
|
|
6078
|
+
return { schemaResult, schema, version: extractSchemaServerRevision(schemaResult) };
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
async function mainResume(parsedArgs, authRef) {
|
|
6082
|
+
const { appType, formUuid, fieldsJsonOrFile } = parsedArgs;
|
|
6083
|
+
assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
|
|
6084
|
+
const { fields, validations } = readFieldsDefinition(fieldsJsonOrFile);
|
|
6085
|
+
assertNoEmojiInFormDefinition(formUuid, fields, [], 'create-form resume input');
|
|
6086
|
+
validateFormFieldDefinitions(fields);
|
|
6087
|
+
|
|
6088
|
+
const firstRead = await readResumeSchema(authRef, appType, formUuid);
|
|
6089
|
+
const root = firstRead.schema.pages[0].componentsTree && firstRead.schema.pages[0].componentsTree[0];
|
|
6090
|
+
const formContainer = root ? findFormContainer(root) : null;
|
|
6091
|
+
const completedStages = ['read_target', 'verify_ownership'];
|
|
6092
|
+
let recoveredBlankShell = false;
|
|
6093
|
+
let plan;
|
|
6094
|
+
|
|
6095
|
+
if (!formContainer) {
|
|
6096
|
+
if (!isRecoverableBlankResumeSchema(firstRead.schema)) {
|
|
6097
|
+
throwCreateFormError(t('create_form.resume_container_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
|
|
6098
|
+
appType,
|
|
6099
|
+
formUuid,
|
|
6100
|
+
retryable: false,
|
|
6101
|
+
remoteWrites: 0,
|
|
6102
|
+
});
|
|
6103
|
+
}
|
|
6104
|
+
const corpId = resolveCorpId(authRef.authData);
|
|
6105
|
+
const formTitle = firstRead.schemaResult.formTitle || firstRead.schemaResult.title || formUuid;
|
|
6106
|
+
firstRead.schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, 'single', 'default', 'top');
|
|
6107
|
+
const resumeValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
|
|
6108
|
+
if (resumeValidationRules.length > 0) {
|
|
6109
|
+
applySmartValidations(firstRead.schema, resumeValidationRules);
|
|
6110
|
+
}
|
|
6111
|
+
await saveFormSchema(authRef, appType, formUuid, firstRead.schema, firstRead.version, 4);
|
|
6112
|
+
completedStages.push('rebuild_blank_schema', 'save_schema');
|
|
6113
|
+
recoveredBlankShell = true;
|
|
6114
|
+
plan = {
|
|
6115
|
+
existing: [],
|
|
6116
|
+
missing: collectResumeDesiredFields(fields, { verifyNestedFields: true }),
|
|
6117
|
+
conflicts: [],
|
|
6118
|
+
};
|
|
6119
|
+
} else if (!Array.isArray(formContainer.children)) {
|
|
6120
|
+
throwCreateFormError(t('create_form.resume_container_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
|
|
6121
|
+
appType,
|
|
6122
|
+
formUuid,
|
|
6123
|
+
retryable: false,
|
|
6124
|
+
remoteWrites: 0,
|
|
6125
|
+
});
|
|
6126
|
+
} else {
|
|
6127
|
+
plan = buildResumeChanges(
|
|
6128
|
+
collectResumeFieldEvidence(formContainer.children),
|
|
6129
|
+
collectResumeDesiredFields(fields)
|
|
6130
|
+
);
|
|
6131
|
+
if (plan.conflicts.length > 0) {
|
|
6132
|
+
throwCreateFormError(t('create_form.resume_field_conflict'), 'CREATE_FORM_RESUME_CONFLICT', {
|
|
6133
|
+
appType,
|
|
6134
|
+
formUuid,
|
|
6135
|
+
retryable: false,
|
|
6136
|
+
remoteWrites: 0,
|
|
6137
|
+
diagnostics: plan.conflicts,
|
|
6138
|
+
});
|
|
6139
|
+
}
|
|
6140
|
+
completedStages.push('compare_fields');
|
|
6141
|
+
}
|
|
6142
|
+
|
|
6143
|
+
if (!recoveredBlankShell && plan.missing.length > 0) {
|
|
6144
|
+
const applied = applyChangesToSchema(
|
|
6145
|
+
firstRead.schema,
|
|
6146
|
+
plan.missing.map(function (field) { return { action: 'add', field }; }),
|
|
6147
|
+
{ verbose: true }
|
|
6148
|
+
);
|
|
6149
|
+
if ((applied.diagnostics || []).length > 0) {
|
|
6150
|
+
throwCreateFormError(t('create_form.resume_field_resolution_failed'), 'CREATE_FORM_RESUME_CONFLICT', {
|
|
6151
|
+
appType,
|
|
6152
|
+
formUuid,
|
|
6153
|
+
retryable: false,
|
|
6154
|
+
remoteWrites: 0,
|
|
6155
|
+
diagnostics: applied.diagnostics,
|
|
6156
|
+
});
|
|
6157
|
+
}
|
|
6158
|
+
const corpId = resolveCorpId(authRef.authData);
|
|
6159
|
+
const updatedContainer = findFormContainer(firstRead.schema.pages[0].componentsTree[0]);
|
|
6160
|
+
fillSerialNumberFormulas(updatedContainer.children, corpId, appType, formUuid);
|
|
6161
|
+
await saveFormSchema(authRef, appType, formUuid, firstRead.schema, firstRead.version, 4);
|
|
6162
|
+
completedStages.push('add_missing_fields', 'save_schema');
|
|
6163
|
+
}
|
|
6164
|
+
|
|
6165
|
+
const finalRead = await readResumeSchema(authRef, appType, formUuid);
|
|
6166
|
+
const finalContainer = findFormContainer(finalRead.schema.pages[0].componentsTree[0]);
|
|
6167
|
+
const finalPlan = buildResumeChanges(
|
|
6168
|
+
collectResumeFieldEvidence(finalContainer && finalContainer.children),
|
|
6169
|
+
collectResumeDesiredFields(fields, { verifyNestedFields: true })
|
|
6170
|
+
);
|
|
6171
|
+
if (finalPlan.missing.length > 0 || finalPlan.conflicts.length > 0) {
|
|
6172
|
+
throwCreateFormError(t('create_form.resume_readback_mismatch'), 'CREATE_FORM_RESUME_READBACK_MISMATCH', {
|
|
6173
|
+
appType,
|
|
6174
|
+
formUuid,
|
|
6175
|
+
retryable: false,
|
|
6176
|
+
resultUnknown: true,
|
|
6177
|
+
completedStages,
|
|
6178
|
+
missingFieldCount: finalPlan.missing.length,
|
|
6179
|
+
diagnostics: finalPlan.conflicts,
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
completedStages.push('verify_final_schema');
|
|
6183
|
+
const formUrl = authRef.baseUrl + '/' + appType + '/workbench/' + formUuid;
|
|
6184
|
+
const output = {
|
|
6185
|
+
success: true,
|
|
6186
|
+
appType,
|
|
6187
|
+
formUuid,
|
|
6188
|
+
completedStages,
|
|
6189
|
+
requestedFieldCount: fields.length,
|
|
6190
|
+
existingFieldCount: plan.existing.length,
|
|
6191
|
+
addedFieldCount: plan.missing.length,
|
|
6192
|
+
finalFieldCount: collectResumeFieldEvidence(finalContainer.children).length,
|
|
6193
|
+
recoveredBlankShell,
|
|
6194
|
+
url: formUrl,
|
|
6195
|
+
};
|
|
6196
|
+
console.log(JSON.stringify(withBrowserHandoff(
|
|
6197
|
+
output,
|
|
6198
|
+
formUrl,
|
|
6199
|
+
{ stage: 'resume_form_success', title: formUuid },
|
|
6200
|
+
parsedArgs.browserOpenMode
|
|
6201
|
+
)));
|
|
6202
|
+
return output;
|
|
6203
|
+
}
|
|
6204
|
+
|
|
5914
6205
|
async function mainUpdate(parsedArgs, authRef) {
|
|
5915
6206
|
const { appType, formUuid, changesJsonOrFile } = parsedArgs;
|
|
5916
6207
|
|
|
@@ -6102,6 +6393,7 @@ async function run(args) {
|
|
|
6102
6393
|
{
|
|
6103
6394
|
create: mainCreate,
|
|
6104
6395
|
update: mainUpdate,
|
|
6396
|
+
resume: mainResume,
|
|
6105
6397
|
patch: mainPatch,
|
|
6106
6398
|
rule: mainRule,
|
|
6107
6399
|
validation: mainValidation,
|
|
@@ -530,8 +530,8 @@ function buildCanvasPageSchemaObject(sourceCode, runtimeCode, importedModules, f
|
|
|
530
530
|
componentName: 'Page',
|
|
531
531
|
id: nextNodeId(),
|
|
532
532
|
props: {
|
|
533
|
-
contentBgColor: 'var(--
|
|
534
|
-
pageStyle: { backgroundColor: 'var(--
|
|
533
|
+
contentBgColor: 'var(--pod-page-bg-color, var(--color-white, #fff))',
|
|
534
|
+
pageStyle: { backgroundColor: 'var(--pod-page-bg-color, var(--color-white, #fff))' },
|
|
535
535
|
contentMargin: '0',
|
|
536
536
|
contentPadding: '0',
|
|
537
537
|
showTitle: false,
|
|
@@ -539,7 +539,7 @@ function buildCanvasPageSchemaObject(sourceCode, runtimeCode, importedModules, f
|
|
|
539
539
|
templateVersion: '1.0.0',
|
|
540
540
|
contentMarginMobile: '0',
|
|
541
541
|
className: 'page_' + nextSuffix(),
|
|
542
|
-
contentBgColorMobile: 'var(--
|
|
542
|
+
contentBgColorMobile: 'var(--pod-page-bg-color, var(--color-white, #fff))',
|
|
543
543
|
},
|
|
544
544
|
condition: true,
|
|
545
545
|
css: [
|
|
@@ -449,6 +449,7 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
449
449
|
'create-form.bind-datasource',
|
|
450
450
|
'create-form.create',
|
|
451
451
|
'create-form.patch',
|
|
452
|
+
'create-form.resume',
|
|
452
453
|
'create-form.rule',
|
|
453
454
|
'create-form.update',
|
|
454
455
|
'create-form.validation',
|
|
@@ -689,6 +690,7 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
689
690
|
'create-form.create',
|
|
690
691
|
'create-form.batch',
|
|
691
692
|
'create-form.patch',
|
|
693
|
+
'create-form.resume',
|
|
692
694
|
'create-form.rule',
|
|
693
695
|
'create-form.update',
|
|
694
696
|
'create-form.validation',
|
|
@@ -1223,7 +1225,8 @@ const COMMAND_GROUPS = [
|
|
|
1223
1225
|
command('create-form.validate-fields', ['create-form', 'validate-fields'], 'create-form validate-fields <fieldsJsonOrFile> [--json]', 'help.cmd_validate_form', {
|
|
1224
1226
|
requiresLogin: false,
|
|
1225
1227
|
}),
|
|
1226
|
-
command('create-form.update', ['create-form', 'update'], 'create-form update <appType>
|
|
1228
|
+
command('create-form.update', ['create-form', 'update'], 'create-form update <appType> <formUuid> (<changesJsonOrFile> | --data-file <changesJsonOrFile>) [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_update_form'),
|
|
1229
|
+
command('create-form.resume', ['create-form', 'resume'], 'create-form resume <appType> <formUuid> <fieldsJsonOrFile> [--json]', 'help.cmd_update_form'),
|
|
1227
1230
|
command('create-form.patch', ['create-form', 'patch'], 'create-form patch <appType> <formUuid> <patchJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
|
|
1228
1231
|
command('create-form.rule', ['create-form', 'rule'], 'create-form rule <appType> <formUuid> <rulesJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
|
|
1229
1232
|
command('create-form.validation', ['create-form', 'validation'], 'create-form validation <appType> <formUuid> <validationsJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
|
package/lib/core/locales/en.js
CHANGED
|
@@ -827,7 +827,15 @@ Examples:
|
|
|
827
827
|
example_update: 'Example: openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"Note"}}]\'',
|
|
828
828
|
usage_label: 'Usage:',
|
|
829
829
|
usage_create_short: ' create: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
|
|
830
|
-
usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
|
|
830
|
+
usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile> or --data-file <changesJsonOrFile>',
|
|
831
|
+
resume_readback_failed: 'Unable to read the target form before resume.',
|
|
832
|
+
resume_ownership_unverified: 'The target form does not belong to the requested app; resume stopped.',
|
|
833
|
+
resume_identity_mismatch: 'The target form identity does not match the readback; resume stopped.',
|
|
834
|
+
resume_schema_invalid: 'The target form schema is unavailable.',
|
|
835
|
+
resume_container_invalid: 'The target form container is unavailable.',
|
|
836
|
+
resume_field_conflict: 'Existing fields conflict with the requested definition; resume stopped.',
|
|
837
|
+
resume_field_resolution_failed: 'Resume field resolution failed.',
|
|
838
|
+
resume_readback_mismatch: 'Resume readback did not confirm all requested fields.',
|
|
831
839
|
example_label: '\nExamples:',
|
|
832
840
|
fields_file_not_found: ' ❌ Fields definition file not found: ',
|
|
833
841
|
fields_format_invalid: 'Invalid fields definition format',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -798,7 +798,15 @@ openyida - 宜搭命令行工具
|
|
|
798
798
|
example_update: '示例:openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"备注"}}]\'',
|
|
799
799
|
usage_label: '用法:',
|
|
800
800
|
usage_create_short: ' 创建: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
|
|
801
|
-
usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
|
|
801
|
+
usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile> 或 --data-file <changesJsonOrFile>',
|
|
802
|
+
resume_readback_failed: '恢复前无法回读目标表单。',
|
|
803
|
+
resume_ownership_unverified: '目标表单不属于指定应用,已停止恢复。',
|
|
804
|
+
resume_identity_mismatch: '目标表单身份与回读结果不一致,已停止恢复。',
|
|
805
|
+
resume_schema_invalid: '目标表单 Schema 不可用。',
|
|
806
|
+
resume_container_invalid: '目标表单容器不可用。',
|
|
807
|
+
resume_field_conflict: '已有字段与请求定义冲突,恢复已停止。',
|
|
808
|
+
resume_field_resolution_failed: '恢复字段解析失败。',
|
|
809
|
+
resume_readback_mismatch: '恢复后的回读未确认全部请求字段。',
|
|
802
810
|
example_label: '\n示例:',
|
|
803
811
|
fields_file_not_found: ' ❌ 字段定义文件不存在: ',
|
|
804
812
|
fields_format_invalid: '字段定义格式不正确',
|
package/lib/core/sample.js
CHANGED
|
@@ -51,12 +51,14 @@ const SAMPLES = {
|
|
|
51
51
|
'canvas-form-drawer': 'canvas-form-drawer.canvas.jsx',
|
|
52
52
|
'form-open-container': 'canvas-form-drawer.canvas.jsx',
|
|
53
53
|
'canvas-dialog': 'canvas-dialog.canvas.jsx',
|
|
54
|
+
'canvas-theme': 'canvas-form-drawer.canvas.jsx',
|
|
54
55
|
'canvas-nav-side': 'canvas-nav/side.jsx',
|
|
55
56
|
'canvas-nav-top': 'canvas-nav/top.jsx',
|
|
56
57
|
'canvas-nav-mixed': 'canvas-nav/mixed.jsx',
|
|
57
58
|
'canvas-nav-dock': 'canvas-nav/dock.jsx',
|
|
58
59
|
'canvas-nav-tabs': 'canvas-nav/tabs.jsx',
|
|
59
60
|
'canvas-nav-data': 'canvas-nav/data.jsx',
|
|
61
|
+
'canvas-nav-content': 'canvas-nav/content.jsx',
|
|
60
62
|
},
|
|
61
63
|
'yida-design': {
|
|
62
64
|
'app-theme': 'app-custom-theme-template.css',
|
|
@@ -91,12 +93,15 @@ const SAMPLE_OUTPUT_FILENAMES = {
|
|
|
91
93
|
'openyida-page-template/canvas-nav-dock': 'canvas-nav-dock.jsx',
|
|
92
94
|
'openyida-page-template/canvas-nav-tabs': 'canvas-nav-tabs.jsx',
|
|
93
95
|
'openyida-page-template/canvas-nav-data': 'canvas-nav-data.jsx',
|
|
96
|
+
'openyida-page-template/canvas-nav-content': 'canvas-nav-content.jsx',
|
|
94
97
|
'openyida-page-template/form-open-container': 'form-open-container.jsx',
|
|
95
98
|
'openyida-page-template/canvas-dialog': 'canvas-dialog.jsx',
|
|
99
|
+
'openyida-page-template/canvas-theme': 'canvas-theme.jsx',
|
|
96
100
|
'yida-design/app-theme': 'app-theme.css',
|
|
97
101
|
};
|
|
98
102
|
|
|
99
103
|
const SAMPLE_HINTS = {
|
|
104
|
+
'openyida-page-template/canvas-nav-content': '合并 CanvasNavigationContent;顶部导航传 navigation,工作台传 children,原生页传 iframeSrc 和 title;宿主已分配高度时显式传 height。',
|
|
100
105
|
'openyida-page-template/canvas-nav-data': '向 loadCanvasNavigation 传入 PRD 规划的 items,通常工作台在首位;接口只过滤不可见入口,保留规划顺序与分组。',
|
|
101
106
|
'openyida-page-template/form-open-container': '将 import、抽屉组件和辅助函数合并到现有 Canvas 页面;表单使用 useYidaFormOpen,其他内容使用 CanvasDrawer。',
|
|
102
107
|
'yida-design/app-theme': '已复制应用主题模板;请由 yida-design 按 design.md 定点修改需要变化的 token。',
|
|
@@ -251,7 +256,7 @@ async function run(args) {
|
|
|
251
256
|
const defaultOutputPath = path.join(process.cwd(), '.cache', 'samples', outputFilename);
|
|
252
257
|
const { outputPath, variables, designFile } = parseOptions(rest, defaultOutputPath);
|
|
253
258
|
const isNavSample = skill === 'openyida-page-template' && name.startsWith('canvas-nav-');
|
|
254
|
-
const prefixPath = isNavSample && !['canvas-nav-tabs', 'canvas-nav-data'].includes(name) ? path.join(path.dirname(sourcePath), 'shared.jsx') : null;
|
|
259
|
+
const prefixPath = isNavSample && !['canvas-nav-tabs', 'canvas-nav-data', 'canvas-nav-content'].includes(name) ? path.join(path.dirname(sourcePath), 'shared.jsx') : null;
|
|
255
260
|
const sidebarPath = isNavSample && ['canvas-nav-side', 'canvas-nav-mixed'].includes(name) ? path.join(path.dirname(sourcePath), 'sidebar.jsx') : null;
|
|
256
261
|
if ([sourcePath, prefixPath, sidebarPath, designFile].filter(Boolean).some(file => path.resolve(outputPath) === path.resolve(file))) {
|
|
257
262
|
throw new CliError('输出路径不能覆盖模板或 design.md', { code: 'SAMPLE_OUTPUT_CONFLICT' });
|
|
@@ -289,6 +294,13 @@ async function run(args) {
|
|
|
289
294
|
} else if (prefixPath) {
|
|
290
295
|
const content = [prefixPath, sidebarPath, sourcePath].filter(Boolean).map(file => fs.readFileSync(file, 'utf8')).join('\n');
|
|
291
296
|
fs.writeFileSync(outputPath, applyTemplateVariables(content, variables), 'utf8');
|
|
297
|
+
} else if (sampleKey === 'openyida-page-template/canvas-theme') {
|
|
298
|
+
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
|
|
299
|
+
const fragment = sourceContent.match(/\/\/ @openyida-canvas-theme:start\r?\n([\s\S]*?)\/\/ @openyida-canvas-theme:end/);
|
|
300
|
+
if (!fragment) {
|
|
301
|
+
throw new CliError('Canvas theme fragment missing', { code: 'SAMPLE_FRAGMENT_MISSING' });
|
|
302
|
+
}
|
|
303
|
+
fs.writeFileSync(outputPath, "import React from 'react';\n\n" + fragment[1], 'utf8');
|
|
292
304
|
} else if (sampleKey === 'openyida-page-template/form-open-container') {
|
|
293
305
|
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
|
|
294
306
|
const fragment = sourceContent.match(/\/\/ @openyida-form-drawer:start\r?\n([\s\S]*?)\/\/ @openyida-form-drawer:end/);
|
|
@@ -491,8 +491,7 @@ function deriveSurfacePalette(primaryColor, surfaceTone, template) {
|
|
|
491
491
|
const darkSurface = surface && [1, 3, 5].every(index => parseInt(surface.slice(index, index + 2), 16) < 128);
|
|
492
492
|
if (surfaceTone !== 'brand-tinted' || darkSurface) {return {};}
|
|
493
493
|
return {
|
|
494
|
-
//
|
|
495
|
-
// 不覆盖上方按导航归属生成的 --oyd-page-background。
|
|
494
|
+
// 品牌氛围属于 Shell;原生页面和 Canvas 共用页面底色,不受导航归属影响。
|
|
496
495
|
'--pod-shell-bg-color-light': mixHex(primaryColor, [255, 255, 255], 0.94),
|
|
497
496
|
'--pod-card-bg-color': 'var(--color-white, #fff)',
|
|
498
497
|
'--color-fill1-1': mixHex(primaryColor, [255, 255, 255], 0.97),
|
|
@@ -684,12 +683,9 @@ function renderDesign(plan) {
|
|
|
684
683
|
for (const [token, value] of Object.entries(derived)) {
|
|
685
684
|
output = replaceTokenValue(output, token, value);
|
|
686
685
|
}
|
|
687
|
-
//
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
output = replaceTokenValue(output, '--oyd-page-background', 'transparent');
|
|
691
|
-
} else if (!selected.themeId.startsWith('dark-')) {
|
|
692
|
-
output = replaceTokenValue(output, '--oyd-page-background', 'var(--pod-page-bg-color, var(--color-white, #fff))');
|
|
686
|
+
// 页面底色统一使用平台 token;导航形态不改变底色,项目显式设计仍优先。
|
|
687
|
+
if (!selected.themeId.startsWith('dark-')) {
|
|
688
|
+
output = replaceTokenValue(output, '--pod-page-bg-color', 'var(--color-white, #fff)');
|
|
693
689
|
}
|
|
694
690
|
const explicitTokens = visual.tokens || {};
|
|
695
691
|
if (typeof explicitTokens !== 'object' || Array.isArray(explicitTokens)) {
|
|
@@ -718,9 +714,9 @@ function renderDesign(plan) {
|
|
|
718
714
|
output = injectAfterFrontmatter(output, [
|
|
719
715
|
'## 项目配色适配(优先于模板默认配色)', '',
|
|
720
716
|
`用户选择的 ${text(color.primaryColorName, primaryColor)} 用于整体浅色氛围。Shell 浅色背景、填充和边界已按同一主色派生,项目显式 token 覆盖优先;最终取值以本文 tokens 为准。`,
|
|
721
|
-
'
|
|
717
|
+
'背景分层:Shell 承载外层氛围;原生页面与自定义页统一消费 --pod-page-bg-color,卡片与面板消费 --pod-card-bg-color。导航隐藏不自动改为透明;渐变、纹理、图片属于页面局部装饰层。明确的应用背景设计通过 app-theme.css 配置平台 token。',
|
|
722
718
|
'模板中“固定无彩画布”“中性灰必须 R=G=B”“禁止主题色进入背景”等默认限制在本项目不适用;保留布局、圆角、阴影与组件结构。不要为了保留模板灰底,把用户选择的浅彩导航改白。',
|
|
723
|
-
'页面背景消费 --
|
|
719
|
+
'页面背景消费 --pod-page-bg-color,卡片消费 --pod-card-bg-color(回退 --color-white),边界消费 --pod-card-border;近白页面与白卡必须保留可见细边框,不能只靠阴影分层。导航、表头、嵌套区域与选中态消费同一组品牌/填充 token。',
|
|
724
720
|
'正文与标题使用 --color-text1-4,表头与 placeholder 使用 --color-text1-10,辅助说明使用 --color-text1-3;保持中性文字明度层级,不把整页文字染成主色。成功、警告、错误沿用应用语义色。',
|
|
725
721
|
'antd 的主色、容器、浮层、文字、边框、填充及语义色都须映射应用 token;浅彩氛围不能仅出现在导航和主按钮。页面实现前先读本节,再读取下面的结构与组件规则。', '',
|
|
726
722
|
].join('\n'));
|