springboot + vue + elementUI项目实战——简洁清新的员工管理系统(一)

发布时间:2024-01-19 14:00

springboot + vue + elementUI + mybatis + redis 清新的员工管理系统

\"springboot

前言

  从这期,项目从需求分析开始,一步步实现一个老经典的清新的员工管理系统,适合有一定 ssm、springboot、mybatis、vue+elementUI 基础的训练项目,虽然没有很复杂的业务,但也要会这些技术栈的基础才行。看下运行效果就开始了,,
适合有一定 ssm、springboot、mybatis、vue+elementUI 基础的训练项目
登录和注册页面,是在源码之家随便找的一个来用:
地址:https://www.mycodes.net/192/10205.htm


1、项目准备

  • 需求分析

    • 模块
    • 功能
  • 库表设计

    • 数据库、ER图
  • 详细设计

    • 流程图、伪代码等方式
  • 编码环节

    • a.环境准备,技术选型
    • b.正式进入编码环节
  • 测试

  • 部署上线(发布到生产环境)


2、技术选型

  • 前端:elementUI + vue + axios
  • 后端:SpringBoot + mysql + mybatis + Redis

3、需求分析

用户模块:

​ a. 用户登录

​ b. 用户注册

​ c. 验证码实现

​ d. 欢迎xxx用户展示

​ e. 安全退出

员工模块:

​ f. 员工列表分页展示

​ g. 员工添加

​ h. 员工删除

​ i. 员工修改

​ j. 员工列表加入redis缓存实现


4、库表设计

1、分析系统中有哪些表?

2、分析表与表之间关系?

3、分析表中字段?

用户表: e_user
\"springboot
员工表: e_emp
\"springboot
创建表:

create table t_user(
	id 				int(6)  auto_increment comment \'id主键\',
    username  		varchar(60) not null   comment \'用户名\',
    realname  		varchar(60) 		   comment \'真实名\',
    password  		varchar(50) not null   comment \'用户密码\',
    gender    		tinyint(1)     comment \'用户性别 (1 男 0 女)\',
    registertime	datetime    comment \'用户注册时间\',
    state  			tinyint(1)  comment \'用户状态(是否启用,1 启用 0 未启用)\',
    primary key(id)
)engine=innodb default charset=utf8
create table t_emp(
	id 				int(6)  auto_increment comment \'id主键\',
    `name`  		varchar(60) not null   comment \'员工姓名\',
    photopath  		varchar(1000) 		   comment \'员工头像\',
    salary  		decimal(20) 		   comment \'员工工资\',
    age    		    int(3)     comment \'员工年龄\',
    primary key(id)
)engine=innodb default charset=utf8

库表入库 l_emp
详细设计

ER图、系统模块结构图

\"springboot

5、编码环节

a.环境搭建

  • springboot + mybatis + mysql 引入前端页面
项目名:employee

