本人所负责的后台管理系统几乎都是这种表格功能,且有很多表格功能较为复杂,既要把id一样的数据做合并,还要能编辑单元格(包括但不限于input、select等),还要有复选框勾选,还能动态新增和删除行数据,列数据大概有80列,虽然每页显示100条数据,但是这是合并的数据,还能动态新增行数据,所以行数据不确定,如下图所示:

怎么解决大数据量的复杂表格卡页面的问题呢

在此先推荐一波本人找到的umy-ui,一套为开发者准备的基于 Vue 2.0 的桌面端组件库,完美解决表格万级数据渲染卡顿,编辑表格卡顿问题

github.com/u-leo/umy-u… 看看人家这格局!!!

大家有条件的帮忙奉献一个赞吧

上线的时候用的是umy-ui的表格功能,但是未开启use-virtual属性,即没有开启虚拟功能,因为当时告知过业务,这个页面数据量很大,又是复选框又是60列可以编辑又是动态新增还有合并,会很卡顿,当时双方“拉勾”说的是不会在页面上编辑和新增,就是看看,正式的只会用导入的功能,所以设置的是20条每页。

          <u-table
            :big-data-checkbox="checked"
            :height="height"
            :data="tableList"
            :span-method="objectSpanMethod"
            class="umy-ui-table"
            ref="tab"
            showBodyOverflow="title"
            showHeaderOverflow="title"
            :row-height="rowHeight"
            :header-cell-class-name="headerRequireCell"   
            @cell-click="customCellClickUmyUi"
            @selection-change="onSelectRowChange"
            border
            <u-table-column type="selection" width="55" fixed/>
            <template v-for="item in tableHeader">
              <u-table-column
                :key="item.purchaseForecastCode || item.timeStamp" 
                :prop="item.dataIndex"
                show-overflow-tooltip 
                :label="item.title" 
                :width="item.width"
                <template slot-scope="{row}">
                    ...省略
                </template>
              </u-table-column>  
            </template>
          </u-table>

