vue:el-table初始化表格选中项踩坑记录/element-ui表格

问题描述

首先,element-ui表格多选功能可以参考官网示例:表格数据多选;手动在表格中选取数据、通过监听selection-change获取选中项,实现起来非常顺利~
但在保存了选项、重新加载表格时,希望将已选项“打勾”却完全没有显示勾选状态:
vue:el-table初始化表格选中项踩坑记录/element-ui表格_第1张图片

debug过程

$nextTick

首先,遇事不决$nextTick!很多时候进行数据操作后页面显示不符合预期的问题,通过把数据操作放在$nextTick回调中就能得到解决!!具体原理可以参考:$nextTick原理深度解析
搭嘎内……并没有解决。。

在表格数据加载完成后再初始化选中项

本杂鱼反复捋顺自己封装的dataTable组件逻辑后,发现父级组件传来的选中项列表发生改变时,很可能表格数据还没有加载完成!因此实际上初始化选中项的时机应该在表格数据加载完成且父级选中项列表改变时!
也就是需要同时监听表格数据和父级选中项列表(同时监听多个属性可参考Vue watch如何同时监听多个属性?),且表格数据数量不少于选中项数量(表格数据加载完成了)再初始化选中项!

打没打内打没哟……Emm…但至少现在不是完全没有选中的效果了:
vue:el-table初始化表格选中项踩坑记录/element-ui表格_第2张图片
可以看到,具体的数据项还是没有勾选效果,但表头的全选框出现了半选效果!

表格选中项必须是表格数据的浅拷贝

本杂鱼有一个猜想,也许是父级传来的选中项列表有问题?于是直接用slice截取表格数据的前三项进行选中,发现no problem哒!但父级传来的选中项列表数据值明明和slice截取的一模一样,只是为了避免对象浅拷贝可能出现的问题(js深拷贝和浅拷贝的知识点可以参考JavaScript深浅拷贝区别),本杂鱼一开始传递的选中项列表是表格数据进行深拷贝后得到的新数组……
哈哈哈!这次终于是解决啦!哦,我真的好dio★▽★

参考代码

此处贴出本杂鱼自己封装的el-table组件DataTable,仅供参考:
(还有一个有趣的知识点,强烈建议还不了解的小伙伴去看看嗷:vue透传)

<!-- 封装el-table -->
<template>
  <div id="DataTable">
    <div class="DataTable-table">
      <el-table :ref="tableKey" :data="tableData" v-bind="tableConfig" v-on="$listeners" height="100%"
        style="width: 100%;">
        <el-table-column v-if="showSelection" type="selection" width="48" align="center" :reserve-selection="reserveSelection"></el-table-column>
        <el-table-column v-if="showNum" type="index" label="序号" width="80" align="center"></el-table-column>
        <slot>
          <el-table-column></el-table-column>
        </slot>
      </el-table>
    </div>
    <div v-if="showPagination" class="DataTable-pagination">
      <el-pagination v-bind="tableConfig" v-on="$listeners" :page-sizes="pageSizes" :layout="pagiLayout"
        :current-page.sync="query.pageNum" :page-size.sync="query.pageSize" :total="total"
        @current-change="currentChange" @size-change="sizeChange"></el-pagination>
    </div>
  </div>
</template>

