Files
2023-11-22 18:55:55 +08:00

621 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import UvNavbar from "@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue";
import { useRouter } from "@/hooks/useRouter";
import { onLoad, onPageScroll, onShow } from "@dcloudio/uni-app";
import { ref, unref } from "vue";
import { useMainStore } from '@/store/store';
import { storeToRefs } from "pinia";
import { orderConfirm, orderCreate } from "@/api/order";
import { useInterface } from "@/hooks/useInterface";
import { addressTabs, discountsPriceRows, payRows, priceRows } from "@/pages/submitOrder/index.data";
import Goods from "@/components/goodsComponents/Goods.vue"
import UvRadioGroup from "@/uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue";
import UvRadio from "@/uni_modules/uv-radio/components/uv-radio/uv-radio.vue";
import { doPayment, PayType } from "@/utils/paymentUtils";
import CouponSelect from "@/pages/submitOrder/components/coupon-select.vue";
import Header from "@/components/Header/index.vue"
import { useScroll } from "@/hooks/useScroll";
import { nextIcon } from "@/utils/images";
const {getParams, push, goBack} = useRouter()
const {toast, loading, hideLoading} = useInterface();
const mainStore = useMainStore();
const {selectAddress, defaultAddress, address} = storeToRefs(mainStore);
const routerParams = ref({})
const selectCouponRef = ref(false)
const couponList = ref([])
const selectCoupon = ref({})
const currentCouponId = ref(0)
const cartIds = ref('')
const flag = ref(false)
const selectCouponFn = async (coupon) => {
currentCouponId.value = coupon.couponId
orderDetail.value = await orderConfirm({
cartId: unref(routerParams).cartId,
addressId: unref(selectAddress)?.id || undefined,
couponId: currentCouponId.value
})
selectCouponRef.value.close()
}
const addressTabSelect = ref(0) // 提货方式
/**
* 选择提货方式
* @param tab
*/
function changeAddressTab(tab) {
if (tab.value === 1) {
toast({title: '敬请期待'})
} else {
addressTabSelect.value = tab.value
}
}
/**
* 去选择地址
*/
function gotoSelectAddress() {
push({url: '/pages/address/address'}, {data: {type: 'select', cartId: unref(routerParams).cartId}})
}
/**
* 获取默认选择的地址
* @returns {Promise<void>}
*/
async function doGetSelectAddress() {
// store内部没有地址就先去获取地址
if (unref(address).length <= 0) {
await mainStore.getAddressList(1)
}
// 获取完地址还是空
if (unref(address).length <= 0) {
return
}
// 没有默认地址设置默认地址
if (!selectAddress.value || !selectAddress.value?.id) {
defaultAddress.value && defaultAddress.value.id ? mainStore.setSelectAddress(defaultAddress.value.id) : mainStore.setSelectAddress(unref(address)[0].id)
}
}
const payType = ref(PayType["0"]) // 支付方式
// 初始订单信息
const orderDetail = ref({
cartInfo: [],
priceGroup: {
costPrice: 0, payIntegral: 0, storeFreePostage: 0, storePostage: 0, totalPrice: 0, vipPrice: 0
}
})
/**
* 获取初始订单信息
* todo 如果没有地址,选择地址后需要重新调用
* @returns {Promise<void>}
*/
async function doGetInitConfirmOrder() {
orderDetail.value = await orderConfirm({
cartId: unref(routerParams).cartId,
addressId: unref(selectAddress)?.id || undefined
})
cartIds.value = unref(routerParams).cartId
currentCouponId.value = orderDetail.value.priceGroup.couponId
flag.value = true
}
const subLoading = ref(false) // 加载中
const showCouponSelect = (index) => {
if (index === 0) {
selectCouponRef.value.open()
}
}
async function handleConfirm() {
if (!unref(selectAddress) || !unref(selectAddress).id) {
toast({title: '请先选择地址'})
return
}
subLoading.value = true
try {
const payInfo = await doCreateServiceOrder()
// 去拉取支付
await doPayment({type: payType.value, payInfo})
// #ifndef H5
push({url: '/pages/payStatus/index'}, {type: 'redirectTo'})
// #endif
} catch (e) {
console.error(e)
toast({title: '支付失败'})
push({url: '/pages/payStatus/index'}, {type: 'redirectTo'})
} finally {
subLoading.value = false
mainStore.cartId = null
}
}
/**
* 服务端创建订单
*/
async function doCreateServiceOrder() {
try {
loading({title: '订单创建中...'})
const res = await orderCreate({
key: unref(orderDetail).orderKey,
addressId: unref(selectAddress).id,
bargainId: 0,
combinationId: 0,
couponId: currentCouponId.value,
from: '',
mark: '',
pinkId: 0,
seckillId: 0,
shippingType: addressTabSelect.value,
useIntegral: 0,
isChannel: 1,
});
return res.result
} finally {
hideLoading()
}
}
const {scrollTop} = useScroll()
/**
* 检查路由参数
*/
function checkRouterParam(params) {
if (!params.cartId) {
toast({title: '路由参数错误'})
return
}
routerParams.value = params
}
onLoad(async options => {
try {
const params = getParams(options)
await checkRouterParam(params)
await doGetInitConfirmOrder()
} catch (e) {
console.error(e)
}
})
onShow(async ()=>{
await doGetSelectAddress()
})
</script>
<template>
<view class="order-confirm">
<!-- header -->
<Header :scroll-top="scrollTop">
提交订单
</Header>
<!-- 地址 -->
<view class="address-box">
<view
class="address-box__inner"
>
<view class="tab-box">
<!-- tab top -->
<view class="title-row flex flex-ai__center flex-jc__sb">
<view
class="item flex flex-ai__center flex-jc__center"
:class="{active:addressTabSelect === tab.value}"
v-for="tab in addressTabs"
:key="tab.value"
@click.stop="changeAddressTab(tab)"
>
{{ tab.label }}
</view>
</view>
<!-- address info -->
<view
class="address-row flex flex-ai__center flex-jc__sb"
v-if="address.length>0"
@click="gotoSelectAddress"
>
<view
class="flex flex-ai__center"
v-if="selectAddress"
>
<uv-icon
name="map"
size="22"
/>
<view class="info">
<view>
{{ selectAddress.realName }} {{ selectAddress.phone }}
</view>
<view class="address-info">
{{ selectAddress.province }} - {{ selectAddress.city }}- {{ selectAddress.district }} -
{{ selectAddress.detail }}
</view>
</view>
</view>
<image
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
<view
class="address-row flex flex-ai__center flex-jc__sb"
v-else
@click="gotoSelectAddress"
>
点击添加地址
<image
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
</view>
</view>
</view>
<!-- 商品清单 -->
<view
class="main-box goods-box"
v-show="orderDetail.cartInfo.length>0"
>
<view class="title-row flex flex-ai__end">
商品清单
<span class="small">
{{ orderDetail.cartInfo.length }}
</span>
</view>
<template v-for="sku in orderDetail.cartInfo">
<view class="goods-row">
<Goods
info-padding="0 10rpx 20rpx 10rpx"
:goods="sku.productInfo"
row
imgWidth="200rpx"
>
<template #options="{goods}">
<view class="goods-detail">
<view class="sku-row">
{{ goods.attrInfo.sku }}
</view>
<view class="price-row flex flex-ai__center flex-jc__sb">
<view class="price flex flex-ai__end">
{{ sku.truePrice }}
<span class="old-price">
{{ goods.otPrice }}
</span>
</view>
<view class="cart-num">
x{{ sku.cartNum }}
</view>
</view>
</view>
</template>
</Goods>
</view>
</template>
</view>
<!-- 优惠价格信息 -->
<view class="main-box price-box">
<template
v-for="(row,index) in discountsPriceRows"
:key="index"
>
<view
class="row flex flex-ai__center flex-jc__sb"
@click="showCouponSelect(index)"
>
<view class="label">{{ row.label }}</view>
<view class="value">
{{ row.prefix }}
{{ orderDetail["priceGroup"][row.field]?.toFixed(2) }}
<image
v-if="row.field=== 'couponPrice'"
class="arrow-icon"
:src="nextIcon"
alt=""
/>
</view>
</view>
</template>
</view>
<view class="coupon-select">
<coupon-select
v-if="flag"
ref="selectCouponRef"
:id="cartIds"
:currentCouponId="currentCouponId"
@submitCoupon="selectCouponFn"
/>
</view>
<!-- 支付方式 -->
<view class="main-box pay-box">
<uv-radio-group
v-model="payType"
class="pay-box__inner flex flex-ai__center flex-jc__center flex-wrap"
shape="circle"
activeColor="#ec6e47"
>
<template
v-for="payItem in payRows"
:key="payItem.type"
>
<view class="pay-item">
<uv-radio
:name="payItem.type"
:disabled="payItem.disabled"
>
<view class="flex flex-ai__center flex-jc__sb">
<view
class="pay_icon"
>
<image
:src="payItem.icon"
alt=""
/>
</view>
<view class="text">
<view>
{{ payItem.label }}
</view>
<view class="e-text">
{{ payItem.eLabel }}
</view>
</view>
</view>
</uv-radio>
</view>
</template>
</uv-radio-group>
</view>
<!-- 价格信息 -->
<view class="main-box price-box">
<template
v-for="(row,index) in priceRows"
:key="index"
>
<view class="row flex flex-ai__center flex-jc__sb">
<view class="label">{{ row.label }}</view>
<view class="value">
{{ row.prefix }}
{{ orderDetail["priceGroup"][row.field]?.toFixed(2) }}
</view>
</view>
</template>
</view>
<!-- 底部操作条 -->
<view class="bottom-empty-height"></view>
<view class="bottom-option-box flex flex-jc__sb flex-ai__center">
<view class="info">
总计{{ orderDetail.priceGroup.totalPrice?.toFixed(2) }}
</view>
<view
class="animation-button sub-button"
:class="{disabled:subLoading}"
@click="handleConfirm"
>
提交订单
</view>
</view>
</view>
</template>
<style
scoped
lang="scss"
>
$bottomHeight: 100rpx;
// global
.main-box {
@include usePadding(35, 0);
margin-top: 20rpx;
width: 100%;
background: $white-color;
}
.arrow-icon {
width: 20rpx;
height: 20rpx;
}
.bottom-empty-height {
width: 100%;
height: $bottomHeight;
padding-bottom: 30rpx;
}
// box
.order-confirm {
position: relative;
.address-box {
@include usePadding(35, 30);
width: 100%;
background: $white-color;
&__inner {
width: 100%;
border: 1px solid #E6E6E6;
box-sizing: border-box;
.tab-box {
width: 100%;
// tab top
.title-row {
.item {
@include usePadding(0, 15);
flex: 1 0 auto;
background: #e6e6e6;
transition: all .4s;
&.active {
background: #333;
color: $white-color;
}
}
}
// address info
.address-row {
@include usePadding(25, 70);
width: 100%;
.info {
margin-left: 20rpx;
.address-info {
color: #333333;
margin: 10rpx 0;
//max-width: 60vw;
//overflow: hidden;
//white-space: nowrap;
//text-overflow: ellipsis;
}
}
}
}
}
}
.goods-box {
.title-row {
@include usePadding(0, 17);
color: #333;
font-size: 32rpx;
border-bottom: 1rpx solid #E6E6E6;
.small {
margin-left: 15rpx;
font-size: 24rpx;
}
}
.goods-row {
@include usePadding(0, 40);
.goods-detail {
width: 100%;
.sku-row {
color: $tips-color;
font-size: 24rpx;
margin-bottom: 30rpx;
}
.price-row {
color: $tips-color;
.price {
color: $primary-color;
font-size: 30rpx;
.old-price {
color: $tips-color;
margin-left: 10rpx;
font-size: 20rpx;
text-decoration: line-through;
}
}
.cart-num {
font-size: 24rpx;
}
}
}
}
}
.price-box {
.row {
@include usePadding(0, 30);
color: #333;
font-size: 28rpx;
border-bottom: 1rpx solid #E6E6E6;
&:last-child {
border-bottom: none;
}
.value {
color: #999;
}
}
}
.pay-box {
:deep(.uv-radio-group ) {
@include usePadding(0, 32);
gap: 20rpx;
.pay-item {
flex: 1 0 45%;
.pay_icon {
width: 63rpx;
margin: 0 20rpx;
aspect-ratio: 1/1;
image {
width: 100%;
height: 100%;
}
}
.text {
font-size: 24rpx;
.e-text {
font-size: 22rpx;
}
}
}
}
}
.bottom-option-box {
position: fixed;
width: 100%;
height: $bottomHeight;
bottom: 0;
left: 0;
background: $white-color;
.info {
@include usePadding(34, 0)
}
.sub-button {
@include usePadding(50, 0);
height: 100%;
display: flex;
align-items: center;
justify-content: center;
transition: all .3s;
}
}
}
:deep(.coupon-select) .uni-popup__wrapper {
height: auto;
max-height: 1000rpx;
overflow-y: auto;
}
</style>