01-分布式基础-全栈开发篇

分布式基础概念

微服务

微服务架构风格,就像是把一个单独的应用程序开发成一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API 这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署,这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

简而言之,拒绝大型单体应用,基于业务边界进行服务微化拆分,每个服务独立部署运行。

集群&分布式&节点

集群是个物理状态,分布式是个工作方式。

只要是一堆机器,也可以叫做集群,他们是不是一起协作干活,这谁也不知道。

《分布式系统原理与范型》定义:

分布式系统是若干独立计算机的集合,这些计算机对于用户来说像单个系统。分布式系统 (distributed system) 是建立网络之上的软件系统。

  • 分布式是指根据不同的业务分布在不同的地方。

  • 集群指的是将几台服务器集中在一起,实现同一业务。

例如:京东是一个分布式系统,众多业务运行在不同的机器上,所有业务构成一个大型的分布式业务集群,每一个小的业务,比如用户系统,访问压力大的时候一台服务器是不够的,我们就应该将用户系统部署到多个服务器,也就是每一个业务系统也可以做集群化。

分布式中的每一个节点,都可以做集群,而集群并不一定就是分布式的

节点:集群中的一个服务器

远程调用

在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的需要互相调用,我们称之为远程调用。

SpringCloud中使用HTTP+JSON的方式来完成远程调用。

负载均衡

img

分布式系统中,A 服务需要调用B服务,B服务在多台机器中都存在, A调用任意一个服务器均可完成功能。

为了使每一个服务器都不要太或者太闲,我们可以负载均衡调用每一个服务器,提升网站的健壮性。

常见的负载均衡算法:

  • 轮询:为第一个请求选择健康池中的每一个后端服务器,然后按顺序往后依次选择,直到最后一个,然后循环。

  • 最小连接:优先选择链接数最少,也就是压力最小的后端服务器,在会话较长的情况下可以考虑采取这种方式。

服务注册/发现&注册中心

A服务调用B服务,A服务不知道B服务当前在哪几台服务器上有,哪些正常的,哪些服务已经下线,解决这个问题可以引入注册中心。

img

如果某些服务下线,我们其他人可以实时的感知到其他服务的状态, 从而避免调用不可用的服务。

配置中心

每一个服务最终都有大量配置,并且每个服务都可能部署在多个服务器上,我们经常需要变更配置,我们可以让每个服务在配置中心获取自己的配置。

配置中心用来集中管理微服务的配置信息。

img

服务熔断&服务降级

在微服务架构中,微服务之间通过网络来进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应,要防止这种情况,必须要有容错机制来保护服务。

img

rpc情景:订单服务 --> 商品服务 --> 库存服务

库存服务出现故障导致响应慢,导致商品服务需要等待,可能等到10s后库存服务才能响应。库存服务的不可用导致商品服务阻塞,商品服务等的期间,订单服务也处于阻塞。一个服务不可用导致整个服务链都阻塞。如果是高并发,第一个请求调用后阻塞10s得不到结果,第二个请求直接阻塞10s。更多的请求进来导致请求积压,全部阻塞,最终服务器的资源耗尽。导致雪崩。

  1. 服务熔断:设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保护机制,后来的请求不再去调用这个服务,本地直接返回默认的数据

  2. 服务降级:在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级运行,降级:某些服务不处理,或者简单处理【抛异常,返回NULL,调用 Mock数据,调用 FallBack 处理逻辑】

API 网关

在微服务架构中,API Gateway 作为整体架构的重要组件,抽象服务中需要的公共功能,同时它提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流监控,日志统计等丰富功能,帮助我们解决很多API管理的难题。

环境搭建

虚拟机环境

  • 使用vagrant快速创建虚拟机

  • 或者直接使用vmware+xshell

安装docker

虚拟化容器技术。Docker基于镜像,可以秒级启动各种容器。每一种容器都是一个完整的运行环境,容器之间互相隔离。

  • 卸载之前的docker

  • 安装docker

  • 启动docker

  • 验证docker

配置docker阿里云镜像加速

登录阿里云,控制台>容器镜像服务>镜像工具>镜像加速器

