CutestUI - 穿梭框组件

组件概述

穿梭框组件用于在两个列表之间进行数据项的交换,支持单选、多选、搜索和批量操作等功能。适用于权限配置、商品分类选择等场景。

特性

基本使用

基础穿梭框

待选列表 0
  • 选项 1
  • 选项 2
  • 选项 3
  • 选项 4
  • 选项 5
已选列表 0
  • 暂无数据
<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>

样式变体

色彩主题

主要主题

待选 1
  • 选项 1
已选 0
  • 暂无数据

成功主题

待选 1
  • 选项 1
已选 0
  • 暂无数据

警告主题

待选 1
  • 选项 1
已选 0
  • 暂无数据

危险主题

待选 1
  • 选项 1
已选 0
  • 暂无数据

信息主题

待选 1
  • 选项 1
已选 0
  • 暂无数据

深色主题

待选 1
  • 选项 1
已选 0
  • 暂无数据

尺寸变体

紧凑尺寸

待选 1
  • 选项 1
已选 0
  • 暂无数据

大尺寸

待选 1
  • 选项 1
已选 0
  • 暂无数据

附加功能

带搜索功能的穿梭框

待选列表 0
  • 选项 1
  • 选项 2
  • 选项 3
  • 选项 4
  • 选项 5
已选列表 0
  • 暂无数据
<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交互

为了使穿梭框正常工作,您需要添加一些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';
                }
            });
        });
    });
});

注意事项

@media (max-width: 768px) { .container { padding: 12px; } h1 { font-size: 24px; } .section h2 { font-size: 20px; } .component-preview { padding: 16px; } table { display: block; overflow-x: auto; } .variant-grid { grid-template-columns: 1fr; } }

CutestUI - 穿梭框组件

组件概述

穿梭框组件用于在两个列表之间进行数据项的选择和移动,支持单选、多选、搜索等功能,适用于权限配置、数据迁移等场景。

基本使用

基础穿梭框

左侧列表 5/10
  • 选项 1
  • 选项 2
  • 选项 3
  • 选项 4
  • 选项 5
  • 选项 6
  • 选项 7
  • 选项 8
  • 选项 9
  • 选项 10
