# 选项多选组件

一个多选下拉组件，允许用户从预定义列表中选择多个选项，并将选中的项显示为可移除的标签。

## 功能

- **多选**：从下拉列表中选择多个选项
- **标签显示**：以可移除标签的形式显示选中的项，支持自定义样式
- **复选框界面**：下拉菜单中直观的复选框选择
- **可自定义标签**：不同的标签变体（无边框、有边框、圆形）和颜色
- **选项控制**：隐藏或禁用特定选项
- **搜索集成**：与 CommonPicker 配合使用，提供一致的行为
- **焦点管理**：适当的焦点处理和键盘导航
- **显示模式**：支持编辑和查看模式
- **值管理**：基于分隔符的灵活值存储
- **空状态**：可自定义的占位符和空文本

## 安装

```bash
npm install @ticatec/uniface-element
```

## 基本用法

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let selectedValues = '';
  let options = [
    { code: 'JS', text: 'JavaScript' },
    { code: 'TS', text: 'TypeScript' },
    { code: 'PY', text: 'Python' },
    { code: 'GO', text: 'Go' }
  ];
</script>

<OptionsMultiSelect 
  {options} 
  bind:value={selectedValues}
/>
```

## API 参考

### 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `value` | `string` | `''` | 以分隔符分隔的选中值字符串 |
| `options` | `Array<any>` | `[]` | 选项对象数组 |
| `keyField` | `string` | `'code'` | 选项键的属性名 |
| `textField` | `string` | `'text'` | 选项显示文本的属性名 |
| `delimiter` | `string` | `';'` | 分隔多个值的分隔符 |
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉样式变体 |
| `compact` | `boolean` | `false` | 启用紧凑显示模式 |
| `disabled` | `boolean` | `false` | 禁用组件 |
| `readonly` | `boolean` | `false` | 使组件只读 |
| `style` | `string` | `''` | 自定义 CSS 样式 |
| `placeholder` | `string` | `''` | 未选择项时的占位符文本 |
| `emptyText` | `string` | `''` | 未选择项时显示的文本（覆盖占位符） |
| `disableOptions` | `Array<string>` | `[]` | 要禁用的选项键 |
| `hideOptions` | `Array<string>` | `[]` | 要隐藏的选项键 |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 编辑或查看模式 |
| `menu$height` | `number` | `0` | 下拉菜单的最大高度 |
| `tagColor` | `string` | `''` | 标签的颜色变体 |
| `tagVariant` | `'borderless' \| 'border' \| 'round'` | `'border'` | 标签视觉样式 |
| `onchange` | `OnChangeHandler<Array<string>>` | `null` | 更改事件处理程序 |
| `onfocus` | `(() => void) \| null` | `null` | 聚焦事件处理程序 |
| `onblur` | `(() => void) \| null` | `null` | 失焦事件处理程序 |

### 方法

| 方法 | 描述 |
|--------|-------------|
| `setFocus()` | 以编程方式聚焦组件 |

## 示例

### 编程语言选择器

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  import FormField from '@ticatec/uniface-element/FormField';
  
  let languages = '';
  let programmingLanguages = [
    { code: 'JS', text: 'JavaScript' },
    { code: 'TS', text: 'TypeScript' },
    { code: 'PY', text: 'Python' },
    { code: 'JAVA', text: 'Java' },
    { code: 'CS', text: 'C#' },
    { code: 'GO', text: 'Go' },
    { code: 'RUST', text: 'Rust' },
    { code: 'PHP', text: 'PHP' }
  ];
  
  function handleLanguageChange(selected) {
    console.log('选择的语言：', selected);
  }
</script>

<FormField label="编程语言">
  <OptionsMultiSelect 
    options={programmingLanguages}
    bind:value={languages}
    variant="outlined"
    tagColor="1"
    tagVariant="round"
    placeholder="选择编程语言"
    onchange={handleLanguageChange}
  />
</FormField>
```

### 技能评估

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let selectedSkills = '';
  let skills = [
    { code: 'FE', text: '前端开发' },
    { code: 'BE', text: '后端开发' },
    { code: 'DB', text: '数据库设计' },
    { code: 'DEVOPS', text: '运维开发' },
    { code: 'UI', text: 'UI/UX 设计' },
    { code: 'TEST', text: '测试' },
    { code: 'MOBILE', text: '移动开发' },
    { code: 'ML', text: '机器学习' }
  ];
  
  let disabledSkills = ['ML']; // 机器学习暂时不可用
  
  $: selectedArray = selectedSkills ? selectedSkills.split(';') : [];
