Uniapp + Vue3 + Vite +Uview + Pinia 实现提交订单以及支付功能(最新附源码保姆级)

news/2024/9/19 8:16:48/ 标签: uni-app, javascript, 前端

Uniapp + Vue3 + Vite +Uview + Pinia 实现提交订单以及支付功能(最新附源码保姆级)

  • 1 效果展示
  • 2 提交订单
    • 2.1 cart.js
    • 2.2 submit-order.vue
  • 3、支付页面
    • order-pay.vue

1 效果展示


在这里插入图片描述

2 提交订单

2.1 cart.js


// src/pages/store/cart/cart.js
import {defineStore
} from 'pinia';
import {reactive,computed
} from 'vue';export const useCartStore = defineStore('cart', () => {// 用 reactive 管理购物车数据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);// 检查是否已经有 notes 字段,如果没有则添加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 = () => {// 遍历所有店铺的所有商品,如果有一个未选中,则全选状态为 falsestate.allChose = state.cartItems.every(shop =>shop.items.every(item => item.isChoose));};// 将购物车数据保存到 localStorageconst saveCartToLocalStorage = () => {localStorage.setItem('cartItems', JSON.stringify(state.cartItems));};// 从 localStorage 中恢复购物车数据const loadCartFromLocalStorage = () => {const savedCart = localStorage.getItem('cartItems');if (savedCart) {state.cartItems = JSON.parse(savedCart);}};return {state,setCartItems, // 暴露 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";// 使用 Pinia storeconst 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 为 false  show.value = false;// console.log('close');  }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;/* Tabbar 高度 */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 {// flex: 1;}.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";// 使用 Pinia storeconst cartStore = useCartStore();// 获取状态和操作const {totalSelectedPrice,selectedItems} = storeToRefs(cartStore);// up-radio-group的v-model绑定的值如果设置为某个radio的name,就会被默认选中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>

http://www.ppmy.cn/news/1526403.html

相关文章

基于MySQL 8.0.39的高性能优化版将于10月份开源

基于8.0版本推出的实用优化版&#xff0c;注重性能和稳定性&#xff0c;旨在为MySQL做出贡献。 目前优化的单机版本如下&#xff1a; GitHub - advancedmysql/mysql-8.0.39: Enhancing performance and stability based on MySQL 8.0.39.

外包干了半年,快要废了。。。

先说一下自己的情况&#xff0c;普通本科&#xff0c;在外包干了半年多的功能测试&#xff0c;这几年因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不能够在这样蹉跎下去了&#xff0c;长时间呆在一个舒适的环境真的会让…

idea2024.2永久使用

废话不多说&#xff0c;先上图 亲测有效 IDEA安装步骤 官网下载&#xff1a;https://www.jetbrains.com/idea/download/ 版本idea 2024.2 双击下一步安装完成 工具使用说明 出现这个界面就ok啦&#xff0c;大功告成&#xff0c;可以愉快的玩耍啦

Golang | Leetcode Golang题解之第405题数字转换为十六进制数

题目&#xff1a; 题解&#xff1a; func toHex(num int) string {if num 0 {return "0"}sb : &strings.Builder{}for i : 7; i > 0; i-- {val : num >> (4 * i) & 0xfif val > 0 || sb.Len() > 0 {var digit byteif val < 10 {digit 0…

OpenCV和Tesseract OCR识别复杂验证码喽~~

目录 代码实现思路 流程&#xff1a; 主要流程&#xff1a; 整体代码 效果展示 原图 处理之后的图 总结 流程图 代码实现思路 使用 OpenCV 进行图像预处理&#xff0c;并通过 Tesseract OCR 来识别验证码中的字符。以下是其实现思路的详细讲解&#xff1a; 流程&…

【R语言】删除数据框中所有行中没有大于200的数值的行

在Perl中还需要循环按行读入文件&#xff0c;而在R中&#xff0c;一行代码解决问题&#xff1a; df <- df[apply(df, 1, function(x) any(x > 200)), ]这是一个使用apply函数对数据框df进行操作的表达式。apply函数用于对数据框、矩阵或数组进行元素级别的操作。 df&am…

electron react离线使用monaco-editor

目录 1.搭建一个 electron-vite 项目 2.安装monaco-editor/react和monaco-editor 3.引入并做monaco-editor离线配置 4.react中使用 5.完整代码示例 6.monaco-editor离线配置官方说明 7.测试 1.搭建一个 electron-vite 项目 pnpm create quick-start/electron 参考链接…

数据结构算法——排序算法

1.排序 1.选择排序 不稳定&#xff0c;一般不用&#xff0c;基本排序 思路&#xff1a;过滤数组&#xff0c;找到最小数&#xff0c;放在前面。 不稳&#xff1a;导致原本在前的数据移动到后面。 int arr[];for(i0;i<arr.length-1;i){int smallesti; for(ji1;j<leng…

微软发布Windows Agent Arena 为生成式AI代理提供基准测试

使用生成式人工智能和大型语言模型来自动化和简化使用 PC 的人员的任务的情况持续增长。不过&#xff0c;人们也需要了解人工智能在完成任务方面的工作表现。本周微软研究院宣布&#xff0c;它已开发出一种专门用于在 Windows PC 上测试人工智能代理的基准。 微软在 GitHub 页面…

Nginx 实现会话保持的方式配置

在 NGINX 中实现会话保持&#xff08;Session Persistence&#xff09;&#xff0c;可以通过多种方法完成。以下是一些常见的方式&#xff1a; 1. 使用 IP 哈希&#xff08;IP Hash&#xff09; IP 哈希是一种简单的负载均衡策略&#xff0c;它基于客户端的 IP 地址将请求分配…

vue中使用fabric.js创建画布、加载图片图形元素、并实现拖拽移动缩放功能(在线组态)

在Web应用开发中&#xff0c;有时我们需要实现图形元素的拖拽、移动和缩放功能&#xff0c;以便构建在线组态、绘图等应用。fabric.js是一个强大的JavaScript库&#xff0c;可以轻松地在浏览器中实现这些功能。本文将介绍如何在Vue项目中使用fabric.js库创建画布&#xff0c;加…

java基于PDF底层内容流的解析对文本内容进行编辑

本文实现了基于坐标位置对PDF内容的底层修改而非覆盖&#xff0c;因此不会出现在某些高级PDF编辑器中可以移除插入内容或者文件随着编辑次数增多而大幅增大&#xff08;原因是原内容还在文件中&#xff09;的问题&#xff0c;而且使用的pdfbox是一个开源的、免费的PDF处理库&am…

Redis的存储原理和数据模型

一、Redis是单线程还是多线程呢&#xff1f; 我们通过跑redis的代码&#xff0c;查看运行的程序可以得知&#xff0c;Redis本身其实是个多线程&#xff0c;其中包括redis-server&#xff0c;bio_close_file&#xff0c;bio_aof_fsync&#xff0c;bio_lazy_free&#xff0c;io_t…

Django——多apps目录情况下的app注册

文章目录 多apps目录下的app注册方式1-添加python导包路径方式2-修改AppConfig类名 多apps目录下的app注册 方式1-添加python导包路径 import sys sys.path.insert(0, str(BASE_DIR / "apps")) print(sys.path)INSTALLED_APPS [django.contrib.admin,django.contr…

leetcode01——27. 移除元素(双指针)、977. 有序数组的平方(双指针)、209. 长度最小的子数组(双指针/滑动窗口)

27. 移除元素 /** 定义快慢指针&#xff0c;均从0开始&#xff0c;fast向后移动&#xff0c;遇到不为删除值的就停下&#xff0c;将值赋值给slow,slow 遇到等于目标值就直接跳过&#xff0c;不等于目标值就赋值&#xff0c;这样就能 过滤掉&#xff08;也就是删除掉&#xff0…

UAC2.0 麦克风——单通道 USB 麦克风

文章目录 单通道 USB 麦克风描述符结构设备描述符配置描述符集合配置描述符接口关联描述符类特殊描述符接口 1 的类特殊描述符Standard AC InterfaceClass-Specific AC Interface HeaderClock SourceInput TerminalOutput TerminalFeature Unit接口 2 的类特殊描述符Standard A…

iOS 18 RC 版本更新,为相机应用引入了“暂停录制视频”功能

苹果公司9月10日正式向全球iPhone用户推送了iOS 18 Release Candidate&#xff08;RC&#xff09;版本。这一版本的发布不仅标志着iOS系统的又一次重大更新&#xff0c;更预示着苹果在提升用户体验、增强隐私保护以及推动AI应用方面的持续努力。 并且此次苹果公司最新推出的 i…

Deep Learning-Based Object Pose Estimation:A Comprehensive Survey

论文&#xff1a;https://arxiv.org/pdf/2405.07801v3 项目&#xff1a;https://github.com/CNJianLiu/Awesome-Object-Pose-Estimation 年份&#xff1a;2024 方向&#xff1a;姿态估计 1. 目标姿态估计定义 估计图像中目标相对于相机的姿态&#xff0c; 目标姿态估计是增…

Leetcode 缺失的第一个正整数

题目意思是找出第一个没出现的最小正整数。 Explanation: Move Numbers to Correct Positions: The idea is to place each number in its corresponding index. For example, 1 should be at index 0, 2 should be at index 1, and so on. This is done using a while loop t…

新手教学系列——用Nginx将页面请求分发到不同后端模块

在当今的Web开发中,前后端分离架构已经成为主流,尤其是大型应用项目。前端可以通过Vue这样的框架来统一管理页面和用户交互,而后端则通常会拆分成多个微服务模块,以便应对不同业务需求和功能扩展。在这样的架构下,Nginx作为一个高效、灵活的Web服务器,能够帮助我们将前端…