安装软件

软件仓库:https://hub.docker.com/

安装MySQL

  • -p 3306:3306 :将容器的3306端口映射到主机的3306端口

  • -v /mydata/mysql/log:/var/log/mysql :将配置文件夹挂载到主机

  • -v /mydata/mysql/data:/var/lib/mysql:将日志文件夹挂载到主机

  • -v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂载到主机

  • -e MYSQL_ROOT_PASSWORD=root:初始化root用户的密码

  • --name指定容器名字

  • -v目录挂载

  • -p指定端口映射

  • -e设置mysql参数

  • -d后台运行

进入mysql容器

修改MySQL配置文件

修改如下:

然后重启容器

安装Redis

在docker hub搜索redis镜像

拉取redis镜像到本地

修改需要自定义的配置(docker-redis默认没有配置文件, 自己在宿主机建立后挂载映射) 创建并修改/mydata/redis/conf/redis.conf。

一定先创建文件,不然会默认创建为文件夹。

启动并创建实例

测试,不用进入容器,而是直接进入redis客户端

开发环境统一

Maven

下载maven,然后配置MAVEN_HOME,并设置%MAVEN_HOME%\bin环境变量。

D:\Program Files\apache-maven-3.8.6\conf\settings.xml,修改如下:export

然后修改idea中maven目录

image-20221002211715271

安装开发插件(可选-方便开发)

在idea安装插件

在vscode安装插件

配置git ssh

配置用户名

配置邮箱

配置ssh免密登录

三次回车后生成了密钥,也可以查看密钥

浏览器登录码云后,个人头像上点设置、然后点ssh公钥、随便填个标题,然后赋值

测试

测试成功

创建github仓库

image-20221002221328228

项目初始化

下载github仓库,然后就这个仓库中创建子项目。

image-20221002231109405
image-20221002231036880

创建父模块:在firelymall中创建pom.xml

之后修改总项目的.gitignore,把小项目里的垃圾文件在提交的时候忽略掉

之后,如果不会使用idea操作git,就是用git命令

创建数据库

创建数据库之前需要启动docker服务

这两个命令的差别就是后者会显示 【已创建但没有启动的容器】 。

我们接下来设置我们要用的容器每次都是自动启动:

如果不配置上面的内容的话,我们也可以选择手动启动

如果要进入已启动的容器

接着创建数据库,然后接着去客户端软件直接我们的操作,在左侧root上右键建立数据库: 字符集选utf8mb4,能兼容utf8且能解决一些乱码的问题。分别 建立了下面数据库。

所有的数据库数据再复杂也不建立外键,因为在电商系统里,数据量大, 做外键关联很耗性能。

firelymall_oms.sql

firelymall-pms.sql

firelymall_sms.sql

firelymall_ums.sql

firelymall_wms.sql

VSCode准备/WebStorm准备

在码云上搜索人人开源,我们使用renren-fast,renren-fast-vue项目。

  • git clone https://gitee.com/renrenio/renren-fast.git

  • git clone https://gitee.com/renrenio/renren-fast-vue.git

下载到了桌面,我们把renren-fast移动到我们的项目文件夹(删掉.git文件),而renren-fast-vue是用VSCode打开的(后面再弄)。

renren-fast

在idea(root)项目里的pom.xml添加一个

然后打开renren-fast/db/mysql.sql,复制全部,在navicat中创建库firelymall_admin,粘贴以下的内容执行。


然后修改项目里renren-fast中的application.yml,修改application-dev.yml中的数据库配置

然后执行java下的RenrenApplication,浏览器输入http://localhost:8080/renren-fast/ 得到{"msg":"invalid token","code":401}就代表无误。

如果报错,在pom.xml修改内容:

然后将下面插件添加版本号:

renren-fast-vue

用webstorm打开renren-fast-vue 。

然后安装node: 注意:版本为v16.12.0(一定不要搞错版本了),python版本为3(因为不同版本等下下面遇到的问题可能不一样) 接下来。

在node中创建如下两个文件夹,然后进行设置

打开系统属性-高级-环境变量,在系统变量中新建 变量名:NODE_PATH,变量值:D:\Program Files\nodejs\node_global\node_modules

