穿梭框组件用于在两个列表之间进行数据项的交换,支持单选、多选、搜索和批量操作等功能。适用于权限配置、商品分类选择等场景。
<div class="transfer">
<!-- 左侧列表 -->
<div class="transfer-list">
<div class="transfer-list-header">
<span class="transfer-list-title">待选列表</span>
<span class="transfer-list-count">0</span>
</div>
<ul class="transfer-list-items transfer-list-content">
<li class="transfer-item" data-value="1">
<span class="transfer-item-checkbox"></span>
<span class="transfer-item-content">选项 1</span>
</li>
<!-- 更多选项... -->
</ul>
</div>
<!-- 操作按钮 -->
<div class="transfer-operations">
<button class="transfer-button transfer-button-to-right" disabled>
<i class="fas fa-chevron-right"></i>
</button>
<button class="transfer-button transfer-button-to-left" disabled>
<i class="fas fa-chevron-left"></i>
</button>
</div>
<!-- 右侧列表 -->
<div class="transfer-list">
<div class="transfer-list-header">
<span class="transfer-list-title">已选列表</span>
<span class="transfer-list-count">0</span>
</div>
<ul class="transfer-list-items transfer-list-content">
<!-- 空状态 -->
<li class="transfer-empty">
<span class="transfer-empty-icon"><i class="fas fa-inbox"></i></span>
<span>暂无数据</span>
</li>
</ul>
</div>
</div>
<div class="transfer">
<!-- 左侧列表 -->
<div class="transfer-list">
<div class="transfer-list-header">
<span class="transfer-list-title">待选列表</span>
<span class="transfer-list-count">0</span>
</div>
<div class="transfer-search">
<input type="text" class="transfer-search-input" placeholder="搜索...">
</div>
<ul class="transfer-list-items transfer-list-content">
<!-- 列表项... -->
</ul>
</div>
<!-- 操作按钮 -->
<div class="transfer-operations">
<button class="transfer-button transfer-button-to-right" disabled>
<i class="fas fa-chevron-right"></i>
</button>
<button class="transfer-button transfer-button-to-left" disabled>
<i class="fas fa-chevron-left"></i>
</button>
</div>
<!-- 右侧列表 -->
<div class="transfer-list">
<div class="transfer-list-header">
<span class="transfer-list-title">已选列表</span>
<span class="transfer-list-count">0</span>
</div>
<div class="transfer-search">
<input type="text" class="transfer-search-input" placeholder="搜索...">
</div>
<ul class="transfer-list-items transfer-list-content">
<!-- 空状态 -->
<li class="transfer-empty">
<span class="transfer-empty-icon"><i class="fas fa-inbox"></i></span>
<span>暂无数据</span>
</li>
</ul>
</div>
</div>
| 类名 | 描述 | 应用位置 |
|---|---|---|
.transfer |
穿梭框容器 | 外层容器 |
.transfer-list |
列表容器 | 左右两个列表 |
.transfer-list-header |
列表头部 | 列表顶部区域 |
.transfer-list-title |
列表标题 | 头部标题文本 |
.transfer-list-count |
列表计数 | 头部项目计数 |
.transfer-search |
搜索区域 | 搜索框容器 |
.transfer-search-input |
搜索输入框 | 搜索输入元素 |
.transfer-list-content |
列表内容区域 | 列表内容容器 |
.transfer-list-items |
列表项目容器 | 列表项的ul元素 |
.transfer-item |
列表项 | 单个项目 |
.transfer-item-checkbox |
列表项复选框 | 项目选择框 |
.transfer-item-content |
列表项内容 | 项目文本内容 |
.transfer-operations |
操作按钮区域 | 中间按钮容器 |
.transfer-button |
操作按钮 | 移动按钮 |
.transfer-button-to-right |
向右移动按钮 | 从左到右移动 |
.transfer-button-to-left |
向左移动按钮 | 从右到左移动 |
.transfer-empty |
空状态 | 列表为空时显示 |
.transfer-empty-icon |
空状态图标 | 空状态中的图标 |
-selected |
选中状态 | 添加到.transfer-item |
-primary |
主要主题 | 添加到.transfer或.transfer-button |
-success |
成功主题 | 添加到.transfer |
-warning |
警告主题 | 添加到.transfer |
-danger |
危险主题 | 添加到.transfer |
-info |
信息主题 | 添加到.transfer |
-dark |
深色主题 | 添加到.transfer |
-compact |
紧凑尺寸 | 添加到.transfer |
-large |
大尺寸 | 添加到.transfer |
穿梭框组件由三个主要部分组成:左侧列表、操作按钮区域和右侧列表。
为了使穿梭框正常工作,您需要添加一些JavaScript代码来处理项目选择、移动和计数更新等功能。
// 初始化穿梭框
function initTransfer(transferElement) {
const leftList = transferElement.querySelector('.transfer-list:nth-child(1) .transfer-list-items');
const rightList = transferElement.querySelector('.transfer-list:nth-child(3) .transfer-list-items');
const toRightButton = transferElement.querySelector('.transfer-button-to-right');
const toLeftButton = transferElement.querySelector('.transfer-button-to-left');
const leftCount = transferElement.querySelector('.transfer-list:nth-child(1) .transfer-list-count');
const rightCount = transferElement.querySelector('.transfer-list:nth-child(3) .transfer-list-count');
// 更新计数
function updateCounts() {
const leftItemsCount = leftList.querySelectorAll('.transfer-item').length;
const rightItemsCount = rightList.querySelectorAll('.transfer-item').length;
leftCount.textContent = leftItemsCount;
rightCount.textContent = rightItemsCount;
// 检查是否有选中项
const leftSelected = leftList.querySelectorAll('.transfer-item.-selected').length > 0;
const rightSelected = rightList.querySelectorAll('.transfer-item.-selected').length > 0;
toRightButton.disabled = !leftSelected;
toLeftButton.disabled = !rightSelected;
// 检查空状态
checkEmptyState(leftList);
checkEmptyState(rightList);
}
// 检查空状态
function checkEmptyState(list) {
const itemsCount = list.querySelectorAll('.transfer-item').length;
const emptyState = list.querySelector('.transfer-empty');
if (itemsCount === 0 && !emptyState) {
const emptyElement = document.createElement('li');
emptyElement.className = 'transfer-empty';
emptyElement.innerHTML = `
<span class="transfer-empty-icon"><i class="fas fa-inbox"></i></span>
<span>暂无数据</span>
`;
list.appendChild(emptyElement);
} else if (itemsCount > 0 && emptyState) {
emptyState.remove();
}
}
// 处理项目选择
function handleItemSelection(item) {
item.classList.toggle('-selected');
updateCounts();
}
// 添加项目点击事件
function addItemClickEvents(list) {
list.querySelectorAll('.transfer-item').forEach(item => {
item.addEventListener('click', () => handleItemSelection(item));
});
}
// 移动项目
function moveItems(fromList, toList) {
const selectedItems = fromList.querySelectorAll('.transfer-item.-selected');
if (selectedItems.length === 0) return;
// 添加退出动画
selectedItems.forEach(item => {
item.classList.add('exiting');
});
// 等待动画完成后移动
setTimeout(() => {
selectedItems.forEach(item => {
item.classList.remove('exiting', '-selected');
toList.appendChild(item);
// 重新添加点击事件
item.addEventListener('click', () => handleItemSelection(item));
});
updateCounts();
}, 300);
}
// 添加按钮点击事件
toRightButton.addEventListener('click', () => moveItems(leftList, rightList));
toLeftButton.addEventListener('click', () => moveItems(rightList, leftList));
// 初始化事件
addItemClickEvents(leftList);
addItemClickEvents(rightList);
// 初始化计数
updateCounts();
}
// 初始化页面上的所有穿梭框
document.addEventListener('DOMContentLoaded', function() {
const basicTransfer = document.getElementById('basicTransfer');
const searchTransfer = document.getElementById('searchTransfer');
if (basicTransfer) initTransfer(basicTransfer);
if (searchTransfer) initTransfer(searchTransfer);
// 搜索功能实现
const searchInputs = document.querySelectorAll('.transfer-search-input');
searchInputs.forEach(input => {
input.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const listItems = this.closest('.transfer-list').querySelector('.transfer-list-items');
const items = listItems.querySelectorAll('.transfer-item');
items.forEach(item => {
const content = item.querySelector('.transfer-item-content').textContent.toLowerCase();
if (content.includes(searchTerm)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
});
});