目录
一、点击结算按钮进行条件判断
用户点击了结算按钮之后,需要先后判断是否勾选了要结算的商品,是否选择了收货地址,是否登录。
在my-settle组件中,为结算按钮绑定点击事件处理函数:
.
在user.js中封装token数据,用于判断用户是否登录
在my-settle组件中,调用相关数据判断是否登录
在my-settle组件中的methods节点中声明settlement事件处理函数:
未选中商品点击结算时:
有地址,有商品,没有token登录时:
二、登录
本章主要设计登录页面my.vue的编写
1、实现登录和用户信息组件的按需展示
components下创建登录组件
创建userinfo用户信息组件
在my-user页面,使用两个组件
my.vue页面获取user.js中的token值,来实现判断是否登录
按需展示页面,没有登录的话跳转到用户登录页面
2、实现登录组件的基本布局
渲染my-login组件的UI结构
<template>
<view class="login-container">
<uni-icons type="contact-filled" size="100" color="#AFAFAF"></uni-icons>
<button type="primary" class="btn-login">一键登录</button>
<text class="tips-text">登录后尽享更多权益</text>
</view>
</template>
美化样式
<style lang="scss">
.login-container {
height: 750rpx;
background-color: #F8F8F8;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
&::after {
content: ' ';
display: block;
width: 100%;
height: 40px;
background-color: white;
position: absolute;
bottom: 0;
left: 0;
border-radius: 100%;
transform: translateY(50%);
}
.btn-login {
width: 90%;
border-radius: 100px;
margin: 15px 0;
background-color: #C00000;
}
.tips-text {
font-size: 12px;
color: gray;
}
}
</style>
3、点击登录获取微信用户的基本信息
先看获取token的接口,需要的参数,以及返回类型
为登录button按钮绑定,获取用户的基本信息
在method中声明事件处理函数
methods: {
//用户授权之后,获取用户信息
getUserProfile() {
uni.getUserProfile({
// 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
desc: '请求授权',
success: (res) => {
console.log(res)
// 这部分是更新用户信息的
// this.updateUserInfo(res.userInfo)
// this.getToken(res)
},
fail: (res) => {
return uni.showToast({
title: '用户取消了登录授权!',
duration: 1500,
icon: 'none',
})
}
})
}
}
成功获取用户信息
这里要注意,那vx开发者工具的此处调成低版本,不然只能在真机上演示
4、将用户的基本信息存储到vuex
在user.js中定义保存用户信息的参数userinfo,以及创建更新用户信息,保存用户信息的方法
在my-login页面调用持久化存储
5、获取token字符串
前面成功获取了后面4个,这里完成获取code功能
因为这个后端接口有时候会有问题,获取不到token,所以我们方便测试,直接把返回结果封装一个loginResult方便我们后续的开发
getToken(info) {
uni.login({
provider: 'weixin',
success: (res) => {
// console.log('login-res', res)
if (res && res.errMsg === 'login:ok') {
const query = {
code: res.code,
encryptedData: info.encryptedData,
iv: info.iv,
rawData: info.rawData,
signature: info.signature
}
console.log("query", query)
// const { data: loginResult } = uni.$http.post('/api/public/v1/users/wxlogin', query)
//获取token失败时候使用
const loginResult = {
"message": {
"user_id": 12,
"user_email_code": null,
"is_active": null,
"user_sex": "男",
"user_qq": "",
"user_tel": "",
"user_xueli": "本科",
"user_hobby": "",
"user_introduce": null,
"create_time": 1525402223,
"update_time": 1525402223,
"token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjIzLCJpYXQiOjE1NjQ3MzAwNzksImV4cCI6MTAwMTU2NDczMDA3OH0.YPt-XeLnjV-_1ITaXGY2FhxmCe4NvXuRnRB8OMCfnPo"
},
"meta": {
"msg": "登录成功",
"status": 200
}
}
console.log(loginResult)
if (loginResult.meta.status !== 200) {
return uni.showToast({
title: '登录失败!',
duration: 1500,
icon: 'none',
})
}
this.updateToken(loginResult.message.token)
uni.showToast({
title: '登录成功!',
duration: 1500,
icon: 'none',
})
this.navigateBack()
} else {
return uni.showToast({
title: '登录失败!',
duration: 1500,
icon: 'none',
})
}
},
fail: (err) => {
return uni.showToast({
title: '登录失败!',
duration: 1500,
icon: 'none',
})
}
})
}
一键登录,登录成功
成功返回token,以及后端请求用户信息
6、将token存储到vuex
在my-login组件中,把vuex中的updateToken方法映射到当前组件中使用
三、用户信息
1、实现用户头像昵称区域的基本布局
my-userinfo.vue页面
<template>
<view class="my-userinfo-container">
<!-- 头像和昵称区域 -->
<view class="top-box">
<image src="" class="avatar"></image>
<view class="nickname">xxx</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
.my-userinfo-container {
height: 100%;
background-color: #F4F4F4;
.top-box {
height: 400rpx;
background-color: #C00000;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.avatar {
width: 90px;
height: 90px;
border-radius: 45px;
border: 2px solid #FFF;
box-shadow: 0 1px 5px black;
}
.nickname {
font-size: 16px;
color: #FFF;
font-weight: bold;
margin-top: 10px;
}
}
}
</style>
2、渲染用户的头像和昵称
3、渲染第一个面板区域
UI结构
<!-- 面板区域 -->
<view class="panel-list">
<!-- 第1个面板 -->
<view class="panel">
<view class="panel-body">
<view class="panel-item">
<text>8</text>
<text>收藏的店铺</text>
</view>
<view class="panel-item">
<text>14</text>
<text>收藏的商品</text>
</view>
<view class="panel-item">
<text>18</text>
<text>关注的商品</text>
</view>
<view class="panel-item">
<text>84</text>
<text>组件</text>
</view>
</view>
</view>
</view>
美化样式
.panel-list {
padding: 0 10px;
position: relative;
top: -10px;
.panel {
background-color: white;
border-radius: 3px;
margin-bottom: 8px;
.panel-body {
display: flex;
justify-content: space-around;
.panel-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
padding: 10px 0;
font-size: 13px;
}
}
}
}
4、渲染第二个面板区域
UI结构
<!-- 面板区域 -->
<view class="panel-list">
<!-- 第1个面板 -->
<view class="panel">
<view class="panel-body">
<view class="panel-item">
<text>8</text>
<text>收藏的店铺</text>
</view>
<view class="panel-item">
<text>14</text>
<text>收藏的商品</text>
</view>
<view class="panel-item">
<text>18</text>
<text>关注的商品</text>
</view>
<view class="panel-item">
<text>84</text>
<text>组件</text>
</view>
</view>
</view>
<!-- 第二个面板 -->
<view class="panel">
<view class="panel-title">
我的订单
</view>
<view class="panel-body">
<view class="panel-item">
<image src="/static/icon2.png" class="icon"></image>
<text>待付款</text>
</view>
<view class="panel-item">
<image src="/static/icon1.png" class="icon"></image>
<text>待收货</text>
</view>
<view class="panel-item">
<image src="/static/icon3.png" class="icon"></image>
<text>退款/退货</text>
</view>
<view class="panel-item">
<image src="/static/icon4.png" class="icon"></image>
<text>全部订单</text>
</view>
</view>
</view>
</view>
美化
.panel-list {
padding: 0 10px;
position: relative;
top: -10px;
.panel {
background-color: white;
border-radius: 3px;
margin-bottom: 8px;
.panel-title {
line-height: 40px;
padding-left: 10px;
font-size: 15px;
border-bottom: 1px solid #F4F4F4;
}
.panel-body {
display: flex;
justify-content: space-around;
.panel-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
padding: 10px 0;
font-size: 13px;
.icon {
width: 35px;
height: 35px;
}
}
}
}
}
5、渲染第三个面板区域
UI结构
<!-- 第三个面板 -->
<view class="panel">
<view class="panel-list-item">
<text>收货地址</text>
<uni-icons type="arrowright" size="15"></uni-icons>
</view>
<view class="panel-list-item">
<text>联系客服</text>
<uni-icons type="arrowright" size="15"></uni-icons>
</view>
<view class="panel-list-item">
<text>退出登录</text>
<uni-icons type="arrowright" size="15"></uni-icons>
</view>
</view>
美化
.panel-list-item{
display: flex;
justify-content: space-between;
align-items: center;
font-size: 15px;
padding: 0 10px;
line-height: 45px;
}
6、实现退出登录功能
四、三秒后自动跳转
在购物车页面,当用户点击《结算》按钮时,如果用户没有登录,则3秒后自动跳转到登录页
1、三秒后自动跳转到登录页面
但是上面代码也存在问题,我们并没有给定时设定边界值,秒数会一直减,以及当下一次打开页面,seconds秒数不重置问题
改造delayNavigate方法:
// 延时导航到my页面
delayNavigate() {
this.seconds = 3
this.showTips(this.seconds)
setInterval(() => {
this.seconds--
if(this.seconds <= 0){
clearInterval(this.timer)
uni.switchTab({
url: '/pages/my/my'
})
return
}
this.showTips(this.seconds)
}, 1000)
},
2、登录成功后再返回之前的页面
在自动跳转到登录页面成功之后,把返回页面的信息存储到vuex中,从而方便登录成功之后,重新返回之前的页面
返回页面的信息对象,主要包括{ openType,from } 两个属性,其中openType表示以哪种方式导航回之前的页面;from表示之前页面的url地址
改造my-settle组件method节点中的delayNavigate方法,当成功跳转到my页面之后,将重定向的信息对象存储到vuex中:
// 延时导航到my页面
delayNavigate() {
this.seconds = 3
this.showTips(this.seconds)
setInterval(() => {
this.seconds--
if(this.seconds <= 0){
clearInterval(this.timer)
uni.switchTab({
url: '/pages/my/my',
success: () => {
this.updateRedirectInfo({
openType: 'switchTab',
from: '/pages/cart/cart'
})
}
})
return
}
this.showTips(this.seconds)
}, 1000)
},
登录成功,自动跳转到购物车页面
五、微信支付
1、微信支付的流程
2、创建订单
在settlement点击结算按钮绑定订单创建的方法 payOrder()
async payOrder() {
//1. 创建订单
//1.1 组织订单的信息对象:
const orderInfo = {
order_price: 0.01,
consignee_addr: this.addstr,
goods: this.cart.filter(x => x.goods_state).map(x => ({
goods_id: x.goods_id,
goods_number: x.goods_count,
goods_price: x.goods_price
}))
}
// 1.2 发起请求创建订单
const { data : res } = await uni.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create',
data: orderInfo,
method: 'POST',
header: {
'Authorization': this.token
},
})
console.log(res)
if(res.meta.status !== 200){
return uni.showToast({
title: '订单创建失败!',
duration: 1500,
icon: 'none',
})
}
// 1.3 得到服务器相应的“订单编号”
const orderNumber = res.message.order_number
console.log(orderNumber)
},
点击结算按钮,成功返回订单编号
3、订单预支付
改造my-settle组件的payOrder方法,实现订单预支付功能;
async payOrder() {
//1. 创建订单
//1.1 组织订单的信息对象:
const orderInfo = {
order_price: 0.01,
consignee_addr: this.addstr,
goods: this.cart.filter(x => x.goods_state).map(x => ({
goods_id: x.goods_id,
goods_number: x.goods_count,
goods_price: x.goods_price
}))
}
// 1.2 发起请求创建订单
const { data : res } = await uni.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create',
data: orderInfo,
method: 'POST',
header: {
'Authorization': this.token
},
})
console.log(res)
if(res.meta.status !== 200){
return uni.showToast({
title: '订单创建失败!',
duration: 1500,
icon: 'none',
})
}
// 1.3 得到服务器相应的“订单编号”
const order_number = res.message.order_number
// 2. 订单预支付
// 2.1发起请求获取订单的支付信息
console.log(order_number)
const { data : res2} = await uni.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder',
data: {
'order_number' : order_number
},
method: 'POST',
header: {
'Authorization': this.token
},
})
console.log(res2)
// 2.2预支付订单生成失败
if(res2.meta.status !== 200){
return uni.showToast({
title: '订单预支付失败!',
duration: 1500,
icon: 'none',
})
}
// 2.3得到订单支付相关的必要参数
const payInfo = res2.message.pay
console.log(payInfo)
// 3. 发起微信支付
},
4、发起微信支付
async payOrder() {
//1. 创建订单
//1.1 组织订单的信息对象:
const orderInfo = {
order_price: 0.01,
consignee_addr: this.addstr,
goods: this.cart.filter(x => x.goods_state).map(x => ({
goods_id: x.goods_id,
goods_number: x.goods_count,
goods_price: x.goods_price
}))
}
// 1.2 发起请求创建订单
const { data : res } = await uni.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create',
data: orderInfo,
method: 'POST',
header: {
'Authorization': this.token
},
})
console.log(res)
if(res.meta.status !== 200){
return uni.showToast({
title: '订单创建失败!',
duration: 1500,
icon: 'none',
})
}
// 1.3 得到服务器相应的“订单编号”
const order_number = res.message.order_number
// 2. 订单预支付
// 2.1发起请求获取订单的支付信息
console.log(order_number)
const { data : res2} = await uni.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder',
data: {
'order_number' : order_number
},
method: 'POST',
header: {
'Authorization': this.token
},
})
console.log(res2)
// 2.2预支付订单生成失败
if(res2.meta.status !== 200){
return uni.showToast({
title: '订单预支付失败!',
duration: 1500,
icon: 'none',
})
}
// 2.3得到订单支付相关的必要参数
const payInfo = res2.message.pay
console.log(payInfo)
// 3. 发起微信支付
// 3.1 调用uni.requestPayment() 发起微信支付
const [err, succ] = await uni.requestPayment(payInfo)
// 3.2 未完成支付
if(err) {
return uni.showToast({
title: '订单未支付!',
duration: 1500,
icon: 'none',
})
}
// 3.3 完成了支付,进一步查询支付的结构
const {data : res3} = await uni.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/chkOrder',
data: {
'order_number' : order_number
},
method: 'POST',
header: {
'Authorization': this.token
},
})
// 3.4 检查订单未完成支付
if(res3.meta.status !== 200) {
return uni.showToast({
title: '订单未支付!',
duration: 1500,
icon: 'none',
})
}
// 3.5检查订单完成支付
uni.showToast({
title: '订单支付完成!',
duration: 1500,
icon: 'success',
})
},