根据接口返回的数据,code相同的进行合并某些列

   <!-- 合并用的:span-method="objectSpanMethod" -->
   objectSpanMethod({ row, column }) {
      let rowspanArr = ['lie1', 'lie2', 'lie3', 'lie4', 'lie5', 'lie6', 'lie7'];
      let span = !column.label || (column.label && column.label.indexOf('N+W') < 0 && !rowspanArr.includes(column.property));
      if (span) {
        if (column.property == 'typeTran') {
          if (row.typeIndex) {
            return [row.typeIndex, 1]
          } else return [0, 0]
        if (row.nameIndex) { // 如果有值,说明需要合并
          return [row.nameIndex, 1]
        } else return [0, 0]

当时为啥没有开启use-virtual虚拟属性呢

因为复选框和合并同时存在时,会有bug,看不到未合并的列时,高度没有撑开,往右滚动时才正常,往左滚回去时高度又缩回去了,如下图

umy-ui官方说法

so,当时没有开启use-virtual虚拟属性。

but上线之后频频收到业务反馈,页面太卡了。嗯,我又开启了漫漫优化的路!

首先满心欢喜的找到了vxe-table

确实不会像umy-ui那样高度撑不开,但是滚动时行数据会在最上面挤在一起出现,sorry,当时没有截图,给不了效果!

最后决定放弃框架的合并方法,自己写合并方法,这样就不会再冲突!接口返回的数据都是平铺的,自行处理下,新增一个children属性,id相同的放在children里面。

    // 处理表格数据
    dealTable(data, idName) {
      let firstRowIdArr = data.filter(e => e.firstRowId);
      let childrenArr = data.filter(e => !e.firstRowId);
      firstRowIdArr.forEach(e => {
        let children = childrenArr.filter(c => c[idName] == e[idName]);
        children.unshift(e);
        e.children = deepClone(children);
      this.tableList = firstRowIdArr;

表格还是用的umy-ui里面的ux-grid,没有使用vxe-table(因为横向滚动还是卡顿)

          <ux-grid
            :big-data-checkbox="checked"
            :height="height" 
            :data="tableList"
            class="umy-ui-table"
            use-virtual
            showBodyOverflow="title"
            showHeaderOverflow="title"
            :rowClassName="rowClassNameParent"
            :cell-class-name="cellClassNameParent"
            :header-cell-class-name="headerRequireCell"
            :fixed-columns-roll="true"
            @cell-click="customCellClickUmyUi"
            @selection-change="onSelectRowChange"
            border
            <ux-table-column type="checkbox" width="55" fixed="left"/>
            <template v-for="item in tableHeader">
              <ux-table-column
                :key="item.dataIndex" 
                :prop="item.dataIndex"
                show-overflow-tooltip 
                :field="item.dataIndex" 
                :title="item.title" 
                :width="item.width"
                <template slot-scope="{row}">
                  <div class="input_width_auto">
                    <!-- 类型 -->
                    <template v-if="'typeTran' == item.dataIndex">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        {{child.typeTran}}
                      </div>
                    </template>
                    <!-- N+W -->
                    <template v-else-if="item.title.indexOf('N+W') > -1">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        <a-input-number
                          v-if="canEditable(child) && child.type == '1'"
                          :precision="0"
                          style="width: 80px"
                          v-model="child[item.dataIndex]"
                          :placeholder="$t('Please enter')"
                          @blur="calWeeklyTotal(child, 'detailId')"
                          @pressEnter="$event.target.blur()"
                        <span v-else>{{child[item.dataIndex]}}</span>  
                      </div>
                    </template>
                    <!-- 编码 -->
                    <template v-else-if="'vendorCode' == item.dataIndex">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        <div v-if="canEditable(child) && child.type == '1'" class="pt_5">
                          <span :class="child.type == '1'? 'button_action':''" 
                                class="click_dispaly_none pl_10 input_display" 
                                v-if="canEditable(child)"
                                @click="showVendorModal($event, child)">{{child[item.dataIndex]}}
                          </span>
                        </div>
                        <span v-else>{{!child[item.dataIndex]? (child.type != '1'? '/': ''): child[item.dataIndex]}}</span>
                      </div>
                    </template>
                    <template v-else-if="['itemCategory'].includes(item.dataIndex)">
                      <span>{{getStatusTran(item.dataIndex, row[item.dataIndex], item.dataIndex)}}</span>
                    </template>
                    <template v-else-if="['approveStatus'].includes(item.dataIndex)">
                      <span>{{row[item.dataIndex]? getStatusTran(item.dataIndex, row[item.dataIndex], item.dataIndex): '未处理'}}</span>
                    </template>
                    <!-- 确认状态 -->
                    <template v-else-if="['quantityDetailStatus'].includes(item.dataIndex)">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        <span>{{!child[item.dataIndex]? (child.type != '1'? '/': 'xxx待确认'): getStatusTran(item.dataIndex, child[item.dataIndex], item.dataIndex)}}</span>
                      </div>
                    </template>
                    <!-- 单号 -->
                    <template v-else-if="'approveCode' == item.dataIndex">
                      <span class="color_2539a0 button_action" @click="showApprovelDetail(row)">{{row[item.dataIndex]}}</span>
                    </template>
                    <!-- 操作 -->
                    <template v-else-if="'operation' == item.dataIndex">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        <div class="table_operation" v-if="canEditable(child) && (child.type == '0' || child.type == '1')">
                          <div v-if="child.type == '0'">
                            <span class="ml_5 mr_5 color_2539a0 button_action" @click="assignSupplier(child)">分配</span>
                          </div>
                          <div v-if="child.type == '1'">
                            <span class="ml_5 mr_5 color_2539a0 button_action" @click="deleteForecastDetail(child)">删除</span>
                            <span class="ml_5 mr_5 color_2539a0 button_action" @click="saveForecastDetail(child)">保存</span>
                          </div>
                        </div>
                        <div v-else>/</div>
                      </div>
                    </template>
                    <!-- 创建人 -->
                    <template v-else-if="'allocationtor' == item.dataIndex">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        <div v-if="child.type != '1'">
                          <span>/</span>
                        </div>
                        <div v-else>{{child[item.dataIndex]}}</div>
                      </div>
                    </template>
                    <template v-else-if="['不合并列1', '不合并列2', '不合并列3'].includes(item.dataIndex)">
                      <div class="bb_ccc" v-for="child in row.children" :key="child.detailId">
                        <span>{{child[item.dataIndex] != 0 && !child[item.dataIndex]? (child.type != '1' && slashArr.includes(item.dataIndex)? '/': ''): child[item.dataIndex]}}</span>
                      </div>
                    </template>
                    <template v-else>
                      <span>{{row[item.dataIndex] != 0 && !row[item.dataIndex]? (row.type != '1' && slashArr.includes(item.dataIndex)? '/': ''): row[item.dataIndex]}}</span>
                    </template>
                  </div>
                </template>
              </ux-table-column>  
            </template>
          </ux-grid>

最重要的还是:rowClassName="rowClassNameParent"和:cell-class-name="cellClassNameParent"

    rowClassNameParent({row}) {
      if(!(row && row.children && row.children.length)) return;
      let className = `vxe-trHeight${row.children.length}`;
      return className;
    cellClassNameParent({column}) {
      let rowspanArr = ['列1', '列2', '列3', '列4', '列5', '列6', '列7', '列8'];
      let span = !column.title || (column.title && column.title.indexOf('N+W') < 0 && !rowspanArr.includes(column.property));
      if(!span) {
        return 'umy-grid-cell-merge';

给表格单元格加class属性

其中设置高度是老人了,具体请看juejin.cn/post/705891…

class名vxe-trHeight4的生成和下面的类似,只是vxe-trHeight4是以37为基准!

less 动态生成想要的class选择器

此时我们就要用到less中的loop,自动生成很多class名,因为每行数据高度可能对应有几十条数据,这边暂定自动生成38条class,即trHeight1、trHeight2...trHeight38。

.h(@i) when(@i <= 38){
  .trHeight@{i} {
     height: @i*54px;
  .h((@i + 1));
.h(1);

自此四个页面的优化完成,丝滑的不行

image.png

分类:
前端