在做后台管理管理系统时,很多页面都是上面是一个搜索功能组件,下面是一个table表格,再加几个按钮控制dialog表单
我把这种页面拆分成上下两个组件,再整一个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做不同的条件渲染,再配合插槽对数据进行处理