【vue2拖拽】使用sortablejs实现拖拽效果

使用sortablejs实现拖拽效果,附带新增,修改,删除


技术:
vue2.X
ant design vue 1.7.8UI框架
sortablejs
less

效果图:

在这里插入图片描述
动态效果图:

vue拖拽

1.安装插件

npm install [email protected]
//或通过yarn安装
yarn add [email protected]

2.代码

template代码块

 <div class="page" onselectstart="return false">
        <div class="addClass">
            <div>编辑标签</div>
        </div>
        <div class="body">
            <div class="bodyRight">
                <div class="bodyRightTitle">
                    <div><span>标签池</span></div>
                </div>
                <div class="bodyRightLabel" ref="addEditTabsRef">
                    <a-spin :spinning="isLoading">
                        <div>
                            <a-tag v-for="tabItem in tagsList" :key="tabItem.id" @close="deleteTag(tabItem)"
                                style="margin-bottom: 5px" @dblclick="handleDblClick(tabItem)" class="tabs">
                                <span class="tagname" v-if="!tabItem.isEditName">
                                    <div> {
   
   { tabItem.tagName }}</div>
                                    <div title="删除"><a-icon type="close" @click.stop="removeLabel(tabItem)" /></div>
                                </span>
                                <a-input v-else class="noFilter" :ref="'input_' + tabItem.id" v-model="tabItem.tagName"
                                    :maxLength="22" @click="inputFocus(tabItem.id)" @pressEnter="sumbitLabel(tabItem)"
                                    placeholder="请输入标签名称" @blur="sumbitLabel(tabItem)"></a-input>
                            </a-tag>
                            <a-tag class="addTabs noFilter" @click="addTabs()" v-if="isAddShow && isEditDataId === null">
                                <a-icon type="plus" />
                            </a-tag>
                        </div>
                    </a-spin>
                </div>
            </div>
        </div>
    </div>

script代码块

import Sortable from 'sortablejs'
export default {
    
    
    name: 'LabelManage',
    data() {
    
    
        return {
    
    
            isAddShow: true,
            tagsList: [],
            isLoading: false,
            isEditDataId: null,
        }
    },
    created() {
    
    
        //初始化调用查询接口
        this._mallQueryTagByGroupCodeApi()
    },
    methods: {
    
    
        //拖拽 此方法是重点
        rowDrop() {
    
    
            const tbody = this.$refs.addEditTabsRef.querySelectorAll('.ant-spin-nested-loading .ant-spin-container div') // 元素选择器名称根据实际内容替换
            const _this = this
            Sortable.create(tbody[0], {
    
    
                sort: true,
                handle: '.tabs.ant-tag', //指定哪个类名的可以拖动
                animation: 300,  // ms, number 单位:ms,定义排序动画的时间
                filter: '.noFilter',  // 过滤器,不需要进行拖动的元素
                preventOnFilter: true, //  在触发过滤器`filter`的时候调用`event.preventDefault()`
                chosenClass: 'dropTabs',
                group: {
    
     name: 'name', pull: false, put: false },
                // 开始拖拽的时候
                onMove: function (/**Event*/evt, originalEvent) {
    
    
                    //如果拖拽到新增后面,则禁止拖拽
                    if (evt.related.getAttribute('class') === 'addTabs ant-tag') {
    
    
                        return false
                    }
                },
                onEnd(evt) {
    
    
                    var navIndex = Object.assign(_this.tagsList)
                    navIndex.splice(evt.newIndex, 0, navIndex.splice(evt.oldIndex, 1)[0])
                    _this.tagsList = navIndex
                    for (const index in _this.tagsList) {
    
    
                        _this.tagsList[index].tagSort = parseInt(index) + 1
                    }


                    for (const index in _this.tagsList) {
    
    
                        _this.tagsList[index].tagSort = parseInt(index) + 1
                    }
                    _this.isLoading = true
                    const data = []
                    _this.tagsList.forEach((item) => {
    
    
                        data.push({
    
    
                            id: item.id,
                            tagSort: item.tagSort,
                        })
                    })
                    //此处调调换顺序的接口
                    _this.$forceUpdate()
                    _this.$message.success('更换顺序成功!')
                    _this.isLoading = false
                },
            })
        },
        //新增标签
        addTabs() {
    
    
            if (this.isEditDataId !== null) {
    
    
                this.$message.warning('有标签未保存成功!')
                this.inputFocus(this.isEditDataId)
                return
            }
            const index = parseInt(this.tagsList.length + 1)

            const value = 99999
            const data = {
    
    
                id: value,
                tagName: '',
                //是否编辑
                isEditName: true,
                //排序
                tagSort: index,
            }
            this.tagsList.splice(index, 0, data)
            this.isEditDataId = value
            this.inputFocus(value)
        },
        //删除标签
        removeLabel(e) {
    
    
            const _that = this
            this.$confirm({
    
    
                title: '请确认是否删除“' + e.tagName + '”标签,该删除标签后,已关联该标签的模型失去该标签,请确认是否删除?”',
                //自定义按钮内容
                okText: '是',
                centered: true,
                cancelText: '否',
                okType: 'danger',
                onOk() {
    
    
                    _that.isLoading = true
                    //此处调删除接口
                    _that.$message.success('删除成功')
                    _that.isLoading = false
                },
                onCancel() {
    
     },
            })
        },
        //标签修改后提交
        sumbitLabel(item) {
    
    
            //如果是新增的没有值则删除该新增行
            if (item.id === 99999 && (item.tagName ?? '') === '') {
    
    
                this.tagsList = this.tagsList.filter((e) => e.id !== 99999)
                this.isEditDataId = null
                return
            }
            if ((item.tagName ?? '') === '') {
    
    
                this.$message.warning('请输入标签名称!')
                this.inputFocus(item.id)
                return
            }
            this.isLoading = true
            if (item.id === 99999) {
    
    
                //此处调新增接口
                this.isEditDataId = null
                item.isEditName = false
                this.$message.success('新增标签成功!')
                this.isLoading = false
            } else {
    
    
                //此处调修改接口
                item.isEditName = false
                this.isEditDataId = null
                this.$message.success('标签名称修改成功!')
                this.isLoading = false
            }
        },
        //触发双击事件
        handleDblClick(item) {
    
    
            if (this.isEditDataId !== null) {
    
    
                this.inputFocus(this.isEditDataId)
                return
            }
            item.isEditName = true
            this.$set(this.tagsList, this.tagsList.indexOf(item), item)
            this.isEditDataId = item.id
            this.inputFocus(item.id)
        },
        //焦点
        inputFocus(id) {
    
    
            this.$nextTick(() => {
    
    
                this.$refs['input_' + id][0].focus()
            })
        },
        //删除标签
        deleteTag(item) {
    
    
            console.log(item)
        },
        //查询标签
        _mallQueryTagByGroupCodeApi() {
    
    
            //拖拽 此处是重点
            this.$nextTick(() => {
    
    
                this.rowDrop()
            })
            for (let index = 0; index < 50; index++) {
    
    
                this.tagsList.push({
    
    
                    "id": index,
                    "tagName": parseInt(index) + 1 + "级钢铁",
                    "tagSort": parseInt(index) + 1
                })
            }
        },
    },
}

