roamfuding-xcx/page_fenbao/gonglue/index.vue

779 lines
21 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">
</u-navbar>
<scroll-view class="attractions-list" @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black">
<view class="swiper-wrapper">
<swiper
class="custom-swiper"
:indicator-dots="false"
:autoplay="true"
:interval="10000"
:duration="1000"
previous-margin="40rpx"
next-margin="40rpx"
:circular="true"
@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">
<image :src="item.photo" mode="aspectFill" class="card-photo"></image>
<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>
<!-- 描述 -->
<view class="card-desc" v-if="item.description || item.remark || item.intro">
{{ item.description || item.remark || item.intro }}
</view>
</view>
<view class="list-ending">
当前没有更多攻略了...
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
backgroundColor: "#fff",
},
searchKeyword: '',
showSort: false,
showDays: false,
showTheme: false,
// 选中项显示
selectedSort: '智能排序',
selectedSortvalue:'',
selectedDays: '',
selectedTheme: '全部',
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:'',
lng:'',
currentSwiperIndex: 0, // 当前轮播图索引
list: [],
}
},
onLoad() {
this.getlist()
this.getlunbo()
},
onShow() {
// 页面显示时关闭所有下拉菜单
// this.closeAllDropdowns()
},
methods: {
// 请求轮播图景区
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
})
}
},
// 请求攻略列表
getlist(){
let url = ''
let jingweidu = ''
if(this.selectedDays !== '' && Number(this.selectedDays) > 0){
const dayNum = Number(this.selectedDays)
if(dayNum < 5){
url = `&dayNumber=${dayNum}`
}else{
url = `&minDayNumber=4`
}
}
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()
}
})
}else{
this.lat = ''
this.lng = ''
this.pageNum = 1
this.getlist()
}
},
// 点击选择行程天数
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
})
}
}
}
</script>
<style lang="scss">
page {
background: #fff;
}
.page {
background: #fff;
min-height: 100vh;
}
swiper{
background: #fff !important;
}
// 轮播图包装器样式
.swiper-wrapper {
position: relative;
margin-top: 30rpx;
height: 550rpx;
overflow: visible;
padding: 0;
.custom-swiper {
width: 100%;
height: 100%;
.swiper-item {
width: 100%;
display: flex;
justify-content: center;
transition: all 0.6s;
transform: scale(.8);
height: 500rpx !important;
margin-top: 25rpx;
&.active {
opacity: 1;
transform: scale(1);
z-index: 10;
height: 550rpx !important;
margin-top: 0;
}
.swiper-card {
position: relative;
width: 100%;
height: 100%;
border-radius: 20rpx;
overflow: hidden;
transition: all 0.6s;
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;
}
}
}
}
}
}
}
}
// 搜索栏样式
.search-container {
display: flex;
align-items: center;
padding: 40rpx 30rpx;
padding-bottom: 20rpx;
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;
}
// 芯片筛选
.filter-chip-container {
background: #f5fffd;
padding: 20rpx 24rpx 10rpx;
padding-top: 0;
display: flex;
flex-direction: column;
gap: 16rpx;
border-radius: 0 0 20rpx 20rpx;
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);
}
// 景点列表样式
.attractions-list {
background: #fff;
width: 750rpx;
height: 87vh;
overflow: scroll;
margin-top: 30rpx;
padding-bottom: 40rpx;
.attractions-section {
display: flex;
flex-direction: column;
gap: 30rpx;
align-items: center;
margin-top: 20rpx;
}
.attraction-card {
width: 700rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
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);
margin: 24rpx 12rpx;
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);
}
}
}
.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;
}
}
// 遮罩层样式 - 只覆盖景点列表区域
.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;
padding-bottom: 10rpx;
}
.filter-container {
padding: 0 20rpx 15rpx;
gap: 20rpx;
}
.attractions-list {
padding: 0 20rpx;
}
}
</style>