Actual combat of applet shopping mall project (Part 2)

Important tips (with complete code link)

(the second part) undertake (the second part). The interface document and Ali icon library have been given in (the first part). The code pasted in this article may be incomplete. The code is complete Please click here to view.

The payment page creates an order and initiates advance payment

(Part 2) inside, we have the token value, and then we need to create an order, obtain the order number, and then initiate advance payment. Now go back to index.js under the pay folder to modify the code and write the next step logic if there is a token value. The interface document used here is shown in the figure below. Interface documentation( address).

// pages/pay/index.js
// Introduce encapsulated request function
import {
  request
} from "../../request/index.js"
Page({
  /**
   * Encapsulate a function to recalculate the bottom toolbar, total price and total quantity while setting the commodity status 
   */
  setCart(cart) {
    let allChecked = true;
    //  Declare total price and total quantity
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      if (v.checked) {
        totalPrice += v.num * v.goods_price;
        totalNum += v.num;
      } else {
        allChecked = false;
      }
    })
    //Determine whether the array is empty
    allChecked = cart.length != 0 ? allChecked : false;
    this.setData({
      cart,
      totalPrice,
      totalNum,
    });
    wx.setStorageSync('cart', cart);
  },

  /**
   * Initial data of the page
   */
  data: {
    address: {},
    //Declare a variable, shopping cart array
    cart: [],
    // Declare a variable, whether to select all the contents of the shopping cart
    allChecked: false,
    // Define total price and total quantity of goods
    totalPrice: 0,
    totalNum: 0
  },

  /**
   * Life cycle function -- listening for page loading
   */
  onLoad: function (options) {

  },

  /**
   * Life cycle function -- monitor page display
   */
  onShow: function () {
    //Get shopping cart data in cache
    let cart = wx.getStorageSync('cart')
    //Get the receiving address information in the cache
    const address = wx.getStorageSync('address');
    //Filtered shopping cart array (really selected items)
    cart = cart.filter(v => v.checked);
    //  Declare total price and total quantity
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      totalPrice += v.num * v.goods_price;
      totalNum += v.num;
    })
    this.setData({
      cart,
      totalPrice,
      totalNum,
      address,
    });
  },
  // Click payment event
  async handleOrderPay(e) {
    //Judge whether there is a token in the cache
    const token = wx.getStorageSync('token');
    //If it doesn't exist
    if (!token) {
      //Jump to the page to get user login information
      wx.navigateTo({
        url: '/pages/auth/index',
      })
      return;
    };
    //Otherwise, there is a token value and an order is created
    // console.log('token already exists');
    // Prepare some required request header parameters
    const header = {
      Authorization: wx.getStorageSync('token')
    };
    //Prepare request body parameters
    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
    }
    // Send a request to create an order and obtain the order number. This interface is unstable, so the returned order number may be empty or other
    const 
      number
     = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create",
      method: "POST",
      data: orderParams,
      header
    });
    //Store the order number in the returned data into the cache and name it order_number
    wx.setStorageSync('order_number', number.data.message.order_number);
    // Fetch from cache
    const order_number=wx.getStorageSync('order_number');
    // Initiate advance payment interface
    const res = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder",
      method: "POST",
      data: {order_number},
      header
    })
    // A bunch of parameters will be returned. Please refer to the interface documentation
    console.log(res);
  },

  /**
   * Life cycle function -- listening for page hiding
   */
  onHide: function () {

  },

  /**
   * Life cycle function -- listen for page unloading
   */
  onUnload: function () {

  },

  /**
   * Page related event handler -- listen to user drop-down actions
   */
  onPullDownRefresh: function () {

  },

  /**
   * Handler for bottom pull event on page
   */
  onReachBottom: function () {

  },

  /**
   * Users click the upper right corner to share
   */
  onShareAppMessage: function () {

  }
})
<!--pages/pay/index.wxml-->
<!-- Receiving address container -->
<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>

<!-- Shopping cart details -->
<view class="cart_content">
  <view class="cart_title">Shopping Cart</view>
  <view class="cart_main">
      <view class="cart_item" wx:for="{{cart}}" wx:key="goods_id">
        <!-- Product picture -->
        <navigator class="cart_image">
          <image mode="widthFix" src="{{item.goods_small_logo}}">
          </image>
        </navigator>
        <!-- Commodity information -->
        <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>


<!-- Bottom navigation bar -->
<view class="footer_tool">
  <!-- Total price -->
  <view class="total_price_wrap">
    <view class="total_price">
      total:<text class="total_price_number">¥{{totalPrice}}</text>
    </view>
    <view>Including freight</view>
  </view>
  <!-- payment -->
  <view class="order_pay_wrap" bindtap="handleOrderPay">payment({{totalNum}})</view>
</view>

The effect is as follows. Let's check some parameters returned. pay: this part of the parameters is included in it.

Next, according to the obtained payment parameters, call the API inside the applet, wx.requestPayment to call wechat payment. Here, a function is encapsulated in util.js under the utils folder as follows

// Encapsulate a function to lift the wechat payment interface
/**
 * promise Wechat payment in the form of applet
 * @param {object} pay Parameters necessary for payment
 */
export const requestPayment=(pay)=>{
  return new Promise((resolve,reject)=>{
   wx.requestPayment({
      ...pay,
     success: (result) => {
      resolve(result)
     },
     fail: (err) => {
       reject(err);
     }
   });
  })
}

Then it is imported and used in index.js under the pay folder, and the background order status is queried. After the query is successful, it jumps to the order page.

