栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

毕业总结范文(毕业实践报告)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

毕业总结范文(毕业实践报告)

小白做毕设 前端主体搭建

直接用pure-design-vip生成模板 准备工作

springboot依赖

java版本的指定

maven的指定(配置好阿里云仓库)

npm的配置(前提安装好依赖)

springboot跨域配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlbasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {

    // 当前跨域请求最大有效时长。这里默认1天
    private static final long MAX_AGE = 24 * 60 * 60;

    @Bean
    public CorsFilter corsFilter() {
        UrlbasedCorsConfigurationSource source = new UrlbasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://localhost:8080"); // 1 设置访问源地址
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(MAX_AGE);
        source.registerCorsConfiguration("
    @Bean
    public Docket restApi() {
        return new Docket(documentationType.SWAGGER_2)
                .groupName("标准接口")
                .apiInfo(apiInfo("Spring Boot中使用Swagger2构建RESTful APIs", "1.0"))
                .useDefaultResponseMessages(true)
                .forCodeGeneration(false)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.qingge.springboot.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    
    private ApiInfo apiInfo(String title, String version) {
        return new ApiInfoBuilder()
                .title(title)
                .description("更多请关注: https://blog.csdn.net/xqnode")
                .termsOfServiceUrl("https://blog.csdn.net/xqnode")
                .contact(new Contact("xqnode", "https://blog.csdn.net/xqnode", "xiaqingweb@163.com"))
                .version(version)
                .build();
    }


}

pom.xml

 
 
     io.springfox
     springfox-boot-starter
     3.0.0
 
前后端数据交互 vue安装axios
npm i axios -S
request.js封装
import axios from 'axios'

const request = axios.create({
    baseURL: '/api',
    timeout: 5000
})

// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';

    // config.headers['token'] = user.token;  // 设置请求头
    return config
}, error => {
    return Promise.reject(error)
});

// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
    response => {
        let res = response.data;
        // 如果是返回的文件
        if (response.config.responseType === 'blob') {
            return res
        }
        // 兼容服务端返回的字符串数据
        if (typeof res === 'string') {
            res = res ? JSON.parse(res) : res
        }
        return res;
    },
    error => {
        console.log('err' + error) // for debug
        return Promise.reject(error)
    }
)


export default request


数据请求测试

代码生成

mp依赖


    com.baomidou
    mybatis-plus-generator
    3.5.1


    org.apache.velocity
    velocity
    1.7

代码生成 CodeGenerator

package com.qingge.springboot.utils;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;

import java.util.Collections;


public class CodeGenerator {

    public static void main(String[] args) {
        generate();
    }

    private static void generate() {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/qing?serverTimezone=GMT%2b8", "root", "123456")
                .globalConfig(builder -> {
                    builder.author("青哥哥") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("D:\代码\小白做毕设2022\springboot\src\main\java\"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.qingge.springboot") // 设置父包名
                            .moduleName(null) // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D:\代码\小白做毕设2022\springboot\src\main\resources\mapper\")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.entityBuilder().enableLombok();
//                    builder.mapperBuilder().enableMapperAnnotation().build();
                    builder.controllerBuilder().enableHyphenStyle()  // 开启驼峰转连字符
                            .enableRestStyle();  // 开启生成@RestController 控制器
                    builder.addInclude("sys_user") // 设置需要生成的表名
                            .addTablePrefix("t_", "sys_"); // 设置过滤表前缀
                })
//                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();

    }
}

模板引擎,自定义配置(controller.java.vm)

package ${package.Controller};


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};

#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end


#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end

#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end

    @Resource
    private ${table.serviceName} ${table.entityPath}Service;

    // 新增或者更新
    @PostMapping
    public boolean save(@RequestBody ${entity} ${table.entityPath}) {
        return ${table.entityPath}Service.saveOrUpdate(${table.entityPath});
    }

    @DeleteMapping("/{id}")
    public Boolean delete(@PathVariable Integer id) {
        return ${table.entityPath}Service.removeById(id);
    }

    @PostMapping("/del/batch")
    public boolean deleteBatch(@RequestBody List ids) {
        return ${table.entityPath}Service.removeByIds(ids);
    }

    @GetMapping
    public List<${entity}> findAll() {
        return ${table.entityPath}Service.list();
    }

    @GetMapping("/{id}")
    public ${entity} findOne(@PathVariable Integer id) {
        return ${table.entityPath}Service.getById(id);
    }

    @GetMapping("/page")
    public Page<${entity}> findPage(@RequestParam Integer pageNum,
                                @RequestParam Integer pageSize) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        return ${table.entityPath}Service.page(new Page<>(pageNum, pageSize), queryWrapper);
    }

}

