谷粒商城实战笔记-54-商品服务-API-三级分类-拖拽效果

news2024/11/16 13:07:08

文章目录

  • 一,54-商品服务-API-三级分类-修改-拖拽效果
    • 1,el-tree控件加上允许拖拽的属性
    • 2,是否允许拖拽
    • 3,完整代码

在这里插入图片描述

一,54-商品服务-API-三级分类-修改-拖拽效果

本节的主要内容是给三级分类树形结构加上拖拽功能,并且根据分类不能大于三级的规则判断是否能拖拽。

1,el-tree控件加上允许拖拽的属性

el-tree控件加上允许拖拽的属性draggable,此外还需要根据层级判断是否能拖动,通过给allow-drop绑定事件allowDrag实现这个需求。
在这里插入图片描述

	allowDrag(draggingNode, dropNode, dropPosition) {
      console.log(draggingNode, dropNode, dropPosition);
      return true
    },

在 Element UI(El-UI)的树组件 el-tree 中,allow-drop 事件是一个自定义槽函数,用于控制是否允许将一个节点拖放到另一个节点上。这个函数接受三个参数,分别代表正在拖动的节点、可能的放置目标节点以及放置位置。这三个参数具体如下:

  1. draggingNode:
    这个参数是 TreeNode 类型的对象,表示当前正在被拖动的节点。它包含了关于拖动节点的所有信息,如节点的数据、状态等。

  2. dropNode:
    同样是 TreeNode 类型的对象,表示潜在的放置目标节点。这是你可能要将 draggingNode 放置到的节点。如果拖动过程中没有特定的放置目标(例如,拖动到树的空白区域),这个参数可能是 undefined 或者不适用。

  3. dropPosition:
    表示相对于 dropNode 的放置位置。这是一个字符串,可以是 'before''after''inner',分别表示拖动的节点将放置在目标节点之前、之后或内部。如果 dropNodeundefined,则这个参数可能表示放置在树的顶部或底部。

allow-drop 函数应该返回一个布尔值,指示是否允许进行拖放操作。如果返回 true,则允许拖放;如果返回 false,则阻止拖放操作。例如,在你的代码中:

接下来实现这个函数的逻辑。

原则是当前拖动的阶段到达要放置的位置后,层级数不能超过3,所以核心有3点:

  • ①计算出以拖动结点为根结点的子树的深度deep。
  • ②结合目标结点的深度及放置位置的类型,判断新位置的层级level。
  • ③deep + level <=3 时允许拖动。

关于第②点,新位置的类型可能有三种:

  • prev,目标节点的前面
  • inner,目标节点的子节点
  • next,目标节点的后面

2,是否允许拖拽

递归统计draggingNode子树的深度。

	// 递归计算draggingNode子树的深度
    countDraggingNodeDeep(draggingNode) {
      var deep = 0;
      if (draggingNode.childNodes && draggingNode.childNodes.length > 0) {
        debugger
        draggingNode.childNodes.forEach(child => {
          deep = Math.max(deep, this.countDraggingNodeDeep(child));
        });
      }
      return deep + 1;
    },

结合draggingNode子树的深度和位置判断是否能拖动。

	allowDrag(draggingNode, dropNode, dropPosition) {
      console.log(draggingNode, dropNode, dropPosition);
      var deep = this.countDraggingNodeDeep(draggingNode);

      console.log(deep,  dropNode.data.catLevel + deep);

      // 根据dropPosition结合dropNode.catLevel来判断draggingNode新位置的位置是否合法
      if (dropPosition === "prev" || dropPosition === "next") {
        return dropNode.data.catLevel + deep - 1 <= 3;
      } else if (dropPosition === "inner" ) {
        return dropNode.data.catLevel + deep <= 3;
      }
    },

3,完整代码

