当前位置:   article > 正文

SPRINGBOOT+VUE项目实战_springboot vue项目实战

springboot vue项目实战

第一章=>
1、ElementUI         
2、布局与主体             
3、增删改查               
4、路由                       
5、播放组件

第二章=>
6、分页                 
7、代码生成                 
8、导入导出               
9、用户登录                 
10、注册与异常处理   

第三章=>
11、JWT               
12、文件上传               
13、权限管理           
14、Redis

第四章=>
15、Echats             
16、百度地图               
17、Markdwon       
18、WangEditor

第五章=>
19、前台页面         
20、视频播放               
21、多级评论           
22、支付宝

第六章=>
23、购物车             
24、借书管理               
25、百度地图           
26、聊天室

第七章=>
27、考试系统         
28、邮箱登录               
29、活动预约           
30、电商系统

****************************************************************************************************************************************************************************

  1. 1、ElementUI
  2. 1】软件安装,开发环境配置。
  3. JDK1.8、mysql5.7、node、navicat、idea2021
  4. *************************************************************************
  5. 2】安装vue-cli
  6. npm i -g @vue/cli
  7. *************************************************************************
  8. vue -V
  9. *************************************************************************
  10. vue create white
  11. *************************************************************************
  12. 用white记录模板创建即可。
  13. *************************************************************************
  14. npm config set registry https://registry.npmmirror.com/
  15. npm config get registry
  16. *************************************************************************
  17. 用WebStorm打开
  18. *************************************************************************
  19. 3】项目结构
  20. http://localhost:8080/
  21. App.vue通过路由展示了Home与About页面
  22. *************************************************************************
  23. 4】安装element
  24. npm i element-ui -S
  25. *************************************************************************使用
  26. <el-button type="primary">主要点击</el-button>

****************************************************************************************************************************************************************************

  1. 2、布局与主体
  2. 1】安装less
  3. npm i less-loader less -S
  4. ************************************************************************样式调整而已
  5. 链接:https://pan.baidu.com/s/15YwRICJKS7aCiBqWcYeAKw
  6. 提取码:iwr9
  7. 完成主页面样式设计

****************************************************************************************************************************************************************************

  1. 5、Springboot框架搭建
  2. 1】配置Lombok、Spring web、MyBatis Framework、MySQL Driver
  3. 2】配置阿里云下载
  4. <!--配置阿里云仓库下载-->
  5. <repositories>
  6. <repository>
  7. <id>nexus-aliyun</id>
  8. <name>nexus-aliyun</name>
  9. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  10. <releases>
  11. <enabled>true</enabled>
  12. </releases>
  13. <snapshots>
  14. <enabled>false</enabled>
  15. </snapshots>
  16. </repository>
  17. </repositories>
  18. <pluginRepositories>
  19. <pluginRepository>
  20. <id>public</id>
  21. <name>nexus-aliyun</name>
  22. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  23. <releases>
  24. <enabled>true</enabled>
  25. </releases>
  26. <snapshots>
  27. <enabled>false</enabled>
  28. </snapshots>
  29. </pluginRepository>
  30. </pluginRepositories>
  31. 3】启动报错
  32. Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
  33. *****************************************************************************
  34. #数据库驱动
  35. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  36. #东八区+数据库连接配置
  37. spring.datasource.url=jdbc:mysql://wdfgdzx.top:3306/black?serverTimezone=GMT2b%8
  38. spring.datasource.username=root
  39. spring.datasource.password=s19911009!
  40. *****************************************************************************
  41. black utf8mb4 utf8mb4_unicode_ci两个配置
  42. *****************************************************************************
  43. #0代表黑,1代表白。前端是8001
  44. server.port=8000

****************************************************************************************************************************************************************************

  1. 6、Mybatis实现数据增删改查
  2. 1】动态sql
  3. 2File -> Settings -> Editor -> File encodings --> 设置properties的编码
  4. 3】mybatis配置
  5. #指定mybatis配置
  6. mybatis.mapper-locations=classpath:mapper/*.xml
  7. *****************************************************************************properties
  8. #0代表黑,1代表白,前端是8001
  9. server.port=8000
  10. #数据库驱动
  11. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  12. #数据库配置
  13. spring.datasource.url=jdbc:mysql://wdfgdzx.top:3306/black?serverTimezone=GMT%2b8&allowMultiQueries=true&useAffectedRows=true
  14. spring.datasource.username=root
  15. spring.datasource.password=s19911009!
  16. #指定mybatis配置
  17. mybatis.mapper-locations=classpath:mapper/*.xml
  18. 4】详细代码
  19. 链接:https://pan.baidu.com/s/1Jz80if48Z5pannN_TOAUTQ
  20. 提取码:fq9j

****************************************************************************************************************************************************************************

  1. 8、分页查询实现
  2. 1】控制器的路基
  3. @PostMapping("list_page")
  4. public HashMap list_page(@RequestBody User user) {
  5. Integer totalNum = userMapper.total();
  6. MyUtils.selectByPageManage(totalNum, user);
  7. HashMap hashMap = new HashMap();
  8. hashMap.put("total", totalNum);
  9. hashMap.put("data", userMapper.list_page(user));
  10. return hashMap;
  11. }
  12. 2】跨域问题的处理
  13. package com.black.config;
  14. import org.springframework.context.annotation.Bean;
  15. import org.springframework.context.annotation.Configuration;
  16. import org.springframework.web.cors.CorsConfiguration;
  17. import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  18. import org.springframework.web.filter.CorsFilter;
  19. @Configuration
  20. public class Cross {
  21. // 当前跨域请求最大有效时长,默认1
  22. private static final long MAX_DAY = 24 * 60 * 60;
  23. @Bean
  24. public CorsFilter buildCorsFilter() {
  25. CorsConfiguration corsConfiguration = new CorsConfiguration();
  26. corsConfiguration.addAllowedOrigin("*"); // 访问源地址
  27. corsConfiguration.addAllowedHeader("*");// 访问源请求头
  28. corsConfiguration.addAllowedMethod("*"); // 访问源请求方法
  29. corsConfiguration.setMaxAge(MAX_DAY);// 设置最大有效时长
  30. UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
  31. urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); // 对接口配置跨域设置
  32. return new CorsFilter(urlBasedCorsConfigurationSource);
  33. }
  34. }
  35. 3】配置axios
  36. npm i axios -S
  37. ************************************************************************
  38. // main.js
  39. import axios from "axios"; // 导入axios
  40. Vue.prototype.$http = axios //在Vue的原型上添加一个$http属性,该属性保存了axios
  41. axios.defaults.baseURL = 'http://localhost:8000'

****************************************************************************************************************************************************************************

  1. 9、MybatisPlus与SwaggerUI
  2. 1】pom配置
  3. <!--mybatis-plus-->
  4. <dependency>
  5. <groupId>com.baomidou</groupId>
  6. <artifactId>mybatis-plus-boot-starter</artifactId>
  7. <version>3.5.1</version>
  8. </dependency>
  9. 2】MybaitsPlus配置
  10. package com.black.config;
  11. import com.baomidou.mybatisplus.annotation.DbType;
  12. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
  13. import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
  14. import org.mybatis.spring.annotation.MapperScan;
  15. import org.springframework.context.annotation.Bean;
  16. import org.springframework.context.annotation.Configuration;
  17. @Configuration
  18. @MapperScan("com.black.mapper") // !!!!!!!!!!!!!!
  19. public class MybatisPlus {
  20. @Bean
  21. public MybatisPlusInterceptor buildMybatisPlusInterceptor() {
  22. MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
  23. mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  24. return mybatisPlusInterceptor;
  25. }
  26. }
  27. 3】简化了UserMapper.interface与UserMapper.xml(只需要写复杂的即可,简单的MP代理了)
  28. package com.black.mapper;
  29. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  30. import com.black.pojo.User;
  31. import org.apache.ibatis.annotations.Param;
  32. import java.util.List;
  33. public interface UserMapper extends BaseMapper<User> { // !!!!!!!!!!
  34. Integer total(@Param("user") User user); //总条数
  35. List<User> list_page(@Param("user") User user); //分页数据
  36. }
  37. ******************************************************************************
  38. MybatisPlus让数据库操作变得更简单...
  39. ******************************************************************************
  40. @TableId(value = "id", type = IdType.AUTO) // 这个会影响是否按顺序增加!!!!!!!
  41. @TableId(type = IdType.AUTO) // 当代码里的字段与数据库不同时,可以通过value="xxx"对应数据库字段
  42. type = IdType.AUTO// 这个影响自增,所以很重要
  43. 4】SwaggerUI简化postman的测试
  44. <!-- swagger接口文档 -->
  45. <dependency>
  46. <groupId>io.springfox</groupId>
  47. <artifactId>springfox-boot-starter</artifactId>
  48. <version>3.0.0</version>
  49. </dependency>
  50. ******************************************************************************
  51. # properties
  52. # 引入swagger3.0时加入的配置 http://localhost:8000/swagger-ui/index.html
  53. spring.mvc.pathmatch.matching-strategy=ant_path_matcher
  54. ******************************************************************************
  55. package com.black.config;
  56. import org.springframework.context.annotation.Bean;
  57. import org.springframework.context.annotation.Configuration;
  58. import springfox.documentation.builders.ApiInfoBuilder;
  59. import springfox.documentation.builders.PathSelectors;
  60. import springfox.documentation.builders.RequestHandlerSelectors;
  61. import springfox.documentation.service.ApiInfo;
  62. import springfox.documentation.service.Contact;
  63. import springfox.documentation.spi.DocumentationType;
  64. import springfox.documentation.spring.web.plugins.Docket;
  65. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  66. @Configuration
  67. @EnableSwagger2
  68. public class Swagger {
  69. @Bean
  70. public Docket restAPI() {
  71. return new Docket(DocumentationType.SWAGGER_2)
  72. .groupName("HIT的接口")
  73. .apiInfo(APIInfo())
  74. .useDefaultResponseMessages(true)
  75. .forCodeGeneration(false)
  76. .select()
  77. .apis(RequestHandlerSelectors.basePackage("com.black.controller")) // !!!!!!!!!!!!
  78. .paths(PathSelectors.any())
  79. .build();
  80. }
  81. private ApiInfo APIInfo() {
  82. return new ApiInfoBuilder()
  83. .title("RESTful")
  84. .description("http://wdfgdzx.top:8001/")
  85. .termsOfServiceUrl("http://wdfgdzx.top:8001/")
  86. .contact(new Contact("HIT", "http://wdfgdzx.top:8001/", "wdfgdzx@163.com"))
  87. .version("V1.0")
  88. .build();
  89. }
  90. }
  91. ******************************************************************************看自己收藏的CSDN一样的
  92. http://localhost:8000/swagger-ui/index.html

****************************************************************************************************************************************************************************

  1. 10、VUE实现增删改查
  2. 1】配置xml SQL查询
  3. Editor->Inspections->SQL->No data sources configured 和 SQL dialect detection
  4. 看收藏的CSDN帖子
  5. ***************************************************************************
  6. 2】安装配置axios
  7. npm i axios -S
  8. ***************************************************************************
  9. import axios from "axios";// 导入axios
  10. /*1、配置后台请求接口*/
  11. const $http = axios.create({ //在Vue的原型上添加一个$http属性,该属性保存了axios
  12. baseURL: "http://localhost:8000",
  13. timeout: 5000
  14. })
  15. /*2、请求拦截器,对发送请求钱做一些处理,比如统一加token/对请求参数统一加密等...*/
  16. $http.interceptors.request.use(config => {
  17. config.headers['Content-Type'] = "application/json;charset=utf-8" // 定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件
  18. return config
  19. })
  20. /*3、响应拦截器,在接口响应后统一处理结果*/
  21. $http.interceptors.response.use(res => {
  22. return res;
  23. })
  24. export default $http
  25. ***************************************************************************
  26. import $http from './util/axios.js' // 引入定义的axios工具
  27. Vue.prototype.$http = $http //引入util/axios.js暴露的$http来使用
  28. ***************************************************************************
  29. 3】行数据脏改
  30. /*修改窗口*/
  31. updateWindow(row) {
  32. this.userFormFlag = true
  33. this.userForm = JSON.parse(JSON.stringify(row)) // !!!!!!!!!!!!
  34. },

