vue 结合原生tabe 渲染表格 实现 固定头部、固定右侧、单选按钮、单元格拖拽监听位置

1.vue 结合原生tabe 渲染表格 实现 固定头部、固定右侧、单选按钮(避免el-table 数据量大卡顿)

// table.vue
<template>
    <div id="oldTable" v-if="headerData.length && tableData.length">
        <div class="table-scroll-mod" :style="{height: height+ 'px'}">
            <div class="table-scroll-box" :style="{height: height+ 'px'}" ref="tableScrollBox">
                <div class="fixed-head" ref="tableFixedHead">
                    <table>
                        <thead>
                        <tr>
                            <th v-if="checkBox" :width="checkBoxWidth">
                                <div class="te_cell">
                                    <input type="checkbox" v-model="isAllCheck" @click="checkAll" name="table"
                                           value="all"/>
                                </div>
                            </th>
                            <th :width="item.width || 100" v-for="(item, index) in headerCol" :key="index"
                                v-if="item.prop !== 'checkbox'">
                                <div class="te_cell">{{item.label}}</div>
                            </th>
                        </tr>
                        </thead>
                    </table>
                </div>
                <div class="fixed-right" :class="isMac ? '' : 'fixed-right_h'" ref="tableFixedRight"
                     :style="{width: (headerCol[rightIndex].width || 100) + 'px'}">
                    <table>
                        <thead>
                        <tr>
                            <td :width="headerCol[rightIndex].width">
                                <div class="te_cell">{{headerCol[rightIndex].label}}</div>
                            </td>
                        </tr>
                        </thead>
                        <tbody>

                        <tr v-for="(item, index) in tableData" :key="index">
                            <td :width="headerCol[rightIndex].width">
                                <div class="te_cell">
                                    <slot name="fixedRight" :row="item"></slot>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                <div class="fixedTopRight" :style="{width: (headerCol[rightIndex].width || 100) + 'px'}">
                    <span class="te_cell">{{headerCol[rightIndex].label}}</span>
                </div>
                <table class="table-content__warrp">
                    <thead>
                    <tr>
                        <th :width="item.width" v-for="(item, index) in headerCol" :key="index">
                            <div class="te_cell">{{item.label}}</div>
                        </th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="(item, index) in tableData" :key="index">
                        <td :width="checkBoxWidth">
                            <div class="te_cell" v-if="checkBox">
                                <input type="checkbox" name="table" v-model="checkBoxList[index]"
                                       @click="checkItem(item, index)" :value="index"/>
                            </div>
                        </td>
                        <td :width="child.width || 100" v-for="(child, index_c) in headerCol" :key="index_c"
                            v-if="child.prop !== 'checkbox'"
                            :draggable="draggable"
                            @dragstart="onDragstart($event, item, child.prop)"
                            @dragend="onDragend($event)"
                            @dragover="onDragover($event)"
                            @drop="onDrop($event, item, child.prop)">
                            <div class="te_cell" v-if="child.slot">
                                <slot :name="child.slot" :row="item"></slot>
                            </div>
                            <div class="te_cell" v-if="!child.slot">{{item[child.prop]}}</div>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</template>
