roamfuding-xcx/page_fenbao/gonglue/index.vue

828 lines
22 KiB
Vue
Raw Permalink Normal View History

2025-11-08 11:21:57 +08:00
<template>
<view class="page">
<u-navbar title="推荐攻略" :border-bottom="false" :background="bgc" back-icon-color="#262B37" title-color='#262B37'
2026-03-16 10:00:58 +08:00
title-size='36' height='36' id="navbar" :custom-back="btnfh">
2025-11-08 11:21:57 +08:00
</u-navbar>
2026-01-15 14:44:11 +08:00
2025-11-08 11:21:57 +08:00
<scroll-view class="attractions-list" @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black">
2026-01-15 14:44:11 +08:00
<view class="swiper-wrapper">
<swiper
class="custom-swiper"
:indicator-dots="false"
:autoplay="true"
:interval="10000"
2026-03-16 10:00:58 +08:00
:duration="500"
2026-01-15 14:44:11 +08:00
previous-margin="40rpx"
next-margin="40rpx"
2026-03-16 10:00:58 +08:00
:circular="false"
2026-01-15 14:44:11 +08:00
@change="onSwiperChange"
@tap="onSwiperClick"
>
<swiper-item
v-for="(item, index) in list"
:key="index"
:class="['swiper-item', { 'active': index === currentSwiperIndex }]"
>
<view class="swiper-card">
<!-- 图片 -->
<image
class="swiper-image"
:src="item.carousel"
mode="aspectFill"
></image>
<!-- 渐变遮罩 -->
<view class="gradient-overlay"></view>
<!-- 标题和景点列表 -->
<view class="swiper-content">
<!-- 主标题 -->
<text class="title-text">{{ item.name }}</text>
<!-- 景点时间轴 -->
<view class="spots-timeline" v-if="item.spotNameList && item.spotNameList.length > 0">
<scroll-view class="timeline-scroll" scroll-x :show-scrollbar="false">
<view class="timeline-container">
<!-- 景点标记点 -->
<view class="spot-markers">
<view class="spot-item" v-for="(spot, spotIndex) in item.spotNameList" :key="spotIndex">
<view class="spot-dot"></view>
<text class="spot-name">{{ spot }}</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- 搜索栏 -->
<view class="search-container">
<view class="search-bar">
<image class="search-icon" src="https://api.ccttiot.com/smartmeter/img/static/uAO3fdmrrHTzqbVx9hYd" mode=""></image>
<input class="search-input" placeholder="搜索景区、场馆" v-model="searchKeyword" />
</view>
<view class="search-btn" @tap="handleSearch">
<text>搜索</text>
</view>
</view>
<!-- 筛选器芯片高亮模式 -->
<view class="filter-chip-container">
<!-- 主题玩法 -->
<view class="chip-row" style="padding-top: 10rpx;">
<!-- <view class="chip-label">主题</view> -->
<scroll-view class="chip-scroll" scroll-x :show-scrollbar="false">
<view class="chip-list">
<view class="chip" :class="{active: selectedThemeid === ''}" @tap="selectTheme({id:'', name:'全部'})">全部</view>
<view class="chip" v-for="(item, index) in themeOptions" :key="index" :class="{active: selectedThemeid === item.id}" @tap="selectTheme(item)">
{{ item.name }}
</view>
</view>
</scroll-view>
</view>
<!-- 排序 -->
<view class="chip-row">
<view class="chip-label">排序</view>
<scroll-view class="chip-scroll" scroll-x :show-scrollbar="false">
<view class="chip-list">
<view class="chip" v-for="(item, index) in sortOptions" :key="index" :class="{active: selectedSortvalue === item.value}" @tap="selectSort(item)">
{{ item.name }}
</view>
</view>
</scroll-view>
</view>
<!-- 行程天数 -->
<view class="chip-row">
<view class="chip-label">天数</view>
<scroll-view class="chip-scroll" scroll-x :show-scrollbar="false">
<view class="chip-list">
<view class="chip" :class="{active: selectedDays === ''}" @tap="selectDays('')">全部</view>
<view class="chip" v-for="(item, index) in daysOptions" :key="index" :class="{active: selectedDays === item}" @tap="selectDays(item)">
{{ item == 5 ? '4天以上' : item + '天' }}
</view>
</view>
</scroll-view>
</view>
</view>
<!-- 景点列表 -->
<view class="attractions-section">
<!-- 遮罩层只覆盖景点列表区域 -->
<view class="dropdown-mask" v-if="showSort || showDays || showTheme" @tap="closeAllDropdowns"></view>
<view class="attraction-card" v-for="(item, index) in attractionsList" :key="index" @click="goToDetail(item)">
<!-- 顶部标签行仅显示标签不显示名称 -->
<view class="card-header" v-if="item.tag || item.tagText || item.area || item.city">
<view class="card-badge" v-if="item.tag || item.tagText || item.area || item.city">
{{ item.tag || item.tagText || item.area || item.city }}
</view>
</view>
<!-- 名称显示在图片上方 -->
<view class="card-name-top" v-if="item.name">
{{ item.name }}
</view>
<!-- 图片与标题块 -->
<view class="card-image">
2026-03-16 10:00:58 +08:00
<image :src="getFirstPhoto(item.photo)" mode="aspectFill" class="card-photo"></image>
2026-01-15 14:44:11 +08:00
<view class="card-title-bar">
<!-- 信息行有字段才展示显示在覆盖层上 -->
<view class="card-info" v-if="item.spotNum || item.duration || (item.distance !== null && item.distance !== undefined)">
<view class="info-item" v-if="item.spotNum">
<image class="info-icon" src="https://api.ccttiot.com/smartmeter/img/static/uaXZbuu10macCqUHCY5o" mode=""></image>
<text>{{ item.spotNum }}个景点</text>
</view>
<view class="info-item" v-if="item.duration">
<image class="info-icon" src="https://api.ccttiot.com/smartmeter/img/static/utR3KxdGuUgrdvIIv2bU" mode=""></image>
<text>{{ item.duration }}小时</text>
</view>
<view class="info-item" v-if="item.distance !== null && item.distance !== undefined">
<image class="info-icon" src="https://api.ccttiot.com/smartmeter/img/static/uSCX0XFEULmiznMgSO2e" mode=""></image>
<text>{{ item.distance }}公里</text>
</view>
</view>
<view class="card-tags" v-if="item.badges && item.badges.length">
<view class="tag" v-for="(tag, idx) in item.badges" :key="idx">{{ tag }}</view>
</view>
</view>
</view>
2026-03-16 10:00:58 +08:00
<!-- 描述最多58字超出截取加... -->
2026-01-15 14:44:11 +08:00
<view class="card-desc" v-if="item.description || item.remark || item.intro">
2026-03-16 10:00:58 +08:00
{{ truncateDesc(item.description || item.remark || item.intro) }}
2026-01-15 14:44:11 +08:00
</view>
</view>
<view class="list-ending">
当前没有更多攻略了...
</view>
</view>
2025-11-08 11:21:57 +08:00
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
backgroundColor: "#fff",
},
searchKeyword: '',
showSort: false,
showDays: false,
showTheme: false,
// 选中项显示
selectedSort: '智能排序',
selectedSortvalue:'',
2026-01-15 14:44:11 +08:00
selectedDays: '',
selectedTheme: '全部',
2025-11-08 11:21:57 +08:00
selectedThemeid:'',
sortOptions: [{name:'智能排序',value:''},{name:'距离最近',value:'distance_asc'},{name:'评分最高',value:'rating_desc'},{name:'价格最低',value:'price_asc'}],
daysOptions: ['1', '2', '3', '4', '5'],
themeOptions: [{name:'自然风光',id:1},{name:'历史文化',id:2},{name:'美食体验',id:3},{name:'休闲娱乐',id:4},{name:'亲子游',id:5}],
attractionsList: [],
isRefreshing:false,
pageNum:1,
total:0,
lat:'',
2026-01-15 14:44:11 +08:00
lng:'',
currentSwiperIndex: 0, // 当前轮播图索引
list: [],
2026-03-16 10:00:58 +08:00
fanhui:''
2025-11-08 11:21:57 +08:00
}
},
2026-03-16 10:00:58 +08:00
onLoad(e) {
2025-11-08 11:21:57 +08:00
this.getlist()
2026-01-15 14:44:11 +08:00
this.getlunbo()
2026-03-16 10:00:58 +08:00
if(e.fanhui){
this.fanhui = e.fanhui
}
console.log(e);
2025-11-08 11:21:57 +08:00
},
onShow() {
// 页面显示时关闭所有下拉菜单
2026-01-15 14:44:11 +08:00
// this.closeAllDropdowns()
2025-11-08 11:21:57 +08:00
},
2026-03-16 10:00:58 +08:00
// 分享到好友(会话)
onShareAppMessage: function() {
return {
title: '玩转福鼎',
path: '/page_fenbao/gonglue/index?fanhui=1'
}
},
// 分享到朋友圈
onShareTimeline: function() {
return {
title: '玩转福鼎',
query: '',
path: '/page_fenbao/gonglue/index?fanhui=1'
}
},
2025-11-08 11:21:57 +08:00
methods: {
2026-03-16 10:00:58 +08:00
btnfh(){
if(this.fanhui == 1){
uni.reLaunch({
url:'/pages/index/index'
})
}else{
uni.navigateBack()
}
},
// 描述最多58字超出截取并加...
truncateDesc(text) {
if (!text) return ''
const str = String(text)
return str.length > 80 ? str.slice(0, 80) + '...' : str
},
2026-01-15 14:44:11 +08:00
// 请求轮播图景区
getlunbo(){
this.$u.get(`/app/strategy/simpleInfo/list`).then(res => {
if(res.code == 200){
// 过滤掉 carousel 为 null 的项
this.list = res.data.filter(item => item.carousel != null && item.carousel !== '')
}
})
},
// 轮播图切换事件
onSwiperChange(e) {
this.currentSwiperIndex = e.detail.current
},
// 轮播图点击事件
onSwiperClick(e) {
const index = e.detail.current || this.currentSwiperIndex
const item = this.list[index]
if (item && item.id) {
uni.navigateTo({
url: '/page_fenbao/gonglue/gongluexq?id=' + item.id
})
}
},
2025-11-08 11:21:57 +08:00
// 请求攻略列表
getlist(){
let url = ''
let jingweidu = ''
2026-01-15 14:44:11 +08:00
if(this.selectedDays !== '' && Number(this.selectedDays) > 0){
const dayNum = Number(this.selectedDays)
if(dayNum < 5){
url = `&dayNumber=${dayNum}`
}else{
url = `&minDayNumber=4`
}
2025-11-08 11:21:57 +08:00
}
if(this.selectedSortvalue == 'distance_asc'){
jingweidu = `currLat=${this.lat == null ? '' : this.lat}&currLon=${this.lng == null ? '' : this.lng}`
}else{
jingweidu = ''
}
this.$u.get(`/app/strategy/list?keyword=${this.searchKeyword}${url}&theme=${this.selectedThemeid}&pageNum${this.pageNum}&pageSize=20&sortBy=${this.selectedSortvalue}&${jingweidu}`).then((res) => {
if(res.code == 200){
this.total = res.total
if(this.pageNum == 1){
this.pageNum++
this.attractionsList = res.rows
}else{
this.pageNum++
this.attractionsList = this.attractionsList.concat(res.rows)
}
}
})
},
// 上拉加载更多
handqixing() {
if(this.attractionsList.length < this.total){
this.getlist()
}
console.log(11)
},
// 下拉刷新列表
onRefresh() {
this.isRefreshing = true
this.pageNum = 1
this.getlist()
setTimeout(() => {
this.isRefreshing = false
}, 1000)
},
// 点击搜索查询列表
handleSearch() {
this.pageNum = 1
this.getlist()
},
// 点击选择排序
selectSort(item) {
this.selectedSort = item.name
this.selectedSortvalue = item.value
if(this.selectedSortvalue == 'distance_asc'){
uni.getLocation({
type: 'gcj02', // 国内地图更兼容
isHighAccuracy: true,
accuracy:'best',
success: (res) => {
console.log('精确坐标:', res)
this.lat = res.latitude
this.lng = res.longitude
this.pageNum = 1
this.getlist()
},
fail: (err) => {
console.error('获取位置失败:', err)
this.lat = ''
this.lng = ''
this.pageNum = 1
this.getlist()
}
})
2026-01-15 14:44:11 +08:00
}else{
this.lat = ''
this.lng = ''
this.pageNum = 1
this.getlist()
2025-11-08 11:21:57 +08:00
}
},
// 点击选择行程天数
selectDays(item) {
this.selectedDays = item
this.pageNum = 1
this.getlist()
},
// 点击选择主题
selectTheme(item) {
this.selectedTheme = item.name
this.selectedThemeid = item.id
this.pageNum = 1
this.getlist()
},
// 跳转到详情页
goToDetail(item) {
uni.navigateTo({
url: '/page_fenbao/gonglue/gongluexq?id=' + item.id
})
2026-03-16 10:00:58 +08:00
},
// 获取第一个图片
getFirstPhoto(photo) {
if (!photo) return ''
// 如果是数组,取第一个元素
if (Array.isArray(photo)) {
return photo[0] || ''
}
// 如果是字符串,可能是逗号分隔的多个图片,取第一个
if (typeof photo === 'string') {
const parts = photo.split(',').filter(Boolean)
return parts[0] || ''
}
return ''
}
2025-11-08 11:21:57 +08:00
}
}
</script>
<style lang="scss">
page {
background: #fff;
}
.page {
background: #fff;
min-height: 100vh;
}
2026-01-15 14:44:11 +08:00
swiper{
background: #fff !important;
}
// 轮播图包装器样式
.swiper-wrapper {
position: relative;
margin-top: 30rpx;
2026-03-16 10:00:58 +08:00
height: 430rpx;
2026-01-15 14:44:11 +08:00
overflow: visible;
padding: 0;
.custom-swiper {
width: 100%;
height: 100%;
.swiper-item {
width: 100%;
display: flex;
justify-content: center;
2026-03-16 10:00:58 +08:00
transition: all 0.1s;
2026-01-15 14:44:11 +08:00
transform: scale(.8);
2026-03-16 10:00:58 +08:00
height: 380rpx !important;
2026-01-15 14:44:11 +08:00
margin-top: 25rpx;
&.active {
opacity: 1;
transform: scale(1);
z-index: 10;
2026-03-16 10:00:58 +08:00
height: 430rpx !important;
2026-01-15 14:44:11 +08:00
margin-top: 0;
}
.swiper-card {
position: relative;
width: 100%;
height: 100%;
border-radius: 20rpx;
overflow: hidden;
2026-03-16 10:00:58 +08:00
transition: all 0.1s;
2026-01-15 14:44:11 +08:00
margin: 0 10rpx;
.swiper-image {
width: 100%;
height: 100%;
display: block;
}
.gradient-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 50%;
background: linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.4) 50%, transparent 100%);
z-index: 2;
}
.swiper-content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 30rpx 20rpx 25rpx;
z-index: 3;
display: flex;
flex-direction: column;
gap: 25rpx;
.title-text {
color: #fff;
font-size: 36rpx;
font-weight: bold;
line-height: 1.4;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
}
// 景点时间轴
.spots-timeline {
width: 100%;
position: relative;
.timeline-scroll {
width: 100%;
white-space: nowrap;
}
.timeline-container {
position: relative;
display: inline-block;
min-width: 100%;
}
// 景点标记容器
.spot-markers {
position: relative;
display: flex;
justify-content: flex-start;
align-items: flex-start;
min-width: 100%;
padding: 0 20rpx;
// 虚线在圆点下方
&::after {
content: '';
position: absolute;
left: 20rpx;
right: 20rpx;
top: 50rpx;
height: 2rpx;
background: repeating-linear-gradient(
to right,
#fff 0,
#fff 8rpx,
transparent 8rpx,
transparent 16rpx
);
opacity: 0.9;
z-index: 1;
}
}
.spot-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
flex-shrink: 0;
min-width: 200rpx;
z-index: 2;
// 红色圆点
.spot-dot {
width: 18rpx;
height: 18rpx;
border-radius: 50%;
background: #ff4444;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
position: relative;
z-index: 3;
margin: 0 0 16rpx 0;
}
// 景点名称(在圆点上方)
.spot-name {
color: #fff;
font-size: 22rpx;
line-height: 1.3;
text-align: center;
text-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.5);
max-width: 180rpx;
margin-bottom: 16rpx;
order: -1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
}
}
}
2025-11-08 11:21:57 +08:00
// 搜索栏样式
.search-container {
display: flex;
align-items: center;
padding: 40rpx 30rpx;
2026-01-15 14:44:11 +08:00
padding-bottom: 20rpx;
2025-11-08 11:21:57 +08:00
background: #fff;
gap: 20rpx;
z-index: 1002;
.search-bar {
flex: 1;
display: flex;
align-items: center;
background: #f5f5f5;
border-radius: 50rpx;
padding: 16rpx 30rpx;
gap: 20rpx;
.search-icon {
width: 32rpx;
height: 32rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
color: #333;
border: none;
background: transparent;
outline: none;
&::placeholder {
color: #999;
}
}
}
.search-btn {
color: #333;
font-size: 32rpx;
text-align: center;
min-width: 120rpx;
}
}
// 筛选器样式
.filter-container {
position: relative;
background: #fff;
padding: 0 30rpx 20rpx;
z-index: 1002;
}
2026-01-15 14:44:11 +08:00
// 芯片筛选
.filter-chip-container {
background: #f5fffd;
padding: 20rpx 24rpx 10rpx;
padding-top: 0;
display: flex;
flex-direction: column;
gap: 16rpx;
2025-11-08 11:21:57 +08:00
border-radius: 0 0 20rpx 20rpx;
2026-01-15 14:44:11 +08:00
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.1);
}
.chip-row {
display: flex;
align-items: center;
gap: 16rpx;
}
.chip-scroll {
flex: 1;
white-space: nowrap;
}
.chip-label {
color: #1a8f88;
font-size: 24rpx;
padding: 6rpx 10rpx;
border-radius: 12rpx;
background: #e2f9f5;
border: 1rpx solid #9be4d5;
}
.chip-list {
display: flex;
flex-wrap: nowrap; // 一行内不换行
gap: 12rpx;
}
.chip {
padding: 10rpx 18rpx;
border-radius: 20rpx;
background: #ecf9ff;
color: #3a556a;
font-size: 24rpx;
border: 1rpx solid #b8e4ff;
transition: all 0.2s ease;
}
.chip.active {
background: linear-gradient(90deg, #2fc1de 0%, #38cfb0 100%);
color: #ffffff;
font-weight: 600;
// box-shadow: 0 4rpx 12rpx rgba(18, 164, 150, 0.28);
2025-11-08 11:21:57 +08:00
}
// 景点列表样式
.attractions-list {
background: #fff;
width: 750rpx;
2026-01-15 14:44:11 +08:00
height: 87vh;
2025-11-08 11:21:57 +08:00
overflow: scroll;
margin-top: 30rpx;
2026-01-15 14:44:11 +08:00
padding-bottom: 40rpx;
.attractions-section {
display: flex;
flex-direction: column;
gap: 30rpx;
align-items: center;
margin-top: 20rpx;
}
2025-11-08 11:21:57 +08:00
.attraction-card {
2026-01-15 14:44:11 +08:00
width: 700rpx;
background: #fff;
2025-11-08 11:21:57 +08:00
border-radius: 20rpx;
overflow: hidden;
2026-01-15 14:44:11 +08:00
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #e7dfcf;
}
.card-header {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 12rpx;
padding: 20rpx 24rpx 10rpx;
}
.card-name {
background: #fff8ea;
color: #b36a14;
font-size: 26rpx;
font-weight: 600;
padding: 10rpx 18rpx;
border-radius: 20rpx;
border: 1rpx solid #e6d6ad;
box-shadow: 0 4rpx 10rpx rgba(0,0,0,0.06);
}
.card-badge {
background: linear-gradient(90deg, #2fc1de 0%, #38cfb0 100%);
color: #fff;
font-size: 22rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
box-shadow: 0 4rpx 10rpx rgba(18, 164, 150, 0.3);
}
.card-name-top {
background: #e2f9f5;
color: #188e86;
font-size: 26rpx;
font-weight: 600;
padding: 10rpx 18rpx;
border-radius: 20rpx;
border: 1rpx solid #9be4d5;
box-shadow: 0 4rpx 10rpx rgba(18, 164, 150, 0.16);
2026-03-16 10:00:58 +08:00
margin: 24rpx;
2026-01-15 14:44:11 +08:00
display: inline-block;
}
.card-image {
position: relative;
height: 320rpx;
border-radius: 20rpx;
overflow: hidden;
margin: 0 24rpx 20rpx;
.card-photo {
width: 100%;
height: 100%;
display: block;
border-radius: 20rpx;
}
.card-title-bar {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 24rpx;
background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.7) 100%);
display: flex;
flex-direction: column;
gap: 12rpx;
}
.card-tags {
display: flex;
gap: 12rpx;
flex-wrap: wrap;
}
.tag {
background: rgba(0,0,0,0.55);
color: #f9f1dd;
padding: 6rpx 14rpx;
border-radius: 16rpx;
font-size: 22rpx;
border: 1rpx solid rgba(255,255,255,0.25);
}
}
.card-info {
display: flex;
flex-wrap: wrap;
gap: 16rpx 30rpx;
padding: 0;
align-items: center;
margin-top: 12rpx;
.info-item {
display: flex;
align-items: center;
gap: 10rpx;
color: #fff;
font-size: 24rpx;
text-shadow: 0 1rpx 3rpx rgba(0,0,0,0.5);
.info-icon {
width: 30rpx;
height: 30rpx;
filter: brightness(0) invert(1);
2025-11-08 11:21:57 +08:00
}
}
}
2026-01-15 14:44:11 +08:00
.card-desc {
padding: 0 24rpx 24rpx;
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
.list-ending {
margin-top: 10rpx;
text-align: center;
color: #bbb;
width: 100%;
padding-bottom: 20rpx;
}
2025-11-08 11:21:57 +08:00
}
// 遮罩层样式 - 只覆盖景点列表区域
.dropdown-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
z-index: 1;
}
// 响应式设计
@media screen and (max-width: 750rpx) {
.search-container {
padding: 15rpx 20rpx;
2026-01-15 14:44:11 +08:00
padding-bottom: 10rpx;
2025-11-08 11:21:57 +08:00
}
.filter-container {
padding: 0 20rpx 15rpx;
gap: 20rpx;
}
.attractions-list {
padding: 0 20rpx;
}
}
</style>