style代码块


<style lang="less" scoped>
.page {
    
    
    display: flex;
    flex-direction: column;

    .addClass {
    
    
        width: 100%;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0px 20px 10px 20px;

        div {
    
    
            font-weight: bold;
        }

        border-bottom: 1px solid #d9d9d9;
    }

    span {
    
    
        font-size: 14px;
    }


    .body {
    
    
        height: calc(100vh - 210px);
        width: 100%;
        padding: 34px 24px;
        display: flex;
        justify-content: space-around;


        .bodyRight {
    
    
            width: 80%;
            height: 100%;

            .bodyRightTitle {
    
    
                height: 40px;
                background: #f7f8fa;
                font-size: 14px;
                padding-left: 14px;
                border-radius: 4px 4px 0 0;
                display: flex;
                align-items: center;
                border-top: 1px solid #d9d9d9;
                border-left: 1px solid #d9d9d9;
                border-right: 1px solid #d9d9d9;
            }

            .bodyRightLabel {
    
    
                width: 100%;
                height: 90%;
                overflow-y: auto;
                border-bottom: 1px solid #d9d9d9;
                border-left: 1px solid #d9d9d9;
                border-right: 1px solid #d9d9d9;
                border-radius: 0 0 4px 4px;

                display: flex;
                flex-wrap: wrap;
                justify-content: flex-start;
                align-content: flex-start;

                /deep/ .ant-spin-container {
    
    
                    div:nth-child(1) {
    
    
                        display: flex;
                        flex-direction: row;
                        flex-wrap: wrap;
                    }

                    display: flex;
                    flex-direction: row;
                }

                /deep/ .ant-tag {
    
    
                    background: none;
                }

                .addTabs {
    
    
                    margin: 10px 16px 0 10px;
                    width: 30px;
                    height: 28px;
                    border: 1px solid #1d953f;
                    border-radius: 34px;
                    font-size: 18px;
                    align-items: center;
                    display: flex;
                    justify-content: center;
                    line-height: 22px;
                    cursor: pointer;
                    color: #1d953f;
                }

                .dropTabs {
    
    
                    border: 1px solid #1890ff !important;
                    color: #1890ff;
                }


                .tabs {
    
    
                    margin: 10px 16px 0 10px;
                    width: 100px;
                    height: 28px;
                    border: 1px solid #e5e6eb;
                    border-radius: 34px;
                    font-size: 14px;
                    align-items: center;
                    display: flex;
                    line-height: 22px;
                    cursor: move;

                    .tagname {
    
    
                        width: 100%;
                        display: flex;
                        justify-content: space-between;
                    }

                    i {
    
    
                        display: none;
                    }

                    &:hover {
    
    
                        i {
    
    
                            font-size: 16px;
                            color: red;
                            display: inline-block;
                        }
                    }
                }

                .chosen {
    
    
                    .tabs {
    
    
                        border: 1px solid #1890ff;
                        background-color: #fff !important;
                        color: #1890ff;
                        cursor: move;
                    }
                }
            }
        }
    }
}
</style>
  

3.总结:

1.sortablejs中文官网:http://www.sortablejs.com/options.html
2.需要在$nextTick里调用拖拽的方法

猜你喜欢

转载自blog.csdn.net/weixin_43861689/article/details/131225023