小程序购物城项目实战(下篇)

重要提示(附完整代码链接)

(下篇)承接(中篇内容),接口文档和阿里图标库已在(上篇)给出,本文所粘贴代码或许存在不完整之处,完整代码请点此查看

支付页面创建订单并发起预支付

(中篇)里边,我们有了token值,紧接着这里就需要创建订单了,获取订单编号,然后在发起预支付。现在回到pay文件夹下的index.js中进行修改代码,编写如果有token值时的下一步逻辑。这里用到了接口文档如下图所示。接口文档(地址)。
在这里插入图片描述

// pages/pay/index.js
// 引入封装好的请求函数
import {
  request
} from "../../request/index.js"
Page({
  /**
   * 封装一个函数,设置商品状态的同时,底部工具栏,总价格,总数量重新计算 
   */
  setCart(cart) {
    let allChecked = true;
    //  声明总价格和总数量
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      if (v.checked) {
        totalPrice += v.num * v.goods_price;
        totalNum += v.num;
      } else {
        allChecked = false;
      }
    })
    //判断数组是否为空
    allChecked = cart.length != 0 ? allChecked : false;
    this.setData({
      cart,
      totalPrice,
      totalNum,
    });
    wx.setStorageSync('cart', cart);
  },

  /**
   * 页面的初始数据
   */
  data: {
    address: {},
    //声明一个变量,购物车数组
    cart: [],
    // 声明一个变量,购物车内容是否全选
    allChecked: false,
    // 定义商品总价格和总数量
    totalPrice: 0,
    totalNum: 0
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    //获取缓存中的购物车数据
    let cart = wx.getStorageSync('cart')
    //获取缓存中的收货地址信息
    const address = wx.getStorageSync('address');
    //过滤后的购物车数组(真正被选中的商品)
    cart = cart.filter(v => v.checked);
    //  声明总价格和总数量
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      totalPrice += v.num * v.goods_price;
      totalNum += v.num;
    })
    this.setData({
      cart,
      totalPrice,
      totalNum,
      address,
    });
  },
  // 点击支付事件
  async handleOrderPay(e) {
    //判断缓存中有无token
    const token = wx.getStorageSync('token');
    //如果不存在
    if (!token) {
      //跳转页面到获取用户登录信息
      wx.navigateTo({
        url: '/pages/auth/index',
      })
      return;
    };
    //否则就是有token值,创建订单
    // console.log('已经有token了');
    // 准备一些必须的请求头参数
    const header = {
      Authorization: wx.getStorageSync('token')
    };
    //准备请求体参数
    const order_price = this.data.totalPrice;
    const consignee_addr = this.data.address.all;
    const cart = this.data.cart;
    let goods = [];
    cart.forEach(v => goods.push({
      goods_id: v.goods_id,
      goods_number: v.num,
      goods_price: v.goods_price
    }))
    const orderParams = {
      order_price,
      consignee_addr,
      goods
    }
    // 发送请求 创建订单,获取订单编号,这个接口,不稳定,所以返回的订单编号可能为空或者其他
    const 
      number
     = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create",
      method: "POST",
      data: orderParams,
      header
    });
    //把返回数据中的订单编号存入缓存中,并命名为order_number
    wx.setStorageSync('order_number', number.data.message.order_number);
    // 从缓存中取出
    const order_number=wx.getStorageSync('order_number');
    // 发起预支付接口
    const res = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder",
      method: "POST",
      data: {order_number},
      header
    })
    // 会返回一堆参数,请见接口文档说明
    console.log(res);
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})
<!--pages/pay/index.wxml-->
<!-- 收货地址容器 -->
<view class="receive_address">
  <view  class="user_info_row">
    <view class="user_info">
      <view> {{address.userName}}</view>
      <view>{{address.all}}</view>
    </view>
    <view class="user_phone">
      {{address.telNumber}}
    </view>
  </view>
</view>

<!-- 购物车具体内容 -->
<view class="cart_content">
  <view class="cart_title">购物车</view>
  <view class="cart_main">
      <view class="cart_item" wx:for="{{cart}}" wx:key="goods_id">
        <!-- 商品图片 -->
        <navigator class="cart_image">
          <image mode="widthFix" src="{{item.goods_small_logo}}">
          </image>
        </navigator>
        <!-- 商品信息 -->
        <view class="cart_info">
          <view class="goods_name">{{item.goods_name}}</view>
          <view class="goods_price_wrap">
            <view class="goods_price">{{item.goods_price}}</view>
            <view class="cart_num_tool">
              <view class="goods_nums">x{{item.num}}</view>
            </view>
          </view>
        </view>
      </view>
  </view>
</view>


<!-- 底部导航栏 -->
<view class="footer_tool">
  <!-- 总价格 -->
  <view class="total_price_wrap">
    <view class="total_price">
      合计:<text class="total_price_number">{{totalPrice}}</text>
    </view>
    <view>包含运费</view>
  </view>
  <!-- 支付 -->
  <view class="order_pay_wrap" bindtap="handleOrderPay">支付({{totalNum}})</view>
</view>

效果如下,我们查看返回的一些参数。pay:里边包含了该部分参数。
在这里插入图片描述
在这里插入图片描述

接下来是根据获取的支付参数,调用小程序内部的API,wx.requestPayment进行调起微信支付。这里先在utils文件夹下的util.js内封装了一个函数如下

// 封装一个函数吊起微信支付接口
/**
 * promise 形式的 小程序的微信支付
 * @param {object} pay 支付所必要的参数
 */
export const requestPayment=(pay)=>{
  return new Promise((resolve,reject)=>{
   wx.requestPayment({
      ...pay,
     success: (result) => {
      resolve(result)
     },
     fail: (err) => {
       reject(err);
     }
   });
  })
}

然后在pay文件夹下的index.js内引入并使用,同时查询下后台 订单状态,查询成功后,则跳转到订单页面。

