【element-ui】二次封装table组件

1,166 阅读2分钟

在做后台管理管理系统时,很多页面都是上面是一个搜索功能组件,下面是一个table表格,再加几个按钮控制dialog表单

image.png 我把这种页面拆分成上下两个组件,再整一个dialog,因为这种页面dialog才有差异化,下面就分享一下二次封装的table组件

<template>
  <div class="container">
    <el-card style="margin-top: 16px">
      <div slot="header" class="searchTitle">
        <span>
          <img src="@/assets/auxiliary/cont_title_icon.png" alt="">
          <span>数据列表</span>
        </span>
        <div v-if="options[options.length-1].label=='操作'" class="export">
          <el-button type="success" @click="handleOpenDialog">新增</el-button>
        </div>
      </div>
      <div class="content">
        <el-table
          v-loading="tableLoading"
          :border="true"
          :data="tableData"
          style="width: 100%"
          :header-cell-style="{ 'text-align': 'center' }"
          :cell-style="{ 'text-align': 'center' }"
          row-key="id"
          :row-style="{ height: '50px' }"
          lazy
          :tree-props="{ children: 'children', hasChildren: 'childExist' }"
        >
          <!-- 常规数据展示 -->
          <template v-for="(optionsItem, i) in options">
          
            <!-- 按钮 -->
            <template v-if="optionsItem.showType == 'controls'">
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                fixed="right"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label"
                :width="optionsItem.width ? optionsItem.width : '250'"
              >
                <template #default="scope">
                  <slot name="button" :rowData="scope.row" />
                </template>
              </el-table-column>
            </template>
            
            <!-- 文件下载 -->
            <template v-else-if="optionsItem.showType == 'downLoadFile'">
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label "
                :width="optionsItem.width?optionsItem.width:''"
              >
                <template #default="scope">
                  <slot :rowData="scope.row">
                    <el-button v-for="(item,index) in scope.row[optionsItem.prop]" :key="item.id?item.id:index" type="text" size="small" @click="downLoadFile(item)">
                      <TextAstrict :text="item.fileName" :text-wrap="1" :text-width="120" style="line-height: 20px;" />
                    </el-button>
                  </slot>
                </template>
              </el-table-column>
            </template>
            
            <!-- IOS格式时间处理 类似2024-07-12T18:12:19.000+0800 -->
            <template v-else-if="optionsItem.showType == 'IOSTime'">
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label "
                :width="optionsItem.width?optionsItem.width:''"
              >
                <template #default="scope">
                  <slot :rowData="scope.row">
                    {{ formatISODate(scope.row[optionsItem.prop]) }}
                  </slot>
                </template>
              </el-table-column>
            </template>
            
            <!-- 状态,需配置ststusList 如 ststusList: [{ label: '成功', value: 0 }, { label: '失败', value: 1 }]-->
            <template v-else-if="optionsItem.showType == 'status'">
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label"
                :width="optionsItem.width ? optionsItem.width : ''"
              >
                <template slot-scope="scope">
                  {{ optionsItem.ststusList.find(e => e.value == scope.row[optionsItem.prop]).label }}
                </template>
              </el-table-column>
            </template>

            <!-- 序号 -->
            <template v-else-if="optionsItem.showType == 'index'">
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                type="index"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label"
                :width="optionsItem.width ? optionsItem.width : ''"
              >
                <template slot-scope="scope">
                  <div>
                    {{ (currentData.pageNum -1) *currentData.pageSize + scope.$index +1 }}
                  </div>
                </template></el-table-column>
            </template>
            
            <!-- 自定义插槽 根据<slot :name="optionsItem.prop" /> -->
            <template v-else-if="optionsItem.showType == 'slot'">
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                type="index"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label"
                :width="optionsItem.width ? optionsItem.width : ''"
              >
                <template slot-scope="scope">
                  <slot :name="optionsItem.prop" :rowData="scope.row" />
                </template></el-table-column>
            </template>
            
            <!-- 没有showType :key="'column' + i"-->
            <template v-else>
              <el-table-column
                :key="'column' + optionsItem.prop + i"
                v-bind="optionsItem"
                :prop="optionsItem.prop"
                :label="optionsItem.label"
                :width="optionsItem.width ? optionsItem.width : ''"
                show-overflow-tooltip
              />
            </template>
          </template>
        </el-table>
        
        <!-- 分页 -->
        <div class="pagination">
          <el-pagination
            background
            :current-page.sync="currentData.pageNum"
            :page-size.sync="currentData.pageSize"
            :total="total"
            :page-sizes="[5, 10, 20, 40, 200]"
            layout="total, sizes, prev, pager, next, jumper"
            @size-change="sizeChange"
            @current-change="currentChange"
          />
        </div>
      </div>
    </el-card>
  </div>
