openyida 1.0.0-beta.3 → 1.0.0-beta.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/README.md +6 -2
- package/bin/yida.js +2 -2
- package/lib/copy.js +112 -29
- package/lib/create-app.js +7 -1
- package/lib/create-form.js +240 -23
- package/lib/env.js +2 -0
- package/lib/locales/en.js +7 -0
- package/lib/locales/ja.js +7 -0
- package/lib/locales/zh.js +7 -0
- package/lib/publish.js +1 -1
- package/lib/utils.js +2 -1
- package/package.json +12 -2
- package/yida-skills/SKILL.md +1 -1
- package/yida-skills/skills/yida-create-app/SKILL.md +33 -4
- package/yida-skills/skills/yida-create-form-page/SKILL.md +17 -4
package/README.md
CHANGED
|
@@ -156,9 +156,13 @@ npx clawhub@latest install nicky1108/yida-app
|
|
|
156
156
|
感谢所有为 OpenYida 做出贡献的开发者!欢迎阅读 [贡献指南](./CONTRIBUTING.md) 参与共建。
|
|
157
157
|
|
|
158
158
|
<p align="left">
|
|
159
|
-
<a href="https://github.com/yize"><img src="https://avatars.githubusercontent.com/u/1578814?v=4&s=48" width="48" height="48" alt="
|
|
160
|
-
<a href="https://github.com/alex-mm"><img src="https://avatars.githubusercontent.com/u/3302053?v=4&s=48" width="48" height="48" alt="
|
|
159
|
+
<a href="https://github.com/yize"><img src="https://avatars.githubusercontent.com/u/1578814?v=4&s=48" width="48" height="48" alt="九神" title="九神"/></a>
|
|
160
|
+
<a href="https://github.com/alex-mm"><img src="https://avatars.githubusercontent.com/u/3302053?v=4&s=48" width="48" height="48" alt="天晟" title="天晟"/></a>
|
|
161
161
|
<a href="https://github.com/nicky1108"><img src="https://avatars.githubusercontent.com/u/4279283?v=4&s=48" width="48" height="48" alt="nicky1108" title="nicky1108"/></a>
|
|
162
|
+
<a href="https://github.com/angelinheys"><img src="https://avatars.githubusercontent.com/u/49426983?v=4&s=48" width="48" height="48" alt="angelinheys" title="angelinheys"/></a>
|
|
163
|
+
<a href="https://github.com/yipengmu"><img src="https://avatars.githubusercontent.com/u/3232735?v=4&s=48" width="48" height="48" alt="yipengmu" title="yipengmu"/></a>
|
|
164
|
+
<a href="https://github.com/Waawww"><img src="https://avatars.githubusercontent.com/u/31886449?v=4&s=48" width="48" height="48" alt="Waawww" title="Waawww"/></a>
|
|
165
|
+
<a href="https://github.com/kangjiano"><img src="https://avatars.githubusercontent.com/u/54129385?v=4&s=48" width="48" height="48" alt="kangjiano" title="kangjiano"/></a>
|
|
162
166
|
</p>
|
|
163
167
|
|
|
164
168
|
---
|
package/bin/yida.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* openyida org switch --corp-id <corpId> 切换组织(无需重新登录)
|
|
19
19
|
* openyida create-app "<名称>" [desc] [icon] [color] 创建应用
|
|
20
20
|
* openyida create-page <appType> "<页面名>" 创建自定义页面
|
|
21
|
-
* openyida create-form create <appType> "<表单名>" <字段JSON> 创建表单页面
|
|
21
|
+
* openyida create-form create <appType> "<表单名>" <字段JSON> [--layout <布局>] [--theme <主题>] [--label-align <对齐>] 创建表单页面
|
|
22
22
|
* openyida create-form update <appType> <formUuid> <修改JSON> 更新表单页面
|
|
23
23
|
* openyida get-schema <appType> <formUuid> 获取表单 Schema
|
|
24
24
|
* openyida publish <源文件路径> <appType> <formUuid> 编译并发布自定义页面
|
|
@@ -57,7 +57,7 @@ openyida - 宜搭命令行工具
|
|
|
57
57
|
logout 退出登录 / 切换账号
|
|
58
58
|
create-app "<名称>" [描述] [图标] [颜色] 创建应用,输出 appType
|
|
59
59
|
create-page <appType> "<页面名>" 创建自定义页面,输出 pageId
|
|
60
|
-
create-form create <appType> "<表单名>" <字段JSON>
|
|
60
|
+
create-form create <appType> "<表单名>" <字段JSON> [--layout <布局>] [--theme <主题>] [--label-align <对齐>] 创建表单页面
|
|
61
61
|
create-form update <appType> <formUuid> <修改JSON> 更新表单页面
|
|
62
62
|
get-schema <appType> <formUuid> 获取表单 Schema
|
|
63
63
|
publish <源文件路径> <appType> <formUuid> 编译并发布自定义页面
|
package/lib/copy.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* openyida copy → 复制 project/ 目录模板(默认,合并模式)
|
|
6
6
|
* openyida copy --force → 复制 project/ 目录模板(强制覆盖,先清空目标目录)
|
|
7
7
|
* openyida copy -skills → 创建 yida-skills/ 软链接(如果存在实际目录则先删除)
|
|
8
|
+
* 悟空环境下:删除已有软链(悟空通过手动上传技能,不需要软链)
|
|
8
9
|
* openyida copy -project → 复制 project/ 目录模板(与默认行为相同,显式指定)
|
|
9
10
|
* openyida copy -project --force → 复制 project/ 目录模板(强制覆盖)
|
|
10
11
|
*
|
|
@@ -16,7 +17,12 @@
|
|
|
16
17
|
*
|
|
17
18
|
* project/ 合并模式(默认):已存在的文件强制覆盖,目标目录中多余的文件保留不动
|
|
18
19
|
* project/ 强制模式(--force):先清空目标目录,再完整复制
|
|
19
|
-
* yida-skills
|
|
20
|
+
* yida-skills/(非悟空):始终创建软链接,如目标存在实际目录则先删除
|
|
21
|
+
* yida-skills/(悟空):删除已有软链或目录(悟空通过手动上传技能,不需要软链)
|
|
22
|
+
*
|
|
23
|
+
* Windows 兼容说明:
|
|
24
|
+
* - 软链接在 Windows 上需要管理员权限或开发者模式,失败时自动降级为目录复制
|
|
25
|
+
* - 路径分隔符统一使用 path.join 处理
|
|
20
26
|
*/
|
|
21
27
|
|
|
22
28
|
"use strict";
|
|
@@ -94,15 +100,22 @@ function forceCopyDir(sourceDir, destDir) {
|
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
103
|
+
* 删除已有的 yida-skills 软链接或目录(悟空环境专用)。
|
|
104
|
+
* 悟空通过手动上传技能,不需要软链,执行 -skills 时只做清理。
|
|
105
|
+
* 使用 lstatSync 而非 existsSync,可以检测到悬空软链(目标不存在但链接本身存在)。
|
|
106
|
+
* @returns {boolean} 是否执行了删除操作
|
|
99
107
|
*/
|
|
100
|
-
function
|
|
101
|
-
|
|
108
|
+
function removeSkillsLink(destLink) {
|
|
109
|
+
let stats;
|
|
110
|
+
try {
|
|
111
|
+
stats = fs.lstatSync(destLink);
|
|
112
|
+
} catch {
|
|
113
|
+
// 路径不存在(包括悬空软链也不存在的情况)
|
|
114
|
+
console.log(t("copy.wukong_skills_not_found", destLink));
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
102
117
|
|
|
103
|
-
|
|
104
|
-
if (fs.existsSync(destLink)) {
|
|
105
|
-
const stats = fs.lstatSync(destLink);
|
|
118
|
+
try {
|
|
106
119
|
if (stats.isSymbolicLink()) {
|
|
107
120
|
fs.unlinkSync(destLink);
|
|
108
121
|
console.log(t("copy.symlink_removed", destLink));
|
|
@@ -113,20 +126,69 @@ function createSymlink(sourceDir, destLink) {
|
|
|
113
126
|
fs.unlinkSync(destLink);
|
|
114
127
|
console.log(t("copy.removed", destLink));
|
|
115
128
|
}
|
|
129
|
+
return true;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error(t("copy.remove_failed", destLink, error.message));
|
|
132
|
+
return false;
|
|
116
133
|
}
|
|
134
|
+
}
|
|
117
135
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
/**
|
|
137
|
+
* 创建软链接:如果目标存在实际目录则先删除,再创建软链接。
|
|
138
|
+
* Windows 上软链需要管理员权限或开发者模式,失败时自动降级为目录复制。
|
|
139
|
+
* @returns {boolean} 是否成功创建
|
|
140
|
+
*/
|
|
141
|
+
function createSymlink(sourceDir, destLink) {
|
|
142
|
+
if (!fs.existsSync(sourceDir)) return false;
|
|
143
|
+
|
|
144
|
+
// 如果目标已存在,判断是目录还是软链接
|
|
145
|
+
if (fs.existsSync(destLink)) {
|
|
146
|
+
try {
|
|
147
|
+
const stats = fs.lstatSync(destLink);
|
|
148
|
+
if (stats.isSymbolicLink()) {
|
|
149
|
+
fs.unlinkSync(destLink);
|
|
150
|
+
console.log(t("copy.symlink_removed", destLink));
|
|
151
|
+
} else if (stats.isDirectory()) {
|
|
152
|
+
fs.rmSync(destLink, { recursive: true, force: true });
|
|
153
|
+
console.log(t("copy.dir_deleted", destLink));
|
|
154
|
+
} else {
|
|
155
|
+
fs.unlinkSync(destLink);
|
|
156
|
+
console.log(t("copy.removed", destLink));
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(t("copy.remove_failed", destLink, error.message));
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Windows 上 junction 只支持目录,且需要管理员权限或开发者模式
|
|
165
|
+
// 失败时降级为目录复制
|
|
166
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
167
|
+
try {
|
|
168
|
+
fs.symlinkSync(sourceDir, destLink, symlinkType);
|
|
169
|
+
console.log(t("copy.symlink_created", destLink, sourceDir));
|
|
170
|
+
return true;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (process.platform === "win32" && error.code === "EPERM") {
|
|
173
|
+
console.log(t("copy.symlink_fallback_copy", destLink));
|
|
174
|
+
const count = mergeCopyDir(sourceDir, destLink);
|
|
175
|
+
console.log(t("copy.files_copied", count));
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
console.error(t("copy.symlink_failed", destLink, error.message));
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
121
181
|
}
|
|
122
182
|
|
|
123
183
|
/**
|
|
124
|
-
*
|
|
184
|
+
* 根据已检测的环境信息返回目标根目录,避免重复调用 detectEnvironment()。
|
|
185
|
+
* @param {string|null} activeToolName
|
|
186
|
+
* @param {string|null} activeProjectRoot
|
|
187
|
+
* @param {Array} envResults
|
|
125
188
|
* @returns {string} 目标根目录路径
|
|
126
189
|
*/
|
|
127
|
-
function
|
|
128
|
-
const
|
|
129
|
-
const activeResult = results.find((r) => r.displayName === activeToolName);
|
|
190
|
+
function resolveDestBaseFromEnv(activeToolName, activeProjectRoot, envResults) {
|
|
191
|
+
const activeResult = envResults.find((r) => r.displayName === activeToolName);
|
|
130
192
|
const isWukong = activeResult && activeResult.dirName === ".real";
|
|
131
193
|
|
|
132
194
|
if (isWukong) {
|
|
@@ -141,7 +203,7 @@ function resolveDestBase() {
|
|
|
141
203
|
|
|
142
204
|
// 未检测到活跃工具
|
|
143
205
|
console.error(t("copy.no_ai_tool"));
|
|
144
|
-
|
|
206
|
+
envResults.forEach((r) => {
|
|
145
207
|
console.error(` ${r.isActive ? "✅" : "⬜"} ${r.displayName}`);
|
|
146
208
|
});
|
|
147
209
|
console.error(t("copy.force_hint"));
|
|
@@ -188,17 +250,24 @@ function run() {
|
|
|
188
250
|
console.log(t("copy.package_root", packageRoot));
|
|
189
251
|
|
|
190
252
|
// 2. 确定目标根目录(检测 AI 工具环境)
|
|
191
|
-
|
|
253
|
+
// 同时获取 isWukong 标志,避免后续重复调用 detectEnvironment()
|
|
254
|
+
const { activeToolName, activeProjectRoot, results: envResults } = detectEnvironment();
|
|
255
|
+
const activeEnvResult = envResults.find((r) => r.isActive);
|
|
256
|
+
const isWukong = activeEnvResult && activeEnvResult.dirName === ".real";
|
|
257
|
+
const destBase = resolveDestBaseFromEnv(activeToolName, activeProjectRoot, envResults);
|
|
192
258
|
console.log(t("copy.dest_base", destBase));
|
|
193
259
|
if (isForce) {
|
|
194
260
|
console.log(t("copy.force_mode"));
|
|
195
261
|
}
|
|
196
262
|
|
|
197
263
|
// 3. 确定要复制/链接的内容
|
|
198
|
-
// - 指定了 -skills
|
|
264
|
+
// - 指定了 -skills:
|
|
265
|
+
// 悟空环境:删除已有的 yida-skills/ 软链(悟空手动上传技能,不需要软链)
|
|
266
|
+
// 其他环境:创建 yida-skills/ 软链接(如果存在实际目录则先删除)
|
|
199
267
|
// - 指定了 -project:只复制 project/
|
|
200
268
|
// - 两者都没指定(默认):只复制 project/
|
|
201
269
|
// - 两者都指定:同时处理两项
|
|
270
|
+
|
|
202
271
|
const shouldCopyProject = wantsProject || (!wantsSkills);
|
|
203
272
|
const shouldLinkSkills = wantsSkills;
|
|
204
273
|
|
|
@@ -215,17 +284,28 @@ function run() {
|
|
|
215
284
|
}
|
|
216
285
|
|
|
217
286
|
if (shouldLinkSkills) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
287
|
+
const destSkillsLink = path.join(destBase, "yida-skills");
|
|
288
|
+
if (isWukong) {
|
|
289
|
+
// 悟空环境:删除已有软链,不创建新软链
|
|
290
|
+
console.log(t("copy.wukong_skills_cleanup"));
|
|
291
|
+
const removed = removeSkillsLink(destSkillsLink);
|
|
292
|
+
results.push({
|
|
293
|
+
label: "yida-skills/",
|
|
294
|
+
dest: destSkillsLink,
|
|
295
|
+
count: removed ? 1 : 0,
|
|
296
|
+
type: "wukong-cleanup"
|
|
297
|
+
});
|
|
298
|
+
} else {
|
|
299
|
+
// 其他环境:创建软链接
|
|
300
|
+
console.log(t("copy.creating_symlink"));
|
|
301
|
+
const success = createSymlink(packageYidaSkillsDir, destSkillsLink);
|
|
302
|
+
results.push({
|
|
303
|
+
label: "yida-skills/",
|
|
304
|
+
dest: destSkillsLink,
|
|
305
|
+
count: success ? 1 : 0,
|
|
306
|
+
type: "symlink"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
229
309
|
}
|
|
230
310
|
|
|
231
311
|
// 4. 打印汇总
|
|
@@ -242,6 +322,9 @@ function run() {
|
|
|
242
322
|
results.forEach((r) => {
|
|
243
323
|
if (r.type === "symlink") {
|
|
244
324
|
console.log(` ${r.label.padEnd(14)} → ${r.dest} (${t("copy.symlink_label")})`);
|
|
325
|
+
} else if (r.type === "wukong-cleanup") {
|
|
326
|
+
const statusText = r.count > 0 ? t("copy.wukong_skills_cleaned") : t("copy.wukong_skills_not_found", r.dest);
|
|
327
|
+
console.log(` ${r.label.padEnd(14)} → ${r.dest} (${statusText})`);
|
|
245
328
|
} else {
|
|
246
329
|
console.log(` ${r.label.padEnd(14)} → ${r.dest} (${t("copy.files_count", r.count)})`);
|
|
247
330
|
}
|
package/lib/create-app.js
CHANGED
|
@@ -95,6 +95,9 @@ async function run(args) {
|
|
|
95
95
|
const description = args[1] || appName;
|
|
96
96
|
const icon = args[2] || "xian-yingyong";
|
|
97
97
|
const iconColor = args[3] || "#0089FF";
|
|
98
|
+
const colour = args[4] || "deepBlue";
|
|
99
|
+
const navTheme = args[5] || "dark";
|
|
100
|
+
const layoutDirection = args[6] || "slide";
|
|
98
101
|
|
|
99
102
|
const SEP = "=".repeat(50);
|
|
100
103
|
console.error(SEP);
|
|
@@ -103,6 +106,7 @@ async function run(args) {
|
|
|
103
106
|
console.error(t("create_app.app_name", appName));
|
|
104
107
|
console.error(t("create_app.app_desc", description));
|
|
105
108
|
console.error(t("create_app.app_icon", icon, iconColor));
|
|
109
|
+
console.error(t("create_app.app_theme", colour, navTheme, layoutDirection));
|
|
106
110
|
|
|
107
111
|
// Step 1: 读取登录态
|
|
108
112
|
console.error(t("common.step_login", 1));
|
|
@@ -131,7 +135,9 @@ async function run(args) {
|
|
|
131
135
|
description: JSON.stringify({ zh_CN: description, en_US: description, type: "i18n" }),
|
|
132
136
|
icon: iconValue,
|
|
133
137
|
iconUrl: iconValue,
|
|
134
|
-
colour
|
|
138
|
+
colour,
|
|
139
|
+
navTheme,
|
|
140
|
+
layoutDirection,
|
|
135
141
|
defaultLanguage: "zh_CN",
|
|
136
142
|
openExclusive: "n",
|
|
137
143
|
openPhysicColumn: "n",
|
package/lib/create-form.js
CHANGED
|
@@ -95,7 +95,35 @@ function buildApiPath(appType, apiName, options = {}) {
|
|
|
95
95
|
// ── 参数解析 ─────────────────────────────────────────
|
|
96
96
|
|
|
97
97
|
function parseArgs() {
|
|
98
|
-
const
|
|
98
|
+
const rawArgs = process.argv.slice(2);
|
|
99
|
+
|
|
100
|
+
// 解析可选参数
|
|
101
|
+
const options = {
|
|
102
|
+
layout: "single", // 布局:single/double/card/section
|
|
103
|
+
theme: "default", // 主题:default/compact/comfortable
|
|
104
|
+
labelAlign: "top", // 标签对齐:top/left/right
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// 复制一份 args 用于解析(避免修改原始数组影响后续处理)
|
|
108
|
+
const args = [...rawArgs];
|
|
109
|
+
|
|
110
|
+
// 解析 --layout, --theme, --label-align 参数
|
|
111
|
+
for (let i = 0; i < args.length; i++) {
|
|
112
|
+
if (args[i] === "--layout" && i + 1 < args.length) {
|
|
113
|
+
options.layout = args[i + 1];
|
|
114
|
+
args.splice(i, 2);
|
|
115
|
+
i--;
|
|
116
|
+
} else if (args[i] === "--theme" && i + 1 < args.length) {
|
|
117
|
+
options.theme = args[i + 1];
|
|
118
|
+
args.splice(i, 2);
|
|
119
|
+
i--;
|
|
120
|
+
} else if (args[i] === "--label-align" && i + 1 < args.length) {
|
|
121
|
+
options.labelAlign = args[i + 1];
|
|
122
|
+
args.splice(i, 2);
|
|
123
|
+
i--;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
99
127
|
const mode = args[0];
|
|
100
128
|
|
|
101
129
|
if (mode === "create") {
|
|
@@ -104,7 +132,13 @@ function parseArgs() {
|
|
|
104
132
|
console.error(t("create_form.example_create"));
|
|
105
133
|
process.exit(1);
|
|
106
134
|
}
|
|
107
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
mode: "create",
|
|
137
|
+
appType: args[1],
|
|
138
|
+
formTitle: args[2],
|
|
139
|
+
fieldsJsonOrFile: args[3],
|
|
140
|
+
...options
|
|
141
|
+
};
|
|
108
142
|
}
|
|
109
143
|
|
|
110
144
|
if (mode === "update") {
|
|
@@ -113,12 +147,24 @@ function parseArgs() {
|
|
|
113
147
|
console.error(t("create_form.example_update"));
|
|
114
148
|
process.exit(1);
|
|
115
149
|
}
|
|
116
|
-
return {
|
|
150
|
+
return {
|
|
151
|
+
mode: "update",
|
|
152
|
+
appType: args[1],
|
|
153
|
+
formUuid: args[2],
|
|
154
|
+
changesJsonOrFile: args[3],
|
|
155
|
+
...options
|
|
156
|
+
};
|
|
117
157
|
}
|
|
118
158
|
|
|
119
159
|
// 兼容旧用法(无 mode 参数,默认 create 模式)
|
|
120
160
|
if (args.length >= 3 && mode !== "create" && mode !== "update") {
|
|
121
|
-
return {
|
|
161
|
+
return {
|
|
162
|
+
mode: "create",
|
|
163
|
+
appType: args[0],
|
|
164
|
+
formTitle: args[1],
|
|
165
|
+
fieldsJsonOrFile: args[2],
|
|
166
|
+
...options
|
|
167
|
+
};
|
|
122
168
|
}
|
|
123
169
|
|
|
124
170
|
console.error(t("create_form.usage_label"));
|
|
@@ -1007,10 +1053,160 @@ function resolveFieldIdReferences(fieldComponents) {
|
|
|
1007
1053
|
});
|
|
1008
1054
|
}
|
|
1009
1055
|
|
|
1056
|
+
// ── 布局配置映射 ─────────────────────────────────────
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* 获取布局配置
|
|
1060
|
+
* @param {string} layout - 布局类型:single/double/card/section
|
|
1061
|
+
* @returns {object} 布局配置对象 { columns, formLayout, groupFields }
|
|
1062
|
+
*/
|
|
1063
|
+
function getLayoutConfig(layout) {
|
|
1064
|
+
const layoutMap = {
|
|
1065
|
+
single: { columns: 1, formLayout: "default", groupFields: false },
|
|
1066
|
+
"1": { columns: 1, formLayout: "default", groupFields: false },
|
|
1067
|
+
double: { columns: 2, formLayout: "default", groupFields: false },
|
|
1068
|
+
"2": { columns: 2, formLayout: "default", groupFields: false },
|
|
1069
|
+
card: { columns: 1, formLayout: "card", groupFields: true },
|
|
1070
|
+
section: { columns: 1, formLayout: "section", groupFields: true },
|
|
1071
|
+
};
|
|
1072
|
+
return layoutMap[layout] || layoutMap.single;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* 获取主题样式配置
|
|
1077
|
+
* @param {string} theme - 主题类型:default/compact/comfortable
|
|
1078
|
+
* @param {string} labelAlign - 标签对齐:top/left/right
|
|
1079
|
+
* @returns {object} 样式配置对象
|
|
1080
|
+
*/
|
|
1081
|
+
function getThemeConfig(theme, labelAlign) {
|
|
1082
|
+
const baseConfig = {
|
|
1083
|
+
labelAlignPc: labelAlign || "top",
|
|
1084
|
+
labelWidthPc: labelAlign === "left" || labelAlign === "right" ? "130px" : "auto",
|
|
1085
|
+
labelWeightPc: "normal",
|
|
1086
|
+
contentMargin: "20",
|
|
1087
|
+
contentPadding: "20",
|
|
1088
|
+
fieldSpacing: "medium",
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
const themeMap = {
|
|
1092
|
+
default: {
|
|
1093
|
+
...baseConfig,
|
|
1094
|
+
contentMargin: "20",
|
|
1095
|
+
contentPadding: "20",
|
|
1096
|
+
},
|
|
1097
|
+
compact: {
|
|
1098
|
+
...baseConfig,
|
|
1099
|
+
contentMargin: "12",
|
|
1100
|
+
contentPadding: "12",
|
|
1101
|
+
fieldSpacing: "small",
|
|
1102
|
+
},
|
|
1103
|
+
comfortable: {
|
|
1104
|
+
...baseConfig,
|
|
1105
|
+
contentMargin: "32",
|
|
1106
|
+
contentPadding: "32",
|
|
1107
|
+
fieldSpacing: "large",
|
|
1108
|
+
},
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
return themeMap[theme] || themeMap.default;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// ── 按 group 分组字段 ─────────────────────────────────
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* 将字段按 group 属性分组
|
|
1118
|
+
* @param {Array} fields - 字段定义数组
|
|
1119
|
+
* @returns {Array} 分组后的字段数组,每个元素是 { groupName, fields }
|
|
1120
|
+
*/
|
|
1121
|
+
function groupFieldsByGroup(fields) {
|
|
1122
|
+
const groups = [];
|
|
1123
|
+
const groupMap = new Map();
|
|
1124
|
+
|
|
1125
|
+
fields.forEach((field) => {
|
|
1126
|
+
const groupName = field.group || "基本信息";
|
|
1127
|
+
if (!groupMap.has(groupName)) {
|
|
1128
|
+
groupMap.set(groupName, []);
|
|
1129
|
+
}
|
|
1130
|
+
groupMap.get(groupName).push(field);
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
groupMap.forEach((groupFields, groupName) => {
|
|
1134
|
+
groups.push({ groupName, fields: groupFields });
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
return groups;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// ── 构建分组字段组件 ─────────────────────────────────
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* 构建分组/卡片布局的字段组件
|
|
1144
|
+
* @param {Array} fields - 字段定义数组
|
|
1145
|
+
* @param {string} formLayout - 布局类型:card/section
|
|
1146
|
+
* @returns {Array} 分组后的组件数组
|
|
1147
|
+
*/
|
|
1148
|
+
function buildGroupedFieldComponents(fields, formLayout) {
|
|
1149
|
+
const groups = groupFieldsByGroup(fields);
|
|
1150
|
+
|
|
1151
|
+
return groups.map((group, groupIndex) => {
|
|
1152
|
+
// 构建分组内的字段组件
|
|
1153
|
+
const groupFieldComponents = group.fields.map((field) => buildFieldComponent(field));
|
|
1154
|
+
|
|
1155
|
+
if (formLayout === "card") {
|
|
1156
|
+
// 卡片式布局:每个分组是一个卡片容器
|
|
1157
|
+
return {
|
|
1158
|
+
componentName: "CardContainer",
|
|
1159
|
+
id: nextNodeId(),
|
|
1160
|
+
props: {
|
|
1161
|
+
title: i18n(group.groupName, group.groupName),
|
|
1162
|
+
collapsible: true,
|
|
1163
|
+
defaultCollapsed: false,
|
|
1164
|
+
showTitle: true,
|
|
1165
|
+
cardStyle: "default",
|
|
1166
|
+
headerStyle: "default",
|
|
1167
|
+
__gridSpan: 1,
|
|
1168
|
+
},
|
|
1169
|
+
condition: true,
|
|
1170
|
+
hidden: false,
|
|
1171
|
+
title: "",
|
|
1172
|
+
isLocked: false,
|
|
1173
|
+
conditionGroup: "",
|
|
1174
|
+
children: groupFieldComponents,
|
|
1175
|
+
};
|
|
1176
|
+
} else {
|
|
1177
|
+
// 分组式布局:每个分组是一个区块
|
|
1178
|
+
return {
|
|
1179
|
+
componentName: "SectionContainer",
|
|
1180
|
+
id: nextNodeId(),
|
|
1181
|
+
props: {
|
|
1182
|
+
title: i18n(group.groupName, group.groupName),
|
|
1183
|
+
collapsible: true,
|
|
1184
|
+
defaultCollapsed: false,
|
|
1185
|
+
showTitle: true,
|
|
1186
|
+
sectionStyle: "default",
|
|
1187
|
+
divider: groupIndex > 0, // 第一个分组不显示分隔线
|
|
1188
|
+
__gridSpan: 1,
|
|
1189
|
+
},
|
|
1190
|
+
condition: true,
|
|
1191
|
+
hidden: false,
|
|
1192
|
+
title: "",
|
|
1193
|
+
isLocked: false,
|
|
1194
|
+
conditionGroup: "",
|
|
1195
|
+
children: groupFieldComponents,
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1010
1201
|
// ── 生成表单 Schema ──────────────────────────────────
|
|
1011
1202
|
|
|
1012
|
-
function buildFormSchema(formTitle, fields, formUuid, corpId, appType,
|
|
1013
|
-
|
|
1203
|
+
function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign) {
|
|
1204
|
+
// 解析布局配置
|
|
1205
|
+
const layoutConfig = getLayoutConfig(layout || "single");
|
|
1206
|
+
const columns = layoutConfig.columns;
|
|
1207
|
+
|
|
1208
|
+
// 解析主题配置
|
|
1209
|
+
const themeConfig = getThemeConfig(theme || "default", labelAlign || "top");
|
|
1014
1210
|
const fieldComponents = fields.map(function (field) {
|
|
1015
1211
|
return buildFieldComponent(field);
|
|
1016
1212
|
});
|
|
@@ -1070,12 +1266,12 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, columns)
|
|
|
1070
1266
|
titleColor: "light",
|
|
1071
1267
|
titleBg: "https://img.alicdn.com/imgextra/i2/O1CN0143ATPP1wIa9TrVvzN_!!6000000006285-2-tps-3360-400.png_.webp",
|
|
1072
1268
|
backgroundColorCustom: "#f1f2f3",
|
|
1073
|
-
sizePc: "medium",
|
|
1074
|
-
labelAlignPc:
|
|
1075
|
-
labelWidthPc:
|
|
1076
|
-
labelWeightPc:
|
|
1077
|
-
labelAlignMobile: "top",
|
|
1078
|
-
labelWidthMobile: "80px",
|
|
1269
|
+
sizePc: themeConfig.fieldSpacing === "small" ? "small" : themeConfig.fieldSpacing === "large" ? "large" : "medium",
|
|
1270
|
+
labelAlignPc: themeConfig.labelAlignPc,
|
|
1271
|
+
labelWidthPc: themeConfig.labelWidthPc,
|
|
1272
|
+
labelWeightPc: themeConfig.labelWeightPc,
|
|
1273
|
+
labelAlignMobile: labelAlign || "top",
|
|
1274
|
+
labelWidthMobile: labelAlign === "left" || labelAlign === "right" ? "80px" : "auto",
|
|
1079
1275
|
labelWeightMobile: "normal",
|
|
1080
1276
|
},
|
|
1081
1277
|
condition: true,
|
|
@@ -1150,15 +1346,36 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, columns)
|
|
|
1150
1346
|
afterSubmit: false,
|
|
1151
1347
|
onProcessActionValidate: false,
|
|
1152
1348
|
afterFormDataInit: false,
|
|
1153
|
-
},
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1349
|
+
}, children: [
|
|
1350
|
+
{
|
|
1351
|
+
componentName: "FormContainer",
|
|
1352
|
+
id: nextNodeId(),
|
|
1353
|
+
props: {
|
|
1354
|
+
formLabel: i18n(formTitle, formTitle),
|
|
1355
|
+
formLabelVisible: true,
|
|
1356
|
+
columns: columns,
|
|
1357
|
+
labelAlign: labelAlign || "top",
|
|
1358
|
+
submitText: i18n("提交", "Submit"),
|
|
1359
|
+
stageText: i18n("暂存", "Stage"),
|
|
1360
|
+
submitAndNewText: i18n("提交并继续", "Submit and New"),
|
|
1361
|
+
fieldId: "formContainer_" + Date.now().toString(36),
|
|
1362
|
+
aiFormConfig: { systemPrompt: "", model: "qwen" },
|
|
1363
|
+
beforeSubmit: false,
|
|
1364
|
+
afterSubmit: false,
|
|
1365
|
+
onProcessActionValidate: false,
|
|
1366
|
+
afterFormDataInit: false,
|
|
1367
|
+
},
|
|
1368
|
+
condition: true,
|
|
1369
|
+
hidden: false,
|
|
1370
|
+
title: "",
|
|
1371
|
+
isLocked: false,
|
|
1372
|
+
conditionGroup: "",
|
|
1373
|
+
// ★ 核心:FormContainer 内层的字段组件(支持分组布局)
|
|
1374
|
+
children: layoutConfig.groupFields
|
|
1375
|
+
? buildGroupedFieldComponents(fields, layoutConfig.formLayout)
|
|
1376
|
+
: fieldComponents,
|
|
1377
|
+
},
|
|
1378
|
+
], },
|
|
1162
1379
|
],
|
|
1163
1380
|
},
|
|
1164
1381
|
{
|
|
@@ -1974,7 +2191,7 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
|
|
|
1974
2191
|
// ── create 模式主流程 ─────────────────────────────────
|
|
1975
2192
|
|
|
1976
2193
|
async function mainCreate(parsedArgs, csrfToken, cookies, baseUrl, cookieData) {
|
|
1977
|
-
const { appType, formTitle, fieldsJsonOrFile } = parsedArgs;
|
|
2194
|
+
const { appType, formTitle, fieldsJsonOrFile, layout, theme, labelAlign } = parsedArgs;
|
|
1978
2195
|
|
|
1979
2196
|
const SEP = "=".repeat(50);
|
|
1980
2197
|
console.error(SEP);
|
|
@@ -2025,7 +2242,7 @@ async function mainCreate(parsedArgs, csrfToken, cookies, baseUrl, cookieData) {
|
|
|
2025
2242
|
console.error(t("create_form.corp_id_ok", corpId));
|
|
2026
2243
|
}
|
|
2027
2244
|
|
|
2028
|
-
const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType,
|
|
2245
|
+
const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign);
|
|
2029
2246
|
var { configResult } = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, 1, 4);
|
|
2030
2247
|
|
|
2031
2248
|
// 输出结果
|
package/lib/env.js
CHANGED
|
@@ -17,6 +17,7 @@ const home = os.homedir();
|
|
|
17
17
|
/**
|
|
18
18
|
* 获取所有已安装的 AI 工具列表(用于展示)。
|
|
19
19
|
* 不判断当前是否活跃,只判断是否安装过。
|
|
20
|
+
* 使用 path.join 拼接路径,兼容 Windows 和 macOS/Linux。
|
|
20
21
|
*
|
|
21
22
|
* @returns {Array} 已安装工具列表
|
|
22
23
|
*/
|
|
@@ -49,6 +50,7 @@ function detectEnvironment() {
|
|
|
49
50
|
const results = installedTools.map(({ dirName, displayName }) => {
|
|
50
51
|
const isWukong = dirName === ".real";
|
|
51
52
|
const isActive = activeTool && activeTool.dirName === dirName;
|
|
53
|
+
// path.join 在 Windows 上自动使用反斜杠,兼容所有平台
|
|
52
54
|
const workspaceRoot = isWukong
|
|
53
55
|
? path.join(home, ".real", "workspace", "project")
|
|
54
56
|
: cwdProject;
|
package/lib/locales/en.js
CHANGED
|
@@ -217,6 +217,7 @@ Examples:
|
|
|
217
217
|
app_name: " App name: {0}",
|
|
218
218
|
app_desc: " Description: {0}",
|
|
219
219
|
app_icon: " Icon: {0} ({1})",
|
|
220
|
+
app_theme: " Theme: colour={0} navTheme={1} layout={2}",
|
|
220
221
|
step_create: "\n📦 Step 2: Create App\n",
|
|
221
222
|
success: " ✅ App created successfully!",
|
|
222
223
|
app_type_label: " appType: {0}",
|
|
@@ -586,6 +587,12 @@ Examples:
|
|
|
586
587
|
files_copied: " Files copied: {0}",
|
|
587
588
|
files_count: "{0} files",
|
|
588
589
|
symlinks_created: " Symlinks created: {0}",
|
|
590
|
+
wukong_skills_cleanup: "\n🗑️ Wukong env: Cleaning up yida-skills/ symlink (Wukong uses manual skill upload, no symlink needed)...",
|
|
591
|
+
wukong_skills_cleaned: "cleaned up",
|
|
592
|
+
wukong_skills_not_found: " ℹ️ No yida-skills/ symlink or directory found, nothing to clean: {0}",
|
|
593
|
+
remove_failed: " ❌ Remove failed: {0} ({1})",
|
|
594
|
+
symlink_fallback_copy: " ⚠️ Windows symlink creation failed (requires admin privileges), falling back to directory copy: {0}",
|
|
595
|
+
symlink_failed: " ❌ Symlink creation failed: {0} ({1})",
|
|
589
596
|
result_symlink: " {0} → {1} (symlink)",
|
|
590
597
|
result_copy: " {0} → {1} ({2} files)",
|
|
591
598
|
},
|
package/lib/locales/ja.js
CHANGED
|
@@ -215,6 +215,7 @@ openyida - Yida CLI ツール
|
|
|
215
215
|
app_name: " アプリ名: {0}",
|
|
216
216
|
app_desc: " 説明: {0}",
|
|
217
217
|
app_icon: " アイコン: {0} ({1})",
|
|
218
|
+
app_theme: " テーマ: colour={0} navTheme={1} layout={2}",
|
|
218
219
|
step_create: "\n📦 Step 2: アプリを作成\n",
|
|
219
220
|
success: " ✅ アプリが正常に作成されました!",
|
|
220
221
|
app_type_label: " appType: {0}",
|
|
@@ -584,6 +585,12 @@ openyida - Yida CLI ツール
|
|
|
584
585
|
files_copied: " コピーしたファイル: {0} 個",
|
|
585
586
|
files_count: "{0} ファイル",
|
|
586
587
|
symlinks_created: " 作成したシンボリックリンク: {0} 個",
|
|
588
|
+
wukong_skills_cleanup: "\n🗑️ 悟空環境:yida-skills/ シンボリックリンクを削除中(悟空はスキルを手動アップロードするため、シンボリックリンク不要)...",
|
|
589
|
+
wukong_skills_cleaned: "削除済み",
|
|
590
|
+
wukong_skills_not_found: " ℹ️ yida-skills/ シンボリックリンクまたはディレクトリが見つかりません: {0}",
|
|
591
|
+
remove_failed: " ❌ 削除失敗: {0} ({1})",
|
|
592
|
+
symlink_fallback_copy: " ⚠️ Windows でシンボリックリンク作成失敗(管理者権限が必要)、ディレクトリコピーにフォールバック: {0}",
|
|
593
|
+
symlink_failed: " ❌ シンボリックリンク作成失敗: {0} ({1})",
|
|
587
594
|
result_symlink: " {0} → {1}(シンボリックリンク)",
|
|
588
595
|
result_copy: " {0} → {1}({2} ファイル)",
|
|
589
596
|
},
|
package/lib/locales/zh.js
CHANGED
|
@@ -214,6 +214,7 @@ openyida - 宜搭命令行工具
|
|
|
214
214
|
app_name: " 应用名称: {0}",
|
|
215
215
|
app_desc: " 应用描述: {0}",
|
|
216
216
|
app_icon: " 图标: {0} ({1})",
|
|
217
|
+
app_theme: " 主题: colour={0} navTheme={1} layout={2}",
|
|
217
218
|
step_create: "\n📦 Step 2: 创建应用\n",
|
|
218
219
|
success: " ✅ 应用创建成功!",
|
|
219
220
|
app_type_label: " appType: {0}",
|
|
@@ -487,6 +488,12 @@ openyida - 宜搭命令行工具
|
|
|
487
488
|
symlinks_created: " 创建软链接: {0} 个",
|
|
488
489
|
result_symlink: " {0} → {1} (软链接)",
|
|
489
490
|
result_copy: " {0} → {1} ({2} 个文件)",
|
|
491
|
+
wukong_skills_cleanup: "\n🗑️ 悟空环境:清理 yida-skills/ 软链(悟空通过手动上传技能,不需要软链)...",
|
|
492
|
+
wukong_skills_cleaned: "已清理",
|
|
493
|
+
wukong_skills_not_found: " ℹ️ 未找到 yida-skills/ 软链或目录,无需清理: {0}",
|
|
494
|
+
remove_failed: " ❌ 删除失败: {0} ({1})",
|
|
495
|
+
symlink_fallback_copy: " ⚠️ Windows 软链创建失败(需要管理员权限),降级为目录复制: {0}",
|
|
496
|
+
symlink_failed: " ❌ 软链接创建失败: {0} ({1})",
|
|
490
497
|
},
|
|
491
498
|
|
|
492
499
|
// ── lib/publish.js ─────────────────────────────────
|
package/lib/publish.js
CHANGED
|
@@ -23,7 +23,7 @@ const http = require("http");
|
|
|
23
23
|
const querystring = require("querystring");
|
|
24
24
|
const { default: babelTransform } = require("./babel-transform");
|
|
25
25
|
const UglifyJS = require("uglify-js");
|
|
26
|
-
const { isLoginExpired, isCsrfTokenExpired } = require("./utils");
|
|
26
|
+
const { findProjectRoot, isLoginExpired, isCsrfTokenExpired, loadCookieData, triggerLogin, refreshCsrfToken } = require("./utils");
|
|
27
27
|
const { t } = require("./i18n");
|
|
28
28
|
|
|
29
29
|
// ── 配置读取 ──────────────────────────────────────────
|
package/lib/utils.js
CHANGED
|
@@ -88,7 +88,8 @@ function detectActiveTool() {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// 悟空(Wukong)
|
|
91
|
-
|
|
91
|
+
// Windows 路径可能使用反斜杠,需同时兼容正斜杠和反斜杠
|
|
92
|
+
if (env.AGENT_WORK_ROOT && (env.AGENT_WORK_ROOT.includes(".real") || env.AGENT_WORK_ROOT.includes(path.join(".real")))) {
|
|
92
93
|
return {
|
|
93
94
|
tool: "wukong",
|
|
94
95
|
displayName: "悟空(Wukong)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openyida",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"openyida": "./bin/yida.js",
|
|
@@ -22,7 +22,17 @@
|
|
|
22
22
|
"lint:fix": "eslint bin/ lib/ scripts/ --ext .js --fix",
|
|
23
23
|
"postinstall": "node scripts/postinstall.js"
|
|
24
24
|
},
|
|
25
|
-
"keywords": [
|
|
25
|
+
"keywords": [
|
|
26
|
+
"yida",
|
|
27
|
+
"aliwork",
|
|
28
|
+
"dingtalk",
|
|
29
|
+
"cli",
|
|
30
|
+
"openyida",
|
|
31
|
+
"low-code",
|
|
32
|
+
"ai",
|
|
33
|
+
"qoder",
|
|
34
|
+
"wukong"
|
|
35
|
+
],
|
|
26
36
|
"author": "OpenYida Contributors",
|
|
27
37
|
"license": "MIT",
|
|
28
38
|
"dependencies": {
|
package/yida-skills/SKILL.md
CHANGED
|
@@ -53,15 +53,38 @@ openyida create-app "考勤管理" "员工考勤打卡系统" "xian-daka" "#00B8
|
|
|
53
53
|
```bash
|
|
54
54
|
openyida create-app <appName> [description] [icon] [iconColor]
|
|
55
55
|
```
|
|
56
|
-
|
|
57
56
|
**参数说明**:
|
|
58
57
|
|
|
59
58
|
| 参数 | 必填 | 默认值 | 说明 |
|
|
60
59
|
| --- | --- | --- | --- |
|
|
61
60
|
| `appName` | 是 | — | 应用名称 |
|
|
62
61
|
| `description` | 否 | 同 appName | 应用描述 |
|
|
63
|
-
| `icon` | 否 | `xian-yingyong` |
|
|
64
|
-
| `iconColor` | 否 | `#0089FF` |
|
|
62
|
+
| `icon` | 否 | `xian-yingyong` | 图标标识(见文末图标列表) |
|
|
63
|
+
| `iconColor` | 否 | `#0089FF` | 图标背景色(见文末色值列表) |
|
|
64
|
+
| `colour` | 否 | `deepBlue` | 主题色(见下方主题色说明) |
|
|
65
|
+
| `navTheme` | 否 | `dark` | 导航风格:`dark`(深色)/ `light`(浅色) |
|
|
66
|
+
| `layoutDirection` | 否 | `slide` | 导航布局:`slide`(侧边栏)/ `ver`(L 型顶导) |
|
|
67
|
+
|
|
68
|
+
**主题色(colour)可选值**:
|
|
69
|
+
|
|
70
|
+
| 值 | 颜色 | 适合场景 |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| `deepBlue` | 深蓝 | 政务、金融、法律、企业管理、正式场合 |
|
|
73
|
+
| `podBlue` | 蓝色 | 科技、教育、通用办公、SaaS 应用 |
|
|
74
|
+
| `royalBlue` | 皇家蓝 | 高端商务、专业服务、企业级应用、信任感强 |
|
|
75
|
+
| `lightBlue` | 浅蓝 | 清新简约、云服务、通讯社交、年轻化应用 |
|
|
76
|
+
| `teal` | 青色 | 医疗健康、环保、清新简洁类应用 |
|
|
77
|
+
| `podGreen` | 绿色 | 农业、环保、健康、生态、可持续发展 |
|
|
78
|
+
| `deepPurple` | 深紫 | 创意设计、艺术、高端品牌、奢侈品 |
|
|
79
|
+
| `purple` | 紫色 | 女性用户、美妆、时尚、创新科技 |
|
|
80
|
+
| `podOrange` | 橙色 | 活力、电商、餐饮、娱乐、社交 |
|
|
81
|
+
| `yellow` | 黄色 | 儿童教育、阳光活力、警示提醒类应用 |
|
|
82
|
+
| `magenta` | 玫红色 | 时尚、创意、社交、娱乐类应用 |
|
|
83
|
+
| `red` | 红色 | 党建、政务、新闻、紧急类应用 |
|
|
84
|
+
| `greyBlue` | 灰蓝 | 稳重商务、工业制造、技术工程、专业工具 |
|
|
85
|
+
| `coffee` | 咖啡 | 传统行业、文化教育、复古风格、温馨舒适 |
|
|
86
|
+
| `black` | 黑色 | 极简设计、奢侈品牌、科技前沿、高端定制 |
|
|
87
|
+
|
|
65
88
|
|
|
66
89
|
**示例**:
|
|
67
90
|
|
|
@@ -69,8 +92,14 @@ openyida create-app <appName> [description] [icon] [iconColor]
|
|
|
69
92
|
# 最简用法
|
|
70
93
|
openyida create-app "考勤管理"
|
|
71
94
|
|
|
72
|
-
#
|
|
95
|
+
# 自定义图标
|
|
73
96
|
openyida create-app "考勤管理" "员工考勤打卡系统" "xian-daka" "#00B853"
|
|
97
|
+
|
|
98
|
+
# 完整参数(含主题色、导航风格、布局)
|
|
99
|
+
openyida create-app "考勤管理" "员工考勤打卡系统" "xian-daka" "#00B853" "deepBlue" "dark" "slide"
|
|
100
|
+
|
|
101
|
+
# 党建应用示例(红色主题 + 浅色导航)
|
|
102
|
+
openyida create-app "党建管理" "党员信息管理系统" "xian-zhengfu" "#FF4D4F" "red" "light" "ver"
|
|
74
103
|
```
|
|
75
104
|
|
|
76
105
|
**输出**:日志输出到 stderr,JSON 结果输出到 stdout:
|
|
@@ -48,17 +48,30 @@ openyida create-form create <appType> <formTitle> <fieldsJsonOrFile>
|
|
|
48
48
|
| `appType` | 是 | 应用 ID,如 `APP_XXX` |
|
|
49
49
|
| `formTitle` | 是 | 表单名称 |
|
|
50
50
|
| `fieldsJsonOrFile` | 是 | 字段定义,支持两种格式:JSON 字符串(以 `[` 开头)或 JSON 文件路径 |
|
|
51
|
+
#### 示例 1:创建简单表单
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
```bash
|
|
54
|
+
openyida create-form create "APP_CQ2P5NRFI5L1D6PB8Q7J" "员工信息登记" fields.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### 示例 2:创建双列表单
|
|
53
58
|
|
|
54
59
|
```bash
|
|
55
|
-
openyida create-form create "
|
|
60
|
+
openyida create-form create "APP_CQ2P5NRFI5L1D6PB8Q7J" "员工信息登记" fields.json --layout double
|
|
56
61
|
```
|
|
57
|
-
|
|
62
|
+
|
|
63
|
+
#### 示例 3:创建卡片式分组表单
|
|
64
|
+
|
|
58
65
|
```bash
|
|
59
|
-
|
|
66
|
+
# fields.json 中包含 group 字段分组
|
|
67
|
+
openyida create-form create "APP_CQ2P5NRFI5L1D6PB8Q7J" "员工信息登记" fields.json --layout card --theme comfortable
|
|
60
68
|
```
|
|
61
69
|
|
|
70
|
+
#### 示例 4:创建紧凑主题、左对齐标签的表单
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
openyida create-form create "APP_CQ2P5NRFI5L1D6PB8Q7J" "员工信息登记" fields.json --layout double --theme compact --label-align left
|
|
74
|
+
```
|
|
62
75
|
**输出**:日志输出到 stderr,JSON 结果输出到 stdout:
|
|
63
76
|
|
|
64
77
|
```json
|