// pages/pay/index.js
import {
  requestPayment
} from "../../utils/util.js";
// Introduce encapsulated request function
import {request} from "../../request/index.js";
Page({
  // Encapsulate a function to lift the wechat payment interface
  /**
   * Encapsulate a function to recalculate the bottom toolbar, total price and total quantity while setting the commodity status 
   */
  setCart(cart) {
    let allChecked = true;
    //  Declare total price and total quantity
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      if (v.checked) {
        totalPrice += v.num * v.goods_price;
        totalNum += v.num;
      } else {
        allChecked = false;
      }
    })
    //Determine whether the array is empty
    allChecked = cart.length != 0 ? allChecked : false;
    this.setData({
      cart,
      totalPrice,
      totalNum,
    });
    wx.setStorageSync('cart', cart);
  },

  /**
   * Initial data of the page
   */
  data: {
    address: {},
    //Declare a variable, shopping cart array
    cart: [],
    // Declare a variable, whether to select all the contents of the shopping cart
    allChecked: false,
    // Define total price and total quantity of goods
    totalPrice: 0,
    totalNum: 0
  },

  /**
   * Life cycle function -- listening for page loading
   */
  onLoad: function (options) {

  },

  /**
   * Life cycle function -- monitor page display
   */
  onShow: function () {
    //Get shopping cart data in cache
    let cart = wx.getStorageSync('cart')
    //Get the receiving address information in the cache
    const address = wx.getStorageSync('address');
    //Filtered shopping cart array (really selected items)
    cart = cart.filter(v => v.checked);
    //  Declare total price and total quantity
    let totalPrice = 0;
    let totalNum = 0;
    cart.forEach(v => {
      totalPrice += v.num * v.goods_price;
      totalNum += v.num;
    })
    this.setData({
      cart,
      totalPrice,
      totalNum,
      address,
    });
  },
  // Click payment event
  async handleOrderPay(e) {
    //Judge whether there is a token in the cache
    const token = wx.getStorageSync('token');
    //If it doesn't exist
    if (!token) {
      //Jump to the page to get user login information
      wx.navigateTo({
        url: '/pages/auth/index',
      })
      return;
    };
    //Otherwise, there is a token value and an order is created
    // console.log('token already exists');
    // Prepare some required request header parameters
    const header = {
      Authorization: wx.getStorageSync('token')
    };
    //Prepare request body parameters
    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
    }
    // Send a request to create an order and obtain the order number. This interface is unstable, so the returned order number may be empty or other
    const
      number = await request({
        url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create",
        method: "POST",
        data: orderParams,
        header
      });
    //Store the order number in the returned data into the cache and name it order_number
    wx.setStorageSync('order_number', number.data.message.order_number);
    // Fetch from cache
    const order_number = wx.getStorageSync('order_number');
    // Initiate advance payment interface
    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')
    //Call the built-in API of the applet and lift the payment window. Here, we encapsulate this operation
    await requestPayment(pay);
// Query background order status
    await request({ url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/chkOrder", 	    method: "POST", data: { order_number } });
      wx.showToast({
        title: 'Order payment succeeded',
        icon: 'success',
        duration: 2000,
      });
    //After successful payment, jump to the order page
    wx.navigateTo({
      url: '/pages/order/index',
    })
  },

  /**
   * Life cycle function -- listening for page hiding
   */
  onHide: function () {

  },

  /**
   * Life cycle function -- listen for page unloading
   */
  onUnload: function () {

  },

  /**
   * Page related event handler -- listen to user drop-down actions
   */
  onPullDownRefresh: function () {

  },

  /**
   * Handler for bottom pull event on page
   */
  onReachBottom: function () {

  },

  /**
   * Users click the upper right corner to share
   */
  onShareAppMessage: function () {

  }
})

Note: because of the token value, an invalid token value is reported again.

Remove paid data from shopping cart

After the order is paid successfully, you need to remove the data of the paid goods in the shopping cart. You only need to add the following code after jumping to the order page code

 //  Manually delete the paid items in the cache
    let newCart = wx.getStorageSync("cart");
    newCart = newCart.filter(v => !v.checked);
    wx.setStorageSync("cart", newCart);

Encapsulate the unified request header into the request

Encapsulate in index.js in the request folder.

  //The initial number of ajax asynchronous requests sent
 let ajaxTimes = 0;
// params is a parameter
export const request = (params) => {
  //Judge whether the / my / request in the url is a private path, and bring the header token
  let header={...params.header};
  if(params.url.includes("/my/")){
    //Splice header
    header["Authorization"]=wx.getStorageSync('token')
  }
  //Each request is added by itself
  ajaxTimes++;
  //Show loading effects
  wx.showLoading({
    title: 'Page loading',
    mask: true
  })
  return new Promise((
    // resolve is the result of a successful request for data, and reject is the result of a failed request
    resolve,
    reject
  ) => {
    wx.request({
      // ... params is the deconstruction parameter
      ...params,
      header:header,
      success: (result) => {
        resolve(result)
      },
      fail: (err) => {
        reject(err);
      },
      //Events triggered by success or failure
      complete: () => {
        //Each time a request is sent, it will be reduced by one. When it is 0, it indicates that the request is completed and the prompt in loading is closed
        ajaxTimes--;
        if (ajaxTimes === 0) {
          //Close icon in page load
          wx.hideLoading()
        }
      }
    });
  })
}

Then delete the place where the header is used in index.js under the pay folder.

Login page

After clicking my icon, you will jump to the personal center page. If you don't log in, an interface to log in will appear.

The four file codes under the login folder are

