新增团餐管理

This commit is contained in:
2026-06-16 09:12:41 +08:00
parent d3718bd378
commit f87f1cda0d
13 changed files with 643 additions and 15 deletions
+16
View File
@@ -172,4 +172,20 @@ export enum MessageTypeEnum {
COMMENT_APPROVED = 13,
/** 评论审核未通过 */
COMMENT_REJECTED = 14,
}
/** 团餐预定状态 */
export enum GroupMealReservationStatus {
/** 待处理 */
PENDING = 1,
/** 已联系 */
CONTACTED = 2,
/** 已确认 */
CONFIRMED = 3,
/** 已拒绝 */
REJECTED = 4,
/** 已完成 */
COMPLETED = 5,
/** 用户取消 */
USER_CANCELLED = 6,
}
+30
View File
@@ -95,6 +95,7 @@
"navbar-future-order": "Future Order",
"navbar-month-order": "This Month's Order",
"navbar-refund-order": "Refund order",
"navbar-group-meal-reservation": "Group meal reservation",
"navbar-reservation": "Today's ordered dishes",
"navbar-set-payment-password": "Set a payment password",
"navbar-settings": "Settings",
@@ -108,6 +109,7 @@
"card-title": "CHEFLINK stir fry machine",
"commonTools": "Common tools",
"coupons": "Coupons",
"groupMealReservation": "Group meals",
"createStore": "Create a store",
"dataStatistics": "Data statistics",
"futureOrders": "Booking order",
@@ -371,6 +373,34 @@
"value": "Value"
}
},
"groupMealReservation": {
"tabs": {
"all": "All"
},
"status": {
"pending": "Pending",
"contacted": "Contacted",
"confirmed": "Confirmed",
"rejected": "Rejected",
"completed": "Completed",
"userCancelled": "Cancelled by user"
},
"contactPhone": "Contact phone",
"peopleCount": "People count",
"perCapitaPrice": "Price per person",
"estimatedTotal": "Estimated total",
"expectedTime": "Expected time",
"scene": "Scene",
"remark": "User remark",
"handleRemark": "Handle remark",
"cancelReason": "Cancel reason",
"userNickName": "User nickname",
"detailTitle": "Group meal details",
"handleTitle": "Update status",
"handleRemarkPlaceholder": "Handle remark (optional)",
"confirmHandle": "Update status to \"{status}\"?",
"userCancelledTip": "Cancelled by user. View only."
},
"income": {
"index": {
"accountToReceive": "Account to receive:",
+30
View File
@@ -95,6 +95,7 @@
"navbar-future-order": "未来订单",
"navbar-month-order": "本月订单",
"navbar-refund-order": "退款订单",
"navbar-group-meal-reservation": "团餐预定",
"navbar-reservation": "今日预定菜品统计",
"navbar-set-payment-password": "设置支付密码",
"navbar-settings": "设置",
@@ -106,6 +107,7 @@
"bookDishesToday": "预订菜品",
"commonTools": "常用工具",
"coupons": "优惠券",
"groupMealReservation": "团餐预定",
"createStore": "创建店铺",
"dataStatistics": "数据统计",
"futureOrders": "预定单",
@@ -371,6 +373,34 @@
"value": "值"
}
},
"groupMealReservation": {
"tabs": {
"all": "全部"
},
"status": {
"pending": "待处理",
"contacted": "已联系",
"confirmed": "已确认",
"rejected": "已拒绝",
"completed": "已完成",
"userCancelled": "用户取消"
},
"contactPhone": "联系电话",
"peopleCount": "团餐人数",
"perCapitaPrice": "人均单价",
"estimatedTotal": "预计总价",
"expectedTime": "期望时间",
"scene": "场景",
"remark": "用户备注",
"handleRemark": "处理备注",
"cancelReason": "取消原因",
"userNickName": "用户昵称",
"detailTitle": "团餐预定详情",
"handleTitle": "处理状态",
"handleRemarkPlaceholder": "请输入处理备注(选填)",
"confirmHandle": "确认将状态更新为「{status}」?",
"userCancelledTip": "用户已取消,仅可查看详情"
},
"income": {
"index": {
"accountToReceive": "到账账户:",
+2 -2
View File
@@ -2,8 +2,8 @@
"name" : "CHEFLINK Merchant",
"appid" : "__UNI__BB8E3C9",
"description" : "美国外卖商户端",
"versionName" : "3.0.1",
"versionCode" : 301,
"versionName" : "3.0.2",
"versionCode" : 302,
"transformPx" : false,
/* 5+App */
"app-plus" : {
@@ -0,0 +1,247 @@
<script lang="ts" setup>
import {
appGroupMealReservationMerchantIdGet,
appGroupMealReservationMerchantStatusPost,
type GroupMealReservationVo,
} from '@/service'
import {GroupMealReservationStatus} from '@/constant/enums'
import {
calcGroupMealTotal,
canMerchantHandleGroupMeal,
getGroupMealContactPhone,
} from '@/utils/groupMealReservation'
import {callPhone, formatTimestampWithMonthName} from '@/utils/utils'
import {useMessage} from 'wot-design-uni'
const message = useMessage()
const {t} = useI18n()
const reservationId = ref('')
const detail = ref<GroupMealReservationVo>()
const loading = ref(false)
const handleRemark = ref('')
const handleActions = computed(() => [
{status: GroupMealReservationStatus.CONTACTED, label: t('pages-user.groupMealReservation.status.contacted')},
{status: GroupMealReservationStatus.CONFIRMED, label: t('pages-user.groupMealReservation.status.confirmed')},
{status: GroupMealReservationStatus.REJECTED, label: t('pages-user.groupMealReservation.status.rejected')},
{status: GroupMealReservationStatus.COMPLETED, label: t('pages-user.groupMealReservation.status.completed')},
])
const canHandle = computed(() => canMerchantHandleGroupMeal(detail.value?.status))
function statusLabel(status?: number) {
const map: Record<number, string> = {
[GroupMealReservationStatus.PENDING]: t('pages-user.groupMealReservation.status.pending'),
[GroupMealReservationStatus.CONTACTED]: t('pages-user.groupMealReservation.status.contacted'),
[GroupMealReservationStatus.CONFIRMED]: t('pages-user.groupMealReservation.status.confirmed'),
[GroupMealReservationStatus.REJECTED]: t('pages-user.groupMealReservation.status.rejected'),
[GroupMealReservationStatus.COMPLETED]: t('pages-user.groupMealReservation.status.completed'),
[GroupMealReservationStatus.USER_CANCELLED]: t('pages-user.groupMealReservation.status.userCancelled'),
}
return status != null ? (map[status] || String(status)) : '-'
}
function loadDetail() {
if (!reservationId.value) return
loading.value = true
appGroupMealReservationMerchantIdGet({
params: {id: reservationId.value},
}).then((res: any) => {
detail.value = res.data
handleRemark.value = res.data?.handleRemark || ''
}).catch((err: any) => {
uni.showToast({
title: err?.msg || err?.message || t('common.prompt.request-failed-please-try-again-later'),
icon: 'none',
})
}).finally(() => {
loading.value = false
})
}
function makeCall() {
callPhone(getGroupMealContactPhone(detail.value))
}
function submitStatus(status: 2 | 3 | 4 | 5) {
if (!detail.value?.id) return
message.confirm({
title: t('common.prompt.system-prompt'),
msg: t('pages-user.groupMealReservation.confirmHandle', {status: statusLabel(status)}),
confirmButtonText: t('common.yes'),
cancelButtonText: t('common.no'),
}).then(() => {
appGroupMealReservationMerchantStatusPost({
body: {
id: detail.value!.id!,
status,
handleRemark: handleRemark.value.trim() || undefined,
},
}).then((res: any) => {
uni.showToast({
title: res?.msg || t('common.operation-success'),
icon: 'none',
})
loadDetail()
}).catch((err: any) => {
uni.showToast({
title: err?.msg || err?.message || t('common.prompt.request-incorrect'),
icon: 'none',
})
})
}).catch(() => {
})
}
function handleActionClick(status: number) {
submitStatus(status as 2 | 3 | 4 | 5)
}
onLoad((options) => {
if (options?.id) {
reservationId.value = options.id
}
})
onShow(() => {
if (reservationId.value) {
loadDetail()
}
})
</script>
<template>
<view class="min-h-100vh bg-#F6F6F6 pb-200rpx">
<navbar :title="t('pages-user.groupMealReservation.detailTitle')"/>
<view v-if="loading" class="px-30rpx pt-30rpx">
<view class="bg-white rounded-16rpx p-30rpx">
<view v-for="n in 6" :key="n" class="w-full h-28rpx skeleton-item mb-24rpx"/>
</view>
</view>
<view v-else-if="detail" class="px-30rpx pt-30rpx">
<view class="bg-white rounded-16rpx p-30rpx mb-24rpx">
<view class="flex-center-sb mb-28rpx">
<text class="text-36rpx text-#333 font-500">{{ detail.contactName || '-' }}</text>
<text class="text-26rpx text-#FF6106">{{ statusLabel(detail.status) }}</text>
</view>
<view class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.contactPhone') }}</text>
<view class="flex items-center" @click="makeCall">
<text class="value text-#007AFF mr-12rpx">{{ getGroupMealContactPhone(detail) || '-' }}</text>
<image class="w-48rpx h-48rpx shrink-0" src="@img/chef/206.png"/>
</view>
</view>
<view class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.scene') }}</text>
<text class="value">{{ detail.scene || '-' }}</text>
</view>
<view class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.peopleCount') }}</text>
<text class="value">{{ detail.peopleCount || 0 }}</text>
</view>
<view class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.perCapitaPrice') }}</text>
<text class="value">${{ detail.perCapitaPrice ?? 0 }}</text>
</view>
<view class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.estimatedTotal') }}</text>
<text class="value font-500">${{ calcGroupMealTotal(detail.peopleCount, detail.perCapitaPrice) }}</text>
</view>
<view class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.expectedTime') }}</text>
<text class="value">
{{ detail.expectedTime ? formatTimestampWithMonthName(detail.expectedTime) : '-' }}
</text>
</view>
<view v-if="detail.remark" class="info-row items-start">
<text class="label">{{ t('pages-user.groupMealReservation.remark') }}</text>
<text class="value flex-1 text-right">{{ detail.remark }}</text>
</view>
<view v-if="detail.handleRemark" class="info-row items-start">
<text class="label">{{ t('pages-user.groupMealReservation.handleRemark') }}</text>
<text class="value flex-1 text-right">{{ detail.handleRemark }}</text>
</view>
<view v-if="detail.cancelReason" class="info-row items-start">
<text class="label">{{ t('pages-user.groupMealReservation.cancelReason') }}</text>
<text class="value flex-1 text-right">{{ detail.cancelReason }}</text>
</view>
<view v-if="detail.user?.nickName" class="info-row">
<text class="label">{{ t('pages-user.groupMealReservation.userNickName') }}</text>
<text class="value">{{ detail.user.nickName }}</text>
</view>
</view>
<view v-if="canHandle" class="bg-white rounded-16rpx p-30rpx">
<text class="text-32rpx text-#333 font-500 mb-24rpx block">
{{ t('pages-user.groupMealReservation.handleTitle') }}
</text>
<view class="bg-#F6F6F6 rounded-16rpx px-24rpx py-20rpx mb-30rpx">
<wd-textarea
v-model="handleRemark"
:placeholder="t('pages-user.groupMealReservation.handleRemarkPlaceholder')"
auto-height
custom-class="!bg-#F6F6F6"
custom-textarea-container-class="!bg-#F6F6F6"
no-border
/>
</view>
<view class="grid grid-cols-2 gap-20rpx">
<view
v-for="action in handleActions"
:key="action.status"
class="h-88rpx center rounded-16rpx bg-#F2F2F2 text-28rpx text-#333 font-500"
@click="handleActionClick(action.status)"
>
{{ action.label }}
</view>
</view>
</view>
<view v-else-if="detail.status === GroupMealReservationStatus.USER_CANCELLED"
class="bg-white rounded-16rpx p-30rpx text-28rpx text-#999 center">
{{ t('pages-user.groupMealReservation.userCancelledTip') }}
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 28rpx;
color: #666;
margin-right: 20rpx;
flex-shrink: 0;
}
.value {
font-size: 28rpx;
color: #333;
text-align: right;
}
}
</style>
@@ -0,0 +1,149 @@
<script lang="ts" setup>
import {
appGroupMealReservationMerchantListPost,
type GroupMealReservationVo,
} from '@/service'
import {GroupMealReservationStatus} from '@/constant/enums'
import {calcGroupMealTotal, getGroupMealContactPhone} from '@/utils/groupMealReservation'
import {callPhone, formatTimestampWithMonthName} from '@/utils/utils'
import {useUserStore} from '@/store'
const {t} = useI18n()
const userStore = useUserStore()
const currentTab = ref(0)
const tabsList = computed(() => [
{title: t('pages-user.groupMealReservation.tabs.all'), status: undefined},
{title: t('pages-user.groupMealReservation.status.pending'), status: GroupMealReservationStatus.PENDING},
{title: t('pages-user.groupMealReservation.status.contacted'), status: GroupMealReservationStatus.CONTACTED},
{title: t('pages-user.groupMealReservation.status.confirmed'), status: GroupMealReservationStatus.CONFIRMED},
{title: t('pages-user.groupMealReservation.status.rejected'), status: GroupMealReservationStatus.REJECTED},
{title: t('pages-user.groupMealReservation.status.completed'), status: GroupMealReservationStatus.COMPLETED},
{title: t('pages-user.groupMealReservation.status.userCancelled'), status: GroupMealReservationStatus.USER_CANCELLED},
])
function statusLabel(status?: number) {
const map: Record<number, string> = {
[GroupMealReservationStatus.PENDING]: t('pages-user.groupMealReservation.status.pending'),
[GroupMealReservationStatus.CONTACTED]: t('pages-user.groupMealReservation.status.contacted'),
[GroupMealReservationStatus.CONFIRMED]: t('pages-user.groupMealReservation.status.confirmed'),
[GroupMealReservationStatus.REJECTED]: t('pages-user.groupMealReservation.status.rejected'),
[GroupMealReservationStatus.COMPLETED]: t('pages-user.groupMealReservation.status.completed'),
[GroupMealReservationStatus.USER_CANCELLED]: t('pages-user.groupMealReservation.status.userCancelled'),
}
return status != null ? (map[status] || String(status)) : '-'
}
function statusClass(status?: number) {
if (status === GroupMealReservationStatus.PENDING) return 'text-#FF6106 bg-#FFF3EB'
if (status === GroupMealReservationStatus.CONTACTED) return 'text-#007AFF bg-#EAF3FF'
if (status === GroupMealReservationStatus.CONFIRMED) return 'text-#00A76D bg-#E8F8F2'
if (status === GroupMealReservationStatus.REJECTED) return 'text-#FF2828 bg-#FFECEC'
if (status === GroupMealReservationStatus.COMPLETED) return 'text-#333 bg-#F2F2F2'
if (status === GroupMealReservationStatus.USER_CANCELLED) return 'text-#999 bg-#F6F6F6'
return 'text-#666 bg-#F6F6F6'
}
const {paging, dataList, loading, queryList} = usePage<GroupMealReservationVo>((pageNum, pageSize) => {
const tab = tabsList.value[currentTab.value]
return appGroupMealReservationMerchantListPost({
params: {pageNum, pageSize},
body: tab?.status != null ? {status: tab.status} : {},
})
})
function tabsChange(e: { index: number }) {
currentTab.value = e.index
paging.value?.reload()
}
function goDetail(item: GroupMealReservationVo) {
if (!item.id) return
uni.navigateTo({
url: `/pages-user/pages/group-meal-reservation/detail?id=${item.id}`,
})
}
function dialPhone(item: GroupMealReservationVo) {
callPhone(getGroupMealContactPhone(item))
}
onShow(() => {
if (!userStore.isLogin) return
nextTick(() => {
paging.value?.reload()
})
})
</script>
<template>
<z-paging ref="paging" v-model="dataList" :auto="false" @query="queryList">
<template #top>
<navbar :title="t('navbar-group-meal-reservation')"/>
<wd-tabs v-model="currentTab" :lineWidth="67" animated color="#14181B" inactiveColor="#666666" sticky
@change="tabsChange">
<wd-tab v-for="(item, index) in tabsList" :key="index" :title="item.title"/>
</wd-tabs>
</template>
<view v-show="loading" class="px-30rpx py-20rpx">
<view v-for="n in 4" :key="n" class="bg-white rounded-16rpx p-24rpx mb-20rpx">
<view class="w-200rpx h-28rpx skeleton-item mb-20rpx"/>
<view class="w-full h-24rpx skeleton-item mb-16rpx"/>
<view class="w-300rpx h-24rpx skeleton-item"/>
</view>
</view>
<view v-show="!loading" class="px-30rpx py-20rpx">
<view
v-for="item in dataList"
:key="item.id"
class="bg-white rounded-16rpx p-24rpx mb-20rpx last:mb-0"
@click="goDetail(item)"
>
<view class="flex-center-sb mb-20rpx">
<text class="text-32rpx text-#333 font-500">{{ item.contactName || '-' }}</text>
<text :class="statusClass(item.status)" class="text-24rpx px-16rpx py-6rpx rounded-8rpx">
{{ statusLabel(item.status) }}
</text>
</view>
<view class="flex-center-sb mb-16rpx">
<text class="text-28rpx text-#666">{{ t('pages-user.groupMealReservation.contactPhone') }}</text>
<view class="flex items-center" @click.stop="dialPhone(item)">
<text class="text-28rpx text-#007AFF mr-10rpx">{{ getGroupMealContactPhone(item) || '-' }}</text>
<image class="w-36rpx h-36rpx shrink-0" src="@img/chef/206.png"/>
</view>
</view>
<view class="flex-center-sb mb-16rpx">
<text class="text-28rpx text-#666">{{ t('pages-user.groupMealReservation.peopleCount') }}</text>
<text class="text-28rpx text-#333">{{ item.peopleCount || 0 }}</text>
</view>
<view class="flex-center-sb mb-16rpx">
<text class="text-28rpx text-#666">{{ t('pages-user.groupMealReservation.perCapitaPrice') }}</text>
<text class="text-28rpx text-#333">${{ item.perCapitaPrice ?? 0 }}</text>
</view>
<view class="flex-center-sb mb-16rpx">
<text class="text-28rpx text-#666">{{ t('pages-user.groupMealReservation.estimatedTotal') }}</text>
<text class="text-28rpx text-#333 font-500">${{ calcGroupMealTotal(item.peopleCount, item.perCapitaPrice) }}</text>
</view>
<view class="flex-center-sb">
<text class="text-28rpx text-#666">{{ t('pages-user.groupMealReservation.expectedTime') }}</text>
<text class="text-28rpx text-#333">
{{ item.expectedTime ? formatTimestampWithMonthName(item.expectedTime) : '-' }}
</text>
</view>
</view>
</view>
</z-paging>
</template>
<style lang="scss" scoped>
:deep(.wd-tabs__nav-item) {
font-size: 28rpx !important;
}
</style>
+6
View File
@@ -99,6 +99,12 @@
{
"path": "pages/home-order/refund-order"
},
{
"path": "pages/group-meal-reservation/index"
},
{
"path": "pages/group-meal-reservation/detail"
},
{
"path": "pages/store-management/index"
},
@@ -21,6 +21,13 @@ function navigateTo(url: string) {
})
return
}
if (url === '/pages-user/pages/group-meal-reservation/index' && !userStore.currentMerchantToken) {
uni.showToast({
title: t('toast.pleaseSelectStore'),
icon: 'none',
})
return
}
uni.navigateTo({
url,
})
@@ -293,30 +300,40 @@ defineExpose({
<view class="text-36rpx text-#333 font-bold lh-36rpx mb-48rpx tracking-[.04em]">
{{ t('pages.home.commonTools') }}
</view>
<view class="grid grid-cols-4">
<view class="flex flex-col items-center relative"
<scroll-view :show-scrollbar="false" class="common-tools-scroll w-full" scroll-x>
<view class="common-tools-row flex flex-nowrap">
<view class="common-tools-item flex flex-col items-center relative shrink-0"
@click="navigateTo('/pages-user/pages/home-order/refund-order')">
<view
v-if="storeInfo?.refundOrderNum && +storeInfo?.refundOrderNum > 0"
class="absolute top--10rpx right-40rpx z-2 min-w-36rpx h-36rpx rounded-full bg-#FF1212 text-28rpx text-#fff center">
class="absolute top--10rpx right-8rpx z-2 min-w-36rpx h-36rpx rounded-full bg-#FF1212 text-28rpx text-#fff center">
{{ +storeInfo?.refundOrderNum > 99 ? '99+' : storeInfo?.refundOrderNum }}
</view>
<image class="w-52rpx h-52rpx mb-24rpx" src="@img/chef/101.png"></image>
<view class="text-24rpx text-#333 lh-20rpx">{{ t('pages.home.refundOrders') }}</view>
<view class="text-24rpx text-#333 lh-20rpx text-center px-8rpx">{{ t('pages.home.refundOrders') }}</view>
</view>
<view class="flex flex-col items-center" @click="navigateTo('/pages-user/pages/income/index')">
<view class="common-tools-item flex flex-col items-center shrink-0"
@click="navigateTo('/pages-user/pages/income/index')">
<image class="w-52rpx h-52rpx mb-24rpx" src="@img/chef/102.png"></image>
<view class="text-24rpx text-#333 lh-20rpx">{{ t('pages.home.myIncome') }}</view>
<view class="text-24rpx text-#333 lh-20rpx text-center px-8rpx">{{ t('pages.home.myIncome') }}</view>
</view>
<view class="flex flex-col items-center" @click="navigateTo('/pages-user/pages/recipe/index')">
<view class="common-tools-item flex flex-col items-center shrink-0"
@click="navigateTo('/pages-user/pages/recipe/index')">
<image class="w-52rpx h-52rpx mb-24rpx" src="@img/chef/103.png"></image>
<view class="text-24rpx text-#333 lh-20rpx">{{ t('pages.home.recipes') }}</view>
<view class="text-24rpx text-#333 lh-20rpx text-center px-8rpx">{{ t('pages.home.recipes') }}</view>
</view>
<view class="flex flex-col items-center" @click="navigateTo('/pages-user/pages/coupons/index')">
<view class="common-tools-item flex flex-col items-center shrink-0"
@click="navigateTo('/pages-user/pages/coupons/index')">
<image class="w-52rpx h-52rpx mb-24rpx" src="@img/chef/104.png"></image>
<view class="text-24rpx text-#333 lh-20rpx">{{ t('pages.home.coupons') }}</view>
<view class="text-24rpx text-#333 lh-20rpx text-center px-8rpx">{{ t('pages.home.coupons') }}</view>
</view>
</view>
<view class="common-tools-item flex flex-col items-center shrink-0"
@click="navigateTo('/pages-user/pages/group-meal-reservation/index')">
<image class="w-52rpx h-52rpx mb-24rpx" src="@img/chef/9191.png"></image>
<view class="text-24rpx text-#333 lh-20rpx text-center px-8rpx">{{ t('pages.home.groupMealReservation') }}</view>
</view>
</view>
</scroll-view>
</view>
<!-- 订单列表 -->
@@ -407,6 +424,21 @@ defineExpose({
</template>
<style lang="scss" scoped>
.common-tools-scroll {
width: 100%;
white-space: nowrap;
}
.common-tools-row {
min-width: 100%;
}
.common-tools-item {
width: 20%;
min-width: 136rpx;
box-sizing: border-box;
}
:deep(.wd-tabs__nav-item) {
font-size: 30rpx !important;
+102
View File
@@ -0,0 +1,102 @@
/* eslint-disable */
import request from '@/http/vue-query';
import type { CustomRequestOptions } from '@/http/types';
export type GroupMealReservationVo = {
id?: number;
merchantId?: number;
userId?: number;
peopleCount?: number;
perCapitaPrice?: number;
scene?: string;
contactName?: string;
contactPhone?: string;
expectedTime?: number;
status?: number;
handleRemark?: string;
cancelReason?: string;
remark?: string;
createTime?: number;
merchant?: {
merchantName?: string;
logo?: string;
merchantAddress?: string;
};
user?: {
nickName?: string;
surname?: string;
firstName?: string;
phone?: string;
};
};
export type GroupMealReservationQueryBo = {
status?: number;
contactName?: string;
contactPhone?: string;
expectedBeginTime?: number;
expectedEndTime?: number;
createBeginTime?: number;
createEndTime?: number;
};
export type GroupMealReservationStatusBo = {
id: number;
status: 2 | 3 | 4 | 5;
handleRemark?: string;
};
/** 商户端团餐预定列表 POST /app/groupMealReservation/merchantList */
export async function appGroupMealReservationMerchantListPost({
params,
body,
options,
}: {
params: { pageNum: number; pageSize: number };
body?: GroupMealReservationQueryBo;
options?: CustomRequestOptions;
}) {
return request<any>('/app/groupMealReservation/merchantList', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
params: {
...params,
},
data: body || {},
...(options || {}),
});
}
/** 商户端团餐预定详情 GET /app/groupMealReservation/merchant/{id} */
export async function appGroupMealReservationMerchantIdGet({
params,
options,
}: {
params: { id: number | string };
options?: CustomRequestOptions;
}) {
return request<{ data?: GroupMealReservationVo }>(`/app/groupMealReservation/merchant/${params.id}`, {
method: 'GET',
...(options || {}),
});
}
/** 商户端处理团餐预定状态 POST /app/groupMealReservation/merchant/status */
export async function appGroupMealReservationMerchantStatusPost({
body,
options,
}: {
body: GroupMealReservationStatusBo;
options?: CustomRequestOptions;
}) {
return request<{ code?: number; msg?: string }>('/app/groupMealReservation/merchant/status', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
+1
View File
@@ -28,6 +28,7 @@ export * from './merchantCart';
export * from './coupon';
export * from './search';
export * from './merchantOrder';
export * from './groupMealReservation';
export * from './featuredMerchant';
export * from './feedback';
export * from './membershipConfig';
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

+15
View File
@@ -0,0 +1,15 @@
import {GroupMealReservationStatus} from '@/constant/enums'
export function calcGroupMealTotal(peopleCount?: number, perCapitaPrice?: number) {
const count = Number(peopleCount) || 0
const price = Number(perCapitaPrice) || 0
return (count * price).toFixed(2)
}
export function canMerchantHandleGroupMeal(status?: number) {
return status === GroupMealReservationStatus.PENDING || status === GroupMealReservationStatus.CONTACTED
}
export function getGroupMealContactPhone(item?: { contactPhone?: string; user?: { phone?: string } }) {
return (item?.contactPhone || item?.user?.phone || '').trim()
}