<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> <tr v-if="noData" class="uni-table-loading"> <td class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</td> </tr> <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>