// pages/login/index.js
Page({
  data: {
    userInfo: {},
    hasUserInfo: false,
    canIUseGetUserProfile: false,
  },
  onLoad() {
    if (wx.getUserProfile) {
      this.setData({
        canIUseGetUserProfile: true
      })
    }
  },
  getUserProfile(e) {
    // It is recommended to use wx.getUserProfile to obtain user information. Every time developers obtain user personal information through this interface, they need to be confirmed by the user
    // The developer shall properly keep the avatar nickname quickly filled in by the user to avoid repeated pop ups
    wx.getUserProfile({
      desc: 'Used to improve member information', // Declare the purpose of obtaining the user's personal information, which will be displayed in the pop-up window later. Please fill in carefully
      success: (res) => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true,
        })
        const users=res.userInfo;
        wx.setStorageSync("userinfo", users)
        wx.navigateBack({
          delta: 1
        });
      }
    })
  },
})
{
  "usingComponents": {},
  "navigationBarTitleText": "Sign in"
}
<!--pages/login/index.wxml-->

<button type="primary" plain bindtap="getUserProfile" > Sign in </button>

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

The final effect is as follows


In order to automatically return to the previous (my) page after clicking the login button, you need to add a navigator tag to index.wxml under the user folder.

<!--pages/user/index.wxml-->
<navigator url="/pages/login/index">Go to login</navigator>

Then click go to login on this page to jump to the login page. After login, return to my page.

Personal Center (my) page

The code here is as follows. It is relatively simple. Pay attention to the use of Ali vector icon library, such as class = "iconfont icon ding_dan". First introduce an icon named ding_dan.

// Note: for the new version of applet released after April 13, 2021, the developer will no longer pop up the pop-up window by calling wx.getUserInfo through the component, directly return the anonymous user's personal information, and the ability to obtain the encrypted openID and unionID data will not be adjusted; If the developer needs to obtain the user's personal information (avatar, nickname, gender and region), it can be obtained through the wx.getUserProfile interface.
// pages/user/index.js
Page({
  data: {
    userinfo: {},
    // Number of items collected
    collectNums: 0
  },
  onShow() {
    // Fetch user information from cache
    const userinfo = wx.getStorageSync("userinfo");
    const collect = wx.getStorageSync("collect") || [];

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

  }
})
{
  "usingComponents": {},
  "navigationBarTitleText": "Personal Center"
}
<!--pages/user/index.wxml-->

<view class="user_info_wrap">
  <view wx:if="{{userinfo.avatarUrl}}" class="user_img_wrap">
  <!-- Because the user information is obtained during writing, -->
    <!-- <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" >Go to login</navigator>
  </view>
</view>
<view class="user_content">
  
  <view class="user_main">
    <!-- Historical footprint -->
    <view class="history_wrap">
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">Collection store</view>
      </navigator>
      <navigator url="/pages/collect/index">
        <view class="his_num">{{collectNums}}</view>
        <view class="his_name">Collectible goods</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">Products of concern</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">My Tracks </view>
      </navigator>
    </view>
    <!-- My order -->
    <view class="orders_wrap">
      <view class="orders_title">My order</view>
      <view class="order_content">
        <navigator url="/pages/order/index?type=1">
        <!-- icon-ding_dan This is how to use Ali vector icon library -->
          <view class="iconfont icon-ding_dan"></view>
          <view class="order_name">All orders</view>
        </navigator>
        <navigator url="/pages/order/index?type=2">
          <view class="iconfont icon-fukuantongzhi"></view>
          <view class="order_name">Pending payment</view>
        </navigator>
        <navigator url="/pages/order/index?type=3">
          <view class="iconfont icon-receipt_address"></view>
          <view class="order_name">Goods to be received</view>
        </navigator>
        <navigator>
          <view class="iconfont icon-tuikuantuihuo;"></view>
          <view class="order_name">refund/return goods</view>
        </navigator>
      </view>
    </view>
    <!-- Receiving address management -->
    <view class="address_wrap">
      Receiving address management
    </view>
    <!-- Application information related -->
    <view class="app_info_wrap">
      <view class="app_info_item app_info_contact">
        <text>Contact customer service</text>
        <text>400-618-4000</text>
      </view>
      <navigator url="/pages/feedback/index" class="app_info_item">Feedback</navigator>
      <view class="app_info_item">About us</view>
    </view>
    <!-- recommend -->
    <view class="recommend_wrap">
      Recommend the app to others
    </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;
  /* Gaussian blurred background image */
  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;
}

The final effect is as follows

Order query implementation

In the index.wxml in the user folder, when we jump the page with hyperlinks such as "all orders" and "to be paid", we also pass the parameter type, which determines to jump to the order folder (i.e. the order page) to display different contents. Here we used the previous in goods_ Similarly, for the custom components of tabs used in the list, first import the components in index.json under the order folder, and first write the initial data of the navigation bar in index.js, and then use.

// pages/order/index.js

Page({

  /**
   * Initial data of the page
   */
  data: {
    // tabs navigation bar initial data
    tabs: [{
        id: 0,
        value: 'comprehensive',
        isActive: true
      },
      {
        id: 1,
        value: 'Pending payment',
        isActive: false
      },
      {
        id: 2,
        value: 'To be shipped',
        isActive: false
      },
      {
        id: 3,
        value: 'refund/return goods',
        isActive: false
      }
    ],
  },
  handleTabsItemChange(e) {
    // 1 get the clicked title index
    const { index } = e.detail;
    this.changeTitleByIndex(index);
    // 2 resend request type=1 index=0
    this.getOrders(index+1);
  },
})
 {
  "usingComponents": {
    "tabs": "../../components/tabs/tabs"
  },
  "navigationBarTitleText": "Order query"
}

<!--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">Order No</view>
        <view class="order_no_value">{{item.order_number}}</view>
      </view>
      <view class="order_price_row">
        <view class="order_price_text">Order price</view>
        <view class="order_price_value">¥{{item.order_price}}</view>
      </view>
      <view class="order_time_row">
        <view class="order_time_text">Order date</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;
}

