管理页面
主要就是mybatis-plus使用,以及前端表单验证。
MyBatis是一款非常热门的数据操作层(持久层)框架。
优点:
- 自定义SQL ,满足所有的复杂查询,方便SQL优化。
- 相对(JPA)来说上手简单一些。
mybatis-plus ?
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。参考mybatis-plus官网。其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。
效果图:
主要依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
直接在配置文件添加配置参数即可:
spring:
......
mybatis-plus:
global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
#刷新mapper 调试神器
refresh-mapper: true
#数据库大写下划线转换
#capital-mode: true
#序列接口实现类配置
#key-generator: com.baomidou.springboot.xxx
#逻辑删除配置
#logic-delete-value: 0
#logic-not-delete-value: 1
#自定义填充策略接口实现
#meta-object-handler: com.umeox.waas.domain.handler.MyMetaObjectHandler
#自定义SQL注入器
#sql-injector: com.baomidou.springboot.xxx
configuration:
map-underscore-to-camel-case: true #entity类字段名映射表字段名
cache-enabled: false
MyBatis-Plus使用:
public interface IService<T> {
/**
* <p>
* 插入一条记录(选择字段,策略插入)
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean insert(T entity);
/**
* <p>
* 插入一条记录(全部字段)
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean insertAllColumn(T entity);
/**
* <p>
* 插入(批量),该方法不适合 Oracle
* </p>
*
* @param entityList 实体对象列表
* @return boolean
*/
boolean insertBatch(List<T> entityList);
/**
* <p>
* 插入(批量)
* </p>
*
* @param entityList 实体对象列表
* @param batchSize 插入批次数量
* @return boolean
*/
boolean insertBatch(List<T> entityList, int batchSize);
/**
* <p>
* 批量修改插入
* </p>
*
* @param entityList 实体对象列表
* @return boolean
*/
boolean insertOrUpdateBatch(List<T> entityList);
/**
* <p>
* 批量修改插入
* </p>
*
* @param entityList 实体对象列表
* @param batchSize
* @return boolean
*/
boolean insertOrUpdateBatch(List<T> entityList, int batchSize);
/**
* <p>
* 批量修改或插入全部字段
* </p>
*
* @param entityList 实体对象列表
* @return boolean
*/
boolean insertOrUpdateAllColumnBatch(List<T> entityList);
/**
* 批量修改或插入全部字段
*
* @param entityList 实体对象列表
* @param batchSize
* @return boolean
*/
boolean insertOrUpdateAllColumnBatch(List<T> entityList, int batchSize);
/**
* <p>
* 根据 ID 删除
* </p>
*
* @param id 主键ID
* @return boolean
*/
boolean deleteById(Serializable id);
/**
* <p>
* 根据 columnMap 条件,删除记录
* </p>
*
* @param columnMap 表字段 map 对象
* @return boolean
*/
boolean deleteByMap(Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,删除记录
* </p>
*
* @param wrapper 实体包装类 {@link Wrapper}
* @return boolean
*/
boolean delete(Wrapper<T> wrapper);
/**
* <p>
* 删除(根据ID 批量删除)
* </p>
*
* @param idList 主键ID列表
* @return boolean
*/
boolean deleteBatchIds(Collection<? extends Serializable> idList);
/**
* <p>
* 根据 ID 选择修改
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean updateById(T entity);
/**
* <p>
* 根据 ID 修改全部字段
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean updateAllColumnById(T entity);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param entity 实体对象
* @param wrapper 实体包装类 {@link Wrapper}
* @return boolean
*/
boolean update(T entity, Wrapper<T> wrapper);
/**
* <p>
* 根据 whereEntity 条件,自定义set值更新记录
* </p>
*
* @param setStr set值字符串
* @param wrapper 实体包装类 {@link Wrapper}
* @return boolean
*/
boolean updateForSet(String setStr, Wrapper<T> wrapper);
/**
* <p>
* 根据ID 批量更新
* </p>
*
* @param entityList 实体对象列表
* @return boolean
*/
boolean updateBatchById(List<T> entityList);
/**
* <p>
* 根据ID 批量更新
* </p>
*
* @param entityList 实体对象列表
* @param batchSize 更新批次数量
* @return boolean
*/
boolean updateBatchById(List<T> entityList, int batchSize);
/**
* <p>
* 根据ID 批量更新全部字段
* </p>
*
* @param entityList 实体对象列表
* @return boolean
*/
boolean updateAllColumnBatchById(List<T> entityList);
/**
* <p>
* 根据ID 批量更新全部字段
* </p>
*
* @param entityList 实体对象列表
* @param batchSize 更新批次数量
* @return boolean
*/
boolean updateAllColumnBatchById(List<T> entityList, int batchSize);
/**
* <p>
* TableId 注解存在更新记录,否插入一条记录
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean insertOrUpdate(T entity);
/**
* 插入或修改一条记录的全部字段
*
* @param entity 实体对象
* @return boolean
*/
boolean insertOrUpdateAllColumn(T entity);
/**
* <p>
* 根据 ID 查询
* </p>
*
* @param id 主键ID
* @return T
*/
T selectById(Serializable id);
/**
* <p>
* 查询(根据ID 批量查询)
* </p>
*
* @param idList 主键ID列表
* @return List<T>
*/
List<T> selectBatchIds(Collection<? extends Serializable> idList);
/**
* <p>
* 查询(根据 columnMap 条件)
* </p>
*
* @param columnMap 表字段 map 对象
* @return List<T>
*/
List<T> selectByMap(Map<String, Object> columnMap);
/**
* <p>
* 根据 Wrapper,查询一条记录
* </p>
*
* @param wrapper 实体对象
* @return T
*/
T selectOne(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper,查询一条记录
* </p>
*
* @param wrapper {@link Wrapper}
* @return Map<String,Object>
*/
Map<String, Object> selectMap(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper,查询一条记录
* </p>
*
* @param wrapper {@link Wrapper}
* @return Object
*/
Object selectObj(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询总记录数
* </p>
*
* @param wrapper 实体对象
* @return int
*/
int selectCount(Wrapper<T> wrapper);
/**
* <p>
* 查询列表
* </p>
*
* @param wrapper 实体包装类 {@link Wrapper}
* @return
*/
List<T> selectList(Wrapper<T> wrapper);
/**
* <p>
* 翻页查询
* </p>
*
* @param page 翻页对象
* @return
*/
Page<T> selectPage(Page<T> page);
/**
* <p>
* 查询列表
* </p>
*
* @param wrapper {@link Wrapper}
* @return
*/
List<Map<String, Object>> selectMaps(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<Object>
*/
List<Object> selectObjs(Wrapper<T> wrapper);
/**
* <p>
* 翻页查询
* </p>
*
* @param page 翻页对象
* @param wrapper {@link Wrapper}
* @return
*/
@SuppressWarnings("rawtypes")
Page<Map<String, Object>> selectMapsPage(Page page, Wrapper<T> wrapper);
/**
* <p>
* 翻页查询
* </p>
*
* @param page 翻页对象
* @param wrapper 实体包装类 {@link Wrapper}
* @return
*/
Page<T> selectPage(Page<T> page, Wrapper<T> wrapper);
}
需要继承的基础接口,有默认的实现。(以菜单为例)
新增操作单个或者数组批量新增。直接调用相应的方法即可
-
修改, 根据ID修改。
只修改一个字段,或者某几个字段。# 不为null的属性都会被修改。 反之如果不想修改的属性直接设置为null即可。 Menu menu = new Menu(); menu.setId(1); menu.setName("新的名称") menuRepository.updateById(menu); # 相当于SQL语句。update menu set name = "新的名称" where id = 1;
不是根据ID修改的怎么处理呢?把所有的按钮设置为无效的。
Menu menu = new Menu(); menu.setStatus(false) udpate(menu, new EntityWrapper<Menu>().eq("type", 1)); # 相当于SQL语句。update menu set status = false where type = 1;
-
查询操作
根据ID查询一个: Menu menu = this.selectById(1); 根据条件查询一个 Menu menu = selectOne(new EntityWrapper<Menu>() .eq("type", 1).last("limit 1")); # last会造成SQL注入的风险。所以这个参数不能是外部传入。 # lt("column",value) ==> column< value; # le("column",value) ==> column<= value; # gt("column",value) ==> column> value; # ge("column",value) ==> column>= value; # between(column, val1, val2) ==> columnbetween val1 and val2; # like(column,value) ==> like "%value%"; # orderBy(column, ture) ==> order By column asc ;false:表示倒序 # selectPage(page) ==> 分页查询 # selectPage(page, new Enw...) ==> 分页查询在家查询条件。
当然也可以自己写SQL语句。多表关联查询时,需要自己定义SQL。
和mybatis使用方式一致。
前端表单校验
<template>
<div class="backdrop" v-loading="loading">
<div v-show="isList">
<el-row>
<el-col :span="10" :offset="1">
<el-input placeholder="请输入内容" clearable v-model="queryObj.value" class="input-with-select">
<el-select v-model="queryObj.type" slot="prepend" placeholder="请选择">
<el-option label="名称" value="name"></el-option>
<el-option label="路径" value="path"></el-option>
</el-select>
<el-button slot="append" type="primary" icon="el-icon-search" @click="loadList"></el-button>
</el-input>
</el-col>
<el-col :span="6">
<el-button v-show="button.save" type="primary" @click="add" icon="el-icon-circle-plus-outline"
class="padding-button"></el-button>
<el-popover v-show="button.delete" placement="top" width="200" v-model="visible2">
<p>确定要删除这些菜单吗?</p>
<br/>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="primary" class="padding-button" @click="visible2 = false">取消
</el-button>
<el-button size="mini" type="danger" class="padding-button" @click="remove">确定</el-button>
</div>
<el-button slot="reference" type="danger" @click="visible2 = true" icon="el-icon-delete"
class="padding-button"></el-button>
</el-popover>
</el-col>
</el-row>
<div class="margin-bottom-10"></div>
<el-table :data="menuses" style="width: 100%" height="590" border
@sort-change="solrLoadMenusess"
@selection-change="electRow">
<!--多选框-->
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column sortable="custom" prop="id" label="ID" ></el-table-column>
<el-table-column sortable="custom" prop="name" label="名称" ></el-table-column>
<el-table-column sortable="custom" prop="url" label="链接" ></el-table-column>
<el-table-column sortable="custom" prop="type" label="类型" >
<template slot-scope="scope">
<el-tag type="success" size="medium" v-if="scope.row.type == 1">菜单</el-tag>
<el-tag type="info" size="medium" v-if="scope.row.type == 2">按钮</el-tag>
</template>
</el-table-column>
<el-table-column sortable="custom" prop="permission" label="权限" ></el-table-column>
<el-table-column label="操作" fixed="right" width="180">
<template slot-scope="scope">
<el-tooltip v-show="button.update" class="item" effect="dark" content="修改菜单" placement="top">
<el-button size="mini" type="primary" @click="update(scope.$index, scope.row)"
icon="el-icon-edit" circle></el-button>
</el-tooltip>
<el-tooltip v-show="button.delete" class="item" effect="dark" content="删除菜单" placement="top">
<el-button size="mini" type="danger" @click="closeMenu(scope.$index, scope.row)"
icon="el-icon-delete" circle></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="pageSizeChange"
@current-change="currentPageChange"
:current-page="queryObj.currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="queryObj.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="queryObj.total">
</el-pagination>
</div>
<div v-show="!isList">
<el-form ref="menu" :model="menu" :rules="rules" label-width="80px" label-position="right"
class="demo-ruleForm" size="mini">
<el-form-item prop="name" label="菜单名称">
<el-col :span="10">
<el-input v-model="menu.name"></el-input>
</el-col>
</el-form-item>
<el-form-item prop="type" label="类型">
<el-col :span="3">
<el-select v-model="menu.type" placeholder="请选择类型">
<el-option label="目录" value="0"></el-option>
<el-option label="菜单" value="1"></el-option>
<el-option label="按钮" value="2"></el-option>
</el-select>
</el-col>
</el-form-item>
<el-form-item prop="path" label="菜单路由" v-if="menu.type == 1">
<el-col :span="10">
<el-input v-model="menu.path"></el-input>
</el-col>
</el-form-item>
<el-form-item label="父级菜单">
<el-col :span="10">
<el-input v-model="menu.parentName" readonly @focus="dialogFormVisible = true"></el-input>
<el-dialog title="选择父级菜单" width="30%"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
:visible.sync="dialogFormVisible">
<el-tree
:data="this.buildMenus()"
@node-click="setParentId"
node-key="id"
:accordion=true
:highlight-current=true
:default-expanded-keys=[menu.parentId]
:current-node-key=menu.parentId
:props="{children: 'childs', label: 'name'}">
</el-tree>
<div slot="footer" class="dialog-footer">
<el-button @click="menu.parentId=0;menu.parentName='';dialogFormVisible = false;">取消
</el-button>
<el-button type="primary" @click="dialogFormVisible = false">确定</el-button>
</div>
</el-dialog>
</el-col>
</el-form-item>
<el-form-item prop="permission" label="菜单权限">
<el-col :span="10">
<el-input v-model="menu.permission"></el-input>
</el-col>
</el-form-item>
<el-form-item label="图标">
<el-col :span="10">
<el-input v-model="menu.icon"></el-input>
</el-col>
</el-form-item>
<el-form-item>
<el-col :span="10">
<el-button type="primary" @click="submitMenu('menu')">提交</el-button>
<el-button @click="notSubmitMenu('menu')">取消</el-button>
</el-col>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
import {Menu} from "@/entity/Menu";
import {Query} from "@/utils/Query";
import {StringUtils} from "@/utils/StringUtils"
@Component({})
export default class MenuMana extends Vue {
# 表单校验规则
rules: object = {
name: [ {required: true, message: '请输入菜单名称', trigger: 'blur'},
{min:2, max:5, message:"限制2~5个字符", trigger: 'blur'}
],
type: [{ required: true, message: '请选择类型', trigger: 'change' }],
permission: [{ validator: (rule: any, value: any, callback: any)=> {
let _this:any = this.$refs["menu"];
if(_this.model.type == "2"){
if (value === "") {
callback(new Error("请输入权限"));
}else {
callback();
}
}else {
callback();
}
}, trigger: 'change' }],
url: [{ required: true, message: '请输入菜单路由', trigger: 'blur' }]
}
loading: boolean = false;
isList: boolean = true;
visible2: boolean = false;
dialogFormVisible: boolean = false;
menuses: Array<Menu> = [];
electMenus: Array<Menu> = [];
allMenus: Array<Menu> = [];
// 初始化菜单信息
menu: Menu = new Menu("", "1", "", "", "0", "");
button: object = {
save: StringUtils.isPermisson('sys:menu:save,sys:menu:select'),
update: StringUtils.isPermisson('sys:menu:update,sys:menu:select'),
delete: StringUtils.isPermisson('sys:menu:delete')
}
queryObj: Query = new Query();
mounted():void{
this.loadList();
this.allMenu().then(resp=>{
console.log("111111111")
});
}
// 加载列表数据
loadList():void {
const _this = this;
_this.loading = true;
_this.axios.get("/menu/list",{params: _this.queryObj})
.then(resp=> {
_this.loading = false;
_this.menuses = resp.data.records;
_this.queryObj.total = resp.data.total;
})
}
// 切换搜索条件
solrLoadMenusess(val: any):void{
this.queryObj.isAsc = val.order == "ascending";
this.queryObj.orderFields = val.prop;
this.loadList();
}
// 切换每页数量
pageSizeChange(val: any):void{
this.queryObj.pageSize = val;
this.loadList();
}
// 切换页面数
currentPageChange(val:any):void{
this.queryObj.currentPage = val;
this.loadList();
}
// 选中的值
electRow(val:any):void{
this.electMenus = val;
}
// ——
// 新增菜单
add():void{
this.isList = false;
this.menu = new Menu("", "1", "", "", "0", "");
}
// 修改菜单
update(index:number, row: Menu):void{
this.isList = false;
this.menu = new Menu("", "1", "", "", "0", "");
this.menu.id = row.id;
this.menu.name = row.name;
this.menu.parentId = row.parentId + "";
this.menu.url = row.url;
this.menu.permission = row.permission;
this.menu.type = row.type + "";
this.menu.icon = row.icon;
this.setMenuParentName();
}
// 设置当前菜单的父级菜单名字
setMenuParentName():void{
let parentName = "";
let id = this.menu.parentId;
this.allMenus.forEach(ele => {
if (ele.id.toString() == id) {
parentName = ele.parentName;
}else if (ele.childs != null) {
ele.childs.forEach(child=>{
if (child.id.toString() == id) {
parentName = child.parentName;
}
})
}
})
this.menu.parentName = parentName;
}
// 批量删除
remove():void{
let _this = this;
_this.visible2 = false;
if (this.electMenus.length == 0) {
_this.$message.error("请选择需要删除的菜单");
return;
}
let arr = new Array<number>();
_this.axios.delete("/menu/delete",{data:arr})
.then(resp=>{
localStorage.removeItem("allMenus");
_this.$message.success("删除成功了!");
if(_this.electMenus.length >= _this.menuses.length && _this.queryObj.currentPage > 1){
_this.queryObj.currentPage = _this.queryObj.currentPage - 1;
}
_this.loadList();
})
}
// 删除单个
closeMenu(idnex:number, row: any):void{
let _this = this;
_this.$alert("确认删除这个菜单吗?", "友情提示", {
confirmButtonText: "确认",
callback: action => {
_this.axios.delete("/menu/delete",{data:[row.id]})
.then(resp=>{
localStorage.removeItem("allMenus");
_this.$message.success("删除成功了!");
if(_this.menuses.length <= 1 && _this.queryObj.currentPage > 1){
_this.queryObj.currentPage = _this.queryObj.currentPage - 1;
}
_this.loadList();
})
}
})
}
// 提交
submitMenu(menu: string):void{
let _this = this;
let el: any = this.$refs[menu];
el.validate((valid: any) => {
if (valid) {
_this.loading = true;
if (_this.menu.id) {
_this.axios.put("/menu/update",_this.menu)
.then((resp: any) => {
_this.loading = false;
if (resp.code == 0){
_this.loading = false;
localStorage.removeItem("allMenus");
_this.$message.success("修改成功了!");
_this.loadList();
}
})
}else{
_this.axios.post("/menu/save",_this.menu)
.then((resp: any)=>{
_this.loading = false;
if(resp.code == 0) {
_this.isList = true;
localStorage.removeItem("allMenus");
_this.$message.success("添加成功了!")
_this.loadList();
};
})
}
}else{
this.$message.error("错误的提交");
return;
}
})
}
// 不提交菜单
notSubmitMenu(menu:string):void{
this.isList = true;
this.menu = new Menu("", "1", "", "", "0", "");
}
// 全部菜单
allMenu():Promise<any>{
let _this = this;
return new Promise(function (resolve, reject) {
let allMenus = localStorage.getItem("allMenus");
if (allMenus!=null && StringUtils.isNotBlank(allMenus)) {
_this.allMenus = JSON.parse(allMenus.toString());
if (_this.allMenus.length != 0) {
resolve(_this.allMenus);
}
}else{
_this.axios.get("/menu/select")
.then((resp: any)=>{
_this.allMenus = resp.data;
localStorage.setItem("allMenus", JSON.stringify(resp.data));
resolve(_this.allMenus);
})
}
});
}
// 处理菜单
buildMenus(): Array<Menu> {
let _this = this;
let arr = new Array<Menu>();
arr = _this.allMenus;
/*arr.forEach(elv=>{
if(elv.childs){
elv.childs.forEach((child:Menu)=>{
child.childs = new Array<Menu>();
})
}
})*/
return arr;
}
// 选择父级菜单时,赋值
setParentId(data: Menu,node: any, eml:any): void{
this.menu.parentId = data.id.toString();
this.menu.parentName = data.name;
}
}
</script>
<style scoped>
.el-col{
margin-bottom: 10px;
margin-top: 10px;
}
</style>