<script>
export default {
  name: 'DataTable',
  inheritAttrs: false, // 透传属性、事件
  props: {
    tableKey: { // 表格ref名称
      type: String,
      default: () => 'dataTable'
    },
    // 表格配置
    showNum: { // 是否显示序号
      type: Boolean,
      default: true
    },
    showSelection: { // 是否显示多选框
      type: Boolean,
      default: false
    },
    reserveSelection: { // 是否支持跨页多选(默认翻页时不清空已选项)
      type: Boolean,
      default: () => true
    },
    selectItems: { // 选中项列表(必须是表格数据的浅拷贝)
      type: Array,
      default: () => []
    },
    // 分页栏配置
    showPagination: { // 是否显示分页栏
      type: Boolean,
      default: true
    },
    pageSizes: { // 分页大小
      type: Array,
      default: () => [10, 20, 50, 100]
    },
    pagiLayout: { // 分页栏布局配置
      type: String,
      default: () => 'total, prev, pager, next, sizes, jumper'
    },
    // 数据配置
    dataUrl: { // 获取表格数据的url
      type: String,
      default: () => ''
    },
    queryMethod: { // 查询方式(默认get)
      type: String,
      default: () => 'get'
    },
    params: { // 查询表格数据的额外参数
      type: Object,
      default: () => {}
    },
    keyParams: { // 必传的查询参数,没有传参时不查询接口
      type: Array,
      default: () => []
    },
    staticData: { // 静态数据
      type: Array,
      default: () => []
    }
  },
  data () {
    return {
      tableData: [], // 表格数据
      total: 0, // 数据总数
      query: { // 表格数据查询参数
        pageNum: 1,
        pageSize: 10
      }
    }
  },
  created () {
    const { dataUrl } = this
    if(!dataUrl) {
      this.tableData = this.staticData
      this.total = this.staticData.length
      this.dataChange()
    } else {
      this.refreshTableData()
    }
  },
  computed: {
    tableConfig () { // 属性透传
      return {
        stripe: true, // 默认显示表格条纹
        'row-key': 'id', // 唯一标识一条数据的字段名称,表格跨页多选的必须配置项
        ...this.$attrs // 支持传过来的属性覆盖默认属性值
      }
    },
    initSelection () { // 当表格数据或选中项发生改变时,重新初始化表格选中项
      const { tableData, selectItems } = this
      return { tableData, selectItems }
    }
  },
  watch: {
    staticData: {
      deep: true,
      immediate: true,
      handler (newVal) {
        if(!this.dataUrl) {
          this.tableData = newVal
	      this.total = newVal.length
          this.dataChange()
        }
      }
    },
    initSelection: {
      deep: true,
      immediate: true,
      handler (newVal) {
        const { tableData, selectItems } = newVal
        // 表格不支持选中数据、表格数据量少于选中的数据量(表格数据还没加载完)的情况下,不进行选中项初始化
        if(!this.showSelection || total < selectItems.length) return
        this.$nextTick(() => {
          this.toggleSelection(selectItems)
        })
      }
    },
    params: {
      deep: true,
      immediate: true,
      handler (newVal) {
        const { dataUrl } = this
        if(!dataUrl) {
          this.tableData = []
	      this.total = 0
          return
        }
        this.query = Object.assign({}, {
          pageNum: 1,
          pageSize: 10
        }, newVal)
        this.refreshTableData()
      }
    }
  },
  mounted () {
  },
  methods: {
    dataChange () { // 数据发生改变时触发
      this.$emit('dataChange', { 
        data: this.tableData,
        total: this.total
      })
    },
    toggleSelection (selection) { // 切换给定列表项的选中状态
      if (selection) {
        this.$nextTick(() => {
          selection.forEach(row => {
            this.$refs[this.tableKey].toggleRowSelection(row)
          })
        })
      } else {
        this.clearSelection();
      }
    },
    clearSelection () { // 清除全部选中项
      this.$nextTick(() => {
        this.$refs[this.tableKey].clearSelection()
      })
    },
    refreshTableData () { // 查询表格数据
      const { dataUrl, queryMethod, query, keyParams } = this
      for(let key of keyParams) { // 查询所必须的参数为空时,不查询数据
        let paramVal = query[key]
        if(typeof paramVal == 'undefined' || paramVal == null || paramVal == "") {
        	this.tableData = []
	      	this.total = 0
			this.dataChange()
			return
		}
      }
      console.log('在这里请求接口', dataUrl, queryMethod, query)
    },
    async currentChange (page) { // 翻页
      const { query } = this
      this.query = Object.assign({}, query, {
        pageNum: page
      })

      await this.refreshTableData()
      this.$emit('currentChange', page)
      this.$nextTick(() => {
        this.$refs[this.tableKey].bodyWrapper.scrollTop = 0
      })
    },
    async sizeChange (pageSize) { // 页面容量改变
      const { query } = this
      this.query = Object.assign({}, query, {
        pageNum: 1,
        pageSize
      })

      await this.refreshTableData()
      this.$emit('sizeChange', pageSize)
      this.$nextTick(() => {
        this.$refs[this.tableKey].bodyWrapper.scrollTop = 0
      })
    }
  }
}

</script>

<style lang="less" scoped>
#DataTable {
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	overflow: hidden;

	.DataTable-table {
		flex: 1;
		overflow: auto;

		.el-table::before {
			background-color: transparent;
		}
	}
	
	.DataTable-pagination {
		position: relative;
		left: 0;
		bottom: 0;
		height: 42px;
		padding-top: 12px;
		display: flex;
		flex-direction: row-reverse;
	}
}

</style>

你可能感兴趣的:(vue.js,前端,el-table)