基於vue和elementUI封裝框選表格元件

lixinchao發表於2018-09-01

前言:主要是 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 ,感覺有點累贅,希望能有大神指出不足之處!

相關文章