<template> 
 | 
    <view class="uni-table-scroll" :class="{ 'table--border': border, 'border-none': !noData }"> 
 | 
        <!-- #ifdef H5 --> 
 | 
        <table class="uni-table" border="0" cellpadding="0" cellspacing="0" :class="{ 'table--stripe': stripe }" :style="{ 'min-width': minWidth + 'px' }"> 
 | 
            <slot></slot> 
 | 
            <view v-if="noData" class="uni-table-loading"> 
 | 
                <view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view> 
 | 
            </view> 
 | 
            <view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view> 
 | 
        </table> 
 | 
        <!-- #endif --> 
 | 
        <!-- #ifndef H5 --> 
 | 
        <view class="uni-table" :style="{ 'min-width': minWidth + 'px' }" :class="{ 'table--stripe': stripe }"> 
 | 
            <slot></slot> 
 | 
            <view v-if="noData" class="uni-table-loading"> 
 | 
                <view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view> 
 | 
            </view> 
 | 
            <view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view> 
 | 
        </view> 
 | 
        <!-- #endif --> 
 | 
    </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
/** 
 | 
 * Table 表格 
 | 
 * @description 用于展示多条结构类似的数据 
 | 
 * @tutorial https://ext.dcloud.net.cn/plugin?id=3270 
 | 
 * @property {Boolean}     border                 是否带有纵向边框 
 | 
 * @property {Boolean}     stripe                 是否显示斑马线 
 | 
 * @property {Boolean}     type                     是否开启多选 
 | 
 * @property {String}     emptyText             空数据时显示的文本内容 
 | 
 * @property {Boolean}     loading             显示加载中 
 | 
 * @event {Function}     selection-change     开启多选时,当选择项发生变化时会触发该事件 
 | 
 */ 
 | 
