el-table实践-适配业务场景:(通用表格、树形表格、合计处理、合并单元格、性能table、自定义tree形table)

350 阅读2分钟
一、el-table测试数据生成

自定义结构 测试数据生成---适配结构:

{ columnType: 'common', label: '交易所', prop: 'exchange', align: 'left', minWidth: 80, fixed: false },

const columns = [
  { dataType: '', tradeType: '', label: '交易所', prop: 'exchange', align: 'left', minWidth: 80, fixed: false },
  { dataType: '', tradeType: '', label: '产品', prop: 'product', align: 'left', minWidth: 80, fixed: false },
  { dataType: '', tradeType: '', label: '月份', prop: 'month', align: 'left', minWidth: 80, fixed: false }
]

// 按结构字段设置测试数据(columns为字段结构,count为返回数据列表数)

function getTestData(columns, count){
// 提供dataType进行数据类型判断
let data = []


function getTestData (columns, count) {
  // 提供dataType进行数据类型判断
  let data = []
  for (let i = 0; i < count; i++) {
    let obj = {}
    columns.forEach(o => {
      for (let pro in o) {
        if (pro === 'prop')
          obj[o[pro]] = String(Math.floor(Math.random() * 10000000))
      }
    })
    data.push(obj)
  }
  console.log(data)
  return data
}

适用于样式判别、Formatter

    <el-table-column
      v-for="(item, index) in tableStruct"
      :key="index"
      :label="item.label"
      :prop="item.prop"
      :min-width="item.width"
      :align="item.align"
      :show-overflow-tooltip="item.showOverFlow"
    >
      <template slot-scope="props">
        <div
          :class="{
            'cell-value-can-click': props.row[item.prop + 'CanClick']
          }"
          :style="{ fontWeight: props.row[item.prop + 'Bold'] ? 600 : 500 }"
          @click="
            props.row[item.prop + 'CanClick'] && cellClick(props.row, item)
          "
        >
          <div v-if="props.row[item.prop + 'NumberType'] === 'number2'">
            {{ amtFormatter(props.row[item.prop], 2) }}
          </div>
       </div>
      </template>
    </el-table-column>
二、el-table组织树形数据

3.1.分组组织数据(列表数据到树形数据)

[[DCE, eb], [DCE, eb]]===》
[    {        "exchange": "DCE",        "product": [            "eb",            "i"        ]
    }
]

[{"product": "eb", "exchange": "DCE"},{"product": "i","exchange": "DCE"},]===>
[{exchange: 'DCE', product: [{"product": "eb", "exchange": "DCE"}, {"product": "eb", "exchange": "DCE"}]},
{exchange: 'DCE', product: [{"product": "eb", "exchange": "DCE"}, {"product": "eb", "exchange": "DCE"}]},]

操作方法:

/**
 * 数据处理(首页 selectMmProductByDate 交易所-品种)---两级children处理
 * @param arr [Array] 被处理的数组
 * @param group_key [String] 分组字段,默认值为exchange,直接取下标
 * @param children [String] 分组之后,名称,默认children
 * @param header [String] 前缀
 */
 export function formatSelectMmProductData (arr, group_key = 'exchange', children = 'children', header = '') {
    let map = {}
    let res = []

  for (let i = 0; i < arr.length; i++) {
    let ai = arr[i]
    if (!map[ai[group_key]]) {
      map[ai[group_key]] = [ai]
    } else {
      map[ai[group_key]].push(ai)
    }
  }
  Object.keys(map).forEach(key => {
    let pros = []
    map[key].forEach(k=>{
      // 最内层id设置
      if(!k.id){
        k.id = k.exchange+k.type+k.product
      }
      pros.push(k)
    })
    const obj = {}
    obj[group_key] = key
    obj[children] = pros
    obj['id'] = header+key
    obj['text'] = key
    res.push(obj)
  })

  return res
}

组织两层数据整理,首先(按第一个统计维度)整理出第一层,并用childTmp记录该层的数据条目;再遍历childTmp,(按第二个统计维度)整理出第二层

