一、页面效果:通过拖拽☰完成拖拽并立即更新顺序 
二、方法:
2-1:引用:
npm install vuedraggable@next
2-2:代码:
<template>
<div class="main" v-loading="loading">
<table class="order_write order_table_top">
<thead></thead>
<tbody>
<tr>
<th>{{ t('common.当前顺序') }}</th>
<td colspan="3">{{ seqArray }}</td>
</tr>
<tr>
<th>{{ t('common.中心管理') }}</th>
<td colspan="3"></td>
</tr>
<tr>
<th>{{ t('common.顺序') }}</th>
<td>
<!-- 拖拽容器 -->
<div ref="sortableContainer" class="sortable-container">
<div v-for="(item, index) in tableList" :key="item.CENTER_SEQ" class="draggable-item">
<span class="drag-handle">☰</span>
<el-input readonly class="iptBox1"
:value="`${item.CENTER_NM}`" />
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="btn-box">
<el-button type="primary" @click="handleSave" size="small">
{{ t('btn.save') }}
</el-button>
<el-button type="primary" plain @click="handleClose">
{{ t('btn.close') }}
</el-button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Sortable from 'sortablejs';
import { useI18n } from 'vue-i18n';
import { toast } from '~/composables/util';
import { closePopup } from '~/composables/popupManager';
import { apiService } from '~/api/setting';
const { t } = useI18n();
const loading = ref(false);
const tableList = ref([]);
const seqArray = ref([]);
const sortableContainer = ref(null);
// 创建广播通道
const channel = new BroadcastChannel('vue-apps-channel');
// 获取列表数据
const getList = async () => {
loading.value = true;
try {
await apiService.getList().then((res) => {
if (res.code === 200) {
tableList.value = JSON.parse(JSON.stringify(res?.data));
if (tableList.value?.length > 0) {
seqArray.value = tableList.value.map((item) => item.CENTER_SEQ);
}
} else {
toast(res.msg || t('common.error'), 'error');
}
});
} finally {
loading.value = false;
}
};
// 初始化 Sortable.js
onMounted(() => {
getList();
// 初始化拖拽功能
new Sortable(sortableContainer.value, {
handle: '.drag-handle', // 指定拖拽手柄
animation: 150, // 动画效果
onEnd: (evt) => {
const { oldIndex, newIndex } = evt;
// 更新 tableList 的顺序
const movedItem = tableList.value.splice(oldIndex, 1)[0];
tableList.value.splice(newIndex, 0, movedItem);
// 更新 seqArray
seqArray.value = tableList.value.map((item) => item.CENTER_SEQ);
console.log('拖拽结束,当前顺序:', seqArray.value);
},
});
});
// 保存排序结果
const handleSave = async () => {
try {
if (!seqArray.value.length) {
toast(t('common.noDataToSave'), 'error');
return;
}
// 更新排序数据
const currentData = {
CENTER_SEQ: seqArray.value,
};
await centerStoreService.sortCenterStore(currentData).then((res) => {
if (res.code === 200) {
toast(t('common.updateSuccess'));
} else {
toast(res.msg, 'error');
}
});
} catch (error) {
toast(t('common.error'), 'error');
} finally {
// 关闭窗口
handleClose();
}
};
// 关闭窗口
const handleClose = () => {
closePopup('centerSort');
channel.postMessage({ type: 'popup', message: false });
};
</script>
<style scoped>
.main {
width: 90%;
padding: 0 8px;
color: #313131;
}
.order_write {
width: 80%;
margin: 20px auto;
background-color: #fff;
width: 100%;
}
.order_write th,
.order_write td {
border: 1px solid #e5e5e5;
padding: 10px;
border-collapse: collapse;
font-size: 12px;
}
.order_write th {
width: 10%;
font-size: 12px;
color: #444;
background-color: #fbfbfbee;
}
.order_write td {
width: 40%;
}
.order_table_top {
border-top: 1px solid #e5e5e5;
}
.sortable-container {
display: flex;
flex-direction: column;
}
.draggable-item {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.drag-handle {
margin-right: 10px;
font-size: 16px;
color: #aaa;
cursor: move;
}
.iptBox1 {
width: 95%;
}
.btn-box {
display: flex;
justify-content: center;
margin-bottom: 2vh;
}
</style>