preface

Recently, I am learning wechat small program development. I also started to copy the APP wechat small program with two classmates. Here I would like to share my learning process and some pits, hoping to help you.

The development of preparation

  • Wechat developer tools
  • VScode code editor
  • Get things APP wechat small program
  • Have a good vant component library
  • Alibaba Vector icon library
  • Markman (Take color measure distance)

The overall architecture

  • This project is based on the small program cloud development, using the template of cloud development quick start template. Because it is a full stack project, the front end uses the small program supported by the WXML + WXSS + JS development mode, named using BEM naming specification. The background is to use the cloud database for data management.

I am responsible for the following part of the project: (Some data is written in config as fixed data, js files are exposed through module.exports and are introduced in the corresponding JS header when needed, for example const {} = require(‘.. /.. /.. /.. / config/buys’)). I used a lot of Vant components in the project and needed to introduce Vant when building the NPM package. See the NPM installation that likes Vant for details. If a page uses a third-party component, it must be declared in the corresponding JSON file. In order to avoid repeated work, it can be directly declared in app.json. Example: (“usingComponents”: “van-search”: “@vant/ appellate /search/index”})

| | - config corresponding data - assem. Js | - buys. Js | - detail. Js | - kind. Js | - search. Js | - pages | - buy_page | - page | - assem selection sort, | - buy to buy the home page | - the detail goods details page | - | - produce kinds brand classification identification introduction page | - search search pageCopy the code

Project planning

  • Before doing this small program, I first analyzed the corresponding functions of each page, understood the interaction details of this small program, clear data set data items. This can be divided into four steps to expand the analysis page, create data set, deconstruct the basic layout of the page, data binding and jump.

