欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于vue和elementUI封装框选表格组件

程序员文章站 2022-04-22 11:00:24
...

前言:主要是 table 表格框选功能

实现功能如下:

  1. 表格框选功能
  2. 右键操作功能(删除、查看、编辑)
  3. 分页功能
  4. 表头分类筛选功能
  5. 回显功能(显示默认选中数据)
  6. 排序功能
  7. 行禁用不可选功能

不说废话,直接上代码,简单粗暴。。。

1、组件

/*
* @auhor : 开发部-前端组-李鑫超
* @property { tableData :  {Array} 表格数据  add v-1.0.0 }
* @property { columData :  {Array} 表头数据  add v-1.0.0 }
* @property { menuList :  {Array} 右键菜单  add v-1.0.0 }
* @property { myKey :  {String} 唯一标识。  add v-1.0.0 }
* @property { isRadio :  {Boolean} 是否单选,默认false。  add v-1.0.0 }
* @property { showpage :  {Boolean} 是否显示分页,默认false。  add v-1.0.0 }
* @property { currentPage :  {Number} 当前页,默认1。  add v-1.0.0 }
* @property { pageSize :  {Number} 每页显示条目个数, 默认20。  add v-1.0.0 }
* @property { pageTotal :  {Number} 总条目数。  add v-1.0.0 }
* @property { backMultipleSelection :  {Array} 回显数据。  add v-1.0.0 }
* @property { emptyText :  {String} 空数据时显示的文本内容,默认暂无数据。  add v-1.0.0 }
* @property { rowDisabled :  {String} 行禁用时,根据该字段判断。  add v-1.0.0 }
* @property { pageDataFlag :  {Boolean} 翻页时,是否保存其他页面选中的数据,默认值false。  add v-1.0.0 }
* @property { sortable :  {Boolean,String} 对应列是否可以排序,如果设置为 'custom',则代表用户希望远程排序,需要监听 Table 的 sort-change 事件。  add v-1.0.0 }
* @method  { makesure :  {Function(data)}  表头tree点击事件 add v-1.0.0 }
* @method  { menuClick :  {Function(item)} item 表格右键选项事件 add v-1.0.0 }
* @method  { currentPageChange :  {Function(page)} 当前页改变事件事件 add v-1.0.0 }
* @method  { handleSelect :  {Function(data)} 选中数据时会触发该事件 add v-1.0.0 }
* @method  { sortChange :  {Function(column, prop, order)} 点击表头排序按钮会触发该事件 add v-1.0.0 }
* @version 1.2.0
* @edit: 2018/10/24
*/
<template>
    <div class='air-table-wrapper'>
        <el-table ref="table"
            :show-header="true"
            :data="tableData"
            tooltip-effect="dark"
            style="width: 100%;"
            :height="height"
            :header-row-class-name="headerClassName"
            id="table"
            @row-click="rowClick"
            @selection-change="selectionChange"
            @select-all="selectAll"
            @select="selectItem"
            @row-contextmenu="rowContextmenu"
            @row-dblclick="rowDblclick"
            @header-click="headerClick"
            @empty-text="emptyText"
            @sort-change="sortChange"
            v-drag>
            <!-- checkbox -->
            <el-table-column type="selection"
                width="56"
                align="center"
                :selectable="selectable"></el-table-column>
            <!-- index 序号 -->
            <el-table-column v-if="isType == 'index'"
                :index="indexMethod"
                type="index"
                :label="'序号'"
                width="56"
                align="center"></el-table-column>
            <!--列表的表头循环-->
            <el-table-column v-for="(column, index) in columData"
                :key="index"
                :sortable="sortable"
                :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
                v-if="currentshowPage">
                <div :class="showAdd ? 'el-table__append-pageAdd el-table__append-page': 'el-table__append-page'">
                    <el-pagination @size-change="handleSizeChange"
                        @current-change="handleCurrentChange"
                        :current-page.sync="page"
                        :page-size="pageSize"
                        layout="total, prev, pager, next"
                        :total="pageTotal">
                    </el-pagination>
                </div>
            </template>
            <!-- 添加按钮 -->
            <template slot=append
                v-if="showAdd">
                <airButton buttonType="colorButtonroundOrange"
                    @click="addClick"></airButton>
            </template>
        </el-table>
        <!-- 右键菜单 -->
        <air-contex-menu v-if="menuList.length"
            :position="position"
            :toggleShow="toggleShow"
            :menuList="menuList"
            @menuClick="menuClick">
        </air-contex-menu>
        <!-- 弹出窗 -->
        <tree-dialog ref="treeDialog"
            :pid="treePid"
            :name="treeName"
            :id="treeId"
            :data="treeData"
            @makesure=makesure
            placeholder="输入信息,按回车搜索"
            :title='treeTitle'>
        </tree-dialog>
    </div>
