谷粒商城实战笔记-71-商品服务-API-属性分组-前端组件抽取&父子组件交互

一,一次性创建所有的菜单

创建菜单的工作非常简单,在页面上操作即可,是重复性的工作。为了避免重复的工作,使用脚本一次性创建所有的菜单。

在这里插入图片描述

二,开发属性分组界面

属性分组界面由左右两个部分构成,左侧是三级分类树,右侧是每个分类关联的分组列表,这种布局可以用Layout布局组件来实现。

  <el-row :gutter="20">
    <el-col :span="6">
      左侧三级分类树形布局
    </el-col>
    <el-col :span="18">
      右侧分组列表
    </el-col>
  </el-row>

1,左侧三级分类树形组件

将左侧三级分类的展示抽象为一个组件,名为category.vue,放在views/modules/common下。

在这里插入图片描述

2,右侧分组列表

这部分使用了el-formel-table组件。

3,左右两部分通信

现在的交互需求是左侧点击了某个分类,右侧要查询这个分类所属的所有分组,左右两边要联动。

难点是左侧是一个组件,定义在组件文件中。

这种情况下,就需要使用vue的父子组件通信机制。

子组件向父组件:通过触发自定义事件,并在父组件中监听这些事件来传递数据。

3.1 子组件发送数据

首先,在category.vue组件中注册结点点击事件。

在这里插入图片描述

在响应事件中使用$.emit向父组件发送数据。

在这里插入图片描述

this.$emit 方法被用来触发一个自定义事件,第一个参数是自定义事件的名称,这里是tree-node-click,父组件在使用子组件时,要通过给这个事件注册响应函数来获取子组件发送的数据。

理解这点非常重要。
理解这点非常重要。
理解这点非常重要。

3.2,父组件接收数据

父组件使用子组件时绑定一个接收数据的事件。

在这里插入图片描述

在这个事件响应函数中处理子组件传递的数据。

	treenodeclick(data, node, component) {
      if (node.level == 3) {
        this.catId = data.catId;
        this.getDataList(); //重新查询
      }
    },

Vue的父子组件通信

Vue.js 中的父子组件通信是构建复杂应用的基础之一。Vue 提供了多种方式来实现父子组件之间的数据传递。下面我将分别介绍父组件向子组件传递数据以及子组件向父组件传递数据的方法。

父组件向子组件传递数据

父组件向子组件传递数据最常用的方式是通过 props。这是 Vue 的单向数据流模型的一部分,确保了数据流动的方向明确且易于追踪。

  • 定义 Props:在子组件中定义接收父组件传递的数据的属性(props)。

    export default {
      props: ['message'],
    }
    
  • 传递 Props:在父组件中使用 <child-component> 标签,并通过属性绑定的方式传递数据。

    <template>
      <div>
        <child-component :message="parentMessage" />
      </div>
    </template>
    
    <script>
    import ChildComponent from './ChildComponent.vue';
    
    export default {
      components: {
        ChildComponent,
      },
      data() {
        return {
          parentMessage: 'Hello from parent',
        };
      },
    };
    </script>
    

子组件向父组件传递数据

子组件可以通过自定义事件的方式向父组件发送数据。这通常涉及到 v-on 或者 @ 指令以及 $emit 方法。

  • 触发事件:在子组件内部使用 $emit 方法触发自定义事件,并传递数据。

    methods: {
      sendMessageToParent() {
        this.$emit('send-message', 'Hello from child');
      },
    }
    
  • 监听事件:在父组件中监听子组件触发的事件,并处理传递过来的数据。

    <template>
      <div>
        <child-component @send-message="handleMessageFromChild" />
      </div>
    </template>
    
    <script>
    import ChildComponent from './ChildComponent.vue';
    
    export default {
      components: {
        ChildComponent,
      },
      methods: {
        handleMessageFromChild(message) {
          console.log('Received message:', message);
        },
      },
    };
    </script>
    
  • 父组件向子组件:通过 props 传递数据。

  • 子组件向父组件:通过触发自定义事件,并在父组件中监听这些事件来传递数据。