右侧列表 0/0
    <div class="transfer">
        <div class="transfer-list">
            <div class="transfer-list-header">
                <span class="transfer-list-title">左侧列表</span>
                <span class="transfer-list-count"><span>5</span>/10</span>
            </div>
            <div class="transfer-list-content">
                <ul class="transfer-list-items">
                    <li class="transfer-item" data-id="1">
                        <span class="transfer-item-checkbox"></span>
                        <span class="transfer-item-content">选项 1</span>
                    </li>
                    <!-- 更多选项... -->
                </ul>
            </div>
        </div>
        
        <div class="transfer-operations">
            <button class="transfer-button -primary" title="向右移动选中项">
                <i class="fas fa-chevron-right"></i>
            </button>
            <button class="transfer-button -primary" title="向左移动选中项" disabled>
                <i class="fas fa-chevron-left"></i>
            </button>
            <button class="transfer-button" title="向右移动全部">
                <i class="fas fa-arrow-right"></i>
            </button>
            <button class="transfer-button" title="向左移动全部" disabled>
                <i class="fas fa-arrow-left"></i>
            </button>
        </div>
        
        <div class="transfer-list">
            <div class="transfer-list-header">
                <span class="transfer-list-title">右侧列表</span>
                <span class="transfer-list-count"><span>0</span>/0</span>
            </div>
            <div class="transfer-list-content">
                <ul class="transfer-list-items"></ul>
            </div>
        </div>
    </div>

    样式变体

    主要主题

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -primary 类应用主要主题样式

    成功主题

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -success 类应用成功主题样式

    警告主题

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -warning 类应用警告主题样式

    危险主题

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -danger 类应用危险主题样式

    信息主题

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -info 类应用信息主题样式

    深色主题

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -dark 类应用深色主题样式

    尺寸变体

    紧凑尺寸

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -compact 类应用紧凑尺寸样式

    大尺寸

    左侧列表 5/10
    • 选项 1
    • 选项 2

    使用 -large 类应用大尺寸样式

    附加功能

    带搜索功能的穿梭框

    左侧列表 5/10
    • 选项 1
    • 选项 2
    • 选项 3
    • 选项 4
    • 选项 5
    • 选项 6
    • 选项 7
    • 选项 8
    • 选项 9
    • 选项 10
    右侧列表 0/0
      <div class="transfer">
          <div class="transfer-list">
              <div class="transfer-list-header">
                  <span class="transfer-list-title">左侧列表</span>
                  <span class="transfer-list-count"><span>5</span>/10</span>
              </div>
              <div class="transfer-search">
                  <input type="text" class="transfer-search-input" placeholder="搜索左侧列表">
              </div>
              <div class="transfer-list-content">
                  <ul class="transfer-list-items">
                      <!-- 列表项... -->
                  </ul>
              </div>
          </div>
          
          <div class="transfer-operations">
              <!-- 操作按钮... -->
          </div>
          
          <div class="transfer-list">
              <div class="transfer-list-header">
                  <span class="transfer-list-title">右侧列表</span>
                  <span class="transfer-list-count"><span>0</span>/0</span>
              </div>
              <div class="transfer-search">
                  <input type="text" class="transfer-search-input" placeholder="搜索右侧列表">
              </div>
              <div class="transfer-list-content">
                  <ul class="transfer-list-items"></ul>
              </div>
          </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.-selected 选中的列表项 处于选中状态的数据项
      .transfer-item-checkbox 列表项复选框 表示列表项是否被选中
      .transfer-item-content 列表项内容 显示数据项文本内容
      .transfer-operations 操作按钮区域 包裹所有操作按钮的容器
      .transfer-button 操作按钮 用于移动数据项的按钮
      .transfer-button.-primary 主要操作按钮 常用的移动按钮(向右/向左)
      .transfer-empty 空状态容器 列表为空时显示的容器
      .transfer-empty-icon 空状态图标 空状态显示的图标
      .transfer.-primary 主要主题 应用主要颜色主题
      .transfer.-success 成功主题 应用成功颜色主题
      .transfer.-warning 警告主题 应用警告颜色主题
      .transfer.-danger 危险主题 应用危险颜色主题
      .transfer.-info 信息主题 应用信息颜色主题
      .transfer.-dark 深色主题 应用深色主题
      .transfer.-compact 紧凑尺寸 应用紧凑尺寸样式
      .transfer.-large 大尺寸 应用大尺寸样式

      使用指南

      基础结构

      CutestUI 穿梭框组件采用了清晰的结构,主要由三个部分组成:左侧列表、操作按钮区域和右侧列表。每个列表都包含头部、搜索框(可选)和内容区域。

      JavaScript 交互

      要使穿梭框正常工作,需要实现以下交互逻辑:

      1. 列表项的选择/取消选择
      2. 选中项的单向和双向移动
      3. 全部项目的单向和双向移动
      4. 列表项数量的更新
      5. 搜索功能的实现
      // 穿梭框基本交互实现示例
      // 初始化元素引用
      const leftList = document.getElementById('leftList');
      const rightList = document.getElementById('rightList');
      const toRightBtn = document.getElementById('toRightBtn');
      const toLeftBtn = document.getElementById('toLeftBtn');
      const allToRightBtn = document.getElementById('allToRightBtn');
      const allToLeftBtn = document.getElementById('allToLeftBtn');
      const leftCount = document.getElementById('leftCount');
      const rightCount = document.getElementById('rightCount');
      
      // 切换项目选择状态
      function toggleItemSelection(item) {
          item.classList.toggle('-selected');
          updateButtonStates();
      }
      
      // 更新按钮状态
      function updateButtonStates() {
          const leftSelected = leftList.querySelectorAll('.transfer-item.-selected').length > 0;
          const rightSelected = rightList.querySelectorAll('.transfer-item.-selected').length > 0;
          const leftHasItems = leftList.querySelectorAll('.transfer-item').length > 0;
          const rightHasItems = rightList.querySelectorAll('.transfer-item').length > 0;
          
          toRightBtn.disabled = !leftSelected;
          toLeftBtn.disabled = !rightSelected;
          allToRightBtn.disabled = !leftHasItems;
          allToLeftBtn.disabled = !rightHasItems;
      }
      
      // 移动选中项
      function moveSelectedItems(fromList, toList) {
          const selectedItems = fromList.querySelectorAll('.transfer-item.-selected');
          selectedItems.forEach(item => {
              toList.appendChild(item);
              item.classList.remove('-selected');
          });
          updateCounts();
          updateButtonStates();
      }
      
      // 移动所有项
      function moveAllItems(fromList, toList) {
          const items = Array.from(fromList.querySelectorAll('.transfer-item'));
          items.forEach(item => {
              toList.appendChild(item);
              item.classList.remove('-selected');
          });
          updateCounts();
          updateButtonStates();
      }
      
      // 更新计数
      function updateCounts() {
          leftCount.textContent = leftList.querySelectorAll('.transfer-item').length;
          rightCount.textContent = rightList.querySelectorAll('.transfer-item').length;
      }
      
      // 添加事件监听器
      leftList.addEventListener('click', (e) => {
          const item = e.target.closest('.transfer-item');
          if (item) {
              toggleItemSelection(item);
          }
      });
      
      rightList.addEventListener('click', (e) => {
          const item = e.target.closest('.transfer-item');
          if (item) {
              toggleItemSelection(item);
          }
      });
      
      toRightBtn.addEventListener('click', () => moveSelectedItems(leftList, rightList));
      toLeftBtn.addEventListener('click', () => moveSelectedItems(rightList, leftList));
      allToRightBtn.addEventListener('click', () => moveAllItems(leftList, rightList));
      allToLeftBtn.addEventListener('click', () => moveAllItems(rightList, leftList));
      
      // 初始化
      updateButtonStates();
      
      // 搜索功能实现示例
      function initSearchFunctionality() {
          const leftSearchInput = document.getElementById('leftSearchInput');
          const rightSearchInput = document.getElementById('rightSearchInput');
          const searchLeftList = document.getElementById('searchLeftList');
          const searchRightList = document.getElementById('searchRightList');
          
          // 左侧列表搜索
          leftSearchInput.addEventListener('input', (e) => {
              const searchTerm = e.target.value.toLowerCase();
              const items = searchLeftList.querySelectorAll('.transfer-item');
              items.forEach(item => {
                  const text = item.querySelector('.transfer-item-content').textContent.toLowerCase();
                  item.style.display = text.includes(searchTerm) ? '' : 'none';
              });
          });
          
          // 右侧列表搜索
          rightSearchInput.addEventListener('input', (e) => {
              const searchTerm = e.target.value.toLowerCase();
              const items = searchRightList.querySelectorAll('.transfer-item');
              items.forEach(item => {
                  const text = item.querySelector('.transfer-item-content').textContent.toLowerCase();
                  item.style.display = text.includes(searchTerm) ? '' : 'none';
              });
          });
      }
      @media (max-width: 768px) { .container { padding: 16px; } h1 { font-size: 24px; } .section h2 { font-size: 20px; } .variant-grid { grid-template-columns: 1fr; } table { display: block; overflow-x: auto; } }

      CutestUI - 穿梭框组件

      组件概述

      穿梭框(Transfer)组件用于在两个列表之间进行数据的移动操作,提供直观的用户界面和丰富的交互体验。

      特性

      基本使用

      基础穿梭框

      可选列表 8
      • 选项 1
      • 选项 2
      • 选项 3
      • 选项 4
      • 选项 5
      • 选项 6
      • 选项 7
      • 选项 8
      已选列表 0
      暂无数据
      <div id="basic-transfer" class="transfer">
          <!-- 左侧列表 -->
          <div class="transfer-list">
              <div class="transfer-list-header">
                  <span class="transfer-list-title">可选列表</span>
                  <span class="transfer-list-count">8</span>
              </div>
              <div class="transfer-list-content">
                  <ul class="transfer-list-items">
                      <li class="transfer-item" data-value="item1">
                          <div class="transfer-item-checkbox"></div>
                          <span class="transfer-item-content">选项 1</span>
                      </li>
                      <!-- 更多选项... -->
                  </ul>
              </div>
          </div>
          
          <!-- 操作按钮 -->
          <div class="transfer-operations">
              <button class="transfer-button -primary" id="to-right" disabled>
                  <i class="fas fa-chevron-right"></i>
              </button>
              <button class="transfer-button -primary" id="all-to-right" disabled>
                  <i class="fas fa-chevron-right"></i>
                  <i class="fas fa-chevron-right"></i>
              </button>
              <button class="transfer-button -primary" id="to-left" disabled>
                  <i class="fas fa-chevron-left"></i>
              </button>
              <button class="transfer-button -primary" id="all-to-left" disabled>
                  <i class="fas fa-chevron-left"></i>
                  <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-list-content">
                  <div class="transfer-empty">
                      <i class="fas fa-inbox transfer-empty-icon"></i>
                      <div>暂无数据</div>
                  </div>
              </div>
          </div>
      </div>

      样式变体

      色彩主题

      主要主题

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -primary 类应用蓝色主题

      成功主题

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -success 类应用绿色主题

      警告主题

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -warning 类应用黄色主题

      危险主题

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -danger 类应用红色主题

      信息主题

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -info 类应用灰色主题

      深色主题

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -dark 类应用深色主题

      尺寸变体

      紧凑尺寸

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -compact 类应用紧凑尺寸

      大尺寸

      可选列表 3
      • 选项 A
      • 选项 B
      • 选项 C

      使用 -large 类应用大尺寸

      附加功能

      带搜索功能的穿梭框

      可选列表 8
      • 选项 1
      • 选项 2
      • 选项 3
      • 选项 4
      • 选项 5
      • 选项 6
      • 选项 7
      • 选项 8
      已选列表 0
      暂无数据
      <div id="search-transfer" class="transfer">
          <!-- 左侧列表 -->
          <div class="transfer-list">
              <div class="transfer-list-header">
                  <span class="transfer-list-title">可选列表</span>
                  <span class="transfer-list-count">8</span>
              </div>
              <div class="transfer-search">
                  <input type="text" class="transfer-search-input" placeholder="搜索...">
              </div>
              <div class="transfer-list-content">
                  <!-- 列表内容... -->
              </div>
          </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>
              <div class="transfer-list-content">
                  <!-- 列表内容... -->
              </div>
          </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 列表项目容器类 项目列表容器
      .transfer-item 列表项目类 单个项目
      .transfer-item-checkbox 项目复选框类 项目前的复选框
      .transfer-item-content 项目内容类 项目文本内容
      .transfer-operations 操作按钮区域类 操作按钮的容器
      .transfer-button 操作按钮类 单个操作按钮
      .transfer-empty 空状态容器类 列表为空时的占位区域
      .transfer-empty-icon 空状态图标类 空状态的图标
      -selected 选中状态类 应用于 .transfer-item
      -primary 主要主题类 应用于 .transfer.transfer-button
      -success 成功主题类 应用于 .transfer
      -warning 警告主题类 应用于 .transfer
      -danger 危险主题类 应用于 .transfer
      -info 信息主题类 应用于 .transfer
      -dark 深色主题类 应用于 .transfer
      -light 浅色主题类 应用于 .transfer
      -compact 紧凑尺寸类 应用于 .transfer
      -large 大尺寸类 应用于 .transfer

      使用指南

      基础结构

      CutestUI 穿梭框组件由三个主要部分组成:左侧列表、操作按钮和右侧列表。每个部分都有特定的CSS类来定义其样式和行为。

      JavaScript 交互

      要实现穿梭框的完整功能,您需要添加一些JavaScript代码来处理项目选择、移动和搜索等交互操作。以下是一个基本的实现示例:

      // 初始化基础穿梭框
      function initBasicTransfer() {
          const transfer = document.getElementById('basic-transfer');
          const leftList = transfer.querySelector('.transfer-list:nth-child(1) .transfer-list-items');
          const rightList = transfer.querySelector('.transfer-list:nth-child(3) .transfer-list-content');
          const leftCount = transfer.querySelector('.transfer-list:nth-child(1) .transfer-list-count');
          const rightCount = transfer.querySelector('.transfer-list:nth-child(3) .transfer-list-count');
          const toRightBtn = document.getElementById('to-right');
          const allToRightBtn = document.getElementById('all-to-right');
          const toLeftBtn = document.getElementById('to-left');
          const allToLeftBtn = document.getElementById('all-to-left');
          
          // 处理项目选择
          leftList.addEventListener('click', function(e) {
              const item = e.target.closest('.transfer-item');
              if (item) {
                  item.classList.toggle('-selected');
                  updateButtons();
              }
          });
          
          // 绑定按钮事件
          toRightBtn.addEventListener('click', function() {
              moveSelectedItems(leftList, rightList);
              updateButtons();
          });
          
          allToRightBtn.addEventListener('click', function() {
              moveAllItems(leftList, rightList);
              updateButtons();
          });
          
          toLeftBtn.addEventListener('click', function() {
              moveSelectedItems(rightList.querySelector('.transfer-list-items'), leftList);
              updateButtons();
          });
          
          allToLeftBtn.addEventListener('click', function() {
              moveAllItems(rightList.querySelector('.transfer-list-items'), leftList);
              updateButtons();
          });
          
          // 更新按钮状态
          function updateButtons() {
              const leftSelected = leftList.querySelectorAll('.transfer-item.-selected').length > 0;
              const leftHasItems = leftList.querySelectorAll('.transfer-item').length > 0;
              const rightListItems = rightList.querySelector('.transfer-list-items');
              const rightSelected = rightListItems ? rightListItems.querySelectorAll('.transfer-item.-selected').length > 0 : false;
              const rightHasItems = rightListItems ? rightListItems.querySelectorAll('.transfer-item').length > 0 : false;
              
              toRightBtn.disabled = !leftSelected;
              allToRightBtn.disabled = !leftHasItems;
              toLeftBtn.disabled = !rightSelected;
              allToLeftBtn.disabled = !rightHasItems;
              
              // 更新计数
              leftCount.textContent = leftHasItems ? leftList.querySelectorAll('.transfer-item').length : 0;
              rightCount.textContent = rightHasItems ? rightListItems.querySelectorAll('.transfer-item').length : 0;
              
              // 处理空状态
              if (rightHasItems) {
                  rightList.querySelectorAll('.transfer-empty').forEach(el => el.remove());
              } else if (!rightList.querySelector('.transfer-empty')) {
                  const empty = document.createElement('div');
                  empty.className = 'transfer-empty';
                  empty.innerHTML = `
                      <i class="fas fa-inbox transfer-empty-icon"></i>
                      <div>暂无数据</div>
                  `;
                  rightList.appendChild(empty);
              }
          }
          
          // 移动选中的项目
          function moveSelectedItems(fromList, toList) {
              const selectedItems = fromList.querySelectorAll('.transfer-item.-selected');
              if (selectedItems.length === 0) return;
              
              // 确保目标列表有列表容器
              let toListItems = toList.querySelector('.transfer-list-items');
              if (!toListItems) {
                  // 移除空状态
                  toList.querySelectorAll('.transfer-empty').forEach(el => el.remove());
                  
                  // 创建列表容器
                  toListItems = document.createElement('ul');
                  toListItems.className = 'transfer-list-items';
                  toList.appendChild(toListItems);
                  
                  // 添加点击事件
                  toListItems.addEventListener('click', function(e) {
                      const item = e.target.closest('.transfer-item');
                      if (item) {
                          item.classList.toggle('-selected');
                          updateButtons();
                      }
                  });
              }
              
              // 移动项目
              selectedItems.forEach(item => {
                  item.classList.remove('-selected');
                  toListItems.appendChild(item);
              });
          }
          
          // 移动所有项目
          function moveAllItems(fromList, toList) {
              const items = fromList.querySelectorAll('.transfer-item');
              if (items.length === 0) return;
              
              // 确保目标列表有列表容器
              let toListItems = toList.querySelector('.transfer-list-items');
              if (!toListItems) {
                  // 移除空状态
                  toList.querySelectorAll('.transfer-empty').forEach(el => el.remove());
                  
                  // 创建列表容器
                  toListItems = document.createElement('ul');
                  toListItems.className = 'transfer-list-items';
                  toList.appendChild(toListItems);
                  
                  // 添加点击事件
                  toListItems.addEventListener('click', function(e) {
                      const item = e.target.closest('.transfer-item');
                      if (item) {
                          item.classList.toggle('-selected');
                          updateButtons();
                      }
                  });
              }
              
              // 移动项目
              items.forEach(item => {
                  item.classList.remove('-selected');
                  toListItems.appendChild(item);
              });
          }
      }
      
      // 初始化搜索功能的穿梭框
      function initSearchTransfer() {
          const transfer = document.getElementById('search-transfer');
          const leftSearch = transfer.querySelector('.transfer-list:nth-child(1) .transfer-search-input');
          const rightSearch = transfer.querySelector('.transfer-list:nth-child(3) .transfer-search-input');
          const leftList = transfer.querySelector('.transfer-list:nth-child(1) .transfer-list-items');
          const rightList = transfer.querySelector('.transfer-list:nth-child(3) .transfer-list-content');
          
          // 左侧搜索
          leftSearch.addEventListener('input', function() {
              const searchTerm = this.value.toLowerCase().trim();
              const items = leftList.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';
                  }
              });
          });
          
          // 右侧搜索(如果有内容)
          rightSearch.addEventListener('input', function() {
              const rightListItems = rightList.querySelector('.transfer-list-items');
              if (!rightListItems) return;
              
              const searchTerm = this.value.toLowerCase().trim();
              const items = rightListItems.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';
                  }
              });
          });
          
          // 其他初始化代码类似于基础穿梭框
          // ...
      }
      
      // 页面加载完成后初始化
      document.addEventListener('DOMContentLoaded', function() {
          initBasicTransfer();
          // 可以添加更多初始化函数
      });

      搜索功能实现

      要实现搜索功能,您可以为搜索输入框添加事件监听器,根据输入的内容过滤显示的项目。上面的代码示例中已经包含了搜索功能的实现。

      响应式适配

      CutestUI 穿梭框组件内置了响应式设计,在小屏幕设备上(宽度小于768px),会自动调整为垂直布局,操作按钮也会水平排列。您不需要额外的JavaScript代码来处理响应式行为,CSS媒体查询已经处理了这些变化。

      @media (max-width: 768px) { .variant-grid { grid-template-columns: 1fr; } .container { padding: 16px; } h1 { font-size: 24px; } .section h2 { font-size: 20px; } }

      CutestUI - 穿梭框组件

      概述

      穿梭框组件用于在两个列表之间移动项目,通常用于选择、分类和转移数据。CutestUI 穿梭框组件设计简洁美观,支持多种交互方式和样式变体。

      特性

      基本使用

      基础穿梭框

      可选项目 0
      • 项目一
      • 项目二
      • 项目三
      • 项目四
      • 项目五
      • 项目六
      • 项目七
      已选项目 0

      基础穿梭框组件,支持项目的双向移动和搜索功能

      <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>
              <div class="transfer-list-content">
                  <ul class="transfer-list-items">
                      <li class="transfer-item">
                          <span class="transfer-item-checkbox"></span>
                          <span class="transfer-item-content">项目名称</span>
                      </li>
                      <!-- 更多项目... -->
                  </ul>
              </div>
          </div>
          
          <!-- 操作按钮 -->
          <div class="transfer-operations">
              <button class="transfer-button -primary">
                  <i class="fas fa-chevron-right"></i>
              </button>
              <button class="transfer-button -primary">
                  <i class="fas fa-chevron-right"></i>
                  <i class="fas fa-chevron-right"></i>
              </button>
              <button class="transfer-button">
                  <i class="fas fa-chevron-left"></i>
              </button>
              <button class="transfer-button">
                  <i class="fas fa-chevron-left"></i>
                  <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>
              <div class="transfer-list-content">
                  <ul class="transfer-list-items">
                      <!-- 右侧项目... -->
                  </ul>
              </div>
          </div>
      </div>

      样式变体

      主要色彩主题

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-primary 类应用主要色彩主题

      成功色彩主题

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-success 类应用成功色彩主题

      警告色彩主题

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-warning 类应用警告色彩主题

      危险色彩主题

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-danger 类应用危险色彩主题

      信息色彩主题

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-info 类应用信息色彩主题

      深色主题

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-dark 类应用深色主题

      尺寸变体

      紧凑尺寸

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-compact 类应用紧凑尺寸

      大尺寸

      左侧列表 2
      • 项目一
      • 项目二
      右侧列表 0
      暂无项目

      使用 .-large 类应用大尺寸

      附加功能

      带搜索功能的穿梭框

      可选项目 3
      • 张三
      • 李四
      • 王五
      已选项目 0

      带搜索功能的穿梭框,支持快速过滤和查找项目

      类名参考

      类名 描述 应用位置
      .transfer 基础穿梭框容器类 div 元素
      .transfer-list 列表区域容器 div 元素
      .transfer-list-header 列表头部 div 元素
      .transfer-list-title 列表标题 span 元素
      .transfer-list-count 列表项目计数 span 元素
      .transfer-search 搜索框容器 div 元素
      .transfer-search-input 搜索输入框 input 元素
      .transfer-list-content 列表内容区域 div 元素
      .transfer-list-items 项目列表 ul 元素
      .transfer-item 列表项目 li 元素
      .transfer-item-checkbox 项目复选框 span 元素
      .transfer-item-content 项目内容 span 元素
      .transfer-operations 操作按钮区域 div 元素
      .transfer-button 操作按钮 button 元素
      .transfer-empty 空状态提示 div 元素
      .transfer-empty-icon 空状态图标 i 元素(Font Awesome)
      .-selected 选中状态 .transfer-item 元素
      .-primary 主要色彩主题 .transfer 元素
      .-success 成功色彩主题 .transfer 元素
      .-warning 警告色彩主题 .transfer 元素
      .-danger 危险色彩主题 .transfer 元素
      .-info 信息色彩主题 .transfer 元素
      .-dark 深色主题 .transfer 元素
      .-light 浅色主题 .transfer 元素
      .-compact 紧凑尺寸 .transfer 元素
      .-large 大尺寸 .transfer 元素

      使用指南

      CutestUI 穿梭框组件设计为易于使用且高度可定制。以下是一些使用指南,帮助您在项目中有效使用穿梭框组件。

      基础结构

      穿梭框组件的基础结构由两个列表区域和中间的操作按钮区域组成,通过不同的类名组合实现各种功能和样式。

      JavaScript 增强

      组件核心样式基于 CSS 实现,但需要少量 JavaScript 来实现交互功能,如项目移动、搜索过滤等。

      // 穿梭框基本交互示例
      function initTransfer(transferId) {
          const transfer = document.getElementById(transferId);
          const leftList = transfer.querySelector('#leftList');
          const rightList = transfer.querySelector('#rightList');
          const leftCount = transfer.querySelector('#leftCount');
          const rightCount = transfer.querySelector('#rightCount');
          const toRightBtn = transfer.querySelector('#toRightBtn');
          const toLeftBtn = transfer.querySelector('#toLeftBtn');
          const toRightAllBtn = transfer.querySelector('#toRightAllBtn');
          const toLeftAllBtn = transfer.querySelector('#toLeftAllBtn');
          
          // 初始化计数
          updateCounts();
          
          // 点击项目选中/取消选中
          leftList.addEventListener('click', handleItemClick);
          rightList.addEventListener('click', handleItemClick);
          
          // 移动选中项目
          toRightBtn.addEventListener('click', () => moveSelectedItems(leftList, rightList));
          toLeftBtn.addEventListener('click', () => moveSelectedItems(rightList, leftList));
          
          // 移动全部项目
          toRightAllBtn.addEventListener('click', () => moveAllItems(leftList, rightList));
          toLeftAllBtn.addEventListener('click', () => moveAllItems(rightList, leftList));
          
          // 处理项目点击
          function handleItemClick(e) {
              const item = e.target.closest('.transfer-item');
              if (item) {
                  item.classList.toggle('-selected');
                  updateButtonStates();
              }
          }
          
          // 移动选中项目
          function moveSelectedItems(fromList, toList) {
              const selectedItems = fromList.querySelectorAll('.transfer-item.-selected');
              
              if (selectedItems.length > 0) {
                  // 克隆选中的项目并添加到目标列表
                  selectedItems.forEach(item => {
                      const newItem = item.cloneNode(true);
                      newItem.classList.remove('-selected');
                      toList.appendChild(newItem);
                  });
                  
                  // 移除源列表中的选中项目
                  selectedItems.forEach(item => item.remove());
                  
                  // 更新计数和按钮状态
                  updateCounts();
                  updateButtonStates();
              }
          }
          
          // 移动全部项目
          function moveAllItems(fromList, toList) {
              const items = Array.from(fromList.querySelectorAll('.transfer-item'));
              
              if (items.length > 0) {
                  // 克隆所有项目并添加到目标列表
                  items.forEach(item => {
                      const newItem = item.cloneNode(true);
                      newItem.classList.remove('-selected');
                      toList.appendChild(newItem);
                  });
                  
                  // 清空源列表
                  fromList.innerHTML = '';
                  
                  // 更新计数和按钮状态
                  updateCounts();
                  updateButtonStates();
              }
          }
          
          // 更新计数
          function updateCounts() {
              leftCount.textContent = leftList.querySelectorAll('.transfer-item').length;
              rightCount.textContent = rightList.querySelectorAll('.transfer-item').length;
              
              // 处理空状态
              if (leftList.querySelectorAll('.transfer-item').length === 0) {
                  leftList.innerHTML = `
                      
      暂无项目
      `; } else { // 移除空状态提示(如果存在) const emptyState = leftList.querySelector('.transfer-empty'); if (emptyState) { emptyState.remove(); } } if (rightList.querySelectorAll('.transfer-item').length === 0) { rightList.innerHTML = `
      暂无项目
      `; } else { // 移除空状态提示(如果存在) const emptyState = rightList.querySelector('.transfer-empty'); if (emptyState) { emptyState.remove(); } } } // 更新按钮状态 function updateButtonStates() { const leftHasSelected = leftList.querySelectorAll('.transfer-item.-selected').length > 0; const rightHasSelected = rightList.querySelectorAll('.transfer-item.-selected').length > 0; const leftHasItems = leftList.querySelectorAll('.transfer-item').length > 0; const rightHasItems = rightList.querySelectorAll('.transfer-item').length > 0; toRightBtn.disabled = !leftHasSelected; toLeftBtn.disabled = !rightHasSelected; toRightAllBtn.disabled = !leftHasItems; toLeftAllBtn.disabled = !rightHasItems; } } // 初始化穿梭框 initTransfer('basicTransfer');

      搜索功能实现

      以下是实现搜索功能的 JavaScript 示例代码:

      // 搜索功能实现示例
      function initSearchableTransfer(transferId) {
          const transfer = document.getElementById(transferId);
          const leftInput = transfer.querySelector('#searchLeftInput');
          const rightInput = transfer.querySelector('#searchRightInput');
          const leftList = transfer.querySelector('#searchLeftList');
          const rightList = transfer.querySelector('#searchRightList');
          
          // 存储原始项目数据
          const originalLeftItems = Array.from(leftList.querySelectorAll('.transfer-item'));
          const originalRightItems = Array.from(rightList.querySelectorAll('.transfer-item'));
          
          // 为搜索输入框添加事件监听
          leftInput.addEventListener('input', () => filterItems(leftList, leftInput.value));
          rightInput.addEventListener('input', () => filterItems(rightList, rightInput.value));
          
          // 过滤项目
          function filterItems(list, searchTerm) {
              // 移除列表中的所有子元素
              while (list.firstChild) {
                  list.removeChild(list.firstChild);
              }
              
              // 获取原始项目数据
              let allItems;
              if (list.id === 'searchLeftList') {
                  allItems = originalLeftItems;
              } else {
                  allItems = originalRightItems;
              }
              
              // 过滤并添加匹配的项目
              const filteredItems = allItems.filter(item => {
                  const text = item.dataset.text || item.textContent.toLowerCase();
                  return text.includes(searchTerm.toLowerCase());
              });
              
              // 添加过滤后的项目到列表
              if (filteredItems.length > 0) {
                  filteredItems.forEach(item => {
                      const newItem = item.cloneNode(true);
                      list.appendChild(newItem);
                  });
              } else {
                  // 显示空状态
                  list.innerHTML = `
                      
      未找到匹配的项目
      `; } } } // 初始化带搜索功能的穿梭框 initSearchableTransfer('searchableTransfer');

      响应式适配

      组件内置了响应式设计,在移动设备上会自动调整为垂直布局,提高在小屏幕上的可用性。

      可访问性考虑

      组件包含基本的无障碍支持,如焦点状态和键盘导航支持。在实际项目中,建议根据具体需求进一步增强可访问性。