</template>
<script>
import airContexMenu from "./contextMenu.js";
import treeDialog from "@/components/newCommon/dialog/treeDialog.vue";
export default {
    name: "airtable",
    // 框选的指令
    directives: {
        drag: {
            // 指令的定义
            inserted: function (el, binding, vnode) {
                var oDiv = el;
                vnode.context.tableTop = getY(oDiv);
                vnode.context.tableLeft = getX(oDiv);
                //方法是确定列表到屏幕的位置
                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;
                }
                // 禁止右键默认菜单
                oDiv.oncontextmenu = function () {
                    return false;
                }
                oDiv.onmousedown = function (ev) {
                    // 防止奇葩操作
                    if ([2, 3, 4, 5].indexOf(ev.buttons) > -1) {
                        if (selDiv) {
                            oDiv.getElementsByClassName("el-table__body")[0].removeChild(selDiv);
                        }
                        return
                    }
                    // 获取当前scrollTop
                    var scrollingTop = vnode.context.targetScroll;
                    if (vnode.context.isRadio) return;
                    vnode.context.$selectState(true);
                    //初始化不显示
                    vnode.context.toggleShow = false;
                    //确保用户在移动鼠标的时候只初始化一次选中
                    var flag = true;
                    // 获取表格的宽高
                    let tableWidth = vnode.context.$el.clientWidth;
                    let tableHeight = vnode.context.$el.clientHeight;
                    //用来存储列表
                    var selList = [];
                    //获得指令下的dom对应的表格
                    var fileNodes = oDiv.getElementsByTagName("tr");
                    //获得鼠标
                    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;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px solid rgba(255,165,22,0.38);background-color:rgba(0,0,0,0.38);z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
                    selDiv.id = "selectDiv";
                    oDiv.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;
                        }
                        vnode.context.selectItemFlag = false;
                        // 为了确保只有一次的渲染每次框选都把默认选中为空(针对当前页)
                        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]);
                                }
                            }
                            // 判断翻页是否保存选中数据
                            // if (vnode.context.pageDataFlag) {
                                // 判断当前页是否有选中的
                                vnode.context.tableData.forEach((item) => {
                                    vnode.context.$refs.table.toggleRowSelection(item, false);
                                    vnode.context.multipleSelection.forEach((ele, i) => {
                                        if (item[vnode.context.myKey] == ele[vnode.context.myKey]) {
                                            vnode.context.multipleSelection.splice(i, 1);
                                            i = i - 1;
                                        }
                                    })
                                })
                            // } else {
                            //     vnode.context.multipleSelection = []
                            // }

                            flag = false;
                        }
                        // 判断鼠标移动是否超出在当前表格区域
                        if (_x <= left || _x >= left + tableWidth || _y <= top || _y >= top + tableHeight) {
                            document.onmousemove = null;
                            if (selDiv) {
                                oDiv.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();
                        }
                        if (vnode.context.mouseflag) {
                            if (selDiv.style.display == "none") {
                                selDiv.style.display = "";
                            }
                            var scrolling = oDiv.getElementsByClassName("el-table__body-wrapper");
                            // 两个表格的时候需增加滚屏的高度
                            // var main_scrolling=document.getElementsByClassName("common_table_height");
                            // selDiv.style.left = Math.min(_x, startX)-left+scrolling[0].scrollLeft +main_scrolling[0].scrollLeft+ "px";
                            // console.log(scrolling)
                            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+main_scrolling[0].scrollTop+ "px";
                            selDiv.style.top =
                                Math.min(_y, startY) - top - oDiv.getElementsByClassName("is-leaf")[0].offsetHeight + scrollingTop + "px";
                            selDiv.style.width = Math.abs(_x - startX) + "px";
                            if (scrolling[0].scrollTop > scrollingTop) {
                                selDiv.style.height = Math.abs(_y - startY) + scrolling[0].scrollTop + "px";
                            } else {
                                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 && !vnode.context.tableData[i][vnode.context.rowDisabled]) {
                                        selList[i].className = selList[i].className + " seled";
                                        vnode.context.$refs.table.toggleRowSelection(
                                            vnode.context.tableData[i],
                                            true
                                        )
                                        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
                                        )
                                        let index = vnode.context.multipleSelection.indexOf(vnode.context.tableData[i])
                                        vnode.context.multipleSelection.splice(index, 1)
                                    }
                                }
                            }
                            // ---------------- 关键算法结束---------------------
                        }
                        if (document.onmousemove) {
                            vnode.context.clearEventBubble(evt);
                        }
                    };
                    //在鼠标抬起后做的重置
                    oDiv.onmouseup = function () {
                        //把鼠标移动事初始化
                        document.onmousemove = null;
                        if (selDiv) {
                            oDiv.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.$selectState(false);
                        // 防止重复$handleSelect()事件
                        if (!vnode.context.selectItemFlag) {
                            vnode.context.$handleSelect();
                            vnode.context.selectItemFlag = false;
                        }
                    };
                };
            }
        }
    },
    components: {
        airContexMenu, treeDialog
    },
    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
        },
        // 每页显示条数
        pageSize: {
            type: Number,
            default: 20
        },
        // 当前页
        currentPage: {
            type: Number,
            default: 1
        },
        // 当表格需要单选的时候
        isRadio: {
            type: Boolean,
            default: false
        },
        // table 回显数据
        backMultipleSelection: {
            type: Array,
            default: () => []
        },
        // 是否显示分页
        showPage: {
            type: Boolean,
            default: true
        },
        // 是否显示添加按钮
        showAdd: {
            type: Boolean,
            default: false
        },
        // 表格高度
        height: {
            type: [Number, String],
            default: 500
        },
        // 空数据时显示的文本内容
        emptyText: {
            type: String,
            default: "暂无数据"
        },
        // 行禁用事件
        rowDisabled: {
            type: String,
            default: "disabled"
        },
        // 翻页是否保存数据
        pageDataFlag: {
            type: Boolean,
            default: true
        },
        // 对应列是否可以排序,如果设置为 'custom',则代表用户希望远程排序,需要监听 Table 的 sort-change 事件
        sortable: {
            type: [Boolean, String],
            default: false
        }
    },
    data() {
        return {
            //指令中确定的时候是鼠标按下事件
            mouseflag: false,
            //选中的数组
            multipleSelection: [],
            //控制右键菜单弹出显示
            dialogVisible: false,
            //右键鼠标的位置
            position: {
                left: 0,
                top: 0
            },
            //控制右键显示隐藏
            toggleShow: false,
            //分页当前的页数
            page: this.currentPage,
            //当前右键点击的列
            currentRow: [],
            //当前滚动的距离,
            targetScroll: 0,
            // 是否点击单选按钮
            selectItemFlag: false,
            // 表头tree 数据
            treeData: [],
            treeName: '',
            treePid: '',
            treeId: '',
            treeTitle: "",
            // 表格top距离
            tableTop: null,
            tableLeft: null,
            timer: null,
            currentshowPage: true
        };
    },
    methods: {
        // 换算index
        indexMethod(index) {
            return index + (this.page - 1) * this.pageSize + 1;
        },
        //清除默认事件
        clearEventBubble(evt) {
            if (evt.stopPropagation) evt.stopPropagation();
            else evt.cancelBubble = true;
            if (evt.preventDefault) evt.preventDefault();
            else evt.returnValue = false;
        },
        //列表单击选中事件
        rowClick(row, event, column) {
            if (row.disabled) {
                return
            }
            // 确定当前的row的index
            var index = 0;
            this.tableData.forEach((ele, i) => {
                if (ele[this.myKey] == row[this.myKey]) {
                    index = i + 1;
                }
            });
            let flag = false;
            this.toggleShow = false;
            // 单选
            if (this.isRadio) {
                // 每次点击重置
                var dom = this.$refs.table.$el.getElementsByTagName("tr");
                this.tableData.forEach(ele => {
                    this.$refs.table.toggleRowSelection(ele, false);
                });
                for (var i = 1; i < dom.length; i++) {
                    dom[i].className = "el-table__row";
                }
                this.$refs.table.toggleRowSelection(row, true);
                this.$refs.table.$el.getElementsByTagName("tr")[index].className =
                    "el-table__row seled";
                this.multipleSelection = Array.of(row)
                this.$handleSelect();
                return
            }
            this.selectItemFlag = true;
            // 如果有就删除
            this.multipleSelection.forEach((ele, i) => {
                if (ele[this.myKey] == row[this.myKey]) {
                    this.$refs.table.toggleRowSelection(row, false);
                    this.$refs.table.$el.getElementsByTagName("tr")[index].className =
                        "el-table__row";
                    flag = true;
                    this.multipleSelection.splice(i, 1)
                }
            });
            // 如果没有就push
            if (!flag) {
                this.$refs.table.toggleRowSelection(row, true);
                //后期优化吧 element的方法用不了 只能自己改变类名
                this.$refs.table.$el.getElementsByTagName("tr")[index].className =
                    "el-table__row seled";
                this.multipleSelection.push(row)
            }
            this.$handleSelect();
        },
        //列表右键点击事件
        rowContextmenu(row, event) {
            if (row[this.rowDisabled]) {
                return
            }
            //确定当前的row的index
            var index = 0;
            this.tableData.forEach((ele, i) => {
                if (ele[this.myKey] == row[this.myKey]) {
                    index = i + 1;
                }
            });
            // 每次点击重置
            var dom = this.$refs.table.$el.getElementsByTagName("tr");
            this.tableData.forEach(ele => {
                this.$refs.table.toggleRowSelection(ele, false);
            });
            for (var i = 1; i < dom.length; i++) {
                dom[i].className = "el-table__row";
            }
            this.$refs.table.toggleRowSelection(row, true);
            this.$refs.table.$el.getElementsByTagName("tr")[index].className =
                "el-table__row seled";
            this.multipleSelection = Array.of(row)
            this.$handleSelect();
            //为当前的row赋值
            this.currentRow = row;
            //阻止默认右键点击事件
            event.returnValue = false;
            //获取右键坐标
            this.position.left = event.clientX;
            this.position.top = event.clientY;
            //菜单出现的flag
            this.toggleShow = true;
            //显示弹出窗
            this.$emit("contextmenu", row, event);
        },
        //右键菜单弹出事件
        menuClick(item) {
            //右键点击以后隐藏
            this.toggleShow = false;
            this.$emit("rowContextmenu", item, this.currentRow);
        },
        //每页条数变化 ui定死每页20条
        handleSizeChange(val) {
            // console.log(`每页 ${val} 条`);
        },
        // 当前页变化
        handleCurrentChange(val) {
            this.page = val;
            this.$emit("currentPageChange", this.page);
        },
        //当批量选中结束调用
        $handleSelect() {
            this.removal();
            // 过滤掉禁用的数据
            this.multipleSelection = this.multipleSelection.filter((item) => {
                return !item[this.rowDisabled]
            });
            this.$emit("handleSelect", this.multipleSelection);
        },
        // 根据myKey去重
        removal() {
            let hash = {};
            this.multipleSelection = this.multipleSelection.reduce((preVal, curVal) => {
                hash[curVal[this.myKey]] ? '' : hash[curVal[this.myKey]] = true && preVal.push(curVal);
                return preVal
            }, [])
        },
        // 是否框选 state
        $selectState(state) {
            this.$emit("selectState", state);
        },
        // 选项发生改变事件 ZZ
        selectionChange(val) {
            // this.multipleSelection = val;
        },
        //监听表格的滚动
        handleScroll(e) {
            this.targetScroll = e.target.scrollTop;
        },
        //当单选时触发的事件
        selectItem(selection, row) {
            this.selectItemFlag = true;
            this.rowClick(row);
        },
        //当表头多选是触发的事件
        selectAll(selection) {
            this.selectItemFlag = true;
            var dom = this.$refs.table.$el.getElementsByTagName("tr");
            if (this.isRadio) {
                this.tableData.forEach(ele => {
                    this.$refs.table.toggleRowSelection(ele, false);
                });
                for (var i = 1; i < dom.length; i++) {
                    dom[i].className = "el-table__row";
                }
                this.multipleSelection = [];
                this.$handleSelect();
                return;
            }
            if (selection.length) {
                for (var i = 1; i < dom.length; i++) {
                    //为了去掉表头的tr从1开始
                    dom[i].className = "el-table__row seled";
                }
                // if(this.pageDataFlag){
                    this.multipleSelection = [...this.multipleSelection, ...selection];
                // }else{
                //     this.multipleSelection = selection;
                // }
                this.removal();
            } else {
                for (var i = 1; i < dom.length; i++) {
                    dom[i].className = "el-table__row";
                }
                this.tableData.forEach((item) => {
                    this.multipleSelection.forEach((ele, i) => {
                        if (item[this.myKey] == ele[this.myKey]) {
                            this.multipleSelection.splice(i, 1)
                            i = i - 1;
                        }
                    })
                })
            }
            this.$handleSelect();
        },
        //双击事件
        rowDblclick(row, event) {
            if (row.disabled) {
                return
            }
            this.$emit("rowDblclick", row, event);
        },
        // 表头点击事件
        headerClick(column, event) {
            if (event.target.nodeName === 'DIV')
                this.columData.map((item, index) => {
                    if (item.name == column.label) {
                        if (item.tree && item.tree.data.length) {
                            this.$refs.treeDialog.$el.getElementsByClassName("el-dialog")[0].style.top = this.tableTop + 53 + 'px';
                            this.$refs.treeDialog.$el.getElementsByClassName("el-dialog")[0].style.left = this.tableLeft + (event.path[1].scrollWidth) * (index) + 55 + 'px';
                            this.treeData = item.tree.data;
                            this.treePid = item.tree.pid;
                            this.treeId = item.tree.id;
                            this.treeName = item.tree.name;
                            this.treeTitle = item.name;
                            this.$refs.treeDialog.dialogOpen();
                        }
                    }
                })
        },
        // tree确定事件/
        makesure(data) {
            this.$emit('makesure', data);
        },
        // 添加按钮点击事件
        addClick() {
            this.$emit('addClick');
        },
        // 初始化回显数据
        initMultipleSelection() {
            this.$nextTick(() => {
                var dom = this.$refs.table.$el.getElementsByTagName("tr");
                this.$refs.table.clearSelection();
                this.tableData.forEach((ele, i) => {
                    dom[i + 1].className = "el-table__row";
                });
                //为了传过来的值进行回显
                if (this.backMultipleSelection.length > 0) {
                    this.backMultipleSelection.forEach(item => {
                        this.tableData.forEach((ele, i) => {
                            if (item[this.myKey] == ele[this.myKey] && !ele[this.rowDisabled]) {
                                this.$refs.table.toggleRowSelection(ele, true);
                                dom[i + 1].className = "el-table__row seled";
                            }
                        });
                    });
                }
            });
        },
        // 自适应分页的位置
        initPage() {
            if (!this.currentshowPage) return
            // 分页显示的位置
            let obj = this.$refs.table.$el.getElementsByClassName("el-table__body-wrapper")[0];
            // 判断是否有滚动条
            if (obj.scrollHeight > obj.clientHeight) {
                this.$refs.table.$el.getElementsByClassName("el-table__append-wrapper")[0].style.cssText = "";
            } else {
                this.$refs.table.$el.getElementsByClassName("el-table__append-wrapper")[0].style.cssText = "position:absolute;right:0;bottom:0;";
            }
        },
        // 行禁用事件
        selectable(row, index) {
            if (row[this.rowDisabled]) {
                return false
            } else {
                return true
            }
        },
        // 清空回显数据
        clearMultipleSelection() {
            this.multipleSelection = [];
        },
        /*
        *@method 表头排序点击事件
        *@param 1 column行数据
        *@param 2 prop == "ascending"  排序
        *         prop == "descending"  降序
        *@param 3 order排序规则
        *@return {返回值类型} 返回值说明
        */
        sortChange(column, prop, order) {
            this.$emit("sortChange", column, prop, order);
        }
    },
    watch: {
        backMultipleSelection: {
            handler: function (newVal, oldVal) {
                //为了回显
                this.multipleSelection = newVal;
                this.initMultipleSelection();
            },
            deep: true
        },
        currentPage: {
            handler(newValue, oldValue) {
                this.page = newValue;
            },
            immediate: true
        },
        showPage: {
            handler(newValue, oldValue) {
                this.currentshowPage = newValue;
            },
            immediate: true
        },
        tableData(val) {
            /* 监听table数据变化时(分页操作),已选中数据做回显 */
            this.$nextTick(() => {
                // 判断翻页是否保存选中数据
                // if (this.pageDataFlag) {
                    var dom = this.$refs.table.$el.getElementsByTagName("tr");
                    this.$refs.table.clearSelection();
                    this.tableData.forEach((ele, i) => {
                        dom[i + 1].className = "el-table__row";
                    });
                    //为了已选中数据进行回显
                    if (this.multipleSelection.length > 0) {
                        this.multipleSelection.forEach(item => {
                            this.tableData.forEach((ele, i) => {
                                if (item[this.myKey] == ele[this.myKey] && !ele[this.rowDisabled]) {
                                    this.$refs.table.toggleRowSelection(ele, true);
                                    dom[i + 1].className = "el-table__row seled";
                                }
                            });
                        });
                    }
                // }else{
                    // this.multipleSelection = [];
                // }
                this.initPage();
            })
        }
    },
    computed: {
        // 通过滚动距计算阴影 class
        headerClassName() {
            if (this.targetScroll == 0) {
                return "air-table-header__class"
            } else if (this.targetScroll > 0 && this.targetScroll <= 100) {
                return "air-table-header__class air-table-header__scroll1"
            } else if (this.targetScroll > 100 && this.targetScroll <= 200) {
                return "air-table-header__class air-table-header__scroll2"
            } else {
                return "air-table-header__class air-table-header__scroll3"
            }
        }
    },
    mounted() {
        document.onclick = () => {
            this.toggleShow = false;
        };
        this.multipleSelection = this.backMultipleSelection;
        this.initMultipleSelection();
        this.$refs.table.$refs.bodyWrapper.addEventListener('scroll', this.handleScroll);
        this.timer = setTimeout(() => {
            this.initPage();
        }, 20);
    },
    beforeDestroy() {
        this.$refs.table.$refs.bodyWrapper.removeEventListener('scroll', this.handleScroll);
        clearTimeout(this.timer);
    }
};
</script>

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

