十、商品服务&属性分组
上一级页面:index-la
十、商品服务&属性分组
10.1 、前端组件抽取 & 父子组件交互
10.1.1 属性分组 - 效果
左边这个树形空间我们已经写过了,在三级分类的时候编写过,相对应的功能,拖拽,添加,删除等等都已经实现,那我们为什么不把他抽取出一个公共的组件便于其他组件利用?
说干就干!
在 modules 中新建 common 文件夹,在common中 新建 category.vue
category.vue 核心代码
抽取需要的结果,多余的结果进行删除
<template>
<el-tree :data="menus" :props="defaultProps" node-key="catId" ref="menuTree" @node-click="nodeClick">
</el-tree>
</template>
10.1.2 Layout 布局
通过基础的 24 分栏,迅速简便地创建布局、类似之前学过的bootstrap
<el-row :gutter="20">
<el-col :span="6">表格</el-col>
<el-col :span="18">菜单</el-col>
</el-row>
左边放表格,右边放菜单
引入 category 组件
import category from "../common/category";
并且声明
//import引入的组件需要注入到对象中才能使用
components: {
category
},
最后在组件中使用
<el-col :span="6">
<!-- 直接使用-->
<category @tree-node-click="treenodeclick"></category>
</el-col>
右边菜单使用代码生成器的代码直接引入即可
10.1.3 父子组件如何进行交互
子组件中的 Tree 组件的一个事件 node-click 节点被点击时的回调
// 向父组件发送事件,后面是需要传递的对象,参数等
this.$emit("tree-node-click",data,node,component)
父组件需要定义相同方法接收
<category @tree-node-click="treenodeclick"></category>
// 感知到节点被点击
treenodeclick(node, data, component) {
console.log("attrgroup感知到category节点被点击");
console.log("刚才被点击的菜单id:" + data.catId);
if (node.level == 3) {
this.catId = data.catId;
this.getDataList();
}
},
10.2 、获取属性分类分组
查看提供的接口文档
请求参数需要我们带对应的分页参数,所以我们在请求的时候得把参数带上
那就定义接口 go
/**
*
* @param params 分页请求相关参数
* @param catelogId 三级分类id
* @return
*/
PageUtils queryPage(Map<String, Object> params, Long catelogId);
实现类
@Override
public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
// 分类id 等于01 查询全部
if (catelogId == 0) {
IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),
new QueryWrapper<AttrGroupEntity>());
return new PageUtils(page);
} else {
// 拿到参数中的 key
String key = (String) params.get("key");
// 先根据分类id进行查询
QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<AttrGroupEntity>()
.eq("catelog_id",catelogId);
// selecet * from attrgroup where catelog_id = ? and (attr_group_id = key or like attr_group_name = key)
// 有时候查询也不会带上key 所以得判断
if (!StringUtils.isEmpty(key)) {
// where条件后加上 and
wrapper.and((obj) -> {
obj.eq("attr_group_id",key).or().like("attr_group_name",key);
});
}
// 组装条件进行查询
IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),
wrapper);
return new PageUtils(page);
}
}
10.3 、分类新增 & 级联选择器
级联选择器是啥?? 没接触过
官方解释
Cascader 级联选择器
当一个数据集合有清晰的层级结构时,可通过级联选择器逐级查看并选择。
对应效果
attrgroup-add-or-update.vue 中 加入该组件
<el-cascader
v-model="dataForm.catelogPath"
placeholder="试试搜索:手机"
:options="categorys"
:props="props"
filterable
></el-cascader>
<!--
placeholder="试试搜索:手机"默认的搜索提示
:options="categorys" 可选项数据源,键名可通过 Props 属性配置
:props="props" 配置选项
filterable 是否可搜索选项
-->
那么问题来了? 我怎样把数据加载到这个组件里面?
在你组件加载完成后,我在调用方法 ( getCategorys() ) 获取菜单数据,在设置到options不就行了吗?
getCategorys(){
this.$http({
url: this.$http.adornUrl("/product/category/list/tree"),
method: "get"
}).then(({ data }) => {
this.categorys = data.data;
});
},
10.4 、分类修改 & 回显级联选择器
修改和新增用的是一个添加组件 那么我们再点击修改后,你如何把 级联显示的数据再次显示出来?
在 AttrGroup点击修改后,会触发addOrUpdateHandle方法,他会通过引用 vue 文件里的 addOrUpdate 并调用他的 init初始化方法
// 新增 / 修改
addOrUpdateHandle(id) {
// 对话框显示
this.addOrUpdateVisible = true;
// 要渲染的组件完成选然后 调用该方法
this.$nextTick(() => {
// this 当前 refs 当前所有组件
this.$refs.addOrUpdate.init(id);
});
},
init执行完成会回调
.then(({ data }) => {
if (data && data.code === 0) {
this.dataForm.attrGroupName = data.attrGroup.attrGroupName;
this.dataForm.sort = data.attrGroup.sort;
this.dataForm.descript = data.attrGroup.descript;
this.dataForm.icon = data.attrGroup.icon;
this.dataForm.catelogId = data.attrGroup.catelogId;
// 设置 级联的路径 从数据中取
this.dataForm.catelogPath = data.attrGroup.catelogPath;
}
});
后端如何根据分组id 查询出 对应的分类?定义接口
/**
* 找到catelogId的完整路径
* 【父/子/孙】
* @param catelogId
*/
Long[] findCatelogPath(Long catelogId);
实现
@Override
public Long[] findCatelogPath(Long catelogId) {
List<Long> paths = new ArrayList<>();
List<Long> parentPath = findParentPath(catelogId, paths);
// 反转
Collections.reverse(parentPath);
//转成Long[] 数组返回
return parentPath.toArray(new Long[parentPath.size()]);
}
/**
* 递归查找
* @param catelogId 三级分类的id
* @param paths 路径
* @return
*/
private List<Long> findParentPath(Long catelogId,List<Long> paths) {
// 1、收集当前节点id
paths.add(catelogId);
// 2、通过分类id拿到 Category 对象
CategoryEntity byId = this.getById(catelogId);
// 3、如果不是根节点 就一直递归下去查找
if (byId.getParentCid() != 0) {
findParentPath(byId.getParentCid(),paths);
}
return paths;
}
在 AttrGroupEntity 中 添加了一个新属性
/**
* 用于存放 级联显示的 父子孙的地址
*/
@TableField(exist = false) // 标注为false 表是不是数据库字段
private Long[] catelogPath;
Controller 具体设置返回数据
/**
* 信息
*/
@RequestMapping("/info/{attrGroupId}")
public R info(@PathVariable("attrGroupId") Long attrGroupId){
// 根据id查询出 分组group对象
AttrGroupEntity attrGroup = attrGroupService.getById(attrGroupId);
// 拿到分类id
Long catelogId = attrGroup.getCatelogId();
// 根据分类id查询出 他的 父 子 孙 对应的数据,并且设置到 attrGroup对象
Long[] catelogPath = categoryService.findCatelogPath(catelogId);
attrGroup.setCatelogPath(catelogPath);
return R.ok().put("attrGroup", attrGroup);
}
10.5、品牌分类关联与级联更新
10.5.1、实现品牌管理搜索
在原本查询中加入新功能
@Override
public PageUtils queryPage(Map<String, Object> params) {
// 1、获取key
String key = (String) params.get("key");
QueryWrapper<BrandEntity> wrapper = new QueryWrapper<>();
// key不为空 brand_id 和 name 进行值匹配
if (!StringUtils.isEmpty(key)) {
wrapper.eq("brand_id",key).or().like("name",key);
}
IPage<BrandEntity> page = this.page(
new Query<BrandEntity>().getPage(params),
wrapper
);
return new PageUtils(page);
}
我们是直接把数据表存进了中间表,如果在真正的品牌名和分类名进行了修改,那么此时中间表的数据就是不对的,这时候数据就不是一致性
解决
在进行修改的时候,也要把中间表的数据进行更改
Brand
// BrandController
/**
/**
* 修改
*/
@RequestMapping("/update")
public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){
brandService.updateDetail(brand);
return R.ok();
}
// Service
@Override
public void updateDetail(BrandEntity brand) {
// 保证字段一致
// 根据id更改
this.updateById(brand);
if (!StringUtils.isEmpty(brand.getName())) {
// 同步更新其他关联表中的数据
categoryBrandRelationService.updateBrand(brand.getBrandId(),brand.getName());
// TODO 更新其他关联
}
}
// Service实现类
@Override
public void updateBrand(Long brandId, String name) {
CategoryBrandRelationEntity relationEntity = new CategoryBrandRelationEntity();
relationEntity.setBrandName(name);
relationEntity.setBrandId(brandId);
this.update(relationEntity,new UpdateWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId));
}
Category
@Override
public void updateCascate(CategoryEntity category) {
this.updateById(category);
categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
}
// Service
@Override
public void updateCascate(CategoryEntity category) {
// 更新自己表对象
this.updateById(category);
// 更新关联表对象
categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
}