基本介绍
pms_category表说明
代表商品的分类
controller
代码如下:
java
@RestController
@RequestMapping("product/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
* 查询所有分类以及子分类列表,以树性结构组装起来
*/
@RequestMapping("/list/tree")
public R list( ){
List<CategoryEntity> entities = categoryService.listWithTree();
return R.ok().put("data", entities);
}
}
service
代码如下:
java
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
@Override
public List<CategoryEntity> listWithTree() {
List<CategoryEntity> entities = baseMapper.selectList(null);
List<CategoryEntity> result = entities.stream().filter(categoryEntity ->
categoryEntity.getParentCid() == 0
).peek((menu) -> {
menu.setChildren(getChildrens(menu, entities));
}).sorted((menu1, menu2) -> {
return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
}).collect(Collectors.toList());
return result;
}
/**
* 递归查找当前菜单的所有子菜单
*
* @param root 当前菜单
* @param all 所有菜单
*/
private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {
List<CategoryEntity> childrens = all.stream().filter(categoryEntity ->
categoryEntity.getParentCid() == root.getCatId()
).peek((menu) -> {
menu.setChildren(getChildrens(menu, all));
}).sorted((menu1, menu2) -> {
return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
}).collect(Collectors.toList());
return childrens;
}
}
启动项目:访问:http://localhost:10000/product/category/list/tree
然后接着登录系统,添加目录"商品系统"
我们发现在菜单管理里面配置的角色管理,配置的URL
是sys/role
,但是真正点击角色管理时,URL
会将/
变成-
,即从sys/role
变成sys-role
然后我的角色管理的vue
文件在目录下:src\views\modules\sys\role.vue
下
所有我们新创建了分类维护 ,URL
为/product/category
,对应应该在src\views\modules\product\category.vue
,于是我们创建category.vue
文件,然后先生成vue
模板文件
复制如下代码到category.vue
中,将固定的数据data
属性中的数据删除掉
html
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
<script>
export default {
data() {
return {
data: [{
label: '一级 1',
children: [{
label: '二级 1-1',
children: [{
label: '三级 1-1-1'
}]
}]
}, {
label: '一级 2',
children: [{
label: '二级 2-1',
children: [{
label: '三级 2-1-1'
}]
}, {
label: '二级 2-2',
children: [{
label: '三级 2-2-1'
}]
}]
}, {
label: '一级 3',
children: [{
label: '二级 3-1',
children: [{
label: '三级 3-1-1'
}]
}, {
label: '二级 3-2',
children: [{
label: '三级 3-2-1'
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
};
},
methods: {
handleNodeClick(data) {
console.log(data);
}
}
};
</script>
我们发现一开始前端项目的请求地址前缀都是http://localhost:8080/renren-fast
开头的,
是因为在E:\renren-fast-vue\static\config\index.js
中定义了前端vue
发送每个请求的前缀,但是我们后台的gulimall-product
服务的端口的是10000
,所以我们需要前端vue
项目首先向网关gateway
发送请求,然后路由到指定的后台服务。
我们先修改这里的请求前缀变为http://localhost:80
,因为网关gulimall-gateway
服务的端口是88
,即之后所有的vue
请求首先发送给网关,然后再由网关路由到指定的服务。
同样我们的后台服务renren-fast
也需要注册到nacos
中,因为renren-fast
中的比如登录验证码也需要前端vue
通过网关gateway
路由到它,所以需要先将它注册到nacos
中,网关才能发现它
首先renren-fast
中的pom
文件需要依赖gulimall-common
,因为gulimall-common
中有nacos
的依赖。
xml
<dependency>
<groupId>com.hs.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
然后在启动类上加上注解@EnableDiscoveryClient
接着在application.xml
中配置renren-fast
微服务名称以及nacos
注册中心地址
yml
spring
application:
name: renren-fast
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
我们接着修改gulimall-gateway
中application.yml
配置路由规则
yml
spring:
cloud:
gateway:
routes:
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/${segment}
## 前端项目,/api
## http://localhost:88/api/captcha.jpg http://localhost:8080/renren-fast/captcha.jpg
## http://localhost:88/api/product/category/list/tree http://localhost:10000/product/category/list/tree
我们发现前端请求的接口是88
,但是页面上显示的会变成8001
,是因为E:\renren-fast-vue\config\index.js
有配置端口转换修改
启动nacos-server
,然后重启renren-fast
服务和gulimall-gateway
网关服务,重新登录系统,发现login
请求报错,看报错是同源策略的报错
跨域问题
问题描述:已拦截跨源请求:同源策略禁止8001端口页面读取位于 http://localhost:88/api/sys/login 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
-
问题分析:这是一种跨域问题。访问的域名或端口和原来请求的域名端口一旦不同,请求就会被限制
-
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js施加的安全限制。(
ajax
可以) -
同源策略:是指协议,域名,端囗都要相同,其中有一个不同都会产生跨域;
跨域流程:
这个跨域请求的实现是通过预检请求实现的,先发送一个OPSTIONS
探路,收到响应允许跨域后再发送真实请求
什么意思呢?跨域是要请求的、新的端口那个服务器限制的,不是浏览器限制的。
js
跨域请求流程:
非简单请求(PUT、DELETE)等,需要先发送预检请求
-----1、预检请求、OPTIONS ------>
<----2、服务器响应允许跨域 ------
浏览器 | | 服务器
-----3、正式发送真实请求 -------->
<----4、响应数据 --------------
在网关服务gulimall-gateway
中配置跨域过滤器
java
package com.hs.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.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
/**
* @Auther: huangshuai
* @Date: 2021/8/17 22:41
* @Description:配置跨域
* @Version:
*/
@Configuration
public class GulimallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration= new CorsConfiguration();
//1、配置跨域
// 允许跨域的头
corsConfiguration.addAllowedHeader("*");
// 允许跨域的请求方式
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求来源
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie跨域
corsConfiguration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
注释掉renren-fast
项目的类CorsConfig
中的配置的跨域
查询分类维护的vue
代码如下:
vue
<!-- -->
<template>
<el-tree :data="menus" :props="defaultProps" @node-click="handleNodeClick">
</el-tree>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如: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 }) => {
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>
新增分类
前端category.vue
代码如下:
vue
<!-- -->
<template>
<div>
<el-tree
:data="menus"
:props="defaultProps"
:expand-on-click-node="false"
show-checkbox
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)">新增</el-button>
<el-button
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)"
>删除</el-button>
</span>
</span>
</el-tree>
<!--对话框组件-->
<el-dialog title="提示" :visible.sync="dialogVisible" width="30%">
<el-form :model="categroy">
<el-form-item label="分类名称">
<el-input v-model="categroy.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: {},
data() {
return {
categroy: {
name: "",
parentCid: 0,
catLevel: 0,
showStatus: 1,
sort: 0
},
//对话框属性,false为对话框是关闭的
dialogVisible: false,
menus: [],
expandedKey: [],
defaultProps: {
children: "children", //子节点
label: "name" //name属性作为标签的值,展示出来
}
};
},
methods: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/category/list/tree"),
method: "get"
}).then(({ data }) => {
this.menus = data.data;
});
},
//点击新增按钮的时候,打开对话框
append(data) {
//对话框打开
this.dialogVisible = true;
//父分类的id
this.categroy.parentCid = data.catId;
//当前新增分类的level
this.categroy.catLevel = data.catLevel * 1 + 1;
},
//添加三级分类方法
addCategory() {
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;
});
},
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(() => {});
}
},
//监听属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
//生命周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
修改分类
前端category.vue
代码如下:
vue
<!-- -->
<template>
<div>
<el-tree
:data="menus"
:props="defaultProps"
:expand-on-click-node="false"
show-checkbox
node-key="catId"
:default-expanded-keys="expandedKey"
draggable
>
<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)">新增</el-button>
<el-button type="text" size="mini" @click="edit(data)">编辑</el-button>
<el-button
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)"
>删除</el-button>
</span>
</span>
</el-tree>
<!--对话框组件-->
<el-dialog :title="title" :visible.sync="dialogVisible" width="30%" :closeOnClickModal="false">
<el-form :model="categroy">
<el-form-item label="分类名称">
<el-input v-model="categroy.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="图标">
<el-input v-model="categroy.icon" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="计量单位">
<el-input v-model="categroy.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: {},
data() {
return {
//弹出框的提示语
title: "",
//弹出对话框的类型,是由于点击新增打开的还是编辑打开的?
dialogType: "",
categroy: {
catId: null,
name: "",
parentCid: 0,
catLevel: 0,
showStatus: 1,
icon: "",
productUnit: "",
sort: 0
},
//对话框属性,false为对话框是关闭的
dialogVisible: false,
menus: [],
expandedKey: [],
defaultProps: {
children: "children", //子节点
label: "name" //name属性作为标签的值,展示出来
}
};
},
methods: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/category/list/tree"),
method: "get"
}).then(({ data }) => {
this.menus = data.data;
});
},
//判断当前是新增还是编辑
submitData() {
if(this.dialogType == "add") {
this.addCategory();
}
if(this.dialogType == "edit") {
this.editCategory();
}
},
//编辑
edit(data) {
console.log("要修改的数据", data);
this.title = "修改分类"
this.dialogType = "edit";
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.icon = data.data.icon;
this.categroy.productUnit = data.data.productUnit;
this.categroy.parentCid = data.data.parentCid;
});
},
//编辑三级分类方法
editCategory() {
var {catId, name, icon, productUnit} = this.categroy;
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.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [this.categroy.parentCid];
//对话框关闭
this.dialogVisible = false;
});
},
//点击新增按钮的时候,打开对话框
append(data) {
this.title = "添加分类"
//对话框的类型
this.dialogType = "add";
//对话框打开
this.dialogVisible = true;
//父分类的id
this.categroy.parentCid = data.catId;
//当前新增分类的level
this.categroy.catLevel = data.catLevel * 1 + 1;
this.categroy.catId = null;
this.categroy.name = "";
this.categroy.icon = "";
this.categroy.productUnit = "";
this.categroy.sort = 0;
this.categroy.showStatus = 1;
},
//添加三级分类方法
addCategory() {
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;
});
},
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(() => {});
}
},
//监听属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
//生命周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
拖拽修改
vue
<template>
<div>
<el-row>
<el-col :span="24">
<el-form :inline="true" :model="dataForm">
<el-form-item label="集装箱号">
<el-input style="width:160px" v-model="dataForm.key" clearable></el-input>
</el-form-item>
<el-form-item label="byd批次号">
<el-input style="width:160px" v-model="dataForm.key" clearable></el-input>
</el-form-item>
<el-form-item label="状态">
<el-input style="width:160px" v-model="dataForm.key" clearable></el-input>
</el-form-item>
<el-form-item label="检索">
<el-input style="width:160px" v-model="dataForm.key" clearable></el-input>
</el-form-item>
<el-form-item label="检索">
<el-input style="width:160px" v-model="dataForm.key" clearable></el-input>
</el-form-item>
<el-form-item label="检索">
<el-input style="width:160px" v-model="dataForm.key" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchSpuInfo">查询</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchSpuInfo">重置</el-button>
</el-form-item>
<el-form-item>
<el-upload
:auto-upload="true"
:file-list="fileList"
:http-request="importWordConfirm"
:limit="1"
:multiple="false"
accept=".xlsx, .xls"
action
ref="upload"
>
<el-button type="primary">导入</el-button>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchSpuInfo">导出</el-button>
</el-form-item>
</el-form>
</el-col>
<el-col :span="24">
<div class="mod-config">
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;"
>
<el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>
<el-table-column prop="id" header-align="center" align="center" label="id"></el-table-column>
<el-table-column prop="spuName" header-align="center" align="center" label="名称"></el-table-column>
<el-table-column prop="spuDescription" header-align="center" align="center" label="描述"></el-table-column>
<el-table-column prop="catalogId" header-align="center" align="center" label="分类"></el-table-column>
<el-table-column prop="brandId" header-align="center" align="center" label="品牌"></el-table-column>
<el-table-column prop="weight" header-align="center" align="center" label="重量"></el-table-column>
<el-table-column prop="publishStatus" header-align="center" align="center" label="上架状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.publishStatus == 0">新建</el-tag>
<el-tag v-if="scope.row.publishStatus == 1">已上架</el-tag>
<el-tag v-if="scope.row.publishStatus == 2">已下架</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" header-align="center" align="center" label="创建时间"></el-table-column>
<el-table-column prop="updateTime" header-align="center" align="center" label="修改时间"></el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作"
>
<template slot-scope="scope">
<el-button
v-if="scope.row.publishStatus == 0"
type="text"
size="small"
@click="productUp(scope.row.id)"
>上架</el-button>
<el-button type="text" size="small" @click="attrUpdateShow(scope.row)">规格</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
import CategoryCascader from "../common/category-cascader";
import BrandSelect from "../common/brand-select";
export default {
//import引入的组件需要注入到对象中才能使用
components: { CategoryCascader, BrandSelect },
props: {},
data() {
//这里存放数据
return {
fileList: [],
catId: 0,
catelogPath: [],
dataForm: {
status: "",
key: "",
brandId: 0,
catelogId: 0
},
catPathSub: null,
brandIdSub: null
};
},
//计算属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
//方法集合
methods: {
searchSpuInfo() {
console.log("搜索条件", this.dataForm);
this.PubSub.publish("dataForm", this.dataForm);
},
//上传文件
importWordConfirm(param) {
const fileObj = param.file;
const formData = new FormData();
formData.append("file", fileObj);
this.$http({
url: this.$http.adornUrl("/product/test/excelImport"),
method: 'post',
data: this.$http.adornData(formData, false),
headers: {
"Content-Type": "multipart/form-data"
}
}).then(({ data }) => {
this.$message({
message: "上传文件成功",
type: "success"
});
});
}
},
//生命周期 - 创建完成(可以访问当前this实例)
created() {},
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {
this.catPathSub = PubSub.subscribe("catPath", (msg, val) => {
this.dataForm.catelogId = val[val.length - 1];
});
this.brandIdSub = PubSub.subscribe("brandId", (msg, val) => {
this.dataForm.brandId = val;
});
},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {
PubSub.unsubscribe(this.catPathSub);
PubSub.unsubscribe(this.brandIdSub);
}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>