跨境派

跨境派

跨境派,专注跨境行业新闻资讯、跨境电商知识分享!

当前位置:首页 > 卖家故事 > Vue3 + Element Plus 封装公共表格组件(带源码)

Vue3 + Element Plus 封装公共表格组件(带源码)

时间:2024-03-26 17:31:11 来源:网络cs 作者:纳雷武 栏目:卖家故事 阅读:

标签: 表格  公共 
阅读本书更多章节>>>>

文章目录

1 前言2 功能3 实现步骤3.1 复制基本表格3.2 支持自动获取表格数据3.3 支持数据列配置及插槽3.3.1 自动生成列3.2.2 支持表头自定义及插槽3.2.3 支持单元格自定义及插槽 3.3 支持操作列配置及插槽3.4 支持多选框配置3.5 支持表尾配置及插槽3.6 支持分页显示 4 使用方法5 源码

1 前言

由于项目中有很多菜单都是列表数据的展示,为避免太多重复代码,故将 Element Plus 的 Table 表格进行封装,实现通过配置展示列表数据

2 功能

在这里插入图片描述

支持自动获取表格数据支持数据列配置及插槽支持操作列配置及插槽支持多选框配置支持表尾配置及插槽支持分页显示

3 实现步骤

3.1 复制基本表格

到 Element Plus官网复制一份最简单的 Table 代码,并删除多余代码

<template>  <el-table :data="tableData">    <el-table-column prop="date" label="Date" />    <el-table-column prop="name" label="Name" />    <el-table-column prop="state" label="State" />    <el-table-column prop="city" label="City" />    <el-table-column prop="address" label="Address" />    <el-table-column prop="zip" label="Zip" />    <el-table-column fixed="right" label="Operations">      <template #default>        <el-button link type="primary" size="small" @click="handleClick">Detail</el-button>      </template>    </el-table-column>  </el-table></template><script lang="ts" setup>const tableData = [  {    date: '2016-05-03',    name: 'Tom',    state: 'California',    city: 'Los Angeles',    address: 'No. 189, Grove St, Los Angeles',    zip: 'CA 90036',    tag: 'Home'  },  {    date: '2016-05-02',    name: 'Tom',    state: 'California',    city: 'Los Angeles',    address: 'No. 189, Grove St, Los Angeles',    zip: 'CA 90036',    tag: 'Office'  },  {    date: '2016-05-04',    name: 'Tom',    state: 'California',    city: 'Los Angeles',    address: 'No. 189, Grove St, Los Angeles',    zip: 'CA 90036',    tag: 'Home'  },  {    date: '2016-05-01',    name: 'Tom',    state: 'California',    city: 'Los Angeles',    address: 'No. 189, Grove St, Los Angeles',    zip: 'CA 90036',    tag: 'Office'  }]const handleClick = () => {  console.log('click')}</script>

3.2 支持自动获取表格数据

tableData 数据改为从 props.api 接口获取

在这里插入图片描述

3.3 支持数据列配置及插槽

3.3.1 自动生成列

数据列(除多选框与操作列外)通过 props.columns 自动生成

在这里插入图片描述

