147 lines
4.6 KiB
Vue
147 lines
4.6 KiB
Vue
<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> |