#end
    
前端讲解

前端不是后端程序员重点,可copy我们的练手项目使用

导入导出接口实现

利用hutool工具实现(功能强大)https://www.hutool.cn/docs/#/poi/Excel%E5%B7%A5%E5%85%B7-ExcelUtil

pom依赖


    cn.hutool
    hutool-all
    5.7.20

 
     org.apache.poi
     poi-ooxml
     4.1.2
 

导出接口 起别名可在实体类用@Alias实现

    @GetMapping("/export")
    public void export(HttpServletResponse response) throws Exception {
        // 从数据库查询出所有的数据
        List list = userService.list();
        // 通过工具类创建writer 写出到磁盘路径
//        ExcelWriter writer = ExcelUtil.getWriter(filesUploadPath + "/用户信息.xlsx");
        // 在内存操作,写出到浏览器
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //自定义标题别名
        writer.addHeaderAlias("username", "用户名");
        writer.addHeaderAlias("password", "密码");
        writer.addHeaderAlias("nickname", "昵称");
        writer.addHeaderAlias("email", "邮箱");
        writer.addHeaderAlias("phone", "电话");
        writer.addHeaderAlias("address", "地址");
        writer.addHeaderAlias("createTime", "创建时间");
        writer.addHeaderAlias("avatarUrl", "头像");

        // 一次性写出list内的对象到excel,使用默认样式,强制输出标题
        writer.write(list, true);

        // 设置浏览器响应的格式
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode("用户信息", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();

    }

导入接口

    @PostMapping("/import")
    public Boolean imp(MultipartFile file) throws Exception {
        InputStream inputStream = file.getInputStream();
        ExcelReader reader = ExcelUtil.getReader(inputStream);
        // 方式1:(推荐) 通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
//        List list = reader.readAll(User.class);

        // 方式2:忽略表头的中文,直接读取表的内容
        List> list = reader.read(1);
        List users = CollUtil.newArrayList();
        for (List row : list) {
            User user = new User();
            user.setUsername(row.get(0).toString());
            user.setPassword(row.get(1).toString());
            user.setNickname(row.get(2).toString());
            user.setEmail(row.get(3).toString());
            user.setPhone(row.get(4).toString());
            user.setAddress(row.get(5).toString());
            user.setAvatarUrl(row.get(6).toString());
            users.add(user);
        }

        userService.saveBatch(users);
        return true;
    }
  

vue导入


  导入 



handleExcelimportSuccess() {
    this.$message.success("导入成功")
    this.load()
}

vue导出

导出 

exp() {
	window.open("http://localhost:9090/user/export")
}
各接口的实现

不再一一列出,看源码接口实现

统一异常处理,包装类

Result.java

package com.qingge.springboot.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {

    private String code;
    private String msg;
    private Object data;

    public static Result success() {
        return new Result(Constants.CODE_200, "", null);
    }

    public static Result success(Object data) {
        return new Result(Constants.CODE_200, "", data);
    }

    public static Result error(String code, String msg) {
        return new Result(code, msg, null);
    }

    public static Result error() {
        return new Result(Constants.CODE_500, "系统错误", null);
    }

}

全局异常处理类(GlobalExceptionHandler.java)

package com.qingge.springboot.exception;

import com.qingge.springboot.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    
    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    public Result handle(ServiceException se){
        return Result.error(se.getCode(), se.getMessage());
    }

}

自定义异常类(ServiceException.java)

package com.qingge.springboot.exception;

import lombok.Getter;


@Getter
public class ServiceException extends RuntimeException {
    private String code;

    public ServiceException(String code, String msg) {
        super(msg);
        this.code = code;
    }

}

JWT JWT依赖
 
 
     com.auth0
     java-jwt
     3.10.3
 
TokenUtils.java
package com.qingge.springboot.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.qingge.springboot.entity.User;
import com.qingge.springboot.service.IUserService;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Component
public class TokenUtils {

    private static IUserService staticUserService;

    @Resource
    private IUserService userService;

