序
前言:
本来搭建好Vue和Springboot前后端框架之后,就不想重复CRUD基础
表格的增删改查,觉得ajax
是互通的,没有必要再重复重复这些工作。因为,一个小项目要善始善终,不能只有登录就没有后续了。尝试一下Element-ui表格,下拉框,进度条
等;Vue引入echarts
或其他样式;就不会知道其中的bug和坑,所以,现在开始挖坑…
思路:
参考任意一个后台管理模板的内容:
- 主界面:大概是以下内容,表格、图表、进度条
- 分目录:
- 表格:基础的CRUD、分页,更多有导出表格Excel等。
更多内容参考:林鑫的demo
- 表格:基础的CRUD、分页,更多有导出表格Excel等。
本文目标: 在之前vue+Springboot的基础上,实现基础表格,echarts
,话不多说,开始挖坑~
提前预告:
用过:Element-table表格+Pagination 分页
之后,
? ElementUI是目前博主用过最赞的前端表格处理工具,没有之一 ?
正文
一、前端设计
1. element-Table 表格
根据官网api文档,综合了一下表格功能:
左侧全选和反选,日期筛选和排序,标签筛选,关键词搜索,自定义编辑和删除按钮
(当然了,这仅是前端界面,根据文档,第一次写,差不多一个小时能全部搞懂,UI框架nice~)
静态页面UserList.vue
<template>
<div>
<el-button @click="toggleSelection([tableData[1], tableData[2]])">切换第二、第三行的选中状态</el-button>
<el-button @click="toggleSelection()">取消选择</el-button>
<el-button @click="resetDateFilter">清除日期过滤器</el-button>
<el-button @click="clearFilter">清除所有过滤器</el-button>
<el-table ref="filterTable" :data="tableData.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))" :default-sort="{prop: 'date', order: 'descending'}" border
tooltip-effect="dark" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column prop="date" label="日期" sortable width="180" column-key="date" :filters="[{text: '2016-05-01', value: '2016-05-01'}, {text: '2016-05-02', value: '2016-05-02'}, {text: '2016-05-03', value: '2016-05-03'}, {text: '2016-05-04', value: '2016-05-04'}]"
:filter-method="filterHandler">
</el-table-column>
<el-table-column prop="name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="address" label="地址" :formatter="formatter">
</el-table-column>
<el-table-column prop="tag" label="标签" width="100" :filters="[{ text: '家', value: '家' }, { text: '公司', value: '公司' }]"
:filter-method="filterTag" filter-placement="bottom-end">
<template slot-scope="scope">
<el-tag :type="scope.row.tag === '家' ? 'primary' : 'success'" disable-transitions>{{scope.row.tag}}</el-tag>
</template>
</el-table-column>
<el-table-column
align="right">
<template slot="header" slot-scope="scope">
<el-input
v-model="search"
size="mini"
placeholder="输入姓名关键字搜索"/>
</template>
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">Edit</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">Delete</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄',
tag: '家'
}, {
date: '2016-05-02',
name: '李小虎',
address: '上海市普陀区金沙江路 1517 弄',
tag: '公司'
}, {
date: '2016-05-03',
name: '张小虎',
address: '上海市普陀区金沙江路 1519 弄',
tag: '家'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄',
tag: '公司'
}],
multipleSelection: [],
search: ''
}
},
methods: {
//多行全选和反选
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.filterTable.toggleRowSelection(row);
});
} else {
this.$refs.filterTable.clearSelection();
}
},
handleSelectionChange(val) {//反选
this.multipleSelection = val;
},
//排序
formatter(row, column) {
return row.address;
},
//标签查找和清除标签
resetDateFilter() {
this.$refs.filterTable.clearFilter('date');
},
clearFilter() {
this.$refs.filterTable.clearFilter();
},
filterTag(value, row) {
return row.tag === value;
},
filterHandler(value, row, column) {
const property = column['property'];
return row[property] === value;
},
//编辑
handleEdit(index, row) {
console.log(index, row);
},
//删除
handleDelete(index, row) {
console.log(index, row);
}
}
}
</script>
2. element-Pagination 分页
<template>
<div class="block">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage"
:page-sizes="[10, 20, 30, 40]" :page-size="10" layout="total, sizes, prev, pager, next, jumper" :total="40" background>
</el-pagination>
</div>
</template>
<script>
export default {
methods: {
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
}
},
data() {
return {
currentPage: 4
};
}
}
</script>
二、结合后端
后端用springboot结合aop封装:【Springboot学习 | 6】Lombok优雅的编码+Aop异常统一管理
返回:code、data、message、success
主要Ajax:
created() {
this.getUserList();
},
methods: {
getUserList() {
this.$ajax.get('/user/aop2').then((res) => { //axios发送get请求
if (res.data) {
console.log(res.data)
this.userList = res.data.data;
} else {
alert("查询失败")
}
})
}
}
完整userList.vue
:
- 注意data的名称
<template>
<div>
<el-button @click="toggleSelection([userList[1], userList[2]])">切换第二、第三行的选中状态</el-button>
<el-button @click="toggleSelection()">取消选择</el-button>
<el-button @click="clearFilter">清除所有过滤器</el-button>
<el-table ref="filterTable" :data="userList.slice((currentPage-1)*pagesize,currentPage*pagesize).filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))"
:default-sort="{prop: 'id', order: 'ascending'}" border tooltip-effect="dark" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column prop="id" label="id" sortable width="180">
</el-table-column>
<el-table-column prop="name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="age" label="年龄" width="180">
</el-table-column>
<el-table-column prop="address" label="地址" width="180" :filters="[{ text: '重庆', value: '重庆' }, { text: '北京', value: '北京' },{ text: '上海', value: '上海' }]"
:filter-method="filterTag" filter-placement="bottom-end">
<template slot-scope="scope">
<el-tag :type="scope.row.address === '重庆' ? 'primary' : 'success'" disable-transitions>{{scope.row.address}}</el-tag>
</template>
</el-table-column>
<el-table-column align="right">
<template slot="header" slot-scope="scope">
<el-input v-model="search" size="mini" placeholder="输入姓名关键字搜索" />
</template>
<template slot-scope="scope">
<el-button size="mini" @click="handleEdit(scope.$index, scope.row)">Edit</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">Delete</el-button>
</template>
</el-table-column>
</el-table>
<div class="block">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage"
:page-sizes="[10, 20, 30, 40]" :page-size="pagesize" layout="total, sizes, prev, pager, next, jumper" :total="userList.length"
background>
</el-pagination>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userList: [],
multipleSelection: [], //全选和反选标记
search: '', //关键词查找
currentPage: 1 ,//最初显示默认要跳转的一页
pagesize:10
}
},
created() {
this.getUserList();
},
methods: {
//多行全选和反选
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.filterTable.toggleRowSelection(row);
});
} else {
this.$refs.filterTable.clearSelection();
}
},
handleSelectionChange(val) { //反选
this.multipleSelection = val;
},
//排序
formatter(row, column) {
return row.address;
},
//标签查找和清除标签
resetDateFilter() {
this.$refs.filterTable.clearFilter('address');
},
clearFilter() {
this.$refs.filterTable.clearFilter();
},
filterTag(value, row) {
return row.address === value;
},
filterHandler(value, row, column) {
const property = column['property'];
return row[property] === value;
},
//编辑
handleEdit(index, row) {
console.log(index, row);
},
//删除
handleDelete(index, row) {
console.log(index, row);
},
//分页
handleSizeChange(size) {
console.log(`每页 ${size} 条`);
this.pagesize = size;
},
handleCurrentChange(currentPage) {
console.log(`当前页: ${currentPage}`);
this.currentPage = currentPage;
},
getUserList() {
this.$ajax.get('/user/aop2').then((res) => { //axios发送get请求
if (res.data) {
console.log(res.data)
this.userList = res.data.data;
} else {
this.$message({
type: 'error',
message: '查询失败',
showClose: true
})
}
})
}
}
}
</script>
最终效果: 以下全部根据官网教程写出来的,稍稍改动整合:
-
关键词查找(姓名模糊查询)
通过设置Scoped slot
来自定义表头。
search方法也比较简单,用的分词过滤器类似原理,如果名称是王小虎
,则输入王
即可查询出来:
el-table :data="tableData.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))"
data() { return { userList: [], multipleSelection: [], //全选和反选标记 search: '', //关键词查找 currentPage: 1 ,//最初显示默认要跳转的一页 pagesize:10 } }
-
多选,排序
-
多选: 实现多选非常简单: 手动添加一个
el-table-column
,设type
属性为selection
即可;@selection-change="handleSelectionChange"
是控制反选,改变选择状态;//多行全选和反选 toggleSelection(rows) { if (rows) { rows.forEach(row => { this.$refs.filterTable.toggleRowSelection(row); }); } else { this.$refs.filterTable.clearSelection(); } }, handleSelectionChange(val) { //反选 this.multipleSelection = val; },
-
排序:
el-table :default-sort = "{prop: 'date', order: 'descending'}"
是降序,ascending是升序。//排序 formatter(row, column) { return row.address; },
- 分页效果
除了参考官网:element-Pagination 分页
-
分页函数加上变量:
//分页 handleSizeChange(size) { this.pagesize = size; }, handleCurrentChange(currentPage) { this.currentPage = currentPage; }
-
很重要的一步是在el-table标签中:data属性要加上监听:
userList.slice((currentPage-1)*pagesize,currentPage*pagesize)
- 标签筛选
data() { return { userList: [], multipleSelection: [], //全选和反选标记 search: '', //关键词查找 currentPage: 1 ,//最初显示默认要跳转的一页 pagesize:10 } } 。。。。。。。。。。 //标签查找和清除标签 resetDateFilter() { this.$refs.filterTable.clearFilter('address');//注意对应的标签 }, clearFilter() { this.$refs.filterTable.clearFilter(); }, filterTag(value, row) { return row.address === value; }, filterHandler(value, row, column) { const property = column['property']; return row[property] === value; },
三、遇见问题及解决
-
问题:
error:The template root requires exactly one element.
原因:因为vue的模版中只有能一个根节点,所以在< template>
中插入第二个元素就会报错
解决:将中的元素先用一个< div>
包起来。<template> <div> <el-button>XXX</el-button> <el-table></el-table> </div> </template>
-
中文乱码,解决方法:后端配置application.properties加上
&characterEncoding=UTF8
四、总结&源码
总结:
element-UI框架分页,查询,排序
都是全查询get
到全部信息之后,再对数据进行分页,排序
等:
首先,element-table比之前自己用各原生page-taglib ?、PageHelper分页 ?、LayUI ? 分页方法都要nb‘,大赞Pagination 分页
?
其次,分词 查询、排序、筛选 思想,比之前我自己肝了三天的条件查询,然后再后端控制注入 要科学。
本次学习颇有收获。
源码:
- 前端: Vue-Day4
- 后端:Springboot-Day7
- sql:t_user