// 构造数据,并设置展开列
let rett = formatSelectMmProductData(
  this.mainTradeDealedTableData,
  'exchange',
  'childTmp'
)
this.expandRowKeys = []
rett.forEach(r => {
  // this.expandRowKeys.push(r.id) // 展开列暂不设置
  r.children = formatSelectMmProductData(
    r.childTmp,
    'type',
    'children',
    r.exchange
  )
})

结合业务场景:el-table树形数据的处理

  1. 数据列表必须拥有children作为子节点

  2. row-key需设置唯一id

  3. expand-row-keys设置展开列,id取row-key指定的字段

     <el-table
     :data="tableData"
     size="mini"
     height="100%"
     border
     row-key="text"
     :expand-row-keys="expandRowKeys"
     >
         ...
     </el-table>
     
    
三、el-table合计数据处理

el-table合计列设置:

show-summary

:summary-method="getSummariesDetail"

另注意el-tab中首次合计行不显示问题,在updated中设置this.$refs.cashFutureDataTable.doLayout()

// columns为列设置,data为数据列表,进行纵向横向处理,因此遍历两遍
// 结果为构造出最后一行(合计行)对应每一列的数据
getSummariesDetail (param) {
  const { columns, data } = param
  const sums = []
  console.log(7777, 'ccc', columns, data)
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = '【合计】xxxxxx'
      return
    }
    // 对指定字段进行求合
    if (['hedging_ratio'].indexOf(column.property) !== -1) {
      return
    }
    // 对data进行筛选
    const values = data.map(item => {
      if (
        ['x1', 'x2', 'x3'].indexOf(item.variety_name) !==
        -1
      ) 
      {
        return Number(item[column.property])
      }
    })
    if (!values.every(value => isNaN(value))) {
      sums[index] = values.reduce((prev, curr) => {
        const value = Number(curr)
        if (!isNaN(value)) {
          return prev + curr
        } else {
          return prev
        }
      }, 0)
      
    } else {
      sums[index] = ''
    }
  })
  // 个别需要依赖自身已得结果的计算
  sums[5] = -1 * Number(sums[4]) / Number(sums[3])
  // 数值格式化
  sums.forEach((o, index)=>{
    sums[index] = this.weightFormatter(o)
  })
  return sums
}, 
四、el-table合并单元格数据处理

el-table合并单元格设置 :span-method="arraySpanMethod"

arraySpanMethod ({ row, column, rowIndex, columnIndex }) {
  let rowspan =
    column.property === 'variety_name'
      ? this.mergeObj[column.property][rowIndex]
      : 1
  return {
    rowspan: rowspan,
    colspan: 1
  }
},

组织合并数据mergeObj

// 数据统计:数据列表中相同数据(当然一般处理排序之后的数据),纵向合并单元格
getMergeObj (list) {
  this.mergeObj = {}
  // 标记
  let mack = 0
  for (const key in list[0]) {
    this.mergeObj[key] = []
    mack = 0
    list.forEach((item, index) => {
      if (index === 0) {
        this.mergeObj[key].push(1)
      } else {
        if (item[key] === list[index - 1][key] && item[key]) {
          this.mergeObj[key][mack] += 1
          this.mergeObj[key].push(0)
        } else {
          mack = index
          this.mergeObj[key].push(1)
        }
      }
    })
  }
  console.log('-合并对象:', this.mergeObj)
},

// 输出数据,用于span-method中合并
{ 
    "id": [ 1,1,1,1,1,1,1,1 ], 
    "depart": [ 3,0,0,1,1,1,1,1 ], 
    "type": [ 1,1,1,1,1,1,1,1 ], 
    "name": [ 1,1,1,1,1,1,1,1 ], 
    "age": [ 1,1,1,1,1,1,1,1 ] 
}
五、el-table数据量多大且不分页的处理:

t.zoukankan.com/liangtao999… www.cnblogs.com/wanghaoran5…

