first commit
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user