分布式文件系统 SpringBoot+FastDFS+Vue.js【三】

分布式文件系统 SpringBoot+FastDFS+Vue.js【三】

  • 七、创建后台--分角色管理
    • 7.1.创建后台数据库表
    • 7.2.创建实体类
      • 7.2.1.Admin
      • 7.2.2.Menu
      • 7.2.3.MenuBean
      • 7.2.4.Role
      • 7.2.5.RoleMenu
    • 7.3.编辑配置文件application.yml
    • 7.4.编写工具类
      • 7.4.1.AuthContextHolder
      • 7.4.2.HttpUtils
      • 7.4.3.StringUtils
      • 7.4.4.JwtHelper
      • 7.4.5.MD5
      • 7.4.6.ShiroMD5
    • 7.5.Mapper层
      • 7.5.1.AdminMapper.xml
      • 7.5.2.MenuMapper.xml
      • 7.5.3.RoleMenuMapper.xml
      • 7.5.4.AdminMapper
      • 7.5.5.MenuMapper
      • 7.5.6.RoleMenuMapper
    • 7.6.Service层
      • 7.6.1.AdminService
      • 7.6.2.MenuBeanService
      • 7.6.3.MenuService
      • 7.6.4.RoleMenuService
      • 7.6.5.AdminServiceImpl
      • 7.6.6.MenuBeanServiceImpl
      • 7.6.7.MenuServiceImpl
      • 7.6.8.RoleMenuServiceImpl
    • 7.7.controller层
      • 7.7.1.AdminController
      • 7.7.2.MenuController
    • 7.8.前端vue改造
      • 7.8.1.登录login.vue
      • 7.8.2.后台页面admin.vue
      • 7.8.2.后台顶部页面navcon.vue
      • 7.8.2.后台左侧页面leftnav.vue
      • 7.8.2.主界面main.vue
      • 7.8.3.identify.vue
      • 7.8.4.request.js
      • 7.8.5.store.js
      • 7.8.6.util.js
      • 7.8.7.index.js
      • 7.8.8.admin.js
      • 7.8.9.menu.js
      • 7.8.10.main.js
    • 7.9.效果展示
  • 八、总结
  • endl 本文章下载链接

七、创建后台–分角色管理

7.1.创建后台数据库表

USE fastdfs;

CREATE TABLE `admin` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '管理员表id',
  `role_id` int(11) DEFAULT '0' COMMENT '角色id',
  `role_name` varchar(50) DEFAULT NULL COMMENT '角色名',
  `name` varchar(100) NOT NULL COMMENT '管理员名',
  `password` varchar(50) NOT NULL COMMENT '管理员密码',
  `sex` varchar(50) DEFAULT NULL COMMENT '性别(男,女)',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态(0:未工作 1:工作中)',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机号',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='管理员表';

