目录
查询-递归树型结构数据获取
首先在PmsCategoryControlle中编写方法
/list/tree
/**
* 查出所有分类以及子分类,以树型结构组装出来
*/
@RequestMapping("/list/tree")
public R list(@RequestParam Map<String, Object> params){
// PageUtils page = pmsCategoryService.queryPage(params);
List<PmsCategoryEntity> categoryEntities = pmsCategoryService.listWithTree();
return R.ok().put("data", categoryEntities);
}
public interface PmsCategoryService extends IService<PmsCategoryEntity> {
PageUtils queryPage(Map<String, Object> params);
List<PmsCategoryEntity> listWithTree();
}
在PmsCategoryEntity中添加一条属性children
@TableField(exist = false)
private List<PmsCategoryEntity> children;
然后编写实现方法代码
@Override
public List<PmsCategoryEntity> listWithTree() {
//1、查出所有分类
List<PmsCategoryEntity> entities = baseMapper.selectList(null);
//2、组装成父子的树型结构
//2.1、首先找到所有的一级分类
List<PmsCategoryEntity> level1Menus = entities.stream().filter(entity->entity.getParentCid()==0).map((menus)->{menus.setChildren(getChildren(menus, entities));
return menus;
}).sorted((menu1,menu2)->{
return menu1.getSort()-menu2.getSort();}).collect(Collectors.toList());
return level1Menus;
}
private List<PmsCategoryEntity> getChildren(PmsCategoryEntity root, List<PmsCategoryEntity> all){
List<PmsCategoryEntity> childrens = all.stream().filter((menu) -> {
return menu.getParentCid() == root.getCatId();
}).map(
//找到子菜单
menu -> {
menu.setChildren(getChildren(menu, all));
return menu;
}).sorted((menu1, menu2) -> {
return (menu1.getSort()==null?0:menu1.getSort())-(menu2.getSort()==null?0:menu2.getSort());
}).collect(Collectors.toList());
return childrens;
}
}
配置网关路由和路径重写
打开人人开源项目前台代码,创建product文件夹,并创建category.vue
<template>
<el-tree
:data="data"
:props="defaultProps"
@node-click="handleNodeClick"
></el-tree>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》'
export default {
//import引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
return {
data: [],
defaultProps: {
children: "children",
label: "label",
},
};
},
//计算属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
methods: {
handleNodeClick(data) {
console.log(data);
},
getMenus(){
this.$http({
url: this.$http.adornUrl('/product/pmscategory/list/tree'),
method: 'get',
}).then(data=>{
console.log("成功获取菜单数据。。。",data)
})
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
需要在index.js中设置路径,都交给网关处理
/**
* 开发环境
*/
;(function () {
window.SITE_CONFIG = {};
// api接口请求地址
window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';
// cdn地址 = 域名 + 版本号
window.SITE_CONFIG['domain'] = './'; // 域名
window.SITE_CONFIG['version'] = ''; // 版本号(年月日时分)
window.SITE_CONFIG['cdnUrl'] = window.SITE_CONFIG.domain + window.SITE_CONFIG.version;
})();
但需要给renren-fast注册服务
首先,导入gulimall-common依赖
<dependency>
<groupId>com.example.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
然后配置文件application.yml中加入以下配置
application:
name: renren-fast
cloud:
nacos:
discovery:
server-addr: 192.168.159.135:8848
然后在启动类上加入以下注解发现服务
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class RenrenApplication {
public static void main(String[] args) {
SpringApplication.run(RenrenApplication.class, args);
}
}
添加网关路由规则
spring:
cloud:
gateway:
routes:
- id: test_route
uri: https://www.baidu.com
predicates:
- Query=url,baidu
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
## 前端项目,/api
启动网关服务和renren-fast服务
网关统一配置跨域
跨域是资源共享的一种安全机制
解决跨域问题有以下几种方案:
1、使用 nginx部署为同一域
2、配置当次请求允许跨域
在网关模块中创建config文件夹,并创建gulimallCorsConfiguration类
package com.example.gulimall.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.server.ServerWebExchange;
@Configuration
public class gulimallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}
}
需要将renren-fast的CorsConfig内容注释
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**")
// .allowedOrigins("*")
// .allowCredentials(true)
// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
// .maxAge(3600);
// }
}
启动
查询-树型展示三级分类数据
点击商品分类无法显示数据是因为网关将所有请求都发送至renren-fast服务了
所以需要去网关再创建一个路由规则,注意商品路由需要放在renren前面
spring:
cloud:
gateway:
routes:
- id: test_route
uri: https://www.baidu.com
predicates:
- Query=url,baidu
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>/?.*), /$\{segment}
# http://localhost:88/api/product/category/list/tree
## 商品服务。/api/product
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
## 前端项目,/api
然后还需要将gulimall-product注册到服务中心,以及配置也注册到nacos中
创建bootstrap.properties
spring.application.name=gulimall-product
spring.cloud.nacos.config.server-addr=192.168.159.135:8848
spring.cloud.nacos.config.namespace=1a9de87f-e17d-4a4e-94fb-7275a889bc4b
application.yml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.159.135:3306/gulimall_pms
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
nacos:
discovery:
server-addr: 192.168.159.135:8848
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
server:
port: 10000
启动类上加上服务发现注解
@EnableDiscoveryClient
最后需要对前端页面代码进行修改category.vue,注意label为name,以及this.data=data.data
<template>
<el-tree
:data="menus"
:props="defaultProps"
@node-click="handleNodeClick"
></el-tree>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》'
export default {
//import引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
return {
menus: [],
defaultProps: {
children: "children",
label: "name",
},
};
},
//计算属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
methods: {
handleNodeClick(data) {
console.log(data);
},
getMenus(){
this.$http({
url: this.$http.adornUrl('/product/pmscategory/list/tree'),
method: 'get',
}).then(({data})=>{
console.log("成功获取菜单数据。。。",data.data)
this.menus = data.data;
})
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
效果如下:
三级分类-删除
打开elementUI,参考以下链接使用 scoped slot实现删除功能
Element - The world's most popular Vue UI framework
最后需要加上node-key=“catId”,表示唯一标识
<template>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
node-key="catId"
>
<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
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)"
>
Delete
</el-button>
</span>
</span></el-tree
>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》'
export default {
//import引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
return {
menus: [],
defaultProps: {
children: "children",
label: "name",
},
};
},
//计算属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
methods: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/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);
},
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
效果如下
三级分类-删除-逻辑删除
参考官方文档
在controller中添加
/**
* 删除
*/
@RequestMapping("/delete")
public R delete(@RequestBody Long[] catIds){
//1. 检查当前删除的菜单,是否被别的地方引用
// pmsCategoryService.removeByIds(Arrays.asList(catIds));
pmsCategoryService.removeMenusByIds(Arrays.asList(catIds));
return R.ok();
}
实现方法
@Override
public void removeMenusByIds(List<Long> asList) {
//TODO 1、检查当前删除的菜单,是否被别的地方引用
baseMapper.deleteBatchIds(asList);
}
使用逻辑删除,步骤如下:
2、逻辑删除
* 1)、配置全局的逻辑删除规则(省略)
* 2)、配置逻辑删除组件(省略)
* 3)、实体类字段上加上@TableLogic注解
global-config:
db-config:
id-type: auto
logic-delete-value: 1
logic-not-delete-value: 0
/**
* 是否显示[0-不显示,1显示]
*/
@TableLogic(value = "1", delval = "0")
private Integer showStatus;
使用psotman测试一下
可以打开debug功能在控制台显示sql语句
logging:
level:
com.example.gulimall: debug
三级分类-删除-删除效果细化
在前台页面中添加remov方法
首先可以添加用户快捷模板
{
"生成vue模板": {
"prefix": "vue",
"body": [
"<template>",
"<div></div>",
"</template>",
"",
"<script>",
"//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)",
"//例如:import 《组件名称》 from '《组件路径》'",
"",
"export default {",
"//import引入的组件需要注入到对象中才能使用",
"components: {},",
"props: {},",
"data() {",
"//这里存放数据",
"return {};",
"},",
"//计算属性 类似于data概念",
"computed: {},",
"//监控data中的数据变化",
"watch: {},",
"//方法集合",
"methods: {},",
"//声明周期 - 创建完成(可以访问当前this实例)",
"created() {},",
"//声明周期 - 挂载完成(可以访问DOM元素)",
"mounted() {},",
"beforeCreate() {}, //生命周期 - 创建之前",
"beforeMount() {}, //生命周期 - 挂载之前",
"beforeUpdate() {}, //生命周期 - 更新之前",
"updated() {}, //生命周期 - 更新之后",
"beforeDestroy() {}, //生命周期 - 销毁之前",
"destroyed() {}, //生命周期 - 销毁完成",
"activated() {} //如果页面有keep-alive缓存功能,这个函数会触发",
"};",
"</script>",
"<style scoped>",
"$4",
"</style>"
],
"description": "生成vue模版"
},
"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请求"
}
}
然后修改remove方法
remove(node, data) {
var ids = [data.catId]
this.$http({
url: this.$http.adornUrl("/product/pmscategory/delete"),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
console.log("删除成功");
this.getMenus();
});
console.log("remove", node, data);
},
虽然可以实现删除功能,但是并不能带来更好的用户体验,因此添加提示信息以及删除完成信息,还有删除完之后不合并菜单的效果
加入MessageBox 弹框
Element - The world's most popular Vue UI framework
<template>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
node-key="catId"
:default-expanded-keys="expandedKey"
>
<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
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)"
>Delete</el-button>
</span>
</span>
</el-tree>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》'
export default {
//import引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
return {
menus: [],
expandedKey: [],
defaultProps: {
children: "children",
label: "name"
}
};
},
//计算属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
methods: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/list/tree"),
method: "get"
}).then(({ data }) => {
console.log("成功获取菜单数据。。。", data.data);
this.menus = data.data;
});
},
append(data) {
console.log("append", data);
},
remove(node, data) {
var ids = [data.catId];
this.$confirm(`是否删除【${data.name}】?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
console.log("remove", node, data);
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
三级分类-新增
设置对话框
Element - The world's most popular Vue UI framework
<el-dialog title="提示" :visible.sync="dialogVisible" width="30%">
<span>这是一段信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
加一个输入框
<el-form :model="category">
<el-form-item label="分类名称">
<el-input v-model="category.name" autocomplete="off"></el-input>
</el-form-item>
</el-form>
代码如下
<template>
<div>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
node-key="catId"
:default-expanded-keys="expandedKey"
>
<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
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)"
>Delete</el-button>
</span>
</span>
</el-tree>
<el-dialog title="提示" :visible.sync="dialogVisible" width="30%">
<el-form :model="category">
<el-form-item label="分类名称">
<el-input v-model="category.name" 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="addCategory">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》'
export default {
//import引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
return {
category: { name: "", parentCid: 0, catLevel: 0, showStatus: 1, sort: 0 },
dialogVisible: false,
menus: [],
expandedKey: [],
defaultProps: {
children: "children",
label: "name"
}
};
},
//计算属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
methods: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/list/tree"),
method: "get"
}).then(({ data }) => {
console.log("成功获取菜单数据。。。", data.data);
this.menus = data.data;
});
},
append(data) {
this.dialogVisible = true;
console.log("append", data);
this.category.parentCid = data.catId;
this.category.catLevel = data.catLevel * 1 + 1;
},
addCategory() {
console.log("提交的三级菜单", this.category);
this.$http({
url: this.$http.adornUrl("/product/pmscategory/save"),
method: "post",
data: this.$http.adornData(this.category, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
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/pmscategory/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
console.log("remove", node, data);
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
注意出现乱码的解决方法是在yml中加入以下这些代码
?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
比如
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.159.135:3306/gulimall_pms?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
三级分类-修改
添加和修改使用一个弹窗,但是需要判断条件
<template>
<div>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
node-key="catId"
:default-expanded-keys="expandedKey"
>
<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 {
dialogType: "",
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: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/list/tree"),
method: "get"
}).then(({ data }) => {
console.log("成功获取菜单数据。。。", data.data);
this.menus = data.data;
});
},
append(data) {
this.dialogType = "add";
this.dialogVisible = true;
console.log("append", data);
this.category.parentCid = data.catId;
this.category.catLevel = data.catLevel * 1 + 1;
this.category.name = "";
this.category.catId = null;
this.category.productUnit = "";
this.category.icon = "";
},
edit(data) {
this.dialogType = "edit";
this.dialogVisible = true;
console.log("edit", data);
//发送请求获取当前结点最新的数据
this.$http({
url: this.$http.adornUrl(`/product/pmscategory/info/${data.catId}`),
method: "get"
}).then(({ data }) => {
//请求成功
console.log("要回显的数据", data);
this.category.name = data.data.name;
this.category.catId = data.data.catId;
this.category.productUnit = data.data.productUnit;
this.category.icon = data.data.icon;
this.category.parentCid = data.data.parentCid;
});
},
remove(node, data) {
var ids = [data.catId];
this.$confirm(`是否删除【${data.name}】?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
console.log("remove", node, data);
},
//提交数据
submitData() {
if (this.dialogType == "add") {
this.addCategory();
}
if (this.dialogType == "edit") {
this.editCategory();
}
},
//添加三级分类数据
addCategory() {
console.log("提交的三级菜单", this.category);
this.$http({
url: this.$http.adornUrl("/product/pmscategory/save"),
method: "post",
data: this.$http.adornData(this.category, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单保存成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
},
//修改三级分类数据
editCategory() {
var { catId, name, icon, productUnit } = this.category; //解构
this.$http({
url: this.$http.adornUrl("/product/pmscategory/update"),
method: "post",
data: this.$http.adornData({ catId, name, icon, productUnit }, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单修改成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
可拖拽结点效果
Element - The world's most popular Vue UI framework、
判断拖拽之后节点深度是否大于3
如果大于3则不能进行拖拽
<template>
<div>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
node-key="catId"
:default-expanded-keys="expandedKey"
draggable
:allow-drop="allowDrop"
>
<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 {
maxLevel: 0,
dialogType: "",
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: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/list/tree"),
method: "get"
}).then(({ data }) => {
console.log("成功获取菜单数据。。。", data.data);
this.menus = data.data;
});
},
append(data) {
this.dialogType = "add";
this.dialogVisible = true;
console.log("append", data);
this.category.parentCid = data.catId;
this.category.catLevel = data.catLevel * 1 + 1;
this.category.name = "";
this.category.catId = null;
this.category.productUnit = "";
this.category.icon = "";
},
edit(data) {
this.dialogType = "edit";
this.dialogVisible = true;
console.log("edit", data);
//发送请求获取当前结点最新的数据
this.$http({
url: this.$http.adornUrl(`/product/pmscategory/info/${data.catId}`),
method: "get"
}).then(({ data }) => {
//请求成功
console.log("要回显的数据", data);
this.category.name = data.data.name;
this.category.catId = data.data.catId;
this.category.productUnit = data.data.productUnit;
this.category.icon = data.data.icon;
this.category.parentCid = data.data.parentCid;
});
},
remove(node, data) {
var ids = [data.catId];
this.$confirm(`是否删除【${data.name}】?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
console.log("remove", node, data);
},
//判断能否被拖拽
allowDrop(draggingNode, dropNode, type) {
//1、被拖拽的当前节点以及所在的父节点总层数不能大于3
//1)、被拖拽的当前节点总层数
console.log("allowDrop", draggingNode, dropNode, type);
this.countNodeLevel(draggingNode.data);
let deep = (this.maxLevel - draggingNode.data.catLevel) + 1;
//当前正在拖拽的节点+父节点所在的深度不大于3即可
if(type == "inner"){
return (deep + dropNode.level) <= 3;
}else{
return (deep + draggingNode.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]);
}
}
},
//提交数据
submitData() {
if (this.dialogType == "add") {
this.addCategory();
}
if (this.dialogType == "edit") {
this.editCategory();
}
},
//添加三级分类数据
addCategory() {
console.log("提交的三级菜单", this.category);
this.$http({
url: this.$http.adornUrl("/product/pmscategory/save"),
method: "post",
data: this.$http.adornData(this.category, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单保存成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
},
//修改三级分类数据
editCategory() {
var { catId, name, icon, productUnit } = this.category; //解构
this.$http({
url: this.$http.adornUrl("/product/pmscategory/update"),
method: "post",
data: this.$http.adornData({ catId, name, icon, productUnit }, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单修改成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
三级分类-拖拽数据收集
1、需要获取当前节点的最新父节点的id
2、当前拖拽节点的最新顺序
3、当前拖拽节点以及子节点的最新层级
handleDrop(draggingNode, dropNode, dropType, ev) {
console.log('handleDrop: ', draggingNode, dropNode, dropType);
//1、当前节点的最新父节点的id
let Pcid = 0;
let siblings = null;
if(dropType == "before" || dropType == "after"){
Pcid = dropNode.parent.data.catId == undefined? 0: dropNode.parent.childNodes;
siblings = dropNode.parent.childNodes;
}else{
Pcid = dropNode.data.catId;
siblings = dropNode.childNodes;
}
//2、当前拖拽节点的最新顺序
for(let i = 0;i<siblings.length;i++){
if(siblings[i].data.catId == draggingNode.data.catId){
//如果遍历的是当前正在拖拽的节点
let catLevel = draggingNode.data.catLevel;
if(siblings[i].level != draggingNode.level){
//当前节点的层级发生变化
catLevel = siblings[i].level;
//修改子节点的层级
this.updateChildNodeLevel(siblings[i]);
}
this.udpataNodes.push({catId: siblings[i].data.catId, sort: i, parentCid: Pcid, catLevel: catLevel});
}else{
this.udpataNodes.push({catId: siblings[i].data.catId, sort: i});
}
}
//3、当前拖拽节点的最新层级
},
updateChildNodeLevel(node){
if(node.childNodes.length > 0){
for(let i = 0;i<node.length;i++){
var cNode = node.childNodes[i].data;
this.udpataNodes.push({catId: cNode.catId, catLevel: node.childNodes[i].level});
this.updateChildNodeLevel(node.childNodes[i]);
}
}
},
三级分类-修改-批量拖拽
添加一个拖拽按钮
Element - The world's most popular Vue UI framework
<template>
<div>
<el-switch v-model="draggable" active-text="开启拖拽" inactive-text="关闭拖拽"></el-switch>
<el-button v-if="draggable" @click="batchSave">批量保存</el-button>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
node-key="catId"
:default-expanded-keys="expandedKey"
:draggable="draggable"
:allow-drop="allowDrop"
@node-drop="handleDrop"
>
<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 {
draggable: false,
Pcid: [],
udpataNodes: [],
maxLevel: 0,
dialogType: "",
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: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/list/tree"),
method: "get"
}).then(({ data }) => {
console.log("成功获取菜单数据。。。", data.data);
this.menus = data.data;
});
},
append(data) {
this.dialogType = "add";
this.dialogVisible = true;
console.log("append", data);
this.category.parentCid = data.catId;
this.category.catLevel = data.catLevel * 1 + 1;
this.category.name = "";
this.category.catId = null;
this.category.productUnit = "";
this.category.icon = "";
},
edit(data) {
this.dialogType = "edit";
this.dialogVisible = true;
console.log("edit", data);
//发送请求获取当前结点最新的数据
this.$http({
url: this.$http.adornUrl(`/product/pmscategory/info/${data.catId}`),
method: "get"
}).then(({ data }) => {
//请求成功
console.log("要回显的数据", data);
this.category.name = data.data.name;
this.category.catId = data.data.catId;
this.category.productUnit = data.data.productUnit;
this.category.icon = data.data.icon;
this.category.parentCid = data.data.parentCid;
});
},
remove(node, data) {
var ids = [data.catId];
this.$confirm(`是否删除【${data.name}】?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
console.log("remove", node, data);
},
batchSave() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/update/sort"),
method: "post",
data: this.$http.adornData(this.udpataNodes, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单修改成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = this.Pcid;
this.udpataNodes = [];
this.maxLevel = 0;
// this.Pcid = 0;
});
},
handleDrop(draggingNode, dropNode, dropType, ev) {
console.log("handleDrop: ", draggingNode, dropNode, dropType);
//1、当前节点的最新父节点的id
let Pcid = 0;
let siblings = null;
if (dropType == "before" || dropType == "after") {
Pcid =
dropNode.parent.data.catId == undefined
? 0
: dropNode.parent.childNodes;
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.data.catLevel;
if (siblings[i].level != draggingNode.level) {
//当前节点的层级发生变化
catLevel = siblings[i].level;
//修改子节点的层级
this.updateChildNodeLevel(siblings[i]);
}
this.udpataNodes.push({
catId: siblings[i].data.catId,
sort: i,
parentCid: Pcid,
catLevel: catLevel
});
} else {
this.udpataNodes.push({ catId: siblings[i].data.catId, sort: i });
}
}
//3、当前拖拽节点的最新层级
console.log("updateNodes", this.udpataNodes);
},
updateChildNodeLevel(node) {
if (node.childNodes.length > 0) {
for (let i = 0; i < node.length; i++) {
var cNode = node.childNodes[i].data;
this.udpataNodes.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);
let deep = Math.abs(this.maxLevel - draggingNode.level) + 1;
//当前正在拖拽的节点+父节点所在的深度不大于3即可
if (type == "inner") {
return deep + dropNode.level <= 3;
} else {
return deep + draggingNode.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]);
}
}
},
//提交数据
submitData() {
if (this.dialogType == "add") {
this.addCategory();
}
if (this.dialogType == "edit") {
this.editCategory();
}
},
//添加三级分类数据
addCategory() {
console.log("提交的三级菜单", this.category);
this.$http({
url: this.$http.adornUrl("/product/pmscategory/save"),
method: "post",
data: this.$http.adornData(this.category, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单保存成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
},
//修改三级分类数据
editCategory() {
var { catId, name, icon, productUnit } = this.category; //解构
this.$http({
url: this.$http.adornUrl("/product/pmscategory/update"),
method: "post",
data: this.$http.adornData({ catId, name, icon, productUnit }, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单修改成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
三级分类-删除-批量删除
<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"
show-checkbox
:expand-on-click-node="false"
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 {
draggable: false,
Pcid: [],
udpataNodes: [],
maxLevel: 0,
dialogType: "",
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: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/list/tree"),
method: "get"
}).then(({ data }) => {
console.log("成功获取菜单数据。。。", data.data);
this.menus = data.data;
});
},
append(data) {
this.dialogType = "add";
this.dialogVisible = true;
console.log("append", data);
this.category.parentCid = data.catId;
this.category.catLevel = data.catLevel * 1 + 1;
this.category.name = "";
this.category.catId = null;
this.category.productUnit = "";
this.category.icon = "";
},
edit(data) {
this.dialogType = "edit";
this.dialogVisible = true;
console.log("edit", data);
//发送请求获取当前结点最新的数据
this.$http({
url: this.$http.adornUrl(`/product/pmscategory/info/${data.catId}`),
method: "get"
}).then(({ data }) => {
//请求成功
console.log("要回显的数据", data);
this.category.name = data.data.name;
this.category.catId = data.data.catId;
this.category.productUnit = data.data.productUnit;
this.category.icon = data.data.icon;
this.category.parentCid = data.data.parentCid;
});
},
remove(node, data) {
var ids = [data.catId];
this.$confirm(`是否删除【${data.name}】?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
console.log("remove", node, 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/pmscategory/delete"),
method: "post",
data: this.$http.adornData(catIds, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单删除成功!",
type: "success"
});
//刷新出新的菜单
this.getMenus();
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
},
batchSave() {
this.$http({
url: this.$http.adornUrl("/product/pmscategory/update/sort"),
method: "post",
data: this.$http.adornData(this.udpataNodes, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单修改成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = this.Pcid;
this.udpataNodes = [];
this.maxLevel = 0;
// this.Pcid = 0;
});
},
handleDrop(draggingNode, dropNode, dropType, ev) {
console.log("handleDrop: ", draggingNode, dropNode, dropType);
//1、当前节点的最新父节点的id
let Pcid = 0;
let siblings = null;
if (dropType == "before" || dropType == "after") {
Pcid =
dropNode.parent.data.catId == undefined
? 0
: dropNode.parent.childNodes;
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.data.catLevel;
if (siblings[i].level != draggingNode.level) {
//当前节点的层级发生变化
catLevel = siblings[i].level;
//修改子节点的层级
this.updateChildNodeLevel(siblings[i]);
}
this.udpataNodes.push({
catId: siblings[i].data.catId,
sort: i,
parentCid: Pcid,
catLevel: catLevel
});
} else {
this.udpataNodes.push({ catId: siblings[i].data.catId, sort: i });
}
}
//3、当前拖拽节点的最新层级
console.log("updateNodes", this.udpataNodes);
},
updateChildNodeLevel(node) {
if (node.childNodes.length > 0) {
for (let i = 0; i < node.length; i++) {
var cNode = node.childNodes[i].data;
this.udpataNodes.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);
let deep = Math.abs(this.maxLevel - draggingNode.level) + 1;
//当前正在拖拽的节点+父节点所在的深度不大于3即可
if (type == "inner") {
return deep + dropNode.level <= 3;
} else {
return deep + draggingNode.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]);
}
}
},
//提交数据
submitData() {
if (this.dialogType == "add") {
this.addCategory();
}
if (this.dialogType == "edit") {
this.editCategory();
}
},
//添加三级分类数据
addCategory() {
console.log("提交的三级菜单", this.category);
this.$http({
url: this.$http.adornUrl("/product/pmscategory/save"),
method: "post",
data: this.$http.adornData(this.category, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单保存成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
},
//修改三级分类数据
editCategory() {
var { catId, name, icon, productUnit } = this.category; //解构
this.$http({
url: this.$http.adornUrl("/product/pmscategory/update"),
method: "post",
data: this.$http.adornData({ catId, name, icon, productUnit }, false)
}).then(({ data }) => {
this.$notify({
title: "成功",
message: "菜单修改成功!",
type: "success"
});
//关闭对话框
this.dialogVisible = false;
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.category.parentCid];
});
}
},
//方法集合
//声明周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//声明周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>