用户列表应该是管理后台最简单的一个列表,数据相对简单,所以今天就从vue-element-template的用户列表开始,实现下图的效果,
功能包括:用户列表,搜索,新建和编辑。
用户数据:用户名,类型,有效期,备注。
首先,使用mock.js模拟api返回数据,文件路径:root/mock/member.js
import Mock from 'mockjs'
const data = Mock.mock({
'items|50': [
{
id: '@id',
username: '@name',
'type|1': ['normal', 'vip', 'admin'],
timestamp: +Mock.Random.date('T'),
pageviews: '@integer(300, 5000)'
}
]
})
export default [
{
url: '/vue-admin-template/member/list',
type: 'get',
response: config => {
const { type, username, page = 1, limit = 20, sort } = config.query
let mockList = data.items.filter(item => {
if (type && item.type !== type) return false
if (username && item.username.indexOf(username) < 0) return false
return true
})
if (sort === '-id') {
mockList = mockList.reverse()
}
const pageList = mockList.filter(
(item, index) => index < limit * page && index >= limit * (page - 1)
)
return {
code: 20000,
data: {
total: mockList.length,
items: pageList
}
}
}
},
{
url: '/vue-admin-template/member/detail',
type: 'get',
response: config => {
const { id } = config.query
for (const memeber of data.items) {
if (memeber.id === +id) {
return {
code: 20000,
data: memeber
}
}
}
}
},
{
url: '/vue-admin-template/member/create',
type: 'post',
response: _ => {
return {
code: 20000,
data: 'success'
}
}
},
{
url: '/vue-admin-template/member/update',
type: 'post',
response: _ => {
return {
code: 20000,
data: 'success'
}
}
}
]
然后再建一个文件root/src/api/member.js,放用户操作的相关方法
import request from '@/utils/request'
export function fetchList(params) {
console.log('api')
return request({
url: '/vue-admin-template/member/list',
method: 'get',
params: params
})
}
export function fetchMember(id) {
return request({
url: 'vue-admin-template/member/detail',
method: 'get',
params: { id }
})
}
export function createMember(data) {
return request({
url: '/vue-admin-template/member/create',
method: 'post',
data
})
}
export function updateMember(data) {
return request({
url: '/vue-admin-template/member/update',
method: 'post',
data
})
}
最后,在views/member/index.vue中,渲染用户列表。代码有点长,但都是vue的基础用户,不难理解。
列表使用elementUI的el-table-column组件
新建和编辑窗口使用el-dialog组件
<template>
<div class="app-container">
<div class="filter-container" style="margin-bottom:10px">
<el-input
v-model="listQuery.username"
placeholder="昵称"
style="width: 200px"
class="filter-item"
@keyup.enter.native="handleFilter"
/>
<el-select
v-model="listQuery.type"
placeholder="类型"
clearable
class="filter-item"
style="width: 130px"
>
<el-option
v-for="item in userTypeOptions"
:key="item.key"
:label="item.display_name+'('+item.key+')'"
:value="item.key"
/>
</el-select>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">搜索</el-button>
<el-button
class="filter-item"
style="margin-left: 10px"
type="primary"
icon="el-icon-edit"
@click="handleCreate"
>新建</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
>
<el-table-column align="center" label="ID" width="95">
<template slot-scope="scope">{{ scope.$index }}</template>
</el-table-column>
<el-table-column label="用户名">
<template slot-scope="scope">{{ scope.row.username }}</template>
</el-table-column>
<el-table-column label="类型" width="110" align="center">
<template slot-scope="scope"><span>{{ scope.row.type }}</span></template>
</el-table-column>
<el-table-column label="Date" width="150px" align="center">
<template slot-scope="{row}">
<span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
<template slot-scope="{ row, $index }">
<el-button type="primary" size="mini" @click="handleUpdate(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="fetchData"
/>
<!-- 弹出对话框,用于新建用户 -->
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form
ref="dataForm"
:rules="rules"
:model="temp"
label-position="left"
label-width="70px"
style="width: 400px margin-left:50px"
>
<el-form-item label="昵称" prop="username">
<el-input v-model="temp.username" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="temp.password" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select
v-model="temp.type"
class="filter-item"
placeholder="Please select"
><el-option v-for="item in userTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" /></el-select>
</el-form-item>
<el-form-item label="有效期" prop="timestamp">
<el-date-picker
v-model="temp.timestamp"
type="datetime"
placeholder="Please pick a date"
/>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="temp.remark"
:autosize="{ minRows: 2, maxRows: 4 }"
type="textarea"
placeholder="Please input"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">确定</el-button>
</div>
</el-dialog>
<!-- 弹出对话框,用于新建用户 end-->
</div>
</template>
<script>
import { fetchList, createMember, updateMember } from '@/api/member'
import { parseTime } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
const userTypeOptions = [
{ key: 'normal', display_name: '普通会员' },
{ key: 'vip', display_name: 'VIP' },
{ key: 'admin', display_name: '管理员' }
]
export default {
components: { Pagination },
filters: {
statusFilter(status) {
const statusMap = { published: 'success', draft: 'gray', deleted: 'danger'
}
return statusMap[status]
},
parseTime(time, cFormat) {
return parseTime(time, cFormat)
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: { page: 1, limit: 20, username: undefined, type: undefined, sort: '+id'
},
userTypeOptions,
sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }
],
temp: { id: undefined, remark: '', timestamp: new Date(), username: '', password: '', type: ''},
dialogFormVisible: false,
dialogStatus: '',
textMap: { update: '编辑', create: '新建' },
// 编辑用户信息-表单验证
rules: {
username: [{
required: true, message: 'username is required', trigger: 'blur' }],
password: [{ required: true, message: 'password is required', trigger: 'blur' }],
type: [{ required: true, message: 'type is required', trigger: 'change' }],
timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }]
}
}
},
created() {
this.fetchData()
},
methods: {
// get memebers list
fetchData() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
setTimeout(() => { this.listLoading = false }, 500)
})
},
handleFilter() {
console.log('search', this.listQuery.username)
this.fetchData()
},
// open the dialog
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
// add a memeber
createData() {
this.$refs['dataForm'].validate(valid => {
if (valid) {
this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
this.temp.author = 'vue-element-admin'
createMember(this.temp).then(() => {
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title: 'Success',
message: 'Created Successfully',
type: 'success',
duration: 2000 })
})
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate(valid => {
if (valid) {
const tempData = Object.assign({}, this.temp)
tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
updateMember(tempData).then(() => {
const index = this.list.findIndex(v => v.id === this.temp.id)
this.list.splice(index, 1, this.temp)
this.dialogFormVisible = false
this.$notify({
title: 'Success',
message: 'Update Successfully',
type: 'success',
duration: 2000
})
})
}
})
},
resetTemp() {
this.temp = { id: undefined, remark: '', timestamp: new Date(), title: '', type: ''
}
}
}
}
</script>