当前位置: 首页 > news >正文

网站页面设计流程百度竞价调价软件

网站页面设计流程,百度竞价调价软件,手机商城官方网站,网站设计需要用到什么技术1.商品服务-三级分类 1.1三级分类介绍 1.2查询三级分类查询-递归树型结构数据获取 1.2.1导入数据pms_catelog.sql到数据表pms_category 1.2.2一次性查出所有分类及子分类 1.2.2.1修改CategoryController.java /*** 查出所有分类以及子分类#xff0c;以树形结构组装起来*/R…1.商品服务-三级分类 1.1三级分类介绍 1.2查询三级分类查询-递归树型结构数据获取 1.2.1导入数据pms_catelog.sql到数据表pms_category 1.2.2一次性查出所有分类及子分类 1.2.2.1修改CategoryController.java /*** 查出所有分类以及子分类以树形结构组装起来*/RequestMapping(/list/tree)public R list(){ListCategoryEntity entities categoryService.listWithTree();return R.ok().put(data, entities);} 1.2.2.2CategoryEntity新增子分类属性 TableField(exist false) //表示数据库表中不存在private ListCategoryEntity children; 1.2.2.3实现接口CategoryService.java的listwithTree() Overridepublic ListCategoryEntity listWithTree() {//1、查出所有分类。baseMapper来自于继承的ServiceImpl类跟CategoryDao一样用法ListCategoryEntity entities baseMapper.selectList(null);//2、递归组装多级分类的树形结构。先过滤得到一级分类再加工递归设置一级分类的子孙分类再排序再收集ListCategoryEntity level1Menus entities.stream().filter(categoryEntity - categoryEntity.getParentCid() 0).map((menu)-{// 设置一级分类的子分类menu.setChildren(getChildren(menu, entities));return menu;}).sorted((menu1, menu2) - {//排序sort是实体类的排序属性值越小优先级越高要判断非空防止空指针异常return (menu1.getSort() null ? 0 : menu1.getSort()) - (menu2.getSort() null ? 0 : menu2.getSort());}).collect(Collectors.toList());return level1Menus;}//递归查找所有菜单的子菜单private ListCategoryEntity getChildren(CategoryEntity root, ListCategoryEntity all){ListCategoryEntity children all.stream().filter(CategoryEntity - CategoryEntity.getParentCid().equals(root.getCatId())).map(categoryEntity - {//1、递归查找子菜单categoryEntity.setChildren(getChildren(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;} 1.2.2.4启动product服务测试结果 打开F12访问localhost:10000/product/category/list/tree 1.3后台页面管理三级分类 1.启动后台管理系统renren-fast 2.启动前端renren-fast-vue;终端输入命令npm run dev 1.3.1新增目录-商品系统 系统管理中的菜单管理点击新增按钮 点击确定后刷新页面可见 数据库中可查到 1.3.2新增菜单-分类维护 1.3.3前端展示三级分类 需求在左侧点击【商品系统-分类维护】希望在此展示3级分类。可以看到 url是http://localhost:8001/#/product-category 填写的菜单路由是product/category 对应的视图是src/view/modules/product/category.vue  (renren-fast-vue文件) 1.3.3.1创建product/category视图 1.创建src/views/mudules/product/category.vue 2.在category.vue中创建vue模板 输入vue加回车可快速生成模板。 3.elementui看如何使用多级目录 Element组件网址 进入Element官网进入组件找到Tree树形控件 模仿用法写入vue !-- -- template el-tree :datadata :propsdefaultProps node-clickhandleNodeClick/el-tree /templatescript //这里可以导入其他文件比如组件工具js第三方插件jsjson文件图片文件等等 //例如import 《组件名称》 from 《组件路径》;export default { //import引入的组件需要注入到对象中才能使用 components: {}, data() {return {data: [],defaultProps: {children: children,label: label}};},methods: {handleNodeClick(data) {console.log(data);},//获取后台数据getMenus(){this.$http({url: this.$http.adornUrl(/product/category/list/tree),method: get}).then(data{console.log(成功了获取到菜单数据...., data)})}}, //监听属性 类似于data概念 computed: {}, //监控data中的数据变化 watch: {}, //生命周期 - 创建完成可以访问当前this实例 created() {//创建完成时就调用getMenus函数this.getMenus(); }, //生命周期 - 挂载完成可以访问DOM元素 mounted() {}, beforeCreate() {}, //生命周期 - 创建之前 beforeMount() {}, //生命周期 - 挂载之前 beforeUpdate() {}, //生命周期 - 更新之前 updated() {}, //生命周期 - 更新之后 beforeDestroy() {}, //生命周期 - 销毁之前 destroyed() {}, //生命周期 - 销毁完成 activated() {}, //如果页面有keep-alive缓存功能这个函数会触发 } /script style scoped/style 1.3.3.2测试 localhost:8001/#/product-category F12刷新页面 发现404请求端口问题  他是给8080端口发的请求而我们的商品服务在10000端口。我们以后还会同时发向更多的端口所以需要配置网关前端只向网关发送请求然后由网关自己路由到相应端口。 1.3.3.3查看在哪定义的请求路径 复制 http://localhost:8080/renren-fasthttp://localhost:8080/renren-fast ctrl shift f 全局搜索 1.3.3.4修改请求到网关 请求地址修改为 刷新测试 刷新发现验证码出不来。 验证码请求路径问题 分析原因前端给网关发验证码请求但是验证码请求在renren-fast服务里所以要想使验证码好使需要把renren-fast服务注册到服务中心并且由网关进行路由 1.3.3.5renren-fast注册到nacos renren-fast修改pom文件依赖gulimall-common 修改renren-fast配置文件application.yml spring:application:name: renren-fastcloud:nacos:discovery:server-addr: 127.0.0.1:8848 启动类添加注解 EnableDiscoveryClient 1.3.3.6配置网关 需求 http://localhost:88/api/xxx 转发-- http://renren-fast:8080/renren-fast/xxx 例如http://localhost:88/api/captcha.jpg   转发--  http://renren-fast:8080/renren-fast/captcha.jpg 修改gulimall-gateway配置文件application.yml - id: admin_routeuri: lb://renren-fast #负载均衡predicates:-Path/api/** #断言 ##前端项目/api ## http://localhost:88/api/captcha.jpg ## http://renren-fast:8080/api/captcha.jpg测试验证发现请求地址为http://renren-fast:88/api/captcha.jpg 再次修改gulimall-gateway配置文件application.yml添加下列配置 spring:cloud:nacos:discovery:server-addr: 127.0.0.1:8848gateway:routes: # 路由id自定义只要唯一即可- id: admin_route # uri路由的目标地址。lb就是负载均衡后面跟服务名称。uri: lb://renren-fast#断言工厂的Path请求路径必须符合指定规则predicates:- Path/api/** # 把所有api开头的请求都转发给renren-fast#局部过滤器。回顾默认过滤器default-filters是与routes同级filters: #路径重写。逗号左边是原路径右边是重写后的路径- RewritePath/api/(?segment.*),/renren-fast/$\{segment}# 默认规则 请求过来http://localhost:88/api/captcha.jpg 转发-- http://renren-fast:8080/renren-fast/captcha.jpg刷新测试出现跨域错误 1.4解决跨域问题 1.4.1跨域和同源策略 跨域指的是浏览器不能执行其他网站的脚本它是由浏览器的同源策略造成的是浏览器对javascript施加的安全限制。 同源策略是指协议域名端口都要相同其中有一个不同都会产生跨域问题 1.4.2跨域流程 预检请求options 1.4.3解决跨域方法1使用nginx反向代理为同一域 1.4.4解决跨域方法2配置当前请求允许跨域 CORSCORS 是一个 W3C 标准全称是“跨域资源共享”Cross-origin resource sharing。它允许浏览器向跨域的服务器发出XMLHttpRequest请求从而克服了 AJAX 只能同源使用的限制。  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 表明该响应的有效时间为多少秒。在有效时间内浏览器无须为同一请求再次发起预检请求。请注意浏览器自身维护了一个最大有效时间如果该首部字段的值超过了最大有效时间将失效。 在网关中统一配置 在gulimall-gateway的gulimall.gateway.config包下中新建配置类 //这个包别导错了有一个很像的。 import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; Configuration public class GulimallCorsConfiguration{Beanpublic CorsWebFilter corsWebFilter(){UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration new CorsConfiguration();//1、配置跨域// 允许跨域的请求头corsConfiguration.addAllowedHeader(*);// 允许跨域的请求方式corsConfiguration.addAllowedMethod(*);// 允许跨域的请求来源corsConfiguration.addAllowedOriginPattern(*); //注释的这句会报错。因为当allowCredentials为真时allowedorigin不能包含特殊值*因为不能在访问-控制-起源“响应头中设置该值。//corsConfiguration.addAllowedOrigin(*);//这句会报错// 是否允许携带cookie跨域corsConfiguration.setAllowCredentials(true);// 任意url都要进行跨域配置两个*号就是可以匹配包含0到多个/的路径source.registerCorsConfiguration(/**,corsConfiguration);return new CorsWebFilter(source);} } 启动测试 发现有多个值错误 注释掉renren-fast中的跨域不然会有一些重复的规则导致跨域失败 io.renren/config/CorsConfig  重启测试成功 1.5前端树形展示三级分类数据 1.5.1网关配置product路由 需求 localhost:88/api/product/xx        -        localhost:10000/product/xx 网关路由配置 # 精确的路由要放在/api的admin_route上面- id: product_routeuri: lb://gulimall-product #路由的目标地址predicates: # 路由断言。也就是判断请求是否符合路由规则的条件。- Path/api/product/** # 路径断言。这个是按照路径匹配只要以/api/product/开头就符合要求filters: #局部过滤器- RewritePath/api/(?segment.*),/$\{segment} #重写路径/api/xx过滤成/xx 1.5.2将product服务注册并配置到nacos 1.新建bootstrap.properties 2.nacos新建命名空间 3.修改application.yml注册到注册中心 4.启动类添加注解EnableDiscoveryClient开启服务注册发现功能 启动测试数据显示如下 1.5.3修改前端组件category.vue 需求将显示的数据展示到页面上 想要的数据在data.data里需要将其解构出来 结构代码修改如下 category.vue代码 !-- -- template el-tree :datamenus :propsdefaultProps node-clickhandleNodeClick/el-tree /templatescript //这里可以导入其他文件比如组件工具js第三方插件jsjson文件图片文件等等 //例如import 《组件名称》 from 《组件路径》;export default { //import引入的组件需要注入到对象中才能使用 components: {}, data() {return {menus: [],defaultProps: {children: children, //子节点label: name //name属性作为标签的值展示出来}};},methods: {handleNodeClick(data) {console.log(data);},getMenus(){this.$http({url: this.$http.adornUrl(/product/category/list/tree),method: get}).then(({data}){console.log(成功了获取到菜单数据...., data.data)this.menus data.data;})}}, //监听属性 类似于data概念 computed: {}, //监控data中的数据变化 watch: {}, //生命周期 - 创建完成可以访问当前this实例 created() {this.getMenus(); }, //生命周期 - 挂载完成可以访问DOM元素 mounted() {}, beforeCreate() {}, //生命周期 - 创建之前 beforeMount() {}, //生命周期 - 挂载之前 beforeUpdate() {}, //生命周期 - 更新之前 updated() {}, //生命周期 - 更新之后 beforeDestroy() {}, //生命周期 - 销毁之前 destroyed() {}, //生命周期 - 销毁完成 activated() {}, //如果页面有keep-alive缓存功能这个函数会触发 } /script style scoped/style 启动product测试 http://localhost:10000/product/category/list/tree 1.6逻辑删除三级分类 1.6.1分类的新增和删除 需求 在每一个菜单后面添加append, delete点击按钮时不进行菜单的打开合并仅点击箭头时展示子分类:expand-on-click-nodefalse当没有子菜单时才可以显示delete按钮当为一级、二级菜单时才显示append按钮。使用v-if判断是否显示el-tree添加多选框show-checkbox设置node-key标识每一个节点的不同 !-- -- templateel-tree:datamenusshow-checkbox:propsdefaultPropsnode-clickhandleNodeClick:expand-on-click-nodefalsenode-keycatIdspan classcustom-tree-node slot-scope{ node, data }span{{ node.label }}/spanspanel-buttontypetextv-ifnode.level 2sizeminiclick() append(data)Append/el-buttonel-buttontypetextv-ifnode.childNodes.length 0sizeminiclick() remove(node, data)Delete/el-button/span/span/el-tree /templatescript //这里可以导入其他文件比如组件工具js第三方插件jsjson文件图片文件等等 //例如import 《组件名称》 from 《组件路径》;export default {//import引入的组件需要注入到对象中才能使用components: {},data() {return {menus: [],defaultProps: {children: children, //子节点label: name, //name属性作为标签的值展示出来},};},methods: {handleNodeClick(data) {},getMenus() {this.$http({url: this.$http.adornUrl(/product/category/list/tree),method: get,}).then(({ data }) {console.log(成功了获取到菜单数据...., data.data);this.menus data.data;});},append(data) {console.log(append, data);},remove(node, data) {console.log(remove, node, data);},},//监听属性 类似于data概念computed: {},//监控data中的数据变化watch: {},//生命周期 - 创建完成可以访问当前this实例created() {this.getMenus();},//生命周期 - 挂载完成可以访问DOM元素mounted() {},beforeCreate() {}, //生命周期 - 创建之前beforeMount() {}, //生命周期 - 挂载之前beforeUpdate() {}, //生命周期 - 更新之前updated() {}, //生命周期 - 更新之后beforeDestroy() {}, //生命周期 - 销毁之前destroyed() {}, //生命周期 - 销毁完成activated() {}, //如果页面有keep-alive缓存功能这个函数会触发 }; /script style scoped /style 1.6.2后端逻辑删除 实体类成员变量加上TableLogic(value 0, delval 1)注解 CategoryEntity注解逻辑删除 /*** 是否显示[0-不显示1显示]*本项目使用的是category表的show_status字段逻辑删除值正好相反 *状态为1表示未删除状态为0表示删除。*/TableLogic(value 1,delval 0)private Integer showStatus; 修改CategoryController.java /** 删除 RequestBody:获取请求体必须发送POST请求 *SpringMvc自动将请求体的数据json),转为对应的对象 */ RequestMapping(/delete) public R delete(RequestBody Long[] catIds){ //1、检查当前删除的菜单是否被别的地方引用 categoryService.removeMenuByIds(Arrays.asList(catId)); return R.ok(); }实现类CategoryServicelmpl.java实现CategoryService.java接口方法CtrlN快速检索 Override public void removeMenuByIds(ListLongasList){ //TODO 1、检查当前删除的菜单是否被别的地方引用 //逻辑删除 baseMapper.deleteBatchIds(asList); }修改日志级别 logging:level:com.xmh.guliamll.product: debug 在测试工具发送POST请求  http://localhost:88/api/product/category/delete 查看控制台打印语句发现是update操作 1.6.3前端逻辑删除 需求 编写前端remove方法实现向后端发送请求点击delete弹出提示框是否删除这个节点 elementui中MessageBox 弹框中的确认消息添加到删除之前删除成功后有消息提示 elementui中Message 消息提示删除后刷新页面后分类应该保持之前展开状态: el-tree组件的default-expanded-keys属性,默认展开。 每次删除之后把删除菜单的父菜单的id值赋给默认展开值即可。 1.6.3.1创建请求代码块快捷命令 文件-首选项-用户片段-新建全局代码片段文件名vue.code-snippetssnippets译为代码片段片段 http-get请求: {prefix: httpget,body:[this.\\$http({,url: this.\\$http.adornUrl(),,method:get,,params:this.\\$http.adornParams({}),}).then(({data}){,})],description:httpGET请求},http-post请求:{prefix:httppost,body:[this.\\$http({,url:this.\\$http.adornUrl(),,method:post,,data: this.\\$http.adornData(data, false),}).then(({data}){ })],description:httpPOST请求}//在el-tree中设置默认展开属性绑定给expandedKey :default-expanded-keysexpandedKey//data中添加属性删除后给它赋值父节点id令树形控件刷新后展开 expandedKey: [],//完整的remove方法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();//设置需要默认展开的菜单。删除后给它赋值父节点id令树形控件刷新后展开this.expandedKey [node.parent.data.catId]});}).catch(() {});}, 1.7新增三级分类 1.7.1前端新增 需求 点击append新增弹出对话框,输入分类名称确定/取消后关闭对话框确定后发送post请求成功后刷新前端展示页面 创建对话框组件el-dialog 对话框标签el-dialog放在el-tree标签上下都是可以的主要是visible.sync属性控制对话框的显示和隐藏 !--对话框组件--el-dialog title提示 :visible.syncdialogVisible width30%el-form :modelcategroyel-form-item label分类名称el-input v-modelcategroy.name autocompleteoff/el-input/el-form-item/el-formspan slotfooter classdialog-footerel-button clickdialogVisible false取 消/el-buttonel-button typeprimary clickaddCategory确 定/el-button/span/el-dialog //data中新增数据 //按照数据库格式声明的数据。注意category属性用来接收输入框的参数需要赋值默认属性包括父id层级、展示状态为1排序值是0categroy: { name: , parentCid: 0, catLevel: 0, showStatus: 1, sort: 0 }, //判断是否显示对话框dialogVisible: false,//修改append方法新增addCategory方法 //点击append后计算category属性显示对话框append(data) {console.log(append, data);this.dialogVisible true;this.categroy.parentCid data.catId;this.categroy.catLevel data.catLevel * 1 1;},//点击确定后发送post请求 //成功后显示添加成功展开刚才的菜单addCategory() {console.log(提交的数据, this.categroy);this.$http({url: this.$http.adornUrl(/product/category/save),method: post,data: this.$http.adornData(this.categroy, false),}).then(({ data }) {this.$message({message: 添加成功,type: success,});//刷新出新的菜单this.getMenus();//设置需要默认展开的菜单this.expandedKey [this.categroy.parentCid];this.dialogVisible false;}); 1.8修改三级分类 1.8.1后端修改 后端修改“回显方法”结果对象的键为data product模块的CategoryController 1.8.2前端修改 需求 新增Edit按钮复制之前的appendupdata方法是由id进行更新的所以data中的category中新增catId增加、修改的同时修改图标和计量单位所以data的category新增incoproductUnit新建edit方法用来绑定Edit按钮。新建editCategory方法用来绑定对话框的确定按钮复用对话框新增、修改新建方法submitData与对话框的确定按钮进行绑定在方法中判断如果dialogTypeadd调用addCategory()如果dialogTypeedit调用editCategory()data数据中新增title,绑定对话框的title用来做提示信息。判断dialogType的值动态提示信息修改回显必须发请求而非直接从实参中获取。防止多个人同时操作对话框中的回显的信息应该是由数据库中读出来的点击Edit按钮发送httpget请求。成功之后发送提示消息展开刚才的菜单编辑之后再点击添加发现会回显刚才编辑的信息。所以在append方法中重置回显的信息 新增修改共享表单对话框所以还要修改添加方法第一步初始化数据 提交修改表单时不能像新增一样把携带初始化值的category直接提交上去 方法一局部更新只解构出表单里的属性封装成对象再提交推荐 方法二全量更新回显时把其他数据库字段也赋值 !--编辑按钮--el-button typetext sizemini click() edit(data)Edit/el-button!--可复用的对话框--el-dialog :titletitle :visible.syncdialogVisible width30%el-form :modelcategroyel-form-item label分类名称el-input v-modelcategroy.name autocompleteoff/el-input/el-form-itemel-form-item label图标el-input v-modelcategroy.inco autocompleteoff/el-input/el-form-itemel-form-item label计量单位el-inputv-modelcategroy.productUnitautocompleteoff/el-input/el-form-item/el-formspan slotfooter classdialog-footerel-button clickdialogVisible false取 消/el-buttonel-button typeprimary clicksubmitData确 定/el-button/span/el-dialog //data 新增了title、dialogType。 categroy中新增了inco、productUnit、catIddata() {return {title: ,dialogType: ,categroy: {name: ,parentCid: 0,catLevel: 0,showStatus: 1,sort: 0,inco: ,productUnit: ,catId: null,},dialogVisible: false,menus: [],expandedKey: [],defaultProps: {children: children, //子节点label: name, //name属性作为标签的值展示出来},};//方法//绑定对话框的确定按钮根据dialogType判断调用哪个函数submitData() {if (this.dialogType add) {this.addCategory();}if (this.dialogType edit) {this.editCategory();}},//绑定Edit按钮设置dialogType、title从后台读取数据展示到对话框内edit(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.categroy.catId data.data.catId;this.categroy.name data.data.name;this.categroy.inco data.data.inco;this.categroy.productUnit data.data.productUnit;});},//绑定对话框的确定按钮向后台发送更新请求传过去想要修改的字段editCategory() {var { catId, name, inco, productUnit } this.categroy;this.$http({url: this.$http.adornUrl(/product/category/update),method: post,data: this.$http.adornData({ catId, name, inco, productUnit }, false),}).then(({ data }) {this.$message({message: 修改成功,type: success,});//刷新出新的菜单this.getMenus();//设置需要默认展开的菜单this.expandedKey [this.categroy.parentCid];this.dialogVisible false;});}//点击append按钮清空编辑之后的回显数据append(data) {this.dialogType add;this.title 添加菜单;console.log(append, data);this.dialogVisible true;this.categroy.parentCid data.catId;this.categroy.catLevel data.catLevel * 1 1;this.categroy.name ,this.categroy.inco ,this.categroy.productUnit }, 1.9修改层级关系实现拖拽效果 1.9.1前端拖拽 实现逻辑 1.在el-tree中加入属性draggable表示节点可拖拽 2.在el-tree中加入属性:allow-dropallowDrop拖拽时判定目标节点能否被放置。 3.allowDrop有三个参数draggingNode表示拖拽的节点dropNode表示拖拽到哪个节点type表示拖拽的类型’prev’、‘inner’ 和 ‘next’表示拖拽到目标节点之前、里面、之后 注意函数实现判断拖拽后必须保持数型的三层结构。 节点的深度 最深深度 - 当前深度 1当拖拽节点拖拽到目标节点的内部要满足 拖拽节点的深度 目标节点的深度 3当拖拽节点拖拽的目标节点的两侧要满足 拖拽节点的深度 目标节点的父节点的深度 3 !--el-tree中添加属性--draggable:allow-dropallowDrop // data中新增属性用来记录当前节点的最大深度 maxLevel: 1,//新增方法allowDrop(draggingNode, dropNode, type) {console.log(allowDrag:, draggingNode, dropNode, type);//节点的最大深度this.countNodeLevel(draggingNode.data);console.log(level:, this.maxLevel);//当前节点的深度let deep (this.maxLevel - draggingNode.data.catLevel) 1;console.log(deep)if (type inner){return (deep dropNode.level) 3;}else{return (deep dropNode.parent.level) 3;}},//计算当前节点的最大深度countNodeLevel(node) {//找到所有的子节点求出最大深度if (node.children ! null node.children.length 0){for (let i 0; i node.children.length; i){if (node.children[i].catLevel this.maxLevel){this.maxLevel node.children[i].catLevel;}this.countNodeLevel(node.children[i]);}}}, 拖拽后的数据收集 在el-tree中加入属性node-drophandleDrop,表示拖拽事件结束后触发事件handleDrop,handleDrop共四个参数draggingNode被拖拽节点对应的 Node; dropNode:结束拖拽时最后进入的节点; dropType:被拖拽节点的放置位置before、after、inner;ev:event拖拽可能影响的节点的数据parentCid、catLevel、sortdata中新增updateNodes把所有要修改的节点都传进来。要修改的数据拖拽节点的parentCid、catLevel、sort要修改的数据新的兄弟节点的sort 把新的节点收集起来然后重新排序要修改的数据子节点的catLevel //el-tree中新增属性绑定handleDrop表示拖拽完触发 node-drophandleDrop//data 中新增数据用来记录需要更新的节点拖拽的节点parentCid、catLevel、sort拖拽后的兄弟节点sort拖拽节点的子节点catLevel updateNodes: [],//新增方法handleDrop(draggingNode, dropNode, dropType, ev) {console.log(handleDrop: , draggingNode, dropNode, dropType);//1、当前节点最新父节点的idlet pCid 0;//拖拽后的兄弟节点分两种情况一种是拖拽到两侧一种是拖拽到内部let sibings null;if (dropType before || dropType after) {pCid dropNode.parent.data.catId undefined ? 0: dropNode.parent.data.catId;sibings dropNode.parent.childNodes;} else {pCid dropNode.data.catId;sibings dropNode.childNodes;}//2、当前拖拽节点的最新顺序//遍历所有的兄弟节点如果是拖拽节点传入catIdsortparentCidcatLevel如果是兄弟节点传入catIdsortfor (let i 0; i sibings.length; i) {if (sibings[i].data.catId draggingNode.data.catId){//如果遍历的是当前正在拖拽的节点let catLevel draggingNode.level;if (sibings[i].level ! draggingNode.level){//当前节点的层级发生变化catLevel sibings[i].level;//修改他子节点的层级this.updateChildNodeLevel(sibings[i]);}this.updateNodes.push({catId:sibings[i].data.catId, sort: i, parentCid: pCid, catLevel:catLevel});}else{this.updateNodes.push({catId:sibings[i].data.catId, sort: i});}}//每次拖拽后把数据清空否则要修改的节点将会越拖越多this.updateNodes [],this.maxLevel 1,}// 修改拖拽节点的子节点的层级 updateChildNodeLevel(node){if (node.childNodes.length 0){for (let i 0; i node.childNodes.length; i){//遍历子节点传入catIdcatLevelvar cNode node.childNodes[i].data;this.updateNodes.push({catId:cNode.catId,catLevel:node.childNodes[i].level});//处理子节点的子节点this.updateChildNodeLevel(node.childNodes[i]);}}}, 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 [pCid];}); 1.9.2批量拖拽功能 添加开关控制拖拽功能是否开启每次拖拽都要和数据库交互不合理。批量拖拽过后一次性保存 !--添加拖拽开关和批量保存按钮--el-switchv-modeldraggableactive-text开启拖拽inactive-text关闭拖拽/el-switchel-button v-ifdraggable sizesmall round clickbatchSave批量保存/el-button //data中新增数据pCid:[], //批量保存过后要展开的菜单iddraggable: false, //绑定拖拽开关是否打开//修改了一些方法修复bug修改过的方法都贴在下面了//点击批量保存按钮发送请求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 [];},//handleDrop(draggingNode, dropNode, dropType, ev) {console.log(handleDrop: , draggingNode, dropNode, dropType);//1、当前节点最新父节点的idlet pCid 0;let sibings null;if (dropType before || dropType after) {pCid dropNode.parent.data.catId undefined? 0: dropNode.parent.data.catId;sibings dropNode.parent.childNodes;} else {pCid dropNode.data.catId;sibings dropNode.childNodes;}//2、当前拖拽节点的最新顺序for (let i 0; i sibings.length; i) {if (sibings[i].data.catId draggingNode.data.catId) {//如果遍历的是当前正在拖拽的节点let catLevel draggingNode.level;if (sibings[i].level ! draggingNode.level) {//当前节点的层级发生变化catLevel sibings[i].level;//修改他子节点的层级this.updateChildNodeLevel(sibings[i]);}this.updateNodes.push({catId: sibings[i].data.catId,sort: i,parentCid: pCid,catLevel: catLevel,});} else {this.updateNodes.push({ catId: sibings[i].data.catId, sort: i });}}this.pCid.push(pCid);console.log(this.pCid)//3、当前拖拽节点的最新层级//console.log(updateNodes, this.updateNodes)//拖拽之后重新置1this.maxLevel 1;},// 修改拖拽判断逻辑allowDrop(draggingNode, dropNode, type) {console.log(allowDrag:, draggingNode, dropNode, type);this.maxLevel draggingNode.level;//节点的最大深度this.countNodeLevel(draggingNode);console.log(maxLevel:, this.maxLevel);//当前节点的深度let deep Math.abs(this.maxLevel - draggingNode.level) 1;console.log(level,deep);if (type inner) {return deep dropNode.level 3;} else {return deep dropNode.parent.level 3;}},//计算深度时用当前数据而不是数据库中的数据。因为可能还没来得及保存到数据库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]);}}}, 1.9.3后端拖拽 思路 在后端编写批量修改的方法update/sort前端发送post请求把要修改的数据发送过来提示信息展开拖拽节点的父节点 CategoryController批量修改功能 //批量修改参数要传数组不能传listRequestMapping(/update/sort)public R updateSort(RequestBody CategoryEntity[] category){categoryService.updateBatchById(Arrays.asList(category));return R.ok();} 测试批量修改功能 http://localhost:88/api/product/category/update/sort 1.10批量删除分类 1.10.1后端批量删除 1.10.2前端批量删除 1.新增删除按钮 el-button typedanger sizesmall clickbatchDelete round批量删除/el-button!--eltree中新增属性用作组件的唯一标示-- refmenuTree 2.批量删除方法 batchDelete(){let catIds [];let catNames [];let checkedNodes this.$refs.menuTree.getCheckedNodes();for (let i 0; i checkedNodes.length; i){catIds.push(checkedNodes[i].catId);catNames.push(checkedNodes[i].name);}this.$confirm(是否批量删除【${catNames}】菜单, 提示, {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((){});}, 1.11前端category.vue最终代码 templatedivel-switch v-modeldraggable active-text开启拖拽 inactive-text关闭拖拽/el-switchel-button v-ifdraggable clickbatchSave拖拽保存/el-buttonel-button typedanger clickbatchDelete批量删除/el-buttonel-tree:datamenus:propsdefaultProps:expand-on-click-nodefalseshow-checkboxnode-keycatId:default-expanded-keysexpandedKey:draggabledraggable:allow-dropallowDropnode-drophandleDroprefmenuTreespan classcustom-tree-node slot-scope{ node, data }span{{ node.label }}/spanspanel-buttonv-ifnode.level 2typetextsizeminiclick() append(data)新增/el-buttonel-button typetext sizemini clickedit(data)编辑/el-buttonel-buttonv-ifnode.childNodes.length0typetextsizeminiclick() remove(node, data)删除/el-button/span/span/el-treeel-dialog:titletitle:visible.syncdialogVisiblewidth30%:close-on-click-modalfalseel-form :modelcategoryel-form-item label分类名称el-input v-modelcategory.name autocompleteoff/el-input/el-form-itemel-form-item label图标el-input v-modelcategory.icon autocompleteoff/el-input/el-form-itemel-form-item label计量单位el-input v-modelcategory.productUnit autocompleteoff/el-input/el-form-item/el-formspan slotfooter classdialog-footerel-button clickdialogVisible false取 消/el-buttonel-button typeprimary clicksubmitData确 定/el-button/span/el-dialog/div /templatescript //这里可以导入其他文件比如组件工具js第三方插件jsjson文件图片文件等等 //例如import 《组件名称》 from 《组件路径》;export default {//import引入的组件需要注入到对象中才能使用components: {},props: {},data() {return {pCid: [],draggable: false,updateNodes: [],maxLevel: 0,title: ,dialogType: , //edit,addcategory: {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: {getMenus() {this.$http({url: this.$http.adornUrl(/product/category/list/tree),method: get}).then(({ data }) {console.log(成功获取到菜单数据..., data.data);this.menus data.data;});},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(() {});},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;});},handleDrop(draggingNode, dropNode, dropType, ev) {console.log(handleDrop: , draggingNode, dropNode, dropType);//1、当前节点最新的父节点idlet pCid 0;let siblings null;if (dropType before || dropType after) {pCid dropNode.parent.data.catId undefined? 0: dropNode.parent.data.catId;siblings dropNode.parent.childNodes;} else {pCid dropNode.data.catId;siblings dropNode.childNodes;}this.pCid.push(pCid);//2、当前拖拽节点的最新顺序for (let i 0; i siblings.length; i) {if (siblings[i].data.catId draggingNode.data.catId) {//如果遍历的是当前正在拖拽的节点let catLevel draggingNode.level;if (siblings[i].level ! draggingNode.level) {//当前节点的层级发生变化catLevel siblings[i].level;//修改他子节点的层级this.updateChildNodeLevel(siblings[i]);}this.updateNodes.push({catId: siblings[i].data.catId,sort: i,parentCid: pCid,catLevel: catLevel});} else {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) {var cNode node.childNodes[i].data;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.maxLevelif (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;}},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
http://www.hkea.cn/news/14516384/

