Uniapp + Vue3 + Vite +Uview + Pinia 实现提交订单以及支付功能(最新附源码保姆级)
1 效果展示 2 提交订单 2.1 cart.js 2.2 submit-order.vue
3、支付页面
1 效果展示
2 提交订单
2.1 cart.js
import {
defineStore
} from 'pinia' ;
import {
reactive,
computed
} from 'vue' ;
export const useCartStore = defineStore ( 'cart' , ( ) => {
const state = reactive ( {
cartItems : [ ] ,
allChose : false
} ) ;
const setCartItems = ( items ) => {
state. cartItems = items. map ( item => ( {
... item,
isChoose : false ,
num : item. num || 1
} ) ) ;
saveCartToLocalStorage ( ) ;
} ;
const selectedItemsCount = computed ( ( ) => {
return state. cartItems. reduce ( ( count, shop ) => {
return count + shop. items. filter ( item => item. isChoose) . reduce ( ( shopCount, item ) =>
shopCount + item. num, 0 ) ;
} , 0 ) ;
} ) ;
const totalSelectedPrice = computed ( ( ) => {
return state. cartItems. reduce ( ( total, shop ) => {
return total + shop. items. filter ( item => item. isChoose) . reduce ( ( shopTotal, item ) =>
shopTotal + item. price * item. num, 0 ) ;
} , 0 ) ;
} ) ;
const toggleItemChoose = ( shopName, itemId ) => {
const shop = state. cartItems. find ( shop => shop. shopName === shopName) ;
console. log ( shop) ;
if ( shop) {
const cartItem = shop. items. find ( cartItem => cartItem. id === itemId) ;
if ( cartItem) {
cartItem. isChoose = ! cartItem. isChoose;
}
updateAllChoseStatus ( ) ;
saveCartToLocalStorage ( ) ;
}
} ;
const changeItemQuantity = ( shopName, itemId, quantity ) => {
const shop = state. cartItems. find ( shop => shop. shopName === shopName) ;
if ( shop) {
const cartItem = shop. items. find ( cartItem => cartItem. id === itemId) ;
if ( cartItem) {
cartItem. num = quantity;
}
saveCartToLocalStorage ( ) ;
}
} ;
const selectedItems = computed ( ( ) => {
const groupedSelectedItems = [ ] ;
state. cartItems. forEach ( shop => {
const selectedShopItems = shop. items. filter ( item => item. isChoose) ;
if ( selectedShopItems. length > 0 ) {
const groupedShop = groupedSelectedItems. find ( group => group. shopName === shop
. shopName) ;
if ( groupedShop) {
groupedShop. items. push ( ... selectedShopItems) ;
if ( ! groupedShop. hasOwnProperty ( 'notes' ) ) {
groupedShop. notes = "" ;
}
} else {
groupedSelectedItems. push ( {
shopName : shop. shopName,
items : selectedShopItems,
notes : ""
} ) ;
}
}
} ) ;
return groupedSelectedItems;
} ) ;
const toggleAllChose = ( ) => {
state. allChose = ! state. allChose;
state. cartItems. forEach ( shop => {
shop. items. forEach ( item => {
item. isChoose = state. allChose;
} ) ;
} ) ;
saveCartToLocalStorage ( ) ;
} ;
const updateAllChoseStatus = ( ) => {
state. allChose = state. cartItems. every ( shop =>
shop. items. every ( item => item. isChoose)
) ;
} ;
const saveCartToLocalStorage = ( ) => {
localStorage. setItem ( 'cartItems' , JSON . stringify ( state. cartItems) ) ;
} ;
const loadCartFromLocalStorage = ( ) => {
const savedCart = localStorage. getItem ( 'cartItems' ) ;
if ( savedCart) {
state. cartItems = JSON . parse ( savedCart) ;
}
} ;
return {
state,
setCartItems,
selectedItems,
selectedItemsCount,
totalSelectedPrice,
toggleItemChoose,
changeItemQuantity,
toggleAllChose,
loadCartFromLocalStorage
} ;
} ) ;
2.2 submit-order.vue
< template>
< view class = "" >
< AddressVue> < / AddressVue>
< view class = "card" >
< template v- for = "(info, j) in selectedItems" : key= "j" >
< view class = "cart-data card-shadow" >
< view class = "" style= "display: flex;" >
{ { info. shopName} } < up- icon name= "arrow-right" > < / up- icon>
< / view>
< template v- for = "(item, index) in info.items" : key= "index" >
< view class = ""
style= "display: flex;padding: 20rpx 0;align-items: center;width: 100%;justify-content: space-around;" >
< view class = "cart-image" >
< up- image : src= "item.image" mode= "widthFix" height= "200rpx" width= "220rpx"
radius= "10" > < / up- image>
< / view>
< view>
< view class = "cart-right" >
< view style= "margin-bottom: 10rpx;font-size: 30rpx;" > { { item. title} } < / view>
< view style= "margin-bottom: 20rpx;font-size: 26rpx;color: #7d7e80;" > { { item. type} }
< / view>
< view class = "" style= "display: flex;align-items: center;" >
< up- text mode= "price" : text= "item.price" > < / up- text>
< view class = "" style= "width: 10rpx;" > < / view>
< up- number- box v- model= "item.num"
@change= "val => changeItemQuantity(item,item.iid, val.value)"
min= "1" > < / up- number- box>
< / view>
< / view>
< / view>
< / view>
< / template>
< view class = "notes" @click= "writeNoteFun(j)" >
< view style= "flex: 1;" > 订单备注< / view>
< view style= "display: flex;color: #7d7e80;width: 400rpx;justify-content: end;" >
< up- text : text= "info.notes.length==0?'无备注':info.notes" : lines= "1" > < / up- text>
< up- icon name= "arrow-right" > < / up- icon>
< / view>
< / view>
< ! -- 弹出层输入备注 -- >
< up- popup : show= "show" mode= "bottom" @close= "close" zIndex= "9999999" round= "20rpx" >
< view class = "" style= "text-align: center;height: 60rpx;line-height: 60rpx;margin-top: 20rpx;" >
订单备注
< / view>
< view style= "padding: 20rpx 40rpx;" >
< up- textarea v- model= "selectedItems[noteIndex].notes" placeholder= "请输入内容" count focus
maxlength= "200" height= "240rpx" > < / up- textarea>
< / view>
< view class = "" style= "display: flex;padding: 20rpx 40rpx;margin-top: 100rpx;" >
< up- button text= "确定" type= "warning" shape= "circle" @click= "enterNoteInputFun()" > < / up- button>
< / view>
< / up- popup>
< / view>
< / template>
< / view>
< view class = "" style= "height: 150rpx;" >
< / view>
< view class = "foot card" >
< view class = "card-connect" >
< view class = "" style= "display: flex; align-items: center;" >
< view style= "padding-left: 20rpx;font-size: 24rpx;" > 已选{ { selectedItemsCount} } 件, 合计< / view>
< view class = "" style= "display: flex;flex: 1;" >
< up- text mode= "price" : text= "totalSelectedPrice" color= "red" size= "18" > < / up- text>
< / view>
< / view>
< view class = "" style= "width: 20rpx;position: relative;" >
< / view>
< view class = "" style= "position: absolute;right: 40rpx;" >
< view class = "" style= "display: flex;" >
< up- button type= "error" text= "去支付" shape= "circle" style= "width: 150rpx;"
@click= "toPayFun" > < / up- button>
< / view>
< / view>
< up- toast ref= "uToastRef" > < / up- toast>
< / view>
< / view>
< / view>
< / template>
< script setup>
import {
ref,
onMounted
} from 'vue' ;
import AddressVue from '@/pages/components/User/Address.vue' ;
import {
useCartStore
} from '@/pages/store/cart/cart.js'
import {
storeToRefs
} from "pinia" ;
const cartStore = useCartStore ( ) ;
const {
state,
selectedItemsCount,
totalSelectedPrice,
selectedItems
} = storeToRefs ( cartStore) ;
const {
toggleItemChoose,
changeItemQuantity,
toggleAllChose
} = cartStore;
const show = ref ( false ) ;
const noteIndex = ref ( 0 ) ;
const writeNoteFun = ( index ) => {
noteIndex. value = index;
show. value = true ;
}
const close = ( ) => {
show. value = false ;
}
const enterNoteInputFun = ( ) => {
show. value = false ;
}
const toPayFun = ( ) => {
uni. navigateTo ( {
url : "/pages/src/home/order-pay/order-pay"
} )
}
onMounted ( ( ) => {
} ) ;
< / script>
< style lang= "scss" scoped>
. notes {
padding- top: 20rpx;
display : flex;
width : 100 % ;
justify- content: space- between;
}
. foot {
position : fixed;
bottom : 0 ;
left : 0 ;
width : 90 % ;
height : 100rpx;
background- color: #FFF ;
display : flex;
align- items: center;
. card- connect {
display : flex;
align- items: center;
justify- content: space- between;
}
}
. card {
margin : 20rpx;
padding : 20rpx;
background- color: #FFF ;
border- radius: 20rpx;
}
. card- shadow {
border- radius: 20rpx;
box- shadow: 10rpx 10rpx 10rpx 10rpx rgba ( 0.2 , 0.1 , 0.2 , 0.2 ) ;
}
. cart- data {
margin- bottom: 40rpx;
padding : 20rpx;
display : flex;
flex- wrap: wrap;
align- items: center;
. cart- image {
}
. cart- right {
display : flex;
flex- direction: column;
padding- left: 20rpx;
}
}
< / style>
3、支付页面
order-pay.vue
< template>
< view>
< view class = "" style= "display: flex;" >
< up- steps current= "1" style= "display: flex;" >
< up- steps- item title= "提交订单成功" : desc= "nowDate" > < / up- steps- item>
< up- steps- item title= "选择支付方式" desc= "" > < / up- steps- item>
< up- steps- item title= "卖家确认发货" desc= "24小时内" > < / up- steps- item>
< / up- steps>
< / view>
< view class = "card" >
< view class = "" style= "text-align: center;padding-top: 40rpx;" >
订单金额
< / view>
< view class = "" style= "display: flex;justify-content: center;padding: 60rpx 0 20rpx 0;" >
< up- text mode= "price" : text= "totalSelectedPrice" color= "red" size= "40" > < / up- text>
< / view>
< view class = "" style= "text-align: center;padding-top: 20rpx;" >
< text> 订单提交成功,请在10 分钟内完成支付< / text>
< / view>
< view class = "" style= "height: 100rpx;" >
< / view>
< up- divider text= "请您选择付款方式" > < / up- divider>
< view class = "" >
< radio- group @change= "radioChange" >
< view class = "" style= "width: 100%;" >
< view class = ""
style= "display: flex;align-items: center;width: 100%;justify-content: space-between;" >
< up- icon name= "weixin-circle-fill" size= "40" color= "green" > < / up- icon>
< text style= "padding-left: 20rpx;flex: 1;" > 微信支付< / text>
< radio : checked= "true" value= "1" > < / radio>
< / view>
< view class = ""
style= "display: flex;align-items: center;width: 100%;justify-content: space-between;margin-top: 20rpx;" >
< up- icon name= "zhifubao-circle-fill" size= "40" color= "blue" > < / up- icon>
< text style= "padding-left: 20rpx;flex: 1;" > 支付宝支付< / text>
< radio style= "right: 0;" value= "2" > < / radio>
< / view>
< / view>
< / radio- group>
< / view>
< / view>
< view class = "" style= "display: flex;margin-top: 40rpx;padding: 0 20rpx;" >
< up- button type= "error" text= "确认支付" shape= "circle" @click= "toPayFun" > < / up- button>
< / view>
< / view>
< / template>
< script setup>
import {
timeFormat
} from 'uview-plus' ;
import {
reactive,
ref
} from 'vue' ;
const nowDate = timeFormat ( new Date ( ) . getTime ( ) , 'hh:MM:ss' ) ;
import {
useCartStore
} from '@/pages/store/cart/cart.js'
import {
storeToRefs
} from "pinia" ;
const cartStore = useCartStore ( ) ;
const {
totalSelectedPrice,
selectedItems
} = storeToRefs ( cartStore) ;
const radiovalue = ref ( ) ;
const radioChange = ( e ) => {
radiovalue. value = e. detail. value;
} ;
const toPayFun = ( ) => {
}
< / script>
< style lang= "less" scoped>
. card {
margin : 20rpx;
padding : 20rpx;
background- color: #FFF ;
border- radius: 20rpx;
}
< / style>