</script>

<div class="skills-selector">
  <h3>技术技能</h3>
  <OptionsMultiSelect 
    options={skills}
    bind:value={selectedSkills}
    variant="filled"
    tagColor="2"
    tagVariant="border"
    disableOptions={disabledSkills}
    placeholder="选择你的技术技能"
    menu$height={200}
  />
  
  <div class="selected-count">
    已选择：{selectedArray.length} 个技能
  </div>
</div>
```

### 活动类别过滤器

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let filterCategories = '';
  let eventCategories = [
    { id: 'CONF', name: '会议' },
    { id: 'WORK', name: '工作坊' },
    { id: 'MEET', name: '聚会' },
    { id: 'WEB', name: '网络研讨会' },
    { id: 'HACK', name: '黑客马拉松' },
    { id: 'NET', name: '网络活动' },
    { id: 'TRAIN', name: '培训' },
    { id: 'EXPO', name: '展览' }
  ];
  
  let hiddenCategories = ['EXPO']; // 当前地区不可用
  
  function applyFilter(categories) {
    console.log('按类别过滤活动：', categories);
    // 在此应用过滤逻辑
  }
</script>

<div class="event-filter">
  <OptionsMultiSelect 
    options={eventCategories}
    keyField="id"
    textField="name"
    bind:value={filterCategories}
    variant="outlined"
    tagColor="3"
    tagVariant="borderless"
    hideOptions={hiddenCategories}
    placeholder="按活动类别过滤"
    onchange={applyFilter}
  />
</div>
```

### 团队分配

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let assignedMembers = '';
  let teamMembers = [
    { userId: '001', fullName: 'Alice Johnson' },
    { userId: '002', fullName: 'Bob Smith' },
    { userId: '003', fullName: 'Carol Davis' },
    { userId: '004', fullName: 'David Wilson' },
    { userId: '005', fullName: 'Eva Brown' },
    { userId: '006', fullName: 'Frank Miller' }
  ];
  
  let unavailableMembers = ['003', '005']; // 休假中
  
  function handleAssignment(members) {
    console.log('分配的团队成员：', members);
    // 发送分配通知
  }
</script>

<div class="team-assignment">
  <label>分配团队成员</label>
  <OptionsMultiSelect 
    options={teamMembers}
    keyField="userId"
    textField="fullName"
    bind:value={assignedMembers}
    variant="outlined"
    tagColor="4"
    tagVariant="round"
    disableOptions={unavailableMembers}
    delimiter=","
    placeholder="为该任务选择团队成员"
    onchange={handleAssignment}
  />
</div>
```

### 产品功能选择

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let selectedFeatures = '';
  let productFeatures = [
    { feature: 'AUTH', description: '用户认证' },
    { feature: 'DASHBOARD', description: '仪表盘分析' },
    { feature: 'REPORTS', description: '自定义报告' },
    { feature: 'API', description: 'API 集成' },
    { feature: 'EXPORT', description: '数据导出' },
    { feature: 'NOTIFICATIONS', description: '推送通知' },
    { feature: 'BACKUP', description: '自动备份' },
    { feature: 'MULTI_LANG', description: '多语言支持' }
  ];
  
  let isReadonly = false;
  
  function toggleReadonly() {
    isReadonly = !isReadonly;
  }
  
  function calculatePrice(features) {
    const featurePrices = {
      'AUTH': 10, 'DASHBOARD': 25, 'REPORTS': 20, 'API': 15,
      'EXPORT': 10, 'NOTIFICATIONS': 12, 'BACKUP': 18, 'MULTI_LANG': 8
    };
    
    return features.reduce((total, feature) => total + (featurePrices[feature] || 0), 0);
  }
  
  $: selectedArray = selectedFeatures ? selectedFeatures.split(';') : [];
  $: totalPrice = calculatePrice(selectedArray);
</script>

<div class="product-config">
  <h3>产品配置</h3>
  
  <OptionsMultiSelect 
    options={productFeatures}
    keyField="feature"
    textField="description"
    bind:value={selectedFeatures}
    variant="filled"
    tagColor="5"
    tagVariant="border"
    {readonly}
    placeholder="选择产品功能"
    menu$height={250}
  />
  
  <div class="config-summary">
    <p>已选择功能：{selectedArray.length}</p>
    <p>总价：${totalPrice}/月</p>
    <button on:click={toggleReadonly}>
      {isReadonly ? '编辑' : '锁定'} 配置
    </button>
  </div>
</div>
```

