删除图示的代码,之后来到pages文件夹下,删除logs文件夹
来到全局文件下,找到app.wxss,将默认样式全部删除
来到全局文件下,找到app.js,将默认代码全部删除
之后输入wx-app创建新代码,这里我更换了主题,黑屏看着有点难受
之后找到首页index.wxml,删除里面的所有代码
找到index.wxss,删除里面的所有代码
这里就不演示图片了
来到index.json文件下,添加这一行代码:
// An highlighted block
"navigationBarTitleText": "优购首页"
在文件主目录下新建以下五个文件夹,效果如下图
目录名 | 作用 |
---|---|
styles | 存放公共样式 |
components | 存放组件 |
lib | 存放第三⽅库 |
utils | 帮助库 |
request | 接口帮助库 |
页面名称 | Value |
---|---|
⾸⻚ | index |
分类⻚⾯ | category |
商品列表⻚⾯ | goods_list |
商品详情⻚⾯ | goods_detail |
购物⻋⻚⾯ | cart |
收藏⻚⾯ | collect |
订单⻚⾯ | order |
搜索⻚⾯ | search |
个⼈中⼼⻚⾯ | user |
意⻅反馈⻚⾯ | feedback |
登录⻚⾯ | login |
授权⻚⾯ | auth |
结算⻚⾯ | pay |
找到app.json文件,在pages数组中新增需要的代码项
// An highlighted block
"pages": [
"pages/index/index",
"pages/category/index",
"pages/goods_list/index",
"pages/goods_detail/index",
"pages/cart/index",
"pages/collect/index",
"pages/order/index",
"pages/user/index",
"pages/feedback/index",
"pages/search/index",
"pages/login/index",
"pages/auth/index",
"pages/pay/index"
],
新建一个icons文件夹,用于存放tabbar图标
在app.json中创建一个tabbar数组,在list中写属性
"tabBar": {
"color": "",
"selectedColor": "",
"backgroundColor": "",
"position": "bottom",
"borderStyle": "black",
"list": [
{
"pagePath": "",
"text": "",
"iconPath": "",
"selectedIconPath": ""
}
]
},
来到全局配置文件app.wxss,书写以下代码:
这里要特别注意,wxss不支持通配符*
page,
view,
text,
swiper,
swiper-item,
image,
navigator {
padding: 0;
margin: 0;
box-sizing: border-box;
}
/* 主题颜色 */
page {
/* 定义主题颜色 */
--themeColor: #d32677;
font-size: 28rpx;
}
如果要在组件中使用全局定义的主题颜色,就需要使用var(–themeColor)
在此,项目需求是点击搜索框可以实现跳转到搜索页面功能
找到之前新建的components文件夹,新建一个组件文件夹Search
使用微信开发小助手的新建component功能建立好四个基本文件
在Search.wxml中建立一个搜索框,实现跳转到搜索页面:
<view class="search_input">
<navigator open-type="navigate" url="/pages/search/index"><text class="iconfont icon-shoucang1"></text>搜索</navigator>
</view>
来到index.json文件下,新增使用组件代码
{
"usingComponents": {"Search":"/components/Search/Search"},
}
之后我们就在index.wxml中使用该Search组件
<view class="xcx_index">
<!-- 搜索框开始 -->
<Search></Search>
<!-- 搜索框结束 -->
</view>
这里需要对外网的资源进行请求,我找的是itheima的
使用wx-request快速的建立一个请求,在此我已经在我的小程序中把它加入了白名单,当然也可以直接不校验https
为了避免回调地狱的问题,使用Promise技术
来到我之前创建的request文件夹,新建一个index.js文件,书写下列代码:
export const request = (params) => {
return new Promise((resolve, reject) => {
wx.request({
...params,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
}
});
})
}
回到首页的那个index.js,在onload生命周期函数中使用它
// An highlighted block
import { request } from "../../request/index.js"
Page({
onLoad: function(options) {
request({ url: "https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata" })
.then(result => {
this.setData({
swiperList: result.data.message
})
})
},
})
之后就是引用轮播图了,使用wx:for渲染图片:
<swiper autoplay="{{true}}" indicator-dots="{{true}}" circular="{{true}}" interval="2000" indicator-active-color="#497">
<swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
<navigator>
<image mode="widthFix" src="{{item.image_src}}"></image>
</navigator>
</swiper-item>
</swiper>
与上述差不多的思路,从接口拿到数据之后渲染到页面上
把发送请求的函数单独写在与onload平级中,然后只需要在onload中使用this调用发送请求的函数
onLoad: function(options) {
this.getCateList();
}
getCateList() {
request({ url: "https://api-hmugo-web.itheima.net/api/public/v1/home/catitems" }).then(result => {
this.setData({
CateList: result.data.message
})
})
},
页面渲染部分是使用wx:for,这里不过多演示代码了
分为标题页和图片页,我需要分别的渲染,大致思路还是拿到数据之后渲染,非常简单
分类界面嘛,少不了搜索功能,引用组件就行,参考2步骤
要满足左右两侧都能滚动而互不干扰的效果,就需要用到一个组件scroll-view,参考完文档之后,最适合我的是scroll-y属性
这里要在wxss中设置一下flex布局!
.fenlei_index .cates_container {
height: calc( 100vh - 90rpx);
display: flex;
}
.fenlei_index .cates_container .left_menu {
flex: 2;
}
.fenlei_index .cates_container .right_content {
flex: 5;
}
index文件代码:
<scroll-view scroll-y class="left_menu">
<view class="menu_item" wx:for="{{leftMenu}}" wx:key="*this">
{{item}}
</view>
</scroll-view>
<scroll-view scroll-y class="right_content">
</scroll-view>
思路还是和之前一样,调接口拿数据
默认拿到的是第一项的数据。
Page({
data: {
leftMenu: [],
rightContent: [],
currentindex: 0
},
onLoad: function(options) {
this.getCates();
},
getCates() {
request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/categories"
}).then(res => {
this.Cates = res.data.message
let leftMenu = this.Cates.map(v => v.cat_name)
let rightContent = this.Cates[0].children
this.setData({
leftMenu,
rightContent
})
})
},
})
回到index,使用wx:for渲染数据
这里涉及到了Tab栏切换的相关知识,给左侧菜单绑定一个事件,把当前的索引号传进去,让右侧数据根据传过来的索引号更新数据
//index代码
<view bind:tap="handleItemTap" data-index="{{index}}">
//js代码
handleItemTap(e) {
const newindex = e.currentTarget.dataset.index
let rightContent = this.Cates[newindex].children
this.setData({
currentindex: newindex,
rightContent
})
},
在一个tab栏内对右侧界面进行上下滑动时,切换至下一个tab栏,会出现右侧界面不置顶的bug,使用scroll-top解决
<scroll-view scroll-top="{{scrollTop}}" scroll-y class="right_content">
</scroll-view>
在data中定义这个scrollTop,在每一次渲染右侧页面时,给scrollTop赋值为0即可
使用缓存技术(本地存储)能够让用户第二次打开小程序的速度提升
web中的本地存储和 小程序中的本地存储的区别:
1 写代码的方式不一样
web: localStorage.setItem("key","value");localStorage.getItem("key")
小程序中: wx.setStorageSync("key", "value"); wx.getStorageSync("key")
2:类型转换
web: 不管存入的是什么类型的数据,最终都会先调用一下 toString(),把数据变成了字符串再存入进去
小程序: 不存在类型转换的这个操作 存什么类似的数据进去,获取的时候就是什么类型
在onload生命周期函数书写代码:在上述4步骤中已经定义了getCates()方法
onLoad: function(options) {
//获取本地存储数据
const Cates = wx.getStorageSync("cates");
//如果没有
if (!Cates) {
this.getCates();
} else {
//有数据
if (Date.now() - Cates.time > 10000) {
this.getCates();
} else {
this.Cates = Cates.data
let leftMenu = this.Cates.map(v => v.cat_name)
let rightContent = this.Cates[0].children
this.setData({
leftMenu,
rightContent
})
}
}
},
改进getCates()方法,在里面加上获取数据后存入本地存储代码:
wx.setStorageSync("cates", { time: Date.now(), data: this.Cates });
这里我的业务功能是:点击步骤6的分类商品,跳转到具体的商品界面
回到第6步写好的navigator,在url属性加上参数
//跳转到商品列表页面时带上后台的id参数
<navigator url="/pages/goods_list/index?cid={{item2.cat_id}}">
商品列表页面当然也少不了搜索功能,引用组件就行,参考2步骤
这里再一次使用到了自定义组件
找到之前新建的components文件夹,新建一个组件文件夹Tabs
使用微信开发小助手的新建component功能建立好四个基本文件
分析结构,需要一个title区域和content区域
1.titie区域使用wx:for渲染,并且绑定点击事件,传递索引参数
2.使用三元表达式判断选中状态
3.content区域留下一个作用域插槽slot即可
<view class="tabs">
<view class="tabs_title">
<view bind:tap="handleTap" data-index="{{index}}" wx:for="{{tabs}}" wx:key="id" class="title_item {{item.isActive?'active':''}}">{{item.value}}</view>
</view>
<view class="tabs_content">
<slot ></slot>
</view>
</view>
来到需要使用tab栏的文件,修改json设置
"usingComponents": {
"Search": "/components/Search/Search",
"Tabs": "/components/Tabs/Tabs"
},
"navigationBarTitleText": "商品列表"
在wxml引入Tabs组件,定义待传递参数
<Tabs tabs="{{tabs}}">
</Tabs>
来到js文件,书写待传递参数
Page({
/**
* 页面的初始数据
*/
data: {
tabs: [{
id: 0,
value: "综合",
isActive: true
},{
id: 1,
value: "销量",
isActive: false
}, {
id: 2,
value: "价格",
isActive: false
}]
},
})
来到子组件的js文件,使用properties接收父组件参数
properties: {
tabs: {
type: Array,
value: []
}
},
来到子组件的js文件,使用methods定义父组件函数
methods: {
handleTap(e) {
const { index } = e.currentTarget.dataset
this.triggerEvent("tabsitemchange", { index })
}
}
wxml代码:
<Tabs bind:tabsitemchange="handleTabsChange" tabs="{{tabs}}">
</Tabs>
来到父组件的js文件,与onload函数平级书写函数
拿到子组件传递过来的索引号,拿到本函数定义的tabs数组数据,遍历,如果索引号与遍历索引相等,则更改选中状态,重新赋值tabs
handleTabsChange(e) {
const { index } = e.detail
let { tabs } = this.data;
tabs.forEach((element, i) => {
i === index ? element.isActive = true : element.isActive = false
});
this.setData({
tabs
})
},
由左侧图片和右侧文字说明组成
这里因为使用了Tab栏切换,所以我需要使用block和wx:if对tabs的选中状态进行判断,来决定显示哪一部分的内容
<block wx:if="{{tabs[0].isActive}}">0</block>
<block wx:elif="{{tabs[1].isActive}}">1</block>
<block wx:else="{{tabs[2].isActive}}">2</block>
定义数据
data: {
goodslist:[]
}
然后从后台拿到数据,这里使用ES7的async await
async getGoodsList() {
const res = await request({ url: "/goods/search", data: this.queryParams })
this.setData({
goodslist: res.goods
});
},
在数组索引0渲染页面:
<view class="first_tab">
<navigator wx:for="{{goodslist}}" wx:key="goods_id" class="goods_item">
<!-- 左侧 -->
<view class="img">
<image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'https://ww1.sinaimg.cn/large/007rAy9hgy1g24by9t530j30i20i2glm.jpg'}}" />
</view>
<!-- 右侧 -->
<view class="goods_info">
<view class="goods_name">{{item.goods_name}}</view>
<view class="goods_price">¥{{item.goods_price}}</view>
</view>
</navigator>
</view>
使用了很多新知识,在这里记录一下:
//这四行代码均是为了一个功能而生:文字最大显示两行
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
wx.showToast({
title: '',
icon: 'none',
image: '',
duration: 1500,
mask: false,
});
这个功能是指:用户上滑页面 滚动条触底 开始加载下一页数据
定义滚动条触底事件:
onReachBottom() {},
定义有关参数:
queryParams: {
query: "",
cid: "",
pagenum: 1,
pagesize: 10,
},
totalpage: 1,
判断还有没有下一页数据
/*
1 获取到总页数
总页数 = Math.ceil(总条数 / 页容量 pagesize)
2 获取到当前的页码 pagenum
3 判断一下 当前的页码是否大于等于 总页数
*/
async getGoodsList() {
//省略一行拿数据代码
const total = res.total
this.totalpage = Math.ceil(total / this.queryParams.pagesize)
this.total = total
//数据请求回来 要对data中的数组进行拼接 而不是全部替换
this.setData({
goodslist: [...this.data.goodslist, ...res.goods]
});
}
onReachBottom() {
if (this.queryParams.pagenum >= this.totalpage) {
//假如没有下一页数据 弹出一个提示
//展示提示框
wx.showToast({
title: '没有更多啦-.-',
});
} else {
//还有下一页数据 来加载下一页数据
//当前的页码+1
this.queryParams.pagenum++;
this.getGoodsList()
}
},
来到需要下拉刷新的json文件,添加以下配置
{
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}
来到js文件,添加下拉事件
onPullDownRefresh: function() {
//清空数据
this.setData({
goodslist: []
})
//归位当前页码
this.queryParams.pagenum = 1;
//重新获取数据
this.getGoodsList()
},
为了增加用户体验,在获取数据是,加上等待框
这里我选择在request文件夹的index.js中设置,保证每一次使用请求时都能有加载框,在请求完成后隐藏加载框,使用wx.hideLoading
//同时发送异步代码的次数
let ajaxtime = 0
export const request={params}=>{
//调用一次请求就+1
ajaxtime++
//显示加载中效果
wx.showLoading({
title: "Loading-.-",
mask: true
});
return new Promise((resolve,reject)=>{
wx.request({
//新增请求完成函数
complete: () => {
ajaxtime--
if (ajaxtime === 0) {
wx.hideLoading();
}
},
})
})
}
这里需要在商品列表页面中的navigator标签传递一个url参数,并且跳转到商品详情界面
<navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}">
修改商品详情的json文件,改变title值
{
"usingComponents": {},
"navigationBarTitleText": "商品详情"
}
事先定义商品细节对象,在js文件中的onload函数,使用options接收navigator标签传递的url参数
data: {
goodsobj:{}
},
onLoad: function (options) {
const goods_id=options.goods_id
this.getGoodsDetail(goods_id)
},
定义上述的getGoodsDetail方法,发请求拿数据,赋值商品细节对象
const res = await request({url:"/goods/detail",data:{goods_id}})
this.setData({
goodsobj:res
})
基本布局是由一个swiper轮播图,商品价格&商品信息,图文详情,以及下方的购物工具栏
依然是使用拿到的服务器数据来渲染页面
这里对布局的代码省略
在联系客服、分享、购物车三个功能处,需要使用button进行功能呈现,但是我的布局采用了view,如果要修改非常麻烦,因此我想到加上一个隐藏的按钮
⭐在此仅举一个例子:
<view class="tool_item">
<view class="iconfont icon-kefu"></view>
<view>联系客服</view>
//隐藏按钮
<button open-type="contact"></button>
</view>
CSS代码:
.tool_item {
//给父容器开相对定位
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 90rpx;
}
.tool_item button {
//给按钮开绝对定位
position: absolute;
//完全透明覆盖
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
给轮播图片swiper-item绑定一个bind:tap函数,并且使用自定义属性传递图片
<swiper-item data-url="{{item.pics_mid}}" bind:tap="handlePreviewImage">
来到js文件,与onload平级,定义这样一个函数,并且需要定义一个全局商品数据,因为data中拿不到服务器发送请求后获得的数据
//全局商品数据
GoodsInfo: {},
handlePreviewImage(){
const urls = this.goodsInfo.pics.map(v=>v.pics_mid)
const current = e.currentTarget.dataset.url
wx.previewImage({
current,
urls
})
};
将14.2步骤购物车按钮写的view标签改为navigator,指定跳转界面
但是这里要特别注意,navigator默认跳转至非tabbar界面,我们的购物车很明显是tabbar界面,因此需要指定open-type="switchTab"
<navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
<view class="iconfont icon-gouwuche"></view>
<view >购物车</view>
</navigator>
点击加入购物车按钮,实现在购物车界面看到添加的物品功能
首先给view标签绑定一个bind:tap事件:
<view bind:tap="handleCartAdd" class="tool_item btn-cart">
来到js文件,与onload平级,定义这样一个函数
1.首先从本地存储中查询是否有物品添加到购物车中,获取到一个cart数组
2.对该cart数组进行索引查询
3.如果结果=-1,证明该商品未被添加,给商品设置数量为1,push到cart数组
4.如果结果为其他值,证明该商品被查询出来,让该商品数量+1
5.把结果返回给本地存储,并且加上一个showToast
handleCartAdd() {
let cart = wx.getStorageSync("cart") || [];
let index = cart.findIndex(v => v.goods_id === this.GoodsInfo.goods_id)
if (index === -1) {
this.GoodsInfo.num = 1
cart.push(this.GoodsInfo)
} else {
cart[index].num++;
}
wx.setStorageSync("cart", cart);
wx.showToast({
title: '加入成功-.-',
icon: 'success',
mask: true,
});
},
这个就比较简单,给按钮绑定一个函数即可
<button bind:tap="handlePut" type="primary" plain="{{true}}">+添加收货地址</button>
I.首先需要拿到用户的授权状态。
II.如果授权结果是空,那么就要诱导用户打开授权界面
III.如果授权结果不为空,那么我们就可以获取用户的收货地址了
IV.将获取的信息放到本地存储中
在utils文件夹下新建一个js文件,用于存放函数
暴露三个wx-函数:
export const getSetting = () => {
return new Promise((resolve, reject) => {
wx.getSetting({
});
})
}
export const chooseAddress = () => {
return new Promise((resolve, reject) => {
wx.chooseAddress({
});
})
}
export const openSetting = () => {
return new Promise((resolve, reject) => {
wx.openSetting({
});
})
}
在购物车的js文件中引入
import { getSetting, chooseAddress, openSetting } from "../../utils/asyncWX"
//防止ES7语法报错
import regeneratorRuntime from "../../lib/runtime/runtime"
async handlePut() {
const res = await getSetting()
const scopeAddress = res.authSetting["scope.address"]
if (scopeAddress === false) {
await openSetting()
}
const address = await chooseAddress()
wx.setStorageSync("address", address);
}
功能:如果本地存储有地址信息,就渲染地址信息,本地存储内没有地址信息,就显示获取地址的按钮
//页面渲染完成获取本地存储的地址
data:{
address:{}
}
onShow() {
const address = wx.getStorgeSync("address")
this.setData({
address,
})
}
回到wxml文件,利用wx:if进行判断
<view wx:if="{{!address.userName}}" class="address_btn"></view>
<view wx:else class="user_info_row"></view>
根据后台返回的数据address进行渲染,较为简单
左侧是否选中框;中间物品图片;右侧物品描述和价格;右下方购买件数与加减按钮
回到之前的商品详情(13步骤),在点击添加购物车按钮后,再传递一个选中属性
handleCartAdd() {
//省略部分代码
...
this.GoodsInfo.checked = true
...
}
这里就需要获得(步骤17)本地存储的购物车数据以动态渲染
data: {
address: {},
cart: {}
},
onShow() {
const cart = wx.getStorageSync("cart");
const address = wx.getStorageSync("address");
this.setData({
address,
cart
})
}
wx:for循环返回的cart对象即可
这里需要对选中的商品进行价格计算,因此需要if进行判断
来到js文件,创建一个函数,用于计算选中的商品总价格
1.获取本地存储的cart数组,遍历返回商品是否全选
2.对数组进行遍历,先筛选掉未被选中的商品,再对剩下的商品进行计算价格,计算件数
3.设置回上述计算的值
setCart(cart) {
wx.setStorageSync("cart", cart);
//every数组方法会遍历会接收一个回调函数,那么每一个回调函数都返回true,那么every方法就会返回true
const allChecked = cart.length ? cart.every(v => v.checked) : false
let totalprice = 0
let totalnum = 0
cart.forEach(element => {
if (element.checked) {
totalprice += element.num * element.goods_price
totalnum += element.num
}
});
this.setData({
cart,
totalprice,
totalnum,
allChecked
})
}
在页面加载完成的时候需要调用这个函数
给每个复选框绑定一个单击事件,并且传递商品的id值,便于js文件拿到来修改对应商品的选中状态
<checkbox-group data-id="{{item.goods_id}}" bindchange="handleItemChange">
来到js文件,新建这个函数
handleItemChange(e) {
const goods_id = e.currentTarget.dataset.id
let { cart } = this.data
let index = cart.findIndex(v => v.goods_id === goods_id)
//对选中状态进行取反
cart[index].checked = !cart[index].checked
//调用上述定义的价格计算函数
this.setCart(cart)
},
给全选复选框绑定一个函数单击事件,从data数据中拿到购物车数据与全选状态,让全选状态取反,并且购物车所有选中项均与全选状态相同
<checkbox-group bindchange="handleItemAllChange">
<checkbox checked="{{allChecked}}"/>全选
</checkbox-group>
来到js文件,编写这个函数
handleItemAllChange() {
let { cart, allChecked } = this.data;
allChecked = !allChecked;
cart.forEach(v => v.checked = allChecked);
//调用计算函数
this.setCart(cart)
},
给加减按钮绑定一个单击事件函数,为了区分是加功能还是减功能,添加一个data-operation属性,并且传递商品的id号以修改
<view bind:tap="handleNum" data-operation="{{-1}}" data-id="{{item.goods_id}}"">-</view>
<view bind:tap="handleNum" data-operation="{{1}}" data-id="{{item.goods_id}}">+</view>
来到js文件,定义函数
handleNum(e){
const {operation,id} = e.currentTarget.dataset;
let {cart} = this.data
let index = cart.findIndex(v=>v.goods_id = id)
cart[index].num+=operation
this.setCart(cart)
}
参考18.3:在utils文件夹下js文件中,书写弹出提示框,确认框函数
export const showModal = (content) => {
return new Promise((resolve, reject) => {
wx.showModal({
title: 'Tip-.-',
content: content,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
},
});
})
}
export const showToast = (title) => {
return new Promise((resolve, reject) => {
wx.showToast({
title: title,
icon: 'none',
duration: 1500,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
},
});
})
}
⭐如果此时数量值小于1,我们就要弹出一个删除商品的提示,并且将数量锁为1
引用22.6定义的两个函数和ES7语法校验
来到js文件,在之前定义的handleNum函数增加下列代码
handleNum(e){
const {operation,id} = e.currentTarget.dataset;
let {cart} = this.data
let index = cart.findIndex(v=>v.goods_id = id)
cart[index].num+=operation
if (cart[index].num < 1) {
cart[index].num = 1
const res = await showModal("Do you want to remove it?")
if (res.confirm) {
cart.splice(index, 1)
this.setCart(cart)
}
}
this.setCart(cart)
}
这里我们需要获取到两个数据,一个是地址,一个是购物车里面的物品,如果两者的值有一个为空,那就需要返回这一次请求
<view bind:tap="handlePay" class="order_pay_wrap">
结算 ({{totalnum}})
</view>
js函数功能较简单,这里介绍一下wx.navigateTo,跳转界面,但是保留当前界面
wx.navigateTo({
url: '/pages/pay/index',
});
与购物车界面布局相似,删除一些不必要的wxml结构,wxss样式,js函数即可
这里需要注意,在页面加载完毕之时,只需要对选中商品进行结算
onShow({
const address = wx.getStorageSync("address") || [];
let cart = wx.getStorageSync("cart");
let checkedCart = cart.filter(v=>v.checked)
let totalprice = 0
let totalnum = 0
checkedCart.forEach(v=>{
totalprice += v.num*v.goods_price
totalnum += v.num
})
this.setData({
address,
checkedCart,
totalprice,
totalnum,
})
})
哪些人 哪些帐号 可以实现微信支付?
1 企业帐号
2 企业帐号的小程序后台中必须给开发者添加上白名单
首先给支付按钮绑定一个函数
此时,如果我们用户没有授权,就需要跳转到授权界面拿到用户的授权信息,存放与token中
授权界面本身元素较少,这里我选择使用一个按钮来进行这个功能,绑定一个函数,来处理用户的授权操作
<button open-type="getUserInfo" type="warn" plain="{{true}}" bindgetuserinfo="handlegetuserinfo">
来到js界面,对定义的函数进行处理,需要拿到用户的各项信息
这里需要引入之前定义的发请求函数、代码简化文件
import { login } from "../../utils/asyncWX"
import { request } from "../../request/index"
import regeneratorRuntime, { isGeneratorFunction } from "../../lib/runtime/runtime"
Page({
async handlegetuserinfo(e) {
const { signature, iv, encryptedData, rawData } = e.detail
const { code } = await login();
const loginParams = { signature, iv, encryptedData, rawData, code }
//发送请求获取token
const { token } = await request({ url: "/users/wxlogin", data: (loginParams), method: "post" })
wx.setStorageSync("token", token);
//回退一个界面
wx.navigateBack({
delta: 1
});
},
})
有了token之后,就可以发请求来创建一个微信支付订单了
给支付按钮绑定一个单击响应函数
<view class="order_pay_wrap" bind:tap="handleOrderPay">
来到js文件,编辑函数:
async handleOrderPay() {}
这里需要在代码简化区提前准备一个发起微信支付请求的函数
来到utils文件夹,导出一个函数:
//pay参数接收获取到的支付信息,使用扩展运算符插入请求中
export const requestPayment = (pay) => {
return new Promise((resolve, reject) => {
wx.requestPayment({
...pay,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
}
});
})
}
从本地存储中拿到token,如果没有跳转到授权页面
async handleOrderPay() {
const token = wx.getStorageSync("token");
if (!token) {
wx.navigateTo({
url: '/pages/order/index',
});
}
}
授权过后,会有一系列的用户信息存储,这一步就是拿到它们用于发送请求
async handleOrderPay() {
//接24.4函数
...
const header = { Authorization: token }
const order_price = this.data.totalprice
const consignee_addr = this.data.address
const checkedCart = this.data.checkedCart
//定义商品空数组
let goods = []
checkedCart.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 }
}
async handleOrderPay() {
//接24.5函数
...
//创建订单 获取订单编号
const { order_number } = await request({ url: "/my/orders/create", method: "POST", data: orderParams, header })
//发起预支付接口
const { pay } = await request({ url: "/my/orders/req_unifiedorder", method: "POST", data: { order_number }, header })
//调出微信支付
await requestPayment(pay);
//查询后台订单状态
const res = await request({ url: "/my/orders/chkOrder", method: "POST", data: { order_number } });
//弹出支付成功对话框
await showToast({ title: "支付成功" });
}
从本地存储中拿到购物车数组,留下未选中商品,返回给本地存储的cart即可
async handleOrderPay() {
//接24.6函数
...
let newCart = wx.getStorageSync("cart");
newCart = newCart.filter(v => !v.checked);
wx.setStorageSync("cart", newCart);
}
async handleOrderPay() {
//接24.7函数
...
wx.navigateTo({
url: '/pages/order/index',
});
}
首先,用户未登录时,在个人中展示一个登录按钮,点击这个按钮会跳转到登录授权界面
<navigator class="user_btn" url="/pages/login/index">登录</navigator>
登录授权界面较为简单,一个按钮,绑定一个open-type和一个函数
来到js文件,操作这个函数,目的是为了把用户信息存放在本地存储中
handleuserinfo(e) {
const { userInfo } = e.detail
wx.setStorageSync("userinfo", userInfo);
wx.navigateBack({
delta: 1
});
}
在页面一渲染完成后,从本地存储中拿到用户信息
Page({
data: {
userinfo: {}
},
onShow() {
const userinfo = wx.getStorageSync("userinfo");
this.setData({
userinfo
})
}
})
老套路,从data拿数据,插值表达式赋值,很简单
让订单页面在跳转的时候携带参数,便于Tab栏切换页面信息
<navigator url="/pages/auth/index?type=1">
<view class="order_name">全部订单</view>
</navigator>
<navigator url="/pages/auth/index?type=2">
<view class="order_name">待付款</view>
</navigator>
<navigator url="/pages/auth/index?type=3">
<view class="order_name">待收货</view>
</navigator>
参考8.3~8.8步骤,这里省略传参等一系列行为
这里需要回到商品详情界面
getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
首先,在onShow函数中,无法使用options拿到页面加载时的参数
这里就需要使用getCurrentPages获取当前页面栈的实例
onShow: function() {
//获取页面栈
let pages = getCurrentPages();
//操作本级页面数据
let currentPage = pages[pages.length - 1]
//拿到原本在onLoad函数中的options
let options = currentPage.options
const goods_id = options.goods_id
this.getGoodsDetail(goods_id)
},
在data中新建一个变量,用于保存商品的收藏状态
data: {
isCollect: false
},
书写onShow函数中定义的getGoodsDetail函数
//原函数中补充以下代码:
let collect = wx.getStorageSync("collect") || [];
let isCollect = collect.some(v => v.goods_id === this.GoodsInfo.goods_id)
this.setData({
isCollect
})
给原收藏icon绑定一个单击响应事件,通过isCollect渲染不同状态的样式
<text bind:tap="handleAddshoucang" class="iconfont {{isCollect?'icon-shoucang1':'icon-shoucang'}}"></text>
来到js文件,书写定义的函数
handleAddshoucang(){
let isCollect = false
let collect = wx.getStorageSync("collect") || [];
let index = collect.findIndex(v => v.goods_id === this.GoodsInfo.goods_id)
//已查询到索引,说明已收藏,删除商品
if (index !== -1) {
collect.splice(index, 1)
isCollect = false
} else {
//未查询到索引,说明未收藏,添加商品
collect.push(this.GoodsInfo)
isCollect = true
}
wx.setStorageSync("collect", collect);
this.setData({
isCollect
})
}
来到个人中心界面,找到收藏的商品栏,绑定一个navigator,跳转到收藏界面
<navigator url="/pages/collect/index">
<view class="his_num">{{collectnums}}</view>
<view class="his_name">收藏的商品</view>
</navigator>
接下来回到js文件,定义一个数据,用于显示收藏的商品数量
data: {
collectnums: 0
},
onShow() {
//获取收藏本地存储数据
const collect = wx.getStorageSync("collect") || [];
this.setData({
collectnums: collect.length
})
}
参考8.3~8.8步骤,这里省略传参等一系列行为
在onShow函数中定义获取本地存储中的collect,赋值给在data中定义的collect数组
点击商品,跳转到对应的商品详情界面。带上商品id参数即可
<navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" wx:for="{{collect}}" wx:key="goods_id" class="goods_item">
由一个搜索框和内容框组成,当搜索框中有内容时,显示一个取消按钮,点击清空所有数据
I 无数据
II 有数据
防止多次改变输入框的搜索内容而多次发送请求,需要使用定时器防抖思维。给输入框绑定一个bindinput,在里面实现这个功能
<input value="{{inputValue}}" bindinput="handleinput" placeholder="write goods in here!" />
js文件,在data中定义一个商品数组,同层级下定义一个定时器,并且书写一个发请求函数,需要带上关键字参数
data: {
goods: [],
//搜索框内容
inputValue:''
},
Timeid: -1,
handleinput(e) {
const { value } = e.detail
if (!value.trim()) {
this.setData({
goods: [],
})
return
}
//清楚防抖定时器
clearTimeout(this.Timeid)
//开启防抖定时器
this.Timeid = setTimeout(() => {
//定时调用搜索请求
this.qsearch(value)
}, 1000)
},
async qsearch(query) {
const res = await request({ url: "/goods/qsearch", data: { query } })
this.setData({
goods: res
})
},
非常简单,拿到data的goods数组,循环拿到里面的商品名称即可,并且带上导航功能,直接跳转到对应的商品:
url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
给按钮绑定一个单击事件
<button bind:tap="handlecancal">取消</button>
js文件书写函数功能,重置数组和搜索框的内容
handlecancal() {
this.setData({
inputValue: "",
goods: []
})
}
调用wx.uploadFile可以上传文件,url表示文件上传至的路径,filePath表示本机的文件路径,name表示上传文件的命名,success表示成功之后的回调函数
这里仅仅只记录这个功能,图片上传功能我用于了意见反馈,但是微信小程序中内置了这个功能
至此所有功能开发完毕,点击右上角的上传按钮
在这之前,一定要去除不校验合法域名的勾选
点击上传按钮,输入版本号和备注,上传完毕后
来到公众平台,点击版本管理,再点击提交审核即可