谷粒商城-树形目录开发-笔记五

谷粒商城-树形目录开发-笔记五

1.列表展示

1.后端

1.gulimall-peoduct application.yml配置

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://192.168.157.129:3306/gulimall_pms
    driver-class-name: com.mysql.jdbc.Driver
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss


mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1
      logic-not-delete-value: 0
server:
  port: 10000
logging:
  level:
    com.atguigu.gulimall: debug

2.gulimall-peoduct bootstrap.properties配置

spring.application.name=gulimall-product
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

3.开启服务注册

@SpringBootApplication
@EnableDiscoveryClient
public class GulimallProductApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(GulimallProductApplication.class, args);
    }

}

4.为CategoryEntity添加一个子目录字段

	/**
	 * 子目录集合
	 */
	@JsonInclude(JsonInclude.Include.NON_EMPTY)
	@TableField(exist=false)
	private List<CategoryEntity> children;

5.CategoryController list方法编写

/**
     * 查出所有分类以及子分类,以树形结构组装起来
     */
    @RequestMapping("/list/tree")
    public R list(){
    
    

        List<CategoryEntity> entities = categoryService.listWithTree();


        return R.ok().put("data", entities);
    }

6.categoryService 编写接口

 List<CategoryEntity> listWithTree();

7.CategoryServiceImpl 实现 listWithTree

@Override
    public List<CategoryEntity> listWithTree() {
    
    
        //1、查出所有分类
        List<CategoryEntity> entities = baseMapper.selectList(null);

        //2、组装成父子的树形结构

        //2.1)、找到所有的一级分类
        List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity ->
             categoryEntity.getParentCid() == 0
        ).map((menu)->{
    
    
            menu.setChildren(getChildrens(menu,entities));
            return menu;
        }).sorted((menu1,menu2)->{
    
    
            return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());

        return level1Menus;
    }

	//递归查找所有菜单的子菜单
    private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){
    
    

        List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
    
    
            return categoryEntity.getParentCid() == root.getCatId();
        }).map(categoryEntity -> {
    
    
            //1、找到子菜单
            categoryEntity.setChildren(getChildrens(categoryEntity,all));
            return categoryEntity;
        }).sorted((menu1,menu2)->{
    
    
            //2、菜单的排序
            return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());

        return children;
    }

8.postman测试访问

http://localhost:10001/product/category/list/tree

9.测试结果

image-20211004111417166

2.前端

1.启动前端项目

npm run dev

2.创建一级菜单

image-20211004113111800

创建成功后数据库sys_menu表里会多一条

image-20211005113136729

3.然后创建子菜单:

image-20211005113550663

4.新建category.vue文件

创建renren-fast-vue\src\views\modules\product目录,子所以是这样来创建,是因为product/category,对应于product-category

在renren-fast-vue\src\views\modules\product目录下新建category.vue文件

image-20211005114255348

5.category.vue完整案例

image-20211006165048730

扫描二维码关注公众号,回复: 13553315 查看本文章
<template>
  <div>
    <el-switch v-model="draggable" active-text="开启拖拽" inactive-text="关闭拖拽"></el-switch>
    <el-button v-if="draggable" @click="batchSave">批量保存</el-button>
    <el-button type="danger" @click="batchDelete">批量删除</el-button>
    <el-tree
      :data="menus"
      :props="defaultProps"
      :expand-on-click-node="false"
      show-checkbox
      node-key="catId"
      :default-expanded-keys="expandedKey"
      :draggable="draggable"
      :allow-drop="allowDrop"
      @node-drop="handleDrop"
      ref="menuTree"
    >
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>{
    
    {
    
     node.label }}</span>
        <span>
          <el-button
            v-if="node.level <=2"
            type="text"
            size="mini"
            @click="() => append(data)"
          >Append</el-button>
          <el-button type="text" size="mini" @click="edit(data)">edit</el-button>
          <el-button
            v-if="node.childNodes.length==0"
            type="text"
            size="mini"
            @click="() => remove(node, data)"
          >Delete</el-button>
        </span>
      </span>
    </el-tree>

    <el-dialog
      :title="title"
      :visible.sync="dialogVisible"
      width="30%"
      :close-on-click-modal="false"
    >
      <el-form :model="category">
        <el-form-item label="分类名称">
          <el-input v-model="category.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="图标">
          <el-input v-model="category.icon" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="计量单位">
          <el-input v-model="category.productUnit" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitData">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';