****************************************************************************************************************************************************************************

  1. 11、SpringBoot代码生成
  2. 1】引入依赖
  3. <!--代码生产-->
  4. <dependency>
  5. <groupId>org.apache.velocity</groupId>
  6. <artifactId>velocity</artifactId>
  7. <version>1.7</version>
  8. </dependency>
  9. <dependency>
  10. <groupId>com.baomidou</groupId>
  11. <artifactId>mybatis-plus-generator</artifactId>
  12. <version>3.5.1</version>
  13. </dependency>
  14. **********************************************************************我只想说打扰了,哥哥
  15. 这么简单的代码,不至于生成,还覆盖了我原有的代码,过!!!!
  16. 不如直接复制+替换关键字不香吗???

****************************************************************************************************************************************************************************

  1. 12、VUE使用路由展示左侧栏
  2. 1】props: ['fff_top_foldData', 'fff_top_foldClick'],/*说明props还可以接受函数,我日尼玛哦*/
  3. 2】路由守卫
  4. *************************************************************************使用vuex解决导航路径问题
  5. npm i vuex -S --force
  6. *************************************************************************myVuex.js
  7. import Vue from 'vue'
  8. import Vuex from 'vuex'
  9. Vue.use(Vuex)
  10. const myVuex = new Vuex.Store({
  11. state: {
  12. currentPathName: '' // 当前路径名称
  13. },
  14. mutations: {
  15. setStateCurrentPathName(state) {
  16. state.currentPathName = localStorage.getItem("currentPathName")
  17. }
  18. }
  19. })
  20. export default myVuex
  21. *************************************************************************main.js
  22. import Vue from 'vue'
  23. import App from './App.vue'
  24. import router from './router/router.js'
  25. import ElementUI from 'element-ui'
  26. import 'element-ui/lib/theme-chalk/index.css'
  27. import './assets/main.css' // 全局样式表
  28. import $http from './util/axios.js' // 引入定义的axios工具
  29. import myVuex from "@/store/myVuex.js"; // 引入vuex管理全局存储
  30. Vue.prototype.$http = $http //引入util/axios.js暴露的$http来使用
  31. Vue.config.productionTip = false
  32. Vue.use(ElementUI, {size: 'mini'}) // 挂载elementUI
  33. new Vue({
  34. router,
  35. myVuex,
  36. render: h => h(App)
  37. }).$mount('#app')
  38. *************************************************************************router.js
  39. import Vue from 'vue'
  40. import VueRouter from 'vue-router'
  41. import Manage from "@/views/Manage.vue";
  42. import User from "@/views/User.vue";
  43. import Home from "@/views/Home.vue";
  44. import myVuex from "@/store/myVuex";
  45. Vue.use(VueRouter)
  46. const routes = [
  47. {
  48. path: '/',
  49. name: 'manage',
  50. component: Manage,
  51. redirect: "/home",
  52. children: [
  53. {path: 'user', name: '用户管理', component: User},
  54. {path: 'home', name: '首页', component: Home}
  55. ]
  56. },
  57. {
  58. path: '/about',
  59. name: 'about',
  60. // route level code-splitting
  61. // this generates a separate chunk (about.[hash].js) for this route
  62. // which is lazy-loaded when the route is visited.
  63. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  64. }
  65. ]
  66. const router = new VueRouter({
  67. mode: 'history',
  68. base: process.env.BASE_URL,
  69. routes
  70. })
  71. /*路由守卫*/
  72. router.beforeEach((to, from, next) => {
  73. // console.log(to)
  74. localStorage.setItem("setStateCurrentPathName", to.name) // 设置即将访问的路由名称,为了在Header中使用
  75. myVuex.commit("setPath") // 触发myVuex里的数据更新
  76. next()
  77. })
  78. export default router
  79. *************************************************************************Top.vue

****************************************************************************************************************************************************************************

  1. 13、SpringBoot实现导入导出
  2. 1】说明学只能是学。但是和军队管理的用还差的很多,还是要多用,做项目来成长。
  3. @GetMapping("/list_export")
  4. public String list_export(HttpServletResponse httpServletResponse) throws Exception {
  5. //1、从数据查询
  6. List<User> userList = userMapper.selectList(null);
  7. ExcelWriter excelWriter = ExcelUtil.getWriter(true);
  8. //2、自定义标题
  9. excelWriter.addHeaderAlias("id", "序号");
  10. excelWriter.addHeaderAlias("name", "姓名");
  11. excelWriter.addHeaderAlias("nick", "昵称");
  12. excelWriter.addHeaderAlias("email", "邮箱");
  13. excelWriter.addHeaderAlias("phone", "手机");
  14. excelWriter.addHeaderAlias("address", "地址");
  15. excelWriter.setOnlyAlias(true); // 仅写出指定字段
  16. excelWriter.write(userList, true);
  17. //3、设置浏览器响应的格式
  18. httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
  19. String fileName = URLEncoder.encode("用户信息", "UTF-8");
  20. httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
  21. //4、拿到输出流并刷新
  22. ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
  23. excelWriter.flush(servletOutputStream, true);
  24. servletOutputStream.close();
  25. excelWriter.close();
  26. return "导出成功";
  27. }
  28. 2】学用需要结合,这样才真正掌握某一项技能。军队管理!!!!!
  29. 3】批量插入的mybatis的写法,重要!!!!!!!!!!!!!!!!!!!!!
  30. <insert id="list_insert" parameterType="java.util.List">
  31. 4】导入的第二种方式
  32. ********************************************************************忽略表头中文
  33. List<CsvRow> rowList = data.getRows(); // 忽略表头文件,直接读取内容
  34. rowList.remove(0);
  35. row.get(5)
  36. ********************************************************************
  37. 写死了,唯一的优点是不用管表头名字
  38. 5】前端调用
  39. ********************************************************************
  40. /*导出*/
  41. list_export() {
  42. window.open("http://localhost:8000/user/list_export")
  43. },
  44. ********************************************************************
  45. <el-upload style="display: inline-block"
  46. action="http://localhost:8000/user/list_import"
  47. name="multipartFile" // !!!!!!!!!!!!!多看文档
  48. :show-file-list="false"
  49. accept="xlsx"
  50. :on-success="onImportSuccess">
  51. <el-button type="primary" class="ml-5">导入<i class="el-icon-upload2"></i></el-button>
  52. </el-upload>

****************************************************************************************************************************************************************************

  1. 14、Springboot+VUE实现登录
  2. 1】登录页的路由+布局
  3. 2】后台写登录接口
  4. @RestController
  5. @RequestMapping("/big")
  6. public class BigController {
  7. @Resource
  8. UserMapper userMapper;
  9. @PostMapping("login")
  10. public String login(@RequestBody User user) {
  11. // 拿到用户输入的账号密码
  12. User existUser = userMapper.selectUserByNamePassword(user);
  13. if (existUser == null) {
  14. return "账号或密码错误"; // 好简单!!!!!!!!!!!
  15. }
  16. return "登录成功";
  17. }
  18. }
  19. *************************************************************************
  20. 400报错,如果后端要求的传参没有传递也会报错400。比如big/login要求传user,如果没传则报错400
  21. *************************************************************************
  22. <el-form :rules="ruleList" :model="user"><!--用来校验表单-->
  23. *************************************************************************
  24. <el-form-item prop="name"><!--必须el-form-item prop="xxx"才能生效-->
  25. 再次说明学以致用最重要!!!!!!!!!!!!!!!!!!!!!!!!
  26. *************************************************************************校验规则先通过才发送请求
  27. ref="userForm"
  28. *************************************************************************用到了引用ref
  29. loginClick() {
  30. this.$refs["userForm"].validate(valid => {
  31. if (valid) { // 表单校验合法
  32. this.$http.post("/big/login", this.user).then(res => {
  33. if (res.data !== "登录成功") {
  34. this.$message.error("用户名或密码错误")
  35. } else {
  36. this.$router.push("/")
  37. }
  38. })
  39. } else {
  40. return false
  41. }
  42. })
  43. }
  44. 3】假如数据库存在脏数据,就是有重复的用户名+密码,怎么处理?
  45. 用List判断数据是否存在
  46. *************************************************************************
  47. 用try catch,捕获到异常然后再处理