<script type="text/ecmascript-6">
    const IS_MAC = function () {
        return /macintosh|mac os x/i.test(navigator.userAgent);
    }();

    function throttle(delay, noTrailing, callback, debounceMode) {
        var timeoutID;
        var cancelled = false;
        var lastExec = 0;

        function clearExistingTimeout() {
            if (timeoutID) {
                clearTimeout(timeoutID);
            }
        }

        function cancel() {
            clearExistingTimeout();
            cancelled = true;
        }

        if (typeof noTrailing !== 'boolean') {
            debounceMode = callback;
            callback = noTrailing;
            noTrailing = undefined;
        }

        function wrapper() {
            for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {
                arguments_[_key] = arguments[_key];
            }

            var self = this;
            var elapsed = Date.now() - lastExec;

            if (cancelled) {
                return;
            }

            function exec() {
                lastExec = Date.now();
                callback.apply(self, arguments_);
            }

            function clear() {
                timeoutID = undefined;
            }

            if (debounceMode && !timeoutID) {

                exec();
            }

            clearExistingTimeout();

            if (debounceMode === undefined && elapsed > delay) {

                exec();
            } else if (noTrailing !== true) {
                timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
            }
        }

        wrapper.cancel = cancel;

        return wrapper;
    }

    export default {
        name: 'oldTable',
        data() {
            return {
                isMac: IS_MAC,
                checkBoxWidth: 40,
                checkBoxList: [],
                isAllCheck: false
            }
        },
        props: {
            draggable: {
                type: Boolean,
                default: true
            },
            headerData: {
                type: Array,
                default: () => []
            },
            tableData: {
                type: Array,
                default: () => []
            },
            checkBox: Boolean,
            height: {
                type: Number,
                default: 500
            }
        },
        computed: {
            headerCol() {
                if (this.checkBox) {
                    let _check = [
                        {
                            prop: 'checkbox',
                            label: '',
                            width: this.checkBoxWidth
                        }
                    ]
                    return [..._check, ...this.headerData]
                } else {
                    return this.headerData
                }
            },
            rightIndex() {
                if (Array.isArray(this.headerData) && this.headerData.length) {
                    return this.headerCol ? this.headerData.length : this.headerData.length - 1
                } else {
                    return 0
                }

            }
        },
        watch: {
            tableData: {
                handler(val, oldVal) {
                    if (val !== oldVal) {
                        this.checkBoxList = Array(this.tableData.length).fill(false)
                    }
                },
                immediate: true,
                deep: true
            }
        },
        activated() {
        },
        created() {
        },
        methods: {
            onDragstart(event, row, prop) {
                this.$emit('drag-start', {event, row, prop});
            },
            onDragend(event) {
                this.$emit('drag-drop-end', {event});
            },
            onDrop(event, row, prop) {
                this.$emit('drag-drop', {event, row, prop});
            },
            onDragover(event) {
                event.preventDefault();
            },
            checkAll() {
                this.checkBoxList = this.checkBoxList.map((v) => !this.isAllCheck)
                this.isAllCheck = !this.isAllCheck
                this.$emit('checkList', this.isAllCheck ? this.tableData : [])
            },
            checkItem(row) {
                if (this.isAllCheck) this.isAllCheck = false
                this.$emit('checkList', [row])
            },
            getMultipleSelection() {
                let _list = []
                this.tableData.forEach((v, index) => {
                    if (this.checkBoxList[index]) {
                        _list.push(v)
                    }
                })
                return _list
            },
            bodyScroll() {
                let scrollLeft = this.box.scrollLeft
                let scrollTop = this.box.scrollTop
                let fixedHead = this.$refs.tableFixedHead
                fixedHead.scrollLeft = scrollLeft
                if (scrollLeft > this.fixedHead.scrollLeft) {
                    this.box.scrollLeft = this.fixedHead.scrollLeft
                }
                let fixedLeft = this.$refs.tableFixedRight
                fixedLeft.scrollTop = scrollTop
                if (scrollTop > this.fixedLeft.scrollTop) {
                    this.box.scrollTop = this.fixedLeft.scrollTop;
                }
            },
            headScroll() {
                let scrollLeft = this.fixedHead.scrollLeft
                let box = this.$refs.tableScrollBox
                box.scrollLeft = scrollLeft
            },
            fixedLeftScroll() {
                let scrollTop = this.fixedLeft.scrollTop
                this.box.scrollTop = scrollTop
            },
            initTable() {
                // 内容滚动区域监听联动头部区域
                this.box = this.$refs.tableScrollBox
                this.box && this.box.addEventListener('scroll', this.bodyScroll, false);

                // 头部滚动区域监听联通内容区域
                this.fixedHead = this.$refs.tableFixedHead;
                this.fixedHead && this.fixedHead.addEventListener('scroll', this.headScroll, false);

                // 固定左侧滚动监听联动内容
                this.fixedLeft = this.$refs.tableFixedRight;
                if (this.isMac) {
                    this.fixedLeft && this.fixedLeft.addEventListener('scroll', this.fixedLeftScroll, false)
                } else {
                    this.fixedLeft && this.fixedLeft.addEventListener('scroll', throttle(300, this.fixedLeftScroll), false)
                }
            }
        },
        mounted() {
            this.initTable()
        },
        components: {},
        beforeDestroy() {
            this.box.removeEventListener('scroll', this.bodyScroll, false)
            this.fixedHead.removeEventListener('scroll', this.headScroll, false)
            this.fixedLeft.removeEventListener('scroll', this.fixedLeftScroll, false)
        },
        destroyed() {
        }
    }
