前端“尚品汇”大型购物商城项目(后台页面)----产品展示功能区(包含有四个页面)day03

后台“尚品汇”大型购物商城项目

视频链接:

https://www.bilibili.com/video/BV1Vf4y1T7bw?spm_id_from=333.337.search-card.all.click

1. 后台项目涉及到的页面

  • 登录页面
  • 首页数据收集与展示页面
  • 商品管理页面
    • 品牌管理
    • 平台属性管理
    • Sku管理
    • Spu管理
  • 权限管理

目录展示:
目录展示

2. Spu管理页面

1. 页面展示

1. 主页面展示:

在这里插入图片描述
这个功能页面中,涉及到的数据比较多,下面是它代码展示:

data() {
    //这里存放数据
    return {
    //这仨是三级分类各自的Id
      category1Id: "",
      category2Id: "",
      category3Id: "",
      //这仨是分页器用到的
      page: 1,
      limit: 3,
      total: 0, //分页器总共的页数
      record: [], //存储spu列表的数据
      //这个可以控制各个组件的显示与隐藏
      scene: 0, //0代表展示spu数据,1添加修改spu  2 添加sku
      spu: {},
      skuList: [], //存储的是sku列表的数据
      dialogVisible: false,
      loading: true,
    };
  },

2. 添加spu界面

整个页面的布局,是由一个表单元素el-form包裹起来的,里面的每一项则是用el-form-item表单子元素包裹的。
在这里插入图片描述有几点需要注意的:

  1. 照片墙,这里可以显示多个照片,下面是他的代码
        <!-- 上传图片 
        list-type:文件列表的类型
        on-preview:图片预览的时候会触发
        on-remove:图片删除的时候会触发
        file-list:照片墙需要展示的数据[数组里面的元素必须要有name、url属性]-->
      <el-upload
        action="/dev-api/admin/product/fileUpload"
        list-type="picture-card"
        :on-preview="handlePictureCardPreview"
        :on-remove="handleRemove"
        :file-list="spuImageList"
        :on-success="handlersuccess"
      >
        <i class="el-icon-plus"></i>
      </el-upload>
      <el-dialog :visible.sync="dialogVisible">
        <img width="100%" :src="dialogImageUrl" alt="" />
      </el-dialog>

在这个照片墙中呢,其中的file-list数组,必须要有Name还有url属性,因此获取到服务器数据之后,要再进一步的处理,

  1. 下拉框,这里在向后台发起请求,获得销售属性之后,要计算出来用户还有哪些没有选择的。下面是他的一个代码展示:
 unSelected() {
      let result = this.saleList.filter((item) => {
        return (
          // 这里用evey代表的是,必须spu中的所有元素跟item的name不一样,才会返回true
          this.spu.spuSaleAttrList.every((item1) => {
            return item.name != item1.saleAttrName;
          })
        );
      });
      return result;
    },
  1. 在这个功能区中涉及到的细节功能很多,比如说是图片上传之前处理图片数组添加属性,上传之后获取新的图片列表,下拉框中选中某个属性之后,如何获取到这个属性名以及属性id在销售属性的table表格中,也同样涉及到了一些功能点。在功能区部分,我会集中说明一下,这块的一些功能是如何完成的。

3. 修改spu页面展示:

这部分页面用的是跟添加一个页面区,只不过的是在进入这个页面去的时候,就会得到数据,并进行展示。
在这里插入图片描述

4. 添加sku页面展示:

在这个页面中整体就是一个el-form表单包裹起来的,涉及到的下拉菜单,图片的展示,这些功能前几个页面也都有涉及,主要就是数据的收集,这里涉及到了好几个列表。
在这里插入图片描述

4. 查看当前spu的全部sku列表

由一个大的table标签组成的。
在这里插入图片描述
页面代码的展示:

 <el-dialog
      :title="`${spu.spuName}的sku列表`"
      :visible.sync="dialogVisible"
      width="70%"
      :before-close="handleClose"
    >
      <el-table :data="skuList" style="width: 100%" v-loading="loading">
        <el-table-column prop="skuName" label="名称" width="width">
        </el-table-column>
        <el-table-column prop="price" label="价格" width="width">
        </el-table-column>
        <el-table-column prop="weight" label="重量" width="width">
        </el-table-column>
        <el-table-column prop="prop" label="默认图片" width="width">
          <template slot-scope="{ row, $index }">
            <el-image
              style="width: 100px; height: 100px"
              :src="row.skuDefaultImg"
            ></el-image>
          </template>
        </el-table-column>
      </el-table>
    </el-dialog>
注意:el-table身上的v-loading="loading",表示的是表格数据的懒加载,在数据还没有回来的时候

6. 当前spu的全部sku列表展示:

