roamfuding-xcx/page_user/meishi/index.vue
2026-03-16 10:00:58 +08:00

940 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<u-navbar title="舌尖美食" :border-bottom="false" :background="bgc" back-icon-color="#262B37" title-color='#262B37'
title-size='36' height='36' id="navbar" :custom-back="btnfh">
</u-navbar>
<!-- 美食榜头部 -->
<view class="ranking-header">
<view :class="tabindex == 0 ? 'active' : ''" @click="btntab(0)">
<image src="https://api.ccttiot.com/smartmeter/img/static/ueH28fQ7rRVcvaMRuFOJ" mode="aspectFill"></image>
<text class="ranking-title">美食榜</text>
</view>
<view :class="tabindex == 1 ? 'active' : ''" @click="btntab(1)">
<image src="https://api.ccttiot.com/smartmeter/img/static/ueH28fQ7rRVcvaMRuFOJ" mode="aspectFill"></image>
<text class="ranking-title">招牌老店</text>
</view>
</view>
<!-- 筛选功能容器 -->
<view class="filter-container">
<!-- 顶部展开按钮 -->
<view class="filter-header" @tap="toggleFilterContent">
<view class="filter-header-left">
<image class="filter-icon" src="https://api.ccttiot.com/smartmeter/img/static/ubHCOkv3QZvpx5xT0GFz" mode="aspectFit"></image>
<text class="filter-header-text">筛选更多美食</text>
</view>
<view class="filter-header-right">
<text class="filter-toggle-text">{{ showFilterContent ? '收起' : '展开' }}</text>
<text class="filter-arrow" :class="{rotate: showFilterContent}"></text>
</view>
</view>
<!-- 筛选内容区域 -->
<view class="filter-content" v-show="showFilterContent">
<!-- 地区筛选 -->
<view class="area-panel-wrapper">
<view class="category-title">
<view class="category-icon"> <image style="width: 32rpx;height: 32rpx;" src="https://api.ccttiot.com/smartmeter/img/static/ugzxntqNzb3RbE9EO8O0" mode=""></image> </view>
<text class="category-text">地域名称</text>
</view>
<view class="area-grid-wrapper">
<view class="grid-item"
:class="{active: selectedArea===item.dictLabel}"
v-for="(item, index) in displayedAreas"
:key="index"
@tap="selectArea(item)">
<text>{{ item.dictLabel }}</text>
</view>
<view class="grid-item more-item"
v-if="hasMoreAreas"
@tap="toggleShowAllAreas">
<text>{{ showAllAreas ? '收起' : '更多...' }}</text>
</view>
</view>
</view>
<!-- 荣誉等级筛选 -->
<view class="filter-panel-wrapper" v-if="tabindex == 0">
<view class="category-title">
<view class="category-icon"> <image style="width: 32rpx;height: 40rpx;" src="https://api.ccttiot.com/smartmeter/img/static/usJNxk7wkgdWQNIi3tgB" mode=""></image> </view>
<text class="category-text">荣誉等级</text>
</view>
<view class="filter-grid">
<view class="grid-item"
:class="{active: isFilterActive(item)}"
v-for="(item, index) in allFilterOptions"
:key="index"
@tap="selectFilterFromPanel(item)">
<text>{{ item.name || item.dictLabel}}</text>
</view>
</view>
</view>
<!-- 底部收起按钮 -->
<view class="filter-footer" @tap="toggleFilterContent">
<!-- <text class="filter-footer-text">收起</text>
<text class="filter-arrow-up">▲</text> -->
</view>
</view>
</view>
<!-- 美食列表 -->
<scroll-view @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black" v-if="tabindex == 0" class="food-list">
<view class="food-card" v-for="(food, index) in foodList" :key="index" @click="btnone(food)">
<view class="food-image">
<image :src="food.imageUrl" mode="aspectFill" class="image"></image>
</view>
<view class="food-content">
<view class="food-title-row">
<text class="food-title">{{ food.name }}</text>
<view class="food-tags" v-if="getTypeNamesList(food.typeNames).length">
<text class="food-tag" v-for="(tag, tagIndex) in getTypeNamesList(food.typeNames)" :key="tagIndex">{{ tag }}</text>
</view>
</view>
<text class="food-description">{{ getDescription(food.description) }}</text>
</view>
</view>
<view class="" style="width: 100%;margin-top: 20rpx;color: #ccc;text-align: center;">
暂时没有更多内容了...
</view>
</scroll-view>
<!-- 招牌老店 -->
<scroll-view @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black" v-if="tabindex == 1" class="food-list">
<view class="food-card" v-for="(store, index) in storeList" :key="index" @click="btndp(store)">
<view class="food-image" v-if="getStoreImages(store.picture).length > 0">
<swiper class="swiper" :indicator-dots="getStoreImages(store.picture).length > 1" :autoplay="true" :interval="3000" :duration="500" indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#fff">
<swiper-item v-for="(img, imgIndex) in getStoreImages(store.picture)" :key="imgIndex">
<image :src="img" mode="aspectFill" class="image"></image>
</swiper-item>
</swiper>
</view>
<view class="food-content" style="padding: 20rpx;padding-left: 20rpx;">
<view class="food-title-row">
<text class="food-title">{{ store.name }}</text>
<view class="food-tags" v-if="getTagsList(store.tags).length">
<text class="food-tag" v-for="(tag, tagIndex) in getTagsList(store.tags)" :key="tagIndex">{{ tag }}</text>
</view>
</view>
</view>
</view>
<view class="" style="width: 100%;margin-top: 20rpx;color: #ccc;text-align: center;">
暂时没有更多内容了...
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
backgroundColor: "#fff",
},
tabindex:0,
foodList: [],
storeList: [],
isRefreshing:false,
pageNum:1,
total:0,
// 位置信息
lat: '',
lng: '',
// 筛选标签
filterTabs: [
{ name: '全部', id: '' },
],
selectedFilterTab: '',
showFilterPanel: false,
// 所有筛选选项(包括扩展选项)
allFilterOptions: [
{ name: '全部', id: '' },
],
// 下拉筛选
showLocation: false,
showArea: false,
selectedLocation: '附近',
selectedArea: '全市区',
selectedAreaid:'',
locationOptions: [
{ name: '300m', value: '300' },
{ name: '500m', value: '500' },
{ name: '1km', value: '1000' },
{ name: '3km', value: '3000' },
{ name: '5km', value: '5000' },
{ name: '全市', value: ''}
],
areaOptions: [{ dictLabel: '全市区', value: '' }],
mstotal:0,
showAllAreas: false, // 控制是否展开全部地区
showFilterContent: false ,// 控制筛选内容展开/收起
fanhui:''
}
},
computed: {
// 计算要显示的地区列表默认7个+更多按钮=8个占两排展开后全部
displayedAreas() {
if (this.showAllAreas) {
return this.areaOptions
}
// 默认显示前7个两排每排4个第二排最后一个位置留给"更多..."按钮)
return this.areaOptions.slice(0, 7)
},
// 判断是否有更多地区可以展开
hasMoreAreas() {
return this.areaOptions.length > 7
}
},
onLoad(option) {
this.getlist()
// this.getbiaoqian()
this.getcity()
this.getLocation()
this.getmslb()
if(option.fanhui){
this.fanhui = option.fanhui
}
},
// 分享到好友(会话)
onShareAppMessage: function() {
return {
title: '玩转福鼎',
path: '/page_user/meishi/index?fanhui=1'
}
},
// 分享到朋友圈
onShareTimeline: function() {
return {
title: '玩转福鼎',
query: '',
path: '/page_user/meishi/index?fanhui=1'
}
},
methods: {
btnfh(){
console.log(this.fanhui,'000');
if(this.fanhui == 1){
uni.reLaunch({
url:'/pages/index/index'
})
}else{
uni.navigateBack()
}
},
// 解析 typeNames 为标签数组(支持字符串逗号分隔或数组)
getTypeNamesList(typeNames) {
if (!typeNames) return [];
if (Array.isArray(typeNames)) return typeNames.filter(Boolean);
if (typeof typeNames === 'string') {
return typeNames.split(/[,,、]/).map(s => s.trim()).filter(Boolean);
}
return [];
},
// 解析 tags 为标签数组(支持字符串逗号分隔或数组)
getTagsList(tags) {
if (!tags) return [];
if (Array.isArray(tags)) return tags.filter(Boolean);
if (typeof tags === 'string') {
return tags.split(/[,,、]/).map(s => s.trim()).filter(Boolean);
}
return [];
},
// 去除HTML标签并截取前50个字符
getDescription(description) {
if (!description) return '';
// 去除HTML标签
let text = description.replace(/<[^>]*>/g, '');
// 去除HTML实体如&nbsp;等)
text = text.replace(/&nbsp;/g, ' ').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#39;/g, "'");
// 去除多余空格并截取
text = text.trim();
if (text.length > 50) {
return text.slice(0, 50) + '...';
}
return text;
},
// 处理店铺图片数组
getStoreImages(picture) {
if (!picture || picture.trim() === '') {
return [];
}
return picture.split(',').filter(img => img && img.trim() !== '');
},
// 点击店铺导航
btndp(item){
uni.navigateTo({
url:'/page_user/techan/techandp?id=' + item.id + '&type=1'
})
// uni.openLocation({
// latitude: parseFloat(item.latitude), // 确保是数字类型
// longitude: parseFloat(item.longitude), // 确保是数字类型
// name: item.name || '目的地', // 地点名称
// success: function(res) {
// console.log('打开地图成功', res);
// },
// fail: function(err) {
// console.error('打开地图失败', err);
// uni.showToast({
// title: '打开地图失败: ' + (err.errMsg || '未知错误'),
// icon: 'none',
// duration: 3000
// })
// }
// })
},
// 请求美食榜列表
getlist(){
this.$u.get(`/app/dish/list?pageNum=${this.pageNum}&pageSize=20&type=${this.selectedFilterTab}&location=${this.selectedAreaid}`).then((res) => {
if(res.code == 200){
this.mstotal = res.total
if(this.pageNum == 1){
this.pageNum++
this.foodList = res.rows
}else{
this.pageNum++
this.foodList = this.foodList.concat(res.rows)
}
}
})
},
// 请求招牌老店列表
getshop(){
// 构建筛选参数
let params = `pageNum=${this.pageNum}&pageSize=20`
// 添加分类筛选
if(this.selectedFilterTab){
params += `&typeIds=${this.selectedFilterTab}`
}
// 添加地区筛选
if(this.selectedAreaid && this.selectedAreaid !== ''){
params += `&location=${this.selectedAreaid}`
}
// 添加距离筛选
if(this.selectedLocation !== '附近' && this.lat && this.lng){
const locationItem = this.locationOptions.find(item => item.name === this.selectedLocation)
if(locationItem && locationItem.value !== ''){
params += `&distance=${locationItem.value}&currLat=${this.lat}&currLon=${this.lng}`
}
} else if(this.lat && this.lng){
// 默认使用当前位置
params += `&currlat=${this.lat}&currLon=${this.lng}`
}
this.$u.get(`/app/store/list?${params}`).then((res) => {
if(res.code == 200){
this.total = res.total
if(this.pageNum == 1){
this.pageNum++
this.storeList = res.rows
}else{
this.pageNum++
this.storeList = this.storeList.concat(res.rows)
}
}
})
},
// 点击切换tab
btntab(num){
this.tabindex = num
this.pageNum = 1
this.selectedFilterTab = ''
this.selectedAreaid = ''
this.selectedArea = '全市区'
this.selectedLocation = '附近'
this.closeAllDropdowns()
if(num == 0){
this.allFilterOptions = [
{ name: '全部', id: '' },
]
this.filterTabs = [
{ name: '全部', id: '' },
]
this.getlist()
this.getmslb()
}else{
this.allFilterOptions = [
{ name: '全部', id: '' },
]
this.filterTabs = [
{ name: '全部', id: '' },
]
this.getshop()
this.getbiaoqian()
}
},
// 上拉加载更多
handqixing() {
console.log('加载更多')
if(this.tabindex == 0){
if(this.mstotal > this.foodList.length){
this.getlist()
}
}else{
if(this.total > this.storeList.length){
this.getshop()
}
}
},
// 下拉刷新
onRefresh() {
this.isRefreshing = true //刷新动画
setTimeout(() => {
this.isRefreshing = false
this.pageNum = 1
if(this.tabindex == 0){ //判断请求的是推荐商家还是美食
this.getlist()
}else{
this.getshop()
}
}, 1000)
},
// 点击美食榜跳转详情
btnone(food){
console.log(1);
uni.navigateTo({
url:'/page_user/meishi/meishixq?id=' + food.id + '&name=' + food.name
})
},
// 查询店铺分类标签
getbiaoqian(){
this.$u.get(`/app/storeType/list`).then((res) => {
if(res.code == 200){
res.data.forEach(item =>{
this.allFilterOptions.push(item)
this.filterTabs.push(item)
})
}
})
},
// 获取美食标签列表
getmslb(){
this.$u.get(`/system/dict/data/type/dish_type`).then((res) => {
if(res.code == 200){
res.data.forEach(item =>{
this.allFilterOptions.push(item)
this.filterTabs.push(item)
})
}
})
},
// 获取全市信息
getcity(){
this.$u.get(`/system/dict/data/type/store_location`).then((res) => {
if(res.code == 200){
res.data.forEach(item =>{
this.areaOptions.push(item)
})
}
})
},
// 获取位置信息
getLocation() {
uni.getLocation({
type: 'gcj02',
success: (res) => {
this.lat = res.latitude
this.lng = res.longitude
},
fail: (err) => {
console.log('获取位置失败', err)
}
})
},
// 判断筛选项是否激活(高亮)
isFilterActive(item) {
const itemId = item.id || item.dictValue
const selectedId = this.selectedFilterTab
// 如果两者都是空字符串或undefined则认为是"全部",应该高亮
if ((!selectedId || selectedId === '') && (!itemId || itemId === '')) {
return true
}
// 如果两者都有值且相等,则高亮
if (selectedId && itemId && selectedId === itemId) {
return true
}
return false
},
// 选择筛选标签
selectFilterTab(item) {
// 确保"全部"选项没有id和dictValue时设置为空字符串
this.selectedFilterTab = item.id || item.dictValue || ''
this.closeAllDropdowns()
this.pageNum = 1
if(this.tabindex == 0){
this.getlist()
}else{
this.getshop()
}
},
// 显示位置下拉菜单
showLocationPicker() {
this.showLocation = !this.showLocation
this.showArea = false
this.showFilterPanel = false
},
// 显示区域下拉菜单
showAreaPicker() {
this.showArea = !this.showArea
this.showLocation = false
this.showFilterPanel = false
// 如果关闭下拉菜单,重置展开状态
if (!this.showArea) {
this.showAllAreas = false
}
},
// 切换筛选面板
toggleFilterPanel() {
this.showFilterPanel = !this.showFilterPanel
this.showLocation = false
this.showArea = false
},
// 从筛选面板选择筛选项
selectFilterFromPanel(item) {
console.log(item);
// 确保"全部"选项没有id和dictValue时设置为空字符串
this.selectedFilterTab = item.id || item.dictValue || ''
this.pageNum = 1
if(this.tabindex == 0){
this.getlist()
}else{
this.getshop()
}
},
// 选择位置
selectLocation(item) {
console.log(item);
this.selectedLocation = item.name || item.dictLabel
this.showLocation = false
this.pageNum = 1
if(this.tabindex == 0){
this.getlist()
}else{
this.getshop()
}
},
// 选择区域
selectArea(item) {
console.log(item);
this.selectedArea = item.dictLabel || item.name
if(item.dictValue){
this.selectedAreaid = item.dictValue
} else {
this.selectedAreaid = item.value || ''
}
// this.showAllAreas = false
this.pageNum = 1
if(this.tabindex == 0){
this.getlist()
}else{
this.getshop()
}
},
// 关闭所有下拉菜单
closeAllDropdowns() {
this.showLocation = false
this.showArea = false
this.showFilterPanel = false
this.showAllAreas = false
},
// 切换显示全部地区
toggleShowAllAreas() {
this.showAllAreas = !this.showAllAreas
},
// 切换筛选内容展开/收起
toggleFilterContent() {
this.showFilterContent = !this.showFilterContent
}
}
}
</script>
<style lang="scss">
.active{
font-weight: bold !important;
.ranking-title{
color: #262B37 !important;
}
}
.page {
height: 100vh;
overflow: scroll;
// padding-bottom: 500rpx;
}
// 美食榜头部样式
.ranking-header {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background: #fff;
view{
display: flex;
align-items: center;
margin-right: 50rpx;
}
image{
width: 48rpx;
height: 48rpx;
margin-right: 10rpx;
}
.star-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.ranking-title {
font-size: 36rpx;
color: #ccc;
}
}
// 美食列表样式
.food-list {
width: 750rpx;
height: 80vh;
margin-top: 20rpx;
padding-bottom: 30rpx;
box-sizing: border-box;
}
// 美食卡片样式
.food-card {
margin: auto;
background: #fff;
border-radius: 14rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
width: 680rpx;
.food-image {
width: 100%;
height: 298rpx;
border-radius: 14rpx 14rpx 0 0;
overflow: hidden;
.swiper {
width: 100%;
height: 100%;
}
.image {
width: 100%;
height: 100%;
}
}
.food-content {
padding: 30rpx;
.food-title-row {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-bottom: 16rpx;
}
.food-title {
display: inline-block;
font-size: 36rpx;
font-weight: bold;
color: #262B37;
line-height: 1.4;
margin-right: 16rpx;
}
.food-tags {
display: inline-flex;
flex-wrap: wrap;
align-items: center;
}
.food-tag {
font-size: 22rpx;
color: #F15A24;
background-color: #FFF4EE;
border-radius: 20rpx;
padding: 4rpx 12rpx;
margin-right: 8rpx;
margin-bottom: 4rpx;
line-height: 1.4;
}
.food-description {
display: block;
font-size: 28rpx;
color: #666;
line-height: 1.6;
text-align: justify;
}
}
}
// 最后一个卡片底部间距
.food-card:last-child {
margin-bottom: 0;
}
// 招牌老店卡片样式
.store-card {
margin: auto;
border-radius: 14rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
height: 360rpx;
position: relative;
overflow: hidden;
width: 680rpx;
// 卡片标题
.card-title {
padding: 30rpx 30rpx 20rpx;
background: rgba(0,0,0,.5);
.store-title {
font-size: 32rpx;
font-weight: bold;
color: #fff;
line-height: 1.4;
}
}
.store-image {
width: 100%;
height: 360rpx;
background: rgba(0,0,0,.5);
.image {
width: 100%;
height: 360rpx;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
.image-overlay {
padding: 0 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 128rpx;
.rating-section {
display: flex;
align-items: center;
flex: 1;
.stars {
display: flex;
margin-right: 16rpx;
.star {
color: #E7322C;
font-size: 28rpx;
margin-right: 4rpx;
}
}
.rating-score {
color: #E7322C;
font-size: 56rpx;
font-weight: bold;
margin-right: 16rpx;
}
.rating-text {
color: #E7322C;
font-size: 28rpx;
margin-right: 16rpx;
}
.review-count {
color: #fff;
font-size: 28rpx;
}
}
.price-section {
.price {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
}
}
}
.store-content {
padding: 0 30rpx 30rpx;
.store-info {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-between;
.store-category {
font-size: 24rpx;
color: #fff;
margin-right: 20rpx;
margin-bottom: 8rpx;
}
.store-location {
font-size: 24rpx;
color: #fff;
margin-right: 20rpx;
margin-bottom: 8rpx;
}
.store-distance {
font-size: 24rpx;
color: #fff;
margin-bottom: 8rpx;
}
}
}
}
// 最后一个招牌老店卡片底部间距
.store-card:last-child {
margin-bottom: 0;
}
// 筛选容器样式
.filter-container {
background: #fff;
margin-bottom: 20rpx;
// 顶部展开按钮
.filter-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx;
background: #fff;
border-bottom: 1rpx solid #F5F5F5;
.filter-header-left {
display: flex;
align-items: center;
.filter-icon {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
}
.filter-header-text {
font-size: 32rpx;
color: #262B37;
font-weight: 500;
}
}
.filter-header-right {
display: flex;
align-items: center;
.filter-toggle-text {
font-size: 28rpx;
color: #666;
margin-right: 8rpx;
}
.filter-arrow {
font-size: 24rpx;
color: #999;
transition: transform 0.3s;
&.rotate {
transform: rotate(180deg);
}
}
}
}
// 筛选内容区域
.filter-content {
background: #fff;
// 底部收起按钮
.filter-footer {
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx 30rpx;
padding-top: 10rpx;
.filter-footer-text {
font-size: 28rpx;
color: #666;
margin-right: 8rpx;
}
.filter-arrow-up {
font-size: 24rpx;
color: #999;
}
}
}
}
// 筛选标签样式 - 占位显示
.filter-panel-wrapper {
background: #fff;
padding: 0 30rpx 30rpx;
padding-bottom: 0;
margin-top: 20rpx;
.category-title {
display: flex;
// align-items: center;
.category-icon {
font-size: 32rpx;
margin-right: 10rpx;
}
.category-text {
font-size: 32rpx;
font-weight: 600;
color: #262B37;
}
}
.filter-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
margin-top: 16rpx;
.grid-item {
padding: 12rpx 24rpx;
border-radius: 40rpx;
font-size: 26rpx;
background: #F5F5F5;
color: #666;
border: none;
transition: all 0.3s;
white-space: nowrap;
text-align: center;
text {
display: block;
}
&.active {
background: #4CAF50;
color: #fff;
font-weight: bold;
}
}
}
}
// 地区筛选样式 - 占位显示
.area-panel-wrapper {
background: #fff;
padding: 20rpx 30rpx 0;
.category-title {
display: flex;
align-items: center;
margin-bottom: 20rpx;
// padding-top: 20rpx;
.category-icon {
font-size: 32rpx;
margin-right: 10rpx;
}
.category-text {
font-size: 32rpx;
font-weight: 600;
color: #262B37;
}
}
.area-grid-wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
.grid-item {
padding: 12rpx 24rpx;
border-radius: 40rpx;
font-size: 26rpx;
background: #F5F5F5;
color: #666;
border: none;
transition: all 0.3s;
white-space: nowrap;
text-align: center;
text {
display: block;
}
&.active {
background: #4CAF50;
color: #fff;
font-weight: bold;
}
&.more-item {
background: #F5F5F5;
color: #666;
cursor: pointer;
&:active {
background: #E0E0E0;
}
}
}
}
}
</style>