vue + elementui-table + sortablejs 实现表格树形拖拽排序(同级和非同级两版)+添加行

2,760 阅读1分钟

仅支持同级拖拽

<template>
    <div class="draggable" style="padding: 20px">
        <el-table ref="sortableTable" row-key="id" :data="tableData" :key="tableKey" style="width: 100%">
            <el-table-column prop="展开" width="40"></el-table-column>
            <el-table-column prop="添加二级" width="60">
                <template slot-scope="scope">
                    <span style="cursor:pointer;color:#100DB1 " @click="addItem(scope['row'])"
                        v-if="scope['row']['level']===1">{{'+ 二级'}}</span>
                </template>
            </el-table-column>
            <el-table-column prop="拖拽" width="40">
                <template slot-scope="scope">
                    <i class="el-icon-setting draggableIcon"></i>
                </template>
            </el-table-column>
            <el-table-column prop="name">
            </el-table-column>
        </el-table>
    </div>
</template>
<script>
import Sortable from 'sortablejs';

export default {
    mounted() {
        this.rowDrop()
    },
    data() {
        return {
            tableKey: 0,
            activeRows: [],
            tableData: [
                {
                    id: 1,
                    level: 1,
                    name: '李一',
                    gender: '男',
                    age: 30,
                    job: "会计",
                    children: [{
                        id: 31,
                        level: 2,
                        name: '李一1',
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄',
                    }, {
                        id: 32,
                        name: '李一2',
                        level: 2,
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄'
                    }]
                },
                {
                    level: 1,
                    id: 2,
                    name: '王二',
                    gender: '未知',
                    age: 18,
                    job: "无业游民"
                },
                {
                    id: 3,
                    name: '张三',
                    gender: '男',
                    age: 50,
                    job: "老板",
                    level: 1,
                    children: [{
                        id: 321,
                        level: 2,
                        name: '张三1',
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄'
                    }, {
                        id: 322,
                        level: 2,
                        name: '张三2',
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄'
                    }]
                },
            ],
            tableConfig: {
                tableItems: [
                    {
                        label: '姓名',
                        prop: 'name',
                    },
                    {
                        label: '性别',
                        prop: 'gender',
                    },
                    {
                        label: '年龄',
                        prop: 'age',
                    },
                    {
                        label: '工作',
                        prop: 'job',
                    },
                ]
            }
        }
    },
    methods: {
        getUuiD(randomLength) {
            return Number(Math.random().toString().substr(2, randomLength) + Date.now()).toString(36)
        },
        // 添加二级
        addItem(r) {
            let data = {
                id: this.getUuiD(),
                name: '李一11111111',
                gender: '2016-05-01',
                age: '王小虎',
                job: '上海市普陀区金沙江路 1519 弄',
            }
            let idx = this.tableData.findIndex(e => e.id === r.id)
            if (idx > -1) {
                let item = this.tableData[idx]
                if (item.hasOwnProperty('children')) {
                    item.children.push(data)
                } else {
                    item['children'] = [{ ...data }]
                }
            }
            this.tableData = [...this.tableData]
        },
        // 行拖拽
        rowDrop() {
            // 获取表格节点
            // this.tableSortDestroy()
            this.$nextTick(() => {
                const el = this.$refs.sortableTable.$el.querySelector('.el-table__body-wrapper tbody')
                if (!el) { return }
                const _this = this
                // 插件调用函数
                Sortable.create(el, {
                    animation: 300,
                    handle: '.draggableIcon',
                    onMove({ dragged, related }) {
                        // _this.$set(_this, 'tableData', _this.arrayTreeSetLevel(_this.tableData)) // 树形结构数据添加level
                        _this.$set(_this, 'activeRows', _this.treeToTile(_this.tableData)) // 把树形的结构转为列表再进行拖拽
                        const oldRow = _this.activeRows[dragged.rowIndex]
                        const newRow = _this.activeRows[related.rowIndex]
                        if (oldRow.level !== newRow.level && oldRow.id !== newRow.id) { //不能跨级拖拽
                            console.log('oldRow.level !== newRow.level')
                            return false
                        }
                    },
                    onEnd({ oldIndex, newIndex }) {
                        const oldRow = _this.activeRows[oldIndex] // 移动的那个元素
                        const newRow = _this.activeRows[newIndex] // 新的元素
                        console.log('onEnd判断--->', oldIndex, newIndex, oldRow.level, newRow.level, oldRow.id, newRow.id)
                        if (oldIndex !== newIndex && oldRow.level === newRow.level && oldRow.id !== newRow.id) {
                            console.log(oldRow)
                            console.log(newRow)
                            const modelProperty = _this.activeRows[oldIndex]
                            console.log('modelProperty', modelProperty)

                            const changeIndex = newIndex - oldIndex
                            const index = _this.activeRows.indexOf(modelProperty)
                            console.log('index', index)
                            if (index < 0) {
                                console.log('index < 0')
                                return
                            }
                            _this.activeRows.splice(index, 1)
                            _this.activeRows.splice(index + changeIndex, 0, modelProperty)
                            console.log('activeRows', _this.activeRows)
                            _this.sortMenuData()

                        }
                    }
                })
            })
        },
        sortMenuData() {
            let res = []
            this.activeRows.forEach(r => {
                if (r.level === 1) {
                    let itemIdx = this.tableData.findIndex(t => t.id === r.id)
                    if (itemIdx > -1) {
                        res.push({ ...this.tableData[itemIdx] })
                    }
                }
            })
            this.tableData = res
            console.log(this.tableData)
            this.tableKey = Math.random()  //狠狠的刷新dom
            this.rowDrop() // 再把拖拽的功能塞入
        },


        treeToTile(treeData, childKey = 'children') { // 将树数据转化为平铺数据
            const arr = []
            const expanded = data => {
                if (data && data.length > 0) {
                    data.filter(d => d).forEach(e => {
                        arr.push(e)
                        expanded(e[childKey] || [])
                    })
                }
            }
            expanded(treeData)
            console.log('treeToTile', arr)
            return arr
        },
    }
}
</script>
<style scoped lang="scss">
/deep/.el-table__row--level-1 {
    background: #F5F5FC;
}
</style>