The final effect is as follows

The function we want to realize now is to click different buttons on the personal center page, such as all orders, goods to be received, jump to the order page (order) to display different contents, and pass the parameter type.

  1. The onShow function starts when the page is opened
    onShow is different from onLoad and cannot receive options parameters on formal parameters
    Judge whether there is a token in the cache
    There is no direct jump to the authorization page
    If there is, go straight down
    1.1. Get the parameter type on the url
    1.2. Determine which array element of the page title is activated and selected according to the type
    1.3. Send a request to obtain order data according to type
    1.4. Render page
  2. Click on a different title to resend the request to get and render the data
// pages/order/index.js
/* 
1 The onShow function starts when the page is opened
   onShow Unlike onLoad, options parameters cannot be received on formal parameters 
   Judge whether there is a token in the cache 
    1 There is no direct jump to the authorization page
    2 Yes, go straight down 
  1 Get parameter type on url
  2 Determine which array element of the page title is activated and selected according to the type 
  2 Send a request to obtain order data according to type
  3 Render Page 
2 Click on a different title to resend the request to get and render the data 
 */
import {
  request
} from "../../request/index.js";
Page({

  /**
   * Initial data of the page
   */
  data: {
    orders: [],
    // tabs navigation bar initial data
    tabs: [{
        id: 0,
        value: 'comprehensive',
        isActive: true
      },
      {
        id: 1,
        value: 'Pending payment',
        isActive: false
      },
      {
        id: 2,
        value: 'To be shipped',
        isActive: false
      },
      {
        id: 3,
        value: 'refund/return goods',
        isActive: false
      }
    ],
  },
  onShow(options) {
    const token = wx.getStorageSync("token");
    if (!token) {
      wx.navigateTo({
        url: '/pages/auth/index'
      });
      return;
    }



    // 1 get the page stack of the current applet - the maximum length of the array is 10 pages 
    let pages = getCurrentPages();
    // 2 the page with the largest index in the array is the current page
    let currentPage = pages[pages.length - 1];
    // 3 get the type parameter on the url
    const {
      type
    } = currentPage.options;
    // 4 activate the selected page title when type=1 index=0 
    this.changeTitleByIndex(type - 1);
    this.getOrders(type);
  },
  // How to get the order list
  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())
      }))
    })
  },
  // Activates the selected title array based on the title index
  changeTitleByIndex(index) {
    // 2 modify source array
    let {
      tabs
    } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    // 3 assign value to data
    this.setData({
      tabs
    })
  },
  handleTabsItemChange(e) {
    // 1 get the clicked title index
    const {
      index
    } = e.detail;
    this.changeTitleByIndex(index);
    // 2 resend request type=1 index=0
    this.getOrders(index + 1);
  },
})

Because the complete realization of the payment function requires the development of enterprise wechat, we have not fully realized the payment function. The order page here only displays a pile of pending payments. The effect is as follows

Other pages are empty.

Realize the collection function of goods

On the product details page, there is a collection button. After clicking, you will be prompted that the collection is successful. At this time, click the personal center page, select the collected products, and click to jump to the collection page to display the collected products just now. The implementation principle is very simple. After clicking collection, the commodity is stored in the cache array. After clicking cancel collection, the information of the commodity is deleted from the cache array.
Now back to goods_ In the detail page folder, modify the index.js code inside. The implementation steps are as follows

  1. When the onShow function is loaded on the page, the data of the commodity collection in the cache is loaded
  2. Judge whether the current product is collected
    2.1 is the icon to change the page
    2.2 if not, there is no operation
  3. Click the item collection button
    3.1 judge whether the commodity exists in the cache array
    3.2 the commodity already exists. Delete it
    3.3 if there is no commodity, add the commodity to the collection array and store it in the cache

We have set a check. In the index.js of commodity details, if the user does not log in, you need to jump to the login page to log in before you can perform the collection function.

