vue二次封装ElementUI 的table组件

1,320 阅读1分钟

vue二次封装ElementUI 的table组件

  • 为了快速开发,便捷书写

配置简介

  1. 直接给data 和 config,可以展示数据
  2. 给分页,和分页的接口方法,可以一键配置不用管, 目前分页参数固定,可以根据具体项目修改
  3. 如果有额外的请求条件,或者是搜索后展示,需要给queryParams
  4. 暂时只有select input link内置type,可以使用render自定义,亦可以自己增加额外的case

config

  1. key列的prop, label列表头显示
  2. render可以自定义渲染表身, 他陪type: custom 使用
  3. renderHeader 可以自定义渲染表头
  4. type是配置可编辑的表格使用, 如input,select,后期可以增加
  5. props 里的 disable,show,editable 为函数,参数是scope,返回boolean
  6. props是配置type后,使用的额外属性,只给可以编辑的dom使用, 如对input配置size,placeholder,select配置options都在这里使用
  7. 支持其他对el-table的所有属性和方法,支持对el-table-column所有属性,方法暂时没做
height
  1. 这个可选属性,如果不传,则默认到父组件的父级100%,table的高度是减去分页组件的高度
config = [
  {
    key: 'name',
    label: '姓名',
    type: 'custom',
    renderHeader: (h, data) => {
      return `姓名${data.row.$index}`
    },
    render: (h, data) => {
      return `张三${data.row.$index}`
    },
    {
      key: 'age',
      label: '年龄',
      type: 'input',
      props: {
        size: 'mini'
      }
    },
    {
      key: 'sex',
      label: '性别',
      type: 'select',
      props: {
        options: [
          {
            label: '男',
            value: '1'
          },
          {
            label: 'nv',
            value: '2'
          }
        ]
      }
    },
    {
      key: 'status',
      label: '状态',
      formatter: (row, column, cellValue, index) => { // 原生方法
        return index%2 ? '启用' : '未启用'
      }
    }
  }
]

组件代码