<el-table :data="skuList" style="width: 100%" v-loading="loading">
        <el-table-column prop="skuName" label="名称" width="width">
        </el-table-column>
        <el-table-column prop="price" label="价格" width="width">
        </el-table-column>
        <el-table-column prop="weight" label="重量" width="width">
        </el-table-column>
        <el-table-column prop="prop" label="默认图片" width="width">
          <template slot-scope="{ row, $index }">
            <el-image
              style="width: 100px; height: 100px"
              :src="row.skuDefaultImg"
            ></el-image>
          </template>
        </el-table-column>
      </el-table>

2. 页面中涉及到的功能点

2.1 首页场景的切换

这里定义了scene属性,用来控制哪个功能区展示。其中scene=0是主页面展示;scene=1是添加或修改spu,这里用的是v-show,不用v-if因为子组件并没有卸载,只是显示与隐藏。这个是spu组件的显示代码: <spuForm v-show="scene == 1" @changeScene="changeScene" ref="spu" />

    // spuForm自定义事件回调,在子组件中点击保存或关闭的时候触发。
    changeScene({ scene, flag }) {
      // flag这个形参是为了区分保存按钮时添加还是修改
      this.scene = scene;
      // 传入当前页
      if (flag == "修改") {
        this.getSpuList(this.page);
      } else {
        this.getSpuList();
      }
    },

2.2 添加spu以及修改spu页面的一些功能

2.2.1添加/修改spu页面的数据获取:

在这里插入图片描述
说明:子组件要向服务器获取存储的品牌的信息、图片数据、品牌的销售属性,下面是修改initSpuData的代码展示:

 async initSpuData(row) {
      //   获取spu信息
      let result = await this.$API.spu.reqSpu(row.id);
      if (result.code == 200) {
        this.spu = result.data;
      }
      //   获取品牌的信息,不需要传入属性
      let tradeMarkResult = await this.$API.spu.reqTradeMarkList();
      if (tradeMarkResult.code == 200) {
        this.tradeMarkList = tradeMarkResult.data;
      }
      //   获取SPU图片的数据,需要传入id
      let spuImageResult = await this.$API.spu.reqSpuImageList(row.id);
      if (spuImageResult.code == 200) {
        //   因为照片墙显示图片的数据,需要的是数组,数组里面的元素需要name和url字段
        // 需要把服务器返回的数据进行修改,因为照片墙里需要的字段是img还有url,但是服务器返回的不是这个样子
        let ImageList = spuImageResult.data;
        ImageList.forEach((element) => {
          element.name = element.imgName;
          element.url = element.imgUrl;
        });
        this.spuImageList = ImageList;
      }
      //   获取平台销售属性
      let saleResult = await this.$API.spu.reqBaseSaleAttrList();
      if (saleResult.code == 200) {
        this.saleList = saleResult.data;
      }
    },
注意:表单收集到了data中的spu里面,初始化的时候,不能就只是一个spu:{},这样写是不行的,数据需要确定收集到哪些字段上面,因此需要在创建的时候就明确。
2.2.2:销售属性收集

销售属性的name和id的收集,他这里是新定义了一个新的变量去接受这个字符串。值是el-option身上的value属性,用了模板字符串,然后值传递到了el-select身上的v-model绑定的attrId上。

 <el-select
    :placeholder="`还有${unSelected.length}未选择`"
    v-model="attrId"
  >
    <el-option
      :label="unselect.name"
      :value="`${unselect.id}:${unselect.name}`"
      v-for="(unselect, index) in unSelected"
      :key="unselect.id"
    ></el-option>
  </el-select>
  <el-button
    type="primary"
    icon="el-icon-plus"
    :disabled="!attrId"
    @click="addSaleAttr"
    >添加销售属性</el-button
  >

addSaleAttr的方法的代码展示:通过split对字符串进行切割,然后将获得到的数据,加入到新创建的newSaleAttr里面,这个对象是为了方便接下来的销售值名称列表的收集。最后再清空一下attrId,方便下次再次获取。

// 添加销售属性
    addSaleAttr() {
      // 把手机到的销售属性按钮进行分割
      const [baseSaleAttrId, saleAttrName] = this.attrId.split(":");
      // 向spu对象里面进行添加
      let newsSaleAttr = {
        baseSaleAttrId,
        saleAttrName,
        spuSaleAttrValueList: [],
      };
      this.spu.spuSaleAttrList.push(newsSaleAttr);
      // 清空数据
      this.attrId = "";
    },
2.2.3:属性名称列表的添加删除功能
  <el-button
    v-else
    class="button-new-tag"
    size="small"
    @click="addSaleAttrValue(row)"
    >添加</el-button
  >

