原生小程序表格封装

30 阅读7分钟

image.png

表格封装 table

【wxml】
<view class="container">
  <treeGrid
    headers="{{tableHeader }}"
    data="{{ row }}"
    open="{{open}}"
    border="{{ border }}"
    maxHeight="{{maxHeight}}"
    theadWid="{{theadWid}}"
    defaultChangeColor="{{defaultChangeColor}}"
    defaultChangeColor1="{{defaultChangeColor1}}"
    bind:toTable2="getToTable2"
    row_class_name="{{ row_class_name }}"
    cell_class_name="{{ cell_class_name }}"
  />
</view>
【js】
//treegrid-treegrid.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    targetId: {
      type: String,
      observer: function (data) {
        this.init()
      },
    },
    years: {
      type: Number,
      optionalTypes: [String],
      value: new Date().getFullYear()
    },
    months: {
      type: Number,
      optionalTypes: [String],
      value: new Date().getMonth()
    },
    tableHeader: {
      type: Array,
      value: []
    },
    row: {
      type: Array,
      optionalTypes: [Object],
      value: null
    },
    open: {
      type: Boolean,
      value: true
    },
    maxHeight: {
      type: String,
      value: '90vh'
    },
    theadWid: {
      type: String,
      value: '900rpx'
    },
    defaultChangeColor: {
      type: Boolean,
      value: true
    },
    defaultChangeColor1: {
      type: Boolean,
      value: true
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    border: true,
    outBorder: true,
    row_class_name: "tr",
    cell_class_name: "",
    // row: {},
  },
  lifetimes: {
    attached() {
      // this.init()
    }
  },
  onShow: function () {
    onfire.un("onClickRow");
    onfire.on("onClickRow", function (data) {
      console.log('onfire => onClickRow ', data);
    });
  },
  /**
   * 组件的方法列表
   */
  methods: {
    init() {
      // this.getData()
    },
    getToTable2(e) {
      this.triggerEvent('getFinallyId', e.detail)
    }
  }
})

【wxss】
.table {
  position: relative;
  font-size: 28rpx;
  background: #fff;
  border-right: none;
  border-radius: 8rpx;
  overflow: hidden;
}

.thead {
  border-bottom: none;
  display: flex;
  justify-content: flex-start;
  border-top-right-radius: 8rpx;
  border-top-left-radius: 8rpx;
  overflow: visible;
  background-color: #08A3EB;
  color: #fff;
  border: 2rpx solid #ebeef5;
  box-sizing: border-box;
}

.thead .td {
  padding: 20rpx 10rpx;
  font-weight: bold;
  display: inline-block;
  text-align: left;
  border-right: 1rpx solid #fff;
}

.thead .td:last-child {
  border-right: none;
}

.thead-border .td {
  border-right: 1rpx solid #ebeef5;
}

.thead-border .td:last-child {
  border-right: none;
}

.tbody {
  box-sizing: border-box;
  font-size: 28rpx;
  color: #666;
  border: 2rpx solid #ebeef5;
  border-top: none;
  border-bottom-left-radius: 8rpx;
  border-bottom-right-radius: 8rpx;
}

.tbody-tr {
  display: flex;
  border-bottom: 2rpx solid #ebeef5;
}

.tbody-tr:last-child {
  border-bottom-left-radius: 8rpx;
  border-bottom-right-radius: 8rpx;
}

.tbody-tr-stripe {
  background: #fff;
  border-bottom: none;
}

.tbody-tr-stripe:nth-child(2n) {
  background: #F6F6F6;
}

.tbody-tr .td {
  white-space: wrap;
  padding: 20rpx 10rpx;
  text-align: center;
}

.tbody-tr-border .td {
  border-right: 1rpx solid #F6F6F6;
}

.tbody-tr-border .td:last-child {
  border-right: none;
}

.no-data {
  display: flex;
  padding: 50rpx;
  color: #666;
  justify-content: center;
}

treeGrid

