vuedraggable Sortable.js 实现拖拽排序功能VUE3

一、页面效果:通过拖拽☰完成拖拽并立即更新顺序  

二、方法:

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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值