---
nav:
  title: 组件
  order: 1
group:
  title: General
  order: 1
title: PisellMultipleSelect
order: 1
category: pro
---

# PisellMultipleSelect 多选下拉组件

## 组件介绍

PisellMultipleSelect 是 Pisell 2.0 组件库中处理多选数据的基础表单组件。它基于 Ant Design Select 组件封装，提供了符合
Pisell 设计规范的多选功能、标签聚合、最大可选数量限制、搜索过滤等核心能力，适用于各种多选场景。

**核心特性**：

- ✅ 三种交互状态：只读、编辑、禁用
- ✅ 标签聚合：自动聚合超出的标签，显示 "+N"
- ✅ 最大可选数量限制：灵活控制可选数量
- ✅ 搜索与过滤：支持防抖搜索和自定义过滤逻辑
- ✅ 完整的状态管理：受控和非受控模式
- ✅ 样式定制：基于 Token 系统和 Less
- ✅ 基于 Ant Design Select 开发
- ✅ 复用逻辑抽取到 @pisell/utils

---

## 基础用法

### 基础示例（受控模式）

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>([]);

    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            value={value}
            onChange={(val) => setValue(val)}
            placeholder="请选择喜欢的水果"
        />
    );
};
```

### 非受控模式

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            defaultValue={['apple', 'banana']}
            placeholder="请选择"
        />
    );
};
```

### 带标签和辅助文本

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            label="喜欢的水果"
            helperText="可以选择多个水果"
            placeholder="请选择"
            required
        />
    );
};
```

### 只读态展示

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            value={['apple', 'banana', 'orange']}
            readOnly
            label="已选择的水果"
        />
    );
};
```

### 禁用态

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            value={['apple', 'banana']}
            disabled
        />
    );
};
```

---

## 标签聚合功能

### 基础标签聚合

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const skillOptions = [
        {value: 'react', label: 'React'},
        {value: 'vue', label: 'Vue.js'},
        {value: 'typescript', label: 'TypeScript'},
        {value: 'nodejs', label: 'Node.js'},
    ];

    return (
        <PisellMultipleSelect
            options={skillOptions}
            value={['react', 'vue', 'typescript', 'nodejs']}
            maxTagCount={2}
            showTooltipOnOverflow
        />
    );
};
```

### 自定义聚合数量

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const options = [
        {value: '1', label: '标签1'},
        {value: '2', label: '标签2'},
        {value: '3', label: '标签3'},
        {value: '4', label: '标签4'},
        {value: '5', label: '标签5'},
    ];
    const value = ['1', '2', '3', '4'];

    return (
        <div style={{display: 'flex', flexDirection: 'column', gap: 16}}>
            {/* 显示 3 个标签 */}
            <PisellMultipleSelect
                options={options}
                value={value}
                maxTagCount={3}
            />

            {/* 显示 1 个标签 */}
            <PisellMultipleSelect
                options={options}
                value={value}
                maxTagCount={1}
            />

            {/* 显示所有标签（不聚合） */}
            <PisellMultipleSelect
                options={options}
                value={value}
                maxTagCount={999}
            />
        </div>
    );
};
```

### 禁用 Tooltip

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const options = [
        {value: '1', label: '标签1'},
        {value: '2', label: '标签2'},
        {value: '3', label: '标签3'},
        {value: '4', label: '标签4'},
    ];

    return (
        <PisellMultipleSelect
            options={options}
            value={['1', '2', '3', '4']}
            maxTagCount={2}
            showTooltipOnOverflow={false}
        />
    );
};
```

---

## 最大可选数量限制

### 限制最大可选数量

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>([]);
    const skillOptions = [
        {value: 'react', label: 'React'},
        {value: 'vue', label: 'Vue.js'},
        {value: 'angular', label: 'Angular'},
        {value: 'typescript', label: 'TypeScript'},
    ];

    return (
        <PisellMultipleSelect
            options={skillOptions}
            label="技术栈（最多选3项）"
            helperText="最多可选择3项"
            value={value}
            onChange={setValue}
            maxCount={3}
        />
    );
};
```

当用户尝试选择第 4 项时，会显示警告提示并阻止选择。

---

## 搜索与过滤

### 基础搜索

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>([]);
    const cityOptions = [
        {value: 'beijing', label: '北京'},
        {value: 'shanghai', label: '上海'},
        {value: 'guangzhou', label: '广州'},
        {value: 'shenzhen', label: '深圳'},
    ];

    return (
        <PisellMultipleSelect
            options={cityOptions}
            label="选择城市"
            placeholder="搜索城市"
            showSearch
            value={value}
            onChange={setValue}
        />
    );
};
```

