# scSelect 组件使用文档

## 组件简介

`scSelect` 是一个基于 Ant Design Vue `a-select` 组件封装的增强型选择器组件，支持多种数据源（data、api、dictionary）和智能缓存机制。

## 特性

- ✅ 支持三种数据源：直接数据（data）、API 接口（api）、字典数据（dictionary）
- ✅ API 数据源自动缓存，减少重复请求
- ✅ 字典数据源自动从缓存读取，登录后自动预加载
- ✅ 支持按需加载数据（focus 时触发）
- ✅ 支持立即加载数据（组件挂载时）
- ✅ 支持自定义字段映射
- ✅ 支持自定义数据处理函数
- ✅ 完全继承 `a-select` 的所有属性和方法
- ✅ 支持插槽透传

## 安装

组件已自动注册，直接使用即可：

```vue
<template>
	<sc-select v-model:value="value" source-type="data" :data="options" />
</template>

<script setup>
import { ref } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const value = ref('')
const options = ref([
	{ label: '选项1', value: '1' },
	{ label: '选项2', value: '2' },
])
</script>
```

## Props

| 参数           | 说明                                           | 类型     | 可选值                          | 默认值                             |
| -------------- | ---------------------------------------------- | -------- | ------------------------------- | ---------------------------------- |
| sourceType     | 数据源类型                                     | String   | 'data' \| 'api' \| 'dictionary' | 'data'                             |
| data           | 直接数据（当 sourceType 为 data 时使用）       | Array    | -                               | []                                 |
| api            | API 接口地址（当 sourceType 为 api 时使用）    | String   | -                               | ''                                 |
| apiParams      | API 请求参数（当 sourceType 为 api 时使用）    | Object   | -                               | {}                                 |
| dictionaryCode | 字典编码（当 sourceType 为 dictionary 时使用） | String   | -                               | ''                                 |
| enableApiCache | 是否启用 API 数据缓存                          | Boolean  | -                               | true                               |
| apiCacheTime   | API 缓存时间（毫秒）                           | Number   | -                               | 300000（5分钟）                    |
| fieldNames     | 字段映射配置                                   | Object   | -                               | { label: 'label', value: 'value' } |
| immediate      | 是否在组件挂载时立即加载数据                   | Boolean  | -                               | false                              |
| dataProcessor  | 数据处理函数                                   | Function | -                               | null                               |

## Events

组件完全继承 `a-select` 的所有事件，包括但不限于：

- `@change` - 选中值变化时触发
- `@focus` - 获得焦点时触发
- `@blur` - 失去焦点时触发

## Methods

通过 ref 可以调用以下方法：

| 方法名             | 说明              | 参数 |
| ------------------ | ----------------- | ---- |
| refresh            | 强制刷新数据      | -    |
| loadApiData        | 手动加载 API 数据 | -    |
| loadDictionaryData | 手动加载字典数据  | -    |

## 使用示例

### 1. 使用 data 数据源

最简单的使用方式，直接提供数据数组：

```vue
<template>
	<a-form>
		<a-form-item label="状态">
			<sc-select v-model:value="form.status" source-type="data" :data="statusOptions" placeholder="请选择状态" />
		</a-form-item>
	</a-form>
</template>

<script setup>
import { reactive } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const form = reactive({
	status: '',
})

const statusOptions = [
	{ label: '启用', value: 1 },
	{ label: '禁用', value: 0 },
]
</script>
```

### 2. 使用 api 数据源

从 API 接口获取数据，自动缓存：

```vue
<template>
	<a-form>
		<a-form-item label="用户">
			<sc-select v-model:value="form.userId" source-type="api" api="users" :api-params="{ page: 1, page_size: 100 }" :enable-api-cache="true" :api-cache-time="600000" placeholder="请选择用户" :field-names="{ label: 'username', value: 'id' }" />
		</a-form-item>
	</a-form>
</template>

<script setup>
import { reactive } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const form = reactive({
	userId: '',
})
</script>
```

### 3. 使用 dictionary 数据源（推荐）

从字典缓存获取数据，性能最佳：

```vue
<template>
	<a-form>
		<a-form-item label="性别">
			<sc-select v-model:value="form.gender" source-type="dictionary" dictionary-code="gender" placeholder="请选择性别" />
		</a-form-item>

		<a-form-item label="用户状态">
			<sc-select v-model:value="form.userStatus" source-type="dictionary" dictionary-code="user_status" placeholder="请选择用户状态" />
		</a-form-item>
	</a-form>
</template>

<script setup>
import { reactive } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const form = reactive({
	gender: '',
	userStatus: '',
})
</script>
```

### 4. 立即加载数据

