Element UI 表格拖动到左侧树


前言

研究表格拖动到树的目的:

随着互联网的不断发展,网页的便捷性操作也越来越重要,方便快捷的操作方式越来越受人青睐,本文就介绍了表格拖动到树的操作逻辑,方便用户修改表格数据的所属。


前期花费了大量时间去使用各种VUE拖动插件(vuedraggable、sortablejs之类),但都无法满足我的需求,最后还是原生JS比较靠谱,才有了今天这篇文章。

一、HTML5拖放事件

在拖放的过程中会触发以下事件:

  • 在拖动目标上触发事件 (源元素):
    • ondragstart - 用户开始拖动元素时触发
    • ondrag - 元素正在拖动时触发
    • ondragend - 用户完成元素拖动后触发
  • 释放目标时触发的事件:
    • ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件
    • ondragover - 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
    • ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
    • ondrop - 在一个拖动过程中,释放鼠标键时触发此事件

二、简单拖动示例

1.复制代码

代码如下(示例):

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>拖动示例</title>
<style>
.droptarget {
    float: left; 
    width: 100px; 
    height: 35px;
    margin: 15px;
    padding: 10px;
    border: 1px solid #aaaaaa;
}
</style>
</head>
<body>

<p>在两个矩形框中来回拖动 p 元素:</p>
<div class="droptarget">
	<p draggable="true" id="dragtarget">拖动我!</p>
</div>
<div class="droptarget"></div>
<p style="clear:both;"><strong>注意:</strong>Internet Explorer 8 及更早 IE 版本或 Safari 5.1 及更早版本的浏览器不支持 drag 事件。</p>
<p id="demo"></p>
<script>
/* 拖动时触发*/
document.addEventListener("dragstart", function(event) {
    //dataTransfer.setData()方法设置数据类型和拖动的数据
    event.dataTransfer.setData("Text", event.target.id);
    // 拖动 p 元素时输出一些文本
    document.getElementById("demo").innerHTML = "开始拖动 p 元素.";    
    //修改拖动元素的透明度
    event.target.style.opacity = "0.4";
});
//在拖动p元素的同时,改变输出文本的颜色
document.addEventListener("drag", function(event) {
    document.getElementById("demo").style.color = "red";
});
// 当拖完p元素输出一些文本元素和重置透明度
document.addEventListener("dragend", function(event) {
    document.getElementById("demo").innerHTML = "完成 p 元素的拖动";
    event.target.style.opacity = "1";
});
/* 拖动完成后触发 */
// 当p元素完成拖动进入droptarget,改变div的边框样式
document.addEventListener("dragenter", function(event) {
    if ( event.target.className == "droptarget" ) {
        event.target.style.border = "3px dotted red";
    }
});
// 默认情况下,数据/元素不能在其他元素中被拖放。对于drop我们必须防止元素的默认处理
document.addEventListener("dragover", function(event) {
    event.preventDefault();
});
// 当可拖放的p元素离开droptarget,重置div的边框样式
document.addEventListener("dragleave", function(event) {
    if ( event.target.className == "droptarget" ) {
        event.target.style.border = "";
    }
});
/*对于drop,防止浏览器的默认处理数据(在drop中链接是默认打开)
复位输出文本的颜色和DIV的边框颜色
利用dataTransfer.getData()方法获得拖放数据
拖拖的数据元素id(“drag1”)
拖拽元素附加到drop元素*/
document.addEventListener("drop", function(event) {
    event.preventDefault();
    if ( event.target.className == "droptarget" ) {
        document.getElementById("demo").style.color = "";
        event.target.style.border = "";
        var data = event.dataTransfer.getData("Text");
        event.target.appendChild(document.getElementById(data));
    }
});
</script>

</body>
</html>

2.运行代码

可以轻松的在两个矩形框中来回拖动 p 元素

三、表格拖动到左侧树示例

1.复制代码

代码如下(示例):

