orgManager/src/components/TextDragSort/index.vue

147 lines
4.6 KiB
Vue
Raw Normal View History

2025-08-14 10:38:42 +08:00
<template>
<div class="">
<div>
<el-icon class="sort-icon" color="#409eff" size="15">
<Rank />
</el-icon>
<el-link type="primary" :underline="false" :disabled="row.sortNo === 1"
@click="moveUp">{{ t('上移') }}</el-link>
<el-link type="primary" :underline="false" :disabled="row.sortNo === 1"
@click="moveDown">{{ t('下移') }}</el-link>
</div>
<div><el-link type="primary" :underline="false" :disabled="row.sortNo === 1"
@click="moveTop">{{ t('置顶') }}</el-link></div>
</div>
</template>
<script setup>
import Sortablejs from 'sortablejs';
const props = defineProps({
id: {
type: String,
default: ''
},
row: {
type: Object,
default: () => ({})
},
tableList: {
type: Array,
default: () => []
}
});
const emits = defineEmits(['update:tableList', 'dragEnd'])
const initDrag = () => {
// 首先,使用 document.querySelector 方法选取页面上指定的元素。这里的选择器 '.el-table__body-wrapper table tbody' 定位到一个表格的 tbody 部分
let el = document.querySelector('.el-table__body-wrapper table tbody');
const tbody = document.querySelector('.el-table__body-wrapper table tbody')
// 同一个页面有多个表格需要排序的话,需要给每个表格添加一个唯一的 id然后在 Sortablejs.create 方法中传入该 id这样就可以保证每个表格的拖拽排序互不干扰
if (props.id) {
el = document.querySelector(`#${props.id} tbody`);
}
// 创建了一个 Sortable.js 实例,将 el 作为容器,用于拖拽排序
Sortablejs.create(tbody, {
animation: 150,
handle: '.sort-icon',
draggable: '.parent-row',
onStart(evt) {
const parentRow = evt.item; // 被拖动的父行
const children = [];
// 收集所有紧跟的子行
let next = parentRow.nextElementSibling;
while (next && !next.classList.contains('parent-row')) {
children.push(next);
next = next.nextElementSibling;
}
// 用一个 DocumentFragment 临时保存子行
const fragment = document.createDocumentFragment();
children.forEach(child => fragment.appendChild(child));
parentRow._movedChildren = fragment;
},
onEnd(evt) {
const parentRow = evt.item;
const movedChildren = parentRow._movedChildren;
if (movedChildren) {
// 插回子行
let insertAfter = parentRow.nextElementSibling;
while (insertAfter && !insertAfter.classList.contains('parent-row')) {
insertAfter = insertAfter.nextElementSibling;
}
while (movedChildren.firstChild) {
tbody.insertBefore(movedChildren.firstChild, insertAfter);
}
parentRow._movedChildren = null;
}
// === 关键修复:重新按父行计算索引 ===
const parentRowsDom = Array.from(tbody.querySelectorAll('.parent-row'));
const oldParentIndex = evt.oldIndex;
const newParentIndex = parentRowsDom.indexOf(parentRow); // 用真实DOM顺序算新索引
// 找父行在 props.tableList 里的真实索引
const parentRows = props.tableList
.map((row, idx) => ({ row, idx }))
.filter(item => item.row.merchName);
const oldIndex = parentRows[oldParentIndex].idx;
const newIndex = parentRows[newParentIndex].idx;
// === 移动父子数据一起 ===
// 找出被拖动父行和所有子行
let groupEnd = oldIndex + 1;
for (let i = oldIndex + 1; i < props.tableList.length; i++) {
if (props.tableList[i].merchName) break;
groupEnd++;
}
const movedGroup = props.tableList.splice(oldIndex, groupEnd - oldIndex);
// 插入到新位置
let insertIndex = newIndex;
if (newIndex > oldIndex) insertIndex = newIndex - (groupEnd - oldIndex) + 1;
props.tableList.splice(insertIndex, 0, ...movedGroup);
emits('dragEnd', { merchName: movedGroup[0].merchName, sort: insertIndex + 1 });
}
});
}
const moveUp = () => {
if (props.row.sort <= 1) return;
emits('dragEnd', { merchName: props.row.merchName, direction: 'up', sort: props.row.sort - 1 });
}
const moveDown = () => {
if (props.row.sort >= props.tableList.length) return;
emits('dragEnd', { merchName: props.row.merchName, direction: 'down', sort: props.row.sort + 1 });
}
const moveTop = () => {
if (props.row.sort <= 1) return;
emits('dragEnd', { merchName: props.row.merchName, sort: 1, isTop: true });
}
onMounted(() => {
nextTick(() => {
initDrag()
})
})
</script>
<style scoped lang="scss">
.sort {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
.sort-icon {
cursor: pointer;
}
</style>