谷粒商城实战笔记-56~57-商品服务-API-三级分类-修改-拖拽功能完成

文章目录

  • 一,56-商品服务-API-三级分类-修改-拖拽功能完成
  • 二,57-商品服务-API-三级分类-修改-批量拖拽效果
    • 1,增加按钮
    • 2,多次拖拽一次保存
    • 完整代码

在构建商品服务API中的三级分类修改功能时,拖拽排序是一个直观且高效的管理方式。通过允许用户直接在界面上调整分类的顺序,可以显著提升用户体验和操作效率。在实现这一功能的过程中,我们不仅关注了单个分类的即时更新,还考虑到了多级分类同时变化的情况,以及如何批量处理这些变化。

在上一节中,我们已经完成了拖拽功能的基础部分,包括对分类顺序、级别和父节点的更新逻辑。现在,我们将进一步优化该功能,以提供更完善的用户体验。

首先,增加了一个可控制的开关按钮,用于启动或禁用拖拽功能。这不仅增强了系统的灵活性,也避免了在不需要进行分类调整时可能出现的误操作。用户可以根据实际需求选择是否启用拖拽模式,这为操作者提供了更大的自主权。

其次,为了提高效率,我们引入了“批量保存拖拽”按钮。当用户进行了多次拖拽操作后,不必逐一保存每个分类的变动,而是可以通过点击这个按钮一次性提交所有更改。这样不仅简化了工作流程,还减少了网络请求次数,提高了系统响应速度。

在实现批量保存功能时,我们使用了Vue.js框架中的$http方法来发起POST请求,将包含所有变动信息的数组nodesInfoAfterDrop发送给后端。后端接收到请求后,利用updateBatchById方法批量更新数据库中的分类信息。如果更新成功,前端会显示成功的消息,并自动重新加载分类列表,确保界面与数据库状态保持一致。

此外,为了保证数据的准确性,在批量保存后,我们还重置了一些关键变量,如expandedKeys和draggingNodeNewPId,确保下一次操作时从一个干净的状态开始。这些细节上的考虑,使得整个拖拽和保存流程更加健壮和可靠。

综上所述,通过增加控制开关和批量保存功能,我们的商品服务API中的三级分类修改功能得到了显著增强,不仅提升了用户体验,也提高了管理效率,是系统功能完善的重要一步。

一,56-商品服务-API-三级分类-修改-拖拽功能完成

上一节已经把需要更新的数据都封装好了,三种结点的数据需要更新。

  • ① 顺序发生变化,需要更新sort值

在这里插入图片描述

  • ② 分类级别发生变化,需要更新catLevel

在这里插入图片描述

  • ③ 父节点发生变化,需要更新parentCid

在这里插入图片描述

后端接口代码,根据cid批量更新。

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

二,57-商品服务-API-三级分类-修改-批量拖拽效果

基于上一节,做两个优化。

1,增加按钮

增加一个开关,控制拖拽功能的开启,只有开启了拖拽功能,才能拖拽。

在这里插入图片描述

2,多次拖拽一次保存

增加一个按钮用来批量保存拖拽后的节点信息。

 <el-button @click="batchSaveDrag" v-if="draggable">批量保存拖拽</el-button>

并绑定一个click事件。

