| New file | 
|  |  |  | 
|---|
|  |  |  | <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> | 
|---|