【基于vue3 对element-plus Table二次封装,render函数实现自定义内容】

最近没有工作,写点小项目。写写笔记记录一下。
这里把element-plus的table进行二次封装,实现“显示/隐藏列”、“按钮插槽”以及实现类似iview框架直接的在表头里面写render函数。

1.封装效果

【基于vue3 对element-plus Table二次封装,render函数实现自定义内容】_第1张图片

2. 实现代码

A-table.vue
HTML部分


      <div class="table">
        <div style="padding: 0px 10px">
    
          <div style="float: left">
            <slot name="header-l">slot>
          div>
          <div  style="float: right">
            <slot name="header-r">slot>
            
            <div class="transfer">
              <el-popover placement="bottom" trigger="click" title="显示/隐藏列" width="340px">
                <template #reference>
                  <el-button :icon="Filter" size="mini">el-button>
                template>
                <el-checkbox-group v-model="checkedValue" @change="handleColumnsChange" style="height: 140px; overflow: auto">
                  <el-checkbox  v-for="item in checkColumns" :key="item.prop" :label="item.label">el-checkbox>
                el-checkbox-group>
              el-popover>
            div>
          div>
        div>
    

    
        <el-table
          :data="tableDataValue"
          v-loading="options.loading"
          :size="options.size"
          :max-height="options.maxHeight"
          :stripe="options.stripe"
          :border="options.border"
          :fit="options.fit"
          :highlight-current-row="options.heightCurrentRow"
          :lazy="options.lazy"
          ref="eleTable"
          @row-click="clickRow"
          @row-dblclick="dblclickRow"
          @row-contextmenu="contextmenu"
          @header-click="headClick"
          @header-contextmenu="headcontextmenu"
          @select="select"
          @select-all="selectAll"
          @current-change="rowChange"
          @selection-change="handleSelectionChange">
          <template v-for="(item, index) in filterColumns" :key="index">
            
            <el-table-column v-if="item.type === 'selection'" type="selection" :width="item.minWidth?item.minWidth : 60" >el-table-column>
            

            
            <el-table-column v-else-if="item.type === 'index'" type="index" :label="item.label?item.label : '序号'" :width="item.minWidth?item.minWidth : 80" :align="item.align?item.align : 'center'">el-table-column>
            

            
            <slot v-else-if="item.slot" :name="item.slot" :tit="index">slot>
            

            
            <el-table-column v-else
              :prop="item.prop"
              :label="item.label"
              :align="item.align"
              :min-width="item.minWidth"
              :show-overflow-tooltip="true"
            >
              <template #default="scope">
                <template v-if="!item.render">
                  <span>{{scope.row[item.prop]}}span>
                template>
                <template v-else>
                  <E-expand :column="item" :row="scope.row" :render="item.render" :index="scope.$index" />
                template>
              template>
            el-table-column>
          template>
        el-table>
    

    
        <div style="text-align: right">
          <el-pagination
            v-model:currentPage="pageNum"
            :page-size="mPageSize"
            :page-sizes="pageSizeOpts"
            layout="total, sizes, prev, pager, next, jumper"
            :total="total"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange">
          el-pagination>
        div>
    
      div>

JS部分