<template>
  <div>
    <el-tree
      node-key="catId"
      :data="menus"
      :props="defaultProps"
      :expand-on-click-node="false"
      show-checkbox
      :default-expanded-keys="expandedKeys"
      :allow-drop="allowDrag"
      draggable
    >
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>{{ node.label }}</span>
        <span>
          <el-button
            v-if="node.level <= 2"
            size="mini"
            @click="() => append(data)"
          >
            Append
          </el-button>
          <el-button
            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="dialogTitle" :visible.sync="dialogFormVisible" :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>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitCategory">确 定</el-button
        >
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  components: {},
  props: {},
  data() {
    return {
      dialogTitle: "", // 编辑窗口标题,新增分类,修改分类
      dialogType: "", // 编辑窗口类型,create表示append,edit表示edit
      dialogFormVisible: false,
      menus: [],
      category: {name: "", parentCid: 0, catLevel: 0, sort: 0, showStatus: 1, icon: "", productUnit: "", catId: null},
      expandedKeys: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },

  methods: {
    allowDrag(draggingNode, dropNode, dropPosition) {
      console.log(draggingNode, dropNode, dropPosition);
      var deep = this.countDraggingNodeDeep(draggingNode);

      console.log(deep,  dropNode.data.catLevel + deep);

      // 根据dropPosition结合dropNode.catLevel来判断draggingNode新位置的位置是否合法
      if (dropPosition === "prev" || dropPosition === "next") {
        return dropNode.data.catLevel + deep - 1 <= 3;
      } else if (dropPosition === "inner" ) {
        return dropNode.data.catLevel + deep <= 3;
      }
    },

    // 递归计算draggingNode子树的深度
    countDraggingNodeDeep(draggingNode) {
      var deep = 0;
      if (draggingNode.childNodes && draggingNode.childNodes.length > 0) {
        debugger
        draggingNode.childNodes.forEach(child => {
          deep = Math.max(deep, this.countDraggingNodeDeep(child));
        });
      }
      return deep + 1;
    },
    append(data) {
      console.log(data);
      this.dialogType = "create";
      this.dialogTitle = "新增分类";
      this.dialogFormVisible = true;
      this.category = {
        name: "",
        parentCid: data.catId,
        catLevel: data.level + 1,
        sort: 0,
        showStatus: 1
      };
    },
    edit(data) {
      console.log(data);
      this.dialogType = "edit";
      this.dialogTitle = "修改分类";
      this.dialogFormVisible = true;
      
      // 根据catId查询最新数据
      this.$http({
        url: this.$http.adornUrl(`/product/category/info/${data.catId}`),
        method: "get",
        data: this.$http.adornData({ catId: data.catId }, false),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.category = {...data.data };
        } else {
          this.$message.error(data.msg);
        }
      });
    },
    submitCategory() {
      if (this.dialogType === "create") {
        this.addCategory();
      } else if (this.dialogType === "edit") {
        this.updateCategory();
      }
    },
    updateCategory() {
      var {catId, name, icon, productUnit } = this.category
      console.log( this.category);
      this.$http({
        url: this.$http.adornUrl("/product/category/update"),
        method: "post",
        data: this.$http.adornData({catId, name, icon, productUnit }, false),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.$message({
            message: "修改成功",
            type: "success",
            duration: 1500,
            onClose: () => {
              console.log("修改成功,关闭消息提示");
              this.dialogFormVisible = false;
              this.getMenus(); // 重新获取数据
              this.expandedKeys =[ this.category.parentCid == 0 ? this.category.catId : this.category.parentCid ]; // 重置展开节点
              console.log(this.expandedKeys);
            },
          });
        } else {
          this.$message.error(data.msg);
        }
      });
    },
    addCategory() {
      this.$http({
        url: this.$http.adornUrl("/product/category/save"),
        method: "post",
        data: this.$http.adornData(this.category, false),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.$message({
            message: "添加成功",
            type: "success",
            duration: 1500,
            onClose: () => {
              console.log("添加成功,关闭消息提示");
              this.dialogFormVisible = false;
              this.getMenus(); // 重新获取数据
              this.expandedKeys =[ this.category.parentCid ]; // 重置展开节点
            },
          });
        } else {
          this.$message.error(data.msg);
        }
      });
    },
    remove(node, data) {
      console.log(node, data);
      var ids = [node.data.catId];

      this.$confirm(
        `确定对[id=${ids.join(",")}]进行[${
          ids.length == 1 ? "删除" : "批量删除"
        }]操作?`,
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      )
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            if (data && data.code === 0) {
              this.$message({
                message: "操作成功",
                type: "success",
                duration: 1500,
                onClose: () => {
                  console.log("删除成功,关闭消息提示");
                  this.getMenus(); // 重新获取数据
                  this.expandedKeys = [ node.parent.data.catId ]; // 重置展开节点
                },
              });
            } else {
              this.$message.error(data.msg);
            }
          });
        })
        .catch(() => {});
    },
    // 获取分类数据
    getMenus() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get",
      }).then(({ data }) => {
        console.log(data);
        this.dataListLoading = false;
        this.menus = data.data;
      });
    },
  },
  created() {
    this.getMenus(); // 获取分类数据
  },
};
</script>
<style scoped>
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1946595.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS怎么关闭自动锁屏?

