很久没有再写管理后台, 每次coding都有不同的想法, 为了更易于扩展, 更快捷, 更模块话…说白了就是懒
基于Vue element-ui构建 并举例用户表的增删改查
<!--
* @Descripttion: 用户管理页面
* @version: 1.0.0
* @Author: 仲灏 Izhaong<164165005@qq.com>
* @Date: 2020-06-27 11:41:32
* @LastEditors: 仲灏 Izhaong<164165005@qq.com>
* @LastEditTime: 2020-06-30 10:42:05
-->
<template>
<div class="user_container">
<complex-table
ref="table"
:columns="tableColumns"
:operates="tableOperates"
:filters="tableFilters"
:api="api"
@createItem="handleCreate"
/>
<el-dialog
destroy-on-close
:title="dialogForm.textMap[dialogForm.status]"
:visible.sync="dialogForm.visible"
>
<FormGenerate ref="form" :items="formItems" @submit="submitForm" />
<div slot="footer" class="dialog-footer">
<el-button @click="dialogForm.visible = false">取消</el-button>
<el-button type="primary" @click="$refs.form.submitForm()">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getUserList, createUser, updateUser, deleteUser } from '@/api/user'
import { getDiffObj } from '@/utils/tools'
import { deepClone } from '@/utils'
import ComplexTable from '../../components/ComplexTable'
import FormGenerate from '../../components/FormGenerate'
export default {
name: 'User',
components: { ComplexTable, FormGenerate },
data() {
return {
api: getUserList,
tableColumns: [
{ label: '用户名称', prop: 'username', align: 'center' },
{ label: '姓名', prop: 'name', align: 'center' },
{ label: '手机号', prop: 'phone', align: 'center' },
{ label: '邮箱', prop: 'email', align: 'center' },
{ label: '创建时间', prop: 'createTime', align: 'center' },
{
label: '状态',
prop: 'status',
align: 'center',
formatter: row => {
return row.status === 0 ? '正常' : '禁用'
}
}
],
tableOperates: [
{
id: 1,
label: '编辑',
type: 'primary',
isPop: false,
method: row => {
this.handleEdit(row)
}
},
{
id: 2,
label: '删除',
type: 'danger',
isPop: true,
method: row => {
this.handleDel(row)
}
}
],
tableFilters: [
{
is: 'el-input',
prop: 'username',
attrs: { placeHolder: '用户名称', style: 'width: 200px;' }
},
{
is: 'el-input',
prop: 'name',
attrs: { placeHolder: '姓名', style: 'width: 200px;' }
},
{
is: 'el-select',
prop: 'status',
attrs: { placeholder: '状态', clearable: true },
options: [
{ label: '正常', value: 0 },
{ label: '禁用', value: 1 }
]
}
],
formAttrs: {
'label-position': 'right',
'label-width': '100px',
size: 'mini'
},
formItems: [
{
attrs: {
prop: 'username',
label: '用户名称',
rules: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }
]
},
component: { is: 'el-input' }
},
{
attrs: {
prop: 'name',
label: '姓名',
rules: [
{ required: true, message: '姓名不能为空', trigger: 'blur' }
]
},
component: { is: 'el-input' }
},
{
attrs: {
prop: 'phone',
label: '手机号',
rules: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{
pattern: /^1[3456789]\d{9}$/,
message: '目前只支持中国大陆的手机号码'
}
]
},
component: { is: 'el-input' }
},
{
attrs: {
prop: 'email',
label: '邮箱',
rules: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱' }
]
},
component: { is: 'el-input' }
},
{
attrs: {
prop: 'status',
label: '状态',
rules: [
{ required: true, message: '状态不能为空', trigger: 'change' }
]
},
component: { is: 'el-radio-group' },
compTemps: [
{ is: 'el-radio', label: 1, key: 1, content: '禁用' },
{ is: 'el-radio', label: 0, key: 0, content: '正常' }
]
}
],
dialogForm: {
visible: false,
textMap: {
update: '编辑',
create: '添加'
},
status: 'create'
}
}
},
methods: {
submitForm(formData) {
console.log(formData)
const isCreate = this.dialogForm.status === 'create'
if (isCreate) {
createUser(formData).then(_ => {
this.$refs.table.getList()
})
} else {
const diffObj = getDiffObj(this.actionRow, formData)
updateUser({ ...diffObj, id: this.actionRow.id })
}
this.dialogForm.visible = false
},
handleCreate() {
this.dialogForm.status = 'create'
this.dialogForm.visible = true
},
handleEdit(row) {
this.actionRow = deepClone(row)
this.dialogForm.status = 'update'
this.dialogForm.visible = true
this.$nextTick(() => {
this.$refs.form.formData = row
})
},
handleDel(row) {
const { id: userId } = row
deleteUser({ userId })
}
}
}
</script>
<!--
* @Descripttion: 数据化表格
* @version: 1.0.0
* @Author: 仲灏 Izhaong<164165005@qq.com>
* @Date: 2020-06-27 15:13:00
* @LastEditors: 仲灏 Izhaong<164165005@qq.com>
* @LastEditTime: 2020-06-30 10:50:25
-->
<template>
<div class="app-container">
<div class="filter-container">
<component
:is="filter.is"
v-for="(filter, index) in filters"
:key="index"
v-model="filterForm[filter.prop]"
v-bind="filter.attrs"
class="filter-item"
>
<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>
<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>
</div>
<el-table :key="tableKey" v-loading="listLoading" :data="list" style="width: 100%;">
<el-table-column v-for="col in columns" :key="col.prop" v-bind="col" />
<el-table-column v-if="operates.length" v-bind="actionsColumn">
<template slot-scope="scope">
<template v-for="(btn, index) in operates">
<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="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>0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.size"
@pagination="getList"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
export default {
name: 'ComplexTable',
components: { Pagination },
props: {
columns: { type: Array, default: () => [] },
operates: { type: Array, default: () => [] },
filters: { type: Array, default: () => [] },
crud: { type: String, default: 'crud' },
actionsColumn: {
type: Object,
default: () => ({
label: '操作',
align: 'center'
})
},
api: [Function, Object]
},
data() {
return {
filterForm: {},
tableKey: 0,
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
size: 20
}
}
},
created() {
this.initFilters()
this.getList()
},
methods: {
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.records
this.total = response.data.total
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
}
}
}
</script>
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scroll-to'
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>
<!--
* @Descripttion: 数据化表单
* @version: 1.0.0
* @Author: 仲灏 Izhaong<164165005@qq.com>
* @Date: 2020-06-28 14:50:34
* @LastEditors: 仲灏 Izhaong<164165005@qq.com>
* @LastEditTime: 2020-06-30 10:49:16
-->
<template>
<el-form
ref="formData"
:model="formData"
v-bind="attrs"
>
<el-form-item v-for="item in items" :key="item.attrs.prop" v-bind="item.attrs">
<template>
<!-- eslint-disable-next-line vue/require-component-is -->
<component v-model="formData[item.attrs.prop]" v-bind="item.component">
<template v-if="item.compTemps && item.compTemps.length">
<component :is="temp.is" v-for="temp in item.compTemps" :key="`${item.attrs.prop}_${temp.key}`" :label="temp.label" :value="temp.value">{{ temp.content }}</component>
</template>
</component>
</template>
</el-form-item>
<el-form-item v-if="showActions">
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'FormGernerate',
props: {
attrs: { type: Object, default: () => ({
'label-position': 'right',
'label-width': '100px',
size: 'mini'
}) },
items: { type: Array, default: () => [] },
showActions: { type: Boolean, default: false }
},
data() {
return {
formData: {}
}
},
created() {
this.initForm()
},
methods: {
submitForm() {
this.$refs.formData.validate((valid) => {
if (valid) {
this.$emit('submit', this.formData)
} else {
console.log(valid)
return false
}
})
},
resetForm() {
this.$refs.formData.resetFields()
},
initForm() {
const props = this.items.map(i => i.attrs.prop)
props.forEach(k => {
this.$set(this.formData, k, undefined)
})
},
resetFormData() {
const props = this.items.map(i => i.attrs.prop)
props.forEach(k => {
this.formData[k] = undefined
})
}
}
}
</script>
/*
* @Descripttion: 管理后台用户接口
* @version: 1.0.0
* @Author: 仲灏 Izhaong<[email protected]>
* @Date: 2020-06-24 17:18:03
* @LastEditors: 仲灏 Izhaong<[email protected]>
* @LastEditTime: 2020-06-30 10:32:36
*/
import request from '@/utils/request'
const userUrl = '/api/user'
...
export function getUserList(params = { page: 1, size: 20 }) {
return request({
url: userUrl + '/list',
method: 'get',
params
})
}
export function createUser(data) {
return request({
url: userUrl,
method: 'post',
data
})
}
export function updateUser(data) {
return request({
url: userUrl,
method: 'put',
data
})
}
export function deleteUser(params) {
return request({
url: userUrl,
method: 'delete',
params
})
}
深拷贝网上一大堆这里就不放了
/**
* @Descripttion: 获取发生变动的对象属性并组成一个新的对象 Obj1 >= obj2 >= diffObj
* @Author: 仲灏 Izhaong<[email protected]>
* @param {Object} obj1 大对象
* @param {Object} obj2 小对象
* @return: 小对象中的属性值不等于大对象中属性值组合的对象
* @LastEditors: 仲灏 Izhaong<[email protected]>
* @LastEditTime: Do not Edit
*/
export const getDiffObj = (obj1, obj2) => {
const diffObj = {}
const keysArr1 = Object.keys(obj1)
keysArr1.forEach(key => {
if (obj1[key] !== obj2[key]) diffObj[key] = obj2[key]
})
return diffObj
}