修改样式

This commit is contained in:
2026-04-11 11:55:03 +08:00
parent ef9210a567
commit ec9282a64f
59 changed files with 8708 additions and 2558 deletions
+284 -100
View File
@@ -1,19 +1,20 @@
<script setup lang="ts">
import {
appMerchantOrderDetailPost,
appCollectCollectPost,
type MerchantOrderVo,
appUserCardSelectDefaultPost, appMerchantOrderPayOrderPost, appMerchantOrderCancelOrderPost
appUserCardSelectDefaultPost,
appMerchantOrderPayOrderPost,
appMerchantOrderCancelOrderPost,
appMerchantOrderZipPayVoucherPost,
} from "@/service";
import { debounce } from 'throttle-debounce'
import ChooseImage from '@/components/choose-image/choose-image.vue'
const { t } = useI18n();
import OrderProgress from './components/order-progress.vue'
import OrderDetailSkeleton from './components/order-detail-skeleton.vue'
import Collection from "@/components/collection/index.vue";
import PriceDetail from "@/pages-store/pages/order/components/price-detail.vue";
import CancelOrder from "@/pages-store/pages/order/components/cancel-order.vue";
import {useConfigStore} from "@/store";
import {CollectionType, OrderStatus, EventEnum, OrderCancelStatus} from "@/constant/enums";
import {OrderStatus, EventEnum, OrderCancelStatus} from "@/constant/enums";
import {formatTimestampWithMonthName, formatTimestampShort, navGoogleMap, callPhone} from "@/utils/utils";
import useEventEmit from "@/hooks/useEventEmit";
const configStore = useConfigStore();
@@ -115,6 +116,39 @@ const orderStatus = computed(() => {
return ''
})
// ====== 为设计稿准备的数据结构(商品缩略卡/总件数/总价等)======
const orderDishList = computed(() => {
const list = orderDetail.value?.merchantOrderDishVoList as unknown as Array<any> | null | undefined
if (!Array.isArray(list)) return []
return list
})
const orderTotalItemCount = computed(() => {
return orderDishList.value.reduce((sum, item) => sum + (Number(item?.count) || 0), 0)
})
const orderTotalPrice = computed(() => {
const n = orderDetail.value?.paidAmount ?? orderDetail.value?.actualPrice ?? 0
return Number(n).toFixed(2)
})
function dishCover(dishItem: any) {
const img = dishItem?.merchantDishVo?.dishImage
if (!img || typeof img !== 'string') return ''
return img.split(',')[0] || ''
}
function dishTitle(dishItem: any) {
return dishItem?.merchantDishVo?.dishName ?? ''
}
const payMethodText = computed(() => {
// 1-信用卡 2-余额(wallet
return orderDetail.value?.payMethod === 1
? t('pages-user.choosePaymethod.creditCard')
: t('pages-user.choosePaymethod.wallet')
})
// 复制订单号
const copyOrderNumber = (text: string) => {
if(!text) return
@@ -133,29 +167,6 @@ const callPhoneFn = (phone: string) => {
callPhone(phone)
}
// 收藏店铺
function handleCollectionClick() {
debouncedEmit(orderDetail.value?.merchantVo?.id, CollectionType.STORE, ()=> {
if (orderDetail.value?.merchantVo) {
orderDetail.value.merchantVo.isCollect = !orderDetail.value.merchantVo.isCollect
}
})
}
// 防抖处理函数
const debouncedEmit = debounce(1300, (id: any, type: CollectionType, callback: ()=> void) => {
// 收藏接口
appCollectCollectPost({
body: {
targetId: id,
targetType: type
}
}).then(res=> {
callback()
})
}, {
atBegin: true, // 立即触发
})
// 支付参数
const payMethodOptions = ref({
orderId: '',
@@ -227,21 +238,72 @@ function openQrCode() {
function navigateTo(url: string) {
uni.navigateTo({ url })
}
// 已支付上传凭证(Zip 等线下支付)
const voucherChooseRef = ref<InstanceType<typeof ChooseImage>>()
const voucherSubmitting = ref(false)
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 : ''
}
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
}
}
</script>
<template>
<navbar />
<view
class="animate-in fade-in animate-ease-out animate-duration-300"
v-show="loading"
>
<!-- 骨架屏 -->
<OrderDetailSkeleton />
</view>
<view
class="animate-in fade-in animate-ease-in animate-duration-300"
v-if="!loading"
>
<view class="order-detail-page">
<navbar />
<view
class="animate-in fade-in animate-ease-out animate-duration-300"
v-show="loading"
>
<!-- 骨架屏 -->
<OrderDetailSkeleton />
</view>
<view
class="animate-in fade-in animate-ease-in animate-duration-300 bg-white"
v-if="!loading"
>
<!-- 已取消-成功取消 -->
<template v-if="orderStatus === OrderStatus.CANCELLED">
<view class="px-30rpx pt-20rpx pb-50rpx">
@@ -289,7 +351,10 @@ function navigateTo(url: string) {
</view>
</view>
<!-- 订单进度 -->
<view v-if="orderDetail?.refundStatus !== OrderCancelStatus.APPLIED || orderDetail?.refundStatus !== OrderCancelStatus.APPROVED" class="mb-52rpx px-30rpx">
<view
v-if="orderDetail?.refundStatus !== OrderCancelStatus.APPLIED && orderDetail?.refundStatus !== OrderCancelStatus.APPROVED"
class="mb-52rpx px-30rpx"
>
<OrderProgress
:steps="orderSteps"
:current-status="orderStatus"
@@ -350,58 +415,77 @@ function navigateTo(url: string) {
</view>
<view class="text-30rpx lh-30rpx text-#333 mb-24rpx mt-36rpx">{{ t('pages-store.order.deliveryPhotos') }}</view>
<view class="flex items-center gap-20rpx">
<template v-for="item in orderDetail?.deliveryPhotos?.split(',')">
<view
v-for="(item, idx) in orderDetail?.deliveryPhotos?.split(',')"
:key="`${idx}-${item}`"
>
<wd-img width="158rpx" height="158rpx" radius="16rpx" mode="aspectFill" :src="item" :enable-preview="true" />
</template>
</view>
</view>
</view>
<view class="w-full h-16rpx bg-#F6F6F6"></view>
</view>
<!-- 商品列表 -->
<view class="px-30rpx py-36rpx">
<!-- 商家信息 -->
<view @click="navigateTo('/pages-store/pages/store/index?id=' + orderDetail?.merchantVo?.id)" class="flex-center-sb h-80rpx mb-36rpx">
<view class="flex items-center">
<image
:src="orderDetail?.merchantVo?.logo"
mode="aspectFill"
class="w-80rpx h-80rpx rounded-full bg-#F2F2F2 mr-24rpx shrink-0"
/>
<text class="text-30rpx lh-30rpx text-#333 font-500">{{ orderDetail?.merchantVo?.merchantName }}</text>
<image
src="@img/chef/142.png"
class="w-32rpx h-32rpx shrink-0 ml-10rpx"
></image>
</view>
<collection :is-collected="orderDetail?.merchantVo?.isCollect" @collectionChange="handleCollectionClick" />
</view>
<view
v-for="item in orderDetail?.merchantOrderDishVoList"
:key="item.id"
class="flex items-start mb-32rpx last:mb-0"
>
<!-- 商品图片 -->
<view class="w-136rpx h-136rpx rounded-16rpx overflow-hidden mr-20rpx shrink-0">
<image
:src="item.merchantDishVo?.dishImage?.split(',')[0]"
mode="aspectFill"
class="w-full h-full bg-#F2F2F2"
/>
</view>
<!-- 商品信息 -->
<view class="flex-1">
<text class="text-30rpx lh-30rpx text-#333 font-500 block mb-20rpx">{{ item.merchantDishVo?.dishName }}</text>
<view class="flex-center-sb text-24rpx lh-24rpx text-#7D7D7D mb-24rpx">
<text class="line-clamp-1">{{ item.merchantSideDishVo?.sideDishName }} {{ item.merchantSideDishItemVo?.name }}</text>
<!-- 数量 -->
<view class="shrink-0 text-28rpx lh-28rpx text-#333 text-right font-500">
X {{ item.count }}
<view class="goods-section px-30rpx py-24rpx">
<view class="goods-layout">
<view class="goods-left">
<!-- 门店行:图标 + 店铺名 + 右箭头 -->
<view
@click="navigateTo('/pages-store/pages/store/index?id=' + orderDetail?.merchantVo?.id)"
class="store-row-detail flex-center-sb mb-20rpx"
>
<view class="flex items-center min-w-0">
<image
src="@img/chef/126.png"
class="w-40rpx h-40rpx shrink-0 mr-16rpx"
mode="aspectFit"
/>
<text class="text-30rpx lh-36rpx text-#333 font-500 line-clamp-1">
{{ orderDetail?.merchantVo?.merchantName }}
</text>
</view>
<image
src="@img/chef/142.png"
class="w-28rpx h-28rpx shrink-0"
mode="aspectFit"
/>
</view>
<text class="text-30rpx lh-30rpx text-#333 font-500">{{ `$${item.merchantDishVo?.discountPrice}` }}</text>
<!-- 商品缩略卡横向滚动 -->
<scroll-view
scroll-x
class="goods-scroll"
:show-scrollbar="false"
:enable-flex="true"
>
<view class="goods-track">
<view
v-for="(item, di) in orderDishList"
:key="item.id ?? di"
class="goods-cell"
>
<view class="goods-img-wrap">
<image
:src="dishCover(item)"
mode="aspectFill"
class="goods-img"
/>
<view v-if="Number(item?.count) > 1" class="goods-qty">
x{{ item?.count }}
</view>
</view>
<text class="goods-caption line-clamp-1">{{ dishTitle(item) }}</text>
</view>
</view>
</scroll-view>
</view>
<view class="goods-price-right">
<text class="goods-price">${{ orderTotalPrice }}</text>
<text class="goods-count">
{{ t('pages.order.totalItemCount', { count: orderTotalItemCount }) }}
</text>
</view>
</view>
</view>
@@ -409,7 +493,7 @@ function navigateTo(url: string) {
<!-- 分隔线 -->
<view class="w-full h-16rpx bg-#F6F6F6"></view>
<view v-if="orderDetail?.receiveMethod === 1" class="pt-36rpx">
<view v-if="orderDetail?.receiveMethod === 1" class="pt-36rpx bg-white">
<view class="text-36rpx lh-36rpx text-#333 font-500 pl-30rpx mb-4rpx">{{ t('pages-store.order.deliveryAddress') }}</view>
<!-- 收货地址 -->
@@ -441,7 +525,6 @@ function navigateTo(url: string) {
{{ orderDetail?.deliveryMethod }}
</view>
</view>
<!-- 联系电话 -->
<view
class="flex items-center py-36rpx px-30rpx"
>
@@ -457,7 +540,7 @@ function navigateTo(url: string) {
</view>
<!-- 分隔线 -->
<view v-else class="px-30rpx pt-36rpx">
<view v-else class="px-30rpx pt-36rpx bg-white">
<view class="text-36rpx lh-36rpx text-#333 font-bold">{{ t('pickupAddress') }}</view>
<view class="flex-center-sb py-40rpx">
<view class="flex items-center">
@@ -486,7 +569,7 @@ function navigateTo(url: string) {
<view class="w-full h-16rpx bg-#F6F6F6"></view>
<view class="border-bottom px-30rpx py-36rpx text-36rpx lh-36rpx text-#333 ">
<view class="bg-white border-bottom px-30rpx py-36rpx text-36rpx lh-36rpx text-#333 ">
<view class="font-500 text-36rpx lh-36rpx mb-40rpx">{{ t('pages-store.order.orderInfo') }}</view>
<!-- 订单编号 -->
<view @click="copyOrderNumber(orderDetail?.orderNo)" class="flex-center-sb text-30rpx lh-30rpx mb-40rpx">
@@ -501,11 +584,10 @@ function navigateTo(url: string) {
</view>
</view>
<!-- 下单时间 mb-40rpx根据支付状态显示 -->
<view class="flex-center-sb text-30rpx lh-30rpx" :class="[orderStatus !== OrderStatus.PENDING_PAYMENT ? 'mb-40rpx' : '']">
<view class="flex-center-sb text-30rpx lh-30rpx mb-40rpx">
<text>{{ t('pages-store.order.orderTime') }}</text>
<text>{{ formatTimestampWithMonthName(orderDetail?.createTime) }}</text>
</view>
<!-- 待支付 -->
<template v-if="orderStatus !== OrderStatus.PENDING_PAYMENT">
<!-- 支付方式 -->
<view class="flex-center-sb text-30rpx lh-30rpx mb-40rpx">
@@ -516,14 +598,9 @@ function navigateTo(url: string) {
mode="aspectFill"
class="w-44rpx h-44rpx shrink-0 mr-10rpx"
/>
<text>{{ orderDetail?.payMethod === 1 ? t('pages-user.choosePaymethod.creditCard') : t('pages-user.choosePaymethod.wallet') }}</text>
<text>{{ payMethodText }}</text>
</view>
</view>
<!-- 支付时间 -->
<view class="flex-center-sb text-30rpx lh-30rpx">
<text>{{ t('pages-user.member.payTime') }}</text>
<text>{{ formatTimestampWithMonthName(orderDetail?.payTime) }}</text>
</view>
</template>
</view>
@@ -607,11 +684,20 @@ function navigateTo(url: string) {
</template>
<template v-else>
<!-- 待支付 -->
<wd-button
:loading="voucherSubmitting"
:disabled="voucherSubmitting"
@click="openUploadVoucher"
custom-class="!h-108rpx !bg-transparent !text-32rpx !font-500 !lh-108rpx !text-#333 !rounded-46rpx !border-#666666 !border-solid !border-1rpx"
block
>
{{ t('pages-store.order.uploadPaidVoucher') }}
</wd-button>
<template v-if="orderStatus === OrderStatus.PENDING_PAYMENT">
<wd-button @click="goPay" custom-class="!h-108rpx !bg-14181B !text-36rpx !lh-108rpx !text-#fff !rounded-16rpx" block>
<wd-button @click="goPay" custom-class="!h-108rpx !bg-14181B !text-32rpx !lh-108rpx !text-#fff !rounded-46rpx !mt-22rpx" block>
{{ t('common.goPay') }}
</wd-button>
<view @click="openCancelOrder" class="text-center mt-52rpx text-36rpx lh-36rpx text-#333 font-500">
<view @click="openCancelOrder" class="text-center mt-22rpx text-28rpx lh-28rpx text-#333 font-500">
{{ t('common.cancel') }}
</view>
</template>
@@ -678,13 +764,111 @@ function navigateTo(url: string) {
<!-- 支付订单 -->
<password-container @success="payPawSuccess" ref="passwordInputRef" />
<!-- 核销订单 -->
<use-code ref="useCodeRef" />
<use-code ref="useCodeRef" />
<!-- 支付凭证:相册/拍照 -->
<choose-image ref="voucherChooseRef" :count="1" @change="onVoucherImageUploaded" />
</view>
</view>
</template>
<style>
page {
background-color: #fff;
background-color: #F6F6F6;
}
</style>
<style scoped>
/* 订单详情:商品缩略卡横向滚动(对齐设计稿) */
.goods-section {
background: #fff;
}
.goods-layout {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.goods-left {
flex: 1;
min-width: 0;
}
.store-row-detail {
align-items: center;
}
.goods-price-right {
flex-shrink: 0;
text-align: right;
padding-left: 24rpx;
padding-bottom: 10rpx;
}
.goods-price {
display: block;
font-size: 32rpx;
line-height: 40rpx;
font-weight: 700;
color: #14181B;
}
.goods-count {
display: block;
margin-top: 8rpx;
font-size: 24rpx;
line-height: 30rpx;
color: #7D7D7D;
font-weight: 500;
}
.goods-scroll {
width: 100%;
}
.goods-track {
display: flex;
flex-direction: row;
gap: 16rpx;
padding-right: 8rpx;
}
.goods-cell {
width: 128rpx;
flex-shrink: 0;
}
.goods-img-wrap {
position: relative;
width: 128rpx;
height: 128rpx;
border-radius: 16rpx;
overflow: hidden;
background: #F2F2F2;
}
.goods-img {
width: 100%;
height: 100%;
}
.goods-qty {
position: absolute;
right: 6rpx;
bottom: 6rpx;
min-width: 36rpx;
padding: 4rpx 10rpx;
background: rgba(20, 24, 27, 0.85);
color: #fff;
font-size: 20rpx;
line-height: 24rpx;
border-radius: 8rpx;
text-align: center;
}
.goods-caption {
display: block;
margin-top: 10rpx;
font-size: 22rpx;
line-height: 28rpx;
color: #333;
}
</style>