在组件挂载时立即加载数据，而不是等到 focus 时：

```vue
<template>
	<sc-select v-model:value="value" source-type="api" api="roles/all" :immediate="true" placeholder="请选择角色" />
</template>

<script setup>
import { ref } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const value = ref('')
</script>
```

### 5. 自定义数据处理

使用 `dataProcessor` 函数自定义数据格式：

```vue
<template>
	<sc-select v-model:value="value" source-type="api" api="roles" :data-processor="processData" placeholder="请选择角色" />
</template>

<script setup>
import { ref } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const value = ref('')

// 自定义数据处理函数
function processData(data) {
	return data.map((item) => ({
		label: `${item.name} (${item.description})`,
		value: item.id,
		extra: item.description,
	}))
}
</script>
```

### 6. 使用 ref 调用方法

```vue
<template>
	<div>
		<sc-select ref="selectRef" v-model:value="value" source-type="api" api="users" placeholder="请选择用户" />
		<a-button @click="handleRefresh">刷新数据</a-button>
	</div>
</template>

<script setup>
import { ref } from 'vue'
import { message } from 'ant-design-vue'
import scSelect from '@/components/scSelect/index.vue'

const selectRef = ref(null)
const value = ref('')

// 刷新数据
function handleRefresh() {
	if (selectRef.value) {
		selectRef.value.refresh()
		message.success('数据已刷新')
	}
}
</script>
```

### 7. 禁用 API 缓存

```vue
<template>
	<sc-select v-model:value="value" source-type="api" api="users" :enable-api-cache="false" placeholder="请选择用户" />
</template>
```

### 8. 多选模式

```vue
<template>
	<sc-select v-model:value="value" mode="multiple" source-type="dictionary" dictionary-code="tags" placeholder="请选择标签" />
</template>

<script setup>
import { ref } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const value = ref([])
</script>
```

### 9. 自定义插槽

```vue
<template>
	<sc-select v-model:value="value" source-type="data" :data="options" placeholder="请选择">
		<template #suffixIcon>
			<SearchOutlined />
		</template>

		<template #notFoundContent>
			<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" description="暂无数据" />
		</template>
	</sc-select>
</template>

<script setup>
import { ref } from 'vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import { Empty } from 'ant-design-vue'
import scSelect from '@/components/scSelect/index.vue'

const value = ref('')
const options = ref([
	{ label: '选项1', value: '1' },
	{ label: '选项2', value: '2' },
])
</script>
```

## 数据格式要求

### data 数据源

支持以下格式：

1. 字符串/数字数组

```javascript
;['选项1', '选项2', '选项3'][(1, 2, 3)]
```

2. 对象数组（标准格式）

```javascript
;[
	{ label: '选项1', value: '1' },
	{ label: '选项2', value: '2' },
]
```

3. 对象数组（自定义字段）

```javascript
[
  { name: '选项1', id: 1 },
  { name: '选项2', id: 2 },
]
// 使用 field-names 映射
field-names="{ label: 'name', value: 'id' }"
```

### api 数据源

API 接口需要返回以下格式：

```javascript
{
  "code": 200,
  "message": "success",
  "data": [
    { label: '选项1', value: '1' },
    { label: '选项2', value: '2' },
  ]
}

// 或者
{
  "code": 200,
  "message": "success",
  "list": [
    { label: '选项1', value: '1' },
    { label: '选项2', value: '2' },
  ]
}
```

### dictionary 数据源

字典数据通过后台管理系统的"数据字典管理"模块配置，登录后会自动缓存到前端。

字典数据格式：

```javascript
{
  code: 'user_status',
  name: '用户状态',
  items: [
    { name: '启用', value: '1', sort: 1, status: 1 },
    { name: '禁用', value: '0', sort: 2, status: 1 },
  ]
}
```

组件会自动转换为：

```javascript
;[
	{ label: '启用', value: '1', name: '启用', sort: 1, status: 1 },
	{ label: '禁用', value: '0', name: '禁用', sort: 2, status: 1 },
]
```

## 字典数据缓存机制

### 缓存流程

1. **登录时**：用户登录成功后，自动调用 `dictionaryStore.loadAllDictionaries()` 加载所有字典数据
2. **存储**：字典数据存储在 Pinia Store 中，并持久化到 localStorage
3. **使用**：组件通过 `dictionaryCode` 从 Store 获取数据，无需重复请求
4. **刷新**：可以调用 `refresh()` 方法强制刷新字典数据

### 清空缓存

```javascript
import { useDictionaryStore } from '@/stores/modules/dictionary'

const dictionaryStore = useDictionaryStore()

// 清空字典缓存
dictionaryStore.clearCache()
```