addSaleAttrValue方法的代码,this.$set方法可以给对象身上添加响应式数据,inputVlisible用于给spuSaleAttrList列表中的输入框、按钮,判断这俩谁显示谁不显示,inputValue则是接受输入的销售属性的值的。**首先为啥不定义一个全局变量,非要在对象身上添加呢?**如果是全局的话,那么点击只有,销售属性的每一行的input框都会显示了,这个只能是特定行的那个input,要控制这个的话,就必须是给对象身上添加响应式属性。

 addSaleAttrValue(row) {
      // 用set给item身上添加响应式属性flag,属性值为false
      this.$set(row, "inputVisible", true);
      this.$set(row, "inputValue", "");
      this.$nextTick((_) => {
        this.$refs.saveTagInput.focus();
      });
    },

input框,在输入完成回车之后触发方法handleInputConfirm。这个方法做了三件事:
①判断输入的属性值是否为空
②判断属性值是否重复,用的som
③将获得的属性值加入到spuSaleAttrValueList中
④inputVisible设置为false

 // tag自定义标签
    handleInputConfirm(row) {
      const { baseSaleAttrId, inputValue } = row;
      // 新增的属性值不能为空
      if (inputValue.trim() == "") {
        this.$message("属性值不能为空");
        return;
      }
      // 属性值不能重复
      let result = row.spuSaleAttrValueList.some((item) => {
        return item.saleAttrValueName == inputValue;
      });
      if (result) {
        this.$message("属性值重复");
        return;
      }
      // 新增的销售属性值
      let newSaleAttrValue = { baseSaleAttrId, saleAttrValueName: inputValue };
      row.spuSaleAttrValueList.push(newSaleAttrValue);
      row.inputVisible = false;
    },

删除输入的属性值:

 @close="row.spuSaleAttrValueList.splice(index, 1)"
2.2.4 保存spu

addOrUpdateSpu的代码如下所示:这个方法做了三件事
①整理参数,因为图片列表之前为了展出出来。加了俩属性在里面,这个呢,就用到了map方法,返回只包含imageName以及imageUrl属性值的图像列表。
②发送请求,将输入给服务器传过去。
③通知父组件返回到场景0。

 // 保存按钮的回调
    async addOrUpdateSpu() {
      // 整理参数
      // 携带参数,这里是直接赋值
      this.spu.spuImageList = this.spuImageList.map((item) => {
        return {
          imageName: item.name,
          //这里是因为照片墙里有的数据是从服务器那边返回回来的,因此需要处理一下
          imageUrl: (item.response && item.response.data) || item.url,
        };
      });
      // 发请求
      let result = await this.$API.spu.reqAddorUpdateSpu(this.spu);
      if (result.code == 200) {
        this.$message({ type: "success", message: "修改成功" });
        // 通知父组件返回到场景0
        this.$emit("changeScene", {
          scene: 0,
          flag: this.spu.id ? "修改" : "添加",
        });
      }
      Object.assign(this._data, this.$options.data());
    },

添加spu/修改spu的功能页面完成。

2.3 添加sku属性

2.3.2 获取数据:

用户点击添加sku按钮,父组件会将一些属性传递给子组件通过绑定在子组件身上的ref获得子组件身上的getData方法。
在这里插入图片描述
下面是getData方法的代码展示:

async getData(category1Id, category2Id, category3Id, spu) {
      // 将父组件给的数据赋值给skuInfo
      this.skuInfo.category3Id = category3Id;
      // 收集父组件给与的数据
      this.skuInfo.spuId = spu.id;
      this.skuInfo.tmId = spu.tmId;
      this.spu = spu;
      // 获取图片数据
      let result = await this.$API.sku.reqSpuImageList(spu.id);
      if (result.code == 200) {
        this.spuImageList = result.data;
        // 给每个图片添加一个属性isdefalut
        this.spuImageList.forEach((item) => {
          this.$set(item, "isDefault", 0);
        });
      }
      // 获取销售属性
      let saleattrListresult = await this.$API.sku.reqSpuSaleAttrList(spu.id);
      if (saleattrListresult.code == 200) {
        this.SpuSaleAttrList = saleattrListresult.data;
      }
      // 获取平台属性的数据
      let attrInfoListresult = await this.$API.sku.reqAttrInfoList(
        category1Id,
        category2Id,
        category3Id
      );
      if (attrInfoListresult.code == 200) {
        this.attrInfoList = attrInfoListresult.data;
      }
    },
2.3.3 获得平台属性以及销售属性

这一部分的功能跟spu功能页中销售属性的获取差不多,就是用到了el-select标签,然后再el-option用for循环遍历,然后再用模板字符串获取销售属性的id以及name,其中平台属性中v-model绑定的是attrInfoList.attrIdandValueId上面,销售属性也是一样。

2.3.4 获取选中的图片列表