<script>
export default {
  name: 'PTable',

  model: {
    prop: 'data',
    event: 'change'
  },

  props: {
    config: { // table的表格配置
      type: Array,
      required: true
    },

    data: { // table的数据
      type: Array,
      required: true
    },

    editable: { // 是否可编辑
      type: Boolean,
      default: true
    },

    height: { // table表格的高度
      type: Number
    },

    hasPagination: { // 是否有分页,暂时全部真分页
      type: Boolean,
      default: false
    },

    queryParams: { // 搜索的参数, 需要附加给table的额外参数
      type: Object,
      default: () => ({})
    },

    queryApi: { // 如果分页的话,一定要传,仅展示,可以直接给数据
      type: Function
    }
  },

  data() {
    return {
      loading: false,

      currentData: JSON.parse(JSON.stringify(this.data)),

      pageSizes: [10, 25, 30, 50],
      currentPageSize: 10,
      currentPage: 4,
      pageLayout: 'total, sizes, prev, pager, next, jumper',
      pageTotal: 400
    }
  },

  created() {
    this.getTableData()
  },

  mounted() {
  },

  methods: {
    handleTableChange(key, eventType, value) {
      this.$emit('change', JSON.parse(JSON.stringify(this.currentData)))
      this.$emit('table-change', key, eventType, value)
    },

    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.currentPageSize = val;
      this.getTableData();
    },

    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.currentPage = val;

      this.getTableData();
    },

    getTableData() {
      if (!this.queryApi) return
      let params = {};
      params.pageNum = this.currentPage;
      params.pageSize = this.currentPageSize;
      Object.assign(params, this.queryParams);

      this.loading = true;
      this.queryApi(params).then(response => {
        this.currentData = response.result_data;
        this.pageTotal = response.count;
      }).finally((error)=>{
        this.loading = false;
      })
    }
  },

  render(h) {
    const renderItem = (scope, data) => {
      const { key, label, type, width, props, render, ...prop } = data
      const canEdit = this.editable === false ? this.editable : (props.editable ? props.editable(scope) : true)
      const isShow = props.show ? props.show(scope) : true // 暂时业务没有用,留着
      const isDisable = props.disable ? props.disable(scope) : false

      let ItemTemplate = ''
      if (canEdit) {
        switch (type) {
          case 'input':
            ItemTemplate = <el-input v-model={scope.row[key]} placeholder={props.placeholder || '请输入'} size="mini" rows={props.rows} {...{props}} disabled={isDisable} onInput={() => this.handleTableChange(key, 'input', scope.row[key])} onBlur={() => this.handleTableChange(key, 'blur', scope.row[key])}></el-input>
            break;
          case 'link':
            ItemTemplate = <el-link {...{props: props}} disabled={isDisable} onClick={() => props.click(scope)}>
              {scope.row[key]}
            </el-link>
            break;
          case 'select':
            ItemTemplate = <el-select v-model={scope.row[key]} disabled={isDisable} placeholder={props.placeholder || '请输入'} size="mini" {...{props}} onChange={() => this.handleTableChange(key, 'change', scope.row[key])}>
              {
                props.options.map((item, index) => {
                  return <el-option
                    key={index}
                    label={item.label}
                    value={item.value}>
                  </el-option>
                })
              }
            </el-select>
            break;
          case 'custom':
            ItemTemplate = render(h, scope)
            break;
          default:
            ItemTemplate = <span>{scope.row[key]}</span>
            break;
        }
      } else {
        ItemTemplate = <span>{scope.row[key]}</span>
      }
      return ItemTemplate
    }

    const renderChildren = (data) => {
      const { key, label, type, width, props, children, ...prop } = data

      let PROPS = {}
      PROPS.type = type;
      PROPS.prop = key;
      PROPS.width = width;
      PROPS.label = label;
      PROPS.headerAlign = prop.headerAlign || 'center';

      let ScopeS = {}

      if (type === 'selection' || type === 'index') {
        PROPS.align = 'center'
      } else if (!type) {
        Object.assign(PROPS, prop)
      } else {
        Object.assign(PROPS, prop)
        ScopeS = {
          scopedSlots: {
            default: (scope) => {
              return renderItem(scope, data)
            }
          }
        }

        if (prop.renderHeader) {
          ScopeS.scopedSlots.header = (scope) => {
            return prop.renderHeader(h, scope)
          }
        }
      }

      for (let [key, value] of Object.entries(PROPS)) {
        (value == undefined) && delete PROPS[key]
      }

      return <el-table-column
        {...{props: PROPS}}
        {...ScopeS}>
        { children && children.length ? children.map(item => renderChildren(item)) : '' }
      </el-table-column>
    }

    const tableHeight = this.height ? (this.height - (this.hasPagination ? 60 : 0) + 'px') : '0';

    return(<div class="p-table-area" loading={this.loading}>
      <section class="table-area" style={{ 'height': tableHeight }}>
        <el-table
          height="100%"
          {...{props: this.$attrs}}
          {...{on: this.$listeners}}
          data={this.currentData}>
          {
            this.config.map((item) => {
              return renderChildren(item)
            })
          }
        </el-table>
      </section>

      {
        this.hasPagination && <section class="page-area">
          <el-pagination
            onSizeChange={this.handleSizeChange}
            OnCurrentChange={this.handleCurrentChange}
            currentPage={this.currentPage}
            pageSizes={this.pageSizes}
            pageSize={this.currentPageSize}
            layout={this.pageLayout}
            total={this.pageTotal}>
          </el-pagination>
        </section>
      }
    </div>)
  }
}
</script>

<style lang="scss" scoped>
.p-table-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 100%;

  .table-area {
    flex: 1;
  }

  .page-area {
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
  }
}
/deep/.el-table td {
  padding: 4px 0;
}

/deep/.el-table th {
  padding: 8px 0;
}
</style>