</script>
<style lang="scss" type="text/scss">
    #oldTable {
        .table-scroll-mod {
            position: relative;
            overflow: hidden;
            border: 1px solid #ebeef5;
        }

        .table-scroll-box {
            white-space: nowrap;
            /*overflow: scroll;*/
            overflow-x: scroll;
            z-index: 2001;

            .te_cell {
                line-height: 23px;
                padding: 0 5px;
                font-size: 12px;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                text-align: center;
            }
        }

        .table-content__warrp {
            td, thead th {
                border-color: #ddd;
                padding: 6px 0;
                font-size: 12px;
            }

            thead th {
                padding: 8px 0;
            }

            td:not(:last-of-type) {
                border-right: 1px solid #ddd;
                box-sizing: border-box;
            }

            tr:not(:last-of-type) {
                border-bottom: 1px solid #ddd;
                box-sizing: border-box;
            }

            background-color: #fff;
        }

        .table-scroll-box > table {
            overflow-x: scroll;
            overflow-y: scroll;
        }

        .table-scroll-box > table tr {
            display: -webkit-flex;
        }

        .table-scroll-box .fixed-head {
            position: absolute;
            top: 0;
            left: 0;
            z-index: 1000;
            width: 100%;
            /*width: calc(100% - 8px);*/
            overflow-x: scroll;
            border-color: #ddd;
            background: #f8f9fb;

            th {
                text-align: center;
                color: #333633;
                font-size: 14px;
                font-weight: inherit;
                padding: 8px 0;
            }

            th:not(:last-of-type) {
                border-right: 1px solid #ddd;
                box-sizing: border-box;
            }
        }

        .table-scroll-box .fixed-head::-webkit-scrollbar {
            width: 0px;
            height: 0px;
        }


        .table-scroll-box .fixed-head::-webkit-scrollbar {
            width: 0px;
            height: 0px;
        }

        .table-scroll-box .fixed-head > table {
            overflow-x: scroll;
        }

        .table-scroll-box .fixed-head > table tr {
            display: -webkit-flex;
        }

        .fixed-right {
            height: 100%;
            overflow-y: scroll;
            position: absolute;
            top: 0;
            right: -2px;
            z-index: 999;

            td {
                text-align: center;
                color: #333633;
                font-size: 14px;
                background-color: #fff;
                font-weight: inherit;
                padding: 5px 0;
            }

            thead td {
                padding: 8px 0;
            }

            tbody td {
                padding: 6px 0;
            }

            table {
                margin: 0;
            }

            tr:not(:last-of-type) {
                border-bottom: 1px solid #ddd;
                box-sizing: border-box;
            }

            box-shadow: 0 0 10px rgba(0, 0, 0, .12);
        }

        .table-scroll-box .table-content__warrp::-webkit-scrollbar {
            background-color: red;
        }

        .fixed-right_h {
            height: calc(100% - 17px) !important;
        }

        .fixedTopRight {
            height: 22px;
            width: 240px;
            background: #f8f9fb;
            text-align: center;
            position: absolute;
            top: 0;
            right: 0;
            z-index: 1001;
            line-height: 22px;
            color: #333633;
            font-size: 14px;
            font-weight: inherit;
            padding: 9.5px 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

    }
</style>

2.引用



import oldTable from '../components/table'

export default {

    title: 'Table',

};