<template>
    <div class="app-container">
        <!--用户数据-->
        <div class="main-wrap">
            <div id="queryBox" class="search-wrap">
                <el-form id="queryForm" ref="queryForm" size="mini" :model="queryParams" :inline="true">
                    <div class="query-wrap">
                        <el-form-item label="搜索" prop="search">
                            <el-input v-model="queryParams.search" placeholder="请输入" clearable size="mini"
                                style="width: 140px" @keyup.enter.native="handleQuery" />
                        </el-form-item>
                        <div style="display: inline-block">
                            <el-form-item>
                                <div class="search-btn">
                                    <div v-preventReClick class="btn-wrap2" @click="handleQuery()">
                                        搜索
                                    </div>
                                </div>
                            </el-form-item>
                            <el-form-item>
                                <div class="search-btn">
                                    <div v-preventReClick class="btn-wrap3" @click="handleReset()">
                                        重置
                                    </div>
                                </div>
                            </el-form-item>
                        </div>
                    </div>
                </el-form>
                <div v-show="showOther" class="other-bg" @click="showOther = false" />
                <div class="right-btn">
                    <el-button type="text" icon="el-icon-refresh" @click="resetQuery">刷新</el-button>
                    <svg-icon v-preventReClick class="magnify" icon-class="magnify" @click="zoomControl()" />
                </div>
            </div>
            <div class="content-wrap">
                <div class="tree-wrap">
                    <el-input size="mini" placeholder="输入关键字进行过滤" v-model="filterText" clearable></el-input>
                    <div class="tree-choose" :style="{ height: chooseHeight }">
                        <el-tree ref="tree" :data="treeList" node-key="id" :props="defaultProps" accordion
                            @node-click="labelChoose" :filter-node-method="filterNode">
                            <span class="custom-tree-node" :data-id="data.id" slot-scope="{ node, data }">
                                <span class="label" :class="{ 'choose-item': chooseId == data.id }">{{
                                    node.label }}</span>
                            </span>
                        </el-tree>
                    </div>
                </div>
                <div class="content-right">
                    <el-table v-loading="loading" border size="mini" :height="height" row-key="id" id="dragTable"
                        :data="dataList" row-class-name="drag-handle">
                        <el-table-column label="用户编号" align="center" prop="id" width="100" />
                        <el-table-column label="用户名称" align="center" prop="username" :show-overflow-tooltip="true" />
                        <el-table-column label="用户账号" align="center" prop="account" :show-overflow-tooltip="true" />
                        <el-table-column label="分组" align="center" prop="group.name" :show-overflow-tooltip="true" />
                        <el-table-column label="角色" align="center" prop="role.name" :show-overflow-tooltip="true" />
                        <el-table-column label="手机号码" align="center" prop="phone" />
                        <div v-if="!loading" slot="empty" class="empty">
                            <img :src="emptyImg">
                        </div>
                    </el-table>
                    <!-- </draggable> -->

                    <element-Pagination v-show="total > 0" :total="total" :page.sync="queryParams.page"
                        :limit.sync="queryParams.perPage" @pagination="getList()" />
                </div>
            </div>
        </div>

    </div>
</template>

<script>
import emptyImg from '@/assets/image/noInfo.png';