<script>
import { computed, getCurrentInstance, onMounted, reactive, toRefs, watch,toRaw } from 'vue'
import {useStore} from 'vuex'
import {Filter, ElMessage} from '@element-plus/icons-vue'
import EExpand from './eExpand'
export default {
  components: {EExpand },
  name: 'IviewVue3JsATable',
  props: {
    config: {type: Object},
    action: {type: String}, // 请求地址
    tableHeight: {type: Number},
    size: {type: String},
    pageSize: {type: Number, default: 10},
    options: { // 表格配置项
      type: Object,
      default: () => {
        return {
          // height: 400,
          stripe: false, // 斑马纹
          highlightCurrentRow: true, // 是否要高亮当前行
          border: false, // 是否有纵向边框
          fit: false, // 列的宽度是否自撑开
          size: 'medium', // Table 的尺寸
          maxHeight: 600, // Table 的最大高度。 
          loading:false,//是否需要等待效果
          lazy:false,// 是否需要懒加载
        }
      }
    },
  },
  setup(props, context) {
    const store = useStore()
    const {proxy} = getCurrentInstance()

    const state = reactive({
      isRowChange: false, // 表格列是否改变
      checkedValue: [], // 复选框已选数据
      tableDataValue: [],
      total: 0, // 表格数据总数
      mPageSize: props.pageSize,
      pageNum: 1,
    })    

/**复选框参数与配置 (START) */
    // 复选框选择项
    const checkColumns = computed(() => {return props.config.table.columns.filter(i => i.label)})
    // 复选框已选项 -model
    const checked = computed(() => {return proxy.checkColumns.map((i, index) =>{ return i.label})})
/**复选框参数与配置 (END) */

    onMounted(() => {
      getData()
      state.checkedValue = checked.value
    })
/**表格参数和配置 (START) */
    const tableConfig = computed(() => {return props.config.table}).value
    // 筛选后的表头
     const filterColumns = computed( () => {
      if(!state.isChange) return props.config.table.columns
      return props.config.table.columns.filter( i => !i.type &&  state.checkedValue.includes(i.label) || i.type)})
/**表格参数和配置 (END) */

/**每页展现条数选择项 */
    const pageSizeOpts = computed(() => {return [...new Set([10,20,30,40, proxy.pageSize])].sort((a, b) => a-b)})

    const getData = async () => {
      if(!props.action) return 
      props.options.loading = true
      const { data, err} = await store.dispatch(props.action, {pageNum: state.pageNum, pageSize: state.mPageSize, ...proxy.formObj.formData()})
      props.options.loading = false
      if (err) return ElMessage({showClose: true,message: err,type: 'error',center: true,duration: 3000})
      state.tableDataValue = data.records
      state.total = data.total
    }

// 表头筛选触发事件
    const handleColumnsChange = (value) => {
      state.isChange = true
      state.value = value
    }
// 单击行事件
    const clickRow = (row, column, event) => {
      let data = {row,column,event}
      context.emit('clickRow', data)
    }
// 双击行事件
    const dblclickRow = (row, column, event) => {
      let data = {row,column,event}
       context.emit('dblclickRow', data)
    }
// 右击行事件
    const contextmenu = (row, column, event) => {
      let data = {row,column,event}
       context.emit('contextmenu', data)
    }
// 头部列点击事件
    const headClick = (row, column, event) => {
      let data = {row,column,event}
       context.emit('headClick', data)
    }
// 头部列右击事件
    const headcontextmenu = (row, column, event) => {
      let data = {row,column,event}
       context.emit('headcontextmenu', data)
    }
// 勾选复选框事件
    const select = (row, column, event) => {
      let data = {row,column,event}
       context.emit('select', data)
    }
// 勾选全选事件
    const selectAll = (row, column, event) => {
      let data = {row,column,event}
       context.emit('selectAll', data)
    }
// 行改变事件
    const rowChange = (row, column, event) => {
      let data = {row,column,event}
       context.emit('rowChange', data)
    }
// 多行选中事件
    const handleSelectionChange = (row, column, event) => {
      let data = {row,column,event}
       context.emit('handleSelectionChange', data)
    }
// 页面展现条数事件
    const handleSizeChange = (pageSize) => {
      proxy.mPageSize = pageSize
      getData()
      console.log('pageSize :>> ', pageSize);
    }
// 页面跳转事件
    const handleCurrentChange = (pageNum) => {
      proxy.pageNum = pageNum
      getData()
      console.log('pageNum :>> ', pageNum);
    }
    const handleEdit = (index, row) => {
      console.log('scope', toRaw(row))
    }
    return {
      Filter,
      ...toRefs(state),
      tableConfig,
      checkColumns,
      filterColumns,
      pageSizeOpts,
      handleEdit,
      clickRow,
      dblclickRow,
      contextmenu,
      headClick,
      headcontextmenu,
      select,
      selectAll,
      rowChange,
      handleSelectionChange,
      handleColumnsChange,
      handleSizeChange,
      handleCurrentChange
    }
  }
}
</script>

E-expand.js

import { h,getCurrentInstance,defineComponent } from 'vue';
export default defineComponent({
  name: 'Expand',
    props: {
        row: Object,
        render: Function,
        index: Number,
        column: {
            type: Object,
            default: null
        }
    },
    // proxy: getCurrentInstance(),
    render() {
        return this.render(this.row)
    }
})

3.调用方法

引入组件

import {ATable} from '_c/table'

调用组件
html部分

<a-table action="business/getApplyEcectionList" :config="config" size="small" @row-change="rowChange">
    <template #header-l>
       <el-button type="primary" size="mini" @click="addApplyVisible = true" >新增el-button>
     template>
 a-table>

js部分 – 定义数据

const state = reactive({
    config: {
       table: {
         columns: [
           {type: 'selection', minWidth: 40, },
           {label: '出差人', prop: 'name', minWidth: 100,align: 'center'},
           {label: '部门名称', prop: 'department', minWidth: 100,align: 'center'},
           {label: '项目名称', prop: 'project', minWidth: 140,align: 'center'},
           {label: '目的地', prop: 'destination', minWidth: 200,align: 'center'},
           {label: '出发时间', prop: 'startTime', minWidth: 180,align: 'center'},
           {label: '出差天数', prop: 'day', minWidth: 100,align: 'center'},
           {label: '出行工具', prop: 'TravelTool', minWidth: 100,align: 'center'},
           {label: '流程状态', prop: 'status', minWidth: 100,align: 'center',
             render: (row) =>
             <div>{row.status}</div>
           },
           {label: '操作栏', prop: 'action', minWidth: 120,align: 'center',
           render: () => 
             <div>
               <el-button type="text">详情</el-button>
               <el-button type="text">审批进度</el-button>
             </div>
           },
         ],
       },
     },
  })

实现的代码部分是上面部分。另外说说有些没解决的细节部分吧。(主要是太懒)

4.缺陷部分

1. 在对表格列进行显示隐藏操作时: 表格会出现抖动。
2. 在对表格列进行显示隐藏操作时: 表格列宽不会自适应的填满容器。

如果各位优秀的朋友们,有解决缺陷部分的思路或者对封装有新的想法,可以告诉我。

你可能感兴趣的