const Template_dom = `
                    
模式状态 模式状态 模式状态 模式状态
模式状态 模式状态 模式状态
模式状态 模式状态 模式状态
{{row.TeacherName2}}
{{row.TeacherName2}}
模式状态 模式状态 模式状态
模式状态 模式状态
模式状态 模式状态
{{ row.memo }}
`
let item = { "lessonid": 172496219484, "sid": 53, "classCode": "模式状态", "className": "模式状态", "lessonNo": 3, "isVip": 0, "sectBegin": "2020-06-15T21:00:00", "sectEnd": "2020-06-15T23:00:00", "SectText": " 2020-06-15 21:00-23:00", "minutesSpan": "120", "hoursSpan": "2", "teacherCode1": null, "teacherName1": "", "teacherCode2": null, "teacherName2": "", "roomCode": "RMLY01059", "lessonTypeCode": 1, "lessonTypeName": "模式状态", "subjectCode": "", "subjectName": null, "courseCode": "CSLY01500024", "courseName": "模式状态", "operatorCode": null, "operatorName": null, "isVideolessonCode": 0, "isVideolessonName": "模式状态", "teachingContentTypeCode": 0, "teachingContentTypeName": "模式状态", "isUpLoadVideo": null, "memoStatus": 0, "memo": null, "memoHistory": null, "isAudit": 0, "auditTime": null, "auditTimeText": null, "auditUser": null, "auditName": null, "sMemo": null, "deptCode": "30", "deptName": "模式状态", "roomName": "模式状态", "areaCode": "模式状态", "areaName": "模式状态", "currentCount": 2, "useCard": 1, "pushStudentStatus": 0, "pushTeacher1Status": 0, "pushTeacher2Status": 0, "pushTeacher1UpLate": 0, "pushTeacher1UpTime": null, "pushTeacher2UpLate": 0, "pushTeacher2UpTime": null, "isHBFlag": 0, "mergeClass": null, "clearStatus": 0, "isLock": false, "gradeOuter": "" } export const toStorybook = () => ({ components: { oldTable }, template: Template_dom, data() { return { colConfigsTable: [ { prop: 'lessonNo', label: '姓名', width: 50 }, { prop: 'classCode', label: '随机编号', width: 120 }, { prop: 'className', label: '随机名称', width: 320 }, { prop: 'SectText', label: '时间Time', width: 150 }, { prop: 'hoursSpan', label: '小时数北京', width: 50 }, { prop: 'courseName', label: '课程名称', width: 150 }, { prop: 'gradeOuter', label: '年级(中国)' }, { prop: 'roomName', label: '教室', width: 160 }, { prop: 'teacherCode1', label: '教师编号', width: 140 }, { prop: 'teacherName1', label: '教师' }, { prop: 'isVideolessonName', label: '授课方式标识', width: 90 }, { prop: 'teachingContentTypeName', label: '内容类型', width: 90 }, { prop: 'pushStudentStatus', label: '学员状态', slot: 'pushStudentStatus' }, { prop: 'pushTeacher1Status', label: '教师状态', slot: 'pushTeacher1Status', width: 100 }, { prop: 'useCard', label: '使用皮卡丘', slot: 'useCard' }, { prop: 'operatorName', label: '排课操作人' }, { prop: 'lessonTypeName', label: '课节类型' }, { prop: 'areaName', label: '教学区', width: 150 }, { prop: 'deptName', label: '标准部门', width: 90 }, { prop: 'isVip', label: '是否VIP', slot: 'isVip' }, { prop: 'minutesSpan', label: '分钟数' }, { prop: 'subjectName', label: '科目' }, { prop: 'isUpLoadVideo', label: '视频是否上传', width: 120, slot: 'isUpLoadVideo' }, { prop: 'currentCount', label: '当前人数(占名额)', width: 120 }, { prop: 'teacherCode2', label: '模式状态', width: 140, slot: 'teacherCode2' }, { prop: 'teacherName2', label: '模式状态', slot: 'teacherName2' }, { prop: 'pushTeacher2Status', label: '模式状态', slot: 'pushTeacher2Status', width: 100 }, { prop: 'isHBFlag', label: '模式状态', slot: 'isHBFlag' }, { prop: 'mergeClass', label: '模式状态', width: 100 }, { prop: 'isAudit', label: '状态', slot: 'isAudit' }, { prop: 'auditUser', label: '审核人ca', width: 150 }, { prop: 'auditTimeText', label: '审核时间Time', width: 150 }, { prop: 'Operator', label: '操作', slot: 'Operator', width: 200 } ], tableData: Array(40).fill(item) }; }, methods: { } }); toStorybook.story = { name: 'table', };

你可能感兴趣的:(vue 结合原生tabe 渲染表格 实现 固定头部、固定右侧、单选按钮、单元格拖拽监听位置)