// pages/goods_detail/index.js
//The method for sending requests is introduced to optimize the
import {
  request
} from "../../request/index.js"
Page({

  /**
   * Initial data of the page
   */
  data: {
    //The data returned by the request is in the form of an object
    goodsObj: {},
    // Is the product collected
    isCollect: false

  },
  //Global variable that defines the large image information array to preview
  goodsInfo: {},
  /**
   * Life cycle function -- listening for page loading
   */
  onLoad: function (options) {
    //The product id passed when getting the jump page
    const {
      goods_id
    } = options;
    this.getGoodsDetail(goods_id);
  },
  /**
   * Get product details data
   */
  async getGoodsDetail(goods_id) {
    const goodsObj = await request({
      url: "https://api-hmugo-web.itheima.net/api/public/v1/goods/detail",
      data: {
        goods_id
      }
    });
    //Assign a value to the previously defined preview large image array after the request is successful
    this.goodsInfo = goodsObj.data.message;
    // 1 get the array of item collections in the cache
    let collect = wx.getStorageSync("collect") || [];
    // 2. Judge whether the current commodity is collected
    let isCollect = collect.some(v => v.goods_id === this.goodsInfo.goods_id);
    this.setData({
      // Optimize the storage data, and only assign and store the data used by the applet
      goodsObj: {
        goods_name: goodsObj.data.message.goods_name,
        goods_price: goodsObj.data.message.goods_price,
        // Some iphone phones do not support webp format
        // Background modification
        // Or use the replace function for temporary modification, where \. webp is the file to find all. webp, g means select all, and. jpg means replace all with. jpg format.
        goods_introduce: goodsObj.data.message.goods_introduce.replace(/\.webp/g, '.jpg'),
        pics: goodsObj.data.message.pics
      },
      isCollect
    })
  },
  /**
   * Click the carousel to preview the large picture event
   */
  handlePrevewImage(e) {
    console.log('preview');
    //  First build an array of pictures to preview
    const urls = this.goodsInfo.pics.map(v => v.pics_mid)
    // Accept the passed image url
    const current = e.currentTarget.dataset.url
    wx.previewImage({
      current: current,
      urls: urls
    })
  },
  /**
   * Event triggered when a user item is added to the shopping cart
   */
  handleCartAdd(e) {
    //Get the cached commodity data and convert it from string format to array format
    let cart = wx.getStorageSync('cart') || [];
    // Determine whether the item already exists in the shopping cart array
    let index = cart.findIndex(v => v.goods_id === this.goodsInfo.goods_id);
    if (index === -1) {
      //Does not exist, added for the first time
      this.goodsInfo.num = 1;
      // Add a checked attribute to the product, with the value of true, so that it can be selected in the check box on the shopping cart page
      this.goodsInfo.checked = true;
      cart.push(this.goodsInfo);
    } else {
      //existence
      cart[index].num++;
    }
    //The shopping cart array is updated to the cache
    wx.setStorageSync("cart", cart);
    //Pop up prompt
    wx.showToast({
      title: 'Added successfully',
      icon: 'success',
      mask: 'true'
    })
  },
  // Click the item collection icon event
  handleCollect() {
    //Get the user login information. If the user logs in, the favorite Icon event can be executed
    let userinfo = wx.getStorageSync("userinfo");
    if (userinfo) {
      let isCollect = false;
      // 1 get the item collection array in the cache
      let collect = wx.getStorageSync("collect") || [];
      // 2 judge whether the product has been collected
      let index = collect.findIndex(v => v.goods_id === this.goodsInfo.goods_id);
      // 3 when index=- 1 means it has been collected 
      if (index !== -1) {
        // You can find the item that has been collected and delete it in the array
        collect.splice(index, 1);
        isCollect = false;
        wx.showToast({
          title: 'Cancellation succeeded',
          icon: 'success',
          mask: true
        });
      } else {
        // No collection
        collect.push(this.goodsInfo);
        isCollect = true;
        wx.showToast({
          title: 'Collection successful',
          icon: 'success',
          mask: true
        });
      }
      // 4 store the array in the cache
      wx.setStorageSync("collect", collect);
      // 5 modify isCollect attribute in data
      this.setData({
        isCollect
      })
    }else{
      wx.navigateTo({
        url: '/pages/login/index',
      })
    }
  },

  /**
   * Life cycle function -- monitor page display
   */
  onShow: function () {

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

  /**
   * Life cycle function -- listening for page hiding
   */
  onHide: function () {

  },
})

Bind click event to favorite button

<!--pages/goods_detail/index.wxml-->
<view class="detail_swiper">
  <!-- Content of rotation chart,bindtap Bind a preview big picture event -->
  <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>
<!-- Product content text -->
<!-- commodity price -->
<view class="goods_price">¥{{goodsObj.goods_price}}</view>
<view class="goods_name_row">
  <!-- Commodity name -->
  <view class="goods_name">{{goodsObj.goods_name}}</view>
  <!-- Collect goods -->
  <view class="goods_collect" bindtap="handleCollect">
    <!-- The collection icons here are imported from Ali icon library -->
    <text class="iconfont {{isCollect?'icon-shoucang1':'icon-shoucang'}}"></text>
    <view class="collect_text">Collection</view>
  </view>
</view>

<!-- Graphic details -->
<view class="goods_info">
  <view class="goods_info_title">Graphic details</view>
  <view class="goods_info_content">
    <!-- Rich text rendering -->
    <rich-text nodes="{{goodsObj.goods_introduce}}"></rich-text>
  </view>
</view>

<!-- Bottom navigation bar -->
<view class="btm_tool">
  <!-- customer service -->
  <view class="tool_item">
    <view class="iconfont icon-kefu"></view>
    <view>customer service</view>
    <!-- Add the function of contacting customer service ,Hidden in the lower layer of customer service, the transparency is 0. Set its height and width to be consistent with that of customer service-->
    <button open-type="contact"></button>
  </view>
  <!-- share -->
  <view class="tool_item">
    <view class="iconfont icon-fenxiang"></view>
    <view>share</view>
    <button open-type="share"></button>
  </view>
  <!-- Shopping Cart -->
  <!-- switchTab Allow jump tabBar(Navigation bar) page -->
  <navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
    <view class="tool_item">
      <view class="iconfont icon-gouwuche"></view>
      <view>Shopping Cart</view>
    </view>
  </navigator>
  <!-- add to cart -->
  <!-- Add click event and product response -->
  <view class="tool_item btn_cart" bindtap="handleCartAdd">
    <view>add to cart</view>
  </view>
  <!-- Buy now -->
  <view class="tool_item btn_buy">
    <view>Buy now</view>
  </view>
</view>

Then modify the code in the collect folder,

// pages/collect/index.js
Page({
  /**
   * Initial data of the page
   */
  data: {
    collect:[],
    tabs: [
      {
        id: 0,
        value: "Commodity collection",
        isActive: true
      },
      {
        id: 1,
        value: "Brand collection",
        isActive: false
      },
      {
        id: 2,
        value: "Store collection",
        isActive: false
      },
      {
        id: 3,
        value: "Browser footsteps",
        isActive: false
      }
    ]
  },
  onShow(){
    const collect=wx.getStorageSync("collect")||[];
    this.setData({
      collect
    });
      
  },
  handleTabsItemChange(e) {
    // 1 get the clicked title index
    const { index } = e.detail;
    // 2 modify source array
    let { tabs } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    // 3 assign value to data
    this.setData({
      tabs
    })
  }
})
{
  "usingComponents": {
    "Tabs":"../../components/tabs/tabs"
  },
  "navigationBarTitleText":"Commodity collection"
}

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

<view class="collect_main">
  <view class="collect_title">
    <text class="collect_tips active">whole</text>
    <text class="collect_tips">It's on sale</text>
    <text class="collect_tips">Coming online soon</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}}">
      <!-- Left picture container -->
      <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>
      <!-- Right commodity container -->
      <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;
}