### 自定义过滤逻辑

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>([]);
    const cityOptions = [
        {value: 'beijing', label: '北京'},
        {value: 'shanghai', label: '上海'},
        {value: 'guangzhou', label: '广州'},
        {value: 'shenzhen', label: '深圳'},
    ];

    return (
        <PisellMultipleSelect
            options={cityOptions}
            label="选择城市"
            placeholder="输入城市名..."
            showSearch
            value={value}
            onChange={setValue}
            filterOption={(input, option) => {
                return option.label.toLowerCase().includes(input.toLowerCase()) ||
                    option.value.toString().includes(input);
            }}
        />
    );
};
```

### 搜索回调

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const options = [
        {value: '1', label: '标签1'},
        {value: '2', label: '标签2'},
        {value: '3', label: '标签3'},
    ];

    return (
        <PisellMultipleSelect
            options={options}
            showSearch
            onSearch={(keyword) => {
                console.log('搜索关键词', keyword);
            }}
        />
    );
};
```

---

## 错误状态与校验

### 显示错误状态

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            label="选择水果"
            value={[]}
            required
            error
            errorText="至少选择一项"
        />
    );
};
```

### 表单校验场景

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>([]);
    const [error, setError] = useState(false);

    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    const handleSubmit = () => {
        if (value.length === 0) {
            setError(true);
        } else {
            setError(false);
            console.log('???:', value);
        }
    };

    return (
        <div>
            <PisellMultipleSelect
                options={fruitOptions}
                label="选择水果（至少选一项）"
                value={value}
                onChange={(val) => {
                    setValue(val);
                    setError(false);
                }}
                required
                error={error}
                errorText="至少选择一项"
            />
            <button onClick={handleSubmit}>提交</button>
        </div>
    );
};
```

---

## 交互配置

### 清空按钮

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>(['apple']);
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            value={value}
            onChange={setValue}
            allowClear
        />
    );
};
```

### 不同尺寸

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
    ];

    return (
        <div style={{display: 'flex', flexDirection: 'column', gap: 16}}>
            {/* 小尺寸 */}
            <PisellMultipleSelect options={fruitOptions} size="small"/>

            {/* 中等尺寸（默认） */}
            <PisellMultipleSelect options={fruitOptions} size="medium"/>

            {/* 大尺寸 */}
            <PisellMultipleSelect options={fruitOptions} size="large"/>
        </div>
    );
};
```

---

## 事件回调

### 值变化回调

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            onChange={(values, options) => {
                console.log('选中值数组', values);
                console.log('选中选项数组', options);
            }}
        />
    );
};
```

### 取消选中回调

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [value, setValue] = useState<string[]>(['apple']);
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            value={value}
            onChange={setValue}
            onDeselect={(value, option) => {
                console.log('取消选中', value, option.label);
            }}
        />
    );
};
```

### 焦点事件

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            onFocus={(event) => {
                console.log('获得焦点');
            }}
            onBlur={(event) => {
                console.log('失去焦点');
            }}
        />
    );
};
```

### 下拉框显隐

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            onDropdownVisibleChange={(visible) => {
                console.log('下拉框', visible ? '打开' : '关闭');
            }}
        />
    );
};
```

---

## 表单场景

### 在 Form 中使用

```tsx
import {Form} from 'antd';
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [fruits, setFruits] = useState<string[]>([]);
    const [skills, setSkills] = useState<string[]>([]);

    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    const skillOptions = [
        {value: 'react', label: 'React', color: '#61dafb'},
        {value: 'vue', label: 'Vue.js', color: '#42b883'},
        {value: 'angular', label: 'Angular', color: '#dd0031'},
        {value: 'typescript', label: 'TypeScript', color: '#3178c6'},
        {value: 'javascript', label: 'JavaScript', color: '#f7df1e'},
        {value: 'nodejs', label: 'Node.js', color: '#339933'},
    ];

    return (
        <Form layout="vertical" style={{maxWidth: 400}}>
            <Form.Item label="喜欢的水果（必填，最多3项）">
                <PisellMultipleSelect
                    options={fruitOptions}
                    value={fruits}
                    onChange={setFruits}
                    placeholder="请选择水果"
                    required
                    maxCount={3}
                    helperText="最多可选择3项"
                />
            </Form.Item>

            <Form.Item label="掌握的技能">
                <PisellMultipleSelect
                    options={skillOptions}
                    value={skills}
                    onChange={setSkills}
                    placeholder="请选择技能"
                    showSearch
                    allowClear
                    maxTagCount={2}
                />
            </Form.Item>
        </Form>
    );
};
```

