462 lines
10 KiB
Vue
462 lines
10 KiB
Vue
<template>
|
||
<scroll-view scroll-y class="viewport">
|
||
<!-- 收货地址 -->
|
||
<navigator
|
||
:render-link="false"
|
||
v-if="selectedAddress"
|
||
class="shipment"
|
||
hover-class="none"
|
||
url="/pagesMember/address/address?from=order"
|
||
>
|
||
<view class="user"> {{ selectedAddress.receiver }} {{ selectedAddress.contact }} </view>
|
||
<view class="address">
|
||
{{ selectedAddress.fullLocation }} {{ selectedAddress.address }}
|
||
</view>
|
||
<text class="icon icon-right"></text>
|
||
</navigator>
|
||
<navigator
|
||
:render-link="false"
|
||
v-else
|
||
class="shipment"
|
||
hover-class="none"
|
||
url="/pagesMember/address/address?from=order"
|
||
>
|
||
<view class="address"> 请选择收货地址 </view>
|
||
<text class="icon icon-right"></text>
|
||
</navigator>
|
||
|
||
<!-- 商品信息 -->
|
||
<view class="goods">
|
||
<navigator
|
||
:render-link="false"
|
||
v-for="item in orderPreList?.goods"
|
||
:key="item.skuId"
|
||
:url="`/pages/goods/goods?id=${item.id}`"
|
||
class="item"
|
||
hover-class="none"
|
||
>
|
||
<image class="picture" :src="item.picture" />
|
||
<view class="meta">
|
||
<view class="name ellipsis"> {{ item.name }} </view>
|
||
<view class="attrs"> {{ item.attrsText.replaceAll(',', ' ') }} </view>
|
||
<view class="prices">
|
||
<view class="pay-price symbol"> {{ item.payPrice }} </view>
|
||
<view class="price symbol">{{ item.price }}</view>
|
||
</view>
|
||
<view class="count">x{{ item.count }}</view>
|
||
</view>
|
||
</navigator>
|
||
</view>
|
||
|
||
<!-- 配送及支付方式 -->
|
||
<view class="related">
|
||
<view class="item">
|
||
<text class="text">配送时间</text>
|
||
<picker :range="deliveryList" range-key="text" @change="onChangeDelivery">
|
||
<view class="icon-fonts picker">{{ activeDelivery.text }}</view>
|
||
</picker>
|
||
</view>
|
||
<view class="item">
|
||
<text class="text">订单备注</text>
|
||
<input
|
||
class="input"
|
||
:cursor-spacing="30"
|
||
placeholder="可选,建议留言前先与商家沟通确认"
|
||
v-model="buyerMessage"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付金额 -->
|
||
<view class="settlement">
|
||
<view class="item">
|
||
<text class="text">商品总价: </text>
|
||
<text class="number symbol">{{ orderPreList?.summary.totalPrice.toFixed(2) }}</text>
|
||
</view>
|
||
<view class="item">
|
||
<text class="text">运费: </text>
|
||
<text class="number symbol">{{ orderPreList?.summary.postFee.toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 吸底工具栏 -->
|
||
<view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
|
||
<view class="total-pay symbol">
|
||
<text class="number"> {{ orderPreList?.summary.totalPayPrice.toFixed(2) }} </text>
|
||
</view>
|
||
<view
|
||
class="button"
|
||
:class="{
|
||
disabled: !selectedAddress || orderPreList == null || orderPreList?.goods.length === 0,
|
||
}"
|
||
@tap="handleSubmitOrder"
|
||
>
|
||
提交订单
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import {
|
||
getMemberOrderPreAPI,
|
||
getMemberOrderPreNowAPI,
|
||
getMemberOrderRepurchaseByIdAPI,
|
||
postCreateOrderAPI,
|
||
} from '@/services/order'
|
||
import { useSelectedAddressStore } from '@/stores/modules/selectedAddress'
|
||
import type { OrderPreResult } from '@/types/order'
|
||
import { onShow } from '@dcloudio/uni-app'
|
||
import { computed, ref } from 'vue'
|
||
|
||
// 获取屏幕边界到安全区域距离
|
||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||
// 订单备注
|
||
const buyerMessage = ref('')
|
||
// 配送时间
|
||
const deliveryList = ref([
|
||
{ type: 1, text: '时间不限 (周一至周日)' },
|
||
{ type: 2, text: '工作日送 (周一至周五)' },
|
||
{ type: 3, text: '周末配送 (周六至周日)' },
|
||
])
|
||
// 当前配送时间下标
|
||
const activeIndex = ref(0)
|
||
// 当前配送时间
|
||
const activeDelivery = computed(() => deliveryList.value[activeIndex.value])
|
||
// 修改配送时间
|
||
const onChangeDelivery: UniHelper.SelectorPickerOnChange = (ev) => {
|
||
activeIndex.value = ev.detail.value
|
||
}
|
||
|
||
// 获取预付订单
|
||
const orderPreList = ref<OrderPreResult>()
|
||
const query = defineProps<{
|
||
skuId?: string
|
||
count?: number
|
||
id?: string
|
||
}>()
|
||
const getOrderPre = async () => {
|
||
let res
|
||
if (query?.skuId && query?.count) {
|
||
// 立即购买
|
||
res = await getMemberOrderPreNowAPI({
|
||
skuId: query.skuId,
|
||
count: query.count,
|
||
})
|
||
} else if (query?.id) {
|
||
// 再次购买
|
||
res = await getMemberOrderRepurchaseByIdAPI(query.id)
|
||
} else {
|
||
// 购物车
|
||
res = await getMemberOrderPreAPI()
|
||
}
|
||
console.log(res)
|
||
if (res.code === '1') {
|
||
orderPreList.value = res.result
|
||
}
|
||
}
|
||
|
||
onShow(() => {
|
||
getOrderPre()
|
||
})
|
||
|
||
// 收货地址
|
||
const useSelectedAddress = useSelectedAddressStore()
|
||
const selectedAddress = computed(() => {
|
||
// 优先使用已选择的地址
|
||
if (useSelectedAddress.selectedAddress) {
|
||
return useSelectedAddress.selectedAddress
|
||
}
|
||
// 如果orderPreList已加载,则查找默认地址
|
||
if (orderPreList.value?.userAddresses && orderPreList.value.userAddresses.length > 0) {
|
||
return orderPreList.value.userAddresses.find((item) => item.isDefault === 1)
|
||
}
|
||
// 都不存在时返回null
|
||
return null
|
||
})
|
||
|
||
// 提交订单
|
||
const handleSubmitOrder = async () => {
|
||
// 判断是否有收货地址
|
||
if (!selectedAddress.value) {
|
||
uni.showToast({
|
||
title: '请选择收货地址',
|
||
icon: 'none',
|
||
})
|
||
return
|
||
}
|
||
|
||
// 判断是否有商品
|
||
if (!orderPreList.value || orderPreList.value.goods.length === 0) {
|
||
uni.showToast({
|
||
title: '没有可提交的商品',
|
||
icon: 'none',
|
||
})
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 创建订单
|
||
const res = await postCreateOrderAPI({
|
||
addressId: selectedAddress.value.id,
|
||
deliveryTimeType: activeDelivery.value.type as 1 | 2 | 3,
|
||
buyerMessage: buyerMessage.value,
|
||
payType: 1, // 在线支付
|
||
payChannel: 2, // 微信支付
|
||
goods: orderPreList.value.goods.map((item) => ({
|
||
skuId: item.skuId,
|
||
count: item.count,
|
||
})),
|
||
})
|
||
|
||
if (res.code === '1') {
|
||
// 提交成功,跳转到支付页面
|
||
uni.showToast({
|
||
title: '订单提交成功',
|
||
icon: 'success',
|
||
})
|
||
console.log(res.result)
|
||
|
||
// 跳转到支付页面,传递订单ID
|
||
uni.redirectTo({
|
||
url: `/pagesOrder/detail/detail?id=${res.result.id}`,
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: res.msg || '订单提交失败',
|
||
icon: 'error',
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('提交订单失败', error)
|
||
uni.showToast({
|
||
title: '订单提交失败',
|
||
icon: 'error',
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
background-color: #f4f4f4;
|
||
}
|
||
|
||
.viewport {
|
||
flex: unset;
|
||
height: calc(100vh - 100rpx);
|
||
}
|
||
|
||
.symbol::before {
|
||
content: '¥';
|
||
font-size: 80%;
|
||
margin-right: 5rpx;
|
||
}
|
||
|
||
.shipment {
|
||
margin: 20rpx;
|
||
padding: 30rpx 30rpx 30rpx 84rpx;
|
||
font-size: 26rpx;
|
||
border-radius: 10rpx;
|
||
background: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png)
|
||
20rpx center / 50rpx no-repeat #fff;
|
||
position: relative;
|
||
|
||
.icon {
|
||
font-size: 36rpx;
|
||
color: #333;
|
||
transform: translateY(-50%);
|
||
position: absolute;
|
||
top: 50%;
|
||
right: 20rpx;
|
||
}
|
||
|
||
.user {
|
||
color: #333;
|
||
margin-bottom: 5rpx;
|
||
}
|
||
|
||
.address {
|
||
color: #666;
|
||
}
|
||
}
|
||
|
||
.goods {
|
||
margin: 20rpx;
|
||
padding: 0 20rpx;
|
||
border-radius: 10rpx;
|
||
background-color: #fff;
|
||
|
||
.item {
|
||
display: flex;
|
||
padding: 30rpx 0;
|
||
border-top: 1rpx solid #eee;
|
||
|
||
&:first-child {
|
||
border-top: none;
|
||
}
|
||
|
||
.picture {
|
||
width: 170rpx;
|
||
height: 170rpx;
|
||
border-radius: 10rpx;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.meta {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
position: relative;
|
||
}
|
||
|
||
.name {
|
||
height: 80rpx;
|
||
font-size: 26rpx;
|
||
color: #444;
|
||
}
|
||
|
||
.attrs {
|
||
line-height: 1.8;
|
||
padding: 0 15rpx;
|
||
margin-top: 6rpx;
|
||
font-size: 24rpx;
|
||
align-self: flex-start;
|
||
border-radius: 4rpx;
|
||
color: #888;
|
||
background-color: #f7f7f8;
|
||
}
|
||
|
||
.prices {
|
||
display: flex;
|
||
align-items: baseline;
|
||
margin-top: 6rpx;
|
||
font-size: 28rpx;
|
||
|
||
.pay-price {
|
||
margin-right: 10rpx;
|
||
color: #cf4444;
|
||
}
|
||
|
||
.price {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
text-decoration: line-through;
|
||
}
|
||
}
|
||
|
||
.count {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
font-size: 26rpx;
|
||
color: #444;
|
||
}
|
||
}
|
||
}
|
||
|
||
.related {
|
||
margin: 20rpx;
|
||
padding: 0 20rpx;
|
||
border-radius: 10rpx;
|
||
background-color: #fff;
|
||
|
||
.item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
min-height: 80rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.input {
|
||
flex: 1;
|
||
text-align: right;
|
||
margin: 20rpx 0;
|
||
padding-right: 20rpx;
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.item .text {
|
||
width: 125rpx;
|
||
}
|
||
|
||
.picker {
|
||
color: #666;
|
||
}
|
||
|
||
.picker::after {
|
||
content: '\e6c2';
|
||
}
|
||
}
|
||
|
||
/* 结算清单 */
|
||
.settlement {
|
||
margin: 20rpx;
|
||
padding: 0 20rpx;
|
||
border-radius: 10rpx;
|
||
background-color: #fff;
|
||
|
||
.item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 80rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.danger {
|
||
color: #cf4444;
|
||
}
|
||
}
|
||
|
||
/* 吸底工具栏 */
|
||
.toolbar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: calc(var(--window-bottom));
|
||
z-index: 1;
|
||
|
||
background-color: #fff;
|
||
height: 100rpx;
|
||
padding: 0 20rpx;
|
||
border-top: 1rpx solid #eaeaea;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
box-sizing: content-box;
|
||
|
||
.total-pay {
|
||
font-size: 40rpx;
|
||
color: #cf4444;
|
||
|
||
.decimal {
|
||
font-size: 75%;
|
||
}
|
||
}
|
||
|
||
.button {
|
||
width: 220rpx;
|
||
text-align: center;
|
||
line-height: 72rpx;
|
||
font-size: 26rpx;
|
||
color: #fff;
|
||
border-radius: 72rpx;
|
||
background-color: #27ba9b;
|
||
}
|
||
|
||
.disabled {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
</style>
|