编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

SpringBoot + Vue 后台管理系统(四):管理页面

wxchong 2024-07-26 22:33:39 开源技术 29 ℃ 0 评论

管理页面

主要就是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>

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表