系统前端模块的搭建

本系统使用Vue3+Vue CLI实现系统前端模块的搭建

环境准备

Vue CLI官方文档

安装node得到npm,使用npm安装vue cli(脚手架),使用vue cli创建项目

npm可设置淘宝镜像,淘宝镜像的域名换了,加快组件下载速度npm config set registry https://registry.npmmirror.com

项目创建

依据官方文档,创建项目web-train

vue cli创建项目官方文档

创建的项目目录

项目目录各个文件介绍 参考资料

image-20231206231748676

进入项目目录,执行npm run serve即可启动前端项目

vue项目启动端口号的修改方法,默认采用8080端口,如果8080被占用,则自动加1

前端模块集成Ant Design Vue

UI组件有很多选择,一种是基于CSS的Bootstrap,适用于各种前端框架;另一种是基于Vue的UI组件,只能用于Vue框架

Ant Design Vue官方文档教程

Ant Design Vue使用流程

  • 下载资源,安装依赖
  • 全局注册或者部分注册
  • 在vue中即可直接使用相关组件

小插曲:关于Ant Design Vue图标组件的使用

icon组件官方文档

  • 需要手动再次下载依赖

    • npm install --save @ant-design/icons-vue
  • main.js中全局注册(官方文档只讲解了局部注册)

    • 全局注册方法

      • ```javascript
        import { createApp } from ‘vue’
        import App from ‘./App.vue’
        import router from ‘./router’
        import store from ‘./store’
        import Antd from ‘ant-design-vue’
        import ‘ant-design-vue/dist/reset.css’
        import * as Icons from ‘@ant-design/icons-vue’

        const app = createApp(App)
        app.use(store).use(router).use(Antd).mount(‘#app’);

        //全局使用图标
        const icons = Icons;
        for(const i in icons){

        app.component(i,icons[i]);
        

        }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37

        ## 短信验证码登录流程

        使用手机号+验证码的方式进行用户的登录注册

        <img src="04 系统前端模块的搭建/image-20231207001544054.png" alt="image-20231207001544054" style="zoom:33%;" />

        登录和注册共用同一个页面,如果手机号存在直接登录,否则后台自动进行注册

        流程:用户输入手机号->点击获取验证码按钮->输入用户码->点击登录

        **补充说明:**

        该登录注册流程容易受到黑客攻击

        - 同一手机号重复请求验证码
        - 后台可以将对应手机号加入黑名单或者单个手机号当天验证码请求次数设置上限
        - 大量不同手机号请求验证码
        - 设置图像验证码,增大被攻击难度
        - <img src="04 系统前端模块的搭建/image-20231207002409475.png" alt="image-20231207002409475" style="zoom:33%;" />



        不带图像验证码的登录注册流程

        <img src="04 系统前端模块的搭建/640bfc3c0994a1fb07522184-1701880296038-2.jpg" alt="640bfc3c0994a1fb07522184" style="zoom: 25%;" />

        带图像验证码的登录注册流程

        <img src="04 系统前端模块的搭建/640bfc7609fdf6fb12562184.jpg" alt="640bfc7609fdf6fb12562184" style="zoom: 25%;" />

        ## 登陆注册二合一界面开发

        新增登录注册页面,同时在`web-train/src/router/index.js`中添加对应的路由(router)配置

        ```tex
        对于router配置,大型项目,页面多,80%页面不常用,可以用懒加载的方式,减少编译后文件的大小,提高初始访问速度;小型目,页面少,可以用静态导入的方式,对编译后的文件大小影响不大

Vue页面的编写

一个vue页面,由三个部分组件,都不是必须的

  • template,相当于html
  • script,JS脚本
  • style,CSS样式

发送短信验证码端口开发

持久层

mapper直接使用以前mybatis generator生成的代码

业务层

新建发送验证码对应请求的实体类com.bang.train.member.req.MemberSendCodeReq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.bang.train.member.req;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MemberSendCodeReq {
//@NotBlank:该参数不能为空,为空返回message,不能进入对应请求
//@Pattern:校验手机号格式,用正则表达式,第一位是1,后十位是数字
@NotBlank(message = "【手机号】不能为空")
@Pattern(regexp = "^1\\d{10}$",message = "手机号码格式错误")
String mobile;
}

在服务层接口中定义新的方法

1
String sendCode(MemberSendCodeReq req);

