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 focusmaxlength= "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>