# ProSelect 选择器

<cite>
**本文引用的文件**
- [packages/components/ProSelect.vue](file://packages/components/ProSelect.vue)
- [packages/core/src/components/ProSelect.vue](file://packages/core/src/components/ProSelect.vue)
- [src/views/ProSelect.vue](file://src/views/ProSelect.vue)
- [packages/libs/request.js](file://packages/libs/request.js)
- [packages/libs/util.js](file://packages/libs/util.js)
- [packages/core/src/ui-adapter/inject.ts](file://packages/core/src/ui-adapter/inject.ts)
- [packages/core/src/ui-adapter/types.ts](file://packages/core/src/ui-adapter/types.ts)
- [packages/core/src/ui-adapter/index.ts](file://packages/core/src/ui-adapter/index.ts)
- [packages/adapters/view-ui-plus/types/index.d.ts](file://packages/adapters/view-ui-plus/types/index.d.ts)
- [packages/index.js](file://packages/index.js)
- [lib/main/pro_iview.js](file://lib/main/pro_iview.js)
- [MIGRATION.md](file://MIGRATION.md)
- [src/main.js](file://src/main.js)
- [README.md](file://README.md)
</cite>

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

## 简介
ProSelect 是一个基于 Vue 3 的高阶选择器组件，支持单选与多选、远程数据请求、本地静态数据、标题模板渲染、自动选中首项等功能。它通过 UI 适配器机制解耦底层 UI 库（如 view-ui-plus、element-plus），在不同 UI 框架间保持一致的使用体验。组件内置筛选、清空、转移渲染等特性，并提供灵活的数据映射与自定义展示能力。

**新增功能**：ProSelect 现已支持全局配置映射，通过 `$PRO_IVIEW.map` 全局配置简化组件配置过程，减少重复的 map 参数传递。

## 项目结构
本仓库采用多包结构，ProSelect 在两个层面存在：
- packages/components：面向向后兼容的轻量封装版本，直接依赖 view-ui-plus 的 Select/Option/Icon。
- packages/core/src：面向多 UI 适配器的核心版本，通过 useUIAdapter 动态注入 Select/Option/Icon 等组件。

此外，组件依赖统一的请求封装与模板工具，以及 UI 适配器的注入机制。

```mermaid
graph TB
subgraph "组件层"
C1["packages/components/ProSelect.vue"]
C2["packages/core/src/components/ProSelect.vue"]
end
subgraph "工具层"
L1["packages/libs/request.js"]
L2["packages/libs/util.js"]
end
subgraph "适配器层"
A1["packages/core/src/ui-adapter/inject.ts"]
A2["packages/core/src/ui-adapter/types.ts"]
A3["packages/core/src/ui-adapter/index.ts"]
D1["packages/adapters/view-ui-plus/types/index.d.ts"]
end
subgraph "全局配置层"
G1["packages/index.js<br/>$PRO_IVIEW.map"]
G2["lib/main/pro_iview.js<br/>$PRO_IVIEW.map"]
end
subgraph "示例与入口"
V1["src/views/ProSelect.vue"]
M1["src/main.js"]
R1["README.md"]
end
C1 --> L1
C1 --> L2
C2 --> L1
C2 --> L2
C2 --> A1
A1 --> A2
A1 --> A3
D1 --> A2
G1 --> G2
V1 --> C1
V1 --> C2
M1 --> V1
R1 --> C1
R1 --> C2
```

**图表来源**
- [packages/components/ProSelect.vue:1-170](file://packages/components/ProSelect.vue#L1-L170)
- [packages/core/src/components/ProSelect.vue:1-168](file://packages/core/src/components/ProSelect.vue#L1-L168)
- [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/core/src/ui-adapter/inject.ts:1-43](file://packages/core/src/ui-adapter/inject.ts#L1-L43)
- [packages/core/src/ui-adapter/types.ts:1-60](file://packages/core/src/ui-adapter/types.ts#L1-L60)
- [packages/core/src/ui-adapter/index.ts:1-7](file://packages/core/src/ui-adapter/index.ts#L1-L7)
- [packages/adapters/view-ui-plus/types/index.d.ts:1-33](file://packages/adapters/view-ui-plus/types/index.d.ts#L1-L33)
- [packages/index.js:16-19](file://packages/index.js#L16-L19)
- [lib/main/pro_iview.js:15:1](file://lib/main/pro_iview.js#L15-L1)
- [src/views/ProSelect.vue:1-115](file://src/views/ProSelect.vue#L1-L115)
- [src/main.js:1-14](file://src/main.js#L1-L14)
- [README.md:1-18](file://README.md#L1-L18)

**章节来源**
- [README.md:1-18](file://README.md#L1-L18)
- [src/main.js:1-14](file://src/main.js#L1-L14)

## 核心组件
- 组件名称：ProSelect
- 组件职责：
  - 负责数据加载（静态 list 或远程 request）
  - 数据映射（支持局部 map 和全局 $PRO_IVIEW.map）
  - 值类型处理（字符串/数组，支持多选）
  - 标题模板渲染（支持 ES6 模板语法）
  - 自动选中首项（可选）
  - 与 UI 适配器对接，动态解析 Select/Option/Icon
- 关键属性（props）：
  - request：远程请求配置或 URL
  - method：HTTP 方法，默认 GET
  - map：数据映射对象，包含 dataPath、titlePath、valuePath
  - first：是否自动选中首项
  - list：本地静态数据
  - modelValue：双向绑定值
  - valueType：值类型提示（string/array）
- 计算属性：
  - mergedMap：合并局部 map 和全局 $PRO_IVIEW.map 的最终映射配置
  - innerValue：根据 attrs.multiple 决定单选/多选的内部值形态
- 事件：
  - update:modelValue：用于 v-model 更新
  - change：当选中首项时触发（当启用 first 且当前无值）

**章节来源**
- [packages/components/ProSelect.vue:14-47](file://packages/components/ProSelect.vue#L14-L47)
- [packages/components/ProSelect.vue:44-60](file://packages/components/ProSelect.vue#L44-L60)
- [packages/components/ProSelect.vue:88-105](file://packages/components/ProSelect.vue#L88-L105)
- [packages/core/src/components/ProSelect.vue:26-59](file://packages/core/src/components/ProSelect.vue#L26-L59)
- [packages/core/src/components/ProSelect.vue:100-117](file://packages/core/src/components/ProSelect.vue#L100-L117)

## 架构总览
ProSelect 的运行时架构分为四层：
- 视图层：组件模板渲染 Select/Option/Icon，并支持 transfer、clearable、filterable 等属性透传
- 逻辑层：负责数据加载、映射、模板渲染、首项自动选择、值类型转换
- 配置层：支持局部 map 和全局 $PRO_IVIEW.map 的双重配置机制
- 适配层：通过 useUIAdapter 注入 UI 组件，实现对不同 UI 库的解耦

```mermaid
sequenceDiagram
participant U as "用户"
participant S as "ProSelect 组件"
participant G as "$PRO_IVIEW.map 全局配置"
participant Req as "请求封装 request.js"
participant Util as "模板工具 util.js"
participant UI as "UI 适配器"
U->>S : "挂载并传入 props"
S->>S : "init() 判断 valueType 和 modelValue 类型"
alt "存在 list"
S->>S : "使用本地 list 作为 innerList"
else "存在 request"
S->>Req : "fetchList() 发起请求"
Req-->>S : "返回响应数据"
S->>S : "按 dataPath 解析 innerList"
end
alt "first 且无值"
S->>S : "选中首项并触发 change"
end
S->>Util : "getExpText() 渲染标题支持模板"
S->>G : "合并全局 map 配置"
S->>UI : "动态解析 Select/Option/Icon"
S-->>U : "渲染下拉选项并支持筛选/清空"
```

**图表来源**
- [packages/components/ProSelect.vue:48-83](file://packages/components/ProSelect.vue#L48-L83)
- [packages/core/src/components/ProSelect.vue:60-95](file://packages/core/src/components/ProSelect.vue#L60-L95)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)
- [packages/libs/util.js:4-19](file://packages/libs/util.js#L4-L19)
- [packages/core/src/ui-adapter/inject.ts:23-34](file://packages/core/src/ui-adapter/inject.ts#L23-L34)

## 详细组件分析

### 单选与多选模式
- 单选：innerValue 直接绑定 modelValue；选择后更新 v-model
- 多选：通过 attrs.multiple 控制；当 modelValue 为字符串时按逗号拆分；当为数组时直接使用
- 值类型：valueType 与 modelValue 类型共同决定是否按字符串处理

```mermaid
flowchart TD
Start(["进入 innerValue.get"]) --> CheckMultiple{"attrs.multiple ?"}
CheckMultiple --> |是| IsArray{"modelValue 是数组 ?"}
IsArray --> |是| UseAsIs["直接使用数组"]
IsArray --> |否| SplitStr["按逗号拆分为数组"]
CheckMultiple --> |否| UseSingle["使用单值 modelValue"]
UseAsIs --> End(["返回"])
SplitStr --> End
UseSingle --> End
```

**图表来源**
- [packages/components/ProSelect.vue:101-104](file://packages/components/ProSelect.vue#L101-L104)
- [packages/core/src/components/ProSelect.vue:102-110](file://packages/core/src/components/ProSelect.vue#L102-L110)

**章节来源**
- [packages/components/ProSelect.vue:91-104](file://packages/components/ProSelect.vue#L91-L104)
- [packages/core/src/components/ProSelect.vue:101-116](file://packages/core/src/components/ProSelect.vue#L101-L116)

### 数据源配置与加载流程
- 静态数据：直接传入 list，组件使用该数组作为选项列表
- 远程数据：传入 request（字符串 URL 或对象），内部通过 request.js 封装发起请求，按 dataPath 提取数据
- 首项自动选择：当 first 为真且当前无值时，自动选择第一条并触发 change 事件
- 响应式更新：watch request/list，变更时重新初始化

```mermaid
sequenceDiagram
participant Comp as "ProSelect"
participant Watch as "watch(request/list)"
participant Req as "request.js"
participant Util as "util.js"
Comp->>Watch : "监听 request/list"
Watch->>Comp : "触发 init()"
alt "list 存在"
Comp->>Comp : "innerList = list"
else "request 存在"
Comp->>Req : "customRequest({ request, method })"
Req-->>Comp : "返回响应"
Comp->>Comp : "按 dataPath 取 innerList"
end
alt "first 且无值"
Comp->>Comp : "选中首项并 emit change"
end
```

**图表来源**
- [packages/components/ProSelect.vue:61-83](file://packages/components/ProSelect.vue#L61-L83)
- [packages/core/src/components/ProSelect.vue:73-95](file://packages/core/src/components/ProSelect.vue#L73-L95)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)

**章节来源**
- [packages/components/ProSelect.vue:61-83](file://packages/components/ProSelect.vue#L61-L83)
- [packages/core/src/components/ProSelect.vue:73-95](file://packages/core/src/components/ProSelect.vue#L73-L95)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)

### 标题模板与自定义渲染
- 标题渲染：通过 map.titlePath 支持普通字段或 ES6 模板语法（如 "${field}"）
- 模板解析：util.template 将模板字符串中的占位符替换为对应字段值
- 图标支持：每个 Option 可显示图标（icon 字段）
- 自定义复杂内容：可通过 titlePath 返回复杂字符串或结合 UI 适配器进行更复杂的渲染（需配合上层 UI 组件能力）

```mermaid
flowchart TD
A["输入 item"] --> B{"titlePath 是否包含模板语法"}
B --> |否| C["直接取字段值"]
B --> |是| D["util.template 渲染"]
C --> E["输出标题文本"]
D --> E
```

**图表来源**
- [packages/components/ProSelect.vue:54-60](file://packages/components/ProSelect.vue#L54-L60)
- [packages/core/src/components/ProSelect.vue:66-72](file://packages/core/src/components/ProSelect.vue#L66-L72)
- [packages/libs/util.js:4-19](file://packages/libs/util.js#L4-L19)

**章节来源**
- [packages/components/ProSelect.vue:121-144](file://packages/components/ProSelect.vue#L121-L144)
- [packages/core/src/components/ProSelect.vue:133-155](file://packages/core/src/components/ProSelect.vue#L133-L155)
- [packages/libs/util.js:4-19](file://packages/libs/util.js#L4-L19)

### UI 适配器与样式定制
- 适配器注入：通过 useUIAdapter 动态解析 Select/Option/Icon 等组件
- 适配器接口：UIAdapter 需要提供 Form/Form/FormItem/Input/Select/Option/Checkbox/Button/Table/Modal/Message/Icon/Page/install 等
- 样式：组件提供 scoped 样式，包含选项水平排列与对齐等基础样式
- 使用方式：在应用中安装 ProCore 并传入 adapter，即可让 ProSelect 使用对应 UI 库的组件

```mermaid
classDiagram
class UIAdapter {
+Form
+FormItem
+Input
+Select
+Option
+Checkbox
+Button
+Table
+Modal
+Message
+Icon
+Page
+install(app)
}
class ProSelect_Core {
+Select
+Option
+Icon
+init()
+fetchList()
+getExpText()
}
ProSelect_Core --> UIAdapter : "useUIAdapter() 注入"
```

**图表来源**
- [packages/core/src/ui-adapter/types.ts:9-48](file://packages/core/src/ui-adapter/types.ts#L9-L48)
- [packages/core/src/components/ProSelect.vue:10-18](file://packages/core/src/components/ProSelect.vue#L10-L18)
- [packages/adapters/view-ui-plus/types/index.d.ts:6-26](file://packages/adapters/view-ui-plus/types/index.d.ts#L6-L26)

**章节来源**
- [packages/core/src/ui-adapter/inject.ts:23-34](file://packages/core/src/ui-adapter/inject.ts#L23-L34)
- [packages/core/src/ui-adapter/types.ts:9-48](file://packages/core/src/ui-adapter/types.ts#L9-L48)
- [packages/adapters/view-ui-plus/types/index.d.ts:6-26](file://packages/adapters/view-ui-plus/types/index.d.ts#L6-L26)
- [packages/core/src/components/ProSelect.vue:133-155](file://packages/core/src/components/ProSelect.vue#L133-L155)

## 全局配置支持

### $PRO_IVIEW.map 全局映射配置
ProSelect 现已支持通过 `$PRO_IVIEW.map` 全局配置来简化组件配置过程。当组件实例化时，会优先使用局部 map 配置，如果局部配置不存在，则自动回退到全局 $PRO_IVIEW.map 配置。

#### 配置优先级
1. **局部配置**：组件 props 中的 map 对象
2. **全局配置**：应用安装时通过 `$PRO_IVIEW.map` 设置的全局配置
3. **默认配置**：组件内置的默认值（dataPath="data", titlePath="title", valuePath="value"）

#### 全局配置设置方式
在应用安装时通过 `pro_iview.map` 选项设置全局映射配置：

```javascript
// 安装组件库时设置全局映射
app.use(ProComponents, {
  pro_iview: {
    map: {
      dataPath: "data",
      titlePath: "title",
      valuePath: "value"
    }
  }
});
```

#### 合并机制实现
组件通过 `mergedMap` 计算属性实现配置合并：

```javascript
mergedMap() {
  return {
    dataPath:
      _.get(this.map, "dataPath") ||
      _.get(this.$PRO_IVIEW, "map.dataPath") ||
      "data",
    titlePath:
      _.get(this.map, "titlePath") ||
      _.get(this.$PRO_IVIEW, "map.titlePath") ||
      "title",
    valuePath:
      _.get(this.map, "valuePath") ||
      _.get(this.$PRO_IVIEW, "map.valuePath") ||
      "value"
  };
}
```

#### 使用场景
- **统一数据结构**：在大型项目中，所有选择器组件共享相同的数据结构约定
- **减少重复配置**：避免在每个组件实例中重复设置相同的映射规则
- **集中管理**：通过全局配置统一管理数据映射规则，便于维护和修改

#### 配置示例
```javascript
// 全局配置
app.use(ProComponents, {
  pro_iview: {
    map: {
      dataPath: "data",
      titlePath: "title",
      valuePath: "value"
    }
  }
});

// 组件使用
<pro-select v-model="value" request="api/data"></pro-select>
// 等同于
<pro-select 
  v-model="value" 
  request="api/data"
  :map="{ dataPath: 'data', titlePath: 'title', valuePath: 'value' }"
></pro-select>
```

**章节来源**
- [packages/components/ProSelect.vue:44-60](file://packages/components/ProSelect.vue#L44-L60)
- [packages/index.js:16-19](file://packages/index.js#L16-L19)
- [lib/main/pro_iview.js:15:1](file://lib/main/pro_iview.js#L15-L1)

## 依赖关系分析
- 组件依赖 request.js 进行网络请求，支持字符串 URL、对象配置与函数回调
- 模板渲染依赖 util.js 的 template 函数，支持 ES6 模板语法
- 核心版本通过 useUIAdapter 注入 UI 组件，避免硬编码依赖具体 UI 库
- **新增**：组件支持 $PRO_IVIEW.map 全局配置，通过 lodash 的 get 方法实现深度获取
- 示例页面演示了两种用法：组件层与核心层的 ProSelect

```mermaid
graph LR
PS["ProSelect.vue"] --> REQ["request.js"]
PS --> UTIL["util.js"]
PS --> UIAD["useUIAdapter()"]
PS --> PROIVIEW["$PRO_IVIEW.map"]
DEMO["src/views/ProSelect.vue"] --> PS
MAIN["src/main.js"] --> DEMO
```

**图表来源**
- [packages/components/ProSelect.vue:3-4](file://packages/components/ProSelect.vue#L3-L4)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)
- [packages/libs/util.js:4-19](file://packages/libs/util.js#L4-L19)
- [packages/core/src/components/ProSelect.vue:5](file://packages/core/src/components/ProSelect.vue#L5)
- [packages/index.js:16-19](file://packages/index.js#L16-L19)
- [src/views/ProSelect.vue:14-111](file://src/views/ProSelect.vue#L14-L111)
- [src/main.js:1-14](file://src/main.js#L1-14)

**章节来源**
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)
- [packages/libs/util.js:4-19](file://packages/libs/util.js#L4-L19)
- [packages/core/src/components/ProSelect.vue:5](file://packages/core/src/components/ProSelect.vue#L5)
- [packages/index.js:16-19](file://packages/index.js#L16-L19)
- [src/views/ProSelect.vue:14-111](file://src/views/ProSelect.vue#L14-L111)
- [src/main.js:1-14](file://src/main.js#L1-14)

## 性能考虑
- 数据加载策略
  - 静态数据：list 较大时建议分页或懒加载，避免一次性渲染过多 Option
  - 远程数据：合理设置缓存与去重，避免重复请求
- 渲染优化
  - 使用 transfer 属性减少 DOM 位置计算开销
  - 标题模板仅在必要时计算，避免复杂模板导致频繁重渲染
- 值类型处理
  - 多选时尽量使用数组，避免字符串拆分带来的额外处理
- UI 适配器
  - 通过注入机制减少组件内硬编码，便于按需引入与 Tree Shaking
- **新增**：全局配置优化
  - 通过 $PRO_IVIEW.map 减少重复的 map 对象创建和传递
  - 避免在多个组件实例中重复设置相同的映射配置

## 故障排查指南
- 未安装 UI 适配器
  - 现象：控制台抛出错误，提示未安装 UI 适配器
  - 处理：确保在应用中安装并传入 adapter
- 请求失败或数据为空
  - 现象：下拉列表为空或报错
  - 处理：检查 request 配置、dataPath 是否正确、服务端返回格式
- 标题模板语法错误
  - 现象：模板渲染异常或抛出语法错误
  - 处理：确认模板字符串合法，字段名与数据结构一致
- 多选值类型不匹配
  - 现象：多选无法正常工作或值被拆分为字符串
  - 处理：确保 modelValue 为数组，或移除 valueType 的强制约束
- **新增**：全局配置未生效
  - 现象：组件仍使用默认映射而非全局配置
  - 处理：确认应用安装时正确设置了 pro_iview.map，检查 $PRO_IVIEW.map 是否在组件实例化前已设置

**章节来源**
- [packages/core/src/ui-adapter/inject.ts:26-31](file://packages/core/src/ui-adapter/inject.ts#L26-L31)
- [packages/libs/request.js:39-77](file://packages/libs/request.js#L39-L77)
- [packages/libs/util.js:4-19](file://packages/libs/util.js#L4-L19)
- [packages/components/ProSelect.vue:91-104](file://packages/components/ProSelect.vue#L91-L104)
- [packages/index.js:16-19](file://packages/index.js#L16-L19)

## 结论
ProSelect 提供了统一的选择器抽象，既能满足快速开发的静态/远程数据需求，又能在多 UI 框架间保持一致的使用体验。通过 UI 适配器与模板渲染机制，组件具备良好的扩展性与可维护性。

**新增功能总结**：
- 全局配置支持：通过 $PRO_IVIEW.map 实现全局映射配置，减少重复配置
- 配置优先级：局部配置 > 全局配置 > 默认配置，确保灵活性
- 集中管理：统一管理数据映射规则，便于维护和修改

建议在实际项目中结合业务场景选择合适的数据源与渲染策略，并遵循性能优化建议以获得最佳用户体验。对于大型项目，推荐使用全局配置来统一管理数据映射规则。

## 附录

### 使用指引与集成步骤
- 安装与注册
  - 通过包管理器安装并注册组件库
  - 在应用中引入 UI 库样式与 ProComponents
- 适配器安装（核心版本）
  - 在应用中安装 ProCore 并传入 adapter（如 ViewUIPlusAdapter）
- **新增**：全局配置设置
  - 在应用安装时通过 `pro_iview.map` 设置全局映射配置
  - 配置项包括 dataPath、titlePath、valuePath 等
- 基本使用
  - 单选：设置 multiple=false，绑定 modelValue
  - 多选：设置 multiple=true，modelValue 为数组
  - 远程数据：设置 request 与 map
  - 静态数据：设置 list
  - **新增**：全局配置：无需手动传入 map，自动使用全局配置

**章节来源**
- [src/main.js:1-14](file://src/main.js#L1-L14)
- [packages/index.js:16-19](file://packages/index.js#L16-L19)
- [lib/main/pro_iview.js:15:1](file://lib/main/pro_iview.js#L15-L1)
- [MIGRATION.md:20-44](file://MIGRATION.md#L20-L44)
- [MIGRATION.md:46-67](file://MIGRATION.md#L46-L67)
- [MIGRATION.md:69-81](file://MIGRATION.md#L69-L81)