【wxml】
<!-- treegrid-treegrid.wxml -->
<view style="border: 1rpx solid #ecf1f8; border-bottom: 0">
  <scroll-view scroll-x="true" scroll-left="{{scrollLeft}}">
    <!-- 表格头 start -->
    <view class="thead" style="width: {{theadWid}};" catchtouchmove="catchTouchMove">
      <view
        wx:for="{{ headers }}"
        wx:key="index"
        wx:for-index="attrIndex"
        wx:for-item="attrItem"
        class="td"
        style="width:{{ headers[attrIndex].width }}; text-align:{{attrItem.align?attrItem.align:'center'}};"
      >
        <text class="table__head__td__text">{{attrItem.label}}</text>
      </view>
    </view>
    <!-- 表格头 end -->
  </scroll-view>
  <!-- 表格体 start -->
  <scroll-view
    scroll-y="true"
    class="tbody"
    style="max-height:{{ maxHeight ? maxHeight : 'auto' }};"
    bindscrolltolower="handleScrollToLower"
  >
    <scroll-view scroll-x="true" bindscroll="scrollX">
      <block wx:if="{{ data != [] }}">
        <block wx:for="{{data}}" wx:key="index">
          <TreeGridLine
            headers="{{ headers }}"
            data="{{ item }}"
            border="{{ border }}"
            row_class_name="{{ row_class_name }}"
            cell_class_name="{{ cell_class_name }}"
            stripe="{{true}}"
            open="{{open}}"
            bind:getChangeId="ChangeId"
            theadWid="{{theadWid}}"
            defaultChangeColor="{{defaultChangeColor}}"
            defaultChangeColor1="{{defaultChangeColor1}}"
            showItem="{{showItem}}"
          ></TreeGridLine>
        </block>
      </block>
      <!-- 列表无数据处理 -->
      <block wx:else>
        <view class="no-data">{{ msg }}</view>
      </block>
    </scroll-view>
  </scroll-view>
  <!-- 表格体 end -->
</view>
【js】

//treegrid-treegrid.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    //树形表格的数据源
    data: {
      optionalTypes: [Array, null],
      value: [],
    },
    //表头数据
    headers: {
      type: Array,
      value: []
    },
    // 设置表格的最大显示高度, 溢出可滚动
    maxHeight: {
      type: String,
      value: 'auto'
    },
    //设置表格的显示宽度
    width: {
      optionalTypes: [String, Number],
      value: '100%'
    },
    // 单元格的宽度
    tdWidth: {
      type: Number,
      value: 35
    },
    // 固定表头 thead达到Header的位置时就应该被fixed了
    offsetTop: {
      type: Number,
      value: 150
    },
    // 是否带有纵向边框
    border: {
      type: Boolean,
      value: false
    },
    msg: {
      type: String,
      value: '暂无数据~'
    },
    header_row_class_name: {
      type: String,
      value: "header-row-class-name"
    },
    row_class_name: {
      type: String,
      value: "row-class-name"
    },
    cell_class_name: {
      type: String,
      value: "cell-class-name"
    },
    open: {
      type: Boolean,
      value: true
    },
    theadWid: {
      type: String,
      value: '900rpx'
    },
    defaultChangeColor: {
      type: Boolean,
      value: true
    },
    defaultChangeColor1: {
      type: Boolean,
      value: true
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    scrolWidth: '100%',
    scrollLeft: 0,
    showItem: {
      showCol1: '',
      showId: '',
    },
  },

  lifetimes: {
    attached() {
      // console.log(this.data.data)
    }
  },

  //数据监听器
  observers: {
    'headers,cell_class_name': function (headers, cell_class_name) {
      const reducer = (accumulator, currentValue) => {
        return accumulator + Number(currentValue.width)
      };
      const scrolWidth = headers.reduce(reducer, 0)

      this.setData({
        scrolWidth: scrolWidth
      })
    }
  },

  /**
   * 组件的方法列表
   */
  methods: {
    ChangeId(val) {
      this.setData({
        'showItem.showCol1': val.detail.col1,
        'showItem.showId': val.detail.dataId
      })
      this.triggerEvent('toTable2', val.detail)
    },
    //禁止滚动
    catchTouchMove(e) {
      return false;
    },
    // 触底事件
    handleScrollToLower(e) {
      if (e.detail.direction == 'bottom') {
        console.log('scroll-view触底事件在这里处理加载下一页数据')
      }
    },
    //监听左右滚动
    scrollX(e) {
      this.setData({
        scrollLeft: e.detail.scrollLeft,
      });
    },
  }
})
【wxss】
.table {
  position: relative;
  font-size: 20rpx;
  background: #fff;
  border-right: none;
  border-radius: 8rpx;
  overflow: hidden;
}