项目包结构:
	src/main/java
		com.ling.xxx
				.util
				.pojo
				.dao
				.service
				.controller
				...
	src/main/resource
		application.yaml
		application-dev.yaml
		application-prod.yaml
		mapper/*.xml	存放mybatis的mapper配置文件
		sql         	用来存放项目中数据库文件
		static      	用来存放静态资源
		
项目编码:UTF-8
  • 引入Druid数据源和 commons-fileupload 依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>  <!--文件上传下载-->
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

  • 配置yaml
server:
  servlet:
    context-path: /ems_vue
  port: 8081

spring:
  application:
    name: employee
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/l_emp?characterEncoding=UTF-8
    username: root
    password: 123456

# 集成mybatis
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.ling.pojo

# 配置日志
logging:
  level:
    root: info
  • 导入静态资源(登录和注册页面,懒得自己写了)到static目录下
    \"springboot
  • 启动项目,此时可通过 http://localhost:8081/ems_vue/login.html 访问到登录页面

6、模块化编码

Message 消息提示:https://element.eleme.cn/#/zh-CN/component/message#fang-fa

a. 用户模块

验证码

1、前端引入 vue 和 axios

<!-- vue 和 axios  -->
<script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>
<script src=\"https://unpkg.com/vue/dist/vue.js\"></script>
<!-- import JavaScript -->
<!--包含 this.$message.success(\"登录成功!!!\"); 等js操作-->
<script src=\"https://unpkg.com/element-ui/lib/index.js\"></script>
<!--挂载vue-->
<script>
    // Vue.prototype.$http = axios;
    // axios.defaults.baseURI = \"http://localhost:8081/ems_vue\";
    // 自定义配置:新建一个 axios 实例
    const $http = axios.create({
        baseURL: \'http://localhost:8081/ems_vue\',
        timeout: 1000,
        headers: {\'X-Custom-Header\': \'foobar\'}
    });
    let app = new Vue({
        el: \"#app\",
        data: {
            
        },
        created() {
        },
        methods: {
            
        }
    })
</script>

========================================================================

<!-- Element Ui -->
<link rel=\"stylesheet\" href=\"https://unpkg.com/element-ui/lib/theme-chalk/index.css\">
<script src=\"https://unpkg.com/element-ui/lib/index.js\"></script>

验证码工具类:

验证码工具类有很多,我这个也是在网上随便找的一个拿来用,忘记保留出处了~

package com.ling.utils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class VerifyUtil {
    // 验证码字符集
    private static final char[] CHARS = {
            \'0\', \'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'8\', \'9\',
            \'a\', \'b\', \'c\', \'d\', \'e\', \'f\', \'g\', \'h\', \'i\', \'j\', \'k\', \'l\', \'m\', \'n\',
            \'o\', \'p\', \'q\', \'r\', \'s\', \'t\', \'u\', \'v\', \'w\', \'x\', \'y\', \'z\',
            \'A\', \'B\', \'C\', \'D\', \'E\', \'F\', \'G\', \'H\', \'I\', \'J\', \'K\', \'L\', \'M\', \'N\',
            \'O\', \'P\', \'Q\', \'R\', \'S\', \'T\', \'U\', \'V\', \'W\', \'X\', \'Y\', \'Z\'};
    // 字符数量
    private static final int SIZE = 4;
    // 干扰线数量
    private static final int LINES = 3;
    // 宽度
    private static final int WIDTH = 80;
    // 高度
    private static final int HEIGHT = 35;
    // 字体大小
    private static final int FONT_SIZE = 25;

    /**
     * 生成随机验证码及图片
     */
    public static Map<String, Object> createImageCode() {
        StringBuffer sb = new StringBuffer();
        // 1.创建空白图片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        // 2.获取图片画笔
        Graphics graphic = image.getGraphics();
        // 3.设置画笔颜色
        graphic.setColor(Color.LIGHT_GRAY);
        // 4.绘制矩形背景
        graphic.fillRect(0, 0, WIDTH, HEIGHT);
        // 5.画随机字符
        Random ran = new Random();
        for (int i = 0; i < SIZE; i++) {
            // 取随机字符索引
            int n = ran.nextInt(CHARS.length);
            // 设置随机颜色
            graphic.setColor(getRandomColor());
            // 设置字体大小
            graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE));
            // 画字符
            graphic.drawString(CHARS[n] + \"\", i * WIDTH / SIZE, HEIGHT * 2 / 3);
            // 记录字符
            sb.append(CHARS[n]);
        }
        // 6.画干扰线
        for (int i = 0; i < LINES; i++) {
            // 设置随机颜色
            graphic.setColor(getRandomColor());
            // 随机画线
            graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT), ran.nextInt(WIDTH), ran.nextInt(HEIGHT));
        }
        // 7.返回验证码和图片
        Map<String, Object> map = new HashMap<>();
        //验证码
        map.put(\"code\", sb.toString());
        //图片
        map.put(\"image\", image);
        return map;
    }

    /**
     * 随机取色
     */
    public static Color getRandomColor() {
        Random ran = new Random();
        return new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
    }
}

