基于Ant DesignPro Vue实现通过SpringBoot后台加载自定义菜单- 前后端分离
本文想基于Ant DesignPro Vue构建的前端+SpringBoot实现的后端接口服务,实现前后端分离开发和独立运行,业务场景是登录认证,认证成功后返回该用户相应权限范围内可见的菜单。
Ant Design Pro相关系列文章:
一、AntDesign Pro安装过程
二、基于Ant DesignPro实现通过SpringBoot后台加载自定义菜单-前端部分
三、基于Ant DesignPro实现通过SpringBoot后台加载自定义菜单-SpringBoot后端部分
四、搭建Vue版Ant Design Pro后台管理系统
五、基于Ant DesignPro Vue实现通过SpringBoot后台加载自定义菜单- 前后端分离
目录
- 基于Ant DesignPro Vue实现通过SpringBoot后台加载自定义菜单- 前后端分离
- 一、通过Ant DesignPro Vue构建前端
- 二、Ant DesignPro Vue前端对接后台服务接口
- 1、去掉Mock
- 2、vue.config.js 中配置api代理
- 3、修改菜单加载是从后台服务接口请求
- 三、创建Ant DesignPro后台服务SpringBoot项目
- 1. File->New->Project
- 2.编辑pom.xml,添加需要的依赖
- 3.编辑application.properties
- 4.创建依赖的实体类
- 4.1 登录请求实体类UserLoginDto.java
- 4.2 向前端画面传输的用户信息实体类 UserVo.java
- 4.3 菜单实体类MenuVo.java
- 4.4 菜单项实体类MenuMetaVo.java
- 5.创建登录认证(/api/auth/)接口响应处理类
- 6.创建用户(/api/user/)接口响应处理类
- 7.创建Account(/api/account)接口响应处理类
- 四、验证
- 1.启动服务端
- 2.启动前端
- 3.访问前端画面
一、通过Ant DesignPro Vue构建前端
参见 搭建Vue版Ant Design Pro后台管理系统
二、Ant DesignPro Vue前端对接后台服务接口
1、去掉Mock
编辑src/main.js,把mock注释
2、vue.config.js 中配置api代理
说明:
上面target配置属性是指后台服务接口URL,默认格式为:target指定的Ulr/api/请求接口
3、修改菜单加载是从后台服务接口请求
三、创建Ant DesignPro后台服务SpringBoot项目
1. File->New->Project
2.编辑pom.xml,添加需要的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/>
</parent>
<groupId>cn.chinaelink.im</groupId>
<artifactId>mcvboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mcboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 热部署加入的引用,1.spring-boot-devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--json需要的依赖 -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib-ext-spring</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--重点:模板引擎,用于显示网页需要的依赖,如果不需要将静态页面放入当前工程,则不需要解注下面依赖-->
<!-- 如果要在当前工程中加入静态页面,首先需要解注下面依赖,并在src/main/resources/目录下创建static和templates目录-->
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.编辑application.properties
#配置管理服务端口号,默认为8080
server.port=8080
#服务访问路径
server.servlet.context-path=/
# 输出的log文件名
logging.file.name=mc
# 输出的文件的路径
logging.file.path=./logs/mcv/
# 限制日志文件的大小
logging.file.max-size=10MB
# 日志的保存天数
logging.file.max-history=7
# 输出级别
logging.level.root=warn
logging.level.cn.com.hxyl.filebs=debug
# xml配置文件
logging.config=classpath:logback-spring.xml
#关闭缓存
#如果不需要将静态页面放入当前工程,则不需要解注下面依赖
#如果要在当前工程中加入静态页面,首先需要解注下面依赖,并将静态页面文件放入src/main/resources/static/目录下
#spring.thymeleaf.cache=false
#spring.thymeleaf.prefix=classpath:/static/
server.tomcat.threads.max=100
server.tomcat.threads.min-spare=30
#开启项目热部署
spring.devtools.restart.enabled=true
4.创建依赖的实体类
4.1 登录请求实体类UserLoginDto.java
package cn.chinaelink.im.mcvboot.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserLoginDto {
// 用户名
private String username;
// 密码
private String password;
// 手机号
private String mobile;
// 验证码
private String captcha;
}
4.2 向前端画面传输的用户信息实体类 UserVo.java
package cn.chinaelink.im.mcvboot.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserVo {
private String id;
private String name;
private String username;
private String password;
private String avatar;
private int status;
private String telephone;
private String lastLoginIp;
private long lastLoginTime;
private String creatorId;
private long createTime;
private int deleted;
private String roleId;
private String lang;
private String token;
}
4.3 菜单实体类MenuVo.java
package cn.chinaelink.im.mcvboot.vo.menu;
import lombok.Data;
@Data
public class MenuVo {
private String name;
private int id;
private int parentId;
private String component;
private String redirect;
private String path;
private MenuMetaVo meta;
}
4.4 菜单项实体类MenuMetaVo.java
package cn.chinaelink.im.mcvboot.vo.menu;
import lombok.Data;
@Data
public class MenuMetaVo {
private String title;
private String icon;
private String target;
private boolean show;
}
5.创建登录认证(/api/auth/)接口响应处理类
所以有接口实现都是参见src/mock/services/目录的相应的模拟接口js的结果定义的
AuthController类
import cn.chinaelink.im.mcvboot.dto.UserLoginDto;
import cn.chinaelink.im.mcvboot.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
@Controller
@Slf4j
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
@ResponseBody
public String login(@RequestBody UserLoginDto userLoginDto) {
JSONObject resJson = new JSONObject();
if (Optional.ofNullable(userLoginDto.getUsername()).isPresent() &&
("ant.design".equals(userLoginDto.getPassword()) ||
"admin".equals(userLoginDto.getUsername()))
) {
UserVo userVo = getAdminUserVo();
resJson.put("result",JSONObject.fromObject(userVo).toString());
resJson.put("message","认证成功");
resJson.put("code",200);
resJson.put("_status", 200);
resJson.put("token",userVo.getToken());
return resJson.toString();
} else if(Optional.ofNullable(userLoginDto.getMobile()).isPresent()){
UserVo userVo = getAdminUserVo();
resJson.put("result",JSONObject.fromObject(userVo).toString());
resJson.put("message","认证成功");
resJson.put("code",200);
resJson.put("_status", 200);
resJson.put("token",userVo.getToken());
return resJson.toString();
}
JSONObject result = new JSONObject();
result.put("isLogin", true);
resJson.put("result",result);
resJson.put("message","错误的用户名和密码,请确认后重试!");
resJson.put("code",401);
resJson.put("_status", 401);
return resJson.toString();
}
@PostMapping("/2step-code")
@ResponseBody
public JSONObject twoFactor() {
JSONObject resJson = new JSONObject();
JSONObject dataJson = new JSONObject();
dataJson.put("stepCode",new Random().nextInt(1));
resJson.put("result", dataJson);
return resJson;
}
@PostMapping("/logout")
@ResponseBody
public JSONObject logout() {
JSONObject data = new JSONObject();
data.put("result", new JSONObject());
data.put("message", "");
return data;
}
private UserVo getAdminUserVo() {
UserVo userVo = new UserVo();
userVo.setId(UUID.randomUUID().toString());
userVo.setName("超级管理员");
userVo.setUsername("admin");
userVo.setAvatar("https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png");
userVo.setStatus(1);
userVo.setLastLoginTime(System.currentTimeMillis());
userVo.setCreatorId("admin");
userVo.setCreateTime(System.currentTimeMillis());
userVo.setRoleId("admin");
userVo.setLang("zh-CN");
userVo.setToken("4291d7da9005377ec9aec4a71ea837f");
return userVo;
}
}
6.创建用户(/api/user/)接口响应处理类
package cn.chinaelink.im.mcvboot.controller.api;
import cn.chinaelink.im.mcvboot.vo.menu.MenuMetaVo;
import cn.chinaelink.im.mcvboot.vo.menu.MenuVo;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
@Controller
@Slf4j
@RequestMapping("/api/user")
public class UserController {
@RequestMapping("/info")
@ResponseBody
public JSONObject info(){
JSONObject userInfoJson = new JSONObject();
userInfoJson.put("id", "4291d7da9005377ec9aec4a71ea837f");
userInfoJson.put("name", "天野远子");
userInfoJson.put("username", "admin");
userInfoJson.put("password", "");
userInfoJson.put("avatar", "/avatar2.jpg'");
userInfoJson.put("status", 1);
userInfoJson.put("telephone", "");
userInfoJson.put("lastLoginIp", "27.154.74.117");
userInfoJson.put("lastLoginTime", 1534837621348L);
userInfoJson.put("creatorId", "admin'");
userInfoJson.put("createTime", 1497160610259L);
userInfoJson.put("merchantCode", "TLif2btpzg079h15bk'");
userInfoJson.put("deleted", 0);
userInfoJson.put("roleId", "admin'");
JSONObject roleObj = new JSONObject();
roleObj.put("id","admin");
roleObj.put("name","管理员");
roleObj.put("describe","拥有所有权限");
roleObj.put("status", 1);
roleObj.put("creatorId","system");
roleObj.put("createTime",1497160610259L);
roleObj.put("deleted", 0);
JSONArray permissionArray = new JSONArray();
JSONObject permission1 = new JSONObject();
permission1.put("roleId","admin");
permission1.put("permissionId","dashboard");
permission1.put("permissionName","仪表盘");
permission1.put("actions","[{\"action\":\"add\",\"defaultCheck\":false,\"describe\":\"新增\"},{\"action\":\"query\",\"defaultCheck\":false,\"describe\":\"查询\"},{\"action\":\"get\",\"defaultCheck\":false,\"describe\":\"详情\"},{\"action\":\"update\",\"defaultCheck\":false,\"describe\":\"修改\"},{\"action\":\"delete\",\"defaultCheck\":false,\"describe\":\"删除\"}]");
JSONArray actionEntitySet = new JSONArray();
JSONObject actionEntity1 = new JSONObject();
actionEntity1.put("action", "add");
actionEntity1.put("describe", "新增");
actionEntity1.put("defaultCheck", false);
actionEntitySet.add(actionEntity1);
JSONObject actionEntity2 = new JSONObject();
actionEntity2.put("action", "query");
actionEntity2.put("describe", "查询");
actionEntity2.put("defaultCheck", false);
actionEntitySet.add(actionEntity2);
JSONObject actionEntity3 = new JSONObject();
actionEntity3.put("action", "get");
actionEntity3.put("describe", "详情");
actionEntity3.put("defaultCheck", false);
actionEntitySet.add(actionEntity3);
JSONObject actionEntity4 = new JSONObject();
actionEntity4.put("action", "update");
actionEntity4.put("describe", "修改");
actionEntity4.put("defaultCheck", false);
actionEntitySet.add(actionEntity4);
JSONObject actionEntity5 = new JSONObject();
actionEntity5.put("action", "delete");
actionEntity5.put("describe", "删除");
actionEntity5.put("defaultCheck", false);
actionEntitySet.add(actionEntity5);
permission1.put("actionEntitySet", actionEntitySet);
permission1.put("actionList", null);
permission1.put("dataAccess", null);
permissionArray.add(permission1);
JSONObject permission2 = new JSONObject();
permission2.put("roleId","admin");
permission2.put("permissionId","exception");
permission2.put("permissionName","异常页面权限");
permission2.put("actions","[{\"action\":\"add\",\"defaultCheck\":false,\"describe\":\"新增\"},{\"action\":\"query\",\"defaultCheck\":false,\"describe\":\"查询\"},{\"action\":\"get\",\"defaultCheck\":false,\"describe\":\"详情\"},{\"action\":\"update\",\"defaultCheck\":false,\"describe\":\"修改\"},{\"action\":\"delete\",\"defaultCheck\":false,\"describe\":\"删除\"}]");
JSONArray actionEntitySet2 = new JSONArray();
JSONObject actionEntity21 = new JSONObject();
actionEntity21.put("action", "add");
actionEntity21.put("describe", "新增");
actionEntity21.put("defaultCheck", false);
actionEntitySet2.add(actionEntity21);
JSONObject actionEntity22 = new JSONObject();
actionEntity22.put("action", "query");
actionEntity22.put("describe", "查询");
actionEntity22.put("defaultCheck", false);
actionEntitySet2.add(actionEntity22);
JSONObject actionEntity23 = new JSONObject();
actionEntity23.put("action", "get");
actionEntity23.put("describe", "详情");
actionEntity23.put("defaultCheck", false);
actionEntitySet2.add(actionEntity23);
JSONObject actionEntity24 = new JSONObject();
actionEntity24.put("action", "update");
actionEntity24.put("describe", "修改");
actionEntity24.put("defaultCheck", false);
actionEntitySet2.add(actionEntity24);
JSONObject actionEntity25 = new JSONObject();
actionEntity25.put("action", "delete");
actionEntity25.put("describe", "删除");
actionEntity25.put("defaultCheck", false);
actionEntitySet2.add(actionEntity25);
permission2.put("actionEntitySet", actionEntitySet2);
permission2.put("actionList", null);
permission2.put("dataAccess", null);
permissionArray.add(permission2);
roleObj.put("permissions", permissionArray);
userInfoJson.put("role", roleObj);
JSONObject body = new JSONObject();
body.put("result", userInfoJson);
return body;
}
@RequestMapping("/nav")
@ResponseBody
public JSONObject getUserMenus(){
List<MenuVo> dataArray = new ArrayList<>();
MenuVo menu1 = new MenuVo();
menu1.setId(1);
menu1.setParentId(0);
menu1.setName("dashboard");
menu1.setComponent("RouteView");
menu1.setRedirect("/dashboard/workplace");
MenuMetaVo meta1 = new MenuMetaVo();
meta1.setTitle("menu.dashboard");
meta1.setIcon("dashboard");
meta1.setShow(true);
menu1.setMeta(meta1);
dataArray.add(menu1);
MenuVo menu2 = new MenuVo();
menu2.setId(7);
menu2.setParentId(1);
menu2.setName("workplace");
menu2.setComponent("Workplace");
MenuMetaVo meta2 = new MenuMetaVo();
meta2.setTitle("menu.dashboard.monitor");
meta2.setShow(true);
menu2.setMeta(meta2);
dataArray.add(menu2);
MenuVo menu3 = new MenuVo();
menu3.setId(3);
menu3.setParentId(1);
menu3.setName("monitor");
menu3.setPath("https://www.baidu.com/");
MenuMetaVo meta3 = new MenuMetaVo();
meta3.setTitle("menu.dashboard.workplace");
meta3.setTarget("_blank");
meta3.setShow(true);
menu3.setMeta(meta3);
dataArray.add(menu3);
MenuVo menu4 = new MenuVo();
menu4.setId(2);
menu4.setParentId(1);
menu4.setName("Analysis");
menu4.setComponent("Analysis");
menu4.setPath("/dashboard/analysis");
MenuMetaVo meta4 = new MenuMetaVo();
meta4.setTitle("menu.dashboard.analysis");
meta4.setShow(true);
menu4.setMeta(meta4);
dataArray.add(menu4);
// Form
MenuVo menu5 = new MenuVo();
menu5.setId(10);
menu5.setParentId(0);
menu5.setName("form");
menu5.setComponent("RouteView");
menu5.setRedirect("/form/base-form");
MenuMetaVo meta5 = new MenuMetaVo();
meta5.setTitle("menu.form");
meta5.setIcon("form");
menu5.setMeta(meta5);
dataArray.add(menu5);
MenuVo menu6 = new MenuVo();
menu6.setId(6);
menu6.setParentId(10);
menu6.setName("basic-form");
menu6.setComponent("BasicForm");
MenuMetaVo meta6 = new MenuMetaVo();
meta6.setTitle("menu.form.basic-form");
menu6.setMeta(meta6);
dataArray.add(menu6);
MenuVo menu7 = new MenuVo();
menu7.setId(5);
menu7.setParentId(10);
menu7.setName("step-form");
menu7.setComponent("StepForm");
MenuMetaVo meta7 = new MenuMetaVo();
meta7.setTitle("menu.form.step-form");
menu7.setMeta(meta7);
dataArray.add(menu7);
MenuVo menu8 = new MenuVo();
menu8.setId(4);
menu8.setParentId(10);
menu8.setName("advanced-form");
menu8.setComponent("AdvanceForm");
MenuMetaVo meta8 = new MenuMetaVo();
meta8.setTitle("menu.form.advanced-form");
menu8.setMeta(meta8);
dataArray.add(menu8);
// List
MenuVo menu9 = new MenuVo();
menu9.setId(10010);
menu9.setParentId(0);
menu9.setName("list");
menu9.setComponent("RouteView");
menu9.setRedirect("/list/table-list");
MenuMetaVo meta9 = new MenuMetaVo();
meta9.setTitle("menu.list");
meta9.setIcon("table");
meta9.setShow(true);
menu9.setMeta(meta9);
dataArray.add(menu9);
MenuVo menu10 = new MenuVo();
menu10.setId(10011);
menu10.setParentId(10010);
menu10.setName("table-list");
menu10.setComponent("TableList");
menu10.setPath("/list/table-list/:pageNo([1-9]\\d*)?");
MenuMetaVo meta10 = new MenuMetaVo();
meta10.setTitle("menu.list.table-list");
meta10.setShow(true);
menu10.setMeta(meta10);
dataArray.add(menu10);
MenuVo menu11 = new MenuVo();
menu11.setId(10012);
menu11.setParentId(10010);
menu11.setName("basic-list");
menu11.setComponent("StandardList");
MenuMetaVo meta11 = new MenuMetaVo();
meta11.setTitle("menu.list.basic-list");
meta11.setShow(true);
menu11.setMeta(meta11);
dataArray.add(menu11);
JSONObject body = new JSONObject();
body.put("result", dataArray);
return body;
}
}
7.创建Account(/api/account)接口响应处理类
package cn.chinaelink.im.mcvboot.controller.api;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Random;
@Controller
@Slf4j
@RequestMapping("/api/account")
public class AccountController {
@RequestMapping("/sms")
@ResponseBody
public String smsCaptcha() {
Random random = new Random();
int num = random.nextInt(89999) + 10000;
JSONObject data = new JSONObject();
data.put("captcha", num);
JSONObject response = new JSONObject();
response.put("result", data);
return response.toString();
}
}
四、验证
1.启动服务端
启动成功后可以通过浏览器访问http://127.0.0.1:8080/
2.启动前端
在命令行进入前端代码所在目录后,执行下面的命令
D:\work\JavaTeam\workspace\IdeaProjects\antDesignPro\antdvPromc> yarn run serve
3.访问前端画面
说明:
通过访问,前端已正常通过http://192.168.0.100:8080这个后端服务接口登录认证成功,并成功的获取到后台返回的菜单(只返回了两项一级菜单)