.air-table-wrapper {
    @include wh(100%, 100%);
    %scroll-calss {
        width: 100%;
        height: 1px;
        position: absolute;
        top: 51px;
        left: 0;
        content: "";
        z-index: 2;
    }
    .seled {
        background: #f5f5f5 !important;
    }
    .no-seled {
        background: #ffffff !important;
    }
    .el-table__body-wrapper {
        overflow: auto;
    }
    .el-table__body tr {
        cursor: pointer;
        box-sizing: border-box;
        border-top: 1px solid #f5f5f5 !important;
        border-bottom: 1px solid #f5f5f5 !important;
    }
    .air-table-header__class th {
        padding: 9px 0 !important;
        box-sizing: border-box;
        border-bottom: 1px solid #f5f5f5 !important;
    }
    .air-table-header__scroll1 {
        position: relative;
        &::after {
            @extend %scroll-calss;
            box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2),
                0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
        }
    }
    .air-table-header__scroll2 {
        position: relative;
        &::after {
            @extend %scroll-calss;
            box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
                0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
        }
    }
    .air-table-header__scroll3 {
        position: relative;
        &::after {
            @extend %scroll-calss;
            box-shadow: 0 3px 3px -2px rgba(0, 0, 0, 0.2),
                0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 1px 8px 0 rgba(0, 0, 0, 0.12);
        }
    }
    .el-table__body td {
        box-sizing: border-box;
        border-bottom: 1px solid #f5f5f5;
    }
    .hover-row {
        border-top: 1px solid #f5f5f5;
        border-bottom: 1px solid #f5f5f5;
        background: #fafafa;
    }
    .el-table__append-page {
        .el-pagination {
            text-align: right;
            margin: 48px 0;
            .btn-prev {
                @extend %airLeft;
            }
            .btn-next {
                margin-right: 0px;
                @extend %airRight;
            }
            .el-pager li {
                @extend %elPagerLi;
            }
            .el-pagination__total {
                @extend %elPaginationTotal;
            }
        }
        .el-pagination button:disabled {
            color: #bdbdbd;
        }
    }
    .el-table__append-pageAdd {
        .el-pagination {
            margin: 48px 0 76px !important;
            .btn-prev {
                @extend %airLeft;
            }
            .btn-next {
                margin-right: 0px;
                @extend %airRight;
            }
            .el-pager li {
                @extend %elPagerLi;
            }
            .el-pagination__total {
                @extend %elPaginationTotal;
            }
        }
        .el-pagination button:disabled {
            color: #bdbdbd;
        }
    }
    .air-table__context--menu {
        box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2),
            0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
        .air-table__context--list {
            cursor: pointer;
            height: 48px;
            @include flexCenter(flex-start, center);
            .air-table__context--icon {
                font-size: 20px;
                margin-left: 20px;
                color: #757575;
            }
            .air-table__context--info {
                font-size: 14px;
                margin-left: 20px;
                color: #212121;
            }
        }
        .air-table__context--list:hover {
            background: #f5f5f5;
        }
    }
    .el-table th {
        padding: 9px 0 !important;
    }
    .air-treeDialog-wrappers .el-dialog {
        margin: 0 !important;
    }
    .el-table-column--selection .cell {
        padding-left: 20px;
        padding-right: 10px;
    }
}
</style>