export default {
    
    
  //import引入的组件需要注入到对象中才能使用
  components: {
    
    },
  props: {
    
    },
  data() {
    
    
    return {
    
    
      pCid: [],
      draggable: false,
      updateNodes: [],
      maxLevel: 0,
      title: "",
      dialogType: "", //edit,add
      category: {
    
    
        name: "",
        parentCid: 0,
        catLevel: 0,
        showStatus: 1,
        sort: 0,
        productUnit: "",
        icon: "",
        catId: null
      },
      dialogVisible: false,
      menus: [],
      expandedKey: [],
      defaultProps: {
    
    
        children: "children",
        label: "name"
      }
    };
  },

  //计算属性 类似于data概念
  computed: {
    
    },
  //监控data中的数据变化
  watch: {
    
    },
  //方法集合
  methods: {
    
    
     //获取列表方法,将列表放入this.menus
    getMenus() {
    
    
      this.$http({
    
    
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get"
      }).then(({
     
      data }) => {
    
    
        console.log("成功获取到菜单数据...", data.data);
        this.menus = data.data;
      });
    },
      //el-tree中的 ref="menuTree"为组件起名,方面下面取值
     //批量删除方法,拿到this.$refs.menuTree组件中选中的元素
    batchDelete() {
    
    
      let catIds = [];
      let checkedNodes = this.$refs.menuTree.getCheckedNodes();
      console.log("被选中的元素", checkedNodes);
      for (let i = 0; i < checkedNodes.length; i++) {
    
    
        catIds.push(checkedNodes[i].catId);
      }
      this.$confirm(`是否批量删除【${
      
      catIds}】菜单?`, "提示", {
    
    
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
    
    
          this.$http({
    
    
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(catIds, false)
          }).then(({
     
      data }) => {
    
    
            this.$message({
    
    
              message: "菜单批量删除成功",
              type: "success"
            });
            this.getMenus();
          });
        })
        .catch(() => {
    
    });
    },
    //批量保存
    //此处才是拖动目录发起请求的地方
    //updateNodes是拖动修改的目录集合
    batchSave() {
    
    
      this.$http({
    
    
        url: this.$http.adornUrl("/product/category/update/sort"),
        method: "post",
        data: this.$http.adornData(this.updateNodes, false)
      }).then(({
     
      data }) => {
    
    
        this.$message({
    
    
          message: "菜单顺序等修改成功",
          type: "success"
        });
        //刷新出新的菜单
        this.getMenus();
        //设置需要默认展开的菜单
        this.expandedKey = this.pCid;
        this.updateNodes = [];
        this.maxLevel = 0;
        // this.pCid = 0;
      });
    },
    //拖拽成功完成时触发的事件
    //共四个参数,依次为:被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置(before、after、inner)、event
    handleDrop(draggingNode, dropNode, dropType, ev) {
    
    
      console.log("handleDrop: ", draggingNode, dropNode, dropType);
      //1、当前节点最新的父节点id
      let pCid = 0;
      //兄弟节点数组
      let siblings = null;
      //节点放在某个节点之前或之后
      if (dropType == "before" || dropType == "after") {
    
    
          //因为与dropNode前后同级关系,那么此节点的pid与dropNode的pid相同,默认为0
        pCid =
          dropNode.parent.data.catId == undefined
            ? 0
            : dropNode.parent.data.catId;
          //同级的目录有dropNode的父级下的所有子节点
        siblings = dropNode.parent.childNodes;
       //节点放在某个节点里
      } else {
    
    
         //放在里面,那么此节点是dropNode的子节点,那么pid为dropNode的catId
        pCid = dropNode.data.catId;
         //同级的目录有dropNode的所有子节点
        siblings = dropNode.childNodes;
      }
      //将这些父id存起来用做修改后自动展开修改的目录
      this.pCid.push(pCid);

      //2、当前拖拽节点的最新顺序,遍历兄弟节点,重新排序所有节点
      for (let i = 0; i < siblings.length; i++) {
    
    
        if (siblings[i].data.catId == draggingNode.data.catId) {
    
    
          //如果遍历的是当前正在拖拽的节点
          //draggingNode.level是element自带的层级与后台无关,前端计算出的层级(是该节点拖动之前的层级)
          let catLevel = draggingNode.level;
          //拖动之后的层级(siblings[i].level)与拖动之前的层级(draggingNode.level)不一致,就说明当前节点的层级发生变化
          if (siblings[i].level != draggingNode.level) {
    
    
            //当前节点的层级发生变化,将拖动之后的层级保存
            catLevel = siblings[i].level;
            //修改所有子节点的层级(此处传入的是拖动之后的节点)
            this.updateChildNodeLevel(siblings[i]);
          }
          //将修改后的节点放入updateNodes数组
          this.updateNodes.push({
    
    
            catId: siblings[i].data.catId,
            sort: i,
            parentCid: pCid,
            catLevel: catLevel
          });
        } else {
    
    
          //遍历的不是当前正在拖拽的节点
          //直接向updateNodes数组放入,此处只会修改排序sort
          this.updateNodes.push({
    
     catId: siblings[i].data.catId, sort: i });
        }
      }

      //3、当前拖拽节点的最新层级
      console.log("updateNodes", this.updateNodes);
    },
   //修改所有子节点的层级     
    updateChildNodeLevel(node) {
    
    
      //判断是否有子节点
      if (node.childNodes.length > 0) {
    
    
        //有子节点就遍历
        for (let i = 0; i < node.childNodes.length; i++) {
    
    
          //去除遍历的当前子节点放入cNode
          var cNode = node.childNodes[i].data;
          //同样也放入updateNodes数组
          this.updateNodes.push({
    
    
            catId: cNode.catId,
            //取出当前子节点前端计算出的子节点的层级
            catLevel: node.childNodes[i].level
          });
          //递归修改所有子节点排序
          this.updateChildNodeLevel(node.childNodes[i]);
        }
      }
    },
    //判断拖动放置条件
    allowDrop(draggingNode, dropNode, type) {
    
    
      //1、被拖动的当前节点以及所在的父节点总层数不能大于3

      //1)、被拖动的当前节点总层数
      console.log("allowDrop:", draggingNode, dropNode, type);
      //
      this.countNodeLevel(draggingNode);
      //当前正在拖动的节点+父节点所在的深度不大于3即可
      let deep = Math.abs(this.maxLevel - draggingNode.level) + 1;
      console.log("深度:", deep);

      //   this.maxLevel
      if (type == "inner") {
    
    
        // console.log(
        //   `this.maxLevel:${this.maxLevel};draggingNode.data.catLevel:${draggingNode.data.catLevel};dropNode.level:${dropNode.level}`
        // );
        return deep + dropNode.level <= 3;
      } else {
    
    
        return deep + dropNode.parent.level <= 3;
      }
    },
    找到所有子节点,求出最大深度maxLevel
    countNodeLevel(node) {
    
    
      if (node.childNodes != null && node.childNodes.length > 0) {
    
    
        for (let i = 0; i < node.childNodes.length; i++) {
    
    
          if (node.childNodes[i].level > this.maxLevel) {
    
    
            this.maxLevel = node.childNodes[i].level;
          }
          //递归
          this.countNodeLevel(node.childNodes[i]);
        }
      }
    },
    //编辑按钮方法
    edit(data) {
    
    
      console.log("要修改的数据", data);
      this.dialogType = "edit";
      this.title = "修改分类";
      this.dialogVisible = true;

      //发送请求获取当前节点最新的数据
      this.$http({
    
    
        url: this.$http.adornUrl(`/product/category/info/${
      
      data.catId}`),
        method: "get"
      }).then(({
     
      data }) => {
    
    
        //请求成功
        console.log("要回显的数据", data);
        this.category.name = data.data.name;
        this.category.catId = data.data.catId;
        this.category.icon = data.data.icon;
        this.category.productUnit = data.data.productUnit;
        this.category.parentCid = data.data.parentCid;
        this.category.catLevel = data.data.catLevel;
        this.category.sort = data.data.sort;
        this.category.showStatus = data.data.showStatus;
        /**
         *         parentCid: 0,
        catLevel: 0,
        showStatus: 1,
        sort: 0,
         */
      });
    },
    //新增按钮方法
    append(data) {
    
    
      console.log("append", data);
      this.dialogType = "add";
      this.title = "添加分类";
      this.dialogVisible = true;
      this.category.parentCid = data.catId;
      this.category.catLevel = data.catLevel * 1 + 1;
      this.category.catId = null;
      this.category.name = "";
      this.category.icon = "";
      this.category.productUnit = "";
      this.category.sort = 0;
      this.category.showStatus = 1;
    },
	//提交按钮方法
    submitData() {
    
    
      if (this.dialogType == "add") {
    
    
        this.addCategory();
      }
      if (this.dialogType == "edit") {
    
    
        this.editCategory();
      }
    },
    //修改三级分类数据
    editCategory() {
    
    
      var {
    
     catId, name, icon, productUnit } = this.category;
      this.$http({
    
    
        url: this.$http.adornUrl("/product/category/update"),
        method: "post",
        data: this.$http.adornData({
    
     catId, name, icon, productUnit }, false)
      }).then(({
     
      data }) => {
    
    
        this.$message({
    
    
          message: "菜单修改成功",
          type: "success"
        });
        //关闭对话框
        this.dialogVisible = false;
        //刷新出新的菜单
        this.getMenus();
        //设置需要默认展开的菜单
        this.expandedKey = [this.category.parentCid];
      });
    },
    //添加三级分类
    addCategory() {
    
    
      console.log("提交的三级分类数据", this.category);
      this.$http({
    
    
        url: this.$http.adornUrl("/product/category/save"),
        method: "post",
        data: this.$http.adornData(this.category, false)
      }).then(({
     
      data }) => {
    
    
        this.$message({
    
    
          message: "菜单保存成功",
          type: "success"
        });
        //关闭对话框
        this.dialogVisible = false;
        //刷新出新的菜单
        this.getMenus();
        //设置需要默认展开的菜单
        this.expandedKey = [this.category.parentCid];
      });
    },
	//批量删除按钮方法
    remove(node, data) {
    
    
      var ids = [data.catId];
      this.$confirm(`是否删除【${
      
      data.name}】菜单?`, "提示", {
    
    
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
    
    
          this.$http({
    
    
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false)
          }).then(({
     
      data }) => {
    
    
            this.$message({
    
    
              message: "菜单删除成功",
              type: "success"
            });
            //刷新出新的菜单
            this.getMenus();
            //设置需要默认展开的菜单
            this.expandedKey = [node.parent.data.catId];
          });
        })
        .catch(() => {
    
    });

      console.log("remove", node, data);
    }
  },
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {
    
    
    this.getMenus();
  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    
    },
  beforeCreate() {
    
    }, //生命周期 - 创建之前
  beforeMount() {
    
    }, //生命周期 - 挂载之前
  beforeUpdate() {
    
    }, //生命周期 - 更新之前
  updated() {
    
    }, //生命周期 - 更新之后
  beforeDestroy() {
    
    }, //生命周期 - 销毁之前
  destroyed() {
    
    }, //生命周期 - 销毁完成
  activated() {
    
    } //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>

