vue2-ProTable-列设置、搜索封装、api封装

之前使用vue3,和别人一起写了个小项目,然后参考了社区里别人的一些优秀的组件风格, 包括vue3的 ProTable, 功能强大, 且使用了很多的 hooks, 特别是一些表格的 比如 列设置的功能、全屏功能、表格里面插槽的功能、搜索表单的 方法、插槽、验证等都做得很全。 但最近要使用 vue2的项目,也同样需要用到这里面的一些功能, 在论坛和 git 上找了半天也没找到有人封装的全一点。最后只能自己实现一下。

使用技术 vue2 + element-ui + vuex

功能

首先介绍一下我需要的是一个什么样的功能的。

  • 搜索部分的封装: 包含展开收起功能、查询重置功能、搜索部分正常表单组件的封装(input、inputNumber、select、date等等),搜索参数的二次处理
  • 表格功能区封装: 包含左侧按钮部分(多选操作,导入导出等),右侧功能部分(列设置,全屏等)
  • 表格内容 支持插槽、操作部分的编辑删除回调函数
  • api集成,表格查询接口集成到组件里
  • 分页功能

看一下效果 上面这个状态, 这个小红点其实如果使用 antd 组件库的话,是有徽标组件的,但是 el 是没有徽标组件的,这里我也是自己封装了一下。

结构

  • 架子这里我是直接找了一个,复用了里面的工程结构及目录结构。

我这个组件封装在了 ProTable 里面, 下面会拆解代码进行介绍,最后会放一下这个demo的 git 大家可以自行查看, 我这里只是基于我现有的业务需求进行了一些封装,大家有新的想法或者意见可以留言, 俺一定会回复的

table

上面是封装的 table 组件的代码,我没有把 tableform 单独再拆分出去, 之前使用vue3的时候, 是拆分了的, 这里满足自己业务功能就没有再拆分了。 这里有几点注意事项

  • 列设置这里是可以有一个拖拽改变顺序的功能, 在注释的那段 el-tree 中, api:allow-drop="allowDrop"
  • table 里面的 column 后面有更改
js 复制代码
 <el-table
    ref="table"
    :key="tableKey"
    v-loading="tableLoading"
    :data="tableData"
    :row-style="{ height: '40px' }"
    :cell-style="{ }"
    :header-cell-style="{ height: '40px', padding: 0, background: '#f6f8fa', color: '#333' }"
    size="mini"
    tooltip-effect="dark"
    v-bind="tableProps"
    @row-click="handelTableClick"
    @selection-change="handleSelectionChange"
    @header-dragend="surverWidth"
   >
    <template
      v-for="item in columnsHanlder(columns)"
    >
      <template v-if="item.show && item.prop === 'selection'">
        <el-table-column :key="item.prop" align="center" :resizable="false" type="selection" />
      </template>
      <template v-else-if="item.show && item.prop === 'actions'">
        <!-- 固定列 -->
        <el-table-column :key="item.prop" fixed="right" align="center" :label="item.label ? 'item.label' : '操作'" :width="item.width" :resizable="false">
          <div slot-scope="scope" class="col-slot">
            <slot name="actions" :scope="{...scope}" />
          </div>
        </el-table-column>
      </template>
      <template v-else>
        <el-table-column
          v-if="item.show"
          :key="item.prop"
          align="center"
          show-overflow-tooltip
          :prop="item.prop"
          :sortable="item.sortable"
          :label="item.label"
          :width="item.width"
          :resizable="false"
        >
          <template slot-scope="scope">
            <span v-if="item.slot">
              <slot :name="item.slot" :scope="scope" />
            </span>
            <span v-else-if="item.type === 'time' && scope.row[item.prop]">{{ dayjs(scope.row[item.prop]).format('YYYY年MM月DD日') }}</span>
            <span v-else>{{ scope.row[item.prop] || '-' }}</span>
          </template>
        </el-table-column>
      </template>
    </template>
  </el-table>

这里使用了 columnsHanlder 方法,来对传入的 columns 进行一次过滤, 给默认参数进行赋值, 同时给 多选 和 操作部分进行了分离, 操作部分需要传入插槽, 然后会把对应这一行的 scope 作为插槽的 slot-scope 传回给父组件。 这里我们拿到的就是上面这个东西啦, 包含这一行的 index, column, row 等信息, 其中 row 就是我们这一行的数据, 注意使用的时候不要修改原数据哦, 编辑成功以后的正常逻辑是刷新~

  • 在下面的 el-table-column 中,我们首先判定是否有插槽,如果有插槽则直接渲染插槽, 如果没有插槽这里就可以做一些通用的处理,比如时间格式的转换,没有数据时候的默认显示等等。
    这里需要注意一点的就是 在我们使用列设置的时候, 因为 el 的固定列的特性, 当列发生改变的时候, 固定列的高度会掉下去一小部分
js 复制代码
  // 解决 自定义列 + 固定列 导致显示问题  参考 https://blog.csdn.net/qq_36126031/article/details/121970398
  updated() {
    this.$refs.table.doLayout()
  },

SearchFormItem

这里我们在 ProTable 组件中就是这样喽, 这里还是一个正常的 form 表单来处理, 这里我没有封装验证规则, 大家有需求的话可以提一下子,因为搜索的验证我觉得没啥意义,输入错了搜不出来数据就完事了,又不是提交表单~, 这里每一个 item 使用 SearchFormItem 进行了封装, 展开收起使用了的就是 一个 防抖函数来触发动态侦听 form 高度的改变