相关文章:

  • 东莞网站建设求职简历公司注册资金多少的利弊
  • 龙岗网站建设代理商安徽网站建设天锐科技
  • 十堰百度网站建设杭州网站建设公司有哪几家
  • 大兴黄村网站建设wordpress自动推送代码
  • 网站切版教程广西钦州有做网站的公司吗
  • 网站域名在哪里查询企业建设营销网站的目的
  • 宁波做网站的大公司排名哈尔滨小程序制作公司
  • 园林网站模板下载渭南有几个县
  • 功能网站开发多少钱在网上做网站
  • 上海企乐网站制作公司培训课程名称大全
  • 自己网站怎么建设在360网站做公告怎么弄
  • 找人制作网站 优帮云阳谷聊城做网站
  • 苗木公司网站模板正规公司简历模板
  • 个人网站开发模式手机微信网站怎么做的好
  • 洪湖自己的网站做网站必须有框架是吗
  • 货运app开发公司优化专业的公司
  • 上海网站建设的网什么类型网站
  • 公司网站域名怎么注册北京网站建设是什么意思
  • 郑州做网站的外包公司有哪些企业信息
  • 做论坛网站价格重庆网站建设只选承越
  • 免费小程序网站wordpress升级失败
  • 网站搭建软件精品课程网站
  • 如皋网站设计宁波住房建设网站
  • 长沙网站建设 鼎誉2015年做哪些网站能致富
  • 豫建设标 网站美丽女性网-大型女性门户网大型程序700m网站程序源码织梦
  • 网站开发方案书博客怎么样做一个个人网站
  • 网站开发工具 比较群晖网站建设
  • 小米路由做网站a站为什么会凉
  • 镇海区住房和建设网站东莞企业seo推广
  • 服务器网站配置有特点的个人网站