---

## 表格场景

### 表格列中使用（只读态）

```tsx
import {Table} from 'antd';
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const tagOptions = [
        {value: 'hot', label: '热门'},
        {value: 'new', label: '新品'},
        {value: 'sale', label: '促销'},
        {value: 'popular', label: '畅销'},
    ];

    const columns = [
        {
            title: '商品名称',
            dataIndex: 'name',
        },
        {
            title: '标签',
            dataIndex: 'tags',
            render: (value: string[]) => (
                <PisellMultipleSelect
                    options={tagOptions}
                    value={value}
                    readOnly
                    maxTagCount={2}
                />
            ),
        },
    ];

    const dataSource = [
        {id: 1, name: '商品A', tags: ['hot', 'new']},
        {id: 2, name: '商品B', tags: ['sale', 'popular', 'hot']},
    ];

    return <Table dataSource={dataSource} columns={columns}/>;
};
```

---

## Ref 方法

### 使用 Ref 控制组件

```tsx
import {useRef} from 'react';
import {PisellMultipleSelect} from '@pisell/materials';
import type {MultipleSelectRef} from '@pisell/materials';

export default () => {
    const selectRef = useRef<MultipleSelectRef>(null);

    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
        {value: 'grape', label: '葡萄'},
    ];

    const handleFocus = () => {
        selectRef.current?.focus();
    };

    const handleClear = () => {
        selectRef.current?.clearValue();
    };

    const handleGetValue = () => {
        const currentValue = selectRef.current?.getValue();
        console.log('当前值:', currentValue);
    };

    const handleAddValue = () => {
        selectRef.current?.addValue('apple');
    };

    const handleRemoveValue = () => {
        selectRef.current?.removeValue('banana');
    };

    return (
        <div>
            <PisellMultipleSelect
                ref={selectRef}
                options={fruitOptions}
            />
            <button onClick={handleFocus}>聚焦</button>
            <button onClick={handleClear}>清空</button>
            <button onClick={handleGetValue}>获取值</button>
            <button onClick={handleAddValue}>添加苹果</button>
            <button onClick={handleRemoveValue}>移除香蕉</button>
        </div>
    );
};
```

---

## 样式定制

### 自定义类名和样式

```tsx
import {PisellMultipleSelect} from '@pisell/materials';

export default () => {
    const fruitOptions = [
        {value: 'apple', label: '苹果'},
        {value: 'banana', label: '香蕉'},
        {value: 'orange', label: '橙子'},
    ];

    return (
        <PisellMultipleSelect
            options={fruitOptions}
            className="custom-select"
            style={{width: 400}}
        />
    );
};
```

### 通过 Token 定制（全局）

在 `packages/materials/src/theme/tokens.ts` 中修改 Select Token：

```
Select: {
  fontSize: 16,
  controlHeight: 48,
  borderRadius: 8,
  colorBorder: '#D0D5DD',
  // ... 其他配置
}
```

### 通过 Less 定制（组件级别）

在 `PisellMultipleSelect.less` 中添加自定义样式。

---

## API 文档

### Props 属性

