目录
- 一、启动之前创建的环境
- 1、启动虚拟机
- 2、启动mysql
- 3、启动redis
- 4、启动nacos
- 5、导入三级分类测试数据
- 二、开发商品服务三级分类列表
- 1、后台模块
- 1)书写商品三级分类表后台业务逻辑
- 2、前端模块
- 1)启动renren-fast前后端项目
- 2)在系统管理-》菜单管理-》新增目录-》商品系统
- 3)新增分类维护
- 4)查看角色管理
- 5)创建商品目录vue文件
- 6)从element UI 官网引入Tree树形控件
- 7)参考role.vue模块写商品目录模块
- 8)修改接口请求地址
- 9)网关配置
- 10)需要将renren-fast注册到网关。
- 11)遇到的问题:找不到类定义
- 12)然后还会报错,报配置不存在,
- 13)修改网关路由
- 14)验证码报错
- 15)重写转发路径
- 16)登录报错,跨域
- 17)端口号发生了变化
- 18)跨域含义
- 19)预检请求
- 20)跨域解析网址
- 21)解决跨域方法
- 22)线上部署环境使用nginx
- 23)开发中使用以下方式(因为开发中不好配置nginx):
- 24)配置跨域
- 25)同源策略多个值
- 3、查询
- 1)配置product的路由
- 2)配置路由顺序
- 3)直接请求网关,测试查询接口:
- 4、删除
- 1)element ui自定义节点内容
- 2)删除完展开内容 :expand-on-click-node="true"
- 2)后端编写删除功能接口
- 1、逻辑删除
- 3)参照role.vue的请求删除接口
- 5、新增
- 1)前端的一些简单配置
- 2)编写后端新增三级目录接口
- 6、修改
- 7、修改-拖拽效果
- 8、修改-获取拖拽数据集
- 9、批量拖拽效果
- 10、批量删除
- 11、总结
一、启动之前创建的环境
1、启动虚拟机
2、启动mysql
3、启动redis
4、启动nacos
5、导入三级分类测试数据
将三级分类的数据导入mysql中,在pms库中的pms_category表。
造一些数据方便开发,正常开发的时候,开发完功能再造数据,先写新增功能。
二、开发商品服务三级分类列表
1、后台模块
1)书写商品三级分类表后台业务逻辑
找到我们的商品模块代码:zhenyanmall-product
刚才我们导入的是pms_category商品三级分类表,因此打开CategoryController.java,然后自己写后台业务逻辑,以父子结构查询出三级分类列表。
2、前端模块
1)启动renren-fast前后端项目
找到我们的reneren-fast-vue项目,启动前端之前,需要启动renren-fast后端项目
npm run dev
默认账号和密码都是admin/admin
2)在系统管理-》菜单管理-》新增目录-》商品系统
3)新增分类维护
4)查看角色管理
此时我们在系统管理-》角色管理,由此可以看到路径http://localhost:8001/#/sys-role
且sys-role代码在
重启前端项目
5)创建商品目录vue文件
因此我们只需要在src/views/modules
目录下创建product
目录对应商品系统
的分类维护
。并创建category.vue文件,在这里写分类维护代码。
因为改项目已经设置了样式,所以需要下载一些样式,或者去掉style
6)从element UI 官网引入Tree树形控件
我们使用的是element ui因此,我们可以从官网:https://element.eleme.cn/#/zh-CN/component/installation
搜索tree 组件,然后拷贝使用。
7)参考role.vue模块写商品目录模块
里面写的是静态数据,我们可以把静态数据模块换掉,写成请求后台的方法获取数据,放到data里面。
可以参考role.vue模块的获取数据的写法。
写好代码之后,请求后台接口。
8)修改接口请求地址
打开控制台,发现并没有获取到数据,请求的地址不是商品项目,而是http://localhost:8080/renren-fast/product/category/list/tree,(而8080是renren-fast后台端口)请求的地址应该是http://localhost:20001/product/category/list/tree
9)网关配置
其实我们需要配置的是网关的地址,让网关去转发到对应的服务地址,而不是直接请求对应服务地址。http://localhost:8080/renren-fast这个地址其实就是renren-fast配置的请求后台转发地址,全局搜索改配置,看看改配置在哪里,然后配置为网关地址,发现配置在static/config/index.js
改为:http://localhost:88
此时我们把请求api地址改了,那么也就不能请求到renren-fast后台了,
10)需要将renren-fast注册到网关。
renren-fast引入common,并添加注册中心注解,添加服务名和注册中心地址(因为spring版本冲突问题,可以不用引入common,可以单独引入注册中心和配置中心)
然后重启RenrenApplication
11)遇到的问题:找不到类定义
org.springframework.cloud.context.properties包下的
ConfigurationPropertiesBeans类报错:
Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/context/properties/ConfigurationBeanFactoryMetadata
at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166)
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2309)
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:467)
... 37 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 41 common frames omitted
分析:
1:查看
ConfigurationPropertiesBeans存在相同名类共有三个
2:去掉 nacos服务注册中心和配置中心的依赖则不报错,此时存在
ConfigurationPropertiesBeans类共有两个(其他项目存在一个版本2.2.0RELEASE,人人开源项目存在3.1.5版本)
3:依赖冲突,在nacos的依赖中去掉依赖:
spring-cloud-context
4:nacos本身也需要去掉依赖
spring-cloud-starter-netflix-ribbon
解决方案:修改pom文件
<!--注册中心 服务注册/发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--配置中心 管理配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
12)然后还会报错,报配置不存在,
因为我们引入了配置中心,但是没有配置配置中心的配置,索引会报错,但是不影响,后续添加配置中心的配置即可
13)修改网关路由
后面就可以修改网关的路由,然后当前端请求api的时候,如果是renren-fast后台,则转到renren-fast,如果是商品服务,转到商品服务后台。
根据路径去匹配,当匹配到该路径时,转到对应的uri。
在前端项目+API,所有的请求都走API
14)验证码报错
请求登录,发现验证码还会报错
从前端项目发送:http://localhost:88/api/captcha.jpg 请求 发给网关,网关看到前缀是api满足断言,转到renren-fast,请求:http://renren-fast:8080/api/captcha.jpg,还是会报错。(实际应该访问的是:http://localhost:8080/renren-fast/captcha.jpg)应该进行重定向
15)重写转发路径
因此需要将http://localhost:88/api/captcha.jpg 转发给 http://localhost:8080/renren-fast/captcha.jpg 使用网关的路径重写
可以正常获取验证码
16)登录报错,跨域
然后登录,发现报如下错误:
17)端口号发生了变化
403Forbidden被拒绝,从’http://localhost:8001’ 访问’http://localhost:88/api/sys/login’ ,请求被cors策略阻塞,也就是跨域,浏览器安全限制期间,拒绝跨域请求。浏览器检查有一个请求头:Access-Control-Allow-Origin,不在请求头里面。
因为从http://localhost:8001访问http://localhost:88/api/sys/login 端口号发生了变化
18)跨域含义
19)预检请求
options:跨域请求的预请求
20)跨域解析网址
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
简单请求不会跨域,
而我们的登录是POST,Application/json的请求,
不是简单请求的都需要发送一个预检请求
21)解决跨域方法
有以下几种方案可以解决跨域问题,跨域的根本原因就是以上三种原因:协议、域名、端口号不同,解决这三种情况即可。
22)线上部署环境使用nginx
23)开发中使用以下方式(因为开发中不好配置nginx):
24)配置跨域
每次请求都要给响应头添加以上字段,因此我们可以写一个filter,我们所有的请求进来,返回给浏览器之前,添加那些响应头,filter不用写在每一个项目,每一个项目都会远程访问跨域,可以直接写在网关里面,因为是网关代理给其他的服务,在网关里面配置统一跨域解决问题。
将网关里面的配置都写到config里面
25)同源策略多个值
重新启动网关服务,登录,预检请求没问题,真正请求登录接口报错,报同源策略多个值,只希望一个,是因为renren-fast脚手架工程后台也配置了跨域,将renren-fast那个跨域注释掉。
3、查询
1)配置product的路由
2)配置路由顺序
要注意,路由配置顺序,范围小的精确的在前面(高优先级),范围大的在后面(低优先级),要不然会被前面给匹配。
3)直接请求网关,测试查询接口:
修改category.vue文件。
4、删除
1)element ui自定义节点内容
编写菜单删除功能,没有子菜单,且未被引用的,可参考element ui自定义节点内容,有两种方式,render-content和 scoped slot,我们在此使用scoped slot,只需要引入span标签即可。(使用的是vue的插槽机制)
然后修改其中的append和remove方法,需要给后台发送请求
2)删除完展开内容 :expand-on-click-node=“true”
从element ui找到对应控件的自定义节点内容,引入添加删除功能,修改内容展开为flase,这样只有点击箭头才能展开内容 :expand-on-click-node=“false”
// 3、去掉单击函数 @node-click=“handleNodeClick”
// 4、我们要做的效果是,只有没有子菜单和引用才显示Delete按钮,只有一级、二级菜单才能显示Append按钮 使用v-if进行判断
// 5、在使用remove方法时,会传入节点参数,几级节点
批量删除
// 7、添加树中唯一节点标识,node-key 每一个节点都有一个catId
2)后端编写删除功能接口
配置mybatis逻辑删除,参考MyBatis-Plus文档
1、逻辑删除
-
1)、配置全局的逻辑删除规则(省略)
-
2)、配置逻辑删除的组件Bean(省略)
-
3)、给Bean加上逻辑删除注解@TableLogic 当实际的逻辑删除值和全局配置的不一致,可以指定对应的逻辑删除值
-
4)、打印sql语句,调整日志级别为debug
根据日志发现删除实际是修改标记字段。
可以使用postman进行测试。
3)参照role.vue的请求删除接口
// 8、请求后台删除接口,要有删除弹出确认框,删除完,更新menus,并且列表还是展开的状态
// 9、使用Message Box弹框,确认删除
// 10、使用Message 消息提示,确定删除成功
// 11、删除之后列表是展开的状态 default-expanded-keys 默认展开的节点的 key 的数组
5、新增
1)前端的一些简单配置
使用element ui中的对话框,使用自定义内容的打开嵌套表单的
.sync修饰符是用来实现父子组件中的数据进行双向绑定功能的。
如果用多个组件,必须有一个根元素
包含所有的组件初始的时候,对话框为false,进行add时,设置为true
然后使用表单,model绑定数据对象
// 12、新增三级分类 引入element ui 的 el-dialog对话框,使用自定义内容的打开嵌套表单的
// 13、修改model的值,表单数据双向绑定
// 14、添加addCategory 方法
// 15、在category中添加对象的属性 在append方法中获取一些数据属性放到category对象中
// 16、在addCategory()方法中请求后台接口 然后 关闭对话框this.dialogVisible = false 刷新出新的菜单this.getMenus() 设置需要默认展开的菜单
2)编写后端新增三级目录接口
可以直接掉逆向生成工程里面的新增三级菜单接口
6、修改
// 17、添加修改按钮,直接拷贝新增的即可,且去掉if,任何时候修改按钮都是存在的
// 18、在edit方法中 设置弹出对话框 设置修改的名称 分类ID
// 19、需要在点击确认的时候到底请求的是添加还是修改,添加标识 dialogType: ‘’, // edit,add
// 20、title提示也需要修改一下,到底是添加还是修改
// 21、添加图标和计量单位
// 22、回显内容修改,多个人修改,应该发送请求获取节点最新数据
// 23、设置修改时,对话框通过点击关闭 close-on-click-modal
// 24、点击添加时,清空回显的值
7、修改-拖拽效果
查看element ui文档,根据tree树型控件的拖拽功能。
// 25、拖拽效果,目前只有三级目录,需要判断是否可以放置该位置 draggable 是否开启拖拽节点功能 allow-drop 拖拽时判定目标节点能否被放置。type 参数有三种情况:‘prev’、‘inner’ 和 ‘next’,分别表示放置在目标节点前、插入至目标节点和放置在目标节点后 Function(draggingNode, dropNode, type)
// 26、被拖动的当前节点以及所在的父节点总层数不能大于3
8、修改-获取拖拽数据集
// 27、拖拽数据收集,将修改节点的位置保存到后台 监听拖拽成功事件 Events 的node-drop 拖拽成功完成时触发的事件 共四个参数,依次为:被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置(before、after、inner)、event
后台编写批量修改功能。
9、批量拖拽效果
// 28、添加批量拖拽按钮 使用组件switch
// 29、添加保存按钮,当全部拖拽成功后,将所有的修改一次性提交后台进行修改,当拖拽按钮为开启的时候,显示,不开启时隐藏 并且从节点本身获取级别,因为拖拽一直在变化
10、批量删除
// 30、使用button进行批量删除 获取选中的节点 看一下tree组件的方法
// 31、要调一个组件提供的一个方法,首先要写一个ref给组件起一个唯一标识,要调el-tree封装的方法的时候,要this.
r
e
f
.
t
r
e
e
.
f
i
l
t
e
r
(
v
a
l
)
t
h
i
s
是
v
u
e
实例,
ref.tree.filter(val) this是vue实例,
ref.tree.filter(val)this是vue实例,ref当前vue实例所有的组件,tree是对应组件的标识名称,filter对应组件的方法
// 32、this.$confirm 弹出确定提示框,将菜单ID转为名称
11、总结
// 1、从element ui找到tree树型控件,引入其中,参考role.vue请求后台远程接口,获取data数据
// 2、从element ui找到对应控件的自定义节点内容,引入添加删除功能,修改内容展开为flase,这样只有点击箭头才能展开内容 :expand-on-click-node=“false”
// 3、去掉单击函数 @node-click=“handleNodeClick”
// 4、我们要做的效果是,只有没有子菜单和引用才显示Delete按钮,只有一级、二级菜单才能显示Append按钮 使用v-if进行判断
// 5、在使用remove方法时,会传入节点参数,几级节点
// 6、批量删除,只需要添加 show-checkbox
// 7、添加树中唯一节点标识,node-key 每一个节点都有一个catId
// 8、请求后台删除接口,要有删除弹出确认框,删除完,更新menus,并且列表还是展开的状态
// 9、使用Message Box弹框 确认删除
// 10、使用Message 消息提示,确定删除成功
// 11、删除之后列表是展开的状态 default-expanded-keys 默认展开的节点的 key 的数组
// 12、新增三级分类 引入element ui 的 el-dialog对话框,使用自定义内容的打开嵌套表单的
// 13、修改model的值,表单数据双向绑定
// 14、添加addCategory 方法
// 15、在category中添加对象的属性 在append方法中获取一些要添加菜单的上级菜单的数据属性放到category对象中
// 16、在addCategory()方法中请求后台接口 然后 关闭对话框this.dialogVisible = false 刷新出新的菜单this.getMenus() 设置需要默认展开的菜单
// 17、添加修改按钮,直接拷贝新增的即可,且去掉if,任何时候修改按钮都是存在的
// 18、在edit方法中 设置弹出对话框 设置修改的名称 分类ID
// 19、需要在点击确认的时候到底请求的是添加还是修改,添加标识 dialogType: ‘’, // edit,add
// 20、title提示也需要修改一下,到底是添加还是修改
// 21、添加图标和计量单位
// 22、回显内容修改,多个人修改,应该发送请求获取节点最新数据
// 23、设置修改时,对话框通过点击关闭 close-on-click-modal
// 24、点击添加时,清空回显的值
// 25、拖拽效果,目前只有三级目录,需要判断是否可以放置该位置 draggable 是否开启拖拽节点功能 allow-drop 拖拽时判定目标节点能否被放置。type 参数有三种情况:‘prev’、‘inner’ 和 ‘next’,分别表示放置在目标节点前、插入至目标节点和放置在目标节点后 Function(draggingNode, dropNode, type)
// 26、被拖动的当前节点以及所在的父节点总层数不能大于3
// 27、拖拽数据收集,将修改节点的位置保存到后台 监听拖拽成功事件 Events 的node-drop 拖拽成功完成时触发的事件 共四个参数,依次为:被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置(before、after、inner)、event
// 28、添加批量拖拽按钮 使用组件switch
// 29、添加保存按钮,当全部拖拽成功后,将所有的修改一次性提交后台进行修改,当拖拽按钮为开启的时候,显示,不开启时隐藏 并且从节点本身获取级别,因为拖拽一直在变化
// 30、使用button进行批量删除 获取选中的节点 看一下tree组件的方法
// 31、要调一个组件提供的一个方法,首先要写一个ref给组件起一个唯一标识,要调el-tree封装的方法的时候,要this.
r
e
f
.
t
r
e
e
.
f
i
l
t
e
r
(
v
a
l
)
t
h
i
s
是
v
u
e
实例,
ref.tree.filter(val) this是vue实例,
ref.tree.filter(val)this是vue实例,ref当前vue实例所有的组件,tree是对应组件的标识名称,filter对应组件的方法
// 32、this.$confirm 弹出确定提示框,将菜单ID转为名称