openyida 0.1.2 → 1.0.0-beta.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 +68 -38
- package/bin/yida.js +164 -761
- package/lib/babel-transform/index.js +244 -0
- package/lib/babel-transform/jsx-utils.js +89 -0
- package/lib/check-update.js +72 -0
- package/lib/copy.js +258 -0
- package/lib/create-app.js +174 -0
- package/lib/create-form.js +2244 -0
- package/lib/create-page.js +89 -0
- package/lib/env.js +164 -0
- package/lib/get-page-config.js +102 -0
- package/lib/get-schema.js +76 -0
- package/lib/login.js +323 -0
- package/lib/publish.js +610 -0
- package/lib/save-share-config.js +268 -0
- package/lib/update-form-config.js +237 -0
- package/lib/utils.js +443 -0
- package/lib/verify-short-url.js +279 -0
- package/package.json +20 -7
- package/project/.cache/demo-schema.json +2353 -0
- package/project/pages/src/demo-birthday-game.js +833 -0
- package/project/pages/src/demo-future-vision-2026.js +1102 -0
- package/project/pages/src/demo-salary-calculator.js +904 -0
- package/project/prd/demo-birthday-game.md +39 -0
- package/project/prd/demo-future-vision-2026.md +78 -0
- package/project/prd/demo-salary-calculator.md +101 -0
- package/scripts/postinstall.js +114 -0
- package/yida-skills/SKILL.md +273 -0
- package/yida-skills/reference/association-form-field.md +469 -0
- package/yida-skills/reference/employee-field.md +17 -0
- package/yida-skills/reference/model-api.md +73 -0
- package/yida-skills/reference/serial-number-field.md +132 -0
- package/yida-skills/reference/yida-api.md +1208 -0
- package/yida-skills/skills/yida-app/SKILL.md +394 -0
- package/yida-skills/skills/yida-create-app/SKILL.md +158 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +598 -0
- package/yida-skills/skills/yida-create-page/SKILL.md +103 -0
- package/yida-skills/skills/yida-custom-page/SKILL.md +533 -0
- package/yida-skills/skills/yida-get-schema/SKILL.md +90 -0
- package/yida-skills/skills/yida-login/SKILL.md +200 -0
- package/yida-skills/skills/yida-logout/SKILL.md +58 -0
- package/yida-skills/skills/yida-page-config/SKILL.md +261 -0
- package/yida-skills/skills/yida-publish-page/SKILL.md +113 -0
- package/.eslintrc.json +0 -25
- package/.github/workflows/ci.yml +0 -123
- package/.github/workflows/publish.yml +0 -105
- package/.github/workflows/update-contributors.yml +0 -151
- package/.openclaw/skills/yida-issue/SKILL.md +0 -27
- package/.openclaw/skills/yida-issue/scripts/create-issue.js +0 -317
- package/CLAUDE.md +0 -168
- package/CONTRIBUTING.md +0 -59
- package/install-skills.ps1 +0 -162
- package/install-skills.sh +0 -175
- package/pages/dist/.gitkeep +0 -0
- package/pages/src/.gitkeep +0 -0
- package/prd/salary-calculator.md +0 -15
- package/tests/cli.test.js +0 -930
- package/tests/install.test.js +0 -277
- package/tests/yida-issue.test.js +0 -314
- /package/{config.json → project/config.json} +0 -0
- /package/{.cache → project/pages/dist}/.gitkeep +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yida-create-page
|
|
3
|
+
description: 宜搭自定义页面创建技能,通过调用 saveFormSchemaInfo 接口快速创建自定义展示页面。
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility:
|
|
6
|
+
- opencode
|
|
7
|
+
- claude-code
|
|
8
|
+
metadata:
|
|
9
|
+
audience: developers
|
|
10
|
+
workflow: yida-development
|
|
11
|
+
version: 1.0.0
|
|
12
|
+
tags:
|
|
13
|
+
- yida
|
|
14
|
+
- low-code
|
|
15
|
+
- page
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# 宜搭自定义页面创建技能
|
|
19
|
+
|
|
20
|
+
## 概述
|
|
21
|
+
|
|
22
|
+
本技能描述如何通过 HTTP 请求调用宜搭 `saveFormSchemaInfo` 接口创建自定义展示页面(display 类型)。创建后可通过 `yida-publish` 技能部署自定义 JSX 代码。
|
|
23
|
+
|
|
24
|
+
## 何时使用
|
|
25
|
+
|
|
26
|
+
当以下场景发生时使用此技能:
|
|
27
|
+
- 用户需要在已有应用中创建自定义展示页面
|
|
28
|
+
- 用户需要创建用于展示内容的主页、列表页等非表单页面
|
|
29
|
+
- 已通过 yida-create-app 创建应用后,需要创建第一个页面
|
|
30
|
+
|
|
31
|
+
## 使用示例
|
|
32
|
+
|
|
33
|
+
### 示例 1:基础用法
|
|
34
|
+
**场景**:在已有应用中创建一个自定义页面
|
|
35
|
+
**命令**:
|
|
36
|
+
```bash
|
|
37
|
+
openyida create-page "APP_XXX" "游戏主页"
|
|
38
|
+
```
|
|
39
|
+
**输出**:
|
|
40
|
+
```json
|
|
41
|
+
{"success":true,"pageId":"FORM-XXX","pageName":"游戏主页","appType":"APP_XXX","url":"{base_url}/APP_XXX/workbench/FORM-XXX"}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 使用方式
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
openyida create-page <appType> <pageName>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**参数说明**:
|
|
51
|
+
|
|
52
|
+
| 参数 | 必填 | 说明 |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `appType` | 是 | 应用 ID,如 `APP_XXX` |
|
|
55
|
+
| `pageName` | 是 | 页面名称 |
|
|
56
|
+
|
|
57
|
+
**示例**:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
openyida create-page "APP_xxx" "游戏主页"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**输出**:日志输出到 stderr,JSON 结果输出到 stdout:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{"success":true,"pageId":"FORM-XXX","pageName":"游戏主页","appType":"APP_XXX","url":"{base_url}/APP_XXX/workbench/FORM-XXX"}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 前置依赖
|
|
70
|
+
|
|
71
|
+
- Node.js
|
|
72
|
+
- 项目根目录存在 `.cache/cookies.json`(首次运行会自动触发扫码登录)
|
|
73
|
+
|
|
74
|
+
## 调用流程
|
|
75
|
+
|
|
76
|
+
1. 读取项目根目录的 `.cache/cookies.json` 获取登录态;若不存在则自动触发扫码登录
|
|
77
|
+
2. 调用 `saveFormSchemaInfo` 接口创建 display 类型页面;根据响应体 `errorCode` 自动处理异常(详见 `yida-login` 技能文档「错误处理机制」章节)
|
|
78
|
+
3. 从返回值中获取页面 ID(formUuid)
|
|
79
|
+
4. **将 `pageId`(formUuid)记录到 `prd/<项目名>.md` 的应用配置章节**
|
|
80
|
+
|
|
81
|
+
## 文件结构
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
yida-create-page/
|
|
85
|
+
└── SKILL.md # 本文档
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 接口说明
|
|
89
|
+
|
|
90
|
+
`saveFormSchemaInfo` 接口的完整参数、返回值和错误处理机制,请参考 `../../reference/yida-api.md` 文档中的「表单设计类 API」章节。
|
|
91
|
+
|
|
92
|
+
> **注意**:创建自定义页面时,`formType` 参数固定为 `display`(区别于表单页面的 `receipt`)。
|
|
93
|
+
|
|
94
|
+
## 与其他技能配合
|
|
95
|
+
|
|
96
|
+
1. **创建应用** → 使用 `yida-create-app` 技能获取 `appType`
|
|
97
|
+
2. **创建自定义页面** → 本技能,获取 `pageId`(formUuid)
|
|
98
|
+
3. **编写 JSX 源码** → **必须先加载 `yida-custom-page` skill**,严格按照其开发规范编写代码
|
|
99
|
+
4. **部署页面代码** → 使用 `yida-publish-page` 技能将代码部署到该页面
|
|
100
|
+
|
|
101
|
+
> ⚠️ **重要警告**:宜搭自定义页面使用类组件模式,**禁止使用 React Hooks**(useState/useEffect)。编写代码前必须先加载 `yida-custom-page` skill 查看完整的开发规范。
|
|
102
|
+
|
|
103
|
+
> **提示**:如果需要创建的是表单页面(带字段的数据收集页),请使用 `yida-create-form-page` 技能。
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yida-custom-page
|
|
3
|
+
description: 宜搭自定义页面开发技能,包含宜搭表单 JS API 调用(增删改查/流程/工具类共 27 个)、React 16 JSX 组件开发规范、状态管理模式与编码约束。
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility:
|
|
6
|
+
- opencode
|
|
7
|
+
- claude-code
|
|
8
|
+
metadata:
|
|
9
|
+
audience: developers
|
|
10
|
+
workflow: yida-development
|
|
11
|
+
version: 1.0.0
|
|
12
|
+
tags:
|
|
13
|
+
- yida
|
|
14
|
+
- low-code
|
|
15
|
+
- react
|
|
16
|
+
- custom-page
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# 宜搭自定义页面开发技能
|
|
20
|
+
|
|
21
|
+
## 概述
|
|
22
|
+
|
|
23
|
+
本技能提供在阿里宜搭低代码平台上开发**自定义页面**的完整能力,涵盖从编码到部署的全流程:
|
|
24
|
+
|
|
25
|
+
| 能力 | 说明 |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| **表单数据操作** | 通过宜搭前端 JS API(`this.utils.yida.*`)对表单数据进行增删改查 |
|
|
28
|
+
| **JSX 组件开发** | 编写 React 16 兼容的 JSX 代码,实现个性化定制页面 |
|
|
29
|
+
| **AI 能力集成** | 调用大模型 AI 接口(`/query/intelligent/txtFromAI.json`)实现智能文本生成 |
|
|
30
|
+
| **自动编译部署** | 通过工具链将源码编译、压缩,并自动合并到宜搭 Schema 中保存 |
|
|
31
|
+
|
|
32
|
+
## 何时使用
|
|
33
|
+
|
|
34
|
+
当以下场景发生时使用此技能:
|
|
35
|
+
- 用户需要开发自定义展示页面(非表单)
|
|
36
|
+
- 用户需要实现复杂的页面交互逻辑
|
|
37
|
+
- 用户需要调用宜搭 JS API 进行数据操作
|
|
38
|
+
- 已有自定义页面,需要编写或修改 JSX 代码
|
|
39
|
+
|
|
40
|
+
## 使用示例
|
|
41
|
+
|
|
42
|
+
> **注意**:编译和发布功能由 `yida-publish-page` 技能提供,此处仅作流程说明。
|
|
43
|
+
|
|
44
|
+
### 示例 1:编译源码
|
|
45
|
+
**场景**:将 JSX 源码编译为宜搭可用的格式
|
|
46
|
+
**依赖**:需先安装 yida-publish-page 依赖
|
|
47
|
+
**命令**:
|
|
48
|
+
```bash
|
|
49
|
+
# openyida 已包含所有依赖,无需单独安装
|
|
50
|
+
node babel-transform/transform.js pages/src/my-page.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 示例 2:发布页面
|
|
54
|
+
**场景**:编译并发布自定义页面到宜搭平台
|
|
55
|
+
**命令**:
|
|
56
|
+
```bash
|
|
57
|
+
openyida publish pages/src/my-page.js APP_XXX FORM-XXX
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 快速开始
|
|
63
|
+
|
|
64
|
+
### 前置条件
|
|
65
|
+
|
|
66
|
+
- Node.js 16+(用于 Babel 编译和发布)
|
|
67
|
+
- Python 3.12+ + `playwright`(用于登录态管理)
|
|
68
|
+
- 首次使用需安装依赖:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# openyida 已包含所有依赖,无需单独安装
|
|
72
|
+
pip install playwright && playwright install chromium
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 编译源码
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
node scripts/babel-transform/transform.js <源文件路径>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**编译流程**:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
源文件(.js) → @ali/vu-babel-transform (Babel 转换) → UglifyJS (压缩) → <name>.compile.js
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 部署到宜搭
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
openyida publish <源文件路径> <appType> <formUuid>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**部署流程**:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
编译源码(Babel + UglifyJS) → 代码动态构建 Schema JSON(填入 source/compiled)
|
|
97
|
+
→ 调用 yida-login 获取登录态(Cookie 持久化) → 调用 saveFormSchema 接口保存
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**参数说明**:
|
|
101
|
+
|
|
102
|
+
| 参数 | 说明 | 示例 |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `appType` | 应用 ID | `APP_XXX` |
|
|
105
|
+
| `formUuid` | 表单 ID | `FORM-XXX` |
|
|
106
|
+
| `源文件路径` | 源码文件路径 | `pages/src/xxx.js` |
|
|
107
|
+
|
|
108
|
+
> `baseUrl` 无需手动传入,`openyida` 会自动获取登录态并从中读取 `base_url`。
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 开发规范
|
|
113
|
+
|
|
114
|
+
> **以下规范是编写宜搭自定义页面代码的核心约束,必须严格遵守。**
|
|
115
|
+
|
|
116
|
+
### 运行环境与约束
|
|
117
|
+
|
|
118
|
+
宜搭自定义页面的 JSX 组件本质上是 **React 类组件中的 render 方法**,而非独立的 React 组件。因此存在以下关键约束:
|
|
119
|
+
|
|
120
|
+
| 约束 | 说明 |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| **React 版本** | 必须兼容 **React 16**,禁止使用 Hooks(`useState`、`useEffect` 等) |
|
|
123
|
+
| **单文件** | 所有代码写在一个文件中(如 `index.js`)|
|
|
124
|
+
| **三方包引入** | 禁止使用 `import/require` 语法,如需使用第三方库,必须通过 `this.utils.loadScript` 加载 CDN 脚本,参考 [yida-api.md](../../reference/yida-api.md) 的「工具类 API」章节。|
|
|
125
|
+
| **函数导出格式** | 使用 `export function xxx() {}` 格式导出函数 |
|
|
126
|
+
| **样式** | 所有 css 必须写在 renderJsx 的方法中,通过 style 的方式引入 |
|
|
127
|
+
| **`this` 上下文** | 所有导出函数中的 `this` 指向宜搭页面的 React 类实例 |
|
|
128
|
+
| **禁止使用 `this.setState` 管理业务状态** | `this.setState` 已被覆盖,仅用于 `forceUpdate`(通过更新 `timestamp`) |
|
|
129
|
+
| **JavaScript 版本** | 使用 ES2015 (ES6) 语法,不能高于 ES2015 版本 |
|
|
130
|
+
| **必须定义 renderJsx 函数** | renderJsx 是宜搭自定义页面核心渲染函数,也是入口函数,必须严格定义,不要改为其他名称 |
|
|
131
|
+
|
|
132
|
+
### 文件结构
|
|
133
|
+
|
|
134
|
+
**一个完整的宜搭自定义页面源文件必须包含:**
|
|
135
|
+
- `_customState` 变量
|
|
136
|
+
- getCustomState 函数
|
|
137
|
+
- setCustomState 函数
|
|
138
|
+
- forceUpdate 函数
|
|
139
|
+
- didMount 函数
|
|
140
|
+
- didUnmount 函数
|
|
141
|
+
- renderJsx 函数
|
|
142
|
+
|
|
143
|
+
以下是一个完整自定义页面示例,包含状态管理、生命周期钩子、渲染函数
|
|
144
|
+
|
|
145
|
+
```jsx
|
|
146
|
+
// ============================================================
|
|
147
|
+
// 状态管理
|
|
148
|
+
// ============================================================
|
|
149
|
+
|
|
150
|
+
const _customState = {
|
|
151
|
+
// 在此定义所有业务状态的初始值
|
|
152
|
+
count: 0,
|
|
153
|
+
loading: false,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 获取状态
|
|
158
|
+
* @param {string} [key] - 传入 key 返回单个值,不传返回全部状态的浅拷贝
|
|
159
|
+
*/
|
|
160
|
+
export function getCustomState(key) {
|
|
161
|
+
if (key) {
|
|
162
|
+
return _customState[key];
|
|
163
|
+
}
|
|
164
|
+
return { ..._customState };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 设置状态(合并更新,自动触发重新渲染)
|
|
169
|
+
* @param {Object} newState - 需要更新的状态键值对
|
|
170
|
+
*/
|
|
171
|
+
export function setCustomState(newState) {
|
|
172
|
+
Object.keys(newState).forEach(function(key) {
|
|
173
|
+
_customState[key] = newState[key];
|
|
174
|
+
});
|
|
175
|
+
this.forceUpdate();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 强制重新渲染(通过更新 timestamp 触发 React 重渲染)
|
|
180
|
+
*/
|
|
181
|
+
export function forceUpdate() {
|
|
182
|
+
this.setState({ timestamp: new Date().getTime() });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ============================================================
|
|
186
|
+
// 生命周期
|
|
187
|
+
// ============================================================
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 页面加载完成时调用
|
|
191
|
+
* 用于:初始化数据、启动定时器、绑定事件等
|
|
192
|
+
*/
|
|
193
|
+
export function didMount() {
|
|
194
|
+
// 初始化逻辑
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 页面卸载时调用
|
|
199
|
+
* 用于:清理定时器、解绑事件、释放资源等
|
|
200
|
+
*/
|
|
201
|
+
export function didUnmount() {
|
|
202
|
+
// 清理逻辑
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function handleSubmit(e) {
|
|
206
|
+
this.setCustomState({ submitted: true });
|
|
207
|
+
this.utils.toast({ title: '提交成功', type: 'success' });
|
|
208
|
+
}
|
|
209
|
+
// ============================================================
|
|
210
|
+
// 渲染
|
|
211
|
+
// ============================================================
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 页面渲染函数(等同于 React 类组件的 render 方法)
|
|
215
|
+
* 注意:必须包含隐藏的 timestamp div 以支持 forceUpdate 机制
|
|
216
|
+
*/
|
|
217
|
+
export function renderJsx() {
|
|
218
|
+
const { timestamp } = this.state;
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<div>
|
|
222
|
+
{/* 必须保留:用于触发重新渲染 */}
|
|
223
|
+
<div style={{ display: "none" }}>{timestamp}</div>
|
|
224
|
+
|
|
225
|
+
{/* 页面内容写在这里 */}
|
|
226
|
+
<div onClick={(e) => {this.handleSubmit(e)}>提交</div>
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 状态管理使用方式
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
// 获取全部状态(返回浅拷贝)
|
|
236
|
+
const state = this.getCustomState();
|
|
237
|
+
|
|
238
|
+
// 获取单个状态值
|
|
239
|
+
const count = this.getCustomState('count');
|
|
240
|
+
|
|
241
|
+
// 设置状态并自动触发重新渲染
|
|
242
|
+
this.setCustomState({ count: count + 1, loading: true });
|
|
243
|
+
|
|
244
|
+
// 仅触发重新渲染(不修改状态)
|
|
245
|
+
this.forceUpdate();
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 生命周期钩子
|
|
249
|
+
|
|
250
|
+
| 钩子函数 | 触发时机 | 典型用途 |
|
|
251
|
+
| --- | --- | --- |
|
|
252
|
+
| `didMount()` | 页面 DOM 加载渲染完毕 | 初始化数据加载、启动定时器、绑定事件 |
|
|
253
|
+
| `didUnmount()` | 页面节点从 DOM 移除 | 清理 `setInterval` / `setTimeout`、解绑事件 |
|
|
254
|
+
|
|
255
|
+
### 全局变量
|
|
256
|
+
|
|
257
|
+
| 变量 | 类型 | 说明 |
|
|
258
|
+
| --- | --- | --- |
|
|
259
|
+
| `window.g_config._csrf_token` | `String` | CSRF Token,调用需认证的接口(如 AI 接口、Schema 保存)时必须携带 |
|
|
260
|
+
| `window.loginUser.userId` | `String` | 当前登录用户的工号 |
|
|
261
|
+
| `window.loginUser.userName` | `String` | 当前登录用户的姓名 |
|
|
262
|
+
| `this.state.urlParams` | `Object` | 页面 URL 中的查询参数 |
|
|
263
|
+
|
|
264
|
+
### 编码注意事项
|
|
265
|
+
|
|
266
|
+
1. **自定义方法必须用 `export function` 定义**:凡是需要在方法内部使用 `this`(包括 `this.utils.yida.*`、`this.setCustomState` 等)的自定义方法,**必须且只能**使用 `export function 方法名() {}` 的形式定义,调用时使用 `this.方法名()`。**禁止**使用 `const fn = () => {}`、`const fn = function() {}` 等形式定义需要访问 `this` 的方法,这些形式无法被宜搭运行时正确绑定 `this`:
|
|
267
|
+
```javascript
|
|
268
|
+
// ✅ 正确:export function + this.方法名() 调用
|
|
269
|
+
export function didMount() {
|
|
270
|
+
this.loadStatistics();
|
|
271
|
+
}
|
|
272
|
+
export function loadStatistics() {
|
|
273
|
+
this.utils.yida.searchFormDatas({ formUuid: 'FORM-XXX', pageSize: 10 });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ❌ 错误①:缺少 export,无法被宜搭运行时识别,this 丢失
|
|
277
|
+
export function didMount() {
|
|
278
|
+
loadStatistics(); // 直接调用,this 丢失
|
|
279
|
+
}
|
|
280
|
+
function loadStatistics() {
|
|
281
|
+
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ❌ 错误②:箭头函数/函数表达式形式,缺少 export,无法被宜搭运行时绑定 this,禁止使用
|
|
285
|
+
const loadStatistics = () => {
|
|
286
|
+
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
|
|
287
|
+
};
|
|
288
|
+
const loadStatistics = function() {
|
|
289
|
+
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
|
|
290
|
+
};
|
|
291
|
+
```
|
|
292
|
+
2. **【严格禁止】事件绑定必须使用箭头函数包裹**:在 `renderJsx` 中绑定任何事件处理器(`onClick`、`onChange`、`onSubmit` 等)时,**必须且只能**使用箭头函数 `(e) => { this.方法名(e) }` 的形式,**严禁**直接写 `this.方法名` 作为事件处理器,否则 `this` 会丢失导致运行时报错:
|
|
293
|
+
|
|
294
|
+
```javascript
|
|
295
|
+
export function handleSubmit(e) {
|
|
296
|
+
this.setCustomState({ submitted: true });
|
|
297
|
+
this.utils.toast({ title: '提交成功', type: 'success' });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ✅ 正确:箭头函数包裹,this 正确捕获
|
|
301
|
+
export function renderJsx() {
|
|
302
|
+
return <button onClick={(e) => { this.handleSubmit(e); }}>提交</button>;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ❌ 错误①:直接传方法引用,this 丢失,运行时报错,绝对禁止!
|
|
306
|
+
export function renderJsx() {
|
|
307
|
+
return <button onClick={this.handleSubmit}>提交</button>;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ❌ 错误②:使用 .bind(this) 绑定,虽然能运行但不符合规范,禁止使用!
|
|
311
|
+
export function renderJsx() {
|
|
312
|
+
return <button onClick={function() { this.handleSubmit(); }.bind(this)}>提交</button>;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
> **生成代码时的自检清单**:检查 `renderJsx` 中所有 `onClick`、`onChange`、`onSubmit` 等事件属性,确保每一个都是 `(e) => { this.xxx(e) }` 形式,不存在任何 `onClick={this.xxx}` 的写法。
|
|
317
|
+
|
|
318
|
+
3. **输入法组合输入处理**:使用 `_isComposing` 标记配合 `compositionstart` / `compositionend` 事件,正确处理中文输入法的组合输入状态,避免输入过程中触发提交
|
|
319
|
+
4. **定时器清理**:在 `didUnmount` 中必须清理所有通过 `setInterval` / `setTimeout` 创建的定时器,防止内存泄漏
|
|
320
|
+
5. **错误处理**:所有 API 调用(`this.utils.yida.*`、`fetch`)必须使用 `.catch()` 处理异常,并通过 `this.utils.toast({ title: message, type: 'error' })` 向用户展示错误提示
|
|
321
|
+
6. **样式方式**:所有样式通过 JavaScript 对象定义(内联样式),在 `renderJsx` 中通过 `style` 属性应用,不使用外部 CSS 文件
|
|
322
|
+
7. **异步操作**:可以使用 `async/await` 语法,Babel 编译会自动转换为 ES5 兼容代码
|
|
323
|
+
8. **pageSize 上限**:调用 `searchFormDatas`、`searchFormDataIds`、`getProcessInstances`、`getProcessInstanceIds` 等分页接口时,`pageSize` 最大值为 **100**,超过会导致接口报错。禁止将 `pageSize` 设置为超过 100 的值,推荐使用 `10`~`100` 之间的合理值。
|
|
324
|
+
9. **输入框使用非受控组件**:在宜搭环境中,`<input>` 的 `value` 属性绑定状态后会触发重渲染导致输入异常。**正确做法**:使用 `defaultValue`,在 `onChange` 中更新 `_customState` 而不调用 `setCustomState`:
|
|
325
|
+
```javascript
|
|
326
|
+
// ❌ 错误:受控组件,每次输入都触发重渲染导致无法输入
|
|
327
|
+
<input value={userAnswer} onChange={function(e) { this.setCustomState({ userAnswer: e.target.value }); }} />
|
|
328
|
+
|
|
329
|
+
// ✅ 正确:非受控组件,仅静默更新状态,不触发重渲染
|
|
330
|
+
<input id="my-input" defaultValue="" onChange={function(e) { _customState.userAnswer = e.target.value; }} />
|
|
331
|
+
|
|
332
|
+
// 需要清空时通过 DOM 操作
|
|
333
|
+
var inputEl = document.getElementById("my-input");
|
|
334
|
+
if (inputEl) { inputEl.value = ""; }
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
10. **DateField 时间戳格式**:保存日期字段时,值必须是 **时间戳(毫秒)**,不能是字符串:
|
|
338
|
+
```javascript
|
|
339
|
+
// ❌ 错误:字符串格式
|
|
340
|
+
dateField_xxx: '2024-01-15'
|
|
341
|
+
|
|
342
|
+
// ✅ 正确:时间戳格式
|
|
343
|
+
dateField_xxx: new Date().getTime()
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
11. **多端适配**:宜搭自定义页面会在 PC 端和移动端同时展示,使用 `this.utils.isMobile()` 判断设备类型:
|
|
347
|
+
```javascript
|
|
348
|
+
const isMobile = this.utils.isMobile();
|
|
349
|
+
var styles = {
|
|
350
|
+
container: { padding: isMobile ? '12px' : '16px', minHeight: '100vh' },
|
|
351
|
+
card: { padding: isMobile ? '12px' : '16px', marginBottom: isMobile ? '8px' : '12px' },
|
|
352
|
+
};
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
12. **清除默认样式**:宜搭自定义页面容器有默认 padding 和圆角,需要强制覆盖:
|
|
356
|
+
```javascript
|
|
357
|
+
var styles = {
|
|
358
|
+
container: { padding: '0 16px', borderRadius: '0 !important', minHeight: '100vh' },
|
|
359
|
+
};
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
13. **性能优化**:
|
|
363
|
+
- 不要在每次 `onChange` 都调用 `setCustomState`,可直接写入 `_customState` 静默更新
|
|
364
|
+
- 只在需要触发重渲染时才调用 `forceUpdate`
|
|
365
|
+
- 在 `renderJsx` 顶部定义事件处理函数,避免每次渲染都创建新的内联函数
|
|
366
|
+
|
|
367
|
+
14. **调试技巧**:
|
|
368
|
+
```javascript
|
|
369
|
+
// 打印当前状态到控制台
|
|
370
|
+
console.log('当前状态:', _customState);
|
|
371
|
+
|
|
372
|
+
// 弹窗提示(适合快速验证逻辑)
|
|
373
|
+
this.utils.toast({ title: '调试信息', type: 'info' });
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
15. **iframe 嵌入表单 URL 规范**:在自定义页面中通过 iframe 嵌入宜搭表单时,需使用正确的 URL 格式:
|
|
377
|
+
|
|
378
|
+
| 场景 | URL 格式 |
|
|
379
|
+
|------|----------|
|
|
380
|
+
| 表单提交页 | `{base_url}/{appType}/submission/{formUuid}` |
|
|
381
|
+
| 数据管理页(列表) | `{base_url}/{appType}/workbench/{formUuid}?iframe=true` |
|
|
382
|
+
| 数据管理页(指定视图) | `{base_url}/{appType}/workbench/{formUuid}?viewUuid={viewUuid}&iframe=true` |
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
// ❌ 错误:formDetail 是表单详情页,不是数据列表
|
|
386
|
+
const wrongUrl = `${baseUrl}/${appType}/formDetail/${formUuid}`;
|
|
387
|
+
|
|
388
|
+
// ✅ 正确:workbench 是运行态数据管理页
|
|
389
|
+
const listUrl = `${baseUrl}/${appType}/workbench/${formUuid}?iframe=true`;
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
> `viewUuid` 可选,从宜搭「数据管理」→「报表视图」页面的 URL 中获取,不传则使用默认视图。
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## API 参考
|
|
397
|
+
|
|
398
|
+
### 表单数据操作
|
|
399
|
+
|
|
400
|
+
通过 `this.utils.yida.<方法名>(params)` 调用,所有接口返回 Promise。
|
|
401
|
+
|
|
402
|
+
| 方法 | 说明 | 必填参数 |
|
|
403
|
+
| --- | --- | --- |
|
|
404
|
+
| `saveFormData` | 新建表单实例 | `formUuid`, `appType`, `formDataJson` |
|
|
405
|
+
| `updateFormData` | 更新表单实例 | `formInstId`, `updateFormDataJson` |
|
|
406
|
+
| `deleteFormData` | 删除表单实例 | `formUuid` |
|
|
407
|
+
| `getFormDataById` | 根据实例 ID 查询详情 | `formInstId` |
|
|
408
|
+
| `searchFormDatas` | 按条件搜索表单实例详情列表 | `formUuid` |
|
|
409
|
+
| `searchFormDataIds` | 按条件搜索表单实例 ID 列表 | `formUuid` |
|
|
410
|
+
| `getFormComponentDefinationList` | 获取表单定义 | `formUuid` |
|
|
411
|
+
|
|
412
|
+
**常用示例 — 新建表单数据**:
|
|
413
|
+
|
|
414
|
+
```javascript
|
|
415
|
+
this.utils.yida.saveFormData({
|
|
416
|
+
formUuid: 'FORM-XXX',
|
|
417
|
+
appType: window.pageConfig.appType,
|
|
418
|
+
formDataJson: JSON.stringify({
|
|
419
|
+
textField_xxx: '单行文本',
|
|
420
|
+
textareaField_xxx: '多行文本',
|
|
421
|
+
}),
|
|
422
|
+
}).then(function(res) {
|
|
423
|
+
console.log('新建成功,实例ID:', res.result);
|
|
424
|
+
}).catch(function(err) {
|
|
425
|
+
this.utils.toast({ title: err.message, type: 'error' });
|
|
426
|
+
}.bind(this));
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**常用示例 — 搜索表单数据**:
|
|
430
|
+
|
|
431
|
+
```javascript
|
|
432
|
+
this.utils.yida.searchFormDatas({
|
|
433
|
+
formUuid: 'FORM-XXX',
|
|
434
|
+
searchFieldJson: JSON.stringify({ textField_xxx: '查询值' }),
|
|
435
|
+
currentPage: 1,
|
|
436
|
+
pageSize: 10,
|
|
437
|
+
}).then(function(res) {
|
|
438
|
+
// res.data: [{ formUuid, formInstId, formData: { textField_xxx: '值' } }]
|
|
439
|
+
// res.totalCount: 符合条件的总数
|
|
440
|
+
console.log('查询结果:', res.data);
|
|
441
|
+
}).catch(function(err) {
|
|
442
|
+
this.utils.toast({ title: err.message, type: 'error' });
|
|
443
|
+
}.bind(this));
|
|
444
|
+
```
|
|
445
|
+
### 流程操作
|
|
446
|
+
|
|
447
|
+
| 方法 | 说明 | 必填参数 |
|
|
448
|
+
| --- | --- | --- |
|
|
449
|
+
| `startProcessInstance` | 发起流程 | `formUuid`, `processCode`, `formDataJson` |
|
|
450
|
+
| `updateProcessInstance` | 更新流程实例 | `processInstanceId`, `updateFormDataJson` |
|
|
451
|
+
| `deleteProcessInstance` | 删除流程实例 | `processInstanceId` |
|
|
452
|
+
| `getProcessInstanceById` | 根据实例 ID 查询流程详情 | `processInstanceId` |
|
|
453
|
+
| `getProcessInstances` | 按条件搜索流程实例详情列表 | — |
|
|
454
|
+
| `getProcessInstanceIds` | 按条件搜索流程实例 ID 列表 | — |
|
|
455
|
+
|
|
456
|
+
### 表单设计类 API
|
|
457
|
+
|
|
458
|
+
以下接口用于表单页面的创建和配置,通过 HTTP 请求调用:
|
|
459
|
+
|
|
460
|
+
| 方法 | 说明 | 调用方式 |
|
|
461
|
+
| --- | --- | --- |
|
|
462
|
+
| `saveFormSchemaInfo` | 创建空白表单 | `POST /dingtalk/web/{appType}/query/formdesign/saveFormSchemaInfo.json` |
|
|
463
|
+
| `getFormSchema` | 获取表单 Schema | `GET /alibaba/web/{appType}/_view/query/formdesign/getFormSchema.json` |
|
|
464
|
+
| `saveFormSchema` | 保存表单 Schema | `POST /dingtalk/web/{appType}/_view/query/formdesign/saveFormSchema.json` |
|
|
465
|
+
| `updateFormConfig` | 更新表单配置 | `POST /dingtalk/web/{appType}/query/formdesign/updateFormConfig.json` |
|
|
466
|
+
|
|
467
|
+
完整参数说明请参考 [yida-api.md](../../reference/yida-api.md) 的「表单设计类 API」章节。
|
|
468
|
+
|
|
469
|
+
### 大模型 AI 接口
|
|
470
|
+
|
|
471
|
+
以下接口用于调用大模型 AI 文本生成能力:
|
|
472
|
+
|
|
473
|
+
| 方法 | 说明 | 调用方式 |
|
|
474
|
+
| --- | --- | --- |
|
|
475
|
+
| `txtFromAI` | AI 文本生成 | `POST /query/intelligent/txtFromAI.json` |
|
|
476
|
+
|
|
477
|
+
**主要参数**:`_csrf_token`(CSRF 令牌)、`prompt`(提示词)、`skill`(技能类型,如 `ToText`)、`maxTokens`(最大返回 token 数)
|
|
478
|
+
|
|
479
|
+
完整参数说明和示例请参考 [model-api.md](../../reference/model-api.md)。
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
### 工具类 API 速查
|
|
484
|
+
|
|
485
|
+
以下工具函数通过 `this.utils.<方法名>()` 调用,无需 `yida` 命名空间:
|
|
486
|
+
|
|
487
|
+
| 方法 | 用途 | 典型场景 |
|
|
488
|
+
| --- | --- | --- |
|
|
489
|
+
| `toast` | 轻提示 | 操作成功/失败提示、loading 状态 |
|
|
490
|
+
| `dialog` | 对话框 | 确认操作、复杂内容展示 |
|
|
491
|
+
| `formatter` | 格式化 | 日期、金额、手机号格式化 |
|
|
492
|
+
| `getDateTimeRange` | 获取时间范围 | 按日/月/周筛选数据 |
|
|
493
|
+
| `getLoginUserId` / `getLoginUserName` | 获取当前用户 | 记录操作人、数据权限控制 |
|
|
494
|
+
| `getLocale` | 获取语言环境 | 多语言适配 |
|
|
495
|
+
| `isMobile` | 判断移动端 | 响应式布局适配 |
|
|
496
|
+
| `isSubmissionPage` | 判断是否提交页面 | 页面逻辑区分 |
|
|
497
|
+
| `isViewPage` | 判断是否查看页面 | 页面逻辑区分 |
|
|
498
|
+
| `openPage` | 打开新页面 | 页面跳转、外链打开 |
|
|
499
|
+
| `router.push` | 页面路由跳转工具 | 页面路由跳转、避免新开页面 |
|
|
500
|
+
| `previewImage` | 图片预览 | 图片查看、多图轮播 |
|
|
501
|
+
| `loadScript` | 动态加载脚本 | 引入第三方库(如二维码生成) |
|
|
502
|
+
|
|
503
|
+
完整参数说明和示例请参考 [yida-api.md](../../reference/yida-api.md) 的「工具类 API」章节。
|
|
504
|
+
|
|
505
|
+
## 工具链
|
|
506
|
+
|
|
507
|
+
| Skill | 说明 | 用法 |
|
|
508
|
+
| --- | --- | --- |
|
|
509
|
+
| **yida-login** | 登录态管理(Cookie 持久化 + 扫码登录) | `openyida login` |
|
|
510
|
+
| **yida-publish-page** | 编译源码 + 构建 Schema + 发布到宜搭 | `openyida publish <源文件路径> <appType> <formUuid>` |
|
|
511
|
+
| **yida-page-config** | 页面配置(URL 验证、公开访问/分享配置) | 详见 `yida-page-config` 技能文档 |
|
|
512
|
+
|
|
513
|
+
### 编译 + 发布(一键完成)
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
openyida publish <源文件路径> <appType> <formUuid>
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**处理流程**:
|
|
520
|
+
1. 通过 `@ali/vu-babel-transform` 将 JSX 转换为 ES5 + UglifyJS 压缩
|
|
521
|
+
2. 通过代码动态构建完整的 Schema JSON,将 `source` 和 `compiled` 填入 `actions.module`
|
|
522
|
+
3. 调用 `yida-login` 获取登录态(Cookie 持久化,首次需扫码登录)
|
|
523
|
+
4. 通过 HTTP POST 调用 `saveFormSchema` 接口保存 Schema
|
|
524
|
+
|
|
525
|
+
### 仅编译(不发布)
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
openyida publish <源文件路径> <appType> <formUuid>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
输入 JSX 源文件,输出编译压缩后的 `<name>.compile.js`(与源文件同目录)。
|
|
532
|
+
|
|
533
|
+
---
|