npm package: @hjxz/pharos-tree-select
Description: hjxz 树级选择组件
Author: vino
Latest version: 0.0.72

# Pharos Tree Select

树级选择组件，支持无限层级数据渲染，父节点含子节点时显示展开/折叠图标，子节点继承父节点缩进样式。

## 特性

- 🌳 支持无限层级树形数据
- 🔽 展开/折叠功能（仅在有子节点时显示）
- 📏 可配置缩进距离（默认 16px）
- ✅ 支持单选/多选模式
- 🎨 现代化 UI 设计
- 📱 响应式布局
- ⚡ 虚拟滚动支持（大数据量优化）
- 🔍 集成搜索功能（搜索框在显示框中）
- 🎛️ 可配置字段映射
- 🎯 智能回显和搜索切换

## 安装

```bash
npm install @hjxz/pharos-tree-select
```

## 使用

### 基础用法

```vue
<template>
  <PharosTreeSelect
    v-model:value="selectedValue"
    :treeData="treeData"
    :indent="20"
    :multiple="true"
    placeholder="请选择"
  />
</template>

<script setup>
import { ref } from 'vue'
import { PharosTreeSelect } from '@hjxz/pharos-tree-select'

const selectedValue = ref([])
const treeData = ref([
  {
    id: '1',
    label: '一级节点',
    children: [
      {
        id: '1-1',
        label: '二级节点 1',
        children: [
          { id: '1-1-1', label: '三级节点 1' },
          { id: '1-1-2', label: '三级节点 2' }
        ]
      },
      { id: '1-2', label: '二级节点 2' }
    ]
  }
])
</script>
```

### 虚拟滚动（大数据量）

```vue
<template>
  <PharosTreeSelect
    v-model:value="selectedValue"
    :treeData="largeTreeData"
    :virtual-scroll="true"
    :item-height="32"
    :visible-count="10"
    placeholder="请选择（虚拟滚动）"
  />
</template>

<script setup>
import { ref } from 'vue'
import { PharosTreeSelect } from '@hjxz/pharos-tree-select'

const selectedValue = ref('')
const largeTreeData = ref([
  // 大量数据，建议 1000+ 节点时使用虚拟滚动
])
</script>
```

### 多选 + 虚拟滚动

```vue
<template>
  <PharosTreeSelect
    v-model:value="selectedValues"
    :treeData="largeTreeData"
    :multiple="true"
    :virtual-scroll="true"
    :item-height="32"
    :visible-count="8"
    placeholder="请选择多个节点"
  />
</template>

<script setup>
import { ref } from 'vue'
import { PharosTreeSelect } from '@hjxz/pharos-tree-select'

const selectedValues = ref([])
const largeTreeData = ref([
  // 大量数据
])
</script>
```

### 集成搜索功能

```vue
<template>
  <!-- 基础搜索 -->
  <PharosTreeSelect
    v-model:value="selectedValue"
    :treeData="treeData"
    :show-search="true"
    search-placeholder="请输入搜索关键词"
    placeholder="请选择节点"
  />
  
  <!-- 自定义搜索过滤 -->
  <PharosTreeSelect
    v-model:value="selectedValue"
    :treeData="treeData"
    :show-search="true"
    :filter-option="customFilter"
    search-placeholder="自定义搜索"
    placeholder="请选择节点"
  />
  
  <!-- 禁用搜索 -->
  <PharosTreeSelect
    v-model:value="selectedValue"
    :treeData="treeData"
    :show-search="false"
    placeholder="请选择节点（无搜索）"
  />
</template>

<script setup>
import { ref } from 'vue'
import { PharosTreeSelect } from '@hjxz/pharos-tree-select'

const selectedValue = ref('')
const treeData = ref([
  // 树形数据
])

// 自定义过滤函数
const customFilter = (input: string, option: TreeNode): boolean => {
  return option.label?.toString().toLowerCase().includes(input.toLowerCase()) || false
}
</script>
```

## API

### Props

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| value / v-model:value | 绑定值 | `string \| string[]` | - |
| treeData | 树形数据 | `TreeNode[]` | `[]` |
| multiple | 是否多选 | `boolean` | `false` |
| indent | 缩进距离（px） | `number` | `16` |
| placeholder | 占位符 | `string` | `'请选择'` |
| disabled | 是否禁用 | `boolean` | `false` |
| checkable | 是否显示复选框 | `boolean` | `false` |
| fieldNames | 字段映射配置 | `TreeFieldNames` | `{ id: 'id', label: 'label', children: 'children', disabled: 'disabled', isLeaf: 'isLeaf', extra: 'extra' }` |
| searchValue | 搜索值 | `string` | - |
| **showSearch** | **是否显示搜索功能** | `boolean` | `true` |
| **searchPlaceholder** | **搜索框占位符** | `string` | `'搜索'` |
| **filterOption** | **自定义搜索过滤函数** | `(input: string, option: TreeNode) => boolean` | - |
| **virtualScroll** | **是否启用虚拟滚动** | `boolean` | `false` |
| **itemHeight** | **虚拟滚动项目高度（px）** | `number` | `32` |
| **visibleCount** | **虚拟滚动可见项目数量** | `number` | `10` |
| **api** | **数据请求接口** | `function` | |

