Transfer 组件设计
概述
Transfer 组件是本组件库中用于实现穿梭框功能的核心组件。它延续了我们组件库一贯的设计理念,采用"容器-面板"分离和"状态驱动"的原则,通过 transfer.vue 和 transferPanel.vue 等核心组件实现了一套完整且易用的穿梭框解决方案。
Transfer 组件专注于处理双向选择场景中的数据流转和交互体验,为用户提供了一套直观、高效的批量选择操作界面,支持自定义样式、禁用项、标题配置和灵活的数据绑定,有效提升用户在数据迁移和选择操作时的体验。
设计理念
容器-面板分离架构
Transfer 组件采用清晰的容器-面板分离架构:
- 容器层 transfer.vue - 负责整体布局、数据管理和状态流转
- 面板层 transferPanel.vue - 专注于单个面板的数据展示和交互
- 数据层 type.ts - 定义数据结构和类型
这种设计使组件各部分职责单一,便于维护和扩展。
状态驱动设计
Transfer 组件采用状态驱动的设计思想:
- 数据驱动 - 通过 modelValue 和 data 属性驱动界面展示
- 状态隔离 - 左右面板的状态相互隔离,通过容器层统一管理
- 事件驱动 - 通过事件机制实现数据流动
这种设计确保了组件的状态可预测性和一致性。
核心功能设计
数据管理机制
Transfer 组件的核心在于数据管理机制:
typescript
const leftData = computed(() => props.data.filter(item => !props.modelValue.includes(item.key)))
const rightData = computed(() => props.data.filter(item => props.modelValue.includes(item.key)))数据管理特点:
- 双向绑定 - 使用 modelValue 实现 v-model 双向绑定
- 自动拆分 - 根据 modelValue 自动拆分左右面板数据
- 数据隔离 - 左右面板数据互不影响,通过容器层统一管理
操作交互设计
Transfer 组件的操作交互设计简洁高效:
typescript
const addToRight = () => {
const newKeys = [...props.modelValue, ...leftSelected.value]
emit('update:modelValue', newKeys)
leftSelected.value = []
}
const removeFromRight = () => {
const newKeys = props.modelValue.filter(k => !rightSelected.value.includes(k))
emit('update:modelValue', newKeys)
rightSelected.value = []
}交互特点:
- 单向流动 - 数据只能通过操作按钮在左右面板间移动
- 批量操作 - 支持同时选择多个项进行移动
- 禁用控制 - 禁用项无法被选择和移动
- 状态反馈 - 按钮状态根据选择情况动态变化
组件结构设计
整体结构
Transfer 组件的整体结构如下:
html
<div class="my-transfer">
<TransferPanel
v-model:selected="leftSelected"
:title="leftTitle"
:data="leftData"
:style="transferStyle"
:hoverBackground="hoverBackground"
/>
<div class="my-transfer__buttons">
<button :disabled="!leftSelected.length" @click="addToRight">→</button>
<button :disabled="!rightSelected.length" @click="removeFromRight">←</button>
</div>
<TransferPanel
v-model:selected="rightSelected"
:title="rightTitle"
:data="rightData"
:style="transferStyle"
:hoverBackground="hoverBackground"
/>
</div>组件层次
Transfer 组件包含多个关键部分:
- 容器 -
.my-transfer- 包裹整个穿梭框组件 - 操作按钮区 -
.my-transfer__buttons- 包含左右移动按钮 - 面板 -
.transfer-panel- 单个数据面板 - 面板标题 -
.transfer-panel__title- 面板标题区域 - 数据列表 -
.transfer-panel__list- 数据项列表 - 数据项 -
.transfer-panel__item- 单个数据项
TransferPanel 组件的核心结构:
html
<div class="transfer-panel" :style="{ ...transferStyle, '--hover-background': hoverBackground }">
<div class="transfer-panel__title">{{ title }}</div>
<ul class="transfer-panel__list">
<li
v-for="item in data"
:key="item.key"
:class="['transfer-panel__item', { 'is-disabled': item.disabled, 'is-selected': selected.includes(item.key) }]"
:style="{ '--hover-background': hoverBackground }"
@click="toggleSelect(item)"
>
<input class="transfer-panel__checkbox" type="checkbox" :checked="selected.includes(item.key)" :disabled="item.disabled" />
{{ item.label }}
</li>
</ul>
</div>交互设计
选择机制
TransferPanel 组件的选择机制设计如下:
typescript
const toggleSelect = (item: Option) => {
if (item.disabled) return
const selected = [...props.selected]
const index = selected.indexOf(item.key)
if (index > -1) {
selected.splice(index, 1)
} else {
selected.push(item.key)
}
emit('update:selected', selected)
}选择规则:
- 点击选择 - 点击数据项切换选择状态
- 禁用控制 - 禁用项无法被选择
- 状态同步 - 通过 update:selected 事件同步选择状态
- 批量操作 - 支持同时选择多个项
数据流转
Transfer 组件的数据流转设计如下:
- 初始状态 - 根据 modelValue 和 data 计算左右面板数据
- 选择状态 - 用户在左右面板选择数据项
- 数据移动 - 用户点击操作按钮,触发数据移动
- 状态更新 - 更新 modelValue,触发界面重新渲染
数据流转特点:
- 单向数据流 - 数据只能通过操作按钮在左右面板间移动
- 状态一致性 - 确保 modelValue 与界面展示一致
- 操作原子性 - 每次移动操作都是原子操作
扩展性设计
Transfer 组件具备出色的扩展性:
- 样式扩展 - 通过 backgroundColor、colorText 和 hoverBackground 属性支持自定义样式
- 标题扩展 - 通过 titles 属性支持自定义标题
- 数据扩展 - 通过 Option接口支持自定义数据结构
- 交互扩展 - 通过事件机制支持自定义交互逻辑
- 样式变量 - 通过 CSS 变量支持细粒度样式控制
样式细节
Transfer 组件在样式处理上有以下特点:
- 视觉层次 - 通过颜色和间距创建清晰的视觉层次
- 状态反馈 - 不同状态有明确的视觉反馈scss
&.is-disabled { color: $disabled-color; cursor: not-allowed; } &.is-selected { background-color: var(--hover-background, $selected-color) !important; font-weight: 500; } - 过渡效果 - 使用过渡效果增强用户体验
- 滚动优化 - 针对长列表优化滚动体验scss
&__list { max-height: 350px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: $border-color transparent; } - 响应式设计 - 适应不同屏幕尺寸
性能优化
通过以下方式优化性能:
- 计算属性 - 使用计算属性缓存数据拆分结果
- 虚拟滚动 - 通过限制最大高度和优化滚动实现虚拟滚动效果
- 事件委托 - 通过事件委托减少事件监听器数量
- 样式优化 - 使用 CSS 变量动态调整样式,减少重排
- 批量操作 - 支持批量选择和移动,减少操作次数
设计价值
Transfer 组件延续了我们组件库的设计哲学:
- 职责分离 - 容器和面板职责清晰分离
- 状态驱动 - 通过状态变化驱动界面更新
- 数据抽象 - 通过Option接口抽象数据结构
- 交互一致性 - 保持与组件库其他组件一致的交互模式
- 可定制性 - 通过属性和样式变量提供丰富的定制能力