weaver-e10-builder 1.6.5 → 1.6.6
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/dist/commands/actionflow.d.ts.map +1 -1
- package/dist/commands/actionflow.js +16 -1
- package/dist/commands/actionflow.js.map +1 -1
- package/dist/commands/menu-permission.d.ts +11 -0
- package/dist/commands/menu-permission.d.ts.map +1 -1
- package/dist/commands/menu-permission.js +113 -26
- package/dist/commands/menu-permission.js.map +1 -1
- package/dist/commands/menu.d.ts +16 -5
- package/dist/commands/menu.d.ts.map +1 -1
- package/dist/commands/menu.js +153 -42
- package/dist/commands/menu.js.map +1 -1
- package/dist/services/actionflow/context_discovery.d.ts +16 -0
- package/dist/services/actionflow/context_discovery.d.ts.map +1 -0
- package/dist/services/actionflow/context_discovery.js +73 -0
- package/dist/services/actionflow/context_discovery.js.map +1 -0
- package/dist/services/actionflow/context_preflight.d.ts +0 -9
- package/dist/services/actionflow/context_preflight.d.ts.map +1 -1
- package/dist/services/actionflow/context_preflight.js +0 -59
- package/dist/services/actionflow/context_preflight.js.map +1 -1
- package/dist/services/actionflow/execution_setting.d.ts +11 -0
- package/dist/services/actionflow/execution_setting.d.ts.map +1 -0
- package/dist/services/actionflow/execution_setting.js +205 -0
- package/dist/services/actionflow/execution_setting.js.map +1 -0
- package/dist/services/actionflow/preflight_service.d.ts.map +1 -1
- package/dist/services/actionflow/preflight_service.js +107 -7
- package/dist/services/actionflow/preflight_service.js.map +1 -1
- package/dist/services/menu-catalog.d.ts +19 -0
- package/dist/services/menu-catalog.d.ts.map +1 -1
- package/dist/services/menu-catalog.js +31 -1
- package/dist/services/menu-catalog.js.map +1 -1
- package/package.json +1 -1
- package/skill-docs/SKILL.md +162 -163
- package/skill-docs/_catalog.md +1 -1
- package/skill-docs/actionflows/SKILL.md +8 -2
- package/skill-docs/actionflows/references/trigger_runtime_notes.md +6 -6
- package/skill-docs/references/common/ref-cli-commands.md +12 -11
- package/skill-docs/references/menu/system-menus.md +44 -1
package/dist/commands/menu.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { AuthSession } from '../core/auth.js';
|
|
2
2
|
import { ApiClient } from '../core/client.js';
|
|
3
|
-
import { allShareMap, buildAddNavigation, buildMoveNavigation, buildOrderNavigation, buildRenameNavigation, diffMenuGuard, findMenus, isMenuInSubtree, isSystemMenu, nestedCount, pickMenuBucket, pickParentId, resolveMenuShowOrder, showOrderOf, siblingIndex, siblingMenus, similarMenus, } from '../services/menu-catalog.js';
|
|
3
|
+
import { allShareMap, buildAddNavigation, buildMoveNavigation, buildOpenModeNavigation, buildOrderNavigation, buildRenameNavigation, diffMenuGuard, findMenus, isMenuInSubtree, isSystemMenu, nestedCount, openModeOf, pickMenuBucket, pickParentId, resolveMenuShowOrder, resolveOpenMode, showOrderOf, terminalTypeOf, siblingIndex, siblingMenus, similarMenus, } from '../services/menu-catalog.js';
|
|
4
4
|
/**
|
|
5
5
|
* 拉全量菜单树并展开。**必须是全量**:分页或只取顶层会把嵌套菜单漏掉,
|
|
6
6
|
* 调用方据此说「这个应用没有这个菜单」就是假否定。
|
|
7
7
|
*/
|
|
8
|
-
export async function fetchMenuTree(client, baseUrl, appId) {
|
|
9
|
-
const url = `${baseUrl}/api/cusapp/app/allMenus?appId=${appId}&terminalType
|
|
8
|
+
export async function fetchMenuTree(client, baseUrl, appId, terminal = 'PC') {
|
|
9
|
+
const url = `${baseUrl}/api/cusapp/app/allMenus?appId=${appId}&terminalType=${terminal}&isFront=0&domain=${encodeURIComponent(baseUrl)}`;
|
|
10
10
|
const data = await client.get(url, { ebbusinessid: appId });
|
|
11
11
|
if (data?.code !== 200)
|
|
12
12
|
return { ok: false, msg: data?.msg || '未知错误' };
|
|
13
13
|
const { nodes, bucket } = pickMenuBucket(data?.data || {});
|
|
14
14
|
return { ok: true, nodes, bucket };
|
|
15
15
|
}
|
|
16
|
-
export async function confirmMenuCreated(client, baseUrl, appId, match) {
|
|
17
|
-
const tree = await fetchMenuTree(client, baseUrl, appId);
|
|
16
|
+
export async function confirmMenuCreated(client, baseUrl, appId, match, terminal = 'PC') {
|
|
17
|
+
const tree = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
18
18
|
if (!tree.ok)
|
|
19
19
|
return { state: 'unreadable', msg: tree.msg };
|
|
20
20
|
const hit = tree.nodes.filter(match);
|
|
@@ -41,12 +41,12 @@ function requireMenuConfirmed(confirm, label) {
|
|
|
41
41
|
* `--parent` 既收父菜单 ID 也收名称(名称在同一应用内重复时报错并列 ID,与 `menu rename` 同口径)。
|
|
42
42
|
* `0` = 顶级菜单。需要算「同级最前面」时必须读菜单树;纯顶级、没给排序选项时不读。
|
|
43
43
|
*/
|
|
44
|
-
export async function resolveMenuPlacement(client, baseUrl, appId, parentSpec, order) {
|
|
44
|
+
export async function resolveMenuPlacement(client, baseUrl, appId, parentSpec, order, terminal = 'PC') {
|
|
45
45
|
const raw = String(parentSpec ?? '').trim();
|
|
46
46
|
const needTree = (raw !== '' && raw !== '0') || order.first === true || order.showOrder !== undefined;
|
|
47
47
|
if (!needTree)
|
|
48
48
|
return { parentId: '0', nodes: [] };
|
|
49
|
-
const tree = await fetchMenuTree(client, baseUrl, appId);
|
|
49
|
+
const tree = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
50
50
|
if (tree.ok === false)
|
|
51
51
|
throw new Error(`解析父菜单失败: ${tree.msg}`);
|
|
52
52
|
const picked = pickParentId(tree.nodes, raw || '0');
|
|
@@ -75,6 +75,20 @@ export function requireMenuOrder(node, nodes, placement, label) {
|
|
|
75
75
|
process.exit(1);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* 回读校验打开方式。读侧(`allMenus`)不一定带 `openMode`,所以分三态:
|
|
80
|
+
* 一致 / 不一致(硬失败)/ 读不到(提示无法核对,不冒充成功)。
|
|
81
|
+
*/
|
|
82
|
+
export function requireMenuOpenMode(node, expected, label) {
|
|
83
|
+
const raw = node.raw?.openMode;
|
|
84
|
+
if (raw === undefined || raw === null || String(raw) === '')
|
|
85
|
+
return 'unreadable';
|
|
86
|
+
if (String(raw) !== String(expected)) {
|
|
87
|
+
console.error(`已提交,但回读显示 ${label} 的打开方式是 ${raw},不是请求的 ${expected} —— 打开方式没生效。`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
return 'verified';
|
|
91
|
+
}
|
|
78
92
|
/**
|
|
79
93
|
* 回读确认菜单落在**指定的父菜单下**。只确认「这条菜单存在」是不够的 ——
|
|
80
94
|
* `pid` 没生效时菜单照样建得出来,只是跑到了顶级,用户看到的正是「子菜单建不出来」。
|
|
@@ -91,7 +105,7 @@ export function requireMenuUnderParent(node, parentId, label) {
|
|
|
91
105
|
* (the VIEWPORT pageId) via getViewlist, then savePublishMenu with pageType=VIEWPORT.
|
|
92
106
|
* Returns the new menu id, or null on failure.
|
|
93
107
|
*/
|
|
94
|
-
export async function addFormTableViewMenu(client, baseUrl, appId, objId, menuName, parentId = '0', showOrder) {
|
|
108
|
+
export async function addFormTableViewMenu(client, baseUrl, appId, objId, menuName, parentId = '0', showOrder, extra = {}) {
|
|
95
109
|
// 1. getViewlist → tableView id (the viewport pageId)
|
|
96
110
|
const vl = await client.get(`${baseUrl}/api/bs/ebuilder/form/viewlist/getViewlist/${objId}?apid=${appId}&tpaid=${objId}&objId=${objId}`, { ebbusinessid: appId });
|
|
97
111
|
const tableViews = vl?.data?.tableView?.detail || [];
|
|
@@ -101,7 +115,7 @@ export async function addFormTableViewMenu(client, baseUrl, appId, objId, menuNa
|
|
|
101
115
|
const name = menuName || tv.name || '台账';
|
|
102
116
|
// 2. build VIEWPORT navigation (matches browser payload)
|
|
103
117
|
const navigation = {
|
|
104
|
-
content: name, id: '', openMode:
|
|
118
|
+
content: name, id: '', openMode: resolveOpenMode(extra.openMode), pid: parentId, type: '0',
|
|
105
119
|
filterCondition: {}, menuType: '1', params: [], showType: '1',
|
|
106
120
|
defaultMenuSelect: false, defaultFrontEnable: '1',
|
|
107
121
|
showOrder: showOrder === undefined ? 1 : Number(showOrder),
|
|
@@ -123,7 +137,7 @@ export async function addFormTableViewMenu(client, baseUrl, appId, objId, menuNa
|
|
|
123
137
|
appId,
|
|
124
138
|
navigation: JSON.stringify(navigation),
|
|
125
139
|
isThirdParty: 'true',
|
|
126
|
-
terminalType: 'PC',
|
|
140
|
+
terminalType: extra.terminal ?? 'PC',
|
|
127
141
|
});
|
|
128
142
|
const res = await client.post(`${baseUrl}/api/cusapp/app/savePublishMenu`, body, {
|
|
129
143
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
@@ -139,10 +153,10 @@ export async function addFormTableViewMenu(client, baseUrl, appId, objId, menuNa
|
|
|
139
153
|
* the form's create page (pageType=LAYOUT, pageId=1_<formObjId>), which triggers the
|
|
140
154
|
* bound workflow when the form has one. Matches browser savePublishMenu payload.
|
|
141
155
|
*/
|
|
142
|
-
export async function addFormStartMenu(client, baseUrl, appId, formObjId, menuName, parentId = '0', showOrder) {
|
|
156
|
+
export async function addFormStartMenu(client, baseUrl, appId, formObjId, menuName, parentId = '0', showOrder, extra = {}) {
|
|
143
157
|
const name = menuName || '发起';
|
|
144
158
|
const navigation = {
|
|
145
|
-
content: name, id: '', openMode:
|
|
159
|
+
content: name, id: '', openMode: resolveOpenMode(extra.openMode), pid: parentId, type: '0',
|
|
146
160
|
filterCondition: {}, menuType: '1', params: [], showType: '1',
|
|
147
161
|
defaultMenuSelect: false, defaultFrontEnable: '1',
|
|
148
162
|
showOrder: showOrder === undefined ? 1 : Number(showOrder),
|
|
@@ -166,7 +180,7 @@ export async function addFormStartMenu(client, baseUrl, appId, formObjId, menuNa
|
|
|
166
180
|
appId,
|
|
167
181
|
navigation: JSON.stringify(navigation),
|
|
168
182
|
isThirdParty: 'true',
|
|
169
|
-
terminalType: 'PC',
|
|
183
|
+
terminalType: extra.terminal ?? 'PC',
|
|
170
184
|
});
|
|
171
185
|
const res = await client.post(`${baseUrl}/api/cusapp/app/savePublishMenu`, body, {
|
|
172
186
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
@@ -183,13 +197,14 @@ export function registerMenuCommands(program) {
|
|
|
183
197
|
.description('列出应用菜单(--raw 可打印原始报文,用于核查某一项到底存了什么)')
|
|
184
198
|
.argument('<appId>', '应用 ID')
|
|
185
199
|
.option('--raw [menuId]', '打印菜单的原始报文;给了菜单 ID 就只打印那一条')
|
|
200
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile —— PC 与移动端是两套独立菜单')
|
|
186
201
|
.action(async (appId, cmdOpts) => {
|
|
187
202
|
const opts = program.opts();
|
|
188
203
|
const auth = await AuthSession.fromOptions(opts);
|
|
189
204
|
if (!auth)
|
|
190
205
|
process.exit(1);
|
|
191
206
|
const client = new ApiClient(auth);
|
|
192
|
-
const tree = await fetchMenuTree(client, auth.getBaseUrl(), appId);
|
|
207
|
+
const tree = await fetchMenuTree(client, auth.getBaseUrl(), appId, terminalTypeOf(cmdOpts.terminal));
|
|
193
208
|
if (!tree.ok) {
|
|
194
209
|
console.error(`查询失败: ${tree.msg}`);
|
|
195
210
|
process.exit(1);
|
|
@@ -237,14 +252,16 @@ export function registerMenuCommands(program) {
|
|
|
237
252
|
.argument('<appId>', '应用 ID')
|
|
238
253
|
.argument('<menu>', '菜单 ID 或名称(名称在同一应用内重复时必须改用 ID)')
|
|
239
254
|
.argument('<newName>', '新名称')
|
|
240
|
-
.
|
|
255
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
256
|
+
.action(async (appId, menuRef, newName, cmdOpts) => {
|
|
241
257
|
const opts = program.opts();
|
|
242
258
|
const auth = await AuthSession.fromOptions(opts);
|
|
243
259
|
if (!auth)
|
|
244
260
|
process.exit(1);
|
|
245
261
|
const client = new ApiClient(auth);
|
|
246
262
|
const baseUrl = auth.getBaseUrl();
|
|
247
|
-
const
|
|
263
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
264
|
+
const tree = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
248
265
|
if (!tree.ok) {
|
|
249
266
|
console.error(`查询失败: ${tree.msg}`);
|
|
250
267
|
process.exit(1);
|
|
@@ -286,7 +303,7 @@ export function registerMenuCommands(program) {
|
|
|
286
303
|
appId,
|
|
287
304
|
navigation: JSON.stringify(navigation),
|
|
288
305
|
isThirdParty: 'true',
|
|
289
|
-
terminalType:
|
|
306
|
+
terminalType: terminal,
|
|
290
307
|
});
|
|
291
308
|
const res = await client.post(`${baseUrl}/api/cusapp/app/savePublishMenu`, body, {
|
|
292
309
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
@@ -299,7 +316,7 @@ export function registerMenuCommands(program) {
|
|
|
299
316
|
}
|
|
300
317
|
// 回读校验:**名字变了**且**别的字段一个都没动**,两条都成立才算成功。
|
|
301
318
|
// 只认「返回 200」曾经放过写坏菜单的报文(LINK 被写成 EMPTY)。
|
|
302
|
-
const after = await fetchMenuTree(client, baseUrl, appId);
|
|
319
|
+
const after = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
303
320
|
if (!after.ok) {
|
|
304
321
|
console.error('改名已提交,但回读失败,无法确认结果 —— 请在页面上核对菜单。');
|
|
305
322
|
process.exit(1);
|
|
@@ -332,26 +349,30 @@ export function registerMenuCommands(program) {
|
|
|
332
349
|
.option('--parent <pid>', '父菜单 ID 或名称(默认 0=顶级菜单)', '0')
|
|
333
350
|
.option('--first', '排在同级菜单最前面')
|
|
334
351
|
.option('--show-order <n>', '同级排序号(整数,越小越靠前;缺省 1)')
|
|
352
|
+
.option('--open-mode <mode>', '打开方式: default(默认窗口,缺省)/ new-window(新窗口)')
|
|
353
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
335
354
|
.action(async (name, pagePath, appId, cmdOpts) => {
|
|
336
355
|
const opts = program.opts();
|
|
337
356
|
const auth = await AuthSession.fromOptions(opts);
|
|
338
357
|
if (!auth)
|
|
339
358
|
process.exit(1);
|
|
359
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
340
360
|
// Fix MSYS path mangling: Git Bash converts /sp/custom/... to D:/Program Files/Git/sp/...
|
|
341
361
|
const msysMatch = pagePath.match(/^[A-Za-z]:[/\\].+?(\/sp\/.+)$/);
|
|
342
362
|
if (msysMatch)
|
|
343
363
|
pagePath = msysMatch[1];
|
|
344
364
|
pagePath = '/' + pagePath.replace(/^\/+/, '');
|
|
345
365
|
const client = new ApiClient(auth);
|
|
346
|
-
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts);
|
|
366
|
+
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts, terminal);
|
|
347
367
|
const navigation = buildAddNavigation({
|
|
348
368
|
name, appId, parentId: placement.parentId, pagePath, showOrder: placement.showOrder,
|
|
369
|
+
openMode: cmdOpts.openMode,
|
|
349
370
|
});
|
|
350
371
|
const { body, boundary } = ApiClient.buildMultipart({
|
|
351
372
|
appId,
|
|
352
373
|
navigation: JSON.stringify(navigation),
|
|
353
374
|
isThirdParty: 'true',
|
|
354
|
-
terminalType:
|
|
375
|
+
terminalType: terminal,
|
|
355
376
|
});
|
|
356
377
|
const url = `${auth.getBaseUrl()}/api/cusapp/app/savePublishMenu`;
|
|
357
378
|
const data = await client.post(url, body, {
|
|
@@ -363,19 +384,23 @@ export function registerMenuCommands(program) {
|
|
|
363
384
|
console.error(`添加失败: ${data?.msg || '未知错误'}`);
|
|
364
385
|
process.exit(1);
|
|
365
386
|
}
|
|
366
|
-
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (n) => n.name === name);
|
|
387
|
+
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (n) => n.name === name, terminal);
|
|
367
388
|
const n = requireMenuConfirmed(confirm, `菜单「${name}」`);
|
|
368
389
|
requireMenuUnderParent(n, placement.parentId, `菜单「${n.name}」`);
|
|
369
390
|
if (confirm.state === 'found') {
|
|
370
391
|
requireMenuOrder(n, confirm.nodes, { ...placement, first: cmdOpts.first }, `菜单「${n.name}」`);
|
|
371
392
|
}
|
|
393
|
+
const openMode = cmdOpts.openMode === undefined ? undefined : resolveOpenMode(cmdOpts.openMode);
|
|
394
|
+
const openModeState = openMode === undefined ? undefined : requireMenuOpenMode(n, openMode, `菜单「${n.name}」`);
|
|
372
395
|
const orderText = placement.showOrder === undefined ? '' : `, showOrder=${showOrderOf(n)}`;
|
|
396
|
+
const openModeText = openMode === undefined ? '' : `, openMode=${openMode}${openModeState === 'unreadable' ? '(读侧未返回,未核对)' : ''}`;
|
|
373
397
|
console.log(opts.json
|
|
374
398
|
? JSON.stringify({
|
|
375
399
|
success: true, name, path: pagePath, menuId: n.id, parentId: n.parentId, depth: n.depth,
|
|
376
|
-
showOrder: placement.showOrder === undefined ? undefined : showOrderOf(n),
|
|
400
|
+
terminal, showOrder: placement.showOrder === undefined ? undefined : showOrderOf(n),
|
|
401
|
+
openMode,
|
|
377
402
|
})
|
|
378
|
-
: `菜单「${name}」已添加 → ${pagePath} (id=${n.id}${n.depth > 0 ? `, 位于第 ${n.depth} 层` : ''}${orderText})`);
|
|
403
|
+
: `菜单「${name}」已添加 → ${pagePath} (id=${n.id}${n.depth > 0 ? `, 位于第 ${n.depth} 层` : ''}${orderText}${openModeText}${terminal === 'H5' ? ', 移动端' : ''})`);
|
|
379
404
|
});
|
|
380
405
|
menu.command('add-dir')
|
|
381
406
|
.description('添加上级目录菜单 —— 空链接的纯分类节点,只用来给子菜单分组,点它不跳页面')
|
|
@@ -384,14 +409,16 @@ export function registerMenuCommands(program) {
|
|
|
384
409
|
.option('--parent <pid>', '父菜单 ID 或名称(默认 0=顶级菜单)', '0')
|
|
385
410
|
.option('--first', '排在同级菜单最前面')
|
|
386
411
|
.option('--show-order <n>', '同级排序号(整数,越小越靠前;缺省 1)')
|
|
412
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
387
413
|
.action(async (appId, name, cmdOpts) => {
|
|
388
414
|
const opts = program.opts();
|
|
389
415
|
const auth = await AuthSession.fromOptions(opts);
|
|
390
416
|
if (!auth)
|
|
391
417
|
process.exit(1);
|
|
392
418
|
const client = new ApiClient(auth);
|
|
419
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
393
420
|
try {
|
|
394
|
-
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts);
|
|
421
|
+
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts, terminal);
|
|
395
422
|
const navigation = buildAddNavigation({
|
|
396
423
|
name, appId, parentId: placement.parentId, linkless: true, showOrder: placement.showOrder,
|
|
397
424
|
});
|
|
@@ -399,7 +426,7 @@ export function registerMenuCommands(program) {
|
|
|
399
426
|
appId,
|
|
400
427
|
navigation: JSON.stringify(navigation),
|
|
401
428
|
isThirdParty: 'true',
|
|
402
|
-
terminalType:
|
|
429
|
+
terminalType: terminal,
|
|
403
430
|
});
|
|
404
431
|
const data = await client.post(`${auth.getBaseUrl()}/api/cusapp/app/savePublishMenu`, body, {
|
|
405
432
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
@@ -410,7 +437,7 @@ export function registerMenuCommands(program) {
|
|
|
410
437
|
console.error(`添加失败: ${data?.msg || '未知错误'}`);
|
|
411
438
|
process.exit(1);
|
|
412
439
|
}
|
|
413
|
-
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (n) => n.name === name);
|
|
440
|
+
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (n) => n.name === name, terminal);
|
|
414
441
|
const n = requireMenuConfirmed(confirm, `上级目录「${name}」`);
|
|
415
442
|
requireMenuUnderParent(n, placement.parentId, `上级目录「${n.name}」`);
|
|
416
443
|
if (confirm.state === 'found') {
|
|
@@ -439,6 +466,7 @@ export function registerMenuCommands(program) {
|
|
|
439
466
|
.argument('<menu>', '菜单 ID 或名称(名称在同一应用内重复时必须改用 ID)')
|
|
440
467
|
.option('--first', '排到同级最前面')
|
|
441
468
|
.option('--show-order <n>', '设置同级排序号(整数,越小越靠前)')
|
|
469
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
442
470
|
.action(async (appId, menuRef, cmdOpts) => {
|
|
443
471
|
const opts = program.opts();
|
|
444
472
|
const auth = await AuthSession.fromOptions(opts);
|
|
@@ -446,11 +474,12 @@ export function registerMenuCommands(program) {
|
|
|
446
474
|
process.exit(1);
|
|
447
475
|
const client = new ApiClient(auth);
|
|
448
476
|
const baseUrl = auth.getBaseUrl();
|
|
477
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
449
478
|
try {
|
|
450
479
|
if (cmdOpts.first !== true && cmdOpts.showOrder === undefined) {
|
|
451
480
|
throw new Error('需要 --first 或 --show-order <n>');
|
|
452
481
|
}
|
|
453
|
-
const tree = await fetchMenuTree(client, baseUrl, appId);
|
|
482
|
+
const tree = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
454
483
|
if (!tree.ok)
|
|
455
484
|
throw new Error(`读取菜单失败: ${tree.msg}`);
|
|
456
485
|
const hits = findMenus(tree.nodes, menuRef);
|
|
@@ -474,7 +503,7 @@ export function registerMenuCommands(program) {
|
|
|
474
503
|
throw new Error('需要 --first 或 --show-order <n>');
|
|
475
504
|
const navigation = buildOrderNavigation(target, newOrder, appId);
|
|
476
505
|
const { body, boundary } = ApiClient.buildMultipart({
|
|
477
|
-
appId, navigation: JSON.stringify(navigation), isThirdParty: 'true', terminalType:
|
|
506
|
+
appId, navigation: JSON.stringify(navigation), isThirdParty: 'true', terminalType: terminal,
|
|
478
507
|
});
|
|
479
508
|
const res = await client.post(`${baseUrl}/api/cusapp/app/savePublishMenu`, body, {
|
|
480
509
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
@@ -483,7 +512,7 @@ export function registerMenuCommands(program) {
|
|
|
483
512
|
});
|
|
484
513
|
if (res?.code !== 200)
|
|
485
514
|
throw new Error(`保存排序失败: ${res?.msg || '未知错误'}`);
|
|
486
|
-
const after = await fetchMenuTree(client, baseUrl, appId);
|
|
515
|
+
const after = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
487
516
|
if (!after.ok)
|
|
488
517
|
throw new Error(`排序已提交,但回读失败(${after.msg}),请在设计器里核对`);
|
|
489
518
|
const node = after.nodes.find((n) => n.id === target.id);
|
|
@@ -513,6 +542,76 @@ export function registerMenuCommands(program) {
|
|
|
513
542
|
process.exit(1);
|
|
514
543
|
}
|
|
515
544
|
});
|
|
545
|
+
menu.command('open-mode')
|
|
546
|
+
.description('设置菜单打开方式:default=默认窗口 / new-window=新窗口')
|
|
547
|
+
.argument('<appId>', '应用 ID')
|
|
548
|
+
.argument('<menu>', '菜单 ID 或名称(名称在同一应用内重复时必须改用 ID)')
|
|
549
|
+
.argument('<mode>', 'default=默认窗口 / new-window=新窗口')
|
|
550
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
551
|
+
.action(async (appId, menuRef, mode, cmdOpts) => {
|
|
552
|
+
const opts = program.opts();
|
|
553
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
554
|
+
if (!auth)
|
|
555
|
+
process.exit(1);
|
|
556
|
+
const client = new ApiClient(auth);
|
|
557
|
+
const baseUrl = auth.getBaseUrl();
|
|
558
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
559
|
+
try {
|
|
560
|
+
const expected = resolveOpenMode(mode);
|
|
561
|
+
const tree = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
562
|
+
if (!tree.ok)
|
|
563
|
+
throw new Error(`读取菜单失败: ${tree.msg}`);
|
|
564
|
+
const hits = findMenus(tree.nodes, menuRef);
|
|
565
|
+
if (!hits.length)
|
|
566
|
+
throw new Error(`应用 ${appId} 下找不到菜单「${menuRef}」`);
|
|
567
|
+
if (hits.length > 1) {
|
|
568
|
+
throw new Error(`名称「${menuRef}」在同一应用下有 ${hits.length} 条,请改用菜单 ID:${hits.map((n) => n.id).join(' / ')}`);
|
|
569
|
+
}
|
|
570
|
+
const target = hits[0];
|
|
571
|
+
if (isSystemMenu(target))
|
|
572
|
+
throw new Error(`「${target.name}」是平台系统菜单(pageType=${target.pageType}),不支持调整打开方式`);
|
|
573
|
+
if (!target.pageType || target.pageType === 'EMPTY') {
|
|
574
|
+
throw new Error(`菜单「${target.name}」没有绑定页面(上级目录),打开方式没有意义`);
|
|
575
|
+
}
|
|
576
|
+
if (openModeOf(target) === expected) {
|
|
577
|
+
console.log(opts.json
|
|
578
|
+
? JSON.stringify({ success: true, unchanged: true, menuId: target.id, openMode: expected })
|
|
579
|
+
: `菜单「${target.name}」的打开方式本来就是 ${expected === '2' ? '新窗口' : '默认窗口'},无需改动。`);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const navigation = buildOpenModeNavigation(target, expected, appId);
|
|
583
|
+
const { body, boundary } = ApiClient.buildMultipart({
|
|
584
|
+
appId, navigation: JSON.stringify(navigation), isThirdParty: 'true', terminalType: terminal,
|
|
585
|
+
});
|
|
586
|
+
const res = await client.post(`${baseUrl}/api/cusapp/app/savePublishMenu`, body, {
|
|
587
|
+
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
588
|
+
ebbusinessid: appId,
|
|
589
|
+
referer: `${baseUrl}/sp/ebdapp/build/${appId}`,
|
|
590
|
+
});
|
|
591
|
+
if (res?.code !== 200)
|
|
592
|
+
throw new Error(`保存打开方式失败: ${res?.msg || '未知错误'}`);
|
|
593
|
+
const after = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
594
|
+
if (!after.ok)
|
|
595
|
+
throw new Error(`已提交,但回读失败(${after.msg}),请在设计器里核对`);
|
|
596
|
+
const node = after.nodes.find((n) => n.id === target.id);
|
|
597
|
+
if (!node)
|
|
598
|
+
throw new Error('已提交,但回读时该菜单不见了');
|
|
599
|
+
const state = requireMenuOpenMode(node, expected, `菜单「${node.name}」`);
|
|
600
|
+
const broken = diffMenuGuard(target, node, ['openMode']);
|
|
601
|
+
if (broken.length) {
|
|
602
|
+
throw new Error(`打开方式生效了,但以下字段被服务端改动(可能影响菜单可用性):${broken.join(';')}`);
|
|
603
|
+
}
|
|
604
|
+
const label = expected === '2' ? '新窗口' : '默认窗口';
|
|
605
|
+
console.log(opts.json
|
|
606
|
+
? JSON.stringify({ success: true, menuId: node.id, openMode: expected, verified: state === 'verified' })
|
|
607
|
+
: `菜单「${node.name}」打开方式已改为「${label}」(openMode=${expected})`
|
|
608
|
+
+ (state === 'unreadable' ? '\n 注意: 菜单列表未返回 openMode,无法回读核对,请在设计器里确认一次。' : '\n 回读校验: 打开方式已生效,其余字段无变化'));
|
|
609
|
+
}
|
|
610
|
+
catch (e) {
|
|
611
|
+
console.error(`设置打开方式失败: ${e.message}`);
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
516
615
|
menu.command('move')
|
|
517
616
|
.description('移动已有菜单到另一个父级下(可同时调整同级排序)')
|
|
518
617
|
.argument('<appId>', '应用 ID')
|
|
@@ -520,6 +619,7 @@ export function registerMenuCommands(program) {
|
|
|
520
619
|
.requiredOption('--parent <pid>', '目标父菜单 ID 或名称(0=顶级菜单)')
|
|
521
620
|
.option('--first', '移动到同级最前面')
|
|
522
621
|
.option('--show-order <n>', '目标同级排序号(整数,越小越靠前)')
|
|
622
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
523
623
|
.action(async (appId, menuRef, cmdOpts) => {
|
|
524
624
|
const opts = program.opts();
|
|
525
625
|
const auth = await AuthSession.fromOptions(opts);
|
|
@@ -527,8 +627,9 @@ export function registerMenuCommands(program) {
|
|
|
527
627
|
process.exit(1);
|
|
528
628
|
const client = new ApiClient(auth);
|
|
529
629
|
const baseUrl = auth.getBaseUrl();
|
|
630
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
530
631
|
try {
|
|
531
|
-
const tree = await fetchMenuTree(client, baseUrl, appId);
|
|
632
|
+
const tree = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
532
633
|
if (!tree.ok)
|
|
533
634
|
throw new Error(`读取菜单失败: ${tree.msg}`);
|
|
534
635
|
const hits = findMenus(tree.nodes, menuRef);
|
|
@@ -574,7 +675,7 @@ export function registerMenuCommands(program) {
|
|
|
574
675
|
}
|
|
575
676
|
const navigation = buildMoveNavigation(target, parentId, appId, newOrder);
|
|
576
677
|
const { body, boundary } = ApiClient.buildMultipart({
|
|
577
|
-
appId, navigation: JSON.stringify(navigation), isThirdParty: 'true', terminalType:
|
|
678
|
+
appId, navigation: JSON.stringify(navigation), isThirdParty: 'true', terminalType: terminal,
|
|
578
679
|
});
|
|
579
680
|
const res = await client.post(`${baseUrl}/api/cusapp/app/savePublishMenu`, body, {
|
|
580
681
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
@@ -583,7 +684,7 @@ export function registerMenuCommands(program) {
|
|
|
583
684
|
});
|
|
584
685
|
if (res?.code !== 200)
|
|
585
686
|
throw new Error(`保存移动失败: ${res?.msg || '未知错误'}`);
|
|
586
|
-
const after = await fetchMenuTree(client, baseUrl, appId);
|
|
687
|
+
const after = await fetchMenuTree(client, baseUrl, appId, terminal);
|
|
587
688
|
if (!after.ok)
|
|
588
689
|
throw new Error(`移动已提交,但回读失败(${after.msg}),请在设计器里核对`);
|
|
589
690
|
const node = after.nodes.find((n) => n.id === target.id);
|
|
@@ -642,31 +743,36 @@ export function registerMenuCommands(program) {
|
|
|
642
743
|
.option('--parent <pid>', '父菜单 ID 或名称(默认 0=顶级菜单)', '0')
|
|
643
744
|
.option('--first', '排在同级菜单最前面')
|
|
644
745
|
.option('--show-order <n>', '同级排序号(整数,越小越靠前;缺省 1)')
|
|
746
|
+
.option('--open-mode <mode>', '打开方式: default(默认窗口,缺省)/ new-window(新窗口)')
|
|
747
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
645
748
|
.action(async (appId, objId, name, cmdOpts) => {
|
|
646
749
|
const opts = program.opts();
|
|
647
750
|
const auth = await AuthSession.fromOptions(opts);
|
|
648
751
|
if (!auth)
|
|
649
752
|
process.exit(1);
|
|
650
753
|
const client = new ApiClient(auth);
|
|
754
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
651
755
|
try {
|
|
652
|
-
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts);
|
|
653
|
-
const menuId = await addFormTableViewMenu(client, auth.getBaseUrl(), appId, objId, name, placement.parentId, placement.showOrder);
|
|
756
|
+
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts, terminal);
|
|
757
|
+
const menuId = await addFormTableViewMenu(client, auth.getBaseUrl(), appId, objId, name, placement.parentId, placement.showOrder, { openMode: cmdOpts.openMode, terminal });
|
|
654
758
|
if (!menuId) {
|
|
655
759
|
console.error('已提交,但服务端没有返回菜单 ID,无法回读确认 —— 请在设计器里核对台账菜单是否加上了。');
|
|
656
760
|
process.exit(1);
|
|
657
761
|
}
|
|
658
|
-
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (x) => x.id === String(menuId));
|
|
762
|
+
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (x) => x.id === String(menuId), terminal);
|
|
659
763
|
const n = requireMenuConfirmed(confirm, `台账菜单 (id=${menuId})`);
|
|
660
764
|
requireMenuUnderParent(n, placement.parentId, `台账菜单「${n.name}」`);
|
|
661
765
|
if (confirm.state === 'found') {
|
|
662
766
|
requireMenuOrder(n, confirm.nodes, { ...placement, first: cmdOpts.first }, `台账菜单「${n.name}」`);
|
|
663
767
|
}
|
|
768
|
+
const openMode = cmdOpts.openMode === undefined ? undefined : resolveOpenMode(cmdOpts.openMode);
|
|
769
|
+
const openModeState = openMode === undefined ? undefined : requireMenuOpenMode(n, openMode, `台账菜单「${n.name}」`);
|
|
664
770
|
console.log(opts.json
|
|
665
771
|
? JSON.stringify({
|
|
666
772
|
success: true, menuId: n.id, appId, objId, name: n.name, parentId: n.parentId, depth: n.depth,
|
|
667
|
-
showOrder: placement.showOrder === undefined ? undefined : showOrderOf(n),
|
|
773
|
+
terminal, showOrder: placement.showOrder === undefined ? undefined : showOrderOf(n), openMode,
|
|
668
774
|
})
|
|
669
|
-
: `台账菜单已添加: ${n.name} (id=${n.id}${n.depth > 0 ? `, 位于第 ${n.depth} 层` : ''}${placement.showOrder === undefined ? '' : `, showOrder=${showOrderOf(n)}`})`);
|
|
775
|
+
: `台账菜单已添加: ${n.name} (id=${n.id}${n.depth > 0 ? `, 位于第 ${n.depth} 层` : ''}${placement.showOrder === undefined ? '' : `, showOrder=${showOrderOf(n)}`}${openMode === undefined ? '' : `, openMode=${openMode}${openModeState === 'unreadable' ? '(读侧未返回,未核对)' : ''}`}${terminal === 'H5' ? ', 移动端' : ''})`);
|
|
670
776
|
}
|
|
671
777
|
catch (e) {
|
|
672
778
|
console.error(`添加失败: ${e.message}`);
|
|
@@ -681,31 +787,36 @@ export function registerMenuCommands(program) {
|
|
|
681
787
|
.option('--parent <pid>', '父菜单 ID 或名称(默认 0=顶级菜单)', '0')
|
|
682
788
|
.option('--first', '排在同级菜单最前面')
|
|
683
789
|
.option('--show-order <n>', '同级排序号(整数,越小越靠前;缺省 1)')
|
|
790
|
+
.option('--open-mode <mode>', '打开方式: default(默认窗口,缺省)/ new-window(新窗口)')
|
|
791
|
+
.option('--terminal <t>', '终端: pc(缺省)/ mobile')
|
|
684
792
|
.action(async (appId, formObjId, name, cmdOpts) => {
|
|
685
793
|
const opts = program.opts();
|
|
686
794
|
const auth = await AuthSession.fromOptions(opts);
|
|
687
795
|
if (!auth)
|
|
688
796
|
process.exit(1);
|
|
689
797
|
const client = new ApiClient(auth);
|
|
798
|
+
const terminal = terminalTypeOf(cmdOpts.terminal);
|
|
690
799
|
try {
|
|
691
|
-
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts);
|
|
692
|
-
const menuId = await addFormStartMenu(client, auth.getBaseUrl(), appId, formObjId, name, placement.parentId, placement.showOrder);
|
|
800
|
+
const placement = await resolveMenuPlacement(client, auth.getBaseUrl(), appId, cmdOpts.parent, cmdOpts, terminal);
|
|
801
|
+
const menuId = await addFormStartMenu(client, auth.getBaseUrl(), appId, formObjId, name, placement.parentId, placement.showOrder, { openMode: cmdOpts.openMode, terminal });
|
|
693
802
|
if (!menuId) {
|
|
694
803
|
console.error('已提交,但服务端没有返回菜单 ID,无法回读确认 —— 请在设计器里核对发起菜单是否加上了。');
|
|
695
804
|
process.exit(1);
|
|
696
805
|
}
|
|
697
|
-
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (x) => x.id === String(menuId));
|
|
806
|
+
const confirm = await confirmMenuCreated(client, auth.getBaseUrl(), appId, (x) => x.id === String(menuId), terminal);
|
|
698
807
|
const n = requireMenuConfirmed(confirm, `发起菜单 (id=${menuId})`);
|
|
699
808
|
requireMenuUnderParent(n, placement.parentId, `发起菜单「${n.name}」`);
|
|
700
809
|
if (confirm.state === 'found') {
|
|
701
810
|
requireMenuOrder(n, confirm.nodes, { ...placement, first: cmdOpts.first }, `发起菜单「${n.name}」`);
|
|
702
811
|
}
|
|
812
|
+
const openMode = cmdOpts.openMode === undefined ? undefined : resolveOpenMode(cmdOpts.openMode);
|
|
813
|
+
const openModeState = openMode === undefined ? undefined : requireMenuOpenMode(n, openMode, `发起菜单「${n.name}」`);
|
|
703
814
|
console.log(opts.json
|
|
704
815
|
? JSON.stringify({
|
|
705
816
|
success: true, menuId: n.id, appId, formObjId, name: n.name, parentId: n.parentId, depth: n.depth,
|
|
706
|
-
showOrder: placement.showOrder === undefined ? undefined : showOrderOf(n),
|
|
817
|
+
terminal, showOrder: placement.showOrder === undefined ? undefined : showOrderOf(n), openMode,
|
|
707
818
|
})
|
|
708
|
-
: `发起菜单已添加: ${n.name} (id=${n.id}${n.depth > 0 ? `, 位于第 ${n.depth} 层` : ''}${placement.showOrder === undefined ? '' : `, showOrder=${showOrderOf(n)}`})`);
|
|
819
|
+
: `发起菜单已添加: ${n.name} (id=${n.id}${n.depth > 0 ? `, 位于第 ${n.depth} 层` : ''}${placement.showOrder === undefined ? '' : `, showOrder=${showOrderOf(n)}`}${openMode === undefined ? '' : `, openMode=${openMode}${openModeState === 'unreadable' ? '(读侧未返回,未核对)' : ''}`}${terminal === 'H5' ? ', 移动端' : ''})`);
|
|
709
820
|
}
|
|
710
821
|
catch (e) {
|
|
711
822
|
console.error(`添加失败: ${e.message}`);
|