修复bug
This commit is contained in:
@@ -23,6 +23,8 @@ const Config = {
|
||||
shareLink: "https://www.howhowfresh.com/h5/",
|
||||
shareImage: "https://hanguomcn.oss-ap-northeast-2.aliyuncs.com/images/20250901105756_1756695476183_56ac07ac.png",
|
||||
shareDesc: "分享专属二维码邀请新用户注册即邀请成功。一起来注册体验点餐功能。",
|
||||
// Zelle 支付二维码图片地址
|
||||
zellePayPath: "https://www.howhowfresh.com/minio/ruoyi/2026/04/14/ef120c97e50c419da2263a59d6679229.png",
|
||||
// 登录端口
|
||||
userPort: 1,
|
||||
// 手机区号数组
|
||||
|
||||
@@ -666,6 +666,7 @@
|
||||
"coupon": {
|
||||
"all-merchants": "Applicable to all merchants",
|
||||
"expiry-date": "Expiry date: ",
|
||||
"merchant-only": "Only available at {name}",
|
||||
"merchant-specific": "For specific merchant use",
|
||||
"no-coupons": "You currently do not have any coupons",
|
||||
"redeem-now": "Redeem now",
|
||||
|
||||
@@ -666,6 +666,7 @@
|
||||
"coupon": {
|
||||
"all-merchants": "适用于所有商户使用",
|
||||
"expiry-date": "到期日:",
|
||||
"merchant-only": "仅{name}可用",
|
||||
"merchant-specific": "指定商户使用",
|
||||
"no-coupons": "您目前没有任何优惠券",
|
||||
"redeem-now": "立即兑换",
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
"name" : "CHEFLINK delivery",
|
||||
"appid" : "__UNI__06509BE",
|
||||
"description" : "",
|
||||
"versionName" : "3.0.6",
|
||||
"versionCode" : 306,
|
||||
"versionName" : "3.1.0",
|
||||
"versionCode" : 310,
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { useConfigStore, useUserStore } from "@/store";
|
||||
import { dayjs } from "@/plugin/index";
|
||||
import Config from '@/config/index'
|
||||
import ChooseImage from '@/components/choose-image/choose-image.vue'
|
||||
import CheckoutSkeleton from "./components/checkout-skeleton.vue";
|
||||
import ChangePhone from "./components/change-phone.vue";
|
||||
import PriceDetail from "./components/price-detail.vue";
|
||||
@@ -19,7 +20,8 @@ import {
|
||||
appMerchantOrderCreateOrderCartBatchPost,
|
||||
appMerchantOrderCalculatePriceCartBatchPost,
|
||||
appMerchantCartListMerchantPost,
|
||||
appMerchantOrderPayOrderBatchPost
|
||||
appMerchantOrderPayOrderBatchPost,
|
||||
appMerchantOrderZipPayVoucherPost
|
||||
} from "@/service";
|
||||
import useEventEmit from "@/hooks/useEventEmit";
|
||||
import {EventEnum} from "@/constant/enums";
|
||||
@@ -268,14 +270,90 @@ const openPriceDetail = () => {
|
||||
function navigateTo(url: string) {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
function chooseCardForCheckout() {
|
||||
payMethodSelected.value = 1
|
||||
uni.navigateTo({
|
||||
url: '/pages-user/pages/select-credit-card/index',
|
||||
events: {
|
||||
selectedCard: function(data: any) {
|
||||
if (data) {
|
||||
payMethodOptions.value.cardId = data.cardId || ''
|
||||
payMethodOptions.value.cardNumber = data.cardNumber || ''
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 支付参数
|
||||
const payMethodOptions = ref({
|
||||
orderId: '',
|
||||
cardId: '',
|
||||
cardNumber: '',
|
||||
payMethod: 1, // 支付方式 1信用卡 2余额
|
||||
payPassword: '',
|
||||
})
|
||||
const payMethodPopupVisible = ref(false)
|
||||
const payMethodSelected = ref(1)
|
||||
const zellePopupVisible = ref(false)
|
||||
const zelleOrderIdForVoucher = ref('')
|
||||
const voucherChooseRef = ref<InstanceType<typeof ChooseImage>>()
|
||||
const voucherSubmitting = ref(false)
|
||||
const zellePayPath = Config.zellePayPath;
|
||||
|
||||
function openPayMethodPopup() {
|
||||
payMethodSelected.value = Number(payMethodOptions.value.payMethod || 1)
|
||||
payMethodPopupVisible.value = true
|
||||
}
|
||||
function confirmPayMethodForSettle() {
|
||||
payMethodOptions.value.payMethod = Number(payMethodSelected.value || 1)
|
||||
payMethodPopupVisible.value = false
|
||||
handleGoSettle()
|
||||
}
|
||||
function openUploadVoucher() {
|
||||
if (!zelleOrderIdForVoucher.value) {
|
||||
uni.showToast({ title: t('pages-store.order.voucherUploadFailed'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
voucherChooseRef.value?.init()
|
||||
}
|
||||
function normalizeVoucherUrl(payload: unknown): string {
|
||||
if (Array.isArray(payload)) {
|
||||
const first = payload[0]
|
||||
return typeof first === 'string' ? first : ''
|
||||
}
|
||||
return typeof payload === 'string' ? payload : ''
|
||||
}
|
||||
async function onVoucherImageUploaded(urls: unknown) {
|
||||
const zipPayVoucher = normalizeVoucherUrl(urls)
|
||||
if (!zipPayVoucher || !zelleOrderIdForVoucher.value) {
|
||||
uni.showToast({ title: t('pages-store.order.voucherUploadFailed'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (voucherSubmitting.value) return
|
||||
voucherSubmitting.value = true
|
||||
try {
|
||||
await appMerchantOrderZipPayVoucherPost({
|
||||
body: {
|
||||
orderId: zelleOrderIdForVoucher.value,
|
||||
zipPayVoucher,
|
||||
}
|
||||
})
|
||||
uni.showToast({ title: t('pages-store.order.voucherSubmitSuccess'), icon: 'none' })
|
||||
zellePopupVisible.value = false
|
||||
setTimeout(() => {
|
||||
if (zelleOrderIdForVoucher.value) {
|
||||
uni.reLaunch({
|
||||
url: `/pages-store/pages/order/index?id=${zelleOrderIdForVoucher.value}`
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
} catch {
|
||||
uni.showToast({ title: t('pages-store.order.voucherSubmitFailed'), icon: 'none' })
|
||||
} finally {
|
||||
voucherSubmitting.value = false
|
||||
}
|
||||
}
|
||||
useEventEmit(EventEnum.CHOOSE_PAYMENT_METHOD, (data) => {
|
||||
if(data) {
|
||||
if(data.payMethod === 1) {
|
||||
@@ -791,6 +869,9 @@ function handleGoSettle() {
|
||||
// 如果是余额支付,弹出支付密码弹窗
|
||||
if(payMethodOptions.value.payMethod === 2) {
|
||||
passwordInputRef.value?.showPasswordInput()
|
||||
} else if (payMethodOptions.value.payMethod === 3) {
|
||||
zelleOrderIdForVoucher.value = resOrderIds.value?.[0] ? String(resOrderIds.value[0]) : ''
|
||||
zellePopupVisible.value = true
|
||||
} else {
|
||||
appMerchantOrderPayOrderBatch()
|
||||
}
|
||||
@@ -799,6 +880,9 @@ function handleGoSettle() {
|
||||
// 如果是余额支付,弹出支付密码弹窗
|
||||
if(payMethodOptions.value.payMethod === 2) {
|
||||
passwordInputRef.value?.showPasswordInput()
|
||||
} else if (payMethodOptions.value.payMethod === 3) {
|
||||
zelleOrderIdForVoucher.value = resOrderIds.value?.[0] ? String(resOrderIds.value[0]) : ''
|
||||
zellePopupVisible.value = true
|
||||
} else {
|
||||
appMerchantOrderPayOrderBatch()
|
||||
}
|
||||
@@ -852,6 +936,9 @@ function handleGoSettle() {
|
||||
// 如果是余额支付,弹出支付密码弹窗
|
||||
if(payMethodOptions.value.payMethod === 2) {
|
||||
passwordInputRef.value?.showPasswordInput()
|
||||
} else if (payMethodOptions.value.payMethod === 3) {
|
||||
zelleOrderIdForVoucher.value = String(resOrderId.value || '')
|
||||
zellePopupVisible.value = true
|
||||
} else {
|
||||
appMerchantOrderPayOrder()
|
||||
}
|
||||
@@ -860,6 +947,9 @@ function handleGoSettle() {
|
||||
// 如果是余额支付,弹出支付密码弹窗
|
||||
if(payMethodOptions.value.payMethod === 2) {
|
||||
passwordInputRef.value?.showPasswordInput()
|
||||
} else if (payMethodOptions.value.payMethod === 3) {
|
||||
zelleOrderIdForVoucher.value = String(resOrderId.value || '')
|
||||
zellePopupVisible.value = true
|
||||
} else {
|
||||
appMerchantOrderPayOrder()
|
||||
}
|
||||
@@ -1345,27 +1435,6 @@ function handleClose(merchantId?: string) {
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<view class="checkout-block">
|
||||
<view class="checkout-section-label">{{ t('pages-store.checkout.payMethodSection') }}</view>
|
||||
<view
|
||||
@click="navigateTo('/pages-user/pages/choose-paymethod/index')"
|
||||
class="checkout-card checkout-gutter checkout-pay-row"
|
||||
>
|
||||
<view class="flex-1 min-w-0 pr-16rpx">
|
||||
<template v-if="payMethodOptions.payMethod === 1">
|
||||
<text class="checkout-card-primary block">{{ t('pages-user.choosePaymethod.creditCard') }}</text>
|
||||
<text v-if="payMethodOptions.cardId" class="checkout-card-secondary block mt-10rpx">{{ payMethodOptions.cardNumber }}</text>
|
||||
<text v-else class="checkout-card-secondary block mt-10rpx">{{ t("pages-user.member.creditCard") }}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="checkout-card-primary block">{{ t('pages-user.choosePaymethod.wallet') }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<image src="@img/chef/142.png" class="checkout-chevron" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 确认订单(单店) -->
|
||||
<view v-if="orderType === 'normal' && cartDataList.length > 0" class="checkout-block">
|
||||
<view class="checkout-section-label">{{ t('pages-store.checkout.confirmOrder') }}</view>
|
||||
@@ -1875,7 +1944,7 @@ function handleClose(merchantId?: string) {
|
||||
<text class="checkout-bottom-bar-one-line">{{ subtotalOneLineText }}</text>
|
||||
</view>
|
||||
<wd-button
|
||||
@click="handleGoSettle"
|
||||
@click="openPayMethodPopup"
|
||||
custom-class="checkout-pay-btn"
|
||||
>{{ t('pages-store.checkout.pay') }}
|
||||
</wd-button>
|
||||
@@ -1891,6 +1960,89 @@ function handleClose(merchantId?: string) {
|
||||
<!-- 价格明细 -->
|
||||
<price-detail ref="priceDetailRef" />
|
||||
<password-container @success="payPawSuccess" ref="passwordInputRef" />
|
||||
<choose-image ref="voucherChooseRef" :count="1" @change="onVoucherImageUploaded" />
|
||||
<wd-popup v-model="payMethodPopupVisible" position="bottom" @close="payMethodPopupVisible = false">
|
||||
<view class="checkout-paymethod-popup">
|
||||
<view class="checkout-paymethod-popup-title">{{ t('pages-store.checkout.payMethodSection') }}</view>
|
||||
|
||||
<view
|
||||
class="checkout-paymethod-option"
|
||||
:class="{ 'is-active': payMethodSelected === 1 }"
|
||||
@click="payMethodSelected = 1"
|
||||
>
|
||||
<view class="checkout-paymethod-option-left">
|
||||
<view class="checkout-paymethod-dot">
|
||||
<view class="checkout-paymethod-dot-inner"></view>
|
||||
</view>
|
||||
<view class="checkout-paymethod-text-wrap">
|
||||
<view class="checkout-paymethod-name">{{ t('pages-user.choosePaymethod.creditCard') }}</view>
|
||||
<view class="checkout-paymethod-desc">{{ payMethodOptions.cardId ? payMethodOptions.cardNumber : t('pages-user.member.creditCard') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="checkout-paymethod-replace-btn"
|
||||
@click.stop="chooseCardForCheckout"
|
||||
>
|
||||
{{ payMethodOptions.cardId ? t('pages-user.choosePaymethod.replace') : t('pages-user.member.creditCard') }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="checkout-paymethod-option"
|
||||
:class="{ 'is-active': payMethodSelected === 2 }"
|
||||
@click="payMethodSelected = 2"
|
||||
>
|
||||
<view class="checkout-paymethod-option-left">
|
||||
<view class="checkout-paymethod-dot">
|
||||
<view class="checkout-paymethod-dot-inner"></view>
|
||||
</view>
|
||||
<view class="checkout-paymethod-text-wrap">
|
||||
<view class="checkout-paymethod-name">{{ t('pages-user.choosePaymethod.wallet') }}</view>
|
||||
<view class="checkout-paymethod-desc">Balance</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="checkout-paymethod-option"
|
||||
:class="{ 'is-active': payMethodSelected === 3 }"
|
||||
@click="payMethodSelected = 3"
|
||||
>
|
||||
<view class="checkout-paymethod-option-left">
|
||||
<view class="checkout-paymethod-dot">
|
||||
<view class="checkout-paymethod-dot-inner"></view>
|
||||
</view>
|
||||
<view class="checkout-paymethod-text-wrap">
|
||||
<view class="checkout-paymethod-name">Zelle</view>
|
||||
<view class="checkout-paymethod-desc">Scan QR and upload voucher</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wd-button @click="confirmPayMethodForSettle" custom-class="checkout-paymethod-confirm-btn" block>
|
||||
{{ t('common.confirm') }}
|
||||
</wd-button>
|
||||
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
<wd-popup v-model="zellePopupVisible" custom-style="background: transparent;" @close="zellePopupVisible = false">
|
||||
<view class="w-620rpx rounded-24rpx bg-white p-28rpx">
|
||||
<view class="text-32rpx lh-32rpx text-#333 font-600 text-center mb-24rpx">Zelle</view>
|
||||
<image v-if="zellePayPath" :src="zellePayPath" mode="aspectFit" class="w-100% h-520rpx rounded-16rpx bg-#F7F7F7" />
|
||||
<view v-else class="h-220rpx rounded-16rpx bg-#F7F7F7 center text-#999 text-24rpx">
|
||||
zellePayPath is empty
|
||||
</view>
|
||||
<wd-button
|
||||
:loading="voucherSubmitting"
|
||||
:disabled="voucherSubmitting"
|
||||
@click="openUploadVoucher"
|
||||
custom-class="!h-92rpx !rounded-40rpx !text-30rpx !mt-26rpx !bg-#14181B"
|
||||
block
|
||||
>
|
||||
{{ t('pages-store.order.uploadPaidVoucher') }}
|
||||
</wd-button>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -2075,6 +2227,122 @@ $checkout-gutter: 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.checkout-paymethod-popup {
|
||||
padding: 36rpx 30rpx 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 28rpx 28rpx 0 0;
|
||||
}
|
||||
|
||||
.checkout-paymethod-popup-title {
|
||||
font-size: 34rpx;
|
||||
line-height: 1.3;
|
||||
font-weight: 700;
|
||||
color: #14181b;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.checkout-paymethod-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 128rpx;
|
||||
padding: 0 24rpx;
|
||||
border: 2rpx solid #ececec;
|
||||
border-radius: 22rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 18rpx;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
|
||||
.checkout-paymethod-option.is-active {
|
||||
border-color: #14181b;
|
||||
background: #f7f8fa;
|
||||
box-shadow: 0 6rpx 20rpx rgba(20, 24, 27, 0.08);
|
||||
}
|
||||
|
||||
.checkout-paymethod-option-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.checkout-paymethod-dot {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #d0d0d0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 18rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.checkout-paymethod-dot-inner {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.checkout-paymethod-option.is-active .checkout-paymethod-dot {
|
||||
border-color: #14181b;
|
||||
}
|
||||
|
||||
.checkout-paymethod-option.is-active .checkout-paymethod-dot-inner {
|
||||
background: #14181b;
|
||||
}
|
||||
|
||||
.checkout-paymethod-text-wrap {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.checkout-paymethod-name {
|
||||
font-size: 30rpx;
|
||||
line-height: 1.25;
|
||||
font-weight: 600;
|
||||
color: #1f1f1f;
|
||||
}
|
||||
|
||||
.checkout-paymethod-desc {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.3;
|
||||
color: #8b8b8b;
|
||||
}
|
||||
|
||||
.checkout-paymethod-replace-btn {
|
||||
margin-left: 16rpx;
|
||||
height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid #e6e6e6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.checkout-paymethod-option:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.checkout-paymethod-confirm-btn {
|
||||
margin-top: 28rpx !important;
|
||||
height: 96rpx !important;
|
||||
border-radius: 46rpx !important;
|
||||
font-size: 30rpx !important;
|
||||
font-weight: 600 !important;
|
||||
background: #14181b !important;
|
||||
}
|
||||
|
||||
.checkout-confirm-head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -20,6 +20,16 @@ import dayjs from 'dayjs'
|
||||
import useEventEmit from "@/hooks/useEventEmit";
|
||||
const configStore = useConfigStore();
|
||||
|
||||
function fillI18nParams(template: string, params: Record<string, string | number>) {
|
||||
let text = template
|
||||
Object.keys(params).forEach((key) => {
|
||||
const value = String(params[key] ?? '')
|
||||
text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), value)
|
||||
text = text.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value)
|
||||
})
|
||||
return text
|
||||
}
|
||||
|
||||
// 价格明细
|
||||
const priceDetailRef = ref<InstanceType<typeof PriceDetail>>();
|
||||
// 打开价格明细
|
||||
@@ -128,6 +138,12 @@ const orderTotalItemCount = computed(() => {
|
||||
return orderDishList.value.reduce((sum, item) => sum + (Number(item?.count) || 0), 0)
|
||||
})
|
||||
|
||||
const orderTotalItemCountText = computed(() => {
|
||||
return fillI18nParams(t('pages.order.totalItemCount'), {
|
||||
count: orderTotalItemCount.value,
|
||||
})
|
||||
})
|
||||
|
||||
const orderTotalPrice = computed(() => {
|
||||
const n = orderDetail.value?.paidAmount ?? orderDetail.value?.actualPrice ?? 0
|
||||
return Number(n).toFixed(2)
|
||||
@@ -292,55 +308,24 @@ const orderNavSecondaryLine = computed(() => {
|
||||
const voucherChooseRef = ref<InstanceType<typeof ChooseImage>>()
|
||||
const voucherSubmitting = ref(false)
|
||||
|
||||
// function openUploadVoucher() {
|
||||
// voucherChooseRef.value?.init()
|
||||
// }
|
||||
function openUploadVoucher() {
|
||||
voucherChooseRef.value?.init()
|
||||
}
|
||||
|
||||
// function normalizeVoucherUrl(payload: unknown): string {
|
||||
// if (Array.isArray(payload)) {
|
||||
// const first = payload[0]
|
||||
// return typeof first === 'string' ? first : ''
|
||||
// }
|
||||
// return typeof payload === 'string' ? payload : ''
|
||||
// }
|
||||
function normalizeVoucherUrl(payload: unknown): string {
|
||||
if (Array.isArray(payload)) {
|
||||
const first = payload[0]
|
||||
return typeof first === 'string' ? first : ''
|
||||
}
|
||||
return typeof payload === 'string' ? payload : ''
|
||||
}
|
||||
|
||||
// async function onVoucherImageUploaded(urls: unknown) {
|
||||
// const zipPayVoucher = normalizeVoucherUrl(urls)
|
||||
// if (!zipPayVoucher) {
|
||||
// uni.showToast({ title: t('pages-store.order.voucherUploadFailed'), icon: 'none' })
|
||||
// return
|
||||
// }
|
||||
// const id = orderDetail.value?.id
|
||||
// if (id == null || String(id).trim() === '') {
|
||||
// uni.showToast({ title: t('pages-store.order.voucherUploadFailed'), icon: 'none' })
|
||||
// return
|
||||
// }
|
||||
// if (voucherSubmitting.value) return
|
||||
// voucherSubmitting.value = true
|
||||
// try {
|
||||
// await appMerchantOrderZipPayVoucherPost({
|
||||
// body: {
|
||||
// orderId: Number(id),
|
||||
// zipPayVoucher,
|
||||
// },
|
||||
// options: { hideErrorToast: true },
|
||||
// })
|
||||
// uni.showToast({ title: t('pages-store.order.voucherSubmitSuccess'), icon: 'none' })
|
||||
// setTimeout(() => {
|
||||
// appMerchantOrderDetail()
|
||||
// }, 500)
|
||||
// }
|
||||
// catch {
|
||||
// uni.showToast({ title: t('pages-store.order.voucherSubmitFailed'), icon: 'none' })
|
||||
// }
|
||||
// finally {
|
||||
// voucherSubmitting.value = false
|
||||
// }
|
||||
// }
|
||||
|
||||
const FIXED_VOUCHER_URL = 'https://www.howhowfresh.com/prod-api/resource/oss/download/2037827804362121218'
|
||||
|
||||
async function openUploadVoucher() {
|
||||
async function onVoucherImageUploaded(urls: unknown) {
|
||||
const zipPayVoucher = normalizeVoucherUrl(urls)
|
||||
if (!zipPayVoucher) {
|
||||
uni.showToast({ title: t('pages-store.order.voucherUploadFailed'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
const id = orderDetail.value?.id
|
||||
if (id == null || String(id).trim() === '') {
|
||||
uni.showToast({ title: t('pages-store.order.voucherUploadFailed'), icon: 'none' })
|
||||
@@ -352,7 +337,7 @@ async function openUploadVoucher() {
|
||||
await appMerchantOrderZipPayVoucherPost({
|
||||
body: {
|
||||
orderId: id,
|
||||
zipPayVoucher: FIXED_VOUCHER_URL,
|
||||
zipPayVoucher,
|
||||
},
|
||||
options: { hideErrorToast: true },
|
||||
})
|
||||
@@ -575,7 +560,7 @@ async function openUploadVoucher() {
|
||||
<view class="goods-price-right">
|
||||
<text class="goods-price">${{ orderTotalPrice }}</text>
|
||||
<text class="goods-count">
|
||||
{{ t('pages.order.totalItemCount', { count: orderTotalItemCount }) }}
|
||||
{{ orderTotalItemCountText }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -856,8 +841,8 @@ async function openUploadVoucher() {
|
||||
<password-container @success="payPawSuccess" ref="passwordInputRef" />
|
||||
<!-- 核销订单 -->
|
||||
<use-code ref="useCodeRef" />
|
||||
<!-- 支付凭证:相册/拍照(暂时不使用文件上传,改用固定URL) -->
|
||||
<choose-image ref="voucherChooseRef" :count="1" />
|
||||
<!-- 支付凭证:相册/拍照,上传后回调真实 URL -->
|
||||
<choose-image ref="voucherChooseRef" :count="1" @change="onVoucherImageUploaded" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -386,6 +386,15 @@ const specPopupDisplayPrice = computed(() => {
|
||||
return centStringToPriceText(baseCent);
|
||||
});
|
||||
|
||||
const detailDisplayPrice = computed(() => {
|
||||
const dish = dishDetailData.value as any;
|
||||
const firstSpecPrice = dish?.merchantSideDishVoList?.[0]?.merchantSideDishItemVoList?.[0]?.price;
|
||||
if (firstSpecPrice != null && String(firstSpecPrice) !== "") {
|
||||
return firstSpecPrice;
|
||||
}
|
||||
return dish?.actualSalePrice;
|
||||
});
|
||||
|
||||
function openSpecPopup() {
|
||||
const sideList = dishDetailData.value?.merchantSideDishVoList ?? [];
|
||||
for (const item of sideList) {
|
||||
@@ -765,7 +774,7 @@ function getStoreDetail() {
|
||||
<view class="price-current">
|
||||
<text class="price-current__sym">$</text>
|
||||
<text class="price-current__num">{{
|
||||
dishDetailData?.discountPrice
|
||||
detailDisplayPrice
|
||||
}}</text>
|
||||
</view>
|
||||
<text
|
||||
@@ -836,12 +845,12 @@ function getStoreDetail() {
|
||||
}}</view>
|
||||
<text class="section-product-name">{{ dishDetailData?.dishName }}</text>
|
||||
<view class="spec-grid">
|
||||
<view class="spec-cell">
|
||||
<!-- <view class="spec-cell">
|
||||
<text class="spec-cell__val">{{ specFromWords(0) }}</text>
|
||||
<text class="spec-cell__lbl">{{
|
||||
t("pages-store.store.dishDetail.origin")
|
||||
}}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
<view class="spec-cell">
|
||||
<text class="spec-cell__val">{{ specUnitDisplay }}</text>
|
||||
<text class="spec-cell__lbl">{{
|
||||
@@ -856,12 +865,12 @@ function getStoreDetail() {
|
||||
t("pages-store.store.dishDetail.category")
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="spec-cell">
|
||||
<!-- <view class="spec-cell">
|
||||
<text class="spec-cell__val">{{ specFromWords(1) }}</text>
|
||||
<text class="spec-cell__lbl">{{
|
||||
t("pages-store.store.dishDetail.allergen")
|
||||
}}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 产品简介 -->
|
||||
@@ -1335,7 +1344,7 @@ function getStoreDetail() {
|
||||
|
||||
.spec-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0;
|
||||
border: 1rpx solid #e8e8e8;
|
||||
border-radius: 16rpx;
|
||||
|
||||
@@ -556,14 +556,20 @@ function feeLine(
|
||||
return { show: true, orig, cur };
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
getCartList();
|
||||
});
|
||||
// onMounted(() => {
|
||||
// loading.value = true;
|
||||
// getCartList();
|
||||
// });
|
||||
|
||||
onShow(() => {
|
||||
if (userStore.isLogin) {
|
||||
userStore.getAppointmentTime();
|
||||
// 从结算页返回时强制刷新购物车,避免已结算商品残留
|
||||
loading.value = true;
|
||||
getCartList();
|
||||
} else {
|
||||
dataList.value = [];
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -42,6 +42,26 @@ function formatCouponDetail(item: any) {
|
||||
return t('pages-store.store.discount')
|
||||
}
|
||||
|
||||
function fillI18nParams(template: string, params: Record<string, string | number>) {
|
||||
let text = template
|
||||
Object.keys(params).forEach((key) => {
|
||||
const value = String(params[key] ?? '')
|
||||
text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), value)
|
||||
text = text.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value)
|
||||
})
|
||||
return text
|
||||
}
|
||||
|
||||
function formatCouponMerchantText(item: any) {
|
||||
const name = String(item?.merchantVo?.merchantName || '').trim()
|
||||
if (name) {
|
||||
return fillI18nParams(t('pages-user.coupon.merchant-only'), { name })
|
||||
}
|
||||
return item?.snapshotMerchantId
|
||||
? t('pages-user.coupon.merchant-specific')
|
||||
: t('pages-user.coupon.all-merchants')
|
||||
}
|
||||
|
||||
function getList(pageNum: number, pageSize: number) {
|
||||
return appCouponUserCouponListPost({
|
||||
params: {
|
||||
@@ -150,7 +170,7 @@ function handleSubmit() {
|
||||
{{ item.snapshotNameZh }}
|
||||
</view>
|
||||
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
||||
{{ item.snapshotMerchantId ? t('pages-user.coupon.merchant-specific') : t('pages-user.coupon.all-merchants') }}
|
||||
{{ formatCouponMerchantText(item) }}
|
||||
</view>
|
||||
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
||||
{{ dayjs(Number(item.snapshotValidEnd)).format('YYYY-MM-DD HH:mm') }}{{ isEnLocale() ? ' expires' : '到期' }}
|
||||
|
||||
@@ -48,6 +48,13 @@ function getDishPromoLabel(item: Record<string, unknown>): string {
|
||||
return typeof raw === 'string' && raw.trim() ? raw.trim() : ''
|
||||
}
|
||||
|
||||
function getFeaturedDishDisplayPrice(item: Record<string, any>) {
|
||||
const firstSpecPrice = item?.merchantSideDishVoList?.[0]?.merchantSideDishItemVoList?.[0]?.price
|
||||
if (firstSpecPrice != null && String(firstSpecPrice) !== '') return firstSpecPrice
|
||||
if (item?.actualSalePrice != null && String(item.actualSalePrice) !== '') return item.actualSalePrice
|
||||
return item?.discountPrice ?? 0
|
||||
}
|
||||
|
||||
function navigateTo(url: string) {
|
||||
if(userStore.checkLogin()) {
|
||||
uni.navigateTo({
|
||||
@@ -445,7 +452,7 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
|
||||
<view class="featured-dish-body">
|
||||
<view class="featured-dish-meta flex items-start justify-between gap-12rpx mb-14rpx">
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="featured-dish-price">US${{ item?.discountPrice }}</text>
|
||||
<text class="featured-dish-price">US${{ getFeaturedDishDisplayPrice(item) }}</text>
|
||||
<!-- <text
|
||||
v-if="Number(item?.originalPrice) > Number(item?.discountPrice)"
|
||||
class="featured-dish-original"
|
||||
|
||||
+65
-37
@@ -42,6 +42,16 @@ const {paging, dataList, loading, queryList, firstLoaded} = usePage<MerchantOrde
|
||||
}
|
||||
})
|
||||
|
||||
function fillI18nParams(template: string, params: Record<string, string | number>) {
|
||||
let text = template
|
||||
Object.keys(params).forEach((key) => {
|
||||
const value = String(params[key] ?? '')
|
||||
text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), value)
|
||||
text = text.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value)
|
||||
})
|
||||
return text
|
||||
}
|
||||
|
||||
function normalizeTimestamp(input: unknown): number | null {
|
||||
if (input == null || input === '') return null
|
||||
|
||||
@@ -71,7 +81,14 @@ function formatOrderCardTime(item: MerchantOrderVo) {
|
||||
}
|
||||
|
||||
function formatOrderPrice(item: MerchantOrderVo) {
|
||||
return (item.paidAmount ?? item.actualPrice ?? 0).toFixed(2)
|
||||
const raw = item?.paidAmount ?? item?.actualPrice ?? 0
|
||||
if (raw == null || raw === '') return '0.00'
|
||||
if (typeof raw === 'number') return raw.toFixed(2)
|
||||
const str = String(raw).trim()
|
||||
if (!str) return '0.00'
|
||||
if (!str.includes('.')) return `${str}.00`
|
||||
const [intPart, decimalPart = ''] = str.split('.')
|
||||
return `${intPart}.${(decimalPart + '00').slice(0, 2)}`
|
||||
}
|
||||
|
||||
function getOrderDishes(item: MerchantOrderVo): Array<Record<string, any>> {
|
||||
@@ -94,6 +111,42 @@ function getTotalDishCount(item: MerchantOrderVo) {
|
||||
return getOrderDishes(item).reduce((s, d) => s + (d.count || 0), 0)
|
||||
}
|
||||
|
||||
function getTotalDishCountText(item: MerchantOrderVo) {
|
||||
return fillI18nParams(t('pages.order.totalItemCount'), {
|
||||
count: getTotalDishCount(item),
|
||||
})
|
||||
}
|
||||
|
||||
function isSameCode(value: unknown, code: unknown) {
|
||||
return String(value ?? '') === String(code ?? '')
|
||||
}
|
||||
|
||||
function isRefundApplied(item: MerchantOrderVo) {
|
||||
return isSameCode(item.refundStatus, OrderCancelStatus.APPLIED)
|
||||
}
|
||||
|
||||
function isRefundApproved(item: MerchantOrderVo) {
|
||||
return isSameCode(item.refundStatus, OrderCancelStatus.APPROVED)
|
||||
}
|
||||
|
||||
function isRefundRejected(item: MerchantOrderVo) {
|
||||
return isSameCode(item.refundStatus, OrderCancelStatus.REJECTED)
|
||||
}
|
||||
|
||||
function getOrderStatusText(item: MerchantOrderVo) {
|
||||
if (isRefundApplied(item)) return t('pages-store.order.orderStatus.refund')
|
||||
if (isRefundApproved(item)) return t('pages-store.order.orderStatus.agreeRefund')
|
||||
if (isRefundRejected(item)) return t('pages-store.order.orderStatus.rejectRefund')
|
||||
if (isSameCode(item.orderStatus, OrderStatus.CANCELLED)) return t('pages-store.order.orderStatus.cancelled')
|
||||
if (isSameCode(item.orderStatus, OrderStatus.PENDING_PAYMENT)) return t('pages-store.order.orderStatus.pendingPayment')
|
||||
if (isSameCode(item.orderStatus, OrderStatus.HAS_PENDING_PAYMENT)) return t('pages-store.order.orderStatus.hasPendingPayment')
|
||||
if (isSameCode(item.orderStatus, OrderStatus.MERCHANT_ACCEPTED)) return t('pages-store.order.orderStatus.received')
|
||||
if (isSameCode(item.receiveMethod, 1) && isSameCode(item.orderStatus, OrderStatus.DELIVERING)) return t('pages-store.order.orderStatus.delivering')
|
||||
if (isSameCode(item.orderStatus, OrderStatus.COMPLETED)) return t('pages-store.order.orderStatus.delivered')
|
||||
if (isSameCode(item.orderStatus, OrderStatus.MERCHANT_REJECTED)) return t('pages-store.store.orderStatus.rejected')
|
||||
return '--'
|
||||
}
|
||||
|
||||
function handleClick(item: MerchantOrderVo) {
|
||||
uni.navigateTo({
|
||||
url: '/pages-store/pages/order/index?id=' + item.id
|
||||
@@ -171,43 +224,18 @@ defineExpose({
|
||||
>
|
||||
<view class="flex-center-sb items-start gap-16rpx">
|
||||
<text class="order-time">{{ formatOrderCardTime(item) }}</text>
|
||||
<text class="order-status shrink-0">
|
||||
<template v-if="
|
||||
item.refundStatus === OrderCancelStatus.APPLIED ||
|
||||
item.refundStatus === OrderCancelStatus.APPROVED ||
|
||||
item.refundStatus === OrderCancelStatus.REJECTED
|
||||
">
|
||||
<template v-if="item.refundStatus === OrderCancelStatus.APPLIED">
|
||||
{{ t('pages-store.order.orderStatus.refund') }}
|
||||
</template>
|
||||
<template v-else-if="item.refundStatus === OrderCancelStatus.APPROVED">
|
||||
{{ t('pages-store.order.orderStatus.agreeRefund') }}
|
||||
</template>
|
||||
<template v-else-if="item.refundStatus === OrderCancelStatus.REJECTED">
|
||||
{{ t('pages-store.order.orderStatus.rejectRefund') }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="item.orderStatus === OrderStatus.CANCELLED">{{ t('pages-store.order.orderStatus.cancelled') }}</template>
|
||||
<template v-if="item.orderStatus === OrderStatus.PENDING_PAYMENT">{{ t('pages-store.order.orderStatus.pendingPayment') }}</template>
|
||||
<template v-if="item.orderStatus === OrderStatus.HAS_PENDING_PAYMENT">{{ t('pages-store.order.orderStatus.hasPendingPayment') }}</template>
|
||||
<template v-if="item.orderStatus === OrderStatus.MERCHANT_ACCEPTED">{{ t('pages-store.order.orderStatus.received') }}</template>
|
||||
<template v-if="item.receiveMethod === 1 && item.orderStatus === OrderStatus.DELIVERING">{{ t('pages-store.order.orderStatus.delivering') }}</template>
|
||||
<template v-if="item.orderStatus === OrderStatus.COMPLETED">{{ t('pages-store.order.orderStatus.delivered') }}</template>
|
||||
<template v-if="item.orderStatus === OrderStatus.MERCHANT_REJECTED">
|
||||
<text class="text-#FF6106">{{ t('pages-store.store.orderStatus.rejected') }}</text>
|
||||
</template>
|
||||
</template>
|
||||
<text class="order-status shrink-0" :class="{ 'text-#FF6106': isSameCode(item.orderStatus, OrderStatus.MERCHANT_REJECTED) }">
|
||||
{{ getOrderStatusText(item) }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="store-row">
|
||||
<image src="@img/chef/126.png" class="store-icon" mode="aspectFit"></image>
|
||||
<text class="store-name line-clamp-1">{{ item.merchantVo?.merchantName }}</text>
|
||||
<view v-if="item.receiveMethod === 1" class="recv-tag recv-tag--del">
|
||||
<view v-if="isSameCode(item.receiveMethod, 1)" class="recv-tag recv-tag--del">
|
||||
{{ t('pages.order.DEL') }}
|
||||
</view>
|
||||
<view v-if="item.receiveMethod === 2" class="recv-tag recv-tag--pu">
|
||||
<view v-if="isSameCode(item.receiveMethod, 2)" class="recv-tag recv-tag--pu">
|
||||
{{ t('pages.order.PU') }}
|
||||
</view>
|
||||
</view>
|
||||
@@ -237,38 +265,38 @@ defineExpose({
|
||||
</scroll-view>
|
||||
<view class="price-block shrink-0">
|
||||
<text class="price-num">${{ formatOrderPrice(item) }}</text>
|
||||
<text class="price-meta">{{ t('pages.order.totalItemCount', { count: getTotalDishCount(item) }) }}</text>
|
||||
<text class="price-meta">{{ getTotalDishCountText(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-row" @click.stop>
|
||||
<view class="action-left">
|
||||
<image
|
||||
v-if="item.receiveMethod === 2 && item.orderStatus === OrderStatus.MERCHANT_ACCEPTED"
|
||||
v-if="isSameCode(item.receiveMethod, 2) && isSameCode(item.orderStatus, OrderStatus.MERCHANT_ACCEPTED)"
|
||||
src="@img/chef/127.png"
|
||||
class="action-icon"
|
||||
/>
|
||||
<image
|
||||
v-if="item.orderStatus >= OrderStatus.MERCHANT_ACCEPTED && item.receiveMethod === 1"
|
||||
v-if="!isSameCode(item.orderStatus, OrderStatus.PENDING_PAYMENT) && !isSameCode(item.orderStatus, OrderStatus.CANCELLED) && isSameCode(item.receiveMethod, 1)"
|
||||
src="@img/chef/128.png"
|
||||
class="action-icon"
|
||||
@click="callPhone(item.merchantVo?.phone)"
|
||||
/>
|
||||
</view>
|
||||
<view class="action-btns">
|
||||
<template v-if="item.refundStatus !== OrderCancelStatus.APPLIED && item.refundStatus !== OrderCancelStatus.APPROVED">
|
||||
<template v-if="!isRefundApplied(item) && !isRefundApproved(item)">
|
||||
<view
|
||||
v-if="item.orderStatus !== OrderStatus.CANCELLED && item.orderStatus !== OrderStatus.COMPLETED && item.orderStatus !== OrderStatus.MERCHANT_REJECTED"
|
||||
v-if="!isSameCode(item.orderStatus, OrderStatus.CANCELLED) && !isSameCode(item.orderStatus, OrderStatus.COMPLETED) && !isSameCode(item.orderStatus, OrderStatus.MERCHANT_REJECTED)"
|
||||
class="btn-outline"
|
||||
@click="handleCancelClick(item)"
|
||||
>{{ t('pages.order.cancelOrder') }}</view>
|
||||
<view
|
||||
v-if="item.receiveMethod === 2 && item.orderStatus === OrderStatus.MERCHANT_ACCEPTED"
|
||||
v-if="isSameCode(item.receiveMethod, 2) && isSameCode(item.orderStatus, OrderStatus.MERCHANT_ACCEPTED)"
|
||||
class="btn-primary"
|
||||
@click="handleClick(item)"
|
||||
>{{ t('pages-store.order.writeOff') }}</view>
|
||||
<view
|
||||
v-if="item.orderStatus === OrderStatus.COMPLETED && item?.dishReviewVoList?.length === 0"
|
||||
v-if="isSameCode(item.orderStatus, OrderStatus.COMPLETED) && item?.dishReviewVoList?.length === 0"
|
||||
class="btn-primary"
|
||||
@click="handleClick(item)"
|
||||
>{{ t('common.evaluate') }}</view>
|
||||
|
||||
@@ -2898,6 +2898,8 @@
|
||||
'merchantVo'?: MerchantVo;
|
||||
/** 份数 */
|
||||
'copies'?: number;
|
||||
|
||||
'actualSalePrice'?: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user