文章目录
目标代码0.结构1.按钮-删除2.按钮-编辑3.debug4.样式5.分页Pagination:功能6.分页Pagination:样式7.搜索框:功能8.搜索框:样式
总效果总代码修改和创建的文件User.vueapi下的index.jsapi下的mock.jsapi下的user.js
上一篇:
【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出
参考视频:VUE项目,VUE项目实战,vue后台管理系统,前端面试,前端面试项目
案例链接【前端】Vue+Element UI案例:通用后台管理系统-导航栏(视频p1-16)https://blog.csdn.net/karshey/article/details/127640658【前端】Vue+Element UI案例:通用后台管理系统-Header+导航栏折叠(p17-19)https://blog.csdn.net/karshey/article/details/127652862【前端】Vue+Element UI案例:通用后台管理系统-Home组件:卡片、表格(p20-22)https://blog.csdn.net/karshey/article/details/127674643【前端】Vue+Element UI案例:通用后台管理系统-Echarts图表准备:axios封装、mock数据模拟实战(p23-25)https://blog.csdn.net/karshey/article/details/127735159【前端】Vue+Element UI案例:通用后台管理系统-Echarts图表:折线图、柱状图、饼状图(p27-30)https://blog.csdn.net/karshey/article/details/127737979【前端】Vue+Element UI案例:通用后台管理系统-面包屑、tag栏(p31-35)https://blog.csdn.net/karshey/article/details/127756733【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出(p36-38)https://blog.csdn.net/karshey/article/details/127787418【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Table表格增删查改、Pagination分页、搜索框(p39-42)https://blog.csdn.net/karshey/article/details/127777962【前端】Vue+Element UI案例:通用后台管理系统-登陆页面Login(p44)https://blog.csdn.net/karshey/article/details/127795302【前端】Vue+Element UI案例:通用后台管理系统-登陆页面功能:登录权限跳转、路由守卫、退出(p45-46)https://blog.csdn.net/karshey/article/details/127849502【前端】Vue+Element UI案例:通用后台管理系统-登陆不同用户显示不同菜单、动态添加路由(p47-48)https://blog.csdn.net/karshey/article/details/127865621【前端】Vue+Element UI案例:通用后台管理系统-项目总结https://blog.csdn.net/karshey/article/details/127867638
目标
总体是这样:
![在这里插入图片描述](https://img-blog.csdnimg.cn/132e3e261f0c4bd2a650fc2ed72cfa56.png)
需求:本篇主要是Table表格增删查改、Pagination分页。上篇已经完成了表单、对话框弹出。
表格Table显示用户数据用户数据由Mock随机生成,提供后端接口删除:可以删除用户数据,弹出消息提示搜索框:可以搜索用户数据分页效果
代码
0.结构
上篇已经完成了提交功能,但由于现在还没有表格,不知道自己写的对不对。我们先迅速地用组件把表格写出来。
当el-table元素中注入data对象数组后,在el-table-column中用prop属性来对应对象中的键名即可填入数据,用label属性来定义表格的列名。可以使用width属性来定义列宽。
![在这里插入图片描述](https://img-blog.csdnimg.cn/436a3b6366c1490e9975ba80e46da93a.png)
效果:
我们先测试一下提交功能:
提交本身没问题,但细节有点问题:我们要规范一下出生日期的格式。
代码:添加value-format="yyyy-MM-DD"
1.按钮-删除
需求:
![在这里插入图片描述](https://img-blog.csdnimg.cn/e8fc48422f784f588b4b40d3ed771cee.png)
自定义列:列名操作按钮:编辑、删除点击后有弹窗
自定义列:
![在这里插入图片描述](https://img-blog.csdnimg.cn/ad49ae0393754cd28eef8dbd458ab586.png)
编辑
删除
点击后的弹窗功能:
methods如下:
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
效果:删除数据+弹窗。
![在这里插入图片描述](https://img-blog.csdnimg.cn/06245997a5a14eaebdd9d9743c949e53.png)
2.按钮-编辑
需求:
点击编辑后:打开表单把当前用户信息填上去(深拷贝)
显然会调用update的接口。
表单有新建和编辑两种打开方式:
新建:打开空表单,左上角为“新建”,调用接口为create编辑:打开表单,自动填进当前用户信息,左上角为“编辑”,调用接口为update
因此我们用一个变量modalType来表示是新建(0)还是编辑(1)。
代码:点击编辑按钮后标记modalType为1,打开表单,进行深拷贝。
// 编辑按钮
handleEdit(index) {
this.modalType = 1
this.openForm()
// 深拷贝
this.form = JSON.parse(JSON.stringify(index))
},
// 打开表单
openForm() {
this.dialogVisible = true
}
接下来的逻辑判断就由submit来实现:若modalType为0,则新增;否则修改:
// 表单提交
submit() {
// 要用箭头函数,若用function会报错,不知道为什么
this.$refs.form.validate((valid) => {
// 符合校验
if (valid) {
// 提交数据
if (this.modalType === 0) {
// 新增
createUser(this.form).then(() => {
this.getList()
})
} else {
// 编辑
updateUser(this.form).then(() => {
this.getList()
})
}
// 清空,关闭
this.closeDialog()
}
})
}
效果:确实显示在了表单中。显然还有一些问题,比如日期显示的是错误的,性别显示的是数字。我们在下一节修改它。
![](https://img-blog.csdnimg.cn/4547ebca4fe14d16a534e3dc166bf794.png)
3.debug
debug:
点击新建则表单左上角显示新建,点击编辑则表单左上角显示编辑性别显示“男女”而非10
新建与编辑:
性别显示:
{{ scope.row.sex == 1 ? '男' : '女' }}
效果:在表格中的性别显示已经改过来了。至于为什么年龄匹配不上,且名为娟的会是男性…因为数据全是随机生成的(详见user.js中的mock.random)
但是出现了新的问题,点击编辑后自动填入的性别还是1和0,为什么呢?
原因:后端返回的数据中sex是integer(0,1)
for (let i = 0; i
id: Mock.Random.guid(),
name: Mock.Random.cname(),
addr: Mock.mock('@county(true)'),
'age|18-60': 1,
birth: Mock.Random.date(),
sex: Mock.Random.integer(0, 1)
})
)
}
而前端的表单中的value是String:
解决方法:动态绑定value,让它可以更改:
效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/a84a62980f0d46a1b94e15c12cb7e5b9.png)
4.样式
当前样式:鼠标滚轮往下滑,可以看到表格的所有数据都显示在一页。许多数据会把table的高度撑的很大。我们要限制table的宽高。
![在这里插入图片描述](https://img-blog.csdnimg.cn/b9e09f36d5034e91bbfa096671e249ae.png)
在html中增加Attribute:height
![在这里插入图片描述](https://img-blog.csdnimg.cn/a4c7d05dae3441a3af45706a75c9f14c.png)
这样会受控于外部样式,所以我们要给外部div也写上样式。
最大的div的高度占浏览器100%,common-table高度为父级的90%,于是el-table表格的高度为common-table的90%。
.manage {
height: 100%;
.common-table {
height: 90%;
}
}
效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/c5490ff1d4ba414386e4b20c81f56891.png)
5.分页Pagination:功能
![在这里插入图片描述](https://img-blog.csdnimg.cn/1dfa848b06b944f48fc65df081f3df95.png)
这只是结构,我们还要完成点击对应页码就跳转到对应Table页的效果:
参数为点击的页码:
// 改变页码
currentChange(val){
console.log(val);
}
到这里我们可以确定:我们能通过currentChange获取到点击的页码,至于如何完成跳转呢?由于表格中显示哪些数据由getList这个函数决定,而getList又是由后端接口中的getUserList决定的: 我们可以看一下后端的接口文档。
/**
* 获取列表
* 要带参数 name, page, limt; name可以不填, page,limit有默认值。
* @param name, page, limit
* @return {{code: number, count: number, data: *[]}}
*/
getUserList: config => {
const { name, page = 1, limit = 20 } = param2Obj(config.url)
// console.log('name:' + name, 'page:' + page, '分页大小limit:' + limit)
const mockList = List.filter(user => {
if (name && user.name.indexOf(name) === -1 && user.addr.indexOf(name) === -1) return false
return true
})
const pageList = mockList.filter((item, index) => index = limit * (page - 1))
return {
code: 20000,
count: mockList.length,
list: pageList
}
}
所以page页码数可以当作参数传给getUser。由注释可知:要带参数 name, page, limt; name可以不填, page,limit有默认值。显然传入的是一个对象。于是我们在data中定义这个对象:
page:要展示第page页limit:每页limit条数据,这里是默认值
pageData:{
page:1,
limit:20
}
每次点击分页时就修改pageData.page,然后把它传给getUser。
// 获取列表数据
getList() {
// 由接口文档知传入一个对象
getUser({params:{...this.pageData}}).then((data) => {
this.tableData = data.data.list
})
}
效果:数据竟然消失了?
看一下控制台,原来错误出在::8081/api/user/get?page=1&limit=20:1 Failed to load resource: the server responded with a status of 404 (Not Found)
由于我们之前定义的getUser是get方法,它的参数是会在url里的,而我们在mock里定义的:
Mock.mock('/api/user/get',user.getUserList)
是写死的,/api/user/get?page=1&limit=20与/api/user/get是匹配不上的!
解决方法:正则表达式。
Mock.mock(/\/api\/user\/get/,user.getUserList)
getList:
getList() {
// 由接口文档知传入一个对象
getUser({ params: { ...this.pageData } }).then((data) => {
this.tableData = data.data.list
this.total = data.data.count || 0
})
}
改变页码:
// 改变页码
currentChange(val) {
this.pageData.page = val
this.getList()
}
效果:
第一页: 第三页:
![在这里插入图片描述](https://img-blog.csdnimg.cn/b999ca4a7fbd4b51a89d1aa444526819.png)
6.分页Pagination:样式
右下角。子绝父相。
.common-table {
height: 90%;
position: relative;
.pager {
position: absolute;
right:20px;
bottom: 0;
}
}
效果:(忽略搜索框!分页的样式是我最后写的…)
![在这里插入图片描述](https://img-blog.csdnimg.cn/ec004f445ff94753bceb9838977ee1da.png)
7.搜索框:功能
搜索框,但是用的是表单组件: html:
查询
由于table显示的数据是本页数据+搜索到的数据的交集,我们要修改getList:
getList() {
// 由接口文档知传入一个对象:要返回的是当前页面数据和搜索到的数据的交集
getUser({ params: { ...this.pageData,...this.searchForm } }).then((data) => {
this.tableData = data.data.list
this.total = data.data.count || 0
})
}
但凡触发了搜索事件:重新获取table数据即可。
search(){
this.getList()
}
效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/26f60b8582874c87a2d02a3653a28942.png)
8.搜索框:样式
让新建按钮和搜索框并排:flex。 让搜索框的输入框和按钮并排:inline。
html:
css:
.manage {
height: 100%;
.manage-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.common-table {
height: 90%;
}
}
效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/857d9afe703c41778ab4ef54cff00eaf.png)
总效果
![在这里插入图片描述](https://img-blog.csdnimg.cn/1cf48ad42ff04a66871304d7b39c9e54.png)
总代码
修改和创建的文件
![在这里插入图片描述](https://img-blog.csdnimg.cn/ed21c25daedb4c1b9049fb90231cc2e8.png)
User.vue
+ 新增
取 消
确 定
查询
{{ scope.row.sex == 1 ? '男' : '女' }}
编辑
删除
import { getUser, createUser, deleteUser, updateUser } from '../api/index'
export default {
data() {
return {
// 表单绑定的数据
form: {
name: '',
age: '',
sex: '',
birth: '',
addr: ''
},
// 表单验证规则
rules: {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
age: [{ required: true, message: '请输入年龄', trigger: 'blur' }],
sex: [{ required: true, message: '请输入性别', trigger: 'blur' }],
birth: [{ required: true, message: '请输入日期', trigger: 'blur' }],
addr: [{ required: true, message: '请输入地址', trigger: 'blur' }],
},
// 表单是否打开
dialogVisible: false,
// 列表数据
tableData: [],
// 打开表单:新建0,编辑1
modalType: 0,
// 分页的对象
pageData: {
page: 1,
limit: 20
},
// 分页页数
total: 0,
// 搜索框表单
searchForm: {
name: ''
}
}
},
methods: {
// 获取列表数据
getList() {
// 由接口文档知传入一个对象:要返回的是当前页面数据和搜索到的数据的交集
getUser({ params: { ...this.pageData, ...this.searchForm } }).then((data) => {
this.tableData = data.data.list
this.total = data.data.count || 0
})
},
// 表单提交
submit() {
// 要用箭头函数,若用function会报错,不知道为什么
this.$refs.form.validate((valid) => {
// 符合校验
if (valid) {
// 提交数据
if (this.modalType === 0) {
// 新增
createUser(this.form).then(() => {
this.getList()
})
} else {
// 编辑
updateUser(this.form).then(() => {
this.getList()
})
}
// 清空,关闭
this.closeDialog()
}
})
},
// 关闭对话框
closeDialog() {
// 先重置
this.$refs.form.resetFields()
// 后关闭
this.dialogVisible = false
},
// 编辑按钮
handleEdit(index) {
this.modalType = 1
this.openForm()
// 深拷贝
this.form = JSON.parse(JSON.stringify(index))
},
// 删除按钮
handleDelete(index) {
this.$confirm('此操作将永久删除该用户, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 删除操作:根据后端接口,参数是对象,id是唯一标识符
deleteUser({ id: index.id }).then(() => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.getList()
});
}).catch(() => {
// 点击取消:不删除了
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
// 新建按钮
handlecreate() {
this.modalType = 0
this.openForm()
},
// 打开表单
openForm() {
this.dialogVisible = true
},
// 改变页码
currentChange(val) {
this.pageData.page = val
this.getList()
},
// 搜索
search() {
this.getList()
}
},
mounted() {
this.getList()
}
}
.manage {
height: 100%;
.manage-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.common-table {
height: 90%;
position: relative;
.pager {
position: absolute;
right:20px;
bottom: 0;
}
}
}
api下的index.js
import http from '../utils/request'
// 请求首页数据,直接把这个对象导出
export const getData = () => {
// 返回一个promise
return http.get('/home/getData')
}
// 下面四个:用户管理-后端-网络请求接口
export const getUser = (params) => {
return http.get('/user/get/', params)
}
export const createUser = (data) => {
return http.post('/user/create', data)
}
export const deleteUser = (data) => {
return http.post('/user/del', data)
}
export const updateUser = (data) => {
return http.post('/user/update', data)
}
api下的mock.js
import Mock from 'mockjs'
import homeMock from '../api/mockServe/home'
import user from './user'
// 定义mock拦截
Mock.mock('/api/home/getData',homeMock)
// 用户管理:增删查改
Mock.mock(/\/api\/user\/get/,user.getUserList)
Mock.mock('/api/user/create','post',user.createUser)
Mock.mock('/api/user/update','post',user.updateUser)
Mock.mock('/api/user/del','post',user.deleteUser)
api下的user.js
import Mock from 'mockjs'
// get请求从config.url获取参数,post从config.body中获取参数
function param2Obj (url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
)
}
let List = []
const count = 200
for (let i = 0; i
id: Mock.Random.guid(),
name: Mock.Random.cname(),
addr: Mock.mock('@county(true)'),
'age|18-60': 1,
birth: Mock.Random.date(),
sex: Mock.Random.integer(0, 1)
})
)
}
export default {
/**
* 获取列表
* 要带参数 name, page, limt; name可以不填, page,limit有默认值。
* @param name, page, limit
* @return {{code: number, count: number, data: *[]}}
*/
getUserList: config => {
const { name, page = 1, limit = 20 } = param2Obj(config.url)
// console.log('name:' + name, 'page:' + page, '分页大小limit:' + limit)
const mockList = List.filter(user => {
if (name && user.name.indexOf(name) === -1 && user.addr.indexOf(name) === -1) return false
return true
})
const pageList = mockList.filter((item, index) => index = limit * (page - 1))
return {
code: 20000,
count: mockList.length,
list: pageList
}
},
/**
* 增加用户
* @param name, addr, age, birth, sex
* @return {{code: number, data: {message: string}}}
*/
createUser: config => {
const { name, addr, age, birth, sex } = JSON.parse(config.body)
console.log(JSON.parse(config.body))
List.unshift({
id: Mock.Random.guid(),
name: name,
addr: addr,
age: age,
birth: birth,
sex: sex
})
return {
code: 20000,
data: {
message: '添加成功'
}
}
},
/**
* 删除用户
* @param id
* @return {*}
*/
deleteUser: config => {
const { id } = JSON.parse(config.body)
if (!id) {
return {
code: -999,
message: '参数不正确'
}
} else {
List = List.filter(u => u.id !== id)
return {
code: 20000,
message: '删除成功'
}
}
},
/**
* 批量删除
* @param config
* @return {{code: number, data: {message: string}}}
*/
batchremove: config => {
let { ids } = param2Obj(config.url)
ids = ids.split(',')
List = List.filter(u => !ids.includes(u.id))
return {
code: 20000,
data: {
message: '批量删除成功'
}
}
},
/**
* 修改用户
* @param id, name, addr, age, birth, sex
* @return {{code: number, data: {message: string}}}
*/
updateUser: config => {
const { id, name, addr, age, birth, sex } = JSON.parse(config.body)
const sex_num = parseInt(sex)
List.some(u => {
if (u.id === id) {
u.name = name
u.addr = addr
u.age = age
u.birth = birth
u.sex = sex_num
return true
}
})
return {
code: 20000,
data: {
message: '编辑成功'
}
}
}
}
|