编辑用户变量的 path,将默认的 C 盘下 APPData/Roaming\npm 修改为 D:\Program Files\nodejs\node_global

常用的是使用官方推荐的 cnpm 命令行工具代替默认的 npm,实际使用的是淘宝源:

如果出现EPERM: operation not permitted,找到~\.nprmc文件删除。

或者,设置node仓库。提高下载速度

配置完毕后,进入vue项目,打开终端,输入:

之后,运行项目:

vue模板

逆向工程搭建

product

下载到桌面后,同样把里面的.git文件删除,然后移动到IDEA项目目录中,同样配置好pom.xml(root) 。

然后在renren-generator中的pom在parent添加如下:

修改renren-generator的application.yml

修改generator.properties

运行RenrenApplication。如果启动不成功,修改application中是port为80。访问http://localhost:80 。

然后点击全部,点击生成代码。下载了压缩包 。

解压压缩包,把main放到firelymall-product的同级目录下。

然后在项目上右击(在项目上右击很重要)new modules— maven—然后在name上输入firelymall-common。

然后在product项目中的pom.xml中加入下面内容

将renren-fast----utils包下的Query和PageUtils、R、Constant复制到common项目的java/com.mall.common.utils下 。

然后将dao层中的@RequiresPermissions这些注解掉,因为是shiro的 。

注释renren-generator\src\main\resources\template/Controller中所有的

复制renren-fast中的xss包粘贴到common的java/com.mall.common目录下。 还复制了exception文件夹。

有什么报错,就去renren-fast中找对应的包移动到common下。

测试

测试与整合商品服务里的mybatis-plus

在common的pom.xml中导入

删掉common里xss/xssfiler和XssHttpServletRequestWrapper 。

在product项目的resources目录下新建application.yml

然后在主启动类上加上注解@MapperScan()

然后去测试,先通过下面方法给数据库添加内容

同理,其他的微服务也按照这样创建,注意端口号。

  • http://localhost:8081/product/brand/list

  • http://localhost:8082/member/member/list

  • http://localhost:8083/order/order/list

  • http://localhost:8084/coupon/coupon/list

  • http://localhost:8085/ware/wareinfo/list

修改下application.yml

SpringCloud Alibaba简介

搭配环境

结合SpringCloud Alibaba技术搭配方案

  • SpringCloud Alibaba- Nacos:注册中心《服务发现/注册)

  • SpringCloud Alibaba-Nacos:配置中心(动态配置管理)

  • SpringCloud- Ribbon:负载均衡

  • SpringCloud-Feign:声明式HTTP客户端(调用远程服务)

  • SpringCloud Alibaba-Sentinel:服务容错(限流、降级、熔断)

  • SpringCloud-Gateway:API网关(webflux编程模式)

  • SpringCloud - Sleuth:调用链监控

  • SpringCloud Alibaba-Seata:原Fescar,即分布式事务解决方案

在common的pom.xml中加入

三者版本对应:

Nacos注册中心

一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

nacos作为我们的注册中心和配置中心。

下载稳定的nacos版本:https://github.com/alibaba/nacos/releases

接着访问:http://localhost:8848/nacos/,账号密码都是nacos

使用nacos:在某个项目里properties里写spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848(yaml同理,指定nacos的地址)。再指定applicatin.name告诉注册到nacos中以什么命名。

依赖:放到common里,不写版本是因为父项目或spring-cloud里面有了版本管理

使用 @EnableDiscoveryClient 注解开启服务注册与发现功能

最后application.yml内容,配置了服务中心名和当前模块名字

然后依次给member、配置上面的yaml,改下name就行。再给每个项目配置类上加上注解@EnableDiscoveryClient。

nacos测试:

image-20221007162411308

测试member和coupon的远程调用

feign是一个声明式的HTTP客户端,他的目的就是让远程调用更加简单。给远程服务发的是HTTP请求。(声明式远程调用)

导入依赖:

在coupon中修改如下的内容

在member的配置类上加注解@EnableDiscoveryClient,告诉member是一个远程调用客户端,member要调用东西的。