3.问题

1.配置网关以及路径重写

刷新页面出现404异常,查看请求发现,请求的是“http://localhost:8080/renren-fast/product/category/list/tree”

image-20211005152116985

解决:

替换“static\config\index.js”文件中的“window.SITE_CONFIG[‘baseUrl’]”

替换前:

window.SITE_CONFIG['baseUrl'] = 'http://localhost:8080/renren-fast';

替换后:

 window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';

image-20211005152449067

http://localhost:88,这个地址是我们网关微服务的接口。

这里我们需要通过网关来完成路径的映射,因此将renren-fast注册到nacos注册中心中,并添加配置中心

application.yml添加

application:
    name: renren-fast
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

配置网关路由,前台的所有请求都是经由“http://localhost:88/api”来转发的,在“gulimall-gateway”中添加路由规则:

 - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**

但是这样做也引入了另外的一个问题,再次访问:http://localhost:8001/#/login,发现验证码不再显示:

分析原因:

  1. 现在的验证码请求路径为,http://localhost:88/api/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6
  2. 原始的验证码请求路径:http://localhost:8001/renren-fast/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6

在admin_route的路由规则下,在访问路径中包含了“api”,因此它会将它转发到renren-fast,网关在转发的时候,会使用网关的前缀信息,为了能够正常的取得验证码,我们需要对请求路径进行重写