    @PostConstruct
    public void setUserService() {
        staticUserService = userService;
    }

    
    public static String genToken(String userId, String sign) {
        return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期
                .sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
    }

    
    public static User getCurrentUser() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = request.getHeader("token");
            if (StrUtil.isNotBlank(token)) {
                String userId = JWT.decode(token).getAudience().get(0);
                return staticUserService.getById(Integer.valueOf(userId));
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }
}

token示例
{"username":"admin","password":"admin","nickname":"管理员11111","avatarUrl":"https://img-blog.csdnimg.cn/c6d0ece75d3f4833bd820b8aa2eb952b.png","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwiZXhwIjoxNjQ0MzgxMDI4fQ.87nwS8ENDOu6RY-4PTLBBzXfDv6-5TiQLQhBXrYGb700"}
JwtInterceptor.java
package com.qingge.springboot.config.interceptor;

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.qingge.springboot.common.Constants;
import com.qingge.springboot.entity.User;
import com.qingge.springboot.exception.ServiceException;
import com.qingge.springboot.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token");
        // 如果不是映射到方法直接通过
        if(!(handler instanceof HandlerMethod)){
            return true;
        }
        // 执行认证
        if (StrUtil.isBlank(token)) {
            throw new ServiceException(Constants.CODE_401, "无token,请重新登录");
        }
        // 获取 token 中的 user id
        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException j) {
            throw new ServiceException(Constants.CODE_401, "token验证失败,请重新登录");
        }
        // 根据token中的userid查询数据库
        User user = userService.getById(userId);
        if (user == null) {
            throw new ServiceException(Constants.CODE_401, "用户不存在,请重新登录");
        }
        // 用户密码加签验证 token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            jwtVerifier.verify(token); // 验证token
        } catch (JWTVerificationException e) {
            throw new ServiceException(Constants.CODE_401, "token验证失败,请重新登录");
        }
        return true;
    }
}

InterceptorConfig.java
package com.qingge.springboot.config;

import com.qingge.springboot.config.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("export", "import");
    }

    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }

}

login方法
@Override
public UserDTO login(UserDTO userDTO) {
    User one = getUserInfo(userDTO);
    if (one != null) {
    BeanUtil.copyProperties(one, userDTO, true);
    // 设置token
    String token = TokenUtils.genToken(one.getId().toString(), one.getPassword());
    userDTO.setToken(token);
    return userDTO;
    } else {
    throw new ServiceException(Constants.CODE_600, "用户名或密码错误");
    }
}
request.js
  let user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : null
    if (user) {
        config.headers['token'] = user.token;  // 设置请求头
    }

 // 当权限验证不通过的时候给出提示
        if (res.code === '401') {
            ElementUI.Message({
                message: res.msg,
                type: 'error'
            });
        }
文件上传 接口实现看源码 application.yml
files:
  upload:
    path: D:大二下学期小白做毕设files
配置拦截器,不需要拦截 前端实现 图标实现 vue安装echarts
npm i echarts -S

Echarts官网:https://echarts.apache.org/zh/index.html

Echarts使用手册:https://echarts.apache.org/handbook/zh/get-started/

引入echarts:import * as echarts from 'echarts'

前端实现看源码 权限菜单讲解 角色管理页面

设计sys_role表用代码生成器生成三层架构复制拷贝一份Role.vue (记得增加路由,修改响应字段、功能,使接口一致)


菜单管理页面

设计sys_menu表用代码生成器生成三层架构复制拷贝一份Menu.vue (记得增加路由,修改响应字段、功能,使接口一致)


Unknown column ‘icon’ in ‘field list’ 报错原因是数据库字段不一致,重修修改数据库字段

分配菜单功能

Role.vue 添加分配菜单按钮

在菜单分配表格使用树形控件

修改Menu.vue,使用树形表格(需要用到children,要修改对应entity对象,添加children属性,同时加上@TableFild(exist=false))

 	@TableField(exist = false)
      private List children;
      
      @TableField("pid")
      private Integer pid;

修改接口,填充children,所以要修改数据库字段,利用id-pid的自关联的关系

