Vue3+elementUI:强大好用的el-tabel+el-pagination封装,后台管理系统必备

vue2基础版本:juejin.cn/post/721624...

应用场景:

在编写后台管理系统时,常常会用到表格和翻页器,每次都需要重复的写很麻烦,并且普通表格只需要渲染对应的prop就可以了,完全可以用一个v-for判断去解决,因此一个自带翻页器的table组件就应运而生了

实现功能:

(带有新增的是和Vue2版本不同的)

  1. 在渲染表格时,只需要传递列表配置即可无脑渲染,无需疯狂cv然后改一些鸡毛蒜皮的小东西
  2. 根据字段判断是否自定义插槽,使用具名插槽去单独编译
  3. 分页器和翻页的方法都绑定到同一个方法,父组件接收一个渲染列表的方法即可
  4. emptyText字段可以自动填写当内容为空时的默认值(新增)
  5. 这里的分页器是单独封装了一个组件用于不需要表单的情况(选用了ruoyi的组件),如果没有对应的应用场景可以去掉(新增)
  6. 提供了size可以自定义修改组件样式大小等(新增)
  7. 可自动渲染序号(从1开始),并且自定义序号列的宽度(新增)
  8. 可修改行样式和单元格样式(新增)
  9. 高度可修改(新增)
  10. 新增多选和回写功能(新增)

实现代码

1. 分页器组件pagination的封装

js 复制代码
<template>
  <div :class="{ 'hidden': hidden }" class="pagination-container">
    <el-pagination
        :background="background"
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :layout="layout"
        :page-sizes="pageSizes"
        :pager-count="pagerCount"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
    />
  </div>
</template>

<script setup>
import {scrollTo} from '@/utils/scroll-to'