CREATE TABLE `role` (
  `role_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色表id',
  `role_name` varchar(25) NOT NULL COMMENT '角色名\r\n1.医生\r\n2.科室\r\n3.医院管理员\r\n4.超级管理员',
  `description` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)',
  PRIMARY KEY (`role_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色表';

CREATE TABLE `menu` (
  `menu_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '资源ID',
  `menu_name` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '资源名称',
  `parent_id` int(11) DEFAULT NULL COMMENT '父id',
  `url` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '资源路径',
  `menus` tinyint(4) DEFAULT '1' COMMENT '是否有子菜单(0:是,1:)',
  `icon` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图标',
  `create_time` datetime NOT NULL COMMENT '创建日期',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)',
  PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=COMPACT COMMENT='菜单栏表';

CREATE TABLE `role_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色菜单表id',
  `role_id` int(11) NOT NULL COMMENT '角色表id(外键)',
  `menu_id` int(11) NOT NULL COMMENT '菜单表id(外键)',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=137 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

INSERT INTO `admin` VALUES (1, 1, '超级管理员', 'root', '123456', '女', 18, 0, '123456@qq.com', '12345678902', '2021-06-30 12:14:13', '2024-02-14 21:40:26', 1);
INSERT INTO `admin` VALUES (2, 2, '用户', 'admin', '123456', '男', 17, 0, '123456@qq.com', '12345678902', '2021-06-30 12:14:13', '2022-03-13 08:41:45', 1);

INSERT INTO `menu` VALUES (2, '文件管理', 0, '', 1, 'el-icon-folder', '2021-06-27 19:58:39', '2024-02-14 22:20:52', 1);
INSERT INTO `menu` VALUES (3, '我的文件', 2, 'file', 0, 'nel-icon-folder-opened', '2021-06-27 21:36:01', '2024-02-14 22:19:50', 1);
INSERT INTO `menu` VALUES (4, '用户管理', 0, '', 1, 'el-icon-user', '2024-02-14 21:58:54', '2024-02-14 22:00:02', 1);
INSERT INTO `menu` VALUES (5, '用户', 4, 'user', 0, 'el-icon-s-custom', '2021-06-27 21:37:17', '2024-02-14 22:15:37', 1);
INSERT INTO `menu` VALUES (6, '个人信息管理', 0, NULL, 1, 'el-icon-user', '2022-03-04 17:12:46', '2024-02-14 21:59:26', 1);
INSERT INTO `menu` VALUES (7, '我的信息', 6, 'info', 0, 'el-icon-user', '2022-03-04 17:13:23', '2024-02-14 22:00:27', 1);
INSERT INTO `menu` VALUES (8, '修改邮箱', 6, 'email', 0, 'el-icon-user', '2022-03-05 10:53:37', '2024-02-14 22:00:29', 1);
INSERT INTO `menu` VALUES (9, '修改密码', 6, 'password', 0, 'el-icon-user', '2022-03-05 10:54:53', '2024-02-14 22:00:33', 1);
INSERT INTO `menu` VALUES (10, '文件上传', 2, 'upload', 0, 'el-icon-upload', '2024-02-14 22:18:49', '2024-02-14 22:18:49', 1);

INSERT INTO `role` VALUES (1, '超级管理员', '拥有所有权限', '2022-03-04 14:46:19', '2022-03-04 15:30:00', 1);
INSERT INTO `role` VALUES (2, '用户', '用户', '2022-03-04 14:46:19', '2024-02-14 21:53:44', 1);

INSERT INTO `role_menu` VALUES (1, 2, 10, '2024-02-14 22:20:19', '2024-02-14 22:20:19', 1);
INSERT INTO `role_menu` VALUES (2, 2, 2, '2024-02-14 22:02:28', '2024-02-14 22:14:49', 1);
INSERT INTO `role_menu` VALUES (3, 2, 3, '2024-02-14 22:02:37', '2024-02-14 22:14:49', 1);
INSERT INTO `role_menu` VALUES (4, 2, 6, '2024-02-14 22:03:05', '2024-02-14 22:14:49', 1);
INSERT INTO `role_menu` VALUES (5, 2, 7, '2024-02-14 22:03:15', '2024-02-14 22:14:49', 1);
INSERT INTO `role_menu` VALUES (6, 2, 8, '2024-02-14 22:03:23', '2024-02-14 22:14:49', 1);
INSERT INTO `role_menu` VALUES (7, 2, 9, '2024-02-14 22:03:36', '2024-02-14 22:14:49', 1);
INSERT INTO `role_menu` VALUES (9, 1, 4, '2024-02-14 22:04:10', '2024-02-14 22:14:55', 1);
INSERT INTO `role_menu` VALUES (10, 1, 5, '2024-02-14 22:04:18', '2024-02-14 22:14:55', 1);
INSERT INTO `role_menu` VALUES (11, 1, 6, '2024-02-14 22:04:25', '2024-02-14 22:14:55', 1);
INSERT INTO `role_menu` VALUES (12, 1, 7, '2024-02-14 22:04:33', '2024-02-14 22:14:55', 1);
INSERT INTO `role_menu` VALUES (13, 1, 8, '2024-02-14 22:04:44', '2024-02-14 22:14:55', 1);
INSERT INTO `role_menu` VALUES (14, 1, 9, '2024-02-14 22:04:53', '2024-02-14 22:14:55', 1);

7.2.创建实体类

7.2.1.Admin

/**
 * 管理员表
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Repository
public class Admin implements Serializable {

    private Integer id; //管理员表id

    private Integer roleId; //角色id

    private String roleName; //角色名

    private String name; //管理员名

    private String password; //管理员密码

    private String sex; //性别(男,女)

    private Integer age; //年龄

    private Integer status; //状态(0:未工作 1:工作中)

    private String email; //邮箱

    private String phone; //手机号

    private LocalDateTime createTime; //创建时间

    private LocalDateTime updateTime; //更新时间

    private Integer isDeleted; //删除标记(0:不可用 1:可用)
}

7.2.2.Menu

/**
 * 菜单栏表
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Repository
public class Menu implements Serializable {

    private Integer menuId; //资源ID

    private String menuName; //资源名称

    private Integer parentId; //父id

    private String url; //资源路径

    private Integer menus; //是否有子菜单(0:是,1:否)

    private String icon; //图标

    private LocalDateTime createTime; //创建日期

    private LocalDateTime updateTime; //更新时间

    private Integer isDelete; //删除标记(0:不可用 1:可用)
}

7.2.3.MenuBean

/**
 * 菜单栏表
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Repository
public class MenuBean implements Serializable {

    private Integer menuId; //资源ID

    private String menuName; //资源名称

    private Integer parentId; //父id

    private String url; //资源路径

    private Integer menus; //是否有子菜单(0:是,1:否)

    private String icon; //图标

    private List<Menu> menuList;
}

7.2.4.Role

/**
 * 角色表
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Repository
public class Role implements Serializable {

    private Integer roleId; //角色表id

    private String roleName; //角色名 1.用户 2.超级管理员

    private String description; //角色描述

    private LocalDateTime createTime; //创建时间

    private LocalDateTime updateTime; //更新时间

    private Integer isDelete; //删除标记(0:不可用 1:可用)
}

7.2.5.RoleMenu

/**
 * 角色菜单中间表
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Repository
public class RoleMenu implements Serializable {

    private Integer id; //角色菜单表id

    private Integer roleId; //角色表id(外键)

    private Integer menuId; //菜单表id(外键)

    private LocalDateTime createTime; //创建时间

    private LocalDateTime updateTime; //更新时间

    private Integer isDelete; //删除标记(0:不可用 1:可用)
}

7.3.编辑配置文件application.yml

server:
  port: 9090

#Mybatis配置
mybatis:
  # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
  # 如果是放在resource目录 classpath:/mapper/*Mapper.xml
  configuration: #sql日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #驼峰命名
    map-underscore-to-camel-case: true
  mapper-locations: classpath:com/orange/fastdfs/mapper/*.xml

#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

spring:
  application:
    name: fastdfs-java  #应用的名字
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  #数据库连接信息
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.229.141:3306/fastdfs?characterEncoding=utf-8&&useSSL=false
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    #数据源其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

# 文件上传的配置
# linux临时文件目录
# mkdir -p /data/tmp/updatefile
  servlet:
    multipart:
      location: /data/tmp/updatefile
      max-file-size: 10MB
      max-request-size: 10MB

7.4.编写工具类

7.4.1.AuthContextHolder

/**
 * 获取当前用户信息工具类
 **/
public class AuthContextHolder {

    //获取当前用户id
    public static Integer getAdminId(HttpServletRequest request) {
        //从header获取token
        String token = request.getHeader("token");
        //jwt从token获取userid
        Integer adminId = JwtHelper.getAdminId(token);
        return adminId;
    }

    //获取当前用户名称
    public static String getAdminName(HttpServletRequest request) {
        //从header获取token
        String token = request.getHeader("token");
        //jwt从token获取userid
        String adminName = JwtHelper.getAdminName(token);
        return adminName;
    }

}

7.4.2.HttpUtils

public class HttpUtils {
    /**
     * 将通知参数转化为字符串
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

7.4.3.StringUtils

public final class StringUtils {

    public static boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    public static boolean isBlank(CharSequence cs) {
        if (cs != null) {
            int length = cs.length();

            for(int i = 0; i < length; ++i) {
                if (!Character.isWhitespace(cs.charAt(i))) {
                    return false;
                }
            }
        }

        return true;
    }

    public static boolean isNotBlank(CharSequence cs) {
        return !isBlank(cs);
    }
}

7.4.4.JwtHelper

/**
 * JWT token 工具类
 **/
@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtHelper {

    //过期时间
    private static long tokenExpiration;
    //签名秘钥
    private static String tokenSignKey;
    //请求头
    private static String tokenHeader;

    //校验token是否正确
    public static boolean verify(String token,Integer adminId,String adminName) {
        try {
            String id = String.valueOf(adminId);
            Algorithm algorithm = Algorithm.HMAC256(id);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("adminName", adminName)
                    .withClaim("adminId",adminId)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 生成签名
     */
    public static String createToken(Integer adminId,String adminName) {
        try {
            // 指定过期时间
            Date date = new Date(System.currentTimeMillis() + tokenExpiration);
            String id = String.valueOf(adminId);
            Algorithm algorithm = Algorithm.HMAC256(id);
            return JWT.create()
                    .withSubject("admin")
                    .withClaim("adminName", adminName)
                    .withClaim("adminId",adminId)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    //根据token字符串得到用户id
    public static Integer getAdminId(String token) {
        if(StringUtils.isBlank(token)) {
            return null;
        }
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("adminId").asInt();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    //根据token字符串得到用户名称
    public static String getAdminName(String token) {
        if(StringUtils.isBlank(token)) {
            return "";
        }
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("adminName").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }



    public static long getTokenExpiration() {
        return tokenExpiration;
    }

    public static void setTokenExpiration(long tokenExpiration) {
        JwtHelper.tokenExpiration = tokenExpiration;
    }

    public static String getTokenSignKey() {
        return tokenSignKey;
    }

    public static void setTokenSignKey(String tokenSignKey) {
        JwtHelper.tokenSignKey = tokenSignKey;
    }

    public static String getTokenHeader() {
        return tokenHeader;
    }

    public static void setTokenHeader(String tokenHeader) {
        JwtHelper.tokenHeader = tokenHeader;
    }

    public static void main(String[] args) {
        String token = JwtHelper.createToken(1,"小鱼儿");
        System.out.println(token);
        System.out.println(JwtHelper.getAdminId(token));
        System.out.println(JwtHelper.getAdminName(token));
        boolean a = verify(token,1,"小鱼儿");
        System.out.println(a);
    }

}

7.4.5.MD5

/**
 * 1.MD5加密字符串(32位大写)
 * 2.MD5加密字符串(32位小写)
 * <p>
 * MD5在线加密:https://md5jiami.51240.com/
 * 3.将二进制字节数组转换为十六进制字符串
 * 4.Unicode中文编码转换成字符串
 */
public class MD5 {

	/**
     * 生成md5
     * @param message
     * @return
     */
    public static String getMD5(String message) {
        String md5str = "";
        try {
            //1 创建一个提供信息摘要算法的对象,初始化为md5算法对象
            MessageDigest md = MessageDigest.getInstance("MD5");

            //2 将消息变成byte数组
            byte[] input = message.getBytes();

            //3 计算后获得字节数组,这就是那128位了
            byte[] buff = md.digest(input);

            //4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串
            md5str = bytesToHex(buff);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return md5str;
    }

    /**
     * MD5加密字符串(32位大写)
     *
     * @param string 需要进行MD5加密的字符串
     * @return 加密后的字符串(大写)
     */
    public static String md5Encrypt32Upper(String string) {
        byte[] hash;
        try {
            //创建一个MD5算法对象,并获得MD5字节数组,16*8=128位
            hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Huh, MD5 should be supported?", e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Huh, UTF-8 should be supported?", e);
        }

        //转换为十六进制字符串
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString().toUpperCase();
    }

    /**
     * MD5加密字符串(32位小写)
     *
     * @param string 需要进行MD5加密的字符串
     * @return 加密后的字符串(小写)
     */
    public static String md5Encrypt32Lower(String string) {
        byte[] hash;
        try {
            //创建一个MD5算法对象,并获得MD5字节数组,16*8=128位
            hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Huh, MD5 should be supported?", e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Huh, UTF-8 should be supported?", e);
        }

        //转换为十六进制字符串
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString().toLowerCase();
    }

    /**
     * 将二进制字节数组转换为十六进制字符串
     *
     * @param bytes 二进制字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuffer md5str = new StringBuffer();
        //把数组每一字节换成16进制连成md5字符串
        int digital;
        for (int i = 0; i < bytes.length; i++) {
             digital = bytes[i];

            if(digital < 0) {
                digital += 256;
            }
            if(digital < 16){
                md5str.append("0");
            }
            md5str.append(Integer.toHexString(digital));
        }
        return md5str.toString().toUpperCase();
    }

    /**
     * Unicode中文编码转换成字符串
     */
    public static String unicodeToString(String str) {
        Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
        Matcher matcher = pattern.matcher(str);
        char ch;
        while (matcher.find()) {
            ch = (char) Integer.parseInt(matcher.group(2), 16);
            str = str.replace(matcher.group(1), ch + "");
        }
        return str;
    }

/*    @Test
    public void testMD5(){
    	String message="123,123";
    	String result=getMD5(message);
    	System.out.println(result);
    }*/

}

7.4.6.ShiroMD5

/**
 * MD5加密工具类
 **/
@Slf4j
public final class ShiroMD5 {

    /**
     * MD5对密码进行加密算法
     */
    public static String getPwd(String email,String password){
        log.info("密码加盐,原密码为:"+password);
        Md5Hash md5 = new Md5Hash(password, email, 1024);
        String newMd5Password = md5.toHex();
        System.out.println("加盐后的密码是:" +newMd5Password);
        log.info("密码加密后,生成的新密码为:"+newMd5Password);
        return newMd5Password;
    }
}

7.5.Mapper层

7.5.1.AdminMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.orange.fastdfsjava.mapper.AdminMapper">

    <select id="selectByNamePwd" resultType="com.orange.fastdfsjava.pojo.Admin">
        select *
        from admin
        where name = #{name}
          and password = #{password}
    </select>

    <select id="selectById" resultType="com.orange.fastdfsjava.pojo.Admin">
        select *
        from admin
        where id = #{id}
    </select>

    <select id="selectCountByNamePwd" resultType="java.lang.Integer">
        select count(*)
        from admin
        where name = #{name}
          and password = #{password}
    </select>
</mapper>

7.5.2.MenuMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.orange.fastdfsjava.mapper.MenuMapper">

    <select id="selectById" resultType="com.orange.fastdfsjava.pojo.Menu">
        select *
        from menu
        where menu_id = #{menuId}
    </select>
</mapper>

7.5.3.RoleMenuMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.orange.fastdfsjava.mapper.RoleMenuMapper">

    <select id="selectByRoleId" resultType="com.orange.fastdfsjava.pojo.RoleMenu">
        select *
        from role_menu
        where role_id = #{roleId}
    </select>
</mapper>

7.5.4.AdminMapper

@Mapper
public interface AdminMapper {
    Admin selectByNamePwd(String name, String password);

    Admin selectById(Integer id);

    int selectCountByNamePwd(String name, String password);
}

7.5.5.MenuMapper

@Mapper
public interface MenuMapper {
    Menu selectById(Integer menuId);
}

7.5.6.RoleMenuMapper

@Mapper
public interface RoleMenuMapper {
    List<RoleMenu> selectByRoleId(Integer roleId);
}

7.6.Service层

7.6.1.AdminService

public interface AdminService {
    Admin selectAdminLogin(String name, String password);

    Admin getAdminById(Integer adminId);

    int selectCountAdminLogin(String name, String password);
}

7.6.2.MenuBeanService

public interface MenuBeanService {

    List<MenuBean> selectByRoleId(Integer roleId);
}

7.6.3.MenuService

public interface MenuService {
    Menu selectByMenuId(Integer menuId);
}

7.6.4.RoleMenuService

public interface RoleMenuService {
    List<RoleMenu> selectByRoleId(Integer roleId);
}

7.6.5.AdminServiceImpl

/**
 * 管理员表
 */
@Service
public class AdminServiceImpl implements AdminService {

    @Resource
    private AdminMapper adminMapper;

    @Override
    public Admin selectAdminLogin(String name, String password) {
        return adminMapper.selectByNamePwd(name, password);
    }

    @Override
    public Admin getAdminById(Integer id) {
        return adminMapper.selectById(id);
    }

    @Override
    public int selectCountAdminLogin(String name, String password) {
        return adminMapper.selectCountByNamePwd(name, password);
    }
}

7.6.6.MenuBeanServiceImpl

@Service
public class MenuBeanServiceImpl implements MenuBeanService {

    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Resource
    private MenuMapper menuMapper;

    @Override
    public List<MenuBean> selectByRoleId(Integer roleId) {

        List<MenuBean> menuBeans = new ArrayList<>();
        List<RoleMenu> roleMenuList = roleMenuMapper.selectByRoleId(roleId);
        for (RoleMenu roleMenu : roleMenuList){
            Menu menu = menuMapper.selectById(roleMenu.getMenuId());
            if (menu.getParentId()==0){
                MenuBean menuBean = new MenuBean();
                menuBean.setMenuId(menu.getMenuId());
                menuBean.setMenuName(menu.getMenuName());
                menuBean.setIcon(menu.getIcon());
                menuBean.setUrl(menu.getUrl());
                menuBean.setParentId(menu.getParentId());
                menuBean.setMenus(menu.getMenus());
                menuBeans.add(menuBean);
            }
        }

        List<MenuBean> menuBeanList = new ArrayList<>();

        for (MenuBean menuBean : menuBeans){
            MenuBean menuBean1 = menuBean;
            List<Menu> menuList = new ArrayList<>();
            for (RoleMenu roleMenu : roleMenuList){
                Menu menu1 = menuMapper.selectById(roleMenu.getMenuId());
                if (menu1.getParentId().equals(menuBean.getMenuId())){
                    menuList.add(menu1);
                }
            }
            menuBean1.setMenuList(menuList);
            menuBeanList.add(menuBean1);
        }

        return menuBeanList;
    }
}

7.6.7.MenuServiceImpl

/**
 * 菜单栏表
 */
@Service
public class MenuServiceImpl implements MenuService {

    @Resource
    private MenuMapper menuMapper;

    @Override
    public Menu selectByMenuId(Integer menuId) {
        return menuMapper.selectById(menuId);
    }
}

7.6.8.RoleMenuServiceImpl

/**
 * 角色菜单中间表
 */
@Service
public class RoleMenuServiceImpl implements RoleMenuService {

    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Override
    public List<RoleMenu> selectByRoleId(Integer roleId) {
        return roleMenuMapper.selectByRoleId(roleId);
    }
}

7.7.controller层

7.7.1.AdminController

/**
 * 管理员表
 */
@Slf4j
@CrossOrigin
@RestController
@RequestMapping("/admins")
public class AdminController {

    @Resource
    private AdminService adminService;

    //管理员进行用户名密码登录
    @PostMapping("{name}/{password}")
    public R loginAdmin(@PathVariable String name, @PathVariable String password) {
        log.info("管理员进行用户名:===>{},密码登录:===>{}", name, password);
        int count = adminService.selectCountAdminLogin(name, password);
        if (count > 0) {
            Admin admin = adminService.selectAdminLogin(name, password);
            String adminName = admin.getName();
            String roleName = admin.getRoleName();
            if (StringUtils.isNotBlank(adminName)) {
                String token = JwtHelper.createToken(admin.getId(), admin.getName());

                return R.ok().setMessage("success").setCode(200).data("adminName", adminName).data("roleName", roleName).data("token", token);
            }
        }
        return R.error().setMessage("fail");
    }

    //根据id查询管理员
    @GetMapping()
    public R getAdminById(HttpServletRequest request) {
        Integer adminId = AuthContextHolder.getAdminId(request);
        Admin admin = adminService.getAdminById(adminId);
        log.info("根据id查询管理员:===>{}", admin);
        return R.ok().setMessage("success").setCode(200).data("admin", admin);
    }

}

7.7.2.MenuController

/**
 * 菜单栏表 前端控制器
 */
@Slf4j
@RestController
@RequestMapping("/menus")
public class MenuController {

    @Resource
    private AdminService adminService;

    @Resource
    private MenuBeanService menuBeanService;

    /**
     * 根据角色获取不同的菜单栏列表
     * @param request
     * @return
     * @throws Exception
     */
    @GetMapping()
    public R getMenuByRoleId(HttpServletRequest request) throws Exception{
        try {
            int adminId = AuthContextHolder.getAdminId(request);
            Admin admin = adminService.getAdminById(adminId);
            Integer roleId = admin.getRoleId();

            List<MenuBean> menuBeanList = menuBeanService.selectByRoleId(roleId);

            return R.ok().setCode(200).setMessage("查询菜单列表success").data("menuBeanList",menuBeanList);
        }catch (Exception e){
            return R.error().setCode(208).setMessage("请求失败,请进行登录后操作");
        }
    }
}

7.8.前端vue改造

npm install js-cookie --save
npm install --save vue-qriously
npm install --save vuex

7.8.1.登录login.vue

<template>
  <div class="login-wrap">
    <el-form label-position="left" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="demo-ruleForm login-container">
      <h3 class="title">用户登录</h3>
      <el-form-item prop="username">
        <el-input type="text" v-model="ruleForm.username" auto-complete="off" placeholder="账号"></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input type="password" v-model="ruleForm.password" auto-complete="off" placeholder="密码"></el-input>
      </el-form-item>
      <el-row>
        <el-col :span="12">
          <el-form-item prop="code">
            <el-input type="text" v-model="ruleForm.code" auto-complete="off" placeholder="图形验证码" @keyup.enter.native="submitForm('ruleForm')"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="12" class="code-box">
          <img :src="ruleForm.codeimg" alt="" class="codeimg">
          <div @click="refreshCode" style="margin-right: 50px;margin-top: -18px">
            <SIdentify :identifyCode="identifyCode"></SIdentify>
          </div>
        </el-col>

      </el-row>
      <el-checkbox class="remember" v-model="rememberpwd">记住密码</el-checkbox>
      <el-form-item style="width:100%;">
        <el-button type="primary" style="width:100%;" @click="submitForm('ruleForm')" :loading="logining">登录</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script type="text/ecmascript-6">

import cookie from "js-cookie";
import SIdentify from "@/view/code/identify";
import adminApi from "../api/fastdfs/admin";
export default {
  name: 'login',
  components: {
    SIdentify
  },
  data() {
    return {
      //定义loading默认为false
      logining: false,
      // 记住密码
      rememberpwd: false,
      ruleForm: {
        //username和password默认为空
        username: '',
        password: '',
        code: '',
        randomStr: '',
        codeimg: ''
      },
      //rules前端验证
      rules: {
        username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
        password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
        code: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
      },

      //验证码
      identifyCodes: "1234567890",
      identifyCode: ""
    }
  },
  // 创建完毕状态(里面是操作)
  created() {
    this.$message({
      message: '账号密码及验证码不为空即可',
      type: 'success'
    })


  },
  mounted() {
    const self = this
    self.identifyCode = "";
    self.makeCode(this.identifyCodes, 4);
    self.ruleForm.codeimg = self.identifyCode

    //验证码输入框
    self.ruleForm.code = self.identifyCode
  },
  // 里面的函数只有调用才会执行
  methods: {
    //生成验证码工具
    randomNum(min, max) {
      return Math.floor(Math.random() * (max - min) + min);
    },
    //刷新验证码
    refreshCode() {
      this.identifyCode = "";
      this.makeCode(this.identifyCodes, 4);
      this.ruleForm.codeimg = this.identifyCode

      //验证码输入框
      this.ruleForm.code = this.identifyCode
    },
    //验证
    makeCode(o, l) {
      for (let i = 0; i < l; i++) {
        this.identifyCode += this.identifyCodes[
            this.randomNum(0, this.identifyCodes.length)
            ];
      }
    },
    setCookies(adminName, token,roleName) {
      cookie.set('adminToken', token, { domain: 'localhost' })
      cookie.set('adminName', adminName, { domain: 'localhost' })
      cookie.set('roleName', roleName, { domain: 'localhost' })
    },
    //获取info列表
    submitForm(formName) {
      this.$refs[formName].validate(valid => {
        if (valid) {
          if (this.ruleForm.code===this.ruleForm.codeimg){
            adminApi.loginAdmin(this.ruleForm.username,this.ruleForm.password).then(response=>{
              if (response.code == 200){
                this.$message.success("登录成功")
                // 登录成功 设置cookie
                this.setCookies(response.data.adminName,response.data.token,response.data.roleName)
                this.$router.push('main')
              }else {
                this.$message.error("用户名或者密码错误...")
              }
            })
          }else {
            // 获取图形验证码
            this.$message.error('验证码输入密码!')
          }
        } else {
          this.logining = false
          return false
        }
      })
    },
  }
}
</script>

<style scoped>
.login-wrap {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding-top: 10%;
  background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjEzNjFweCIgaGVpZ2h0PSI2MDlweCIgdmlld0JveD0iMCAwIDEzNjEgNjA5IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0Ni4yICg0NDQ5NikgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+R3JvdXAgMjE8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iQW50LURlc2lnbi1Qcm8tMy4wIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0i6LSm5oi35a+G56CB55m75b2VLeagoemqjCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTc5LjAwMDAwMCwgLTgyLjAwMDAwMCkiPgogICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMjEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDc3LjAwMDAwMCwgNzMuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMTgiIG9wYWNpdHk9IjAuOCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzQuOTAxNDE2LCA1NjkuNjk5MTU4KSByb3RhdGUoLTcuMDAwMDAwKSB0cmFuc2xhdGUoLTc0LjkwMTQxNiwgLTU2OS42OTkxNTgpIHRyYW5zbGF0ZSg0LjkwMTQxNiwgNTI1LjE5OTE1OCkiPgogICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLTExIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjI1IiBjeD0iNjMuNTc0ODc5MiIgY3k9IjMyLjQ2ODM2NyIgcng9IjIxLjc4MzA0NzkiIHJ5PSIyMS43NjYwMDgiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0zIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjU5OTk5OTk2NCIgY3g9IjUuOTg3NDY0NzkiIGN5PSIxMy44NjY4NjAxIiByeD0iNS4yMTczOTEzIiByeT0iNS4yMTMzMDk5NyI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zOC4xMzU0NTE0LDg4LjM1MjAyMTUgQzQzLjg5ODQyMjcsODguMzUyMDIxNSA0OC41NzAyMzQsODMuNjgzODY0NyA0OC41NzAyMzQsNzcuOTI1NDAxNSBDNDguNTcwMjM0LDcyLjE2NjkzODMgNDMuODk4NDIyNyw2Ny40OTg3ODE2IDM4LjEzNTQ1MTQsNjcuNDk4NzgxNiBDMzIuMzcyNDgwMSw2Ny40OTg3ODE2IDI3LjcwMDY2ODgsNzIuMTY2OTM4MyAyNy43MDA2Njg4LDc3LjkyNTQwMTUgQzI3LjcwMDY2ODgsODMuNjgzODY0NyAzMi4zNzI0ODAxLDg4LjM1MjAyMTUgMzguMTM1NDUxNCw4OC4zNTIwMjE1IFoiIGlkPSJPdmFsLTMtQ29weSIgZmlsbD0iI0NGREFFNiIgb3BhY2l0eT0iMC40NSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02NC4yNzc1NTgyLDMzLjE3MDQ5NjMgTDExOS4xODU4MzYsMTYuNTY1NDkxNSIgaWQ9IlBhdGgtMTIiIHN0cm9rZT0iI0NGREFFNiIgc3Ryb2tlLXdpZHRoPSIxLjczOTEzMDQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNDIuMTQzMTcwOCwyNi41MDAyNjgxIEw3LjcxMTkwMTYyLDE0LjU2NDA3MDIiIGlkPSJQYXRoLTE2IiBzdHJva2U9IiNFMEI0QjciIHN0cm9rZS13aWR0aD0iMC43MDI2Nzg5NjQiIG9wYWNpdHk9IjAuNyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtZGFzaGFycmF5PSIxLjQwNTM1Nzg5OTg3MzE1MywyLjEwODAzNjk1MzQ2OTk4MSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02My45MjYyMTg3LDMzLjUyMTU2MSBMNDMuNjcyMTMyNiw2OS4zMjUwOTUxIiBpZD0iUGF0aC0xNSIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjAuNzAyNjc4OTY0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1kYXNoYXJyYXk9IjEuNDA1MzU3ODk5ODczMTUzLDIuMTA4MDM2OTUzNDY5OTgxIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjYuODUwOTIyLCAxMy41NDM2NTQpIHJvdGF0ZSgzMC4wMDAwMDApIHRyYW5zbGF0ZSgtMTI2Ljg1MDkyMiwgLTEzLjU0MzY1NCkgdHJhbnNsYXRlKDExNy4yODU3MDUsIDQuMzgxODg5KSIgZmlsbD0iI0NGREFFNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLTQiIG9wYWNpdHk9IjAuNDUiIGN4PSI5LjEzNDgyNjUzIiBjeT0iOS4xMjc2ODA3NiIgcng9IjkuMTM0ODI2NTMiIHJ5PSI5LjEyNzY4MDc2Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xOC4yNjk2NTMxLDE4LjI1NTM2MTUgQzE4LjI2OTY1MzEsMTMuMjE0MjgyNiAxNC4xNzk4NTE5LDkuMTI3NjgwNzYgOS4xMzQ4MjY1Myw5LjEyNzY4MDc2IEM0LjA4OTgwMTE0LDkuMTI3NjgwNzYgMCwxMy4yMTQyODI2IDAsMTguMjU1MzYxNSBMMTguMjY5NjUzMSwxOC4yNTUzNjE1IFoiIGlkPSJPdmFsLTQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkuMTM0ODI3LCAxMy42OTE1MjEpIHNjYWxlKC0xLCAtMSkgdHJhbnNsYXRlKC05LjEzNDgyNywgLTEzLjY5MTUyMSkgIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTYuMjk0NzAwLCAxMjMuNzI1NjAwKSByb3RhdGUoLTUuMDAwMDAwKSB0cmFuc2xhdGUoLTIxNi4yOTQ3MDAsIC0xMjMuNzI1NjAwKSB0cmFuc2xhdGUoMTA2LjI5NDcwMCwgMzUuMjI1NjAwKSI+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMiIgZmlsbD0iI0NGREFFNiIgb3BhY2l0eT0iMC4yNSIgY3g9IjI5LjExNzY0NzEiIGN5PSIyOS4xNDAyNDM5IiByeD0iMjkuMTE3NjQ3MSIgcnk9IjI5LjE0MDI0MzkiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjMiIGN4PSIyOS4xMTc2NDcxIiBjeT0iMjkuMTQwMjQzOSIgcng9IjIxLjU2ODYyNzUiIHJ5PSIyMS41ODUzNjU5Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMi1Db3B5IiBzdHJva2U9IiNDRkRBRTYiIG9wYWNpdHk9IjAuNCIgY3g9IjE3OS4wMTk2MDgiIGN5PSIxMzguMTQ2MzQxIiByeD0iMjMuNzI1NDkwMiIgcnk9IjIzLjc0MzkwMjQiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yIiBmaWxsPSIjQkFDQUQ5IiBvcGFjaXR5PSIwLjUiIGN4PSIyOS4xMTc2NDcxIiBjeT0iMjkuMTQwMjQzOSIgcng9IjEwLjc4NDMxMzciIHJ5PSIxMC43OTI2ODI5Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI5LjExNzY0NzEsMzkuOTMyOTI2OCBMMjkuMTE3NjQ3MSwxOC4zNDc1NjEgQzIzLjE2MTYzNTEsMTguMzQ3NTYxIDE4LjMzMzMzMzMsMjMuMTc5NjA5NyAxOC4zMzMzMzMzLDI5LjE0MDI0MzkgQzE4LjMzMzMzMzMsMzUuMTAwODc4MSAyMy4xNjE2MzUxLDM5LjkzMjkyNjggMjkuMTE3NjQ3MSwzOS45MzI5MjY4IFoiIGlkPSJPdmFsLTIiIGZpbGw9IiNCQUNBRDkiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtOSIgb3BhY2l0eT0iMC40NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTcyLjAwMDAwMCwgMTMxLjAwMDAwMCkiIGZpbGw9IiNFNkExQTYiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yLUNvcHktMiIgY3g9IjcuMDE5NjA3ODQiIGN5PSI3LjE0NjM0MTQ2IiByeD0iNi40NzA1ODgyNCIgcnk9IjYuNDc1NjA5NzYiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTAuNTQ5MDE5NjA4LDEzLjYyMTk1MTIgQzQuMTIyNjI2ODEsMTMuNjIxOTUxMiA3LjAxOTYwNzg0LDEwLjcyMjcyMiA3LjAxOTYwNzg0LDcuMTQ2MzQxNDYgQzcuMDE5NjA3ODQsMy41Njk5NjA5NSA0LjEyMjYyNjgxLDAuNjcwNzMxNzA3IDAuNTQ5MDE5NjA4LDAuNjcwNzMxNzA3IEwwLjU0OTAxOTYwOCwxMy42MjE5NTEyIFoiIGlkPSJPdmFsLTItQ29weS0yIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzLjc4NDMxNCwgNy4xNDYzNDEpIHNjYWxlKC0xLCAxKSB0cmFuc2xhdGUoLTMuNzg0MzE0LCAtNy4xNDYzNDEpICI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0xMCIgZmlsbD0iI0NGREFFNiIgY3g9IjIxOC4zODIzNTMiIGN5PSIxMzguNjg1OTc2IiByeD0iMS42MTc2NDcwNiIgcnk9IjEuNjE4OTAyNDQiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0xMC1Db3B5LTIiIGZpbGw9IiNFMEI0QjciIG9wYWNpdHk9IjAuMzUiIGN4PSIxNzkuNTU4ODI0IiBjeT0iMTc1LjM4MTA5OCIgcng9IjEuNjE3NjQ3MDYiIHJ5PSIxLjYxODkwMjQ0Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMTAtQ29weSIgZmlsbD0iI0UwQjRCNyIgb3BhY2l0eT0iMC4zNSIgY3g9IjE4MC4wOTgwMzkiIGN5PSIxMDIuNTMwNDg4IiByeD0iMi4xNTY4NjI3NSIgcnk9IjIuMTU4NTM2NTkiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMjguOTk4NTM4MSwyOS45NjcxNTk4IEwxNzEuMTUxMDE4LDEzMi44NzYwMjQiIGlkPSJQYXRoLTExIiBzdHJva2U9IiNDRkRBRTYiIG9wYWNpdHk9IjAuOCI+PC9wYXRoPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTEwIiBvcGFjaXR5PSIwLjc5OTk5OTk1MiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA1NC4xMDA2MzUsIDM2LjY1OTMxNykgcm90YXRlKC0xMS4wMDAwMDApIHRyYW5zbGF0ZSgtMTA1NC4xMDA2MzUsIC0zNi42NTkzMTcpIHRyYW5zbGF0ZSgxMDI2LjYwMDYzNSwgNC42NTkzMTcpIj4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC03IiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiIGN4PSI0My44MTM1NTkzIiBjeT0iMzIiIHJ4PSIxMS4xODY0NDA3IiByeT0iMTEuMjk0MTE3NiI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzQuNTk2Nzc0LCAyMy4xMTExMTEpIiBmaWxsPSIjQkFDQUQ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtNyIgb3BhY2l0eT0iMC40NSIgY3g9IjkuMTg1MzQ3MTgiIGN5PSI4Ljg4ODg4ODg5IiByeD0iOC40NzQ1NzYyNyIgcnk9IjguNTU2MTQ5NzMiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkuMTg1MzQ3MTgsMTcuNDQ1MDM4NiBDMTMuODY1NzI2NCwxNy40NDUwMzg2IDE3LjY1OTkyMzUsMTMuNjE0MzE5OSAxNy42NTk5MjM1LDguODg4ODg4ODkgQzE3LjY1OTkyMzUsNC4xNjM0NTc4NyAxMy44NjU3MjY0LDAuMzMyNzM5MTU2IDkuMTg1MzQ3MTgsMC4zMzI3MzkxNTYgTDkuMTg1MzQ3MTgsMTcuNDQ1MDM4NiBaIiBpZD0iT3ZhbC03Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zNC42NTk3Mzg1LDI0LjgwOTY5NCBMNS43MTY2NjA4NCw0Ljc2ODc4OTQ1IiBpZD0iUGF0aC0yIiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbCIgc3Ryb2tlPSIjQ0ZEQUU2IiBzdHJva2Utd2lkdGg9IjAuOTQxMTc2NDcxIiBjeD0iMy4yNjI3MTE4NiIgY3k9IjMuMjk0MTE3NjUiIHJ4PSIzLjI2MjcxMTg2IiByeT0iMy4yOTQxMTc2NSI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGN0UxQUQiIGN4PSIyLjc5NjYxMDE3IiBjeT0iNjEuMTc2NDcwNiIgcng9IjIuNzk2NjEwMTciIHJ5PSIyLjgyMzUyOTQxIj48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTM0LjYzMTI0NDMsMzkuMjkyMjcxMiBMNS4wNjM2NjY2Myw1OS43ODUwODIiIGlkPSJQYXRoLTEwIiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xOSIgb3BhY2l0eT0iMC4zMyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI4Mi41MzcyMTksIDQ0Ni41MDI4NjcpIHJvdGF0ZSgtMTAuMDAwMDAwKSB0cmFuc2xhdGUoLTEyODIuNTM3MjE5LCAtNDQ2LjUwMjg2NykgdHJhbnNsYXRlKDExNDIuNTM3MjE5LCAzMjcuNTAyODY3KSI+CiAgICAgICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDEuMzMzNTM5LCAxMDQuNTAyNzQyKSByb3RhdGUoMjc1LjAwMDAwMCkgdHJhbnNsYXRlKC0xNDEuMzMzNTM5LCAtMTA0LjUwMjc0MikgdHJhbnNsYXRlKDEyOS4zMzM1MzksIDkyLjUwMjc0MikiIGZpbGw9IiNCQUNBRDkiPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLTQiIG9wYWNpdHk9IjAuNDUiIGN4PSIxMS42NjY2NjY3IiBjeT0iMTEuNjY2NjY2NyIgcj0iMTEuNjY2NjY2NyI+PC9jaXJjbGU+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMy4zMzMzMzMzLDIzLjMzMzMzMzMgQzIzLjMzMzMzMzMsMTYuODkwMDExMyAxOC4xMDk5ODg3LDExLjY2NjY2NjcgMTEuNjY2NjY2NywxMS42NjY2NjY3IEM1LjIyMzM0NDU5LDExLjY2NjY2NjcgMCwxNi44OTAwMTEzIDAsMjMuMzMzMzMzMyBMMjMuMzMzMzMzMywyMy4zMzMzMzMzIFoiIGlkPSJPdmFsLTQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExLjY2NjY2NywgMTcuNTAwMDAwKSBzY2FsZSgtMSwgLTEpIHRyYW5zbGF0ZSgtMTEuNjY2NjY3LCAtMTcuNTAwMDAwKSAiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC01LUNvcHktNiIgZmlsbD0iI0NGREFFNiIgY3g9IjIwMS44MzMzMzMiIGN5PSI4Ny41IiByPSI1LjgzMzMzMzMzIj48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQzLjUsODguODEyNjY4NSBMMTU1LjA3MDUwMSwxNy42MDM4NTQ0IiBpZD0iUGF0aC0xNyIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTcuNSwzNy4zMzMzMzMzIEwxMjcuNDY2MjUyLDk3LjY0NDk3MzUiIGlkPSJQYXRoLTE4IiBzdHJva2U9IiNCQUNBRDkiIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwb2x5bGluZSBpZD0iUGF0aC0xOSIgc3Ryb2tlPSIjQ0ZEQUU2IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciIHBvaW50cz0iMTQzLjkwMjU5NyAxMjAuMzAyMjgxIDE3NC45MzU0NTUgMjMxLjU3MTM0MiAzOC41IDE0Ny41MTA4NDcgMTI2LjM2Njk0MSAxMTAuODMzMzMzIj48L3BvbHlsaW5lPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTkuODMzMzMzLDk5Ljc0NTM4NDIgTDE5NS40MTY2NjcsODkuMjUiIGlkPSJQYXRoLTIwIiBzdHJva2U9IiNFMEI0QjciIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyIgb3BhY2l0eT0iMC42Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIwNS4zMzMzMzMsODIuMTM3MjEwNSBMMjM4LjcxOTQwNiwzNi4xNjY2NjY3IiBpZD0iUGF0aC0yNCIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMjY2LjcyMzQyNCwxMzIuMjMxOTg4IEwyMDcuMDgzMzMzLDkwLjQxNjY2NjciIGlkPSJQYXRoLTI1IiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNSIgZmlsbD0iI0MxRDFFMCIgY3g9IjE1Ni45MTY2NjciIGN5PSI4Ljc1IiByPSI4Ljc1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLTUtQ29weS0zIiBmaWxsPSIjQzFEMUUwIiBjeD0iMzkuMDgzMzMzMyIgY3k9IjE0OC43NSIgcj0iNS4yNSI+PC9jaXJjbGU+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC01LUNvcHktMiIgZmlsbC1vcGFjaXR5PSIwLjYiIGZpbGw9IiNEMURFRUQiIGN4PSI4Ljc1IiBjeT0iMzMuMjUiIHI9IjguNzUiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNS1Db3B5LTQiIGZpbGwtb3BhY2l0eT0iMC42IiBmaWxsPSIjRDFERUVEIiBjeD0iMjQzLjgzMzMzMyIgY3k9IjMwLjMzMzMzMzMiIHI9IjUuODMzMzMzMzMiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNS1Db3B5LTUiIGZpbGw9IiNFMEI0QjciIGN4PSIxNzUuNTgzMzMzIiBjeT0iMjMyLjc1IiByPSI1LjI1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+);
  /* background-color: #112346; */
  background-repeat: no-repeat;
  background-position: center right;
  background-size: 100%;
}
.login-container {
  border-radius: 10px;
  margin: 0px auto;
  width: 350px;
  padding: 30px 35px 15px 35px;
  background: #fff;
  border: 1px solid #eaeaea;
  text-align: left;
  box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
}
.title {
  margin: 0px auto 40px auto;
  text-align: center;
  color: #505458;
}
.remember {
  margin: 0px 0px 35px 0px;
}
.code-box {
  text-align: right;
}
.codeimg {
  height: 40px;
}
</style>

7.8.2.后台页面admin.vue

<template>

  <el-container class="index-con">
    <el-aside :class="showclass">
      <leftnav></leftnav>
    </el-aside>
    <el-container class="main-con">
      <el-header class="index-header">
        <navcon></navcon>
      </el-header>
      <el-main clss="index-main">
        <router-view>


        </router-view>
      </el-main>
    </el-container>
  </el-container>
</template>
<script>
// 导入组件
import navcon from '../components/navcon.vue'
import leftnav from '../components/leftnav.vue'
export default {
  name: 'admin',
  data() {
    return {
      showclass: 'asideshow',
      showtype: false
    }
  },
  // 注册组件
  components: {
    navcon,
    leftnav
  },
  methods: {},
  created() {
    // 监听
    this.$root.Bus.$on('toggle', value => {
      if (value) {
        this.showclass = 'asideshow'
      } else {
        setTimeout(() => {
          this.showclass = 'aside'
        }, 300)
      }
    })
  },
  beforeUpdate() {},
  // 挂载前状态(里面是操作)
  beforeMount() {
    // 弹出登录成功
    /*this.$message({
      message: '登录成功',
      type: 'success'
    })*/
  }
}
</script>
<style >
.index-con {
  height: 100%;
  width: 100%;
  box-sizing: border-box;
}

.aside {
  width: 64px !important;
  height: 100%;
  background-color: #334157;
  margin: 0px;
}
.asideshow {
  width: 240px !important;
  height: 100%;
  background-color: #334157;
  margin: 0px;
}
.index-header,
.index-main {
  padding: 0px;
  border-left: 2px solid #333;
}
</style>

7.8.2.后台顶部页面navcon.vue

/**
* 头部菜单
*/
<template>
  <el-menu class="el-menu-demo" mode="horizontal" background-color="#334157" text-color="#fff" active-text-color="#fff">
    <el-button class="buttonimg">
      <img class="showimg" :src="collapsed?imgsq:imgshow" @click="toggle(collapsed)">
    </el-button>
    <el-submenu index="2" class="submenu">
      <template slot="title">{{adminName}}</template>
      <el-menu-item>设置</el-menu-item>
      <el-menu-item>个人中心</el-menu-item>
      <el-menu-item @click="exit()" index="2-3">退出</el-menu-item>
    </el-submenu>
  </el-menu>
</template>
<script>

import cookie from "js-cookie";
export default {
  name: 'navcon',
  data() {
    return {
      collapsed: true,
      imgshow: require('../assets/img/show.png'),
      imgsq: require('../assets/img/sq.png'),
      adminName: ''
    }
  },
  // 创建完毕状态(里面是操作)
  created() {
    this.adminName = cookie.get('adminName')
  },
  methods: {
    // 退出登录
    exit() {
      this.$confirm('退出登录, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
          .then(() => {
            setTimeout(() => {
              this.$router.push({ path: '/' })
              cookie.set('adminToken', '', {domain: 'localhost'})
              cookie.set('adminName', '', {domain: 'localhost'})
              this.$message({
                type: 'success',
                message: '已退出登录!'
              })
            }, 1000)
          })
          .catch(() => {
            this.$message({
              type: 'info',
              message: '已取消'
            })
          })
    },
    // 切换显示
    toggle(showtype) {
      this.collapsed = !showtype
      this.$root.Bus.$emit('toggle', this.collapsed)
    }
  }
}
</script>
<style scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  border: none;
}
.submenu {
  float: right;
  position: absolute;
  right: 0;
  top: 0px;
}
.buttonimg {
  height: 60px;
  background-color: transparent;
  border: none;
}
.showimg {
  width: 26px;
  height: 26px;
  position: absolute;
  top: 17px;
  left: 17px;
}
.showimg:active {
  border: none;
}
</style>

7.8.2.后台左侧页面leftnav.vue

/**
* 左边菜单
*/
<template>
  <el-menu :collapse="collapsed" collapse-transition router unique-opened class="el-menu-vertical-demo" background-color="#334157" text-color="#fff" active-text-color="#ffd04b">
    <div class="logobox">
      <img class="logoimg" src="../assets/img/logo.png" alt="">
    </div>

    <router-link to="/main">
      <el-menu-item index="Index">
        <template slot="title">
          <i class="el-icon-s-home"></i>
          <span slot="title">首页</span>
        </template>
      </el-menu-item>
    </router-link>

    <el-submenu v-for="menu in menuBeanList" :key="menu.menuid" :index="menu.menuName">
      <template slot="title">
        <i class="iconfont" :class="menu.icon"></i>&nbsp;
        <span>{{menu.menuName}}</span>
      </template>
      <el-menu-item-group v-if="menu.menuList != null">
        <el-menu-item v-for="chmenu in menu.menuList" :index="'/'+chmenu.url" :key="chmenu.menuId">
          <i class="iconfont" :class="chmenu.icon"></i>&nbsp;
          <span>{{chmenu.menuName}}</span>
        </el-menu-item>
      </el-menu-item-group>
    </el-submenu>
  </el-menu>
</template>

<script>
import menuApi from "../api/fastdfs/menu";
export default {
  name: 'leftnav',
  data() {
    return {
      collapsed: false,
      allmenu: [],
      menuBeanList:[],
    }
  },
  // 创建完毕状态(里面是操作)
  created() {
    this.$root.Bus.$on('toggle', value => {
      this.collapsed = !value
    })
    menuApi.getMenuByRoleId().then(response=>{
      this.menuBeanList = response.data.menuBeanList
    })

  }
}
</script>
<style>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 240px;
  min-height: 400px;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
  border: none;
  text-align: left;
}
.el-menu-item-group__title {
  padding: 0px;
}
.el-menu-bg {
  background-color: #1f2d3d !important;
}
.el-menu {
  border: none;
}
.logobox {
  height: 40px;
  line-height: 40px;
  color: #9d9d9d;
  font-size: 20px;
  text-align: center;
  padding: 20px 0px;
}
.logoimg {
  height: 40px;
}
</style>

7.8.2.主界面main.vue

<template>
  <div>

    <div class="body" style="z-index:1">
      <div class="stars" ref="starsRef">
        <div class="star" v-for="(item, index) in starsCount" :key="index"></div>
        <div style="z-index: 10;font-size: 50px;color: #a0cfff">
          <span>欢迎登录后台管理系统=>{{ adminName }}</span>
        </div>
      </div>
      <div style="z-index: 10;font-size: 50px;color: #a0cfff;margin-top: 200px;margin-left: 200px">
        <span>欢迎登录后台管理系统=>{{ adminName }}</span>
      </div>
    </div>

  </div>

</template>

<script>
import cookie from "js-cookie";

export default {
  name: "main",
  data() {
    return {
      adminName: '',
      num: 5,
      starsCount: 1000, //星星数量
      distance: 900, //间距
    }
  },
  // 创建完毕状态(里面是操作)
  created() {
    this.adminName = cookie.get('adminName')
  },
  mounted() {
    console.log(this.$refs.starsRef.children);
    let starNodes = Array.from(this.$refs.starsRef.children);
    starNodes.forEach((item) => {
      let speed = 0.2 + Math.random() * 1;
      let thisDistance = this.distance + Math.random() * 300;
      item.style.transformOrigin = `0 0 ${thisDistance}px`;
      item.style.transform =
          `translate3d(0,0,-${thisDistance}px)
		        rotateY(${Math.random() * 360}deg)
		        rotateX(${Math.random() * -50}deg)
		        scale(${speed},${speed})`;
    });
  }

}
</script>

<style scoped>
.body {
  position: absolute;
  width: 86%;
  height: 90%;
  margin: 0;
  padding: 0;
  background: radial-gradient(200% 100% at bottom center,
  #f7f7b6,
  #e96f92,
  #1b2947);
  background: radial-gradient(200% 105% at top center,
  #1b2947 10%,
  #75517d 40%,
  #e96f92 65%,
  #f7f7b6);
  background-attachment: fixed;
  overflow: hidden;
}

@keyframes rotate {
  0% {
    transform: perspective(400px) rotateZ(20deg) rotateX(-40deg) rotateY(0);
  }

  100% {
    transform: perspective(400px) rotateZ(20deg) rotateX(-40deg) rotateY(-360deg);
  }
}

.stars {
  transform: perspective(500px);
  transform-style: preserve-3d;
  position: absolute;
  perspective-origin: 50% 100%;
  left: 45%;
  animation: rotate 90s infinite linear;
  bottom: 0;
}

.star {
  width: 2px;
  height: 2px;
  background: #f7f7b6;
  position: absolute;
  left: 0;
  top: 0;
  backface-visibility: hidden;
}
</style>

7.8.3.identify.vue

<template>
  <div class="s-canvas">
    <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
  </div>
</template>
<script>
export default{
  name: 'SIdentify',
  props: {
    identifyCode: {
      type: String,
      default: '1234'
    },
    fontSizeMin: {
      type: Number,
      default: 16
    },
    fontSizeMax: {
      type: Number,
      default: 40
    },
    backgroundColorMin: {
      type: Number,
      default: 180
    },
    backgroundColorMax: {
      type: Number,
      default: 240
    },
    colorMin: {
      type: Number,
      default: 50
    },
    colorMax: {
      type: Number,
      default: 160
    },
    lineColorMin: {
      type: Number,
      default: 40
    },
    lineColorMax: {
      type: Number,
      default: 180
    },
    dotColorMin: {
      type: Number,
      default: 0
    },
    dotColorMax: {
      type: Number,
      default: 255
    },
    contentWidth: {
      type: Number,
      default: 112
    },
    contentHeight: {
      type: Number,
      default: 38
    }
  },
  methods: {
    // 生成一个随机数
    randomNum (min, max) {
      return Math.floor(Math.random() * (max - min) + min)
    },
    // 生成一个随机的颜色
    randomColor (min, max) {
      let r = this.randomNum(min, max)
      let g = this.randomNum(min, max)
      let b = this.randomNum(min, max)
      return 'rgb(' + r + ',' + g + ',' + b + ')'
    },
    drawPic () {
      let canvas = document.getElementById('s-canvas')
      let ctx = canvas.getContext('2d')
      ctx.textBaseline = 'bottom'
      // 绘制背景
      ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
      ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
      // 绘制文字
      for (let i = 0; i < this.identifyCode.length; i++) {
        this.drawText(ctx, this.identifyCode[i], i)
      }
      this.drawLine(ctx)
      this.drawDot(ctx)
    },
    drawText (ctx, txt, i) {
      ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
      ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei'
      let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1))
      let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
      var deg = this.randomNum(-45, 45)
      // 修改坐标原点和旋转角度
      ctx.translate(x, y)
      ctx.rotate(deg * Math.PI / 180)
      ctx.fillText(txt, 0, 0)
      // 恢复坐标原点和旋转角度
      ctx.rotate(-deg * Math.PI / 180)
      ctx.translate(-x, -y)
    },
    drawLine (ctx) {
      // 绘制干扰线
      for (let i = 0; i < 8; i++) {
        ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
        ctx.beginPath()
        ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
        ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
        ctx.stroke()
      }
    },
    drawDot (ctx) {
      // 绘制干扰点
      for (let i = 0; i < 100; i++) {
        ctx.fillStyle = this.randomColor(0, 255)
        ctx.beginPath()
        ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
        ctx.fill()
      }
    }
  },
  watch: {
    identifyCode () {
      this.drawPic()
    }
  },
  mounted () {
    this.drawPic()
  }
}
</script>


7.8.4.request.js

import axios from 'axios'
import {Message} from 'element-ui'
import cookie from 'js-cookie'

// 创建axios实例
const service = axios.create({
  baseURL: 'http://localhost:9090', // api 的 base_url
  timeout: 1000000 // 请求超时时间
})

// http request 拦截器
// 请求拦截器
service.interceptors.request.use(
  config => {
    // 可以在这里添加请求头等信息
    // token 先不处理,后续使用时在完善
    if (cookie.get('adminToken')) {
      config.headers['token'] = cookie.get('adminToken')
    }
    return config;
  },
  err => {
    // 请求错误处理
    console.log(err);
    return Promise.reject(err);
  }
);

// http response 拦截器
// 响应拦截器
service.interceptors.response.use(
  response => {
    // 对响应数据做处理,例如只返回data部分
    //const data = response.data;
    //return data;
    if (response.code === 208) {
      this.$message.warning(response.message)
      router.push('login')
      return
    } else {
      if (response.data.code === 200) {
        return response.data
      } else {
        Message({
          message: response.data.message,
          type: 'error',
          duration: 5 * 1000
        })
        return Promise.reject(response.data)
      }
    }
  },
  error => {
    // 响应错误处理
    console.log('err' + error); // for debug
    //return Promise.reject(error);
    return Promise.reject(error.response);
  }
);

export default service;

7.8.5.store.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);
// 登录验证
export default new Vuex.Store({
    state: {
        user: false
    },
    mutations: {
        // 登录
        login(state, adminName) {
            state.adminName = adminName;
            localStorage.setItem("adminName", adminName);
        },
        // 退出
        logout(state, adminName) {
            state.adminName = "";
            localStorage.setItem("adminName", "");
        }
    }
})

7.8.6.util.js

/**
 * 时间戳
 * @param {*} timestamp  时间戳
 */
 const timestampToTime = (timestamp) => {
    let date = new Date(timestamp) //时间戳为10位需*1000,时间戳为13位的话不需乘1000
    let Y = date.getFullYear() + '-'
    let M =
        (date.getMonth() + 1 < 10 ?
            '0' + (date.getMonth() + 1) :
            date.getMonth() + 1) + '-'
    let D =
        (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '
    let h =
        (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'
    let m =
        (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) +
        ':'
    let s =
        date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
    return Y + M + D + h + m + s
};
/**
 * 存储localStorage
 */
const setStore = (name, content) => {
    if (!name) return;
    if (typeof content !== 'string') {
        content = JSON.stringify(content);
    }
    window.localStorage.setItem(name, content);
}

/**
 * 获取localStorage
 */
const getStore = name => {
    if (!name) return;
    return window.localStorage.getItem(name);
}

/**
 * 删除localStorage
 */
const removeStore = name => {
    if (!name) return;
    window.localStorage.removeItem(name);
}

/**
 * 设置cookie
 **/
function setCookie(name, value, day) {
    let date = new Date();
    date.setDate(date.getDate() + day);
    document.cookie = name + '=' + value + ';expires=' + date;
};

/**
 * 获取cookie
 **/
function getCookie(name) {
    let reg = RegExp(name + '=([^;]+)');
    let arr = document.cookie.match(reg);
    if (arr) {
        return arr[1];
    } else {
        return '';
    }
};

/**
 * 删除cookie
 **/
function delCookie(name) {
    setCookie(name, null, -1);
};

/**
 * 导出 
 **/
export default{
    timestampToTime,
    setStore,
    getStore,
    removeStore,
    setCookie,
    getCookie,
    delCookie
}

7.8.7.index.js

//在路由文件router/index.js中配置
// 该文件专门用于创建整个应用的路由器
// 创建应用程序的路由器
import Vue from 'vue'
import VueRouter from 'vue-router'
// 此时就可以在Vue实例中配置路由器了
Vue.use(VueRouter)

// 创建并暴露一个路由器
export default new VueRouter({
  routes:[
    {
      path:'/',
      name:'login',
      meta:{
        title:"登录页"
      },
      //redirect:'front/login',
      component:() => import('../view/login'),
      /*hidden: true,
      meta: {
          requireAuth: false //登录权限实现(登录拦截)
      }*/
    },
    {
      path:'/admin',
      name:'admin',
      meta:{
        title:"首页"
      },
      component:() => import('../view/admin'),
      children:[
        {
          path:'/main',
          name:'main',
          meta:{
            title:"首页"
          },
          component:() => import('../view/main')
        },

        //用户管理
        {
          path:'/user',
          name:'user',
          meta:{
            title:"用户管理:超级管理员:用户"
          },
          component:() => import('../view/user/user')
        },
      ],
    },

    {
      path:'/admin',
      name:'admin',
      meta:{
        title:"文件管理"
      },
      component:() => import('../view/admin'),
      children:[
        {
          path:'/main',
          name:'main',
          meta:{
            title:"首页"
          },
          component:() => import('../view/main')
        },

        //用户管理
        {
          path:'/user',
          name:'user',
          meta:{
            title:"用户管理:超级管理员:用户"
          },
          component:() => import('../view/user/user')
        },

        //文件管理
        {
          path:'/upload',
          name:'upload',
          meta:{
            title:"文件管理:上传文件"
          },
          component:() => import('../view/fastdfs/fastdfsuploadimg')
        },
        {
          path:'/file',
          name:'file',
          meta:{
            title:"文件管理:文件"
          },
          component:() => import('../view/fastdfs/fastdfsimg')
        },

      ],
    },

  ],
  mode:'history', //去除#号
  base: process.env.BASE_URL,
})


7.8.8.admin.js

import request from "@/utils/request";

const api_name = '/admins'

export default {
    //登录
    loginAdmin(name,password){
        return request({
            url: `${api_name}/${name}/${password}`,
            method:'post'
        })
    },
    //获取当前用户信息
    getAdminById(){
        return request({
            url: `${api_name}`,
            method:'get'
        })
    }

}

7.8.9.menu.js

import request from "@/utils/request";

const api_name = '/menus'

export default {
    //获取菜单列表
    getMenuByRoleId(){
        return request({
            url: `${api_name}`,
            method:'get'
        })
    },

}

7.8.10.main.js

// 引入App
import App from './App.vue'
//引入Vue
import Vue from 'vue'
//引入axios
import axios from 'axios'
//引入VueAxios
//安装VueAxios模块后,不需要在每个组件中单独导入axios,只需将axios请求改为this.axios
import VueAxios from 'vue-axios'
//引入VueRouter
import VueRouter from 'vue-router'
//完整引入
//引入ElementUI组件库
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
//新建一个router文件夹,在文件夹下新建一个index.js文件
//引入路由器
import router from './router/index'
// 引入状态管理
import store from './utils/store'

//引入qs模块发送post
import qs from 'qs'

Vue.prototype.$qs = qs

import SIdentify from "@/view/code/identify";

Vue.use(SIdentify)
// 过滤器
import * as custom from './utils/util'

// 二维码生成器
import VueQriously from 'vue-qriously'

Vue.use(VueQriously)
Vue.config.silent = true

// 设置全局变量
Vue.prototype.$baseImagePath = 'http://192.168.229.141'
//this.$baseImagePath直接引用
//把axios挂载到vue上
Vue.prototype.$axios = axios;
//使用Vue.use来注册安装插件
Vue.use(VueRouter)
Vue.use(router)
Vue.use(VueAxios, axios)
//使用ElementUI组件库
Vue.use(ElementUI)
//关闭Vue的生产提示
Vue.config.productionTip = false

Object.keys(custom).forEach(key => {
  Vue.filter(key, custom[key])
})

// 创建和挂载根实例
new Vue({
  store, //使用store vuex状态管理
  router, //将路由器注入到new Vue实例中,建立关联
  render: h => h(App), //将App组件放入容器中
  data: {
    // 空的实例放到根组件下,所有的子组件都能调用
    Bus: new Vue()
  }
}).$mount('#app');

7.9.效果展示

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

在这里插入图片描述

在这里插入图片描述

八、总结

  • 案例有些不足
  • 功能太简单
  • 功能复杂可以做一个类似网盘的文件管理系统
  • 仅仅学习使用某些功能
  • 暂不深入开发
  • 有兴趣的伙伴可以尝试一番
  • 类似于阿里云oss

endl 本文章下载链接

本文章下载链接:https://www.lanzv.com/i6Oqt1obmaji 密码:378n

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/389630.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

成员方法传参机制

一、成员方法传参机制 1、值传递&#xff1a;形参改变不影响实参 2、地址传递&#xff1a;形参改变影响实参

函数式编程的技巧

14.1.2 科里化 给出科里化的理论定义之前&#xff0c;让我们先来看一个例子。应用程序通常都会有国际化的需求将一套单位转换到另一套单位是经常碰到的问题。 单位转换通常都会涉及转换因子以及基线调整因子的问题。比如&#xff0c;将摄氏度转换到华氏度的公式是CtoF(x)x*9/…

【高级C语言】从汇编代码看volatile关键字的作用

本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客内容主要围绕&#xff1a; 5G/6G协议讲解 算力网络讲解&#xff08;云计算&#xff0c;边缘计…

apk反编译修改教程系列---简单去除apk登陆 修改vip与一些反编译基础常识【十二】

往期教程&#xff1a; 安卓玩机-----反编译apk 修改apk 去广告 去弹窗等操作中的一些常识apk反编译修改教程系列-----修改apk应用名称 任意修改名称 签名【一】 apk反编译修改教程系列-----任意修改apk版本号 版本名 防止自动更新【二】 apk反编译修改教程系列-----修改apk中…

政安晨:【示例演绎】【Python】【Numpy数据处理】快速入门(二)

环境准备 大家如果第一次看到&#xff0c;可以先从我这个演绎系列的第一篇文章开始&#xff0c;包括准备环境等等。 第一篇文章如下&#xff1a; 政安晨&#xff1a;【示例演绎】【Python】【Numpy数据处理】快速入门&#xff08;一&#xff09;https://blog.csdn.net/snowd…

怎么搭建自己的网站?

怎么搭建自己的网站 一.领取一个免费域名和SSL证书&#xff0c;和CDN 特点&#xff1a;支持Cloudflare CDN Cloudflare是全球知名的CDN提供商&#xff0c;如果你不想暴露你的源站&#xff0c;又想使用我们的二级域名&#xff0c;不需要前往Cloudflare添加域名&#xff0c;修…

【Linux取经路】文件系统之被打开的文件——文件描述符的引入

文章目录 一、明确基本共识二、C语言文件接口回顾2.1 文件的打开操作2.2 文件的读取写入操作2.3 三个标准输入输出流 三、文件有关的系统调用3.1 open3.1.1 比特位级别的标志位传递方式 3.2 write3.2.1 模拟实现 w 选项3.2.2 模拟实现 a 选项 3.3 read 四、访问文件的本质4.1 再…

STM32,嵌入式系统中的I2C协议

I2C协议——读写EEPROM 关注我&#xff0c;共同交流&#xff0c;一起成长 前言一、协议简介二、I2C特性及架构三、通信过程 前言 这是一种主要用于集成电路和集成电路&#xff08;IC&#xff09;通信&#xff0c;计算机中复杂的问题大多数就是用分层来进行解决&#xff0c;这个…

k8s-项目部署案例

一、容器交付流程 在k8s平台部署项目流程 在K8s部署Java网站项目 DockerFile 如果是http访问&#xff0c;需要在镜像仓库配置可信任IP 三、使用工作负载控制器部署镜像 建议至少配置两个标签 一个是声明项目类型的 一个是项目名称的 继续配置属性 资源配额 健康检查 五、使…

积分(二)——复化Simpson(C++)

前言 前言 simpson积分 simpson积分公式 ∫ a b f ( x ) d x ≈ b − a 6 [ f ( a ) f ( b ) 4 f ( a b 2 ) ] \int_{a}^{b}f(x)dx \approx \frac{b-a}{6}[f(a)f(b)4f(\frac{ab}{2})] ∫ab​f(x)dx≈6b−a​[f(a)f(b)4f(2ab​)] 与梯形积分类似&#xff0c;当区间[a,b]较…

Java 和 JavaScript 的奇妙协同:语法结构的对比与探索(下)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

卷积神经网络的基本结构

卷积神经网络的基本结构 与传统的全连接神经网络一样&#xff0c;卷积神经网络依然是一个层级网络&#xff0c;只不过层的功能和形式发生了变化。 典型的CNN结构包括&#xff1a; 数据输入层&#xff08;Input Layer&#xff09;卷积层&#xff08;Convolutional Layer&#x…

社区商铺开什么店最好?从商业计划书到实际运营

在社区商铺开店&#xff0c;选择适合的业态是成功的关键。作为一名开店 5 年的资深创业者&#xff0c;我想分享一些关于社区店的干货和见解。 这篇文章&#xff0c;我用我的项目给大家举例子&#xff01; 鲜奶吧作为一种新兴的业态&#xff0c;以提供新鲜、健康的乳制品为主&…

vue3 之 倒计时函数封装

理解需求 编写一个函数useCountDown可以把秒数格式化为倒计时的显示xx分钟xx秒 1️⃣formatTime为显示的倒计时时间 2️⃣start是倒计时启动函数&#xff0c;调用时可以设置初始值并且开始倒计时 实现思路分析 安装插件 dayjs npm i dayjs倒计时逻辑函数封装 // 封装倒计时…

C++类和对象-多态->多态的基本语法、多态的原理剖析、纯虚函数和抽象类、虚析构和纯虚析构

#include<iostream> using namespace std; //多态 //动物类 class Animal { public: //Speak函数就是虚函数 //函数前面加上virtual关键字&#xff0c;变成虚函数&#xff0c;那么编译器在编译的时候就不能确定函数调用了。 virtual void speak() { …

流量主小程序/公众号h5开源代码 源码分享

小程序开源代码合集 1、网课搜题小程序源码/小猿题库多接口微信小程序源码自带流量主 搭建教程 1、微信公众平台注册自己的小程序 2、下载微信开发者工具和小程序的源码 3、上传代码到自己的小程序 界面截图&#xff1a; 开源项目地址&#xff1a;https://ms3.ishenglu.com…

python 人脸检测器

import cv2# 加载人脸检测器 关键文件 haarcascade_frontalface_default.xml face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# 读取图像 分析图片 ren4.png image cv2.imread(ren4.png) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 进行人脸…

php基础学习之函数

基本概念 是一种语法结构&#xff0c;将实现某一个功能的代码块封装到一个结构中&#xff0c;从而实现代码的重复利用 php函数的定义语法 &#xff08;与C/Java很类似&#xff0c;区别在于没有数据类型&#xff0c;因为php是弱类型语言&#xff09; function 函数名(参数){ //…

波奇学Linux:文件系统打开文件

从文件系统来看打开文件 计算机系统和磁盘交互的大小是4kb 物理内存的4kb&#xff0c;磁盘的4kb文件叫做页帧 磁盘数据块的以4kb为单位。 减少IO的次数&#xff0c;减少访问外设的次数--硬件 基于局部性的原理&#xff0c;预加载机制--软件 操作系统管理内存 操作系统对…

leetcode hot 100最小花费爬楼梯

本题和之前的爬楼梯类似&#xff0c;但是需要考虑到花费的问题&#xff01;**注意&#xff0c;只有在爬的时候&#xff0c;才花费体力&#xff01;**那么&#xff0c;我们还是按照动态规划的五部曲来思考。 首先我们要确定dp数组的含义&#xff0c;那么就是我们爬到第i层所花费…