@GetMapping
    public Result findAll(@RequestParam(defaultValue = "") String name) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        if(StrUtil.isNotBlank(name)){
            queryWrapper.like("name",name);
        }
        //查询所有菜单
        List list = menuService.list(queryWrapper);
        //查询父菜单
        List parentMenu = list.stream().filter(menu -> (menu.getPid() == null)).collect(Collectors.toList());
        for (Menu menu : parentMenu) {
            //为每个父菜单封装二级菜单
            List childrenList = list.stream().filter(m -> menu.getId().equals(m.getPid())).collect(Collectors.toList());
            menu.setChildren(childrenList);
        }
        return Result.success(parentMenu);
    }

修改前端页面去掉分页,添加新增子菜单功能

新增子菜单

handleAdd(pid) {
      this.dialogFormVisible = true;
      this.form = {};
      this.form.pid=pid;
    },

save() {
      // console.log(this.form);
      request.post("menu", this.form).then(res => {
        if (res.code=='200') {
          this.$message.success("保存成功");
          this.dialogFormVisible = false;
          this.load();
        } else {
          this.$message.error("保存失败");
        }
      })
    },

修改Role.vue的分配菜单,调用接口数据,记得加上prop属性才能显示数据

完善图标功能显示,新建Dict字典表(name,value,type) 新建entity,mapper

新建接口(查询dict表中所有的icon类型)

@GetMapping("/icons")
    public Result icons() {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type", Constants.DICT_TYPE_ICON);
        List dictList = this.dictMapper.selectList(queryWrapper);
        return Result.success(dictList);
    }

Menu.vue 前端下拉框的实现,表格中图标的显示


                  
                    
                      {{item.name}}
                    
                  
              

 
              
            

Role.vue 分配菜单中图标的添加,以及菜单的默认展开功能


            
               {{ data.name }}
            
            

handleMenu(){
      this.menuVisible=true;
      this.request.get("menu").then(res=>{
        this.treeData=res.data;
        this.expanded=res.data.map(v=>v.id);
      })
    }

新建角色菜单关系表sys_role_menu(role_id,menu_id)新建实体类,mapper

写两个接口,先删后增 关于role_menu实体类的增删与查

@GetMapping("roleMenu/{roleId}")
    public List findMenuIdByRoleId(@PathVariable("roleId") Integer roleId) {
        return roleService.findMenuIdByRoleId(roleId);
    }
@PostMapping("roleMenu/{roleId}")
    public Result addMenu(@PathVariable("roleId")Integer roleId,@RequestBody List menuIds){
         roleService.addMenu(roleId,menuIds);
         return Result.success();
    }

@Select("select menu_id from sys_role_menu where role_id=#{roleId}")
    List findMenuIdByRoleId(@Param("roleId") Integer roleId);

@Override
    public void addMenu(Integer roleId, List menuIds) {
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RoleMenu::getRoleId,roleId);
        roleMenuMapper.delete(queryWrapper);
        for (Integer menuId : menuIds) {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setMenuId(menuId);
            roleMenu.setRoleId(roleId);
            roleMenuMapper.insert(roleMenu);
        }
    }

前端调用,修改树形控件传参数(role_id,Listmenu_id)实现新增关系和菜单默认选择功能


            
               {{ data.name }}
            
            

 handleMenu(id){
      this.menuVisible=true;
      this.request.get("menu").then(res=>{
        this.treeData=res.data;
        this.expanded=res.data.map(v=>v.id);
      })
      this.roleId=id;
      this.request.get("role/roleMenu/"+this.roleId).then(res=>{
        this.checked=res;
      })
    },

saveRoleMenu(){
      this.request.post("role/roleMenu/"+this.roleId,this.$refs.tree.getCheckedKeys()).then(res=>{
        if(res.code=='200'){
          this.$message.success("绑定成功");
          this.menuVisible=false;
        }
      })
    }

修改数据库user表(role) role表(flag)对应实体类也要进行修改

前端User.vue界面表格与表单填加角色信息显示和添加功能,Role.vue添加标识编辑与显示


            


                
                  
                  
                
              

this.request.get("role").then(res=>{
      this.options=res;
    })

修改UserDto对象(role,List

)完善登录接口

 public Result login(UserDto userDto) {
        User user = getUserInfo(userDto);
        if(user==null){
            throw new ServiceException(Constants.CODE_600,"用户名或密码错误");
        }
        BeanUtils.copyProperties(user,userDto);
        //menus属性填充
        String roleFlag = userDto.getRole(); // ROLE_ADMIN
        List roleMenu = getRoleMenu(roleFlag);
        userDto.setMenus(roleMenu);

        String token = TokenUtils.createToken(user.getId().toString(), user.getPassword());
        userDto.setToken(token);
        return Result.success(userDto);
    }