.thead {
  font-weight: 700;
  position: sticky;
  top: 0rpx;
  z-index: 100;
  /* width: 1500rpx; */
  height: 60rpx;
  line-height: 60rpx;
  display: flex;
  white-space: nowrap;
  font-size: 20rpx;
  color: #fff;
  border-bottom: 2rpx solid #ecf1f8;
  background-color: #08A3EB;
}

.thead .td {
  /* padding: 20rpx 5rpx;
  font-weight: bold;
  display: inline-block;
  text-align: left; */
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #08A3EB;
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
  -o-text-overflow: ellipsis;
  text-overflow: ellipsis;
  /* border-right: 1rpx solid #fff; */
}

.thead .td:last-child {
  border-right: none;
}

.thead .td:nth-child(1) {
  position: sticky;
  z-index: 101;
  left: 0rpx;
  /* justify-content: left; */
}

.table__head__td__text {
  display: inline;
}

.tbody {
  /* box-sizing: border-box;
  font-size: 28rpx;
  color: #666;
  border: 2rpx solid #ebeef5;
  border-top: none;
  border-bottom-left-radius: 8rpx;
  border-bottom-right-radius: 8rpx;
  letter-spacing: -1rpx; */
  display: block;
  position: relative;
  overflow: scroll;
  /* width: 100%; */
  height: 100vh;
}

.tbody-tr {
  display: flex;
  /* border-bottom: 1px solid #ebeef5; */
}

.tbody-tr:last-child {
  border-bottom-left-radius: 8rpx;
  border-bottom-right-radius: 8rpx;
}

.tbody-tr-stripe {
  background: #fff;
  /* border-bottom: none; */
}

.tbody-tr-stripe:nth-child(2n) {
  background: #F6F6F6;
}

/* .tbody-tr .td {
  white-space: wrap;
  padding: 20rpx 10rpx;
  text-align: center;
} */

.tbody-tr-border .td {
  border-right: 1rpx solid #F6F6F6;
}

.tbody-tr-border .td:last-child {
  border-right: none;
}

.no-data {
  display: flex;
  padding: 50rpx;
  color: #666;
  justify-content: center;
}

TreeGridLine

