element table 实现鼠标拖拽选中

父组件

<template>
    <div id='add'>
        <airtable
            :tableData="tableData"
            :columData="columData"
            :menuList ="menuList"
            :pageTotal="pageTotal"
            @rowContextmenu="rowContextmenu"
            @currentPageChange="currentPageChange"
            @handleSelect="handleSelect"
        >
            <template slot=date slot-scope="scope">
                <!--把所有的数据都变成插槽导出-->
                <div class="air-table-slot__div">{{scope.row.date}}</div>
            </template>
            <template slot=name slot-scope="scope">
                <!--把所有的数据都变成插槽导出-->
                <div class="air-table-slot__div">{{scope.row.name}}</div>
            </template>
            <template slot=address slot-scope="scope">
                <!--把所有的数据都变成插槽导出-->
                <div class="air-table-slot__div">{{scope.row.address}}</div>
            </template>
        </airtable>
    </div>
</template>
<script>
import airtable from "@/components/newCommon/table/table.vue";
export default {
    components: {
        airtable
    },
    name: 'text-form',
    data() {
        return {
            columData:[
                {
                    name: '时间',
                    key: 'date',
                    isFixed: true,//固定列参数
                    "auto"
                },
                {
                    name: '名字',
                    key: 'name',
                    isFixed: true,//固定列参数
                    "auto"
                },
                {
                    name: '地址',
                    key: 'address',
                    isFixed: true,//固定列参数
                    "auto"
                },
            ],
            //table 表头字段数据
            tableData: [
                
            ],
            //这个是右键的按钮操作
            menuList:[{icon:"air-icon-moreapps",name:"操作"},{icon:"air-icon-moreapps",name:"曾加"},{icon:"air-icon-moreapps",name:"修改"}],
            //从后台获得的分页的总数
            pageTotal:100
        }
    },
    methods: {
        //鼠标右键菜单点击事件 传出来的是要做的操作和当前的列
        rowContextmenu(item,row){
            console.log(item,row);
        },
        //监听当前页的变化
        currentPageChange(currentpage){
            console.log(currentpage);
        },
        //当鼠标批量编辑时或者点击鼠标选中时触发 传来的是选中的列表数据
        handleSelect(multipleSelection){
            console.log(multipleSelection)
        }
    },
    watch: {
        
    },
    created(){
        for(var i=0;i<20;i++){
            this.tableData.push({
                date: '2016-05-03',
                name: '王小虎',
                address: '上海市普陀区金沙江路 1518 弄dddddddddddddddddddddddddddd',
                id:i
            })
        }
    }
}

</script>

<style>
#add {
    height:100%;
}
.el-carousel__item h3 {
    color: #475669;
    font-size: 18px;
    opacity: 0.75;
    line-height: 300px;
    margin: 0;
}

.el-carousel__item:nth-child(2n) {
    background-color: #99a9bf;
}

.el-carousel__item:nth-child(2n + 1) {
    background-color: #d3dce6;
}
.mengban {
    position: fixed;
    border: 1px solid rgb(0, 119, 255);
    background-color: rgba(0, 119, 255, 0.3);
    -moz-user-select: none;
    -khtml-user-select: none;
    user-select: none;
}
tbody{
    position: relative;
}
.air-table-slot__div {
    min- 200px;
    max- 300px;
    overflow: hidden;/*超出部分隐藏*/
    white-space: nowrap;/*不换行*/
    text-overflow:ellipsis;/*超出部分文字以...显示*/
}
</style>

  子组件table

<template>
    <div class='air-table-wrapper'>
        <el-table 
            ref="table"
            :show-header="true"
            :data="tableData"
            tooltip-effect="dark"
            style=" 100%;"
            :header-row-class-name="headerClassName"
            :height="'500'"
            id="table" 
            @row-click="rowClick"
            @row-contextmenu="rowContextmenu"
            v-drag>
            <el-table-column  :type="isType" width="55" align="center"></el-table-column>
            <!--列表的表头循环-->
            <el-table-column
                v-for="(column, index) in columData"
                :key="index"
                :label="column.name"
                :width="column.width">
                <template slot-scope="scope">
                    <!--把所有的数据都变成插槽导出-->
                    <slot :name="column.key" :row="scope.row"> </slot>
                </template>
            </el-table-column>
            <template slot=append>
                <div :class="['el-table__append-page']">
                    <el-pagination
                        @size-change="handleSizeChange"
                        @current-change="handleCurrentChange"
                        :current-page.sync="currentPage"
                        :page-size="20"
                        layout="total, prev, pager, next"
                        :total="pageTotal">
                    </el-pagination>
                </div>
            </template>
        </el-table>
        <air-contex-menu
            :position="position"
            :toggleShow="toggleShow"
            :menuList="menuList"
            @menuClick="menuClick"
        ></air-contex-menu>
    </div>
