购物车逻辑完善

个人信息存储优化
团购支付
This commit is contained in:
2024-03-31 17:22:14 +08:00
parent 1fc0aa432b
commit b502385272
19 changed files with 859 additions and 141 deletions

View File

@@ -16,8 +16,8 @@
超级\n秒杀
</view>
<view class='price c-flex-column' style='flex: 1'>
<text>{{ detailBean?.payPrice || 0 }}</text>
<text>{{ detailBean?.goodsPrice || 0 }}</text>
<text>{{ groupBuyBean?.payPrice || 0 }}</text>
<text>{{ groupBuyBean?.price || 0 }}</text>
</view>
<view class='countdown-time c-flex-column'>
<view class='c-flex-row'>{{ countdownTime?.days || 0 }}
@@ -33,12 +33,12 @@
<view class='goods-info-view c-flex-column'>
<view class='c-flex-row'>
<text class='goods-price accent-text-color'>{{ detailBean?.payPrice || 0 }}</text>
<text>销量{{ detailBean?.totalNum || 0 }}</text>
<text class='goods-price accent-text-color'>{{ groupBuyBean?.payPrice || 0 }}</text>
<text>销量{{ groupBuyBean?.totalNum || 0 }}</text>
</view>
<view class='c-flex-row'>
<text style='flex: 1'>{{ detailBean?.name }}</text>
<text style='flex: 1'>{{ groupBuyBean?.name }}</text>
<view class='share-button c-flex-column'>
<image :src='assetsUrl("ic_share.png")'></image>
<button class='btn' plain open-type='share'>分享</button>
@@ -51,7 +51,7 @@
<text>选择</text>
<text>规格 颜色/尺码</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' />
<text>1种颜色可选</text>
<text>{{ getStockColorCount }}种颜色可选</text>
</view>
</view>
@@ -70,27 +70,27 @@
</view>
<!-- 商品详情图片 -->
<view class='card-view image-container' v-if='detailBean?.content'>
<view class='card-view image-container' v-if='groupBuyBean?.content'>
<text class='card-view-title'>商品详情</text>
<image
v-for='(item,index) in JSON.parse(detailBean?.content).filter((a: any) => a.type === 2).map((b: any) => b.images)'
v-for='(item,index) in JSON.parse(groupBuyBean?.content).filter((a: any) => a.type === 2).map((b: any) => b.images)'
:src='item' mode='aspectFill' :key='index' />
</view>
<!-- 商品详情 -->
<view class='card-view goods-container'>
<view class='c-flex-row'>
<image :src='detailBean?.goods?.images' />
<image :src='groupBuyBean?.goods?.images' />
<view class='c-flex-column'>
<text class='goods-name'>{{ detailBean?.goods?.name }}</text>
<text class='goods-name'>{{ groupBuyBean?.goods?.name }}</text>
<text style='color: #a6a6a6'>精选</text>
<view class='tag-view c-flex-row'>
<text>团长推荐</text>
<text>今日必买</text>
</view>
<text style='color: #a6a6a6'>不参与会员折扣</text>
<text style='color: #F32B2B'>¥{{ detailBean?.goods?.payPrice }}</text>
<text class='bought_count'>已团{{ detailBean?.sendNum }}</text>
<text style='color: #F32B2B'>¥{{ groupBuyBean?.goods?.payPrice }}</text>
<text class='bought_count'>已团{{ groupBuyBean?.sendNum }}</text>
</view>
</view>
</view>
@@ -99,7 +99,7 @@
<view class='card-view coupon-container'>
<text class='card-view-title'>赠送优惠券</text>
<view class='c-flex-column'>
<coupon-item v-for='(item,index) in detailBean?.couponsList' :key='index' :item='item' />
<coupon-item v-for='(item,index) in groupBuyBean?.couponsList' :key='index' :item='item' />
</view>
</view>
@@ -125,13 +125,13 @@
<view class='place-order-button' @click.stop='placeOrder'>跟团购买</view>
</view>
</view>
<sku-dialog ref='skuDialogRef' :bean='detailBean?.goods' @confirm='confirmGoodsSku' />
<sku-dialog ref='skuDialogRef' :flash-price='groupBuyBean?.payPrice || 0' />
</template>
<script lang='ts' setup>
import { assetsUrl } from '@/utils/assets';
import dayjs from 'dayjs';
import { goPath } from '@/utils';
import { formatTimeWithZeroPad, goPath } from '@/utils';
import SkuDialog from '@/components/sku-dialog.vue';
import CouponItem from './components/coupon-item.vue';
@@ -139,33 +139,33 @@ import { getGroupBuyDetail, getGroupBuyRecordList, preOrder } from '@/api/groupb
import { getGoodsList } from '@/api/goods';
import { GoodsBean } from '@/api/goods/types';
import { useUserStore } from '@/store';
import { GroupBuyBean, RecordBean } from '@/api/groupbuy/types';
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const skuDialogRef = ref();
const detailBean = ref();
const groupBuyBean = ref<GroupBuyBean>();
const recommendList = ref<GoodsBean[]>();
const skuBean = ref();
const bannerList = ref([]);
const swiperIndex = ref(0);
let interval: number;
const countdownTime = ref<{
days: number,
hours: number,
minutes: number,
seconds: number
days: string,
hours: string,
minutes: string,
seconds: string
}>();
const recordList = ref([]);
const recordList = ref<RecordBean[]>([]);
const currentPageNum = ref(1);
onLoad(async (e: any) => {
detailBean.value = await getGroupBuyDetail(e.id);
detailBean.value.goods.price = detailBean.value.price;
bannerList.value = JSON.parse(detailBean.value.content).filter((item: any) => item.type === 2).map((item: any) => item.images);
groupBuyBean.value = await getGroupBuyDetail(e.id);
groupBuyBean.value.goods.price = groupBuyBean.value.price;
bannerList.value = JSON.parse(groupBuyBean.value.content).filter((item: any) => item.type === 2).map((item: any) => item.images);
countdown();
await fetchRecommendList();
await fetchBuyRecordList();
@@ -177,6 +177,12 @@ onUnload(() => {
}
});
const getStockColorCount = computed(() => {
const list = Array.from(new Set(groupBuyBean.value?.goods?.stocks?.map(item => item.colorName)))
.map(colorName => groupBuyBean.value?.goods?.stocks?.find(item => item.colorName === colorName)!);
return list.length;
});
const fetchRecommendList = async () => {
const { rows } = await getGoodsList({
page: {
@@ -195,7 +201,7 @@ const fetchBuyRecordList = async (refresh: boolean = true) => {
if(!refresh) {
currentPageNum.value += 1;
}
const { list } = await getGroupBuyRecordList(detailBean.value.id, currentPageNum.value, 20);
const { list } = await getGroupBuyRecordList(groupBuyBean?.value?.id || '', currentPageNum.value, 20);
recordList.value = recordList.value.concat(list);
};
@@ -209,16 +215,16 @@ const swiperChange = (e: any) => {
const countdown = () => {
interval = setInterval(() => {
if(detailBean.value?.endDate) {
if(groupBuyBean.value?.endDate) {
let now = new Date();
let end = dayjs(detailBean.value?.endDate).toDate().getTime();
let end = dayjs(groupBuyBean.value?.endDate).toDate().getTime();
let remaining = Math.floor((end - now.getTime()) / 1000);
if(remaining > 0) {
countdownTime.value = {
days: Math.floor(remaining / 60 / 60 / 24),
hours: Math.floor(remaining / 60 / 60 % 24),
minutes: Math.floor(remaining / 60 % 60),
seconds: Math.floor(remaining % 60)
days: formatTimeWithZeroPad(Math.floor(remaining / 60 / 60 / 24)),
hours: formatTimeWithZeroPad(Math.floor(remaining / 60 / 60 % 24)),
minutes: formatTimeWithZeroPad(Math.floor(remaining / 60 % 60)),
seconds: formatTimeWithZeroPad(Math.floor(remaining % 60))
};
} else {
clearInterval(interval);
@@ -228,31 +234,27 @@ const countdown = () => {
};
const showSkuDialog = (fn: Function) => {
skuDialogRef.value.show(fn);
};
const confirmGoodsSku = (e: any) => {
skuBean.value = e;
skuDialogRef.value.show(groupBuyBean?.value?.goods?.goodsId, fn);
};
const placeOrder = async () => {
async function create() {
async function create(bean: GoodsBean) {
const params = {
'colorId': '1725029269178814466',
'sizeId': '0',
'goodsId': detailBean.value.goods.goodsId,
'groupId': detailBean.value.id,
'colorId': bean.checkedStock.colorId,
'sizeId': bean.checkedStock.sizeId,
'goodsId': groupBuyBean?.value?.goods.goodsId,
'groupId': groupBuyBean?.value?.id,
'memberId': userInfo.value.id,
'shareId': '123456'
};
const result = await preOrder(params);
goPath('/pages/common/groupbuy/order');
goPath(`/pages/common/groupbuy/order?orderBean=${encodeURIComponent(JSON.stringify(result))}`);
}
showSkuDialog(() => {
create();
showSkuDialog((e: GoodsBean) => {
create(e);
});
};
</script>
@@ -360,7 +362,7 @@ const placeOrder = async () => {
margin-left: 12rpx;
margin-right: 12rpx;
background: #FFFFFF;
border-radius: 7rpx 7rpx 7rpx 7rpx;
border-radius: 7rpx;
color: #F32B2B;
}
@@ -467,7 +469,7 @@ const placeOrder = async () => {
font-size: 24rpx;
color: #999999;
position: absolute;
top: 40rpx;
top: 50rpx;
left: 100rpx;
}
}
@@ -527,7 +529,7 @@ const placeOrder = async () => {
.bottom-view {
background: #FFFFFF;
padding: 20rpx 30rpx 78rpx 33rpx;
padding: 20rpx 30rpx 78rpx 30rpx;
position: fixed;
left: 0;
right: 0;

View File

@@ -2,21 +2,27 @@
<view class='content'>
<view class='card-view'>
<image class='goods-image' :src='assetsUrl("test_bg.png")' />
<view class='c-flex-column' style='flex: 1'>
<text class='goods-name'>商品名称</text>
<text class='goods-sku'>颜色尺码</text>
</view>
<template v-for='(item,index) in orderBean?.orderGoods' :key='index'>
<image class='goods-image' :src='item?.images' />
<view class='c-flex-column' style='flex: 1'>
<text class='goods-name'>{{ item?.goodsName }}</text>
<text class='goods-sku'>{{ item?.stockStock?.colorName }} {{ item?.stockStock?.sizeName }}</text>
</view>
<view class='c-flex-column'>
<text class='goods-num'>x1</text>
<text class='goods-price'>¥100</text>
</view>
<view class='c-flex-column'>
<text class='goods-num'>x{{ item?.goodsNum }}</text>
<view class='c-flex-row'>
<text class='goods-price' style='margin-right: 10rpx;text-decoration: line-through'>¥{{ item?.originPrice }}
</text>
<text class='goods-price'>¥{{ item?.consumePrice }}</text>
</view>
</view>
</template>
</view>
<view class='card-view'>
<text style='flex: 1'>实付</text>
<text style='color: #F32B2B'>¥100</text>
<text style='color: #F32B2B'>¥{{ orderBean?.totalPrice || 0 }}</text>
</view>
<view class='bottom-view c-flex-row'>
@@ -27,12 +33,57 @@
</template>
<script lang='ts' setup>
import { pay, progress } from '@/api/groupbuy';
import { OrderBean } from '@/api/groupbuy/types';
import { parseParameter, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { useUserStore } from '@/store';
import { assetsUrl } from '@/utils/assets';
const userState = useUserStore();
const { terminalInfo } = storeToRefs(userState);
const orderBean = ref<OrderBean>();
onLoad((e: any) => {
orderBean.value = JSON.parse(decodeURIComponent(e?.orderBean));
});
const payment = () => {
let signParams = {
return_url: 'return_url',
total_amount: orderBean.value?.totalPrice,
client_sn: orderBean.value?.id,
terminal_sn: terminalInfo.value.terminalSn,
subject: 'subject',
subject_img: 'subject_img',
merchant_name: 'merchant_name',
notify_url: 'https://www.baidu.com'
};
// signParams = util.sortASCII(signParams, true);
sortASCII(signParams, true);
//参数拼接
// const signStr = util.pars(signParams) + '&key=' + terminalInfo.value.terminalKey;
const signStr = parseParameter(signParams) + '&key=' + terminalInfo.value.terminalKey;
console.log('签名字符串', signStr);
// const sign = utilMd5.hexMD5(signStr).toUpperCase();
const sign = hexMD5(signStr);
console.log('签名结果', sign);
const params = {
'id': orderBean.value?.id,
'orderSn': signParams.client_sn,
'terminal_key': terminalInfo.value.terminalKey,
'terminal_sn': terminalInfo.value.terminalSn
};
progress(params);
pay({
'orderId': orderBean.value?.id,
'result': JSON.stringify('{payResult:xxx}')
});
};
</script>
<style lang='scss' scoped>

View File

@@ -106,7 +106,7 @@ onShow(async () => {
getCompanyList(userInfo.value.maOpenId).then(res => {
const companyList = res.map((res: { company: any }) => res.company);
const userList = res.map((res: { user: any }) => res.user);
if(getCompanyId() == undefined) {
// if(!getCompanyId()) {
uni.showActionSheet({
itemList: companyList.map((res: { companyName: string }) => res.companyName),
success: (res) => {
@@ -114,7 +114,7 @@ onShow(async () => {
store.setUserInfo(userList[res.tapIndex]);
}
});
}
// }
});
}
});

View File

@@ -31,21 +31,24 @@
<view class='shopping-cart' @click.stop='goPath("/pages/mall/subs/shoppingcart/index")'>
<image :src='assetsUrl("ic_shopping_cart.png")' />
<text v-if='shoppingCartList.length>0'>{{ shoppingCartList.length }}</text>
<text v-if='shoppingCartList.length>0'>{{ shoppingCartList?.length }}</text>
</view>
</view>
<sku-dialog ref='skuDialogRef' />
</template>
<script setup lang='ts'>
import SkuDialog from '@/components/sku-dialog.vue';
import { assetsUrl } from '@/utils/assets';
import { goPath } from '@/utils';
import { getCategoryList, getGoodsList } from '@/api/goods';
import { CategoryBean, GoodsBean } from '@/api/goods/types';
import useShoppingCartStore from '@/store/modules/shoppingcart';
const shoppingCart = useShoppingCartStore();
const { shoppingCartList } = storeToRefs(shoppingCart);
const shoppingCartStore = useShoppingCartStore();
const { shoppingCartList } = storeToRefs(shoppingCartStore);
const skuDialogRef = ref();
const categoryList = ref<CategoryBean[]>([]);
const categorySelectedIndex = ref<number>(0);
const goodsList = ref<GoodsBean[]>([]);
@@ -89,10 +92,8 @@ const getRandomFloatInRange = (a: number, b: number) => {
};
const addShoppingCart = (goodsBean: GoodsBean) => {
shoppingCart.save({
...goodsBean,
name: goodsBean.goodsName,
count: 1
skuDialogRef.value.show(goodsBean.goodsId, (e: GoodsBean) => {
shoppingCartStore.save(e);
});
};
</script>

View File

@@ -32,7 +32,7 @@
<text>选择</text>
<text>规格 颜色/尺码</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' />
<text>1种颜色可选</text>
<text>{{ getStockColorCount }}种颜色可选</text>
</view>
<view class='divider' style='width:auto;height: 0.5rpx;margin-left: 100rpx;display: none' />
<view class='c-flex-row' style='display: none' @click.stop='showSkuDialog'>
@@ -127,6 +127,12 @@ onLoad(async (e: any) => {
recommendList.value = rows;
});
const getStockColorCount = computed(() => {
const list = Array.from(new Set(goodsBean.value?.stocks?.map(item => item.colorName)))
.map(colorName => goodsBean.value?.stocks?.find(item => item.colorName === colorName)!);
return list.length;
});
const swiperChange = (e: any) => {
swiperIndex.value = e.detail.current;
};
@@ -136,21 +142,16 @@ const goBack = () => {
};
const showSkuDialog = (fn: Function) => {
skuDialogRef.value.show(fn);
skuDialogRef.value.show(goodsBean.value?.id, fn);
};
const addShoppingCart = () => {
showSkuDialog((e: StockBean) => {
const index = shoppingCartStore.getSameGoodsIndex(goodsBean.value?.id || '', e.colorId, e.sizeId);
showSkuDialog((e: GoodsBean) => {
const index = shoppingCartStore.getSameGoodsIndex(goodsBean.value?.id || '', e.checkedStock?.colorId, e.checkedStock?.sizeId);
if(index >= 0) {
shoppingCartStore.updateCount(index, e.count);
shoppingCartStore.updateCount(index, e.checkedStock?.count);
} else {
shoppingCartStore.save(
{
...goodsBean.value,
checkedStock: e
}
);
shoppingCartStore.save(e);
}
showToast('加入购物车成功');
});
@@ -260,13 +261,13 @@ const placeOrder = () => {
.goods-sku-view {
background: #FFFFFF;
padding: 0 30rpx;
padding: 20rpx 30rpx;
margin-top: 20rpx;
view:nth-of-type(n+1) {
position: relative;
align-items: center;
height: 105rpx;
height: 85rpx;
text:nth-of-type(1) {
width: 100rpx;
@@ -292,14 +293,14 @@ const placeOrder = () => {
font-size: 24rpx;
color: #999999;
position: absolute;
top: 80rpx;
top: 50rpx;
left: 100rpx;
}
}
view:nth-of-type(1) {
align-items: flex-start;
padding-top: 20rpx;
//padding-top: 20rpx;
}
}

View File

@@ -10,7 +10,7 @@
<scroll-view>
<view class='c-flex-column' v-for='(item, index) in shoppingCartList' :key='index'>
<view class='c-flex-row'>
<image v-show='isEditMode' class='checkbox'
<image class='checkbox'
:src='assetsUrl(item.checked?"ic_checkbox_active_red.png":"ic_checkbox_normal.png")'
@click.stop='item.checked=!item.checked' />
<image class='goods-image' :src='item.images' />
@@ -18,8 +18,8 @@
<text>{{ item.name }}</text>
<view class='c-flex-row'>
<view class='sku-view' @click.stop='showSkuDialog(index)'>
<text>{{ item?.checkedStock.colorName }}{{ item?.checkedStock.sizeName }}
x{{ item?.checkedStock.count }}
<text>{{ item?.checkedStock?.colorName }}{{ item?.checkedStock?.sizeName }}
x{{ item?.checkedStock?.count }}
</text>
<image :src='assetsUrl("ic_arrow_down_gray.png")' />
</view>
@@ -29,7 +29,7 @@
<view class='count-image' @click.stop='countChange(index,false)'>
<image :src='assetsUrl("ic_reduce.png")' />
</view>
<text>{{ item?.checkedStock.count || 0 }}</text>
<text>{{ item?.checkedStock?.count || 0 }}</text>
<view class='count-image' @click.stop='countChange(index,true)'>
<image :src='assetsUrl("ic_plus.png")' />
</view>
@@ -85,7 +85,7 @@ watch(checkedAll, (newValue) => {
});
const totalPayPrice = computed(() => {
return shoppingCartList.value.reduce((a, b) => a + b.price * b.checkedStock.count, 0);
return shoppingCartList.value.filter(res => res.checked).reduce((a, b) => a + b.price * b.checkedStock.count, 0);
});
const deleteShoppingCart = () => {
@@ -93,21 +93,21 @@ const deleteShoppingCart = () => {
};
const showSkuDialog = (index: number) => {
new Promise((resolve, reject) => {
new Promise((resolve, _) => {
temporaryGoodsBean.value = shoppingCartList.value[index];
temporaryStockBean.value = shoppingCartList.value[index].checkedStock;
resolve(temporaryGoodsBean.value);
}).then(res => {
skuDialogRef.value.show((e: StockBean) => {
}).then((res: any) => {
skuDialogRef.value.show(res.id, (e: GoodsBean) => {
//重新选择sku后判断当前购物中是否存在相同sku的商品
const existsIndex = shoppingCartStore.getSameGoodsIndex(temporaryGoodsBean.value?.id || '', e.colorId, e.sizeId);
const existsIndex = shoppingCartStore.getSameGoodsIndex(res?.id || '', e.checkedStock.colorId, e.checkedStock.sizeId);
//不存在则更新当前商品sku
if(existsIndex < 0) {
shoppingCartStore.updateStock(index, e);
shoppingCartStore.updateStock(index, e.checkedStock);
}
//如果已存在,则更新已存在商品数量,并删除当前商品
else {
shoppingCartStore.updateCount(existsIndex, e.count);
shoppingCartStore.updateCount(existsIndex, e.checkedStock.count - (temporaryGoodsBean?.value?.checkedStock?.count || 0));
if(existsIndex != index) {
shoppingCartStore.delete(index);
}

View File

@@ -11,11 +11,12 @@ import CouponItem from './components/coupon-item.vue';
import { getCouponList } from '@/api/user';
import { useUserStore } from '@/store';
import { getCompanyId } from '@/utils';
import { GroupBuyBean } from '@/api/groupbuy/types';
const store = useUserStore();
const { userInfo } = storeToRefs(store);
const coupons = ref([]);
const coupons = ref<GroupBuyBean[]>([]);
onLoad(async () => {
fetchData(0);