export default { 
 | 
    name: 'uniTable', 
 | 
    options: { 
 | 
        virtualHost: true 
 | 
    }, 
 | 
    emits:['selection-change'], 
 | 
    props: { 
 | 
        data: { 
 | 
            type: Array, 
 | 
            default() { 
 | 
                return [] 
 | 
            } 
 | 
        }, 
 | 
        // 是否有竖线 
 | 
        border: { 
 | 
            type: Boolean, 
 | 
            default: false 
 | 
        }, 
 | 
        // 是否显示斑马线 
 | 
        stripe: { 
 | 
            type: Boolean, 
 | 
            default: false 
 | 
        }, 
 | 
        // 多选 
 | 
        type: { 
 | 
            type: String, 
 | 
            default: '' 
 | 
        }, 
 | 
        // 没有更多数据 
 | 
        emptyText: { 
 | 
            type: String, 
 | 
            default: '没有更多数据' 
 | 
        }, 
 | 
        loading: { 
 | 
            type: Boolean, 
 | 
            default: false 
 | 
        }, 
 | 
        rowKey: { 
 | 
            type: String, 
 | 
            default: '' 
 | 
        } 
 | 
    }, 
 | 
    data() { 
 | 
        return { 
 | 
            noData: true, 
 | 
            minWidth: 0, 
 | 
            multiTableHeads: [] 
 | 
        } 
 | 
    }, 
 | 
    watch: { 
 | 
        loading(val) {}, 
 | 
        data(newVal) { 
 | 
            let theadChildren = this.theadChildren 
 | 
            let rowspan = 1 
 | 
            if (this.theadChildren) { 
 | 
                rowspan = this.theadChildren.rowspan 
 | 
            } 
 | 
             
 | 
            // this.trChildren.length - rowspan 
 | 
            this.noData = false 
 | 
            // this.noData = newVal.length === 0  
 | 
        } 
 | 
    }, 
 | 
    created() { 
 | 
        // 定义tr的实例数组 
 | 
        this.trChildren = [] 
 | 
        this.thChildren = [] 
 | 
        this.theadChildren = null 
 | 
        this.backData = [] 
 | 
        this.backIndexData = [] 
 | 
    }, 
 | 
  
 | 
    methods: { 
 | 
        isNodata() { 
 | 
            let theadChildren = this.theadChildren 
 | 
            let rowspan = 1 
 | 
            if (this.theadChildren) { 
 | 
                rowspan = this.theadChildren.rowspan 
 | 
            } 
 | 
            this.noData = this.trChildren.length - rowspan <= 0 
 | 
        }, 
 | 
        /** 
 | 
         * 选中所有 
 | 
         */ 
 | 
        selectionAll() { 
 | 
            let startIndex = 1 
 | 
            let theadChildren = this.theadChildren 
 | 
            if (!this.theadChildren) { 
 | 
                theadChildren = this.trChildren[0] 
 | 
            } else { 
 | 
                startIndex = theadChildren.rowspan - 1 
 | 
            } 
 | 
            let isHaveData = this.data && this.data.length > 0 
 | 
            theadChildren.checked = true 
 | 
            theadChildren.indeterminate = false 
 | 
            this.trChildren.forEach((item, index) => { 
 | 
                if (!item.disabled) { 
 | 
                    item.checked = true 
 | 
                    if (isHaveData && item.keyValue) { 
 | 
                        const row = this.data.find(v => v[this.rowKey] === item.keyValue) 
 | 
                        if (!this.backData.find(v => v[this.rowKey] === row[this.rowKey])) { 
 | 
                            this.backData.push(row) 
 | 
                        } 
 | 
                    } 
 | 
                    if (index > (startIndex - 1) && this.backIndexData.indexOf(index - startIndex) === -1) { 
 | 
                        this.backIndexData.push(index - startIndex) 
 | 
                    } 
 | 
                } 
 | 
            }) 
 | 
            // this.backData = JSON.parse(JSON.stringify(this.data)) 
 | 
            this.$emit('selection-change', { 
 | 
                detail: { 
 | 
                    value: this.backData, 
 | 
                    index: this.backIndexData 
 | 
                } 
 | 
            }) 
 | 
        }, 
 | 
        /** 
 | 
         * 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中) 
 | 
         */ 
 | 
        toggleRowSelection(row, selected) { 
 | 
            // if (!this.theadChildren) return 
 | 
            row = [].concat(row) 
 | 
  
 | 
            this.trChildren.forEach((item, index) => { 
 | 
                // if (item.keyValue) { 
 | 
  
 | 
                const select = row.findIndex(v => { 
 | 
                    // 
 | 
                    if (typeof v === 'number') { 
 | 
                        return v === index - 1 
 | 
                    } else { 
 | 
                        return v[this.rowKey] === item.keyValue 
 | 
                    } 
 | 
                }) 
 | 
                let ischeck = item.checked 
 | 
                if (select !== -1) { 
 | 
                    if (typeof selected === 'boolean') { 
 | 
                        item.checked = selected 
 | 
                    } else { 
 | 
                        item.checked = !item.checked 
 | 
                    } 
 | 
                    if (ischeck !== item.checked) { 
 | 
                        this.check(item.rowData||item, item.checked, item.rowData?item.keyValue:null, true) 
 | 
                    } 
 | 
                } 
 | 
                // } 
 | 
            }) 
 | 
            this.$emit('selection-change', { 
 | 
                detail: { 
 | 
                    value: this.backData, 
 | 
                    index:this.backIndexData 
 | 
                } 
 | 
            }) 
 | 
        }, 
 | 
  
 | 
        /** 
 | 
         * 用于多选表格,清空用户的选择 
 | 
         */ 
 | 
        clearSelection() { 
 | 
            let theadChildren = this.theadChildren 
 | 
            if (!this.theadChildren) { 
 | 
                theadChildren = this.trChildren[0] 
 | 
            } 
 | 
            // if (!this.theadChildren) return 
 | 
            theadChildren.checked = false 
 | 
            theadChildren.indeterminate = false 
 | 
            this.trChildren.forEach(item => { 
 | 
                // if (item.keyValue) { 
 | 
                    item.checked = false 
 | 
                // } 
 | 
            }) 
 | 
            this.backData = [] 
 | 
            this.backIndexData = [] 
 | 
            this.$emit('selection-change', { 
 | 
                detail: { 
 | 
                    value: [], 
 | 
                    index: [] 
 | 
                } 
 | 
            }) 
 | 
        }, 
 | 
        /** 
 | 
         * 用于多选表格,切换所有行的选中状态 
 | 
         */ 
 | 
        toggleAllSelection() { 
 | 
            let list = [] 
 | 
            let startIndex = 1 
 | 
            let theadChildren = this.theadChildren 
 | 
            if (!this.theadChildren) { 
 | 
                theadChildren = this.trChildren[0] 
 | 
            } else { 
 | 
                startIndex = theadChildren.rowspan - 1 
 | 
            } 
 | 
            this.trChildren.forEach((item, index) => { 
 | 
                if (!item.disabled) { 
 | 
                    if (index > (startIndex - 1) ) { 
 | 
                        list.push(index-startIndex) 
 | 
                    } 
 | 
                } 
 | 
            }) 
 | 
            this.toggleRowSelection(list) 
 | 
        }, 
 | 
  
 | 
        /** 
 | 
         * 选中\取消选中 
 | 
         * @param {Object} child 
 | 
         * @param {Object} check 
 | 
         * @param {Object} rowValue 
 | 
         */ 
 | 
        check(child, check, keyValue, emit) { 
 | 
            let theadChildren = this.theadChildren 
 | 
            if (!this.theadChildren) { 
 | 
                theadChildren = this.trChildren[0] 
 | 
            } 
 | 
             
 | 
             
 | 
             
 | 
            let childDomIndex = this.trChildren.findIndex((item, index) => child === item) 
 | 
            if(childDomIndex < 0){ 
 | 
                childDomIndex = this.data.findIndex(v=>v[this.rowKey] === keyValue) + 1 
 | 
            } 
 | 
            const dataLen = this.trChildren.filter(v => !v.disabled && v.keyValue).length 
 | 
            if (childDomIndex === 0) { 
 | 
                check ? this.selectionAll() : this.clearSelection() 
 | 
                return 
 | 
            } 
 | 
  
 | 
            if (check) { 
 | 
                if (keyValue) { 
 | 
                    this.backData.push(child) 
 | 
                } 
 | 
                this.backIndexData.push(childDomIndex - 1) 
 | 
            } else { 
 | 
                const index = this.backData.findIndex(v => v[this.rowKey] === keyValue) 
 | 
                const idx = this.backIndexData.findIndex(item => item === childDomIndex - 1) 
 | 
                if (keyValue) { 
 | 
                    this.backData.splice(index, 1) 
 | 
                } 
 | 
                this.backIndexData.splice(idx, 1) 
 | 
            } 
 | 
  
 | 
            const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.checked && !item.disabled) 
 | 
            if (!domCheckAll) { 
 | 
                theadChildren.indeterminate = false 
 | 
                theadChildren.checked = true 
 | 
            } else { 
 | 
                theadChildren.indeterminate = true 
 | 
                theadChildren.checked = false 
 | 
            } 
 | 
  
 | 
            if (this.backIndexData.length === 0) { 
 | 
                theadChildren.indeterminate = false 
 | 
            } 
 | 
  
 | 
            if (!emit) { 
 | 
                this.$emit('selection-change', { 
 | 
                    detail: { 
 | 
                        value: this.backData, 
 | 
                        index: this.backIndexData 
 | 
                    } 
 | 
                }) 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
} 
 | 
</script> 
 | 
  
 | 
<style lang="scss"> 
 | 
$border-color: #ebeef5; 
 | 
  
 | 
.uni-table-scroll { 
 | 
    width: 100%; 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    overflow-x: auto; 
 | 
    /* #endif */ 
 | 
} 
 | 
  
 | 
.uni-table { 
 | 
    position: relative; 
 | 
    width: 100%; 
 | 
    border-radius: 5px; 
 | 
    // box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1); 
 | 
    background-color: #fff; 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    box-sizing: border-box; 
 | 
    display: table; 
 | 
    overflow-x: auto; 
 | 
    ::v-deep .uni-table-tr:nth-child(n + 2) { 
 | 
        &:hover { 
 | 
            background-color: #f5f7fa; 
 | 
        } 
 | 
    } 
 | 
    ::v-deep .uni-table-thead { 
 | 
        .uni-table-tr { 
 | 
            // background-color: #f5f7fa; 
 | 
            &:hover { 
 | 
                background-color:#fafafa; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    /* #endif */ 
 | 
} 
 | 
  
 | 
.table--border { 
 | 
    border: 1px $border-color solid; 
 | 
    border-right: none; 
 | 
} 
 | 
  
 | 
.border-none { 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    border-bottom: none; 
 | 
    /* #endif */ 
 | 
} 
 | 
  
 | 
.table--stripe { 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    ::v-deep .uni-table-tr:nth-child(2n + 3) { 
 | 
        background-color: #fafafa; 
 | 
    } 
 | 
    /* #endif */ 
 | 
} 
 | 
  
 | 
/* 表格加载、无数据样式 */ 
 | 
.uni-table-loading { 
 | 
    position: relative; 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    display: table-row; 
 | 
    /* #endif */ 
 | 
    height: 50px; 
 | 
    line-height: 50px; 
 | 
    overflow: hidden; 
 | 
    box-sizing: border-box; 
 | 
} 
 | 
.empty-border { 
 | 
    border-right: 1px $border-color solid; 
 | 
} 
 | 
.uni-table-text { 
 | 
    position: absolute; 
 | 
    right: 0; 
 | 
    left: 0; 
 | 
    text-align: center; 
 | 
    font-size: 14px; 
 | 
    color: #999; 
 | 
} 
 | 
  
 | 
.uni-table-mask { 
 | 
    position: absolute; 
 | 
    top: 0; 
 | 
    bottom: 0; 
 | 
    left: 0; 
 | 
    right: 0; 
 | 
    background-color: rgba(255, 255, 255, 0.8); 
 | 
    z-index: 99; 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    display: flex; 
 | 
    margin: auto; 
 | 
    transition: all 0.5s; 
 | 
    /* #endif */ 
 | 
    justify-content: center; 
 | 
    align-items: center; 
 | 
} 
 | 
  
 | 
.uni-table--loader { 
 | 
    width: 30px; 
 | 
    height: 30px; 
 | 
    border: 2px solid #aaa; 
 | 
    // border-bottom-color: transparent; 
 | 
    border-radius: 50%; 
 | 
    /* #ifndef APP-NVUE */ 
 | 
    animation: 2s uni-table--loader linear infinite; 
 | 
    /* #endif */ 
 | 
    position: relative; 
 | 
} 
 | 
  
 | 
@keyframes uni-table--loader { 
 | 
    0% { 
 | 
        transform: rotate(360deg); 
 | 
    } 
 | 
  
 | 
    10% { 
 | 
        border-left-color: transparent; 
 | 
    } 
 | 
  
 | 
    20% { 
 | 
        border-bottom-color: transparent; 
 | 
    } 
 | 
  
 | 
    30% { 
 | 
        border-right-color: transparent; 
 | 
    } 
 | 
  
 | 
    40% { 
 | 
        border-top-color: transparent; 
 | 
    } 
 | 
  
 | 
    50% { 
 | 
        transform: rotate(0deg); 
 | 
    } 
 | 
  
 | 
    60% { 
 | 
        border-top-color: transparent; 
 | 
    } 
 | 
  
 | 
    70% { 
 | 
        border-left-color: transparent; 
 | 
    } 
 | 
  
 | 
    80% { 
 | 
        border-bottom-color: transparent; 
 | 
    } 
 | 
  
 | 
    90% { 
 | 
        border-right-color: transparent; 
 | 
    } 
 | 
  
 | 
    100% { 
 | 
        transform: rotate(-360deg); 
 | 
    } 
 | 
} 
 | 
</style> 
 |