diff --git a/package2/seckill/seckillGroup.vue b/package2/seckill/seckillGroup.vue index 2069022..b6d0dc5 100644 --- a/package2/seckill/seckillGroup.vue +++ b/package2/seckill/seckillGroup.vue @@ -37,11 +37,17 @@ - + 搜索 + + 随机推荐 + 价格升序 + 价格降序 + + @@ -54,7 +60,7 @@ - 已售{{item.sold}} + 已售999+ {{coupon}} @@ -81,8 +87,38 @@ 你有2元优惠券待使用 · 剩02:32:17 - 去使用 + + + + + + + + + + + {{currentItem.title}} + + ¥{{currentItem.price}} + ¥{{currentItem.originalPrice}} + + + + + + 选{{spec.name}}(可选{{spec.canbuy}}个) + + + {{option}} + + + + + 确认抢购 + + @@ -93,102 +129,284 @@ menuButtonInfo: { top: 0 }, - checkedTab: 'all', + regionId: '', + checkedTab: '', tabList: [{ name: '精选', - key: 'all' - }, { - name: '团美味趣露营', - key: 'camp' - }, { - name: '食品生鲜', - key: 'fresh' - }, { - name: '纸品清洁', - key: 'clean' - }, { - name: '家居百货', - key: 'home' + key: '' }], - seckillList: [] + seckillList: [], + searchName: '', + pageNum: 1, + pageSize: 10, + pages: 1, + loadStatus: 'more', + currentItem: null, + parsedSpecs: [], + sortType: '' } }, computed: { currentList() { - if (this.checkedTab === 'all') { - return this.seckillList - } - return this.seckillList.filter(item => item.category === this.checkedTab) + return this.seckillList } }, onLoad() { this.menuButtonInfo = uni.getMenuButtonBoundingClientRect() - this.getSeckillList() + this.initRegion() }, onShow() { this.menuButtonInfo = uni.getMenuButtonBoundingClientRect() }, + onReachBottom() { + if (this.pageNum >= this.pages || this.loadStatus === 'loading') return + this.pageNum++ + this.getSeckillList() + }, methods: { + initRegion() { + const area = uni.getStorageSync('area') + if (!area) { + this.tui.toast('请先选择区域') + return + } + try { + const areaInfo = typeof area === 'string' ? JSON.parse(area) : area + this.regionId = areaInfo.id || '' + } catch (e) { + this.regionId = '' + } + if (!this.regionId) { + this.tui.toast('请先选择区域') + return + } + this.getCategoryList() + }, + getCategoryList() { + this.tui.request("/app/seckillGroup/category/list", "GET", { + regionId: this.regionId + }, false, true).then((res) => { + if (res.code == 200) { + const list = Array.isArray(res.result) ? res.result : [] + this.tabList = [{ + name: '精选', + key: '' + }, ...list.map(item => ({ + name: item.categoryName, + key: item.id + }))] + this.checkedTab = '' + this.resetProductList() + this.getSeckillList() + } else { + this.tui.toast(res.message) + } + }).catch(() => {}) + }, getSeckillList() { - this.seckillList = [{ - id: 1, - category: 'fresh', - title: '正宗玉菇甜瓜新鲜水果冰淇淋脆', - image: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/e6adc80518c24c488522ab19f036af27.png', - tag: '甜度18+', - sold: '2000+', - progress: 86, - coupons: ['立减2元平台券', '券 立减5元', '坏了包赔'], - price: '17.8', - save: '13.2' - }, { - id: 2, - category: 'camp', - title: '巧面馆麻辣红油泡面川渝宿舍装', - image: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/05d2286ac1be4ae784858409889690d5.png', - tag: '106g*4袋', - sold: '6万+', - progress: 78, - coupons: ['立减2元平台券', '送赠品', '极速退款'], - price: '7.9', - save: '5' - }, { - id: 3, - category: 'clean', - title: '全麦面包纯全麦黑麦低无胆蔗糖', - image: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/d6b53eb217644e74bbf957ff7462c27b.png', - tag: '20袋', - sold: '2万+', - progress: 72, - coupons: ['立减2元平台券', '运费险', '极速退款'], - price: '14.9', - save: '13' - }, { - id: 4, - category: 'home', - title: '贵州酸辣粉宿舍速食夜宵整箱装', - image: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/0ac5e0095a5d4097b338ec450df8d3dd.png', - tag: '爆辣多汁', - sold: '6万+', - progress: 91, - coupons: ['满89件减3元', '运费险', '秒杀价'], - price: '13.9', - save: '9' - }] + if (!this.regionId) return + this.loadStatus = 'loading' + const data = { + regionId: this.regionId, + categoryId: this.checkedTab, + keywords: this.searchName, + pageNum: this.pageNum, + pageSize: this.pageSize + } + if (this.sortType) { + data.sort = 'seckillPrice' + data.order = this.sortType + } + this.tui.request("/app/seckillGroup/product/page", "POST", data, false, false).then((res) => { + this.loadStatus = 'nomore' + if (res.code == 200) { + const result = res.result || {} + const records = Array.isArray(result.records) ? result.records : [] + const list = records.map(item => this.formatSeckillItem(item)) + if (this.pageNum == 1) { + this.seckillList = list + } else { + this.seckillList = [...this.seckillList, ...list] + } + this.pages = result.pages || 1 + } else { + this.tui.toast(res.message) + } + }).catch(() => { + this.loadStatus = 'nomore' + }) + }, + formatSeckillItem(item) { + const totalStock = Number(item.totalStock || 0) + const soldStock = Number(item.soldStock || 0) + const originalPrice = Number(item.originalPrice || 0) + const seckillPrice = Number(item.seckillPrice || 0) + const save = Math.max(0, originalPrice - seckillPrice) + return { + id: item.id, + productId: item.productId, + category: item.categoryId, + title: item.productName, + image: item.productPicture, + tag: this.getSpecTag(item), + sold: soldStock > 10000 ? (soldStock / 10000).toFixed(1).replace('.0', '') + '万+' : soldStock + '+', + progress: totalStock > 0 ? Math.min(100, Math.round(soldStock * 100 / totalStock)) : 0, + coupons: ['秒杀价', '限时开抢', '库存' + (item.availableStock || 0)], + price: seckillPrice.toFixed(2), + save: save.toFixed(2), + seckillPrice: seckillPrice, + originalPrice: originalPrice.toFixed(2), + availableStock: item.availableStock || 0, + shopId: item.shopId, + shopName: item.shopName, + shopPhone: item.shopPhone, + shopAddress: item.shopAddress, + getAreaId: item.getAreaId, + attributeList: item.attributeList, + raw: item + } + }, + getSpecTag(item) { + if (item.attributeList) { + try { + const attr = JSON.parse(item.attributeList) + if (Array.isArray(attr) && attr.length > 0) { + return attr[0].value || attr[0].title || item.unit || '秒杀' + } + if (typeof attr === 'object') { + return attr.value || attr.title || item.unit || '秒杀' + } + } catch (e) { + return item.attributeList + } + } + return item.unit || '秒杀' + }, + resetProductList() { + this.pageNum = 1 + this.pages = 1 + this.seckillList = [] }, switchTab(key) { this.checkedTab = key + this.resetProductList() + this.getSeckillList() }, goSearch() { - uni.showToast({ - title: '秒杀搜索即将开放', - icon: 'none' - }) + this.resetProductList() + this.getSeckillList() + }, + setSort(type) { + if (this.sortType === type) return + this.sortType = type + this.resetProductList() + this.getSeckillList() }, grabItem(item) { - uni.showToast({ - title: item.title + ' 已加入秒杀提醒', - icon: 'none' + if (!item || !item.productId) { + this.tui.toast('商品信息异常') + return + } + if (Number(item.availableStock || 0) <= 0) { + this.tui.toast('该商品已抢光') + return + } + this.currentItem = item + this.parsedSpecs = this.parseSpecs(item.attributeList) + if (this.parsedSpecs.length > 0) { + this.$refs.specPopup.open('bottom') + return + } + this.goBuyFood(item, {}) + }, + parseSpecs(attributeList) { + if (!attributeList || attributeList === '{}') return [] + try { + const attrs = typeof attributeList === 'string' ? JSON.parse(attributeList) : attributeList + if (Array.isArray(attrs)) return [] + const specs = [] + for (let key in attrs) { + if (attrs[key] && attrs[key].title && attrs[key].title.length > 0) { + specs.push({ + name: key, + options: attrs[key].title, + canbuy: attrs[key].canbuy || 1, + selected: [] + }) + } + } + return specs + } catch (e) { + return [] + } + }, + selectSpec(sIndex, option) { + const spec = this.parsedSpecs[sIndex] + const index = spec.selected.indexOf(option) + if (index !== -1) { + spec.selected.splice(index, 1) + } else if (spec.selected.length < spec.canbuy) { + spec.selected.push(option) + } else if (spec.canbuy === 1) { + spec.selected = [option] + } else { + this.tui.toast(spec.name + '最多选' + spec.canbuy + '个') + } + this.$forceUpdate() + }, + submitSeckillBuy() { + if (!this.currentItem) return + const specs = {} + for (let spec of this.parsedSpecs) { + if (spec.selected.length === 0) { + this.tui.toast('请选择' + spec.name) + return + } + specs[spec.name] = spec.canbuy === 1 ? spec.selected[0] : spec.selected + } + this.$refs.specPopup.close() + this.goBuyFood(this.currentItem, specs) + }, + goBuyFood(item, specs) { + const seckillPrice = Number(item.seckillPrice || (item.raw && item.raw.seckillPrice) || 0) + const originalPrice = Number(item.originalPrice || (item.raw && item.raw.originalPrice) || 0) + const product = { + id: item.productId, + productName: item.title, + productPicture: item.image, + attributeList: item.attributeList || '', + attributeListPrice: JSON.stringify({ + default: { + specPrice: seckillPrice.toFixed(2), + originalPrice: originalPrice.toFixed(2) + } + }), + price: originalPrice, + seckillPrice: seckillPrice, + originalPrice: originalPrice, + lunchBox: 0, + shopId: item.shopId, + seckillGroupProductId: item.id + } + const cart = [{ + cartId: item.id + '_' + JSON.stringify(specs || {}), + item: product, + specs: specs, + quantity: 1, + price: seckillPrice.toFixed(2) + }] + const shopItem = { + id: item.shopId, + shopId: item.shopId, + shopName: item.shopName, + contactPhone: item.shopPhone, + shopPhone: item.shopPhone, + shopAddress: item.shopAddress, + shopArea: item.getAreaId + } + uni.navigateTo({ + url: '/package1/buyFood/buyFood?cart=' + encodeURIComponent(JSON.stringify(cart)) + + '&shopItem=' + encodeURIComponent(JSON.stringify(shopItem)) + '&packageFee=0' }) }, back() { @@ -433,6 +651,32 @@ width: 22rpx; } + .sort-strip { + width: 95%; + margin: 0 auto 12rpx; + display: flex; + align-items: center; + } + + .sort-item { + height: 46rpx; + padding: 0 20rpx; + margin-right: 12rpx; + border-radius: 999rpx; + background: rgba(255, 255, 255, 0.72); + color: #8a6b55; + font-size: 22rpx; + font-weight: 800; + line-height: 46rpx; + box-shadow: 0 8rpx 18rpx rgba(203, 99, 37, 0.08); + } + + .sort-item.active { + background: #fff0e9; + color: #ff4b25; + box-shadow: 0 10rpx 20rpx rgba(255, 92, 38, 0.15); + } + .goods-list { width: 95%; margin: 0 auto; @@ -653,12 +897,126 @@ width: 112rpx; height: 52rpx; border-radius: 999rpx; - background: linear-gradient(135deg, #ff4b8a 0%, #ff7a55 100%); - color: #fff; + display: block; + } + + .spec-popup { + width: 100%; + max-height: 78vh; + padding: 28rpx 28rpx 40rpx; + border-radius: 36rpx 36rpx 0 0; + background: #fff; + box-sizing: border-box; + position: relative; + } + + .spec-close { + position: absolute; + top: 24rpx; + right: 24rpx; + z-index: 2; + } + + .spec-product { + display: flex; + padding-right: 60rpx; + } + + .spec-product-img { + width: 150rpx; + height: 150rpx; + border-radius: 22rpx; + background: #ffeacf; + flex-shrink: 0; + } + + .spec-product-info { + flex: 1; + min-width: 0; + padding-left: 20rpx; + } + + .spec-product-title { + min-height: 74rpx; + color: #2f2a25; + font-size: 30rpx; + font-weight: 900; + line-height: 38rpx; + } + + .spec-product-price { + margin-top: 18rpx; + color: #f4431f; + font-size: 42rpx; + font-weight: 900; + } + + .spec-price-symbol { + font-size: 26rpx; + } + + .spec-origin-price { + margin-left: 14rpx; + color: #aaa; font-size: 24rpx; + text-decoration: line-through; + } + + .spec-scroll { + max-height: 46vh; + margin-top: 28rpx; + } + + .spec-group { + margin-bottom: 24rpx; + } + + .spec-title { + color: #3c312a; + font-size: 28rpx; + font-weight: 900; + line-height: 64rpx; + } + + .spec-options { + display: flex; + flex-wrap: wrap; + } + + .spec-option { + min-width: 116rpx; + height: 66rpx; + padding: 0 24rpx; + margin: 0 18rpx 18rpx 0; + border-radius: 18rpx; + background: #f7f8f8; + color: #6f5b4d; + font-size: 26rpx; + font-weight: 700; + line-height: 66rpx; + text-align: center; + box-sizing: border-box; + } + + .spec-option.active { + background: #fff0e9; + color: #ff4b25; + border: 2rpx solid #ff6735; + line-height: 62rpx; + } + + .spec-submit { + width: 92%; + height: 88rpx; + margin: 20rpx auto 0; + border-radius: 999rpx; + background: linear-gradient(135deg, #ff6735 0%, #ff2f0e 100%); + color: #fff; + font-size: 30rpx; font-weight: 900; - line-height: 52rpx; + line-height: 88rpx; text-align: center; + box-shadow: 0 14rpx 28rpx rgba(255, 83, 34, 0.22); } @keyframes heroFloat { diff --git a/pages/index/index.vue b/pages/index/index.vue index c4c9aaa..51f8040 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -1803,6 +1803,7 @@ display: flex; align-items: center; position: relative; + top: 14rpx; overflow: hidden; animation: seckillEntryFloat 2.6s ease-in-out infinite; }