想要远程调用的步骤:

  • 引入openfeign

  • 编写一个接口,接口告诉springcloud这个接口需要调用远程服务

    • 在接口里声明@FeignClient("firelymall-coupon")他是一个远程调用客户端且要调用coupon服务

    • 要调用coupon服务的/coupon/coupon/member/list方法

  • 开启远程调用功能 @EnableFeignClients,要指定远程调用功能放的基础包

刚才写的优惠券的功能,复制函数部分,在member的com.mall.firelymall.member.feign包下新建类:

@FeignClient+@RequestMapping构成远程调用的坐标。

其他类中看似只是调用了CouponFeignService.membercoupons(),而实际上该方法跑去nacos里和rpc里调用了才拿到东西返回。

然后我们在member的控制层写一个测试请求

重新启动服务刷新容器。

测试:http://localhost:8082/member/member/coupons

image-20221008180941004

Nacos配置中心

简单示例

用nacos作为配置中心。配置中心的意思是不在application.properties等文件中配置了,而是放到nacos配置中心公用,这样无需每台机器都改。

common中添加依赖 nacos配置中心

在coupons项目中创建/src/main/resources/bootstrap.properties ,这个文件是springboot里规定的,优先级别application.properties高

还是原来我们使用配置的方式,只不过优先级变了,所以匹配到了nacos的配置。

在application.properties设置值进行测试。

添加测试

运行测试:http://localhost:8084/coupon/coupon/test

image-20221008215012103

在启动日志就可以看到在nacos中加载的位置。

image-20221008215639636

然后在nacos中创建配置,并发布配置信息。

image-20221008215825147

重启服务后,就可以加载到配置了,但是现在还不能实时加载,还需要一个注解@RefreshScope

需要加载在在coupon的控制层上。

重启后(让注解生效),在nacos浏览器里修改配置,修改就可以观察到能动态修改了。

nacos的配置内容优先于项目本地的配置内容

测试:http://localhost:8084/coupon/coupon/test

image-20221008220301110

命名空间与配置分组

在nacos浏览器中还可以配置:

  • 命名空间:用作配置隔离。(一般每个微服务一个命名空间)

    • 默认public。默认新增的配置都在public空间下

    • 开发、测试、开发可以用命名空间分割。properties每个空间有一份。也可以为每个微服务配置一个命名空间,微服务互相隔离

    • 在bootstrap.properties里配置(测试完去掉,学习不需要)

  • 配置集:一组相关或不相关配置项的集合。

  • 配置集ID:类似于配置文件名,即Data ID

  • 配置分组:默认所有的配置集都属于DEFAULT_GROUP。双十一,618的优惠策略改分组即可

其他划分方案:

NameSpace:命名空间 默认为public,其作用可以用来实现环境隔离作用,比如我们的开发环境、测试环境、生产环境。

Group:默认分组为DEFAULT_GROUP,Group 可以将不同的微服务进行分组划分。

Service/DataId: 也就是微服务工程,一个微服务工程可以有多个集群中心,比如一个消费者工程可以使用两个中心,每个中心读取不同的配置文件,每个中心内可以有多个实例实现集群。

最终方案:每个微服务创建自己的命名空间,然后使用配置分组区分环境(dev/test/prod)。

加载多配置集

我们要把原来application.yml里的内容都分文件抽离出去。我们在nacos里创建好后,在coupons里指定要导入的配置即可。

然后修改bootstrap.properties

查看,控制台输出内容有

Gateway网关

简述

动态上下线:发送请求需要知道商品服务的地址,如果商品服务器有123服务器,1号掉线后,还得改,所以需要网关动态地管理,他能从注册中心中实时地感知某个服务上线还是下线。【先通过网关,网关路由到服务提供者】

拦截:请求也要加上询问权限,看用户有没有权限访问这个请求,也需要网关。

所以我们使用spring cloud的gateway组件做网关功能。

网关是请求流量的入口,常用功能包括路由转发,权限校验,限流控制等。[springcloud gateway](https://spring.io/projects/spring-cloud-gateway

)取代了zuul网关。

参考手册:https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/

