From 0bffe81c6b77ad92b4e9c38531ad1b2306f66248 Mon Sep 17 00:00:00 2001 From: "pikachu1995@126.com" Date: Mon, 28 Sep 2026 14:37:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E9=A2=84=E7=BA=A6?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E4=B8=8E=E7=A4=BC=E5=93=81=E5=8D=A1=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增礼品卡页面,支持礼品卡的选择与使用。 - 调整预约下单流程,增强预约信息展示与用户交互。 - 更新支付成功页面的导航逻辑,确保用户体验流畅。 - 优化搜索页面的历史关键词加载逻辑,提升性能。 --- README.md | 1 + api/appointment.js | 17 +- components/m-buy/goods.vue | 36 +- manifest.json | 2 +- pages.json | 14 +- pages/cart/gift-card/index.vue | 209 ++++ pages/cart/payment/payOrder.vue | 2 +- pages/cart/payment/success.vue | 6 +- pages/navigation/search/searchPage.vue | 54 +- pages/order/afterSales/afterSalesDetail.vue | 51 +- pages/order/afterSales/afterSalesSelect.vue | 10 +- .../order/appointment/appointmentCheckout.vue | 942 ++++++++++++++---- .../appointment/appointmentReschedule.vue | 234 ----- pages/order/components/verificationCodes.vue | 44 + pages/order/fillorder.vue | 416 ++++---- pages/order/myOrder.vue | 232 +++-- pages/order/orderDetail.vue | 318 ++++-- pages/product/goods.vue | 96 +- pages/product/product/style.scss | 22 +- pages/product/shopPage.vue | 45 +- pages/tabbar/user/my.vue | 96 +- utils/appointmentGoods.js | 438 +++++++- utils/filters.js | 14 +- utils/goodsType.js | 4 + utils/theme.js | 7 +- 25 files changed, 2272 insertions(+), 1038 deletions(-) create mode 100644 pages/cart/gift-card/index.vue delete mode 100644 pages/order/appointment/appointmentReschedule.vue create mode 100644 pages/order/components/verificationCodes.vue diff --git a/README.md b/README.md index 8951d6f..a5b607a 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ LILISHOP 是基于 Spring Boot / Spring Cloud / Vue / Uniapp 开发的 Java 开 - ✅ 卡密商品 - ✅ 二级分销 - ✅ 定时任务管理 +- ✅ 服务商品 - 🔜 主题色管理 - 🔜 店铺结算汇总 - 🔜 支付方式汇总 diff --git a/api/appointment.js b/api/appointment.js index 24c7984..a72e5ad 100644 --- a/api/appointment.js +++ b/api/appointment.js @@ -6,7 +6,7 @@ import { http, Method } from '@/utils/request.js' /** 查询可预约时段 */ export function listSlots(skuId, slotDate) { return http.request({ - url: '/buyer/appointment/slots', + url: '/appointment/slots', method: Method.GET, needToken: true, params: { skuId, slotDate }, @@ -16,7 +16,7 @@ export function listSlots(skuId, slotDate) { /** 绑定预约结算上下文(addToCart 后、createTrade 前) */ export function bindCheckout(data) { return http.request({ - url: '/buyer/appointment/checkout/bind', + url: '/appointment/checkout/bind', method: Method.POST, needToken: true, data, @@ -26,7 +26,7 @@ export function bindCheckout(data) { /** 预约商品扩展配置 */ export function getGoodsExt(goodsId) { return http.request({ - url: `/buyer/appointment/goods/${goodsId}/ext`, + url: `/appointment/goods/${goodsId}/ext`, method: Method.GET, needToken: false, }) @@ -35,18 +35,9 @@ export function getGoodsExt(goodsId) { /** 买家预约订单详情(时段、核销码) */ export function getAppointmentOrderDetail(orderSn) { return http.request({ - url: `/buyer/appointment/order/${orderSn}`, + url: `/appointment/order/${orderSn}`, method: Method.GET, needToken: true, }) } -/** 改期申请 */ -export function requestReschedule(orderSn, payload) { - return http.request({ - url: `/buyer/appointment/orders/${orderSn}/reschedule`, - method: Method.POST, - needToken: true, - data: payload || {}, - }) -} diff --git a/components/m-buy/goods.vue b/components/m-buy/goods.vue index 7c5b8e4..2cb9679 100644 --- a/components/m-buy/goods.vue +++ b/components/m-buy/goods.vue @@ -53,7 +53,11 @@ ,{{ num }}个 - + + 可预约 + 按时段选择 + + 库存 {{ availableStock }} @@ -105,7 +109,9 @@ 加入购物车 - 立即购买 + + {{ isAppointmentGoods(goodsDetail?.goodsType) ? '立即预约' : '立即购买' }} + @@ -124,6 +130,8 @@ import { isAppointmentGoods, getECouponStock, getECouponMaxBuyNum, + getAppointmentStock, + getAppointmentMaxBuyNum, } from '@/utils/goodsType.js' const props = withDefaults(defineProps<{ @@ -159,11 +167,14 @@ const formatList = ref([]) const currentSelected = ref([]) const skuList = ref('') -// E_COUPON 库存读 SKU quantity,上限 min(quantity, 999) +// E_COUPON 读 SKU quantity;预约商品 quantity 固定 0,库存走时段 const availableStock = computed(() => { if (isECoupon(props.goodsDetail?.goodsType)) { return getECouponStock(props.goodsDetail) } + if (isAppointmentGoods(props.goodsDetail?.goodsType)) { + return getAppointmentStock(props.goodsDetail) + } return Number(props.goodsDetail?.quantity) || 0 }) @@ -171,6 +182,9 @@ const maxBuyNum = computed(() => { if (isECoupon(props.goodsDetail?.goodsType)) { return getECouponMaxBuyNum(props.goodsDetail) } + if (isAppointmentGoods(props.goodsDetail?.goodsType)) { + return getAppointmentMaxBuyNum(props.goodsDetail) + } return 999 }) @@ -340,17 +354,17 @@ const addToCartOrBuy = (val: string) => { } }) } else { + if (isAppointmentGoods(props.goodsDetail?.goodsType)) { + const goodsId = props.goodsDetail.goodsId || '' + uni.navigateTo({ + url: `/pages/order/appointment/appointmentCheckout?skuId=${props.goodsDetail.id}&goodsId=${goodsId}&num=${num.value}`, + }) + closeMask() + return + } data.cartType = resolveCartType() API_trade.addToCart(data).then((res) => { if (res.data.success) { - if (isAppointmentGoods(props.goodsDetail?.goodsType)) { - const goodsId = props.goodsDetail.goodsId || '' - uni.navigateTo({ - url: `/pages/order/appointment/appointmentCheckout?skuId=${props.goodsDetail.id}&goodsId=${goodsId}&num=${num.value}`, - }) - closeMask() - return - } uni.navigateTo({ url: `/pages/order/fillorder?way=${data.cartType}&addr=${props.addr?.id || ''}&parentOrder=${encodeURIComponent(JSON.stringify(parentOrder.value || ''))}`, }) diff --git a/manifest.json b/manifest.json index 050da72..c5c635b 100644 --- a/manifest.json +++ b/manifest.json @@ -210,7 +210,7 @@ }, "plugins" : {}, "libVersion" : "3.5.8", - "__usePrivacyCheck__" : true, + "__usePrivacyCheck__" : false, "requiredPrivateInfos" : [ "chooseLocation", "getLocation" ] }, "h5" : { diff --git a/pages.json b/pages.json index 05abb10..8a8b926 100644 --- a/pages.json +++ b/pages.json @@ -777,6 +777,12 @@ "navigationBarTitleText": "我的礼品卡" } }, + { + "path": "gift-card/index", + "style": { + "navigationBarTitleText": "礼品卡" + } + }, { "path": "payment/payOrder", "style": { @@ -876,13 +882,7 @@ { "path": "appointment/appointmentCheckout", "style": { - "navigationBarTitleText": "预约信息" - } - }, - { - "path": "appointment/appointmentReschedule", - "style": { - "navigationBarTitleText": "申请改期" + "navigationBarTitleText": "立即预约" } }, { diff --git a/pages/cart/gift-card/index.vue b/pages/cart/gift-card/index.vue new file mode 100644 index 0000000..9d7fced --- /dev/null +++ b/pages/cart/gift-card/index.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/pages/cart/payment/payOrder.vue b/pages/cart/payment/payOrder.vue index 52f4efc..f244b23 100644 --- a/pages/cart/payment/payOrder.vue +++ b/pages/cart/payment/payOrder.vue @@ -124,7 +124,7 @@ function hideLoadingIfNeeded() { } function callback(paymentMethod: string) { - uni.navigateTo({ + uni.redirectTo({ url: '/pages/cart/payment/success?paymentMethod=' + paymentMethod + diff --git a/pages/cart/payment/success.vue b/pages/cart/payment/success.vue index 21ff222..9b5722b 100644 --- a/pages/cart/payment/success.vue +++ b/pages/cart/payment/success.vue @@ -60,9 +60,11 @@ function checkOrder() { uni.reLaunch({ url: '/pages/mine/deposit/operation', }) - } else { - navigateTo('/pages/order/myOrder?status=0') + return } + uni.reLaunch({ + url: '/pages/order/myOrder?status=0', + }) } function navigateTo(url: string, type?: string) { diff --git a/pages/navigation/search/searchPage.vue b/pages/navigation/search/searchPage.vue index 615e532..9a9ddf3 100644 --- a/pages/navigation/search/searchPage.vue +++ b/pages/navigation/search/searchPage.vue @@ -37,7 +37,7 @@
- + 热门搜索 @@ -624,31 +624,43 @@ function loadOldKeyword(index: number) { }) } +function getHistoryKeywords(limit = 10) { + try { + const raw = uni.getStorageSync('OldKeys') + const list = raw ? JSON.parse(raw) : [] + return Array.isArray(list) ? list.filter(Boolean).slice(0, limit) : [] + } catch { + return [] + } +} + async function loadHotKeyword() { - hotKeywordList.value = [] - if ( - !storage.getHotWords().time || - storage.getHotWords().time <= new Date().getTime() / 1000 - ) { - const res = await getHotKeywords(10) - const keywords = res.data.result + const cached = storage.getHotWords() || {} + const now = Date.now() / 1000 + let keywords: string[] = [] - keywords.forEach((item: string) => { - hotKeywordList.value.push(item) - }) - - const hotData = { - time: new Date().getTime() / 1000 + 30 * 5, - keywords: keywords, - } - storage.setHotWords(hotData) + if (cached.time && cached.time > now && Array.isArray(cached.keywords) && cached.keywords.length) { + keywords = cached.keywords } else { - const keywords = storage.getHotWords().keywords - - keywords.forEach((item: string) => { - hotKeywordList.value.push(item) + try { + const res = await getHotKeywords(10) + if (res?.data?.success && Array.isArray(res.data.result)) { + keywords = res.data.result.filter(Boolean) + } + } catch { + keywords = [] + } + storage.setHotWords({ + time: now + 30 * 5, + keywords, }) } + + if (!keywords.length) { + keywords = getHistoryKeywords(10) + } + + hotKeywordList.value = keywords loadDefaultKeyword() } diff --git a/pages/order/afterSales/afterSalesDetail.vue b/pages/order/afterSales/afterSalesDetail.vue index fe01e0f..557753e 100644 --- a/pages/order/afterSales/afterSalesDetail.vue +++ b/pages/order/afterSales/afterSalesDetail.vue @@ -17,7 +17,7 @@ - - diff --git a/pages/order/components/verificationCodes.vue b/pages/order/components/verificationCodes.vue new file mode 100644 index 0000000..afe9184 --- /dev/null +++ b/pages/order/components/verificationCodes.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/pages/order/fillorder.vue b/pages/order/fillorder.vue index 219eb69..7ed2831 100644 --- a/pages/order/fillorder.vue +++ b/pages/order/fillorder.vue @@ -1,5 +1,5 @@