// pages/pay/index.js
import {
  requestPayment
} from "../../utils/util.js";
// 引入封装好的请求函数
import {request} from "../../request/index.js";
Page({
  // 封装一个函数吊起微信支付接口
  /**
   * 封装一个函数,设置商品状态的同时,底部工具栏,总价格,总数量重新计算 
   */
  setCart(cart) {
    let allChecked = true;
    //  声明总价格和总数量
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      if (v.checked) {
        totalPrice += v.num * v.goods_price;
        totalNum += v.num;
      } else {
        allChecked = false;
      }
    })
    //判断数组是否为空
    allChecked = cart.length != 0 ? allChecked : false;
    this.setData({
      cart,
      totalPrice,
      totalNum,
    });
    wx.setStorageSync('cart', cart);
  },

  /**
   * 页面的初始数据
   */
  data: {
    address: {},
    //声明一个变量,购物车数组
    cart: [],
    // 声明一个变量,购物车内容是否全选
    allChecked: false,
    // 定义商品总价格和总数量
    totalPrice: 0,
    totalNum: 0
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    //获取缓存中的购物车数据
    let cart = wx.getStorageSync('cart')
    //获取缓存中的收货地址信息
    const address = wx.getStorageSync('address');
    //过滤后的购物车数组(真正被选中的商品)
    cart = cart.filter(v => v.checked);
    //  声明总价格和总数量
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      totalPrice += v.num * v.goods_price;
      totalNum += v.num;
    })
    this.setData({
      cart,
      totalPrice,
      totalNum,
      address,
    });
  },
  // 点击支付事件
  async handleOrderPay(e) {
    //判断缓存中有无token
    const token = wx.getStorageSync('token');
    //如果不存在
    if (!token) {
      //跳转页面到获取用户登录信息
      wx.navigateTo({
        url: '/pages/auth/index',
      })
      return;
    };
    //否则就是有token值,创建订单
    // console.log('已经有token了');
    // 准备一些必须的请求头参数
    const header = {
      Authorization: wx.getStorageSync('token')
    };
    //准备请求体参数
    const order_price = this.data.totalPrice;
    const consignee_addr = this.data.address.all;
    const cart = this.data.cart;
    let goods = [];
    cart.forEach(v => goods.push({
      goods_id: v.goods_id,
      goods_number: v.num,
      goods_price: v.goods_price
    }))
    const orderParams = {
      order_price,
      consignee_addr,
      goods
    }
    // 发送请求 创建订单,获取订单编号,这个接口,不稳定,所以返回的订单编号可能为空或者其他
    const
      number = await request({
        url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create",
        method: "POST",
        data: orderParams,
        header
      });
    //把返回数据中的订单编号存入缓存中,并命名为order_number
    wx.setStorageSync('order_number', number.data.message.order_number);
    // 从缓存中取出
    const order_number = wx.getStorageSync('order_number');
    // 发起预支付接口
    const zhifu = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder",
      method: "POST",
      data: {
        order_number
      },
    });
    console.log(zhifu);
    wx.setStorageSync('pay', zhifu.data.message.pay);
    const {pay} = wx.getStorageSync('pay')
    //调用小程序内置API,吊起支付窗口,这里我们是把此操作进行了一个封装函数操作
    await requestPayment(pay);
// 查询后台订单状态
    await request({ url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/chkOrder", 	    method: "POST", data: { order_number } });
      wx.showToast({
        title: '订单支付成功',
        icon: 'success',
        duration: 2000,
      });
    //支付成功后跳转到订单页面
    wx.navigateTo({
      url: '/pages/order/index',
    })
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})

注意:这里因为token值的缘故,再次出现了无效的token值报错。

移除购物车已支付的数据

当订单支付成功后,需要移除购物车中,已经支付了的商品的数据。只需要在跳转到订单页面代码之后,追加下列代码

 //  手动删除缓存中 已经支付了的商品
    let newCart = wx.getStorageSync("cart");
    newCart = newCart.filter(v => !v.checked);
    wx.setStorageSync("cart", newCart);

封装统一的请求头到request中

在request文件夹中的index.js进行封装。

  //发送ajax异步请求的初始次数
 let ajaxTimes = 0;
// params是参数
export const request = (params) => {
  //判断url中是否带有 /my/请求的是私有的路径,带上header token
  let header={...params.header};
  if(params.url.includes("/my/")){
    //拼接header
    header["Authorization"]=wx.getStorageSync('token')
  }
  //每发一次请求就自加
  ajaxTimes++;
  //显示加载中效果
  wx.showLoading({
    title: '页面加载中',
    mask: true
  })
  return new Promise((
    // resolve是请求数据成功的结果,reject是请求失败
    resolve,
    reject
  ) => {
    wx.request({
      // ...params是解构参数
      ...params,
      header:header,
      success: (result) => {
        resolve(result)
      },
      fail: (err) => {
        reject(err);
      },
      //无论成功还是失败都会触发的事件
      complete: () => {
        //每发送完一次请求,就自减一,当为0时,表明请求完毕,关闭加载中提示
        ajaxTimes--;
        if (ajaxTimes === 0) {
          //关闭页面加载中图标
          wx.hideLoading()
        }
      }
    });
  })
}

随后在pay文件夹下的index.js内删掉使用header的地方即可。

登录页面

在点击我的这个图标后,会跳转的个人中心页面,如果没登录的话会出现一个要登录的界面。
在这里插入图片描述
login文件夹下的四个文件代码分别为

// pages/login/index.js
Page({
  data: {
    userInfo: {},
    hasUserInfo: false,
    canIUseGetUserProfile: false,
  },
  onLoad() {
    if (wx.getUserProfile) {
      this.setData({
        canIUseGetUserProfile: true
      })
    }
  },
  getUserProfile(e) {
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true,
        })
        const users=res.userInfo;
        wx.setStorageSync("userinfo", users)
        wx.navigateBack({
          delta: 1
        });
      }
    })
  },
})
{
  "usingComponents": {},
  "navigationBarTitleText": "登录"
}
<!--pages/login/index.wxml-->

<button type="primary" plain bindtap="getUserProfile" > 登录 </button>

/* pages/login/index.wxss */
button{
  margin-top: 40rpx;
  width: 70%;
}

最后效果如下

在这里插入图片描述
为了测验点击登录按钮后自动返回上一页(我的)页面,需要在user文件夹下的index.wxml增加一个navigator标签。

<!--pages/user/index.wxml-->
<navigator url="/pages/login/index">前往登录</navigator>

在这里插入图片描述

随后在该页面点击前往登录,就会跳转到登录页面,登录完成后,返回到我的页面。

个人中心(我的)页面

这里代码如下,比较简单,注意阿里矢量图标库的使用方法,如class="iconfont icon-ding_dan",要先引入名为ding_dan的图标。

// 注意:2021年4月13日后发布的新版本小程序,开发者通过组件调用wx.getUserInfo将不再弹出弹窗,直接返回匿名的用户个人信息,获取加密后的openID、unionID数据的能力不做调整;若开发者需要获取用户的个人信息(头像、昵称、性别与地区),可以通过wx.getUserProfile接口进行获取。
// pages/user/index.js
Page({
  data: {
    userinfo: {},
    // 被收藏的商品的数量
    collectNums: 0
  },
  onShow() {
    // 从缓存中取出用户信息
    const userinfo = wx.getStorageSync("userinfo");
    const collect = wx.getStorageSync("collect") || [];

    this.setData({
      userinfo,
      collectNums: collect.length
    });

  }
})
{
  "usingComponents": {},
  "navigationBarTitleText": "个人中心"
}
<!--pages/user/index.wxml-->

<view class="user_info_wrap">
  <view wx:if="{{userinfo.avatarUrl}}" class="user_img_wrap">
  <!-- 因为编写过程中获取用户信息, -->
    <!-- <image class="user_bg" src="{{userinfo.avatarUrl}}" ></image> -->
    <image class="user_bg" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></image>
    <view class="user_info">
      <!-- <image class="user_icon" src="{{userinfo.avatarUrl}}"></image> -->
      <image class="user_icon" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></image>
      <view class="user_name">{{userinfo.nickName}}</view>
    </view>
  </view>
  <view wx:else class="user_btn">
    <navigator url="/pages/login/index" >前往登录</navigator>
  </view>
