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.csv
or.json
Files, 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:
<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.)