将三级分类的数据导入mysql中,在pms库中的pms_category表。
造一些数据方便开发,正常开发的时候,开发完功能再造数据,先写新增功能。
找到我们的商品模块代码:zhenyanmall-product
刚才我们导入的是pms_category商品三级分类表,因此打开CategoryController.java,然后自己写后台业务逻辑,以父子结构查询出三级分类列表。
找到我们的reneren-fast-vue项目,启动前端之前,需要启动renren-fast后端项目
npm run dev
默认账号和密码都是admin/admin
此时我们在系统管理-》角色管理,由此可以看到路径http://localhost:8001/#/sys-role
因此我们只需要在src/views/modules
目录下创建product
目录对应商品系统
的分类维护
。并创建category.vue文件,在这里写分类维护代码。
因为改项目已经设置了样式,所以需要下载一些样式,或者去掉style
我们使用的是element ui因此,我们可以从官网:https://element.eleme.cn/#/zh-CN/component/installation
里面写的是静态数据,我们可以把静态数据模块换掉,写成请求后台的方法获取数据,放到data里面。
可以参考role.vue模块的获取数据的写法。
写好代码之后,请求后台接口。
打开控制台,发现并没有获取到数据,请求的地址不是商品项目,而是http://localhost:8080/renren-fast/product/category/list/tree,(而8080是renren-fast后台端口)请求的地址应该是http://localhost:20001/product/category/list/tree
其实我们需要配置的是网关的地址,让网关去转发到对应的服务地址,而不是直接请求对应服务地址。http://localhost:8080/renren-fast这个地址其实就是renren-fast配置的请求后台转发地址,全局搜索改配置,看看改配置在哪里,然后配置为网关地址,发现配置在static/config/index.js
改为:http://localhost:88
此时我们把请求api地址改了,那么也就不能请求到renren-fast后台了,
renren-fast引入common,并添加注册中心注解,添加服务名和注册中心地址(因为spring版本冲突问题,可以不用引入common,可以单独引入注册中心和配置中心)
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.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
<version>2.1.0.RELEASEversion>
<exclusions>
<exclusion>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-contextartifactId>
exclusion>
<exclusion>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
<version>2.1.0.RELEASEversion>
<exclusions>
<exclusion>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-contextartifactId>
exclusion>
<exclusion>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
exclusion>
exclusions>
dependency>
因为我们引入了配置中心,但是没有配置配置中心的配置,索引会报错,但是不影响,后续添加配置中心的配置即可
后面就可以修改网关的路由,然后当前端请求api的时候,如果是renren-fast后台,则转到renren-fast,如果是商品服务,转到商品服务后台。
根据路径去匹配,当匹配到该路径时,转到对应的uri。
在前端项目+API,所有的请求都走API
从前端项目发送:http://localhost:88/api/captcha.jpg 请求 发给网关,网关看到前缀是api满足断言,转到renren-fast,请求:http://renren-fast:8080/api/captcha.jpg,还是会报错。(实际应该访问的是:http://localhost:8080/renren-fast/captcha.jpg)应该进行重定向
因此需要将http://localhost:88/api/captcha.jpg 转发给 http://localhost:8080/renren-fast/captcha.jpg 使用网关的路径重写
可以正常获取验证码
403Forbidden被拒绝,从’http://localhost:8001’ 访问’http://localhost:88/api/sys/login’ ,请求被cors策略阻塞,也就是跨域,浏览器安全限制期间,拒绝跨域请求。浏览器检查有一个请求头:Access-Control-Allow-Origin,不在请求头里面。
因为从http://localhost:8001访问http://localhost:88/api/sys/login 端口号发生了变化
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
简单请求不会跨域,
而我们的登录是POST,Application/json的请求,
不是简单请求的都需要发送一个预检请求
有以下几种方案可以解决跨域问题,跨域的根本原因就是以上三种原因:协议、域名、端口号不同,解决这三种情况即可。
每次请求都要给响应头添加以上字段,因此我们可以写一个filter,我们所有的请求进来,返回给浏览器之前,添加那些响应头,filter不用写在每一个项目,每一个项目都会远程访问跨域,可以直接写在网关里面,因为是网关代理给其他的服务,在网关里面配置统一跨域解决问题。
将网关里面的配置都写到config里面
重新启动网关服务,登录,预检请求没问题,真正请求登录接口报错,报同源策略多个值,只希望一个,是因为renren-fast脚手架工程后台也配置了跨域,将renren-fast那个跨域注释掉。
要注意,路由配置顺序,范围小的精确的在前面(高优先级),范围大的在后面(低优先级),要不然会被前面给匹配。
编写菜单删除功能,没有子菜单,且未被引用的,可参考element ui自定义节点内容,有两种方式,render-content和 scoped slot,我们在此使用scoped slot,只需要引入span标签即可。(使用的是vue的插槽机制)
然后修改其中的append和remove方法,需要给后台发送请求
从element ui找到对应控件的自定义节点内容,引入添加删除功能,修改内容展开为flase,这样只有点击箭头才能展开内容 :expand-on-click-node=“false”
// 3、去掉单击函数 @node-click=“handleNodeClick”
// 4、我们要做的效果是,只有没有子菜单和引用才显示Delete按钮,只有一级、二级菜单才能显示Append按钮 使用v-if进行判断
// 5、在使用remove方法时,会传入节点参数,几级节点
批量删除
// 7、添加树中唯一节点标识,node-key 每一个节点都有一个catId
配置mybatis逻辑删除,参考MyBatis-Plus文档
1)、配置全局的逻辑删除规则(省略)
// 8、请求后台删除接口,要有删除弹出确认框,删除完,更新menus,并且列表还是展开的状态
// 9、使用Message Box弹框,确认删除
// 10、使用Message 消息提示,确定删除成功
// 11、删除之后列表是展开的状态 default-expanded-keys 默认展开的节点的 key 的数组
使用element ui中的对话框,使用自定义内容的打开嵌套表单的
.sync修饰符是用来实现父子组件中的数据进行双向绑定功能的。
如果用多个组件,必须有一个根元素
包含所有的组件然后使用表单,model绑定数据对象
// 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、点击添加时,清空回显的值
查看element ui文档,根据tree树型控件的拖拽功能。
// 25、拖拽效果,目前只有三级目录,需要判断是否可以放置该位置 draggable 是否开启拖拽节点功能 allow-drop 拖拽时判定目标节点能否被放置。type 参数有三种情况:‘prev’、‘inner’ 和 ‘next’,分别表示放置在目标节点前、插入至目标节点和放置在目标节点后 Function(draggingNode, dropNode, type)
// 26、被拖动的当前节点以及所在的父节点总层数不能大于3
// 27、拖拽数据收集,将修改节点的位置保存到后台 监听拖拽成功事件 Events 的node-drop 拖拽成功完成时触发的事件 共四个参数,依次为:被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置(before、after、inner)、event
后台编写批量修改功能。
// 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转为名称
// 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转为名称