# 设备管理系统 **Repository Path**: gonghao_git/equipment-management-system ## Basic Information - **Project Name**: 设备管理系统 - **Description**: 设备管理系统 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 1 - **Created**: 2023-04-03 - **Last Updated**: 2025-03-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: 设备管理系统系统-借还设备, Vue, Java ## README ## 开发文档: > 项目名:springboot > 组文件:com.eliota springboot > 环境: JDK:8 > springboot构建 > 插件选择: > springframework: 2.7.9 > Lombok > Spring Web > MybatisFramework > Mysql Driver > > 工程开始: ``` vue: https://cn.vuejs.org/ elmentUI https://element.eleme.io/#/zh-CN/component/installation Vuex: https://vuex.vuejs.org/zh/index.html Vite官网: https://cn.vitejs.dev/ axios中文文档: http://www.axios-js.com/zh-cn/docs/ mybatisPlus: https://baomidou.com/ ``` ``` 淘宝镜像加速: npm config set registry https://registry.npm.taobao.org ``` - 创建vue工程 ``` npm install -g @vue/cli -vue create 项目名称 vue create springboot-vue-demo -Manger select feature -VueX -Router -Babel -3.x Y -In package.json -进入创建的文件夹下 npm run serve ``` IDEAL设置 编辑配置 --> npm --> script --> serve - 设置启动自启 ``` - package.json "serve": "vue-cli-service serve --open", ``` ![image-20230315045844000](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230315045844000.png) 在vue.config.js添加服务启动设置 ``` module.exports = defineConfig({ transpileDependencies: true, devServer: { open: true, host: 'localhost', port: 8080 } }) ``` Hellovuew是删除,桌面组件清空,创建Header.vue,设置div快,设置全局cssy样式 ``` -global.css /*先清除样式*/ *{ /*外边距*/ margin: 0; padding: 0; /*盒装模型设置*/ box-sizing: border-box; } ``` - 安装elementUI > [(38条消息) Vue3项目下载并使用element-plus及echarts的方法_vue下载echarts_发呆的海绵宝宝的博客-CSDN博客](https://blog.csdn.net/qq_52673040/article/details/127792056) > > [组件 | Element](https://element.eleme.cn/#/zh-CN/component/quickstart) > > -main.js > > import ElementUI from 'element-ui'; > > import 'element-ui/lib/theme-chalk/index.css'; > > Vue.use(ElementUI); 组件引用和跳转: > ``` > 1.-创建oneView.vue > > > 内含有一句话: > > > 2.-router有个index.js可以注册路由 > const routes = { > { > path: '/one', > name: 'one', > component: () =>import('../views/OneView.vue') > //此处不写.vue好像也可以 > } > } > > 3.链接 > Vue3 > ``` 字符串传递: ``` --将template组件输出为一个Helloword组件,可通过 --该文件template标签下中留一个字符串的替换位置

{{HelloMsg}}

