商品详情页面
总体思路
ts
搭建商品详情静态页面
修改商品详情的标题 json文件
商品card组件传递 商品id
商品详情页面获取 商品id 发送请求获取数据
获取商品id
商品card组件页面:传递商品id
ts
card组件中 item中绑定单击事件 设置自定义属性获取商品id 设置回调函数
card组件中设置方法
methods:{
// 点击获取商品的详情页面
getDetail(event){
// 获取商品id
event....id
// 路由跳转
微信小程序中的路由没有 params ,只有query
query: /pages/goodsDetails/goodsDetails?goodsId=${goodsId}
params: /pages/goodsDetails/goodsDetails/1/2 (微信小程序没有这种写法)
}
}
xml
<view class="item" wx:for="{{list}}" wx:key="id" bind:tap="goDetetail" data-goodsid="{{item.id}}" >
ts
methods: {
//点击商品进入商品详情页
goDetetail(event){
//商品的ID
const goodsId = event.currentTarget.dataset.goodsid;
//点击商品进入详情
wx.navigateTo({
url: `/pages/goodsDetail/goodsDetail?goodsId=${goodsId}`,
})
}
}
获取商品详情数据
ts
封装获取商品详情的接口api 参数[goodsId]
定义获取商品详情的函数,函数中发送请求
商品详情onLoad时获取数据
onLoad(option){
option 获取商品的goodsId
存储 商品goodsID
调用 获取商品详情 函数
存储 商品详情 数据
}
渲染商品详情页面数据
js
//获取商品的详情的数据
async getGoodsInfo() {
const result = await reqGoodsDetail(this.data.goodsId);
if (result.code == 200) {
this.setData({
goods: result.data
})
}
},
onLoad(options){
//存储商品的ID
this.setData({
goodsId: options.goodsId
});
//获取商品的详情
this.getGoodsInfo();
},
动作面板模块
点击立即购买或加入购物车,弹出卡片填写祝福语和数量
ts
动作面板的显示与隐藏
van-action-sheet组件 show属性 show="{{showSheet}}" 控制显示与隐藏
van-action-sheet组件 bind:click-overlay="{{overplay}}"事件 点击其他区域隐藏动作面板
加入购物车和立即购买 分别控制显示和隐藏购买数量区域
定义一个数据状态type:"",用于区分 加入购物车 和 立即购买
点击加入购物车时,修改type数据状态
点击立即购买时,修改type数据状态
使用wx:if 控制计数条的显示与隐藏
定义 overplay 函数
点击遮罩层后侧,关闭弹窗显示
修改showSheet数据状态,关闭动作面板,清空type数据状态
xml
<!-- show:控制动作面板显示与隐藏布尔值 -->
<van-action-sheet show="{{showSheet}}" bind:click-overlay="overPlay">
<!-- 动作面板内部展示商品内容 -->
<view class="sheet_wrapper">
<!-- 商品信息 -->
<view class="goods_item flex">
<image class="img mid" src="{{goods.imageUrl}}"></image>
<view class="info flex">
<view class="title">{{goods.name}}</view>
<view class="buy">
<view class="price">
<view class="symbol">¥</view>
<view class="num">{{goods.price}}</view>
</view>
<!-- 立即购买的时候不显示数量添加 -->
<view wx:if="{{type==='cart'}}" class="buy-btn" style="minWidth:400rpx;">
<van-stepper value="{{skuNum}}" bind:change="skuNumChange"/>
</view>
</view>
</view>
</view>
<!-- 祝福语 -->
<view class="time-wraper">
<view class="title">祝福语</view>
<textarea model:value="{{remarks}}" class="form-textarea" placeholder="必填,写上您的祝福语,给心爱的他(她)送上你的祝福(请勿填写特殊符号或表情符号)" name="textarea" />
</view>
<!-- 确定按钮 -->
<view class="sheet-footer-btn">
<van-button block type="primary" round bind:tap="confirm">确定</van-button>
</view>
</view>
</van-action-sheet>
js
//点击动作面板后面遮罩层
overPlay(){
//控制面板隐藏
this.setData({
showSheet: false,
type: ''
})
},
动作面板数据收集
ts
定义数据状态 skuNum:1 收集计数器商品数量(默认是1)
在 计数器 组件上 通过 bind:change = "skuNumChange" 事件的回调函数中的参数[event] event.detail 获取当前输入的值
定义 计数器的回调函数 获取当前计数器的值
skuNumChange(event){
event.detail() // 获取当前计数器最新的值
存储商品的数量
}
定义数据状态 祝福语 remark:''
在textarea组件中设置 model:value={{}} 设置双向绑定获取祝福语
xml
<!-- 立即购买的时候不显示数量添加 -->
<view wx:if="{{type==='cart'}}" class="buy-btn" style="minWidth:400rpx;">
<van-stepper value="{{skuNum}}" bind:change="skuNumChange"/>
</view>
<!-- 祝福语 -->
<view class="time-wraper">
<view class="title">祝福语</view>
<textarea model:value="{{remarks}}" class="form-textarea" placeholder="必填,写上您的祝福语," name="textarea" />
</view>
js
// 计步器函数
skuNumChange(event){
//存储商品的数量
this.setData({
skuNum: event.detail
})
},
立即购买和加入购物车
ts
立即购买按钮 绑定单击事件 bind:tap="buynowHandle"
修改showSheet数据状态
加入购物车和立即购买 分别控制显示和隐藏购买数量区域
定义一个数据状态type:"",用于区分 加入购物车 和 立即购买
点击加入购物车时,修改type数据状态
点击立即购买时,修改type数据状态
使用wx:if 控制计数条的显示与隐藏
定义 overplay 函数
点击遮罩层后侧,关闭弹窗显示
修改showSheet数据状态,关闭动作面板,清空type数据状态
封装 加入购物车 接口api 参数[商品id,商品数量,祝福语]
在确定按钮 绑定单击事件 bind:tap="confim"
confim(){
区分是加入购物车还是立即购买,通过type区分
如果是加入购物车,调用 加入购物车函数 addGoodsToCart()
如果是立即购买,调用 立即购买函数 buyImmediate
}
定义 加入购物车 函数 addGoodsToCart(){}
addGoodsToCart(){
未登录时(通过是否存在token),跳转到登陆页面,提示信息 跳转到编辑页面,
编辑页面点击确定后,登陆成功
登录后,返回到商品页面
登录后,执行下边逻辑
判断是否 填写了 祝福语
有,放行
没有,使用vant中的toast组件 做信息提示
toast组件是 js 代码,同时也需要在 wxml页面引入 van-toast组件标签
(van-toast组件标签放在任何位置都可以,一般放在最后)
整理参数
发加入购物车请求
关闭动作面板,清空 计数器数量 和 祝福语
加入购物车成功 提示信息(选)
}
定义 立即购买 函数 goOrderByImme
goOrderByImme(){
解构 商品id 和 祝福语
路由跳转到订单页面order,同时传递 商品id 和 祝福语,通过query的方式传递参数
清空 商品id 和 祝福语 数据
}
xml
<view class="bottom_right flex">
<view class="add_shop_cart" bind:tap="addCartHandle">加入购物车</view>
<view class="buy_now" bind:tap="buynowHandle">立即购买</view>
</view>
<!-- 确定按钮 -->
<view class="sheet-footer-btn">
<van-button block type="primary" round bind:tap="confirm">确定</van-button>
</view>
ts
import {reqGoodsDetail ,reqAddOrUpdateCart} from '../../api/index'
import Toast from '@vant/weapp/toast/toast';
//立即购买按钮回调
buynowHandle() {
//显示控制面板展示商品的详情
this.setData({
showSheet: true,
type: "buy"
})
},
//加入购物车按钮的回调
addCartHandle() {
//显示动作面包
this.setData({
showSheet: true,
type: 'cart'
})
},
//点击动作面板后面遮罩层
overPlay(){
//控制面板隐藏
this.setData({
showSheet: false,
type: ''
})
},
//对应按钮的确定回调
confirm() {
// 判断有没有祝福语
if (this.data.remarks.trim() == '') {
Toast.fail('请填写祝福语');
return;
}
//判断用户是否登录
const token = wx.getStorageSync('TOKEN');
//登录
if (token) {
// 加入购物车
if (this.data.type == 'cart') {
// 调用加入购物车函数
this.addGoodsToCart();
} else {
// 调用立即购买的函数
this.goOrderByImme();
}
} else {
//未登录去登录(不能加入购物车,不能支付)
wx.navigateTo({
url: '/pages/login/login',
})
}
},
// 加入购物车方法
async addGoodsToCart() {
//整理参数
const {
goodsId,
skuNum,
remarks
} = this.data;
//发请求
//加入购物车成功
await reqAddOrUpdateCart(goodsId, skuNum, remarks);
Toast.success('加入购物车成功');
//关闭动作面板
this.setData({
showSheet: false,
skuNum: 1,
remarks: ''
})
},
// 跳转订单页面函数
goOrderByImme(){
//立即购买的商品的ID与祝福语
const {goodsId,remarks} = this.data;
wx.navigateTo({
url: `/pages/order/order?goodsId=${goodsId}&remarks=${remarks}`,
})
//清除祝福语
this.setData({
remarks:''
})
}
商品详情代码
/pages/goodsDetail.js
js
import {reqGoodsDetail ,reqAddOrUpdateCart} from '../../api/index'
import Toast from '@vant/weapp/toast/toast';
Page({
data:{
goodsId: '', //商品的详情ID
goods: {}, //存储商品的详情的数据
showSheet: false, //控制动作面包显示与隐藏
type: '', //区分购物车立即购买
skuNum: 1, //收集计数器商品数量
remarks: '', //祝福语
},
onLoad(options){
//存储商品的ID
this.setData({
goodsId: options.goodsId
});
//获取商品的详情
this.getGoodsInfo();
},
//获取商品的详情的数据
async getGoodsInfo() {
const result = await reqGoodsDetail(this.data.goodsId);
if (result.code == 200) {
this.setData({
goods: result.data
})
}
},
//加入购物车按钮的回调
addCartHandle() {
//显示动作面包
this.setData({
showSheet: true,
type: 'cart'
})
},
//立即购买按钮回调
buynowHandle() {
//显示控制面板展示商品的详情
this.setData({
showSheet: true,
type: "buy"
})
},
// 计步器函数
skuNumChange(event){
//存储商品的数量
this.setData({
skuNum: event.detail
})
},
// 隐藏遮罩层
//点击动作面板后面遮罩层
overPlay(){
//控制面板隐藏
this.setData({
showSheet: false,
type: ''
})
},
// 点击确定按钮
//对应按钮的确定回调
confirm() {
// 判断有没有祝福语
if (this.data.remarks.trim() == '') {
Toast.fail('请填写祝福语');
return;
}
//判断用户是否登录
const token = wx.getStorageSync('TOKEN');
//登录
if (token) {
// 加入购物车
if (this.data.type == 'cart') {
// 调用加入购物车函数
this.addGoodsToCart();
} else {
// 调用立即购买的函数
this.goOrderByImme();
}
} else {
//未登录去登录(不能加入购物车,不能支付)
wx.navigateTo({
url: '/pages/login/login',
})
}
},
// 加入购物车方法
async addGoodsToCart() {
//整理参数
const {
goodsId,
skuNum,
remarks
} = this.data;
//发请求
//加入购物车成功
await reqAddOrUpdateCart(goodsId, skuNum, remarks);
Toast.success('加入购物车成功');
//关闭动作面板
this.setData({
showSheet: false,
skuNum: 1,
remarks: ''
})
},
// 跳转订单页面函数
goOrderByImme(){
//立即购买的商品的ID与祝福语
const {goodsId,remarks} = this.data;
wx.navigateTo({
url: `/pages/order/order?goodsId=${goodsId}&remarks=${remarks}`,
})
//清除祝福语
this.setData({
remarks:''
})
}
})
/pages/goodsDetail.wxml
xml
<!--pages/goodsDetail/goodsDetail.wxml-->
<view class="good_detail_container">
<!-- 商品大图 -->
<view class="banner_img">
<image class="img" src="{{goods.imageUrl}}" mode="aspectFit"></image>
</view>
<!-- 商品info -->
<view class="good_info">
<view class="price">
<view class="price_num">¥ {{goods.price}}</view>
<view class="price_origin_num">¥ {{goods.marketPrice}}</view>
</view>
<view class="title">{{goods.name}}</view>
<view class="desc">{{goods.packing}}</view>
</view>
<!-- 商品详情 -->
<view class="good_detail">
<image class="img" wx:for="{{goods.detailList}}" wx:key="index" src="{{item}}" mode="aspectFit" ></image>
</view>
<!-- 底部选项区域 -->
<view class="good_footer flex">
<view class="bottom_left flex">
<!-- 声明式导航:点击图标跳转首页 open-type跳转方式:默认navigate-->
<navigator url="/pages/home/home" open-type="reLaunch">
<view class="icon_wrap flex">
<van-icon name="wap-home" color="#666" size="22px"></van-icon>
<text>首页</text>
</view>
</navigator>
<!-- 声明式导航:点击图标跳转购物车 -->
<navigator url="/pages/shopcart/shopcart" open-type="reLaunch">
<view class="icon_wrap flex">
<van-icon name="shopping-cart" color="#666" size="22px"></van-icon>
<text>购物车</text>
</view>
</navigator>
</view>
<view class="bottom_right flex">
<view class="add_shop_cart" bind:tap="addCartHandle">加入购物车</view>
<view class="buy_now" bind:tap="buynowHandle">立即购买</view>
</view>
</view>
<!--弹窗区域 -->
<!-- show:控制动作面板显示与隐藏布尔值 -->
<van-action-sheet show="{{showSheet}}" bind:click-overlay="overPlay">
<!-- 动作面板内部展示商品内容 -->
<view class="sheet_wrapper">
<!-- 商品信息 -->
<view class="goods_item flex">
<image class="img mid" src="{{goods.imageUrl}}"></image>
<view class="info flex">
<view class="title">{{goods.name}}</view>
<view class="buy">
<view class="price">
<view class="symbol">¥</view>
<view class="num">{{goods.price}}</view>
</view>
<!-- 立即购买的时候不显示数量添加 -->
<view wx:if="{{type==='cart'}}" class="buy-btn" style="minWidth:400rpx;">
<van-stepper value="{{skuNum}}" bind:change="skuNumChange"/>
</view>
</view>
</view>
</view>
<!-- 祝福语 -->
<view class="time-wraper">
<view class="title">祝福语</view>
<textarea model:value="{{remarks}}" class="form-textarea" placeholder="必填,写上您的祝福语,给心爱的他(她)送上你的祝福(请勿填写特殊符号或表情符号)" name="textarea" />
</view>
<!-- 确定按钮 -->
<view class="sheet-footer-btn">
<van-button block type="primary" round bind:tap="confirm">确定</van-button>
</view>
</view>
</van-action-sheet>
</view>
<!-- 请提示组件 -->
<van-toast id="van-toast" />
/pages/goodsDetail.json
json
{
"usingComponents": {
"van-action-sheet": "@vant/weapp/action-sheet/index",
"van-stepper": "@vant/weapp/stepper/index",
"van-icon": "@vant/weapp/icon/index",
"van-button": "@vant/weapp/button/index",
"van-toast": "@vant/weapp/toast/index"
}
}
订单详情页面
购物车页面结算按钮 和 商品详情页面立即购买按钮 都可以跳转到订单详情页面
总体思路
ts
新建 订单详情静态页面
修改订单详情页面标题
封装接口
ts
//获取订单详情页-立即购买的商品的信息
export const reqBuyNowGoodsInfo = (goodsId, blessing) => request({
url: `/mall-api/order/buy/${goodsId}?blessing=${blessing}`
})
//获取订单收货人地址信息
export const reqAdress = () => request({
url: `/mall-api/userAddress/getOrderAddress`
})
获取获取订单详情数据
ts
封装 获取订单详情数据(通过立即购买按钮跳转) 接口 参数[goodsId,祝福语]
export const reqBuyNowGoodsInfo = () =>{
}
订单详情页面定义 商品id 和祝福语数据状态
订单详情页面的onLoad(options){} options参数中获取 query 参
通过options参数中的数据判断是从 立即购买按钮 来的还是从 购物车结算按钮 来的
onLoad(options){
如果options.remarks存在,代表是从立即购买按钮来的
存储 商品id和祝福语
调用 发送获取立即购买商品信息
}
定义 获取订单详情数据 函数(通过立即购买确定按钮来的) getOrderListByImme(){}
getOrderListByImme(){
获取 商品id和 祝福语 参数
调用 获取立即购买商品id 的接口[需要商品id和祝福语]
获取 立即购买商品信息 存储商品数据,同时渲染数据
}
定义 获取订单详情数据 函数(通过购物车结算按钮来的) getOrderListBySettle(){}
getOrderListBySettle(){
发送获取购物车数据请求
存储购物车商品数据 cartList:[]
订单页面渲染购物车商品数据
调用计算商品总价函数
}
ts
import { reqCart, reqBuyNowGoodsInfo, reqAdress, reqSubmit, reqPayInfo, reqPayStatus } from '../../api/index'
Page({
data: {
userAddress: {}, //收货人的地址
goodsId: 0, //存储立即购买而来的商品的ID
remarks: '', //存储立即购买的携带祝福语
cartList: [], //展示商品的数组
totalPrice: 0, //存储商品的总价
buyName: '', //收集订购人的名字
buyPhone: '', //订购人手机号码
isShowPop: false, //控制pop弹窗显示与隐藏
minDate: new Date().getTime(), //日期显示最小的时间
},
onLoad: function (options) {
// 携带祝福语:立即购买而来
if (options.remarks) {
// 存储祝福语与立即购买商品的ID
this.setData({
goodsId: options.goodsId,
remarks: options.remarks
});
// 调用 获取立即购买商品 方法
this.getGoodsByImme()
} else {
// 调用 获取购物车商品 方法
this.getGoodsByCart();
}
},
// 获取 立即购买商品 方法
async getGoodsByImme(){
// 解构出 商品id和祝福语
const {goodsId,remarks} = this.data;
// 发送请求 获取立即购买的商品的信息
const result = await reqBuyNowGoodsInfo(goodsId, remarks);
// 如果响应状态码为200
if (result.code == 200) {
// 存储数据状态
this.setData({
cartList: result.data.cartVoList, //商品信息
totalPrice: result.data.totalAmount, //立即购买商品总价
})
}
},
// 获取 购物车商品 方法
async getGoodsByCart (){
// 发送获取购物车请求
const result = await reqCart();
// 判断响应码是否为200
if (result.code == 200) {
// 存储购物车的数据
this.setData({
cartList: result.data
});
// 获取购物车数据完毕以后计算总价
this.computedTotal();
}
},
})
计算购物车商品价格
ts
定义购物车商品总价 totalPrice,渲染总价数据
定义计算商品总价的函数 computedTotal(){
使用数组的 reduce方法计算购物车的总价
更新totalPrice数据状态
}
ts
// 计算购物车商品总价
computedTotal(){
const total = this.data.cartList.reduce((prev,next)=>{
return prev += next.count * next.price;
},0)
//更新总价
this.setData({
totalPrice:total
})
},
日期选择并渲染(moment插件)
ts
在van-cell标签上绑定单击事件showPop,同时定义数据状态isShowPopup,用于控制popup组件显示与隐藏
定义 showPop 函数,修改isShowPopup数据状态,控制popUp组件的显示与隐藏
在van-popup标签上绑定单击事件bind:click-overlay="close",控制popup组件的隐藏
在van-datetime-picker标签上绑定事件bind:cancel="close",控制popup组件隐藏
在van-datetime-picker标签上绑定事件bind:confirm="confirm",收集确定的事件
在van-datetime-picker设置 min-date属性控制最小的时间,同时定义数据状态获取当前的时间(使用JS原生 new Date)
定义 close 函数,设置数据状态isShowPopup
定义 confirm 函数,获取确定的事件参数是event对象 event.detail代表当前时间戳
通过moment插件将时间戳转为年月日格式,并存储
关闭popup组件显示
渲染日期数据
通过moments或datejs将时间戳转为年月日
npm install moment
import moment from 'moment'
moment(时间戳).format('YYYY-MM-DD')
项目代码
xml
<!-- 日期选项卡 -->
<van-popup round bind:click-overlay="close" position="bottom" custom-style="height: 50%" show="{{isShowPop}}">
<van-datetime-picker bind:confirm="confirm" min-date="{{minDate}}" bind:cancel="close" type="date" title='今天是个好日子' />
</van-popup>
ts
// 弹出日期选择卡
showPop(){
this.setData({
isShowPop: true
})
},
//收集确定的预期时间
confirm(event) {
this.setData({
// 把当前时间的字符串 处理为 响应格式
deliveryDate: moment(event.detail).format('YYYY-MM-DD'),
// 关闭pop
isShowPop: false
})
},
//遮罩层与取消
close() {
this.setData({
isShowPop: false
})
},
默认地址
ts
默认地址和添加收货人 使用wx:if控制显示和隐藏,通过userInfo.name不存在,则显示添加收货人,否则显示默认地址
点击添加收货人
跳转到地址列表页面(没有则显示新建地址按钮)
xml
<view class="address_card">
<!-- 用户没有默认地址 -->
<navigator wx:if="{{!userAddress.name}}" class="flex nav" hover-class="navigator-hover" url="/packageA/pages/address/address" >
<van-icon size="22px" name="add" />
<view class="title_text">添加收货人地址</view>
</navigator>
<!-- 用户有默认地址,显示默认地址 -->
<navigator wx:else class="flex nav nav_address" hover-class="navigator-hover" url="/packageA/pages/address/address">
<view class="address_content">
<view class="address_title">{{userAddress.fullAddress}}</view>
<view class="address_full">{{userAddress.tagName}}</view>
<view class="info">
<text class="username">{{userAddress.name}}</text>
<text class="phone">{{userAddress.phone}}</text>
</view>
</view>
<van-icon color="#BBBBBB" name="arrow" size="22px" />
</navigator>
<view class="top_line"></view>
</view>
结算支付
ts
封装提交订单的接口 获取订单编号 参数[请求体]
请求地址
请求方法
收货人的id
订购人的姓名
订购人手机号
商品列表
收货日期
订单详情页面的结算按钮绑定单击事件 submitOrder(){}
定义结算按钮提交订单函数 获取订单号 submitOrder(){}
submitOrder(){
收集参数
发送请求,获取数据result
如果result数据存在,则调用 获取支付参数信息 函数,获取支付参数信息
}
收集 结算按钮提交订单函数 参数 const data={}
定义 订购人姓名数据状态同时使用 model:value双向绑定
定义 订购人手机号同时使用 model:value双向绑定
定义 期望送达日期 通过confim函数收集
祝福语同时使用 model:value双向绑定
商品数组 数据状态已经存储了
wx.requestPayment({})
根据订单号,获取支付参数信息
封装 获取支付参数信息 接口 参数[订单号]
定义 获取支付参数信息 函数 参数[订单号]
发送 获取支付参数信息 请求,获取支付参数数据 result
如果 result.code === 200 发起微信支付
wx.requestPayment(object)
支付成功的回调
success(){
查询支付状态(真实情况),调用 查询支付状态 接口
如果响应的状态是成功的
跳转到支付成功页面
新建一个支付成功页面
支付成功页面放一个图片和一个按钮,按钮跳转首页,使用wx.reLaunch方法跳转到tabBar首页
}
封装 查询支付状态 接口 参数[订单号] 返回订单的支付状态(向后台发送请求)
报错问题
ts
包报错,把构建后的文件夹删除,把node-models文件夹删除,然后重新安装包,重新构建就可以了
详细代码
ts
import { reqCart, reqBuyNowGoodsInfo, reqAdress, reqSubmit, reqPayInfo, reqPayStatus } from '../../api/index'
import moment from 'moment';
Page({
/**
* 页面的初始数据
*/
data: {
userAddress: {}, //收货人的地址
goodsId: 0, //存储立即购买而来的商品的ID
remarks: '', //存储立即购买的携带祝福语
cartList: [], //展示商品的数组
totalPrice: 0, //存储商品的总价
buyName: '', //收集订购人的名字
buyPhone: '', //订购人手机号码
isShowPop: false, //控制pop弹窗显示与隐藏
minDate: new Date().getTime(), //日期显示最小的时间
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 携带祝福语:立即购买而来
if (options.remarks) {
// 存储祝福语与立即购买商品的ID
this.setData({
goodsId: options.goodsId,
remarks: options.remarks
});
// 调用 获取立即购买商品 方法
this.getGoodsByImme()
} else {
// 调用 获取购物车商品 方法
this.getGoodsByCart();
}
},
// 获取 立即购买商品 方法
async getGoodsByImme(){
// 解构出 商品id和祝福语
const {goodsId,remarks} = this.data;
// 发送请求 获取立即购买的商品的信息
const result = await reqBuyNowGoodsInfo(goodsId, remarks);
// 如果响应状态码为200
if (result.code == 200) {
// 存储数据状态
this.setData({
cartList: result.data.cartVoList, //商品信息
totalPrice: result.data.totalAmount, //立即购买商品总价
})
}
},
// 获取 购物车商品 方法
async getGoodsByCart (){
// 发送获取购物车请求
const result = await reqCart();
// 判断响应码是否为200
if (result.code == 200) {
// 存储购物车的数据
this.setData({
cartList: result.data
});
// 获取购物车数据完毕以后计算总价
this.computedTotal();
}
},
// 计算购物车商品总价
computedTotal(){
const total = this.data.cartList.reduce((prev,next)=>{
return prev += next.count * next.price;
},0)
//更新总价
this.setData({
totalPrice:total
})
},
// 获取用户 收货地址
async getUserAddress() {
const result = await reqAdress();
if (result.code == 200) {
this.setData({
userAddress: result.data
})
}
},
// 弹出日期选择卡
showPop(){
this.setData({
isShowPop: true
})
},
//收集确定的预期时间
confirm(event) {
this.setData({
// 把当前时间的字符串 处理为 响应格式
deliveryDate: moment(event.detail).format('YYYY-MM-DD'),
// 关闭pop
isShowPop: false
})
},
//遮罩层与取消
close() {
this.setData({
isShowPop: false
})
},
// 结算按钮回调
async submitOrder(){
//整理参数
const data = {
userAddressId: this.data.userAddress.id, //收货人地址的ID
buyName: this.data.buyName, //订购人姓名
buyPhone: this.data.buyPhone, //订购人手机号码
deliveryDate: this.data.deliveryDate, //期望送达日期
remarks: this.data.remarks, //祝福语
cartList: this.data.cartList, //商品数组
}
// 发送请求
const result = await reqSubmit(data);
if (result.code == 200) {
console.log("订单号是 ",result.data)
//获取支付参数信息
this.getPayParams(result.data);
}
},
//获取wx支付相关的参数
async getPayParams(orderNo) {
//获取支付参数
const result = await reqPayInfo(orderNo);
console.log('获取的订单参数是 ',result.data);
if (result.code == 200) {
//发起微信支付:你们没有办法真机测试
wx.requestPayment({
nonceStr: result.data.nonceStr,
package: result.data.package,
paySign: result.data.paySign,
timeStamp: result.data.timeStamp,
signType: result.data.signType,
//支付成功的回调
async success() {
// 询问后台服务器支付的结果在进行下一步处理
const result = await reqPayStatus(orderNo);
console.log("询问服务支付结果 ",result)
if (result.code == 200) {
wx.navigateTo({
url: '/pages/order/paySuccess/paysuccess',
})
}
}
})
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
//不管是购物车而来、还是立即购买而来都需要收货人的地址
this.getUserAddress();
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
order.wxml
xml
<!-- 订单详情 -->
<view class="order_detail_container">
<!-- 地址管理 -->
<view class="address_card">
<!-- 用户没有默认地址 -->
<navigator wx:if="{{!userAddress.name}}" class="flex nav" hover-class="navigator-hover" url="/packageA/pages/address/address" >
<van-icon size="22px" name="add" />
<view class="title_text">添加收货人地址</view>
</navigator>
<!-- 用户有默认地址,显示默认地址 -->
<navigator wx:else class="flex nav nav_address" hover-class="navigator-hover" url="/packageA/pages/address/address">
<view class="address_content">
<view class="address_title">{{userAddress.fullAddress}}</view>
<view class="address_full">{{userAddress.tagName}}</view>
<view class="info">
<text class="username">{{userAddress.name}}</text>
<text class="phone">{{userAddress.phone}}</text>
</view>
</view>
<van-icon color="#BBBBBB" name="arrow" size="22px" />
</navigator>
<view class="top_line"></view>
</view>
<!-- 订购人信息 -->
<van-cell-group>
<van-field label-class="filed-label" label="订购人姓名" placeholder="订购人的姓名" model:value="{{buyName}}"></van-field>
<van-field label-class="filed-label" model:value="{{buyPhone}}" label="订购人手机号" type="number" maxlength="11" placeholder="订购人的手机号" />
<van-cell title="期望送达日期" value="{{ deliveryDate}}" is-link bind:tap="showPop" />
<van-cell title="订单备注" use-label-slot>
<textarea model:value="{{remarks}}" slot="label" class="form-textarea" placeholder="美满的祝福语啊!" name="textarea" />
</van-cell>
</van-cell-group>
<!-- 商品列表信息 -->
<view class="goods_wrapper">
<view class="goods_list">
<view class="goods_item" wx:for="{{cartList}}" wx:key="goodsId">
<view class="img">
<image src="{{item.imageUrl}}" />
</view>
<view class="content">
<view class="goods_title">{{item.name}}</view>
<view class="goods_price">{{item.price}}</view>
</view>
<view class="num">
<van-stepper value="{{item.count}}" />
</view>
</view>
</view>
<!-- 支付方式 -->
<view class="time-wraper flex">
<image src="https://img02.hua.com/m/images/m_payment_wxzf.png" />
<view class="title">支付方式</view>
<van-checkbox value="{{true}}"></van-checkbox>
</view>
</view>
<!-- 底部结算 -->
<view class="footer flex">
<view class="left">¥{{totalPrice}}</view>
<viwe class="right" bind:tap="submitOrder">去结算</viwe>
</view>
<!-- 日期选项卡 -->
<van-popup round bind:click-overlay="close" position="bottom" custom-style="height: 50%" show="{{isShowPop}}">
<van-datetime-picker bind:confirm="confirm" min-date="{{minDate}}" bind:cancel="close" type="date" title='今天是个好日子' />
</van-popup>
</view>
order.json
json
{
"navigationBarTitleText": "订单详情",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index",
"van-field": "@vant/weapp/field/index",
"van-cell": "@vant/weapp/cell/index",
"van-stepper": "@vant/weapp/stepper/index",
"van-checkbox": "@vant/weapp/checkbox/index",
"van-popup": "@vant/weapp/popup/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-datetime-picker": "@vant/weapp/datetime-picker/index"
}
}
地址页面
用户地址列表
ts
封装 获取订单收货人地址接口 参数[token]
在订单详情页面的 onLoad(){} 中定义调用 订单收货人地址 函数 getAddress(){}
getAddress(){
函数中发送 获取订单收货人地址 接口
定义收货人地址 数据状态
渲染订单页面 收货人数据
}
定义获取地址列表数据函数
发送请求
定义响应式数据,存储数据
封装获取地址列表数据 接口api 参数[token]
地址列表页面的onShow()生命周期发送请求
调用获取地址列表函数
点击新建地址按钮
跳转到新增地址页面
使用wx:if和wx:else控制 空数据和地址列表的条件渲染
使用wx:for渲染地址列表数据
地址列表数据渲染
js
Page({
data: {
addressArr: [], //存储全部收件人地址
},
// 获取用户地址列表
async getUserAddress() {
const result = await reqAddressList();
if (result.code == 200) {
this.setData({
addressArr: result.data
})
}
},
// 编辑按钮回调
edit(event) {
const id = event.currentTarget.dataset.id;
wx.navigateTo({
url: `/packageA/pages/address/add/add?id=${id}`,
})
},
// 删除按钮回调
async del(event) {
const id = event.currentTarget.dataset.id;
const result = await reqDeleteUser(id);
if(result.code==200){
//再次获取全部收件人地址
this.getUserAddress();
}
},
//每一次页面出现的时候都获取一次地址列表的数据
onShow() {
//获取地址列表
this.getUserAddress();
},
})
xml
<view class="list-warpper" wx:if="{{addressArr.length}}">
<view class="title">我的收货地址</view>
<!-- 展示收件人的信息 -->
<view class="list-item flex" wx:for="{{addressArr}}" wx:key="id">
<view class="info">
<view class="address-info">
<text>{{item.address}}</text>
<text wx:if="{{item.isDefault==1}}" class="default-tag">默认</text>
</view>
<view class="user-info">
<view class="tag">{{item.tagName}}</view>
<text>{{item.name}} {{item.phone}}</text>
</view>
</view>
<view class="edit-address">
<van-icon bind:tap="edit" data-id="{{item.id}}" class="edit" name="edit" size="22px" color="#999" />
<van-icon data-id="{{item.id}}" bind:tap="del" name="delete" size="22px" color="#999" />
</view>
</view>
</view>
<!-- 当没有收件人的信息的时候展示 -->
<van-empty description="还没有收获地址,快去添加吧~" wx:else/>
<view class="footer">
<view class="btn">
<navigator url="/packageA/pages/address/add/add">
新建地址
</navigator>
</view>
</view>
新增地址页面
收集数据
ts
渲染静态页面和样式
收集数据:收集收件人姓名,收集收件人手机号,收集收件人地址,收件人详细地址,收件人默认地址,tagName
封装 添加收件人 接口api
双向绑定收件人姓名,手机号,收件人详细地址model:value
ts
data: {
name: '', //新增收件人的名字,
phone: '', //收件人的手机号码
provinceCode: '', //省的区域编号
cityCode: '', //市
districtCode: '', //区
address: '', //详细地址
tagName: '家', //标签
isDefault: 0, //默认地址
//展示地址:省/市/区
region: '',
// 存储 用户收货地址id
id: ''
},
xml
<form class="form-content">
<van-cell-group>
<!-- 收件人 -->
<van-field placeholder="收件人" label-class="filed-label" model:value="{{ name }}" label="收货人" />
<!-- 手机号 -->
<van-field placeholder="手机号" label-class="filed-label" model:value="{{ phone }}" label="手机号" maxlength="11" />
<!-- 微信小程序自带的组件,并非vant -->
<picker mode="region" bindchange="selectCity">
<van-field label-class="filed-label" label="地址" value="{{region}}" placeholder="省/市/区" />
</picker>
<van-field label-class="filed-label" model:value="{{ address }}" label="详细地址" placeholder="门牌号等(例如:10栋1001号)" />
<van-cell title="标签" title-width="94px">
<van-radio-group model:value="{{ tagName }}" direction="horizontal">
<van-radio name="家">家</van-radio>
<van-radio name="公司">公司</van-radio>
</van-radio-group>
</van-cell>
<van-cell title="是否设置为默认地址">
<switch checked="{{isDefault}}" bindchange="handler" />
</van-cell>
</van-cell-group>
</form>
获取省市区
ts
使用微信小程序组件picker组件:mode="region",绑定单击事件 bind:change="selectCity"
定义 selectCity事件回调(点击确定的时候触发)参数[event] event.detail 是省市区的编码
this.setData({
定义省编码:event.detail.code[0]
})
定义 省市区地址编码 region
this.setData({
region:event.detail.value.join('/')
})
picker组件中使用 vant-field组件填充内容
xml
<picker mode="region" bindchange="selectCity">
<van-field label-class="filed-label" label="地址" value="{{region}}" placeholder="省/市/区" />
</picker>
js
//picker的change事件
selectCity(event) {
//收集数据:请求携带
this.setData({
provinceCode: event.detail.code[0], //省的code
cityCode: event.detail.code[1], //市
districtCode: event.detail.code[2], //区
region: event.detail.value.join('/')
})
},
收集标签数据
ts
收集标签 (双向绑定)
使用van-radio标签的name属性设置 "家"或"公司" 属性值
使用van-radio-group设置model:value属性双向绑定,默认绑定的数值为单选name属性设置的属性值
xml
<van-cell title="标签" title-width="94px">
<van-radio-group model:value="{{ tagName }}" direction="horizontal">
<van-radio name="家">家</van-radio>
<van-radio name="公司">公司</van-radio>
</van-radio-group>
</van-cell>
收集默认状态
ts
使用微信小程序的 switch组件 绑定bind:change="handle"事件回调
定义 handle 函数 参数[event]
xml-dtd
<van-cell title="是否设置为默认地址">
<switch checked="{{isDefault}}" bindchange="handler" />
</van-cell>
ts
//收集开关的数据
handler(event) {
this.setData({
isDefault: event.detail.value ? 1 : 0
})
},
保存按钮
ts
点击确定按钮,发送请求绑定 bind:tap="save"
在 onLoad 生命周期函数中 通过存在id 判断是 更新操作还是新增操作
onLoad(options) {
// 如果id存在 则代表 编辑按钮 过来的
if (options.id) {
this.setData({
// 存储地址的id
id: options.id
});
// 获取 当前编辑按钮点击的 地址信息
this.getAddressById();
}
},
async save() {
如果 id 不存在则代表是新增地址
整理参数
发新增地址请求
成功返回数据
路由返回到地址列表中
地址列表中重新获取数据
}
ts
Page({
//保存按钮
async save() {
// 如果id存在,则代表更新按钮
if (this.data.id) {
var result = await reqUpdateAddress(this.data);
} else {
//添加
var result = await reqAddAddress(this.data);
}
//判断添加或者更新是否成功
if (result.code == 200) {
// 路由返回
wx.navigateBack();
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 如果id存在 则代表 编辑按钮 过来的
if (options.id) {
this.setData({
// 存储地址的id
id: options.id
});
// 获取 当前编辑按钮点击的 地址信息
this.getAddressById();
}
},
})
ts
订单详情页面,展示默认地址,把获取 收货人函数 放在 onShow(){} 钩子中
编辑用户地址
用户地址列表页面
ts
小程序的页面跳转只能带基本数据类型 path?key=1&name=auqa
所以只能通过传递一个id,然后通过这个id重新发送请求获取地址数据
在编辑按钮上设置自定义属性data-id={{id}},同时绑定单击事件 bind:tap="edit"
定义edit函数
路由跳转到新建地址页面,并传递id
编辑按钮绑定单击事件 edit
edit(event) {
// 获取点击对象的id
const id = event.currentTarget.dataset.id;
// 路由导航到编辑页面
wx.navigateTo({
url: `/packageA/pages/address/add/add?id=${id}`,
})
},
删除按钮绑定单击事件 del
async del(event) {
// 获取id
const id = event.currentTarget.dataset.id;
// 发送请求
const result = await reqDeleteUser(id);
if(result.code==200){
//再次获取全部收件人地址
this.getUserAddress();
}
},
新增用户页面
ts
封装 更新 地址列表数据 接口api
封装 通过id获取地址信息数据 接口api
定义 通过id获取地址信息数据 函数
getAddressById(){
发送 通过id获取地址信息数据 接口api 请求
存储数据
处理省市区名字拼接
}
新增地址页面的 onLoad(){} 钩子中获取 query中的id
如果 option.id 存在,则是 编辑按钮 过来的
调用 通过id获取地址信息数据 函数
在保存 save 函数中
根据 id是否存在判断是 更新操作还是新增操作
id存在,则发送更新请求
id不存在,则发送新增请求
请求响应成功,路由返回
wx.navigateBack();
ts
// pages/address/add/add.js
import {
reqAddAddress,
reqEditAddress,
reqUpdateAddress
} from '../../../../api/index';
Page({
async getAddressById() {
// 由于微信小程序路由跳转不能携带引用类型数据
// 因此如果是编辑而来,需要根据用户ID发请求获取更新用户地址信息
const result = await reqEditAddress(this.data.id);
if (result.code == 200) {
const {
name,
phone,
address,
tagName,
isDefault,
provinceCode,
cityCode,
districtCode,
provinceName,
cityName,
districtName
} = result.data;
this.setData({
name,
phone,
address,
tagName,
isDefault,
provinceCode,
cityCode,
districtCode,
region: `${provinceName}/${cityName}/${districtName}`
})
}
},
onLoad(options) {
// 如果id存在 则代表 编辑按钮 过来的
if (options.id) {
this.setData({
// 存储地址的id
id: options.id
});
// 获取 当前编辑按钮点击的 地址信息
this.getAddressById();
}
},
async save() {
// 如果id存在,则代表更新按钮
if (this.data.id) {
var result = await reqUpdateAddress(this.data);
} else {
//添加
var result = await reqAddAddress(this.data);
}
//判断添加或者更新是否成功
if (result.code == 200) {
// 路由返回
wx.navigateBack();
}
},
})
删除用户地址
用户地址列表
ts
封装 删除地址列表 接口api
在 地址列表的删除 按钮上设置 自定义属性 data-id 绑定单击事件 bind:tap="del"
del(event){
获取 id
发送删除地址列表请求
再次获取收件人地址
}
ts
// 删除按钮回调
async del(event) {
const id = event.currentTarget.dataset.id;
const result = await reqDeleteUser(id);
if(result.code==200){
//再次获取全部收件人地址
this.getUserAddress();
}
},
xml
<view class="edit-address">
<van-icon bind:tap="edit" data-id="{{item.id}}" class="edit" name="edit" size="22px" color="#999" />
<van-icon data-id="{{item.id}}" bind:tap="del" name="delete" size="22px" color="#999" />
</view>
微信小程序分包
ts
小程序的项目上限不能超过2M,微信不需要审核代码,直接打回
如果项目超出了2M,设置分包
注意:tabBar页面必须放在主包内
配置方法
ts
微信小程序 分包
配置方法
首先在根目录新建文件夹 packageA
把pages文件夹内的A页面移动到 packageA/pages中
删除app.json应用配置文件中的 page:{} 对应路由字段 A
在app.json 应用配置文件中添加 subpackages字段中,添加子包的路由A
"subpackages":[
{
"root":"packageA",
"pages":[
"pages/A"
]
}
]
在页面使用时,需要在路由地址上添加 root路径 例如:
<navigator wx:else class="flex nav nav_address" hover-class="navigator-hover" url="/packageA/pages/address/address">
注意:
独立分包:子包不能够使用全局静态资源
包大于2M真机就测试不了,需要使用分包