黑马小程序品优购商城项目分析

黑马电商品优购小程序

几日前完成了该项目,整理了下大概的逻辑思路,希望和大家一起交流学习,文档中不足之处希望各位不吝赐教。

该项目使用小程序原生mina框架

项目页面的搭建

页面名称文件
首页index
分类category
商品列表goods_list
商品详情goods_detail
购物车cart
收藏collect
订单order
搜索search
个人中心user
意见反馈user
登录login
授权auth
结算pay
  • 项目文件目录

在这里插入图片描述

首页

在这里插入图片描述

分类页面

在这里插入图片描述

1.点击左侧菜单切换内容

  1. 获取被点击的标题身上的索引

  2. 给data中的currentIndex赋值

  3. 根据不同的索引来渲染右侧的商品内容

     handleItemTap (e) {
        const { index } = e.currentTarget.dataset;
        let rightContent = this.Cates[index].children;
        this.setData({
          currentIndex: index,
          rightContent,
          scrollTop: 0
        })
    
      }
    

商品列表

在这里插入图片描述

Tab栏

在这里插入图片描述

1.自定义组件传值

  • ⽗组件通过属性的⽅式给⼦组件传递参数、⼦组件通过事件的⽅式向⽗组件传递参数
  • ⽗组件 把数据 {{tabs}} 传递到 ⼦组件的 tabItems 属性中
  • ⽗组件 监听 onMyTab 事件
  • ⼦组件 触发 bindmytap 中的事件
  • ⾃定义组件触发事件时,需要使⽤ triggerEvent ⽅法,指定 事件名 、 detail 对象
  • ⽗ -> ⼦ 动态传值 this.selectComponent("#tabs");

2.Tab栏切换

  • 获取被点击的标题索引

  • 修改源数组

  • 赋值到data中

  • const { index } = e.detail;
    let { tabs } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    this.setData({
      tabs
    })
    

3.下拉页面功能

  • ⻚⾯的json⽂件中开启设置 enablePullDownRefresh:true
    在这里插入图片描述

    • ⻚⾯的JS中,绑定事件 onPullDownRefresh
    // 下拉刷新事件 
      onPullDownRefresh () {
        // 1 重置数组
        this.setData({
          goodsList: []
        })
        // 2 重置页码
        this.QueryParams.pagenum = 1;
        // 3 发送请求
        this.getGoodsList();
      }
    
  1. 启⽤上拉⻚⾯功能 onReachBottom ⻚⾯触底事件

  2. 加载下⼀⻚功能

    // 页面上滑 滚动条触底事件
      onReachBottom () {
        //  1 判断还有没有下一页数据
        if (this.QueryParams.pagenum >= this.totalPages) {
          // 没有下一页数据
          wx.showToast({ title: '没有下一页数据' });
        } else {
          // 还有下一页数据
          this.QueryParams.pagenum++;
          this.getGoodsList();
        }
      },
    

商品详情

在这里插入图片描述

1.点击图片预览

在这里插入图片描述

  • 实现效果

在这里插入图片描述

2.加入购物车 逻辑

  • 先绑定点击事件
  • 获取缓存中的购物车数据 数组格式
  • 先判断 当前的商品是否已经存在于 购物车
  • 已经存在 修改商品数据 执行购物车数量++ 重新把购物车数组 填充回缓存中
  • 不存在于购物车的数组中 直接给购物车数组添加一个新元素 新元素 带上 购买数量属性 num 重新把购物车数组 填充回缓存中
  • 弹出提示
 // 点击 加入购物车
  handleCartAdd() {
    // 1 获取缓存中的购物车 数组
    let cart = wx.getStorageSync("cart") || [];
    // 2 判断 商品对象是否存在于购物车数组中
    let index = cart.findIndex(v => v.goods_id === this.GoodsInfo.goods_id);
    if (index === -1) {
      //3  不存在 第一次添加
      this.GoodsInfo.num = 1;
      this.GoodsInfo.checked = true;
      cart.push(this.GoodsInfo);
    } else {
      // 4 已经存在购物车数据 执行 num++
      cart[index].num++;
    }
    // 5 把购物车重新添加回缓存中
    wx.setStorageSync("cart", cart);
    // 6 弹窗提示
    wx.showToast({
      title: '加入成功',
      icon: 'success',
      // true 防止用户 手抖 疯狂点击按钮 
      mask: true
    });

  },