这里涉及需要收集到skuInfo身上的两个变量,skuDefaultImg(默认图片)以及skuImageList(从用户选中的图片列表中收集)
在这里插入图片描述
图片列表绑定的事件:@selection-change="handleSelectionChange"

 // table表格复选按钮的事件
    handleSelectionChange(val) {
      // 获取到用户选中图片的信息数据,但是需要注意的是,当前收集的数据,缺少skuDefaultImg
      this.imageList = val;
    },
注意:这里有一点是需要注意的,就是在收集图片列表的时候,又重新定义了一个数组来去收集,而不能在原来数组的基础上去收集,因为原来数组需要在页面进行展示,不能修改。

收集skuDefaultImg时,需要点击图片右边的按钮,且只能有一个默认图片,点击只有,按钮样式会切换。这里用到了排他思想:

changeDdefault(row) {
      // 排他方法
      this.spuImageList.forEach((item) => {
        item.isDefault = 0;
      });
      row.isDefault = 1;
      this.skuInfo.skuDefaultImg = row.imgUrl;
    },
2.3.5 获取已经收集好了的表单进行保存提交

下面是save保存的代码展示:
他主要完成了一下几个功能:
①将收集到的平台属性进行处理,因为之前将id还有name组合成了一个字符串,这里就通过split把他们分开了,然后定义了一个对象,push到了attrInfoList里面。
②将整理好的attrInfoList赋值给skuInfo中的skuAttrValueList 。
③整理销售属性,同样跟平台属性的处理是一样的。
④整理图片数据
⑤发请求,将收集好的表单元素传给服务器
⑥想父组件发请求,执行自定义方法changeScene2,切换视图
清空data中的数据,将其中赋值了的值空,这里写的很巧妙Object.assign(this._data, this.$options.data());

 async save() {
      let arr = [];
      this.attrInfoList.forEach((item) => {
        if (item.attrIdandValueId) {
          const [attrId, valueId] = item.attrIdandValueId.split(":");
          let obj = { attrId, valueId };
          arr.push(obj);
        }
      });
      // 将整理好的参数传递给
      this.skuInfo.skuAttrValueList = arr;
      // 整理销售属性
      this.skuInfo.skuSaleAttrValueList = this.SpuSaleAttrList.reduce(
        (prev, item) => {
          if (item.saleattrIdandValueId) {
            const [
              saleAttrId,
              saleAttrValueId,
            ] = item.saleattrIdandValueId.split(":");
            prev.push({ saleAttrId, saleAttrValueId });
          }
          return prev;
        },
        []
      );
      // 下面就剩下图片了
      this.skuInfo.skuImageList = this.imageList.map((item) => {
        return {
          imgName: item.imgName,
          imgUrl: item.imgUrl,
          isDefault: item.isDefault,
          spuImgId: item.id,
        };
      });
      // 发请求
      let result = await this.$API.sku.reqAddSku(this.skuInfo);
      if (result.code == 200) {
        this.$message({ type: "success", message: "保存成功" });
        this.$emit("changeScene2", 0);
        Object.assign(this._data, this.$options.data());
      }
    },

2.4 首页按钮的封装,使其可以在鼠标放到这个按钮上的时候,显示提示信息。

下面是封装好的组件内的代码展示:

<template>
  <a :title="title" style="margin: 10px">
    <!-- 使用 v-bind="$attrs" 属性,vm.$attrs 是一个属性,其包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。
    这些未识别的属性可以通过 v-bind="$attrs" 传入内部组件。未识别的事件可通过v-on="$listeners"传入。 -->
    <el-button v-bind="$attrs" v-on="$listeners"></el-button>
  </a>
</template>

3. sku管理页面

1. 页面展示

在这里插入图片描述

2. 查看详情页面

点击操作按钮中的查看详情弹出来的
在这里插入图片描述
这里的轮播图用到的标签组件是el-carousel,下面是他的代码部分:

  <!-- 轮播图 -->
          <el-carousel height="150">
            <el-carousel-item
              v-for="(item, index) in skuInfo.skuImageList"
              :key="item.id"
            >
              <el-image
                :src="item.imgUrl"
                style="width=100%;height=150px "
              ></el-image>
              <!-- <h3>{{ item.imgUrl }}</h3> -->
            </el-carousel-item>
          </el-carousel>

总结:

在写这个项目的时候,要格外的注意,服务器需要什么样的数据,如何去获取到,每个组件标签都跟哪些数据进行绑定的,有哪些数据需要传递到服务器的,父组件如何控制子组件显示与隐藏,还有就是数据在从组件标签身上获取的时候,需要进行哪些操作,什么数据应该写在computed什么数据应该写在watch中,这些东西在写前端页面逻辑的时候较为重要。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值