表格复用
在后端管理项目中,我们通常有着许多重复利用的组件,尤其是表格与分页器是用得最多的。我们采用的是 element-ui
所以我们对其进行了封装。
封装组件
com-list.vue
<template>
<section class="com-list">
<el-table
v-loading="loading"
:data="sourceData"
@selection-change="selectionChange"
class="com-list__table">
<!-- 选择框 -->
<el-table-column
v-if="selectVisible"
type="selection"
width="45"
align="center"
/>
<!-- 主体数据 -->
<template v-for="(column, idx) in columns">
<el-table-column
v-if="column.prop"
:key="idx"
:prop="column.prop"
:label="column.label"
:formatter="column.formatter"
:sortable="column.sortable"
:align="column.align"
:width="column.width">
</el-table-column>
<slot
v-else-if="column.slot"
:name="column.slot"
/>
</template>
<!-- 分页 -->
<div v-if="needPage" class="com-list__footer" slot="append">
<el-row type="flex" justify="left">
<el-col>
<el-button v-if="deleteVisible" type="danger" size="small">批量删除</el-button>
<el-button v-if="setGroupVisible" size="small">设置分组</el-button>
<slot name="read" />
</el-col>
<el-col class="pagination" v-if="needPage" :span="6">
<el-pagination
small
background
layout="prev, pager, next"
:total="pageTotal"
:page-size="pageRequest.limit"
:current-page.sync="pageRequest.page"
@current-change="page => $emit('pageChange', page)"
@size-change="size => $emit('pageChange', size)"
>
</el-pagination>
</el-col>
</el-row>
</div>
</el-table>
</section>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component
export default class ComList extends Vue {
@Prop({default: false}) loading!: boolean
@Prop() sourceData!: any // 表格数据
@Prop() columns!: Array<object> // 表格项
@Prop({default: false}) selectVisible?: boolean // 选择框
@Prop({default: false}) deleteVisible?: boolean // 批量删除
@Prop({default: false}) setGroupVisible?: boolean // 设置分组
@Prop({default: true}) needPage?: boolean // 是否有分页
@Prop({default: () => {return {page: 1, limit: 10, total: 0} }}) pageRequest?: object // 分页数据
@Prop({default: 0}) pageTotal?: number // 分页总数
private selections: Array<object> = []
private selectionChange(selections: Array<object>) {
this.selections = selections
this.$emit('selectionChange', { selections })
}
}
</script>
<style lang="scss" scoped>
.com-list {
::v-deep &__table {
border: 1px solid #EBF3FA;
thead {
font-size: 14px;
color: #333;
th {
height: 70px;
background: #EBF3FA;
}
}
thead th, tbody, td {
&:first-child:not(.el-table-column--selection) {
.cell {
padding-left: 36px;
}
}
}
}
&__footer {
margin: 22px;
text-align: left;
.pagination {
margin-top: 6px;
text-align: right;
}
.el-pagination {
text-align: right;
padding: 0;
}
}
}
</style>
其中的slot
是很关键的一点,它能让我们自定义每一项,其他的倒无非是增加相关属性而已。
使用组件
<com-list
:source-data="tableData"
:columns="columns"
:selectVisible="true"
:page-request="listQuery"
:page-total="total"
:loading="listLoading"
:page-sizes="[10, 25, 50]"
@pageChange="pageChange"
@sizeChange="sizeChange"
@selectionChange="selectionChange"
>
<template #status>
<el-table-column label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
{{scope.row.status === 1 ? '已处理' : '待处理'}}
</el-tag>
</template>
</el-table-column>
</template>
<template #operation>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="onLock(scope.row.cate_id)">查看</el-button>
</template>
</el-table-column>
</template>
<template #read>
<el-button size="small" @click="setRead">标记已读</el-button>
</template>
</com-list>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import ComList from '@/components/ComList/index.vue'
import Page from '@/mixins/pageMixins'
@Component({
components: {
ComList
}
})
export default class Message extends Page {
public fetchList = this.$api.Message.getList
private columns: Array<object> = [
{
label: 'ID',
prop: 'id'
},
{
label: '消息标题',
prop: 'title'
},
{
slot: 'status'
},
{
label: '时间',
prop: 'create_time'
},
{
slot: 'operation'
}
]
private selections: Array<object> = []
private selectionChange (parmars: any) {
const arr: Array<object> = []
parmars.selections.forEach((el: any) => {
arr.push(el.cate_id)
})
this.selections = arr
}
}
</script>
需要注意的是,我们在其中引入了一个minxins
它能让我们将一些属性以及方法抽离出来,达到复用的目的。在vue-property-decorator
中我们使用的是 extends
来继承 mixins
。
抽取可复用的方法以及属性
mixins/pageMinxis.ts
import { Component, Vue, Watch } from 'vue-property-decorator'
@Component
export default class Page extends Vue {
// 页码及每页条数
listQuery = {
page: 1,
limit: 10
}
total = 0
listLoading = false // 是否加载中
fetchList: any = null // 请求接口函数
tableData = [] // 获取数据
searchForm = {} // 筛选条件
@Watch('listQuery', { immediate: true, deep: true })
listQueryChange () {
this.getList()
}
// 页码切换
pageChange(page: number) {
this.listQuery.page = page
}
// 每页条数切换
sizeChange(size: number) {
this.listQuery.limit = size
}
// 获取列表数据
async getList() {
if (!this.fetchList || typeof this.fetchList !== 'function') {
this.$message.error('请把列表接口函数赋值给fetchList')
return
}
this.listLoading = true
const query = this.$filterEmptyValue(this.listQuery, this.searchForm)
const RESULT = await this.fetchList(query)
if (RESULT.code === 1) {
this.tableData = RESULT.data.data
this.total = RESULT.data.total
this.listLoading = false
}
}
/**
* @description: 操作项目及回调
* fn: 需要执行的函数
* cb: 回调(可传空)
* ...rest: 需要执行函数的参数
*/
async mixinHandleItem(fn: Function, cb: Function, ...rest: any) {
const { code, data } = await fn(...rest)
if (code === 1) {
this.$message.success('操作成功')
if (cb && typeof cb === 'function') {
cb()
}
} else {
this.$message.error(data || '操作失败,请重试')
}
}
}
我们可以复用方法以及覆盖属性,即可高效利用代码
对以上代码的优化
components/ComList.vue
<template>
<section class="com-list">
<el-table
v-loading="loading"
:data="sourceData"
@selection-change="selectionChange"
class="com-list__table">
<!-- 选择框 -->
<el-table-column
v-if="selectVisible"
type="selection"
width="45"
align="center"
/>
<!-- 主体数据 -->
<template v-for="(column, index) in columns">
<el-table-column
v-if="column.render"
:key="column.prop"
:label="column.label"
>
<template slot-scope="scope">
<Render
:row="scope.row"
:index="index"
:render="column.render"
/>
</template>
</el-table-column>
<slot
v-else-if="column.slot"
:name="column.slot"
/>
<el-table-column
v-else
:key="column.prop"
v-bind="setAttrs(column)"
/>
</template>
<!-- 分页 -->
<div v-if="needPage" class="com-list__footer" slot="append">
<el-row type="flex" justify="left">
<el-col>
<el-button v-if="deleteVisible" type="danger" size="small">批量删除</el-button>
<el-button v-if="setGroupVisible" size="small">设置分组</el-button>
<slot name="read" />
</el-col>
<el-col class="pagination" v-if="needPage" :span="6">
<el-pagination
small
background
layout="prev, pager, next"
:total="pageTotal"
:page-size="pageRequest.limit"
:current-page.sync="pageRequest.page"
@current-change="page => $emit('pageChange', page)"
@size-change="size => $emit('pageChange', size)"
>
</el-pagination>
</el-col>
</el-row>
</div>
</el-table>
</section>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
import Render from './render'
@Component({
components: {
Render
}
})
export default class ComList extends Vue {
@Prop({default: false}) loading!: boolean
@Prop() sourceData!: object // 表格数据
@Prop() columns!: Array<object> // 表格项
@Prop({default: false}) selectVisible?: boolean // 选择框
@Prop({default: false}) deleteVisible?: boolean // 批量删除
@Prop({default: false}) setGroupVisible?: boolean // 设置分组
@Prop({default: true}) needPage?: boolean // 是否有分页
@Prop({default: () => {return {page: 1, limit: 10, total: 0} }}) pageRequest?: object // 分页数据
@Prop({default: 0}) pageTotal?: number // 分页总数
private selections: Array<object> = []
private selectionChange(selections: Array<object>) {
this.selections = selections
this.$emit('selectionChange', { selections })
}
private setAttrs (params: object) {
// eslint-disable-next-line
const { slot, ...options }: any = params // 排除 slot
return { ...options }
}
}
</script>
<style lang="scss" scoped>
.com-list {
::v-deep &__table {
border: 1px solid #EBF3FA;
thead {
font-size: 14px;
color: #333;
th {
height: 70px;
background: #EBF3FA;
}
}
thead th, tbody, td {
&:first-child:not(.el-table-column--selection) {
.cell {
padding-left: 36px;
}
}
}
}
&__footer {
margin: 22px;
text-align: left;
.pagination {
margin-top: 6px;
text-align: right;
}
.el-pagination {
text-align: right;
padding: 0;
}
}
}
</style>
components/render.ts
import { CreateElement, RenderContext } from 'vue/types/umd'
export default {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render: (h: CreateElement, ctx: RenderContext) => {
const params: any = {
row: ctx.props.row,
index: ctx.props.index
}
if (ctx.props.column) params.column = ctx.props.column
return ctx.props.render(h, params)
}
}
利用 render
函数定制表格内的内容
使用
import { Component } from 'vue-property-decorator'
import { CreateElement } from 'vue/types/umd'
import ComList from '@/components/ComList/index.vue'
import Page from '@/mixins/pageMixins'
@Component({
components: {
ComList
}
})
export default class Work extends Page {
fetchList = this.$api.WorkOrder.getList
columns: Array<object> = [
{
label: '工单编号',
prop: 'id'
},
{
label: '问题类型',
prop: 'type',
formatter: this.formatterType
},
{
label: '问题标题',
prop: 'title'
},
{
label: '状态',
prop: 'status',
render: (h: CreateElement, { row }: any) => {
return h('el-tag', {
props: {
type: row.status === 1 ? 'success' : 'danger'
}
}, row.status === 1 ? '启用' : '停用')
}
},
{
label: '问题详情',
prop: 'content'
},
{
label: '提交时间',
prop: 'create_time'
},
{
slot: 'operation'
}
]
typeOption: Array<object> = []
formatterType (row: any) {
let str = ''
this.typeOption.forEach((el: any) => {
if (el.id === row.type) str = el.group_name
})
return str
}
}
不知为何这个语法只能渲染 element-ui
里的组件,如果div
等则无法放入内容。
网友评论