621 lines
15 KiB
Vue
621 lines
15 KiB
Vue
<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>
|