</template>

上面简单展示了还有更多showType这里不做过多赘述,例如图片,step步骤等

可以看见有三个事件:openDialog 打开新增弹框,sizeChange currentChange分页事件

<script setup>
import { ref, reactive, onMounted, defineProps, defineEmits } from 'vue'
import dayjs from 'dayjs'
import TextAstrict from '@/components/CzcComponent/textAstrict.vue'
import { formatISODate } from '@/components/CzcComponent/tool'
onMounted(() => {

})
const props = defineProps({
  currentData: {
    type: Object,
    default: () => ({
      total: 10,
      pageNum: 1,
      pageSize: 10
    })
  },
  options: {
    type: Array,
    default: () => []
  },
  tableData: {
    type: Array,
    default: () => []
  },
  total: {
    type: Number,
    default: 10
  },
  tableLoading: {
    type: Boolean
  }
})

const emit = defineEmits(['openDialog', 'sizeChange', 'currentChange'])
// 附件下载
const downLoadFile = (data) => {
  const a = document.createElement('a')
  a.href = data.filePath
  a.download = data.fileName // 设置下载文件名
  a.target = '_blank'
  a.style.display = 'none'
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a) // 移除临时<a>标签
}

const handleOpenDialog = (e) => {
  emit('openDialog')
}

const sizeChange = () => {
  emit('sizeChange', props.currentData.pageSize)
}

const currentChange = () => {
  emit('currentChange', props.currentData.pageNum)
}
</script>

在页面上使用组件时:

<!-- 表格组件 -->
    <MyTable
      :table-loading="tableLoading"
      :total.sync="tableDataTotal"
      :current-data.sync="searchForm"
      :table-data="myTableData"
      :options="tableOptions"
      @openDialog="openDialogFunc"
      @sizeChange="sizeChangeFunc"
      @currentChange="currentChangeFunc"
    >
      <template #button="{ rowData }">
        <el-button type="primary" size="small" @click="editBtnFunc(rowData)">编辑</el-button>
        <el-button type="danger" size="small" @click="delBtnFunc(rowData)">删除</el-button>
      </template>
      <template #roleIds="{ rowData }">
        <p v-for="item in rowData.roleIds" :key="item" style="margin: 5px;">
          <!-- {{ allRolesList.find(el => el.id === item).name }} -->
          {{ getRoleName(item) }}
        </p>
      </template>
    </MyTable>

需要注意的是tableOptions,是表格的配置,建议使用计算属性来返回,如:

const tableOptions = computed(() => {
  return [
    {
      label: '客户端ID',
      prop: 'clientId',
      showType: 'default',
      width: 200
    },
    {
      label: '客户端密匙',
      prop: 'clientSecret',
      showType: 'default'
    },
    {
      label: '客户端描述',
      prop: 'description',
      showType: 'default'
    },
    {
      label: '角色列表',
      prop: 'roleIds',
      showType: 'slot',
      width: 200
    },
    {
      label: '有效截止日期',
      prop: 'validityDate',
      showType: 'default'
    },
    {
      label: '操作',
      prop: 'controls',
      showType: 'controls',
      width: 250
    }
  ]
})

注意, prop: 'roleIds',这一项的showType是slot,所以可以直接在页面上使用插槽进行数据处理展示,最后一项label: '操作'也是,直接在页面上添加按钮及事件

组件的核心是通过传入tableOptions,根据每一项的showType做不同的条件渲染,再配合插槽对数据进行处理