服务层实现类中编写新增方法的具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//发送验证码接口
@Override
public String sendCode(MemberSendCodeReq req) {
String mobile = req.getMobile();
//1.判断当前手机号是否已被注册
Member member = getMembersByMobile(mobile);
if(ObjectUtil.isNull(member)){//2.手机号为空,将用户数据插入数据库
Member newMember = new Member();
//采用雪花算法生成注册用户在数据库中对应的ID
newMember.setId(SnowUtil.getSnowflakeId());
newMember.setMobile(mobile);
memberMapper.insert(newMember);
}
//3.生成验证码,随机4为字符串
// String code = RandomUtil.randomString(4);
String code = "8888";
//4.将验证码保存短信记录,用于后续用户输入的验证码进行验证
/*
一般可以建立一个专门数据库,该数据库包含字段:
手机号
短信验证码
有效期
是否已使用:避免单个验证码重复使用
业务类型:可能多个业务需要验证码,比如找回密码,避免用户用验证码攻击其他业务
发送时间:便于分析,比如统计一段时间内某个手机号请求的次数
使用时间:
*/
//5.对接第三方短信通道(比如阿里云等),将验证码发送用户
//6.返回生成的验证码
return code;
}

控制层

1
2
3
4
5
四要素:
请求地址url: localhost:8080/member/send-code
请求方法: post
请求参数: MemberSendCodeReq req
响应结果: CommonResp<String>
1
2
3
4
5
6
@PostMapping("/send-code")
public CommonResp<String> sendCode(@Valid MemberSendCodeReq req){
CommonResp<String> commResp = new CommonResp<>();
commResp.setContent(memberService.sendCode(req));
return commResp;
}

短信验证码登录接口开发

持久层

mapper直接使用以前mybatis generator生成的代码

业务层

新建登录对应请求的实体类com.bang.train.member.req.MemberLoginReq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.bang.train.member.req;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MemberLoginReq {
//@NotBlank:该参数不能为空,为空返回message,不能进入对应请求
//@Pattern:校验手机号格式,用正则表达式,第一位是1,后十位是数字
@NotBlank(message = "【手机号】不能为空")
@Pattern(regexp = "^1\\d{10}$",message = "手机号码格式错误")
String mobile; //手机号

@NotBlank(message = "【短信验证码】不能为空")
String code; //短信验证码
}

在服务层接口中定义新的方法

1
MemberLoginResp login(MemberLoginReq req);

服务层实现类中编写新增方法的具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//验证码登录接口
@Override
public MemberLoginResp login(MemberLoginReq req) {
String mobile = req.getMobile();
String code = req.getCode();
//1.判断当前手机号是否已被注册
Member member = getMembersByMobile(mobile);
if(ObjectUtil.isNull(member)){//2.手机号为空,提示用户需要先获取验证码
throw new BusinessException(BusinessExceptionEnum.MEMBER_MOBILE_NOT_EXIST);
}
//3.验证校验,实际应查询验证码短信记录数据库,这里为了测试简便,直接写死
//实际可能包含验证码正确性、时效性、业务类型匹配等多种校验
if(!"8888".equals(code)){ //验证码错误
throw new BusinessException(BusinessExceptionEnum.MEMBER_MOBILE_CODE_ERROR);
}
//4.校验通过,返回用户对象
//对于系统而言,用户只需登录一次,登录对象中可能包含用户昵称、头像等数据信息,所以应该直接返回整个用户对象
//但是又不能直接将后台数据完整返回前端,所以需要创建一个响应实体类,并将后台数据转换为响应类
MemberLoginResp memberLoginResp = BeanUtil.copyProperties(member,MemberLoginResp.class);
return memberLoginResp;

}

控制层

1
2
3
4
5
四要素:
请求地址url: localhost:8080/member/login
请求方法: post
请求参数: MemberLoginReq req
响应结果: CommonResp<MemberLoginResp>
1
2
3
4
5
6
@PostMapping("/login")
public CommonResp<MemberLoginResp> login(@Valid MemberLoginReq req){
CommonResp<MemberLoginResp> commResp = new CommonResp<>();
commResp.setContent(memberService.login(req));
return commResp;
}

集成Axios完成登录功能

下载第三方依赖:

  • 进入web前端工程目录
  • npm install axios安装依赖