The final effect is as follows

Implementation of search page function

The search page first has a search box. We previously customized a component, which is essentially a hyperlink. You can enter keywords in the search box for search. On this page, we do not use custom components to implement the contents of the search folder.

Note: there is a place here, that is, the anti shake (anti shake) timer has not been input for a period of time. It is sending a request, otherwise every character input will send a request, and the page will shake.

  • The anti shake general input box prevents repeated input and repeated sending requests
  • Throttling is generally used for page pull-down and pull-up
  • Both can be implemented by defining a global timer id
// pages/search/index.js
/* 
1 Input box binding value change event input event
  1 Gets the value of the input box
  2 Legitimacy judgment 
  3 Verify by sending the value of the input box to the background
  4 The returned data is printed on the page
2 Anti shake (anti shake) timer throttling 
  0 The anti shake general input box prevents repeated input and repeated sending requests
  1 Throttling is generally used for page pull-down and pull-up 
  1 Define global timer id
 */
import { request } from "../../request/index.js";

Page({
  data: {
    goods:[],
    // Is the Cancel button displayed
    isFocus:false,
    // Enter the value of the box
    inpValue:""
  },
  TimeId:-1,
  // Event triggered when the value of the input box changes
  handleInput(e){
    // 1 get the value of the input box
    const {value}=e.detail;
    // 2 test legitimacy
    if(!value.trim()){
      this.setData({
        goods:[],
        isFocus:false
      })
      // Illegal value
      return;
    }
    // 3 prepare to send request to get data
    this.setData({
      isFocus:true
    })
    clearTimeout(this.TimeId);
    this.TimeId=setTimeout(() => {
      this.qsearch(value);
    }, 1000);
  },
  // Send request to get search suggestion data
  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
    })
  },
  // Click the Cancel button
  handleCancel(){
    this.setData({
      inpValue:"",
      isFocus:false,
      goods:[]
    })
  }
})
{
  "usingComponents": {},
  "navigationBarTitleText":"Search center"
}
<!--pages/search/index.wxml-->
<view class="search_row">
  <input value="{{inpValue}}" placeholder="Please enter the item you want to search" bindinput="handleInput"> </input>
  <button bindtap="handleCancel" hidden="{{!isFocus}}">cancel</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;
}

The final effect is as follows

Feedback page

In the official development document of the applet, the button button has an open type opening ability, which can directly drop the feedback. Here we do not borrow the open ability to customize a page. The first is to modify the code of the personal center and add a jump link function to the feedback part.

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

<view class="user_info_wrap">
  <view wx:if="{{userinfo.avatarUrl}}" class="user_img_wrap">
  <!-- Because the avatar of user information obtained during writing will fail to connect, the network diagram is used instead -->
    <!-- <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" >Go to login</navigator>
  </view>
</view>
<view class="user_content">
  
  <view class="user_main">
    <!-- Historical footprint -->
    <view class="history_wrap">
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">Collection store</view>
      </navigator>
      <navigator url="/pages/collect/index">
        <view class="his_num">{{collectNums}}</view>
        <view class="his_name">Collectible goods</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">Products of concern</view>
      </navigator>
      <navigator>
        <view class="his_num">0</view>
        <view class="his_name">My Tracks </view>
      </navigator>
    </view>
    <!-- My order -->
    <view class="orders_wrap">
      <view class="orders_title">My order</view>
      <view class="order_content">
      <!-- Use here url During the jump, it passed type Parameter, click different links below to jump to order Page, but displays different content. -->
        <navigator url="/pages/order/index?type=1">
        <!-- icon-ding_dan This is how to use Ali vector icon library -->
          <view class="iconfont icon-ding_dan"></view>
          <view class="order_name">All orders</view>
        </navigator>
        <navigator url="/pages/order/index?type=2">
          <view class="iconfont icon-fukuantongzhi"></view>
          <view class="order_name">Pending payment</view>
        </navigator>
        <navigator url="/pages/order/index?type=3">
          <view class="iconfont icon-receipt_address"></view>
          <view class="order_name">Goods to be received</view>
        </navigator>
        <navigator>
          <view class="iconfont icon-tuikuantuihuo;"></view>
          <view class="order_name">refund/return goods</view>
        </navigator>
      </view>
    </view>
    <!-- Receiving address management -->
    <view class="address_wrap">
      Receiving address management
    </view>
    <!-- Application information related -->
    <view class="app_info_wrap">
      <view class="app_info_item app_info_contact">
        <text>Contact customer service</text>
        <text>400-618-4000</text>
      </view>
      <navigator url="/pages/feedback/index" class="app_info_item">Feedback</navigator>
      <view class="app_info_item">About us</view>
    </view>
    <!-- recommend -->
    <view class="recommend_wrap">
      Recommend the app to others
    </view>
  </view>
</view>

The next step is to modify the code of index.json in the feedback folder, including page naming, introduction of top navigation bar components and other preparations.

{
  "usingComponents": {
    "tabs": "../../components/tabs/tabs"
  },
  "navigationBarTitleText": "Feedback"
}

Then, in order to realize the function of adding and deleting pictures in the feedback page, we customize a component, create a new UpImg folder under the components folder, right-click the folder, select new components, and then enter UpImg. The wechat developer tool will automatically generate the following files for us.

