Uniapp + Vue3 + Vite +Uview + Pinia 实现提交订单以及付出功能(最新附源 ...

打印 上一主题 下一主题

主题 677|帖子 677|积分 2031

1 效果展示



2 提交订单

2.1 cart.js


  1. // src/pages/store/cart/cart.js
  2. import {
  3.         defineStore
  4. } from 'pinia';
  5. import {
  6.         reactive,
  7.         computed
  8. } from 'vue';
  9. export const useCartStore = defineStore('cart', () => {
  10.         // 用 reactive 管理购物车数据
  11.         const state = reactive({
  12.                 cartItems: [], // 购物车商品列表
  13.                 allChose: false // 全选状态
  14.         });
  15.         // 设置购物车数据
  16.         const setCartItems = (items) => {
  17.                 state.cartItems = items.map(item => ({
  18.                         ...item,
  19.                         isChoose: false, // 初始化为未选中状态
  20.                         num: item.num || 1 // 初始化数量
  21.                 }));
  22.                 saveCartToLocalStorage(); // 每次设置后将数据持久化
  23.         };
  24.         // 计算已选中的商品数量
  25.         const selectedItemsCount = computed(() => {
  26.                 return state.cartItems.reduce((count, shop) => {
  27.                         return count + shop.items.filter(item => item.isChoose).reduce((shopCount, item) =>
  28.                                 shopCount + item.num, 0);
  29.                 }, 0);
  30.         });
  31.         // 计算已选中商品的总价格
  32.         const totalSelectedPrice = computed(() => {
  33.                 return state.cartItems.reduce((total, shop) => {
  34.                         return total + shop.items.filter(item => item.isChoose).reduce((shopTotal, item) =>
  35.                                 shopTotal + item.price * item.num, 0);
  36.                 }, 0);
  37.         });
  38.         // 切换商品的选中状态
  39.         const toggleItemChoose = (shopName, itemId) => {
  40.                 const shop = state.cartItems.find(shop => shop.shopName === shopName);
  41.                 console.log(shop);
  42.                 if (shop) {
  43.                         const cartItem = shop.items.find(cartItem => cartItem.id === itemId);
  44.                         if (cartItem) {
  45.                                 cartItem.isChoose = !cartItem.isChoose;
  46.                         }
  47.                         updateAllChoseStatus(); // 每次切换选中状态后更新全选状态
  48.                         saveCartToLocalStorage();
  49.                 }
  50.         };
  51.         // 修改商品数量
  52.         const changeItemQuantity = (shopName, itemId, quantity) => {
  53.                 const shop = state.cartItems.find(shop => shop.shopName === shopName);
  54.                 if (shop) {
  55.                         const cartItem = shop.items.find(cartItem => cartItem.id === itemId);
  56.                         if (cartItem) {
  57.                                 cartItem.num = quantity;
  58.                         }
  59.                         saveCartToLocalStorage();
  60.                 }
  61.         };
  62.         // 获取所有已选中的商品
  63.         const selectedItems = computed(() => {
  64.                 const groupedSelectedItems = [];
  65.                 state.cartItems.forEach(shop => {
  66.                         const selectedShopItems = shop.items.filter(item => item.isChoose);
  67.                         if (selectedShopItems.length > 0) {
  68.                                 // 查找是否已经存在相同店铺名的对象
  69.                                 const groupedShop = groupedSelectedItems.find(group => group.shopName === shop
  70.                                         .shopName);
  71.                                 if (groupedShop) {
  72.                                         // 如果存在,直接将选中的商品添加到该店铺的商品列表中
  73.                                         groupedShop.items.push(...selectedShopItems);
  74.                                         // 检查是否已经有 notes 字段,如果没有则添加
  75.                                         if (!groupedShop.hasOwnProperty('notes')) {
  76.                                                 groupedShop.notes = "";
  77.                                         }
  78.                                 } else {
  79.                                         // 如果不存在,创建一个新的店铺对象,并将选中的商品添加进去
  80.                                         groupedSelectedItems.push({
  81.                                                 shopName: shop.shopName,
  82.                                                 items: selectedShopItems,
  83.                                                 notes: ""
  84.                                         });
  85.                                 }
  86.                         }
  87.                 });
  88.                 return groupedSelectedItems;
  89.         });
  90.         // 切换全选状态
  91.         const toggleAllChose = () => {
  92.                 state.allChose = !state.allChose;
  93.                 state.cartItems.forEach(shop => {
  94.                         shop.items.forEach(item => {
  95.                                 item.isChoose = state.allChose;
  96.                         });
  97.                 });
  98.                 saveCartToLocalStorage();
  99.         };
  100.         // 更新全选状态
  101.         const updateAllChoseStatus = () => {
  102.                 // 遍历所有店铺的所有商品,如果有一个未选中,则全选状态为 false
  103.                 state.allChose = state.cartItems.every(shop =>
  104.                         shop.items.every(item => item.isChoose)
  105.                 );
  106.         };
  107.         // 将购物车数据保存到 localStorage
  108.         const saveCartToLocalStorage = () => {
  109.                 localStorage.setItem('cartItems', JSON.stringify(state.cartItems));
  110.         };
  111.         // 从 localStorage 中恢复购物车数据
  112.         const loadCartFromLocalStorage = () => {
  113.                 const savedCart = localStorage.getItem('cartItems');
  114.                 if (savedCart) {
  115.                         state.cartItems = JSON.parse(savedCart);
  116.                 }
  117.         };
  118.         return {
  119.                 state,
  120.                 setCartItems, // 暴露 setCartItems 方法
  121.                 selectedItems,
  122.                 selectedItemsCount,
  123.                 totalSelectedPrice,
  124.                 toggleItemChoose,
  125.                 changeItemQuantity,
  126.                 toggleAllChose,
  127.                 loadCartFromLocalStorage
  128.         };
  129. });
复制代码
2.2 submit-order.vue

  1. <template>
  2.         <view class="">
  3.                 <AddressVue></AddressVue>
  4.                 <view class="card">
  5.                         <template v-for="(info, j) in selectedItems" :key="j">
  6.                                 <view class="cart-data card-shadow">
  7.                                         <view class="" style="display: flex;">
  8.                                                 {{info.shopName}}<up-icon name="arrow-right"></up-icon>
  9.                                         </view>
  10.                                         <template v-for="(item, index) in info.items" :key="index">
  11.                                                 <view class=""
  12.                                                         style="display: flex;padding: 20rpx 0;align-items: center;width: 100%;justify-content: space-around;">
  13.                                                         <view class="cart-image">
  14.                                                                 <up-image :src="item.image" mode="widthFix" height="200rpx" width="220rpx"
  15.                                                                         radius="10"></up-image>
  16.                                                         </view>
  17.                                                         <view>
  18.                                                                 <view class="cart-right">
  19.                                                                         <view style="margin-bottom: 10rpx;font-size: 30rpx;">{{item.title}}</view>
  20.                                                                         <view style="margin-bottom: 20rpx;font-size: 26rpx;color: #7d7e80;">{{item.type}}
  21.                                                                         </view>
  22.                                                                         <view class="" style="display: flex;align-items: center;">
  23.                                                                                 <up-text mode="price" :text="item.price"></up-text>
  24.                                                                                 <view class="" style="width: 10rpx;"></view>
  25.                                                                                 <up-number-box v-model="item.num"
  26.                                                                                         @change="val => changeItemQuantity(item,item.iid, val.value)"
  27.                                                                                         min="1"></up-number-box>
  28.                                                                         </view>
  29.                                                                 </view>
  30.                                                         </view>
  31.                                                 </view>
  32.                                         </template>
  33.                                         <view class="notes" @click="writeNoteFun(j)">
  34.                                                 <view style="flex: 1;">订单备注</view>
  35.                                                 <view style="display: flex;color: #7d7e80;width: 400rpx;justify-content: end;" >
  36.                                                         <up-text :text="info.notes.length==0?'无备注':info.notes" :lines="1"></up-text>
  37.                                                         <up-icon name="arrow-right"></up-icon>
  38.                                                 </view>
  39.                                         </view>
  40.                                         <!-- 弹出层输入备注 -->
  41.                                         <up-popup :show="show" mode="bottom" @close="close" zIndex="9999999" round="20rpx">
  42.                                                 <view class="" style="text-align: center;height: 60rpx;line-height: 60rpx;margin-top: 20rpx;">
  43.                                                         订单备注
  44.                                                 </view>
  45.                                                 <view  style="padding: 20rpx 40rpx;">
  46.                                                         <up-textarea v-model="selectedItems[noteIndex].notes"  placeholder="请输入内容" count focus
  47.                                                                 maxlength="200" height="240rpx"></up-textarea>
  48.                                                 </view>
  49.                                                 <view class="" style="display: flex;padding: 20rpx 40rpx;margin-top: 100rpx;">
  50.                                                         <up-button text="确定" type="warning" shape="circle" @click="enterNoteInputFun()"></up-button>
  51.                                                 </view>
  52.                                         </up-popup>
  53.                                 </view>
  54.                         </template>
  55.                 </view>
  56.                 <view class="" style="height: 150rpx;">
  57.                 </view>
  58.                 <view class="foot card">
  59.                         <view class="card-connect">
  60.                                 <view class="" style="display: flex; align-items: center;">
  61.                                         <view style="padding-left: 20rpx;font-size: 24rpx;">已选{{selectedItemsCount}}件,合计</view>
  62.                                         <view class="" style="display: flex;flex: 1;">
  63.                                                 <up-text mode="price" :text="totalSelectedPrice" color="red" size="18"></up-text>
  64.                                         </view>
  65.                                 </view>
  66.                                 <view class="" style="width: 20rpx;position: relative;">
  67.                                 </view>
  68.                                 <view class="" style="position: absolute;right: 40rpx;">
  69.                                         <view class="" style="display: flex;">
  70.                                                 <up-button type="error" text="去支付" shape="circle" style="width: 150rpx;"
  71.                                                         @click="toPayFun"></up-button>
  72.                                         </view>
  73.                                 </view>
  74.                                 <up-toast ref="uToastRef"></up-toast>
  75.                         </view>
  76.                 </view>
  77.         </view>
  78. </template>
  79. <script setup>
  80.         import {
  81.                 ref,
  82.                 onMounted
  83.         } from 'vue';
  84.         import AddressVue from '@/pages/components/User/Address.vue';
  85.         import {
  86.                 useCartStore
  87.         } from '@/pages/store/cart/cart.js'
  88.         import {
  89.                 storeToRefs
  90.         } from "pinia";
  91.         // 使用 Pinia store
  92.         const cartStore = useCartStore();
  93.         // 获取状态和操作
  94.         // 获取状态和操作
  95.         const {
  96.                 state,
  97.                 selectedItemsCount,
  98.                 totalSelectedPrice,
  99.                 selectedItems
  100.         } = storeToRefs(cartStore);
  101.         const {
  102.                 toggleItemChoose,
  103.                 changeItemQuantity,
  104.                 toggleAllChose
  105.         } = cartStore;
  106.         // 创建响应式数据  
  107.         const show = ref(false);
  108.         const noteIndex = ref(0);
  109.         const writeNoteFun = (index) => {
  110.                 // 记录备注数据的下标
  111.                 noteIndex.value = index;
  112.                 show.value = true;
  113.         }
  114.         const close = () => {
  115.                 // 关闭逻辑,设置 show 为 false  
  116.                 show.value = false;
  117.                 // console.log('close');  
  118.         }
  119.         const enterNoteInputFun = () => {
  120.                 show.value = false;
  121.         }
  122.         // 支付调起
  123.         const toPayFun = () => {
  124.                 uni.navigateTo({
  125.                         url: "/pages/src/home/order-pay/order-pay"
  126.                 })
  127.         }
  128.         onMounted(() => {
  129.         });
  130. </script>
  131. <style lang="scss" scoped>
  132.         .notes {
  133.                 padding-top: 20rpx;
  134.                 display: flex;
  135.                 width: 100%;
  136.                 justify-content: space-between;
  137.         }
  138.         .foot {
  139.                 position: fixed;
  140.                 bottom: 0;
  141.                 left: 0;
  142.                 width: 90%;
  143.                 /* 占据全宽 */
  144.                 height: 100rpx;
  145.                 /* Tabbar 高度 */
  146.                 background-color: #FFF;
  147.                 display: flex;
  148.                 align-items: center;
  149.                 .card-connect {
  150.                         display: flex;
  151.                         align-items: center;
  152.                         justify-content: space-between;
  153.                 }
  154.         }
  155.         .card {
  156.                 margin: 20rpx;
  157.                 padding: 20rpx;
  158.                 background-color: #FFF;
  159.                 border-radius: 20rpx;
  160.         }
  161.         .card-shadow {
  162.                 border-radius: 20rpx;
  163.                 box-shadow: 10rpx 10rpx 10rpx 10rpx rgba(0.2, 0.1, 0.2, 0.2);
  164.         }
  165.         .cart-data {
  166.                 margin-bottom: 40rpx;
  167.                 padding: 20rpx;
  168.                 display: flex;
  169.                 flex-wrap: wrap;
  170.                 align-items: center;
  171.                 .cart-image {
  172.                         // flex: 1;
  173.                 }
  174.                 .cart-right {
  175.                         display: flex;
  176.                         flex-direction: column;
  177.                         padding-left: 20rpx;
  178.                 }
  179.         }
  180. </style>
复制代码
3、付出页面

order-pay.vue


  1. <template>
  2.         <view>
  3.                 <view class="" style="display: flex;">
  4.                         <up-steps current="1" style="display: flex;">
  5.                                 <up-steps-item title="提交订单成功" :desc="nowDate"></up-steps-item>
  6.                                 <up-steps-item title="选择支付方式" desc=""></up-steps-item>
  7.                                 <up-steps-item title="卖家确认发货" desc="24小时内"></up-steps-item>
  8.                         </up-steps>
  9.                 </view>
  10.                 <view class="card">
  11.                         <view class="" style="text-align: center;padding-top: 40rpx;">
  12.                                 订单金额
  13.                         </view>
  14.                         <view class="" style="display: flex;justify-content: center;padding: 60rpx 0 20rpx 0;">
  15.                                 <up-text mode="price" :text="totalSelectedPrice" color="red" size="40"></up-text>
  16.                         </view>
  17.                         <view class="" style="text-align: center;padding-top: 20rpx;">
  18.                                 <text>订单提交成功,请在10分钟内完成支付</text>
  19.                         </view>
  20.                         <view class="" style="height: 100rpx;">
  21.                         </view>
  22.                         <up-divider text="请您选择付款方式"></up-divider>
  23.                         <view class="">
  24.                                 <radio-group @change="radioChange">
  25.                                         <view class="" style="width: 100%;">
  26.                                                 <view class=""
  27.                                                         style="display: flex;align-items: center;width: 100%;justify-content: space-between;">
  28.                                                         <up-icon name="weixin-circle-fill" size="40" color="green"></up-icon>
  29.                                                         <text style="padding-left: 20rpx;flex: 1;">微信支付</text>
  30.                                                         <radio :checked="true" value="1"></radio>
  31.                                                 </view>
  32.                                                 <view class=""
  33.                                                         style="display: flex;align-items: center;width: 100%;justify-content: space-between;margin-top: 20rpx;">
  34.                                                         <up-icon name="zhifubao-circle-fill" size="40" color="blue"></up-icon>
  35.                                                         <text style="padding-left: 20rpx;flex: 1;">支付宝支付</text>
  36.                                                         <radio style="right: 0;" value="2"></radio>
  37.                                                 </view>
  38.                                         </view>
  39.                                 </radio-group>
  40.                         </view>
  41.                 </view>
  42.                 <view class="" style="display: flex;margin-top: 40rpx;padding: 0 20rpx;">
  43.                         <up-button type="error" text="确认支付" shape="circle" @click="toPayFun"></up-button>
  44.                 </view>
  45.         </view>
  46. </template>
  47. <script setup>
  48.         import {
  49.                 timeFormat
  50.         } from 'uview-plus';
  51.         import {
  52.                 reactive,
  53.                 ref
  54.         } from 'vue';
  55.         const nowDate = timeFormat(new Date().getTime(), 'hh:MM:ss');
  56.         import {
  57.                 useCartStore
  58.         } from '@/pages/store/cart/cart.js'
  59.         import {
  60.                 storeToRefs
  61.         } from "pinia";
  62.         // 使用 Pinia store
  63.         const cartStore = useCartStore();
  64.         // 获取状态和操作
  65.         const {
  66.                 totalSelectedPrice,
  67.                 selectedItems
  68.         } = storeToRefs(cartStore);
  69.         // up-radio-group的v-model绑定的值如果设置为某个radio的name,就会被默认选中
  70.         const radiovalue = ref();
  71.         const radioChange = (e) => {
  72.                 radiovalue.value = e.detail.value;
  73.         };
  74.        
  75.         const toPayFun = () =>{
  76.                 // 调起支付
  77.         }
  78. </script>
  79. <style lang="less" scoped>
  80.         .card {
  81.                 margin: 20rpx;
  82.                 padding: 20rpx;
  83.                 background-color: #FFF;
  84.                 border-radius: 20rpx;
  85.         }
  86. </style>
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

乌市泽哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表