使用流程

  1. 在对应Vue文件中引入依赖

    1
    import axios from 'axios';
  1. 编写点击发送验证码事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const sendCode = () => {
    axios.post("http://localhost:8000/member/send-code", {
    mobile: loginForm.mobile
    }).then(response => {
    let data = response.data;
    if (data.success) {
    notification.success({ description: '发送验证码成功!' });
    loginForm.code = "8888";
    } else {
    notification.error({ description: data.message });
    }
    });
    };
  2. 编写点击登录事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const login = () => {
    axios.post("http://localhost:8000/member/login", loginForm).then((response) => {
    let data = response.data;
    if (data.success) {
    notification.success({ description: '登录成功!' });
    } else {
    notification.error({ description: data.message });
    }
    })
    };
  3. 问题一:解决前后端跨域问题:

    • 问题描述:前后端不在同一个域。IP一样,端口不一样,也算跨域。跨域是前后端分离不可避免的问题

    • 问题解决:在后端网关模块applicatio.yaml文件中进行配置,允许跨域访问

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      spring:
      cloud:
      gateway:
      globalcors:
      corsConfigurations:
      '[/**]':
      # 允许携带认证信息
      allow-credentials: true
      # 允许跨域的源(网站域名/ip),设置*为全部
      allowedOriginPatterns: "*"
      # 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
      allowedMethods: "*"
      # 允许跨域请求里的head字段,设置*为全部
      allowedHeaders: "*"
      #跨域检测有效期
      maxAge: 3600
  4. 问题二:解决前后端参数传递问题

    • 前端参数传递是以json形式传递的,后端目前只支持以表单的形式访问,在后端请求参数前加上@RequestBody注解即可改为支持json方式的请求数据,此时就不再支持以表单形式提交

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @PostMapping("/send-code")
      public CommonResp<String> sendCode(@Valid @RequestBody MemberSendCodeReq req){
      CommonResp<String> commResp = new CommonResp<>();
      commResp.setContent(memberService.sendCode(req));
      return commResp;
      }

      @PostMapping("/login")
      public CommonResp<MemberLoginResp> login(@Valid @RequestBody MemberLoginReq req){
      CommonResp<MemberLoginResp> commResp = new CommonResp<>();
      commResp.setContent(memberService.login(req));
      return commResp;
      }
    • 对应的请求格式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      ### 验证码获取
      POST http://localhost:8000/member/send-code
      Content-Type: application/json

      {
      "mobile": "12345678908"
      }

      ###登录
      POST http://localhost:8000/member/login
      Content-Type: application/json

      {
      "mobile":"12345678908",
      "code":"8888"
      }

增加Axios拦截器配置

axios发送请求获取响应结果,应该能够打印对应日志信息,便于前后端联调

main.js中进行axios拦截器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* axios拦截器
*/

//拦截请求
axios.interceptors.request.use(function (config) {
console.log('请求参数:', config);
return config;
}, error => {
return Promise.reject(error);
});
//拦截响应
axios.interceptors.response.use(function (response) {
console.log('返回结果:', response);
return response;
}, error => {
console.log('返回错误:', error);
return Promise.reject(error);
});

在浏览器的控制台能够看到每次请求的日志信息

image-20231209111736666

Vue CLI多环境配置

现在前端请求后端接口的url是写死的,但在实际开发过程中,应该让后端接口的域名能够动态配置(开发环境、测试环境、部署环境的接口地址都不一样)

1
2
3
4
5
6
7
8
9
在前端根目录下增加文件 .env.xxx,xxx表是不同的环境
启动命令里增加 --mode xxx,就启动xxx环境的配置

.env.xx文件编写:
增加多环境变量:
NODE_ENV是内置变量
自定义变量用“VUE_APP_”开头
使用变量:
process.env. xxx

编写多个env文件

.env.dev

1
2
NODE_ENV=development
VUE_APP_SERVER=http://localhost:8000

.env.prod

1
2
NODE_ENV=production
VUE_APP_SERVER=http://train-server.bang.com

在main.js中引用对应变量,让其生效

1
2
3
axios.defaults.baseURL = process.env.VUE_APP_SERVER;
console.log('环境:', process.env.NODE_ENV);
console.log('服务端:', process.env.VUE_APP_SERVER);

在各个Vue页面中,可以省略请求前缀URL编写

image-20231209114124939

启动命令中指定对应环境

npm run serve --port 80 --mode xxx

其中xxx.env文件名后缀

新增web控台主页面

可以选择Ant Design Vue中的Layout布局作为基础进行修改

  • 新增控台主页面main.vue
  • index.js中新增对应路由
  • 实现跳转,由登录页面登陆成功跳转至主页面