禁止自动锁屏 有时候几分钟不用Centos&#xff0c;系统就自动锁屏了&#xff0c;这是一种安全措施&#xff0c;防止别人趁你不在时使用你的系统。但对于大部分人而言&#xff0c;这是没有必要的&#xff0c;尤其是Centos虚拟机&#xff0c;里面没啥重要的东西&#xff0c;每次…

基于面向对象重构模型训练器

引言 深度学习领域我们常用jupyter来演练代码&#xff0c;但实际生产环境中不可能像jupyter一样&#xff0c;所有代码逻辑都在面向过程编程&#xff0c;这会导致代码可复用性差&#xff0c;维护难度高。 前面这篇文章 基于pytorch可视化重学线性回归模型 已经封装了数据加载器…

Jenkins卡在等待界面解决方法

一、问题 部署jenkins服务器出现Please wait while Jenkins is getting ready to work。 二、原因分析 jenkins里面文件指向国外的官网&#xff0c;因为防火墙的原因连不上。 三、解决方法 将配置文件里面的url换成国内镜像&#xff1a; &#xff08;1&#xff09;修改配…

[k8s源码]9.workqueue

client-go 是一个库&#xff0c;提供了与 Kubernetes API 服务器交互的基础设施。它提供了诸如 Informer、Lister、ClientSet 等工具&#xff0c;用于监听、缓存和操作 Kubernetes 资源。而自定义控制器则利用这些工具来实现特定的业务逻辑和自动化任务。业务逻辑实现&#xff…

jmeter实战(2)- 入门使用教程

一、运行Jmeter 参考上一篇博客&#xff1a;jmeter实战&#xff08;1&#xff09;- Mac环境安装 二、创建线程组 JMeter的线程组是进行负载测试的基本构建单元&#xff0c;它用于模拟多个用户对目标系统进行并发访问。线程组中的属性允许你控制测试的并发级别和执行模式。 1.…

聚观早报 | Meta将推出新款AR眼镜;iPhone SE 4将升级显示屏

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 7月24日消息 Meta将推出新款AR眼镜 iPhone SE 4将升级显示屏 华硕天选Air 2024开启预约 巴菲特再次减持比亚迪股…

DT浏览器首页征集收录海内外网址

DT浏览器首页征集收录海内外网址&#xff0c;要求页面整洁&#xff0c;内容丰富&#xff0c;知识性和可读性强&#xff0c;符合大众价值观&#xff0c;不含恶意代码

linux添加普通用户后无法使用K8S的kubectl命令怎么办/Linux普通用户管理K8S/Linux下普通用户无法使用K8S命令

1.给Linux添加普通用户 sudo useradd mqq #添加mqq账号 sudo passwd mqq #给mqq账号设置密码&#xff0c;需要输入2次&#xff0c;我输入密码是Admin1232.利用mqq用户输入K8S命令报错 3.给mqq用户提权 suduers文件位于路径/etc/sudoers #编辑文件/etc/sudoers vim /etc/su…

第十四章 数据库

第十四章 数据库 14.1 引言 数据存储在传统上是使用单独的没有关联的文件&#xff0c;称为平面文件 14.1.1 定义 定义&#xff1a;数据库是一个组织内被应用程序使用的逻辑相一致的相关数据的集合 14.1.2 数据库的优点 数据库的优点&#xff1a; 冗余少避免数据的不一致…

