likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 16

应用场景:

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

实现功能:

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

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

实现代码

1. 分页器组件pagination的封装

<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部分
<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语法糖)
<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>

使用方法

示例

使用序号

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

<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数据结构如下

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

Vue3+elementUI:强大好用的el-tabel+el-pagination封装,后台管理系统必备 当对应字段中没有值(null,'',undefined)的时候,默认展示emptyText中的内容

// 主要列信息
const columns = [
  {prop: 'time', label: '成团时间',emptyText:'--'},
  ...
]