| <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.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> |