private List getRoleMenu(String roleFlag){
        QueryWrapper queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("flag",roleFlag);
        Integer roleId = roleMapper.selectOne(queryWrapper).getId();
        List menuIds = roleMenuMapper.findMenuIdByRoleId(roleId);
        //调用menuService查询所有菜单
        List allMenu = menuService.findAllMenu("");
        // new一个最后筛选完成之后的list
        List result=new ArrayList<>();
        // 筛选当前用户角色的菜单
        for (Menu menu : allMenu) {
            if(menuIds.contains(menu.getId())){
                result.add(menu);
            }
            List children = menu.getChildren();
            // removeIf()  移除 children 里面不在 menuIds集合中的 元素
            children.removeIf(child->!menuIds.contains(child.getId()));
        }
        return result;
    }

修改Aside.vue实现动态菜单

Aside.vue

        
          
            
            {{ item.name }}
          
        
        
          
            
            
              
                
                {{ subItem.name }}
              
            
          
        
  

data(){
    return {
      menus:localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")):[]
    }
  },

Login.vue

login(){
      this.$refs['userForm'].validate((valid)=>{
        if(valid){//表单校验合法
          this.request.post("user/login",this.user).then(res=>{
            if(!res.data){
              this.$message.error(res.msg);
            }else{
              this.$router.push("/");
              localStorage.setItem("user",JSON.stringify(res.data));
              localStorage.setItem("menus",JSON.stringify(res.data.menus));
              this.$message.success("登录成功");
            }
          })
        }else{
          return false;
        }
      })
    }

修改sys_menu(page_path) 修改实体类 前端页面Menu.vue表格与表单显示页面路径

修改router/index.js 实现动态路由,在login.vue动态设置路由 我实现不了