复制代码

2、css样式

.air-table-wrapper {
    display: flex;
    flex-direction: column;
    @include wh(100%, 100%);
    .el-table__body {
        overflow: hidden;
        // padding-bottom: 139px;
    }
    .el-table__body td .concat-table-container {
        max-width: 350px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        cursor: pointer
    }
    .el-table__fixed-body-wrapper {
        position: relative
    }
    .el-table__body tr {
        height: $tableRowHeight;
    }
    .el-table__body-wrapper .is-scrolling-none {
        height: auto !important;
    }

    .el-table {
        width: 100%;
        // min-height: 900px;
        // height:-webkit-calc(100% - 56px) !important;
        td {
            border-bottom: none;
            height: $tableRowHeight;
            padding: 0px;
            max-width: 400px;
        }
        th.is-leaf {
            border-bottom: 1px solid rgba(0, 0, 0, 0.12);
            height: 53px;
            font-size: 16px;
            color: rgba(0, 0, 0, 0.87);
        }
        th {
            &:first-child {
                // border-left: 4px solid #fff;
                //text-align: center
            }
            .cell {
                height: 34px !important;
                font-weight: 100;
                line-height: 34px;
                overflow: visible !important;
                text-overflow: ellipsis;
                white-space: nowrap;
                max-width: 400px;
            }
        }
        .el-table__body {
            tr {
                td:first-child {
                    // border-left: 4px solid #fff;
                    //text-align: center
                }
            }
            tr.hover-row {
                td {
                    background: rgba(0, 0, 0, 0.02)
                }
                // td:first-child{border-left: 4px solid $themeColor}
            }
            tr.current-row {
                td {
                    background: rgba(0, 0, 0, 0.04)
                }
                // td:first-child{border-left: 4px solid $themeColor}
            }
            .el-checkbox__inner{
                @include wh(16px, 16px);
                border: 2px solid #bdbdbd;
            }
            .el-radio__inner {
                @include wh(16px, 16px);
                border: 2px solid #bdbdbd;
                border-radius: 0px
            }
        }
        .el-table__header {
            .el-checkbox__inner {
                @include wh(16px, 16px);
                border: 2px solid #bdbdbd;
            }
            .el-checkbox__input.is-indeterminate .el-checkbox__inner::before{
                top: 7px;
            }
        }
        .el-checkbox__inner::after {
            -webkit-box-sizing: content-box;
            box-sizing: content-box;
            content: "";
            border: 2px solid #fff;
            border-left: 0;
            border-top: 0;
            height: 7px;
            left: 4px;
            position: absolute;
            top: 1px;
        }
        .el-radio__label {
            display: none
        }
        .el-radio__inner::after {
            -webkit-box-sizing: content-box;
            box-sizing: content-box;
            content: "";
            border: 2px solid #fff;
            transform: rotate(45deg) scaleY(1);
            border-left: 0;
            border-top: 0;
            height: 7px;
            left: 4px;
            position: absolute;
            top: 1px;
            border-radius: 0px;
            background: none
        }
        .el-radio__input.is-checked .el-radio__inner::after {
            transform: rotate(45deg) scaleY(1);
        }
    }
}
复制代码