category.vue组件完整代码

<template>
  <div>
    <el-input placeholder="输入关键字进行过滤" v-model="filterText"></el-input>
    <el-tree
      :data="menus"
      :props="defaultProps"
      node-key="catId"
      ref="menuTree"
      @node-click="nodeclick"
      :filter-node-method="filterNode"
      :highlight-current = "true"
    ></el-tree>
  </div>
</template>

<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';

export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  data() {
    //这里存放数据
    return {
      filterText: "",
      menus: [],
      expandedKey: [],
      defaultProps: {
        children: "children",
        label: "name"
      }
    };
  },
  //计算属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {
    filterText(val) {
      this.$refs.menuTree.filter(val);
    }
  },
  //方法集合
  methods: {
    //树节点过滤
    filterNode(value, data) {
      if (!value) return true;
      return data.name.indexOf(value) !== -1;
    },
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get"
      }).then(({ data }) => {
        this.menus = data.data;
      });
    },
    nodeclick(data, node, component) {
      console.log("子组件category的节点被点击", data, node, component);
      //向父组件发送事件;
      this.$emit("tree-node-click", data, node, component);
    }
  },
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {
    this.getMenus();
  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {},
  beforeCreate() {}, //生命周期 - 创建之前
  beforeMount() {}, //生命周期 - 挂载之前
  beforeUpdate() {}, //生命周期 - 更新之前
  updated() {}, //生命周期 - 更新之后
  beforeDestroy() {}, //生命周期 - 销毁之前
  destroyed() {}, //生命周期 - 销毁完成
  activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>

</style>

attrgroup.vue完整代码

<template>
  <el-row :gutter="20">
    <el-col :span="6">
      <category @tree-node-click="treenodeclick"></category>
    </el-col>
    <el-col :span="18">
      <div class="mod-config">
        <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
          <el-form-item>
            <el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
          </el-form-item>
          <el-form-item>
            <el-button @click="getDataList()">查询</el-button>
            <el-button type="success" @click="getAllDataList()">查询全部</el-button>
            <el-button
              v-if="isAuth('product:attrgroup:save')"
              type="primary"
              @click="addOrUpdateHandle()"
            >新增</el-button>
            <el-button
              v-if="isAuth('product:attrgroup:delete')"
              type="danger"
              @click="deleteHandle()"
              :disabled="dataListSelections.length <= 0"
            >批量删除</el-button>
          </el-form-item>
        </el-form>
        <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="attrGroupId" header-align="center" align="center" label="分组id"></el-table-column>
          <el-table-column prop="attrGroupName" header-align="center" align="center" label="组名"></el-table-column>
          <el-table-column prop="sort" header-align="center" align="center" label="排序"></el-table-column>
          <el-table-column prop="descript" header-align="center" align="center" label="描述"></el-table-column>
          <el-table-column prop="icon" header-align="center" align="center" label="组图标"></el-table-column>
          <el-table-column prop="catelogId" header-align="center" align="center" label="所属分类id"></el-table-column>
          <el-table-column
            fixed="right"
            header-align="center"
            align="center"
            width="150"
            label="操作"
          >
            <template slot-scope="scope">
              <el-button type="text" size="small" @click="relationHandle(scope.row.attrGroupId)">关联</el-button>
              <el-button
                type="text"
                size="small"
                @click="addOrUpdateHandle(scope.row.attrGroupId)"
              >修改</el-button>
              <el-button type="text" size="small" @click="deleteHandle(scope.row.attrGroupId)">删除</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>
        <!-- 弹窗, 新增 / 修改 -->
        <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>

        <!-- 修改关联关系 -->
        <relation-update v-if="relationVisible" ref="relationUpdate" @refreshData="getDataList"></relation-update>
      </div>
    </el-col>
  </el-row>
</template>

<script>
/**
 * 父子组件传递数据
 * 1)、子组件给父组件传递数据,事件机制;
 *    子组件给父组件发送一个事件,携带上数据。
 * // this.$emit("事件名",携带的数据...)
 */
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
import Category from "../common/category";
import AddOrUpdate from "./attrgroup-add-or-update";
import RelationUpdate from "./attr-group-relation";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: { Category, AddOrUpdate, RelationUpdate },
  props: {},
  data() {
    return {
      catId: 0,
      dataForm: {
        key: ""
      },
      dataList: [],
      pageIndex: 1,
      pageSize: 10,
      totalPage: 0,
      dataListLoading: false,
      dataListSelections: [],
      addOrUpdateVisible: false,
      relationVisible: false
    };
  },
  activated() {
    this.getDataList();
  },
  methods: {
    //处理分组与属性的关联
    relationHandle(groupId) {
      this.relationVisible = true;
      this.$nextTick(() => {
        this.$refs.relationUpdate.init(groupId);
      });
    },
    //感知树节点被点击
    treenodeclick(data, node, component) {
      if (node.level == 3) {
        this.catId = data.catId;
        this.getDataList(); //重新查询
      }
    },
    getAllDataList(){
      this.catId = 0;
      this.getDataList();
    },
    // 获取数据列表
    getDataList() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl(`/product/attrgroup/list/${this.catId}`),
        method: "get",
        params: this.$http.adornParams({
          page: this.pageIndex,
          limit: this.pageSize,
          key: this.dataForm.key
        })
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.dataList = data.page.list;
          this.totalPage = data.page.totalCount;
        } else {
          this.dataList = [];
          this.totalPage = 0;
        }
        this.dataListLoading = false;
      });
    },
    // 每页数
    sizeChangeHandle(val) {
      this.pageSize = val;
      this.pageIndex = 1;
      this.getDataList();
    },
    // 当前页
    currentChangeHandle(val) {
      this.pageIndex = val;
      this.getDataList();
    },
    // 多选
    selectionChangeHandle(val) {
      this.dataListSelections = val;
    },
    // 新增 / 修改
    addOrUpdateHandle(id) {
      this.addOrUpdateVisible = true;
      this.$nextTick(() => {
        this.$refs.addOrUpdate.init(id);
      });
    },
    // 删除
    deleteHandle(id) {
      var ids = id
        ? [id]
        : this.dataListSelections.map(item => {
            return item.attrGroupId;
          });
      this.$confirm(
        `确定对[id=${ids.join(",")}]进行[${id ? "删除" : "批量删除"}]操作?`,
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }
      ).then(() => {
        this.$http({
          url: this.$http.adornUrl("/product/attrgroup/delete"),
          method: "post",
          data: this.$http.adornData(ids, false)
        }).then(({ data }) => {
          if (data && data.code === 0) {
            this.$message({
              message: "操作成功",
              type: "success",
              duration: 1500,
              onClose: () => {
                this.getDataList();
              }
            });
          } else {
            this.$message.error(data.msg);
          }
        });
      });
    }
  }
};
</script>
<style scoped>
</style>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据引用中提供的信息,谷粒商城是一个B2C模式的电商平台,主要销售自营商品给客户。B2C模式指的是商对客的模式,即商家直接向消费者销售商品服务谷粒商城使用微服务架构,这是一种将应用程序拆分成独立且可独立开发、部署和扩展的小型服务的架构。引用还提到谷粒商城的技术涵盖微服务架构、分布式、全栈、集群、部署和自动化运维等方面。 因此,从前慢-谷粒商城篇章6主要讨论的可能是谷粒商城的技术方面,包括微服务架构、分布式、全栈、集群、部署和自动化运维等内容。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [从前慢-谷粒商城篇章1](https://blog.csdn.net/m0_67391121/article/details/125383572)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [2020年谷粒商城md笔记](https://download.csdn.net/download/ZHUXIUQINGIT/12543161)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值