first commit

This commit is contained in:
2026-02-26 09:25:47 +08:00
commit 40665dda67
708 changed files with 100122 additions and 0 deletions
@@ -0,0 +1,134 @@
<template>
<view class="order-skeleton bg-#F6F6F6">
<view class="px-30rpx py-32rpx">
<!-- 订单状态骨架屏 -->
<view class="mb-20rpx">
<view class="flex items-center mb-18rpx">
<view class="w-42rpx h-42rpx rounded-full mr-12rpx skeleton-item"></view>
<view class="w-150rpx h-36rpx skeleton-item"></view>
</view>
<view class="w-200rpx h-24rpx skeleton-item"></view>
</view>
<!-- 警告提示框骨架屏 -->
<view class="bg-white rounded-16rpx p-24rpx mb-20rpx">
<view class="w-100rpx h-28rpx mb-10rpx skeleton-item"></view>
<view class="w-full h-20rpx skeleton-item"></view>
</view>
<!-- 配送地址卡片骨架屏 -->
<view class="bg-white rounded-16rpx pt-34rpx mb-20rpx">
<view class="px-24rpx border-bottom pb-36rpx">
<!-- 配送地址标题 -->
<view class="w-120rpx h-28rpx mb-22rpx skeleton-item"></view>
<!-- 地址信息区域 -->
<view class="flex items-center mb-6rpx">
<view class="w-40rpx h-40rpx rounded-full mr-10rpx skeleton-item"></view>
<view class="w-250rpx h-28rpx skeleton-item"></view>
</view>
<!-- 地址详情 -->
<view class="w-full h-26rpx skeleton-item"></view>
</view>
<!-- 操作按钮区域 -->
<view class="flex justify-between h-90rpx">
<view class="center h-full w-full border-r-1rpx border-r-solid border-r-#DEDEDE">
<view class="w-40rpx h-40rpx rounded-full mr-16rpx skeleton-item"></view>
<view class="w-80rpx h-24rpx skeleton-item"></view>
</view>
<view class="center w-full">
<view class="w-40rpx h-40rpx rounded-full mr-16rpx skeleton-item"></view>
<view class="w-60rpx h-24rpx skeleton-item"></view>
</view>
</view>
</view>
<!-- 送达时间骨架屏 -->
<view class="mb-20rpx bg-white px-24rpx h-160rpx rounded-16rpx flex flex-col justify-center">
<view class="w-120rpx h-28rpx mb-32rpx skeleton-item"></view>
<view class="w-300rpx h-28rpx skeleton-item"></view>
</view>
<!-- 商品信息卡片骨架屏 -->
<view class="w-690rpx bg-white rounded-16rpx p-24rpx mb-30rpx">
<!-- 商品信息标题 -->
<view class="w-120rpx h-28rpx mb-34rpx skeleton-item"></view>
<!-- 商品列表骨架屏 -->
<template v-for="item in 3" :key="item">
<view class="flex items-center gap-24rpx mb-34rpx last:mb-0">
<view class="w-120rpx h-120rpx rounded-16rpx shrink-0 skeleton-item"></view>
<view class="flex-1 h-120rpx flex flex-col justify-between">
<view class="w-200rpx h-28rpx skeleton-item"></view>
<view class="w-150rpx h-24rpx skeleton-item"></view>
<view class="flex justify-between items-center">
<view class="w-80rpx h-30rpx skeleton-item"></view>
<view class="w-60rpx h-28rpx skeleton-item"></view>
</view>
</view>
</view>
</template>
<!-- 展开收起按钮骨架屏 -->
<view class="mt-34rpx center">
<view class="w-262rpx h-50rpx rounded-4rpx skeleton-item"></view>
</view>
<!-- 订单总计骨架屏 -->
<view class="mt-32rpx border-t-1rpx pt-16rpx">
<view class="flex justify-between mb-16rpx">
<view class="w-80rpx h-30rpx skeleton-item"></view>
<view class="w-100rpx h-30rpx skeleton-item"></view>
</view>
<view class="flex justify-between mb-16rpx">
<view class="flex items-center">
<view class="w-120rpx h-30rpx skeleton-item"></view>
<view class="w-28rpx h-28rpx ml-10rpx skeleton-item"></view>
</view>
<view class="w-80rpx h-30rpx skeleton-item"></view>
</view>
<view class="flex justify-between">
<view class="w-60rpx h-30rpx skeleton-item"></view>
<view class="w-100rpx h-30rpx skeleton-item"></view>
</view>
</view>
</view>
<!-- 订单信息卡片骨架屏 -->
<view class="bg-white rounded-16rpx pt-32rpx mb-30rpx">
<!-- 订单信息标题 -->
<view class="flex items-center mb-30rpx">
<view class="bg-#FF7112 w-10rpx h-30rpx mr-14rpx"></view>
<view class="w-120rpx h-28rpx skeleton-item"></view>
</view>
<!-- 订单详情骨架屏 -->
<view class="px-24rpx">
<template v-for="item in 4" :key="item">
<view class="flex justify-between items-center h-88rpx border-bottom">
<view class="w-120rpx h-28rpx skeleton-item"></view>
<view class="w-180rpx h-28rpx skeleton-item"></view>
</view>
</template>
</view>
</view>
</view>
<!-- 底部按钮区域骨架屏 -->
<view class="fixed bottom-0 left-0 right-0 z-1 bg-white shadow-[0rpx_-6rpx_24rpx_rgba(0,0,0,0.16)]">
<view class="h-118rpx p-[10rpx+30rpx] box-border flex gap-22rpx">
<view class="w-full h-98rpx rounded-20rpx skeleton-item"></view>
<view class="w-full h-98rpx rounded-20rpx skeleton-item"></view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
// 订单详情页面骨架屏组件
</script>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,69 @@
<script lang="ts" setup>
import {useConfigStore} from "@/store";
const {t} = useI18n();
const configStore = useConfigStore();
// 用户会员状态是否已开通
const show = ref(false);
const priceData = ref({})
function onOpen(data: any, num: number) {
priceData.value = data
show.value = true;
}
function handleClose() {
show.value = false;
}
defineExpose({
onOpen,
});
</script>
<template>
<wd-popup
v-model="show"
position="bottom"
@close="handleClose"
>
<view>
<view class="center h-102rpx bg-#F7F7F7 text-40rpx lh-40rpx font-bold text-#333">
{{ t('pages-user.order.checkout.priceDetail.title') }}?
</view>
<view class="border-bottom text-32rpx lh-32rpx font-500 text-#333 py-36rpx px-30rpx">
<view class="flex-center-sb mb-20rpx">
<view>{{ t('pages-user.order.checkout.priceDetail.serviceFees') }}</view>
<view>${{
(Number(priceData?.tip) + Number(priceData?.deliveryFee)).toFixed(2)
}}
</view>
</view>
<view class="text-24rpx lh-28rpx text-#9E9E9E">
{{ t('pages-user.order.checkout.priceDetail.desc') }}
</view>
</view>
<view class="flex-center-sb border-bottom h-104rpx text-32rpx lh-32rpx font-500 text-#333 px-30rpx">
<view>{{ t('pages-user.order.checkout.priceDetail.taxation') }}</view>
<view>${{ Number(priceData?.tax) }}</view>
</view>
<view class="px-30rpx pt-40rpx">
<wd-button
block
custom-class="!h-108rpx !text-36rpx !rounded-16rpx !bg-#14181B"
@click="handleClose"
>
{{ t('common.gotIt') }}
</wd-button>
</view>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</wd-popup>
</template>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,87 @@
<script lang="ts" setup>
import {appMerchantOrderRejectRefundPost} from "@/service";
import {useConfigStore} from "@/store";
const {t} = useI18n();
const emit = defineEmits(['close'])
const configStore = useConfigStore()
const show = ref(false);
const formData = reactive({
rejectReason: '',
orderId: ''
})
function onOpen(orderId: string) {
formData.orderId = orderId
show.value = true;
}
function handleClose() {
show.value = false;
}
function handleSubmit() {
if (!formData.rejectReason) {
uni.showToast({
title: t('pages-user.order.toast.refuseReasonRequired'),
icon: 'none'
})
return
}
appMerchantOrderRejectRefundPost({
body: {
...formData
}
}).then(res => {
uni.showToast({
title: t('pages-user.order.toast.refuseSuccess'),
icon: 'none'
})
emit('close')
handleClose()
})
}
defineExpose({
onOpen,
});
</script>
<template>
<wd-popup v-model="show" custom-style="border-radius:32rpx 32rpx 0 0;" position="bottom" @close="handleClose">
<view class="px-30rpx pt-40rpx relative">
<view class="text-34rpx text-#333 font-bold text-center mb-37rpx">{{ t('pages-user.order.refuseReason') }}</view>
<image class="w-28rpx h-28rpx absolute top-40rpx right-30rpx" src="@img/chef/100404.png"
@click="handleClose"></image>
<view
class="min-h-234rpx box-border bg-#F7F7F7 rounded-14rpx overflow-hidden px-18rpx py-10rpx mb-36rpx"
>
<wd-textarea
v-model="formData.rejectReason"
:maxlength="100"
:placeholder="t('pages-user.order.toast.refuseReasonRequired')"
auto-height
custom-class="!bg-#F7F7F7"
custom-textarea-container-class="!bg-#F7F7F7"
no-border
/>
</view>
<wd-button block custom-class="!h-98rpx !text-30rpx !font-bold !rounded-16rpx !bg-#14181B"
@click="handleSubmit">
{{ t('common.submit') }}
</wd-button>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</wd-popup>
</template>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,105 @@
<script setup lang="ts">
interface Props {
// 当前评分值
modelValue: number
// 最大星级数量
maxStars?: number
// 星星大小
starSize?: number
// 激活颜色
activeColor?: string
// 未激活颜色
inactiveColor?: string
// 是否显示评分数值
showScore?: boolean
// 是否禁用交互
disabled?: boolean
// 自定义类名
customClass?: string
}
interface Emits {
(e: 'update:modelValue', value: number): void
(e: 'change', value: number): void
}
const props = withDefaults(defineProps<Props>(), {
maxStars: 5,
starSize: 44,
activeColor: '#F86F1F',
inactiveColor: '#D1D1D1',
showScore: true,
disabled: false,
customClass: ''
})
const emit = defineEmits<Emits>()
// 点击星星设置评分
const setRating = (rating: number) => {
if (props.disabled) return
emit('update:modelValue', rating)
emit('change', rating)
}
</script>
<template>
<view :class="['star-rating flex items-center', customClass]">
<!-- 星级评分 -->
<view class="flex items-center gap-10rpx">
<view
v-for="star in maxStars"
:key="star"
:class="[
'mr-8rpx last:mr-0 flex-center',
disabled ? '' : 'cursor-pointer'
]"
@click="setRating(star)"
>
<image
v-show="star <= modelValue"
src="@img/chef/155.png"
mode="aspectFill"
class="w-24rpx h-24rpx"
/>
<image
v-show="star > modelValue"
src="@img/chef/154.png"
mode="aspectFill"
class="w-24rpx h-24rpx"
/>
</view>
</view>
<!-- 评分数值 -->
<text
v-if="showScore"
class="text-28rpx lh-28rpx text-#333 font-500 ml-80rpx"
>
{{ modelValue }}.0
</text>
</view>
</template>
<style scoped lang="scss">
.star-rating {
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.cursor-pointer {
cursor: pointer;
}
.bg-active {
transition: background-color 0.2s ease;
}
.bg-inactive {
transition: background-color 0.2s ease;
}
}
</style>
@@ -0,0 +1,243 @@
<script lang="ts" setup>
import {appMerchantOrderDeliverOrderPost, appMerchantOrderDeliveryListPost} from "@/service";
import {useConfigStore} from "@/store";
import ChooseImage from "@/components/choose-image/choose-image.vue";
import {z} from 'zod';
const {t} = useI18n();
const emits = defineEmits(['submit'])
const configStore = useConfigStore()
const show = ref(false);
const formData = reactive({
deliveryPhone: '',
orderId: '',
deliveryAvatar: '',
deliverySurname: '',
deliveryFirstName: '',
})
// Zod 校验 schema
const formSchema = z.object({
deliveryPhone: z.string().min(1, t('pages-user.order.startDeliveryForm.validation.deliveryPhoneRequired')),
orderId: z.string().min(1, t('pages-user.order.startDeliveryForm.validation.orderIdRequired')),
deliverySurname: z.string().min(1, t('pages-user.order.startDeliveryForm.validation.deliverySurnameRequired')),
deliveryFirstName: z.string().min(1, t('pages-user.order.startDeliveryForm.validation.deliveryFirstNameRequired')),
})
function onOpen(orderId: string) {
formData.orderId = orderId
show.value = true;
// 获取最新配送订单的配送员信息
getLatestDeliveryOrderInfo()
}
const userList = ref([])
// 当前选中的配送员信息下标
const currentSort = ref(null)
function getLatestDeliveryOrderInfo() {
appMerchantOrderDeliveryListPost({}).then(res => {
console.log('获取最新配送订单的配送员信息', res)
userList.value = res.data
isShowAdd.value = res.data.length <= 0;
})
}
function changeUser(item: any, index: number) {
currentSort.value = index
formData.deliveryAvatar = item.deliveryAvatar
formData.deliverySurname = item.deliverySurname
formData.deliveryFirstName = item.deliveryFirstName
formData.deliveryPhone = item.deliveryPhone
}
function handleClose() {
show.value = false;
}
function handleSubmit() {
try {
// 使用 zod 校验表单数据
formSchema.parse(formData)
// 校验通过,调用接口
appMerchantOrderDeliverOrderPost({
body: {
...formData
}
}).then(res => {
emits('submit')
handleClose()
})
} catch (error) {
// 校验失败,显示错误信息
if (error instanceof z.ZodError) {
const firstError = error.errors[0]
uni.showToast({
title: firstError.message,
icon: 'none'
})
}
}
}
const chooseImageRef = ref()
function chooseImage() {
if (chooseImageRef.value?.init) {
chooseImageRef.value.init();
}
}
function onImageChange(file: any) {
if (file.length > 0) {
formData.deliveryAvatar = file[0]
}
}
const isShowAdd = ref(false)
defineExpose({
onOpen,
});
</script>
<template>
<wd-popup v-model="show" custom-style="border-radius:32rpx 32rpx 0 0;" position="bottom"
@close="handleClose">
<view>
<view class="flex-center-sb h-102rpx bg-#F7F7F7 px-30rpx">
<view class="text-30rpx text-#999" @click="handleClose">{{ t('common.cancel') }}</view>
<view class="text-34rpx text-#333">{{ t('pages-user.order.startDeliveryForm.title') }}</view>
<view class="text-30rpx text-#FF6106" @click="handleSubmit">{{ t('common.confirm') }}</view>
</view>
<!--已经有配送员信息-->
<template v-if="!isShowAdd">
<scroll-view class="h-400rpx" scroll-y="true">
<view class="px-30rpx">
<template v-for="(item, index) in userList">
<view class="w-full py-24rpx bg-white flex-center-sb" @click="changeUser(item, index)">
<view class="flex items-center">
<image
:src="item.deliveryAvatar"
class="w-76rpx h-76rpx rounded-50%"
mode="aspectFill"
/>
<view class="ml-20rpx">
<view>{{ item.deliverySurname || '' }} {{ item.deliveryFirstName || '' }}</view>
<view>{{ item.deliveryPhone || '' }}</view>
</view>
</view>
<image
:src="
index === currentSort
? '/static/images/chef/152.png'
: '/static/images/chef/134.png'
"
class="w-40rpx h-40rpx"
mode="aspectFit"
/>
</view>
</template>
</view>
</scroll-view>
<view class="px-30rpx w-full" @click="isShowAdd = true">
<wd-button class="w-full !h-98rpx">{{ t('pages-user.recipe.index.add') }}</wd-button>
</view>
</template>
<template v-else>
<view class="px-30rpx py-20rpx">
<view class="mt-20rpx">
<view class="text-28rpx lh-28rpx text-#333 font-500 mb-20rpx">
{{ t('pages-user.order.startDeliveryForm.deliveryAvatar') }}
</view>
<view class="h-98rpx" @click="chooseImage">
<image
v-if="formData.deliveryAvatar"
:src="formData.deliveryAvatar"
class="w-76rpx h-76rpx rounded-50%"
mode="aspectFill"
/>
<image
v-else
class="w-76rpx h-76rpx rounded-50%"
mode="aspectFill"
src="@img/chef/default_avatar.png"
/>
</view>
</view>
<view class="mt-20rpx">
<view class="text-28rpx lh-28rpx text-#333 font-500 mb-20rpx">
{{ t('pages-user.order.startDeliveryForm.deliveryPhone') }}
</view>
<view class="flex-center-sb h-98rpx px-24rpx rounded-16rpx bg-#F6F6F6">
<wd-input
v-model="formData.deliveryPhone"
:focus-when-clear="false"
:placeholder="t('common.enter')"
clearable
confirm-type="done"
custom-class="!text-30rpx !bg-transparent flex-1"
no-border
placeholderStyle="font-size: 30rpx;color: #999;"
use-prefix-slot
>
</wd-input>
</view>
</view>
<view class="mt-20rpx">
<view class="text-28rpx lh-28rpx text-#333 font-500 mb-20rpx">
{{ t('pages-user.order.startDeliveryForm.deliverySurname') }}
</view>
<view class="flex-center-sb h-98rpx px-24rpx rounded-16rpx bg-#F6F6F6">
<wd-input
v-model="formData.deliverySurname"
:focus-when-clear="false"
:placeholder="t('common.enter')"
clearable
confirm-type="done"
custom-class="!text-30rpx !bg-transparent flex-1"
no-border
placeholderStyle="font-size: 30rpx;color: #999;"
use-prefix-slot
>
</wd-input>
</view>
</view>
<view class="mt-20rpx">
<view class="text-28rpx lh-28rpx text-#333 font-500 mb-20rpx">
{{ t('pages-user.order.startDeliveryForm.deliveryFirstName') }}
</view>
<view class="flex-center-sb h-98rpx px-24rpx rounded-16rpx bg-#F6F6F6">
<wd-input
v-model="formData.deliveryFirstName"
:focus-when-clear="false"
:placeholder="t('common.enter')"
clearable
confirm-type="done"
custom-class="!text-30rpx !bg-transparent flex-1"
no-border
placeholderStyle="font-size: 30rpx;color: #999;"
use-prefix-slot
>
</wd-input>
</view>
</view>
</view>
</template>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</wd-popup>
<ChooseImage ref="chooseImageRef" @change="onImageChange"/>
</template>
<style lang="scss" scoped>
:deep(.wd-input__clear) {
background-color: transparent !important;
}
</style>
@@ -0,0 +1,109 @@
<script lang="ts" setup>
import ChooseImage from "@/components/choose-image/choose-image.vue";
import {appMerchantOrderArrivedOrderPost} from "@/service";
const {t} = useI18n();
const formData = ref({
orderId: '',
dishImage: []
})
onLoad((options) => {
if (options.id) {
formData.value.orderId = options.id
}
})
function removeImage(index: number) {
formData.value.dishImage.splice(index, 1)
}
function onImageChange(file: any) {
if (file.length > 0) {
formData.value.dishImage.push(file[0])
}
}
const chooseImageRef = ref()
function handleChooseImage() {
if (chooseImageRef.value?.init) {
chooseImageRef.value.init();
}
}
function submitDelivery() {
// if (formData.value.dishImage.length === 0) {
// uni.showToast({
// title: t('pages-user.order.deliveredOrder.uploadPhoto'),
// icon: 'none'
// })
// return
// }
appMerchantOrderArrivedOrderPost({
body: {
orderId: formData.value.orderId,
deliveryPhotos: formData.value.dishImage.join(',')
}
}).then(res => {
uni.showToast({
title: t('toast.submitSuccess'),
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
})
}
</script>
<template>
<view>
<navbar :title="t('pages-user.order.delivered')"></navbar>
<view class="px-30rpx">
<view class="pt-52rpx pb-32rpx text-36rpx text-#333 font-500">
{{ t('pages-user.order.deliveredTips') }}:
</view>
<view class="grid grid-cols-3 gap-20rpx">
<template v-for="(item, index) in formData.dishImage">
<view class="relative w-210rpx h-210rpx">
<image
v-if="item"
class="absolute top-0rpx right-0rpx z-1 w-36rpx h-36rpx"
src="@img/chef/190.png"
@click="removeImage(index)"
></image>
<image
:src="item"
class="w-210rpx h-210rpx rounded-16rpx"
mode="aspectFill"
></image>
</view>
</template>
<view v-if="formData.dishImage.length < 4" class="w-210rpx h-210rpx center flex-col bg-#F6F6F6 rounded-20rpx"
@click="handleChooseImage">
<wd-icon color="#3D3D3D" name="add" size="32px"></wd-icon>
<text class="text-30rpx lh-30rpx text-#333 mt-20rpx">{{ t('pages-user.food.add-food.addPicture') }}</text>
</view>
</view>
</view>
<fixed-bottom-large-btn
:text="t('common.confirm')"
class="z-100"
fixed
@click="submitDelivery"
/>
<ChooseImage ref="chooseImageRef" @change="onImageChange"/>
</view>
</template>
<style>
page {
background-color: #fff;
}
</style>
+701
View File
@@ -0,0 +1,701 @@
<script lang="ts" setup>
import {useConfigStore, useUserStore} from "@/store";
import {
appMerchantOrderAgreeRefundPost,
appMerchantOrderDetailPost,
appMerchantOrderReceiveOrderPost,
type MerchantOrderVo,
} from "@/service";
import RefusePopup from "./components/refuse-popup/refuse-popup.vue";
import {OrderCancelStatus, OrderStatus} from "@/constant/enums";
import {callPhone, formatDateTimeRange, formatTimestampWithMonthName, navGoogleMap} from "@/utils/utils";
import PriceDetail from "./components/price-detail/price-detail.vue";
import startDelivery from "./components/start-delivery/start-delivery.vue";
import OrderSkeleton from "./components/order-skeleton.vue";
import {useMessage} from "wot-design-uni";
import {rejectUserOrderApi} from "@/pages-user/service";
const message = useMessage();
const {t} = useI18n();
const configStore = useConfigStore()
const userStore = useUserStore()
const refusePopup = ref()
const collapseValue = ref(false)
// 拨打电话
const makeCall = () => {
callPhone(orderDetail.value.phone)
}
// 复制订单号
const copyOrderNumber = (text: string) => {
if (!text) return
uni.setClipboardData({
data: text,
success: () => {
uni.showToast({
title: t('toast.orderNumberCopied'),
icon: 'none'
})
}
})
}
// 导航到地址
const navigateToAddress = () => {
if (!orderDetail.value.merchantOrderUserAddressVo.latitude || !orderDetail.value.merchantOrderUserAddressVo.longitude) return
navGoogleMap(orderDetail.value.merchantOrderUserAddressVo.latitude + ',' + orderDetail.value.merchantOrderUserAddressVo.longitude)
}
// 拒绝取消订单
const refuseOrder = () => {
refusePopup.value.onOpen(orderId.value)
}
// 同意取消订单
const agreeOrder = () => {
appMerchantOrderAgreeRefundPost({
params: {
orderId: orderId.value,
}
}).then(res => {
uni.showToast({
title: t('toast.agreeSuccess'),
icon: 'none'
})
setTimeout(() => {
// 获取订单详情
appMerchantOrderDetail()
}, 500)
})
}
function navigateTo(url: string) {
uni.navigateTo({url})
}
const orderId = ref('')
onLoad((options) => {
if (options.id) {
orderId.value = options.id
}
})
onShow(() => {
if (orderId.value) {
// 获取订单详情
appMerchantOrderDetail()
}
})
const orderDetail = ref<MerchantOrderVo>()
const loading = ref(false)
function appMerchantOrderDetail() {
loading.value = true;
appMerchantOrderDetailPost({
params: {
orderId: orderId.value,
}
}).then((res: any) => {
console.log('订单详情', res)
orderDetail.value = res.data
}).finally(() => {
loading.value = false;
})
}
// 订单状态
const orderStatus = computed(() => {
if (orderDetail.value) {
return orderDetail.value.orderStatus
}
return ''
})
// 是否展开显示所有商品
const isExpanded = ref(false)
// 展示的商品列表
const showDishList = computed(() => {
if (orderDetail.value?.merchantOrderDishVoList) {
// 如果展开或商品数量小于等于3,显示所有商品;否则只显示前3个
if (isExpanded.value || orderDetail.value.merchantOrderDishVoList.length <= 3) {
return orderDetail.value.merchantOrderDishVoList
}
return orderDetail.value.merchantOrderDishVoList.slice(0, 3)
}
return []
})
// 切换展开/收起状态
const toggleExpand = () => {
isExpanded.value = !isExpanded.value
}
const priceDetailRef = ref(null)
// 打开价格明细
const openPriceDetail = () => {
priceDetailRef.value?.onOpen({
tip: orderDetail.value.tip,
tax: orderDetail.value.tax,
deliveryFee: orderDetail.value.deliveryFee
}, 0);
};
// 接单
function startReceiving() {
appMerchantOrderReceiveOrderPost({
params: {
orderId: orderId.value,
}
}).then((res) => {
console.log('接单', res)
uni.showToast({
title: t('pages-user.order.toast.receiveSuccess'),
icon: 'none'
})
// 获取订单详情
setTimeout(() => {
appMerchantOrderDetail()
}, 500)
})
}
// 开始配送
const startDeliveryRef = ref(null)
function startDeliveryFun() {
startDeliveryRef.value?.onOpen(orderDetail.value.id)
}
// 点击已送达
function deliveredOrder() {
navigateTo('/pages-user/pages/order/delivered-order?id=' + orderDetail.value.id)
}
// 核销订单
function writeOff() {
if (!userStore.currentMerchantToken) {
uni.showToast({
title: t('toast.pleaseSelectStore'),
icon: 'none',
})
return
}
navigateTo('/pages-user/pages/scan-code/index')
}
function submitDelivery() {
appMerchantOrderDetail()
}
// 拒绝开始订单, 直接拒绝用户的订单
function rejectStartOrder() {
message
.confirm({
title: t("common.prompt.system-prompt"),
msg: `${t("common.prompt.system-prompt-reject")}`,
confirmButtonText: t("common.yes"),
cancelButtonText: t("common.no"),
cancelButtonProps: {
customClass:
"!h-88rpx !w-258rpx !min-w-auto !text-30rpx !lh-42rpx !font-bold !border-#666666 !rounded-20rpx",
},
confirmButtonProps: {
customClass:
"!h-88rpx !w-258rpx !min-w-auto !text-30rpx !lh-42rpx !font-bold !bg-primary !rounded-20rpx",
},
})
.then(async () => {
rejectUserOrderApi({
orderId: orderId.value,
}).then(res => {
uni.showToast({
title: t('toast.rejectSuccess'),
icon: 'none'
})
setTimeout(() => {
// 获取订单详情
appMerchantOrderDetail()
}, 500)
})
})
.catch(() => {
});
}
</script>
<template>
<view class="">
<!-- 顶部状态栏和导航 -->
<navbar :title="t('pages-user.order.details')"></navbar>
<!-- 骨架屏 -->
<OrderSkeleton v-if="loading"/>
<!-- 主要内容区域 -->
<view v-else class="w-full px-30rpx py-32rpx">
<!-- 订单状态 -->
<view class="mb-20rpx">
<template
v-if="+orderDetail?.refundStatus === OrderCancelStatus.APPLIED || +orderDetail?.refundStatus === OrderCancelStatus.APPROVED || +orderDetail?.refundStatus === OrderCancelStatus.REJECTED">
<template v-if="+orderDetail?.refundStatus === OrderCancelStatus.APPLIED">
<view class="flex items-center mb-18rpx">
<image class="w-42rpx h-42rpx shrink-0 mr-12rpx" src="@img/chef/201.png"></image>
<view class="!text-36rpx !text-#333 font-500">
{{ t('pages-user.order.pending') }}
</view>
</view>
<view class="bg-white px-24rpx py-34rpx rounded-10rpx">
<view class="text-32rpx text-#F8311D">{{ t('pages-user.order.cancelOrder') }}</view>
<view class="text-28rpx text-#333">{{
t('pages-user.order.refundReason')
}}{{ orderDetail?.cancelReason || '' }}
</view>
</view>
</template>
<template v-if="+orderDetail?.refundStatus === OrderCancelStatus.APPROVED">
<view class="flex items-center mb-18rpx">
<image class="w-42rpx h-42rpx shrink-0 mr-12rpx" src="@img/chef/203.png"></image>
<view class="!text-36rpx !text-#333 font-500">
{{ t('pages-user.order.agree') }}
</view>
</view>
<view class="bg-white px-24rpx py-34rpx rounded-10rpx">
<view class="text-32rpx text-#00A76D">{{ t('pages-user.order.agreeRefund') }}!</view>
<view class="text-28rpx text-#333">{{
t('pages-user.order.refundReason')
}}{{ orderDetail?.cancelReason || '' }}
</view>
</view>
</template>
<template v-if="+orderDetail?.refundStatus === OrderCancelStatus.REJECTED">
<view class="flex items-center mb-18rpx">
<image class="w-42rpx h-42rpx shrink-0 mr-12rpx" src="@img/chef/203.png"></image>
<view class="!text-36rpx !text-#333 font-500">
{{ t('pages-user.order.reject') }}
</view>
</view>
<view class="bg-white px-24rpx py-34rpx rounded-10rpx">
<view class="text-32rpx text-#F8311D">{{ t('pages-user.order.refuseRefund') }}!</view>
<view class="text-28rpx text-#333">{{
t('pages-user.order.refundReason')
}}{{ orderDetail?.cancelReason || '' }}
</view>
</view>
</template>
</template>
<template v-else>
<view class="flex items-center mb-18rpx">
<image v-if="orderStatus === OrderStatus.HAS_PENDING_PAYMENT" class="w-42rpx h-42rpx shrink-0 mr-12rpx"
src="@img/chef/202.png"></image>
<image v-else class="w-42rpx h-42rpx shrink-0 mr-12rpx" src="@img/chef/201.png"></image>
<view class="!text-36rpx !text-#333 font-500">
<template v-if="orderStatus === OrderStatus.HAS_PENDING_PAYMENT">
{{ t('pages-user.order.status.pendingPayment') }}
</template>
<template v-if="orderStatus === OrderStatus.MERCHANT_ACCEPTED">
{{ t('pages-user.order.status.merchantAccepted') }}
</template>
<template v-if="orderStatus === OrderStatus.DELIVERING">{{
t('pages-user.order.status.delivering')
}}
</template>
<template v-if="orderStatus === OrderStatus.COMPLETED">{{
t('pages-user.order.status.completed')
}}
</template>
<template v-if="orderStatus === OrderStatus.MERCHANT_REJECTED">
<view class="text-28rpx text-#333 font-bold">{{ t('pages-user.order.status.merchantRejected') }}</view>
<view class="text-24rpx text-#666">{{ t('pages-user.order.status.merchantRejectedDesc') }}</view>
</template>
</view>
</view>
<view class="!text-24rpx !text-#999">
<template v-if="orderStatus === OrderStatus.HAS_PENDING_PAYMENT">
{{ t('pages-user.order.status.waitingForOrder') }}
</template>
<template v-if="orderStatus === OrderStatus.MERCHANT_ACCEPTED">
{{ t('pages-user.order.status.merchantAcceptedDesc') }}
</template>
<template v-if="orderStatus === OrderStatus.DELIVERING">{{
t('pages-user.order.status.deliveringDesc')
}}
</template>
<template v-if="orderStatus === OrderStatus.COMPLETED">{{
t('pages-user.order.status.completedDesc')
}}
</template>
</view>
</template>
</view>
<!-- 警告提示框 -->
<view v-if="orderDetail?.orderRemark"
class="bg-#FFF0E8 border-2rpx border-#FF7112 border-solid text-28rpx lh-28rpx rounded-16rpx px-24rpx py-36rpx flex items-start mt-28rpx mb-20rpx">
<view class="text-#333 shrink-0">{{ t('pages-user.order.remark') }}</view>
<view class="text-#FF6106 ml-10rpx">{{
orderDetail?.orderRemark
}}
</view>
</view>
<!-- 配送地址卡片 -->
<!-- 配送订单 -->
<template v-if="orderDetail?.receiveMethod === 1">
<view class="bg-white rounded-16rpx pt-34rpx mb-20rpx">
<view class="px-24rpx border-bottom pb-36rpx">
<!-- 配送地址标题 -->
<text class="text-28rpx text-#333333">{{ t('pages-user.order.deliveryAddress') }}</text>
<!-- 地址信息区域 -->
<view class="mt-22rpx">
<!-- 位置图标 -->
<view class="flex items-center">
<image class="w-40rpx h-40rpx shrink-0 mr-10rpx" src="@img/chef/200.png"></image>
<view class="mb-6rpx">
<text class="text-28rpx text-#333333 font-normal">
{{ orderDetail?.userVo?.firstName }} {{ orderDetail?.userVo?.surname }}
{{ orderDetail?.userVo?.areaCode }} {{ orderDetail?.userVo?.phone }}
</text>
</view>
</view>
<!-- 地址详情 -->
<view class="leading-31rpx">
<text class="text-26rpx text-#666666">
{{ orderDetail?.merchantOrderUserAddressVo?.formattedAddress }}
{{ orderDetail?.merchantOrderUserAddressVo?.displayName }}
</text>
</view>
</view>
</view>
<!-- 操作按钮区域 -->
<view class="flex justify-between h-90rpx">
<!-- 电话联系 -->
<view class="center h-full w-full border-r-1rpx border-r-solid border-r-#DEDEDE" @click="makeCall">
<image class="w-40rpx h-40rpx shrink-0 mr-16rpx" src="@img/chef/205.png"></image>
<text class="text-24rpx text-#333333 font-normal">{{ t('pages-user.order.phoneContact') }}</text>
</view>
<!-- 导航 -->
<view class="center w-full w-full" @click="navigateToAddress">
<image class="w-40rpx h-40rpx shrink-0 mr-16rpx" src="@img/chef/204.png"></image>
<text class="text-24rpx text-#333333 font-normal">{{ t('pages-user.order.mapNavigation') }}</text>
</view>
</view>
</view>
</template>
<!-- 自取订单 -->
<template v-if="orderDetail?.receiveMethod === 2">
<view class="bg-white rounded-16rpx p-24rpx mb-20rpx flex-center-sb">
<view class="flex items-center text-28rpx text-#333">
<text>{{ t('pages-user.order.pickUp') }}:</text>
<image :src="orderDetail?.userVo?.avatar" class="w-68rpx h-68rpx shrink-0 ml-10rpx mr-20rpx"></image>
<text class="mr-24rpx">{{ orderDetail?.userVo?.firstName }} {{ orderDetail?.userVo?.surname }}</text>
<text>{{ orderDetail?.phone }}</text>
</view>
<image class="w-60rpx h-60rpx shrink-0" src="@img/chef/206.png" @click="makeCall"></image>
</view>
</template>
<!-- 送达时间 -->
<view
class="mb-20rpx bg-white px-24rpx h-160rpx rounded-16rpx flex flex-col items-start justify-center text-28rpx text-#333333">
<text class="mb-32rpx">
<template v-if="orderDetail?.receiveMethod === 1">
{{ t('pages-user.order.deliveryTime') }}
</template>
<template v-else>
{{ t('pages-user.order.mealPickupTime:') }}:
</template>
</text>
<text class="">{{ formatDateTimeRange(orderDetail?.startScheduledTime, orderDetail?.endScheduledTime) }}</text>
</view>
<!-- 商品信息卡片 -->
<view class="w-690rpx bg-white rounded-16rpx p-24rpx mb-30rpx">
<!-- 商品信息标题 -->
<view class="mb-34rpx">
<text class="text-28rpx text-#333333 font-normal">{{ t('pages-user.order.productInfo') }}</text>
</view>
<template v-for="item in showDishList">
<!-- 商品 -->
<view class="flex items-center gap-24rpx mb-34rpx last:mb-0">
<image :src="item.merchantDishVo?.dishImage?.split(',')[0]" class="w-120rpx h-120rpx rounded-16rpx shrink-0"
mode="aspectFill">
</image>
<view class="flex-1 h-120rpx">
<text class="text-28rpx text-#333333 font-normal block line-clamp-1">
{{ item.merchantDishVo?.dishName }}
</text>
<text class="text-24rpx text-#7D7D7D font-normal line-clamp-1">
{{ item.merchantSideDishVo?.sideDishName }} {{ item.merchantSideDishItemVo?.name }}
</text>
<view class="flex-center-sb">
<text class="text-30rpx text-#333333 font-500">${{ item.merchantDishVo?.discountPrice }}</text>
<text class="text-28rpx text-#333333 font-500">X{{ item.count }}</text>
</view>
</view>
</view>
</template>
<!-- 展开收起按钮 -->
<view v-if="orderDetail?.merchantOrderDishVoList?.length > 3" class="mt-34rpx center" @click="toggleExpand">
<view class="w-262rpx h-50rpx bg-#F4F4F4 center rounded-4rpx">
<text class="text-22rpx text-#666 font-normal">
{{ isExpanded ? t('pages-user.order.collapse') : t('pages-user.order.expand') }}
({{ t('pages-user.order.total') }}{{
orderDetail?.merchantOrderDishVoList.length
}}{{ t('pages-user.order.items') }})
</text>
<image
:class="{ 'rotate-180': isExpanded }"
class="w-20rpx h-20rpx shrink-0 ml-4rpx transition-transform duration-300"
src="@img/chef/210.png"
></image>
</view>
</view>
<!-- 订单总计 -->
<view class="mt-32rpx border-t-1rpx">
<view class="flex justify-between mb-16rpx text-30rpx text-#333333 font-normal">
<text class="">{{ t('pages.order.subtotal') }}</text>
<text class="">${{ orderDetail?.actualPrice }}</text>
</view>
<view class="flex justify-between mb-16rpx text-30rpx text-#333333 font-normal">
<view class="flex items-center">
<text class="">{{ t('pages.order.taxesAndFees') }}</text>
<image class="w-28rpx h-28rpx shrink-0 ml-10rpx" src="@img/chef/207.png" @click="openPriceDetail"></image>
</view>
<view class="flex items-center gap-16rpx">
<text class="">${{
(Number(orderDetail?.tax) + Number(orderDetail?.tip) + Number(orderDetail?.deliveryFee)).toFixed(2)
}}
</text>
</view>
</view>
<view class="flex justify-between text-30rpx text-#333333 font-normal">
<text class="">{{ t('pages.order.total') }}</text>
<text class="">${{ orderDetail?.paidAmount }}</text>
</view>
</view>
</view>
<!-- 订单信息卡片 -->
<view class="bg-white rounded-16rpx pt-32rpx mb-30rpx text-28rpx text-#333333 font-normal">
<!-- 订单信息标题 -->
<view class="flex items-center mb-30rpx">
<view class="bg-#FF7112 w-10rpx h-30rpx mr-14rpx"></view>
<text class="">{{ t('pages-user.order.orderInfo') }}</text>
</view>
<!-- 订单详情 -->
<view class="px-24rpx">
<view class="flex-center-sb h-88rpx border-top border-bottom">
<text class="text-28rpx text-#333333 font-normal">{{ t('pages-user.order.orderNumber') }}</text>
<view class="flex items-center" @click="copyOrderNumber(orderDetail?.orderNo)">
<text class="text-28rpx text-#333333 font-normal">{{ orderDetail?.orderNo }}</text>
<image class="w-24rpx h-24rpx shrink-0 ml-16rpx" src="@img/chef/1340.png"></image>
</view>
</view>
<view class="flex-center-sb h-88rpx border-bottom">
<text class="text-28rpx text-#333333 font-normal">{{ t('pages-user.order.createTime') }}</text>
<text class="text-28rpx text-#333333 font-normal">{{
formatTimestampWithMonthName(orderDetail?.createTime)
}}
</text>
</view>
<view class="flex-center-sb h-88rpx border-bottom">
<text class="text-28rpx text-#333333 font-normal">{{ t('pages-user.order.payTime') }}</text>
<text class="text-28rpx text-#333333 font-normal">{{
formatTimestampWithMonthName(orderDetail?.payTime)
}}
</text>
</view>
<view class="flex-center-sb h-88rpx">
<text class="text-28rpx text-#333333 font-normal">{{ t('pages-user.order.payMethod') }}</text>
<text class="text-28rpx text-#333333 font-normal">
{{
orderDetail?.payMethod === 1 ? t('pages-user.order.creditCardPayment') : t('pages-user.order.balancePayment')
}}
</text>
</view>
</view>
</view>
</view>
<!-- 主要内容区域 -->
<!-- 订单取消状态 -->
<template
v-if="+orderDetail?.refundStatus === OrderCancelStatus.APPLIED || +orderDetail?.refundStatus === OrderCancelStatus.APPROVED">
<template v-if="+orderDetail?.refundStatus === OrderCancelStatus.APPLIED">
<view class="">
<view :style="[configStore.iosSafeBottomPlaceholder]" class="h-118rpx"></view>
<view :style="[{
position: 'fixed',
bottom: '0',
left: '0',
right: '0',
}]" class="z-1 bg-#fff shadow-[0rpx_-6rpx_24rpx_rgba(0,0,0,0.16)]">
<view class="h-118rpx p-[10rpx+30rpx] box-border flex-center-sb gap-22rpx">
<wd-button
block
custom-class="!border-#666666 !border-solid !border-1rpx w-full !h-98rpx !text-30rpx !text-#333 !lh-42rpx !font-bold !rounded-20rpx !bg-white"
@click="agreeOrder">
{{ t('common.agree') }}
</wd-button>
<wd-button block
custom-class="w-full !h-98rpx !text-30rpx !lh-42rpx !font-bold !rounded-20rpx !bg-#14181B"
@click="refuseOrder">
{{ t('common.reject') }}
</wd-button>
</view>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</view>
</template>
</template>
<template v-else>
<!-- 配送订单 -->
<template v-if="orderDetail?.receiveMethod === 1">
<!-- 待接单 -->
<template v-if="orderStatus === OrderStatus.HAS_PENDING_PAYMENT">
<view class="">
<view :style="[configStore.iosSafeBottomPlaceholder]" class="h-118rpx"></view>
<view :style="[{
position: 'fixed',
bottom: '0',
left: '0',
right: '0',
}]" class="z-1 bg-#fff shadow-[0rpx_-6rpx_24rpx_rgba(0,0,0,0.16)]">
<view class="h-118rpx p-[10rpx+30rpx] box-border flex-center-sb gap-22rpx">
<wd-button block
custom-class="w-full !h-98rpx !text-30rpx !lh-42rpx !font-bold !rounded-20rpx !bg-#14181B"
@click="startReceiving">
{{ t('pages-user.order.receiving') }}
</wd-button>
<wd-button
block
custom-class="!border-#666666 !border-solid !border-1rpx w-full !h-98rpx !text-30rpx !text-#333 !lh-42rpx !font-bold !rounded-20rpx !bg-white"
@click="rejectStartOrder">
{{ t('common.reject') }}
</wd-button>
</view>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</view>
</template>
<!-- 已接单 -->
<template v-if="orderStatus === OrderStatus.MERCHANT_ACCEPTED">
<fixed-bottom-large-btn
:text="t('pages-user.order.startDelivery')"
class="z-100"
fixed
@click="startDeliveryFun"
/>
</template>
<!-- 配送中 -->
<template v-if="orderStatus === OrderStatus.DELIVERING">
<fixed-bottom-large-btn
:text="t('pages-user.order.delivered')"
class="z-100"
fixed
@click="deliveredOrder"
/>
</template>
<!-- 已评价 -->
<template v-if="orderStatus === OrderStatus.COMPLETED && orderDetail?.dishReviewVoList.length > 0">
<view class="w-full">
<view :style="[configStore.iosSafeBottomPlaceholder]" class="h-118rpx"></view>
<view :style="[{
position: 'fixed',
bottom: '0',
left: '0',
right: '0',
}]" class="z-1 bg-#fff shadow-[0rpx_-6rpx_24rpx_rgba(0,0,0,0.16)] w-screen">
<view class="w-full h-118rpx p-[10rpx+30rpx] box-border">
<wd-button
block
custom-class="!border-#666666 !border-solid !border-1rpx !h-98rpx !text-30rpx !text-#333 !lh-42rpx !rounded-20rpx !bg-white"
@click="navigateTo('/pages-user/pages/order/order-reviews?id=' + orderId)">
{{ t('pages-user.order.viewReview') }}
</wd-button>
</view>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</view>
</template>
</template>
<!-- ------------------------------------------------------ -->
<!-- 自取订单 -->
<template v-if="orderDetail?.receiveMethod === 2">
<!-- 待接单 -->
<template v-if="orderStatus === OrderStatus.HAS_PENDING_PAYMENT">
<view class="">
<view :style="[configStore.iosSafeBottomPlaceholder]" class="h-118rpx"></view>
<view :style="[{
position: 'fixed',
bottom: '0',
left: '0',
right: '0',
}]" class="z-1 bg-#fff shadow-[0rpx_-6rpx_24rpx_rgba(0,0,0,0.16)]">
<view class="h-118rpx p-[10rpx+30rpx] box-border flex-center-sb gap-22rpx">
<wd-button block
custom-class="w-full !h-98rpx !text-30rpx !lh-42rpx !font-bold !rounded-20rpx !bg-#14181B"
@click="startReceiving">
{{ t('pages-user.order.receiving') }}
</wd-button>
<wd-button
block
custom-class="!border-#666666 !border-solid !border-1rpx w-full !h-98rpx !text-30rpx !text-#333 !lh-42rpx !font-bold !rounded-20rpx !bg-white"
@click="rejectStartOrder">
{{ t('common.reject') }}
</wd-button>
</view>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</view>
</view>
</template>
<!-- 已接单 -->
<template v-if="orderStatus === OrderStatus.MERCHANT_ACCEPTED">
<fixed-bottom-large-btn
:text="t('pages-user.order.writeOff')"
class="z-100"
fixed
@click="writeOff"
/>
</template>
</template>
</template>
<!-- 拒绝取消订单 -->
<refuse-popup ref="refusePopup" @close="appMerchantOrderDetail"/>
<!-- 价格详情 -->
<price-detail ref="priceDetailRef"/>
<!-- 配送员信息 -->
<start-delivery ref="startDeliveryRef" @submit="submitDelivery"/>
</view>
</template>
<style>
page {
background-color: #F6F6F6;
}
</style>
@@ -0,0 +1,125 @@
<script lang="ts" setup>
import StarRating from './components/star-rating/star-rating.vue';
import {appMerchantOrderDetailPost} from '@/service';
import {formatTimestampWithMonthName} from '@/utils/utils';
import type {MerchantDishReviewVo, MerchantOrderVo} from '@/service/types';
const {t} = useI18n()
// 路由参数-订单ID
const orderId = ref('')
// 订单详情
const orderDetail = ref<MerchantOrderVo>()
// 加载状态
const loading = ref(false)
onLoad((options) => {
if (options.id) {
orderId.value = options.id as string
getOrderDetail()
} else {
uni.navigateBack()
}
})
function getOrderDetail() {
loading.value = true
appMerchantOrderDetailPost({
params: {orderId: orderId.value}
}).then((res: any) => {
orderDetail.value = res.data
}).finally(() => {
loading.value = false
})
}
// 仅展示菜品评价列表,关联并展示菜品信息
type DishReviewItem = {
review: MerchantDishReviewVo
dishName: string
dishImageUrl: string
reviewImages: string[]
reviewTimeText: string
}
const dishReviewList = computed<DishReviewItem[]>(() => {
const reviews = orderDetail.value?.dishReviewVoList || []
const dishes = orderDetail.value?.merchantOrderDishVoList || []
return reviews.map((rv: any) => {
const matchedDish = dishes.find(d => d.id === rv.merchantOrderDishId || d.dishId === rv.dishId)
const dishName = matchedDish?.merchantDishVo?.dishName || ''
const dishImageUrl = (matchedDish?.merchantDishVo?.dishImage || '').split(',')[0] || ''
const reviewImages = (rv.images || '').split(',').filter(Boolean)
const reviewTimeText = rv.createTime ? formatTimestampWithMonthName(Number(rv.createTime)) : ''
return {review: rv, dishName, dishImageUrl, reviewImages, reviewTimeText}
})
})
</script>
<template>
<view class="">
<!-- 顶部状态栏和导航 -->
<navbar :title="t('pages-user.order.viewReview')"></navbar>
<!-- 评价内容区域 -->
<view class="px-30rpx pt-24rpx">
<template v-if="dishReviewList.length">
<view v-for="(item, idx) in dishReviewList" :key="idx" class="mb-24rpx">
<!-- 商品信息卡片 -->
<view class="flex items-center bg-#F7F7F7 rounded-6rpx py-10rpx px-16rpx">
<!-- 商品图片 -->
<view class="w-80rpx h-80rpx shrink-0 rounded-10rpx mr-20rpx">
<image
v-if="item.dishImageUrl"
:src="item.dishImageUrl"
class="w-full h-full object-cover rounded-10rpx"
mode="aspectFill"
/>
</view>
<!-- 商品信息 -->
<view class="flex-1">
<text class="text-28rpx font-normal text-#333333 block mb-12rpx">{{ item.dishName }}</text>
<text class="text-24rpx font-normal text-#999999">{{ item.reviewTimeText }}</text>
</view>
</view>
<!-- 评价详情卡片 -->
<view class="">
<!-- 星级评分 -->
<view class="rating-section flex items-center my-20rpx">
<StarRating :disabled="true" :modelValue="item.review.rating || 0" :showScore="false"/>
<text class="text-24rpx font-normal text-#FF6E1A ml-10rpx">{{
(item.review.rating || 0)
}}{{ t('pages-user.order.score') }}
</text>
</view>
<!-- 评价文字 -->
<text class="text-24rpx font-normal text-#666666 leading-32rpx">{{ item.review.content || '' }}</text>
<!-- 评价图片 -->
<view v-if="item.reviewImages.length" class="review-images flex gap-20rpx mt-20rpx mb-14rpx">
<view
v-for="(image, index) in item.reviewImages"
:key="index"
class="image-item w-160rpx h-160rpx rounded-16rpx overflow-hidden bg-#f0f0f0"
>
<image
:src="image"
class="w-full h-full object-cover"
mode="aspectFill"
/>
</view>
</view>
</view>
</view>
</template>
<view v-else class="text-26rpx text-#999999">{{ t('pages-user.order.noReview') }}</view>
</view>
</view>
</template>
<style>
page {
background-color: #fff;
}
</style>