三大核心概念:

  • Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates断言, and a collection of filters. A route is matched if the aggregate predicate is true。发一个请求给网关,网关要将请求路由到指定的服务。路由有id,目的地uri,断言的集合,匹配了断言就能到达指定位置

  • Predicate断言: This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters.就是java里的断言函数,匹配请求里的任何信息,包括请求头等。根据请求头路由哪个服务

  • Filter: These are instances of Spring Framework GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request。过滤器请求和响应都可以被修改。

客户端发请求给服务端。中间有网关。先交给映射器,如果能处理就交给handler处理,然后交给一系列filer,然后给指定的服务,再返回回来给客户端。

-代表数组,可以设置Cookie等内容。只有断言成功了,才路由到指定的地址。

简单示例

创建微服务,使用initilizer,Group:com.mall.firelymall,``Artifact: firelymall-gatewaypackage:com.mall.firelymall.gateway`。 搜索gateway选中。

在gateway服务中开启注册服务发现@EnableDiscoveryClient,配置nacos注册中心地址applicaion.properties。这样gateway也注册到了nacos中,其他服务就能找到nacos,网关也能通过nacos找到其他服务

bootstrap.properties 填写nacos配置中心地址

再去nacos里创建命名空间gateway(项目与项目用命名空间隔离),然后在命名空间里创建文件firelymall-gateway.yml。

然后修改本地bootstrap.properties

然后重启,测试。

在项目里创建application.yml,根据条件转发到uri等

前端

Vue

跳过

三级分类

商品种类

  • CategoryController:返回商品种类

  • CategoryService:提供服务方法

  • CategoryServiceImpl:实现服务方法,从数据库从读取商品种类

启动项目

然后启动:renren-fast、nacos、gateway、product,renren-fast-vue。

之后点击菜单管理,点击新增

image-20221014124748253

然后,点击新增

image-20221014124907145

在renren中,http://localhost:8001/#/product-category,分类维护地址的展现的内容应该在项目中src/views/modules下的product/category.vue文件中。

没有该文件,就创建,自己写。

编写vue文件

在项目中src/views/modules下创建product/category.vue文件中。

image-20221014125745642

编写vue文件,然后重启测试查看

image-20221014130002421

使用element的树形菜单

image-20221014131226083

测试成功

网关转发

然后,重启访问,可以发现不能获取数据,是因为两个的端口不一样。因此通过网关进行处理。在renren-fast-vue中,需要修改网关接口,在static/config/index.js

接着让重新登录http://localhost:88/#/login,验证码是请求88的,所以不显示。而验证码是来源于fast后台的。

问题:他要去nacos中查找api服务,但是nacos里有的是fast服务,就通过网关过滤器把api改成fast服务

所以让fast注册到服务注册中心,这样请求88网关转发到8080fast。

让fast里加入注册中心的依赖。

在renren-fast项目中添加

然后在fast启动类上加上注解@EnableDiscoveryClient,重启,这时候需要将url进行转发。在gateway中按格式加入。

lb代表负载均衡。

跨域问题

url:http://localhost:8001/#/login

从8001访问88,引发CORS跨域请求,浏览器会拒绝跨域请求。具体来说当前页面是8001端口,但是要跳转88端口,这是不可以的(post请求json可以)。

问题描述:已拦截跨源请求:同源策略禁止8001端口页面读取位于 http://localhost:88/api/sys/login 的远程资源。(原因:CORS 头缺少 ‘Access-Control-Allow-Origin’)。

问题分析:这是一种跨域问题。访问的域名或端口和原来请求的域名端口一旦不同,请求就会被限制

  • 跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js施加的安全限制。(ajax可以)

  • 同源策略:是指协议,域名,端囗都要相同,其中有一个不同都会产生跨域;

跨域流程:

这个跨域请求的实现是通过预检请求实现的,先发送一个OPSTIONS探路,收到响应允许跨域后再发送真实请求

什么意思呢?跨域是要请求的、新的端口那个服务器限制的,不是浏览器限制的。

跨域的解决方案

  • 方法1:设置nginx包含admin和gateway。都先请求nginx,这样端口就统一了

img
  • 方法2:让服务器告诉预检请求能跨域

在响应头中添加:参考:https://blog.csdn.net/qq_38128179/article/details/84956552

  • Access-Control-Allow-Origin : 支持哪些来源的请求跨域

  • Access-Control-Allow-Method : 支持那些方法跨域

  • Access-Control-Allow-Credentials :跨域请求默认不包含cookie,设置为true可以包含cookie

  • Access-Control-Expose-Headers : 跨域请求暴露的字段

    • CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

  • Access-Control-Max-Age :表明该响应的有效时间为多少秒。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将失效

解决方法:在网关中定义“GulimallCorsConfiguration”类,该类用来做过滤,允许所有的请求跨域。

然后需要修改renren-fast项目,注释掉“io.renren.config.CorsConfig”类。然后再次进行访问。

修改gateway配置文件

修改vue文件

image-20221022181900950

删除/添加数据

删除数据

添加delete和append标识,并且增加复选框

测试删除数据,打开postman输入“ http://localhost:88/api/product/category/delete ”,请求方式设置为POST,为了比对效果,可以在删除之前查询数据库的pms_category表。

由于delete请求接收的是一个数组,所以这里使用JSON方式,传入了一个数组:

image-20221108112323749

再次查询数据库能够看到cat_id为1432的数据已经被删除了。

修改“product.controller.CategoryController”类,添加如下代码:

product.service.impl.CategoryServiceImpl

然而多数时候,我们并不希望删除数据,而是标记它被删除了,这就是逻辑删除;可以设置show_status为0,标记它已经被删除。

mybatis-plus的逻辑删除:

修改“product.entity.CategoryEntity”类,添加上@TableLogic,表明使用逻辑删除:

然后在POSTMan中测试一下是否能够满足需要。另外在“src/main/resources/application.yml”文件中,设置日志级别,打印出SQL语句:

打印的日志:

删除细节优化

添加数据

在模板上添加分类对话框

在data属性中增加对话框显示属性dialogVisible和提交数据category

分别添加添加确定对应函数

修改数据

添加修改按钮

使对话框回显数据并显示标题修改分类,由于与 增加分类公用统一对话框,所以需要添加属性title并定制函数submitData()

菜单拖动

初始化

开启拖拽功能

<el-tree>添加属性draggable开启拖拽功能

限制可拖拽范围

由于我们的菜单是三级分类,所以未防止超出三级的情况,有部分情况不允许被拖入:比如被拖拽的节点本身包含两级菜单,将其拖进第二层级的节点,那么最深层级就达到了四级,为防止这种情况的出现,我们需要编写在<el-tree>中绑定allow-drop属性并编写allowDrop()函数

allowDrop()的思路为将被拖拽节点的子节点通过递归遍历找出最深节点的level,然后将被拖拽节点的相对深度与目标节点的相对深度相加,看是否超出最大深度3

拖拽完成

拖拽完成后我们需要更新三个状态:

  1. 当前节点最新的父节点id,

  2. 当前拖拽节点的最新顺序

    遍历姊妹节点的顺序即为新顺序

  3. 当前拖拽节点的最新层级

    当前拖拽层级变化需要更新拖拽节点及其子节点

拖拽完成后需要更新变化的节点,根据被拖拽节点的防止位置的不同,变化的部分也有所不同

  • inner

    父节点为dropNode节点

    姊妹节点为dropNode的孩子节点

  • before/after

    父节点为dropNode的父节点

    姊妹节点为dropNode的父节点的孩子节点

设置菜单拖动开关

现在存在的一个问题是每次拖拽的时候,都会发送请求,更新数据库这样频繁的与数据库交互,现在想要实现一个拖拽过程中不更新数据库,拖拽完成后,通过批量保存统一提交拖拽后的数据。

现在还存在一个问题,如果是将一个菜单连续的拖拽,最终还放到了原来的位置,但是updateNode中却出现了很多节点更新信息,这样显然也是一个问题。

批量删除

添加删除按钮

<el-tree>中添加 ref="tree"属性以获得选中节点

最后更新于

这有帮助吗?