springboot和vue如何部署到服务器(SpringBootVue后台管理系统)
springboot和vue如何部署到服务器(SpringBootVue后台管理系统)新增/修改列表mybatis-plus ?Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。参考mybatis-plus官网。其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。效果图:
管理页面
主要就是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>