1.个人中心页面my.vue
<template>
<view class="my-container">
<my-login v-if="!token"></my-login>
<my-userinfo v-else></my-userinfo>
</view>
</template>
<script>
import badgeMix from '@/mixins/tabbar-badge.js';
import { mapState } from 'vuex';
export default {
mixins:[badgeMix],
data() {
return {
};
},
computed:{
...mapState('m_user',['token'])
}
}
</script>
<style lang="scss">
page,.my-container{
height: 100%;
}
</style>
2. store-cart.js
export default {
namespaced: true,
state: () => ({
cart: JSON.parse(uni.getStorageSync('cart') || '[]')
}),
mutations: {
addToCart(state, goods) {
const findRes = state.cart.find(x => x.id === goods.id);
if (!findRes) {
state.cart.push(goods);
} else {
findRes.count++;
}
this.commit('m_cart/saveToStorage')
},
saveToStorage(state) {
uni.setStorageSync('cart', JSON.stringify(state.cart))
},
updateGoodsState(state, goods) {
const findRes = state.cart.find(x => x.id === goods.id)
if (findRes) {
findRes.state = goods.state;
this.commit('m_cart/saveToStorage');
}
},
updateGoodsCount(state, goods) {
const findRes = state.cart.find(x => x.id === goods.id)
if (findRes) {
findRes.count = goods.count;
this.commit('m_cart/saveToStorage');
}
},
removeGoodsById(state, id) {
state.cart = state.cart.filter(x => x.id !== id);
this.commit('m_cart/saveToStorage');
},
updateAllGoodsState(state, newState) {
state.cart.forEach(x => x.state = newState)
this.commit('m_cart/saveToStorage')
}
},
getters: {
total(state) {
return state.cart.reduce((total, item) => total += item.count, 0)
},
checkedCount(state) {
return state.cart.filter(x => x.state).reduce((total, item) =>
total += item.count, 0)
},
checkedGoodsAmount(state) {
return state.cart.filter(x => x.state).reduce((total, item) => total += item.count * item.price, 0).toFixed(
2)
}
}
}
3.store-user.js
export default {
namespaced: true,
state: () => ({
address: JSON.parse(uni.getStorageSync('address') || '{}'),
token: uni.getStorageSync('token') || '',
userinfo: JSON.parse(uni.getStorageSync('userinfo') || '{}'),
redirectInfo: null
}),
mutations: {
updateAddress(state, address) {
state.address = address
this.commit('m_user/saveAddressToStorage')
},
saveAddressToStorage(state) {
uni.setStorageSync('address', JSON.stringify(state.address))
},
updateUserInfo(state, userinfo) {
state.userinfo = userinfo
this.commit('m_user/saveUserInfoToStorage')
},
saveUserInfoToStorage(state) {
uni.setStorageSync('userinfo', JSON.stringify(state.userinfo))
},
updateToken(state, token) {
state.token = token
this.commit('m_user/saveTokenToStorage')
},
saveTokenToStorage(state) {
uni.setStorageSync('token', JSON.stringify(state.token))
},
updateRedirectInfo(state,info){
state.redirectInfo=info
console.log(state.redirectInfo)
}
},
getters: {
addstr(state) {
if (!state.address.provinceName) return '';
return state.address.provinceName + state.address.cityName + state.address.countyName + state.address
.detailInfo
}
}
}
4.结算组件my-settle.vue
<template>
<view class="my-settle-container">
<!-- 全选 -->
<label class="radio" @click="changeAllState">
<radio :checked="isFullCheck" /><text>全选</text>
</label>
<!-- 合计 -->
<view class="amount-box">
合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
</view>
<!-- 结算 -->
<view class="btn-settle" @click="settlement">结算({{checkedCount}})</view>
</view>
</template>
<script>
import {
mapGetters,
mapMutations,
mapState
} from 'vuex';
export default {
name: "my-settle",
data() {
return {
seconds: 3,
timer: null
};
},
computed: {
...mapGetters('m_cart', ['checkedCount', 'total', 'checkedGoodsAmount']),
...mapGetters('m_user', ['addstr']),
...mapState('m_user', ['token']),
...mapState('m_cart', ['cart']),
isFullCheck() {
return this.total === this.checkedCount
}
},
methods: {
...mapMutations('m_cart', ['updateAllGoodsState']),
...mapMutations('m_user', ['updateRedirectInfo']),
changeAllState() {
this.updateAllGoodsState(!this.isFullCheck)
},
settlement() {
if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品');
if (!this.addstr) return uni.$showMsg('请选择收货地址');
if (!this.token) return this.delayNavigate();
this.payOrder();
},
payOrder() {
const orderInfo = {
order_price: 0.01,
consignee_addr: this.addstr,
goods: this.cart.filter(x => x.state).map(x => ({
id: x.id,
number: x.count,
price: x.price
}))
}
uni.request({
url: uni.$baseUrl + '/api/User/PayOrder',
method: 'POST',
data: {
orderInfo
}
})
.then((res) => {
if (res.data.code != 200) return uni.$showMsg('创建订单失败!');
const orderNumber = res.data.data.order_number;
uni.request({
url: uni.$baseUrl + '/api/User/PayOrder',
method: 'POST',
data: {
orderInfo
}
}).then((res) => {
if (res.data.code != 200) return uni.$showMsg('预付订单生成失败!');
const payInfo = res.data.data.pay
uni.requestPayment(payInfo).then((res) => {
if(res.err) return uni.$showMsg('订单未支付!');
uni.request({
url: uni.$baseUrl + '/api/User/CHKOrder',
data: {
'payInfo': payInfo
}
}).then((res) => {
if (res.data.code != 200) return uni.$showMsg('订单未支付!');
uni.showToast({
icon: 'success',
title: '订单支付完成!'
})
})
})
})
});
},
delayNavigate() {
this.seconds = 3;
this.showTips(this.seconds);
this.timer = 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)
},
showTips(n) {
uni.showToast({
icon: 'none',
title: '请登录后再结算! ' + n + ' 秒之后自动跳转到登录页',
mask: true,
duration: 1500,
})
}
}
}
</script>
<style lang="scss">
.my-settle-container {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
padding-left: 5px;
}
.radio {
display: flex;
align-items: center;
}
.amount-box {
.amount {
color: #c00000;
font-weight: bold;
}
}
.btn-settle {
background-color: #c00000;
height: 50px;
color: white;
line-height: 50px;
padding: 0 5px;
width: 100px;
text-align: center;
}
</style>
5.登录组件my-login.vue
<template>
<view class="login-container">
<uni-icons type="contact-filled" size="100" color="#AFAFAF"></uni-icons>
<button type="primary" class="btn-login" @click="getUserInfo">一键登录</button>
<text class="tips-text">登录后尽享更多权益</text>
</view>
</template>
<script>
import {
mapMutations,
mapState
} from 'vuex';
export default {
name: "my-login",
data() {
return {
};
},
computed: {
...mapState('m_user', ['redirectInfo'])
},
methods: {
...mapMutations('m_user', ['updateUserInfo', 'updateToken', 'updateRedirectInfo']),
getUserInfo() {
let that = this;
uni.getUserProfile({
desc: '接口调试'
}).then((res) => {
that.updateUserInfo(res.userInfo);
that.getToken(res);
});
},
getToken(info) {
uni.login({
provider: 'weixin',
}).then((res) => {
const query = {
code: res.code,
encryptedData: info.encryptedData,
iv: info.iv,
rawData: info.rawData,
signature: info.signature
}
console.log(query)
uni.request({
url: uni.$baseUrl + '/api/User/Login',
method: 'POST',
data: {
query
}
})
.then((res) => {
if (res.data.code != 200) return uni.$showMsg('登录失败!');
uni.$showMsg('登录成功!');
this.updateToken(res.data.data);
this.navigateBack();
});
});
},
navigateBack() {
if (this.redirectInfo && this.redirectInfo.openType === 'switchTab') {
uni.switchTab({
url: this.redirectInfo.from,
complete: () => {
this.updateRedirectInfo(null)
}
})
}
}
}
}
</script>
<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: 80%;
border-radius: 100px;
margin: 15px 0;
}
.tips-text {
font-size: 12px;
color: gray;
}
}
</style>
6.个人信息组件my.vue
<template>
<view class="my-userinfo-container">
<!-- 头像和昵称区域 -->
<view class="top-box">
<image :src="userinfo.avatarUrl" class="avatar"></image>
<view class="nickname">{{userinfo.nickName}}</view>
</view>
<!-- 面板区域 -->
<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>8</text>
<text>收藏的商品</text>
</view>
<view class="panel-item">
<text>8</text>
<text>关注的商品</text>
</view>
<view class="panel-item">
<text>8</text>
<text>足迹</text>
</view>
</view>
</view>
<!-- 面板2 -->
<view class="panel">
<view class="panel-title">
我的订单
</view>
<view class="panel-body">
<view class="panel-item">
<image src="/static/my/待付款.png" class="icon"></image>
<text>待付款</text>
</view>
<view class="panel-item">
<image src="/static/my/待收货.png" class="icon"></image>
<text>待收货</text>
</view>
<view class="panel-item">
<image src="/static/my/换货.png" class="icon"></image>
<text>退款/退货</text>
</view>
<view class="panel-item">
<image src="/static/my/账单.png" class="icon"></image>
<text>全部订单</text>
</view>
</view>
</view>
<!-- 面板3 -->
<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" @click="logout">
<text>退出登录</text>
<uni-icons type="arrowright" size="15"></uni-icons>
</view>
</view>
</view>
</view>
</template>
<script>
import {mapState,mapMutations} from 'vuex'
export default {
name: "my-userinfo",
data() {
return {
};
},
computed:{
...mapState('m_user',['userinfo'])
},
methods:{
...mapMutations('m_user',['updateAddress','updateUserInfo','updateToken']),
logout(){
uni.showModal({
title:'提示',
content:'确认退出登录吗?'
}).then((res)=>{
if(res.confirm){
this.updateAddress({});
this.updateUserInfo({});
this.updateToken('');
}
})
}
}
}
</script>
<style lang="scss">
.my-userinfo-container {
height: 100%;
background-color: #f4f4f4;
.top-box {
height: 400rpx;
background-color: #55aaff;
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: #ffffff;
font-weight: bold;
margin-top: 10px;
}
}
}
.panel-list{
padding: 0 10px;
position: relative;
top:-10px;
.panel{
background-color: white;
border-radius: 3px;
margin-bottom: 8px;
.panel-title{
line-height: 45px;
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;
}
}
}
}
}
.panel-list-item{
display: flex;
justify-content: space-between;
align-items: center;
font-size: 15px;padding: 0 10px;
line-height: 45px;
}
</style>
7.效果示例