</view>
<view class="user_content">
  
  <view class="user_main">
    <!-- 历史足迹 -->
    <view class="history_wrap">
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">收藏的店铺</view>
      </navigator>
      <navigator url="/pages/collect/index">
        <view class="his_num">{{collectNums}}</view>
        <view class="his_name">收藏的商品</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">关注的商品</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">我的足迹</view>
      </navigator>
    </view>
    <!-- 我的订单 -->
    <view class="orders_wrap">
      <view class="orders_title">我的订单</view>
      <view class="order_content">
        <navigator url="/pages/order/index?type=1">
        <!-- icon-ding_dan是阿里矢量图标库的使用方法 -->
          <view class="iconfont icon-ding_dan"></view>
          <view class="order_name">全部订单</view>
        </navigator>
        <navigator url="/pages/order/index?type=2">
          <view class="iconfont icon-fukuantongzhi"></view>
          <view class="order_name">待付款</view>
        </navigator>
        <navigator url="/pages/order/index?type=3">
          <view class="iconfont icon-receipt_address"></view>
          <view class="order_name">待收货</view>
        </navigator>
        <navigator>
          <view class="iconfont icon-tuikuantuihuo;"></view>
          <view class="order_name">退款/退货</view>
        </navigator>
      </view>
    </view>
    <!-- 收货地址管理 -->
    <view class="address_wrap">
      收货地址管理
    </view>
    <!-- 应用信息相关 -->
    <view class="app_info_wrap">
      <view class="app_info_item app_info_contact">
        <text>联系客服</text>
        <text>400-618-4000</text>
      </view>
      <navigator url="/pages/feedback/index" class="app_info_item">意见反馈</navigator>
      <view class="app_info_item">关于我们</view>
    </view>
    <!-- 推荐 -->
    <view class="recommend_wrap">
      把应用推荐给其他人
    </view>
  </view>
</view>

/* pages/user/index.wxss */
page {
  background-color: #edece8;
}

.user_info_wrap {
  height: 45vh;
  overflow: hidden;
  background-color: var(--themColor);
  position: relative;
}

.user_info_wrap .user_img_wrap {
  position: relative;
}

.user_info_wrap .user_img_wrap .user_bg {
  height: 50vh;
  /* 高斯模糊 背景图像 */
  filter: blur(10rpx);
}

.user_info_wrap .user_img_wrap .user_info {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: 20%;
  text-align: center;
}

.user_info_wrap .user_img_wrap .user_info .user_icon {
  width: 150rpx;
  height: 150rpx;
  border-radius: 50%;
}

.user_info_wrap .user_img_wrap .user_info .user_name {
  color: #fff;
  margin-top: 40rpx;
}

.user_info_wrap .user_btn {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: 40%;
  border: 1rpx solid greenyellow;
  color: greenyellow;
  font-size: 38rpx;
  padding: 30rpx;
  border-radius: 10rpx;
}

.user_content {
  position: relative;
}

.user_content .user_main {
  padding-bottom: 100rpx;
  color: #666;
  position: absolute;
  width: 90%;
  left: 50%;
  transform: translateX(-50%);
  top: -40rpx;
}

.user_content .user_main .history_wrap {
  background-color: #fff;
  display: flex;
}

.user_content .user_main .history_wrap navigator {
  flex: 1;
  text-align: center;
  padding: 10rpx 0;
}

.user_content .user_main .history_wrap navigator .his_num {
  color: var(--themColor);
}

.user_content .user_main .orders_wrap {
  background-color: #fff;
  margin-top: 30rpx;
}

.user_content .user_main .orders_wrap .orders_title {
  padding: 20rpx;
  border-bottom: 1rpx solid #ccc;
}

.user_content .user_main .orders_wrap .order_content {
  display: flex;
}

.user_content .user_main .orders_wrap .order_content navigator {
  padding: 15rpx 0;
  flex: 1;
  text-align: center;
}

.user_content .user_main .orders_wrap .order_content navigator .iconfont {
  color: var(--themColor);
  font-size: 40rpx;
}

.user_content .user_main .address_wrap {
  margin-top: 30rpx;
  background-color: #fff;
  padding: 20rpx;
}

.user_content .user_main .app_info_wrap {
  margin-top: 30rpx;
  background-color: #fff;
}

.user_content .user_main .app_info_wrap .app_info_item {
  padding: 20rpx;
  border-bottom: 1rpx solid #ccc;
}

.user_content .user_main .app_info_wrap .app_info_contact {
  display: flex;
  justify-content: space-between;
}

.user_content .user_main .recommend_wrap {
  margin-top: 30rpx;
  background-color: #fff;
  padding: 20rpx;
}

最后效果如下

在这里插入图片描述

订单查询实现

在user文件夹中的index.wxml,我们把“全部订单”,“待付款”等超链接,进行页面跳转的时候,同时传递了参数type,这决定了跳转到order文件夹(即订单页面)显示不同的内容。这里我们使用了之前在goods_list中使用过的tabs自定义组件,同理,先在order文件夹下的index.json引入组件,index.js中先写明导航栏的初始数据,然后使用。

// pages/order/index.js

Page({

  /**
   * 页面的初始数据
   */
  data: {
    // tabs导航栏初始数据
    tabs: [{
        id: 0,
        value: '综合',
        isActive: true
      },
      {
        id: 1,
        value: '待付款',
        isActive: false
      },
      {
        id: 2,
        value: '待发货',
        isActive: false
      },
      {
        id: 3,
        value: '退款/退货',
        isActive: false
      }
    ],
  },
  handleTabsItemChange(e) {
    // 1 获取被点击的标题索引
    const { index } = e.detail;
    this.changeTitleByIndex(index);
    // 2 重新发送请求 type=1 index=0
    this.getOrders(index+1);
  },
})
 {
  "usingComponents": {
    "tabs": "../../components/tabs/tabs"
  },
  "navigationBarTitleText": "订单查询"
}

<!--pages/order/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">
  <view class="order_main">
    <view wx:for="{{orders}}" wx:key="order_id" class="order_item">

      <view class="order_no_row">
        <view class="order_no_text">订单编号</view>
        <view class="order_no_value">{{item.order_number}}</view>
      </view>
      <view class="order_price_row">
        <view class="order_price_text">订单价格</view>
        <view class="order_price_value">{{item.order_price}}</view>
      </view>
      <view class="order_time_row">
        <view class="order_time_text">订单日期</view>
        <view class="order_time_value">{{item.create_time_cn}}</view>
      </view>

    </view>
  </view>