</template>
<script>
import airContexMenu from './contextMenu.js';
export default {
    name: 'airtable',    
    // 框选的指令
    directives: {
        drag: {
            // 指令的定义
            inserted: function (el, binding, vnode) {
                var oDiv = el;
                //监听用户鼠标事件
                oDiv.onmousedown = function (ev) {
                    //初始化不显示
                    vnode.context.toggleShow=false;   
                    //确保用户在移动鼠标的时候只初始化一次选中
                    var flag = true;
                    //用来存储列表
                    var selList = [];
                    //获得指令下的dom对应的表格
                    var fileNodes = oDiv.getElementsByTagName("tr");     
                    var countI = 0;
                    //获得鼠标
                    var evt = window.event || arguments[0];
                    var startX = (evt.x || evt.clientX);
                    var startY = (evt.y || evt.clientY);
                    var top,left;
                    //时时获得
                    top=getY(oDiv);
                    left=getX(oDiv);
                    var selDiv = document.createElement("div");
                    selDiv.style.cssText = "position:absolute;0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px dashed #0099FF;background-color:#C3D5ED;z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
                    selDiv.id = "selectDiv";
                    document.getElementsByClassName("el-table__body")[0].appendChild(selDiv);
                    selDiv.style.left = startX + "px";
                    selDiv.style.top = startY + "px";
                    var _x = null;
                    var _y = null;
                    vnode.context.clearEventBubble(evt);
                    // //打开开关
                    vnode.context.mouseflag = true;
                    // //鼠标拖动时画框
                    document.onmousemove = function (ev) {
                        evt = window.event || arguments[0];
                        _x = (evt.x || evt.clientX);
                        _y = (evt.y || evt.clientY);
                        //为了确定是点击事件还是移动事件
                        if(Math.abs(_x - startX)<5 && Math.abs(_y - startY)<5){
                            return;
                        }
                        //为了确保只有一次的渲染每次框选都把默认选中为空
                        if(flag){
                            //重置选中css
                            for (var i = 0; i < fileNodes.length; i++) {
                                if (fileNodes[i].className.indexOf("el-table__row") != -1) {
                                    fileNodes[i].className = "el-table__row";
                                    selList.push(fileNodes[i]);
                                }
                            }      
                            vnode.context.multipleSelection=[]
                            vnode.context.tableData.forEach((ele)=>{
                                vnode.context.$refs.table.toggleRowSelection(ele,false)
                            })
                            flag = false;
                        }
                        if (vnode.context.mouseflag) {
                            if (selDiv.style.display == "none") {
                                selDiv.style.display = "";
                            }
                            var scrolling=oDiv.getElementsByClassName("is-scrolling-none");
                            selDiv.style.left = Math.min(_x, startX)-left+scrolling[0].scrollLeft + "px";
                            //48是表头的高度
                            selDiv.style.top = Math.min(_y, startY)-top - 48+scrolling[0].scrollTop+ "px";
                            selDiv.style.width = Math.abs(_x - startX) + "px";
                            selDiv.style.height = Math.abs(_y - startY) + "px";

                            // ---------------- 关键算法确定列表的选中靠的是index---------------------
                            var _l = selDiv.offsetLeft, _t = selDiv.offsetTop;
                            var _w = selDiv.offsetWidth, _h = selDiv.offsetHeight;
                            for (var i = 0; i < selList.length; i++) {
                                var sl = selList[i].offsetWidth + selList[i].offsetLeft;
                                var st = selList[i].offsetHeight + selList[i].offsetTop;
                                if (sl > _l && st > _t && selList[i].offsetLeft < _l + _w && selList[i].offsetTop < _t + _h) {
                                    if (selList[i].className.indexOf("seled") == -1) {                                     
                                        selList[i].className = selList[i].className + " seled";
                                        vnode.context.$refs.table.toggleRowSelection(vnode.context.tableData[i],true)
                                        //把选中的都存入到table标签中的已选中
                                        if(vnode.context.tableData[i])
                                            vnode.context.multipleSelection.push(vnode.context.tableData[i]);
                                    }
                                } else {
                                    if (selList[i].className.indexOf("seled") != -1 ) {                                  
                                        selList[i].className = "el-table__row";
                                        vnode.context.$refs.table.toggleRowSelection(vnode.context.tableData[i],false);
                                        vnode.context.multipleSelection.forEach((ele,i)=>{
                                            //这里没用对象是否相等用的是传入table中的唯一的myKey字段
                                            if(ele[vnode.context.myKey]==vnode.context.tableData[i][vnode.context.myKey] ){
                                                vnode.context.multipleSelection.splice(i,1);
                                            }
                                        })
                                    }
                                }
                            }

                        }
                        vnode.context.clearEventBubble(evt);
                    };
                    //方法是确定列表到屏幕的位置
                    function getX(obj){
                        var parObj=obj;
                        var left=obj.offsetLeft;
                        while(parObj=parObj.offsetParent){
                            left+=parObj.offsetLeft;
                        }
                        return left;
                    }
                    //方法是确定列表到屏幕的位置
                    function getY(obj){
                        var parObj=obj;
                        var top=obj.offsetTop;
                        while(parObj = parObj.offsetParent){
                            top+=parObj.offsetTop;
                        }
                        return top;
                    }
                    //在鼠标抬起后做的重置
                    document.onmouseup = function () {
                        //把鼠标移动事初始化
                        document.onmousemove=null;
                        if (selDiv) {
                            document.getElementsByClassName("el-table__body")[0].removeChild(selDiv);
                        }
                        selList = null, _x = null, _y = null, selDiv = null, startX = null, startY = null, evt = null;
                        vnode.context.mouseflag = false;
                        vnode.context.$handleSelect();
                    };
                };
            }
        }
    },
    components: {
        airContexMenu,
    },
    props: {
        //对于列表中唯一的字段myKey默认为id
        myKey: {
            type: String,
            default: "id"
        },
        //列表的数据
        tableData: {
            type: Array,
            default: () => []
        },
        //传过来的表头信息
        columData: {
            type: Array,
            default: () => []
        },
        //有没有checkbox
        isType : {
            type: String,
            default: "selection"
        },
        //右键菜单
        menuList:{
            type: Array,
            default: () => []
        },
        //分页的总页数
        pageTotal:{
            type:Number,
            default: 0
        }
    },
    data() {
        return {
            //指令中确定的时候是鼠标按下事件
            mouseflag: false,
            //选中的数组
            multipleSelection: [],
            //控制右键菜单弹出显示
            dialogVisible:false,
            //右键鼠标的位置
            position:{
                left:0,
                top:0
            },
            //控制右键显示隐藏
            toggleShow :false,
            //分页当前的页数
            currentPage:1 ,
            //当前右键点击的列
            currentRow:[],
            //当前滚动的距离,
            targetScroll:0
        }
    },
    methods: {
        //清除默认事件
        clearEventBubble(evt) {
            if (evt.stopPropagation)
                evt.stopPropagation();
            else
                evt.cancelBubble = true;
            if (evt.preventDefault)
                evt.preventDefault();
            else
                evt.returnValue = false;
        },
        //列表单击选中事件
        rowClick(row, event, column) {
            let flag=true;            
            //确定当前的row的index
            var index = 0;
            this.tableData.forEach((ele,i)=>{
                if(ele[this.myKey] == row[this.myKey]){
                    index = i+1;
                }
            })
            this.toggleShow = false;
            //如果有就移除
            this.multipleSelection.forEach((ele,i)=>{
                if(ele[this.myKey]==row[this.myKey]){
                    this.$refs.table.toggleRowSelection(row,false);
                    //后期优化吧 element的方法用不了 只能自己改变类名
                    this.$refs.table.$el.getElementsByTagName("tr")[index].className = "el-table__row";
                    this.multipleSelection.splice(i,1);
                    flag = false;
                }
            })
            //如果没有就push
            if(flag){
                this.$refs.table.toggleRowSelection(row,true);
                this.multipleSelection.push(row);
                //后期优化吧 element的方法用不了 只能自己改变类名
                this.$refs.table.$el.getElementsByTagName("tr")[index].className = "el-table__row seled";
            }        
        },
        //列表右键点击事件
        rowContextmenu(row, event){
            //为当前的row赋值
            this.currentRow = row;
            //阻止默认右键点击事件
            event.returnValue = false;
            //获取右键坐标
            this.position.left = event.clientX
            this.position.top = event.clientY
            //菜单出现的flag
            this.toggleShow = true;
            //显示弹出窗    
        },
        //右键菜单弹出事件
        menuClick(item){
            //右键点击以后隐藏
            this.toggleShow=false;
            this.$emit("rowContextmenu",item,this.currentRow)
        },
        //每页条数变化 ui定死每页20条
        handleSizeChange(val) {
            // console.log(`每页 ${val} 条`);
        },
        //当前页变化
        handleCurrentChange(val) {
            this.currentPage = val;
            this.$emit('currentPageChange',this.currentPage)
        },
        //当批量选中结束调用
        $handleSelect(){
            this.$emit('handleSelect',this.multipleSelection)
        },
        //监听表格的滚动
        handleScroll(e){
            this.targetScroll = e.target.scrollTop;          
        },
        //
        headerClassName(){
            return "air-table-header__class";
        }
    },
    computed: {
       
    },
    created() {
        
        //确保右键菜单消失
        document.onclick = ()=>{
            this.toggleShow=false;
        }
    },
    mounted() {
        
    }
}