排查问题:卡慢,由于大量的虚拟dom和真实dom

解决方案:巧妙的dom操作-只渲染可视化的dom,根据列表中数据数量*height计算该区域的高度,使用空节点撑出高度,计算滚动条滚动的top位置,然后把列表放在这里(解决方法为去掉无用dom,只渲染可视化的dom)

六、自定义table

自定义table+treeMenu,横纵锁定cell进行处理

tree部分则采用递归组件形式,通过数据中children判断是否有子节点,level判断当前数据的层次

  <div class="vant-table" ref="vantTableRef">
    <div class="table_th">
      <div
        ref="table_th"
        v-for="(item, index) in option.column"
        :key="index"
        :style="{
          'flex-grow': item.prop !== 'name' ? 1 : 0,
          width: item.prop === 'name' ? firstWidth + 'px' : 0
        }"
      >
        <div v-if="!isArrow">{{ item.label }}</div>
        <div v-else>
          <div v-if="item.prop == 'name'">
            {{ item.label }}
          </div>
          <div v-else>
            <Arrow
              ref="fxpxSort"
              :name="item.label"
              code="fxpx"
              @load="load($event, item, index)"
            ></Arrow>
          </div>
        </div>
      </div>
      <div :style="{ width: lastWidth + 'px' }"></div>
      <!-- <div style="width: 10px" v-if="isToggle"></div> -->
    </div>
    <div :key="i" v-for="(item, i) in tableData">
      <div class="table_td" ref="table_td" @click="toggle(item)">
        <div
          v-for="(context, index) in option.column"
          :key="index"
          class="td"
          :style="{
            'flex-grow': context.prop !== 'name' ? 1 : 0,
            width: context.prop === 'name' ? firstWidth + 'px' : 0
          }"
        >
          <span v-if="context.prop !== 'name' && context.prop !== 'count'">
            <span v-if="context.prop === 'delta1'">
              {{
                commonUtils.stringFormatUtil.decimalMoneyFormat(
                  item['delta'],
                  2
                )
              }}</span
            >
            <span
              v-else-if="
                context.prop === 'position_vega' || context.prop === 'gamma'
              "
              :style="{
                color: !item['limit']
                  ? 'black'
                  : Math.abs(item['position_vega']) > item['limit'] ||
                    Math.abs(item['gamma']) > item['limit']
                  ? 'red'
                  : 'green'
              }"
            >
              {{
                commonUtils.stringFormatUtil.decimalMoneyFormat(
                  item[context.prop],
                  2
                ) === '-0.00'
                  ? 0
                  : commonUtils.stringFormatUtil.decimalMoneyFormat(
                      item[context.prop],
                      2
                    )
              }}</span
            >
            <span v-else>
              {{
                item[context.prop] || item[context.prop] === 0
                  ? commonUtils.stringFormatUtil.decimalMoneyFormat(
                      item[context.prop],
                      2
                    )
                  : '-'
              }}</span
            >
          </span>
          <span v-if="context.prop == 'name'">
            <div v-if="item.expand" class="el-icon-arrow-down"></div>
            <div v-else class="el-icon-arrow-up"></div>
            {{ IsRank ? i + 1 + '.' + item[context.prop] : item[context.prop] }}
          </span>
          <span v-if="context.prop == 'count'">
            {{
              commonUtils.stringFormatUtil.decimalMoneyFormat(
                item[context.prop],
                0
              )
            }}
          </span>
        </div>

        <div :style="{ width: lastWidth + 'px' }"></div>
      </div>
      <treeMenu
        :level="2"
        :firstWidth="firstWidth"
        :lastWidth="lastWidth"
        :tableData="item.children"
        :option="option"
      ></treeMenu>
    </div>
  </div>

表格结构

GammaOption: {
        column: [
          {
            label: '板块',
            prop: 'name'
          },
          {
            label: '金额',
            prop: 'amount'
          },
          {
            label: '重量',
            prop: 'weight'
          }
        ]
      },