Then modify the file code in the folder UpImg

// components/UpImg/UpImg.js
Component({
  /**
   * List of properties for the component
   */
  properties: {
    src:{
      type:String,
      value:""
    }
  },

  /**
   * Initial data of components
   */
  data: {

  },

  /**
   * Component method list
   */
  methods: {

  }
})
{
  "component": true,
  "usingComponents": {}
}
<!--components/UpImg/UpImg.wxml-->
<view class="up_img_wrap">
  <!-- picture -->
  <image src="{{src}}"></image>
  <!-- Icon representing deletion -->
  <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;
}

Then go back to index.json in the feedback folder and introduce the component UpImg.

{
  "usingComponents": {
    "tabs": "../../components/tabs/tabs",
    "UpImg":"../../components/UpImg/UpImg"
  },
  "navigationBarTitleText": "Feedback"
}

Modify the code logic in index.js

// pages/feedback/index.js
// 1, When you click the + button, a click event is triggered and the built-in API for selecting pictures in the applet is called
// Get the selected picture path (array format)
// Store the image path into the data variable
// The page can be displayed according to the image array
// 2, When clicking the custom component, delete the currently clicked image
// The first is to get the index of the clicked element
// Get the image array in data
// Delete the picture from the array according to the index
// Reset the array back to data
// 3, After the user clicks the submit button
// Get the content of the text field and verify the legitimacy. If the verification fails, a pop-up prompt will pop up
//     First, define a variable to accept the data in the text field, and bind an input response event to the text field
// After the verification is passed, the image selected by the user is uploaded to a special image storage server, and then the server will return the url address of the image
//      Traverse the array of stored pictures and upload them one by one
//      You are defining a variable to store the returned image url address
// The text field content and the returned picture url address are submitted to the background server
// After successful submission, clear the current page and return to the previous page.
Page({
  data: {
    // Introduce custom components and set initial data
    tabs: [{
        id: 0,
        value: "Experience problem",
        isActive: true
      },
      {
        id: 1,
        value: "Complaints from commodities and merchants",
        isActive: false
      }
    ],
    // The path array of the selected picture
    chooseImgs: [],
    // Receive variables stored in text fields
    textVal: [],
  },
  // url address of the picture returned by the picture storage server
  UpLoadImgs: [],
  // Click event on the top navigation bar
  handleTabsItemChange(e) {
    // 1 get the clicked title index
    const {
      index
    } = e.detail;
    // 2 modify source array
    let {
      tabs
    } = this.data;
    tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
    // 3 assign value to data
    this.setData({
      tabs
    })
  },
  // Click the + sign to select a picture event
  handleChooseImg(e) {
    // Call built-in API
    wx.chooseImage({
      // Number of pictures selected at the same time
      count: 9,
      // Picture format
      sizeType: ['original', 'compressed'],
      // Source of pictures, photo albums and cameras
      sourceType: ['album', 'camera'],
      success: (result) => {
        // After printing and viewing the content successfully, our image path is stored in result.tempfilepathsZ
        // console.log(result);
        this.setData({
          // Image array splicing can make the array splicing of multiple selected images, old images and new images
          // this.data.chooseImgs is an array of old pictures currently stored
          chooseImgs: [...this.data.chooseImgs, ...result.tempFilePaths]
        })
      }
    })
  },
  // Click the custom component to delete the picture event
  handleRemoveImg(e) {
    // Get the index of the clicked component
    // console.log(e);
    const {
      index
    } = e.currentTarget.dataset.index;
    // Get the image array in data
    let {
      chooseImgs
    } = this.data;
    // Delete element, index and number of elements to delete
    chooseImgs.splice(index, 1)
    // Fill in data
    this.setData({
      chooseImgs
    })
  },
  // Input response event for text field
  handleTextInput(e) {
    // console.log(e);
    this.setData({
      textVal: e.detail.value
    })
  },
  // Response event for submit button
  handleFormSubmit(e) {
    // Get the content of the text field and the image array at this time
    const {
      textVal,
      chooseImgs
    } = this.data;
    // Verify legitimacy
    // trim removes the beginning and end spaces and line terminators from the string
    // If textVal is empty after deletion, it can be used again! If it is negative, the illegal prompt in if will be executed
    var str=textVal.trim;
    if (!str) {
      // wrongful
      wx.showToast({
        title: 'Your input is empty!',
        mask: 'true',
        icon: 'error'
      })
      return;
    };
    // Upload pictures to a special picture server and use the built-in API of the applet
    // The api for uploading files does not support uploading multiple files at the same time, so you need to traverse the array storing pictures and upload them one by one
    // Show waiting pictures
    wx.showLoading({
      title: "Uploading",
      mask: true
    });
    // Determine whether there is an array of pictures to upload
    if(chooseImgs.length != 0){
      chooseImgs.forEach((v, i) => {
        wx.uploadFile({
          // File path to upload
          filePath: v,
          // Here, the name of name is determined through communication with the backend
          name: 'image',
          // Where do you want to upload the picture
          url: 'https://img.coolcr.cn/api/upload',
          // Accompanying text content
          formData: {},
          success: (result) => {
            let url = JSON.parse(result.data).data.url;
            this.UpLoadImgs.push(url);
            // Triggered after all pictures are uploaded
            if (i === chooseImgs.length - 1) {
              // Close pop-up window
              wx.hideLoading();
              // Here we use log output instead of submitting data to the back end
              console.log("Submit the content of the text and the image array of the Internet to the background");
              // Reset this page after submission
              this.setData({
                textVal: "",
                chooseImgs: []
              })
              // Back to previous page
              wx.navigateBack({
                delta: 1
              });
            }
          }
        })
      })}else(
        // If there is no picture upload but only text, execute the following
        wx.hideLoading(),
        console.log('Only text was submitted'),
        wx.navigateBack({
          delta: 1
        })
      )
    }
})

