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>
|