# 数据格式化

<cite>
**本文引用的文件**
- [ProTypeItem.vue](file://packages/components/ProTypeItem/ProTypeItem.vue)
- [util.js（组件库）](file://packages/libs/util.js)
- [ProForm.vue](file://packages/core/src/components/ProForm.vue)
- [ProSearch.vue 示例](file://src/views/ProSearch.vue)
- [ProTable.vue 示例](file://src/views/ProTable.vue)
- [util.js（核心库）](file://packages/core/src/libs/util.js)
</cite>

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

## 简介
本篇文档聚焦于 ProTypeItem 的“数据格式化”能力，系统讲解 format 属性的配置与使用方式，解释 commonToFn 函数如何将字符串转换为可执行的 JavaScript 函数，阐述“数据包装器（wapper）”机制及 formatWapper 属性的用法；并梳理从输入值到格式化值的完整处理流程。文档还提供数值、日期、字符串等常见格式化场景的示例指引，并给出格式化函数编写规范与性能优化建议，以及与 update:modelValue 事件的协同使用说明。

## 项目结构
围绕数据格式化的相关模块主要分布在：
- 组件层：ProTypeItem 负责渲染与格式化触发
- 工具层：commonToFn 提供字符串/函数/对象格式化入口
- 容器层：ProForm/ProSearch/ProTable 将格式化结果写回模型或合并为包装对象

```mermaid
graph TB
subgraph "组件层"
PTI["ProTypeItem.vue"]
end
subgraph "工具层"
UTIL["util.js组件库"]
end
subgraph "容器层"
PF["ProForm.vue"]
PS["ProSearch.vue 示例"]
PT["ProTable.vue 示例"]
end
PTI --> UTIL
PF --> PTI
PS --> PF
PT --> PF
```

图表来源
- [ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [util.js（组件库）:1-110](file://packages/libs/util.js#L1-L110)
- [ProForm.vue:80-144](file://packages/core/src/components/ProForm.vue#L80-L144)
- [ProSearch.vue 示例:1-59](file://src/views/ProSearch.vue#L1-L59)
- [ProTable.vue 示例:1-306](file://src/views/ProTable.vue#L1-L306)

章节来源
- [ProTypeItem.vue:1-132](file://packages/components/ProTypeItem/ProTypeItem.vue#L1-L132)
- [util.js（组件库）:1-110](file://packages/libs/util.js#L1-L110)
- [ProForm.vue:80-144](file://packages/core/src/components/ProForm.vue#L80-L144)

## 核心组件
- ProTypeItem：负责渲染具体输入控件，并在用户输入时触发格式化逻辑，将格式化后的值通过 update:modelValue 或包装后通过 wapperInput 事件回传给父容器。
- commonToFn：统一的格式化入口，支持传入函数、字符串表达式或对象（含 value 字段），将输入值转换为目标格式。
- ProForm/ProSearch/ProTable：作为容器，接收来自 ProTypeItem 的格式化结果，更新本地模型或合并为包装对象。

章节来源
- [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)
- [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- [ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)

## 架构总览
下面以序列图展示一次典型的数据格式化流程：用户在输入框中输入 -> ProTypeItem 触发 input -> commonToFn 执行格式化 -> 根据 formatWapper 决定是直接回写还是发出包装对象 -> 容器根据事件类型合并到模型。

```mermaid
sequenceDiagram
participant U as "用户"
participant PTI as "ProTypeItem"
participant UT as "commonToFn"
participant PF as "ProForm/容器"
U->>PTI : 输入值变更
PTI->>PTI : input(value)
PTI->>UT : commonToFn(that, format, value)
UT-->>PTI : 返回格式化后的值
alt formatWapper 为真
PTI-->>PF : $emit("wapperInput", {wapper, value})
PF->>PF : 合并 { ...model, ...wapper }
else 直接回写
PTI-->>PF : $emit("update : modelValue", wapper)
PF->>PF : 更新字段值
end
```

图表来源
- [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)
- [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- [ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)

## 详细组件分析

### ProTypeItem 的输入处理与格式化
- 输入监听：在组件 render 中为控件绑定 onUpdate:modelValue 回调，内部调用 input(value)。
- 格式化入口：当 renderItem 中存在 format 字段时，调用 commonToFn(this, format, value) 计算目标值。
- 输出策略：
  - 若 format.formatWapper 为真，则通过 wapperInput 事件向外传递 { wapper, value }，由容器决定如何合并到模型。
  - 否则直接通过 update:modelValue 将格式化后的值回写到模型。

```mermaid
flowchart TD
Start(["输入变更"]) --> CheckFormat{"是否存在 format?"}
CheckFormat --> |否| EmitDirect["emit update:modelValue(value)"]
CheckFormat --> |是| CallCommon["调用 commonToFn(that, format, value)"]
CallCommon --> WapperCheck{"formatWapper 是否为真?"}
WapperCheck --> |是| EmitWrapper["emit wapperInput({wapper, value})"]
WapperCheck --> |否| EmitDirect
EmitWrapper --> End(["结束"])
EmitDirect --> End
```

图表来源
- [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)

章节来源
- [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)

### commonToFn 的工作机制
- 支持三种形式的 format：
  - 函数：直接调用 format(value)
  - 字符串：通过 new Function("value", `return ${format};`).bind(that) 动态生成函数执行
  - 对象：若返回值为假且 format 为对象，则尝试 format.value
- 返回值：优先使用函数/字符串表达式的执行结果；若对象未命中则回退为原值。

```mermaid
flowchart TD
A["输入: that, format, value"] --> B{"format 类型?"}
B --> |Function| C["format(value)"]
B --> |String| D["new Function('value', 'return '+format).bind(that)(value)"]
B --> |Object| E{"nomalFn 结果为真?"}
E --> |是| F["返回 nomalFn 结果"]
E --> |否| G["尝试 format.value 并返回"]
C --> H["返回最终值"]
D --> H
F --> H
G --> H
```

图表来源
- [util.js（组件库）:57-78](file://packages/libs/util.js#L57-L78)

章节来源
- [util.js（组件库）:57-78](file://packages/libs/util.js#L57-L78)

### 数据包装器（wapper）与 formatWapper
- 当 formatWapper 为真时，ProTypeItem 不直接回写单值，而是发出 wapperInput 事件，携带 { wapper, value }。
- 容器（如 ProForm）收到该事件后，会将当前模型与 wapper 合并，再通过 update:modelValue 回写，从而实现“多字段打包”的格式化输出。

```mermaid
sequenceDiagram
participant PTI as "ProTypeItem"
participant PF as "ProForm"
PTI-->>PF : $emit("wapperInput", {wapper, value})
PF->>PF : const merged = {...model, ...wapper}
PF-->>PF : emit("update : modelValue", merged)
```

图表来源
- [ProTypeItem.vue:41-42](file://packages/components/ProTypeItem/ProTypeItem.vue#L41-L42)
- [ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)

章节来源
- [ProTypeItem.vue:41-42](file://packages/components/ProTypeItem/ProTypeItem.vue#L41-L42)
- [ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)

### 常见格式化场景与示例指引
- 数值格式化
  - 场景：将输入字符串转换为数字并进行四舍五入/保留小数位。
  - 指引：在 format.value 中编写字符串表达式，使用 Math/Number 等内置对象完成转换。
  - 参考路径：[ProTable.vue 示例:180-188](file://src/views/ProTable.vue#L180-L188)
- 日期格式化
  - 场景：将日期范围选择器的数组转换为 { start, end } 的对象，并格式化为指定字符串。
  - 指引：使用 dayjs 等日期库在 format.value 中进行格式化。
  - 参考路径：[ProSearch.vue 示例:31-35](file://src/views/ProSearch.vue#L31-L35)、[ProTable.vue 示例:39-43](file://src/views/ProTable.vue#L39-L43)
- 字符串处理
  - 场景：去除首尾空格、大小写转换、拼接前缀/后缀。
  - 指引：在 format.value 中使用字符串方法链式处理。
  - 参考路径：[ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)

章节来源
- [ProSearch.vue 示例:31-35](file://src/views/ProSearch.vue#L31-L35)
- [ProTable.vue 示例:39-43](file://src/views/ProTable.vue#L39-L43)
- [ProTable.vue 示例:180-188](file://src/views/ProTable.vue#L180-L188)
- [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)

### 编写格式化函数的规范与最佳实践
- 使用字符串表达式时，确保表达式可被 new Function 安全执行，避免访问未授权上下文。
- 在 formatWapper 为真时，返回的对象键名应与模型字段一致，以便容器合并。
- 对复杂逻辑建议封装为函数，便于复用与测试。
- 注意边界情况：空值、非预期类型、异常抛出等，必要时在容器侧进行兜底处理。

章节来源
- [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)

## 依赖关系分析
- ProTypeItem 依赖 commonToFn 进行格式化计算。
- ProForm/ProSearch/ProTable 作为容器，消费 ProTypeItem 的两种输出：update:modelValue 或 wapperInput。
- util.js（组件库）与 util.js（核心库）功能一致，保证在不同构建产物中行为一致。

```mermaid
graph LR
PTI["ProTypeItem.vue"] --> CT["commonToFn(util.js)"]
PF["ProForm.vue"] --> PTI
PS["ProSearch.vue 示例"] --> PF
PT["ProTable.vue 示例"] --> PF
CT --> |执行| UTIL["util.js组件库"]
```

图表来源
- [ProTypeItem.vue:4-5](file://packages/components/ProTypeItem/ProTypeItem.vue#L4-L5)
- [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- [ProForm.vue:80-96](file://packages/core/src/components/ProForm.vue#L80-L96)

章节来源
- [ProTypeItem.vue:4-5](file://packages/components/ProTypeItem/ProTypeItem.vue#L4-L5)
- [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- [ProForm.vue:80-96](file://packages/core/src/components/ProForm.vue#L80-L96)

## 性能考量
- 动态函数执行（new Function）会带来一定开销，建议：
  - 将高频格式化逻辑抽离为纯函数并在 format.value 中复用。
  - 避免在字符串表达式中进行重型计算，必要时在容器层缓存中间结果。
  - 对于大范围批量输入，考虑节流/防抖策略，减少频繁回写。
- 包装器合并（wapperInput）会触发深拷贝与对象合并，建议：
  - 控制返回对象的字段数量与层级，避免过大的 payload。
  - 在容器层仅合并必要的字段，减少不必要的响应式追踪。

## 故障排查指南
- 表达式语法错误
  - 现象：格式化未生效或报错。
  - 排查：确认 format.value 的字符串表达式语法正确，避免非法标识符与未闭合括号。
  - 参考路径：[util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- formatWapper 未生效
  - 现象：期望通过 wapperInput 合并多个字段，但只更新了单值。
  - 排查：确认 formatWapper 为真，且容器已监听并处理 wapperInput 事件。
  - 参考路径：[ProTypeItem.vue:41-42](file://packages/components/ProTypeItem/ProTypeItem.vue#L41-L42)、[ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)
- 日期格式化异常
  - 现象：日期范围格式化后显示异常。
  - 排查：确认 dayjs 等依赖可用，format.value 中的日期格式化方法正确。
  - 参考路径：[ProSearch.vue 示例:31-35](file://src/views/ProSearch.vue#L31-L35)、[ProTable.vue 示例:39-43](file://src/views/ProTable.vue#L39-L43)

章节来源
- [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
- [ProTypeItem.vue:41-42](file://packages/components/ProTypeItem/ProTypeItem.vue#L41-L42)
- [ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)
- [ProSearch.vue 示例:31-35](file://src/views/ProSearch.vue#L31-L35)
- [ProTable.vue 示例:39-43](file://src/views/ProTable.vue#L39-L43)

## 结论
ProTypeItem 的数据格式化体系通过 format 属性与 commonToFn 实现灵活的输入值转换，结合 formatWapper 与 wapperInput 事件，既能满足单值回写，也能实现多字段打包输出。合理设计 format.value 与容器合并策略，可在保证灵活性的同时兼顾性能与可维护性。

## 附录
- 术语
  - format：用于描述如何将输入值转换为目标格式的配置对象或表达式。
  - formatWapper：布尔标志，指示是否以“包装器”形式回传格式化结果。
  - wapper：由 formatWapper 启动时产生的包装对象，通常包含多个字段。
- 相关路径参考
  - [ProTypeItem.vue:37-49](file://packages/components/ProTypeItem/ProTypeItem.vue#L37-L49)
  - [util.js（组件库）:67-78](file://packages/libs/util.js#L67-L78)
  - [ProForm.vue:303-312](file://packages/core/src/components/ProForm.vue#L303-L312)
  - [ProSearch.vue 示例:31-35](file://src/views/ProSearch.vue#L31-L35)
  - [ProTable.vue 示例:39-43](file://src/views/ProTable.vue#L39-L43)