const props = defineProps({
  total: {
    required: true,
    type: Number
  },
  page: {
    type: Number,
    default: 1
  },
  limit: {
    type: Number,
    default: 10
  },
  pageSizes: {
    type: Array,
    default() {
      return [5, 10, 20, 30, 50]
    }
  },
  // 移动端页码按钮的数量端默认值5
  pagerCount: {
    type: Number,
    default: document.body.clientWidth < 992 ? 5 : 7
  },
  layout: {
    type: String,
    default: 'total, sizes, prev, pager, next, jumper'
  },
  background: {
    type: Boolean,
    default: true
  },
  autoScroll: {
    type: Boolean,
    default: true
  },
  hidden: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits();
const currentPage = computed({
  get() {
    return props.page
  },
  set(val) {
    emit('update:page', val)
  }
})
const pageSize = computed({
  get() {
    return props.limit
  },
  set(val) {
    emit('update:limit', val)
  }
})

function handleSizeChange(val) {
  if (currentPage.value * val > props.total) {
    currentPage.value = 1
  }
  emit('update:limit', val)
  emit('pagination', {page: currentPage.value, limit: val})
  if (props.autoScroll) {
    scrollTo(0, 800)
  }
}

function handleCurrentChange(val) {
  emit('update:page', val)
  emit('pagination', {page: val, limit: pageSize.value})
  if (props.autoScroll) {
    scrollTo(0, 800)
  }
}

</script>

<style scoped>
.pagination-container {
  background: #fff;
  padding: 32px 16px;
}

.pagination-container.hidden {
  display: none;
}
</style>

2.Table组件的封装

HTML部分
html 复制代码
<template>
  <div class="my-table" :class="`my-table--${size}`">
    <el-table
        :ref="tableRef || 'filterTable'"
        :row-class-name="tableRowClassName"
        v-loading="loading"
        :data="tableData"
        :border="border"
        :cell-style="cellStyle"
        :height="height"
        :max-height="maxHeight"
        tooltip-effect="dark"
        @selection-change="selectionChange"
        @sort-change="sortChange"
    >
      <el-table-column v-if="selection" type="selection" width="45" fixed align="center" :selectable="checkSelect">
      </el-table-column>
      <el-table-column v-if="showIndex" label="序号" :width="indexWidth" type="index" align="center">
        <template #default="scope">
          <span>{{ (currentPage - 1) * pageSize + scope.$index + 1 }}</span>
        </template>
      </el-table-column>
      <template v-for="(item, index) in columns">
        <!-- solt 自定义列-->
        <!--
        自定义列的使用方法:
        在使用插槽时#后跟slotType
        <template #createTime="scope">
        <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
        -->
        <template v-if="item.type === 'slot'">
          <el-table-column
              :class-name="item.class"
              :key="index"
              :width="item.width"
              :min-width="item.minWidth"
              :prop="item.prop"
              :label="item.label"
              :align="item.align ? item.align : 'center'"
              :fixed="item.fixed ? item.fixed : false"
          >
            <template #default="scope">
              <slot :name="item.slotType" :row="scope.row" :index="scope.$index" :column="scope.column"/>
            </template>
          </el-table-column>
        </template>
        <!--普通表格-->
        <template v-else>
          <el-table-column
              :show-overflow-tooltip="item.showOverflowTooltip === undefined ? true : item.showOverflowTooltip"
              v-if="item.visible !== false"
              :key="index"
              :sortable="item.sortable"
              :prop="item.prop"
              :label="item.label"
              :formatter="item.formatter || ((row)=>formatEmpty(row,item))"
              :width="item.width"
              :min-width="item.minWidth"
              :align="item.align ? item.align : 'center'"
              :fixed="item.fixed ? item.fixed : false"
          ></el-table-column>
        </template>
      </template>
    </el-table>
    <pagination
        v-if="showPagination"
        v-show="total > 0"
        :total="total"
        :autoScroll="autoScroll"
        :pagerCount="pagerCount"
        v-model:page="currentPage"
        v-model:limit="pageSize"
        :page-sizes="pageSizes"
        @pagination="handlePagination"
    />
  </div>
</template>
js部分(setup语法糖)
js 复制代码
<script setup>
const props = defineProps({
  // 表单数据
  tableData: {
    type: Array,
    default() {
      return []
    }
  },
  // 表单大小
  size: {
    type: String,
    default: 'normal'
  },
  // 表单ref
  tableRef: {
    type: String,
    default: ''
  },
  // 数据列表配置
  columns: {
    type: Array,
    default() {
      return []
    }
  },
  // 是否开启翻页时自动回到顶部
  autoScroll: {
    type: Boolean,
    default: true
  },
  // 序号的宽度
  indexWidth: {
    type: Number,
    default: 80
  },
  // 行样式
  tableRowClassName: {
    type: Function,
    default() {
      return () => {
      }
    }
  },
  // 移动端页码按钮的数量端默认值5
  pagerCount: {
    type: Number,
    default: document.body.clientWidth < 992 ? 5 : 7
  },
  // 分页
  pageSizes: {
    type: Array,
    default() {
      return [10, 20, 30, 50]
    }
  },
  // 边框
  border: {type: Boolean, default: false},
  // 高度
  height: {type: [Number, String], default: null},
  // 最大高度
  maxHeight: {type: [Number, String], default: null},
  // 加载状态
  loading: {type: Boolean, default: false},
  // 是否多选
  selection: {type: Boolean, default: false},
  // 单元格的 style 的回调方法
  cellStyle: {
    type: Function,
    default() {
      return () => {
      }
    }
  },
  // 是否展示翻页组件
  showPagination: {type: Boolean, default: true},
  // 是否展示序号
  showIndex: {type: Boolean, default: false},
  // 总数
  total: {
    type: Number,
    default: 20
  },
  // pagination的page
  page: {
    type: Number,
    default: 1
  },
  // pagination的limit
  limit: {
    type: Number,
    default: 10
  },
  // 查询是否能选中的数据
  checkData: {
    type: Array,
    default() {
      return []
    }
  }
})
const emit = defineEmits();
const currentPage = computed({
  get() {
    return props.page
  },
  set(val) {
    emit('update:page', val)
  }
})
const pageSize = computed({
  get() {
    return props.limit
  },
  set(val) {
    emit('update:limit', val)
  }
})
const formatEmpty = (row, item) => {
  return row[item.prop] || row[item.prop] === 0 ? row[item.prop] : (item.emptyText || '')
}

function checkSelect(row) {
  let list = props.checkData
  let isSelect = true
  for (let i of list) {
    if (row.id === i.id) {
      isSelect = false
      return
    }
  }
  return isSelect
}

function selectionChange(selection) {
  emit('selectionChange', selection)
}

function sortChange(filters) {
  emit('sortChange', filters)
}


function handlePagination(data) {
  if (data.page !== currentPage.value || data.limit !== props.limit) {
    emit('update:page', data.page)
    emit('update:limit', data.limit)
    emit('pagination', data)
  }
}

</script>

使用方法

示例

使用序号
html 复制代码
<hc-table :tableData="activityList" :columns="columns" :total="total"
          v-model:page="queryParams.pageNum"
          v-model:limit="queryParams.pageSize"
          show-index
          :loading="loading"
          @pagination="getList"
>
  <template #activityTime="scope">
    {{ scope.row.startTime }} - {{ scope.row.endTime }}
  </template>
  ....
  </template>
</hc-table>

其中activityList为列表数据,total,page,limit和正常使用分页器一样传值,show-index为展示序号,loading为加载状态,getList为翻页方法,columns数据结构如下

js 复制代码
// 主要列信息
const columns = [
  // 默认展示prop中的值
  {prop: 'name', label: '活动名称'},
  // 自定义列 展示slotType插槽中的值
  {type: 'slot', slotType: 'activityTime', label: '活动时间', width: '165'},
  ...
]
使用默认值

当对应字段中没有值(null,'',undefined)的时候,默认展示emptyText中的内容

js 复制代码
// 主要列信息
const columns = [
  {prop: 'time', label: '成团时间',emptyText:'--'},
  ...
]
相关推荐
用户461169657286 分钟前
网站 HTTPS 不稳定?证书排查指南
前端
n876518 分钟前
Playwright Java WEB UI 自动化实现框架与逻辑
java·前端·ui
keyipatience20 分钟前
TypeScript学习(1)
前端·javascript·学习·typescript·html·css3
xifangge20251 小时前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
wangruofeng1 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
honkun61 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js
爱勇宝2 小时前
研究数字孪生的这半年,做了个 Demo,也有了一些新想法
前端·程序员
不可能片场2 小时前
工程实践 一个缓存把选片从十分钟压到一分钟
前端·electron
前端小万2 小时前
探索副业五个月,最终以放弃告终
前端·后端·面试
FRing032 小时前
JavaScript入门到精通二十四(正则表达式、元字符、修饰符)
开发语言·前端·javascript·正则表达式