</tabs>
/* pages/order/index.wxss */
.order_main .order_item {
  padding: 20rpx;
  border-bottom: 1rpx solid #ccc;
  color: #666;
}
.order_main .order_item .order_no_row {
  display: flex;
  padding: 10rpx 0;
  justify-content: space-between;
}
.order_main .order_item .order_price_row {
  display: flex;
  padding: 10rpx 0;
  justify-content: space-between;
}
.order_main .order_item .order_price_row .order_price_value {
  color: var(--themeColor);
  font-size: 32rpx;
}
.order_main .order_item .order_time_row {
  display: flex;
  padding: 10rpx 0;
  justify-content: space-between;
}

最后效果如下
在这里插入图片描述
现在我们要实现的功能是,在个人中心页面点击不同的按钮后,如全部订单,待收货等,跳转到order页面(订单)中显示不同的内容,通过传递过来的参数type。

  1. 页面被打开的时候 onShow函数启动
    onShow 不同于onLoad 无法在形参上接收 options参数
    判断缓存中有没有token
    没有 直接跳转到授权页面
    有则 直接往下进行
    1.1. 获取url上的参数type
    1.2. 根据type来决定页面标题的数组元素 哪个被激活选中
    1.3. 根据type 去发送请求获取订单数据
    1.4. 渲染页面
  2. 点击不同的标题 重新发送请求来获取和渲染数据
// pages/order/index.js
/* 
1 页面被打开的时候 onShow函数启动
   onShow 不同于onLoad 无法在形参上接收 options参数 
   判断缓存中有没有token 
    1 没有 直接跳转到授权页面
    2 有 直接往下进行 
  1 获取url上的参数type
  2 根据type来决定页面标题的数组元素 哪个被激活选中 
  2 根据type 去发送请求获取订单数据
  3 渲染页面
2 点击不同的标题 重新发送请求来获取和渲染数据 
 */
import {
  request
} from "../../request/index.js";
Page({

  /**
   * 页面的初始数据
   */
  data: {
    orders: [],
    // tabs导航栏初始数据
    tabs: [{
        id: 0,
        value: '综合',
        isActive: true
      },
      {
        id: 1,
        value: '待付款',
        isActive: false
      },
      {
        id: 2,
        value: '待发货',
        isActive: false
      },
      {
        id: 3,
        value: '退款/退货',
        isActive: false
      }
    ],
  },
  onShow(options) {
    const token = wx.getStorageSync("token");
    if (!token) {
      wx.navigateTo({
        url: '/pages/auth/index'
      });
      return;
    }



    // 1 获取当前的小程序的页面栈-数组 长度最大是10页面 
    let pages = getCurrentPages();
    // 2 数组中 索引最大的页面就是当前页面
    let currentPage = pages[pages.length - 1];
    // 3 获取url上的type参数
    const {
      type
    } = currentPage.options;
    // 4 激活选中页面标题 当 type=1 index=0 
    this.changeTitleByIndex(type - 1);
    this.getOrders(type);
  },
  // 获取订单列表的方法
  async getOrders(type) {
    const res = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/all",
      data: {
        type
      }
    });
    this.setData({
      orders: res.data.message.orders.map(v => ({
        ...v,
        create_time_cn: (new Date(v.create_time * 1000).toLocaleString())
      }))
    })
  },
  // 根据标题索引来激活选中 标题数组
  changeTitleByIndex(index) {
    // 2 修改源数组
    let {
      tabs
    } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    // 3 赋值到data中
    this.setData({
      tabs
    })
  },
  handleTabsItemChange(e) {
    // 1 获取被点击的标题索引
    const {
      index
    } = e.detail;
    this.changeTitleByIndex(index);
    // 2 重新发送请求 type=1 index=0
    this.getOrders(index + 1);
  },
})

因为,支付功能的完整实现需要企业微信进行开发,所以我们前边并没有完全实现支付功能,在这里的订单页面,只有显示一堆待付款。效果如下
在这里插入图片描述
其他页面为空。
在这里插入图片描述

实现商品的收藏功能

在商品的详情页面,有一个收藏按钮,点击后会提示收藏成功,此时在点击个人中心页面,选择收藏的商品点击,则跳转到收藏页面,展示刚才被收藏的商品。实现的原理很简单,点击收藏后,将该商品存入缓存数组中,点击取消收藏后,该商品的信息从缓存数组中删除掉。
现在回到goods_detail页面文件夹,修改里边index.js代码。实现的步骤如下

  1. 页面加载onShow函数的时候 加载缓存中的商品收藏的数据
  2. 判断当前商品是不是被收藏
    2.1 是 改变页面的图标
    2.2 不是的话,无操作
  3. 点击商品收藏按钮
    3.1 判断该商品是否存在于缓存数组中
    3.2 已经存在 把该商品删除
    3.3 没有存在 把商品添加到收藏数组中 存入到缓存中即可

我们设置了一个检验在商品详情的index.js中,如果用户未登录,那么需要跳转到登录页面进行登录后才可以进行收藏功能。

// pages/goods_detail/index.js
//引入用来发送请求的方法,优化后的
import {
  request
} from "../../request/index.js"
Page({

  /**
   * 页面的初始数据
   */
  data: {
    //请求返回的数据是以对象形式
    goodsObj: {},
    // 商品是否被收藏
    isCollect: false

  },
  //定义要预览的大图信息数组全局变量
  goodsInfo: {},
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    //拿到跳转页面时传递过来的商品id
    const {
      goods_id
    } = options;
    this.getGoodsDetail(goods_id);
  },
  /**
   * 获取商品详情数据
   */
  async getGoodsDetail(goods_id) {
    const goodsObj = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/goods/detail",
      data: {
        goods_id
      }
    });
    //请求成功之后给之前定义的预览大图数组赋值
    this.goodsInfo = goodsObj.data.message;
    // 1 获取缓存中的商品收藏的数组
    let collect = wx.getStorageSync("collect") || [];
    // 2 判断当前商品是否被收藏
    let isCollect = collect.some(v => v.goods_id === this.goodsInfo.goods_id);
    this.setData({
      // 优化存储数据,只赋值存储小程序用到的数据
      goodsObj: {
        goods_name: goodsObj.data.message.goods_name,
        goods_price: goodsObj.data.message.goods_price,
        // iphone部分手机不支持webp格式
        // 后台修改
        // 或者自己临时修改 使用replace函数 其中\.webp是找到所有.webp的文件,g表示全选,.jpg表示全部替换为.jpg格式。
        goods_introduce: goodsObj.data.message.goods_introduce.replace(/\.webp/g, '.jpg'),
        pics: goodsObj.data.message.pics
      },
      isCollect
    })
  },
  /**
   * 点击轮播图预览大图事件
   */
  handlePrevewImage(e) {
    console.log('预览');
    //  先构建要预览的图片数组
    const urls = this.goodsInfo.pics.map(v => v.pics_mid)
    // 接受传递过来的图片url
    const current = e.currentTarget.dataset.url
    wx.previewImage({
      current: current,
      urls: urls
    })
  },
  /**
   * 用户商品加入购物车事件
   */
  handleCartAdd(e) {
    //获取缓存的商品数据,并由字符串格式转为数组格式
    let cart = wx.getStorageSync('cart') || [];
    // 判定商品是否已存在购物车数组中
    let index = cart.findIndex(v => v.goods_id === this.goodsInfo.goods_id);
    if (index === -1) {
      //不存在,第一次添加
      this.goodsInfo.num = 1;
      // 给商品增添一个checked的属性,值为true,以便在购物车页面的复选框进行选中
      this.goodsInfo.checked = true;
      cart.push(this.goodsInfo);
    } else {
      //存在
      cart[index].num++;
    }
    //购物车数组更新到缓存中
    wx.setStorageSync("cart", cart);
    //弹窗提示
    wx.showToast({
      title: '添加成功',
      icon: 'success',
      mask: 'true'
    })
  },
  // 点击商品收藏图标事件
  handleCollect() {
    //获取用户登录信息,如果用户登录了才能执行收藏图标事件
    let userinfo = wx.getStorageSync("userinfo");
    if (userinfo) {
      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
      })
    }else{
      wx.navigateTo({
        url: '/pages/login/index',
      })
    }
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

    let pages = getCurrentPages();
    let currentPage = pages[pages.length - 1];
    let options = currentPage.options;
    const {
      goods_id
    } = options;
    this.getGoodsDetail(goods_id);
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },
})