### 兴趣调查

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let interests = '';
  let surveyOptions = [
    { code: 'TECH', text: '科技' },
    { code: 'HEALTH', text: '健康与健身' },
    { code: 'TRAVEL', text: '旅行' },
    { code: 'FOOD', text: '美食与烹饪' },
    { code: 'SPORTS', text: '运动' },
    { code: 'MUSIC', text: '音乐' },
    { code: 'ART', text: '艺术与文化' },
    { code: 'BOOKS', text: '书籍与文学' },
    { code: 'GAMES', text: '游戏' },
    { code: 'FINANCE', text: '个人理财' }
  ];
  
  function handleInterestChange(selected) {
    console.log('用户兴趣更新：', selected);
    // 保存到用户资料
  }
  
  function handleFocus() {
    console.log('调查部分聚焦');
  }
  
  function handleBlur() {
    console.log('调查部分失焦');
  }
</script>

<div class="survey-section">
  <h3>你对什么感兴趣？</h3>
  <p>选择所有你感兴趣的主题（最少3个，最多7个）</p>
  
  <OptionsMultiSelect 
    options={surveyOptions}
    bind:value={interests}
    variant="outlined"
    tagColor="6"
    tagVariant="round"
    placeholder="选择你的兴趣"
    emptyText="尚未选择任何兴趣"
    onchange={handleInterestChange}
    onfocus={handleFocus}
    onblur={handleBlur}
  />
</div>
```

### 紧凑表格用法

```svelte
<script>
  import OptionsMultiSelect from '@ticatec/uniface-element/OptionsMultiSelect';
  
  let permissionOptions = [
    { code: 'READ', text: '读取' },
    { code: 'WRITE', text: '写入' },
    { code: 'DELETE', text: '删除' },
    { code: 'ADMIN', text: '管理员' }
  ];
  
  let users = [
    { id: 1, name: 'John Doe', permissions: 'READ;WRITE' },
    { id: 2, name: 'Jane Smith', permissions: 'READ;WRITE;DELETE' },
    { id: 3, name: 'Admin User', permissions: 'READ;WRITE;DELETE;ADMIN' }
  ];
  
  function updatePermissions(userId, permissions) {
    const user = users.find(u => u.id === userId);
    if (user) {
      user.permissions = permissions.join(';');
      users = [...users];
    }
  }
</script>

<table class="permissions-table">
  <thead>
    <tr>
      <th>用户</th>
      <th>权限</th>
    </tr>
  </thead>
  <tbody>
    {#each users as user}
      <tr>
        <td>{user.name}</td>
        <td>
          <OptionsMultiSelect 
            options={permissionOptions}
            value={user.permissions}
            compact
            variant="outlined"
            tagColor="7"
            tagVariant="borderless"
            onchange={(perms) => updatePermissions(user.id, perms)}
          />
        </td>
      </tr>
    {/each}
  </tbody>
</table>
```

## 标签样式

组件支持不同的标签变体和颜色：

### 标签变体
- `borderless`：无边框的干净标签
- `border`：带可见边框的标签
- `round`：圆形药丸样式标签

### 标签颜色
使用颜色字符串（例如 "1"、"2"、"3"）为标签应用不同的配色方案。

## 最佳实践

1. **选项管理**：保持选项列表专注且相关
2. **性能**：对于超大选项列表，考虑虚拟化
3. **可访问性**：提供清晰的标签和描述
4. **验证**：根据需要实现最小/最大选择限制
5. **用户体验**：选择适合你设计系统的标签样式
6. **数据格式**：选择不会与数据冲突的分隔符
7. **状态管理**：处理动态选项的加载状态

## 样式

```css
.options-popover {
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  background: white;
}

.option-item {
  padding: 8px 12px;
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.option-item:hover {
  background-color: #f5f5f5;
}

.option-item.disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
```

## 可访问性

- 全面支持键盘导航
- 为屏幕阅读器提供 ARIA 标签和角色
- 焦点管理和视觉指示器
- 高对比度模式兼容性
- 复选框可访问性标准

## 相关组件

- [选项单选](../options-select/README.md) - 用于单一选项选择
- [标签](../tag/README.md) - 用于单个标签样式
- [表单字段](../form-field/README.md) - 用于表单布局和标签
- [通用选择器](../common/README.md) - 基础选择器功能