You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
8.8 KiB
Vue

1 year ago
<template>
<view class="tm-table" >
<scroll-view v-if="fixedHeader" :scroll-with-animation="true" :scroll-left="scrollx_px_header" @scroll="scrollChange($event,'header')" scroll-x="" class="tm-table-scroll tm-table-scroll-body fulled">
<view class=" tm-table-scroll-header">
<tm-row v-if="listdata.header.length>0" preatClass="tm-table-header " style="white-space: nowrap;">
<block v-for="(item,index) in listdata.header" :key="index">
<tm-col :maxCol="maxGrid" :grid="item['grid']?item.grid:col"
:color="(listdata.headerBgcolor?listdata.headerBgcolor:item.color)+ (black_tmeme?' bk ':'')"
custom-class="" :padding="5">
<view @click="rank(index)" class="text-size-s vertical-align-middle text-weight-b text-overflow-1 flex-center"
style="white-space: initial;height: 36px;">
<view class="flex-center vertical-align-middle fulled-height">
<text>{{item[rangKey]}}</text>
<view v-if="item['sort']" class="flex-col flex-center pl-10 flex-shrink" style="line-height: 0;">
<text :class="[index==rank_index&&rank_type==0?'':'opacity-6']" class="iconfont icon-sort-up text-size-xs "></text>
<text :class="[index==rank_index&&rank_type==1?'':'opacity-6']" class="iconfont icon-sort-down text-size-xs mt-14"></text>
</view>
</view>
</view>
</tm-col>
</block>
</tm-row>
</view>
</scroll-view>
<scroll-view :scroll-with-animation="true" :scroll-left="scrollx_px_body" @scroll="scrollChange($event,'body')" scroll-x :scroll-y="height_s?true:false" class="tm-table-scroll tm-table-scroll-body fulled"
:style="{
height:height_s?`${height_s}px`:'auto'
}">
<view v-if="!fixedHeader" class=" tm-table-scroll-header">
<tm-row v-if="listdata.header.length>0" preatClass="tm-table-header " style="white-space: nowrap;">
<block v-for="(item,index) in listdata.header" :key="index">
<tm-col :maxCol="maxGrid" :grid="item['grid']?item.grid:col"
:color="(listdata.headerBgcolor?listdata.headerBgcolor:item.color)+ (black_tmeme?' bk ':'')"
custom-class="" :padding="5">
<view @click="rank(index)" class="text-size-s vertical-align-middle text-weight-b text-overflow-1 flex-center"
style="white-space: initial;height: 36px;">
<view class="flex-center vertical-align-middle fulled-height">
<text>{{item[rangKey]}}</text>
<view v-if="item['sort']" class="flex-col flex-center pl-10 flex-shrink" style="line-height: 0;">
<text :class="[index==rank_index&&rank_type==0?'':'opacity-6']" class="iconfont icon-sort-up text-size-xs "></text>
<text :class="[index==rank_index&&rank_type==1?'':'opacity-6']" class="iconfont icon-sort-down text-size-xs mt-14"></text>
</view>
</view>
</view>
</tm-col>
</block>
</tm-row>
</view>
<block v-for="(item,index) in listdata.col" :key="index">
<tm-row v-if="listdata.col.length>0" preatClass="tm-table-header" style="white-space: nowrap;">
<block v-for="(item2,index2) in item" :key="index2">
<tm-col
:maxCol="maxGrid"
:grid="item2['grid']?item2['grid']:(listdata.header[index2]['grid']?listdata.header[index2]['grid']:col)"
:color="item2.color?item2.color:(listdata.header[index2].color?listdata.header[index2].color+' text':(item2.color?item2.color+' text':((index+1)%2?activeCellColorRow+' text':'white'))) + (black_tmeme?' bk ':'')">
<view @click="onclick(index,index2,item2)"
class="text-size-s border-grey-lighten-3-b-1 flex-center wrap fulled-height overflow"
:class="black_tmeme?' bk ':''"
:style="{height: `${rowHeight}rpx`}">
<view class=" overflow fulled fulled-height " :class="(item2['rowAlign']||listdata.header[index2]['rowAlign']||rowAlign)">
<slot name="cell" :data="{parentIndex:index,childrenIndex:index2,data:item2}">
<text class="pa-10">{{item2[rangKey]}}</text>
</slot>
</view>
</view>
</tm-col>
</block>
</tm-row>
</block>
</scroll-view>
</view>
</template>
<script>
/**
* 表格
* @property {Number} height = [] 默认0表格的整体高度如果设定了高度超过后变为滚动
* @property {Number} col = [] 默认0默认的列宽记住是1-12
* @property {Boolean} fixedHeader = [] 默认false是否固定顶部表头
* @property {String} activeCellColorRow = [] 默认blue默认高亮的行颜色
* @property {String} rang-key = [] 默认textlist对象时读取文本的字段默认text
* @property {Number} rowHeight = [] 默认72单元格的高度单位rpx
* @property {Array} rowAlign = [] 默认'flex-center'
* 单元格对齐方式本库类也可能通过单元格数据对齐覆盖此全局变量以控制单个的对齐方式
* @property {Number} maxGrid = [] 默认12,布局的列表比如我想一行5个就可以用到此属性设置为10然后grid=2即可
* @property {String} sort-key = [] 默认text默认text即rangKey字段进行排序排序的字段名称
* @property {Object} list = [] 默认{}list数据格式见文档
* @example <tm-search-table.vue :list="list2"></tm-search-table.vue>
*/
import tmRow from "@/tm-vuetify/components/tm-row/tm-row.vue"
import tmCol from "@/tm-vuetify/components/tm-col/tm-col.vue"
import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
export default {
components: {
tmRow,
tmCol,
tmIcons
},
name: "tm-table",
props: {
height: {
type: Number,
default: 0
},
black: {
type: Boolean | String,
default: null
},
// 默认的列宽
col: {
type: Number,
default: 3
},
// 默认高亮的单元格。
activeCellColorRow: {
type: String,
default: 'primary'
},
// 对象时读取文本的字段默认text
rangKey: {
type: String,
default: 'text'
},
sortKey:{
type:String,
default:'text'
},
list: {
type: Object,
default: () => {
return {};
}
},
//默认计算方式是12列布局。
maxGrid:{
type:Number,
default:12
},
fixedHeader:{
type:Boolean|String,
default:false,
},
rowHeight:{
type:Number,
default:72
},
rowAlign:{
type:String,
default:'flex-center'
}
},
data() {
return {
scrollx_px_header: 0,
scrollx_px_body: 0,
scrollx_obj: null,
orderlist: null,
rank_index:null,
rank_type:2,
list_default: {
header: [], //头部数据
col: [], //行的数据
headerBgcolor: 'primary', //头部的背景颜色
}
};
},
created() {
this.listdata = this.list;
},
watch:{
list:{
deep:true,
handler(){
this.listdata = this.list;
}
}
},
computed: {
black_tmeme: function() {
if (this.black !== null) return this.black;
return this.$tm.vx.state().tmVuetify.black;
},
listdata: {
get: function() {
return this.orderlist||this.list_default;
},
set: function(val) {
let list = {
...this.list_default,
...val
};
this.orderlist = uni.$tm.deepClone(list);
}
},
height_s: function() {
return uni.upx2px(this.height);
}
},
methods: {
rank(index, type) {
if(!this.listdata.header[index]['sort']){
return;
}
let t = this;
let list = uni.$tm.deepClone(this.listdata);
this.rank_index = index;
if(this.rank_type==0){
let str = this.listdata.col[0][index][t.rangKey]
if(/^[0-9]+(.[0-9]{0,})?$/g.test(str)){
list.col.sort((a,b)=>b[index][t.sortKey]-a[index][t.sortKey])
this.rank_type = 1
}else{
list = this.list
this.rank_type = 2
}
this.listdata = list;
return;
}else if(this.rank_type==1){
this.listdata = this.list;
this.rank_type = 2
return;
}else{
this.rank_type = 0
let str = this.listdata.col[0][index][t.rangKey]
if(/^[0-9]+(.[0-9]{0,})?$/g.test(str)){
list.col.sort((a,b)=>a[index][t.sortKey]-b[index][t.sortKey])
}else{
list.col.reverse()
}
this.listdata = list;
return;
}
},
scrollChange(e,type){
if(type=='header'){
this.scrollx_px_body = e.detail.scrollLeft
}else if(type=="body"){
this.scrollx_px_header = e.detail.scrollLeft
}
},
onclick(index, index2, item) {
this.$emit("click", {
row: index,
col: index2,
data: item
})
}
}
}
</script>
<style lang="less" scoped>
.tm-table {
.tm-table-scroll-header {
position: relative;
z-index: 2;
.tm-header-body {}
}
.tm-table-scroll-body {
position: relative;
z-index: 1;
}
}
.tm-table-header {
flex-flow: nowrap !important;
white-space: nowrap !important;
display: flex;
flex-flow: row nowrap;
.tm-col {
flex-shrink: 0;
height: 100%;
}
}
</style>