// 注意:刷新页面会导致页面路由重置
export const setRoutes = () => {
  console.log(router.getRoutes())
  const storeMenus = localStorage.getItem("menus");
  if (storeMenus) {

    // 获取当前的路由对象名称数组
    const currentRouteNames = router.getRoutes().map(v => v.name);
    if (!currentRouteNames.includes('Manage')) {
      // 拼装动态路由
      const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [
          { path: 'person', name: '个人信息', component: () => import('../views/Person.vue')},
          // { path: 'password', name: '修改密码', component: () => import('../views/Password.vue')}
        ] }
      const menus = JSON.parse(storeMenus)
      menus.forEach(item => {
        if (item.url) {  // 当且仅当path不为空的时候才去设置路由
          let itemMenu = { path: item.url.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue')}
          manageRoute.children.push(itemMenu)
        } else if(item.children.length) {
          item.children.forEach(item => {
            if (item.url) {
              let itemMenu = { path: item.url.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue')}
              manageRoute.children.push(itemMenu)
            }
          })
        }
      })
      // 动态添加到现在的路由对象中去
      router.addRoute(manageRoute)
    }

  }
}

setRoutes();

实现404页面






.bgImg {
  background: url("../assets/404.jpg") no-repeat;
  background-size: 100% 100vh;
}


const routes = [
    {
    path: ' '../views/404.vue')
  }
]

修改添加二级菜单没有一级菜单的bug 在添加的实现中进行二级判断,额外把父菜单也插入进去

public void setRoleMenu(Integer roleId, List menuIds) {
//        QueryWrapper queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("role_id", roleId);
//        roleMenuMapper.delete(queryWrapper);

        // 先删除当前角色id所有的绑定关系
        roleMenuMapper.deleteByRoleId(roleId);

        // 再把前端传过来的菜单id数组绑定到当前的这个角色id上去
        List menuIdsCopy = CollUtil.newArrayList(menuIds);
        for (Integer menuId : menuIds) {
            Menu menu = menuService.getById(menuId);
            if (menu.getPid() != null && !menuIdsCopy.contains(menu.getPid())) { // 二级菜单 并且传过来的menuId数组里面没有它的父级id
                // 那么我们就得补上这个父级id
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(roleId);
                roleMenu.setMenuId(menu.getPid());
                roleMenuMapper.insert(roleMenu);
                menuIdsCopy.add(menu.getPid());
            }
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(roleId);
            roleMenu.setMenuId(menuId);
            roleMenuMapper.insert(roleMenu);
        }
    }

在store/index.js 实现注销方法在管理员分配菜单后触发,重新登录

const store = new Vuex.Store({
    state: {
        currentPathName: ''
    },
    mutations: {
        setPath (state) {
            state.currentPathName = localStorage.getItem("currentPathName")
        },
        logout(){
            localStorage.removeItem("user");
            localStorage.removeItem("menus");
            router.push("/login")
        }
    }
})
 handleMenu(role){
      this.menuVisible=true;
      this.request.get("menu").then(res=>{
        this.treeData=res.data;
        this.expanded=res.data.map(v=>v.id);
      })
      this.roleId=role.id;
      this.roleFlag=role.flag;
      this.request.get("role/roleMenu/"+this.roleId).then(res=>{
        this.checked=res;
      })
    },

saveRoleMenu(){

      this.request.post("role/roleMenu/"+this.roleId,this.$refs.tree.getCheckedKeys()).then(res=>{
        if(res.code=='200'){
          this.$message.success("绑定成功");
          this.menuVisible=false;
          if(this.roleFlag==="SYS_ADMIN"){
            this.$store.commit("logout");
          }
        }
      })
      // console.log( this.$refs.tree.getCheckedNodes().map(v=>v.id));
    }

修改bug:一级菜单选中后全选中二级菜单

handleMenu(role){
      this.menuVisible=true;
      this.request.get("menu").then(res=>{
        this.treeData=res.data;
        this.expanded=res.data.map(v=>v.id);
      })
      this.roleId=role.id;
      this.roleFlag=role.flag;
      this.request.get("role/roleMenu/"+this.roleId).then(res=>{
        this.checked=res;
        this.ids.forEach(id=>{
          if(!this.checked.includes(id)){
            this.$nextTick(() => {
              this.$refs.tree.setChecked(id, false)
            })
          }
        })
      })
    },
        
        this.request.get("menu/ids").then(res=>{
      this.ids=res.data;
    })

//要额外写个查询所有菜单id的接口

public Result getAllMenu() {
        List list = list();
//        List ids=new ArrayList<>();
//        for (Menu menu : list) {
//            Integer id=menu.getId();
//            ids.add(id);
//        }
        List ids = list.stream().map(menu -> menu.getId()).collect(Collectors.toList());
        return Result.success(ids);

        
    }

修改bug:一登录直接404 配置路由守卫

{
    path: '/404',
    name: '404',
    component: () => import('../views/404.vue')
  },


router.beforeEach((to, from, next) => {
  localStorage.setItem("currentPathName", to.name)  // 设置当前的路由名称
  store.commit("setPath")

  // 未找到路由的情况
  if (!to.matched.length) {
    const storeMenus = localStorage.getItem("menus")
    if (storeMenus) {
      next("/404")
    } else {
      // 跳回登录页面
      next("/login")
    }
  }
  // 其他的情况都放行
  next()

})

修改bug: 个人信息、修改密码的404 设置固定路由

let managerRoute = {path: '/', name: 'Manager',component: () => import('../views/Manage'), redirect: "/home",children: [
         { path: 'person', name: '个人信息', component: () => import('../views/Person.vue')},
         // { path: 'password', name: '修改密码', component: () => import('../views/Password.vue')},

修改bug:对于未来元素(还没出现的元素)进行方法调用 1.调整代码顺序,先渲染未来元素再进行对未来元素的方法调用,2. 使用$nextTick处理未来元素,等到这个元素渲染完成之后,再去使用它

selectMenu(role) {
      this.roleId = role.id
      this.roleFlag = role.flag

      // 请求菜单数据
      this.request.get("/menu").then(res => {
        this.menuData = res.data

        // 把后台返回的菜单数据处理成 id数组
        this.expends = this.menuData.map(v => v.id)
      })

      this.request.get("/role/roleMenu/" + this.roleId).then(res => {
        this.checks = res.data
       // this.menuDialogVis = true
        this.ids.forEach(id => {
          if (!this.checks.includes(id)) {
            // 可能会报错:Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'setChecked')
            this.$nextTick(() => {
              this.$refs.tree.setChecked(id, false)
            })
          }
        })
        this.menuDialogVis = true
      })
    },
  }

修改bug:给管理员新添页面,配菜菜单分配跳到登录后,进新页面报404 提供重置路由想法,让其能再去添加路由

// 提供一个重置路由的方法
export const resetRouter = () => {
  router.matcher = new VueRouter({
    mode: 'history',
    base: process.env.base_URL,
    routes
  })
}
//登录的时候调用调用这个接口
服务器部署

购买阿里云服务器

重置密码

安装X-shell7远程连接工具,新建会话连接

查看服务器配置(free -h,df -h,top)

开启端口号(安全组)9090、80、3306

服务器中安装软件,安装xftp传输(/home/package)jdk,nginx

利用部署笔记,有道云笔记 安装docker

利用docker安装mysql,在本机Navicat新建连接服务器的数据库

项目修改配置,打包到服务器 要改数据库连接url的ip(用外网ip)

授权mysql,刷新授权

启动服务器中的jar包,测试访问 http://39.108.128.26:9090/swagger-ui/index.html#/

令jar包后台部署

nohup java -jar springboot-0.0.1-SNAPSHOT.jar &

ps -ef | grep java 后台查询java进程

kill -9 [进程号] 关闭后台进程

安装 anywhere前端静态资源服务器插件;启动dist目录中的前端项目

npm install anywhere -g

安装nginx(根据笔记安装) 根据服务器ip输入测试nginx安装是否成功(80端口要打开)

配置nginx vue /dist 重启nginx

location / {
            root /home/server/dist;
            index index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

./nginx -s reload

前端上传和导出localhost修改成服务器外网ip

:action="'http://'+serverIp+':9090/'"
  

后台配置同样配置serverIp 文件上传,导出,接口的使用都需要公网iP

重新打包,重新后台启动

先kill,在重新启动

tailf nohup.out(查看后台日志)

npm run build (前端打dist包)

cd /usr/local/nginx/sbin
./niginx -s reload 重启nginx

申请备案获取自己域名,若是本地访问可以修改C:windowssystem32driversetchosts 后面加ip地址,加网址

常用命令

chmod 777 文件名 --- 修改文件权限

ps -ef | grep java 后台查询
实现一对一,一对多,多对多的关联查询

添加课程表course(id,name,score,times-varchar,state-tinyint -1,teacher_id)

直接代码生成

角色修改,生成Course.vue前端页面(可以使用生成器)

写课表对应老师的功能 (多表查询)

@GetMapping("/page")
    public Result findPage(@RequestParam(defaultValue = "") String name,
                           @RequestParam Integer pageNum,
                           @RequestParam Integer pageSize) {
//        QueryWrapper queryWrapper = new QueryWrapper<>();
//        queryWrapper.orderByDesc("id");
//        if (!"".equals(name)) {
//            queryWrapper.like("name", name);
//        }
        return Result.success(courseService.findPage(new Page<>(pageNum, pageSize), name));
    }
    
        public Page findPage(Page coursePage,String name) {
//        Page page = page(coursePage, queryWrapper);
//        List courseList = page.getRecords();
//        for (Course course : courseList) {
//            course.setTeacher(userService.getById(course.getTeacherId()).getNickname());
//        }
//        return page;
        return courseMapper.findPage(coursePage,name);
    }
    
    Page findPage(Page coursePage, @RequestParam("name") String name);

        SELECT course.*
        from sys_course course
                 LEFT JOIN student_course sc
                           on sc.course_id=course.id
                 LEFT JOIN sys_user user
                           on sc.student_id=user.id
        WHERe sc.student_id=#{studentId}
    
我的课程 


      
        
        
        
        
      
    

handleMyCourse(){
      let studentId=this.user.id;
      this.request.get("/course/studentCourse/"+studentId).then(res=>{
        this.myCourseVis=true;
        this.courseData=res.data;
      })
    }

前台主页实现

前台布局,页面的实现

跳转路由问题

特殊接口不用权限(自定义注解实现)

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface AuthAccess {
}


if(!(handler instanceof HandlerMethod)){
            return true;
        }else{
            AuthAccess authAccess = ((HandlerMethod) handler).getMethodAnnotation(AuthAccess.class);
            if(authAccess!=null){
                return  true;
            }
        }
springboot集成redis做缓存

添加依赖(springboot-cache)

 
            org.springframework.boot
            spring-boot-starter-cache
        

添加注解,实现spring cache的集成使用(可根据博客食用)

 @PostMapping("/update")
    @CachePut(value = "files", key = "'frontAll'")
    public Result update(@RequestBody Files files) {
        fileMapper.updateById(files);
        return Result.success(fileMapper.selectList(null));

    }

    @DeleteMapping("/{id}")
    @CacheEvict(value="files",key="'frontAll'")
    public Result delete(@PathVariable Integer id) {
        Files files = fileMapper.selectById(id);
        files.setIsDelete(true);
        fileMapper.updateById(files);
        return Result.success();
    }

@GetMapping("findAll")
    @Cacheable(value = "files" ,key = "'frontAll'")
    public Result findAll(){
        List filesList = fileMapper.selectList(null);
        return Result.success(filesList);
    }

添加依赖(springboot-redis)


            org.springframework.boot
            spring-boot-starter-redis
            1.4.7.RELEASE
        

增加配置,使用redis

public Result findAll(){
        String  str = (String) redisTemplate.opsForValue().get(Constants.REDIS_KEY);
        List filesList;
        if(str!=null){
            filesList = JSONUtil.toBean(str, new TypeReference>() {
            }, true);
        }else{
            filesList = fileMapper.selectList(null);
            String jsonStr = JSONUtil.toJsonStr(filesList);
            redisTemplate.opsForValue().set(Constants.REDIS_KEY,jsonStr,30,TimeUnit.MINUTES);
        }

        return Result.success(filesList);
    }


//清空缓存
public void flushRedis(String key){
        redisTemplate.delete(key);
    }
百度地图集成

根据官方文档学习是最好的

申请key值

Hello Word

  




点标记

  //定义事件处理方法
  var clickHandler=function(evt){
    var lat = evt.latLng.getLat().toFixed(6);
    var lng = evt.latLng.getLng().toFixed(6);
    console.log("您点击的的坐标是:"+ lat + "," + lng);
  }
//Map实例创建后,通过on方法绑定点击事件
  map.on("click",clickHandler)

var markerLayer = new TMap.MultiMarker({
    map: map,  //指定地图容器
    //样式定义
    styles: {
      //创建一个styleId为"myStyle"的样式(styles的子属性名即为styleId)
      "myStyle": new TMap.MarkerStyle({
        "width": 25,  // 点标记样式宽度(像素)
        "height": 35, // 点标记样式高度(像素)

      })
    },
    //点标记数据数组
    geometries: [{
      "id": "1",   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
      "styleId": 'myStyle',  //指定样式id
      "position": new TMap.LatLng(23.040630,113.370344),  //点标记坐标位置
    },
      {
        "id": "2",   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'myStyle',  //指定样式id
        "position": new TMap.LatLng(23.041616,113.372654),  //点标记坐标位置
      }
    ]
  });

折线

//创建 MultiPolyline
  var polylineLayer = new TMap.MultiPolyline({
    id: 'polyline-layer', //图层唯一标识
    map: map,//设置折线图层显示到哪个地图实例中
    //折线样式定义
    styles: {
      'style_blue': new TMap.PolylineStyle({
        'color': '#3777FF', //线填充色
        'width': 6, //折线宽度
        'borderWidth': 5, //边线宽度
        'borderColor': '#FFF', //边线颜色
        'lineCap': 'butt' //线端头方式
      }),
      'style_red': new TMap.PolylineStyle({
        'color': '#CC0000', //线填充色
        'width': 6, //折线宽度
        'borderWidth': 5, //边线宽度
        'borderColor': '#CCC', //边线颜色
        'lineCap': 'round' //线端头方式
      })
    },
    //折线数据定义
    geometries: [
      {//第1条线
        'id': 'pl_1',//折线唯一标识,删除时使用
        'styleId': 'style_blue',//绑定样式名
        'paths': [new TMap.LatLng(23.040630,113.370344), new TMap.LatLng(23.041616,113.372654)]
      },
    ]
  });

信息窗口

//创建InfoWindow实例,并进行初始化
  var infowindow=new TMap.InfoWindow({
    content:"广州大学", //信息窗口内容
    position:new TMap.LatLng(23.040630,113.370344),//显示信息窗口的坐标
    map:map
  });

  infowindow.close();

  markerLayer.on("click", function () {
    //设置infoWindow
    infowindow.open(); //打开信息窗
  })
Vue集成视频播放

修改上传大小限制前台增加Video组件增加跳转界面VideoDetail

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号