Referring to the small program of wechat of dewu APP, the following is the tabBar of my small program. (A little rough, but you can still see 😢)

  "tabBar": {
    "selectedColor": "# 000000"."borderStyle": "white"."backgroundColor": "#fff"."list": [{"text": "Buy"."pagePath": "pages/buy_page/page/buy/buy"."iconPath": "images/buy.png"."selectedIconPath": "images/buy_active.png"
      },
      {
        "text": "Identification query"."pagePath": "pages/disting/disting"."iconPath": "images/disting.png"."selectedIconPath": "images/disting_active.png"
      },
      {
        "text": "Care"."pagePath": "pages/wash/wash"."iconPath": "images/wash.png"."selectedIconPath": "images/wash_active.png"
      },
      {
        "text": "ζˆ‘"."pagePath": "pages/my_page/my/my"."iconPath": "images/my.png"."selectedIconPath": "images/my_active.png"}},Copy the code

Cloud database

A cloud database is a NoSQL database. Each table is a collection. For my project part, I mainly built a collection of goods.

Dewu_goods Goods table is used to store information about created goods. - _id - amway whether it is recommended - Brand brand - buyer Number of buyers - ctime Data creation time - Digest details - img details - PIC - Kind - price - Sex - Title - Type Home indexCopy the code

After creating a data set, you need to modify the data permission to access it.

You can perform these operations in the database, noting that the import data format needs to be.csvor.jsonFiles, you can first use Excel tables to establish data sets how to convert into the corresponding format files directly into the database.

const db = wx.cloud.database() // Cloud database
const dewuCollection = db.collection('dewu') // Import the data set into the js file
Copy the code

Project deconstruction

The following is my main realization of the object APP small program interface

Next, deconstruct the details of each page.

Buy the home page

Buy home page Style

<view class="page"> <! -- Set dewu-hd search bar with van-sticky --><van-sticky>    
  <! -- dewu-hd using Flex layout -->
    <view class="dewu-hd">
      <view class="dewu-hd-search" bindtap="gotoSearch">
        <van-search placeholder="Search order Number" disabled />
      </view>
      <view class="dewu-kinds" bindtap="kinds"><image src=""></image>
      </view>
    </view>
  </van-sticky><! -- Van-tabs implements purchase page navigation and corresponds to content pages --><van-tabs class="dewu-tabs">
    <van-tab title="Recommended">
      <view class="dewu-tip">
        <view class="dewu-tip-img-hd"><image src=""></image>
        </view>
        <! -- Use van-Grid layout to set borders to hide quick positioning -->
        <van-grid>
          <van-grid-item use-slot>
            <image style="" src=""></image>
            <text>Authentic guarantee</text>
          </van-grid-item>
        </van-grid>
      </view>
      <view class="van-items">
        <van-grid class="van-grid-bd">
        <! -- Grid layout custom van-grid-item style -->
          <van-grid-item use-slot>
            <view class="item-img"><image src=""></image></view>
            <view class="item-text">
              <span>{{}}</span>
            </view>
          </van-grid-item>
        </van-grid>
      </view>
    </van-tab>
  </van-tabs>
</view>
Copy the code

Absolute positioning is used in the item van-grid-item. Tips Set direction to Horizontal so that the contents of the grid are aligned horizontally. The disabled property of the search box is set to disable. When van-Grid is used to customize each attribute, the use-slot attribute must be set. Otherwise, the attribute does not take effect.

The page layout is not complicated, but I still ran into a pit when writing it (I feel like I jumped into it too πŸ–). When doing deWu-HD top suction, I directly wrapped it with van-sticky, but the actual effect is that tabs also need to be fixed under DEWu-HD. Do not use the same method as above, the effect will be the entire van-tabs top and the page cannot slide. In this case, you only need to add a sticky attribute to van-tabs and set offset-top. Note that the two attributes need to be used together to take effect.

Get item

  async onLoad() {
     this.proData()   // Get the recommended data item
     this.shoeData()  // Get the shoe data item
  },
  proData() {
     const {data} = await dewuCollection
    .where({
      amway: db.command.eq('TRUE')
    })
    .field({          // Get the specified data item to improve performance
      _id:true.pic:true.title:true.buyer:true.price:true
    })  
    .get()
    // console.log(data);
    this.setData({
      produces: data,
    })
  }
  shoeData() {
    let data1 = await dewuCollection
    .where({
      type: 1
    }) 
    .get()
    // console.log(data1.data);
    this.setData({
      shoes: data1.data
    })
  }
Copy the code

Binding Details page

  gotoDetail(e) {
    // console.log(e);
    wx.navigateTo({
      url: '/pages/buy_page/page/detail/detail? id='+e.currentTarget.dataset.id,
    })
  },
Copy the code

Using the unique attribute of commodity _id, when the data item ID is set to be equal to _id, the details page will be jumped and corresponding data will be displayed.

Product Details page

Product details page style

<view class="page"> <! -- Head slider and title --><view class="detail_hd">
    <swiper class="swiper__hd">
        <swiper-item class="swiper_hd"></swiper-item>
    </swiper>
    <view class="dots1">
        <view class="{{current==index? 'active':''}}"></view>
    </view>
    <view class="detail_hd-title">{{img.digest}}</view>
    <view class="detail_hd-price">
      <text id="p2">${{img. Price}}</text>
    </view>
  </view>
  <van-cell class="size" bind:click="showPopup1">
    <view class="size-l">Choose size</view>
    <view class="size-r">Please select size</view>
    <image class="ricon" style="width:26rpx; height:26rpx;" src=""></image>
  </van-cell><! -- Flex layout each swiper-item contains three items --><view class="detail_bd">
  <swiper></swiper></view>

  <van-goods-action>
    <button>Buy now</button>
  </van-goods-action>
</view>
Copy the code

The whole is divided into detail_HD and detail_BD. To customize swiper, you need to set the corresponding dot display picture and change the style. To set whether to enable slider switch animation, you need to use the tri operator to determine whether to add a new style class name. You can use the first-letter pseudo-element to define the οΏ₯symbol style when defining the commodity price style. Reference component van-goods-action makes the buy button suck bottom.

<van-popup closeable position="bottom" custom-style="height: 75%">
    <view class="detail_size-hd">
      <view class="detail_size-hd-img">
        <image bindtap="previewImage" mode="aspectFit" src="{{img.pic}}">
        </image>
      </view>
      <view class="detail_size-hd-price">
        <text style="font-size:25rpx;">RMB</text>
        <text wx:if="{{activeSizeIndex==-1}}">--</text>
        <text wx:if="{{activeSizeIndex==index}}">{{item.price}}</text>
      </view>
      <view>
        <image src=""></image>
        <text wx:if="{{activeSizeIndex==-1}}">Please select goods</text>
        <text wx:if="{{activeSizeIndex==index}}">The selected {{item. The size}}</text>
      </view>
    </view><! -- Size layout --><view class="detail_size-bd">
      <van-grid square gutter="10">
        <van-grid-item>
          <view class="size">
            <text id="p3">{{item.size}}</text>
            <text id="p4">${{item. Price}}</text>
          </view>
        </van-grid-item>
      </van-grid>
    </view>
    <view>
      <button>{{}}</button>
    </view>
  </van-popup>
Copy the code

Using the van-popup component, set the event to the corresponding label to bind the pop-up. Example:
The trigonometry operator sets the default style and controls the selected border style, and sets the closeable property to enable the close button. Square sets van-grid-item to square, gutter to spacing.

<van-sticky sticky offset-top="{{180}}">
      <view class="head">
        <view class="detail_produce-hd">Related to recommend</view>
        <view class="detail_close" bindtap="onClose2">
          <image style="width:40rpx; height:40rpx;" src=""></image>
        </view>
      </view>
    </van-sticky>
Copy the code

Set detail_produce- HD top drawing and close icon bind:close=”onClose” event on the right.

Obtain product details

  async onLoad(options) {  // Get the commodity data for the _id
    console.log(options);
    let id = options.id
    console.log(id);
    wx.cloud.database().collection('dewu') 
    .doc(id)             
    .get()
    .then(res= > {
      console.log(res);
      this.setData({
       img :res.data
      })
    })
  },
Copy the code

Pop-up layer

  showPopup() {   // Display the pop-up layer
    this.setData({ 
      show: true}); },onClose() {     // Close the pop-up layer
    this.setData({ 
      show: false}); },Copy the code

Choose size

  pickSize(e) {
    let flag = e.currentTarget.dataset.flag
    let index = e.currentTarget.dataset.index
    if(flag==index) {   
      this.setData({
        activeSizeIndex: -1.flag: -1})}else {
      this.setData({
        activeSizeIndex: index,
        flag: index
      }) 
    }
  },
Copy the code

Click the size, flag==index is the selected state, click again or click another size, set to unselected state, otherwise make flag equal to index, so that it becomes the selected state.

Search page

Search page style

<view class="page">
  <view class="search">
    <van-stichy>
      <van-search value="{{value}}" bind:clear="onClear" placeholder="Input commodity name and Article Number"/>
    </van-stichy>
    
    <! -- Block wrap flex layout -->
    <block wx:if="{{showHistory == true && historyList.length > 0}}">
      <view class="historyContainer">
        <view class="title">Search history<image class="delete" src=""></image>
        </view>
        <view class="historyList">
          <view class="historyItem">
            <text class="order">{{}}</text>
          </view>
        </view>
      </view>
    </block>
  </view>
</view>
Copy the code

The search page is mainly divided into the head search box and content (search recommendations, history and search list of goods) two parts. Here, van-sticky is used to wrap the search box to the top, and the content part is wrapped with the block label. Wx :if control attribute is used to determine whether to display.

Search records

  async onSearch(e) {
    // console.log(e);
    if(! e.detail.trim()) { wx.showToast({title: 'Please enter the name of the product',})return
    }
    let {value, historyList} = this.data
    if(historyList.indexOf(value) ! = = -1) {
      historyList.splice(historyList.indexOf(value), 1)
    }
    historyList.unshift(value)
    this.setData({
      historyList
    })
    wx.setStorageSync('value', historyList)
    let keyword = e.detail.trim()
    let results = await dewuCollection
      .where({
        title: db.RegExp({
          regexp: keyword,
          options: 'i'
        })
      })
      .get()
    if (results.data.length == 0 || keyword == ' ') {
      wx.showToast({
        title: 'Nonexistence'+keyword,
      })
    }
    else {
      await dewuCollection
      .where({
        title: db.RegExp({
          regexp: keyword,
          options: 'i'
        })
      })
      .orderBy('hot'.'desc')
      .get()
      .then(res= > {
        console.log(res);
        this.setData({
          results: res.data
        })
      })
    }
  },
onLoad() {
    this.getSearchHistory()  // Get the history search
  },
getSearchHistory() {
    let historyList = wx.getStorageSync('value')
    if(historyList) {
      this.setData({
        historyList
      })
    }
  },
Copy the code

When the page is loaded, obtain the historical search records from the local storage, determine whether the value is empty when searching onSearch, insert the valid value into the historyList, use the unshift method here, so as to ensure that the most recent search records are displayed in front. Using regular expression fuzzy query database in accordance with the items stored in the array Results, when results. Length > 0 to display the list of goods. Use wx.setStorageSync to store the value in the cache and wx.getStorageSync to print it out. Check whether the value already exists by using the indexOf method. If yes, delete the item from historyList.

Search history

  async historySearch(e) {
    // console.log(e);
    let historyList = this.data.historyList
    let value = historyList[e.currentTarget.dataset.index]
    this.setData({
      value,               / / modify the value
      showHotList: false.// Hide popular searches
      showHistory: false.// Hide the history search
      results: []          // Clear the list of items})},Copy the code

When you click on the historical search term, setData changes the corresponding value, and then call the onSearch method.

Empty the controls

  onClear() {
    this.setData({
      results: [].value: ' '.showHotList: true.showHistory: true
    });
  },
  onChange(e) {  // The search box modifies data in real time as input changes
    // console.log(e.detail);
    this.setData({
      value: e.detail,
      showHotList: false.showHistory: false.results: []})// console.log(this.data.showHotList);
    if (this.data.value==' ') {
      this.setData({
        showHotList: true.showHistory: true})}},Copy the code

Clearing your Search history

  deleteSearchHistory() {
    wx.showModal({
      content: 'Confirm clearing history'.success: (res) = > {
        if(res.confirm) {
          this.setData({
            historyList: []
          })
        }
      }
    })
    wx.removeStorageSync('value')},Copy the code

Click Delete icon to pop up the dialog box wx.showModal to realize interaction, the user clicks OK to clear the historyList and use wx.removeStorageSync to delete the history records stored locally.

Brand classification page

Category page style

<view class="page">
  <van-sticky>
    <view class="search" bindtap="gotoSearch">
    <van-search placeholder="Search for goods" input-align="center" disabled />
  </view>
  </van-sticky>
  
  <view class="kinds">
    <view class="hd">
    <scroll-view class="scroll-view-left">
      <view class="scroll-view-left-item {{activeNavIndex == index? 'active': ''}}">
        <text>{{}}</text>
      </view>
    </scroll-view>
  </view>
  
  <view class="bd">
    <scroll-view>
      <view>
        <view class="kind-title">
        <van-divider contentPosition="center">{{}}</van-divider>
        </view>
        <van-grid>
        <van-grid-item>{{}}</van-grid-item>
        </van-grid>
      </view>
    </scroll-view>
  </view>
  </view>
</view>
Copy the code

The classification page mainly uses the scroll-view to set vertical scrolling. When clicking the left scroll-view-left-item, the item becomes identified (# 00CBCC) and displays the corresponding brand category item kindsItem. We should set font size to 0. We should set font in the child scroll-view-left-item element to avoid block margins affecting the layout.

Initialize the category

  onLoad: function (options) {
      this.setData({
        kindNav: kindNav,
        kindall: kindItem,
    // console.log(this.data.kindall);
    let kinds=[];
    // console.log(this.data.kindall)
    this.data.kindall.forEach(kind= > { // Loop to get the corresponding kindNav from all categories and store it in the array
      if(kind.camptype == 0) {
        kinds.push(kind)
      }
    })
    this.setData({
      kindItem: kinds,
    })
    }, )
  },
Copy the code

Choose classification

  changeKinds(e) {
    console.log(e);
    let {index, type} = e.currentTarget.dataset;
    console.log(index, type);//index refers to the index of the recommended brand. Type is related to kind.js campType
    this.setData({
      activeNavIndex: index,
    })
    let title=[]
    this.data.kindTitles.forEach(kindTitle= > {
      if(index == kindTitle.titletype) {
        title.push(kindTitle)
      }
    })
    this.setData({
      kindItem: kinds,
    })
  },
Copy the code

Binding filter page

  gotoAssem(e) {
    // console.log(e); Use the kind property value to be unique (buy page tabs title)
    wx.navigateTo({
      url: '/pages/buy_page/page/assem/assem? title='+e.currentTarget.dataset.title,
    })
  },
Copy the code

Filter sort page

Sort page style

<view class="page">
  <van-sticky>
    <view class="search" bindtap="gotoSearch">
      <van-search placeholder="{{titles}}" disabled />
    </view>
    <view class="tab">
      <view wx:for="{{tabs}}" wx:key="index" data-index="{{index}}"
        class="tab-item {{activeTabIndex == index? 'active': ''}}" bindtap="changeItem">
        <text>{{item.title}}</text>
        <image style="width:26rpx; height:26rpx;" src="{{item.pic}}"></image>
      </view>
    </view>
  </van-sticky>
</view>
Copy the code

TAB uses Flex layout. Refer to the layout of goods on the Buy page.

  <van-popup>
    <scroll-view class="pop" scroll-y>
      <van-collapse>
        <van-collapse-item title="Applicable group" value="All" name="1">
        </van-collapse-item>
        <van-grid column-num="3" gutter="{{10}}">
          <van-grid-item class="{{activeIndex1==index? 'active1':''}}">{{}}</van-grid-item>
        </van-grid>
      </van-collapse>
      
      <van-goods-action>
        <button>reset</button>
        <button>determine</button>
      </van-goods-action>
    </scroll-view>
  </van-popup>
Copy the code

The van-grid content should not be placed in the van-collapse item. It should be placed at the same level as the van-collapse item. Otherwise, it will form a blank space under the cell and the content cannot be displayed normally.

Initial commodity order

  async onLoad(options) {
    // console.log(options);
    let title = options.title
    let data1 = await dewuCollection
    .where({
      kind: title        // get the corresponding data when binding jump (kind only)
    }) 
    .get()
    // console.log(data1);
    this.setData({
      goods: data1.data,
      titles: title
    })
  },
Copy the code

The basic sorting

  async changeItem(e) {  
    // console.log(e);
    let index = e.currentTarget.dataset.index  //index indicates the sort
    this.setData({
      activeTabIndex: index
    })
    // console.log(index);
    if(index == 1) {                        // Sales rank
    await dewuCollection
    .where({
      kind: this.data.titles
    })
    .orderBy('buyer'.'desc') 
    .get()
    .then(res= > {
      this.setData({
        goods: res.data,
        index: index
      })
      // console.log(this.data.index);})}if(index == 0) {                        // aggregate sort
      await dewuCollection
      .where({
        kind: this.data.titles
      })
      .get()
      .then(res= > {
        this.setData({
          goods: res.data
        })
      })
    }
    if(index == 2 && this.data.flag == -1) {  // Sort the prices in descending order
      await dewuCollection
      .where({
        kind: this.data.titles
      })
      .orderBy('price'.'desc') 
      .get()
      .then(res= > {
        this.setData({
          goods: res.data,
          flag: 1})})return
    }
    if(index == 3) {                         // Create a time sort
      await dewuCollection
      .where({
        kind: this.data.titles
      })
      .orderBy('ctime'.'desc') 
      .get()
      .then(res= > {
        this.setData({
          goods: res.data
        })
      })
    }
    if(index == 4) {                         // Pop up the filter layer
      this.setData({ 
        show: true})},else if(index == 2 && this.data.flag == 1) {    // Price ascending sort
      await dewuCollection
      .where({
        kind: this.data.titles
      })
      .orderBy('price'.'asc') 
      .get()
      .then(res= > {
        this.setData({
          goods: res.data,
          flag: -1}}}})),Copy the code

Set a flag property with the default value of -1. If flag==-1, click the descending order and set flag==1. If flag==1, click the descending order and set flag==-1.

Screening of sorting

  pick(e) {
    let flag = e.currentTarget.dataset.flag
    let index = e.currentTarget.dataset.index
    let cd = this.data.human[index].kind
    if(flag==index) {
      this.setData({
        activeIndex1: -1.flag1: -1.cd1: ' '})}else {
      this.setData({
        activeIndex1: index,
        flag1: index,
        cd1: cd
      }) 
    }
  },
Copy the code

Screen to reset

  replace() {    // Click the reset button to return all filters to their default values
    this.setData({
      flag1: -1.activeIndex1: -1.flag2: -1.activeIndex2: -1.flag3: -1.activeIndex3: -1.cd1: ' '.cd2: ' '.cd3: 0.cd4: 10000000,}}),Copy the code

(num: Infinity) is not allowed to be declared in data (num: Infinity), where Infinity does not apply, optimizations are currently declared as constants.

Confirmation screen

  async ischeck() {   // Click ok button to filter the display results
    let cd3 = Number(this.data.cd3)
    let cd4 = Number(this.data.cd4)==0?1000000:Number(this.data.cd4)
    let index = Number(this.data.index)
    if(this.data.cd1! =' ' && this.data.cd2! =' ') {await dewuCollection
    .where({
      kind: this.data.titles,
      sex: this.data.cd1,
      brand: this.data.cd2,
      price: _.gt(cd3).and(_.lt(cd4)),
    })
    .get()
    .then(res= > {
      this.setData({
        goods: res.data,
        show: false,})})return}},Copy the code

The difficulties in discharge pit

    <van-grid-item use-slot wx:for="{{shoes}}" data-item="{{item}}" 
    wx:key="index" data-id="{{item._id}}" bindtap="gotoDetail"></van-grid-item>
    
      gotoDetail(e) {
    // console.log(e);
    wx.navigateTo({
      url: '/pages/buy_page/page/detail/detail? id='+e.currentTarget.dataset.id,
    })
  },      
Copy the code

Jump to the details page and retain the corresponding data item. In this case, the _id of each item is assigned to the data-id. Only when the ids are equal, the data corresponding to the _id can be jumped and accepted.

        <van-grid-item class="{{activeSizeIndex==index? 'size-active':''}}" 
        use-slot wx:for="{{size}}" wx:key="index" data-flag="{{flag}}" 
        data-index="{{index}}" bindtap="pickSize">
          <view class="size">
            <text id="p3">{{item.size}}</text>
            <text id="p4">${{item. Price}}</text>
          </view>
        </van-grid-item>
        
         pickSize(e) {
            let flag = e.currentTarget.dataset.flag
            let index = e.currentTarget.dataset.index
            if(flag==index) {
              this.setData({
                activeSizeIndex: -1.flag: -1})}else {
              this.setData({
                activeSizeIndex: index,
                flag: index
              }) 
            }
          },
Copy the code

Click the size to select and change the text, click this item again to reset the style, click other items to deselect, select the clicked item. In this case, double control is performed by setting an additional flag and combining index.

      <view 
      wx:for="{{kindNav}}"
      wx:key="index"
      data-index="{{index}}"
      data-type="{{item.type}}"
      bindtap="changeKinds"
      class="scroll-view-left-item {{activeNavIndex == index? 'active': ''}}">
        <text>{{item.text}}</text>
      </view>
      
    changeKinds(e) {
        console.log(e);
        let {index, type} = e.currentTarget.dataset;
        console.log(index, type);//index refers to the index of the recommended brand. Type and kind.js campType
        this.setData({
          activeNavIndex: index,
        })
        let kinds = []
        this.data.kindall.forEach(kind= > {
          if(kind.camptype == type) {
            kinds.push(kind)
          }
        })
        this.setData({
          kindItem: kinds,
        })
      }
Copy the code

Bind type and kind. campType, assign the current item index to activeNavIndex when clicking on the item change, store all data items with Kindall, use foreach loop to traverse kindall, Type campType ==type into an array called KINDS, then setData.

  deleteSearchHistory() {
    wx.showModal({
      content: 'Confirm clearing history'.success: (res) = > {
        if(res.confirm) {
          this.setData({
            historyList: []
          })
        }
      }
    })
    wx.removeStorageSync('value')},Copy the code

Not only is the historyList set to empty, but wx.removeStorageSync is used to clear the cache from the local store.

tips

When writing your own projects, use console.log() to print and track data changes. Check out the documentation for W3CSchool, wechat development, and vant-downloadp.

The source code

Source code of this project

conclusion

The process of writing the project was a challenge for me. After all, it was the first time for me to focus on the cooperative project, and the bugs encountered in the project were annoying, but I felt a great sense of achievement after I kept writing the function. I am very grateful to the teachers and classmates who helped me in writing the project. If you like this article or see something helpful here, please like it at 😺! At the same time, I also hope to see the article you can give me some advice, looking forward to discussing with you to learn wechat small program! (Check out πŸ‘‰ to learn more.)