ZLMRTCClient配置说明与用法(含示例)

webRTC播放视频 后面在项目中会用到通过推拉播放视频流的技术&#xff0c;所以最近预研了一下webRTC 首先需要引入封装好的webRTC客户端的js文件ZLMRTCClient.js 下面是地址需要的自行下载 http://my.zsyou.top/2024/ZLMRTCClient.js 配置说明 new ZLMRTCClient.Endpoint…

小猪佩奇.js

闲着没事 使用js 画一个小猪佩奇把 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</tit…

BUUCTF [MRCTF2020]Ezpop

这道题对于刚接触到pop链的我直接把我整懵了&#xff0c;一边看着魔术方法一边分析 魔术方法可以看这里PHP 魔术方法 - 简介 - PHP 魔术方法 - 简单教程&#xff0c;简单编程 (twle.cn) 代码解析 经过以上的分析我们可以理一下解题思路&#xff1a;接收参数反序列化之前先触发…

基于 HTML+ECharts 实现智慧交通数据可视化大屏(含源码)

构建智慧交通数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 随着城市化进程的加快&#xff0c;智慧交通系统已成为提升城市管理效率和居民生活质量的关键。通过数据可视化&#xff0c;交通管理部门可以实时监控交通流量、事故发生率、道路状况等关键指标&#xff0c;…

C#使用csvhelper实现csv的操作

新建控制台项目 安装csvhelper 33.0.1 写入csv 新建Foo.cs namespace CsvSut02;public class Foo {public int Id { get; set; }public string Name { get; set; } }批量写入 using System.Globalization; using CsvHelper; using CsvHelper.Configuration;namespace Csv…

Python3网络爬虫开发实战(2)爬虫基础库

文章目录 一、urllib1. urlparse 实现 URL 的识别和分段2. urlunparse 用于构造 URL3. urljoin 用于两个链接的拼接4. urlencode 将 params 字典序列化为 params 字符串5. parse_qs 和 parse_qsl 用于将 params 字符串反序列化为 params 字典或列表6. quote 和 unquote 对 URL的…

通信原理思科实验五:家庭终端以太网接入Internet实验

实验五 家庭终端以太网接入Internet实验 一实验内容 二实验目的 三实验原理 四实验步骤 1.按照上图选择对应的设备&#xff0c;并连接起来 为路由器R0两个端口配置IP 为路由器R1端口配置IP 为路由器设备增加RIP&#xff0c;配置接入互联网的IP的动态路由项 5.为路由器R1配置静…

Mysql-索引视图

目录 1.视图 1.1什么是视图 1.2为什么需要视图 1.3视图的作用和优点 1.4创建视图 1.5更新视图 1.6视图使用规则 1.7修改视图 1.8删除视图 2.索引 2.1什么是索引 2.2索引特点 2.3索引分类 2.4索引优缺点 2.5创建索引 2.6查看索引 2.7删除索引 1.视图 1.1什么是…

[Javascript】前端面试基础3【每日学习并更新10】

Web开发中会话跟踪的方法有那些 cookiesessionurl重写隐藏inputip地址 JS基本数据类型 String&#xff1a;用于表示文本数据。Number&#xff1a;用于表示数值&#xff0c;包括整数和浮点数。BigInt&#xff1a;用于表示任意精度的整数。Boolean&#xff1a;用于表示逻辑值…

流量录制与回放:jvm-sandbox-repeater工具详解

在软件开发和测试过程中&#xff0c;流量录制与回放是一个非常重要的环节&#xff0c;它可以帮助开发者验证系统在特定条件下的行为是否符合预期。本文将详细介绍一款强大的流量录制回放工具——jvm-sandbox-repeater&#xff0c;以及如何利用它来提高软件测试的效率和质量。 …

linux进程——解析命令行参数——环境变量详解

前言&#xff1a;本节内容还是linux进程&#xff0c; 主要讲解里面的环境变量——我们首先要知道的就是环境变量其实就是操作系统维护的一组kv值&#xff0c; 环境变量是系统提供的一组 变量名变量值 形式的变量。不同的环境变量之间具有不同的用途&#xff0c; 并且具有全局属…