【wxml】
<view class="container" style="width: {{theadWid}};">
  <view
    class="tbody-tr {{ stripe ? 'tbody-tr-stripe' : '' }} {{ border ? 'tbody-tr-border' : ''}} {{data.col1==showItem.showCol1&&data.dataId==showItem.showId?'showActive':''}}"
    data-it="{{data}}"
    bindtap="onRowClick"
    data-level="{{data.level}}"
  >
    <view
      wx:for-item="head"
      wx:key="index"
      wx:for="{{ headers }}"
      class="td"
      style="width:{{ headers[index].width }};color:{{ headers[index].color }};font-size:13px;"
    >
      <block wx:if="{{data[head['prop']]==data.col1}}">
        <image
          wx:if="{{data.nodes.length > 0 && index == 0}}"
          src="{{open ? 'https://cofco001-1308084433.cos.ap-beijing.myqcloud.com/image/miniprogram/Icon/show_more.png' : 'https://cofco001-1308084433.cos.ap-beijing.myqcloud.com/image/miniprogram/Icon/show_less.png'}}"
          class="indent{{data.level}} tbody-tr_img"
          catchtap="toggle"
        ></image>
        <image
          style="visibility: hidden"
          wx:if="{{data.nodes.length == 0 && index == 0}}"
          src="../../image/page_turning_right.png"
          class="indent{{data.level}} tbody-tr_img"
          bindtap="bubbling"
        ></image>
        <!-- defaultChangeColor:首列没有点击不变色 -->
        <view
          wx:if="{{data.col4 && data.col5&&defaultChangeColor||data.col1&&data.col2 && data.col3 && defaultChangeColor1 }}"
          style="color: #3583d8; display: inline-block; flex: 1"
        >
          {{data[head["prop"]]}}
        </view>
        <view wx:else style="display: inline-block; flex: 1">{{data[head["prop"]]}}</view>
      </block>
      <block wx:else>
        <view style="text-align: center">{{data[head["prop"]]}}</view>
      </block>
    </view>
  </view>

  <view hidden="{{ !open }}">
    <TreeGridLine
      wx:for="{{ data.nodes }}"
      wx:for-item="it"
      wx:key="idx"
      wx:for-index="idx"
      headers="{{ headers }}"
      data="{{ it }}"
      stripe="{{ stripe }}"
      border="{{ border }}"
      row_class_name="{{ row_class_name }}"
      cell_class_name="{{ cell_class_name }}"
      open="{{open}}"
      data-it="{{it}}"
      data-it1="{{it}}"
      data-level="{{data.level}}"
      defaultChangeColor="{{defaultChangeColor}}"
      defaultChangeColor1="{{defaultChangeColor1}}"
      showItem="{{showItem}}"
      bindtap="onRowClick"
    ></TreeGridLine>
  </view>
</view>
【js】
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    data: {
      type: Object,
      value: {},
    },
    headers: {
      type: Array,
      value: [],
    },
    // 是否带有纵向边框
    border: {
      type: Boolean,
      value: false
    },
    row_class_name: {
      type: String,
      value: "row-class-name"
    },
    cell_class_name: {
      type: String,
      value: "cell-class-name"
    },
    // 默认树的状态(展开true 或 折叠false)
    open: {
      type: Boolean,
      value: true
    },
    stripe: {
      type: Boolean,
      value: true,
    },
    theadWid: {
      type: String,
      value: '900rpx'
    },
    defaultChangeColor: {
      type: Boolean,
      value: true
    },
    defaultChangeColor1: {
      type: Boolean,
      value: true
    },
    showItem: {
      type: Object,
      value: {}
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    // open: true,
    // url: ''
  },

  lifetimes: {
    attached() {
      // console.log('子 this.open = ', this.data)
    }
  },

  /**
   * 组件的方法列表
   */
  methods: {
    //catchtap 阻止冒泡事件 只展开或折叠 不触发行点击事件
    toggle: function (e) {
      // console.log('toggle e = ', e, this.data.open)
      this.setData({
        open: !this.data.open
      })

      console.log(88);
    },
    //catchtap 阻止冒泡事件 只展开或折叠 不触发行点击事件
    bubbling: function () {
      return;
    },
    onRowClick(e) {
      const { it, level } = e.currentTarget.dataset
      // console.log(it.level, level, 'it.level == level')
      if (it.level == level) {
        this.triggerEvent('getChangeId', it, {
          bubbles: true,
          composed: true,
        })
        // this.triggerEvent('getFinallyId', it)
      }
      // else {
      //   this.triggerEvent('getChangeId', it)
      // }
      // let dataId = e.currentTarget.dataset.it
      // this.triggerEvent('getChangeId', dataId)
    },
    // ChangeId(val) {
    //   console.log(val, "bbb!!!!!!")
    //   this.triggerEvent('toTable2', val.detail)
    // },
  }
})

