diff --git a/src/locale/en.json b/src/locale/en.json index ea2b2b3..13b241d 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -234,14 +234,15 @@ "featured-dishes": "Featured Dishes", "nearby-merchants": "Nearby Merchants", "open-member": "Become a Member", - "recharge-now": "Recharge Now", + "recharge-now": "Deposit Now", "quickTabs": { - "memberZone": "Member Zone", - "liveSeafoodAir": "Limited Live Seafood", - "mustEatList": "CHEFLINK Must-Eat", - "newCalendar": "New Arrival Calendar", + "memberZone": "Members' Picks", + "liveSeafoodAir": "Live Seafood – Limited Supply", + "mustEatList": "Must-Try List", + "newCalendar": "New Arrival", "newCalendarNav": "Today's New", - "freshSeafoodToday": "Fresh Seafood Today", + "freshSeafoodToday": "Day-Boat Fresh Catch", + "groupCatering": "Catering & Group Orders", "energyMeal": "Energy Meals" }, "mustEatListTabs": { @@ -481,6 +482,8 @@ "deliveryInfo": "Delivery", "driverTip": "Driver tip", "driverTipNote": "100% of your tip goes to the driver.", + "tipByDeliveryDatePrefix": "Delivery date ", + "tipNextTime": "Maybe next time", "fillAddressHint": "Add delivery address and contact", "freeTag": "Free", "localDelivery": "Local delivery", @@ -488,6 +491,8 @@ "localDeliverySubtitleSuffix": ".", "memberThanksPrefix": "Thanks for being a ", "memberThanksSuffix": " member.", + "missingParamsHint": "Missing checkout parameters. Please return to cart and try again.", + "emptyCartHint": "Your cart is empty. Please add items before checkout.", "orderTotalLine": "Order total", "orderBreakdown": "Order details", "pay": "Pay", @@ -551,6 +556,7 @@ "cancellationTitle": "Merchant processing in progress", "cancelled": "Cancelled", "deliveryAddress": "Delivery address", + "deliveryDate": "Delivery date", "deliveryPhotos": "Delivery of photos", "deliveryTime": "Delivery time", "beforeDeadline": " before", @@ -596,8 +602,71 @@ "empty": "No energy meals yet", "unavailable": "This bundle is unavailable" }, + "groupCatering": { + "cancelAction": "Cancel Reservation", + "cancelConfirm": "Are you sure you want to cancel this group meal reservation?", + "cancelReasonPlaceholder": "Enter a cancellation reason (optional)", + "cancelSuccess": "Cancelled successfully", + "cancelTitle": "Cancel Reservation", + "contactNamePlaceholder": "Enter contact name", + "contactPhonePlaceholder": "Enter contact phone", + "detailTitle": "Reservation Details", + "fieldCancelReason": "Cancellation Reason", + "fieldContactName": "Contact Name", + "fieldContactPhone": "Contact Phone", + "fieldEstimatedTotal": "Estimated Total", + "fieldExpectedTime": "Expected Meal/Delivery Time", + "fieldHandleRemark": "Merchant Notes", + "fieldMerchant": "Merchant", + "fieldPeopleCount": "Headcount", + "fieldPerCapitaPrice": "Price Per Person", + "fieldRemark": "Remarks", + "fieldScene": "Occasion", + "intro": "Group meal reservations are inquiry requests sent to merchants. They do not go through cart checkout or payment. Fill in headcount, budget, and expected time, and the merchant will follow up with menu and pricing details.", + "listEmpty": "No group meal reservations yet", + "emptyAction": "Submit a Reservation", + "merchantEmpty": "No merchants available", + "peopleCountPlaceholder": "Enter headcount", + "perCapitaPricePlaceholder": "Enter price per person", + "remarkPlaceholder": "e.g. delivery address or dietary notes", + "sceneOptions": { + "birthday": "Birthday Party", + "company": "Company Meeting", + "other": "Other", + "party": "Party / Celebration", + "school": "School Event" + }, + "scenePlaceholder": "Custom occasion", + "selectDate": "Select date", + "selectExpectedTime": "Select expected time", + "selectMerchant": "Select merchant", + "selectTime": "Select time", + "statusCancelled": "Cancelled by User", + "statusCompleted": "Completed", + "statusConfirmed": "Confirmed", + "statusContacted": "Contacted", + "statusPending": "Pending", + "statusRejected": "Rejected", + "submitAction": "Submit Reservation", + "submitSuccess": "Submitted successfully", + "tabList": "My Reservations", + "tabSubmit": "New Reservation", + "tips": "Note: Estimated total = headcount × price per person. Final pricing is subject to merchant confirmation.", + "title": "Catering & Group Orders", + "validation": { + "contactName": "Please enter contact name", + "contactPhone": "Please enter contact phone", + "expectedTime": "Please select expected meal/delivery time", + "merchant": "Please select a merchant", + "peopleCount": "Headcount must be greater than 0", + "perCapitaPrice": "Price per person cannot be less than 0", + "scene": "Please enter an occasion" + }, + "viewAndCancel": "View details / cancellable" + }, "store": { "addToCart": "Add to cart", + "quantity": "Quantity", "appetizers": "Appetizers", "merchantDiscounts": "Merchant discounts", "claimNow": "Claim now", @@ -845,10 +914,22 @@ "password-length-limit": "Password must be 8-16 characters long" }, "recharge": { + "activityDetailTitle": "Recharge Promotion", + "activityPlaceholderDesc": "Promotion details are coming soon.", + "activityRechargeBtn": "Deposit Now", + "activityRulesTitle": "Promotion Rules", "amount": "Recharge amount", "amount-invalid": "The recharge amount cannot be less than 0", "description": "Please enter your recharge amount", "pay-method": "Please select a payment method", + "payMethodStripe": "Credit card (Stripe)", + "payMethodZip": "ZIP voucher payment", + "zipPayHint": "Scan the QR code to pay, then upload your payment proof", + "uploadVoucher": "Upload payment proof", + "voucherRequired": "Please upload payment proof first", + "voucherUploaded": "Proof uploaded", + "zipSubmitSuccess": "Proof submitted — pending review", + "promotionTier": "Deposit {recharge}, get {gift} bonus", "success": "Recharge successful", "title": "Recharge Balance" }, diff --git a/src/locale/zh-Hans.json b/src/locale/zh-Hans.json index f845d35..60040c5 100644 --- a/src/locale/zh-Hans.json +++ b/src/locale/zh-Hans.json @@ -236,13 +236,14 @@ "open-member": "开通会员", "recharge-now": "立即充值", "quickTabs": { - "memberZone": "会员专区", - "liveSeafoodAir": "限量空运活海鲜", - "mustEatList": "CHEFLINK必吃榜", + "memberZone": "会员精选", + "liveSeafoodAir": "限量活海鲜", + "mustEatList": "必吃榜单", "newCalendar": "上新日历", "newCalendarNav": "今日上新", "freshSeafoodToday": "今日现打海鲜", - "energyMeal": "能量餐" + "groupCatering": "团餐预定", + "energyMeal": "能量餐食" }, "mustEatListTabs": { "merchant": "商家榜单", @@ -481,6 +482,8 @@ "deliveryInfo": "配送信息", "driverTip": "司机小费", "driverTipNote": "您所支付的小费 100% 归司机所有", + "tipByDeliveryDatePrefix": "配送日期 ", + "tipNextTime": "下次再说吧", "fillAddressHint": "请填写您的送货地址及联系方式", "freeTag": "免费", "localDelivery": "本地配送", @@ -488,6 +491,8 @@ "localDeliverySubtitleSuffix": " 统一配送!", "memberThanksPrefix": "感谢您成为 ", "memberThanksSuffix": " 会员。", + "missingParamsHint": "页面参数缺失,请返回购物车重新结算", + "emptyCartHint": "购物车暂无商品,请返回添加后再结算", "orderTotalLine": "订单总计", "orderBreakdown": "订单明细", "pay": "付款", @@ -551,6 +556,7 @@ "cancellationTitle": "商户处理中", "cancelled": "已取消", "deliveryAddress": "配送地址", + "deliveryDate": "配送日期", "deliveryPhotos": "送达照片", "deliveryTime": "送达时间", "beforeDeadline": "前", @@ -596,8 +602,71 @@ "empty": "暂无能量餐", "unavailable": "套餐暂不可购买" }, + "groupCatering": { + "cancelAction": "取消预定", + "cancelConfirm": "确定要取消这条团餐预定吗?", + "cancelReasonPlaceholder": "请输入取消原因(选填)", + "cancelSuccess": "取消成功", + "cancelTitle": "取消预定", + "contactNamePlaceholder": "请输入联系人", + "contactPhonePlaceholder": "请输入联系电话", + "detailTitle": "预定详情", + "fieldCancelReason": "取消原因", + "fieldContactName": "联系人", + "fieldContactPhone": "联系电话", + "fieldEstimatedTotal": "预计总价", + "fieldExpectedTime": "期望用餐/配送时间", + "fieldHandleRemark": "商家备注", + "fieldMerchant": "选择商家", + "fieldPeopleCount": "团餐人数", + "fieldPerCapitaPrice": "人均单价", + "fieldRemark": "备注", + "fieldScene": "用餐场景", + "intro": "团餐预定是向商家提交的咨询线索,不进入购物车或支付流程。请填写人数、预算和期望时间,商家会与您进一步沟通菜单与报价。", + "listEmpty": "暂无团餐预定记录", + "emptyAction": "去提交预定", + "merchantEmpty": "暂无可选商家", + "peopleCountPlaceholder": "请输入人数", + "perCapitaPricePlaceholder": "请输入人均单价", + "remarkPlaceholder": "例如配送地址、忌口要求等", + "sceneOptions": { + "birthday": "生日宴", + "company": "公司会议", + "other": "其他", + "party": "派对宴请", + "school": "学校活动" + }, + "scenePlaceholder": "可自定义场景", + "selectDate": "选择日期", + "selectExpectedTime": "请选择期望时间", + "selectMerchant": "请选择商家", + "selectTime": "选择时间", + "statusCancelled": "用户取消", + "statusCompleted": "已完成", + "statusConfirmed": "已确认", + "statusContacted": "已联系", + "statusPending": "待处理", + "statusRejected": "已拒绝", + "submitAction": "提交预定", + "submitSuccess": "提交成功", + "tabList": "我的预定", + "tabSubmit": "提交预定", + "tips": "温馨提示:预计总价 = 团餐人数 × 人均单价,最终以商家确认报价为准。", + "title": "团餐预定", + "validation": { + "contactName": "请输入联系人", + "contactPhone": "请输入联系电话", + "expectedTime": "请选择期望用餐/配送时间", + "merchant": "请选择商家", + "peopleCount": "团餐人数必须大于 0", + "perCapitaPrice": "人均单价不能小于 0", + "scene": "请输入用餐场景" + }, + "viewAndCancel": "查看详情 / 可取消" + }, "store": { "addToCart": "加入购物车", + "quantity": "数量", "appetizers": "开胃菜", "merchantDiscounts": "商家折扣", "claimNow": "立即领取", @@ -845,10 +914,22 @@ "password-length-limit": "密码长度必须为8-16位" }, "recharge": { + "activityDetailTitle": "充值活动", + "activityPlaceholderDesc": "活动详情即将上线,敬请期待。", + "activityRechargeBtn": "立即充值", + "activityRulesTitle": "活动规则", "amount": "充值金额", "amount-invalid": "充值金额不能小于0", "description": "请输入您的充值金额", "pay-method": "请选择支付方式", + "payMethodStripe": "信用卡(Stripe)", + "payMethodZip": "ZIP 凭证支付", + "zipPayHint": "请扫码完成转账后,上传支付凭证截图", + "uploadVoucher": "上传支付凭证", + "voucherRequired": "请先上传支付凭证", + "voucherUploaded": "凭证已上传", + "zipSubmitSuccess": "已提交凭证,等待后台审核", + "promotionTier": "充{recharge}送{gift}", "success": "充值成功", "title": "余额充值" }, diff --git a/src/manifest.json b/src/manifest.json index 874148c..7ba565a 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,8 +2,8 @@ "name" : "CHEFLINK delivery", "appid" : "__UNI__06509BE", "description" : "", - "versionName" : "3.2.5", - "versionCode" : 325, + "versionName" : "3.2.9", + "versionCode" : 329, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/src/pages-store/pages/dishes/components/featured-dish-topic-page.vue b/src/pages-store/pages/dishes/components/featured-dish-topic-page.vue index 512037e..bf2ad61 100644 --- a/src/pages-store/pages/dishes/components/featured-dish-topic-page.vue +++ b/src/pages-store/pages/dishes/components/featured-dish-topic-page.vue @@ -160,8 +160,8 @@ defineExpose({ - - + + - - + + +import { dayjs } from '@/plugin/index' +import { + appGroupMealReservationCancelPost, + appGroupMealReservationIdGet, + type GroupMealReservationVo, +} from '@/service' +import { useConfigStore } from '@/store' +import { formatTimestampWithMonthName } from '@/utils/utils' +import { + calcGroupMealEstimatedTotal, + canCancelGroupMeal, + formatGroupMealMoney, +} from './utils' + +const { t } = useI18n() +const configStore = useConfigStore() + +const reservationId = ref('') +const loading = ref(true) +const submitting = ref(false) +const detail = ref({}) +const cancelReason = ref('') + +const estimatedTotal = computed(() => + formatGroupMealMoney(calcGroupMealEstimatedTotal(detail.value)), +) + +const statusText = computed(() => { + const status = Number(detail.value.status) + const keyMap: Record = { + 1: 'statusPending', + 2: 'statusContacted', + 3: 'statusConfirmed', + 4: 'statusRejected', + 5: 'statusCompleted', + 6: 'statusCancelled', + } + const key = keyMap[status] + return key ? t(`pages-store.groupCatering.${key}`) : '--' +}) + +const canCancel = computed(() => canCancelGroupMeal(Number(detail.value.status))) + +function formatExpectedTime(value?: number) { + if (!value) return '--' + return formatTimestampWithMonthName(Number(value)) +} + +async function loadDetail() { + if (!reservationId.value) return + loading.value = true + try { + const res = await appGroupMealReservationIdGet({ id: reservationId.value }) + detail.value = res.data ?? {} + } catch { + detail.value = {} + } finally { + loading.value = false + } +} + +async function handleCancel() { + if (!canCancel.value || submitting.value || !reservationId.value) return + submitting.value = true + try { + await appGroupMealReservationCancelPost({ + body: { + id: reservationId.value, + cancelReason: cancelReason.value.trim() || undefined, + }, + }) + uni.showToast({ + title: t('pages-store.groupCatering.cancelSuccess'), + icon: 'none', + }) + setTimeout(() => { + uni.navigateBack() + }, 800) + } finally { + submitting.value = false + } +} + +function confirmCancel() { + uni.showModal({ + title: t('pages-store.groupCatering.cancelTitle'), + content: t('pages-store.groupCatering.cancelConfirm'), + success: (res) => { + if (res.confirm) { + handleCancel() + } + }, + }) +} + +onLoad((query?: Record) => { + reservationId.value = String(query?.id ?? '') + loadDetail() +}) + + +