编写一个验证码接口: getImage

@RestController
@CrossOrigin   //允许跨域
public class UserController {

    //生成验证码图片==》响应一个 base64 字符串
    @GetMapping(\"/getImage\")
    public String getImageCode(HttpServletRequest request) throws IOException {
        //1.使用工具类生成验证码(包括image和code)
        Map<String, Object> imageCode = VerifyUtil.createImageCode();
        String code = (String) imageCode.get(\"code\");

        //2.将验证码放入servletContext作用域(前后端分离是没有session概念的)
        request.getServletContext().setAttribute(\"code\", code);

        BufferedImage image = (BufferedImage) imageCode.get(\"image\");
        //3.将图片转化为base64
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //5.响应给浏览器  ImageIO :工具类
        ImageIO.write(image, \"png\", outputStream);
        String encode = Base64Utils.encodeToString(outputStream.toByteArray());

        return encode;
    }

}

前端请求接口并接收数据

let app = new Vue({
    el: \"#app\",
    data: {
        imgCode: \"\",
    },
    created() {
        this.getCodeImage()
    },
    methods: {
        //获取验证码
        async getCodeImage() {
            const {data: res} = await $http.get(\"/getImage\");
            // console.log(\"解构前:\" + res.data);
            console.log(\"后端返回的base64数据:\", res);
            this.imgCode = \"data:image/png;base64,\" + res;
        }
    }
})

用户注册

编写user实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String id;//String 类型的api相当多,方便处理
    private String name;
    private String realname;
    private String password;
    //前端和sql语句传的是true或false,存入数据库的是1或0
    private boolean gender; //用户性别 (1 男 0 女)
    private Date registertime;
    //数据库一个表中有一个tinyint类型的字段,值为0或者1,如果取出来的话,0会变成false,1会变成true。
    private boolean state;  //用户状态(是否启用,1 启用 0 未启用)
}

注册业务实现流程

================1、UserDao=========================
@Mapper
@Repository
public interface UserDao {
    
    // 注册前先判断该用户名是否已存在
    // (该方法不需要再新建一个service接口,直接在UserServiceImpl的注册方法中调用该方法即可)
    // 用户登录可复用该方法
    public User findByUserName(String Username);
    //用户注册
    public void saveUser(User user);
}

===============2、UserMapper.xml=========================
<!-- namespace=绑定一个对应的Dao接口 -->
<mapper namespace=\"com.ling.dao.UserDao\">
    
    <!--注册前先判断该用户名是否已存在,
    登录可复用该方法(根据前端传入的user.name,若结果不为空在比对密码是否正确)-->
    -->
    <select id=\"findByUserName\" resultType=\"User\">
        select * from t_user where username=#{username};
    </select>

    <!-- saveUser对应dao层的方法名,parameterType:参数类型 -->
    <insert id=\"saveUser\" parameterType=\"User\">
        insert into t_user (username,password,gender,registertime,state)
        values (#{username},#{password},#{gender},#{registertime},#{state});
    </insert>

</mapper>

================3、UserService=========================
public interface UserService {
    //用户注册
    public void saveUser(User user);
}

===============4、UserServiceImpl=========================
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    //用户注册
    @Override
    public void saveUser(User user) {
        //0.根据前端输入的用户名,判断用户是否已存在
        User byUserName = userDao.findByUserName(user.getUsername());
        if (byUserName==null){
            //1.设置用户注册时间
            user.setRegistertime(new Date());
            //2.生成用户状态
            user.setState(false);
            //3.调用dao
            userDao.saveUser(user);
        }else {
            throw new RuntimeException(\"该用户已存在!\");
        }
        
    }
    
}

================5、UserController=========================
@RestController
@CrossOrigin   //允许跨域
public class UserController {

    ......
    
    @Autowired
    UserService userService;

    //用户注册
    // 这里必须加 @RequestBody 注解才能拿到前端传递的user对象,
    // 因为前端的 axios 传递数据使用的是 json 格式,这里使用@RequestBody将json字符串转换为对象
    // code:前端输入的验证码,request:为了拿到之前servletContext中存放的验证码yCode对象
    @PostMapping(\"/register\")
    public Map<String,Object> register(@RequestBody User user,String code,HttpServletRequest request){
        System.out.println(user);
        System.out.println(code);

        Map<String, Object> map = new HashMap<>();
        // try/catch捕捉异常 快捷键 CTRL+ALT+T
        try {
            String yCode = (String) request.getServletContext().getAttribute(\"yCode\");
            if (yCode.equalsIgnoreCase(code)){
                //调用业务方法
                userService.saveUser(user);
                map.put(\"state\",true);
                map.put(\"msg\",\"提示:注册成功!\");
            }else {
                throw new RuntimeException(\"验证码错误\");
            }
        } catch (Exception e) {
            e.printStackTrace();
            map.put(\"state\",false);
            // map.put(\"msg\",\"提示:注册失败!\");
            // e.getMessage() 可拿到对应的异常信息,
            // 包括serviceimpl中抛出的异常(该用户已存在!)
            map.put(\"msg\",\"提示:\"+e.getMessage());
        }
        return map;
    }

}

前端异步请求并传值/取值

注意:整个过程中 axios 异步通信是不用提交表单的,所以表单的 name 属性没有任何意义,可直接删除,通过vue的 v-model=“user.username” 双向绑定来获取输入的数据

所以提交按钮类型也不需要 submit 而 是用 button 绑定一个点击事件即可

<!DOCTYPE html>
<html lang=\"en\">
<head>
    ......
    <!--和 element-ui/lib/index.js 一起可使用 this.$message.success()-->
    <link rel=\"stylesheet\" href=\"https://unpkg.com/element-ui/lib/theme-chalk/index.css\">
</head>
<body>
......
    <!-- Form Panel    -->
    <div class=\"col-lg-6 bg-white\">
        <div class=\"form d-flex align-items-center\">
            <div class=\"content\">
                <div class=\"form-group\">
                    <input id=\"register-username\" v-model=\"user.username\" class=\"input-material\" type=\"text\"
                           placeholder=\"请输入用户名/姓名\">
                    <div class=\"invalid-feedback\">
                        用户名必须在2~10位之间
                    </div>
                </div>
                <div class=\"form-group\">
                    <input id=\"register-password\" v-model=\"user.password\" class=\"input-material\"
                           type=\"password\"
                           placeholder=\"请输入密码\">
                    <div class=\"invalid-feedback\">
                        密码必须在6~10位之间
                    </div>
                </div>
                <div class=\"form-group\">
                    <input id=\"register-passwords\" class=\"input-material\" type=\"password\"
                           placeholder=\"确认密码\">
                    <div class=\"invalid-feedback\">
                        两次密码必须相同 且在6~10位之间
                    </div>
                </div>
                <div class=\"form-group\">
                    &emsp;&emsp;<input type=\"radio\" v-model=\"user.gender\" value=\"true\">
                    &emsp;&emsp;<input type=\"radio\" v-model=\"user.gender\" value=\"false\">
                </div>
                <div class=\"form-group\" style=\"display: flex\">
                    <img :src=\"imgCode\" id=\"img-vcode\"
                         @click=\"getCodeImage\" alt=\"验证码\" style=\"padding: 0 10px\">
       <input class=\"input-material\" v-model=\"code\" type=\"text\"
                           placeholder=\"输入验证码\">
                </div>
     <div class=\"form-group\">
       <button id=\"regbtn\" type=\"button\" @click=\"register\" class=\"btn btn-primary\">
                 注册
           </button>
      </div>
             <small>已有账号?</small><a href=\"login.html\" class=\"signup\">&nbsp;登录</a>
            </div>
.......

<script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>
            
<!--<script src=\"https://unpkg.com/vue/dist/vue.js\"></script>-->
<script src=\"https://cdn.jsdelivr.net/npm/vue/dist/vue.js\"></script>
            
<!-- 和element-ui/lib/theme-chalk/index.css 一起可使用 this.$message.success(\"登录成功!\"); 等操作-->
<script src=\"https://unpkg.com/element-ui/lib/index.js\"></script>
<!--挂载vue-->
<script>
    // Vue.prototype.$http = axios;
    // axios.defaults.baseURI = \"http://localhost:8081/ems_vue\";
    // 自定义配置:新建一个 axios 实例
    const $http = axios.create({
        baseURL: \'http://localhost:8081/ems_vue\',
        timeout: 1000,
        headers: {\'X-Custom-Header\': \'foobar\'}
    });
    let app = new Vue({
        el: \"#app\",
        data: {
            imgCode: \"\",
            user: {
                // value=\"true\",传值默认为男,前端传true或false,存入数据库的是1或0
                gender: true
            },
            code: \"\"
        },
        created() {
            this.getCodeImage()
        },
        methods: {
            //获取验证码
            async getCodeImage() {
                const {data: res} = await $http.get(\"/getImage\");
                // console.log(\"解构前:\" + res.data);
                console.log(\"后端返回的base64数据:\", res);
                this.imgCode = \"data:image/png;base64,\" + res;
            },
            // 注册
            async register() {
                const {data: res} = await $http.post(\"/register?code=\" + this.code, this.user);
                console.log(\"后端返回的数据:\", res);
                if (res.state) {
                    // this.$message.success(\"注册成功!\");
                    alert(res.msg + \"点击确定跳转至登录页面\");
                    location.href = \'./login.html\';
                } else {
                    this.$message.error(\"注册失败\");
                    // 该用户已存在!或 验证码错误
                    alert(res.msg)
                }
            }
        }
    })
</script>
......
</body>
</html>

用户登录

======service层接口=======
//用户登录
public User login(User user);

======service层实现类=======
//登录
@Override
public User login(User user) {
    //1.根据前端输入的用户名,判断用户是否已存在
    User userByName = userDao.findByUserName(user.getUsername());
    //或 if (!ObjectUtils.isEmpty(userByName))  对象不为空
    if (userByName!=null){
        //用户存在,比对密码
        if (userByName.getPassword().equals(user.getPassword())){
            return userByName;
        }else {
            throw new RuntimeException(\"密码不正确!\");
        }
    }else {
        throw new RuntimeException(\"用户不存在!\");
    }
}

===============Controller层=======================
//登录
@PostMapping(\"/login\")
    public Map<String, Object> login(@RequestBody User user) {
        System.out.println(\"前端传来的user:\"+user);
        Map<String, Object> map = new HashMap<>();
        try {
            User userDB = userService.login(user);
            map.put(\"userDB\",userDB);
            map.put(\"state\",true);
            map.put(\"msg\",\"登录成功!\");
        } catch (Exception e) {
            e.printStackTrace();
            map.put(\"state\",false);
            map.put(\"msg\",e.getMessage());
        }
        return map;
    }

前端

<!DOCTYPE html>
<html lang=\"en\">
<head>
    ......

    <!--和 element-ui/lib/index.js 一起可使用 this.$message.success()-->
    <link rel=\"stylesheet\" href=\"https://unpkg.com/element-ui/lib/theme-chalk/index.css\">

</head>
<body>
<div class=\"page login-page\" id=\"app\">
    ...
          <form method=\"post\" action=\"#\" class=\"form-validate\" id=\"loginFrom\">
             <div class=\"form-group\">
      <input id=\"login-username\" type=\"text\" required data-msg=\"请输入用户名\"
                        placeholder=\"用户名\" v-model=\"user.username\" value=\"admin\"
                                           class=\"input-material\">
                                </div>
                                <div class=\"form-group\">
       <input id=\"login-password\" v-model=\"user.password\" type=\"password\" required
                                           data-msg=\"请输入密码\"
                                           placeholder=\"密码\" class=\"input-material\">
                                </div>
       <button id=\"login\" type=\"button\" @click=\"login\" class=\"btn btn-primary\">登录</button>
...

<script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>

<!--<script src=\"https://unpkg.com/vue/dist/vue.js\"></script>-->
<script src=\"https://cdn.jsdelivr.net/npm/vue/dist/vue.js\"></script>
<!-- 和element-ui/lib/theme-chalk/index.css 一起可使用 this.$message.success(\"登录成功!\"); 等操作-->
<script src=\"https://unpkg.com/element-ui/lib/index.js\"></script>
<!--挂载vue-->
<script>
    const $http = axios.create({
        baseURL: \'http://localhost:8081/ems_vue\',
        timeout: 1000,
        headers: {\'X-Custom-Header\': \'foobar\'}
    });
    let app = new Vue({
        el: \"#app\",
        data: {
            //用户信息,请求提交参数
            user: {}
        },
        created() {
        },
        methods: {
            // 用户登录
            async login() {
                const {data: res} = await $http.post(\"/login\", this.user);
                console.log(\"后端返回的数据:\", res);
                if (res.state) {
                    this.$message.success(\'登录成功\');
                    localStorage.setItem(\"user\",JSON.stringify(res.userDB));
                    location.href = \'./home.html\';
                } else {
                    this.$message.error(\"登录失败!\");
                }
            }
        }
    })
</script>
</body>
</html>

问题:登录成功时应将后,端返回的用户信息保存在浏览器中,方便后面其他业务使用

前后端分离的项目用session保存,存取比较麻烦,可以使用localStorage保存

Local Storage 以key :value 形式,可以在浏览器的内存中存储一些信息
\"springboot

if (res.state) {
 this.$message.success(\'登录成功\');
 // 将后端返回的用户信息保存在浏览器的 Local Storage 中(key:value),方便后面业务使用
 // 这样保存的是一个对象,不方便使用,使用javascript中的 JSON.stringify() 将对象转化为json字符串
 // localStorage.setItem(\"user\",res.userDB);
 
 localStorage.setItem(\"user\",JSON.stringify(res.userDB));
 console.log(\"存入localStorage的user:\"+localStorage.getItem(\"user\"));
 
console.log(\"将json字符串转换成json对象的user:\"+JSON.parse(localStorage.getItem(\"user\")));   
 
} else {
 this.$message.error(res.msg);
}

\"springboot

拓展 sessionStorage

除了使用浏览器的 localStorage保存外,也可以使用浏览器的 sessionStorage 来保存用户对象,sessionStorage 也是 key :value 形式保存,以key取值,和 **localStorage **用法相同

// JSON.stringify(res.userDB) 将userDB对象转化为json字符串
sessionStorage.setItem(\"user\", JSON.stringify(res.userDB));


// JSON.parse() 将一个json字符串转化为对象
console.log(\"存入sessionStorage的user:\", JSON.parse(sessionStorage.getItem(\"user\")));


===========================拓展:安全退出时可用到========================
// 清除localStorage中的数据
localStorage.clear();
//移除user
localStorage.removeItem(\"user\");
//刷新页面(已淘汰)
location.reload(true)

END

  这期先更这些,后面主要是员工管理模块的CRUD,还有文件上传,以及elementUI的一些技术点,比较重要,后面的会写详细些,更新完了就将项目上传至码云。并首先在个人公众号遇见0和1发布,获取资料、学习更多的编程以及编程之外的知识,欢迎关注哦

\"在这里插入图片描述\"

编程之外

项目源码已,上传至码云:https://gitee.com/lingstudy/small_employee
编写不易,若对你有帮助的话,点个 Star 支持一下吧!

欢迎入坑哦!
\"在这里插入图片描述\"

\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号