</script>

<style lang="scss">
    @import "../../../public/style/mixin.scss";

    .air-table-wrapper {
        @include wh(100%, 100%);
        .seled {
            background: #f5f5f5 !important;
        }
        .no-seled{
            background: #ffffff !important;
        }
        .el-table__body tr{
            cursor: pointer;
            box-sizing: border-box;
            border-top:1px solid #f5f5f5;
            border-bottom:1px solid #f5f5f5;
        }
        .air-table-header__class th{
            border-bottom:1px solid #f5f5f5 !important;
        }
        .el-table__body td{
            border-bottom:1px solid #f5f5f5;
        }
        .hover-row {
            border-top:1px solid #f5f5f5;
            border-bottom:1px solid #f5f5f5;
            background: #fafafa;
        }
        .el-table__append-page{
            padding-top:48px;
            padding-bottom:48px;
            .el-pagination{
                text-align: right;
            }
        }
        .el-table__append-info{
            position: absolute;
            bottom:0px;
            100%;
            .el-pagination{
                text-align: right;
            }
        }
        .air-table__context--menu{
            box-shadow:0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12);
            .air-table__context--list {
                cursor: pointer;
                height: 48px;
                @include flexCenter(flex-start,center);
                .air-table__context--icon{
                    font-size: 14px;
                    margin-left:20px;
                    color:#757575;
                }
                .air-table__context--info{
                    font-size: 14px;
                    margin-left:20px;
                    color:#212121;
                }
            }
            .air-table__context--list:hover{
                background: #f5f5f5;
            }
        }
        
        
    }