| 属性名                   | 类型                               | 默认值        | 说明                  |
|-----------------------|----------------------------------|------------|---------------------|
| **核心数据**              |                                  |            |                     |
| options               | `SelectOption<T>[]`              | -          | 选项列表（必填）            |
| value                 | `T[]`                            | -          | 当前选中值数组（受控）         |
| defaultValue          | `T[]`                            | -          | 默认值数组（非受控）          |
| **交互控制**              |                                  |            |                     |
| placeholder           | `string`                         | `'请选择'`    | 占位符文本               |
| showSearch            | `boolean`                        | `false`    | 是否显示搜索框             |
| allowClear            | `boolean`                        | `false`    | 是否显示清空按钮            |
| disabled              | `boolean`                        | `false`    | 是否禁用                |
| readOnly              | `boolean`                        | `false`    | 是否只读                |
| **样式与布局**             |                                  |            |                     |
| size                  | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸                  |
| className             | `string`                         | -          | 自定义类名               |
| style                 | `CSSProperties`                  | -          | 自定义样式               |
| **表单标签与校验**           |                                  |            |                     |
| label                 | `string`                         | -          | 标签文本                |
| helperText            | `string`                         | -          | 辅助提示文本              |
| required              | `boolean`                        | `false`    | 是否必填                |
| error                 | `boolean`                        | `false`    | 是否显示错误状态            |
| errorText             | `string`                         | -          | 错误提示文本              |
| **多选专属配置**            |                                  |            |                     |
| maxTagCount           | `number`                         | `3`        | 最多显示的标签数量           |
| showTooltipOnOverflow | `boolean`                        | `true`     | 超出标签hover时显示tooltip |
| maxCount              | `number`                         | -          | 最大可选数量（无限制）         |
| **筛选与排序**             |                                  |            |                     |
| filterable            | `boolean`                        | `false`    | 是否启用过滤              |
| filterOption          | `(input, option) => boolean`     | -          | 自定义过滤函数             |
| sortable              | `boolean`                        | `false`    | 是否启用排序              |
| sortDirection         | `'asc' \| 'desc'`                | -          | 排序方向                |
| **无障碍访问**             |                                  |            |                     |
| aria-label            | `string`                         | -          | ARIA 标签             |
| aria-required         | `boolean`                        | -          | ARIA 是否必填           |
| aria-invalid          | `boolean`                        | -          | ARIA 是否无效           |
| aria-describedby      | `string`                         | -          | ARIA 描述关联 ID        |
| aria-multiselectable  | `boolean`                        | `true`     | ARIA 多选标识           |

### SelectOption 类型

```
interface SelectOption<T = string | number> {
    value: T;           // 选项值（唯一标识）
    label: string;      // 选项显示文本
    disabled?: boolean; // 是否禁用该选项
    color?: string;     // 标签背景色（可选）
    icon?: ReactNode;   // 选项图标
}
```

### 事件回调

| 事件名                     | 参数                                                 | 说明        |
|-------------------------|----------------------------------------------------|-----------|
| onChange                | `(value: T[], options: SelectOption<T>[]) => void` | 值变化时的回调   |
| onDeselect              | `(value: T, option: SelectOption<T>) => void`      | 取消选中某项时触发 |
| onSearch                | `(value: string) => void`                          | 搜索输入回调    |
| onFocus                 | `(event: React.FocusEvent) => void`                | 获得焦点回调    |
| onBlur                  | `(event: React.FocusEvent) => void`                | 失去焦点回调    |
| onDropdownVisibleChange | `(visible: boolean) => void`                       | 下拉框显隐回调   |

### Ref 方法

| 方法名         | 参数                     | 说明      |
|-------------|------------------------|---------|
| focus       | `() => void`           | 聚焦到组件   |
| blur        | `() => void`           | 失焦      |
| getValue    | `() => T[]`            | 获取当前值数组 |
| setValue    | `(value: T[]) => void` | 设置值数组   |
| clearValue  | `() => void`           | 清空所有值   |
| addValue    | `(value: T) => void`   | 添加一个选中值 |
| removeValue | `(value: T) => void`   | 移除一个选中值 |

---

## 完整示例

### 技能选择（带标签聚合和最大数量限制）

```tsx
import {Form} from 'antd';
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [skills, setSkills] = useState<string[]>([]);
    const [error, setError] = useState(false);

    const skillOptions = [
        {value: 'react', label: 'React', color: '#61dafb'},
        {value: 'vue', label: 'Vue.js', color: '#42b883'},
        {value: 'angular', label: 'Angular', color: '#dd0031'},
        {value: 'typescript', label: 'TypeScript', color: '#3178c6'},
        {value: 'javascript', label: 'JavaScript', color: '#f7df1e'},
        {value: 'nodejs', label: 'Node.js', color: '#339933'},
    ];

    const handleSubmit = () => {
        if (skills.length === 0) {
            setError(true);
            return;
        }
        console.log('选中技能:', skills);
    };

    return (
        <Form.Item label="技术栈" required>
            <PisellMultipleSelect
                options={skillOptions}
                value={skills}
                onChange={(val) => {
                    setSkills(val);
                    setError(false);
                }}
                placeholder="请选择你掌握的技能"
                maxCount={5}
                maxTagCount={2}
                showTooltipOnOverflow
                helperText="最多可选择5项"
                error={error}
                errorText="至少选择一项技能"
                showSearch
                allowClear
            />
        </Form.Item>
    );
};
```

### 标签管理（带搜索）