给收藏按钮绑定点击事件

<!--pages/goods_detail/index.wxml-->
<view class="detail_swiper">
  <!-- 轮播图内容,bindtap绑定一个预览大图事件 -->
  <swiper autoplay="true" circular="true" indicator-dots="true" bindtap="handlePrevewImage"
    data-url="{{item.pics_mid}}">
    <swiper-item wx:for="{{goodsObj.pics}}" wx:key="pics_id">
      <image mode="widthFix" src="{{item.pics_mid}}"></image>
    </swiper-item>
  </swiper>
</view>
<!-- 商品内容文字 -->
<!-- 商品价格 -->
<view class="goods_price">{{goodsObj.goods_price}}</view>
<view class="goods_name_row">
  <!-- 商品名字 -->
  <view class="goods_name">{{goodsObj.goods_name}}</view>
  <!-- 收藏商品 -->
  <view class="goods_collect" bindtap="handleCollect">
    <!-- 这里的收藏图标是引入了阿里图标库的 -->
    <text class="iconfont {{isCollect?'icon-shoucang1':'icon-shoucang'}}"></text>
    <view class="collect_text">收藏</view>
  </view>
</view>

<!-- 图文详情 -->
<view class="goods_info">
  <view class="goods_info_title">图文详情</view>
  <view class="goods_info_content">
    <!-- 富文本渲染 -->
    <rich-text nodes="{{goodsObj.goods_introduce}}"></rich-text>
  </view>
</view>

<!-- 底部导航栏 -->
<view class="btm_tool">
  <!-- 客服 -->
  <view class="tool_item">
    <view class="iconfont icon-kefu"></view>
    <view>客服</view>
    <!-- 增加联系客服功能 ,隐藏在客服的下一层,透明度为0,设置其高和宽与客服部分一致-->
    <button open-type="contact"></button>
  </view>
  <!-- 分享 -->
  <view class="tool_item">
    <view class="iconfont icon-fenxiang"></view>
    <view>分享</view>
    <button open-type="share"></button>
  </view>
  <!-- 购物车 -->
  <!-- switchTab允许跳转tabBar(导航栏)页面 -->
  <navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
    <view class="tool_item">
      <view class="iconfont icon-gouwuche"></view>
      <view>购物车</view>
    </view>
  </navigator>
  <!-- 加入购物车 -->
  <!-- 增加点击事件,添加商品响应 -->
  <view class="tool_item btn_cart" bindtap="handleCartAdd">
    <view>加入购物车</view>
  </view>
  <!-- 立即购买 -->
  <view class="tool_item btn_buy">
    <view>立即购买</view>
  </view>
</view>

然后在collect文件夹内进行修改代码,

// pages/collect/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    collect:[],
    tabs: [
      {
        id: 0,
        value: "商品收藏",
        isActive: true
      },
      {
        id: 1,
        value: "品牌收藏",
        isActive: false
      },
      {
        id: 2,
        value: "店铺收藏",
        isActive: false
      },
      {
        id: 3,
        value: "浏览器足迹",
        isActive: false
      }
    ]
  },
  onShow(){
    const collect=wx.getStorageSync("collect")||[];
    this.setData({
      collect
    });
      
  },
  handleTabsItemChange(e) {
    // 1 获取被点击的标题索引
    const { index } = e.detail;
    // 2 修改源数组
    let { tabs } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    // 3 赋值到data中
    this.setData({
      tabs
    })
  }
})
{
  "usingComponents": {
    "Tabs":"../../components/tabs/tabs"
  },
  "navigationBarTitleText":"商品收藏"
}

<!--pages/collect/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">

<view class="collect_main">
  <view class="collect_title">
    <text class="collect_tips active">全部</text>
    <text class="collect_tips">正在热卖</text>
    <text class="collect_tips">即将上线</text>
  </view>
  <view class="collect_content">
    <navigator class="goods_item" wx:for="{{collect}}" wx:key="goods_id"
      url="/pages/goods_detail/index?goods_id={{item.goods_id}}">
      <!-- 左侧 图片容器 -->
      <view class="goods_img_wrap">
        <image mode="widthFix"
          src="{{item.goods_small_logo?item.goods_small_logo:'https://ww1.sinaimg.cn/large/007rAy9hgy1g24by9t530j30i20i2glm.jpg'}}">
        </image>
      </view>
      <!-- 右侧 商品容器 -->
      <view class="goods_info_wrap">
        <view class="goods_name">{{item.goods_name}}</view>
        <view class="goods_price">{{item.goods_price}}</view>
      </view>
    </navigator>
  </view>
</view>

</tabs>

