tm-table.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <template>
  2. <view class="tm-table" >
  3. <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">
  4. <view class=" tm-table-scroll-header">
  5. <tm-row v-if="listdata.header.length>0" preatClass="tm-table-header " style="white-space: nowrap;">
  6. <block v-for="(item,index) in listdata.header" :key="index">
  7. <tm-col :maxCol="maxGrid" :grid="item['grid']?item.grid:col"
  8. :color="(listdata.headerBgcolor?listdata.headerBgcolor:item.color)+ (black_tmeme?' bk ':'')"
  9. custom-class="" :padding="5">
  10. <view @click="rank(index)" class="text-size-s vertical-align-middle text-weight-b text-overflow-1 flex-center"
  11. style="white-space: initial;height: 36px;">
  12. <view class="flex-center vertical-align-middle fulled-height">
  13. <text>{{item[rangKey]}}</text>
  14. <view v-if="item['sort']" class="flex-col flex-center pl-10 flex-shrink" style="line-height: 0;">
  15. <text :class="[index==rank_index&&rank_type==0?'':'opacity-6']" class="iconfont icon-sort-up text-size-xs "></text>
  16. <text :class="[index==rank_index&&rank_type==1?'':'opacity-6']" class="iconfont icon-sort-down text-size-xs mt-14"></text>
  17. </view>
  18. </view>
  19. </view>
  20. </tm-col>
  21. </block>
  22. </tm-row>
  23. </view>
  24. </scroll-view>
  25. <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"
  26. :style="{
  27. height:height_s?`${height_s}px`:'auto'
  28. }">
  29. <view v-if="!fixedHeader" class=" tm-table-scroll-header">
  30. <tm-row v-if="listdata.header.length>0" preatClass="tm-table-header " style="white-space: nowrap;">
  31. <block v-for="(item,index) in listdata.header" :key="index">
  32. <tm-col :maxCol="maxGrid" :grid="item['grid']?item.grid:col"
  33. :color="(listdata.headerBgcolor?listdata.headerBgcolor:item.color)+ (black_tmeme?' bk ':'')"
  34. custom-class="" :padding="5">
  35. <view @click="rank(index)" class="text-size-s vertical-align-middle text-weight-b text-overflow-1 flex-center"
  36. style="white-space: initial;height: 36px;">
  37. <view class="flex-center vertical-align-middle fulled-height">
  38. <text>{{item[rangKey]}}</text>
  39. <view v-if="item['sort']" class="flex-col flex-center pl-10 flex-shrink" style="line-height: 0;">
  40. <text :class="[index==rank_index&&rank_type==0?'':'opacity-6']" class="iconfont icon-sort-up text-size-xs "></text>
  41. <text :class="[index==rank_index&&rank_type==1?'':'opacity-6']" class="iconfont icon-sort-down text-size-xs mt-14"></text>
  42. </view>
  43. </view>
  44. </view>
  45. </tm-col>
  46. </block>
  47. </tm-row>
  48. </view>
  49. <block v-for="(item,index) in listdata.col" :key="index">
  50. <tm-row v-if="listdata.col.length>0" preatClass="tm-table-header" style="white-space: nowrap;">
  51. <block v-for="(item2,index2) in item" :key="index2">
  52. <tm-col
  53. :maxCol="maxGrid"
  54. :grid="item2['grid']?item2['grid']:(listdata.header[index2]['grid']?listdata.header[index2]['grid']:col)"
  55. :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 ':'')">
  56. <view @click="onclick(index,index2,item2)"
  57. class="text-size-s border-grey-lighten-3-b-1 flex-center wrap fulled-height overflow"
  58. :class="black_tmeme?' bk ':''"
  59. :style="{height: `${rowHeight}rpx`}">
  60. <view class=" overflow fulled fulled-height " :class="(item2['rowAlign']||listdata.header[index2]['rowAlign']||rowAlign)">
  61. <slot name="cell" :data="{parentIndex:index,childrenIndex:index2,data:item2}">
  62. <text class="pa-10">{{item2[rangKey]}}</text>
  63. </slot>
  64. </view>
  65. </view>
  66. </tm-col>
  67. </block>
  68. </tm-row>
  69. </block>
  70. </scroll-view>
  71. </view>
  72. </template>
  73. <script>
  74. /**
  75. * 表格
  76. * @property {Number} height = [] 默认:0,表格的整体高度,如果设定了高度,超过后变为滚动。
  77. * @property {Number} col = [] 默认:0,默认的列宽,记住是1-12列
  78. * @property {Boolean} fixedHeader = [] 默认:false,是否固定顶部表头
  79. * @property {String} activeCellColorRow = [] 默认:blue,默认高亮的行颜色
  80. * @property {String} rang-key = [] 默认:text,list对象时读取文本的字段,默认text
  81. * @property {Number} rowHeight = [] 默认:72,单元格的高度。单位rpx
  82. * @property {Array} rowAlign = [] 默认:'flex-center',
  83. * 单元格对齐方式本库类,也可能通过单元格数据对齐覆盖此全局变量,以控制单个的对齐方式
  84. * @property {Number} maxGrid = [] 默认:12,布局的列表,比如我想一行5个,就可以用到此属性,设置为10,然后grid=2即可。
  85. * @property {String} sort-key = [] 默认:text,默认text即rangKey字段进行排序。排序的字段名称。
  86. * @property {Object} list = [] 默认:{},list数据格式见文档
  87. * @example <tm-table :list="list2"></tm-table>
  88. */
  89. import tmRow from "@/tm-vuetify/components/tm-row/tm-row.vue"
  90. import tmCol from "@/tm-vuetify/components/tm-col/tm-col.vue"
  91. import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
  92. export default {
  93. components: {
  94. tmRow,
  95. tmCol,
  96. tmIcons
  97. },
  98. name: "tm-table",
  99. props: {
  100. height: {
  101. type: Number,
  102. default: 0
  103. },
  104. black: {
  105. type: Boolean | String,
  106. default: null
  107. },
  108. // 默认的列宽
  109. col: {
  110. type: Number,
  111. default: 3
  112. },
  113. // 默认高亮的单元格。
  114. activeCellColorRow: {
  115. type: String,
  116. default: 'primary'
  117. },
  118. // 对象时读取文本的字段,默认text
  119. rangKey: {
  120. type: String,
  121. default: 'text'
  122. },
  123. sortKey:{
  124. type:String,
  125. default:'text'
  126. },
  127. list: {
  128. type: Object,
  129. default: () => {
  130. return {};
  131. }
  132. },
  133. //默认计算方式是12列布局。
  134. maxGrid:{
  135. type:Number,
  136. default:12
  137. },
  138. fixedHeader:{
  139. type:Boolean|String,
  140. default:false,
  141. },
  142. rowHeight:{
  143. type:Number,
  144. default:72
  145. },
  146. rowAlign:{
  147. type:String,
  148. default:'flex-center'
  149. }
  150. },
  151. data() {
  152. return {
  153. scrollx_px_header: 0,
  154. scrollx_px_body: 0,
  155. scrollx_obj: null,
  156. orderlist: null,
  157. rank_index:null,
  158. rank_type:2,
  159. list_default: {
  160. header: [], //头部数据
  161. col: [], //行的数据
  162. headerBgcolor: 'primary', //头部的背景颜色
  163. }
  164. };
  165. },
  166. created() {
  167. this.listdata = this.list;
  168. },
  169. watch:{
  170. list:{
  171. deep:true,
  172. handler(){
  173. this.listdata = this.list;
  174. }
  175. }
  176. },
  177. computed: {
  178. black_tmeme: function() {
  179. if (this.black !== null) return this.black;
  180. return this.$tm.vx.state().tmVuetify.black;
  181. },
  182. listdata: {
  183. get: function() {
  184. return this.orderlist||this.list_default;
  185. },
  186. set: function(val) {
  187. let list = {
  188. ...this.list_default,
  189. ...val
  190. };
  191. this.orderlist = uni.$tm.deepClone(list);
  192. }
  193. },
  194. height_s: function() {
  195. return uni.upx2px(this.height);
  196. }
  197. },
  198. methods: {
  199. rank(index, type) {
  200. if(!this.listdata.header[index]['sort']){
  201. return;
  202. }
  203. let t = this;
  204. let list = uni.$tm.deepClone(this.listdata);
  205. this.rank_index = index;
  206. if(this.rank_type==0){
  207. let str = this.listdata.col[0][index][t.rangKey]
  208. if(/^[0-9]+(.[0-9]{0,})?$/g.test(str)){
  209. list.col.sort((a,b)=>b[index][t.sortKey]-a[index][t.sortKey])
  210. this.rank_type = 1
  211. }else{
  212. list = this.list
  213. this.rank_type = 2
  214. }
  215. this.listdata = list;
  216. return;
  217. }else if(this.rank_type==1){
  218. this.listdata = this.list;
  219. this.rank_type = 2
  220. return;
  221. }else{
  222. this.rank_type = 0
  223. let str = this.listdata.col[0][index][t.rangKey]
  224. if(/^[0-9]+(.[0-9]{0,})?$/g.test(str)){
  225. list.col.sort((a,b)=>a[index][t.sortKey]-b[index][t.sortKey])
  226. }else{
  227. list.col.reverse()
  228. }
  229. this.listdata = list;
  230. return;
  231. }
  232. },
  233. scrollChange(e,type){
  234. if(type=='header'){
  235. this.scrollx_px_body = e.detail.scrollLeft
  236. }else if(type=="body"){
  237. this.scrollx_px_header = e.detail.scrollLeft
  238. }
  239. },
  240. onclick(index, index2, item) {
  241. this.$emit("click", {
  242. row: index,
  243. col: index2,
  244. data: item
  245. })
  246. }
  247. }
  248. }
  249. </script>
  250. <style lang="less" scoped>
  251. .tm-table {
  252. .tm-table-scroll-header {
  253. position: relative;
  254. z-index: 2;
  255. .tm-header-body {}
  256. }
  257. .tm-table-scroll-body {
  258. position: relative;
  259. z-index: 1;
  260. }
  261. }
  262. .tm-table-header {
  263. flex-flow: nowrap !important;
  264. white-space: nowrap !important;
  265. display: flex;
  266. flex-flow: row nowrap;
  267. .tm-col {
  268. flex-shrink: 0;
  269. height: 100%;
  270. }
  271. }
  272. </style>