十、商品服务&属性分组
上一级页面: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());
    }