# 自定义字段类型

<cite>
**本文引用的文件**
- [src/views/ProForm.vue](file://src/views/ProForm.vue)
- [packages/components/ProForm.vue](file://packages/components/ProForm.vue)
- [packages/components/ProTypeItem/ProTypeItem.vue](file://packages/components/ProTypeItem/ProTypeItem.vue)
- [packages/components/ProSelect.vue](file://packages/components/ProSelect.vue)
- [packages/libs/request.js](file://packages/libs/request.js)
- [packages/libs/util.js](file://packages/libs/util.js)
- [packages/index.js](file://packages/index.js)
- [src/main.js](file://src/main.js)
- [packages/mixin/locale.js](file://packages/mixin/locale.js)
- [README.md](file://README.md)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本开发文档围绕 ProForm 的“自定义字段类型”能力展开，重点讲解如何通过 renderForm 函数或对象形式的渲染描述来扩展表单字段类型，并结合自定义组件实现复杂业务场景（如级联选择、富文本编辑器、地图选择器等）。文档同时说明 renderForm 的函数签名与参数传递机制、数据绑定与验证流程、以及如何与 UI 适配器集成以保证在不同 UI 框架下的统一表现。

## 项目结构
该仓库采用多包结构，核心组件集中在 packages/components 与 packages/core/src/components 中；示例页面位于 src/views 下。ProForm 作为表单容器，内部通过 ProTypeItem 渲染列配置中的 renderForm 描述；ProSelect 提供远程/本地数据源的选择器能力；request 工具负责统一请求封装；util 工具提供模板解析与通用函数转换；index.js 作为插件入口完成全局组件注册与指令挂载。

```mermaid
graph TB
subgraph "应用层"
Demo["示例页面<br/>src/views/ProForm.vue"]
Main["应用入口<br/>src/main.js"]
end
subgraph "组件库"
ProFormComp["ProForm 组件<br/>packages/components/ProForm.vue"]
ProTypeItem["ProTypeItem 渲染器<br/>packages/components/ProTypeItem/ProTypeItem.vue"]
ProSelect["ProSelect 选择器<br/>packages/components/ProSelect.vue"]
Request["请求工具<br/>packages/libs/request.js"]
Util["工具库<br/>packages/libs/util.js"]
IndexPkg["插件入口<br/>packages/index.js"]
end
Demo --> ProFormComp
ProFormComp --> ProTypeItem
ProTypeItem --> ProSelect
ProSelect --> Request
ProFormComp --> Util
Main --> IndexPkg
IndexPkg --> ProFormComp
IndexPkg --> ProSelect
```

图表来源
- [src/views/ProForm.vue:1-198](file://src/views/ProForm.vue#L1-L198)
- [packages/components/ProForm.vue:146-308](file://packages/components/ProForm.vue#L146-L308)
- [packages/components/ProTypeItem/ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [packages/components/ProSelect.vue:1-156](file://packages/components/ProSelect.vue#L1-L156)
- [packages/libs/request.js:1-78](file://packages/libs/request.js#L1-L78)
- [packages/libs/util.js:1-110](file://packages/libs/util.js#L1-L110)
- [packages/index.js:1-69](file://packages/index.js#L1-L69)
- [src/main.js:1-14](file://src/main.js#L1-L14)

章节来源
- [README.md:1-18](file://README.md#L1-L18)
- [src/views/ProForm.vue:1-198](file://src/views/ProForm.vue#L1-L198)
- [packages/components/ProForm.vue:146-308](file://packages/components/ProForm.vue#L146-L308)
- [packages/components/ProTypeItem/ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [packages/components/ProSelect.vue:1-156](file://packages/components/ProSelect.vue#L1-L156)
- [packages/libs/request.js:1-78](file://packages/libs/request.js#L1-L78)
- [packages/libs/util.js:1-110](file://packages/libs/util.js#L1-L110)
- [packages/index.js:1-69](file://packages/index.js#L1-L69)
- [src/main.js:1-14](file://src/main.js#L1-L14)

## 核心组件
- ProForm：表单容器，负责列分组、布局、校验、提交与重置。其内部通过 ProTypeItem 渲染列配置中的 renderForm 描述。
- ProTypeItem：通用渲染器，支持 renderForm 为函数或对象两种方式。函数模式下注入上下文参数，对象模式下支持 type、props、on、scopedSlots、style、text、useExp、format 等。
- ProSelect：基于 UI 组件的选择器，支持本地 list 与远程 request，具备数据映射 map（dataPath、titlePath、valuePath）与首项填充 first 等能力。
- request 工具：统一封装 axios 请求，支持字符串/对象/函数三种 request 形态，自动拼接查询参数与表单数据，注入 Token 头部。
- util 工具：提供模板字符串解析、JSON 序列化/反序列化（含函数与 undefined 处理）、通用字符串转函数、HTML 标签判定等。
- 插件入口 index.js：完成全局组件注册、指令挂载、国际化与全局属性注入。

章节来源
- [packages/components/ProForm.vue:146-308](file://packages/components/ProForm.vue#L146-L308)
- [packages/components/ProTypeItem/ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [packages/components/ProSelect.vue:1-156](file://packages/components/ProSelect.vue#L1-L156)
- [packages/libs/request.js:1-78](file://packages/libs/request.js#L1-L78)
- [packages/libs/util.js:1-110](file://packages/libs/util.js#L1-L110)
- [packages/index.js:1-69](file://packages/index.js#L1-L69)

## 架构总览
下面的时序图展示了从列配置到最终渲染与数据回写的关键流程，包括 renderForm 函数/对象的两种渲染模式、数据格式化与事件冒泡、以及与 UI 适配器的关系。

```mermaid
sequenceDiagram
participant Dev as "开发者配置<br/>columns.renderForm"
participant PF as "ProForm<br/>表单容器"
participant PTI as "ProTypeItem<br/>渲染器"
participant Comp as "具体组件<br/>如 ProSelect"
participant Req as "request 工具"
Dev->>PF : "配置 columns[...].renderForm"
PF->>PTI : "传入 item/renderItem/modelValue"
alt "renderForm 为函数"
PTI->>Dev : "调用函数并注入上下文参数"
Dev-->>PTI : "返回 VNode含 v-model 与事件"
else "renderForm 为对象"
PTI->>PTI : "解析 type/props/on/scopedSlots/style/text/useExp/format"
PTI->>Comp : "创建组件实例"
Comp->>Req : "远程请求可选"
Req-->>Comp : "返回数据并映射"
end
PTI->>PF : "update : modelValue 或 wapperInput"
PF->>PF : "触发 change/wapperInput 并单项校验"
```

图表来源
- [packages/components/ProForm.vue:207-303](file://packages/components/ProForm.vue#L207-L303)
- [packages/components/ProTypeItem/ProTypeItem.vue:26-124](file://packages/components/ProTypeItem/ProTypeItem.vue#L26-L124)
- [packages/components/ProSelect.vue:48-87](file://packages/components/ProSelect.vue#L48-L87)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)

## 详细组件分析

### renderForm 函数签名与参数传递机制
- 函数模式：当 columns[i].renderForm 为函数时，ProTypeItem 在渲染时会调用该函数并传入上下文对象，包含 h、onUpdate:modelValue、modelValue/value、input、row/outData 等键。该函数应返回一个 VNode，若未自动挂载 v-model，则由渲染器自动补齐。
- 对象模式：当 columns[i].renderForm 为对象时，支持以下键：
  - type：组件名或 HTML 标签名
  - props：透传给组件的属性
  - on：事件监听
  - scopedSlots：具名作用域插槽（支持数组转对象）
  - style：内联样式（支持 useExp 表达式动态计算）
  - text：内容文本（支持 useExp 动态渲染）
  - useExp：启用表达式求值
  - format：格式化函数/表达式，用于将输入值转换为存储值或包裹更新（支持 formatWapper）
- 值绑定与事件冒泡：ProTypeItem 内部通过 input 方法将值转换后通过 update:modelValue 或 wapperInput 事件向上冒泡，ProForm 接收后通过 change/wapperInput 更新 modelValue 并进行单项校验。

章节来源
- [packages/components/ProTypeItem/ProTypeItem.vue:26-124](file://packages/components/ProTypeItem/ProTypeItem.vue#L26-L124)
- [packages/components/ProForm.vue:273-293](file://packages/components/ProForm.vue#L273-L293)

### 数据绑定与验证
- 数据绑定：ProForm 通过 getValue 获取当前字段值，将 modelValue 与 item.key 绑定到 ProTypeItem；ProTypeItem 将 update:modelValue 事件转换为 input 回调，再由 ProForm 的 change/wapperInput 合并回 modelValue。
- 单项校验：每次值变更后，ProForm 使用 validateField 对当前字段进行单项校验，避免全量校验带来的性能问题与体验问题。
- 规则转换：ruleChange 会将字符串正则转换为正则对象，确保校验规则生效。

章节来源
- [packages/components/ProForm.vue:207-303](file://packages/components/ProForm.vue#L207-L303)

### 自定义组件与 UI 适配器集成
- 组件注册：插件入口 index.js 遍历组件集合并注册为全局组件，确保在 renderForm 中可直接使用组件名或 HTML 标签名。
- UI 适配：示例中使用了 view-ui-plus 的 Select、Input、Button 等组件；ProSelect 内部也基于 UI 组件实现，通过 v-bind 将 attrs 透传，从而在不同 UI 框架下保持一致行为。
- 全局属性：插件注入 $http、$dayjs、$lodash、$PRO_IVIEW 等全局属性，便于在组件与工具中复用。

章节来源
- [packages/index.js:1-69](file://packages/index.js#L1-L69)
- [src/main.js:1-14](file://src/main.js#L1-L14)
- [packages/components/ProSelect.vue:121-144](file://packages/components/ProSelect.vue#L121-L144)

### 复杂业务场景示例

#### 级联选择器
- 思路：利用 renderForm 对象模式，type 指向自定义级联组件（如 Cascader），通过 props 传入数据源与映射，借助 request 工具实现远程数据加载；使用 scopedSlots 实现联动逻辑（上游值变化时动态刷新下游选项）。
- 关键点：使用 format 包裹更新值，或通过 wapperInput 批量合并多个字段；在 change/wapperInput 中触发单项校验。
- 参考实现路径
  - [packages/components/ProTypeItem/ProTypeItem.vue:52-98](file://packages/components/ProTypeItem/ProTypeItem.vue#L52-L98)
  - [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)
  - [packages/components/ProForm.vue:273-293](file://packages/components/ProForm.vue#L273-L293)

#### 富文本编辑器
- 思路：renderForm 返回富文本组件（如基于 quill 或 tinymce 的封装），通过 props 初始化配置，使用 onUpdate:modelValue 接收富文本内容并转换为存储值；必要时使用 format 进行二次处理。
- 关键点：确保组件支持 v-model；在 format 中处理空值与格式化逻辑；通过单项校验提升交互体验。
- 参考实现路径
  - [packages/components/ProTypeItem/ProTypeItem.vue:99-122](file://packages/components/ProTypeItem/ProTypeItem.vue#L99-L122)
  - [packages/libs/util.js:56-78](file://packages/libs/util.js#L56-L78)

#### 地图选择器
- 思路：renderForm 返回地图组件（如基于高德/腾讯地图的封装），通过 props 传入初始位置与搜索能力；在组件内部完成坐标拾取与地址解析，通过 onUpdate:modelValue 输出经纬度与地址信息；使用 format 将多字段合并为对象或字符串。
- 关键点：使用 formatWapper 触发 wapperInput，实现多字段一次性更新；在 change/wapperInput 中进行单项校验。
- 参考实现路径
  - [packages/components/ProTypeItem/ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)
  - [packages/components/ProForm.vue:284-293](file://packages/components/ProForm.vue#L284-L293)

### 示例：ProSelect 远程数据与映射
- request 支持字符串 URL、对象配置与函数形态；map 支持 dataPath、titlePath、valuePath；first 为无值时自动选择首项并回写。
- 适用场景：下拉选择、单选/多选、带搜索过滤等。

```mermaid
flowchart TD
Start(["初始化"]) --> CheckList["是否存在本地 list?"]
CheckList --> |是| UseList["使用本地 list"]
CheckList --> |否| HasRequest{"是否有 request?"}
HasRequest --> |是| Fetch["调用 request 工具获取数据"]
Fetch --> MapData["根据 dataPath/titlePath/valuePath 映射"]
HasRequest --> |否| EndEmpty["结束无数据"]
MapData --> First{"是否启用 first 且当前值为空?"}
First --> |是| SetFirst["设置首项为默认值并回写"]
First --> |否| Done["完成"]
UseList --> First
SetFirst --> Done
Done --> End(["结束"])
```

图表来源
- [packages/components/ProSelect.vue:48-87](file://packages/components/ProSelect.vue#L48-L87)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)

章节来源
- [packages/components/ProSelect.vue:1-156](file://packages/components/ProSelect.vue#L1-L156)
- [packages/libs/request.js:1-78](file://packages/libs/request.js#L1-L78)

## 依赖关系分析
- ProForm 依赖 ProTypeItem 进行字段渲染，依赖 request 工具进行远程数据请求，依赖 util 工具进行表达式与格式化处理。
- ProTypeItem 既支持函数渲染也支持对象渲染，内部通过 commonToFn、isHTMLTag 等工具实现通用能力。
- 插件入口 index.js 负责全局组件注册与指令挂载，确保在 renderForm 中可用。

```mermaid
graph LR
PF["ProForm"] --> PTI["ProTypeItem"]
PTI --> U["util 工具"]
PTI --> Cmp["具体组件如 ProSelect"]
Cmp --> R["request 工具"]
Main["应用入口"] --> Pkg["插件入口 index.js"]
Pkg --> PF
Pkg --> Cmp
```

图表来源
- [packages/components/ProForm.vue:146-308](file://packages/components/ProForm.vue#L146-L308)
- [packages/components/ProTypeItem/ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [packages/libs/util.js:1-110](file://packages/libs/util.js#L1-L110)
- [packages/libs/request.js:1-78](file://packages/libs/request.js#L1-L78)
- [packages/index.js:1-69](file://packages/index.js#L1-L69)
- [src/main.js:1-14](file://src/main.js#L1-L14)

章节来源
- [packages/components/ProForm.vue:146-308](file://packages/components/ProForm.vue#L146-L308)
- [packages/components/ProTypeItem/ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [packages/libs/util.js:1-110](file://packages/libs/util.js#L1-L110)
- [packages/libs/request.js:1-78](file://packages/libs/request.js#L1-L78)
- [packages/index.js:1-69](file://packages/index.js#L1-L69)
- [src/main.js:1-14](file://src/main.js#L1-L14)

## 性能考量
- 单项校验：每次变更后仅对当前字段进行校验，减少不必要的全量校验开销。
- 表达式求值：useExp 与 commonToFn 仅在需要时执行，避免频繁计算。
- 数据映射：ProSelect 的 map 与 request 工具在首次渲染时完成，后续通过 watch 监听 request/list 变化，避免重复请求。
- 组件宽度策略：ProTypeItem 通过 comFillArr 控制宽度填充，减少布局抖动。

章节来源
- [packages/components/ProForm.vue:273-303](file://packages/components/ProForm.vue#L273-L303)
- [packages/components/ProTypeItem/ProTypeItem.vue:26-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L26-L49)
- [packages/components/ProSelect.vue:106-118](file://packages/components/ProSelect.vue#L106-L118)

## 故障排查指南
- renderForm 未生效
  - 检查 columns[i].renderForm 是否正确传入函数或对象；确认对象模式下 type 是否为已注册组件名或有效 HTML 标签名。
  - 若为函数模式，确认返回的 VNode 是否挂载了 v-model；若未挂载，渲染器会自动补齐。
- 值无法回写
  - 确认组件是否正确发出 update:modelValue 事件；ProTypeItem 的 input 会将值经 format 转换后回传。
  - 若使用 formatWapper，请确认触发的是 wapperInput 事件。
- 远程数据不更新
  - 检查 request 对象的 request/url 是否正确；确认 watch 对 request/list 的监听是否生效。
  - 如需动态替换 URL 中的占位符，使用 request 工具内置的 replactUrl。
- 校验异常
  - 确认 rules 中的 pattern 是否为正则对象；ProForm 会在 ruleChange 中将其转换为正则。
  - 若为自定义校验，建议在 change/wapperInput 后调用 validateField 对当前字段进行单项校验。

章节来源
- [packages/components/ProTypeItem/ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)
- [packages/components/ProSelect.vue:61-83](file://packages/components/ProSelect.vue#L61-L83)
- [packages/libs/request.js:23-31](file://packages/libs/request.js#L23-L31)
- [packages/components/ProForm.vue:251-259](file://packages/components/ProForm.vue#L251-L259)

## 结论
通过 renderForm 的函数与对象两种渲染模式，ProForm 能够灵活地扩展各类自定义字段类型。配合 ProTypeItem 的统一渲染与事件冒泡机制、request 工具的远程数据能力、以及 util 工具的表达式与格式化支持，开发者可以快速实现复杂业务场景（如级联选择、富文本、地图选择器等）。同时，插件入口与 UI 适配器确保了在不同 UI 框架下的统一表现与良好的开发体验。

## 附录
- 快速上手清单
  - 在 columns[i].renderForm 中提供函数或对象描述
  - 确保组件支持 v-model 或在函数中手动处理 update:modelValue
  - 使用 format/formatWapper 进行值转换与批量更新
  - 在 change/wapperInput 后调用 validateField 进行单项校验
  - 如需远程数据，使用 request 工具并配置 map 映射
- 参考示例路径
  - [src/views/ProForm.vue:36-91](file://src/views/ProForm.vue#L36-L91)
  - [packages/components/ProTypeItem/ProTypeItem.vue:99-122](file://packages/components/ProTypeItem/ProTypeItem.vue#L99-L122)
  - [packages/components/ProSelect.vue:61-83](file://packages/components/ProSelect.vue#L61-L83)