****************************************************************************************************************************************************************************

  1. 15、Springboot+Vue实现注册与异常处理
  2. 1】登录信息存储
  3. package com.black.util;
  4. // 常量类
  5. public class Constants {
  6. public static final String CODE_200 = "200"; // 成功
  7. public static final String CODE_400 = "400"; // 参数错误
  8. public static final String CODE_401 = "401"; // 权限不足
  9. public static final String CODE_500 = "500"; // 系统错误
  10. public static final String CODE_600 = "600"; // 其他错误
  11. }
  12. *******************************************************************************
  13. package com.black.util;
  14. import lombok.AllArgsConstructor;
  15. import lombok.Data;
  16. import lombok.NoArgsConstructor;
  17. // 返回结果包装类
  18. @Data
  19. @NoArgsConstructor
  20. @AllArgsConstructor
  21. public class Res {
  22. private String code;
  23. private String message;
  24. private Object object;
  25. // 成功的两个方法
  26. public static Res success() {
  27. return new Res(Constants.CODE_200, "", null);
  28. }
  29. public static Res success(Object object) {
  30. return new Res(Constants.CODE_200, "", object);
  31. }
  32. // 不成功的方法
  33. public static Res error() {
  34. return new Res(Constants.CODE_500, "系统错误", null);
  35. }
  36. public static Res error(String code, String message) {
  37. return new Res(code, message, null);
  38. }
  39. }
  40. 2】全局异常处理,最终还是用到了Res,这个先保留了解下...
  41. 3】前端的处理
  42. localStorage.setItem("user", JSON.stringify(res.data.object)) // 存储用户信息到浏览器
  43. *******************************************************************************
  44. user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
  45. *******************************************************************************
  46. logoutClick() {
  47. this.$router.push("/login")
  48. localStorage.removeItem("user") // 移出浏览器用户信息
  49. this.$message.success("退出成功")
  50. }
  51. 4】注册页面与接口的改造,统一返回封装的Res

****************************************************************************************************************************************************************************

  1. 16、Springboot使用JWT
  2. 1】JWT全称是json web token
  3. 它将用户信息加密到token里,服务器不保存任何用户信息。
  4. 服务器通过使用保存的密匙验证token的正确性,只要正确即可通过验证。
  5. ***************************************************************************
  6. 优点:
  7. 简洁:通过URL POST参数或者在http header发送,数据量小,传输速度也快。
  8. ***************************************************************************
  9. 自包含,避免了多次的数据库查询。
  10. ***************************************************************************
  11. 因为token是以json加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
  12. ***************************************************************************
  13. 不需要在服务端保存会话信息,特别适用于分布式的---微服务---。
  14. ***************************************************************************
  15. 缺点:
  16. 无法作废已颁布的令牌;
  17. 不易于应对数据过期;
  18. 2】组成,一个token是三个组成部分。
  19. header头部;载荷payload;签证singature。用.分割
  20. ***************************************************************************
  21. <!--JWT-->
  22. <dependency>
  23. <groupId>com.auth0</groupId>
  24. <artifactId>java-jwt</artifactId>
  25. <version>3.10.3</version>
  26. </dependency>
  27. ***************************************************************************
  28. package com.black.util;
  29. import cn.hutool.core.date.DateUtil;
  30. import com.auth0.jwt.JWT;
  31. import com.auth0.jwt.algorithms.Algorithm;
  32. import com.black.pojo.User;
  33. import java.util.Date;
  34. public class Token {
  35. public static String productToken(User user) {
  36. return JWT.create().withAudience(user.getId() + "") // user.id作为载荷
  37. .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //2小时后token过期
  38. .sign(Algorithm.HMAC256(user.getPassword())); // 以 password 作为 token 的密钥
  39. }
  40. }
  41. 3】token的重要作用
  42. /*2、请求拦截器,对发送请求钱做一些处理,比如统一加token/对请求参数统一加密等...*/
  43. $http.interceptors.request.use(config => {
  44. config.headers['Content-Type'] = "application/json;charset=utf-8" // 定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件
  45. // 从本地存储拿,设置请求头!!!!!!!!!!!!
  46. let user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
  47. if (user) {
  48. config.headers['token'] = user.token; // 设置
  49. }
  50. return config
  51. })
  52. ***************************************************************************后台拦截器
  53. 权限验证不同过,前端提示
  54. /*3、响应拦截器,在接口响应后统一处理结果*/
  55. $http.interceptors.response.use(res => {
  56. //alert(JSON.stringify(res))
  57. if (res.data.code === "401") {
  58. elementUI.Message({
  59. message: res.data.message,
  60. type: "error"
  61. })
  62. }
  63. return res;
  64. })
  65. 4】后台获取当前用户
  66. private static UserMapper staticUserMapper; // 因为@Resource不能定义为静态,这里是为了转存
  67. @Resource
  68. private UserMapper userMapper; // 因为有个静态方法引用了
  69. @PostConstruct
  70. public void setUserMapper() {
  71. staticUserMapper = userMapper;
  72. }
  73. ***************************************************************************
  74. // 获取当前用户
  75. public static User getNowUser() {
  76. try {
  77. HttpServletRequest httpServletRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); //拿到httpServletRequest
  78. String token = httpServletRequest.getHeader("token");//拿到token
  79. if (StrUtil.isNotBlank(token)) {
  80. String id = JWT.decode(token).getAudience().get(0); //拿到user.id
  81. return staticUserMapper.selectById(id);//拿到user
  82. }
  83. } catch (Exception e) {
  84. e.printStackTrace();
  85. return null;
  86. }
  87. return null;
  88. }
  89. ***************************************************************************
  90. System.err.println(JWTInterceptor.getNowUser()); // 全局可以获取
  91. 任何方法,任何Controller去使用