3.商品收藏

  • 页面onShow的时候 加载缓存中的商品收藏的数据

  • 判断当前商品是不是被收藏

    • 是 改变页面的图标

    • 不是

  • 点击商品收藏按钮

    • 判断该商品是否存在于缓存数组中
    • 已经存在 把该商品删除
    • 没有存在 把商品添加到收藏数组中 存入到缓存中即可
 // 点击 商品收藏图标
  handleCollect () {
    let isCollect = false;
    // 1 获取缓存中的商品收藏数组
    let collect = wx.getStorageSync("collect") || [];
    // 2 判断该商品是否被收藏过
    let index = collect.findIndex(v => v.goods_id === this.GoodsInfo.goods_id);
    // 3 当index!=-1表示 已经收藏过 
    if (index !== -1) {
      // 能找到 已经收藏过了  在数组中删除该商品
      collect.splice(index, 1);
      isCollect = false;
      wx.showToast({
        title: '取消成功',
        icon: 'success',
        mask: true
      });

    } else {
      // 没有收藏过
      collect.push(this.GoodsInfo);
      isCollect = true;
      wx.showToast({
        title: '收藏成功',
        icon: 'success',
        mask: true
      });
    }
    // 4 把数组存入到缓存中
    wx.setStorageSync("collect", collect);
    // 5 修改data中的属性  isCollect
    this.setData({
      isCollect
    })
  }

购物车

在这里插入图片描述

1.获取用户的收货地址

  • 绑定点击事件

  • 调用小程序内置 api 获取用户的收货地址 wx.chooseAddress

  • 获取 用户权限 状态 scope

  • 假设 用户 点击获取收货地址的提示框 确定 scope 值 true 直接调用 获取收货地址

  • 假设 用户 从来没有调用过 收货地址的api scope undefined 直接调用 获取收货地址

  • 假设 用户 点击获取收货地址的提示框 取消

    • scope 值 false
    • 诱导用户 自己 打开 授权设置页面(wx.openSetting) 当用户重新给与 获取地址权限的时候
    • 获取收货地址
    • 把获取到的收货地址 存入到 本地存储中
// 点击 收货地址
  async handleChooseAddress() {
    try {
      // 1 获取 权限状态
      const res1 = await getSetting();
      const scopeAddress = res1.authSetting["scope.address"];
      // 2 判断 权限状态
      if (scopeAddress === false) {
        await openSetting();
      }
      // 4 调用获取收货地址的 api
      let address = await chooseAddress();
      address.all = address.provinceName + address.cityName + address.countyName + address.detailInfo;

      // 5 存入到缓存中
      wx.setStorageSync("address", address);

    } catch (error) {
      console.log(error);
    }
  },

2.全选的实现 数据的展示

  • onShow 获取缓存中的购物车数组
  • 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中
// 商品全选功能
  handleItemAllCheck() {
    // 1 获取data中的数据
    let { cart, allChecked } = this.data;
    // 2 修改值
    allChecked = !allChecked;
    // 3 循环修改cart数组 中的商品选中状态
    cart.forEach(v => v.checked = allChecked);
    // 4 把修改后的值 填充回data或者缓存中
    this.setCart(cart);
  },

3.商品数量编辑

  • 获取传递过来的参数
  • 获取购物车数组
  • 找到需要修改的商品的索引
  • 判断是否要执行删除
  • 进行修改数量
  • 设置回缓存和data中
// 商品数量的编辑功能
  async handleItemNumEdit (e) {
    // 1 获取传递过来的参数 
    const { operation, id } = e.currentTarget.dataset;
    // 2 获取购物车数组
    let { cart } = this.data;
    // 3 找到需要修改的商品的索引
    const index = cart.findIndex(v => v.goods_id === id);
    // 4 判断是否要执行删除
    if (cart[index].num === 1 && operation === -1) {
      // 4.1 弹窗提示
      const res = await showModal({ content: "您是否要删除?" });
      if (res.confirm) {
        cart.splice(index, 1);
        this.setCart(cart);
      }
    } else {
      // 4  进行修改数量
      cart[index].num += operation;
      // 5 设置回缓存和data中
      this.setCart(cart);
    }
  },

个人中心

在这里插入图片描述

收藏

在这里插入图片描述

订单

在这里插入图片描述

搜索

在这里插入图片描述

1.输入框绑定 值改变事件 input事件

  • 获取到输入框的值
  • 合法性判断 (非空)
  • 检验通过 把输入框的值 发送到后台
  • 返回的数据打印到页面上

2.防抖 (防止抖动) 定时器 节流

  • 防抖 一般 输入框中 防止重复输入 重复发送请求
  • 节流 一般是用在页面下拉和上拉
  • 定义全局的定时器id
