diff --git a/env/.env.development b/env/.env.development index 4c97902..aa7e2d2 100644 --- a/env/.env.development +++ b/env/.env.development @@ -5,6 +5,6 @@ VITE_DELETE_CONSOLE=false #本地环境 #VITE_SERVER_BASEURL=https://howhowfresh.com/prod-api -VITE_SERVER_BASEURL=http://192.168.5.23:8080 +VITE_SERVER_BASEURL=http://192.168.5.3:8080 #VITE_SERVER_BASEURL=http://192.168.0.148:8888 #VITE_SERVER_BASEURL=http://liuyao.nat100.top/meiguowaimai \ No newline at end of file diff --git a/src/components/choose-image/choose-image.vue b/src/components/choose-image/choose-image.vue index bbaecd5..516a268 100644 --- a/src/components/choose-image/choose-image.vue +++ b/src/components/choose-image/choose-image.vue @@ -1,6 +1,5 @@ \ No newline at end of file diff --git a/src/pages-store/pages/list/index.vue b/src/pages-store/pages/list/index.vue index d640b66..8758a27 100644 --- a/src/pages-store/pages/list/index.vue +++ b/src/pages-store/pages/list/index.vue @@ -1,61 +1,751 @@ \ No newline at end of file +.cat-header-row { + display: flex; + align-items: center; + gap: 16rpx; + padding: 12rpx 24rpx 20rpx; +} + +.cat-icon-btn { + width: 72rpx; + height: 72rpx; + border-radius: 50%; + background: #fff; + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.cat-icon-btn--cart { + position: relative; +} + +.cat-cart-badge { + position: absolute; + top: 2rpx; + right: 2rpx; + min-width: 28rpx; + height: 28rpx; + padding: 0 6rpx; + font-size: 18rpx; + line-height: 28rpx; + font-weight: 600; + color: #fff; + text-align: center; + background: #e23636; + border-radius: 999rpx; +} + +.cat-search { + flex: 1; + min-width: 0; + height: 72rpx; + border-radius: 36rpx; + background: #fff; + box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + padding: 0 28rpx; +} + +.cat-search-icon { + width: 28rpx; + height: 28rpx; + flex-shrink: 0; +} + +.cat-search-placeholder { + margin-left: 16rpx; + font-size: 28rpx; + font-weight: 500; + color: #999; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.cat-scroll-x { + width: 100%; + white-space: nowrap; +} + +.cat-recipe-track { + display: inline-flex; + flex-direction: row; + padding: 8rpx 24rpx 16rpx; + gap: 36rpx; +} + +.cat-recipe-item { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 120rpx; + flex-shrink: 0; +} + +.cat-recipe-ring { + width: 102rpx; + height: 102rpx; + border-radius: 50%; + padding: 0; + background: #fff; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.07); + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; +} + +.cat-recipe-ring--on { + border: 4rpx solid #14181b; +} + +.cat-recipe-img { + width: 94rpx; + height: 94rpx; + border-radius: 50%; +} + +.cat-recipe-label { + margin-top: 12rpx; + font-size: 22rpx; + line-height: 1.2; + color: #333; + text-align: center; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; +} + +.cat-recipe-label--on { + color: #14181b; + font-weight: 600; +} + +.cat-waterfall { + min-height: 200rpx; +} + +.cat-dish-card { + background: #fff; + border-radius: 24rpx; + overflow: hidden; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06); +} + +.cat-dish-collect { + position: absolute; + z-index: 2; + top: 12rpx; + right: 12rpx; + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.cat-dish-collect-icon { + width: 44rpx; + height: 44rpx; + filter: drop-shadow(0 2rpx 6rpx rgba(0, 0, 0, 0.18)); +} + +.cat-dish-soldout { + position: absolute; + z-index: 2; + left: 16rpx; + top: 16rpx; + padding: 0 14rpx; + height: 48rpx; + border-radius: 24rpx; + background: rgba(20, 24, 27, 0.75); + color: #fff; + font-size: 24rpx; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; +} + +.cat-dish-body { + padding: 20rpx 20rpx 22rpx; +} + +.cat-dish-price { + color: #e02e24; + font-size: 32rpx; + font-weight: 600; + line-height: 1.2; +} + +.cat-dish-original { + margin-left: 10rpx; + color: #b3b3b3; + font-size: 22rpx; + text-decoration: line-through; + vertical-align: baseline; +} + +.cat-dish-sales { + color: #999; + font-size: 24rpx; + line-height: 1.35; + max-width: 48%; + text-align: right; +} + +.cat-dish-title { + color: #1a1a1a; + font-size: 28rpx; + font-weight: 500; + line-height: 1.45; +} + +.cat-dish-member { + display: inline-flex; + align-items: center; + max-width: calc(100% - 88rpx); +} + +.cat-dish-member-inner { + display: inline-block; + padding: 6rpx 18rpx; + border-radius: 999rpx; + background: linear-gradient(180deg, #fff5eb 0%, #ffe8d6 100%); + color: #c45c1a; + font-size: 24rpx; + font-weight: 500; + line-height: 1.35; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100%; +} + +.cat-dish-add { + width: 56rpx; + height: 56rpx; + border-radius: 50%; + background: #14181b; + box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.12); +} + +.cat-dish-add--busy { + opacity: 0.45; + pointer-events: none; +} + +.cat-dish-promo { + padding: 12rpx 16rpx; + border-radius: 12rpx; + background: #fff0f0; +} + +.cat-dish-promo-text { + color: #e02e24; + font-size: 22rpx; + font-weight: 500; + line-height: 1.4; +} + +.dish-new-ribbon { + position: absolute; + top: 0; + left: 0; + width: 184rpx; + height: 184rpx; + overflow: hidden; + z-index: 5; + pointer-events: none; + + &__text { + position: absolute; + top: 26rpx; + left: -66rpx; + width: 240rpx; + text-align: center; + font-size: 22rpx; + font-weight: 700; + color: #fff; + letter-spacing: 2rpx; + line-height: 44rpx; + background: #E23636; + transform: rotate(-45deg); + } +} + diff --git a/src/pages-store/pages/order/checkout.vue b/src/pages-store/pages/order/checkout.vue index 1a38c94..a4fdc20 100644 --- a/src/pages-store/pages/order/checkout.vue +++ b/src/pages-store/pages/order/checkout.vue @@ -177,8 +177,8 @@ function handleClickSegmented(index: number) { // 折叠面板 const collapseValue = ref([""]); -// 单个订单的小费 -const selectedTipIndex = ref(0); +// 单个订单的小费(与设计稿一致:默认 $2) +const selectedTipIndex = ref(2); const diyTipValue = ref('') // 批量订单:每个店铺的小费索引映射 merchantId -> tipIndex const merchantTipIndexMap = ref>({}) @@ -186,18 +186,9 @@ const merchantTipIndexMap = ref>({}) const merchantDiyTipValueMap = ref>({}) const tipOptions = ref([ - { - label: "$3", - value: 3, - }, - { - label: "$5", - value: 5, - }, - { - label: "$10", - value: 10, - }, + { label: "$ 2", value: 2 }, + { label: "$ 3", value: 3 }, + { label: "$ 4", value: 4 }, ]); // 单个订单的小费选择 @@ -443,7 +434,13 @@ async function getBatchCartInfo() { const results = await Promise.all(merchantPromises); cartDataList.value = results.filter(item => item !== null); console.log('批量模式-最终购物车数据', cartDataList.value); - + cartDataList.value.forEach((m: any) => { + const id = String(m.id); + if (merchantTipIndexMap.value[id] === undefined) { + merchantTipIndexMap.value[id] = 2; + } + }); + // 批量模式:查询菜品会员折扣价 + 计算价格(严格串行) if(cartIds.length > 0) { await appMerchantCartCalculateSavings() @@ -637,18 +634,7 @@ async function getAddressList() { addressesList.value = res.rows if(addressesList.value.length > 0) { currentAddressId.value = addressesList.value[0].id - - let data = addressesList.value[0] - - // 回显送达偏好(deliveryType: 1-亲自送达 2-放门口) - if(+data.deliveryType === 1) { - visitMethod.value.label = t('components.visit.leaveItToMePersonally') - visitMethod.value.value = 0 - } - if(+data.deliveryType === 2) { - visitMethod.value.label = t('components.visit.putItAtTheDoor') - visitMethod.value.value = 1 - } + // 结算页配送偏好默认「放门口」,不按地址簿 deliveryType 回显 } } @@ -663,16 +649,7 @@ function chooseAddress() { console.log('获取被打开页面传送到当前页面的数据', data) // 将新选择的地址id存储到selectedAddressId中 selectedAddressId.value = data.data - - // 回显送达偏好(deliveryType: 1-亲自送达 2-放门口) - if(+data.deliveryType === 1) { - visitMethod.value.label = t('components.visit.leaveItToMePersonally') - visitMethod.value.value = 0 - } - if(+data.deliveryType === 2) { - visitMethod.value.label = t('components.visit.putItAtTheDoor') - visitMethod.value.value = 1 - } + // 配送偏好默认「放门口」,切换地址时保持用户在本页已选或默认值,不回写地址簿 // 重新计算价格 void appMerchantOrderCalculatePriceCart() @@ -1085,6 +1062,156 @@ function navigateToCoupon(merchantId?: string) { }, }) } +const isUserMemberCheckout = computed(() => { + const vo = userStore.userInfo.userMembershipVo; + if (!vo) return false; + const exp = vo.expireTime; + if (exp) return dayjs().isBefore(dayjs(Number(exp))); + return false; +}); + +const cartTotalPieceCount = computed(() => { + if (orderType.value === 'batch') { + let n = 0; + cartDataList.value.forEach((m: any) => { + (m.merchantCartVoList || []).forEach((item: any) => { + n += Number(item.count) || 0; + }); + }); + return n || batchCartIds.value.length; + } + return cartDataList.value.reduce( + (sum, item: any) => sum + (Number(item.count) || 0), + 0, + ); +}); + +const serviceFeeAmount = computed(() => { + const p: any = priceData.value; + if (!p) return 0; + const original = + Number(p.actualAmount ?? p.totalActualAmount ?? 0) || 0; + const ratio = Number(p.merchantVo?.platformServiceFeeRatio) || 0; + return Math.round(original * ratio * 100) / 100; +}); + +const deliveryPillText = computed(() => { + const raw = + deliveryMethod.value === 0 + ? userSelectedDeliveryTimeDate.value + : userSelectedSelfPickupTimeDate.value; + if (!raw) return t('pages-store.checkout.chooseTime'); + const str = String(raw); + const m = str.match(/^(\d{2})-(\d{2})/); + if (m) { + const y = dayjs().year(); + const d = dayjs(`${y}-${m[1]}-${m[2]}`); + if (d.isValid()) { + const w = d.format('dddd'); + return `${w}, ${m[1]}/${m[2]} >`; + } + } + return `${str} >`; +}); + +function safeToNumber(val: unknown, fallback = 0) { + const n = Number(val); + return Number.isFinite(n) ? n : fallback; +} + +function safeMoneyStr(val: unknown, fallback = 0) { + return safeToNumber(val, fallback).toFixed(2); +} + +const displayDeliveryFeeStr = computed(() => { + const p: any = priceData.value; + if (!p) return '0.00'; + if (orderType.value === 'batch') { + return safeMoneyStr(p.totalDeliveryFee, 0); + } + return safeMoneyStr(p.deliveryFee, 0); +}); + +const displayGoodsAmountStr = computed(() => { + const p: any = priceData.value; + if (!p) return '0.00'; + if (orderType.value === 'batch') { + return safeMoneyStr(p.totalActualAmount, 0); + } + return safeMoneyStr(p.actualAmount, 0); +}); + +const displayTaxStr = computed(() => { + const p: any = priceData.value; + if (!p) return '0.00'; + if (orderType.value === 'batch') { + return safeMoneyStr(p.totalTax, 0); + } + return safeMoneyStr(p.tax, 0); +}); + +const displayTipStr = computed(() => { + const p: any = priceData.value; + if (!p) return '0.00'; + if (orderType.value === 'batch') { + return safeMoneyStr(p.totalTip, 0); + } + return safeMoneyStr(p.tip, 0); +}); + +const displayPaidStr = computed(() => { + const p: any = priceData.value; + if (!p) return '0.00'; + if (orderType.value === 'batch') { + return safeMoneyStr(p.totalPaidAmount, 0); + } + return safeMoneyStr(p.paidAmount, 0); +}); + +const scheduledServiceEndLabel = computed(() => { + const slot = (diyTime.value as any)?.timeSlot; + if (slot && String(slot).includes(' - ')) { + return String(slot).split(' - ')[1]?.trim() || ''; + } + return ''; +}); + +const showAppointmentEntry = computed(() => { + if (deliveryMethod.value === 0) { + return storeIsDeliveryService.value || orderType.value === 'batch'; + } + return storeIsSelfPickup.value || orderType.value === 'batch'; +}); + +/** 商家标价配送费(用于划线价,实际运费可能因活动/距离更低) */ +const listDeliveryFeeAmount = computed(() => { + const p: any = priceData.value; + const raw = p?.merchantVo?.deliveryFee; + if (raw === undefined || raw === null || raw === '') return null; + const n = Number(raw); + return Number.isFinite(n) ? n : null; +}); + +const actualDeliveryFeeNum = computed(() => { + const p: any = priceData.value; + if (!p) return 0; + if (orderType.value === 'batch') { + return Number(p.totalDeliveryFee ?? 0); + } + return Number(p.deliveryFee ?? 0); +}); + +const showListDeliveryStrike = computed(() => { + const list = listDeliveryFeeAmount.value; + if (list == null) return false; + return list > actualDeliveryFeeNum.value + 0.005; +}); + +const listDeliveryFeeStr = computed(() => { + const list = listDeliveryFeeAmount.value; + return list != null ? list.toFixed(2) : ''; +}); + function handleClose(merchantId?: string) { if(orderType.value === 'batch' && merchantId) { // 批量模式:清除对应店铺的优惠券 @@ -1099,7 +1226,12 @@ function handleClose(merchantId?: string) {