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 new file mode 100644 index 0000000..a72e5ad --- /dev/null +++ b/api/appointment.js @@ -0,0 +1,43 @@ +/** + * 预约服务买家端 API + */ +import { http, Method } from '@/utils/request.js' + +/** 查询可预约时段 */ +export function listSlots(skuId, slotDate) { + return http.request({ + url: '/appointment/slots', + method: Method.GET, + needToken: true, + params: { skuId, slotDate }, + }) +} + +/** 绑定预约结算上下文(addToCart 后、createTrade 前) */ +export function bindCheckout(data) { + return http.request({ + url: '/appointment/checkout/bind', + method: Method.POST, + needToken: true, + data, + }) +} + +/** 预约商品扩展配置 */ +export function getGoodsExt(goodsId) { + return http.request({ + url: `/appointment/goods/${goodsId}/ext`, + method: Method.GET, + needToken: false, + }) +} + +/** 买家预约订单详情(时段、核销码) */ +export function getAppointmentOrderDetail(orderSn) { + return http.request({ + url: `/appointment/order/${orderSn}`, + method: Method.GET, + needToken: true, + }) +} + diff --git a/api/promotions.js b/api/promotions.js index 69b4455..2724ea0 100644 --- a/api/promotions.js +++ b/api/promotions.js @@ -202,4 +202,40 @@ export function getMineBargainLog(params) { }); } +/** + * 分页查询当前客户礼品卡 + */ +export function getGiftCardCashMemberCardPage(params) { + return http.request({ + url: '/promotion/giftCardCash/memberCard', + method: Method.GET, + needToken: true, + params, + }) +} + +/** + * 绑定礼品卡(卡号 + 兑换码/卡密) + */ +export function bindGiftCardCashMemberCard(data) { + return http.request({ + url: '/promotion/giftCardCash/memberCard/bind', + method: Method.POST, + needToken: true, + header: { 'content-type': 'application/json' }, + data, + }) +} + +/** + * 激活礼品卡 + */ +export function activateGiftCardCashMemberCard(memberCardId) { + return http.request({ + url: `/promotion/giftCardCash/memberCard/${memberCardId}/activate`, + method: Method.POST, + needToken: true, + }) +} + diff --git a/components/m-buy/goods.vue b/components/m-buy/goods.vue index 55ae097..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) ? '立即预约' : '立即购买' }} + @@ -121,8 +127,11 @@ import { goodsFormatPrice } from '@/utils/filters.js' import { isECoupon, isVirtualGoods, + isAppointmentGoods, getECouponStock, getECouponMaxBuyNum, + getAppointmentStock, + getAppointmentMaxBuyNum, } from '@/utils/goodsType.js' const props = withDefaults(defineProps<{ @@ -158,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 }) @@ -170,15 +182,19 @@ const maxBuyNum = computed(() => { if (isECoupon(props.goodsDetail?.goodsType)) { return getECouponMaxBuyNum(props.goodsDetail) } + if (isAppointmentGoods(props.goodsDetail?.goodsType)) { + return getAppointmentMaxBuyNum(props.goodsDetail) + } return 999 }) -// 电子卡券禁止加购(后端 CARD_KEY_E_COUPON_CART_FORBIDDEN) +// 电子卡券/预约商品禁止加购 const showAddToCart = computed(() => { return ( buyType.value != 'PINTUAN' && !isVirtualGoods(props.goodsDetail?.goodsType) && - !isECoupon(props.goodsDetail?.goodsType) + !isECoupon(props.goodsDetail?.goodsType) && + !isAppointmentGoods(props.goodsDetail?.goodsType) ) }) @@ -338,6 +354,14 @@ 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) { 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 78db80d..8a8b926 100644 --- a/pages.json +++ b/pages.json @@ -771,6 +771,18 @@ } }, + { + "path": "gift-card/myGiftCards", + "style": { + "navigationBarTitleText": "我的礼品卡" + } + }, + { + "path": "gift-card/index", + "style": { + "navigationBarTitleText": "礼品卡" + } + }, { "path": "payment/payOrder", "style": { @@ -867,6 +879,12 @@ "enablePullDownRefresh": true } }, + { + "path": "appointment/appointmentCheckout", + "style": { + "navigationBarTitleText": "立即预约" + } + }, { "path": "deliverDetail", "style": { 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/gift-card/myGiftCards.vue b/pages/cart/gift-card/myGiftCards.vue new file mode 100644 index 0000000..9612fa1 --- /dev/null +++ b/pages/cart/gift-card/myGiftCards.vue @@ -0,0 +1,432 @@ + + + + + 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 0786a8f..557753e 100644 --- a/pages/order/afterSales/afterSalesDetail.vue +++ b/pages/order/afterSales/afterSalesDetail.vue @@ -17,7 +17,7 @@