/* pages/collect/index.wxss */
.collect_main {
  background-color: #f3f4f6;
}
.collect_main .collect_title {
  padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {
  padding: 15rpx;
  border: 1rpx solid #ccc;
  margin-left: 25rpx;
  background-color: #fff;
}
.collect_main .collect_title .active {
  color: var(--themeColor);
  border-color: currentColor;
}
.collect_main .collect_content .goods_item {
  display: flex;
  border-bottom: 1px solid #ccc;
  background-color: #fff;
}
.collect_main .collect_content .goods_item .goods_img_wrap {
  flex: 2;
  display: flex;
  justify-content: center;
  align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {
  width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {
  flex: 3;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {
  color: var(--themeColor);
  font-size: 32rpx;
}

最后效果如下
在这里插入图片描述
在这里插入图片描述

搜索页面功能的实现

搜索页面首先有搜索框,我们之前自定义的一个组件,本质是一个超链接,可以在搜索框里边进行输入关键字搜索。在此页面,search文件夹的内容,我们不使用自定义的组件实现。

注意:这里边有个地方,即 防抖 (防止抖动) 使用定时器经过一段时间没有输入了,在发送请求 ,否则每输入一个字符都会发送一次请求,页面抖动。

  • 防抖 一般 输入框中 防止重复输入 重复发送请求
  • 节流 一般是用在页面下拉和上拉
  • 两种都可以通过定义全局的定时器id实现
// pages/search/index.js
/* 
1 输入框绑定 值改变事件 input事件
  1 获取到输入框的值
  2 合法性判断 
  3 检验通过 把输入框的值 发送到后台
  4 返回的数据打印到页面上
2 防抖 (防止抖动) 定时器  节流 
  0 防抖 一般 输入框中 防止重复输入 重复发送请求
  1 节流 一般是用在页面下拉和上拉 
  1 定义全局的定时器id
 */
import { request } from "../../request/index.js";

Page({
  data: {
    goods:[],
    // 取消 按钮 是否显示
    isFocus:false,
    // 输入框的值
    inpValue:""
  },
  TimeId:-1,
  // 输入框的值改变 就会触发的事件
  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:"https://api-hmugo-web.itheima.net/api/public/v1/goods/qsearch",data:{query}});
    console.log(res);
    this.setData({
      goods:res
    })
  },
  // 点击 取消按钮
  handleCancel(){
    this.setData({
      inpValue:"",
      isFocus:false,
      goods:[]
    })
  }
})
{
  "usingComponents": {},
  "navigationBarTitleText":"搜索中心"
}
<!--pages/search/index.wxml-->
<view class="search_row">
  <input value="{{inpValue}}" placeholder="请输入您要搜索的商品" bindinput="handleInput"> </input>
  <button bindtap="handleCancel" hidden="{{!isFocus}}">取消</button>
</view>
<view class="search_content">
  <navigator url="/pages/goods_detail/index?goods_id={{item.data.message.goods_id}}" class="search_item" wx:for="{{goods.data.message}}" wx:key="goods_id">
    {{item.goods_name}}
  </navigator>
</view> 
/* pages/search/index.wxss */
page {
  background-color: #dedede;
  padding: 20rpx;
}
.search_row {
  height: 60rpx;
  display: flex;
}
.search_row input {
  background-color: #fff;
  flex: 1;
  height: 100%;
  padding-left: 30rpx;
}
.search_row button {
  width: 110rpx;
  height: 100%;
  padding: 0;
  margin: 0 10rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 26rpx;
}
.search_content {
  margin-top: 30rpx;
}
.search_content .search_item {
  background-color: #fff;
  font-size: 26rpx;
  padding: 15rpx 10rpx;
  border-bottom: 1rpx solid #ccc;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

最后效果如下
在这里插入图片描述

意见反馈页面

在小程序的官方开发文档,button按钮有一个open-type开放能力,可以直接掉起意见反馈。这里我们不借用开放能力,自定义一个页面。首先是修改个人中心的代码,给意见反馈部分,增加一个跳转链接功能。

<!--pages/user/index.wxml-->

<view class="user_info_wrap">
  <view wx:if="{{userinfo.avatarUrl}}" class="user_img_wrap">
  <!-- 因为编写过程中获取用户信息的头像,会连接失败,所以用网图代替 -->
    <!-- <image class="user_bg" src="{{userinfo.avatarUrl}}" ></image> -->
    <image class="user_bg" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></image>
    <view class="user_info">
      <!-- <image class="user_icon" src="{{userinfo.avatarUrl}}"></image> -->
      <image class="user_icon" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></image>
      <view class="user_name">{{userinfo.nickName}}</view>
    </view>
  </view>
  <view wx:else class="user_btn">
    <navigator url="/pages/login/index" >前往登录</navigator>
  </view>
</view>
<view class="user_content">
  
  <view class="user_main">
    <!-- 历史足迹 -->
    <view class="history_wrap">
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">收藏的店铺</view>
      </navigator>
      <navigator url="/pages/collect/index">
        <view class="his_num">{{collectNums}}</view>
        <view class="his_name">收藏的商品</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">关注的商品</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">我的足迹</view>
      </navigator>
    </view>
    <!-- 我的订单 -->
    <view class="orders_wrap">
      <view class="orders_title">我的订单</view>
      <view class="order_content">
      <!-- 这里使用url跳转时,传递了type参数,点击下边不同链接时,跳转至order页面,但是显示不同内容。 -->
        <navigator url="/pages/order/index?type=1">
        <!-- icon-ding_dan是阿里矢量图标库的使用方法 -->
          <view class="iconfont icon-ding_dan"></view>
          <view class="order_name">全部订单</view>
        </navigator>
        <navigator url="/pages/order/index?type=2">
          <view class="iconfont icon-fukuantongzhi"></view>
          <view class="order_name">待付款</view>
        </navigator>
        <navigator url="/pages/order/index?type=3">
          <view class="iconfont icon-receipt_address"></view>
          <view class="order_name">待收货</view>
        </navigator>
        <navigator>
          <view class="iconfont icon-tuikuantuihuo;"></view>
          <view class="order_name">退款/退货</view>
        </navigator>
      </view>
    </view>
    <!-- 收货地址管理 -->
    <view class="address_wrap">
      收货地址管理
    </view>
    <!-- 应用信息相关 -->
    <view class="app_info_wrap">
      <view class="app_info_item app_info_contact">
        <text>联系客服</text>
        <text>400-618-4000</text>
      </view>
      <navigator url="/pages/feedback/index" class="app_info_item">意见反馈</navigator>
      <view class="app_info_item">关于我们</view>
    </view>
    <!-- 推荐 -->
    <view class="recommend_wrap">
      把应用推荐给其他人
    </view>
  </view>
</view>

接下来是在feedback文件夹下,index.json进行修改代码,包括,页面命名,顶部导航栏组件的引入等准备工作。

{
  "usingComponents": {
    "tabs": "../../components/tabs/tabs"
  },
  "navigationBarTitleText": "意见反馈"
}

然后为了实现意见反馈页面中能实现添加图片,也能删除图片功能,我们自定义一个组件,在components文件夹下新建UpImg文件夹,然后鼠标右键选择该文件夹,选择新建compinents,然后输入UpImg,微信开发者工具自动帮我们生成以下文件。
在这里插入图片描述
然后在该文件夹UpImg下修改里边文件代码

// components/UpImg/UpImg.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    src:{
      type:String,
      value:""
    }
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {

  }
})
{
  "component": true,
  "usingComponents": {}
}
<!--components/UpImg/UpImg.wxml-->
<view class="up_img_wrap">
  <!-- 图片 -->
  <image src="{{src}}"></image>
  <!-- 表示删除的图标 -->
  <icon type="clear" size="23" color="red">
  </icon>
</view>
/* components/UpImg/UpImg.wxss */
.up_img_wrap{
  width: 90rpx;
  height: 90rpx;
  position: relative;
}
.up_img_wrap image{
  width: 100%;
  height: 100%;
  border-radius: 15rpx;
}
.up_img_wrap icon{
  position: absolute;
  top:-22rpx;
  right: -22rpx;
}