js 复制代码
// 页面初始化时候调用,以及窗口size调整 以及侧边菜单变化时调用
toShowMore: debounce(function() {
  this.$nextTick(() => {
    if (!this.$refs.FormRef) return
    const formHeight = window.getComputedStyle(this.$refs.FormRef.$el)
      .getPropertyValue('height')
      .replace('px', '')
    if (formHeight < 50) {
      this.showMoreButton = false
    } else {
      this.showMoreButton = true
    }
  })
}, 300),

以下是封装的每一个 form ,使用动态组件的方式进行, 针对日期时间组件根据自己的业务进行了通用配置

js 复制代码
<template>
  <component
    :is="`el-${column.search.el}`"
    v-if="column.search && column.search.el"
    v-model="searchParam[column.search.key || handleProp(column.prop)]"
    range-separator="至"
    start-placeholder="开始时间"
    end-placeholder="结束时间"
    :data="column.search.el === 'tree-select' ? columnEnum : []"
    :options="['cascader', 'select-v2'].includes(column.search.el) ? columnEnum : []"
    :clearable="clearable"
    v-bind="handleSearchProps"
  >
    <template v-if="column.search.el === 'cascader'" v-slot="data">
      <span>{{ data[fieldNames.label] }}</span>
    </template>
    <template v-if="column.search.el === 'select'">
      <component
        :is="`el-option`"
        v-for="(col, index) in columnEnum"
        :key="index"
        :label="col[fieldNames.label]"
        :value="col[fieldNames.value]"
      />
    </template>
    <slot v-else />
  </component>
</template>

<script>
export default {
  props: {
    column: Object,
    searchParam: Object
  },
  computed: {
    fieldNames() {
      return {
        label: this.column.fieldNames && this.column.fieldNames.label ? this.column.fieldNames.label : 'label',
        value: this.column.fieldNames && this.column.fieldNames.value ? this.column.fieldNames.value : 'value'
      }
    },
    columnEnum() {
      return this.column.enum || []
    },
    handleSearchProps() {
      const label = this.fieldNames.label
      const value = this.fieldNames.value
      const searchEl = this.column.search && this.column.search.el
      const searchProps = this.column.search && this.column.search.props ? this.column.search.props : {}
      let handleProps = searchProps
      if (searchEl === 'tree-select') {
        handleProps = {
          ...searchProps,
          props: { label, ...searchProps.props },
          nodeKey: value
        }
      }
      if (searchEl === 'cascader') {
        handleProps = {
          ...searchProps,
          props: { label, value, ...searchProps.props }
        }
      }
      return handleProps
    },
    placeholder() {
      const search = this.column.search
      return search && search.props && search.props.placeholder
        ? search.props.placeholder
        : search && search.el === 'input'
          ? '请输入'
          : '请选择'
    },
    clearable() {
      const search = this.column.search
      return (
        search && search.props && search.props.clearable
          ? search.props.clearable
          : search && (search.defaultValue == null || search.defaultValue === undefined)
      )
    }
  },
  methods: {
    handleProp(prop) {
      const propArr = prop.split('.')
      if (propArr.length === 1) return prop
      return propArr[propArr.length - 1]
    }
  }
}
</script>

Pagination

  • 针对分页,我们可以使用了固定的一些配置, 同时也可以自己插入一个分页的插槽,插槽中 scope 返回的是分页信息, 父组件来改变这个分页信息来控制, 同时可以使用 子组件实例ref 上的 getTableList 方法进行表格数据更新。
js 复制代码
  <slot :scope="pageable" name="pagination">
    <Pagination
      v-if="pagination"
      :pageable="pageable"
      :handle-size-change="handleSizeChange"
      :handle-current-change="handleCurrentChange"
    />
  </slot>

上面是 ProTable 中使用, 下面是封装的

js 复制代码
<template>
<!-- 分页组件 -->
<el-pagination
  class="pagination"
  :current-page="pageable.pageNum"
  :page-sizes="[10, 25, 50, 100]"
  :page-size.sync="pageable.pageSize"
  :total="pageable.total"
  layout="total, sizes, prev, pager, next, jumper"
  :hide-on-single-page="true"
  @size-change="handleSizeChange"
  @current-change="handleCurrentChange"
/>
</template>
<!-- eslint-disable vue/require-default-prop -->
<script>

export default {
props: {
  pageable: Object,
  handleSizeChange: Function,
  handleCurrentChange: Function
}
}
</script>
<style lang="scss" scoped>
.pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 20px;
}
</style>

地址

相关推荐
AI悦创Python辅导11 分钟前
如何挑选适合项目场景的数据分析工具?
前端
用户92724725021914 分钟前
新闻自动采集并通过API发布到博客
前端·后端
清风920017 分钟前
Logback——日志技术(基础)
java·前端·logback
EndingCoder17 分钟前
排序算法与前端交互优化
开发语言·前端·javascript·算法·排序算法·交互
晓131319 分钟前
JavaScript加强篇——第五章 DOM节点(加强)与BOM
java·开发语言·javascript
三月的一天42 分钟前
在 React Three Fiber 中实现 3D 模型点击扩散波效果
前端·react.js·前端框架
爱敲代码的小冰42 分钟前
npm 切换 node 版本 和npm的源
前端·npm·node.js
DoraBigHead1 小时前
🧠【彻底读懂 reduce】acc 是谁?我是谁?我们要干嘛?
前端·javascript·面试
future14121 小时前
项目开发日记
前端·学习·c#·游戏开发
汪子熙1 小时前
CSS 中 td:last-child a 选择器详解
前端·javascript