You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1947 lines
41 KiB

<template>
<view class="student-store-page">
<view class="hero">
<!-- <view class="hero-glow hero-glow-a"></view>
<view class="hero-glow hero-glow-b"></view>
<view class="hero-sticker sticker-near">附近正在拼</view> -->
<view class="top-bar">
<view class="back-btn" @tap="back" :style="{'padding-top': menuButtonInfo.top + 'px'}">
<uni-icons type="left" size="26" color="#fff"></uni-icons>
</view>
<view class="page-title"></view>
</view>
<view class="hero-community">
<view class="hero-left">
<view class="hand-tag">Campus walk</view>
<view class="hero-title">万能杂货铺
<view class="brand-chip" style="width: 340rpx;">半径里# 下课后去哪儿拼一单</view>
</view>
</view>
<view class="squirrel-card">
<image class="squirrel-img" src="/static/images/img/songshu.png" mode="aspectFit"></image>
<view class="squirrel-dialog">今天一起逛吗</view>
<view class="group-pop"></view>
</view>
</view>
</view>
<view class="hot-search-card">
<view class="hot-search-main" @tap="focusSearch">
<view class="hot-search-icon">🔎</view>
<view class="hot-search-copy">
<view class="hot-search-title">大家都在搜</view>
<swiper class="hot-word-swiper" vertical circular autoplay :interval="2300" :duration="420">
<swiper-item v-for="word in hotWords" :key="word">
<view class="hot-word">{{word}}</view>
</swiper-item>
</swiper>
</view>
<input class="hot-search-input" type="text" placeholder="输入你想拼的校园快乐" v-model="searchForm.keyWord"
confirm-type="search" @confirm="goSearch" />
<view class="trend-badge" @tap.stop="goSearch">搜索</view>
</view>
</view>
<view class="interest-scroll">
<view class="interest-list">
<view class="interest-card" :class="['interest-' + (index % 6), {'active': checkedTab === item.key}]"
v-for="(item,index) in tabList" :key="item.key" @tap="switchTab(item)">
<view class="interest-icon">{{item.icon}}</view>
<view class="interest-name">{{item.name}}</view>
<view class="interest-sub">{{item.sub}}</view>
<view class="interest-pulse"></view>
</view>
</view>
</view>
<view class="sort-strip">
<view class="sort-title">校园拼团雷达</view>
<view class="sort-item" :class="{'active': searchSale}" @tap="searchShop('sale','')">拼单最多</view>
<view class="sort-item" :class="{'active': searchScore}" @tap="searchShop('score','')">同学最爱</view>
</view>
<view class="shop-list">
<view class="empty-card" v-if="shopList.length === 0 && loadStatus !== 'loading'">
<view class="empty-icon">店</view>
<view class="empty-title">暂时没找到同款快乐</view>
<view class="empty-sub">换个关键词或分类试试,新的校园店铺会出现在这里</view>
</view>
<view class="shop-card community-shop-card" v-for="(item,index) in shopList" :key="index" @tap="goShop(item)">
<view @tap.stop="buyingye" v-if="item.status === 0" class="closed-mask">
<view class="closed-text">休息中</view>
</view>
<view class="rank-tag">热度 {{index + 1}}</view>
<view class="live-dot"></view>
<view class="shop-sticker">{{item.uiLiveText}}</view>
<view class="shop-top">
<view class="shop-img">
<image :src="item.shopIcon" mode="aspectFill"></image>
<view class="shop-img-badge">校</view>
</view>
<view class="shop-info">
<view class="shop-name">{{item.shopName}}</view>
<view class="shop-desc">{{item.subtitle || '校园附近宝藏小店,约上同学一起拼'}}</view>
<view class="social-row">
<view class="avatar-stack">
<view class="mini-avatar">同</view>
<view class="mini-avatar">舍</view>
<view class="mini-avatar">拼</view>
</view>
<view class="social-copy">同校{{item.uiBuyerCount}}人在买 · {{item.uiDormText}}</view>
</view>
<view class="tag-row">
<text>{{item.uiShopType}}</text>
<text>{{item.uiMissingText}}人就成团</text>
<text v-if="item.shopAreaTitle">{{item.shopAreaTitle}}</text>
</view>
</view>
</view>
<view class="group-status-row">
<view class="status-pill success">刚刚有人拼成功</view>
<view class="status-pill nearby">附近宿舍在拼</view>
<view class="heat-bar">
<view class="heat-progress" :style="item.uiHeatStyle"></view>
</view>
</view>
<view class="product-strip community-products" v-if="item.uiPreviewProducts.length > 0">
<view class="product-card" v-for="(product,productIndex) in item.uiPreviewProducts" :key="productIndex"
@tap.stop="goShop(item)">
<image :src="product.productPicture" mode="aspectFill"></image>
<view class="product-name">{{product.productName}}</view>
<view class="product-price">
<text class="group-label">拼团</text>
<text class="group-price">¥{{getGroupPrice(product)}}</text>
</view>
</view>
</view>
<view class="shop-bottom">
<view class="bottom-copy">进店看看同学们都在拼什么</view>
<view class="go-btn">找搭子</view>
</view>
</view>
<uni-load-more :status="loadStatus" v-if="shopList.length > 0" />
</view>
<view class="coupon-float">
<view class="coupon-icon">拼</view>
<view class="coupon-text">校园生活不止上课,快乐也可以拼着买</view>
<image class="coupon-use" src="/static/images/img/loading.gif" mode="aspectFit"></image>
</view>
</view>
</template>
<script>
export default {
data() {
return {
menuButtonInfo: {
top: 0
},
loadStatus: 'more',
isArea: false,
searchSale: false,
searchScore: false,
sortMode: 'all',
checkedTab: '',
areaTitle: '区域',
shopArea: [],
totalPages: 1,
searchForm: {
regionId: '',
merchantType: 2,
shopName: '',
shopType: '',
keyWord: '',
shopArea: '',
pageNumber: 1,
pageSize: '10',
sort: 'shoprank',
order: 'desc',
sortOrder: 'shoprank',
orderOrder: 'desc',
sortScore: '',
orderScore: '',
sortSale: '',
orderSale: ''
},
tabList: [{
name: '全部',
key: '',
value: '',
icon: '逛',
sub: '随便看看'
}, {
name: '学生小店',
key: 'student',
value: 5,
isStudentOnly: true,
icon: '舍',
sub: '宿舍宝藏'
}, {
name: '考学课程',
key: 'course',
value: 11,
icon: '课',
sub: '学分救星'
}, {
name: '美食',
key: 'food',
value: 1,
icon: '吃',
sub: '烤肉/火锅'
}, {
name: '休闲玩乐',
key: 'play',
value: 12,
icon: '玩',
sub: '网台K棋'
}, {
name: '美发美容',
key: 'beauty',
value: 13,
icon: '美',
sub: '清爽一下'
}, {
name: '甜点饮品',
key: 'drink',
value: 2,
icon: '咖',
sub: '快乐补给'
}, {
name: '酒店民宿',
key: 'hotel',
value: 14,
icon: '店',
sub: '小憩一下'
}, {
name: '电影演出',
key: 'movie',
value: 15,
icon: '演',
sub: '下课约影'
}, {
name: '其他',
key: 'other',
value: 16,
icon: '其',
sub: '更多美好'
}],
hotWords: ['🍢 宿舍夜宵拼团', '🎤 KTV四人局还差1人', '💅 周末美甲搭子', '🥩 烤肉套餐同校热拼'],
quickHotWords: ['夜宵', 'KTV', '美甲', '台球', '烤肉', '资料'],
sceneList: [{
icon: '课',
title: '学生小店',
sub: '资料/技能/二手'
}, {
icon: '玩',
title: 'KTV台球',
sub: '周末约搭子'
}, {
icon: '食',
title: '烤肉套餐',
sub: '拼桌更划算'
}],
shopList: []
}
},
filters: {
sliceMsg(val) {
let price = ''
if (typeof(val) == 'string' && val) {
try {
const priceMap = JSON.parse(val)
for (let key in priceMap) {
price = priceMap[key].specPrice
}
} catch (e) {
price = ''
}
}
return price
}
},
onLoad() {
this.initRegion()
},
onShow() {
this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
},
onReachBottom() {
if (this.searchForm.pageNumber >= this.totalPages || this.loadStatus === 'loading') return
this.searchForm.pageNumber += 1
this.getShopList()
},
methods: {
initRegion() {
const area = uni.getStorageSync('area')
if (!area) {
this.tui.toast('请先选择区域')
return
}
try {
const areaInfo = typeof area === 'string' ? JSON.parse(area) : area
this.searchForm.regionId = areaInfo.id || ''
} catch (e) {
this.searchForm.regionId = ''
}
if (!this.searchForm.regionId) {
this.tui.toast('请先选择区域')
return
}
this.getShopArea()
this.getShopList()
},
getShopArea() {
this.tui.request('/app/shopArea/getByParentId/' + this.searchForm.regionId, 'GET', {}, false, true).then((res) => {
if (res.code == 200) {
this.shopArea = Array.isArray(res.result) ? res.result : []
} else {
this.tui.toast(res.message)
}
}).catch(() => {})
},
getShopList() {
if (!this.searchForm.regionId) return
this.loadStatus = 'loading'
if (this.searchSale) {
this.searchForm.sortSale = 'saleCount'
this.searchForm.orderSale = 'desc'
this.searchForm.sortOrder = ''
} else {
this.searchForm.sortSale = ''
}
if (this.searchScore) {
this.searchForm.sortScore = 'shopScore'
this.searchForm.orderScore = 'desc'
this.searchForm.sortOrder = ''
} else {
this.searchForm.sortScore = ''
}
this.tui.request('/app/shop/getByCondition', 'GET', this.searchForm, false, true).then((res) => {
this.loadStatus = 'nomore'
if (res.code == 200) {
const result = res.result || {}
const list = Array.isArray(result.content) ? result.content.map((item, index) => this.formatShopItem(item, index)) : []
if (this.searchForm.pageNumber == 1) {
this.shopList = list
} else {
this.shopList = [...this.shopList, ...list]
}
this.totalPages = result.totalPages || 1
this.judgeBusinessStatus()
this.isArea = false
} else {
this.tui.toast(res.message)
}
uni.hideLoading()
}).catch(() => {
this.loadStatus = 'nomore'
})
},
judgeBusinessStatus() {
for (let i = 0; i < this.shopList.length; i++) {
const takeaway = this.shopList[i].shopTakeaway
if (takeaway && takeaway.businessHourBegin && takeaway.businessHourEnd && this.shopList[i].status == 1) {
this.shopList[i].status = this.isWithinBusinessHours(takeaway.businessHourBegin, takeaway.businessHourEnd) ? 1 : 0
}
}
},
isWithinBusinessHours(begin, end) {
const now = new Date()
const currentMinutes = now.getHours() * 60 + now.getMinutes()
const parseTime = (timeStr) => {
const parts = String(timeStr || '00:00').split(':').map(Number)
return (parts[0] || 0) * 60 + (parts[1] || 0)
}
return currentMinutes >= parseTime(begin) && currentMinutes <= parseTime(end)
},
switchTab(item) {
this.checkedTab = item.key
this.resetList()
this.searchForm.keyWord = ''
if (item.isStudentOnly) {
this.searchForm.shopType = ''
this.$set(this.searchForm, 'isStudent', 1)
} else {
this.searchForm.shopType = item.value || ''
this.$delete(this.searchForm, 'isStudent')
}
this.getShopList()
},
quickSearch(item) {
this.searchForm.keyWord = item.title
this.clearCategoryCondition()
this.resetList()
this.getShopList()
},
searchHotWord(word) {
this.searchForm.keyWord = word.replace(/[^\u4e00-\u9fa5A-Za-z0-9]/g, '')
this.clearCategoryCondition()
this.resetList()
this.getShopList()
},
focusSearch() {},
goSearch() {
this.clearCategoryCondition()
this.resetList()
this.getShopList()
},
clearCategoryCondition() {
this.checkedTab = ''
this.searchForm.shopType = ''
this.$delete(this.searchForm, 'isStudent')
},
searchShop(type, value) {
this.searchForm.pageNumber = 1
if (type == 'area') {
if (value) {
this.searchForm.shopArea = value.id
this.areaTitle = value.title
} else {
this.searchForm.shopArea = ''
this.areaTitle = '区域'
}
} else if (type == 'score') {
this.searchScore = !this.searchScore
this.searchSale = false
this.searchForm.sort = 'shopScore'
} else if (type == 'sale') {
this.searchSale = !this.searchSale
this.searchScore = false
this.searchForm.sort = 'saleCount'
} else if (type == 'all') {
this.sortMode = 'all'
this.searchForm.sortOrder = 'shoprank'
this.searchForm.orderOrder = 'desc'
this.searchForm.shopArea = ''
this.searchForm.shopType = ''
this.searchForm.keyWord = ''
this.$delete(this.searchForm, 'isStudent')
this.checkedTab = ''
this.searchForm.sort = 'shoprank'
this.areaTitle = '区域'
this.searchScore = false
this.searchSale = false
}
this.getShopList()
},
resetList() {
this.searchForm.pageNumber = 1
this.totalPages = 1
this.shopList = []
},
checkArea() {
this.isArea = !this.isArea
},
buyingye() {
uni.showToast({
title: '店铺暂不营业,晚点再来逛',
icon: 'none'
})
},
goShop(item) {
uni.navigateTo({
url: '/package2/group/groupBuySingle?type=shop&orderScene=storeGroup&item=' + encodeURIComponent(JSON.stringify(item))
})
},
getShopType(item) {
if (item.shopTypeTitle) return item.shopTypeTitle
if (item.shopTakeaway && item.shopTakeaway.type) return item.shopTakeaway.type
return '校园生活'
},
formatShopItem(item, index) {
const heatPercent = this.getHeatPercent(item, index)
return {
...item,
uiShopType: this.getShopType(item),
uiBuyerCount: this.getBuyerCount(item, index),
uiDormText: this.getDormText(index),
uiMissingText: this.getMissingCount(index),
uiLiveText: this.getLiveText(index),
uiHeatStyle: 'width: ' + heatPercent + '%;',
uiPreviewProducts: this.getPreviewProducts(item)
}
},
getBuyerCount(item, index) {
const count = Number(item.saleCount || 0)
return count > 0 ? Math.min(99, count + 8 + index * 3) : 18 + index * 5
},
getDormText(index) {
const dorms = ['3号楼正在拼', '东区宿舍刚下单', '图书馆旁热拼', '操场边有人约', '北门寝室在凑']
return dorms[index % dorms.length]
},
getMissingCount(index) {
return index % 3 === 0 ? '还差1' : (index % 3 === 1 ? '还差2' : '满3')
},
getLiveText(index) {
const texts = ['刚成团', '同校热拼', '搭子在等', '附近爆款']
return texts[index % texts.length]
},
getHeatPercent(item, index) {
const base = Number(item.saleCount || 0)
return Math.min(96, 48 + (base % 32) + index * 5)
},
hasGroupProducts(item) {
return this.getPreviewProducts(item).some(product => product.productGroupBuyPrices && product.productGroupBuyPrices.length > 0)
},
getPreviewProducts(item) {
const products = Array.isArray(item.products) ? item.products : []
return products.filter(product => product.delFlag == 1).slice(0, 3)
},
getGroupPrice(product) {
const prices = product.productGroupBuyPrices
if (!Array.isArray(prices) || prices.length === 0) {
return this.getOriginPrice(product) || '--'
}
let minPrice = Number(prices[0].groupPrice || 0)
for (let i = 1; i < prices.length; i++) {
const price = Number(prices[i].groupPrice || 0)
if (price > 0 && (minPrice === 0 || price < minPrice)) {
minPrice = price
}
}
return minPrice ? minPrice.toFixed(2) : '--'
},
getOriginPrice(product) {
if (typeof(product.attributeListPrice) !== 'string') return ''
try {
const priceMap = JSON.parse(product.attributeListPrice)
for (let key in priceMap) {
return priceMap[key].specPrice
}
} catch (e) {
return ''
}
return ''
},
back() {
uni.navigateBack()
}
}
}
</script>
<style lang="scss" scoped>
.student-store-page {
min-height: 100vh;
padding-bottom: 150rpx;
background:
radial-gradient(circle at 82% 120rpx, rgba(255, 241, 210, 0.86) 0, rgba(255, 241, 210, 0) 260rpx),
radial-gradient(circle at 10% 260rpx, rgba(255, 190, 185, 0.3) 0, rgba(255, 190, 185, 0) 240rpx),
linear-gradient(180deg, #ffd0b8 0%, #ffe6d6 260rpx, #fff7ef 560rpx, #fffaf6 100%);
box-sizing: border-box;
color: #3f2618;
}
.hero {
min-height: 520rpx;
padding: 0 24rpx 28rpx;
box-sizing: border-box;
color: #fff;
position: relative;
overflow: hidden;
background:
linear-gradient(115deg, rgba(255, 190, 156, 0.96) 0%, rgba(255, 211, 177, 0.9) 46%, rgba(255, 236, 218, 0.82) 100%);
}
.hero::after {
content: '';
width: 520rpx;
height: 520rpx;
border-radius: 50%;
background: rgba(255, 244, 218, 0.48);
position: absolute;
right: -250rpx;
top: -180rpx;
}
.hero-glow {
border-radius: 50%;
position: absolute;
filter: blur(2rpx);
}
.hero-glow-a {
width: 260rpx;
height: 260rpx;
left: -90rpx;
top: 130rpx;
background: rgba(255, 255, 255, 0.34);
}
.hero-glow-b {
width: 220rpx;
height: 220rpx;
right: 120rpx;
bottom: 20rpx;
background: rgba(255, 147, 126, 0.18);
}
.hero-sticker,
.hero-bubble-tip {
position: absolute;
z-index: 1;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.68);
border: 1rpx solid rgba(255, 255, 255, 0.82);
color: #9b4d28;
font-size: 20rpx;
font-weight: 900;
box-shadow: 0 12rpx 26rpx rgba(172, 93, 45, 0.1);
animation: stickerFloat 5.4s ease-in-out infinite;
}
.hero-sticker {
padding: 8rpx 18rpx;
transform: rotate(-6deg);
}
.sticker-hot {
left: 34rpx;
bottom: 128rpx;
}
.sticker-near {
right: 34rpx;
top: 172rpx;
transform: rotate(7deg);
animation-delay: 0.6s;
}
.hero-bubble-tip {
padding: 7rpx 16rpx;
background: rgba(255, 246, 232, 0.74);
color: #e96632;
}
.bubble-tip-a {
right: 190rpx;
bottom: 72rpx;
animation-delay: 1.1s;
}
.bubble-tip-b {
left: 220rpx;
top: 116rpx;
animation-delay: 1.6s;
}
.top-bar {
height: 82rpx;
display: flex;
align-items: center;
position: relative;
z-index: 2;
}
.back-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.page-title {
margin-left: 18rpx;
color: #fff;
font-size: 34rpx;
font-weight: 900;
letter-spacing: 2rpx;
text-shadow: 0 4rpx 12rpx rgba(154, 82, 42, 0.22);
}
.more-dot,
.brand-chip {
min-width: 88rpx;
height: 48rpx;
padding: 0 16rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.46);
color: #f57438;
font-size: 22rpx;
font-weight: 900;
line-height: 48rpx;
box-sizing: border-box;
}
.hero-community {
margin-top: 36rpx;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 2;
}
.hero-left {
flex: 1;
min-width: 0;
}
.hand-tag {
display: inline-block;
padding: 8rpx 20rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.58);
color: #9b4d28;
font-size: 22rpx;
font-weight: 900;
transform: rotate(-3deg);
box-shadow: 0 10rpx 22rpx rgba(154, 82, 42, 0.08);
}
.hero-social-line {
margin-top: 8rpx;
display: flex;
align-items: center;
color: rgba(108, 58, 32, 0.8);
font-size: 24rpx;
font-weight: 900;
line-height: 34rpx;
}
.line-dot {
width: 8rpx;
height: 8rpx;
margin: 0 12rpx;
border-radius: 50%;
background: #ff7444;
}
.topic-card {
width: 420rpx;
margin-top: 22rpx;
padding: 14rpx 18rpx;
border-radius: 24rpx;
background: rgba(255, 255, 255, 0.42);
border: 1rpx solid rgba(255, 255, 255, 0.62);
box-shadow: 0 14rpx 32rpx rgba(172, 93, 45, 0.1);
box-sizing: border-box;
backdrop-filter: blur(10rpx);
}
.topic-label {
color: #b56a46;
font-size: 20rpx;
font-weight: 900;
}
.topic-text {
margin-top: 6rpx;
color: #4f311f;
font-size: 26rpx;
font-weight: 900;
}
.squirrel-card {
width: 206rpx;
height: 230rpx;
margin-left: 14rpx;
border-radius: 46rpx;
background: linear-gradient(160deg, rgba(255, 255, 255, 0.72), rgba(255, 239, 211, 0.44));
border: 1rpx solid rgba(255, 255, 255, 0.72);
box-shadow: 0 24rpx 50rpx rgba(187, 93, 35, 0.18);
position: relative;
animation: heroFloat 4.2s ease-in-out infinite;
}
.squirrel-img {
width: 120rpx;
height: 120rpx;
position: absolute;
left: 12rpx;
bottom: 18rpx;
}
.squirrel-dialog {
width: 150rpx;
padding: 10rpx 12rpx;
border-radius: 22rpx;
background: #fff;
color: #9b4d28;
font-size: 20rpx;
font-weight: 900;
position: absolute;
left: 28rpx;
top: 28rpx;
box-shadow: 0 10rpx 24rpx rgba(154, 82, 42, 0.1);
}
.group-pop {
width: 70rpx;
height: 70rpx;
border-radius: 26rpx;
background: linear-gradient(145deg, #fff 0%, #ffe1aa 100%);
color: #f0642f;
font-size: 38rpx;
font-weight: 900;
line-height: 70rpx;
text-align: center;
position: absolute;
right: 18rpx;
bottom: 56rpx;
box-shadow: 0 14rpx 28rpx rgba(255, 126, 39, 0.16);
animation: softBeat 2.6s ease-in-out infinite;
}
.campus-meter {
margin-top: 22rpx;
display: flex;
position: relative;
z-index: 2;
gap: 12rpx;
}
.meter-item {
flex: 1;
min-width: 0;
padding: 14rpx 8rpx;
border-radius: 24rpx;
background: rgba(255, 255, 255, 0.5);
border: 1rpx solid rgba(255, 255, 255, 0.64);
text-align: center;
box-shadow: 0 12rpx 28rpx rgba(172, 93, 45, 0.08);
}
.meter-num {
color: #e96632;
font-size: 30rpx;
font-weight: 900;
line-height: 34rpx;
}
.meter-label {
margin-top: 4rpx;
color: #8f684c;
font-size: 19rpx;
font-weight: 800;
white-space: nowrap;
}
.hero-content {
margin-top: 42rpx;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 2;
}
.hero-copy {
flex: 1;
min-width: 0;
}
.hero-kicker {
display: inline-block;
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.58);
color: #9b4d28;
font-size: 22rpx;
font-weight: 900;
}
.hero-title {
margin-top: 18rpx;
font-size: 54rpx;
font-weight: 900;
line-height: 66rpx;
text-shadow: 0 10rpx 24rpx rgba(157, 74, 18, 0.28);
}
.hero-sub {
width: 440rpx;
margin-top: 14rpx;
color: rgba(108, 58, 32, 0.8);
font-size: 24rpx;
font-weight: 700;
line-height: 34rpx;
}
.hero-card {
width: 196rpx;
height: 210rpx;
padding-top: 18rpx;
border: 1rpx solid rgba(255, 255, 255, 0.72);
border-radius: 40rpx;
background: linear-gradient(160deg, rgba(255, 255, 255, 0.72), rgba(255, 239, 211, 0.42));
box-shadow: 0 22rpx 46rpx rgba(187, 93, 35, 0.2);
box-sizing: border-box;
text-align: center;
animation: heroFloat 3s ease-in-out infinite;
}
.hero-bubble {
width: 112rpx;
height: 112rpx;
margin: 0 auto;
border-radius: 38rpx;
background: linear-gradient(145deg, #fff 0%, #ffe1aa 100%);
color: #f0642f;
font-size: 54rpx;
font-weight: 900;
line-height: 112rpx;
box-shadow: 0 18rpx 30rpx rgba(255, 126, 39, 0.18);
}
.hero-card-title {
margin-top: 14rpx;
color: #8b4324;
font-size: 26rpx;
font-weight: 900;
}
.hero-card-sub {
margin-top: 4rpx;
color: rgba(116, 62, 36, 0.74);
font-size: 20rpx;
font-weight: 800;
}
.tab-scroll {
width: 100%;
margin-top: -30rpx;
white-space: nowrap;
position: relative;
z-index: 3;
}
.hot-search-card {
width: 94%;
margin: -160rpx auto 18rpx;
padding: 16rpx;
border-radius: 34rpx;
background: rgba(255, 255, 255, 0.58);
border: 1rpx solid rgba(255, 255, 255, 0.72);
box-shadow: 0 18rpx 42rpx rgba(178, 102, 48, 0.12);
box-sizing: border-box;
position: relative;
z-index: 5;
backdrop-filter: blur(14rpx);
}
.hot-search-main {
height: 76rpx;
padding: 0 16rpx;
border-radius: 26rpx;
background: rgba(255, 255, 255, 0.72);
display: flex;
align-items: center;
position: relative;
overflow: hidden;
box-shadow: inset 0 0 0 1rpx rgba(255, 205, 158, 0.32);
}
.hot-search-main::after {
content: '';
width: 130rpx;
height: 130rpx;
border-radius: 50%;
background: rgba(255, 225, 177, 0.35);
position: absolute;
right: -50rpx;
top: -48rpx;
}
.hot-search-icon {
width: 52rpx;
height: 52rpx;
border-radius: 18rpx;
background: #fff4e8;
line-height: 52rpx;
text-align: center;
font-size: 24rpx;
position: relative;
z-index: 1;
}
.hot-search-copy {
width: 290rpx;
padding-left: 14rpx;
position: relative;
z-index: 1;
}
.hot-search-title {
color: #b56a46;
font-size: 19rpx;
font-weight: 900;
line-height: 24rpx;
}
.hot-word-swiper {
height: 34rpx;
margin-top: 2rpx;
}
.hot-word {
color: #3f2618;
font-size: 24rpx;
font-weight: 900;
line-height: 34rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.hot-search-input {
flex: 1;
min-width: 0;
height: 76rpx;
color: #4f311f;
font-size: 22rpx;
position: relative;
z-index: 1;
}
.trend-badge {
width: 72rpx;
height: 42rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #ff7444 0%, #ffae53 100%);
color: #fff;
font-size: 21rpx;
font-weight: 900;
line-height: 42rpx;
text-align: center;
position: relative;
z-index: 1;
box-shadow: 0 10rpx 20rpx rgba(255, 126, 39, 0.22);
animation: softBeat 2.8s ease-in-out infinite;
}
.hot-keywords {
margin-top: 14rpx;
display: flex;
overflow: hidden;
}
.hot-keyword {
flex-shrink: 0;
margin-right: 12rpx;
padding: 8rpx 16rpx;
border-radius: 999rpx;
background: rgba(255, 244, 232, 0.86);
color: #8f684c;
font-size: 21rpx;
font-weight: 900;
box-shadow: inset 0 0 0 1rpx rgba(255, 205, 158, 0.28);
}
.interest-scroll {
width: 94%;
margin: 6rpx auto 18rpx;
}
.interest-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0 0 8rpx;
}
.interest-card {
width: 19%;
height: 118rpx;
margin: 0 0 14rpx;
padding: 12rpx 8rpx;
border-radius: 24rpx;
box-sizing: border-box;
position: relative;
overflow: hidden;
box-shadow: 0 16rpx 34rpx rgba(178, 102, 48, 0.1);
animation: categoryBreath 4.6s ease-in-out infinite;
transition: transform 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.35);
}
.interest-card:active {
transform: scale(0.92) rotate(0deg) !important;
animation: jellyTap 0.42s cubic-bezier(0.2, 0.9, 0.3, 1.35);
}
.interest-card.active {
box-shadow: 0 18rpx 38rpx rgba(255, 126, 39, 0.2);
transform: translateY(-6rpx) rotate(0deg) !important;
}
.interest-0 {
background: linear-gradient(145deg, #fff3d9 0%, #ffdba8 100%);
transform: rotate(-4deg);
}
.interest-1 {
background: linear-gradient(145deg, #ffe8df 0%, #ffc6b4 100%);
transform: rotate(3deg);
animation-delay: 0.2s;
}
.interest-2 {
background: linear-gradient(145deg, #fff9df 0%, #ffe99a 100%);
transform: rotate(-2deg);
animation-delay: 0.4s;
}
.interest-3 {
background: linear-gradient(145deg, #fff0ea 0%, #ffd0c7 100%);
transform: rotate(4deg);
animation-delay: 0.6s;
}
.interest-4 {
background: linear-gradient(145deg, #fff7e8 0%, #ffc98c 100%);
transform: rotate(-3deg);
animation-delay: 0.8s;
}
.interest-5 {
background: linear-gradient(145deg, #fff1da 0%, #ffe0b8 100%);
transform: rotate(2deg);
animation-delay: 1s;
}
.interest-icon {
width: 40rpx;
height: 40rpx;
border-radius: 15rpx;
background: rgba(255, 255, 255, 0.72);
color: #e96632;
font-size: 20rpx;
font-weight: 900;
line-height: 40rpx;
text-align: center;
box-shadow: 0 8rpx 18rpx rgba(178, 102, 48, 0.08);
}
.interest-name {
margin-top: 8rpx;
color: #3f2618;
font-size: 21rpx;
font-weight: 900;
line-height: 26rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.interest-sub {
margin-top: 2rpx;
color: #8f684c;
font-size: 16rpx;
font-weight: 800;
line-height: 21rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.interest-pulse {
width: 14rpx;
height: 14rpx;
border-radius: 50%;
background: #ff7444;
position: absolute;
right: 12rpx;
top: 12rpx;
box-shadow: 0 0 0 6rpx rgba(255, 116, 68, 0.12);
animation: heatPulse 2.6s ease-in-out infinite;
}
.tab-list {
display: inline-flex;
padding: 0 22rpx;
}
.tab-item {
height: 60rpx;
padding: 0 28rpx;
margin-right: 14rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.84);
color: #8f684c;
font-size: 25rpx;
font-weight: 900;
line-height: 60rpx;
box-shadow: 0 12rpx 24rpx rgba(197, 112, 47, 0.1);
}
.tab-item.active {
background: linear-gradient(135deg, #ff8d63 0%, #ffb35e 100%);
color: #fff;
box-shadow: 0 14rpx 28rpx rgba(255, 126, 39, 0.24);
}
.search-strip {
width: 94%;
height: 68rpx;
margin: 18rpx auto 10rpx;
display: flex;
align-items: center;
}
.hot-pill {
width: 150rpx;
height: 48rpx;
flex-shrink: 0;
border-radius: 999rpx;
background: #fff0dc;
color: #a84e22;
box-shadow: inset 0 0 0 1rpx rgba(255, 185, 115, 0.28);
font-size: 24rpx;
font-weight: 900;
line-height: 48rpx;
text-align: center;
}
.search-box {
flex: 1;
min-width: 0;
height: 54rpx;
margin-left: 14rpx;
padding: 0 8rpx 0 16rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.95);
border: 1rpx solid rgba(255, 180, 118, 0.42);
display: flex;
align-items: center;
box-shadow: 0 14rpx 30rpx rgba(197, 112, 47, 0.1);
box-sizing: border-box;
}
.search-box input {
flex: 1;
min-width: 0;
height: 54rpx;
padding-left: 8rpx;
color: #4f311f;
font-size: 23rpx;
}
.search-btn {
width: 76rpx;
height: 40rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #ff7444 0%, #ff9f38 100%);
color: #fff;
font-size: 22rpx;
font-weight: 900;
line-height: 40rpx;
text-align: center;
}
.quick-panel {
width: 94%;
margin: 0 auto 14rpx;
display: flex;
gap: 14rpx;
}
.quick-card {
flex: 1;
min-width: 0;
padding: 18rpx 12rpx;
border-radius: 26rpx;
background: rgba(255, 255, 255, 0.92);
border: 1rpx solid rgba(255, 202, 151, 0.36);
box-shadow: 0 14rpx 28rpx rgba(197, 112, 47, 0.1);
box-sizing: border-box;
}
.quick-card:active,
.shop-card:active,
.tab-item:active,
.sort-item:active {
transform: scale(0.97);
}
.quick-icon {
width: 50rpx;
height: 50rpx;
border-radius: 18rpx;
background: linear-gradient(135deg, #ff875a 0%, #ffba63 100%);
color: #fff;
font-size: 24rpx;
font-weight: 900;
line-height: 50rpx;
text-align: center;
}
.quick-title {
margin-top: 12rpx;
color: #3f2618;
font-size: 25rpx;
font-weight: 900;
}
.quick-sub {
margin-top: 4rpx;
color: #967052;
font-size: 20rpx;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sort-strip {
width: 94%;
margin: 0 auto 14rpx;
display: flex;
align-items: center;
flex-wrap: wrap;
position: relative;
z-index: 4;
}
.sort-title {
width: 100%;
margin: 0 0 12rpx 4rpx;
color: #7f4e31;
font-size: 26rpx;
font-weight: 900;
letter-spacing: 1rpx;
}
.sort-item {
height: 48rpx;
padding: 0 22rpx;
margin-right: 12rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.86);
color: #8f684c;
font-size: 22rpx;
font-weight: 900;
line-height: 48rpx;
box-shadow: 0 10rpx 20rpx rgba(197, 112, 47, 0.08);
position: relative;
}
.sort-item.active {
background: #ffead3;
color: #e96632;
box-shadow: 0 12rpx 24rpx rgba(255, 126, 39, 0.18);
border: 1rpx solid rgba(255, 150, 82, 0.34);
}
.area-sort {
max-width: 210rpx;
white-space: nowrap;
}
.area-panel {
width: 220rpx;
max-height: 360rpx;
padding: 10rpx 0;
border-radius: 24rpx;
background: rgba(126, 79, 55, 0.92);
position: absolute;
top: 62rpx;
right: 0;
overflow: scroll;
box-shadow: 0 18rpx 36rpx rgba(126, 79, 55, 0.18);
}
.area-item {
height: 58rpx;
padding: 0 18rpx;
color: #fff;
font-size: 23rpx;
font-weight: 800;
line-height: 58rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.shop-list {
width: 94%;
margin: 0 auto;
}
.empty-card {
min-height: 520rpx;
padding-top: 120rpx;
border: 1rpx solid rgba(255, 211, 153, 0.74);
border-radius: 34rpx;
background: rgba(255, 255, 255, 0.86);
box-shadow: 0 16rpx 34rpx rgba(197, 112, 47, 0.1);
text-align: center;
box-sizing: border-box;
}
.empty-icon {
width: 102rpx;
height: 102rpx;
margin: 0 auto 22rpx;
border-radius: 34rpx;
background: linear-gradient(135deg, #ff9a72 0%, #ffd38a 100%);
color: #fff;
font-size: 34rpx;
font-weight: 900;
line-height: 102rpx;
}
.empty-title {
color: #4a2f1d;
font-size: 30rpx;
font-weight: 900;
}
.empty-sub {
width: 78%;
margin: 14rpx auto 0;
color: #a7876b;
font-size: 24rpx;
line-height: 38rpx;
}
.shop-card {
margin-bottom: 20rpx;
padding: 18rpx 16rpx 16rpx;
border-radius: 32rpx;
background: rgba(255, 255, 255, 0.98);
border: 1rpx solid rgba(255, 221, 188, 0.72);
box-shadow: 0 16rpx 34rpx rgba(178, 102, 48, 0.12);
position: relative;
overflow: hidden;
box-sizing: border-box;
}
.community-shop-card {
padding: 20rpx 18rpx 18rpx;
border-radius: 36rpx;
}
.shop-card::after {
content: '';
width: 250rpx;
height: 250rpx;
border-radius: 50%;
background: rgba(255, 225, 177, 0.38);
position: absolute;
right: -140rpx;
top: -120rpx;
}
.closed-mask {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 8;
border-radius: 32rpx;
background: rgba(92, 48, 21, 0.16);
display: flex;
align-items: center;
justify-content: center;
}
.closed-text {
padding: 12rpx 30rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.92);
color: #ff8352;
font-size: 26rpx;
font-weight: 900;
}
.rank-tag {
position: absolute;
top: 18rpx;
right: 18rpx;
z-index: 2;
padding: 5rpx 12rpx;
border-radius: 999rpx;
background: #fff0dc;
color: #e96632;
font-size: 18rpx;
font-weight: 900;
}
.live-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: #ff7444;
position: absolute;
top: 26rpx;
left: 26rpx;
z-index: 2;
box-shadow: 0 0 0 8rpx rgba(255, 116, 68, 0.14);
}
.shop-sticker {
position: absolute;
right: 18rpx;
top: 58rpx;
z-index: 2;
padding: 6rpx 12rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.86);
color: #e96632;
font-size: 19rpx;
font-weight: 900;
box-shadow: 0 8rpx 18rpx rgba(178, 102, 48, 0.1);
transform: rotate(5deg);
}
.shop-top {
display: flex;
position: relative;
z-index: 1;
}
.shop-img {
width: 204rpx;
height: 204rpx;
flex-shrink: 0;
border-radius: 28rpx;
background: #fff1df;
position: relative;
overflow: hidden;
box-shadow: 0 12rpx 26rpx rgba(178, 102, 48, 0.12);
}
.shop-img image {
width: 100%;
height: 100%;
}
.shop-img-badge {
width: 42rpx;
height: 42rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.96);
color: #e96632;
font-size: 22rpx;
font-weight: 900;
line-height: 42rpx;
text-align: center;
position: absolute;
left: 10rpx;
top: 10rpx;
}
.shop-info {
flex: 1;
min-width: 0;
padding-left: 20rpx;
box-sizing: border-box;
}
.shop-name {
padding-right: 90rpx;
color: #2f2016;
font-size: 34rpx;
font-weight: 900;
line-height: 42rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.shop-desc {
margin-top: 8rpx;
color: #876247;
font-size: 23rpx;
line-height: 34rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rate-row {
height: 34rpx;
margin-top: 10rpx;
display: flex;
align-items: center;
}
.social-row {
margin-top: 12rpx;
display: flex;
align-items: center;
}
.avatar-stack {
width: 86rpx;
height: 34rpx;
position: relative;
flex-shrink: 0;
}
.mini-avatar {
width: 34rpx;
height: 34rpx;
border-radius: 50%;
background: linear-gradient(145deg, #fff 0%, #ffd9ad 100%);
border: 2rpx solid #fff;
color: #e96632;
font-size: 17rpx;
font-weight: 900;
line-height: 30rpx;
text-align: center;
position: absolute;
top: 0;
box-sizing: border-box;
}
.mini-avatar:nth-child(1) {
left: 0;
}
.mini-avatar:nth-child(2) {
left: 24rpx;
}
.mini-avatar:nth-child(3) {
left: 48rpx;
}
.social-copy {
flex: 1;
min-width: 0;
color: #8f715b;
font-size: 21rpx;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rate-score {
margin-right: 8rpx;
color: #f08324;
font-size: 25rpx;
font-weight: 900;
}
.sale-count {
margin-left: 10rpx;
color: #8f715b;
font-size: 21rpx;
font-weight: 800;
}
.tag-row {
min-height: 32rpx;
margin-top: 12rpx;
overflow: hidden;
}
.tag-row text {
display: inline-block;
margin: 0 8rpx 8rpx 0;
padding: 4rpx 10rpx;
border-radius: 9rpx;
background: #fff0dc;
color: #d65f2c;
font-size: 20rpx;
font-weight: 800;
line-height: 26rpx;
}
.tag-row text:nth-child(2) {
background: rgba(255, 215, 190, 0.82);
color: #9f4f30;
}
.group-status-row {
margin-top: 18rpx;
display: flex;
align-items: center;
position: relative;
z-index: 1;
}
.status-pill {
height: 40rpx;
margin-right: 10rpx;
padding: 0 12rpx;
border-radius: 999rpx;
font-size: 19rpx;
font-weight: 900;
line-height: 40rpx;
white-space: nowrap;
}
.status-pill.success {
background: #fff0dc;
color: #d65f2c;
}
.status-pill.nearby {
background: #fff7e2;
color: #9f6a2d;
}
.heat-bar {
flex: 1;
height: 14rpx;
border-radius: 999rpx;
background: rgba(255, 226, 198, 0.78);
overflow: hidden;
}
.heat-progress {
height: 100%;
border-radius: 999rpx;
background: linear-gradient(90deg, #ff7444 0%, #ffd16e 100%);
}
.product-strip {
margin-top: 18rpx;
display: flex;
gap: 12rpx;
position: relative;
z-index: 1;
overflow: hidden;
}
.product-card {
width: 31.6%;
flex: 0 0 31.6%;
min-width: 0;
padding: 10rpx;
border-radius: 22rpx;
background: #fff7ee;
border: 1rpx solid rgba(255, 226, 198, 0.82);
box-sizing: border-box;
box-shadow: 0 10rpx 22rpx rgba(178, 102, 48, 0.08);
}
.community-products .product-card:first-child {
transform: rotate(-1deg);
}
.community-products .product-card:nth-child(2) {
transform: rotate(1deg);
}
.community-products .product-card:nth-child(3) {
transform: rotate(-0.6deg);
}
.product-card image {
width: 100%;
height: 150rpx;
border-radius: 18rpx;
background: #fff1df;
}
.product-name {
margin-top: 8rpx;
color: #3f2618;
font-size: 21rpx;
font-weight: 800;
line-height: 30rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.product-price {
margin-top: 4rpx;
display: flex;
align-items: center;
}
.group-label {
margin-right: 6rpx;
padding: 2rpx 7rpx;
border-radius: 8rpx;
background: #fff0dc;
color: #b4572d;
font-size: 18rpx;
font-weight: 900;
}
.group-price {
color: #f0441f;
font-size: 23rpx;
font-weight: 900;
}
.shop-bottom {
height: 58rpx;
margin-top: 14rpx;
padding-top: 12rpx;
border-top: 1rpx solid rgba(255, 126, 39, 0.1);
display: flex;
align-items: center;
position: relative;
z-index: 1;
}
.bottom-copy {
flex: 1;
color: #8f715b;
font-size: 22rpx;
font-weight: 800;
}
.go-btn {
width: 132rpx;
height: 50rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #ff7444 0%, #ff9f38 100%);
color: #fff;
font-size: 24rpx;
font-weight: 900;
line-height: 50rpx;
text-align: center;
box-shadow: 0 12rpx 24rpx rgba(255, 126, 39, 0.26);
}
.coupon-float {
width: 92%;
height: 76rpx;
padding: 0 12rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.95);
display: flex;
align-items: center;
position: fixed;
left: 4%;
bottom: 32rpx;
z-index: 10;
border: 1rpx solid rgba(255, 202, 151, 0.46);
box-shadow: 0 14rpx 34rpx rgba(197, 112, 47, 0.18);
box-sizing: border-box;
}
.coupon-icon {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
background: #ff7444;
color: #fff;
font-size: 22rpx;
font-weight: 900;
line-height: 44rpx;
text-align: center;
}
.coupon-text {
flex: 1;
padding-left: 12rpx;
color: #4f311f;
font-size: 24rpx;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.coupon-use {
width: 112rpx;
height: 52rpx;
border-radius: 999rpx;
display: block;
}
@keyframes heroFloat {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-12rpx);
}
}
@keyframes stickerFloat {
0%,
100% {
transform: translateY(0) rotate(-5deg);
}
50% {
transform: translateY(-10rpx) rotate(3deg);
}
}
@keyframes softBeat {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.045);
}
}
@keyframes categoryBreath {
0%,
100% {
box-shadow: 0 16rpx 34rpx rgba(178, 102, 48, 0.1);
}
50% {
box-shadow: 0 22rpx 42rpx rgba(178, 102, 48, 0.16);
}
}
@keyframes jellyTap {
0% {
transform: scale(0.92);
}
55% {
transform: scale(1.04);
}
100% {
transform: scale(1);
}
}
@keyframes heatPulse {
0%,
100% {
opacity: 0.9;
box-shadow: 0 0 0 7rpx rgba(255, 116, 68, 0.12);
}
50% {
opacity: 1;
box-shadow: 0 0 0 13rpx rgba(255, 116, 68, 0.04);
}
}
@keyframes cardBreath {
0%,
100% {
transform: translateY(0);
box-shadow: 0 16rpx 34rpx rgba(178, 102, 48, 0.12);
}
50% {
transform: translateY(-3rpx);
box-shadow: 0 22rpx 44rpx rgba(178, 102, 48, 0.16);
}
}
@keyframes heatMove {
0%,
100% {
opacity: 0.86;
}
50% {
opacity: 1;
}
}
</style>