随后回到feedback文件夹中的index.json引入组件UpImg。

{
  "usingComponents": {
    "tabs": "../../components/tabs/tabs",
    "UpImg":"../../components/UpImg/UpImg"
  },
  "navigationBarTitleText": "意见反馈"
}

在index.js内修改代码逻辑

// pages/feedback/index.js
// 一、点击+号按钮的时候,触发点击事件,调用小程序内置的选择图片的API
// 获取到选中的图片路径(数组格式)
// 把图片路径存入到data变量中
// 页面就可以根据图片数组进行显示了
// 二、点击自定义组件的时候,删除当前被点击的图片
// 首先是获取被点击元素的索引
// 获取data中的图片数组
// 根据索引,在数组中删除该图片
// 把数组重新设置回data中
// 三、提交按钮的事件,用户点击提交按钮后
// 获取文本域的内容,进行合法性验证,验证不通过就弹窗提示
//     要先定义一个变量接受文本域内数据,同时给文本域绑定一个输入响应事件
// 验证通过后,就把用户选择的图片上传到专门的图片保存服务器中,然后服务器会返回图片的带url的地址
//      遍历存储图片的数组挨个上传
//      自己在定义一个变量存储返回的图片url地址
// 文本域内容和返回的图片url地址提交到后台服务器中
// 提交成功后,清空当前页面,返回上一页。
Page({
  data: {
    // 引入自定义组件,设置初始数据
    tabs: [{
        id: 0,
        value: "体验问题",
        isActive: true
      },
      {
        id: 1,
        value: "商品、商家投诉",
        isActive: false
      }
    ],
    // 被选中的图片的路径数组
    chooseImgs: [],
    // 接收文本域存储的变量
    textVal: [],
  },
  // 图片储服务器返回的图片的url地址
  UpLoadImgs: [],
  // 顶部导航栏的点击事件
  handleTabsItemChange(e) {
    // 1 获取被点击的标题索引
    const {
      index
    } = e.detail;
    // 2 修改源数组
    let {
      tabs
    } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    // 3 赋值到data中
    this.setData({
      tabs
    })
  },
  // 点击+号选择图片事件
  handleChooseImg(e) {
    // 调用内置的API
    wx.chooseImage({
      // 同时选择的图片数量
      count: 9,
      // 图片的格式
      sizeType: ['original', 'compressed'],
      // 图片的来源,相册和照相机
      sourceType: ['album', 'camera'],
      success: (result) => {
        // 打印查看成功后的内容,在result.tempfilepathsZ中存储着我们的图片路径
        // console.log(result);
        this.setData({
          // 图片数组进行拼接,可以使得多次选中图片,旧图片和新图片的数组拼接
          // this.data.chooseImgs是当前存储的旧图片的数组
          chooseImgs: [...this.data.chooseImgs, ...result.tempFilePaths]
        })
      }
    })
  },
  // 点击自定义组件删除图片事件
  handleRemoveImg(e) {
    // 获取被点击组件的索引
    // console.log(e);
    const {
      index
    } = e.currentTarget.dataset.index;
    // 获取data中的图片数组
    let {
      chooseImgs
    } = this.data;
    // 删除元素,要删除的元素的索引和数量
    chooseImgs.splice(index, 1)
    // 填回data中
    this.setData({
      chooseImgs
    })
  },
  // 文本域的输入响应事件
  handleTextInput(e) {
    // console.log(e);
    this.setData({
      textVal: e.detail.value
    })
  },
  // 提交按钮的响应事件
  handleFormSubmit(e) {
    // 获取文本域内容和此时的图片数组
    const {
      textVal,
      chooseImgs
    } = this.data;
    // 验证合法性
    // trim是从字符串中删除开头和结尾的空格和行终止符
    // textVal删除后如果为空,再使用!取反,则执行if中的不合法提示
    var str=textVal.trim;
    if (!str) {
      // 不合法
      wx.showToast({
        title: '您的输入为空!',
        mask: 'true',
        icon: 'error'
      })
      return;
    };
    // 上传图片到专门的图片服务器,使用小程序内置API
    // 上传文件的api不支持多个文件同时上传,所以要遍历存储图片的数组挨个上传
    // 显示正在等待的图片
    wx.showLoading({
      title: "正在上传中",
      mask: true
    });
    // 判断有没有需要上传的图片数组
    if(chooseImgs.length != 0){
      chooseImgs.forEach((v, i) => {
        wx.uploadFile({
          // 要上传的文件路径
          filePath: v,
          // 这里name的名称是和后端沟通确定的
          name: 'image',
          // 图片要上传到哪里
          url: 'https://img.coolcr.cn/api/upload',
          // 顺带的文本内容
          formData: {},
          success: (result) => {
            let url = JSON.parse(result.data).data.url;
            this.UpLoadImgs.push(url);
            // 所有图片都上传完毕才触发的if
            if (i === chooseImgs.length - 1) {
              // 关闭弹窗
              wx.hideLoading();
              // 这里使用log输出 代替我们向后端提交数据
              console.log("把文本的内容和外网的图片数组 提交到后台中");
              // 提交后,重置本页面
              this.setData({
                textVal: "",
                chooseImgs: []
              })
              // 返回上一页
              wx.navigateBack({
                delta: 1
              });
            }
          }
        })
      })}else(
        // 若没有图片上传,只有文本,则执行如下
        wx.hideLoading(),
        console.log('只提交了文本'),
        wx.navigateBack({
          delta: 1
        })
      )
    }
})

在index.wxml中书写结构

<!--pages/feedback/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">
  <view class="fb_main">
    <view class="fb_title">问题的种类</view>
    <view class="fb_tips">
      <text>功能建议</text>
      <text>购买遇到问题</text>
      <text>性能问题</text>
      <text>其他</text>
    </view>
    <view class="fb_content">
    <!-- 文本域标签textarea -->
      <textarea value="{{textVal}}" bindinput="handleTextInput" placeholder="请描述一下您的问题"> </textarea>
      <view class="fb_tool">
      <!-- +号按钮增加一个选择图片事件 -->
        <button bindtap="handleChooseImg">+</button>
        <!-- 渲染选择的图片 -->
        <!-- 当数据是一些简单数据的时候,我们这里的Wx:key要写为*this -->
        <!-- data-index是传递当前点击的图片的索引值 -->
        <view class="up_img_item" wx:for="{{chooseImgs}}" wx:key="*this" bindtap="handleRemoveImg"
          data-index="{{index}}">
          <UpImg src="{{item}}"></UpImg>
        </view>
      </view>
    </view>
    <view class="form_btn_wrap">
      <button bindtap="handleFormSubmit" type="warn">
        <icon type="success_no_circle" size="23" color="white">
        </icon>
        提交
      </button>
    </view>
  </view>
</tabs>

在index.wxss书写样式