关于路径重写

个人理解路径重写:修改路径前缀

修改“admin_route”路由规则:

- id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{
    
    segment}

2.网关统一配置跨域

再次访问:http://localhost:8001/#/login,验证码能够正常的加载了。

但是很不幸新的问题又产生了,访问被拒绝了

image-20211005164406057

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

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

none

解决跨域的办法:(这里我们开发使用第二种)

image-20200425193136641

image-20211005165337352

image-20200425193614185

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

发现仍然登录失败,renren-fast也为我们做了这一步,我们要去除他的

image-20211005164944074

解决:将renren-fast的comfig包下的CorsConfig中的addCorsMappings方法注释起来

image-20211005165602551

登录成功

2.目录删除

使用逆向工程生成的批量删除即可

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

测试结果为物理删除.应改为逻辑删除

gulimall-product配置全局的逻辑删除规则,在“src/main/resources/application.yml”文件中添加如下内容:

mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      #逻辑删除值
      logic-delete-value: 1
      #逻辑未删除值
      logic-not-delete-value: 0

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

/**
	 * 是否显示[0-不显示,1显示]
	 */
	@TableLogic(value = "1",delval = "0")
	private Integer showStatus;

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

logging:
  level:
    com.bigdata.gulimall.product: debug

再次测试:逻辑删除成功