```tsx
import {PisellMultipleSelect} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [tags, setTags] = useState<string[]>(['hot']);

    const tagOptions = [
        {value: 'hot', label: '热门'},
        {value: 'new', label: '新品'},
        {value: 'sale', label: '促销'},
        {value: 'popular', label: '畅销'},
        {value: 'recommended', label: '推荐'},
        {value: 'limited', label: '限量'},
    ];

    return (
        <PisellMultipleSelect
            options={tagOptions}
            label="商品标签"
            helperText="为商品添加标签，便于分类和搜索"
            value={tags}
            onChange={setTags}
            placeholder="搜索并选择标签..."
            showSearch
            allowClear
            maxTagCount={3}
            onDeselect={(val, option) => {
                console.log('移除标签:', option.label);
            }}
        />
    );
};
```

---

## 常见问题

### 1. 如何处理空值？

只读态会显示 `-`，编辑态会显示占位符：

```
// 只读态显示空值
<PisellMultipleSelect
    options={fruitOptions}
    value={[]}
    readOnly
/>
// 显示: -
```

### 2. 标签聚合是什么？

当选中的标签数量超过 `maxTagCount` 时，多余的标签会被聚合为 "+N" 显示：

```
<PisellMultipleSelect
    options={options}
    value={['a', 'b', 'c', 'd']}
    maxTagCount={2}
/>
// 显示: 选项A, 选项B, +2
```

hover "+2" 时会显示 Tooltip 展示完整列表。

### 3. 如何限制最大可选数量？

使用 `maxCount` 属性：

```
<PisellMultipleSelect
    options={options}
    maxCount={5}
    helperText="最多可选择5项"
/>
```

达到上限时会显示警告提示并阻止继续选择。

### 4. 如何实现搜索功能？

设置 `showSearch={true}`：

```
<PisellMultipleSelect
    options={options}
    showSearch
    placeholder="搜索..."
/>
```

### 5. 受控模式和非受控模式有什么区别？

**受控模式**：值由外部管理

```
const [value, setValue] = useState<string[]>([]);
<PisellMultipleSelect value={value} onChange={setValue}/>
```

**非受控模式**：值由组件内部管理

```
<PisellMultipleSelect defaultValue={['apple']}/>
```

### 6. 如何在表格中使用？

只读态适合在表格中展示：

```
{
  title: '标签',
  dataIndex: 'tags',
  render: (value: string[]) => (
    <PisellMultipleSelect
      options={tagOptions}
      value={value}
      readOnly
      maxTagCount={2}
    />
  ),
}
```

### 7. 如何使用 Ref 方法？

```
const ref = useRef<MultipleSelectRef>(null);

// 聚焦
ref.current?.focus();

// 获取值
const value = ref.current?.getValue();

// 添加值
ref.current?.addValue('apple');

// 移除值
ref.current?.removeValue('banana');

// 清空
ref.current?.clearValue();
```

### 8. maxTagCount 和 maxCount 有什么区别？

- **maxTagCount**：控制**显示**的标签数量，超出部分聚合为 "+N"
- **maxCount**：控制**可选**的最大数量，达到上限后无法继续选择

```
<PisellMultipleSelect
    options={options}
    maxTagCount={2}  // 最多显示 2 个标签
    maxCount={5}     // 最多选择 5 项
/>
```

---

## 最佳实践

1. **受控模式优先**：在表单场景推荐使用受控模式，便于统一管理

2. **标签聚合**：

- 选项较多时建议设置 `maxTagCount={2-3}`
- 启用 `showTooltipOnOverflow` 提供完整信息

3. **数量限制**：

- 根据业务需求合理设置 `maxCount`
- 在 `helperText` 中提示最大可选数量

4. **搜索优化**：

- 选项较多时（>10个）启用 `showSearch`
- 搜索已内置防抖优化（300ms）

5. **只读态优化**：在表格、列表等展示场景使用 `readOnly`，避免渲染下拉框

6. **错误提示**：使用 `error` 和 `errorText` 提供清晰的错误反馈

7. **清空功能**：重要的多选场景建议启用 `allowClear`

---

## 注意事项

1. **类型安全**：value 的类型是 `T[]`，确保数组类型正确
2. **选项唯一性**：确保每个选项的 `value` 是唯一的
3. **搜索防抖**：搜索已内置 300ms 防抖，无需额外处理
4. **只读态限制**：只读态不会渲染真实的 Select 组件，某些 Props 无效
5. **样式优先级**：Token > Less > inline style
6. **标签聚合性能**：标签聚合计算已使用 useMemo 优化

---

## 相关组件

- **PisellSingleSelect** - 单选下拉组件
- **PisellSingleLineText** - 单行文本组件
- **PisellLongText** - 长文本组件