</style>

  子组件中的右键菜单组件

export default {
    name: 'airContexMenu',
    props: {
        position: {
            type: Object,
            default: () => {
                return {
                    top: 0,
                    left: 0
                }
            }
        },
        toggleShow:{
            type:Boolean,
            default: false
        },
        menuList : {
            type: Array,
            default: () => []
        }
    },
    render(h) {
        var self = this;
        var menuInfo = [];
        this.menuList.forEach((ele)=>{
            menuInfo.push(h('li',{
                style: {
                    "100%",
                    display:"flex"
                },
                class: ['air-table__context--list','ripple'],
                on: {   
                        click:function (){
                            self.$emit('menuClick', ele)
                        }
                    }
                },
            [
                h('i', {
                    class: [ele.icon,'air-table__context--icon']
                }),
                h('span',{class:['air-table__context--info']}, ele.name)
            ]))
        })
        return h(
            'div', {
                style: {
                  "300px",
                  padding: "8px 0px",
                  position:"fixed",
                  left: this.position.left + "px",
                  top: this.position.top + "px",
                  display: this.toggleShow ? "block" : "none",
                  zIndex:99999,
                  backgroundColor:"#fff",
                  borderRadius:"4px"
                },
                class: ['air-table__context--menu']
            },
            menuInfo
        );
    },
    computed: {
        
    },
    data() {
        return {
        
        };
    },
    created() {
        
    },
    methods: {
        menuClick(){
            
        }
    }
};

  

原文地址:https://www.cnblogs.com/smallteeth/p/9382529.html