### Events

| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| change | 选择变化时触发 | `(value: string \| string[])` |
| expand | 节点展开/折叠时触发 | `(expandedKeys: string[], { expanded: boolean, node: TreeNode })` |
| selectChange | 选择变化时触发 | `(value: string \| string[])` |
| nodeExpand | 节点展开/折叠时触发 | `(node: TreeNode, expanded: boolean)` |
| load | 节点懒加载时触发 | `(node: TreeNode)` |
| search | 搜索时触发 | `(keyword: string)` |
| clear | 清空时触发 | `(value: string \| string[])` |
| dropdownChange | 下拉框显示/隐藏时触发 | `(visible: boolean)` |

### TreeNode

| 属性 | 说明 | 类型 | 必填 |
|------|------|------|------|
| id | 节点唯一标识 | `string` | ✅ |
| label | 节点显示文本 | `string` | ✅ |
| children | 子节点 | `TreeNode[]` | ❌ |
| disabled | 是否禁用 | `boolean` | ❌ |
| isLeaf | 是否为叶子节点 | `boolean` | ❌ |
| extra | 额外数据 | `any` | ❌ |

### TreeFieldNames

| 属性 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| id | 节点唯一标识字段名 | `string` | `'id'` |
| label | 节点显示文本字段名 | `string` | `'label'` |
| children | 子节点字段名 | `string` | `'children'` |
| disabled | 禁用状态字段名 | `string` | `'disabled'` |
| isLeaf | 叶子节点字段名 | `string` | `'isLeaf'` |
| extra | 额外数据字段名 | `string` | `'extra'` |

## 虚拟滚动

当数据量较大（建议 1000+ 节点）时，可以启用虚拟滚动来优化性能：

### 性能优化

- **渲染优化**: 只渲染可见区域的节点，大幅减少 DOM 节点数量
- **内存优化**: 减少内存占用，提高页面响应速度
- **滚动流畅**: 保持流畅的滚动体验

### 配置参数

- `virtualScroll`: 是否启用虚拟滚动
- `itemHeight`: 每个节点的高度（px），用于计算滚动位置
- `visibleCount`: 可见区域显示的节点数量，影响容器高度

### 使用建议

1. **数据量**: 建议在 1000+ 节点时启用虚拟滚动
2. **高度设置**: `itemHeight` 应与实际节点高度一致
3. **可见数量**: `visibleCount` 建议设置为 8-15 之间
4. **性能测试**: 在目标设备上测试滚动性能

### 注意事项

- 虚拟滚动模式下，节点高度必须固定
- 搜索功能在虚拟滚动模式下仍然可用
- 展开/折叠功能完全兼容虚拟滚动

## 集成搜索功能

搜索功能已集成到组件的显示框中，提供更好的用户体验：

### 功能特性

- **智能切换**: 点击显示框时自动进入搜索模式
- **回显功能**: 显示已选中的内容，支持多选回显
- **实时搜索**: 输入时实时过滤树形数据
- **自定义过滤**: 支持自定义搜索过滤逻辑
- **键盘支持**: 支持 ESC 键退出搜索模式

### 搜索模式

1. **显示模式**: 显示已选中的内容或占位符
2. **搜索模式**: 点击后进入搜索状态，显示搜索框
3. **自动切换**: 选择选项后自动退出搜索模式

### 自定义搜索

```typescript
// 自定义过滤函数
const customFilter = (input: string, option: TreeNode): boolean => {
  // 支持拼音搜索
  const pinyinMap = {
    'beijing': '北京',
    'shanghai': '上海'
  }
  
  // 检查拼音匹配
  for (const [pinyin, chinese] of Object.entries(pinyinMap)) {
    if (pinyin.includes(input.toLowerCase()) && 
        option.label?.includes(chinese)) {
      return true
    }
  }
  
  // 默认文本匹配
  return option.label?.toLowerCase().includes(input.toLowerCase()) || false
}
```

### 使用建议

1. **搜索占位符**: 设置清晰的搜索提示文本
2. **过滤函数**: 根据业务需求自定义搜索逻辑
3. **性能优化**: 大数据量时建议结合虚拟滚动使用
4. **用户体验**: 保持搜索框的响应性和流畅性