/* pages/feedback/index.wxss */
page {
  /* 页面背景颜色 */
  background-color: #eeeeee;
}
.fb_main {
  padding: 20rpx;
  color: #666;
}
.fb_main .fb_tips {
  display: flex;
  flex-wrap: wrap;
}
.fb_main .fb_tips text {
  width: 30%;
  padding: 10rpx;
  text-align: center;
  background-color: #fff;
  margin: 20rpx 10rpx;
}
.fb_main .fb_content {
  background-color: #fff;
  margin-top: 20rpx;
}
.fb_main .fb_content textarea {
  padding: 10rpx;
}
.fb_main .fb_tool {
  display: flex;
  flex-wrap: wrap;
  padding-bottom: 30rpx;
}
.fb_main .fb_tool button {
  margin: 0;
  width: 90rpx;
  height: 90rpx;
  font-size: 60rpx;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-left: 20rpx;
  margin-top: 20rpx;
  color: #ccc;
}
.fb_main .fb_tool .up_img_item {
  margin-left: 20rpx;
  margin-top: 20rpx;
}
.fb_main .form_btn_wrap {
  margin-top: 20rpx;
  display: flex;
  justify-content: flex-end;
}
.fb_main .form_btn_wrap button {
  margin: 0;
  width: 30%;
}

最后效果如下
在这里插入图片描述

在这里插入图片描述

首页中添加商品的跳转链接

现在给首页中,点击相关图片后,实现跳转,首先是轮播图。因为这里后台接口写的商品的地址有修改,比如goods_id为129的商品,他的地址url为 /pages/goods_detail/main?goods_id=129",而我们的文件夹内的文件都是index命名的,所以这里借用了goods_id指定路径。给navigator增加跳转url。同时也给分类一栏增添了url,写死了一个路径url="/pages/category/index"

<view class="gouwu">
  <!-- 搜索框 -->
  <SearchInput></SearchInput>
  <!-- 轮播图 -->
  <view class="lunbotu">
    <!-- swiper、image等标签一开始设置过了默认的宽度和高度 -->
    <!-- 在swiper标签上开始autoplay自动轮播属性,并显示轮播指示点 和衔接轮播-->
    <swiper autoplay="true" indicator-dots="true" circular="true">
      <!-- 需要给swiper-item进行循环 -->
      <!-- 循环的内容都是从服务器端返回并存入swiperList中获得的 -->
      <swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
      <!-- 给首页的轮播图增添跳转页面功能 -->
      <!--因为这里后台接口写的商品的地址有修改,为 /pages/goods_detail/main?goods_id=129",而我们
      的文件都是index命名的,所以这里借用了goods_id指定路径-->
        <navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" >
          <!-- 图片标签增加mode属性widthFix,图片标签的内容的高宽等比例变化 -->
          <image mode="widthFix" src="{{item.image_src}}"></image>
        </navigator>
      </swiper-item>
    </swiper>
  </view>
  <!-- 分类导航 -->
  <view class="fenlei">
    <navigator wx:for="{{cateList}}" wx:key="name" url="/pages/category/index" open-type="switchTab">
      <image mode="widthFix" src="{{item.image_src}}"></image>
    </navigator>
  </view>
  <!-- 楼层 -->
  <view class="louceng">
    <view class="floor_group" wx:for="{{floorList}}" wx:for-item="item1" wx:for-index="index1" wx:key="floor_title">
      <!-- 楼层的标题 -->
      <view class="title">
        <image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
      </view>
      <!-- 楼层的具体内容 -->
      <view class="list">
        <navigator wx:for="{{item1.product_list}}" wx:for-item="item2" wx:for-index="index2" wx:key="name">
          <image mode="{{index2===0? 'widthFix':'scaleToFill'}}" src="{{item2.image_src}}"></image>
        </navigator>
      </view>
    </view>
  </view>
</view>

最后给首页的具体内容中的图片增添跳转链接,如时尚女装中的图片,点击会跳转到商品列表页。

<view class="gouwu">
  <!-- 搜索框 -->
  <SearchInput></SearchInput>
  <!-- 轮播图 -->
  <view class="lunbotu">
    <!-- swiper、image等标签一开始设置过了默认的宽度和高度 -->
    <!-- 在swiper标签上开始autoplay自动轮播属性,并显示轮播指示点 和衔接轮播-->
    <swiper autoplay="true" indicator-dots="true" circular="true">
      <!-- 需要给swiper-item进行循环 -->
      <!-- 循环的内容都是从服务器端返回并存入swiperList中获得的 -->
      <swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
        <!-- 给首页的轮播图增添跳转页面功能 -->
        <!--因为这里后台接口写的商品的地址有修改,为 /pages/goods_detail/main?goods_id=129",而我们
      的文件都是index命名的,所以这里借用了goods_id指定路径-->
        <navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}">
          <!-- 图片标签增加mode属性widthFix,图片标签的内容的高宽等比例变化 -->
          <image mode="widthFix" src="{{item.image_src}}"></image>
        </navigator>
      </swiper-item>
    </swiper>
  </view>
  <!-- 分类导航 -->
  <view class="fenlei">
    <!-- 给分类导航添加跳转url -->
    <navigator wx:for="{{cateList}}" wx:key="name" url="/pages/category/index" open-type="switchTab">
      <image mode="widthFix" src="{{item.image_src}}"></image>
    </navigator>
  </view>
  <!-- 楼层 -->
  <view class="louceng">
    <view class="floor_group" wx:for="{{floorList}}" wx:for-item="item1" wx:for-index="index1" wx:key="floor_title">
      <!-- 楼层的标题 -->
      <view class="title">
        <image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
      </view>
      <!-- 楼层的具体内容 -->
      <view class="list">
      <!-- 点击具体内容里边的图片时 ,跳转到相关页面 -->
        <navigator wx:for="{{item1.product_list}}" wx:for-item="item2" wx:for-index="index2" wx:key="name"
          url="{{item2.navigator_url}}">
          <image mode="{{index2===0? 'widthFix':'scaleToFill'}}" src="{{item2.image_src}}"></image>
        </navigator>
      </view>
    </view>
  </view>
</view>

项目发布

实际应用中,代码写完后,会由企业的相关人员进行测试,这里我们跳过,进入到项目发布步骤。首先,把小程序开发者工具,本地设置中不校验合法域名,进行取消勾选。否则在用户的实际应用时,会造成不安全因素。然后我们把小程序开发中使用的所有外网链接,在小程序云平台,加入到白名单就可以了。
在这里插入图片描述
然后这里的APPID要修改为自己的,或者企业的,不能是测试号了
在这里插入图片描述
然后确保代码总共不超过2M。
在这里插入图片描述
然后点击上传
在这里插入图片描述
然后打开微信公众平台,点击版本管理,然后下拉页面,找到开发版本
在这里插入图片描述
在这里插入图片描述
这里如果我们选择提交审核,那么就会提交给微信官方审核,然后正式上线。这里我选择的是体验版,同时,将一些链接url放入白名单,找到开发,开发管理,服务器域名中进行配置。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值