export default {
    name: 'UserList',
    // components: { draggable },
    data() {
        return {
            // 表格动态高度
            height: undefined,
            // 表格数据为空时默认图片
            emptyImg: emptyImg,
            // 遮罩层
            loading: true,
            // 总条数
            total: 0,
            // 用户表格数据
            dataList: null,
            // 是否显示弹出层
            open: false,
            chooseHeight: '',
            // 表单参数
            form: {},
            // 查询参数
            queryParams: {
                page: 1,
                perPage: 12,
                search: undefined,
                status: undefined,
                group_id: undefined,
                role_id: undefined
            },
            showOther: false,
            defaultProps: {
                children: 'children',
                label: 'label'
            },
            filterText: undefined,
            chooseId: undefined,
            treeList: [{
                label: '一级 1',
                id: 1,
                children: [{
                    label: '二级 1-1',
                    id: 11,
                    children: [{
                        id: 111,
                        label: '三级 1-1-1'
                    }]
                }]
            }, {
                label: '一级 2',
                id: 2,
                children: [{
                    id: 21,
                    label: '二级 2-1',
                    children: [{
                        id: 211,
                        label: '三级 2-1-1'
                    }]
                }, {
                    label: '二级 2-2',
                    id: 22,
                    children: [{
                        id: 221,
                        label: '三级 2-2-1'
                    }]
                }]
            }, {
                label: '一级 3',
                id: 3,
                children: [{
                    id: 31,
                    label: '二级 3-1',
                    children: [{
                        id: 311,
                        label: '三级 3-1-1'
                    }]
                }, {
                    id: 32,
                    label: '二级 3-2',
                    children: [{
                        id: 321,
                        label: '三级 3-2-1'
                    }]
                }]
            }]
        };
    },
    watch: {
        filterText(val) {
            this.$refs.tree.filter(val);
        }
    },
    created() {
        const name = this.$route.name + 'PageSize';
        const pageSize = localStorage.getItem(name);
        if (pageSize) {
            this.queryParams.perPage = pageSize * 1;
        }

        this.getList();
    },
    mounted() {
        const height = document.getElementById('queryBox').clientHeight;
        const val = 40 + height * 1;
        this.height = 'calc((100vh - 1rem) - ' + val + 'px)';
        this.chooseHeight = 'calc((100vh - 1.2rem) - ' + val + 'px)';
    },
    methods: {
        // 缩放
        zoomControl() {
            // this.$store.dispatch('app/toggleSideBar')
            this.$store.dispatch('app/toggleHideleft');
        },
        // 树节点筛选
        filterNode(value, data) {
            if (!value) return true;
            return data.label.indexOf(value) !== -1;
        },
        labelChoose(row) {
            this.chooseId = row.id;
            this.getList();
        },
        // 行拖拽
        openTableDrop() {
            const tbody = document.querySelector('#dragTable tbody');
            if (tbody) {
                const dataList = this.dataList;
                const tr = tbody.getElementsByTagName('tr');
                for (let index = 0; index < tr.length; index++) {
                    tr[index].draggable = true;
                    tr[index].ondragstart = function(e) {
                        console.log(index);
                        const treeNodes = document.getElementsByClassName('tree-choose')[0].getElementsByClassName('el-tree-node__content');
                        for (let index2 = 0; index2 < treeNodes.length; index2++) {
                            treeNodes[index2].draggable = true;
                            treeNodes[index2].ondragstart = function(e) {
                                console.log(e);
                            };
                            treeNodes[index2].ondragover = function(e) {
                                e.preventDefault();
                            };
                            // 拖动结果
                            treeNodes[index2].ondrop = function() {
                                console.log('拖动的表格数据是:');
                                console.log(dataList[index]);
                                const treeId = treeNodes[index2].getElementsByClassName('custom-tree-node')[0].dataset.id;
                                console.log('拖动到的树ID是:' + treeId);
                            };
                        }
                    };
                    tr[index].ondragover = function(e) {
                        e.preventDefault();
                    };
                    tr[index].ondrop = function(e) {
                        console.log(e);
                    };
                }
            }
        },
        /** 查询用户列表 */
        getList() {
            this.loading = true;
            const dataList = [{
                id: 717, created_at: '2023-04-19 10:21:27',
                status: 1,
                statusVal: '正常',
                username: 'admin',
                phone: '13357118896',
                account: 'admin',
                role: {
                    id: 1,
                    name: '超级管理员'
                },
                group: { id: 1, name: '默认分组' }
            }, {
                id: 718, created_at: '2023-04-19 13:21:27',
                status: 1,
                statusVal: '正常',
                username: '张三',
                phone: '13757118896',
                account: 'zhangsan',
                role: {
                    id: 2,
                    name: '管理员'
                },
                group: { id: 2, name: '分组2' }
            }];
            this.dataList = dataList;
            this.total = 2;
            this.loading = false;
            this.$nextTick(() => {
                // 表格可拖拽
                this.openTableDrop();
            });
        },
        /** 搜索按钮操作 */
        handleQuery() {
            this.showOther = false;
            this.queryParams.page = 1;
            this.getList();
        },
        /** 重置按钮操作 */
        resetQuery() {
            this.handleQuery();
        },
        // 重置按钮
        handleReset() {
            this.resetForm('queryForm');
        }
    }
};
</script>
<style lang="scss" scoped>
@import '@/assets/styles/mixin.scss';

.content-wrap {
    display: flex;

    .tree-wrap {
        background: #fff;
        width: 200px;
        padding: 0.1rem;
        border: 0.01rem solid #d3d7e1;
        margin: 0 0.1rem 0.01rem 0.01rem;

        .tree-choose {
            overflow: hidden auto;
            @include scrollBarNone;
            font-size: 13px;

            ::v-deep .is-leaf.el-tree-node__expand-icon {
                display: none;
            }

            .label {
                padding-left: 0.05rem;
            }

            .site-item {
                display: block;
                max-width: calc(1.8rem - 60px);
                overflow: hidden;
                white-space: nowrap;
                text-overflow: ellipsis;
            }

            .choose-item {
                color: rgb(28, 103, 218);
            }
        }
    }

    .content-right {
        width: calc(100% - 200px);
    }
}
</style>

2.运行代码

代码运行后如下,拖动表格行到左侧树后,控制台会打印出拖动表格行的数据以及拖动到树的id.
在这里插入图片描述