支持跨级拖拽

<template>
    <div class="draggable" style="padding: 20px">
        <el-table ref="sortableTable" row-key="id" :data="tableData" :key="tableKey" style="width: 100%">
            <el-table-column prop="展开" width="40"></el-table-column>
            <el-table-column prop="添加二级" width="60">
                <template slot-scope="scope">
                    <span style="cursor:pointer;color:#100DB1 " @click="addItem(scope['row'])"
                        v-if="scope['row']['level']===1">{{'+ 二级'}}</span>
                </template>
            </el-table-column>
            <el-table-column prop="拖拽" width="40">
                <template slot-scope="scope">
                    <i class="el-icon-setting draggableIcon"></i>
                </template>
            </el-table-column>
            <el-table-column prop="name">
            </el-table-column>
        </el-table>
    </div>
</template>
<script>
import Sortable from 'sortablejs';

export default {
    mounted() {
        this.rowDrop()
    },
    data() {
        return {
            tableKey: 0,
            activeRows: [],
            tableData: [
                {
                    id: 1,
                    level: 1,
                    name: '李一',
                    gender: '男',
                    age: 30,
                    job: "会计",
                    children: [{
                        id: 31,
                        level: 2,
                        name: '李一1',
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄',
                    }, {
                        id: 32,
                        name: '李一2',
                        level: 2,
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄'
                    }]
                },
                {
                    level: 1,
                    id: 2,
                    name: '王二',
                    gender: '未知',
                    age: 18,
                    job: "无业游民"
                },
                {
                    id: 3,
                    name: '张三',
                    gender: '男',
                    age: 50,
                    job: "老板",
                    level: 1,
                    children: [{
                        id: 321,
                        level: 2,
                        name: '张三1',
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄'
                    }, {
                        id: 322,
                        level: 2,
                        name: '张三2',
                        gender: '2016-05-01',
                        age: '王小虎',
                        job: '上海市普陀区金沙江路 1519 弄'
                    }]
                },
            ],
            tableConfig: {
                tableItems: [
                    {
                        label: '姓名',
                        prop: 'name',
                    },
                    {
                        label: '性别',
                        prop: 'gender',
                    },
                    {
                        label: '年龄',
                        prop: 'age',
                    },
                    {
                        label: '工作',
                        prop: 'job',
                    },
                ]
            }
        }
    },
    methods: {
        getUuiD(randomLength) {
            return Number(Math.random().toString().substr(2, randomLength) + Date.now()).toString(36)
        },
        // 添加二级
        addItem(r) {
            let data = {
                id: this.getUuiD(),
                name: '李一11111111',
                gender: '2016-05-01',
                age: '王小虎',
                job: '上海市普陀区金沙江路 1519 弄',
            }
            let idx = this.tableData.findIndex(e => e.id === r.id)
            if (idx > -1) {
                let item = this.tableData[idx]
                if (item.hasOwnProperty('children')) {
                    item.children.push(data)
                } else {
                    item['children'] = [{ ...data }]
                }
            }
            this.tableData = [...this.tableData]
        },
        // 行拖拽
        rowDrop() {
            // 获取表格节点
            // this.tableSortDestroy()
            this.$nextTick(() => {
                const el = this.$refs.sortableTable.$el.querySelector('.el-table__body-wrapper tbody')
                if (!el) { return }
                const _this = this
                // 插件调用函数
                Sortable.create(el, {
                    animation: 300,
                    handle: '.draggableIcon',
                    onMove({ dragged, related }) {
                        _this.$set(_this, 'activeRows', _this.treeToTile(_this.tableData)) // 把树形的结构转为列表再进行拖拽
                    },
                    onEnd({ oldIndex, newIndex }) {
                        const oldRow = _this.activeRows[oldIndex] // 移动的那个元素
                        const newRow = _this.activeRows[newIndex] // 新的元素

                        if (oldIndex !== newIndex && oldRow.id !== newRow.id) {
                            const modelProperty = _this.activeRows[oldIndex]

                            const changeIndex = newIndex - oldIndex
                            const index = _this.activeRows.indexOf(modelProperty)
                            if (oldRow.level === newRow.level && oldRow.level === 1) {
                                // 一级之间的拖拽
                                if (index < 0) {
                                    console.log('index < 0')
                                    return
                                }
                                _this.activeRows.splice(index, 1)
                                _this.activeRows.splice(index + changeIndex, 0, modelProperty)
                                _this.sortMenuData('1-1')
                            } else if (oldRow.level === newRow.level && oldRow.level === 2) {
                                // 二级之间的拖拽
                                // _this.activeRows.splice(index, 1)
                                let res = [];
                                _this.tableData.forEach(t => {
                                    if (t.hasOwnProperty('children')) {
                                        // 在原来的一级下删除二级
                                        let oldChildIndex = t.children.findIndex(c => c.id === oldRow.id)
                                        if (oldChildIndex > -1) {
                                            t.children.splice(oldChildIndex, 1)
                                        }
                                        // 在新的一级下添加二级
                                        let newChildIndex = t.children.findIndex(c => c.id === newRow.id)
                                        if (newChildIndex > -1) {
                                            t.children = [...t.children.slice(0, newChildIndex), oldRow, ...t.children.slice(newChildIndex)]
                                        }
                                    }
                                    res.push(t)
                                    _this.tableData = res
                                })
                                _this.tableKey = Math.random()
                                _this.rowDrop()
                            } else if (oldRow.level === 2 && newRow.level === 1) {
                                // 二级移动到一级
                                // console.log('2-1', oldRow, newRow)
                                let res = [];
                                _this.tableData.forEach(t => {
                                    if (t.hasOwnProperty('children')) {
                                        // 在原来的一级下删除二级
                                        let oldChildIndex = t.children.findIndex(c => c.id === oldRow.id)
                                        if (oldChildIndex > -1) {
                                            t.children.splice(oldChildIndex, 1)
                                        }
                                    }
                                    // 将提升为一级的二级放到相应位置
                                    let item = { ...oldRow, level: 1 }
                                    if (t.id === newRow.id) {
                                        res.push(t, item)
                                    } else {
                                        res.push(t)
                                    }
                                    _this.tableData = res
                                })
                                _this.tableKey = Math.random()
                                _this.rowDrop()
                            } else if (oldRow.level === 1 && newRow.level === 2) {
                                // 一级移动到二级
                                console.log('1-2', oldRow, newRow)
                                let res = [];

                                let item = [{ ...oldRow, level: 2 }]
                                if (oldRow.hasOwnProperty('children')) {
                                    item = [...item, ...oldRow.children]
                                    delete item[0].children
                                }
                                _this.tableData.forEach(t => {
                                    // 将降为二级的目标以及他的children放到相应的位置
                                    if (t.hasOwnProperty('children')) {
                                        let newChildIndex = t.children.findIndex(c => c.id === newRow.id)
                                        if (newChildIndex > -1) {
                                            t.children = [...t.children.slice(0, newChildIndex), ...item, ...t.children.slice(newChildIndex)]
                                        }
                                    }
                                    if (t.id !== oldRow.id) {
                                        res.push(t)
                                    }
                                })
                                _this.tableData = res
                                _this.tableKey = Math.random()
                                _this.rowDrop()
                            }
                        }
                    }
                })
            })
        },
        sortMenuData(type) {
            if (type === '1-1') {
                let res = []
                this.activeRows.forEach(r => {
                    if (r.level === 1) {
                        let itemIdx = this.tableData.findIndex(t => t.id === r.id)
                        if (itemIdx > -1) {
                            res.push({ ...this.tableData[itemIdx] })
                        }
                    }
                })
                this.tableData = res
            }
            this.tableKey = Math.random()  //狠狠的刷新dom
            this.rowDrop() // 再把拖拽的功能塞入
        },


        treeToTile(treeData, childKey = 'children') { // 将树数据转化为平铺数据
            const arr = []
            const expanded = data => {
                if (data && data.length > 0) {
                    data.filter(d => d).forEach(e => {
                        arr.push(e)
                        expanded(e[childKey] || [])
                    })
                }
            }
            expanded(treeData)
            // console.log('treeToTile', arr)
            return arr
        },
    }
}
</script>
<style scoped lang="scss">
/deep/.el-table__row--level-1 {
    background: #F5F5FC;
}
</style>