--在其他页面通过标签传递字符串 -- --由于Helloworld.vue就在首页显示,本身不涉及跳转里面文字涉及uel跳转,直接在index.js导包引用 import HomeView from '../views/HomeView.vue' --HelloWorld中的连接页面 ManageSystem --index.js注册 { path: '/one', name: 'one', component: () =>import('../views/OneView.vue') },{ path: '/two', name: 'two', component: () =>import('../views/TwoView') },{ path: '/three', name: 'three', component: () =>import('../views/Three.vue') },{ path: '/manger', name: 'MangerView', component: () => import('../views/MangerView') } ``` - Manger界面代码:01: - ![image-20230320101811783](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230320101811783.png) ``` ``` - manger02 ![image-20230320164149596](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230320164149596.png) ``` ``` - 跨域问题 ![image-20230320164055064](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230320164055064.png) 方法一: - 1.创建CorsConfiguration对象设置跨域请求放行对象 - 2.UrlBasedCorsConfigurationSource中注册放行设置 - 3.获取CorsFilter方法返回值 ``` @Configuration public class GlobalCorsConfig { //跨域请求最大有效时间,1天 private static final long MAX_AGE = 24*60*60; //http://localhost:8080 @Bean public CorsFilter corsFilter() { //1. 添加 CORS配置信息 CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); //放行哪些原始域 config.setAllowCredentials(true); //是否发送 Cookie config.addAllowedMethod("*"); //放行哪些请求方式 config.addAllowedHeader("*"); //放行哪些原始请求头部信息 config.addExposedHeader("*"); //暴露哪些头部信息 config.setMaxAge(MAX_AGE); //2. 添加映射路径 UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); corsConfigurationSource.registerCorsConfiguration("/**",config); //3. 返回新的CorsFilter return new CorsFilter(corsConfigurationSource); } } ``` ![image-20230320171036756](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230320171036756.png) >当`allowCredentials`为true时,`allowedOrigins`不能包含特殊值“*”,因为不能在“`Access Control Allow Origin`”响应头上设置该值。要允许凭据指向一组源,请显式列出它们,或者考虑改用“`allowedOriginPatterns`”。 >config.setAllowCredentials(true);时, config.addAllowedOrigin("*");不能用*,指定放行域名 ``` --改 config.addAllowedOrigin("*"); //放行哪些原始域 config.addAllowedOrigin("http://localhost:8080"); //放行哪些原始域 ``` 方法二: - 1.实现WebMvcConfigurer接口方法 - 2.直接在CorsRegistry实例中注册设置 ``` @Configuration public class GlobalCorsConfig implements WebMvcConfigurer { //跨域请求最大有效时间,1天 private static final long MAX_AGE = 24*60*60; @Override public void addCorsMappings(CorsRegistry registry) { // 设置允许跨域的路由 registry.addMapping("/**") // 设置允许跨域请求的域名------------修改此行 .allowedOriginPatterns("*") // 是否允许证书(cookies) .allowCredentials(true) // 设置允许的方法 .allowedMethods("*") // 跨域允许时间 .maxAge(MAX_AGE); } } ``` - 忽略字段 ``` @JsonIgnore//返回时Json工具转换时忽略改字段 ``` - 数组替换(补充) > ``` > const arr = [1,2,3,4,5] > arr.fill(0,1,3) // [1,0,0,0,5] > arr,fill(0,1) //[1,0,0,0,0] > Array(10).fill(item1,0,1).fill(item2,2) > ``` - 分页和查找 ```
load(){ fetch("http://localhost:9091/user/page?pageNum="+this.pageNum +"&pageSize="+this.pageSize+"&userName="+this.userName) .then(res => res.json()) .then(res => { console.log(res); this.tableData = res.data; this.total = res.total; }) }, handleSelectionChange(val) { this.multipleSelection = val console.log(this.multipleSelection) }, handleSizeChange(pageSize){ this.pageSize = pageSize console.log(pageSize) this.load() }, handleCurrentChange(pageNum){ this.pageNum = pageNum console.log(pageNum) this.load() } --controller @GetMapping("/page") public Map Page(@RequestParam Integer pageNum,@RequestParam Integer pageSize,@RequestParam String userName){ pageNum = (pageNum-1) * pageSize; if(userName == null){ log.info("userName值为null"); }else if("".equals(userName)){ log.info("userName的值时空字符串"); }else { log.info("userName的值为{}",userName); } List users = null; if(userName == null || userName.length() == 0){ users = userMapper.selectPage(pageNum, pageSize); }else { users = userMapper.selectPageByName(pageNum,pageSize,userName); } Integer total = userMapper.selectTotal(userName); Map res = new HashMap<>(); res.put("data",users); res.put("total",total); return res; } --service @Select("select t_user.id,username, password, nikename, phone, email, address " + "from gew.t_user where username like concat('%',#{userName},'%')" + "limit #{pageNum},#{pageSize}") List selectPageByName(Integer pageNum,Integer pageSize,String userName); @Select("select count(username) from gew.t_user where username like concat('%',#{userName},'%')") Integer selectTotal(String userName); ``` - 整合mybatis-plus ``` com.baomidou mybatis-plus-boot-starter 3.5.2 ``` - MybatisPlusConfig ``` @Configuration @MapperScan("com.baomidou.cloud.service.*.mapper") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return mybatisPlusInterceptor; } } ``` - UserMapper.java - UserMapper.xml - UserService.java - UserController.java ``` package com.eliota.mapper; import com.eliota.pojo.User; import org.apache.ibatis.annotations.*; import java.sql.Timestamp; import java.util.List; /** * @author M.G * @create 2023-03-17 10:50 * @date 2023/3/17 10:50 */ @Mapper public interface UserMapper { /** * 查找所有 * @return */ @Select("select * from gew.t_user") List findAll(); @Select("select t_user.id,username, password, nikename, phone, email, address from gew.t_user limit #{pageNum},#{pageSize};") List selectPage(Integer pageNum,Integer pageSize); @Select("select t_user.id,username, password, nikename, phone, email, address " + "from gew.t_user where username like concat('%',#{userName},'%')" + "limit #{pageNum},#{pageSize}") List selectPageByName(Integer pageNum,Integer pageSize,String userName); @Select("select count(username) from gew.t_user where username like concat('%',#{userName},'%')") Integer selectTotal(String userName); /** * 查id用户记录条数 * @param id * @return */ @Select("select count(1) from gew.t_user where id = #{id}") int countId(Integer id); /** * 插入数据 * @param user * @return */ @Insert("insert into gew.t_user(username, password, nikename, phone, email, address)\n" + "values (#{userName},#{password},#{nikeName},#{phone},#{email},#{address})") int insert(User user); int updateUser(User user); @Delete("delete from gew.t_user where id = #{id};") int deleteById(Integer id); } ``` ``` update gew.t_user username = #{userName}, password = #{password}, nikename = #{nikeName}, phone = #{phone}, email = #{email}, address = #{address}, id = #{id} ``` ``` package com.eliota.service; import com.eliota.mapper.UserMapper; import com.eliota.pojo.User; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author M.G * @create 2023-03-17 14:52 * @date 2023/3/17 14:52 */ @Slf4j @Service public class UserService { @Autowired private UserMapper userMapper; private int flag = 1; public String save(User user){ Integer uId = user.getId(); if(!"".equals(uId) && uId != null){ int count = userMapper.countId(uId); /*System.out.println("数据库中存在记录数:" + count);*/ if (count == 0){ int rows = userMapper.insert(user); return "插入" + rows + "行数据,成功"; }else { userMapper.updateUser(user); return "更新id为" + uId + "的用户,成功"; } } return "失败,请检查id"; } /* public String save(User user) { Integer uId = user.getId(); System.out.println(uId); if(uId >0 && !"".equals(uId)){ boolean flag = GenericUtils.findEntityById(uId, UserMapper.class); log.info("是否存在id为:{}:的数据:{}",user.getId(),flag); if(flag == true){ userMapper.updateById(user); String message = "更新行数为:" + userMapper.countId(uId) + "行"; return message; }else { userMapper.insert(user); return "插入" + userMapper.countId(uId) + "行"; } }else{ return "id为空"; } }*/ } ``` ``` package com.eliota.controller; import com.eliota.mapper.UserMapper; import com.eliota.pojo.User; import com.eliota.service.UserService; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.sql.Date; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * @author M.G * @create 2023-03-17 11:06 * @date 2023/3/17 11:06 */ @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserMapper userMapper; @Autowired private UserService userService; @GetMapping public List findAll(){ return userMapper.findAll(); } @PostMapping("/save") public String save(@RequestBody User user){ return userService.save(user); } @DeleteMapping("/{id}") public int deleteUsrById(@Param("id") Integer id){ return userMapper.deleteById(id); } @GetMapping("/page") public Map Page(@RequestParam Integer pageNum,@RequestParam Integer pageSize,@RequestParam String userName){ pageNum = (pageNum-1) * pageSize; if(userName == null){ log.info("userName值为null"); }else if("".equals(userName)){ log.info("userName的值时空字符串"); }else { log.info("userName的值为{}",userName); } List users = null; if(userName == null || userName.length() == 0){ users = userMapper.selectPage(pageNum, pageSize); }else { users = userMapper.selectPageByName(pageNum,pageSize,userName); } Integer total = userMapper.selectTotal(userName); Map res = new HashMap<>(); res.put("data",users); res.put("total",total); return res; } } ``` - MybatisPlus结构改动 - UserMapper继承BaseMapper<>并且指定表对象泛型 ``` -- 1.指定Mappery映射 @MapperScan("com.eliota.mapper") --2. mapper作为数据连接接口先改,继承BaseMapper --由于PlusConfig中指定了mapper扫描也可不写@Mapper注解 public interface UserMapper extends BaseMapper -- 3.Service实现类(可直接用plus实现类,也可拆分接口和是实现类重写方法) @Service public class UserServiceImpl extends ServiceImpl{} -- 4.r若要拆分接口可以写成接口实现类 public interface UserService extends IService{} @Service public Class UserServiceImpl extends ServiceImpl implement UserService{} ``` - 实体类实用注解 ``` -指定数据表名 @TableName("t_user") -Id注解,value和type参数 @TableId(type = IdType.AUTO) -数据库字段名绑定,当类属性名不一致需要对应上,否则驼峰命名匹配,例如nikeName > nike_name @TableField(value = "nikename") private String nikeName; ``` - 整合SwaggerApi接口测试 阿里云Swagger测试说明博客:https://developer.aliyun.com/article/972252 Sagger版本过高运行问题解决博客:https://stackoverflow.com/questions/40241843/failed-to-start-bean-documentationpluginsbootstrapper-in-spring-data-rest > 阿里博客部署:Swgger2/3 ui 和 bootstrap ui都尝试过没有问题,运行问题描述详细另一篇论坛中详述了 > > 论坛中的ABC方案都可行,降低SpringBoot版本没试过 ![image-20230321021413143](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230321021413143.png) ![image-20230321021602485](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230321021602485.png) - pom包 - Swagger2 ``` io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 ``` > 2需要配置Config类设置如下,同时配置yml中的路径匹配策略不然报错,不配yml也可添加@EnableWebMvc注解,配置详细说明可看阿里云那篇博客 ``` package com.eliota.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * @author M.G * @create 2023-03-21 0:44 * @date 2023/3/21 0:44 * * http://localhost:端口swagger-ui.html * http://localhost:端口/doc.html */ @Configuration public class SwaggerConfig { @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("后端接口") .apiInfo(apiInfo("Springboot项目Api接口","1.0")) .useDefaultResponseMessages(true) .forCodeGeneration(false) .select() .apis(RequestHandlerSelectors.basePackage("com")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo(String title,String version){ return new ApiInfoBuilder() .title(title) .description("Springboot项目Swagger显示界面") .termsOfServiceUrl("http://localhost:9091") .contact(new Contact("Eliota","域名:......","eliota@test.com")) .version(version) .build(); } } ``` ``` spring: mvc: pathmatch: matching-strategy: ant_path_matcher ``` - Swagger和Bootstrap-ui ``` io.springfox springfox-boot-starter 3.0.0 com.github.xiaoymin swagger-bootstrap-ui 1.9.6 ``` > 直接默认,运行 - 3.0加个@EnableOpenApi也可以用更open的ui,个人是不用注解也行,但后期问题还没发生,感觉区别不大 - ui链接地址 ``` -Swagger3普通版 http://localhost:端口swagger-ui.html -Swagger3:openApi版 http://localhost:9091/swagger-ui/index.html -bootStrap版 http://localhost:端口/doc.html ``` - 安装axios > npm i axios -S - 通用设置 ``` // 二次封装 axios (拦截器) import axios from "axios"; const request = axios.create({ baseURL: '/api', // baseURL: 'http://81.71.65.4:3003', timeout: 5000 }) request.interceptors.request.use((config) => { config.headers['Content-Type'] = 'application/json;charset=utf-8' return config; }, (error) => { return Promise.reject(error); }) request.interceptors.response.use((response) => { // 接口响应时需要做的事 let res = response.data //如果返回是文件 if(response.config.responseType === 'blob'){ return res } if(typeof res === 'String'){ //是字符串返回JSON,否则原样返回 res = res ? JSON.parse(res) : res } return res }, (error) => { // alert('网络错误!') console.log('err>>>>>>>>>>' + error) return Promise.reject(error) }) export default request ``` - 1.1页面基本布局 ``` ``` Day2 - 增删改查和批量删除 - 页面加载 ``` created() { this.load() } ``` ``` - request的url头设置 const request = axios.create({ baseURL: 'http://localhost:9091', // baseURL: 'http://81.71.65.4:3003', timeout: 5000 }) ``` - 搜索和清空搜索框 ```vue -- 搜索框搜索用户名 username 搜索 -- 默认为空 data(){ usereName: '', } -- 按钮绑定分页查询 -- GET: /user/page findPage(){Num,Size,userName} load(){ request.get("/user/page",{ params: { pageNum: this.pageNum, pageSize: this.pageSize, userName: this.userName, } }).then(res => { console.log(res) //返回值赋值给表数据 this.tableData = res.records this.total = res.total }) } -- data初始值 date(){ tabelData: [], //总条数 total:0, //初始页码 pageNum: 1, //分页数据量 pageSize: 3, } -- 重置按钮:清空搜索框内容 reset(){ //重置搜索框 this.userName = "" this.load() } -- tabel属性 ``` - 新增 ``` 新增 --初始化 data(){ dialogFormVisible: false form: {} } -- 绑定事件 handleAdd(){ //新增 this.dialogFormVisible = true this.form = {} }, --新增触发添加对话框 - 对话框校验 》》》待开发 - 对话框确定按钮点击触发新增和更新 - 若id不存在新增,id存在更新 //保存 save(){ request.post("/user/save",this.form) .then(res => { //若果返回结果为true if(res){ this.$message.success("保存成功") this.dialogFormVisible = false }else { this.$message.error("保存失败") } }) }, ``` - 修改(数据回显) ``` --按钮样式 --初始化变量值,新增对话框默认也为关闭dialogFormVisible data(){ dialogFormVisible: false, form: {} } -- 方法 插槽锁定行领域 获取行消息 handleEdit(row){ this.form = row this.dialogFormVisible = true } - el-dialog保存更新 ``` - 单条删除和批量删除 - 单条删除 ``` --按钮外套一层弹窗提示是否删除 删除 -- 插槽获取作用域中id值 handleDel(id){ request.delete('/user/' + id).then(res => { if(res){ this.$message.success("删除成功") this.load() }else { this.$message.error("删除失败") } }) }, -- 多条删除 -- 添加多选框 -- 多选框事件handleSelectionChange -- 初始化 data(){ multipleSelection: [] } --handleSelectionChange handleSelectionChange(val) { //行任意val值的数据 console.log(this.multipleSelection) this.multipleSelection = val }, -- 批量删除嵌套弹窗提醒 批量删除 -- 事件 bathDel(){ //map键值对绑定multipleSelection中的id值 [{},{},{}] => [multipleItem.1.id,..2.id,..3.id,..4] let ids = this.multipleSelection.map(v => v.id) request.post("/user/bath/del",ids).then(res => { if(res){ this.$message.success("批量删除成功") this.load() }else { this.$message.error("批量删除失败") } }) console.log(ids) }, ``` - 取消全选按钮 ``` --递归扫描,不空清除 toggleSelection(rows) { if (rows) { rows.forEach(row => { this.$refs.multipleTable.toggleRowSelection(row); }); } else { this.$refs.multipleTable.clearSelection(); } }, ``` - myabtis-plus代码生成器 ``` 1.8 3.5.2 com.baomidou mybatis-plus-generator ${generator.version} ``` - **Velocity** 模板引擎 语法参考博客园:[Velocity语法大全 - codingsilence - 博客园 (cnblogs.com)](https://www.cnblogs.com/codingsilence/archive/2011/03/29/2146580.html) ``` FastAutoGenerator.create("url", "username", "password") .globalConfig(builder -> { builder.author("baomidou") // 设置作者 .enableSwagger() // 开启 swagger 模式 .fileOverride() // 覆盖已生成文件 .outputDir("D://"); // 指定输出目录 }) .packageConfig(builder -> { builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名 .moduleName("system") // 设置父包模块名 .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径 }) .strategyConfig(builder -> { builder.addInclude("t_simple") // 设置需要生成的表名 .addTablePrefix("t_", "c_"); // 设置过滤表前缀 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .execute(); ```