解决问题
只举一个例子(我正好需要用到的)
在写中后台时, 如果对 表格组件 再度封装了, 比如这样的
以element-ui 为例:
plain
<template>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</template>对html数据化, 封装成一个组件.
vue
// complex-table.vue
<template>
<el-table
:data="list"
style="width: 100%;"
>
<el-table-column v-for="col in columns" :key="col.prop" v-bind="col"></el-table-column>
</el-table>
</template>
<script>
export default {
props: {
columns: Array
}
}
</script>
--------------------------
// index.vue
<template>
<div class="box_container">
<complex-table
:columns="tableColumns"
/>
</div>
</template>
<script>
export default {
data() {
return {
tableColumns: [
{ label: '日期', prop: 'sn', align: 'center' },
{ label: '姓名', prop: 'phone', align: 'center' },
{ label: '地址', prop: 'address', align: 'center' }
]
}
}
}
</script>那么问题来了, 如果我要在table中加入头像列, 但是后端返回的是url地址, 你又怎么办呢,你可以这样
vue
// complex-table.vue
...
<el-table>
<el-table-column v-for="col in columns" :key="col.prop" v-bind="col">
<template slot-scope="scope">
<template v-if="col.prop==='avatar'">
<el-avatar :src="row.avatar" />
</template>
<template v-else>
<span>{{ scope.row[col.prop] }}</span>
</template>
</template>
</el-table-column>
</el-table>
...
// index.vue
...
data() {
return {
tableColumns: [
{ label: '头像', prop: 'avatar', align: 'center' },
{ label: '日期', prop: 'date', align: 'center' },
{ label: '姓名', prop: 'name', align: 'center' },
{ label: '地址', prop: 'address', align: 'center' }
]
}
}
...万一又是 **<el-tag>** 标签, **el-button** 不能一直v-if吧
或许也可以使用 <component :is="**" v-bind="**" >这种形式, 如果遇到方法传值又感觉差点什么,差scope传值了
解决方案
使用函数式组件, 可灵活使用, 原理跟 valueFormat 类似, 不废话直接上代码
新建文件 columnRender.js
vue
/*
* @Description: 函数式组件渲染单元表格
* @Author: 仲灏<izhaong 164165005@qq.com>
* @Date: 2020-09-16 15:33:25
* @LastEditors: 仲灏<izhaong 164165005@qq.com>
* @LastEditTime: 2020-09-16 15:47:23
*/
export default {
functional: true,
props: {
row: Object,
render: Function
},
render(h, ctx) {
const params = {
row: ctx.props.row
}
return ctx.props.render(h, params)
}
}
// complexTable
...
<template v-else-if="'render' in col">
<!-- <component :is="col.tag" v-bind="col.attrs">{{ col.tagValue }}</component> -->
<Render :row="row" :render="col.render" />
</template>
...
// index.js
tableColumns: [
{ label: 'name', prop: 'name', align: 'center' },
{ label: '是否有效', prop: 'isValid', align: 'center', render: (h, { row }) => {
return h('el-tag', { attrs: { type: row.isValid ? 'success' : 'info' }}, row.isValid ? '有效' : '无效')
} }
]结语
第一次bb这个多话, 之前直接就啪~ 贴代码了.
当然如果封装的组件足够强大, 能够兼顾涵盖该项目大部分业务, 就可以把组件当做页面使用, 全部使用纯数据驱动, 当然你的业务相似度要高
最终代码
complexTable.vue
vue
<!--
* @Descripttion: 数据化表格
* @version: 1.0.0
* @Author: 仲灏 Izhaong<164165005@qq.com>
* @Date: 2020-06-27 15:13:00
* @LastEditors: 仲灏<izhaong 164165005@qq.com>
* @LastEditTime: 2020-11-20 10:22:20
-->
<template>
<div class="complex-table_container app-container">
<div class="filter-container d-flex align-items-center">
<component
:is="filter.is"
v-for="(filter, index) in filters"
:key="index"
v-model="filterForm[filter.prop]"
v-bind="filter.attrs"
class="filter-item mr-10"
>
<template v-if="filter.options">
<el-option
v-for="(option, i) in filter.options"
:key="`${index}_${i}`"
:value="option.value"
:label="option.label"
/>
</template>
</component>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<slot name="action">
<el-button
v-if="crud.includes('c')"
class="filter-item"
style="margin-left: 10px;"
type="primary"
icon="el-icon-edit"
@click="$emit('createItem')"
>添加</el-button>
</slot>
</div>
<el-table
:key="tableKey"
v-loading="listLoading"
:height="tableHeight"
:data="list"
size="small"
border
style="width: 100%;"
>
<el-table-column
type="index"
width="50"
/>
<el-table-column v-for="col in columns" :key="col.prop" v-bind="col">
<template v-if="'index' in col" slot-scope="{row}">
<template v-if="'render' in col">
<Render :row="row" :render="col.render" />
</template>
<span v-else>{{ col.formatter ? col.formatter(row) : row[col.prop] }}</span>
</template>
</el-table-column>
<el-table-column v-if="handle.length" v-bind="handleColumn" :width="(handle.length * 80)+'px'">
<template slot-scope="scope">
<template v-for="(btn, index) in handle">
<el-button
v-if="!btn.isPop"
:key="index"
style="margin: 5px;"
size="mini"
:type="btn.type"
@click.native.prevent="btn.method(scope.row,scope)"
>{{ btn.label }}</el-button>
<el-popconfirm
v-if="btn.isPop"
:key="index"
placement="right"
confirm-button-text="确定"
cancel-button-text="取消"
icon="el-icon-info"
icon-color="red"
title="确定删除吗?"
@onConfirm="getList();btn.method(scope.row, scope)"
>
<el-button
slot="reference"
style="margin: 5px;"
size="mini"
:type="btn.type"
>{{ btn.label }}</el-button>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>listQuery.size"
style="padding: 8px 16px; margin-top: 10px;"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.size"
@pagination="getList"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import Render from './render'
export default {
name: 'ComplexTable',
components: { Pagination, Render },
props: {
columns: { type: Array, default: () => [] },
operates: { type: Array, default: () => [] },
handle: { type: Array, default: () => [] },
filters: { type: Array, default: () => [] },
crud: { type: String, default: 'crud' },
actionsColumn: {
type: Object,
default: () => ({
label: '操作',
align: 'center'
})
},
handleColumn: {
type: Object,
default: () => ({
label: '操作',
align: 'center'
})
},
// eslint-disable-next-line vue/require-default-prop
api: [Function, Object]
},
data() {
return {
filterForm: {},
tableKey: 0,
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
size: 20
}
}
},
computed: {
tableHeight() {
if (this.total > this.listQuery.size) {
return window.document.body.clientHeight - 242
} else {
return window.document.body.clientHeight - 180
}
}
},
created() {
this.initFilters()
this.getList()
},
methods: {
inpubr($event) {
event.target.blur()
},
initFilters() {
const props = this.filters.map((item) => item.prop)
props.forEach((key) => {
this.$set(this.filterForm, key, '')
})
},
getList() {
this.listLoading = true
this.api({ ...this.listQuery, ...this.filterForm }).then((response) => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
}
}
}
</script>
<style lang="scss">
.complex-table_container {
.el-table__body-wrapper {
&::-webkit-scrollbar {
/*滚动条整体样式*/
width: 6px !important;
/*高宽分别对应横竖滚动条的尺寸*/
height: 6px !important;
background: #ffffff !important;
cursor: pointer !important;
}
&::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 5px !important;
-webkit-box-shadow: inset 0 0 5px rgba(240, 240, 240, 0.5) !important;
background: rgba(63, 98, 131, 0.8) !important;
cursor: pointer !important;
}
&::-webkit-scrollbar-track {
/*滚动条里面轨道*/
-webkit-box-shadow: inset 0 0 5px rgba(240, 240, 240, 0.5) !important;
border-radius: 0 !important;
background: rgba(240, 240, 240, 0.5) !important;
cursor: pointer !important;
}
}
}
</style>