### 获取缓存信息

```javascript
import { useDictionaryStore } from '@/stores/modules/dictionary'

const dictionaryStore = useDictionaryStore()

// 获取缓存信息
const info = dictionaryStore.getCacheInfo()
console.log('字典数量:', info.count)
console.log('最后加载时间:', new Date(info.lastLoadTime))
```

## API 数据缓存机制

### 缓存策略

- 默认启用缓存，缓存时间为 5 分钟
- 缓存 key 由 `api` 地址和 `apiParams` 参数组成
- 缓存过期后自动重新请求
- 可以通过 `enableApiCache` 关闭缓存
- 可以通过 `apiCacheTime` 自定义缓存时间

### 缓存 key 示例

```javascript
// 同一个 API，不同参数会有不同的缓存
api: 'users'
apiParams: { page: 1, page_size: 20 }
// 缓存 key: 'users{"page":1,"page_size":20}'

apiParams: { page: 2, page_size: 20 }
// 缓存 key: 'users{"page":2,"page_size":20}'
```

## 注意事项

1. **字典数据源**：确保在后台管理系统中配置了对应的字典数据
2. **API 数据源**：确保 API 接口返回的数据格式正确
3. **缓存失效**：修改字典数据后，需要调用 `refresh()` 方法刷新缓存
4. **字段映射**：如果后端返回的字段名不是 `label` 和 `value`，需要使用 `fieldNames` 映射
5. **按需加载**：默认在 focus 时加载数据，如果需要立即加载，设置 `immediate=true`

## 最佳实践

1. **优先使用字典数据源**：对于固定选项（如状态、类型等），优先使用字典数据源
2. **合理使用缓存**：API 数据源建议启用缓存，减少服务器压力
3. **字段映射**：明确指定 `fieldNames`，避免数据格式不一致
4. **错误处理**：API 请求失败时，组件会自动处理错误并记录日志
5. **性能优化**：对于大量数据，建议使用分页加载或虚拟滚动

## 常见问题

### Q: 字典数据不显示？

A: 检查以下几点：

1. 后台管理系统中是否配置了对应的字典
2. 字典编码是否正确
3. 字典状态是否为启用
4. 字典项状态是否为启用

### Q: API 数据加载失败？

A: 检查以下几点：

1. API 地址是否正确
2. API 是否需要认证
3. 返回数据格式是否符合要求
4. 控制台是否有错误信息

### Q: 如何刷新字典数据？

A: 使用 `refresh()` 方法：

```javascript
const selectRef = ref(null)
selectRef.value?.refresh()
```

### Q: 如何禁用缓存？

A: 设置 `enableApiCache` 为 `false`：

```vue
<sc-select source-type="api" api="users" :enable-api-cache="false" />
```

## 完整示例

```vue
<template>
	<a-form :model="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
		<!-- 使用数据源 -->
		<a-form-item label="数据源示例">
			<sc-select
				v-model:value="form.dataSource"
				source-type="data"
				:data="[
					{ label: '字典数据', value: 'dictionary' },
					{ label: 'API数据', value: 'api' },
					{ label: '直接数据', value: 'data' },
				]"
				placeholder="请选择数据源"
			/>
		</a-form-item>

		<!-- 使用字典数据 -->
		<a-form-item label="用户状态">
			<sc-select v-model:value="form.status" source-type="dictionary" dictionary-code="user_status" placeholder="请选择用户状态" />
		</a-form-item>

		<!-- 使用 API 数据 -->
		<a-form-item label="所属角色">
			<sc-select v-model:value="form.roleId" source-type="api" api="roles/all" :enable-api-cache="true" :immediate="true" :field-names="{ label: 'name', value: 'id' }" placeholder="请选择角色" />
		</a-form-item>

		<!-- 多选模式 -->
		<a-form-item label="标签">
			<sc-select v-model:value="form.tags" mode="multiple" source-type="dictionary" dictionary-code="tags" placeholder="请选择标签" />
		</a-form-item>

		<!-- 带搜索 -->
		<a-form-item label="省份">
			<sc-select v-model:value="form.province" source-type="api" api="cities/provinces" show-search :filter-option="filterOption" placeholder="请选择省份" />
		</a-form-item>
	</a-form>
</template>

<script setup>
import { reactive } from 'vue'
import scSelect from '@/components/scSelect/index.vue'

const form = reactive({
	dataSource: '',
	status: '',
	roleId: '',
	tags: [],
	province: '',
})

// 搜索过滤
function filterOption(input, option) {
	return option.label.toLowerCase().includes(input.toLowerCase())
}
</script>
```
