element ui plus 穿梭框 拖动排序

<template>
  <div class="container">
    <el-transfer
      ref="sortableRef"
      v-model="rightData"
      filterable
      :filter-method="filterMethod"
      filter-placeholder="请输入"
      :data="data"
      @change="transferChange"
    >
      <template #default="{ option }">
        <div
          class="transferLable"
          :draggable="rightData.includes(option.key)"
          @dragstart="handleDragStart(option)"
          @dragenter="handleDragenter($event, option)"
          @dragend="handleDrop($event)"
        >
          <span class="trnsferValue">{{ option.label }}</span>
          <span id="draggable" class="sort">
            <el-icon><Rank /></el-icon>
          </span>
        </div>
      </template>
    </el-transfer>
  </div>
</template>

<script setup name="nurserItem">
import { ref } from "vue";
import { Rank } from "@element-plus/icons-vue";

const data = ref([
  { key: 111, label: "11" },
  { key: 122, label: "22" },
  { key: 133, label: "33" },
  { key: 144, label: "44" },
  { key: 155, label: "55" },
  { key: 166, label: "66" },
]);
const rightData = ref([]);

const filterMethod = (query, item) => {
  if (!query) {
    return true;
  }
  return item.label.includes(query);
};

// 往右侧添加时,手动添加头部
const transferChange = (_, direction, movedKeys) => {
  if (direction === "right") {
    const arrList = data.value.filter((item) => !movedKeys.includes(item.key));
    const arrUnshift = data.value.filter((item) => movedKeys.includes(item.key));
    data.value = [...arrUnshift, ...arrList];
  }
  console.log(
    "往右侧添加时,手动添加头部 enrichedRightData.value",
    enrichedRightData.value
  );
  // console.log("往右侧添加时,手动添加头部 data.value", data.value);
};

let dragTarget = null; // 用于存储被拖动的目标项
let dragIndex = -1; // 被拖动项在数组中的原始索引
let targetOption = null; // 拖动过程中停放目标

// 开始拖动
const handleDragStart = (option) => {
  dragTarget = option;
  dragIndex = data.value.findIndex((item) => item === option);
};

// 放置时重新排序数组
const handleDragenter = (event, option) => {
  event.preventDefault();
  if (!dragTarget || !option) return;
  targetOption = option;
  if (event.target.draggable) {
    clearMovingDOM();
    const targetIndex = data.value.findIndex((item) => item.key === targetOption.key);
    if (targetIndex < dragIndex) {
      // 往上拖拽
      event.target.className = "movingTop";
    } else {
      // 往下拖拽
      event.target.className = "movingBottom";
    }
  }

  console.log("放置时重新排序数组 enrichedRightData.value", enrichedRightData.value);
};

// 鼠标放开--拖拽结束
const handleDrop = () => {
  const targetIndex = data.value.findIndex((item) => item.key === targetOption.key);
  const newIndex = targetIndex;

  // 更新数组顺序
  const [removed] = data.value.splice(dragIndex, 1);
  data.value.splice(newIndex, 0, removed);

  console.log("鼠标放开--拖拽结束 enrichedRightData.value", enrichedRightData.value);
  // console.log("鼠标放开--拖拽结束 data.value", data.value);

  // 重置拖动状态
  dragTarget = null;
  targetOption = null;
  dragIndex = -1;
  clearMovingDOM();
};

const enrichedRightData = computed(() => {
  return rightData.value.map((key) => {
    const item = data.value.find((d) => d.key === key); // 找到原始对象
    if (!item) return { key, label: "", index: -1 }; // 防御性处理
    const indexInData = data.value.findIndex((d) => d.key === key);
    var r = {
      key: item.key,
      label: item.label,
      index: indexInData,
    };
    console.log("enrichedRightData 执行结果", r);
    return r;
  });
});

// 清除moving Class名
const clearMovingDOM = () => {
  document.querySelectorAll(".movingBottom").forEach((Element) => {
    Element.className = "transferLable";
  });
  document.querySelectorAll(".movingTop").forEach((Element) => {
    Element.className = "transferLable";
  });
};
</script>

<style scoped>
.container {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
}

::v-deep(.el-transfer-panel__item).el-checkbox {
  margin-right: 10px;

  .transferLable {
    display: flex;
    justify-content: space-between !important;
  }
}

.el-transfer ::v-deep(.el-transfer-panel):first-child .sort {
  display: none;
}

.moving {
  border-bottom: 1px solid #409eff;
}

.movingTop {
  border-top: 1px solid #409eff;
}

.movingBottom {
  border-bottom: 1px solid #409eff;
}
</style>

  

posted @ 2025-05-18 08:26  CrossPython  阅读(48)  评论(0)    收藏  举报