3、contextMenu.js

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: {
                    width:"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: {
                  width:"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(){

        }
    }
};

复制代码

3、treeDialog.vue

/*
* @property { data :  {Array} 接口传来的数组 }
* @property { showCheckbox :  {Boolean} 是否显示多选小方框 }
* @property { placeholder :  {String} 提示语,上方搜索框提示语。 }
* @method   @check : 父组件使用check来接收已选中的所有数据组成的数组
* @property { width : {String} 弹窗的宽度可以传50%也可以传200px }
* @property { title : {String} 弹窗上方的名字 }
* @property { showScreen : {Boolean} 是否需要筛选框 }
* @property { id : {String} id字段 }
* @property { pid : {String} 父id字段 }
* @property { name : {String} name字段 }
* @method  @nodeclick : 节点被点击时的回调
* @property { defaultExpandAll : {Boolean} 是否默认展开 }
* @method  @dialogOpen 打开弹出框
* @method  @dialogClose 关闭弹出框
* @method  closeOnClickModal 是否可以点击遮罩层来关闭
* @method  close 关闭事件
* @version 1.0.0
* @edit: 2018/8/2
*/
<template>
    <div class="air-treeDialog-wrappers">
        <el-dialog :title="treeTitle"
            :visible.sync="dialogVisible"
            :width="width"
            @close="closed"
            :before-close = "beforeclose">
            <commonfiltertree :placeholder="placeholder"
                :data="allData"
                :showCheckbox="showCheckbox"
                @check='getcheckdata'
                :showScreen="showScreen"
                @click='getCurrentKey'
                @checkkey='getCheckedKeys'
                :defaultExpandAll="defaultExpandAll"
                :defaultCheckedKeys="defaultCheckedKeys"
                :id="treeId"
                :pid="treePid"
                :name="treeName"
                :isfilterText="isfilterText"
                :close-on-click-modal="closeOnClickModal"
                ref="tree"></commonfiltertree>
            <div class="foot">
                <airButton class="foot-button"
                    @click="dialogClose"
                    buttonType="colorButtonOrange"
                    buttonName="确定"></airButton>
            </div>
        </el-dialog>
    </div>
