lan-low-code-cli 0.0.0-stage → 2.0.0
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 +813 -3
- package/bin/llc.js +13 -0
- package/dist/api/formDefine.js +161 -0
- package/dist/client.js +140 -0
- package/dist/commands/app.js +252 -0
- package/dist/commands/config.js +75 -0
- package/dist/commands/control.js +158 -0
- package/dist/commands/field.js +125 -0
- package/dist/commands/form.js +1294 -0
- package/dist/commands/locale.js +60 -0
- package/dist/config.js +106 -0
- package/dist/constants.js +275 -0
- package/dist/generator/index.js +1304 -0
- package/dist/index.js +136 -0
- package/dist/types.js +50 -0
- package/dist/utils/icon.js +26 -0
- package/dist/utils/locale.js +53 -0
- package/dist/utils/logger.js +78 -0
- package/dist/utils/random.js +79 -0
- package/dist/widgets.js +304 -0
- package/package.json +40 -3
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerControlCommand = registerControlCommand;
|
|
4
|
+
const config_1 = require("../config");
|
|
5
|
+
const constants_1 = require("../constants");
|
|
6
|
+
const logger_1 = require("../utils/logger");
|
|
7
|
+
const widgets_1 = require("../widgets");
|
|
8
|
+
function createRegistry(opts) {
|
|
9
|
+
const runtime = (0, config_1.resolveRuntime)({ baseUrl: opts.baseUrl, token: opts.token });
|
|
10
|
+
return new widgets_1.WidgetRegistry({
|
|
11
|
+
baseUrl: runtime.baseUrl,
|
|
12
|
+
token: runtime.token,
|
|
13
|
+
timeout: runtime.timeout,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function flattenConfig(items, path = '', out = []) {
|
|
17
|
+
if (!Array.isArray(items))
|
|
18
|
+
return out;
|
|
19
|
+
items.forEach((item) => {
|
|
20
|
+
if (!item || typeof item !== 'object')
|
|
21
|
+
return;
|
|
22
|
+
const node = item;
|
|
23
|
+
const nextPath = node.key ? `${path}${path ? '.' : ''}${node.key}` : path;
|
|
24
|
+
if (node.key)
|
|
25
|
+
out.push({ path: nextPath, default: node.default });
|
|
26
|
+
if (Array.isArray(node.children))
|
|
27
|
+
flattenConfig(node.children, nextPath, out);
|
|
28
|
+
});
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
/** 桌面端控件包(排除移动端与子包) */
|
|
32
|
+
function desktopPackages(packages) {
|
|
33
|
+
return packages.filter((p) => p.name.startsWith('@elem/xform-') && !p.name.startsWith('@elem/xform-m-'));
|
|
34
|
+
}
|
|
35
|
+
function registerControlCommand(program) {
|
|
36
|
+
const control = program
|
|
37
|
+
.command('control')
|
|
38
|
+
.description('查询环境可用的表单控件及其属性(--add-field 的取值依据)');
|
|
39
|
+
registerListCommand(control);
|
|
40
|
+
registerShowCommand(control);
|
|
41
|
+
}
|
|
42
|
+
function registerListCommand(control) {
|
|
43
|
+
control
|
|
44
|
+
.command('list')
|
|
45
|
+
.description('列出环境中可用的表单控件(数据源:@elem/index.json + 各控件 config.json)')
|
|
46
|
+
.option('--kind <kind>', '按 xform.kind 过滤:control(默认)| layout | appearance')
|
|
47
|
+
.option('--all', '包含全部 kind(不过滤)')
|
|
48
|
+
.option('--base-url <url>', '服务端基址(覆盖配置)')
|
|
49
|
+
.option('--token <token>', '鉴权 Token(覆盖配置)')
|
|
50
|
+
.option('--json', '以 JSON 输出')
|
|
51
|
+
.action(async (opts) => {
|
|
52
|
+
const registry = createRegistry(opts);
|
|
53
|
+
const packages = await registry.listPackages();
|
|
54
|
+
if (packages.length === 0) {
|
|
55
|
+
logger_1.logger.warn('未获取到控件包清单,请检查 --base-url / --token');
|
|
56
|
+
logger_1.logger.info(' 数据源:{origin}/web/sys/artifact/elements/index.json');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const desktops = desktopPackages(packages);
|
|
60
|
+
const mobileNames = new Set(packages.map((p) => p.name));
|
|
61
|
+
logger_1.logger.step(`解析 ${desktops.length} 个控件包的元数据(首次运行会逐个拉取并缓存,稍等)`);
|
|
62
|
+
const rows = [];
|
|
63
|
+
for (const pkg of desktops) {
|
|
64
|
+
const meta = await registry.get(pkg.name);
|
|
65
|
+
const xform = (meta?.xform ?? {});
|
|
66
|
+
const short = pkg.name.replace(/^@elem\/xform-/, '');
|
|
67
|
+
const mobileCandidate = `@elem/xform-m-${short}`;
|
|
68
|
+
const type = String(xform.type ?? '');
|
|
69
|
+
rows.push({
|
|
70
|
+
type,
|
|
71
|
+
dataType: String(xform.dataType ?? ''),
|
|
72
|
+
dictType: String(xform.dictType ?? ''),
|
|
73
|
+
for: String(xform.for ?? ''),
|
|
74
|
+
kind: String(xform.kind ?? ''),
|
|
75
|
+
desktop: pkg.name,
|
|
76
|
+
mobile: mobileNames.has(mobileCandidate) ? mobileCandidate : pkg.name,
|
|
77
|
+
registered: Boolean(constants_1.CONTROL_TYPE_MAP[type]),
|
|
78
|
+
options: xform.dictType === constants_1.OPTION_DICT_TYPE,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const kind = opts.all ? undefined : String(opts.kind ?? 'control');
|
|
82
|
+
const filtered = kind ? rows.filter((r) => r.kind === kind) : rows;
|
|
83
|
+
filtered.sort((a, b) => a.dataType === b.dataType ? a.type.localeCompare(b.type) : a.dataType.localeCompare(b.dataType));
|
|
84
|
+
if (opts.json) {
|
|
85
|
+
logger_1.logger.json(filtered);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
logger_1.logger.table(filtered.map((r) => ({
|
|
89
|
+
控件类型: r.type,
|
|
90
|
+
数据项类型: r.dataType,
|
|
91
|
+
适用: r.for,
|
|
92
|
+
桌面端: r.desktop,
|
|
93
|
+
移动端: r.mobile === r.desktop ? '同桌面端' : r.mobile,
|
|
94
|
+
备注: [r.registered ? '已登记' : '', r.options ? '支持选项' : ''].filter(Boolean).join('/'),
|
|
95
|
+
})));
|
|
96
|
+
const registered = filtered.filter((r) => r.registered).length;
|
|
97
|
+
logger_1.logger.info(logger_1.color.gray(`共 ${filtered.length} 个(kind=${kind ?? '全部'});其中 ${registered} 个已登记到 CONTROL_TYPE_MAP,可直接用于 --add-field`));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/* PART3 */
|
|
101
|
+
/** control show:查看某个控件的详细元数据与属性面板配置项(含默认值) */
|
|
102
|
+
function registerShowCommand(control) {
|
|
103
|
+
control
|
|
104
|
+
.command('show')
|
|
105
|
+
.description('查看某个控件的详细元数据与属性面板配置项(含默认值)')
|
|
106
|
+
.option('--element <pkg>', '控件包名,如 @elem/xform-money')
|
|
107
|
+
.option('--type <type>', '按控件逻辑类型查找,如 moneytext / select')
|
|
108
|
+
.option('--base-url <url>', '服务端基址(覆盖配置)')
|
|
109
|
+
.option('--token <token>', '鉴权 Token(覆盖配置)')
|
|
110
|
+
.option('--json', '以 JSON 输出')
|
|
111
|
+
.action(async (opts) => {
|
|
112
|
+
const registry = createRegistry(opts);
|
|
113
|
+
let pkg = opts.element;
|
|
114
|
+
if (!pkg && opts.type) {
|
|
115
|
+
const wanted = String(opts.type).trim();
|
|
116
|
+
pkg = (constants_1.CONTROL_TYPE_MAP[wanted] ?? constants_1.DEFAULT_CONTROL_MAP[wanted])?.desktop;
|
|
117
|
+
if (!pkg) {
|
|
118
|
+
logger_1.logger.step('按类型在控件包中查找: ' + wanted);
|
|
119
|
+
for (const item of desktopPackages(await registry.listPackages())) {
|
|
120
|
+
const meta = await registry.get(item.name);
|
|
121
|
+
if (meta?.xform?.type === wanted) {
|
|
122
|
+
pkg = item.name;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!pkg) {
|
|
129
|
+
logger_1.logger.warn('请用 --element <控件包名> 或 --type <控件类型> 指定控件');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const meta = await registry.get(pkg);
|
|
133
|
+
if (!meta) {
|
|
134
|
+
logger_1.logger.warn('未获取到控件元数据: ' + pkg);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const items = flattenConfig(meta.config);
|
|
138
|
+
if (opts.json) {
|
|
139
|
+
logger_1.logger.json({ pkg, meta, configItems: items });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
logger_1.logger.title('控件: ' + pkg);
|
|
143
|
+
logger_1.logger.info(' xform = ' + JSON.stringify(meta.xform));
|
|
144
|
+
logger_1.logger.info(' platform = ' + JSON.stringify(meta.platform));
|
|
145
|
+
logger_1.logger.info(' version = ' + JSON.stringify(meta.version));
|
|
146
|
+
logger_1.logger.info(' 配置项(' + items.length + '):');
|
|
147
|
+
// 属性面板的「字段权限」分组(对应 auth 的 required/editable/visible)
|
|
148
|
+
const perm = items.filter((i) => i.path.startsWith('control.fieldPermission'));
|
|
149
|
+
if (perm.length) {
|
|
150
|
+
logger_1.logger.info(' [字段权限] ' + perm.map((i) => i.path.split('.').pop()).join(' / '));
|
|
151
|
+
}
|
|
152
|
+
items.forEach((i) => {
|
|
153
|
+
const d = i.default !== undefined ? ' default=' + JSON.stringify(i.default) : '';
|
|
154
|
+
logger_1.logger.info(' ' + i.path + d);
|
|
155
|
+
});
|
|
156
|
+
logger_1.logger.info(logger_1.color.gray('提示: 默认值来自控件 config.json;未标 default 的属性由前端写死'));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerFieldCommand = registerFieldCommand;
|
|
4
|
+
const formDefine_1 = require("../api/formDefine");
|
|
5
|
+
const client_1 = require("../client");
|
|
6
|
+
const config_1 = require("../config");
|
|
7
|
+
const logger_1 = require("../utils/logger");
|
|
8
|
+
function createClient(opts) {
|
|
9
|
+
return new client_1.LcodeClient((0, config_1.resolveRuntime)({ baseUrl: opts.baseUrl, token: opts.token }));
|
|
10
|
+
}
|
|
11
|
+
/** 将字段数组扁平化输出 */
|
|
12
|
+
function printFields(fields, json) {
|
|
13
|
+
if (json) {
|
|
14
|
+
logger_1.logger.json(fields);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!fields || fields.length === 0) {
|
|
18
|
+
logger_1.logger.warn('未获取到字段');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
logger_1.logger.table(fields.map((f) => ({
|
|
22
|
+
字段名: f.fdName,
|
|
23
|
+
标题: f.fdLabel,
|
|
24
|
+
数据类型: f.fdDataType,
|
|
25
|
+
控件类型: f.fdType,
|
|
26
|
+
长度: f.fdLength ?? '',
|
|
27
|
+
系统字段: f.fdIsSystem ? '是' : '否',
|
|
28
|
+
uuid: f.uuid ?? '',
|
|
29
|
+
})));
|
|
30
|
+
logger_1.logger.info(logger_1.color.gray(`共 ${fields.length} 个字段`));
|
|
31
|
+
}
|
|
32
|
+
function registerFieldCommand(program) {
|
|
33
|
+
const field = program.command('field').description('获取可用字段(表单字段 / 外部表字段 / 字段库)');
|
|
34
|
+
field
|
|
35
|
+
.command('list')
|
|
36
|
+
.description('列出表单已有字段(--form-id)或外部表字段(--table)')
|
|
37
|
+
.option('--form-id <fdId>', '表单 id,从表单数据模型获取字段')
|
|
38
|
+
.option('--table <tableName>', '表名,从外部映射表获取字段')
|
|
39
|
+
.option('--model-id <fdId>', '数据模型 id,直接获取该模型字段')
|
|
40
|
+
.option('--base-url <url>', '服务端基址(覆盖配置)')
|
|
41
|
+
.option('--token <token>', '鉴权 Token(覆盖配置)')
|
|
42
|
+
.option('--json', '以 JSON 输出')
|
|
43
|
+
.action(async (opts) => {
|
|
44
|
+
if (!opts.formId && !opts.table && !opts.modelId) {
|
|
45
|
+
throw new Error('请至少指定 --form-id、--model-id 或 --table 之一');
|
|
46
|
+
}
|
|
47
|
+
const client = createClient(opts);
|
|
48
|
+
if (opts.formId) {
|
|
49
|
+
logger_1.logger.step(`加载表单数据模型:${opts.formId}`);
|
|
50
|
+
const models = await formDefine_1.dataModelApi.loadByFormId(client, opts.formId);
|
|
51
|
+
if (opts.json) {
|
|
52
|
+
logger_1.logger.json(models);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!models || models.length === 0) {
|
|
56
|
+
logger_1.logger.warn('该表单暂无数据模型/字段');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
models.forEach((model) => {
|
|
60
|
+
logger_1.logger.title(`\n[${model.fdType}] ${model.fdName} (表:${model.fdTableName})`);
|
|
61
|
+
printFields(model.fdFields ?? []);
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (opts.modelId) {
|
|
66
|
+
logger_1.logger.step(`加载数据模型字段:${opts.modelId}`);
|
|
67
|
+
const fields = await formDefine_1.dataModelApi.getModelFields(client, opts.modelId);
|
|
68
|
+
printFields(fields ?? [], opts.json);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
logger_1.logger.step(`获取外部表字段:${opts.table}`);
|
|
72
|
+
const fields = await formDefine_1.dataModelApi.loadFieldMeta(client, { fdTableName: opts.table });
|
|
73
|
+
printFields(fields ?? [], opts.json);
|
|
74
|
+
});
|
|
75
|
+
field
|
|
76
|
+
.command('library')
|
|
77
|
+
.description('查询字段库列表')
|
|
78
|
+
.option('--keyword <keyword>', '按名称模糊搜索')
|
|
79
|
+
.option('--page-no <n>', '页码', (v) => Number(v), 1)
|
|
80
|
+
.option('--page-size <n>', '每页数量', (v) => Number(v), 20)
|
|
81
|
+
.option('--base-url <url>', '服务端基址(覆盖配置)')
|
|
82
|
+
.option('--token <token>', '鉴权 Token(覆盖配置)')
|
|
83
|
+
.option('--json', '以 JSON 输出')
|
|
84
|
+
.action(async (opts) => {
|
|
85
|
+
const client = createClient(opts);
|
|
86
|
+
const conditions = opts.keyword
|
|
87
|
+
? { fdName: { $contains: String(opts.keyword).trim() } }
|
|
88
|
+
: undefined;
|
|
89
|
+
const result = await formDefine_1.wordsFieldApi.list(client, {
|
|
90
|
+
pageNo: opts.pageNo,
|
|
91
|
+
pageSize: opts.pageSize,
|
|
92
|
+
conditions,
|
|
93
|
+
columns: ['fdId', 'fdName', 'fdCode', 'fdDisplayName', 'fdControlType', 'fdSourceType'],
|
|
94
|
+
});
|
|
95
|
+
const content = result?.content ?? [];
|
|
96
|
+
if (opts.json) {
|
|
97
|
+
logger_1.logger.json(result);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
logger_1.logger.table(content.map((item) => ({
|
|
101
|
+
fdId: item.fdId ?? '',
|
|
102
|
+
名称: item.fdName ?? '',
|
|
103
|
+
编码: item.fdCode ?? '',
|
|
104
|
+
显示名: item.fdDisplayName ?? '',
|
|
105
|
+
控件类型: item.fdControlType ?? '',
|
|
106
|
+
来源: item.fdSourceType ?? '',
|
|
107
|
+
})));
|
|
108
|
+
logger_1.logger.info(logger_1.color.gray(`共 ${content.length} 条`));
|
|
109
|
+
});
|
|
110
|
+
field
|
|
111
|
+
.command('gen-key')
|
|
112
|
+
.description('根据名称生成字段编码(编码自动填充)')
|
|
113
|
+
.option('--name <name>', '字段名称')
|
|
114
|
+
.option('--table <tableName>', '所属表名')
|
|
115
|
+
.option('--base-url <url>', '服务端基址(覆盖配置)')
|
|
116
|
+
.option('--token <token>', '鉴权 Token(覆盖配置)')
|
|
117
|
+
.action(async (opts) => {
|
|
118
|
+
const client = createClient(opts);
|
|
119
|
+
const code = await formDefine_1.wordsFieldApi.generateFdKey(client, {
|
|
120
|
+
fdName: opts.name,
|
|
121
|
+
fdTableName: opts.table,
|
|
122
|
+
});
|
|
123
|
+
logger_1.logger.success(`生成编码:${typeof code === 'string' ? code : JSON.stringify(code)}`);
|
|
124
|
+
});
|
|
125
|
+
}
|