<div style="display: none;" hidden="true" aria-hidden="true" data-nosnippet>Are you an LLM? You can read better optimized documentation at /pages/3de341.md for this page in Markdown format</div>
https://www.yuque.com/docs/share/10067c29-d80c-4d39-a624-32881f8fad58?# 《Vue 中后台表格的增删改查统一解决方案(mixin版)》
前言
个人是比较喜欢这一版的, 也是最开始做程序员写的,
使用方法
部分中后台页面都是非常同质化的 CRUD 组成的,很多时候都是一个 Table,然后提供一些操作按钮,并且有一个新增表单。看起来就像这样 
组件的封装
table filter
参数配置:
vue
tableFilters: [
{
is: 'el-input',
prop: 'title',
attrs: { placeHolder: '请输入景点名称', style: 'width: 200px;' }
},
{
is: 'el-input',
prop: 'merchant',
attrs: { placeHolder: '请选择商户', style: 'width: 200px;' }
}
]vue
<div class="filter-container d-flex align-items-center justify-content-between pr-20">
<section>
<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>
</section>
<section>
<el-button class="filter-item" type="warning" icon="el-icon-refresh-left" @click="filterForm={};getList()">重置</el-button>
<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="success" icon="el-icon-plus" @click="$emit('createItem')">添加</el-button>
</slot>
</section>
</div>
----------------
filters: { type: Array, default: () => [] }table columns
传入参数:
vue
tableColumns: [
{ label: '名称', prop: 'title', width: 130 },
{ label: '联系人', prop: 'name', width: 60, align: 'center' },
{ label: '电话', prop: 'phone', width: 130, align: 'center' },
{ label: '账号', prop: 'acount', width: 140 },
{ label: '人数', prop: 'count', width: 50, align: 'center' },
{ label: '地址', prop: 'address', width: 'auto' },
{
label: '状态', prop: 'status', width: 80, align: 'center', render: (h, { row }) => {
return h('el-tag', { attrs: { type: row.status === '正常' ? 'success' : 'info', size: 'small' }}, row.status)
}
},
{ label: '创建时间', prop: 'createTime', width: 160, align: 'center' }
],vue
<el-table-column v-for="col in columns" :key="col.prop" v-bind="col">
<template 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>
------------------
columns: { type: Array, default: () => [] },table item handler
传入参数:
vue
tableHandle: [
{
label: '修改',
type: 'primary',
isPop: false,
method: row => {
this.editItem(row)
}
},
{
label: '删除',
type: 'danger',
isPop: true,
method: row => {
this.deleteItem(row)
}
}
],vue
<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="$message.success('操作成功');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>
-------------------
handle: { type: Array, default: () => [] },table pagination
vue
<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" />
---------
total: 0,
listQuery: {
page: 1,
size: 20
}all page
使用页面(参数传入):
vue
<!--
* @Description:
* @Author: 仲灏<izhaong 164165005@qq.com>
* @Date: 2020-11-17 17:49:51
* @LastEditors: 仲灏<izhaong 164165005@qq.com>
* @LastEditTime: 2020-11-20 14:11:38
-->
<template>
<div class="app-container">
<complex-table ref="table" :columns="tableColumns" :handle="tableHandle" :filters="tableFilters" :api="api" @createItem="$router.push('/merchant/create')" />
</div>
</template>
<script>
import ComplexTable from '../../components/ComplexTable'
import { fetchList } from '@/api/merchant'
export default {
name: 'MerchantList',
components: { ComplexTable },
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
api: fetchList,
tableColumns: [
{ label: '名称', prop: 'title', width: 130 },
{ label: '联系人', prop: 'name', width: 60, align: 'center' },
{ label: '电话', prop: 'phone', width: 130, align: 'center' },
{ label: '账号', prop: 'acount', width: 140 },
{ label: '人数', prop: 'count', width: 50, align: 'center' },
{ label: '地址', prop: 'address', width: 'auto' },
{
label: '状态', prop: 'status', width: 80, align: 'center', render: (h, { row }) => {
return h('el-tag', { attrs: { type: row.status === '正常' ? 'success' : 'info', size: 'small' }}, row.status)
}
},
{ label: '创建时间', prop: 'createTime', width: 160, align: 'center' }
],
tableHandle: [
{
label: '修改',
type: 'primary',
isPop: false,
method: row => {
this.editItem(row)
}
},
{
label: '删除',
type: 'danger',
isPop: true,
method: row => {
this.deleteItem(row)
}
}
],
tableFilters: [
{
is: 'el-input',
prop: 'title',
attrs: { placeHolder: '请输入商户名称', style: 'width: 200px;' }
},
{
is: 'el-input',
prop: 'name',
attrs: { placeHolder: '请输入联系人姓名', style: 'width: 200px;' }
},
{
is: 'el-input',
prop: 'phone',
attrs: { placeHolder: '请输入联系人电话', style: 'width: 200px;' }
}
]
}
},
methods: {
editItem(row) {
console.log(row)
this.$router.push(`/merchant/edit/123456789`)
},
deleteItem(row) {
console.log(row)
}
}
}
</script>组件页面
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-23 15:39:49
-->
<template>
<div class="complex-table_container app-container">
<div class="filter-container d-flex align-items-center justify-content-between pr-20">
<section>
<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>
</section>
<section>
<el-button class="filter-item" type="warning" icon="el-icon-refresh-left" @click="filterForm={};getList()">重置</el-button>
<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="success" icon="el-icon-plus" @click="$emit('createItem')">添加</el-button>
</slot>
</section>
</div>
<el-table :key="tableKey" v-loading="listLoading" :height="tableHeight" :data="list" size="small" style="width: 100%;">
<el-table-column
type="index"
width="50"
label="序列"
align="center"
/>
<el-table-column v-for="col in columns" :key="col.prop" v-bind="col">
<template 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="$message.success('操作成功');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>