hookehuyr

docs: 为LangChain Agent添加完整的表单配置文档集

添加详细的文档集,为LangChain Agent生成表单配置提供完整的参考指南。包括:
- 数据结构总览:核心FormField、ComponentProps、Option等结构定义
- 组件类型参考:30+种表单组件的详细说明和配置要点
- 配置规范:component_props的完整属性规范和数据提交格式
- LangChain Agent使用指南:自然语言到组件的映射、必填识别、选项提取规则
- 示例数据:20+个真实配置示例和完整表单模板
- 规则控制系统:字段显示/隐藏规则的详细实现机制
- API接口文档:表单管理和数据操作的所有接口说明
- 页面数据提取指南:从实际运行表单中提取配置的方法
1 +# 变更日志
2 +
3 +记录项目开发过程中的重要变更和完成任务。
4 +
5 +## 2026-03-16
6 +
7 +### 16:54:40 - 完成任务
8 +
9 +**影响文件**:
10 +- `CLAUDE.md`
11 +
12 +**变更摘要**:
13 +- 无详细描述
1 +# 数据结构总览
2 +
3 +## 核心数据结构
4 +
5 +### 1. 表单字段结构 (FormField)
6 +
7 +每个表单字段都是一个对象,包含以下核心属性:
8 +
9 +```typescript
10 +interface FormField {
11 + // 字段标识
12 + key: string // 字段唯一标识,如 "field_1"
13 + name: string // 表单验证用的名称,通常等于 key
14 +
15 + // 字段值
16 + value: any // 字段当前值
17 + default?: any // 默认值
18 +
19 + // 组件相关
20 + component: Component // Vue 组件引用
21 + component_props: ComponentProps // 组件属性配置
22 +
23 + // 验证相关
24 + required?: boolean // 是否必填
25 + rules?: ValidationRule[] // 验证规则
26 + type?: string // 表单输入类型(text/textarea等)
27 +
28 + // 其他
29 + index?: number // 字段排序索引
30 +}
31 +```
32 +
33 +### 2. 组件属性结构 (ComponentProps)
34 +
35 +```typescript
36 +interface ComponentProps {
37 + // 基础属性
38 + tag: string // 组件类型标识(如 "input", "radio")
39 + name: string // 组件名称
40 + label: string // 字段标签(显示名称)
41 + placeholder?: string // 占位符文本
42 + default?: any // 默认值
43 +
44 + // 显示控制
45 + disabled?: boolean // 是否隐藏
46 + disabled_show?: boolean // 隐藏时是否显示图标
47 + readonly?: boolean // 是否只读
48 + required?: boolean // 是否必填
49 +
50 + // 选项类组件
51 + options?: Option[] // 选项列表(radio/checkbox/select)
52 +
53 + // 样式相关
54 + align?: string // 文本对齐方式(left/center/right)
55 + direction?: string // 排列方向(vertical/horizontal)
56 + background_color?: string // 背景色
57 +
58 + // 高级功能
59 + note?: string // 提示文本(支持HTML)
60 + is_camera_scan?: boolean // 是否启用微信扫一扫
61 + camera_scan_type?: string // 扫码类型(ALL_CODE/QR_CODE/BAR_CODE)
62 +
63 + // 图片上传
64 + max_count?: number // 最大上传数量
65 + max_size?: number // 最大文件大小(MB)
66 +
67 + // 评分
68 + x_score?: number // 分数值
69 +
70 + // 分组相关
71 + is_field_group?: boolean // 是否是集合组
72 + group_field_name?: string // 所属组字段名
73 +}
74 +```
75 +
76 +### 3. 选项结构 (Option)
77 +
78 +用于 Radio、Checkbox、Picker 等组件:
79 +
80 +```typescript
81 +interface Option {
82 + // 基础
83 + title: string // 选项显示文本
84 + value: any // 选项值
85 + checked?: boolean // 是否默认选中
86 +
87 + // 样式
88 + color?: string // 选项颜色
89 + background_color?: string // 背景色
90 +
91 + // 状态
92 + disabled?: boolean // 是否禁用
93 +
94 + // 描述信息
95 + desc_text?: string // 描述文本
96 + desc_type?: string // 描述类型(text/url)
97 + desc_url?: string // 描述链接URL
98 + desc_btn_name?: string // 描述按钮名称
99 +
100 + // 补充信息
101 + is_input?: boolean // 是否需要输入补充信息
102 + input_required?: boolean // 补充信息是否必填
103 + input_placeholder?: string // 补充信息占位符
104 + affix?: string // 补充信息值
105 +}
106 +```
107 +
108 +### 4. API 响应结构
109 +
110 +```typescript
111 +interface APIResponse<T = any> {
112 + code: number // 状态码:1=成功,其他=失败
113 + msg: string // 响应消息
114 + data: T // 响应数据
115 + show?: boolean // 是否显示错误提示
116 +}
117 +```
118 +
119 +## 数据流转
120 +
121 +### 1. 表单渲染流程
122 +
123 +```
124 +后端API → 表单配置数据 → createComponentType() → 动态组件渲染
125 +```
126 +
127 +### 2. 表单提交流程
128 +
129 +```
130 +用户输入 → 组件v-model → item.value → addFormDataAPI() → 后端保存
131 +```
132 +
133 +## 关键概念
134 +
135 +### 1. 动态组件系统
136 +
137 +项目使用 `component_props.tag` 属性决定渲染哪种组件:
138 +- 系统通过 `src/hooks/useComponentType.js` 中的 `createComponentType()` 函数
139 +- 根据 `tag` 值动态分配对应的 Vue 组件
140 +- 支持 30+ 种组件类型
141 +
142 +### 2. 验证系统
143 +
144 +- 基于 Vant Form 的验证系统
145 +- 通过 `rules` 数组配置验证规则
146 +- 必填项自动添加 `{ required: true, message: 'xxx' }`
147 +
148 +### 3. 规则系统
149 +
150 +- 字段可以通过规则控制其他字段的显示/隐藏
151 +- 通过 `emit("active")` 触发规则检查
152 +- 规则组件:`RuleField``MultiRuleField`
153 +
154 +### 4. Cookie 保存
155 +
156 +- 未完成表单自动保存到 Cookie
157 +- 格式:`{ form_code: { field_key: value } }`
158 +- 过期时间:1 天
159 +
160 +### 5. 周期选择
161 +
162 +- 某些表单支持周期选择
163 +- 通过 API `/srv/?a=cycle_list` 获取可用周期
164 +- 周期选择后 URL 会携带 `x_cycle` 参数
1 +# 表单字段组件类型
2 +
3 +## 支持的组件类型列表
4 +
5 +项目支持 **30+ 种** 动态表单字段组件,通过 `component_props.tag` 属性指定。
6 +
7 +## 文本输入类
8 +
9 +### 1. input - 单行文本输入
10 +- **组件**: `TextField`
11 +- **用途**: 短文本输入,如姓名、标题等
12 +- **特殊功能**: 微信扫一扫(`is_camera_scan`
13 +
14 +### 2. textarea - 多行文本输入
15 +- **组件**: `TextareaField`
16 +- **用途**: 长文本输入,如备注、描述等
17 +- **特性**: 自动高度(`autosize`
18 +
19 +### 3. number - 数字输入
20 +- **组件**: `NumberField`
21 +- **用途**: 数字类型输入,如年龄、数量等
22 +
23 +## 选择类
24 +
25 +### 4. radio - 单项选择
26 +- **组件**: `RadioField`
27 +- **用途**: 单选选项,如性别、是否等
28 +- **数据结构**: `options` 数组
29 +- **特殊功能**:
30 + - 选项颜色(`color`, `background_color`
31 + - 补充信息输入(`is_input`
32 + - 描述信息(`desc_text`, `desc_url`
33 +
34 +### 5. checkbox - 多项选择
35 +- **组件**: `CheckboxField`
36 +- **用途**: 多选选项,如爱好、标签等
37 +- **数据结构**: `options` 数组
38 +- **排列方向**: `direction` (vertical/horizontal)
39 +
40 +### 6. select - 单列选择器
41 +- **组件**: `PickerField`
42 +- **用途**: 下拉单选
43 +- **数据结构**: `options` 数组
44 +- **返回值**: 选中的 `value`
45 +
46 +## 日期时间类
47 +
48 +### 7. date - 日期选择器
49 +- **组件**: `DatePickerField`
50 +- **用途**: 选择日期
51 +
52 +### 8. time - 时间选择器
53 +- **组件**: `TimePickerField`
54 +- **用途**: 选择时间
55 +
56 +### 9. datetime - 日期时间选择器
57 +- **组件**: `DateTimePickerField`
58 +- **用途**: 选择日期和时间
59 +
60 +### 10. calendar - 日历选择器
61 +- **组件**: `CalendarField`
62 +- **用途**: 日历形式选择日期
63 +
64 +## 上传类
65 +
66 +### 11. image_uploader - 图片上传
67 +- **组件**: `ImageUploaderField`
68 +- **用途**: 上传图片到七牛云
69 +- **配置**:
70 + - `max_count`: 最大数量
71 + - `max_size`: 最大文件大小(MB)
72 +- **返回值**: `[{ url, name }]`
73 +
74 +### 12. file_uploader - 文件上传
75 +- **组件**: `FileUploaderField`
76 +- **用途**: 上传普通文件
77 +
78 +## 特殊输入类
79 +
80 +### 13. phone - 手机号输入
81 +- **组件**: `PhoneField`
82 +- **用途**: 手机号码输入和验证
83 +- **验证**: 11位手机号格式
84 +
85 +### 14. email - 邮箱输入
86 +- **组件**: `EmailField`
87 +- **用途**: 邮箱地址输入和验证
88 +
89 +### 15. id_card - 身份证输入
90 +- **组件**: `IdentityField`
91 +- **用途**: 身份证号码输入和验证
92 +
93 +### 16. name - 姓名输入
94 +- **组件**: `NameField`
95 +- **用途**: 中文姓名输入
96 +
97 +### 17. gender - 性别选择
98 +- **组件**: `GenderField`
99 +- **用途**: 性别选择(男/女)
100 +
101 +## 位置类
102 +
103 +### 18. address - 地址选择器
104 +- **组件**: `AreaPickerField`
105 +- **用途**: 省/市/区三级联动选择
106 +- **数据**: 使用 `@vant/area-data`
107 +
108 +## 展示类
109 +
110 +### 19. desc - 文字描述
111 +- **组件**: `DesField`
112 +- **用途**: 纯文字展示,不支持输入
113 +
114 +### 20. divider - 分割线
115 +- **组件**: `DividerField`
116 +- **用途**: 视觉分割线
117 +
118 +### 21. note - 富文本控件
119 +- **组件**: `NoteField`
120 +- **用途**: 富文本内容展示
121 +
122 +### 22. marquee - 跑马灯
123 +- **组件**: `MarqueeField`
124 +- **用途**: 滚动公告文字
125 +
126 +## 特殊功能类
127 +
128 +### 23. sign - 电子签名
129 +- **组件**: `SignField`
130 +- **用途**: 手写签名板
131 +- **依赖**: `vue-esign`
132 +
133 +### 24. rate - 评分选择器
134 +- **组件**: `RatePickerField`
135 +- **用途**: 星级评分
136 +
137 +### 25. appointment - 预约控件
138 +- **组件**: `AppointmentField`
139 +- **用途**: 时间段预约选择
140 +- **数据结构**: 复杂的日期+时间段结构
141 +
142 +### 26. rule - 活动规则控件
143 +- **组件**: `RuleField`
144 +- **用途**: 显示规则说明,支持弹窗
145 +
146 +### 27. multi_rule - 活动规则控件(多选)
147 +- **组件**: `MultiRuleField`
148 +- **用途**: 多选规则选项
149 +
150 +### 28. contact - 联系人控件
151 +- **组件**: `ContactField`
152 +- **用途**: 联系人信息输入
153 +
154 +### 29. button - 按钮控件
155 +- **组件**: `ButtonField`
156 +- **用途**: 自定义按钮
157 +
158 +### 30. custom - 自定义控件
159 +- **组件**: `CustomField`
160 +- **用途**: 完全自定义的组件
161 +
162 +## 高级类
163 +
164 +### 31. group - 组集合输入控件
165 +- **组件**: `GroupField`
166 +- **用途**: 将多个字段分组显示
167 +- **配置**: `field_groups` 数组
168 +
169 +### 32. org_picker - 树形选择控件
170 +- **组件**: `OrgPickerField`
171 +- **用途**: 组织架构树形选择
172 +
173 +### 33. volunteer_group - 义工组别选择
174 +- **组件**: `VolunteerGroupField`
175 +- **用途**: 义工组别单选
176 +
177 +### 34. table_editor - 表格编辑控件
178 +- **组件**: `TableField`
179 +- **用途**: 动态表格编辑,支持增删行
180 +
181 +### 35. video - 视频控件
182 +- **组件**: `VideoField`
183 +- **状态**: 已注释,暂未使用
184 +
185 +## 组件注册位置
186 +
187 +所有组件在 `src/hooks/useComponentType.js` 中注册:
188 +
189 +```javascript
190 +export function createComponentType(data) {
191 + _.each(data, (item, index) => {
192 + // 统一名称
193 + item.name = item.key
194 +
195 + // 根据 tag 分配组件
196 + if (item.component_props.tag === 'input') {
197 + item.component = TextField;
198 + }
199 + if (item.component_props.tag === 'radio') {
200 + item.component = RadioField;
201 + }
202 + // ... 更多组件
203 + })
204 +}
205 +```
206 +
207 +## 新增组件流程
208 +
209 +1.`src/components/` 下创建组件目录
210 +2. 创建 `index.vue` 组件文件
211 +3.`src/hooks/useComponentType.js` 添加 if 判断分支
212 +4. 组件必须支持以下 props:
213 + - `item`: Object (包含 component_props 和 value)
214 + - 必须通过 emit 发送 "active" 事件
1 +# component_props 配置规范
2 +
3 +## 通用属性
4 +
5 +所有组件类型都支持的属性:
6 +
7 +| 属性 | 类型 | 必填 | 默认值 | 说明 |
8 +|------|------|------|--------|------|
9 +| `tag` | string | ✅ | - | 组件类型标识,决定渲染哪个组件 |
10 +| `name` | string | ✅ | - | 组件名称 |
11 +| `label` | string | ✅ | - | 字段标签(显示名称) |
12 +| `default` | any | ❌ | - | 默认值 |
13 +| `placeholder` | string | ❌ | "请输入" | 占位符文本 |
14 +| `required` | boolean | ❌ | false | 是否必填 |
15 +| `disabled` | boolean | ❌ | false | 是否隐藏字段 |
16 +| `disabled_show` | boolean | ❌ | false | 隐藏时是否显示闭眼图标 |
17 +| `readonly` | boolean | ❌ | false | 是否只读 |
18 +| `note` | string | ❌ | - | 提示文本(支持HTML) |
19 +
20 +## 选项类组件属性
21 +
22 +Radio、Checkbox、Picker 等组件额外支持:
23 +
24 +| 属性 | 类型 | 说明 |
25 +|------|------|------|
26 +| `options` | Option[] | 选项列表数组 |
27 +| `direction` | string | 排列方向:`vertical`(垂直)/ `horizontal`(水平) |
28 +| `align` | string | 文本对齐:`left` / `center` / `right` |
29 +
30 +### Option 结构
31 +
32 +```typescript
33 +interface Option {
34 + title: string // 显示文本
35 + value: any // 选项值
36 + checked?: boolean // 默认选中
37 + disabled?: boolean // 禁用状态
38 + color?: string // 选项颜色
39 + background_color?: string // 背景色
40 +
41 + // 描述信息
42 + desc_text?: string // 描述文本
43 + desc_type?: string // "text" 或 "url"
44 + desc_url?: string // 描述链接
45 + desc_btn_name?: string // 描述按钮名称
46 +
47 + // 补充信息
48 + is_input?: boolean // 是否需要补充信息输入
49 + input_required?: boolean // 补充信息是否必填
50 + input_placeholder?: string // 补充信息占位符
51 + affix?: string // 补充信息值
52 +}
53 +```
54 +
55 +## 文本输入类属性
56 +
57 +### input / textarea / number
58 +
59 +| 属性 | 类型 | 说明 |
60 +|------|------|------|
61 +| `align` | string | 文本对齐方式 |
62 +| `is_camera_scan` | boolean | 是否启用微信扫一扫 |
63 +| `camera_scan_type` | string | 扫码类型:`ALL_CODE` / `QR_CODE` / `BAR_CODE` |
64 +| `is_edit_camera_scan_result` | boolean | 是否允许编辑扫码结果 |
65 +
66 +## 上传类属性
67 +
68 +### image_uploader
69 +
70 +| 属性 | 类型 | 默认值 | 说明 |
71 +|------|------|--------|------|
72 +| `max_count` | number | 9 | 最大上传数量 |
73 +| `max_size` | number | 10 | 最大文件大小(MB) |
74 +| `image_type` | string | "jpg/..." | 允许的图片类型 |
75 +
76 +**返回值结构**:
77 +```javascript
78 +{
79 + key: "image_uploader",
80 + filed_name: "field_1",
81 + value: [
82 + { url: "https://...", name: "image.jpg" }
83 + ]
84 +}
85 +```
86 +
87 +### file_uploader
88 +
89 +`image_uploader` 类似,用于普通文件上传。
90 +
91 +## 日期时间类属性
92 +
93 +### date / time / datetime / calendar
94 +
95 +| 属性 | 类型 | 说明 |
96 +|------|------|------|
97 +| `min_date` | Date/Date[] | 最小可选日期 |
98 +| `max_date` | Date/Date[] | 最大可选日期 |
99 +| `formatter` | Function | 格式化函数 |
100 +
101 +## 预约控件属性
102 +
103 +### appointment
104 +
105 +**复杂配置结构**:
106 +
107 +```javascript
108 +{
109 + tag: "appointment",
110 + label: "预约时间",
111 + appointment_title: "选择入寺时间",
112 + options: [
113 + {
114 + title: "03月27日", // 日期标题
115 + placeholder: "剩余余量 9878", // 提示信息
116 + disabled: false, // 是否禁用该日期
117 + columns: [ // 时间段列表
118 + {
119 + checked: false,
120 + disabled: false,
121 + value: "1",
122 + text: "16:00-17:30 余1399"
123 + }
124 + ]
125 + }
126 + ]
127 +}
128 +```
129 +
130 +## 规则控件属性
131 +
132 +### rule / multi_rule
133 +
134 +| 属性 | 类型 | 说明 |
135 +|------|------|------|
136 +| `desc_text` | string | 规则内容(支持 `\n` 换行) |
137 +| `desc_type` | string | `text`(弹窗)或 `url`(跳转) |
138 +| `desc_url` | string | 规则详情链接 |
139 +| `desc_btn_name` | string | 按钮名称,如"查看详情" |
140 +
141 +## 组集合控件属性
142 +
143 +### group
144 +
145 +```javascript
146 +{
147 + tag: "group",
148 + label: "物品详情",
149 + field_groups: [
150 + {
151 + tag: "input",
152 + label: "物品描述",
153 + field_name: "field_10",
154 + group_field_name: "field_4", // 所属组字段
155 + // ... 其他属性
156 + }
157 + ]
158 +}
159 +```
160 +
161 +## 评分控件属性
162 +
163 +### rate
164 +
165 +| 属性 | 类型 | 说明 |
166 +|------|------|------|
167 +| `x_score` | number | 显示分值(会显示在标签旁) |
168 +
169 +## 表格控件属性
170 +
171 +### table_editor
172 +
173 +| 属性 | 类型 | 说明 |
174 +|------|------|------|
175 +| `default` | string/html | 默认表格内容(HTML格式) |
176 +
177 +## 特殊功能属性
178 +
179 +### 微信扫码
180 +
181 +`input` 类型支持:
182 +
183 +```javascript
184 +{
185 + tag: "input",
186 + is_camera_scan: true,
187 + camera_scan_type: "ALL_CODE" // ALL_CODE | QR_CODE | BAR_CODE
188 +}
189 +```
190 +
191 +### 补充信息
192 +
193 +Radio、Checkbox 支持选择后输入补充信息:
194 +
195 +```javascript
196 +options: [
197 + {
198 + title: "其他",
199 + value: "other",
200 + is_input: true, // 启用补充输入
201 + input_required: true, // 补充信息必填
202 + input_placeholder: "请输入补充信息",
203 + affix: "" // 补充信息值
204 + }
205 +]
206 +```
207 +
208 +### 显示隐藏控制
209 +
210 +通过规则动态控制字段显示/隐藏:
211 +
212 +```javascript
213 +{
214 + tag: "input",
215 + disabled: false // true=隐藏,false=显示
216 +}
217 +```
218 +
219 +## 样式属性
220 +
221 +### 对齐方式
222 +
223 +```javascript
224 +{
225 + align: "left" // left | center | right
226 +}
227 +```
228 +
229 +### 颜色
230 +
231 +选项类组件支持单独配置颜色:
232 +
233 +```javascript
234 +options: [
235 + {
236 + title: "重要",
237 + value: "important",
238 + color: "#ff0000", // 文字颜色
239 + background_color: "#fff0f0" // 背景色
240 + }
241 +]
242 +```
243 +
244 +## 数据提交格式
245 +
246 +### 普通字段
247 +
248 +```javascript
249 +{
250 + key: "field_1",
251 + value: "用户输入的值"
252 +}
253 +```
254 +
255 +### 选项类字段(Radio)
256 +
257 +```javascript
258 +{
259 + key: "field_1",
260 + value: "选项值",
261 + affix: "补充信息: xxx", // 如果有补充信息
262 + type: "radio"
263 +}
264 +```
265 +
266 +### 选项类字段(Checkbox)
267 +
268 +```javascript
269 +{
270 + key: "field_1",
271 + value: ["值1", "值2"], // 数组
272 + type: "checkbox"
273 +}
274 +```
275 +
276 +### 图片上传
277 +
278 +```javascript
279 +{
280 + key: "field_1",
281 + filed_name: "field_1",
282 + value: [
283 + { url: "https://...", name: "image.jpg" }
284 + ],
285 + type: "image_uploader"
286 +}
287 +```
1 +# API 接口文档
2 +
3 +## 基础配置
4 +
5 +### 请求基础配置
6 +
7 +所有请求默认携带参数:
8 +```javascript
9 +{
10 + f: 'custom_form' // 固定参数
11 +}
12 +```
13 +
14 +### 响应格式
15 +
16 +```typescript
17 +interface APIResponse<T = any> {
18 + code: number // 1=成功,其他=失败
19 + msg: string // 响应消息
20 + data: T // 响应数据
21 + show?: boolean // 是否显示错误提示(code !== 1 时)
22 +}
23 +```
24 +
25 +### 请求封装
26 +
27 +```javascript
28 +import { fn, fetch } from '@/api/fn';
29 +
30 +// GET 请求
31 +fn(fetch.get('/srv/?a=xxx', params))
32 +
33 +// POST 请求
34 +fn(fetch.post('/srv/?a=xxx', params))
35 +```
36 +
37 +## 表单管理接口
38 +
39 +### 1. 新增表单
40 +
41 +**接口**: `/srv/?a=add_form`
42 +**方法**: POST
43 +**参数**:
44 +```javascript
45 +{
46 + client_id: string, // 主体客户id
47 + name: string, // 表单名称
48 + note: string // 表单描述
49 +}
50 +```
51 +
52 +### 2. 查询表单配置(字段和规则)
53 +
54 +**接口**: `/srv/?a=query_form_all_field`
55 +**方法**: GET
56 +**参数**:
57 +```javascript
58 +{
59 + f: "custom_form", // 固定参数
60 + form_code: string, // 表单唯一标识(必填)
61 + page_type: string, // 页面类型:add/edit/view(可选)
62 + data_id: string, // 数据ID(编辑时使用)
63 + flow_node_code: string // 流程节点代码(可选)
64 +}
65 +```
66 +
67 +**返回**:
68 +```javascript
69 +{
70 + code: 1,
71 + msg: "OK",
72 + data: {
73 + id: 835950,
74 + code: "nxqkbf",
75 + name: "表单名称",
76 + table_name: "customize_35697_nxqkbf",
77 + max_field_num: 3,
78 + field_list: [
79 + {
80 + tag: "input", // 组件类型
81 + field_name: "field_1", // 字段名
82 + label: "姓名", // 显示标签
83 + required: true, // 是否必填
84 + data_type: "text", // 数据类型
85 + placeholder: "请输入",
86 + disabled: false,
87 + readonly: false
88 + }
89 + ],
90 + rule_list: [ // 字段显示/隐藏规则
91 + {
92 + field_names: ["field_2"],
93 + mode: "SHOW",
94 + logical_op: "OR",
95 + expr_list: [...]
96 + }
97 + ],
98 + flow_process_list: null // 工作流流程列表
99 + }
100 +}
101 +```
102 +
103 +**调用示例**:
104 +```bash
105 +curl "https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf"
106 +```
107 +
108 +---
109 +
110 +### 2.1. 查询表单设置(表单级配置)
111 +
112 +**接口**: `/srv/?a=query_form_setting`
113 +**方法**: GET
114 +**参数**:
115 +```javascript
116 +{
117 + f: "custom_form", // 固定参数
118 + form_code: string // 表单唯一标识(必填)
119 +}
120 +```
121 +
122 +**返回**:
123 +```javascript
124 +{
125 + code: 1,
126 + msg: "OK",
127 + data: [{
128 + id: 835950,
129 + code: "nxqkbf",
130 + name: "表单名称",
131 + max_field_num: 3, // 最大字段数
132 + data_count: 7, // 已提交数据条数
133 + extend: {
134 + server_time: "2026-03-16 17:15:40",
135 + is_back_user: false // 是否后台用户
136 + },
137 + property_list: {
138 + sjsj_enable: 1, // 是否启用数据收集
139 + commit_action: "text", // 提交动作类型
140 + commit_text_type: "default",
141 + commit_text_template: "提交成功", // 提交文本模板
142 + edit_commit_action: "text",
143 + edit_commit_text_type: "default",
144 + edit_commit_text_template: "提交成功"
145 + }
146 + }]
147 +}
148 +```
149 +
150 +**调用示例**:
151 +```bash
152 +curl "https://oa-dev.onwall.cn/srv/?a=query_form_setting&f=custom_form&form_code=nxqkbf"
153 +```
154 +
155 +---
156 +
157 +### 2.2. 两个接口的区别
158 +
159 +| 接口 | 获取内容 | 用途 |
160 +|------|---------|------|
161 +| `query_form_all_field` | 字段列表、规则列表 | 渲染表单结构 |
162 +| `query_form_setting` | 表单配置、提交设置 | 获取表单级属性 |
163 +
164 +### 3. 添加表单字段
165 +
166 +**接口**: `/srv/?a=add_form_field`
167 +**方法**: POST
168 +**参数**:
169 +```javascript
170 +{
171 + form_code: string, // 表单唯一标识
172 + component_code: string // 组件标识
173 +}
174 +```
175 +
176 +### 4. 添加表单字段属性设置
177 +
178 +**接口**: `/srv/?a=add_form_setting`
179 +**方法**: POST
180 +**参数**:
181 +```javascript
182 +{
183 + form_code: string, // 表单唯一标识
184 + field_name: string, // 表单字段名(表单级属性可为空)
185 + component_code: string, // 组件标识
186 + property_code: string, // 属性标识
187 + setting_value: string // 属性值(JSON数组字符串)
188 +}
189 +```
190 +
191 +### 5. 查询表单设置
192 +
193 +**接口**: `/srv/?a=query_form_setting`
194 +**方法**: GET
195 +**参数**:
196 +```javascript
197 +{
198 + form_code: string // 表单唯一标识
199 +}
200 +```
201 +
202 +**返回**:
203 +```javascript
204 +{
205 + enable: number, // 0=停止表单,1=开启表单
206 + is_time_range: number, // 0=不设定,1=设定
207 + is_count_down: number, // 0=不显示,1=显示
208 + begin_time: string, // 开启时间
209 + end_time: string // 停止时间
210 +}
211 +```
212 +
213 +### 6. 验证便当密码
214 +
215 +**接口**: `/srv/?a=verify_password`
216 +**方法**: POST
217 +**参数**:
218 +```javascript
219 +{
220 + form_code: string, // 表单唯一标识
221 + mmtx_password: string // 用户输入的密码
222 +}
223 +```
224 +
225 +### 7. 义工组长查询义工信息
226 +
227 +**接口**: `/srv/?a=volunteer_source_leader`
228 +**方法**: GET
229 +**参数**:
230 +```javascript
231 +{
232 + form_code: string, // 表单唯一标识
233 + page_type: string, // 页面类型
234 + force_back: string, // 是否强制后台账号模式检查
235 + volunteer_source: string, // 报名渠道:self/leader
236 + volunteer_phone: string // 义工手机号
237 +}
238 +```
239 +
240 +## 数据管理接口
241 +
242 +### 1. 添加表单数据
243 +
244 +**接口**: `/srv/?a=add_formdata`
245 +**方法**: POST
246 +**参数**:
247 +```javascript
248 +{
249 + form_code: string, // 表单唯一标识
250 + data: object // 待添加的数据(JSON对象)
251 +}
252 +```
253 +
254 +**示例**:
255 +```javascript
256 +{
257 + form_code: "form_001",
258 + data: {
259 + field_1: "张三",
260 + field_2: "13800138000",
261 + field_3: ["选项1", "选项2"]
262 + }
263 +}
264 +```
265 +
266 +### 2. 查询表单数据
267 +
268 +**接口**: `/srv/?a=query_formdata`
269 +**方法**: GET
270 +**参数**:
271 +```javascript
272 +{
273 + form_code: string, // 表单唯一标识
274 + id: string // 数据ID
275 +}
276 +```
277 +
278 +### 3. 修改表单数据
279 +
280 +**接口**: `/srv/?a=modi_formdata`
281 +**方法**: POST
282 +**参数**:
283 +```javascript
284 +{
285 + form_code: string, // 表单唯一标识
286 + id: string, // 数据ID
287 + data: object // 待修改的数据(JSON对象)
288 +}
289 +```
290 +
291 +### 4. 流程表单数据
292 +
293 +**接口**: `/srv/?a=flow_formdata`
294 +**方法**: POST
295 +**参数**:
296 +```javascript
297 +{
298 + form_code: string, // 表单唯一标识
299 + data_id: string, // 数据ID
300 + data: object, // 表单数据
301 + flow_node_code: string, // 流程节点
302 + flow_node_action_id: string, // 流程节点按钮ID
303 + flow_content: string // 流程审批意见
304 +}
305 +```
306 +
307 +## 组件接口
308 +
309 +### 1. 查询组件
310 +
311 +**接口**: `/srv/?a=query_component`
312 +**方法**: GET
313 +**参数**:
314 +```javascript
315 +{
316 + group_code: string, // 分组标识
317 + component_code: string, // 组件标识
318 + name: string // 组件名称(模糊查询)
319 +}
320 +```
321 +
322 +### 2. 查询部门列表
323 +
324 +**接口**: `/srv/?a=flow_setting&t=flow_dept_list`
325 +**方法**: GET
326 +**参数**:
327 +```javascript
328 +{
329 + form_code: string // 表单code
330 +}
331 +```
332 +
333 +**请求头建议**:
334 +```javascript
335 +{
336 + headers: {
337 + "only-data": true, // 去重
338 + "keep-data": true // 缓存
339 + }
340 +}
341 +```
342 +
343 +### 3. 查询角色列表
344 +
345 +**接口**: `/srv/?a=flow_setting&t=flow_role_list`
346 +**方法**: GET
347 +**参数**:
348 +```javascript
349 +{
350 + form_code: string // 表单code
351 +}
352 +```
353 +
354 +### 4. 搜索用户部门角色
355 +
356 +**接口**: `/srv/?a=flow_setting&t=search_user_dept_role`
357 +**方法**: GET
358 +**参数**:
359 +```javascript
360 +{
361 + form_code: string, // 表单code
362 + word: string // 搜索内容
363 +}
364 +```
365 +
366 +## 通用接口
367 +
368 +### 1. 发送验证码
369 +
370 +**接口**: `/srv/?a=sms`
371 +**方法**: POST
372 +**参数**:
373 +```javascript
374 +{
375 + phone: string // 手机号码
376 +}
377 +```
378 +
379 +### 2. 获取七牛上传 Token
380 +
381 +**接口**: `/srv/?a=upload`
382 +**方法**: POST
383 +**参数**:
384 +```javascript
385 +qs.stringify({
386 + name: string, // 文件名
387 + hash: string // 文件哈希(ETag)
388 +})
389 +```
390 +
391 +### 3. 保存文件
392 +
393 +**接口**: `/srv/?a=upload&t=save_file`
394 +**方法**: POST
395 +**参数**:
396 +```javascript
397 +qs.stringify({
398 + format: string, // 图片格式
399 + hash: string, // 文件哈希
400 + height: number, // 图片高度
401 + width: number, // 图片宽度
402 + filekey: string // 七牛返回的文件key
403 +})
404 +```
405 +
406 +## 周期接口
407 +
408 +### 1. 获取周期列表
409 +
410 +**接口**: `/srv/?a=cycle_list`
411 +**方法**: GET
412 +**参数**:
413 +```javascript
414 +{
415 + form_code: string // 表单唯一标识
416 +}
417 +```
418 +
419 +**返回**:
420 +```javascript
421 +{
422 + is_cycle: boolean, // 是否需要周期选择
423 + cycle_list: [ // 周期列表
424 + {
425 + cycle_id: string,
426 + cycle_name: string,
427 + start_date: string,
428 + end_date: string
429 + }
430 + ]
431 +}
432 +```
433 +
434 +## HTTP 请求缓存控制
435 +
436 +### 请求去重(only-data)
437 +
438 +避免短时间内重复请求:
439 +
440 +```javascript
441 +fn(fetch.get('/api/xxx', params, {
442 + headers: { "only-data": true }
443 +}))
444 +```
445 +
446 +### 持久化缓存(keep-data)
447 +
448 +缓存请求结果(30秒,最多50条):
449 +
450 +```javascript
451 +fn(fetch.get('/api/xxx', params, {
452 + headers: { "keep-data": true }
453 +}))
454 +```
455 +
456 +### 同时使用
457 +
458 +```javascript
459 +fn(fetch.get('/api/xxx', params, {
460 + headers: {
461 + "only-data": true,
462 + "keep-data": true
463 + }
464 +}))
465 +```
466 +
467 +## 错误处理
468 +
469 +### 特殊错误码
470 +
471 +| 错误码 | 说明 | 处理 |
472 +|--------|------|------|
473 +| 402 | 未授权/登录失效 | 跳转登录页,show=false |
474 +| 其他 | 业务错误 | 显示错误提示 |
475 +
476 +### 402 处理示例
477 +
478 +```javascript
479 +if (response.data.code === 402) {
480 + response.data.show = false; // 不显示默认错误
481 + showToast({
482 + message: '登录失效!将跳转到登录页面',
483 + duration: 1500,
484 + onClose: () => {
485 + window.location.href = location.origin + '/admin/';
486 + }
487 + })
488 +}
489 +```
1 +# 示例数据
2 +
3 +## 完整表单配置示例
4 +
5 +### 示例 1:用户信息表单
6 +
7 +```javascript
8 +{
9 + key: "field_1",
10 + value: "",
11 + component_props: {
12 + tag: "input",
13 + name: "name",
14 + label: "姓名",
15 + placeholder: "请输入姓名",
16 + required: true,
17 + default: ""
18 + }
19 +}
20 +```
21 +
22 +### 示例 2:性别选择
23 +
24 +```javascript
25 +{
26 + key: "field_2",
27 + value: "",
28 + component_props: {
29 + tag: "gender",
30 + name: "gender",
31 + label: "性别",
32 + required: true,
33 + default: "",
34 + options: [
35 + {
36 + title: "男",
37 + value: "男",
38 + checked: false
39 + },
40 + {
41 + title: "女",
42 + value: "女",
43 + checked: false
44 + }
45 + ]
46 + }
47 +}
48 +```
49 +
50 +### 示例 3:手机号输入
51 +
52 +```javascript
53 +{
54 + key: "field_3",
55 + value: "",
56 + component_props: {
57 + tag: "phone",
58 + name: "phone",
59 + label: "手机号",
60 + placeholder: "请输入手机号",
61 + required: true,
62 + default: ""
63 + }
64 +}
65 +```
66 +
67 +### 示例 4:单项选择(带补充信息)
68 +
69 +```javascript
70 +{
71 + key: "field_4",
72 + value: "",
73 + component_props: {
74 + tag: "radio",
75 + name: "category",
76 + label: "报名类型",
77 + required: true,
78 + direction: "vertical",
79 + default: "",
80 + options: [
81 + {
82 + title: "个人报名",
83 + value: "personal",
84 + checked: true,
85 + color: "#1989fa"
86 + },
87 + {
88 + title: "团队报名",
89 + value: "team",
90 + checked: false,
91 + color: "#ff976a"
92 + },
93 + {
94 + title: "其他",
95 + value: "other",
96 + checked: false,
97 + is_input: true, // 启用补充输入
98 + input_required: true, // 补充信息必填
99 + input_placeholder: "请说明具体类型",
100 + desc_text: "请选择最符合的报名类型",
101 + desc_type: "text",
102 + desc_btn_name: "查看说明"
103 + }
104 + ]
105 + }
106 +}
107 +```
108 +
109 +### 示例 5:多项选择
110 +
111 +```javascript
112 +{
113 + key: "field_5",
114 + value: [],
115 + component_props: {
116 + tag: "checkbox",
117 + name: "hobbies",
118 + label: "兴趣爱好",
119 + required: false,
120 + direction: "vertical",
121 + options: [
122 + {
123 + title: "运动",
124 + value: "sport",
125 + checked: false,
126 + background_color: "#f0f9ff"
127 + },
128 + {
129 + title: "阅读",
130 + value: "reading",
131 + checked: false,
132 + background_color: "#fff0f0"
133 + },
134 + {
135 + title: "音乐",
136 + value: "music",
137 + checked: false,
138 + background_color: "#f0fff0"
139 + },
140 + {
141 + title: "旅游",
142 + value: "travel",
143 + checked: false,
144 + background_color: "#fffaf0"
145 + }
146 + ]
147 + }
148 +}
149 +```
150 +
151 +### 示例 6:下拉选择
152 +
153 +```javascript
154 +{
155 + key: "field_6",
156 + value: "",
157 + component_props: {
158 + tag: "select",
159 + name: "city",
160 + label: "所在城市",
161 + placeholder: "请选择城市",
162 + required: true,
163 + options: [
164 + { text: "北京", value: "beijing" },
165 + { text: "上海", value: "shanghai" },
166 + { text: "广州", value: "guangzhou" },
167 + { text: "深圳", value: "shenzhen" }
168 + ]
169 + }
170 +}
171 +```
172 +
173 +### 示例 7:地址选择
174 +
175 +```javascript
176 +{
177 + key: "field_7",
178 + value: "",
179 + component_props: {
180 + tag: "address",
181 + name: "address",
182 + label: "详细地址",
183 + placeholder: "请选择省市区",
184 + required: true
185 + }
186 +}
187 +```
188 +
189 +### 示例 8:日期选择
190 +
191 +```javascript
192 +{
193 + key: "field_8",
194 + value: "",
195 + component_props: {
196 + tag: "date",
197 + name: "birthday",
198 + label: "出生日期",
199 + placeholder: "请选择日期",
200 + required: true
201 + }
202 +}
203 +```
204 +
205 +### 示例 9:图片上传
206 +
207 +```javascript
208 +{
209 + key: "field_9",
210 + value: [],
211 + component_props: {
212 + tag: "image_uploader",
213 + name: "photos",
214 + label: "上传照片",
215 + required: true,
216 + max_count: 3, // 最多3张
217 + max_size: 5, // 最大5MB
218 + default: []
219 + }
220 +}
221 +```
222 +
223 +### 示例 10:多行文本
224 +
225 +```javascript
226 +{
227 + key: "field_10",
228 + value: "",
229 + component_props: {
230 + tag: "textarea",
231 + name: "description",
232 + label: "个人简介",
233 + placeholder: "请输入个人简介",
234 + required: false,
235 + default: ""
236 + }
237 +}
238 +```
239 +
240 +### 示例 11:电子签名
241 +
242 +```javascript
243 +{
244 + key: "field_11",
245 + value: "",
246 + component_props: {
247 + tag: "sign",
248 + name: "signature",
249 + label: "本人签名",
250 + required: true,
251 + default: ""
252 + }
253 +}
254 +```
255 +
256 +### 示例 12:评分
257 +
258 +```javascript
259 +{
260 + key: "field_12",
261 + value: "",
262 + component_props: {
263 + tag: "rate",
264 + name: "rating",
265 + label: "活动评分",
266 + required: true,
267 + x_score: 5, // 默认5分
268 + default: ""
269 + }
270 +}
271 +```
272 +
273 +### 示例 13:身份证号
274 +
275 +```javascript
276 +{
277 + key: "field_13",
278 + value: "",
279 + component_props: {
280 + tag: "id_card",
281 + name: "id_card",
282 + label: "身份证号",
283 + placeholder: "请输入身份证号",
284 + required: true
285 + }
286 +}
287 +```
288 +
289 +### 示例 14:邮箱
290 +
291 +```javascript
292 +{
293 + key: "field_14",
294 + value: "",
295 + component_props: {
296 + tag: "email",
297 + name: "email",
298 + label: "电子邮箱",
299 + placeholder: "请输入邮箱地址",
300 + required: false
301 + }
302 +}
303 +```
304 +
305 +### 示例 15:预约控件
306 +
307 +```javascript
308 +{
309 + key: "field_15",
310 + value: "",
311 + component_props: {
312 + tag: "appointment",
313 + name: "appointment_time",
314 + label: "预约时间",
315 + required: true,
316 + appointment_title: "选择预约时间段",
317 + options: [
318 + {
319 + title: "03月27日",
320 + placeholder: "剩余余量 98",
321 + disabled: false,
322 + columns: [
323 + {
324 + checked: false,
325 + disabled: false,
326 + value: "1",
327 + text: "09:00-10:30 余98"
328 + },
329 + {
330 + checked: false,
331 + disabled: false,
332 + value: "2",
333 + text: "14:00-15:30 余50"
334 + },
335 + {
336 + checked: false,
337 + disabled: true,
338 + value: "3",
339 + text: "16:00-17:30 余0"
340 + }
341 + ]
342 + },
343 + {
344 + title: "03月28日",
345 + placeholder: "剩余余量 120",
346 + disabled: false,
347 + columns: [
348 + {
349 + checked: false,
350 + disabled: false,
351 + value: "4",
352 + text: "09:00-10:30 余120"
353 + },
354 + {
355 + checked: false,
356 + disabled: false,
357 + value: "5",
358 + text: "14:00-15:30 余80"
359 + }
360 + ]
361 + }
362 + ]
363 + }
364 +}
365 +```
366 +
367 +### 示例 16:表格编辑
368 +
369 +```javascript
370 +{
371 + key: "field_16",
372 + value: "",
373 + component_props: {
374 + tag: "table_editor",
375 + name: "table_data",
376 + label: "物品清单",
377 + default: "<table><tr><th>名称</th><th>数量</th></tr><tr><td>物品A</td><td>10</td></tr></table>"
378 + }
379 +}
380 +```
381 +
382 +### 示例 17:集合组
383 +
384 +```javascript
385 +{
386 + key: "field_17",
387 + value: "",
388 + component_props: {
389 + tag: "group",
390 + name: "group_info",
391 + label: "物品信息",
392 + field_groups: [
393 + {
394 + tag: "input",
395 + name: "item_name",
396 + label: "物品名称",
397 + field_name: "field_18",
398 + group_field_name: "field_17",
399 + placeholder: "请输入物品名称",
400 + required: true
401 + },
402 + {
403 + tag: "number",
404 + name: "item_count",
405 + label: "数量",
406 + field_name: "field_19",
407 + group_field_name: "field_17",
408 + placeholder: "请输入数量",
409 + required: true
410 + }
411 + ]
412 + }
413 +}
414 +```
415 +
416 +### 示例 18:规则说明
417 +
418 +```javascript
419 +{
420 + key: "field_20",
421 + value: "",
422 + component_props: {
423 + tag: "rule",
424 + name: "rule_info",
425 + label: "活动规则",
426 + default: "",
427 + desc_text: "1. 活动时间:2024年3月1日-3月31日\n2. 参与资格:18岁以上\n3. 每人限参与一次",
428 + desc_type: "text",
429 + desc_btn_name: "查看完整规则"
430 + }
431 +}
432 +```
433 +
434 +### 示例 19:只读字段
435 +
436 +```javascript
437 +{
438 + key: "field_21",
439 + value: "系统自动生成",
440 + component_props: {
441 + tag: "input",
442 + name: "readonly_field",
443 + label: "申请编号",
444 + readonly: true, // 只读
445 + default: "系统自动生成"
446 + }
447 +}
448 +```
449 +
450 +### 示例 20:带提示的字段
451 +
452 +```javascript
453 +{
454 + key: "field_22",
455 + value: "",
456 + component_props: {
457 + tag: "input",
458 + name: "username",
459 + label: "用户名",
460 + placeholder: "请输入用户名",
461 + required: true,
462 + note: "用户名长度为4-16个字符,支持字母、数字、下划线"
463 + }
464 +}
465 +```
466 +
467 +### 示例 21:带扫码的输入框
468 +
469 +```javascript
470 +{
471 + key: "field_23",
472 + value: "",
473 + component_props: {
474 + tag: "input",
475 + name: "qr_code",
476 + label: "二维码",
477 + placeholder: "请输入或扫描二维码",
478 + required: true,
479 + is_camera_scan: true,
480 + camera_scan_type: "QR_CODE" // ALL_CODE | QR_CODE | BAR_CODE
481 + }
482 +}
483 +```
484 +
485 +### 示例 22:分割线
486 +
487 +```javascript
488 +{
489 + key: "field_24",
490 + value: "",
491 + component_props: {
492 + tag: "divider",
493 + name: "divider",
494 + label: "分割线"
495 + }
496 +}
497 +```
498 +
499 +### 示例 23:富文本说明
500 +
501 +```javascript
502 +{
503 + key: "field_25",
504 + value: "",
505 + component_props: {
506 + tag: "note",
507 + name: "rich_text",
508 + label: "活动详情",
509 + default: "<p>这是一段<strong>富文本</strong>内容</p>"
510 + }
511 +}
512 +```
513 +
514 +## 复杂表单完整示例
515 +
516 +```javascript
517 +const formConfig = [
518 + // 基本信息
519 + {
520 + key: "field_name",
521 + value: "",
522 + component_props: {
523 + tag: "name",
524 + label: "姓名",
525 + required: true,
526 + placeholder: "请输入姓名"
527 + }
528 + },
529 + {
530 + key: "field_gender",
531 + value: "",
532 + component_props: {
533 + tag: "gender",
534 + label: "性别",
535 + required: true
536 + }
537 + },
538 + {
539 + key: "field_phone",
540 + value: "",
541 + component_props: {
542 + tag: "phone",
543 + label: "手机号",
544 + required: true,
545 + placeholder: "请输入手机号"
546 + }
547 + },
548 +
549 + // 报名信息
550 + {
551 + key: "field_type",
552 + value: "",
553 + component_props: {
554 + tag: "radio",
555 + label: "报名类型",
556 + required: true,
557 + direction: "vertical",
558 + options: [
559 + { title: "个人报名", value: "personal", checked: true },
560 + { title: "团队报名", value: "team" },
561 + {
562 + title: "其他",
563 + value: "other",
564 + is_input: true,
565 + input_placeholder: "请说明具体类型"
566 + }
567 + ]
568 + }
569 + },
570 +
571 + // 附加信息
572 + {
573 + key: "field_hobbies",
574 + value: [],
575 + component_props: {
576 + tag: "checkbox",
577 + label: "兴趣爱好",
578 + direction: "vertical",
579 + options: [
580 + { title: "运动", value: "sport" },
581 + { title: "阅读", value: "reading" },
582 + { title: "音乐", value: "music" }
583 + ]
584 + }
585 + },
586 +
587 + // 时间选择
588 + {
589 + key: "field_date",
590 + value: "",
591 + component_props: {
592 + tag: "date",
593 + label: "活动日期",
594 + required: true,
595 + placeholder: "请选择日期"
596 + }
597 + },
598 +
599 + // 图片上传
600 + {
601 + key: "field_photo",
602 + value: [],
603 + component_props: {
604 + tag: "image_uploader",
605 + label: "上传照片",
606 + required: true,
607 + max_count: 3,
608 + max_size: 5
609 + }
610 + },
611 +
612 + // 说明文字
613 + {
614 + key: "field_note",
615 + value: "",
616 + component_props: {
617 + tag: "note",
618 + label: "注意事项",
619 + default: "<p>请确保所填信息真实有效</p>"
620 + }
621 + },
622 +
623 + // 电子签名
624 + {
625 + key: "field_sign",
626 + value: "",
627 + component_props: {
628 + tag: "sign",
629 + label: "本人签名",
630 + required: true
631 + }
632 + }
633 +]
634 +```
635 +
636 +## 数据提交格式
637 +
638 +### 提交到后端的格式
639 +
640 +```javascript
641 +{
642 + form_code: "form_001",
643 + data: {
644 + field_name: "张三",
645 + field_gender: "男",
646 + field_phone: "13800138000",
647 + field_type: {
648 + key: "field_type",
649 + value: "personal",
650 + type: "radio"
651 + },
652 + field_hobbies: {
653 + key: "field_hobbies",
654 + value: ["sport", "music"],
655 + type: "checkbox"
656 + },
657 + field_date: "2024-03-15",
658 + field_photo: {
659 + key: "field_photo",
660 + value: [
661 + { url: "https://...", name: "photo1.jpg" }
662 + ],
663 + type: "image_uploader"
664 + },
665 + field_sign: {
666 + key: "field_sign",
667 + value: "data:image/png;base64,...",
668 + type: "sign"
669 + }
670 + }
671 +}
672 +```
673 +
674 +## LangChain Agent 使用的提示词模板
675 +
676 +```javascript
677 +const FORM_GENERATION_PROMPT = `
678 +你是一个表单配置生成专家。根据用户的自然语言描述,生成符合以下规范的表单字段配置。
679 +
680 +## 组件类型映射
681 +
682 +用户描述 → 组件类型:
683 +- "姓名/名字" → name
684 +- "性别" → gender
685 +- "手机号/电话" → phone
686 +- "邮箱" → email
687 +- "身份证" → id_card
688 +- "单选/单项选择" → radio
689 +- "多选/多项选择" → checkbox
690 +- "下拉/选择" → select
691 +- "日期" → date
692 +- "时间" → time
693 +- "地址/省市区" → address
694 +- "图片上传" → image_uploader
695 +- "文件上传" → file_uploader
696 +- "多行文本/描述/简介" → textarea
697 +- "数字" → number
698 +- "评分/打分" → rate
699 +- "签名/电子签名" → sign
700 +- "富文本/说明" → note
701 +- "分割线" → divider
702 +
703 +## 必填属性
704 +
705 +- "必填/必须/一定要" → required: true
706 +- "可选/非必须" → required: false
707 +
708 +## 配置模板
709 +
710 +### 文本输入
711 +{
712 + key: "field_{index}",
713 + value: "",
714 + component_props: {
715 + tag: "input",
716 + name: "{field_name}",
717 + label: "{显示名称}",
718 + placeholder: "请输入{显示名称}",
719 + required: {是否必填}
720 + }
721 +}
722 +
723 +### 单选/多选
724 +{
725 + key: "field_{index}",
726 + value: "",
727 + component_props: {
728 + tag: "{radio|checkbox}",
729 + name: "{field_name}",
730 + label: "{显示名称}",
731 + required: {是否必填},
732 + direction: "vertical",
733 + options: [
734 + { title: "{选项1}", value: "{value1}" },
735 + { title: "{选项2}", value: "{value2}" }
736 + ]
737 + }
738 +}
739 +
740 +### 图片上传
741 +{
742 + key: "field_{index}",
743 + value: [],
744 + component_props: {
745 + tag: "image_uploader",
746 + name: "{field_name}",
747 + label: "{显示名称}",
748 + required: {是否必填},
749 + max_count: {数量},
750 + max_size: {大小MB}
751 + }
752 +}
753 +
754 +请根据用户需求生成对应的表单配置JSON。
755 +`;
756 +```
1 +# LangChain Agent 使用指南
2 +
3 +## 概述
4 +
5 +本文档为使用 LangChain 构建自然语言生成表单的 Agent 提供参考数据和配置指南。
6 +
7 +## Agent 任务定义
8 +
9 +**任务**:将用户的自然语言描述转换为表单字段配置 JSON
10 +
11 +**输入**:自然语言描述
12 +**输出**:表单字段配置数组
13 +
14 +## 自然语言到组件映射
15 +
16 +### 基础输入类
17 +
18 +| 用户关键词 | 组件类型 | tag | 配置要点 |
19 +|-----------|----------|-----|----------|
20 +| 姓名、名字 | NameField | `name` | 必填通常为 true |
21 +| 性别 | GenderField | `gender` | 选项:男/女 |
22 +| 手机号、电话 | PhoneField | `phone` | 自动验证11位 |
23 +| 邮箱 | EmailField | `email` | 自动验证格式 |
24 +| 身份证 | IdentityField | `id_card` | 自动验证格式 |
25 +| 数字、数量 | NumberField | `number` | 整数类型 |
26 +| 单行文本、输入 | TextField | `input` | 最多255字符 |
27 +| 多行文本、描述 | TextareaField | `textarea` | 支持自动高度 |
28 +
29 +### 选择类
30 +
31 +| 用户关键词 | 组件类型 | tag | 数据结构 |
32 +|-----------|----------|-----|----------|
33 +| 单选、单项选择 | RadioField | `radio` | options 数组 |
34 +| 多选、多项选择 | CheckboxField | `checkbox` | options 数组 |
35 +| 下拉、选择 | PickerField | `select` | options 数组 |
36 +| 省、市、区、地址 | AreaPickerField | `address` | 三级联动 |
37 +
38 +### 日期时间类
39 +
40 +| 用户关键词 | 组件类型 | tag | 备注 |
41 +|-----------|----------|-----|------|
42 +| 日期、出生日期 | DatePickerField | `date` | - |
43 +| 时间 | TimePickerField | `time` | - |
44 +| 日期时间、开始时间 | DateTimePickerField | `datetime` | - |
45 +| 日历 | CalendarField | `calendar` | 日历形式 |
46 +
47 +### 上传类
48 +
49 +| 用户关键词 | 组件类型 | tag | 配置参数 |
50 +|-----------|----------|-----|----------|
51 +| 图片、照片、上传图 | ImageUploaderField | `image_uploader` | max_count, max_size |
52 +| 文件、附件、上传 | FileUploaderField | `file_uploader` | max_count, max_size |
53 +
54 +### 特殊功能类
55 +
56 +| 用户关键词 | 组件类型 | tag | 备注 |
57 +|-----------|----------|-----|------|
58 +| 签名、签字、电子签名 | SignField | `sign` | 手写签名板 |
59 +| 评分、打分、星级 | RatePickerField | `rate` | 支持 x_score 显示 |
60 +| 预约、时间段 | AppointmentField | `appointment` | 复杂配置 |
61 +| 说明、富文本 | NoteField | `note` | HTML 内容 |
62 +| 分割线、分隔 | DividerField | `divider` | - |
63 +| 跑马灯、公告 | MarqueeField | `marquee` | 滚动文字 |
64 +| 规则、说明 | RuleField | `rule` | 支持弹窗 |
65 +| 按钮 | ButtonField | `button` | 自定义按钮 |
66 +
67 +### 高级类
68 +
69 +| 用户关键词 | 组件类型 | tag | 备注 |
70 +|-----------|----------|-----|------|
71 +| 表格、清单 | TableField | `table_editor` | 动态表格 |
72 +| 分组、集合 | GroupField | `group` | field_groups 数组 |
73 +| 树形选择、组织 | OrgPickerField | `org_picker` | 树结构 |
74 +| 组别、义工组 | VolunteerGroupField | `volunteer_group` | 义工组选择 |
75 +
76 +## 必填识别
77 +
78 +### 强必填关键词
79 +
80 +- 必填、必须、一定要、强制
81 +- 不可缺少、不能为空
82 +- 必须提供、务必填写
83 +
84 +### 可选关键词
85 +
86 +- 可选、非必须、非强制
87 +- 选填、可以不填
88 +- 如果有、尽量填写
89 +
90 +### 默认规则
91 +
92 +- 姓名通常必填
93 +- 手机号通常必填
94 +- 附件类通常选填
95 +- 说明文字通常选填
96 +
97 +## 数量识别
98 +
99 +### 图片上传数量
100 +
101 +- "一张图"、"1张" → max_count: 1
102 +- "三张图"、"3张" → max_count: 3
103 +- "最多5张" → max_count: 5
104 +- "多张"、"若干" → max_count: 9(默认)
105 +
106 +### 文件大小限制
107 +
108 +- "5MB以内"、"小于5M" → max_size: 5
109 +- "10MB" → max_size: 10
110 +
111 +## 选项提取
112 +
113 +### 单选/多选选项提取
114 +
115 +**用户输入**:"性别:男、女、其他"
116 +
117 +**提取结果**
118 +```javascript
119 +options: [
120 + { title: "男", value: "男" },
121 + { title: "女", value: "女" },
122 + { title: "其他", value: "其他" }
123 +]
124 +```
125 +
126 +### 带描述的选项
127 +
128 +**用户输入**:"同意(详见规则)、不同意"
129 +
130 +**提取结果**
131 +```javascript
132 +options: [
133 + {
134 + title: "同意",
135 + value: "agree",
136 + desc_text: "点击查看详细规则",
137 + desc_type: "text",
138 + desc_btn_name: "详见规则"
139 + },
140 + {
141 + title: "不同意",
142 + value: "disagree"
143 + }
144 +]
145 +```
146 +
147 +### 带补充输入的选项
148 +
149 +**用户输入**:"其他(需说明)"
150 +
151 +**提取结果**
152 +```javascript
153 +options: [
154 + {
155 + title: "其他",
156 + value: "other",
157 + is_input: true,
158 + input_required: true,
159 + input_placeholder: "请说明具体情况"
160 + }
161 +]
162 +```
163 +
164 +## 复杂需求处理
165 +
166 +### 分组需求
167 +
168 +**用户输入**:"物品信息(包含物品名称和数量)"
169 +
170 +**处理方式**
171 +1. 识别为 group 类型
172 +2. 创建 field_groups 数组
173 +3. 每个子项作为独立字段
174 +
175 +```javascript
176 +{
177 + key: "field_1",
178 + value: "",
179 + component_props: {
180 + tag: "group",
181 + label: "物品信息",
182 + field_groups: [
183 + {
184 + tag: "input",
185 + label: "物品名称",
186 + field_name: "field_1_1",
187 + group_field_name: "field_1"
188 + },
189 + {
190 + tag: "number",
191 + label: "数量",
192 + field_name: "field_1_2",
193 + group_field_name: "field_1"
194 + }
195 + ]
196 + }
197 +}
198 +```
199 +
200 +### 选项依赖
201 +
202 +**用户输入**:"选择'其他'时需要输入补充信息"
203 +
204 +**处理方式**
205 +- 在对应选项设置 `is_input: true`
206 +- 设置 `input_required` 根据上下文判断
207 +
208 +### 提示信息
209 +
210 +**用户输入**:"用户名(4-16个字符,字母数字)"
211 +
212 +**处理方式**
213 +- label: "用户名"
214 +- note: "4-16个字符,支持字母、数字、下划线"
215 +
216 +## Prompt 模板
217 +
218 +### 系统提示词
219 +
220 +```python
221 +FORM_BUILDER_SYSTEM_PROMPT = """
222 +你是一个表单配置生成专家。你的任务是将用户的自然语言描述转换为符合规范的表单字段配置JSON。
223 +
224 +## 输出格式
225 +
226 +返回一个JSON数组,每个元素代表一个表单字段。
227 +
228 +## 字段结构
229 +
230 +每个字段必须包含:
231 +- key: 字段唯一标识(格式:field_序号)
232 +- value: 初始值
233 +- component_props: 组件属性对象
234 + - tag: 组件类型
235 + - label: 显示名称
236 + - required: 是否必填
237 + - 其他属性根据组件类型而定
238 +
239 +## 组件类型选择规则
240 +
241 +根据用户描述选择最合适的组件类型:
242 +- 文本输入:input(单行)、textarea(多行)、number(数字)
243 +- 选择类:radio(单选)、checkbox(多选)、select(下拉)
244 +- 日期时间:date、time、datetime
245 +- 上传类:image_uploader、file_uploader
246 +- 特殊:name、gender、phone、email、id_card、sign、rate等
247 +
248 +## 选项提取规则
249 +
250 +当用户列出选项时:
251 +1. 识别是单选还是多选
252 +2. 提取每个选项的标题和值
253 +3. 检测是否有补充信息需求
254 +4. 检测是否有描述信息
255 +
256 +## 必填识别规则
257 +
258 +- "必填、必须、一定要" → required: true
259 +- "可选、选填" → required: false
260 +- 姓名、手机号等基础信息通常必填
261 +- 说明、附件类通常选填
262 +
263 +请根据用户输入生成相应的表单配置。
264 +"""
265 +```
266 +
267 +### 用户输入示例
268 +
269 +```python
270 +# 示例1:简单表单
271 +user_input = "创建一个报名表单,包含姓名(必填)、性别(必填,男/女)、手机号(必填)"
272 +
273 +# 示例2:带选项
274 +user_input = "报名类型,单选,必填,选项:个人报名、团队报名"
275 +
276 +# 示例3:图片上传
277 +user_input = "上传证件照,最多3张,每张不超过5MB,必填"
278 +
279 +# 示例4:分组
280 +user_input = "物品清单,包含物品名称(必填)和数量(必填)"
281 +
282 +# 示例5:复杂需求
283 +user_input = "活动报名表,包含:姓名(必填)、性别(男/女/其他,其他需说明)、手机号(必填)、兴趣爱好(多选:运动、阅读、音乐、旅游)、活动日期(必填)、上传照片(最多3张)、本人签名(必填)"
284 +```
285 +
286 +## LangChain 工具调用
287 +
288 +### 推荐使用 LangChain 的工具
289 +
290 +1. **Structured Output** - 确保输出格式正确
291 +2. **Few-shot Prompting** - 提供示例
292 +3. **Output Parser** - 解析 JSON 输出
293 +
294 +### 代码示例
295 +
296 +```python
297 +from langchain.output_parsers import PydanticOutputParser
298 +from langchain_core.prompts import ChatPromptTemplate
299 +from pydantic import BaseModel, Field
300 +from typing import List, Optional
301 +
302 +class FormField(BaseModel):
303 + key: str = Field(description="字段唯一标识")
304 + tag: str = Field(description="组件类型")
305 + label: str = Field(description="显示名称")
306 + required: bool = Field(description="是否必填")
307 + placeholder: Optional[str] = Field(default="", description="占位符")
308 + options: Optional[List[dict]] = Field(default=None, description="选项列表")
309 + max_count: Optional[int] = Field(default=None, description="最大上传数量")
310 + max_size: Optional[int] = Field(default=None, description="最大文件大小")
311 +
312 +class FormConfiguration(BaseModel):
313 + fields: List[FormField] = Field(description="表单字段列表")
314 +
315 +# 创建解析器
316 +parser = PydanticOutputParser(pydantic_object=FormConfiguration)
317 +
318 +# 创建提示模板
319 +prompt = ChatPromptTemplate.from_messages([
320 + ("system", FORM_BUILDER_SYSTEM_PROMPT),
321 + ("user", "{user_input}")
322 +])
323 +
324 +# 创建链
325 +chain = prompt | llm | parser
326 +
327 +# 调用
328 +result = chain.invoke({"user_input": "创建一个用户信息表单..."})
329 +```
330 +
331 +## 验证和错误处理
332 +
333 +### 输出验证
334 +
335 +生成的 JSON 需要验证:
336 +
337 +1. **结构验证**
338 + - 是否是有效的 JSON 数组
339 + - 每个字段是否包含必需属性
340 +
341 +2. **类型验证**
342 + - tag 值是否在支持的组件列表中
343 + - required 是否为布尔值
344 + - options 是否为数组
345 +
346 +3. **业务验证**
347 + - 必填字段是否合理
348 + - 选项是否为空
349 + - 数量限制是否合理
350 +
351 +### 常见错误处理
352 +
353 +| 错误 | 处理方式 |
354 +|------|----------|
355 +| 无法识别组件类型 | 询问用户具体需求 |
356 +| 选项描述不清晰 | 提供选项模板让用户选择 |
357 +| 必填不明确 | 默认为选填,提示用户确认 |
358 +| 数量限制缺失 | 使用默认值(图片:3,文件:5) |
359 +
360 +## 完整工作流程
361 +
362 +```
363 +用户输入
364 +
365 +意图识别(新增/修改/查询)
366 +
367 +实体提取(字段名、组件类型、选项)
368 +
369 +配置生成(生成 component_props)
370 +
371 +格式验证(JSON 结构、类型检查)
372 +
373 +结果输出(表单配置 JSON)
374 +```
375 +
376 +## 最佳实践
377 +
378 +1. **逐步确认**:复杂需求分步确认,而非一次性生成
379 +2. **提供示例**:让用户选择最接近的示例
380 +3. **默认值策略**:不确定时使用保守的默认值
381 +4. **错误友好**:错误信息要具体指出问题和解决方案
1 +# 规则控制系统
2 +
3 +> **最后更新**: 2026-03-16
4 +> **核心文件**: `src/views/index.vue`
5 +
6 +## 📋 概述
7 +
8 +规则控制系统通过动态配置来实现表单字段的显示/隐藏逻辑,是表单系统的核心功能之一。
9 +
10 +### 核心特性
11 +
12 +-**双模式控制**: 支持 SHOW(显示)和 HIDE(隐藏)两种规则模式
13 +-**逻辑运算**: 支持 AND(且)和 OR(或)逻辑操作符
14 +-**级联隐藏**: 控制字段被隐藏时,其控制的所有字段也会被隐藏
15 +-**多规则支持**: 单个字段可以同时受多条规则控制
16 +-**优先级机制**: HIDE 规则优先级高于 SHOW 规则
17 +
18 +---
19 +
20 +## 🏗️ 数据结构
21 +
22 +### rule_list(规则列表)
23 +
24 +规则列表位于 `formInfo.value['rule_list']`,是表单级别配置。
25 +
26 +```typescript
27 +interface Rule {
28 + field_names: string[]; // 受此规则控制的字段名列表
29 + mode: 'SHOW' | 'HIDE'; // 规则模式:显示或隐藏
30 + logical_op: 'AND' | 'OR'; // 逻辑操作符
31 + expr_list: RuleExpr[]; // 规则表达式列表
32 + is_invalid?: boolean; // 规则是否无效(可选)
33 +}
34 +
35 +interface RuleExpr {
36 + field_name: string; // 控制字段名
37 + values: string[]; // 匹配值列表
38 +}
39 +```
40 +
41 +### 规则流程图
42 +
43 +```
44 +┌─────────────────────────────────────────────────────────────┐
45 +│ 规则控制系统架构 │
46 +├─────────────────────────────────────────────────────────────┤
47 +│ │
48 +│ formInfo.rule_list │
49 +│ ↓ │
50 +│ ┌─────────────────────────────────────────────┐ │
51 +│ │ checkRules() - 规则评估入口 │ │
52 +│ └─────────────────────────────────────────────┘ │
53 +│ ↓ │
54 +│ ┌─────────────────────────────────────────────┐ │
55 +│ │ 1. 收集规则涉及的所有字段 │ │
56 +│ │ 2. 初始化字段状态(disabled=false) │ │
57 +│ │ 3. 为每个字段分配规则(x_rules) │ │
58 +│ │ 4. 合并重复规则 │ │
59 +│ │ 5. 评估规则并设置 disabled 状态 │ │
60 +│ └─────────────────────────────────────────────┘ │
61 +│ ↓ │
62 +│ ┌─────────────────────────────────────────────┐ │
63 +│ │ evaluateMultipleRules() - 多规则评估 │ │
64 +│ │ ↓ │ │
65 +│ │ 分离 SHOW/HIDE 规则 │ │
66 +│ │ ↓ │ │
67 +│ │ SHOW 规则: any 满足 = 显示 │ │
68 +│ │ HIDE 规则: any 满足 = 隐藏 │ │
69 +│ │ ↓ │ │
70 +│ │ HIDE 优先 → 隐藏 │ │
71 +│ │ 否则 → 根据 SHOW 规则决定 │ │
72 +│ └─────────────────────────────────────────────┘ │
73 +│ ↓ │
74 +│ ┌─────────────────────────────────────────────┐ │
75 +│ │ handleCascadeHiding() - 级联隐藏 │ │
76 +│ │ 如果控制字段被隐藏,其控制的所有字段 │ │
77 +│ │ 也会被隐藏 │ │
78 +│ └─────────────────────────────────────────────┘ │
79 +│ │
80 +└─────────────────────────────────────────────────────────────┘
81 +```
82 +
83 +---
84 +
85 +## 🔧 核心函数
86 +
87 +### 1. checkRules()
88 +
89 +规则评估的主入口函数,负责初始化和协调整个规则评估流程。
90 +
91 +```javascript
92 +const checkRules = () => {
93 + const rule_list = formInfo.value['rule_list'] ? [...formInfo.value['rule_list']] : [];
94 +
95 + // 1. 收集所有规则中涉及的字段
96 + const fieldsInRules = new Set();
97 + rule_list.forEach(rule => {
98 + if (rule.field_names && Array.isArray(rule.field_names)) {
99 + rule.field_names.forEach(fieldName => {
100 + fieldsInRules.add(fieldName);
101 + });
102 + }
103 + });
104 +
105 + // 2. 初始化字段规则,只重置在规则中出现过的字段
106 + formData.value.forEach(item => {
107 + item.x_rules = [];
108 + if (fieldsInRules.has(item.key) && item.component_props) {
109 + item.component_props.disabled = false;
110 + }
111 + });
112 +
113 + // 3. 收集每个字段的规则
114 + rule_list.forEach(rule => {
115 + formData.value.forEach(item => {
116 + if (rule.field_names?.includes(item.key) && !rule.is_invalid) {
117 + item.x_rules.push({
118 + mode: rule.mode,
119 + logical_op: rule.logical_op,
120 + expr_list: rule.expr_list,
121 + });
122 + }
123 + });
124 + });
125 +
126 + // 4. 合并规则
127 + formData.value.forEach(item => {
128 + const mergedRules = mergeAndDeduplicate(item.x_rules);
129 + item.field_rules = mergedRules;
130 + });
131 +
132 + // 5. 处理规则逻辑 - 支持多条规则
133 + formData.value.forEach(item => {
134 + if (item.field_rules && item.field_rules.length > 0 && item.component_props) {
135 + const finalDisabledState = evaluateMultipleRules(item.field_rules);
136 + item.component_props.disabled = finalDisabledState;
137 + }
138 + });
139 +
140 + // 6. 处理级联隐藏
141 + handleCascadeHiding();
142 +};
143 +```
144 +
145 +### 2. evaluateMultipleRules()
146 +
147 +评估字段的所有规则,确定最终的显示/隐藏状态。
148 +
149 +**核心逻辑**:
150 +- SHOW 规则:任一满足即显示(some)
151 +- HIDE 规则:任一满足即隐藏(some)
152 +- HIDE 规则优先级更高
153 +
154 +```javascript
155 +const evaluateMultipleRules = (rulesList) => {
156 + if (!rulesList || rulesList.length === 0) {
157 + return false; // 没有规则时默认显示
158 + }
159 +
160 + let showRules = [];
161 + let hideRules = [];
162 +
163 + // 分离SHOW和HIDE规则
164 + rulesList.forEach(rule => {
165 + if (rule.mode === 'SHOW') {
166 + showRules.push(rule);
167 + } else if (rule.mode === 'HIDE') {
168 + hideRules.push(rule);
169 + }
170 + });
171 +
172 + // 评估SHOW规则
173 + let showResult = true; // 默认显示
174 + if (showRules.length > 0) {
175 + // 如果有SHOW规则,需要至少一个SHOW规则条件满足才显示
176 + showResult = showRules.some(rule => evaluateRuleCondition(rule));
177 + }
178 +
179 + // 评估HIDE规则
180 + let hideResult = false; // 默认不隐藏
181 + if (hideRules.length > 0) {
182 + // 如果有HIDE规则,任何一个HIDE规则条件满足就隐藏
183 + hideResult = hideRules.some(rule => evaluateRuleCondition(rule));
184 + }
185 +
186 + // 最终逻辑:HIDE规则优先级更高
187 + if (hideResult) {
188 + return true; // 隐藏
189 + } else {
190 + return !showResult; // SHOW规则不满足时隐藏
191 + }
192 +};
193 +```
194 +
195 +### 3. evaluateRuleCondition()
196 +
197 +评估单条规则的条件是否满足。
198 +
199 +**处理逻辑**:
200 +1. 遍历规则表达式列表
201 +2.`postData.value` 获取表单提交值
202 +3. 处理字符串类型(单选、下拉):清理冒号前缀后匹配
203 +4. 处理数组类型(多选):使用 lodash intersection 取交集
204 +5. 根据逻辑操作符(AND/OR)计算最终结果
205 +
206 +```javascript
207 +const evaluateRuleCondition = (fieldRules) => {
208 + if (!fieldRules || !fieldRules.expr_list || fieldRules.expr_list.length === 0) {
209 + return false;
210 + }
211 +
212 + const results = [];
213 +
214 + fieldRules.expr_list.forEach(expr => {
215 + let form_submission_value = postData.value[expr.field_name];
216 + const rule_matching_value = expr.values || [];
217 +
218 + // 处理空值情况
219 + if (form_submission_value === null || form_submission_value === undefined) {
220 + results.push(false);
221 + return;
222 + }
223 +
224 + let matchResult = false;
225 +
226 + if (typeof form_submission_value === 'string') {
227 + // 处理字符串类型(单选,下拉)
228 + let cleanValue = form_submission_value;
229 + if (form_submission_value.indexOf(':') !== -1) {
230 + cleanValue = form_submission_value.split(':')[0];
231 + }
232 + matchResult = rule_matching_value.includes(cleanValue);
233 + } else if (Array.isArray(form_submission_value)) {
234 + // 处理数组类型(多选)
235 + const cleanValues = form_submission_value.map(item => {
236 + if (typeof item === 'string' && item.includes(':')) {
237 + return item.split(':')[0].trim();
238 + }
239 + return item;
240 + });
241 + matchResult = _.intersection(rule_matching_value, cleanValues).length > 0;
242 + }
243 +
244 + results.push(matchResult);
245 + });
246 +
247 + // 根据逻辑操作符计算最终结果
248 + if (fieldRules.logical_op === 'AND') {
249 + return results.every(result => result === true);
250 + } else { // OR
251 + return results.some(result => result === true);
252 + }
253 +};
254 +```
255 +
256 +### 4. handleCascadeHiding()
257 +
258 +处理级联隐藏逻辑,确保当控制字段被隐藏时,其控制的所有字段也会被隐藏。
259 +
260 +```javascript
261 +const handleCascadeHiding = () => {
262 + const rule_list = formInfo.value['rule_list'] || [];
263 +
264 + // 获取所有规则控制字段的映射
265 + const ruleControlMap = new Map();
266 + rule_list.forEach(rule => {
267 + if (rule.expr_list && rule.expr_list.length > 0) {
268 + const controlField = rule.expr_list[0].field_name;
269 + if (!ruleControlMap.has(controlField)) {
270 + ruleControlMap.set(controlField, []);
271 + }
272 + ruleControlMap.get(controlField).push({
273 + field_names: rule.field_names || [],
274 + mode: rule.mode
275 + });
276 + }
277 + });
278 +
279 + // 检查级联隐藏
280 + formData.value.forEach(item => {
281 + if (ruleControlMap.has(item.key) && item.component_props && item.component_props.disabled) {
282 + // 如果控制字段被隐藏,则隐藏其控制的所有字段
283 + const controlledRules = ruleControlMap.get(item.key);
284 + controlledRules.forEach(rule => {
285 + rule.field_names.forEach(fieldName => {
286 + const targetField = formData.value.find(field => field.key === fieldName);
287 + if (targetField && targetField.component_props) {
288 + targetField.component_props.disabled = true;
289 + }
290 + });
291 + });
292 + }
293 + });
294 +};
295 +```
296 +
297 +---
298 +
299 +## 🔄 触发机制
300 +
301 +规则评估在以下时机触发:
302 +
303 +### 1. 表单初始化时
304 +
305 +表单数据加载完成后,调用 `checkRules()` 初始化所有字段的显示状态。
306 +
307 +### 2. 字段值变化时
308 +
309 +当用户修改表单字段值时,通过 `@active` 事件触发规则重新评估:
310 +
311 +```vue
312 +<component
313 + v-for="item in formData"
314 + :is="item.component_props.tag"
315 + v-model="postData[item.key]"
316 + @active="checkRules"
317 +/>
318 +```
319 +
320 +### 3. 数据结构
321 +
322 +- **formData**: 表单字段配置数组
323 +- **postData**: 表单实际提交的数据
324 +- **formInfo**: 表单元数据(包含 rule_list)
325 +
326 +---
327 +
328 +## 📊 实际示例
329 +
330 +### 示例 1:简单 SHOW 规则
331 +
332 +**场景**:只有当"用户类型"选择"企业用户"时,才显示"企业名称"字段。
333 +
334 +```json
335 +{
336 + "rule_list": [
337 + {
338 + "field_names": ["company_name"],
339 + "mode": "SHOW",
340 + "logical_op": "OR",
341 + "expr_list": [
342 + {
343 + "field_name": "user_type",
344 + "values": ["enterprise"]
345 + }
346 + ]
347 + }
348 + ]
349 +}
350 +```
351 +
352 +### 示例 2:HIDE 规则
353 +
354 +**场景**:当"居住类型"选择"租房"时,隐藏"房产证号"字段。
355 +
356 +```json
357 +{
358 + "rule_list": [
359 + {
360 + "field_names": ["property_cert_no"],
361 + "mode": "HIDE",
362 + "logical_op": "OR",
363 + "expr_list": [
364 + {
365 + "field_name": "residence_type",
366 + "values": ["rent"]
367 + }
368 + ]
369 + }
370 + ]
371 +}
372 +```
373 +
374 +### 示例 3:AND 逻辑
375 +
376 +**场景**:同时满足"年龄大于18"且"有收入证明"时,显示"信用卡申请"字段。
377 +
378 +```json
379 +{
380 + "rule_list": [
381 + {
382 + "field_names": ["credit_card_application"],
383 + "mode": "SHOW",
384 + "logical_op": "AND",
385 + "expr_list": [
386 + {
387 + "field_name": "age",
388 + "values": ["adult", "senior"]
389 + },
390 + {
391 + "field_name": "income_proof",
392 + "values": ["yes"]
393 + }
394 + ]
395 + }
396 + ]
397 +}
398 +```
399 +
400 +### 示例 4:OR 逻辑
401 +
402 +**场景**:选择"VIP用户"或"企业用户"任一条件时,显示"专属客服"字段。
403 +
404 +```json
405 +{
406 + "rule_list": [
407 + {
408 + "field_names": ["exclusive_service"],
409 + "mode": "SHOW",
410 + "logical_op": "OR",
411 + "expr_list": [
412 + {
413 + "field_name": "user_type",
414 + "values": ["vip"]
415 + },
416 + {
417 + "field_name": "user_type",
418 + "values": ["enterprise"]
419 + }
420 + ]
421 + }
422 + ]
423 +}
424 +```
425 +
426 +### 示例 5:多规则控制
427 +
428 +**场景**:字段受多条规则控制,HIDE 规则优先。
429 +
430 +```json
431 +{
432 + "rule_list": [
433 + {
434 + "field_names": ["special_discount"],
435 + "mode": "SHOW",
436 + "logical_op": "OR",
437 + "expr_list": [
438 + {
439 + "field_name": "membership_level",
440 + "values": ["gold", "platinum"]
441 + }
442 + ]
443 + },
444 + {
445 + "field_names": ["special_discount"],
446 + "mode": "HIDE",
447 + "logical_op": "OR",
448 + "expr_list": [
449 + {
450 + "field_name": "account_status",
451 + "values": ["suspended", "banned"]
452 + }
453 + ]
454 + }
455 + ]
456 +}
457 +```
458 +
459 +**结果**:即使满足 VIP 条件,如果账户被暂停,也不会显示特殊折扣字段。
460 +
461 +### 示例 6:级联隐藏
462 +
463 +**场景**
464 +- 字段 A 控制字段 B 的显示
465 +- 字段 B 控制字段 C 的显示
466 +- 当字段 A 被隐藏时,字段 B 和 C 都应该被隐藏
467 +
468 +```json
469 +{
470 + "rule_list": [
471 + {
472 + "field_names": ["field_b"],
473 + "mode": "SHOW",
474 + "logical_op": "OR",
475 + "expr_list": [
476 + {
477 + "field_name": "field_a",
478 + "values": ["show_b"]
479 + }
480 + ]
481 + },
482 + {
483 + "field_names": ["field_c"],
484 + "mode": "SHOW",
485 + "logical_op": "OR",
486 + "expr_list": [
487 + {
488 + "field_name": "field_b",
489 + "values": ["show_c"]
490 + }
491 + ]
492 + }
493 + ]
494 +}
495 +```
496 +
497 +---
498 +
499 +## 🎯 LangChain Agent 集成要点
500 +
501 +### 自然语言到规则的映射
502 +
503 +当用户用自然语言描述规则时,Agent 需要将其转换为规则配置:
504 +
505 +| 用户描述 | mode | logical_op |
506 +|---------|------|------------|
507 +| "当...时显示" | SHOW | OR |
508 +| "只有...才显示" | SHOW | AND |
509 +| "当...时隐藏" | HIDE | OR |
510 +| "只要...就隐藏" | HIDE | OR |
511 +
512 +### Prompt 模板
513 +
514 +```python
515 +RULE_GENERATION_TEMPLATE = """
516 +你是一个表单规则配置专家。根据用户的自然语言描述,生成规则配置。
517 +
518 +用户描述:{user_description}
519 +
520 +可用的字段:
521 +{available_fields}
522 +
523 +请生成规则配置,格式如下:
524 +```json
525 +{{
526 + "rule_list": [
527 + {{
528 + "field_names": ["受控制的字段名"],
529 + "mode": "SHOW" | "HIDE",
530 + "logical_op": "AND" | "OR",
531 + "expr_list": [
532 + {{
533 + "field_name": "控制字段名",
534 + "values": ["值1", "值2"]
535 + }}
536 + ]
537 + }}
538 + ]
539 +}}
540 +```
541 +
542 +规则说明:
543 +1. mode: "SHOW" 表示满足条件时显示,"HIDE" 表示满足条件时隐藏
544 +2. logical_op: "AND" 表示所有条件都要满足,"OR" 表示任一条件满足即可
545 +3. HIDE 规则优先级高于 SHOW 规则
546 +"""
547 +```
548 +
549 +### 示例对话
550 +
551 +**用户输入**
552 +```
553 +"当用户类型是企业时,显示企业名称和统一社会信用代码字段"
554 +```
555 +
556 +**Agent 输出**
557 +```json
558 +{
559 + "rule_list": [
560 + {
561 + "field_names": ["company_name", "credit_code"],
562 + "mode": "SHOW",
563 + "logical_op": "OR",
564 + "expr_list": [
565 + {
566 + "field_name": "user_type",
567 + "values": ["enterprise", "company"]
568 + }
569 + ]
570 + }
571 + ]
572 +}
573 +```
574 +
575 +---
576 +
577 +## ⚠️ 注意事项
578 +
579 +### 1. 值格式处理
580 +
581 +系统会自动处理带冒号的值(如 `"option1:选项1"`),只匹配冒号前的部分。
582 +
583 +### 2. 空值处理
584 +
585 +当控制字段的值为 `null``undefined` 时,规则匹配结果为 `false`
586 +
587 +### 3. 多选字段
588 +
589 +多选字段(checkbox)的值是数组,使用 lodash `intersection` 检查是否有交集。
590 +
591 +### 4. 规则优先级
592 +
593 +- HIDE 规则优先级高于 SHOW 规则
594 +- 同类型规则之间使用 OR 逻辑(任一满足即可)
595 +
596 +### 5. 性能考虑
597 +
598 +- 每次字段值变化都会触发所有规则的重新评估
599 +- 对于复杂表单,建议优化规则数量和复杂度
600 +
601 +---
602 +
603 +## 🔗 相关文档
604 +
605 +- [00-数据结构总览.md](./00-数据结构总览.md)
606 +- [02-component_props-配置规范.md](./02-component_props-配置规范.md)
607 +- [05-LangChain-Agent-使用指南.md](./05-LangChain-Agent-使用指南.md)
1 +# 页面数据提取指南
2 +
3 +> **最后更新**: 2026-03-16
4 +> **用途**: 从实际运行的表单页面中提取配置数据
5 +
6 +## 📋 概述
7 +
8 +当用户提供一个表单 URL(如 `https://oa-dev.onwall.cn/f/custom_form/front/index.html#/?code=nxqkbf`)时,需要从页面中提取完整的表单配置数据。
9 +
10 +---
11 +
12 +## 🏗️ 数据存储结构
13 +
14 +### Pinia Store
15 +
16 +表单数据存储在 Pinia store 中:
17 +
18 +```javascript
19 +// Store 名称: 'main'
20 +// Store 定义: src/store/index.js
21 +
22 +{
23 + formInfo: {}, // 表单完整信息(包含字段列表、规则等)
24 + formSetting: {}, // 表单数据收集设置
25 + successInfo: {}, // 表单提交返回值
26 +}
27 +```
28 +
29 +### formInfo 数据结构
30 +
31 +```typescript
32 +interface FormInfo {
33 + name: string; // 表单名称
34 + field_list: FieldItem[]; // 字段列表
35 + rule_list?: Rule[]; // 规则列表(可选)
36 + // ... 其他属性
37 +}
38 +
39 +interface FieldItem {
40 + tag: string; // 组件类型
41 + field_name: string; // 字段名
42 + label: string; // 显示标签
43 + required: boolean; // 是否必填
44 + data_type: string; // 数据类型
45 + default?: any; // 默认值
46 + options?: Option[]; // 选项列表(选择类组件)
47 + disabled?: boolean; // 是否禁用
48 + readonly?: boolean; // 是否只读
49 + placeholder?: string; // 占位符
50 + // ... 其他属性
51 +}
52 +```
53 +
54 +---
55 +
56 +## 🌐 API 数据结构详解
57 +
58 +### 核心 API 接口
59 +
60 +表单系统有两个核心 API 接口,分别获取不同维度的数据:
61 +
62 +| API 接口 | 获取内容 | 主要用途 |
63 +|---------|---------|----------|
64 +| `query_form_all_field` | 字段列表、规则列表 | 渲染表单结构 |
65 +| `query_form_setting` | 表单级配置 | 获取表单属性 |
66 +
67 +### API 1: query_form_all_field - 字段和规则
68 +
69 +**请求**
70 +```bash
71 +GET https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf
72 +```
73 +
74 +**响应结构**
75 +```json
76 +{
77 + "code": 1,
78 + "msg": "OK",
79 + "data": {
80 + "id": 835950,
81 + "code": "nxqkbf",
82 + "name": "测试LangChain",
83 + "short_name": "customize_35697_nxqkbf",
84 + "table_name": "customize_35697_nxqkbf",
85 + "max_field_num": 3,
86 + "description": null,
87 + "rule_list": [],
88 + "field_list": [...]
89 + },
90 + "flow_process_list": null
91 +}
92 +```
93 +
94 +**field_list 字段类型详解**
95 +
96 +```typescript
97 +// 1. 页面头部 (page_header)
98 +{
99 + tag: "page_header",
100 + field_name: "object_1",
101 + label: "测试LangChain",
102 + index: 0,
103 + banner: {
104 + ext: "png",
105 + src: "https://cdn.ipadbiz.cn/...",
106 + name: ""
107 + },
108 + banner_type: "image",
109 + interaction_type: "h5show" // 仅展示,不可编辑
110 +}
111 +
112 +// 2. 输入框 (input)
113 +{
114 + tag: "input",
115 + field_name: "field_2",
116 + label: "标题",
117 + index: 2,
118 + required: false,
119 + data_type: "text",
120 + placeholder: "请输入",
121 + disabled: false,
122 + readonly: false,
123 + default: "",
124 + unique: false,
125 + interaction_type: "h5edit" // 可编辑
126 +}
127 +
128 +// 3. 提交按钮 (page_commit)
129 +{
130 + tag: "page_commit",
131 + field_name: "object_3",
132 + text: "提交",
133 + index: 1,
134 + is_back: false,
135 + back_title: "上一页",
136 + interaction_type: "h5show"
137 +}
138 +```
139 +
140 +### API 2: query_form_setting - 表单配置
141 +
142 +**请求**
143 +```bash
144 +GET https://oa-dev.onwall.cn/srv/?a=query_form_setting&f=custom_form&form_code=nxqkbf
145 +```
146 +
147 +**响应结构**
148 +```json
149 +{
150 + "code": 1,
151 + "msg": "OK",
152 + "data": [{
153 + "id": 835950,
154 + "code": "nxqkbf",
155 + "name": "测试LangChain",
156 + "max_field_num": 3,
157 + "data_count": 7,
158 + "client_id": 35697,
159 + "extend": {
160 + "server_time": "2026-03-16 17:15:40",
161 + "is_back_user": false
162 + },
163 + "property_list": {
164 + "sjsj_enable": 1,
165 + "commit_action": "text",
166 + "commit_text_type": "default",
167 + "commit_text_template": "提交成功",
168 + "edit_commit_action": "text",
169 + "edit_commit_text_type": "default",
170 + "edit_commit_text_template": "提交成功"
171 + }
172 + }]
173 +}
174 +```
175 +
176 +**property_list 详解**
177 +
178 +| 属性 | 类型 | 说明 |
179 +|------|------|------|
180 +| `sjsj_enable` | number | 是否启用数据收集(0=关闭,1=开启) |
181 +| `commit_action` | string | 提交动作类型(text=显示文本) |
182 +| `commit_text_template` | string | 提交成功后显示的文本 |
183 +| `edit_commit_action` | string | 编辑提交动作 |
184 +| `edit_commit_text_template` | string | 编辑提交成功文本 |
185 +
186 +### 完整数据关系图
187 +
188 +```
189 +┌─────────────────────────────────────────────────────────────┐
190 +│ 表单数据完整结构 │
191 +├─────────────────────────────────────────────────────────────┤
192 +│ │
193 +│ 表单 URL │
194 +│ https://oa-dev.onwall.cn/f/custom_form/front/#/?code=xxx │
195 +│ ↓ │
196 +│ 提取 form_code │
197 +│ ───────────────────────────────────────────────────────── │
198 +│ ↓ │
199 +│ ┌─────────────────────────────────────────────────────┐ │
200 +│ │ API 1: query_form_all_field │ │
201 +│ │ → 字段列表 (field_list) │ │
202 +│ │ → 规则列表 (rule_list) │ │
203 +│ └─────────────────────────────────────────────────────┘ │
204 +│ ↓ │
205 +│ ┌─────────────────────────────────────────────────────┐ │
206 +│ │ API 2: query_form_setting │ │
207 +│ │ → 表单配置 (property_list) │ │
208 +│ │ → 扩展信息 (extend) │ │
209 +│ └─────────────────────────────────────────────────────┘ │
210 +│ ↓ │
211 +│ 合并数据 → LangChain Agent 可用的完整表单配置 │
212 +│ │
213 +└─────────────────────────────────────────────────────────────┘
214 +```
215 +
216 +---
217 +
218 +## 🔧 数据提取方法
219 +
220 +### 方法 1: 直接 API 调用(推荐)
221 +
222 +**核心原理**:直接调用后端 API 接口获取表单配置数据
223 +
224 +**API 端点**
225 +```
226 +GET https://oa-dev.onwall.cn/srv/?a=query_form_all_field
227 +```
228 +
229 +**请求参数**
230 +```javascript
231 +{
232 + f: "custom_form", // 固定参数
233 + form_code: "nxqkbf", // 从 URL 参数中获取(必填)
234 + page_type: "add", // 页面类型:add/edit/view(可选)
235 + data_id: null, // 数据ID(编辑时使用)
236 + flow_node_code: "", // 流程节点代码(流程表单使用)
237 + force_back: "" // 强制后台模式标识
238 +}
239 +```
240 +
241 +**✅ 重要**:此 API **不需要特殊认证**,可以直接通过 curl 或 HTTP 请求调用!
242 +
243 +**调用方式**
244 +
245 +```bash
246 +# 使用 curl 直接调用
247 +curl "https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf"
248 +```
249 +
250 +```javascript
251 +// 使用 fetch/axios
252 +const response = await fetch('https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf');
253 +const data = await response.json();
254 +
255 +// 或使用项目内部的 API 函数
256 +import { queryFormAPI } from '@/api/form';
257 +const { data } = await queryFormAPI({ form_code: 'nxqkbf' });
258 +```
259 +
260 +**完整 API 响应结构**
261 +```json
262 +{
263 + "code": 1,
264 + "msg": "OK",
265 + "data": {
266 + "id": 835950,
267 + "code": "nxqkbf",
268 + "name": "表单名称",
269 + "field_list": [
270 + {
271 + "tag": "input",
272 + "field_name": "field_1",
273 + "label": "姓名",
274 + "required": true,
275 + "data_type": "text"
276 + }
277 + ],
278 + "rule_list": []
279 + }
280 +}
281 +```
282 +
283 +---
284 +
285 +### 方法 2: Chrome DevTools MCP(需要登录)
286 +
287 +**前提条件**:已使用浏览器登录系统
288 +
289 +使用 Chrome DevTools MCP 工具直接从页面提取数据:
290 +
291 +```javascript
292 +// 在页面中执行以下脚本获取表单数据
293 +const app = document.querySelector('#app').__vueParentComponent.appContext.app;
294 +const pinia = app.config.globalProperties.$pinia;
295 +const store = pinia.state.value.main;
296 +
297 +// 获取表单信息
298 +const formInfo = store.formInfo;
299 +
300 +// 返回结果
301 +JSON.stringify(formInfo, null, 2);
302 +```
303 +
304 +---
305 +
306 +### 方法 3: Vue DevTools API(需要登录)
307 +
308 +通过 Vue DevTools 获取组件状态:
309 +
310 +```javascript
311 +// 获取根组件
312 +const rootComponent = document.querySelector('#app').__vueParentComponent;
313 +
314 +// 获取 formInfo
315 +const formInfo = rootComponent.ctx.formInfo;
316 +
317 +// 返回结果
318 +JSON.stringify(formInfo, null, 2);
319 +```
320 +
321 +---
322 +
323 +## 📝 完整提取流程
324 +
325 +### 方法 A: 直接 API 调用(需要登录态)
326 +
327 +**适用场景**:在已登录环境下,需要批量获取表单配置
328 +
329 +**步骤 1**: 解析 URL 获取 form_code
330 +
331 +```javascript
332 +// 从 URL 中提取 form_code
333 +// URL: https://oa-dev.onwall.cn/f/custom_form/front/index.html#/?code=nxqkbf
334 +const url = new URL('https://oa-dev.onwall.cn/f/custom_form/front/index.html');
335 +const formCode = 'nxqkbf'; // 从 URL 参数 code 获取
336 +```
337 +
338 +**步骤 2**: 调用查询 API
339 +
340 +```javascript
341 +// 使用项目 API 函数
342 +import { queryFormAPI } from '@/api/form';
343 +
344 +async function getFormConfig(formCode) {
345 + try {
346 + const response = await queryFormAPI({
347 + form_code: formCode,
348 + page_type: 'add',
349 + data_id: null,
350 + flow_node_code: '',
351 + force_back: ''
352 + });
353 +
354 + if (response && response.code === 1) {
355 + return {
356 + formName: response.data.name,
357 + fieldList: response.data.field_list,
358 + ruleList: response.data.rule_list || [],
359 + bannerConfig: response.data.banner_config,
360 + submitConfig: response.data.submit_config
361 + };
362 + }
363 + } catch (error) {
364 + console.error('获取表单配置失败:', error);
365 + throw error;
366 + }
367 +}
368 +```
369 +
370 +**步骤 3**: 数据验证
371 +
372 +```javascript
373 +function validateFormConfig(data) {
374 + const required = ['formName', 'fieldList'];
375 + const missing = required.filter(key => !data[key]);
376 +
377 + if (missing.length > 0) {
378 + throw new Error(`缺少必要字段: ${missing.join(', ')}`);
379 + }
380 +
381 + if (!Array.isArray(data.fieldList) || data.fieldList.length === 0) {
382 + throw new Error('字段列表为空或格式错误');
383 + }
384 +
385 + return true;
386 +}
387 +```
388 +
389 +---
390 +
391 +### 方法 B: 浏览器环境提取(Chrome DevTools)
392 +
393 +**步骤 1**: 打开页面并登录
394 +
395 +使用 Chrome DevTools MCP 打开 URL 并确保已登录:
396 +
397 +```javascript
398 +await mcp__chrome_devtools__navigate_page({
399 + type: 'url',
400 + url: 'https://oa-dev.onwall.cn/f/custom_form/front/index.html#/?code=nxqkbf',
401 + timeout: 30000
402 +});
403 +```
404 +
405 +**步骤 2**: 等待页面加载
406 +
407 +等待数据加载完成:
408 +
409 +```javascript
410 +await mcp__chrome_devtools__wait_for({
411 + text: '提交', // 等待提交按钮出现
412 + timeout: 10000
413 +});
414 +```
415 +
416 +**步骤 3**: 提取数据
417 +
418 +执行脚本提取 formInfo:
419 +
420 +```javascript
421 +const result = await mcp__chrome_devtools__evaluate_script({
422 + function: `() => {
423 + try {
424 + // 尝试从 Pinia store 获取
425 + const app = document.querySelector('#app').__vueParentComponent?.appContext?.app;
426 + if (app?.config?.globalProperties?.$pinia) {
427 + const pinia = app.config.globalProperties.$pinia;
428 + const store = pinia.state.value.main;
429 + return {
430 + success: true,
431 + source: 'pinia',
432 + data: store.formInfo
433 + };
434 + }
435 +
436 + // 尝试从组件直接获取
437 + const rootComponent = document.querySelector('#app').__vueParentComponent;
438 + if (rootComponent?.ctx?.formInfo) {
439 + return {
440 + success: true,
441 + source: 'component',
442 + data: rootComponent.ctx.formInfo
443 + };
444 + }
445 +
446 + return {
447 + success: false,
448 + error: '无法找到表单数据'
449 + };
450 + } catch (error) {
451 + return {
452 + success: false,
453 + error: error.message
454 + };
455 + }
456 + }`
457 +});
458 +```
459 +
460 +**步骤 4**: 解析数据
461 +
462 +根据提取的数据,解析出:
463 +
464 +1. **表单基本信息**: 表单名称、描述
465 +2. **字段列表**: 所有字段的配置
466 +3. **规则列表**: 字段显示/隐藏规则
467 +4. **页眉配置**: banner、标题等
468 +5. **提交配置**: 提交按钮配置
469 +
470 +---
471 +
472 +## 🎯 数据提取模板
473 +
474 +```javascript
475 +/**
476 + * 从表单页面提取完整配置
477 + */
478 +async function extractFormConfig(pageUrl) {
479 + // 1. 打开页面
480 + await mcp__chrome_devtools__navigate_page({
481 + type: 'url',
482 + url: pageUrl,
483 + timeout: 30000
484 + });
485 +
486 + // 2. 等待页面加载
487 + await mcp__chrome_devtools__wait_for({
488 + text: '提交',
489 + timeout: 10000
490 + });
491 +
492 + // 3. 提取数据
493 + const result = await mcp__chrome_devtools__evaluate_script({
494 + function: `() => {
495 + const app = document.querySelector('#app').__vueParentComponent?.appContext?.app;
496 + const pinia = app?.config?.globalProperties?.$pinia;
497 + return pinia ? pinia.state.value.main.formInfo : null;
498 + }`
499 + });
500 +
501 + // 4. 解析配置
502 + if (result) {
503 + return {
504 + formName: result.name,
505 + fieldList: result.field_list,
506 + ruleList: result.rule_list || [],
507 + metadata: {
508 + extractedAt: new Date().toISOString(),
509 + source: pageUrl
510 + }
511 + };
512 + }
513 +
514 + throw new Error('无法提取表单数据');
515 +}
516 +```
517 +
518 +---
519 +
520 +## 📊 真实 API 返回数据示例
521 +
522 +### 测试表单: `nxqkbf`
523 +
524 +**API 1: query_form_all_field 返回**
525 +
526 +```json
527 +{
528 + "code": 1,
529 + "msg": "OK",
530 + "data": {
531 + "id": 835950,
532 + "code": "nxqkbf",
533 + "name": "测试LangChain",
534 + "short_name": "customize_35697_nxqkbf",
535 + "table_name": "customize_35697_nxqkbf",
536 + "max_field_num": 3,
537 + "description": null,
538 + "rule_list": [],
539 + "field_list": [
540 + {
541 + "tag": "page_header",
542 + "name": "page_header_0",
543 + "index": 0,
544 + "label": "测试LangChain",
545 + "banner": {
546 + "ext": "png",
547 + "src": "https://cdn.ipadbiz.cn/space/FlBDc7PgmmN0fD4GdMW_xUCFgs5T.png"
548 + },
549 + "field_id": 835961,
550 + "invisible": false,
551 + "field_name": "object_1",
552 + "banner_type": "image",
553 + "interaction_type": "h5show"
554 + },
555 + {
556 + "tag": "input",
557 + "name": "input_2",
558 + "index": 2,
559 + "label": "标题",
560 + "unique": false,
561 + "default": "",
562 + "disabled": false,
563 + "field_id": 835962,
564 + "readonly": false,
565 + "required": false,
566 + "data_type": "text",
567 + "field_name": "field_2",
568 + "is_encrypt": false,
569 + "placeholder": "请输入",
570 + "interaction_type": "h5edit"
571 + },
572 + {
573 + "tag": "page_commit",
574 + "name": "page_commit_1",
575 + "text": "提交",
576 + "index": 1,
577 + "is_back": false,
578 + "field_id": 835964,
579 + "invisible": false,
580 + "back_title": "上一页",
581 + "field_name": "object_3",
582 + "interaction_type": "h5show"
583 + }
584 + ]
585 + }
586 +}
587 +```
588 +
589 +**API 2: query_form_setting 返回**
590 +
591 +```json
592 +{
593 + "code": 1,
594 + "msg": "OK",
595 + "data": [{
596 + "id": 835950,
597 + "code": "nxqkbf",
598 + "name": "测试LangChain",
599 + "max_field_num": 3,
600 + "data_count": 7,
601 + "client_id": 35697,
602 + "extend": {
603 + "server_time": "2026-03-16 17:15:40",
604 + "is_back_user": false
605 + },
606 + "property_list": {
607 + "sjsj_enable": 1,
608 + "commit_action": "text",
609 + "commit_text_type": "default",
610 + "commit_text_template": "提交成功",
611 + "edit_commit_action": "text",
612 + "edit_commit_text_type": "default",
613 + "edit_commit_text_template": "提交成功"
614 + }
615 + }]
616 +}
617 +```
618 +
619 +### 数据解析
620 +
621 +从以上真实数据可以得出:
622 +
623 +| 字段类型 | tag | field_name | 说明 |
624 +|---------|-----|------------|------|
625 +| 页面头部 | page_header | object_1 | 显示 banner 和标题 |
626 +| 输入框 | input | field_2 | 用户可编辑的输入字段 |
627 +| 提交按钮 | page_commit | object_3 | 表单提交按钮 |
628 +
629 +**关键字段说明**
630 +- `interaction_type: "h5show"` - 仅展示,不可编辑(如 banner、提交按钮)
631 +- `interaction_type: "h5edit"` - 可编辑(如输入框)
632 +- `field_name` - 数据提交时的字段名
633 +- `required` - 是否必填
634 +
635 +---
636 +
637 +## 🎯 数据提取示例
638 +
639 +### 输入 URL
640 +
641 +```
642 +https://oa-dev.onwall.cn/f/custom_form/front/index.html#/?code=nxqkbf
643 +```
644 +
645 +### 提取的数据结构
646 +
647 +```json
648 +{
649 + "name": "用户信息采集表",
650 + "field_list": [
651 + {
652 + "tag": "input",
653 + "field_name": "field_1",
654 + "label": "姓名",
655 + "required": true,
656 + "data_type": "text",
657 + "placeholder": "请输入姓名",
658 + "disabled": false,
659 + "readonly": false
660 + },
661 + {
662 + "tag": "gender",
663 + "field_name": "field_2",
664 + "label": "性别",
665 + "required": false,
666 + "data_type": "text",
667 + "options": [
668 + { "title": "男", "value": "男" },
669 + { "title": "女", "value": "女" }
670 + ]
671 + }
672 + ],
673 + "rule_list": [
674 + {
675 + "field_names": ["field_3"],
676 + "mode": "SHOW",
677 + "logical_op": "OR",
678 + "expr_list": [
679 + {
680 + "field_name": "field_1",
681 + "values": ["VIP用户"]
682 + }
683 + ]
684 + }
685 + ]
686 +}
687 +```
688 +
689 +---
690 +
691 +## 🔄 数据转换
692 +
693 +### 将页面数据转换为标准格式
694 +
695 +提取的数据需要转换为 LangChain Agent 可理解的标准格式:
696 +
697 +```javascript
698 +function convertToStandardFormat(extractedData) {
699 + return {
700 + formName: extractedData.name,
701 + fields: extractedData.field_list.map(field => ({
702 + key: field.field_name,
703 + component_props: {
704 + tag: field.tag,
705 + label: field.label,
706 + required: field.required,
707 + placeholder: field.placeholder || '',
708 + options: field.options || [],
709 + disabled: field.disabled || false,
710 + readonly: field.readonly || false
711 + }
712 + })),
713 + rules: extractedData.rule_list || []
714 + };
715 +}
716 +```
717 +
718 +---
719 +
720 +## ⚠️ 注意事项
721 +
722 +### 1. API 访问说明
723 +
724 +**表单查询 API 可以直接访问,无需特殊认证!**
725 +
726 +```bash
727 +# 最简单的调用方式
728 +curl "https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf"
729 +```
730 +
731 +**完整 URL 格式**
732 +```
733 +https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code={form_code}
734 +```
735 +
736 +### 2. 数据验证
737 +
738 +提取后需要验证数据完整性:
739 +
740 +```javascript
741 +function validateExtractedData(data) {
742 + const required = ['name', 'field_list'];
743 + const missing = required.filter(key => !data[key]);
744 +
745 + if (missing.length > 0) {
746 + throw new Error(`缺少必要字段: ${missing.join(', ')}`);
747 + }
748 +
749 + // 验证字段列表
750 + if (!Array.isArray(data.field_list) || data.field_list.length === 0) {
751 + throw new Error('字段列表为空或格式错误');
752 + }
753 +
754 + return true;
755 +}
756 +```
757 +
758 +### 3. 错误处理
759 +
760 +```javascript
761 +try {
762 + const config = await extractFormConfig(url);
763 + validateExtractedData(config);
764 + return convertToStandardFormat(config);
765 +} catch (error) {
766 + console.error('数据提取失败:', error.message);
767 + // 返回错误信息
768 + return {
769 + success: false,
770 + error: error.message,
771 + url: url
772 + };
773 +}
774 +```
775 +
776 +### 4. URL 解析
777 +
778 +从用户提供的表单 URL 中提取参数并构建 API 请求:
779 +
780 +```javascript
781 +// 示例 URL: https://oa-dev.onwall.cn/f/custom_form/front/index.html#/?code=nxqkbf
782 +function parseFormUrl(url) {
783 + const params = new URLSearchParams(url.split('?')[1]);
784 +
785 + return {
786 + // 基础参数
787 + form_code: params.get('code'),
788 + model: params.get('model'), // 预览模式
789 + data_id: params.get('data_id'), // 数据 ID(编辑时)
790 + page_type: params.get('page_type'), // add/edit/info/flow
791 + flow_node_code: params.get('flow_node_code'), // 流程节点
792 + force_back: params.get('force_back'), // 强制后台模式
793 + x_cycle: params.get('x_cycle'), // 周期 ID
794 + volunteer_source: params.get('volunteer_source'), // 义工来源
795 + openid: params.get('openid'), // 微信 openid
796 +
797 + };
798 +}
799 +
800 +// 使用示例
801 +const url = 'https://oa-dev.onwall.cn/f/custom_form/front/index.html#/?code=nxqkbf&page_type=add';
802 +const params = parseFormUrl(url);
803 +
804 +// 构建完整的 API 请求
805 +const apiParams = {
806 + f: 'custom_form',
807 + form_code: params.form_code,
808 + page_type: params.page_type || 'add',
809 + data_id: params.data_id || null,
810 + flow_node_code: params.flow_node_code || '',
811 + force_back: params.force_back || '',
812 + x_cycle: params.x_cycle || '',
813 + volunteer_source: params.volunteer_source || ''
814 +};
815 +```
816 +
817 +**URL 参数说明**
818 +
819 +| 参数 | 说明 | 示例值 | 必填 |
820 +|------|------|--------|------|
821 +| `code` | 表单代码 | `nxqkbf` | ✅ |
822 +| `model` | 模式 | `preview` | ❌ |
823 +| `page_type` | 页面类型 | `add`/`edit`/`info`/`flow` | ❌ |
824 +| `data_id` | 数据 ID | `12345` | ❌ |
825 +| `flow_node_code` | 流程节点代码 | `node_001` | ❌ |
826 +| `force_back` | 强制后台模式 | `1` | ❌ |
827 +| `x_cycle` | 周期 ID | `cycle_001` | ❌ |
828 +| `volunteer_source` | 义工来源 | `self`/`leader` | ❌ |
829 +| `openid` | 微信 openid | `oxxxxx` | ❌ |
830 +
831 +**简化的 API 调用**
832 +
833 +如果只需要获取表单结构,可以只传递必要的参数:
834 +
835 +```bash
836 +# 最小化调用(只需要 form_code)
837 +curl "https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf"
838 +
839 +# 完整调用(包含所有 URL 参数)
840 +curl "https://oa-dev.onwall.cn/srv/?a=query_form_all_field&f=custom_form&form_code=nxqkbf&page_type=add&data_id=null&flow_node_code=&force_back="
841 +```
842 +
843 +---
844 +
845 +## 🔗 相关文档
846 +
847 +- [00-数据结构总览.md](./00-数据结构总览.md)
848 +- [01-表单字段组件类型.md](./01-表单字段组件类型.md)
849 +- [06-规则控制系统.md](./06-规则控制系统.md)
850 +- [03-API-接口文档.md](./03-API-接口文档.md)
1 +# docs 文档索引
2 +
3 +本文档目录为 LangChain Agent 设计表单生成功能提供完整的项目架构和数据结构参考。
4 +
5 +## 文档列表
6 +
7 +### 00-数据结构总览.md
8 +**用途**:了解项目的核心数据结构
9 +**内容**
10 +- 表单字段结构 (FormField)
11 +- 组件属性结构 (ComponentProps)
12 +- 选项结构 (Option)
13 +- API 响应结构
14 +- 数据流转流程
15 +
16 +**适合**:Agent 需要理解整体数据模型时
17 +
18 +---
19 +
20 +### 01-表单字段组件类型.md
21 +**用途**:了解所有支持的组件类型
22 +**内容**
23 +- 30+ 种组件类型完整列表
24 +- 每种组件的用途和配置要点
25 +- 组件注册位置
26 +- 新增组件流程
27 +
28 +**适合**:Agent 需要选择合适的组件类型时
29 +
30 +---
31 +
32 +### 02-component_props-配置规范.md
33 +**用途**:了解组件属性的详细配置
34 +**内容**
35 +- 通用属性(所有组件)
36 +- 各类组件专属属性
37 +- Option 结构详解
38 +- 数据提交格式
39 +- 样式属性
40 +
41 +**适合**:Agent 生成具体组件配置时
42 +
43 +---
44 +
45 +### 03-API-接口文档.md
46 +**用途**:了解后端 API 接口
47 +**内容**
48 +- 表单管理接口(增删改查)
49 +- 数据管理接口
50 +- 组件接口
51 +- 通用接口(上传、验证码等)
52 +- HTTP 缓存控制
53 +
54 +**适合**:Agent 需要与后端交互时
55 +
56 +---
57 +
58 +### 04-示例数据.md
59 +**用途**:提供各类组件的配置示例
60 +**内容**
61 +- 20+ 个真实配置示例
62 +- 完整表单配置示例
63 +- 数据提交格式示例
64 +- LangChain Prompt 模板
65 +
66 +**适合**:Few-shot Learning、Prompt 模板构建
67 +
68 +---
69 +
70 +### 05-LangChain-Agent-使用指南.md
71 +**用途**:Agent 开发指南
72 +**内容**
73 +- 自然语言到组件映射表
74 +- 必填/数量/选项识别规则
75 +- 复杂需求处理
76 +- Prompt 模板
77 +- LangChain 代码示例
78 +- 验证和错误处理
79 +
80 +**适合****开发 LangChain Agent 的主要参考文档**
81 +
82 +---
83 +
84 +### 06-规则控制系统.md
85 +**用途**:表单字段显示/隐藏规则系统
86 +**内容**
87 +- 规则系统概述(SHOW/HIDE 模式、AND/OR 逻辑)
88 +- 规则数据结构(rule_list、Rule、RuleExpr)
89 +- 核心函数分析(checkRules、evaluateMultipleRules、evaluateRuleCondition、handleCascadeHiding)
90 +- 触发机制(@active 事件)
91 +- 实际示例(简单/复杂/多规则/级联隐藏)
92 +- LangChain Agent 集成要点
93 +
94 +**适合**:Agent 需要生成规则配置或理解字段间依赖关系时
95 +
96 +---
97 +
98 +### 07-页面数据提取指南.md
99 +**用途**:从实际运行的表单页面中提取配置数据
100 +**内容**
101 +- Pinia Store 数据存储结构
102 +- 数据提取方法(Chrome DevTools MCP / Vue DevTools API / 直接 API)
103 +- 完整提取流程(打开页面 → 等待加载 → 提取数据 → 解析配置)
104 +- 数据提取模板和代码示例
105 +- 数据转换和验证
106 +
107 +**适合**:用户提供表单 URL 时,需要从页面提取完整配置
108 +
109 +---
110 +
111 +## 快速查找指南
112 +
113 +### 按场景查找
114 +
115 +| 场景 | 推荐文档 |
116 +|------|----------|
117 +| 理解项目架构 | 00-数据结构总览 |
118 +| 选择组件类型 | 01-表单字段组件类型、05-LangChain-Agent-使用指南 |
119 +| 生成配置 | 02-component_props-配置规范、04-示例数据 |
120 +| Few-shot 示例 | 04-示例数据 |
121 +| 开发 Agent | 05-LangChain-Agent-使用指南 |
122 +| 生成规则配置 | 06-规则控制系统 |
123 +| 理解字段依赖 | 06-规则控制系统 |
124 +| **从 URL 提取表单** | **07-页面数据提取指南** |
125 +
126 +### 按组件类型查找
127 +
128 +| 组件类别 | 文档位置 |
129 +|----------|----------|
130 +| 文本输入类 | 01-表单字段组件类型(文档输入类) |
131 +| 选择类 | 01-表单字段组件类型(选择类)、04-示例数据(示例4-6) |
132 +| 日期时间类 | 01-表单字段组件类型(日期时间类)、04-示例数据(示例8) |
133 +| 上传类 | 01-表单字段组件类型(上传类)、04-示例数据(示例9) |
134 +| 特殊功能类 | 01-表单字段组件类型(特殊功能类)、04-示例数据(示例11-20) |
135 +
136 +### 按属性查找
137 +
138 +| 属性 | 文档位置 |
139 +|------|----------|
140 +| 通用属性 | 02-component_props-配置规范(通用属性) |
141 +| options 配置 | 02-component_props-配置规范(选项类组件属性) |
142 +| 图片上传配置 | 02-component_props-配置规范(上传类属性) |
143 +| 样式属性 | 02-component_props-配置规范(样式属性) |
144 +| 规则配置 | 06-规则控制系统(数据结构、触发机制) |
145 +| 字段显示/隐藏 | 06-规则控制系统(SHOW/HIDE 模式) |
146 +| 级联隐藏 | 06-规则控制系统(级联隐藏逻辑) |
147 +
148 +## Agent 开发建议
149 +
150 +### 1. 先读顺序
151 +
152 +1. **05-LangChain-Agent-使用指南** - 了解 Agent 设计要点
153 +2. **01-表单字段组件类型** - 了解可用的组件
154 +3. **04-示例数据** - 获取 Few-shot 示例
155 +4. **02-component_props-配置规范** - 深入了解配置细节
156 +5. **06-规则控制系统** - 了解字段显示/隐藏规则(如需动态表单)
157 +
158 +### 2. 关键数据结构
159 +
160 +Agent 必须正确理解的核心结构:
161 +
162 +```typescript
163 +// 最核心的结构
164 +FormField {
165 + key: string
166 + value: any
167 + component_props: {
168 + tag: string // 决定组件类型
169 + label: string // 显示名称
170 + required: boolean
171 + // ... 其他属性
172 + }
173 +}
174 +```
175 +
176 +### 3. 组件类型映射表
177 +
178 +直接参考 `05-LangChain-Agent-使用指南` 中的映射表,建立用户描述 → 组件类型的映射。
179 +
180 +### 4. Few-shot 示例使用
181 +
182 +`04-示例数据.md` 中提取典型示例作为 Prompt 的一部分,提高输出质量。
183 +
184 +### 5. 验证策略
185 +
186 +生成配置后,验证以下内容:
187 +- [ ] tag 值是否有效(参考 01 文档的组件列表)
188 +- [ ] required 是否为布尔值
189 +- [ ] 选项类组件是否有 options 数组
190 +- [ ] 上传类组件是否有合理的数量限制
191 +
192 +## 数据流转图
193 +
194 +```
195 +用户自然语言
196 +
197 + [Agent 解析]
198 +
199 + 意图识别 + 实体提取
200 +
201 + 组件类型选择 + 配置生成
202 +
203 + [验证输出]
204 +
205 +表单字段配置 JSON
206 +
207 + createComponentType()
208 +
209 + 动态表单渲染
210 +```
211 +
212 +## 联系方式
213 +
214 +如有问题或需要补充文档,请查阅:
215 +- 项目源码:`src/hooks/useComponentType.js`
216 +- 组件目录:`src/components/`
217 +- API 目录:`src/api/`