image-20211006163252411

3.目录拖动(此部分逻辑代码在前端请参照1.1.2.5)

同一个菜单内拖动 正常
拖动到父菜单的前面或后面 正常
拖动到父菜单同级的另外一个菜单中 正常

关注的焦点在于,拖动到目标节点中,使得目标节点的catlevel+deep小于3即可。

拖动菜单时需要修改顺序和级别

需要考虑两种类型节点的catLevel

一种关系是:如果是同一个节点下的子节点的前后移动,则不需要修改其catLevel

如果是拖动到另外一个节点内或父节点中,则要考虑修改其catLevel

如果拖动到与父节点平级的节点关系中,则要将该拖动的节点的catLevel,设置为兄弟节点的Level,

先考虑parentCid还是先考虑catLevel?

两种关系在耦合

另外还有一种是前后拖动的情况

哪个范围最大?

肯定是拖动类型关系最大,

如果是前后拖动,则拖动后需要看待拖动节点的层级和设置待拖动节点的parentId,

如果待拖动节点和目标节点的层级相同,则认为是同级拖动,只需要修改节点的先后顺序即可;

否则认为是跨级拖动,则需要修改层级和重新设置parentID

如果

以拖动类型来分,并不合适,比较合适的是跨级拖动和同级拖动

如何判断是跨级拖动还是同级拖动,根据拖动的层级来看,如果是同一级的拖动,只需要修改先后顺序即可,但是这样也会存在一个问题,就是当拖动到另外一个分组下的同级目录中,显然也需要修改parentID,究竟什么样的模型最好呢?

另外也可以判断在跨级移动时,跨级后的parentID是否相同,如果不相同,则认为是在不同目录下的跨级移动需要修改parentID。

顺序、catLevel和parentID

同级移动:

(1)首先判断待移动节点和目标节点的catLevel是否相同,

(2)相同则认为是同级移动,

如果此时移动后目标节点的parentID和待移动节点的相同,但是移动类型是前后移动,只需要调整顺序即可,此时移动类型是inner,则需要修改catLevel和parentId和顺序

如果此时移动后目标节点的parentID和待移动节点的不相同,但是移动类型是前后移动,则需要调整顺序和parentId,此时移动类型是inner,则需要修改catLevel和parentId和顺序

通过这两步的操作能看到一些共性,如果抽取移动类型作为大的分类,则在这种分类下,

如果是前后移动,则分为下面几种情况:

同级别下的前后移动:界定标准为catLevel相同,但是又可以分为parentID相同和parentID不同,parent相同时,只需要修改顺序即可;parentID不同时,需要修改parentID和顺序

不同级别下的前后移动:界定标准为catLevel不同,此时无论如何都要修改parentID,顺序和catLevel

如果是inner类型移动,则分为一下的几种情况。

此时不论是同级inner,还是跨级innner,都需要修改parentID,顺序和catLevel

哪种情况需要更新子节点呢?

那就要看要拖拽的节点是否含有子节点,如果有子节点,则需要更新子节点的catLevel,不需要更新它之间的顺序和parentId,只需要更新catLevel即可。这种更新子节点的Level应该归类,目前的目标是只要有子节点就更新它的catLevel,

(2)如果待移动节点和目标节点的catLevel不同,则认为是跨级移动。如果是移动到父节点中,则需要设置catLevel,parentID和顺序。此时需要分两种情况来考虑,如果是移动到父节点中,则需要设置catLevel,parentID和顺序,如果是移动到兄弟节点中,则需要设置

包含移动到父节点同级目录,兄弟节点中。

在gulimall-product/src/main/java/site/zhourui/gulimall/product/controller/CategoryController.java新增

@RequestMapping("/update/sort")
    //@RequiresPermissions("product:category:update")
    public R updateSort(@RequestBody CategoryEntity[] category){
    
    
        categoryService.updateBatchById(Arrays.asList(category));
        return R.ok();
    }

4.新增不用修改,用逆向工程生成的即可

猜你喜欢

转载自blog.csdn.net/qq_31745863/article/details/120646105
今日推荐