</template>
<script>
import commonfiltertree from "@/components/newCommon/tree/tree.vue";
export default {
    props: {
        //提示语,上方搜索框提示语
        placeholder: {
            type: String,
            default: ""
        },
        //处理好的数据
        data: {
            type: Array,
            default: () => []
        },
        // 是否显示多选小方框
        showCheckbox: {
            type: Boolean,
            default: true
        },
        //弹窗宽度
        width: {
            type: String,
            default: "360px"
        },
        //题目(水费电费水电费)
        title: {
            type: String,
            default: ""
        },
        //是否需要筛选框
        showScreen: {
            type: Boolean,
            default: true
        },
        //是否默认展开
        defaultExpandAll: {
            type: Boolean,
            default: false
        },
        //id 的字段名
        id: {
            type: String,
            default: "id"
        },
        //父id 的字段名
        pid: {
            type: String,
            default: "pid"
        },
        //内容的字段名
        name: {
            type: String,
            default: "name"
        },
        // //回显数组
        // defaultCheckedKeys: {
        //     type: Array,
        //     default: () => []
        // }
        closeOnClickModal: {
            type: Boolean,
            default: true
        }

    },
    components: {
        commonfiltertree
    },
    data() {
        return {
            filterText: '',
            countent: "",
            checkbox: this.showCheckbox,
            data1: new Array,
            dialogTableVisible: false,
            screen: this.showScreen,
            dialogVisible: false,
            allData: this.data,
            treeName: this.name,
            treeId: this.id,
            treePid: this.pid,
            treeTitle: this.title,
            checkKeyData: [],
            isfilterText: false,
            defaultCheckedKeys: []
        };
    },
    watch: {
        filterText(val) {
            this.$refs.tree2.filter(val);
        },
        data(val) {
            this.allData = val;
        },
        pid(val) {
            this.treePid = val;
        },
        id(val) {
            this.treeId = val;
        },
        name(val) {
            this.treeName = val;
        },
        title(val) {
            this.treeTitle = val;
        }
    },
    methods: {
        getcheckdata(data) {
            //有多选框的时候返回的data数组
            this.$emit('check', data);
        },
        getCurrentKey(data) {
            //点击的时候返回当前点击的key
            this.checkKeyData = data;
            this.defaultCheckedKeys[0] = data.id;
            console.log(this.defaultCheckedKeys)
            this.$emit('click', data);
        },
        getCheckedKeys(data) {
            //有多选框时返回的key所组成的数组
            this.checkKeyData = data;
            this.$emit('checkkey', data);
        },
        //打开弹出框
        dialogOpen() {
            this.dialogVisible = true;
            this.isfilterText = false;
        },
        //关闭弹出框
        dialogClose() {
            this.dialogVisible = false;
            this.isfilterText = true;
            this.$emit('makesure', this.checkKeyData);
        },
        //关闭返回
        closed() {
            this.$emit("closed");
            this.dialogVisible = false;
        },
        beforeclose(done){
            this.dialogVisible = false;
            this.$refs.tree.clearnodekey()
        }
    }
};
</script>
<style lang="scss" >
// @import "../../../public/style/common.scss";
.air-treeDialog-wrappers {
    .el-dialog {
        //min-height: 280px;
        max-height: 568px;
        border-radius: 4px;
        box-shadow: 0px 20px 20px #777;
    }
    .el-dialog .el-dialog__header {
        background-color: transparent;
    }
    .el-dialog .el-dialog__headerbtn .el-dialog__close {
        color: #000;
    }
    .el-dialog__headerbtn {
        top: 16px;
    }
    .el-dialog .el-dialog__header {
        border-bottom: 1px solid #fafafa;
    }
    .el-dialog__body {
        padding: 0;
        border-bottom-left-radius: 4px;
        border-bottom-right-radius: 4px;
    }
    .expanded {
        color: #616161;
    }
    .is-leaf {
        color: transparent;
    }

    .air-tree-wrappers .el-tree {
        margin-bottom: 0px;
    }
    .foot {
        width: 100%;
        height: 56px;
        background-color: #fff;
        border-top: 1px solid #f5f5f5;
        border-bottom-left-radius: 4px;
        border-bottom-right-radius: 4px;
        .foot-button {
            float: right;
            margin-top: 11px;
            margin-right: 24px;
        }
    }
}
</style>

复制代码

结语: 通过操作 DOM 来改变 class ,感觉有点累赘,希望能有大神指出不足之处!

转载于:https://juejin.im/post/5b8a2fca6fb9a019f007128c