Skip to content
This repository was archived by the owner on Mar 18, 2026. It is now read-only.

Commit 712eb58

Browse files
author
alex.mm
committed
fix: 统一选项字段使用 dataSource 格式,完善 SKILL 文档
- 将 options 属性改为 dataSource,与宜搭实际 Schema 保持一致 - 完善 RadioField/CheckboxField/SelectField/MultiSelectField 的选项格式说明 - 更新 create-form-page.js 脚本,支持直接使用 dataSource - 补充完整的选项对象结构文档(text, value, sid, disable, defaultChecked) - 添加强制规则说明:text.zh_CN/text.en_US/value 必须是字符串
1 parent e9dee24 commit 712eb58

3 files changed

Lines changed: 117 additions & 38 deletions

File tree

skills/yida-create-form-page/SKILL.md

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ node .claude/skills/yida-create-form-page/scripts/create-form-page.js create <ap
5252
**示例(JSON 字符串,推荐)**
5353

5454
```bash
55-
node .claude/skills/yida-create-form-page/scripts/create-form-page.js create "APP_XXX" "用户信息表" '[{"type":"TextField","label":"姓名","required":true},{"type":"SelectField","label":"部门","options":["技术部","产品部"]}]'
55+
node .claude/skills/yida-create-form-page/scripts/create-form-page.js create "APP_XXX" "用户信息表" '[{"type":"TextField","label":"姓名","required":true},{"type":"SelectField","label":"部门","dataSource":[{"text":{"zh_CN":"技术部","en_US":"技术部","type":"i18n"},"value":"技术部","sid":"serial_xxx","disable":false,"defaultChecked":false},{"text":{"zh_CN":"产品部","en_US":"产品部","type":"i18n"},"value":"产品部","sid":"serial_xxx","disable":false,"defaultChecked":false}]}]'
5656
```
5757
**示例(JSON 文件)**
5858
```bash
@@ -104,7 +104,7 @@ node .claude/skills/yida-create-form-page/scripts/create-form-page.js update "AP
104104
```json
105105
[
106106
{ "type": "TextField", "label": "姓名", "required": true },
107-
{ "type": "SelectField", "label": "部门", "options": ["技术部", "产品部", "设计部"] },
107+
{ "type": "SelectField", "label": "部门", "dataSource": [{"text":{"zh_CN":"技术部","en_US":"技术部","type":"i18n"},"value":"技术部","sid":"serial_xxx","disable":false,"defaultChecked":false},{"text":{"zh_CN":"产品部","en_US":"产品部","type":"i18n"},"value":"产品部","sid":"serial_xxx","disable":false,"defaultChecked":false}] },
108108
{ "type": "DateField", "label": "入职日期" },
109109
{ "type": "NumberField", "label": "年龄" },
110110
{ "type": "TableField", "label": "费用明细", "children": [
@@ -125,7 +125,7 @@ node .claude/skills/yida-create-form-page/scripts/create-form-page.js update "AP
125125
| `behavior` | String || 字段行为,`NORMAL`(正常,默认)/ `READONLY`(只读)/ `HIDDEN`(隐藏) |
126126
| `visibility` | String[] || 显示端,`["PC", "MOBILE"]`(默认)/ `["PC"]`(仅 PC)/ `["MOBILE"]`(仅移动端) |
127127
| `labelAlign` | String || 标签对齐方式,`top`(默认)/ `left` / `right` |
128-
| `options` | String[] | 条件必填 | 选项列表,选项类字段必填 |
128+
| `dataSource` | Array | 条件必填 | 选项数据源数组,**选项类字段(RadioField/SelectField/CheckboxField/MultiSelectField)必填**。每个元素是选项对象,格式详见下方各字段类型说明 |
129129
| `multiple` | Boolean || 是否多选,`EmployeeField`/`DepartmentSelectField`/`CountrySelectField`/`AssociationFormField` 可用 |
130130
| `children` | Object[] | 条件必填 | 子字段列表,`TableField` 必填 |
131131
| `associationForm` | Object | 条件必填 | 关联表单配置对象,`AssociationFormField` 必填,详见下方说明 |
@@ -169,24 +169,70 @@ node .claude/skills/yida-create-form-page/scripts/create-form-page.js update "AP
169169
| --- | --- | --- |
170170
| `dataSourceType` | `"custom"` | 数据源类型 |
171171
| `valueType` | `"custom"` | 值类型 |
172+
| `dataSource` | 数组 | 选项数据源数组,每个元素是选项对象 |
173+
| `defaultDataSource` | 对象 | 默认数据源配置,包含 `options` 数组 |
172174

173-
**选项数据格式**`dataSource` `defaultDataSource.options` 中每个选项的结构如下,**`text.zh_CN``value` 必须是字符串,不能是对象**
175+
**`dataSource` 数组元素结构**
174176

175-
```json
176-
{
177-
"text": { "zh_CN": "选项一", "en_US": "选项一", "type": "i18n" },
178-
"value": "选项一",
179-
"sid": "serial_xxx",
180-
"disable": false,
181-
"defaultChecked": false
182-
}
183-
```
177+
| 属性 | 类型 | 必填 | 说明 |
178+
| --- | --- | --- | --- |
179+
| `text` | Object || 选项显示文本,i18n 对象格式 |
180+
| `text.zh_CN` | String || 中文显示文本,**必须是字符串** |
181+
| `text.en_US` | String || 英文显示文本,**必须是字符串** |
182+
| `text.type` | String || 固定为 `"i18n"` |
183+
| `value` | String || 选项值,**必须是字符串** |
184+
| `sid` | String || 选项唯一标识,格式为 `serial_xxx` |
185+
| `disable` | Boolean || 是否禁用,默认 `false` |
186+
| `defaultChecked` | Boolean || 是否默认选中,默认 `false` |
187+
188+
**`defaultDataSource` 对象结构**
189+
190+
| 属性 | 类型 | 说明 |
191+
| --- | --- | --- |
192+
| `complexType` | String | 固定为 `"custom"` |
193+
| `options` | Array | 选项数组,元素结构与 `dataSource` 相同 |
194+
| `formula` | String | 公式配置,默认空字符串 |
195+
| `url` | String | 数据源 URL,默认空字符串 |
196+
| `searchConfig` | Object | 搜索配置 |
197+
| `searchConfig.type` | String | 请求类型,固定为 `"JSONP"` |
198+
| `searchConfig.url` | String | 请求 URL,默认空字符串 |
199+
| `searchConfig.beforeFetch` | String | 请求前处理脚本,默认空字符串 |
200+
| `searchConfig.afterFetch` | String | 请求后处理脚本,默认空字符串 |
201+
202+
**完整示例**
184203

185-
❌ 错误格式(`text.zh_CN``value` 不能是 `{ label, value }` 对象):
186204
```json
187205
{
188-
"text": { "zh_CN": { "label": "选项一", "value": "选项一" }, "en_US": "New Option", "type": "i18n" },
189-
"value": { "label": "选项一", "value": "选项一" }
206+
"dataSourceType": "custom",
207+
"dataSource": [
208+
{
209+
"text": { "zh_CN": "选项一", "en_US": "Option 1", "type": "i18n" },
210+
"value": "选项一",
211+
"sid": "serial_khe7yak4",
212+
"disable": false,
213+
"defaultChecked": false
214+
}
215+
],
216+
"defaultDataSource": {
217+
"complexType": "custom",
218+
"options": [
219+
{
220+
"text": { "zh_CN": "选项一", "en_US": "Option 1", "type": "i18n" },
221+
"value": "选项一",
222+
"sid": "serial_khe7yak4",
223+
"disable": false,
224+
"defaultChecked": false
225+
}
226+
],
227+
"formula": "",
228+
"url": "",
229+
"searchConfig": {
230+
"type": "JSONP",
231+
"url": "",
232+
"beforeFetch": "",
233+
"afterFetch": ""
234+
}
235+
}
190236
}
191237
```
192238

@@ -198,6 +244,30 @@ node .claude/skills/yida-create-form-page/scripts/create-form-page.js update "AP
198244
| `autoWidth` | `true` | 自动宽度 |
199245
| `filterLocal` | `true` | 本地过滤 |
200246
| `mode` | `"single"` / `"multiple"` | 选择模式 |
247+
| `dataSourceType` | `"custom"` | 数据源类型 |
248+
| `dataSource` | 数组 | 选项数据源数组,每个元素是选项对象 |
249+
| `defaultDataSource` | 对象 | 默认数据源配置,包含 `options` 数组 |
250+
251+
**选项数据格式**:与 RadioField/CheckboxField 完全一致,每个选项对象包含以下属性:
252+
253+
| 属性 | 类型 | 必填 | 说明 |
254+
| --- | --- | --- | --- |
255+
| `text` | Object || 选项显示文本,i18n 对象格式 |
256+
| `text.zh_CN` | String || 中文显示文本,**必须是字符串** |
257+
| `text.en_US` | String || 英文显示文本,**必须是字符串** |
258+
| `text.type` | String || 固定为 `"i18n"` |
259+
| `value` | String || 选项值,**必须是字符串** |
260+
| `sid` | String || 选项唯一标识,格式为 `serial_xxx` |
261+
| `disable` | Boolean || 是否禁用,默认 `false` |
262+
| `defaultChecked` | Boolean || 是否默认选中,默认 `false` |
263+
264+
**`defaultDataSource` 对象结构**
265+
- `complexType`: `"custom"`
266+
- `options`: 选项数组,元素结构与 `dataSource` 相同
267+
- `formula`: 公式配置,默认空字符串
268+
- `url`: 数据源 URL,默认空字符串
269+
- `searchConfig`: 搜索配置对象,包含 `type`(固定 `"JSONP"`)、`url``beforeFetch``afterFetch`
270+
201271

202272
#### DateField
203273

@@ -351,10 +421,10 @@ format 格式:
351421
```json
352422
[
353423
{ "action": "add", "field": { "type": "TextField", "label": "姓名", "required": true } },
354-
{ "action": "add", "field": { "type": "SelectField", "label": "部门", "options": ["技术部", "产品部"] }, "after": "姓名" },
424+
{ "action": "add", "field": { "type": "SelectField", "label": "部门", "dataSource": [{"text":{"zh_CN":"技术部","en_US":"技术部","type":"i18n"},"value":"技术部","sid":"serial_xxx","disable":false,"defaultChecked":false},{"text":{"zh_CN":"产品部","en_US":"产品部","type":"i18n"},"value":"产品部","sid":"serial_xxx","disable":false,"defaultChecked":false}] }, "after": "姓名" },
355425
{ "action": "delete", "label": "备注" },
356426
{ "action": "update", "label": "年龄", "changes": { "required": true, "placeholder": "请输入年龄" } },
357-
{ "action": "update", "label": "状态", "changes": { "label": "审批状态", "options": ["待审批", "已通过", "已拒绝"] } }
427+
{ "action": "update", "label": "状态", "changes": { "label": "审批状态", "dataSource": [{"text":{"zh_CN":"待审批","en_US":"待审批","type":"i18n"},"value":"待审批","sid":"serial_xxx","disable":false,"defaultChecked":false},{"text":{"zh_CN":"已通过","en_US":"已通过","type":"i18n"},"value":"已通过","sid":"serial_xxx","disable":false,"defaultChecked":false},{"text":{"zh_CN":"已拒绝","en_US":"已拒绝","type":"i18n"},"value":"已拒绝","sid":"serial_xxx","disable":false,"defaultChecked":false}] } }
358428
]
359429
```
360430

@@ -373,7 +443,7 @@ format 格式:
373443
| `label` | String | 修改字段标签 |
374444
| `required` | Boolean | 修改是否必填 |
375445
| `placeholder` | String | 修改占位提示 |
376-
| `options` | String[] | 修改选项列表(选项类字段:RadioField/SelectField/CheckboxField/MultiSelectField) |
446+
| `dataSource` | Array | 修改选项数据源(选项类字段:RadioField/SelectField/CheckboxField/MultiSelectField),每个元素是选项对象,格式见 [RadioField 选项格式](#radiofield--checkboxfield) |
377447
| `multiple` | Boolean | 修改是否多选(EmployeeField/DepartmentSelectField/CountrySelectField) |
378448
| `behavior` | String | 修改字段行为:`NORMAL` / `READONLY` / `HIDDEN` |
379449
| `visibility` | String[] | 修改显示端:`["PC", "MOBILE"]` / `["PC"]` / `["MOBILE"]` |

skills/yida-create-form-page/scripts/create-form-page.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,9 +1612,9 @@ function applyFieldChanges(component, changes) {
16121612
props.placeholder = i18n(changes.placeholder);
16131613
}
16141614

1615-
// ── 特殊处理:options(需要转换为 dataSource 格式
1616-
if (changes.options !== undefined && OPTION_FIELD_TYPES.indexOf(component.componentName) !== -1) {
1617-
var newDataSource = buildOptionDataSource(changes.options);
1615+
// ── 特殊处理:dataSource(选项类字段直接更新 dataSource)
1616+
if (changes.dataSource !== undefined && OPTION_FIELD_TYPES.indexOf(component.componentName) !== -1) {
1617+
var newDataSource = changes.dataSource;
16181618
props.dataSource = newDataSource;
16191619
if (props.defaultDataSource) {
16201620
props.defaultDataSource.options = newDataSource;

skills/yida-custom-page/SKILL.md

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ node publish.js <appType> <formUuid> <源文件路径>
122122
| 约束 | 说明 |
123123
| --- | --- |
124124
| **React 版本** | 必须兼容 **React 16**,禁止使用 Hooks(`useState``useEffect` 等) |
125-
| **单文件** | 所有代码写在一个文件中(如 `index.js`|
125+
| **单文件** | 所有代码写在一个文件中(如 `index.js`|
126+
| **三方包引入** | 禁止使用 `import/require` 语法,如需使用第三方库,必须通过 `this.utils.loadScript` 加载 CDN 脚本,参考 [yida-api.md](reference/yida-api.md) 的「工具类 API」章节。|
126127
| **函数导出格式** | 使用 `export function xxx() {}` 格式导出函数 |
127128
| **样式** | 所有 css 必须写在 renderJsx 的方法中,通过 style 的方式引入 |
128129
| **`this` 上下文** | 所有导出函数中的 `this` 指向宜搭页面的 React 类实例 |
@@ -131,7 +132,16 @@ node publish.js <appType> <formUuid> <源文件路径>
131132

132133
### 文件结构
133134

134-
一个完整的宜搭自定义页面源文件必须包含以下三个导出函数和状态管理模块:
135+
**一个完整的宜搭自定义页面源文件必须包含:**
136+
- `_customState` 变量
137+
- getCustomState 函数
138+
- setCustomState 函数
139+
- forceUpdate 函数
140+
- didMount 函数
141+
- didUnmount 函数
142+
- renderJsx 函数
143+
144+
以下是一个完整自定义页面示例,包含状态管理、生命周期钩子、渲染函数
135145

136146
```javascript
137147
// ============================================================
@@ -249,19 +259,7 @@ this.forceUpdate();
249259

250260
### 编码注意事项
251261

252-
1. **箭头函数捕获 this**:同 react 的 render 函数一样,在 `renderJsx` 内部定义的事件处理函数中,**必须使用箭头函数**自动捕获 `this`
253-
```javascript
254-
export function renderJsx() {
255-
// ✅ 正确:箭头函数自动捕获 this
256-
const handleSubmit = () => {
257-
this.setCustomState({ submitted: true });
258-
this.utils.toast({ title: '提交成功', type: 'success' });
259-
};
260-
return <button onClick={handleSubmit}>提交</button>;
261-
}
262-
```
263-
264-
2. **自定义方法必须用 `export function` 定义**:凡是需要在方法内部使用 `this`(包括 `this.utils.yida.*``this.setCustomState` 等)的自定义方法,**必须且只能**使用 `export function 方法名() {}` 的形式定义,调用时使用 `this.方法名()`**禁止**使用 `const fn = () => {}``const fn = function() {}` 等形式定义需要访问 `this` 的方法,这些形式无法被宜搭运行时正确绑定 `this`
262+
1. **自定义方法必须用 `export function` 定义**:凡是需要在方法内部使用 `this`(包括 `this.utils.yida.*``this.setCustomState` 等)的自定义方法,**必须且只能**使用 `export function 方法名() {}` 的形式定义,调用时使用 `this.方法名()`**禁止**使用 `const fn = () => {}``const fn = function() {}` 等形式定义需要访问 `this` 的方法,这些形式无法被宜搭运行时正确绑定 `this`
265263
```javascript
266264
// ✅ 正确:export function + this.方法名() 调用
267265
export function didMount() {
@@ -287,6 +285,17 @@ this.forceUpdate();
287285
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
288286
};
289287
```
288+
2. 事件绑定说明:**同 React 事件绑定中的 this 处理一样**:在 `renderJsx` 中绑定事件处理器时,**必须使用箭头函数**来捕获 `this`,事件的回调方法需要使用 export function 定义:
289+
290+
```javascript
291+
export function handleSubmit(e) {
292+
this.setCustomState({ submitted: true });
293+
this.utils.toast({ title: '提交成功', type: 'success' });
294+
};
295+
export function renderJsx() {
296+
return <button onClick={(e) => {this.handleSubmit(e)}}>提交</button>;
297+
}
298+
```
290299

291300
3. **输入法组合输入处理**:使用 `_isComposing` 标记配合 `compositionstart` / `compositionend` 事件,正确处理中文输入法的组合输入状态,避免输入过程中触发提交
292301
4. **定时器清理**:在 `didUnmount` 中必须清理所有通过 `setInterval` / `setTimeout` 创建的定时器,防止内存泄漏
@@ -297,7 +306,7 @@ this.forceUpdate();
297306
9. **输入框使用非受控组件**:在宜搭环境中,`<input>``value` 属性绑定状态后会触发重渲染导致输入异常。**正确做法**:使用 `defaultValue`,在 `onChange` 中更新 `_customState` 而不调用 `setCustomState`
298307
```javascript
299308
// ❌ 错误:受控组件,每次输入都触发重渲染导致无法输入
300-
<input value={userAnswer} onChange={function(e) { self.setCustomState({ userAnswer: e.target.value }); }} />
309+
<input value={userAnswer} onChange={function(e) { this.setCustomState({ userAnswer: e.target.value }); }} />
301310

302311
// ✅ 正确:非受控组件,仅静默更新状态,不触发重渲染
303312
<input id="my-input" defaultValue="" onChange={function(e) { _customState.userAnswer = e.target.value; }} />

0 commit comments

Comments
 (0)