1 安装前端脚手架
1.1 安装node.js
- 自行安装node.js
- 检查node.js 和 npm的版本
- node -v
- npm -v
- 切换淘宝镜像
- npm config set registry https://registry.npm taobao.org
- 安装vue客户端工具
- npm install -g @vue/cli --force
- 检查安装的成果,启动node
- vue ui
2 配置前端脚手架
1.解压文件到工程目录下
2. 利用客户端工具, 打开文件
输入正确的地址后,点击下方的导入这个文件夹,进入到控制台
- 编译项目
- 前端项目运行效果
2 后端项目搭建
2.1 创建项目
2.2 编辑pom.xml文件
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入插件lombok 自动的set/get/构造方法插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
2.3 编辑层级代码
3 关于脚手架的说明
3.1 目录结构
3.2 关于main.js的说明
概念: VUE 引入组件的概念
组件好处: 封装CSS样式/封装JS样式/HTML代码片段. xxxx.vue命名.
父子组件参数传递: 需要使用 Vue.prototype. $http = axios 以后使用 使用 $http.xxx 发起Ajax请求.
3.3 关于路由说明
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI}
]
4 用户登录业务实现
4.1 页面JS分析
4.2 用户登录JS
4.3 用户业务接口文档说明
- 请求路径: /user/login
- 请求方式: POST
- 请求参数
参数名称 | 参数说明 | 备注 |
---|---|---|
username | 用户名 | 不能为空 |
password | 密码 | 不能为空 |
- 响应数据 SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回密钥token信息 |
返回值格式如下:
{"status":200,"msg":"服务器调用成功!","data":"1e893a97634847b3a8b499b173bea620"}
4.4 编辑SysResult对象
package com.jt.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class SysResult {
private Integer status;
private String msg;
private Object data;
public static SysResult fail(){
return new SysResult(201,"服务调用失败!",null);
}
public static SysResult success(){
return new SysResult(200,"服务调用成功!",null);
}
public static SysResult success(Object data){
return new SysResult(200,"服务调用成功!",data);
}
public static SysResult success(String msg,Object data){
return new SysResult(200,msg,data);
}
}
4.5 用户登录模块实现
4.5.1 加密算法MD5
规则说明: MD5加密算法,只能由明文转化为密文. 不可以反向编译.
破解MD5加密算法:
4.5.2 编辑UserController
package com.jt.controller;
import com.jt.pojo.User;
import com.jt.service.UserService;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@CrossOrigin
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAll")
public List<User> findAll(){
return userService.findAll();
}
/**
* URL地址: /user/login
* 请求类型: post
* 参数: JSON串 username/password
* 返回值: SysResult对象
*/
@PostMapping("/login")
public SysResult login(@RequestBody User user){
String token = userService.login(user);
if(token == null){
return SysResult.fail();
}else {
return SysResult.success(token);
}
}
}
4.5.3 编辑UserService
package com.jt.service;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.List;
import java.util.UUID;
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public List<User> findAll() {
return userMapper.findAll();
}
/**
* 业务需求:
* 1.将密码进行加密处理
* 2.根据username/password 查询数据库获取数据.
* 3. 有数据 用户名密码正确
* 无数据 用户名和密码错误
* @param user
* @return
*/
@Override
public String login(User user) {
//1.将密码加密处理
String password = user.getPassword();
//2.利用md5加密算法 进行加密
String md5Pass = DigestUtils.md5DigestAsHex(password.getBytes());
user.setPassword(md5Pass);
//3.查询数据库数据
User userDB = userMapper.findUserByUP(user);
if(userDB == null){
//说明: 用户名和密码错误
return null;
}else {
//说明: 用户名和密码正确
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid;
}
}
}
4.5.4 编辑UserMapper
package com.jt.mapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from demo_user")
List<User> findAll();
@Select("select * from user where username=#{username} and password = #{password}")
User findUserByUP(User user);
}
4.5.5 关于秘钥说明
说明: 当用户登录之后,可以跳转到系统的首页. 到了系统首页之后,用户可以进行其它的业务操作. 系统如何判断正在操作业务的用户 已经登录?
业务说明:
一般在登录认证系统中,都会返回秘钥信息.来作为用户登录的凭证.
秘钥特点: 最好独一无二.
动态生成秘钥: UUID
/**
* 业务需求:
* 1.将密码进行加密处理
* 2.根据username/password 查询数据库获取数据.
* 3. 有数据 用户名密码正确
* 无数据 用户名和密码错误
* @param user
* @return
*/
@Override
public String login(User user) {
//1.将密码加密处理
String password = user.getPassword();
//2.利用md5加密算法 进行加密
String md5Pass = DigestUtils.md5DigestAsHex(password.getBytes());
user.setPassword(md5Pass);
//3.查询数据库数据
User userDB = userMapper.findUserByUP(user);
if(userDB == null){
//说明: 用户名和密码错误
return null;
}
//说明: 用户名和密码正确,返回秘钥
String uuid = UUID.randomUUID().toString()
.replace("-","");
return uuid;
}
4.6 关于Session和Cookie说明
4.6.1 业务需求说明
用户的请求是一次请求,一次响应. 当响应结束时,服务器返回的数据 也会销毁. 问题: 如果销毁了token 则认为用户没有登录.需要重复登录.
如何解决该问题: 应该持久化token信息.
4.6.2 Session
Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。有关使用Session 对象的详细信息,请参阅“ASP应用程序”部分的“管理会话”。注意会话状态仅在支持cookie的浏览器中保留。
特点: Session总结
1. Session 称之为 会话控制 技术
2. Session生命周期, 会话结束 对象销毁.
3. Session的数据存储在内存中.
4. Session只可以临时存储数据.不能永久存储.
4.6.3 Cookie总结
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息 [1] 。
特点:
1. 类型: 小型文本文件.
2. 文件通常是加密的.
3. cookie 可以临时或者永久存储.
4.6.4 关于Cookie和Session说明
- 手机银行的登录信息? Session存储. 数据安全性高
- 腾讯视频会员登录信息? Cookie存储 1个月免密登录.
- 公司的财务系统登录信息? Session存储
- 购物系统的登录信息? Cookie存储.
4.6.5 用户登录信息存储
//获取用户token信息
let token = result.data
window.sessionStorage.setItem("token",token)
5 系统跳转
5.1 系统首页跳转
- 编辑路由JS
- 首页跳转效果
5.2 路由导航守卫
5.2.1 需求说明
说明: 当用户在没有登录的条件下. 用户可以手动输入请求地址. 可以直接跳转项目. 这样的方式非常不安全.
解决方案: 前端通过拦截器控制用户是否登录.
拦截器说明: 用户拦截的是URL中跳转的路径.
结果: 1.拦截 跳转到登录页面.
2.放行 跳转用户目标页面.
5.2.2 路由导航守卫
说明: 编辑index.js文件
/**
* 参数说明:
* 1.to 到哪里去
* 2.from 从哪里来
* 3.next 请求放行
* 拦截器策略:
* 1.如果用户访问/login登录页面 直接放行
* 2.如果访问其它页面,则校验是否有token
* 有token 放行
* 没有token 跳转到登录页面
*/
router.beforeEach((to,from,next) => {
if(to.path === '/login') return next()
//获取token数据信息
let token = window.sessionStorage.getItem('token')
if(token === null || token === ''){
return next("/login")
}
//放行请求
next()
})
6 左侧菜单展现
6.1左侧菜单展现
6.1.1 表设计说明
6.1.2 关于Rights POJO说明
@Data
@Accessors(chain = true)
public class Rights extends BasePojo{
private Integer id;
private String name;
private Integer parentId;
private String path;
private Integer level;
private List<Rights> children; //不是表格固有属性,一对多关系,父级菜单下,要包含所有的子级菜单
}
6.1.3 关于层级代码结构
6.1.4 前端JS说明
- 生命周期函数调用JS函数
created() {
//动态获取左侧菜单信息
this.getMenuList()
//设定模式选中按钮
this.defaultActive = window.sessionStorage.getItem("activeMenu")
},
- 发起Ajax请求获取服务器数据
async getMenuList() {
const {data: result} = await this.$http.get('/rights/getRightsList')
if(result.status !== 200) return this.$message.error("左侧菜单查询失败")
this.menuList = result.data
},
6.1.5 接口文档说明
- 请求路径 /rights/getRightsList
- 请求类型 GET
- 请求参数 无
- 响应数据 SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回权限List集合 |
- 响应数据如图所示
6.1.6 父子关系封装/Sql语句写法
要求: 查询所有一级菜单和一级菜单所对应的二级菜单 要求关联查询
SELECT p.id,p.name,p.parent_id,p.path,p.level,p.created,p.updated,
c.id c_id,c.name c_name,c.parent_id c_parent_id,c.path c_path,
c.level c_level,c.created c_created,c.updated c_updated
FROM
rights p
LEFT JOIN
rights c
ON
c.parent_id = p.id
WHERE p.parent_id = 0
6.1.7 编辑RightsController
@RestController
@CrossOrigin
@RequestMapping("/rights")
public class RightsController {
@Autowired
private RightsService rightsService;
/**
* 查询一级二级数据
* URL: /rights/getRightsList
* 参数: 无
* 返回值: SysResult(List<Rights>)
*/
@GetMapping("/getRightsList")
public SysResult getRightsList(){
List<Rights> rights = rightsService.getRightsList();
return SysResult.success(rights);
}
}
6.1.8 编辑RightsService
@Service
public class RightsServiceImpl implements RightsService{
@Autowired
private RightsMapper rightsMapper;
@Override
public List<Rights> getRightsList() {
return rightsMapper.getRightsList();
}
}
6.1.9 编辑RightsMapper/xml映射文件
- RightsMapper接口
public interface RightsMapper {
public List<Rights> getRightsList();
}
- 编辑Rights映射文件
<mapper namespace="com.jt.mapper.RightsMapper">
<select id="getRightsList" resultMap="rightsRM">
select p.id,p.name,p.parent_id,p.path,p.level,p.created,p.updated,
c.id c_id,c.name c_name,c.parent_id c_parent_id,c.path c_path,
c.level c_level,c.created c_created,c.updated c_updated
from
rights p
left join
rights c
on
c.parent_id = p.id
where p.parent_id = 0
</select>
<resultMap id="rightsRM" type="Rights" autoMapping="true">
<id column="id" property="id"/>
<!--一对一封装子级菜单List集合-->
<collection property="children" ofType="Rights">
<!--封装主键ID-->
<id column="c_id" property="id"/>
<result column="c_name" property="name"/>
<result column="c_parent_id" property="parentId"/>
<result column="c_path" property="path"/>
<result column="c_level" property="level"/>
<result column="c_created" property="created"/>
<result column="c_updated" property="updated"/>
</collection>
</resultMap>
</mapper>
6. .1.10 页面效果展现
6.1.11 关于项目报错调试步骤
说明: 通过控制台 检查请求路径/响应信息/及JS报错信息. 后台服务器端口号固定 8091
6.2 Ajax 如何实现异步调用?
Ajax特点:
- 局部刷新
- 异步访问
核心组件: Ajax 引擎!!!
同步概念:用户访问服务器发起请求,服务器返回数据为同步访问,特点是效率高,但是友好性不好,所以一般使用异步加载;
异步概念:异步加载时需要使用Ajax引擎作为中间过渡,用户向Ajax引擎发起请求,由Ajax引擎访问服务器,服务器返回数据到Ajax引擎,用户需服务器不直接联系,特点是Ajax引擎可以去处理一些服务器问题导致的无法及时访问及时返回数据的问题;
6.3 关于页面跳转子级路由说明
编辑路由文件,添加user路径
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import ElementUI from '../components/ElementUI.vue'
import Home from '../components/Home.vue'
import User from '../components/user/user.vue'
//使用路由机制
Vue.use(VueRouter)
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
{path: '/home', component: Home},
{path: '/user', component: User}
]
6.3.1 现象说明
1.页面路由跳转 用户点击子级菜单时.页面将整个后端页面进行覆盖. 效果如下.
2.实际效果: 应该在首页的右侧 展现新的页面信息.
6.3.2 知识点讲解
功能说明: 组件之间的嵌套问题.
定义路由步骤:
- 定义路由url地址.
- 路由填充位(占位符)
- 定义组件(了解)
- 定义路由策略
- 实现路由挂载
父子组件嵌套总结:
6. 定义父级组件
2. 路由策略:
3. 如果需要嵌套 通过 router-view 进行占位, 通过children属性定义父子关系的结构. 当点击子组件时,会在父级组件的router-view中展现子组件.
6.3.3 首页嵌套规则
-
在Home组件中定义路由的占位符
-
定义父子组件的策略