【wxss】
.container {
  --indent-width: 14rpx;
  /* width: 1500rpx; */
}

.indent2 {
  margin-left: calc(var(--indent-width));
}

.indent3 {
  margin-left: calc(var(--indent-width)*2);
}

.indent4 {
  margin-left: calc(var(--indent-width)*3);
}

.indent5 {
  margin-left: calc(var(--indent-width)*4);
}

.indent6 {
  margin-left: calc(var(--indent-width)*5);
}

.indent7 {
  margin-left: calc(var(--indent-width)*6);
}

.indent8 {
  margin-left: calc(var(--indent-width)*7);
}

.indent9 {
  margin-left: calc(var(--indent-width)*8);
}

.tbody {
  box-sizing: border-box;
  font-size: 28rpx;
  color: #666;
  border: 1px solid #ebeef5;
  border-top: none;
  border-bottom-left-radius: 8rpx;
  border-bottom-right-radius: 8rpx;
}

.tbody-tr {
  display: flex;
  border-bottom: 1px solid #ebeef5;
}

.tbody-tr .tbody-tr_img {
  width: 40rpx;
  height: 40rpx;
  vertical-align: middle;
}

.tbody-tr:last-child {
  border-bottom-left-radius: 8rpx;
  border-bottom-right-radius: 8rpx;
}

.tbody-tr-stripe {
  background: #fff;
  /* border-bottom: none; */
  display: flex;
  align-items: center;
  /* justify-content: center; */
}

.tbody-tr-stripe:nth-child(2n) {
  background: #F6F6F6;
}

.tbody-tr .td {
  white-space: wrap;
  /* padding: 20rpx 7rpx; */
}

.tbody-tr-border .td {
  /* border: 0rpx solid #dbe2e9; */
  /* height: 80rpx;
  line-height: 80rpx; */
  padding: 20rpx 0rpx;
}

.tbody-tr-border .td:last-child {
  border-right: none;
}

.tbody-tr-border .td:nth-child(1) {
  position: sticky;
  z-index: 101;
  left: 0;
  background-color: #fff;
  display: flex;
}

.showActive {
  background-color: #F5F5F5;
}

.showActive .td:nth-child(1) {
  background-color: #F5F5F5;
}

页面调用

<table
        tableHeader="{{tableHeader}}"
        row="{{row}}"
        open="{{false}}"
        bind:getFinallyId="finallyId"
        maxHeight="{{'28vh'}}"
        defaultChangeColor="{{true}}"
      ></table>

数据

  tableHeader: [
    { label: "名称", prop: "col1", width: "28%", color: "#000", align: "left" },
    { label: "当前", prop: "col2", width: "17%", color: "#000" },
    { label: "同比", prop: "col3", width: "17%", color: "#000" },
    { label: "去年", prop: "col4", width: "17%", color: "#000" },
    { label: "全年", prop: "col5", width: "17%", color: "#000" },
    { label: "预算", prop: "col6", width: "17%", color: "#000" },
  ],
    row: [
    {
      level: 1,
      id: "83671",
      dataId: "yyzsr",
      years: "2025",
      months: "10",
      col1: "一、收入",
      col2: "439",
      col3: "27%",
      col4: "4830",
      col5: "5233",
      col6: "-8%",
      nodes: [
        {
          level: 2,
          id: "83677",
          delFlag: "0",
          dataId: "zljt_yxfx_syqkb_yyzsr_qz_yysr",
          years: "2025",
          months: "10",
          col1: "  其中:营业收入",
          col2: "432",
          col3: "25%",
          col4: "4753",
          col5: "5233",
          col6: "-9%",
          col7: "5798",
          col8: "82%",
          col9: "346.42",
        },
      ],
    },
    {
      level: 1,
      id: "83621",
      dataId: "yyzcb",
      years: "2025",
      months: "10",
      col1: "二、总成本",
      col2: "442",
      col3: "35%",
      col4: "4740",
      col5: "5158",
      col6: "-8%",
    },
  ],