<el-table-column  v-for="item in props.columns"  :key="item.prop"  :prop="item.prop"  :label="item.label"  :sortable="item.sortable ? 'custom' : false"  :width="item.width"></el-table-column>
interface TableConfigInterface {  api: string // 表格数据获取接口  columns: {    // 显示列    prop: string // 键名    label?: string // 表头显示名称    formatter?: (row: unknown) => string // 自定义单元格格式化方法,参数为当前行数据    tooltip?: string // 表头 tooltip    sortable?: boolean // 是否可以排序    width?: number | string // 宽度    style?: string // 单元格样式    labelStyle?: string // 表头样式  }[]}
3.2.2 支持表头自定义及插槽
el-table-column 使用 <template #header> 自定义表头<slot :name="item.prop + 'Header'"> 支持插槽labelStyle 支持样式自定义支持 tooltip
<el-table-column  v-for="item in props.columns"  :key="item.prop"  :prop="item.prop"  :label="item.label"  :sortable="item.sortable ? 'custom' : false"  :width="item.width">  <template #header>    <slot :name="item.prop + 'Header'">      <div class="inline-flex" :style="item.labelStyle">        <span>{{ item.label }}</span>        <el-tooltip          popper-class="table-tooltip"          effect="dark"          placement="top-start"          :content="item.tooltip"          v-if="item.tooltip"        >          <el-icon><i-ep-Warning /></el-icon>        </el-tooltip>      </div>    </slot>  </template></el-table-column>
3.2.3 支持单元格自定义及插槽
el-table-column 使用 <template #default="scope"> 自定义单元格<slot :name="item.prop" :row="scope.row"> 支持插槽style 属性支持自定义样式formatter 方法支持自定义显示内容
<el-table-column  v-for="item in props.columns"  :key="item.prop"  :prop="item.prop"  :label="item.label"  :sortable="item.sortable ? 'custom' : false"  :width="item.width">  <template #header>    <slot :name="item.prop + 'Header'">      <div class="inline-flex" :style="item.labelStyle">        <span>{{ item.label }}</span>        <el-tooltip          popper-class="table-tooltip"          effect="dark"          placement="top-start"          :content="item.tooltip"          v-if="item.tooltip"        >          <el-icon><i-ep-Warning /></el-icon>        </el-tooltip>      </div>    </slot>  </template>  <template #default="scope">    <slot :name="item.prop" :row="scope.row">      <div :style="item.style">        <span v-if="item.formatter">{{ item.formatter(scope.row) }}</span>        <span v-else>{{ scope.row[item.prop] }}</span>      </div>    </slot>  </template></el-table-column>

3.3 支持操作列配置及插槽

el-table-column 使用 <template #default="scope"> 自定义操作列<slot :name="item.prop" :row="scope.row"> 支持插槽visible 方法支持自定义按钮显示逻辑
<el-table-column  fixed="right"  label="操作"  :width="props.operation?.width"  v-if="props.operation?.columns">  <template #default="scope">    <slot name="operations" :row="scope.row">      <span v-for="item in props.operation?.columns" :key="item.text || item.icon">        <el-button          v-if="setVisible(scope.row, item.visible)"          :type="item.type"          :link="item.link"          :plain="item.plain"          @click="item.click(scope.row)"          size="small"          class="margin-right: 4px"        >          <el-icon v-if="item.icon" :class="item.icon"></el-icon>          {{ item.text }}        </el-button>      </span>    </slot>  </template></el-table-column>
// 操作框逻辑const setVisible = (row: unknown, visible?: (row: unknown) => boolean) => {  if (!visible || visible(row)) {    return true  }  return false}

3.4 支持多选框配置

<el-table>增加 selection 列

<el-table-column fixed :selectable="setSelectable" type="selection" v-if="showSelectBox" />

TableConfigInterface 增加 rowKey、selectable

interface TableConfigInterface {  // ......  rowKey?: string // 行数据的 Key  selectable?: boolean | ((row: unknown) => boolean) // 当前行多选框是否可以勾选,参数为当前行数据,默认为 false} const props = withDefaults(defineProps<TableConfigInterface>(), {  rowKey: 'id',})// 多选框逻辑const disabledList = reactive<string[]>([]) // 禁止勾选的数据const showSelectBox = computed(() => props.selectable && disabledList.length < tableData.length)const setSelectable = (row: unknown) => {  const selectable =    typeof props.selectable === 'boolean' ? props.selectable : props.selectable?.(row)  if (!selectable && !disabledList.includes(row?.[props.rowKey])) {    disabledList.push(row?.[props.rowKey])  }  return selectable}

3.5 支持表尾配置及插槽

<el-table>增加 @selection-change 及 ref 配置<slot name="footer"> 支持插槽visible 方法支持自定义按钮显示逻辑
<el-table  v-loading="loading"  :data="tableData"  @selection-change="handleSelectionChange"  table-layout="auto"  ref="tableRef">  <!-- ...... --></el-table><div v-if="showSelectBox" class="p-14">  <el-checkbox    v-model="isSelected"    @click="tableRef?.toggleAllSelection()"    :indeterminate="indeterminate"    label="全选"    style="vertical-align: middle; margin-right: 10px"  />  <slot name="footer" :rows="selectionRows">    <span v-for="item in props.footer?.operations" :key="item.text || item.icon">      <el-button        v-if="item.visible ? item.visible() : true"        :type="item.type || 'primary'"        :link="item.link"        :plain="item.plain"        :disabled="!selectionRows.length"        @click="item.click(selectionRows)"        style="margin-left: 10px"      >        <el-icon v-if="item.icon" :class="item.icon"></el-icon>        {{ item.text }}      </el-button>    </span>  </slot></div>
const tableRef = ref()const isSelected = ref(false) // 是否有选中数据const selectionRows = ref<unknown[]>([]) // 当前选中的数据const handleSelectionChange = (rows: unknown[]) => {  selectionRows.value = rows  isSelected.value = rows.length > 0}const indeterminate = computed(  () =>    selectionRows.value.length > 0 &&    selectionRows.value.length < tableData.length - disabledList.length)

3.6 支持分页显示

最底部增加<el-pagination></el-pagination>

<el-pagination  background  :total="tableData.length"  :layout="props.layout"  v-model:current-page="pagination.currentPage"  v-model:page-size="pagination.pageSize"  @current-change="getTableData"  @size-change="getTableData"  class="p-y-20"></el-pagination>
interface TableConfigInterface {  // ......  layout?: string // 组件布局} const pagination = ref({  currentPage: 1,  pageSize: 10})

4 使用方法

<template>  <TableComponent v-bind="tableConfig">    <template #nameHeader>      <div>姓名</div>    </template>    <template #name>      <div>Yana</div>    </template>  </TableComponent></template><script setup lang="ts">const tableConfig: TableConfigInterface = {  api: 'getTableData',  columns: [    {      prop: 'date',      label: 'Date',      tooltip: 'This is Date'    },    {      prop: 'name',      label: 'Name'    },    {      prop: 'state',      label: 'State'    },    {      prop: 'city',      label: 'City'    },    {      prop: 'address',      label: 'Address'    },    {      prop: 'zip',      label: 'Zip',      style: 'color: red',      labelStyle: 'color: red',      sortable: true    }  ],  operation: {    columns: [      {        text: '编辑',        click: () => {},        visible: (row) => row.date === '2016-05-07'      }    ]  },  rowKey: 'date',  selectable: (row) => row.date === '2016-05-07',  footer: {    operations: [      {        text: '删除',        click: () => {}      }    ]  }}</script>

5 源码

<template>  <div v-loading="loading" class="table-wrapper">    <el-table      :data="tableData"      :max-height="props.maxHeight"      :default-sort="props.defaultSort"      @selection-change="handleSelectionChange"      @sort-change="handleSortChange"      @row-click="goDetail"      :row-style="{ cursor: 'pointer' }"      table-layout="auto"      ref="tableRef"    >      <el-table-column fixed :selectable="setSelectable" type="selection" v-if="showSelectBox" />      <el-table-column        v-for="item in props.columns"        :key="item.prop"        :prop="item.prop"        :label="item.label"        :sortable="item.sortable ? 'custom' : false"        :width="item.width"      >        <template #header>          <slot name="header">            <div class="inline-flex" :style="item.labelStyle">              <span>{{ item.label }}</span>              <el-tooltip                popper-class="table-tooltip"                effect="dark"                placement="top-start"                :content="item.tooltip"                v-if="item.tooltip"              >                <el-icon><i-ep-Warning /></el-icon>              </el-tooltip>            </div>          </slot>        </template>        <template #default="scope">          <slot :name="item.prop" :row="scope.row">            <div :style="item.style">              <span v-if="item.formatter">{{ item.formatter(scope.row) }}</span>              <span v-else>{{ scope.row[item.prop] }}</span>            </div>          </slot>        </template>      </el-table-column>      <el-table-column        fixed="right"        label="操作"        :width="props.operation?.width"        v-if="props.operation?.columns"      >        <template #default="scope">          <slot name="operations" :row="scope.row">            <span v-for="item in props.operation?.columns" :key="item.text || item.icon">              <el-button                v-if="setVisible(scope.row, item.visible)"                :type="item.type"                :link="item.link"                :plain="item.plain"                @click="item.click(scope.row)"                size="small"                style="margin-right: 4px"              >                <el-icon v-if="item.icon" :class="item.icon"></el-icon>                {{ item.text }}              </el-button>            </span>          </slot>        </template>      </el-table-column>    </el-table>    <div v-if="showSelectBox" class="p-14">      <el-checkbox        v-model="isSelected"        @click="tableRef?.toggleAllSelection()"        :indeterminate="indeterminate"        label="全选"        style="vertical-align: middle; margin-right: 10px"      />      <slot name="footer" :rows="selectionRows">        <span v-for="item in props.footer?.operations" :key="item.text || item.icon">          <el-button            v-if="item.visible ? item.visible() : true"            :type="item.type || 'primary'"            :link="item.link"            :plain="item.plain"            :disabled="!selectionRows.length"            @click="item.click(selectionRows)"            style="margin-left: 10px"          >            <el-icon v-if="item.icon" :class="item.icon"></el-icon>            {{ item.text }}          </el-button>        </span>      </slot>    </div>  </div>  <el-pagination    background    :total="tableData.length"    :layout="props.layout"    v-model:current-page="pagination.currentPage"    v-model:page-size="pagination.pageSize"    @current-change="getTableData"    @size-change="getTableData"    class="p-y-20"  /></template><script lang="ts" setup>interface OperationInterface {  click: (row: unknown) => void // 按钮点击方法,参数为当前行数据  text?: string // 按钮显示文字  icon?: string // 按钮 icon  visible?: (row?: unknown) => boolean // 设置按钮是否可见,参数为当前行数据,默认为 true  type?: string // 按钮类型['primary'| 'success'| 'warning'| 'danger'| 'info']  link?: boolean // 是否为链接按钮  plain?: boolean // 是否为朴素按钮}interface TableConfigInterface {  api: string // 表格数据获取接口  rowKey?: string // 行数据的 Key  columns: {    // 显示列    prop: string // 键名    label?: string // 表头显示名称    formatter?: (row: unknown) => string // 自定义单元格格式化方法,参数为当前行数据    tooltip?: string // 表头 tooltip    sortable?: boolean // 是否可以排序    width?: number | string // 宽度    style?: string // 单元格样式    labelStyle?: string // 表头样式  }[]  selectable?: boolean | ((row: unknown) => boolean) // 当前行多选框是否可以勾选,参数为当前行数据,默认为 false  operation?: {    // 操作列    columns: OperationInterface[]    width?: number | string // 宽度  }  footer?: {    // 操作列    operations: OperationInterface[]  }  defaultSort?: {    // 默认排序    prop: string // 默认排序的列    order?: string // ['ascending'| 'descending'], 没有指定 order, 则默认顺序是 ascending  }  maxHeight?: number | string // 表格最大高度  layout?: string}const props = withDefaults(defineProps<TableConfigInterface>(), {  rowKey: 'id',  layout: 'prev, pager, next, total'})const pagination = ref({  currentPage: 1,  pageSize: 10})const tableRef = ref()let tableData = reactive<unknown[]>([])// 多选框逻辑const isSelected = ref(false) // 是否有选中数据const selectionRows = ref<unknown[]>([]) // 当前选中的数据const handleSelectionChange = (rows: unknown[]) => {  selectionRows.value = rows  isSelected.value = rows.length > 0}const disabledList = reactive<string[]>([]) // 禁止勾选的数据const setSelectable = (row: unknown) => {  const selectable =    typeof props.selectable === 'boolean' ? props.selectable : props.selectable?.(row)  if (!selectable && !disabledList.includes(row?.[props.rowKey])) {    disabledList.push(row?.[props.rowKey])  }  return selectable}const indeterminate = computed(  () =>    selectionRows.value.length > 0 &&    selectionRows.value.length < tableData.length - disabledList.length)const showSelectBox = computed(() => props.selectable && disabledList.length < tableData.length)// 操作框逻辑const showOperation = ref(false)const setVisible = (row: unknown, visible?: (row: unknown) => boolean) => {  if (!visible || visible(row)) {    showOperation.value = true    return true  }  return false}// 排序const handleSortChange = (data: { prop: string; order: string | null }) => {  const { prop, order } = data  console.log(prop, order)  // getTableData}// 跳转详情页const goDetail = (row: unknown) => {  console.log(row)}// 发送接口const loading = ref(true)const getTableData = () => {  loading.value = true  showOperation.value = false  tableData = [    {      date: '2016-05-02',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-03',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-04',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-05',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-06',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-07',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-08',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-09',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-10',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    },    {      date: '2016-05-11',      name: 'Tom',      state: 'California',      city: 'Los Angeles',      address: 'No. 189, Grove St, Los Angeles',      zip: 'CA 90036'    }  ]  loading.value = false}getTableData()</script><style lang="scss" scoped>.table-wrapper {  border-top: 1px solid #eaeaea;  border-left: 1px solid #eaeaea;  border-right: 1px solid #eaeaea;}.inline-flex {  display: inline-flex;  align-items: center;}.p-14 {  border-bottom: 1px solid #eaeaea;  padding: 14px;}.p-y-20 {  padding-top: 20px;  padding-bottom: 20px;  justify-content: center;}</style><style lang="scss">.table-tooltip {  max-width: 220px;}</style>
阅读本书更多章节>>>>

本文链接:https://www.kjpai.cn/gushi/2024-03-26/149081.html,文章来源:网络cs,作者:纳雷武,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

文章评论