batchSaveDrag() {// 4,调用后端接口,将数组nodesInfoAfterDrop发送给后端this.$http({url: this.$http.adornUrl("/product/category/update/sort"),method: "post",data: this.$http.adornData(this.nodesInfoAfterDrop, false),}).then(({ data }) => {if (data && data.code === 0) {this.$message({message: "操作成功",type: "success",duration: 1500,onClose: () => {console.log("删除成功,关闭消息提示");this.getMenus(); // 重新获取数据this.expandedKeys = [this.draggingNodeNewPId]; // 重置展开节点this.draggingNodeNewPId = 0; // 重置����的新parentCidthis.nodesInfoAfterDrop = [];},});} else {this.$message.error(data.msg);}});},

完整代码

<template><div><el-switchstyle="display: block"v-model="draggable"active-color="#13ce66"inactive-color="#ff4949"active-text="开启拖拽"inactive-text="关闭拖拽"></el-switch><el-button @click="batchSaveDrag" v-if="draggable">批量保存拖拽</el-button><el-treenode-key="catId":data="menus":props="defaultProps":expand-on-click-node="false"show-checkbox:default-expanded-keys="expandedKeys":allow-drop="allowDrag"@node-drop="nodeDrop":draggable="draggable"><span class="custom-tree-node" slot-scope="{ node, data }"><span>{{ node.label }}</span><span><el-buttonv-if="node.level <= 2"size="mini"@click="() => append(data)">Append</el-button><el-button size="mini" @click="() => edit(data)"> Edit </el-button><el-buttonv-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-inputv-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 {draggingNodeNewPId: 0,draggable: false,nodesInfoAfterDrop: [],dialogTitle: "", // 编辑窗口标题,新增分类,修改分类dialogType: "", // 编辑窗口类型,create表示append,edit表示editdialogFormVisible: false,menus: [],category: {name: "",parentCid: 0,catLevel: 0,sort: 0,showStatus: 1,icon: "",productUnit: "",catId: null,},expandedKeys: [],defaultProps: {children: "children",label: "name",},};},methods: {nodeDrop(draggingNode, dropNode, dropPosition) {console.log("draggingNode:", draggingNode, dropNode, dropPosition);// 1,更新draggingNode的parentCid,根据dropPosition的不同值,分为两种情况if (dropPosition === "before" || dropPosition === "after") {this.draggingNodeNewPId = dropNode.data.parentCid;} else {this.draggingNodeNewPId = dropNode.data.cid;}console.log("draggingNodeNewPId:", this.draggingNodeNewPId, dropNode.data.parentCid, dropNode.data.catId);// 2,更新draggingNode及其子节点的sort// 2.1如果draggingNode的parentCid没有更新,只需要重排draggingNode的兄弟结点的sort;// 2.2如果draggingNode的parentCid有更新,不仅需要重排draggingNode的原来的兄弟结点的sort,还需要重排draggingNode新的兄弟结点的sortvar siblingNodesOld = [];var siblingNodesNew = [];// draggingNode.parent为空,所以要根据parentCid找到其parent。// 写一个根据cid从menus中查询结点的函数let draggingNodeOldParentNode = this.getNodeByCid(draggingNode.data.parentCid,this.getLevel1Nodes(dropNode));console.log("draggingNodeOldParentNode:", draggingNodeOldParentNode);siblingNodesOld = draggingNodeOldParentNode.childNodes;console.log("siblingNodesOld:", siblingNodesOld);if (draggingNode.data.parentCid !== this.draggingNodeNewPId) {if (dropPosition === "before" || dropPosition === "after") {siblingNodesNew = dropNode.parent.childNodes;} else {siblingNodesNew = dropNode.childNodes;}}for (var i = 0; i < siblingNodesOld.length; i++) {this.nodesInfoAfterDrop.push({catId: siblingNodesOld[i].data.catId,sort: i,});}console.log("update sortu0....",siblingNodesNew,siblingNodesOld,dropNode,dropPosition);for (var i = 0; i < siblingNodesNew.length; i++) {this.nodesInfoAfterDrop.push({catId: siblingNodesNew[i].data.catId,sort: i,});}console.log("update sort....",siblingNodesNew,siblingNodesOld,dropNode,dropPosition);// 3,更新draggingNode及其子节点的catLevelvar catLevelNeedUpdate = true;if (draggingNode.data.catLevel === dropNode.data.catLevel &&(dropPosition === "before" || dropPosition === "after")) {catLevelNeedUpdate = false;}var draggingNodeAfterDrop = null;if (catLevelNeedUpdate) {var draggingParentNodeAfterDrop = {};if (dropPosition === "before" || dropPosition === "after") {draggingParentNodeAfterDrop = dropNode.parent;} else {draggingParentNodeAfterDrop = dropNode;}draggingParentNodeAfterDrop.childNodes.forEach((child) => {if (child.data.catId === draggingNode.data.catId) {draggingNodeAfterDrop = child;}});// 递归变量draggingNodeAfterDrop及其childNodes,将其data属性的cateLevel置为level属性值this.updateCatLevel(draggingNodeAfterDrop);}console.log("draggingNodeNewPId", this.draggingNodeNewPId)this.nodesInfoAfterDrop.push({catId: draggingNode.data.catId,parentCid: this.draggingNodeNewPId,});console.log(this.nodesInfoAfterDrop);},batchSaveDrag() {// 4,调用后端接口,将数组nodesInfoAfterDrop发送给后端this.$http({url: this.$http.adornUrl("/product/category/update/sort"),method: "post",data: this.$http.adornData(this.nodesInfoAfterDrop, false),}).then(({ data }) => {if (data && data.code === 0) {this.$message({message: "操作成功",type: "success",duration: 1500,onClose: () => {console.log("删除成功,关闭消息提示");this.getMenus(); // 重新获取数据this.expandedKeys = [this.draggingNodeNewPId]; // 重置展开节点this.draggingNodeNewPId = 0; // 重置����的新parentCidthis.nodesInfoAfterDrop = [];},});} else {this.$message.error(data.msg);}});},getLevel1Nodes(node) {var tmpNode = node;while (tmpNode.parent) {tmpNode = tmpNode.parent;}return tmpNode.childNodes;},// 写一个根据cid从menus中查询结点的函数getNodeByCid(cid, nodes) {if (cid === 0) {return nodes[0].parent;}// 递归查询for (var i = 0; i < nodes.length; i++) {if (nodes[i].data.catId === cid) {return nodes[i];}if (nodes[i].childNodes && nodes[i].childNodes.length > 0) {var node = this.getNodeByCid(cid, nodes[i].childNodes);if (node) {return node;}}}return null;},// 递归更新draggingNode及其子节点的catLevelupdateCatLevel(node) {if (!node) {return;}this.nodesInfoAfterDrop.push({catId: node.data.catId,catLevel: node.level,});if (node.childNodes && node.childNodes.length > 0) {node.childNodes.forEach((child) => {this.updateCatLevel(child);});}},allowDrag(draggingNode, dropNode, dropPosition) {var deep = this.countDraggingNodeDeep(draggingNode);// 根据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.mzph.cn/web/48914.shtml

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

相关文章

Java | Leetcode Java题解之第260题只出现一次的数字III

题目&#xff1a; 题解&#xff1a; class Solution {public int[] singleNumber(int[] nums) {int xorsum 0;for (int num : nums) {xorsum ^ num;}// 防止溢出int lsb (xorsum Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum));int type1 0, type2 0;for (int n…

Prometheus配置alertmanager告警

1、拉取镜像并运行 1、配置docker镜像源 [rootlocalhost ~]# vim /etc/docker/daemon.json {"registry-mirrors": ["https://dfaad.mirror.aliyuncs.com"] } [rootlocalhost ~]# systemctl daemon-reload [rootlocalhost ~]# systemctl restart docker2、…

刷题了: 151.翻转字符串里的单词 |卡码网:55.右旋转字符串

151.翻转字符串里的单词 题目链接:https://leetcode.cn/problems/reverse-words-in-a-string/description/ 文章讲解:https://programmercarl.com/0151.%E7%BF%BB%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84%E5%8D%95%E8%AF%8D.html 视频讲解:https://www.bilibi…

vue2之jessibuca视频插件使用教程

vue2之jessibuca视频插件使用教程 jessibuca简介前期准备下载相关jsvue index.html文件引入 组件封装使用小知识 引入iconfont jessibuca简介 Jessibuca是一款开源的纯H5直播流播放器&#xff0c;通过Emscripten将音视频解码库编译成Js&#xff08;ams.js/wasm)运行于浏览器之中…

PHP基础语法(五)

一、定义语法 1、索引数组 2、php5.4之后简化的语法 3、关联数组 4、多维数组 $colors array("Red", "Green", "Blue");$colors ["Red", "Green", "Blue"];$person array("name" > "Jo…

基于PyCharm在Windows系统上远程连接Linux服务器中Docker容器进行Python项目开发与部署

文章目录 摘要项目结构项目开发项目上线参考文章 摘要 本文介绍了如何在Windows 10系统上使用PyCharm专业版2024.1&#xff0c;通过Docker容器在阿里云CentOS 7.9服务器上进行Python项目的开发和生产部署。文章详细阐述了项目结构的搭建、PyCharm的使用技巧、以及如何将开发项…

Superset二次开发之Dashboard状态变更源码解读

接口PUTapi/v1/dashboard/<dashboard_ID>前端文件 superset-frontend\src\dashboard\components\PublishedStatus\index.jsx 69行 togglePublished() togglePublished() { this.props.savePublished(this.props.dashboardId, !this.props.isPublished); } …

基于深度学习的医学影像分类

基于深度学习的医学影像分类是利用深度学习模型对医学影像&#xff08;如X光片、CT、MRI等&#xff09;进行自动分类&#xff0c;以辅助医生进行疾病诊断和治疗决策。以下是对这一领域的系统介绍&#xff1a; 1. 任务和目标 医学影像分类的主要任务和目标包括&#xff1a; 疾…

12.Spring事务和事务传播机制

文章目录 1.为什么需要事务2.Spring 中事务的实现2.1 MySQL 中的事务使⽤2.2 Spring 编程式事务2.3 Spring 声明式事务&#xff08;自动&#xff09;2.3.1 Transactional 作⽤范围2.3.2 Transactional 参数说明2.3.3 注意事项2.3.4 Transactional ⼯作原理 3.事务隔离级别3.1 事…

vue+element的table合并单元格(竖着合并行)及合计行添加并计算

1 效果: 代码分析: 1 表格头配置: 2 懒得写的:自己复制吧 <el-table:data"tableData"style"width: 98%":height"height"v-loading"isLoading"stripe"false" :span-method"objectSpanMethod"show-summary:summ…

c++ 线程

在 C 中&#xff0c;std::thread 构造函数可以用于将参数传递给线程。这里是一个基本的示例&#xff0c;展示了如何使用 std::thread 来传递参数&#xff1a; #include <iostream> #include <thread>// 定义一个被线程调用的函数 void threadFunc(int arg1, doubl…

C++多线程编程中的锁详解

在现代软件开发中&#xff0c;多线程编程是提升应用程序性能和响应能力的重要手段。然而&#xff0c;多线程编程也带来了数据竞争和死锁等复杂问题。为了确保线程间的同步和共享数据的一致性&#xff0c;C标准库提供了多种锁机制。 1. std::mutex std::mutex是最基础的互斥锁…

视图、存储过程、触发器

一、视图 视图是从一个或者几个基本表&#xff08;或视图&#xff09;导出的表。它与基 本表不同&#xff0c;是一个虚表&#xff0c;视图只能用来从查询&#xff0c;不能做增删改(虚拟的表) 1.创建视图 创建视图的语法&#xff1a; create view 视图名【view_xxx / v_xxx】 a…

深入理解MySQL锁机制与性能优化:详解记录锁、间隙锁、临键锁及慢SQL查询分析

1. 事务隔离和锁机制详解 记录锁 第一种情况,当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。 比如 where id = 1 4 7 10。 间隙锁 第二种情况,当我们查询的记录不存在,无论是用等值查询还是范围…

如何使用 API 查看极狐GitLab 镜像仓库中的镜像?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…

Thinkphp开发文档二次整理版

基础部分 安装 环境要求 ​ *php>7.1.0 命令下载 通过Composer进行下载&#xff0c;操作步骤下载软件 phpstudy --->点击软件管理 --->安装Composer --->再点击网站 --->点击管理 --->点击Composer --->复制如下命令代码&#xff1a; ​ 稳定版&…

国际化技术参考

一、概述 国际化就是用户可以选择对应的语言,页面展示成对应的语言; 一个系统的国际化按照信息的所在位置,可以分为三种国际化信息: 前端页面信息后端提示信息数据库的字典类信息二、前端页面国际化 使用i18n库实现国际化 i18n国际化库思路:通过jquery或者dom操作拿到需…

推荐4款简单高效的视频转文字工具。

最近我要将很多的以前的培训视频转换成笔记&#xff0c;觉得很麻烦&#xff0c;于是就搜索有没有什么工具可以帮助。结果就真的找到了很多将视频转换成文字的软件和网站。解决了一个大工程&#xff0c;后来发现其实很多人都会碰到像我这样的问题&#xff0c;于是在这里将我使用…

【前端】JavaScript入门及实战106-110

文章目录 106 a的索引问题107 使用DOM操作CSS108 读取元素当前的样式109 getStyle()110 其他样式操作的属性滚动条练习 106 a的索引问题 <!DOCTYPE html> <html> <head> <title></title> <meta charset"utf-8"> <script typ…

HR问:前端实习生简历实习筛不了,一天上百份,但是都一样,怎么办?

前两天&#xff0c;我们HR过来问&#xff1a;“前端的实习生投的实在太多了&#xff0c;一天下来就几百份了&#xff0c;怎么办&#xff1f;” 我大致看了看这些简历发现&#xff0c;这几百份的简历除了学校不同之外&#xff0c;几乎没有什么区别。 如何筛选&#xff1f; 有…