****************************************************************************************************************************************************************************

  1. 17、文件上传
  2. 1】swagger-ui的配置,在放行代码指定这些,JWT拦截里放行。牛批!!!!!
  3. "/swagger**/**",
  4. "/webjars/**",
  5. "/v2/**",
  6. "/doc.html"
  7. **************************************************************************文件操作都放行
  8. "/document/**"
  9. **************************************************************************上传不要@RequestBody
  10. public String upload(Document document) throws Exception
  11. **************************************************************************
  12. #上传文件大小指定(单个)
  13. spring.servlet.multipart.max-file-size=100MB
  14. **************************************************************************
  15. File uploadFile = new File(uploadDir + "/" + IdUtil.fastSimpleUUID() + "." + type);
  16. **************************************************************************
  17. 2】同样的图片去重43分钟,利用md5值是否相同来比较,这个是自己想的解决办法,必须赞!!!!!!!!!!!!
  18. @PostMapping("/upload")
  19. public String upload(Document document) throws Exception {
  20. String originName = document.getMultipartFile().getOriginalFilename();
  21. String type = FileUtil.extName(originName);
  22. long size = document.getMultipartFile().getSize();
  23. // 1、存到磁盘、存储数据库
  24. File uploadDir = new File(MyUtils.getFinalPath());
  25. System.err.println(uploadDir);
  26. if (!uploadDir.exists()) {
  27. System.out.println(uploadDir.mkdir()); //不存在创建新目录
  28. }
  29. // 2、最终文件路径
  30. String fileUUID = IdUtil.fastSimpleUUID() + "." + type;
  31. String url = "http://localhost:8000/document/" + fileUUID;
  32. File uploadFile = new File(uploadDir + "/" + fileUUID);
  33. //3、获取md5
  34. if (!uploadFile.exists()) {
  35. document.getMultipartFile().transferTo(uploadFile); // 这时候才存磁盘!!!!!
  36. }
  37. String md5 = SecureUtil.md5(uploadFile);
  38. // 4、判断数据库里md5是否存在
  39. document.setMd5(md5);
  40. List<Document> documentList = documentMapper.md5_list(document); // 数据库查询到相同md5的对象
  41. System.out.println(documentList);
  42. if (documentList.size() != 0) {
  43. url = documentList.get(0).getUrl(); // 存在的话,url直接从已存在数据库信息拿到
  44. System.out.println(uploadFile.delete());// 删除本地存储的文件
  45. }
  46. // 5、存数据库
  47. Document insertDocument = new Document();
  48. insertDocument.setUrl(url);
  49. insertDocument.setName(originName);
  50. insertDocument.setType(type);
  51. insertDocument.setSize(size / 1024);
  52. insertDocument.setMd5(md5);
  53. documentMapper.insert(insertDocument);
  54. return url;
  55. }
  56. **************************************************************************
  57. 这里青戈处理了很久....我尼玛!!!!!!!!
  58. 而且青戈处理的不对....浪费良久后青戈处理对了...
  59. 还是谦虚点,还是有比我牛批的地方...
  60. 3】上传图像,这个name很重要的!!!!!!!!!!!
  61. <!--头像-->
  62. <el-upload
  63. name="multipartFile"
  64. class="avatar-uploader"
  65. action="http://localhost:8000/document/upload"
  66. :show-file-list="false"
  67. :on-success="onSuccess">
  68. <img v-if="userForm.avatar" :src="userForm.avatar" class="avatar">
  69. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  70. </el-upload>
  71. 4】处理保存后,左上角头像的更新问题(初步处理,没有一键触发!!!!)
  72. 子传父、父传子
  73. **************************************************************************Person.vue
  74. // 触发父级更新user方法
  75. this.$emit("person_fff_user", this.user)
  76. **************************************************************************Composite.vue
  77. data() {
  78. return {
  79. foldData: 'foldClass el-icon-s-fold',// 折叠类
  80. collapseFlag: false, // 是否折叠
  81. sideWidth: 200, // 左侧边栏宽度
  82. logoTextShowFlag: true,
  83. user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
  84. // 这个不取,第一次展示就是空了!!!!!!!!!
  85. }
  86. },
  87. methods: {
  88. /*子传父*/
  89. async person_fff_user(val) { //这里val就是person传过来的user
  90. const {data: res} = await this.$http.post("user/select", val) // 从后台获取数据
  91. // console.log(res.object)
  92. this.user = res.object
  93. },
  94. <Top :fff_top_fold-data="foldData"
  95. :fff_top_foldClick="foldClick"
  96. :fff_top_user="user"> <!--向person传递user-->
  97. </Top>
  98. **************************************************************************Top.vue
  99. <Top :fff_top_fold-data="foldData"
  100. :fff_top_foldClick="foldClick"
  101. :fff_top_user="user"> <!--向person传递user-->
  102. </Top>
  103. props: ['fff_top_foldData', 'fff_top_foldClick', 'fff_top_pathName', 'fff_top_user'],/*说明props还可以接受函数,我日尼玛哦*/
  104. <div style="display: inline-block;">
  105. <img :src="fff_top_user.avatar"
  106. style="width: 30px;border-radius: 10%;position: relative;top:5px;right: 5px;">
  107. <span>{{fff_top_user.name}}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
  108. </div>
  109. **************************************************************************
  110. Person----Composite---Top
  111. 子----------父------------子

****************************************************************************************************************************************************************************

  1. 18、整合Echarts
  2. 1】配置
  3. npm i echarts -S
  4. ************************************************************************
  5. import * as echarts from 'echarts'
  6. 折线图 柱状图 饼图
  7. ************************************************************************
  8. this.$http.post("/echarts/select").then(res => {
  9. // this.$http.post("/echarts/vip").then(res => {
  10. // console.log(res)
  11. // alert(JSON.stringify(res.data.object.x))
  12. // alert(JSON.stringify(res.data.object.y))
  13. option.xAxis.data = res.data.object.x;
  14. // alert(JSON.stringify(res))
  15. // option.xAxis.data = ["第一季度", "第二季度", "第三季度", "第四季度",]; // 演示从数据库查询的
  16. option.series[0].data = res.data.object.y;
  17. option && myChart.setOption(option);
  18. })
  19. 请求后台更改数据,主要是属性的更改,与后台json结合,加上option的位置要在更改后放置!!!
  20. 2】概览部分
  21. <!--头部-->
  22. <el-row :gutter="10">
  23. <el-col :span="6">
  24. <el-card style="color: #409eff">
  25. <div><i class="el-icon-user-solid"></i> 用户总数</div>
  26. <div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
  27. 100
  28. </div>
  29. </el-card>
  30. </el-col>
  31. <el-col :span="6">
  32. <el-card style="color: #67C23A">
  33. <div><i class="el-icon-money"></i> 销售总量</div>
  34. <div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
  35. 100
  36. </div>
  37. </el-card>
  38. </el-col>
  39. <el-col :span="6">
  40. <el-card style="color: #E6A23C">
  41. <div><i class="el-icon-bank-card"></i> 收益总额</div>
  42. <div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
  43. 100000,00
  44. </div>
  45. </el-card>
  46. </el-col>
  47. <el-col :span="6">
  48. <el-card style="color: #F56C6C">
  49. <div><i class="el-icon-s-shop"></i> 门店总数</div>
  50. <div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
  51. 20
  52. </div>
  53. </el-card>
  54. </el-col>
  55. </el-row>
  56. ************************************************************************
  57. option = {
  58. title: {
  59. left: 'center' // 标题属性
  60. },
  61. tooltip: { // 鼠标移入显示数据标签
  62. trigger: 'item'
  63. },
  64. legend: { // 显示说明左侧
  65. orient: 'vertical',
  66. left: 'left'
  67. },
  68. xAxis: {
  69. type: 'category',
  70. data: []
  71. },
  72. yAxis: {
  73. type: 'value'
  74. },
  75. series: [
  76. {
  77. name: 'member', // 和legend是绑定的
  78. data: [],
  79. type: 'line'
  80. }
  81. ]
  82. };

****************************************************************************************************************************************************************************

  1. 20、权限管理
  2. 1】Role
  3. 复制前端的Role,全局替换。修改下细节字段即可
  4. *****************************************************************************
  5. 复制后端的Role、RoleMapper.interface、RoleMapper.xml、RoleController
  6. *****************************************************************************
  7. 修改下细节字段即可
  8. 2】菜单分配页实现新功能 32分钟
  9. <!--树形控件-->
  10. <el-tree
  11. :data="menu.data"
  12. show-checkbox
  13. node-key="id"
  14. :default-expanded-keys="[2]"
  15. :default-checked-keys="[3]"
  16. @check-change="onCheckChange">
  17. </el-tree>
  18. *****************************************************************************
  19. 如果是PostMapping,用post必须指定body里一个{}空对象查询
  20. 如果要求登录注意header参数的token设置
  21. *****************************************************************************关键代码
  22. /*找到所有*/
  23. @PostMapping("/list_all")
  24. public Res list_all(@RequestBody Menu menu) {
  25. List<Menu> menuList = menuMapper.selectList(null); // 查询所有
  26. List<Menu> fatherMenuList = menuList.stream().filter(temp -> temp.getFather() == null).collect(Collectors.toList());// 找出father为null的一级菜单
  27. for (Menu item : fatherMenuList) { // 找出每一个一级菜单的子菜单,并设置给item(父菜单)
  28. List<Menu> childrenList = menuList.stream().filter(temp -> item.getId().equals(temp.getFather())).collect(Collectors.toList());
  29. item.setChildren(childrenList);
  30. }
  31. return Res.success(fatherMenuList);
  32. }
  33. 2】角色拥有的菜单,一个字段搞定的事情,青戈非要再用一个表+实体类....
  34. private String menuIds; // 拥有的菜单集合
  35. @TableField(exist = false)
  36. private List<Integer> menuIdList;
  37. *****************************************************************************这个问题处理
  38. 确实有点难度,就在于如果传参与获取的类型不一致,尽量前后端不要绑定,
  39. 绑定就很难处理,调用会报错400。分开后就不存在这个问题,特别是参与获取的类型不一致
  40. menuIdList: [], 前端需要的是数组!!!!!!!!!
  41. this.roleForm.menuIds ,后端需要的参数是字符串!!!!!!!!!!!!!!!!!!!
  42. 3】动态路由
  43. 给用户一个角色role
  44. *****************************************************************************
  45. role.name不允许重复设置。
  46. 索引-角色名称/name/UNIQUE/BTREE---后端捕获异常,返回给前端提示...
  47. *****************************************************************************多测试,空指针bug
  48. @PostMapping("/select")
  49. public Res select(@RequestBody Role role) {
  50. Role existRole = roleMapper.selectById(role.getId());
  51. // 处理成前端需要的格式menuIds ->menuIdList 提供前端需要的格式!!!!!!!!!!!!!!!!!!!
  52. List<Integer> menuIdList = new ArrayList<>();
  53. if (existRole.getMenuIds() != null) {
  54. String menuIds = existRole.getMenuIds().replace("[", "").replace("]", "");
  55. String[] tempArray = menuIds.split(",");
  56. for (String temp : tempArray) {
  57. menuIdList.add(Integer.parseInt(temp));
  58. }
  59. }
  60. List<Menu> menuList = menuMapper.selectBatchIds(menuIdList);
  61. existUser.setMenuList(menuList);// !!!!!!!!!都是为了这个
  62. return Res.success(existRole);// 需要返回对象
  63. }
  64. *****************************************************************************
  65. 我的方法节省了很大的功夫,同时也有很多细节BUG需要耐心排查。
  66. *****************************************************************************30分钟
  67. removeIf()等,我都不需要写这些东西,因为存的好,表建的好!!!!!!!!!
  68. *****************************************************************************都浓缩成这个方法了!!!!!!!
  69. @PostMapping("/login")
  70. public Res login(@RequestBody User user) {
  71. // 拿到用户输入的账号密码
  72. User existUser;
  73. try {
  74. existUser = userMapper.selectUserByNamePassword(user);
  75. } catch (Exception e) { // 如果系统中存在多条等异常情况
  76. e.printStackTrace();
  77. return Res.error(Constants.CODE_500, "系统错误");
  78. }
  79. if (existUser == null) {
  80. return Res.error(Constants.CODE_400, "用户名或密码错误");
  81. }
  82. // 设置token,保证existUser要用id与password
  83. String token = Token.productToken(existUser);
  84. existUser.setToken(token);
  85. existUser.setPassword("xxx"); // 拿到token后,保护用户密码
  86. Role role = new Role();
  87. role.setName(existUser.getRole());
  88. //根据用户角色,查到角色完整信息
  89. Role existRole = roleMapper.selectRoleByName(role);
  90. //根据角色的菜单,生成菜单List,设置到existUser中
  91. List<Integer> menuIdList = new ArrayList<>();
  92. if (existRole.getMenuIds() != null) { // 存在菜单
  93. String menuIds = existRole.getMenuIds().replace("[", "").replace("]", "");
  94. String[] tempArray = menuIds.split(",");
  95. for (String temp : tempArray) {
  96. menuIdList.add(Integer.parseInt(temp));
  97. }
  98. }
  99. // 不存在菜单,设置为空
  100. List<Menu> menuList = menuMapper.selectBatchIds(menuIdList);
  101. List<Menu> fatherMenuList = menuList.stream().filter(temp -> temp.getFather() == null).collect(Collectors.toList());// 找出father为null的一级菜单
  102. for (Menu item : fatherMenuList) { // 找出每一个一级菜单的子菜单,并设置给item(父菜单)
  103. List<Menu> childrenList = menuList.stream().filter(temp -> item.getId().equals(temp.getFather())).collect(Collectors.toList());
  104. item.setChildren(childrenList);
  105. }
  106. existUser.setMenuList(fatherMenuList);// !!!!!!!!!都是为了这个
  107. return Res.success(existUser);// 这里返回existUser 是因为虽然用户输入的是名+密码,但是实际拿到还有图标等信息
  108. }
  109. *****************************************************************************实现动态菜单
  110. 确实有省功夫的地方,但是青戈的这个思路还是很值得学习的!!!!!!
  111. <div v-for="item in menuList" :key="item.id">
  112. <!--有path一级菜单,全靠login Controller返回的父子关系数据-->
  113. <div v-if="item.path">
  114. <el-menu-item :index="item.path">
  115. <i :class="item.icon"></i><!--这里如果不放在上面,收缩时会看不到-->
  116. <template slot="title">
  117. <span>{{item.name}}</span>
  118. </template>
  119. </el-menu-item>
  120. </div>
  121. <!--二级菜单-->
  122. <div v-else>
  123. <el-submenu :index="item.id+''">
  124. <template slot="title">
  125. <i :class="item.icon"></i>
  126. <span>{{item.name}}</span>
  127. </template>
  128. <div v-for="subItem in item.children" :key="subItem.id">
  129. <el-menu-item :index="subItem.path">
  130. <i :class="subItem.icon"></i>
  131. <span>{{subItem.name}}</span>
  132. </el-menu-item>
  133. </div>
  134. </el-submenu>
  135. </div>
  136. </div>
  137. *****************************************************************************
  138. <el-menu :default-openeds="openList"
  139. background-color="rgb(48,65,86)"
  140. text-color="white"
  141. active-text-color="yellow"
  142. :collapse-transition="false"
  143. :collapse="fff_left_collapseFlag"
  144. router>
  145. *****************************************************************************
  146. openList: localStorage.getItem("menuList") ? JSON.parse(localStorage.getItem("menuList")).map(v => v.id + '') : []
  147. 4】动态路由终版起飞
  148. export const setRoutes = () => {
  149. const menuList = JSON.parse(localStorage.getItem("menuList"));
  150. if (menuList) {
  151. const finalRoute = { // 动态的添加进去
  152. path: '/', component: Composite,
  153. redirect: "/login",
  154. children: []
  155. }
  156. menuList.forEach(item => {
  157. if (item.path) { // 一级路由
  158. let menu = {
  159. path: item.path.replace("/", ""),
  160. name: item.name,
  161. component: () => import('../views/' + item.vue + '.vue')
  162. }
  163. finalRoute.children.push(menu)
  164. } else if (item.children.length) {
  165. item.children.forEach(subItem => { // 二级路由
  166. if (subItem.path) {
  167. let subMenu = {
  168. path: subItem.path.replace("/", ""),
  169. name: subItem.name,
  170. component: () => import('../views/' + subItem.vue + '.vue')
  171. }
  172. finalRoute.children.push(subMenu)
  173. }
  174. })
  175. }
  176. })
  177. // 动态添加到现有路由
  178. router.addRoute(finalRoute)
  179. }
  180. }
  181. *****************************************************************************Login.vue
  182. import {setRoutes} from '../router/router.js'
  183. setRoutes() // 动态设置当前用户路由
  184. *****************************************************************************处理其他细节问题
  185. router.js里调下自己!!!!!!!!!!!!!!!
  186. setRoutes();// 刷新页面的时候刷新路由
  187. *****************************************************************************报错重复路由...
  188. vue-router.esm.js?3423:16 [vue-router] Duplicate named routes definition: { name: "主页", path: "/home" }
  189. *****************************************************************************解决,没有的时候才添加
  190. // 动态添加到现有路由
  191. const routeNameList = router.getRoutes().map(v => v.name)
  192. if (!routeNameList.includes('composite')) {
  193. router.addRoute(finalRoute)
  194. }
  195. *****************************************************************************访问其他页面提示404
  196. {path: '*', name: '404', component: NotFound},
  197. *****************************************************************************
  198. <template>
  199. <div style="text-align: center;margin-top: 300px;">
  200. <h1>404 NotFound 请您检查路由地址是否正确</h1>
  201. <br>
  202. <h2>您也可以联系微信:15921291928</h2>
  203. </div>
  204. </template>
  205. <script>
  206. export default {
  207. name: "NotFound"
  208. }
  209. </script>
  210. <style scoped>
  211. </style>
  212. *****************************************************************************自己的逻辑又解决了一个问题!!!!
  213. //后台处理下,选择主页1 选择用户管理3,但是没传过来2系统管理的问题。就是记录了子菜单,但是没有父菜单,动态路由的时候有问题
  214. List<Integer> tempList = new ArrayList<>(); // 用另一个容器存储,防止遍历时出现问题
  215. for (int i = 0; i < menuIdList.size(); i++) {
  216. tempList.add(menuIdList.get(i));
  217. }
  218. for (Integer menuId : menuIdList) {
  219. Menu menu = menuMapper.selectById(menuId);
  220. if (menu.getFather() != null && !tempList.contains(menu.getFather())) {// 这个就是有孩子,但是集合id里面没有对应父级id
  221. tempList.add(menu.getFather()); // 临时容器存储
  222. }
  223. }
  224. // 不存在菜单,设置为空
  225. List<Menu> menuList = menuMapper.selectBatchIds(tempList);
  226. List<Menu> fatherMenuList = menuList.stream().filter(temp -> temp.getFather() == null).collect(Collectors.toList());// 找出father为null的一级菜单
  227. for (Menu item : fatherMenuList) { // 找出每一个一级菜单的子菜单,并设置给item(父菜单)
  228. List<Menu> childrenList = menuList.stream().filter(temp -> item.getId().equals(temp.getFather())).collect(Collectors.toList());
  229. item.setChildren(childrenList);
  230. }
  231. existUser.setMenuList(fatherMenuList);// !!!!!!!!!都是为了这个
  232. return Res.success(existUser);// 这里返回existUser 是因为虽然用户输入的是名+密码,但是实际拿到还有图标等信息
  233. *****************************************************************************分配完毕的重新登录
  234. setRoutes()// 动态设置当前用户路由
  235. localStorage.setItem("user", JSON.stringify(res.data.object)) // 存储用户信息到浏览器
  236. localStorage.setItem("menuList", JSON.stringify(res.data.object.menuList)) // 存储菜单信息
  237. *****************************************************************************
  238. if (!localStorage.getItem("user")) { // 首次登录必须刷新路由,否则存在权限不对应问题
  239. router.addRoute(finalRoute)
  240. }
  241. *****************************************************************************这个可以处理,也有弊端
  242. created() {
  243. if (window.location.href.indexOf("#reloaded") == -1) {
  244. window.location.href = window.location.href + "#reloaded";
  245. window.location.reload();
  246. }
  247. },
  248. *****************************************************************************全局搜索
  249. Ctrl + Shift + R
  250. myVuex.commit("userLogout") 全局退出登录
  251. *****************************************************************************操作完管理员角色需要重新登录
  252. giveMenu(row) {
  253. this.roleForm.id = row.id; // 为了分配菜单用的
  254. this.roleForm.name = row.name
  255. *****************************************************************************
  256. if (this.$refs.tree !== undefined && this.roleForm.id !== undefined) {
  257. if (this.roleForm.name === "管理员") {
  258. myVuex.commit("userLogout")
  259. }
  260. }
  261. *****************************************************************************
  262. 父级菜单的处理,还真不能存到数据库。就后台动态返回就完事了...
  263. 5】解决一些系统BUG
  264. 访问http://localhost:8080/ 提示404问题解决
  265. *****************************************************************************404与登录页面处理
  266. if (to.matched.length === 0) { // 未找到路由的情况
  267. const storeMenuList = localStorage.getItem("menuList")
  268. if (storeMenuList) {
  269. next("/404")
  270. } else {
  271. next("/login") // 如果没有菜单信息跳转到登录页面
  272. }
  273. }
  274. next() // 放行
  275. *****************************************************************************牛批
  276. 网友水友...发现了我发现的问题,青戈说的好像也对
  277. 知识的学习,重要的在于运用,我日TM
  278. 404BUG的处理。根源还是没有添加路由,还是上次的路由。如何处理呢?
  279. 重置路由就解决了
  280. // 重置路由的方法
  281. export const clearRouter = () => {
  282. router.matcher = new VueRouter({
  283. mode: 'history',
  284. base: process.env.BASE_URL,
  285. routes
  286. })
  287. }
  288. *****************************************************************************
  289. userLogout(state) { // 全局退出方法
  290. // 清空缓存
  291. localStorage.removeItem("user")
  292. localStorage.removeItem("menuList")
  293. router.push("/login")
  294. // 重置清空路由
  295. clearRouter()
  296. }
  297. *****************************************************************************
  298. 终于终于搞定了 牛批的版本

****************************************************************************************************************************************************************************

  1. 24、服务器部署
  2. 1】自己动手解决,系统菜单收缩后仍展示问题
  3. 买阿里云、开端口号
  4. ******************************************************************************
  5. firewall-cmd --zone=public --list-ports #查看历史开启端口
  6. systemctl status firewalld #查看防火墙状态
  7. systemctl start firewalld #开启防火墙
  8. firewall-cmd --zone=public --add-port=端口号/tcp --permanent #开启新的端口号
  9. firewall-cmd --reload #重启防火墙
  10. iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 端口号 #将80映射到某个端口
  11. firewall-cmd --zone=public --remove-port=端口号/tcp --permanent # 关闭端口号
  12. 2】nginx的配置
  13. #把默认的80端口改成8002端口。代理dist目录/root/site/white/dist
  14. cd /usr/local/nginx/conf
  15. vim nginx.conf
  16. ******************************************************************************
  17. cd /usr/local/nginx/sbin
  18. ./nginx
  19. ps -ef | grep nginx
  20. ******************************************************************************
  21. netstat -lnp|grep 8002
  22. kill -9 21458
  23. ******************************************************************************
  24. 访问http://wdfgdzx.top:8002/
  25. ******************************************************************************代理如下目录
  26. location / {
  27. root /root/site/white/dist;
  28. index index.html index.htm;
  29. try_files $uri $uri/ /index.html;
  30. }
  31. ******************************************************************************重启
  32. cd /usr/local/nginx/sbin
  33. ./nginx -s reload
  34. 3】配置动态whiteIp blackIp地址 25-36分钟
  35. // 线上前端IP
  36. export const whiteIp = "wdfgdzx.top:8000"
  37. ******************************************************************************前端
  38. localhost.js white.js
  39. ******************************************************************************后端
  40. localhost.properties black.properties
  41. 4】使用的技术
  42. 前端:VUE2 Vue-Router Vuex ElementUI Axios
  43. 后端:SpringBoot Hutool Poi Lombok MybatisPlus

****************************************************************************************************************************************************************************

  1. 261V11对多、多V多查询
  2. 1】以老师、学生教学为示例
  3. *****************************************************************************41
  4. 一个表四个复制
  5. 2】主要是数据库的查询练习,看着学习吧。1V1查询
  6. *****************************************************************************
  7. <!--1.2查询分页数据-->
  8. *****************************************************************************每次select的时候
  9. 用了left join
  10. 用teacher_id去left join user.id,从而获得user.nick as 当做teacherName去使用。在前端展示
  11. !!!!!!!!!!!!!!!牛批,悟透了!!!!!!! 哈哈哈哈哈
  12. *****************************************************************************
  13. 从科目到讲课老师:是1V1
  14. 从讲课老师到多个科目:是1V多 老师是1 课程是多
  15. 3】老师查看自己教授的课程。1V多
  16. 4】学生选课 多V多
  17. <!--设置学生选课信息,先删除后设置-->

****************************************************************************************************************************************************************************

  1. 32、个人中心、修改头像、数据联动/修改密码
  2. 1】个人中心还需要讲吗?.............
  3. 一个个人中心讲解了30分钟....
  4. 2】修改密码,autocomplete="new-password"这个好用,不会自动填充密码
  5. <el-form-item label="用户名">
  6. <el-input v-model="user.name" disabled autocomplete="off"></el-input>
  7. </el-form-item>
  8. <el-form-item label="新密码" prop="newPassword">
  9. <el-input v-model="passwordForm.newPassword" autocomplete="new-password" show-password></el-input>
  10. </el-form-item>
  11. <el-form-item label="确认新密码" prop="confirmPassword">
  12. <el-input v-model="passwordForm.confirmPassword" autocomplete="new-password"
  13. show-password></el-input>
  14. </el-form-item>

****************************************************************************************************************************************************************************

  1. 33、集成wangeditor
  2. 1】安装
  3. npm i wangeditor -S --force
  4. ********************************************************************************
  5. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  6. private Date recordTime;
  7. private String modifyPerson;
  8. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  9. private Date modifyTime;
  10. ********************************************************************************
  11. Ctrl+R 区分大小写后,批量替换,直接起飞!!!!!!!!!!!!!
  12. ********************************************************************************
  13. Error in v-on handler: "Error: 无效的节点选择器:content"
  14. nextTick解决这个问题
  15. ********************************************************************************
  16. // 新增的时候开始操作wangEditor
  17. this.$nextTick(() => {
  18. const editor = new E('#content')
  19. editor.create()
  20. })
  21. ********************************************************************************23
  22. 对于wangEditor的打开窗口显示,我解决了一个历史性难题,牛批!!!!!!!!!!!
  23. ********************************************************************************
  24. @PostMapping("/editor")
  25. public JSON editor(MultipartFile multipartFile) throws IOException { // 这里是三方WangEditor发起的请求,所以不能用包装对象,emmm!!!!!!
  26. String originName = multipartFile.getOriginalFilename();
  27. String type = FileUtil.extName(originName);
  28. long size = multipartFile.getSize();
  29. // 1、存到磁盘、存储数据库
  30. File uploadDir = new File(MyUtils.getFinalPath());
  31. System.err.println(uploadDir);
  32. if (!uploadDir.exists()) {
  33. System.out.println(uploadDir.mkdir()); //不存在创建新目录
  34. }
  35. // 2、最终文件路径
  36. String fileUUID = IdUtil.fastSimpleUUID() + "." + type;
  37. String url = blackIp + "/document/" + fileUUID;
  38. File uploadFile = new File(uploadDir + "/" + fileUUID);
  39. //3、获取md5
  40. if (!uploadFile.exists()) {
  41. multipartFile.transferTo(uploadFile); // 这时候才存磁盘!!!!!
  42. }
  43. String md5 = SecureUtil.md5(uploadFile);
  44. Document document = new Document();
  45. // 4、判断数据库里md5是否存在
  46. document.setMd5(md5);
  47. List<Document> documentList = documentMapper.md5_list(document); // 数据库查询到相同md5的对象
  48. System.out.println(documentList);
  49. if (documentList.size() != 0) {
  50. url = documentList.get(0).getUrl(); // 存在的话,url直接从已存在数据库信息拿到
  51. System.out.println(uploadFile.delete());// 删除本地存储的文件
  52. }
  53. // 5、存数据库
  54. Document insertDocument = new Document();
  55. insertDocument.setUrl(url);
  56. insertDocument.setName(originName);
  57. insertDocument.setType(type);
  58. insertDocument.setSize(size / 1024);
  59. insertDocument.setMd5(md5);
  60. documentMapper.insert(insertDocument);
  61. JSONObject jsonObject = new JSONObject();
  62. // 第一个设置
  63. jsonObject.set("errno", 0);
  64. JSONObject data = new JSONObject();
  65. data.set("url", url);
  66. JSONArray jsonArray = new JSONArray();
  67. jsonArray.add(data);
  68. // 第二个设置
  69. jsonObject.set("data", jsonArray);
  70. return jsonObject;
  71. }

****************************************************************************************************************************************************************************

  1. 19、前台页面
  2. 1】先跟着老师实现吧,整合后面自己思考下!!!!
  3. 还是很多收获的...
  4. *************************************************************************************
  5. 不管是否前台后台一起写,组件名最好不要重名,懂吧!!!!!!!!!!!!
  6. // 配置前台页面路由
  7. {
  8. path: '/white',
  9. name: 'White',
  10. component: White, // 这是综合管理页面,访问每个子页面的时候它都会展示
  11. children: [
  12. {path: '/main', name: 'Main', component: Main},
  13. ]
  14. },
  15. *************************************************************************************
  16. 注意在这里加了判断:控制路由跳转
  17. if (res.data.object.role === "学生") {
  18. this.$router.push("/main") // 跳转前台首页
  19. } else { // 跳到后台首页
  20. this.$router.push("/home")
  21. this.$message.success("登录成功,欢迎您")
  22. }
  23. *************************************************************************************
  24. 登录什么的可以复用原有的管理端....
  25. *************************************************************************************
  26. <el-menu :default-active="'1'" class="el-menu-demo" mode="horizontal" router>
  27. 这个router让我排查了一会!!!!!!!!!!!!!!!!!!!!!!!!!!
  28. 最后记得带router,日尼玛
  29. *************************************************************************************
  30. 青戈真是哥,1小时的视频,我学习捉摸了很久才get到精髓所在。
  31. *************************************************************************************
  32. 个人信息页面、修改密码加回来 emmm,卧槽了!!!!!!!!!!!

****************************************************************************************************************************************************************************

  1. 14、实现Redis缓存效果
  2. 1】缓存数据,让用户体验更好,速度更快,避免对数据库的多次请求。
  3. spring自带的也有cache缓存,但是我们重点学习redis
  4. ****************************************************************************************
  5. 单机测试:发现,没有缓存,接口会频繁请求数据库,会有打满风险。
  6. 2】主角:Springboot集成Redis
  7. Redis的学习安装什么的自己在学习一遍吧!!!!!!!!!!!!!!
  8. <!--Redis-->
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-data-redis</artifactId>
  12. </dependency>
  13. ****************************************************************************************
  14. // 刷新Redis 其实是直接清空!!!!!!!!!!!!
  15. // 如果用到了,在增删改的时候都需要调用下flushRedis(Constants.LIST_ALL_KEY);
  16. private void flushRedis(String key) {
  17. stringRedisTemplate.delete(key);
  18. }
  19. ****************************************************************************************
  20. 实现了减轻数据库压力的作用。
  21. 当然这样比较粗放,可以更精细化,比如删除那一条,重新从数据库查询,重新set
  22. 感觉也差不多!!!!!!!!!!!!!!!!!!
  23. ****************************************************************************************
  24. 更优的方法,是去除之前的缓存json--->变成List--->操作List的增删改(和数据库保持一致的
  25. 的同时,不用二次查询数据库...)!!!!!!!!!!!!!!!!!!!
  26. --->NoSQL???好像也还行......

****************************************************************************************************************************************************************************

  1. 20、视频播放
  2. 1】先实现后台文件的预览功能,配置使用kkfileview(不好用 算了)
  3. 写个Video页面,然后再写Detail页面
  4. *************************************************************************************
  5. npm i vue-video-player -S --force
  6. *************************************************************************************
  7. npm i video.js -S --force
  8. *************************************************************************************
  9. <template>
  10. <div class="Detail-container" style="margin-top: 5px;">
  11. <div id="player">
  12. <video-player class="video-player vjs-big-play-centered"
  13. ref="videoPlayer"
  14. :playsinline="false"
  15. :options="playerOptions">
  16. </video-player>
  17. </div>
  18. </div>
  19. </template>
  20. <script>
  21. // 在组件中引入
  22. import {videoPlayer} from 'vue-video-player'
  23. import 'vue-video-player/src/custom-theme.css'
  24. import 'video.js/dist/video-js.css'
  25. export default {
  26. name: "Detail",
  27. // 注册
  28. components: {videoPlayer},
  29. data() {
  30. return {
  31. // video: {},
  32. // 视频控制设置
  33. playerOptions: {
  34. playbackRates: [0.5, 1.0, 1.5, 2.0], // 可选的播放速度
  35. autoplay: false, // 如果为true,浏览器准备好时开始回放。
  36. muted: false, // 默认情况下将会消除任何音频。
  37. loop: false, // 是否视频一结束就重新开始。
  38. preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
  39. language: "zh-CN",
  40. aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9""4:3"
  41. fluid: true, //true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
  42. sources: [
  43. {
  44. type: "video/mp4", // 类型
  45. src: "", // url地址
  46. },
  47. ],
  48. poster: "", // 封面地址
  49. notSupportedMessage: "此视频暂无法播放,请稍后再试", // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
  50. controlBar: {
  51. timeDivider: true, // 当前时间和持续时间的分隔符
  52. durationDisplay: true, // 显示持续时间
  53. remainingTimeDisplay: true, // 是否显示剩余时间功能
  54. fullscreenToggle: true, // 是否显示全屏按钮
  55. }
  56. }
  57. // -------
  58. }
  59. },
  60. created() {
  61. let id = this.$route.query.id // 从上个路径拿到的id
  62. this.$http.post("/document/select_id", {id: id}).then(res => {
  63. // console.log(res)
  64. if (res.data.code = "200") {
  65. this.video = res.data.object;
  66. this.playerOptions.sources[0].src = res.data.object.url // 赋值视频地址
  67. } else {
  68. this.$message.error(res.data.code)
  69. }
  70. })
  71. }
  72. }
  73. </script>
  74. <style scoped lang="less">
  75. </style>

****************************************************************************************************************************************************************************

  1. 21、多级评论
  2. 1】新建novel表和写对应的后端代码!!!!!!!!!!!
  3. 2】处理前端和安装相关插件
  4. npm i mavon-editor@2.10.4 -S --force
  5. ***************************************************************************************
  6. import mavonEditor from 'mavon-editor'
  7. import 'mavon-editor/dist/css/index.css'
  8. Vue.use(mavonEditor) // 挂载
  9. ***************************************************************************************
  10. import axios from "axios";
  11. ***************************************************************************************
  12. imgAdd(pos, $file) {
  13. let $vm = this.$refs.md
  14. // 第一步.将图片上传到服务器.
  15. const formData = new FormData();
  16. formData.append('file', $file);
  17. axios({
  18. url: `http://${whiteIp}/document/upload`, // 这是自己后台的接口
  19. method: 'post',
  20. data: formData,
  21. headers: {'Content-Type': 'multipart/form-data'},
  22. }).then((res) => {
  23. // 第二步.将返回的url替换到文本原位置![...](./0) -> ![...](url)
  24. /**
  25. * $vm 指为mavonEditor实例,可以通过如下两种方式获取
  26. * 1. 通过引入对象获取: `import {mavonEditor} from ...` 等方式引入后,
  27. * `$vm`为`mavonEditor`
  28. * 2. 通过$refs获取: html声明ref : `<mavon-editor ref=md ></mavon-editor>
  29. * `$vm`为 `this.$refs.md`
  30. */
  31. $vm.$img2Url(pos, res.data);
  32. })
  33. },
  34. ***************************************************************************************
  35. 处理小说内容查看的方式 30分钟
  36. 3】最最中重点:小说评论功能的实现
  37. @PostMapping("/select_tree_by_novel_id/{novelId}")
  38. public Res select_tree_by_novel_id(@PathVariable Integer novelId) {
  39. List<Discuss> discussList = discussMapper.selectListByNovelId(novelId);
  40. List<Discuss> rootList = discussList.stream().filter(discuss -> discuss.getRootId() == null).collect(Collectors.toList()); // 所有没有父级的
  41. for (Discuss discuss : rootList) {
  42. List<Discuss> childrenList = discussList.stream().filter(temp -> temp.getRootId() != null && temp.getRootId().equals(discuss.getId())).collect(Collectors.toList());
  43. // 设置父级评论的用户昵称+用户ID
  44. childrenList.forEach(children -> {
  45. Discuss tempDisCuss = discussMapper.selectById(children.getFatherId()); // 根据父id查到对应的评论!!!!注意父id和根id的区别!!!!!!!!
  46. User tempUser = userMapper.selectById(tempDisCuss.getUserId()); // 用用户ID查询到用户
  47. // 设置父级用户ID+昵称
  48. children.setFatherUserId(tempUser.getId());
  49. children.setFatherUserNick(tempUser.getNick());
  50. });
  51. discuss.setDiscussChildrenList(childrenList);
  52. }
  53. return Res.success(rootList); // 注意返回的是rootList!!!!!!!!!!!!!!!!
  54. }
  55. ***************************************************************************************
  56. @PostMapping("/insertOrUpdate")
  57. public Res insertOrUpdate(@RequestBody Discuss discuss) { // @RequestBody很重要
  58. if (discuss.getId() != null) {
  59. discussMapper.updateById(discuss);
  60. } else {
  61. if (discuss.getFatherId() != null) {
  62. Discuss fatherDiscuss = discussMapper.selectById(discuss.getFatherId()); // 找到父节点
  63. if (fatherDiscuss.getFatherId() != null) { // 说明父节点,也有父节点,这时候要操作了!!!!!!!!!!!!
  64. discuss.setRootId(fatherDiscuss.getRootId()); // 设置和父节点同样的祖宗ID!!!!!!!!!!!!!!!!!
  65. // 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
  66. // 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
  67. // 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
  68. // 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
  69. // 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
  70. // 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
  71. }
  72. }
  73. discussMapper.insert(discuss);
  74. }
  75. return Res.success(null);
  76. }

****************************************************************************************************************************************************************************

  1. 22、支付宝
  2. 1】生成公钥私钥的工具
  3. https://opendocs.alipay.com/common/02kipk?pathHash=0d20b438
  4. 2】沙箱环境
  5. https://openhome.alipay.com/develop/sandbox/app
  6. 27分钟,orderMapper内容看不到!!!!!!!!!!!!!!!
  7. 3】图书管理、订单管理页面...25分钟
  8. package com.black.controller;
  9. import com.alipay.easysdk.factory.Factory;
  10. import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
  11. import com.black.mapper.OrdersMapper;
  12. import com.black.pojo.Alipay;
  13. import org.springframework.web.bind.annotation.*;
  14. import javax.annotation.Resource;
  15. import javax.servlet.http.HttpServletRequest;
  16. import java.net.URLEncoder;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. @RestController
  20. @RequestMapping("/alipay")
  21. public class AlipayController {
  22. @Resource
  23. private OrdersMapper ordersMapper;
  24. @GetMapping("/pay")
  25. public String pay(Alipay aliPay) {
  26. AlipayTradePagePayResponse alipayTradePagePayResponse = null;
  27. try {
  28. // 发起API调用请求,以创建首付款二维码为例
  29. alipayTradePagePayResponse = Factory.Payment.Page().pay(aliPay.getSubject(),
  30. aliPay.getTraceNo(),
  31. aliPay.getTotalAmount(),
  32. "");
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. return alipayTradePagePayResponse.getBody();
  37. }
  38. @PostMapping("/notify")
  39. public String notify(HttpServletRequest httpServletRequest) throws Exception {
  40. if (httpServletRequest.getParameter("trade_status").equals("TRADE_SUCCESS")) {
  41. System.out.println("---支付宝异步回调---");
  42. Map<String, String> paramsMap = new HashMap<>();
  43. Map<String, String[]> requestParamsMap = httpServletRequest.getParameterMap();
  44. for (String name : requestParamsMap.keySet()) {
  45. paramsMap.put(name, httpServletRequest.getParameter(name));
  46. }
  47. String tradeNo = paramsMap.get("out_trade_no");
  48. String gmtPayment = paramsMap.get("gmt_payment");
  49. String alipayNo = paramsMap.get("trade_no");
  50. // 支付宝验签
  51. if (Factory.Payment.Common().verifyNotify(paramsMap)) {
  52. System.out.println("交易名称" + paramsMap.get("subject"));
  53. System.out.println("交易状态" + paramsMap.get("trade_status"));
  54. System.out.println("支付宝交易凭证" + paramsMap.get("trade_no"));
  55. System.out.println("商户订单号" + paramsMap.get("out_trade_no"));
  56. System.out.println("交易金额" + paramsMap.get("total_amount"));
  57. System.out.println("买家在支付宝唯一id" + paramsMap.get("buyer_id"));
  58. System.out.println("买家付款时间" + paramsMap.get("gmt_payment"));
  59. System.out.println("买家付款金额" + paramsMap.get("gmt_pay_amount"));
  60. // 更新订单为已支付
  61. ordersMapper.updateState(tradeNo, "已支付", gmtPayment, alipayNo);
  62. }
  63. }
  64. return "success";
  65. }
  66. }
  67. ************************************************************************************
  68. <update id="updateState">
  69. update orders
  70. set state=#{state},
  71. payment_time=#{gmtPayment},
  72. alipay_no=#{alipayNo}
  73. where no =#{tradeNo}
  74. </update>
  75. 4】退款功能
  76. <!--支付宝-->
  77. <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk -->
  78. <dependency>
  79. <groupId>com.alipay.sdk</groupId>
  80. <artifactId>alipay-sdk-java</artifactId>
  81. <version>4.22.110.ALL</version>
  82. </dependency>
  83. ************************************************************************************
  84. 用这个包
  85. @GetMapping("/return_pay")
  86. public Res return_pay(Alipay alipay) throws Exception {
  87. // 7天无理由退款
  88. String now = DateUtil.now();
  89. Orders orders = ordersMapper.getByNo(alipay.getTraceNo());
  90. if (orders != null) {
  91. // hutool工具类,判断时间间隔
  92. long between = DateUtil.between(DateUtil.parseDateTime(orders.getPaymentTime()), DateUtil.parseDateTime(now), DateUnit.DAY);
  93. if (between > 7) {
  94. return Res.error("-1", "该订单已超过7天,不支持退款");
  95. }
  96. }
  97. // 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
  98. AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL,
  99. alipayConfig.getAppId(), alipayConfig.getAppPrivateKey(), FORMAT, CHARSET,
  100. alipayConfig.getAlipayPublicKey(), SIGN_TYPE);
  101. // 2. 创建 Request,设置参数
  102. AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
  103. JSONObject bizContent = new JSONObject();
  104. bizContent.set("trade_no", alipay.getAlipayTraceNo()); // 支付宝回调的订单流水号
  105. bizContent.set("refund_amount", alipay.getTotalAmount()); // 订单的总金额
  106. bizContent.set("out_request_no", alipay.getTraceNo()); // 我的订单编号
  107. request.setBizContent(bizContent.toString());
  108. // 3. 执行请求
  109. AlipayTradeRefundResponse response = alipayClient.execute(request);
  110. if (response.isSuccess()) { // 退款成功,isSuccess 为true
  111. System.out.println("调用成功");
  112. // 4. 更新数据库状态
  113. ordersMapper.updatePayState(alipay.getTraceNo(), "已退款", now);
  114. return Res.success();
  115. } else { // 退款失败,isSuccess 为false
  116. System.out.println(response.getBody());
  117. return Res.error(response.getCode(), response.getBody());
  118. }
  119. }

****************************************************************************************************************************************************************************

  1. 23、购物车 1小时10分钟
  2. 1】商品
  3. 2】购物车
  4. 3】订单中心

****************************************************************************************************************************************************************************

24、在线考试

****************************************************************************************************************************************************************************

·

****************************************************************************************************************************************************************************

26、聊天室
  1. 1】两个浏览器,两个账号!!!
  2. WsConfig
  3. WsUtil
  4. ***************************************************************************************
  5. 设置拦截器:
  6. public void addInterceptors(InterceptorRegistry interceptorRegistry) {
  7. interceptorRegistry.addInterceptor(jwtInterceptor()) // 调用@Bean返回的
  8. .addPathPatterns("/**")// 拦截的所有请求,判断token是否合法来决定是否需要登录。
  9. .excludePathPatterns(
  10. "/big/login",
  11. "/big/register",
  12. "/document/**",
  13. "/**/list_export",
  14. "/**/list_import",
  15. "/swagger**/**",
  16. "/webjars/**",
  17. "/v2/**",
  18. "/doc.html",
  19. "/alipay/**",
  20. "ws_server/**"); // 需要配置4个swagger放行
  21. }
  22. ***************************************************************************************
  23. 学习之,改进之!!!!!!!!!!!!!!!!!!
  24. <template>
  25. <div class="Chat-container" style="padding: 10px;margin-bottom:50px; ">
  26. <el-row>
  27. <!--在线人数区域-->
  28. <el-col :span="4">
  29. <el-card style="width: 300px;height: 300px;color: #333333">
  30. <div style="padding-bottom: 10px;border-bottom: 1px solid #cccccc">
  31. 在线用户<span style="font-size: 12px;">(点击聊天气泡开始聊天)</span>
  32. </div>
  33. <div style="padding: 10px 0;" v-for="item in userList" :key="item.name">
  34. <span>{{item.name}}</span>
  35. <i class="el-icon-chat-dot-round" style="margin-left: 10px;font-size: 16px;cursor: pointer"
  36. @click="clickChatUser(item)"></i>
  37. <span style="font-size: 12px;color: limegreen;margin-left: 5px;" v-if="item.name===chatUser">chatting...</span>
  38. </div>
  39. </el-card>
  40. </el-col>
  41. <!--聊天窗口-->
  42. <el-col :span="20">
  43. <div style="width: 800px;margin: 0 auto;background-color: white;border-radius: 5px;box-shadow: 0 0 10px #cccccc">
  44. <div style="text-align: center;line-height: 50px;">
  45. WsWeb聊天室<span style="color: limegreen" v-if="chatUser">(与{{chatUser}}聊天中...)</span>
  46. </div>
  47. <div style="height: 350px;overflow: auto;border-top:1px solid #ccc" v-html="content"></div>
  48. <div style="height: 200px;">
  49. <textarea v-model="text"
  50. style="height: 160px;width: 100%;padding: 20px; border: none;border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;outline: none">
  51. </textarea>
  52. <div style="text-align: right;padding-right: 10px">
  53. <el-button type="primary" size="mini" @click="send">发送</el-button>
  54. </div>
  55. </div>
  56. </div>
  57. </el-col>
  58. </el-row>
  59. </div>
  60. </template>
  61. <script>
  62. import {whiteIp} from "../../public/config";
  63. let socket;
  64. export default {
  65. name: "Chat",
  66. data() {
  67. return {
  68. user: {},
  69. userList: [],
  70. chatUser: '',
  71. chatUserAvatar: '',
  72. text: '',
  73. messageList: [],
  74. content: ''
  75. }
  76. },
  77. created() {
  78. this.wsInit()
  79. },
  80. methods: {
  81. clickChatUser(item) {
  82. this.chatUser = item.name;
  83. this.$http.post("/user/select", item).then(res => {
  84. if (res.data.code = "200") {
  85. // console.log(res)
  86. this.chatUserAvatar = res.data.object.avatar;
  87. }
  88. })
  89. },
  90. wsInit() {
  91. this.user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {} // 获取本地存储用户
  92. let userName = this.user.name
  93. let _this = this;
  94. if (typeof (WebSocket) == 'undefined') {
  95. console.log('您的浏览器不支持ws...')
  96. } else {
  97. console.log('您的浏览器支持ws!!!')
  98. let socketUrl = "ws://" + whiteIp + "/ws_server/" + userName
  99. if (socket != null) {
  100. socket.close()
  101. socket = null
  102. }
  103. socket = new WebSocket(socketUrl);
  104. // ws的几个事件,在vue中定义
  105. socket.onopen = function () {
  106. console.log('ws已经打开...')
  107. }
  108. socket.onmessage = function (message) {
  109. console.log('收到数据===' + message.data)
  110. let data = JSON.parse(message.data)
  111. if (data.userList) {
  112. _this.userList = data.userList.filter(item => item.name !== userName)
  113. } else {
  114. // if (data.from === _this.chatUser)
  115. if (data.from) {
  116. // console.log(data.from)
  117. _this.chatUser = data.from;
  118. _this.$http.post("/user/select", {name: data.from}).then(res => {
  119. if (res.data.code = "200") {
  120. // console.log(res)
  121. _this.chatUserAvatar = res.data.object.avatar;
  122. _this.messageList.push(data)
  123. // 构建发送的消息
  124. _this.createContent(data.from, null, data.text)
  125. }
  126. })
  127. }
  128. }
  129. }
  130. // 关闭事件
  131. socket.onclose = function () {
  132. console.log('ws已关闭...')
  133. }
  134. socket.onerror = function () {
  135. console.log('发生错误...')
  136. }
  137. }
  138. },
  139. send() {
  140. if (!this.chatUser) {
  141. this.$message({type: 'warning', message: "请选择聊天对象"})
  142. return;
  143. }
  144. if (!this.text) {
  145. this.$message({type: 'warning', message: "请输入内容"})
  146. } else {
  147. if (typeof (WebSocket) == "undefined") {
  148. console.log("您的浏览器不支持WebSocket");
  149. } else {
  150. console.log("您的浏览器支持WebSocket");
  151. // 组装待发送的消息 json
  152. // {"from": "zhang", "to": "admin", "text": "聊天文本"}
  153. let message = {from: this.user.name, to: this.chatUser, text: this.text}
  154. socket.send(JSON.stringify(message)); // 将组装好的json发送给服务端,由服务端进行转发
  155. this.messageList.push({user: this.user.name, text: this.text})
  156. // 构建消息内容,本人消息
  157. this.createContent(null, this.user.name, this.text)
  158. this.text = '';
  159. }
  160. }
  161. },
  162. createContent(remoteUser, nowUser, text) { // 这个方法是用来将 json的聊天消息数据转换成 html的。
  163. let html
  164. // 当前用户消息
  165. if (nowUser) { // nowUser 表示是否显示当前用户发送的聊天消息,绿色气泡
  166. html = "<div class=\"el-row\" style=\"padding: 5px 0\">\n" +
  167. " <div class=\"el-col el-col-22\" style=\"text-align: right; padding-right: 10px\">\n" +
  168. " <div class=\"tip left\">" + text + "</div>\n" +
  169. " </div>\n" +
  170. " <div class=\"el-col el-col-2\">\n" +
  171. " <span class=\"el-avatar el-avatar--circle\" style=\"height: 40px; width: 40px; line-height: 40px;\">\n" +
  172. " <img src=\"" + this.user.avatar + "\" style=\"object-fit: cover;\">\n" +
  173. " </span>\n" +
  174. " </div>\n" +
  175. "</div>";
  176. } else if (remoteUser) { // remoteUser表示远程用户聊天消息,蓝色的气泡
  177. html = "<div class=\"el-row\" style=\"padding: 5px 0\">\n" +
  178. " <div class=\"el-col el-col-2\" style=\"text-align: right\">\n" +
  179. " <span class=\"el-avatar el-avatar--circle\" style=\"height: 40px; width: 40px; line-height: 40px;\">\n" +
  180. " <img src=\"" + this.chatUserAvatar + "\" style=\"object-fit: cover;\">\n" +
  181. " </span>\n" +
  182. " </div>\n" +
  183. " <div class=\"el-col el-col-22\" style=\"text-align: left; padding-left: 10px\">\n" +
  184. " <div class=\"tip right\">" + text + "</div>\n" +
  185. " </div>\n" +
  186. "</div>";
  187. }
  188. console.log(html)
  189. this.content += html;
  190. }
  191. }
  192. }
  193. </script>
  194. <style>
  195. .tip {
  196. color: white;
  197. text-align: center;
  198. border-radius: 10px;
  199. font-family: sans-serif;
  200. padding: 10px;
  201. width: auto;
  202. display: inline-block !important;
  203. display: inline;
  204. }
  205. .right {
  206. background-color: deepskyblue;
  207. }
  208. .left {
  209. background-color: forestgreen;
  210. }
  211. </style>

****************************************************************************************************************************************************************************

  1. 30、电商系统与主流
  2. 1】电商系统:
  3. 在终端执行这两个就能运行起来了:
  4. set NODE_OPTIONS=--openssl-legacy-provider
  5. npm run serve
  6. 2】博客系统:
  7. set NODE_OPTIONS=--openssl-legacy-provider
  8. npm run serve
  9. ****************************************************************************************
  10. npm i --force
  11. set NODE_OPTIONS=--openssl-legacy-provider
  12. npm run dev
  13. 3】电影系统
  14. Solution1:
  15. npm install -g npm-check-updates
  16. ncu -u
  17. npm install

****************************************************************************************************************************************************************************

  1. 方式一:安装python解决(正确配置系统环境变量),python(v2.7 recommended,
  2. v3.x.x is not supported) - 推荐
  3. 下载:http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi
  4. 自行下载
  5. npm install --python=python2.7 #先下载
  6. npm config set python python2.7 #再设置环境
  7. **********************************************************************************************
  8. # npm install node-sass@latest
  9. npm install node-sass@4.12 --force 好使 卧槽
  10. **********************************************************************************************
  11. 删除node_modules文件
  12. 卸载webpack npm remove webpack-dev-server
  13. 重装指定版本webpack npm install webpack-dev-server@2.9.1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/1016963
推荐阅读
相关标签
  

闽ICP备14008679号