// 输入框的值改变 就会触发的事件
  handleInput(e){
    // 1 获取输入框的值
    const {value}=e.detail;
    // 2 检测合法性
    if(!value.trim()){
      this.setData({
        goods:[],
        isFocus:false
      })
      // 值不合法
      return;
    }
    // 3 准备发送请求获取数据
    this.setData({
      isFocus:true
    })
    clearTimeout(this.TimeId);
    this.TimeId=setTimeout(() => {
      this.qsearch(value);
    }, 1000);
  },
  // 发送请求获取搜索建议 数据
  async qsearch(query){
    const res=await request({url:"/goods/qsearch",data:{query}});
    console.log(res);
    this.setData({
      goods:res
    })
  },

意见反馈

在这里插入图片描述

1 点击 “+” 触发tap点击事件

1 调用小程序内置的 选择图片的 api

2 获取到 图片的路径 数组

3 把图片路径 存到 data的变量中

4 页面就可以根据 图片数组 进行循环显示 自定义组件

// 点击 “+” 选择图片
handleChooseImg() {
// 2 调用小程序内置的选择图片api
wx.chooseImage({
  // 同时选中的图片的数量
  count: 9,
  // 图片的格式  原图  压缩
  sizeType: ['original', 'compressed'],
  // 图片的来源  相册  照相机
  sourceType: ['album', 'camera'],
  success: (result) => {

    this.setData({
      // 图片数组 进行拼接 
      chooseImgs: [...this.data.chooseImgs, ...result.tempFilePaths]
    })
  }
});

},

2 点击 自定义图片 组件

1 获取被点击的元素的索引

2 获取 data中的图片数组

3 根据索引 数组中删除对应的元素

4 把数组重新设置回data中

// 点击 自定义图片组件
  handleRemoveImg(e) {
    // 2 获取被点击的组件的索引
    const { index } = e.currentTarget.dataset;
    // 3 获取data中的图片数组
    let { chooseImgs } = this.data;
    // 4 删除元素
    chooseImgs.splice(index, 1);
    this.setData({
      chooseImgs
    })
  },

3 提交

  • 获取文本域的内容 类似 输入框的获取

    • data中定义变量 表示 输入框内容
    • 文本域 绑定 输入事件 事件触发的时候 把输入框的值 存入到变量中
  • 对这些内容 合法性验证

  • 验证通过 用户选择的图片 上传到专门的图片的服务器 返回图片外网的链接

  • 遍历图片数组

  • 挨个上传

  • 自己再维护图片数组 存放 图片上传后的外网的链接

  • 文本域 和 外网的图片的路径 一起提交到服务器 前端的模拟 不会发送请求到后台

  • 清空当前页面

  • 返回上一页

// 提交按钮的点击
  handleFormSubmit() {
    // 1 获取文本域的内容 图片数组
    const { textVal, chooseImgs } = this.data;
    // 2 合法性的验证
    if (!textVal.trim()) {
      // 不合法
      wx.showToast({
        title: '输入不合法',
        icon: 'none',
        mask: true
      });
      return;
    }
    // 3 准备上传图片 到专门的图片服务器 
    // 上传文件的 api 不支持 多个文件同时上传  遍历数组 挨个上传 
    // 显示正在等待的图片
    wx.showLoading({
      title: "正在上传中",
      mask: true
    });

    // 判断有没有需要上传的图片数组

    if (chooseImgs.length != 0) {
      chooseImgs.forEach((v, i) => {
        wx.uploadFile({
          // 图片要上传到哪里
          url: 'http://img.coolcr.cn/index/api.html',
          // 被上传的文件的路径
          filePath: v,
          // 上传的文件的名称 后台来获取文件  file
          name: "image",
          // 顺带的文本信息
          formData: {},
          success: (result) => {
            console.log(result);
            let url = JSON.parse(result.data).url;
            this.UpLoadImgs.push(url);

            // 所有的图片都上传完毕了才触发  
            if (i === chooseImgs.length - 1) {
              wx.hideLoading();
              console.log("把文本的内容和外网的图片数组 提交到后台中");
              //  提交都成功了
              // 重置页面
              this.setData({
                textVal: "",
                chooseImgs: []
              })
              // 返回上一个页面
              wx.navigateBack({
                delta: 1
              });

            }
          }
        });
      })
    }else{
      wx.hideLoading();
        
      console.log("只是提交了文本");
      wx.navigateBack({
        delta: 1
      });
        
    }
  }

登录

在这里插入图片描述
在这里插入图片描述

结算

在这里插入图片描述

因为支付功能需要权限,所以这部分没有做,除了支付其他的部分是都可以做的,大家加油!


版权声明:本文为weixin_52690585原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。