总结

以上就是我的表格拖动到左侧树方案,有不同的见解或者疑问,欢迎留言。

<think>好的,我现在需要解决用户提到的Element UI表格中固定列与其他列样式冲突的问题,也就是当拖动滚动条时出现的样式刺穿现象。首先,我得回忆一下Element UI表格的使用和常见问题。 用户的问题是在使用固定列时,滚动条拖动导致样式不一致,比如边框消失、背景色错位等。这可能是因为固定列的样式在滚动时没有正确同步更新,或者层级(z-index)的问题导致其他列被覆盖。 首先,我应该考虑检查固定列的样式是否应用正确。Element UI的固定列通过设置`fixed`属性实现,通常会在表格两侧生成固定的列,这些列在滚动时保持不动。但有时候,浏览器渲染或CSS层级可能导致样式冲突。 接下来,可能的原因之一是z-index设置不当。固定列的z-index可能不够高,导致在滚动时被其他元素覆盖。这时候需要调整固定列的z-index值,确保它们位于其他列的上方。例如,可以尝试在全局样式中增加类似`.el-table__fixed-right { z-index: 3 !important; }`的代码,但需要注意不要影响其他组件。 另一个常见问题是边框消失。当滚动时,固定列和滚动列的边框可能因为动态渲染而断开。这时候可以通过CSS伪元素来强制添加边框。例如,使用::after伪类在固定列的右侧添加边框,确保即使滚动时边框依然可见。例如: ```css .el-table__fixed-right::after { content: ''; position: absolute; right: 0; top: 0; height: 100%; width: 1px; background-color: #EBEEF5; } ``` 此外,表头与表体的对齐问题也可能导致样式错乱。这通常是由于固定列和滚动列的宽度计算不一致引起的。可以尝试在表格数据更新后手动调用`doLayout`方法,强制表格重新计算布局: ```javascript this.$nextTick(() => { this.$refs.table.doLayout(); }); ``` 还有用户提到背景色不一致,可能是因为固定列的单元格样式没有正确继承或覆盖。需要检查固定列单元格的类名,确保应用相同的背景色。例如,使用深色主题时,可能需要覆盖默认的背景色: ```css .el-table__fixed-right .el-table__row { background-color: #2d2d2d !important; } ``` 同时,如果用户使用了表头吸顶功能,可能需要调整吸顶表头的z-index,避免与固定列冲突。例如,设置`.el-table__header-wrapper { z-index: 2 !important; }`。 最后,可能需要考虑浏览器兼容性问题,尤其是在低版本浏览器中CSS属性的支持情况。测试不同浏览器的表现,必要时添加浏览器前缀或使用替代方案。 总结下来,解决方案可能包括调整z-index、使用伪元素添加边框、手动触发布局更新、统一背景色样式等。需要根据具体情况逐一排查并应用相应的修复方法。</think>针对Element UI表格固定列样式冲突问题,以下是分步骤解决方案: ### 一、层级覆盖问题 1. 调整固定列z-index ```css /* 提升右侧固定列层级 */ .el-table__fixed-right { z-index: 3 !important; } /* 提升左侧固定列层级 */ .el-table__fixed-left { z-index: 3 !important; } ``` ### 二、边框消失问题 2. 添加伪元素强制显示边框 ```css /* 右侧固定列添加右边框 */ .el-table__fixed-right::after { content: ""; position: absolute; right: 0; top: 0; width: 1px; height: 100%; background-color: #EBEEF5; z-index: 4; } /* 左侧固定列添加右边框 */ .el-table__fixed-left::after { right: -1px; /* 解决边框重叠问题 */ } ``` ### 三、对齐错位问题 3. 手动触发布局更新 ```javascript // 在数据更新后调用 this.$nextTick(() => { this.$refs.yourTable.doLayout(); }); ``` ### 四、背景色不一致 4. 强制统一背景色 ```css /* 深色模式示例 */ .el-table__fixed-body-wrapper .el-table__row { background: #2d2d2d !important; } /* 表头背景统一 */ .el-table__fixed-header-wrapper .el-table__header th { background: #3a3a3a !important; } ``` ### 五、滚动条样式优化 5. 调整滚动条位置 ```css /* 防止滚动条覆盖固定列 */ .el-table__body-wrapper { overflow-x: auto !important; margin-bottom: 8px !important; /* 给滚动条留出空间 */ } ``` ### 六、表头吸顶冲突 6. 调整表头层级 ```css .el-table__header-wrapper { z-index: 2 !important; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值