Write structure in index.wxml

<!--pages/feedback/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">
  <view class="fb_main">
    <view class="fb_title">Types of problems</view>
    <view class="fb_tips">
      <text>Function suggestion</text>
      <text>Problem with purchase</text>
      <text>Performance issues</text>
      <text>other</text>
    </view>
    <view class="fb_content">
    <!-- Text field label textarea -->
      <textarea value="{{textVal}}" bindinput="handleTextInput" placeholder="Please describe your problem"> </textarea>
      <view class="fb_tool">
      <!-- +Add a select Picture event to button No -->
        <button bindtap="handleChooseImg">+</button>
        <!-- Render the selected picture -->
        <!-- When the data is some simple data, we here Wx: key To write as*this -->
        <!-- data-index Is the index value of the currently clicked picture -->
        <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>
        Submit
      </button>
    </view>
  </view>
</tabs>

Write style in index.wxss

/* pages/feedback/index.wxss */
page {
  /* Page background color */
  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%;
}

The final effect is as follows

Add a jump link to the product on the home page

Now to the home page, click the relevant picture to realize the jump. The first is the rotation picture. Because the address of the goods written in the background interface has been modified, such as goods_ For the product with ID 129, its address URL is / pages/goods_detail/main?goods_id=129 ", and the files in our folder are named by index, so here we borrow the goods_id to specify the path. We add a jump URL to the navigator. At the same time, we also add a URL to the category column and write a path url="/pages/category/index ".

<view class="gouwu">
  <!-- Search box -->
  <SearchInput></SearchInput>
  <!-- Rotation chart -->
  <view class="lunbotu">
    <!-- swiper,image Wait for the label to be set beyond the default width and height at the beginning -->
    <!-- stay swiper Start on label autoplay Automatic rotation attribute, and display rotation indication point and convergence rotation-->
    <swiper autoplay="true" indicator-dots="true" circular="true">
      <!-- Need to give swiper-item Cycle -->
      <!-- The contents of the loop are returned from the server and stored swiperList Obtained from -->
      <swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
      <!-- Add jump page function to the rotation chart of the home page -->
      <!--Because the address of the commodity written in the background interface has been modified, it is /pages/goods_detail/main?goods_id=129",And we
      All the documents are index Named, so I borrowed it here goods_id Specify path-->
        <navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" >
          <!-- Picture label added mode attribute widthFix,The height and width of the content of the picture label change proportionally -->
          <image mode="widthFix" src="{{item.image_src}}"></image>
        </navigator>
      </swiper-item>
    </swiper>
  </view>
  <!-- Classified navigation -->
  <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>
  <!-- floor -->
  <view class="louceng">
    <view class="floor_group" wx:for="{{floorList}}" wx:for-item="item1" wx:for-index="index1" wx:key="floor_title">
      <!-- Title of the floor -->
      <view class="title">
        <image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
      </view>
      <!-- Specific content of floor -->
      <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>

Finally, add jump links to the pictures in the specific content of the home page, such as the pictures in fashionable women's clothing. Click to jump to the commodity list page.

<view class="gouwu">
  <!-- Search box -->
  <SearchInput></SearchInput>
  <!-- Rotation chart -->
  <view class="lunbotu">
    <!-- swiper,image Wait for the label to be set beyond the default width and height at the beginning -->
    <!-- stay swiper Start on label autoplay Automatic rotation attribute, and display rotation indication point and convergence rotation-->
    <swiper autoplay="true" indicator-dots="true" circular="true">
      <!-- Need to give swiper-item Cycle -->
      <!-- The contents of the loop are returned from the server and stored swiperList Obtained from -->
      <swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
        <!-- Add jump page function to the rotation chart of the home page -->
        <!--Because the address of the commodity written in the background interface has been modified, it is /pages/goods_detail/main?goods_id=129",And we
      All the documents are index Named, so I borrowed it here goods_id Specify path-->
        <navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}">
          <!-- Picture label added mode attribute widthFix,The height and width of the content of the picture label change proportionally -->
          <image mode="widthFix" src="{{item.image_src}}"></image>
        </navigator>
      </swiper-item>
    </swiper>
  </view>
  <!-- Classified navigation -->
  <view class="fenlei">
    <!-- Add jump to category navigation 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>
  <!-- floor -->
  <view class="louceng">
    <view class="floor_group" wx:for="{{floorList}}" wx:for-item="item1" wx:for-index="index1" wx:key="floor_title">
      <!-- Title of the floor -->
      <view class="title">
        <image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
      </view>
      <!-- Specific content of floor -->
      <view class="list">
      <!-- Click the picture inside the specific content to jump to the relevant page -->
        <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>

Project release

In the actual application, after the code is written, it will be tested by the relevant personnel of the enterprise. Here we skip and enter the project release step. First, uncheck the illegal domain name in the applet developer tool and local settings. Otherwise, it will cause unsafe factors in the actual application of users. Then we put all the extranet chains used in applet development Then, on the applet cloud platform, add to the white list.

Then, the APPID here should be modified to its own or enterprise, not the test number

Then make sure that the total code does not exceed 2M.

Then click upload

Then open Wechat public platform , click version management, and then pull down the page to find the development version


Here, if we choose to submit for review, we will submit it to wechat for official review, and then officially launch it. Here, I choose the experience version. At the same time, put some link URLs into the white list, find the development, development management and server domain name for configuration.

Tags: Front-end Mini Program

Posted on Mon, 25 Oct 2021 08:46:37 -0400 by pjc2003