feat: redesign homepage with hero, announcements, card promos, scenic spots

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-06-24 19:31:05 +08:00
co-authored by Claude
parent cf6ef6ace3
commit aefdb3944e
+201 -95
View File
@@ -1,49 +1,85 @@
<template> <template>
<view class="page"> <view class="page">
<view class="header"> <!-- 头部区域:背景图 + 渐变遮罩 + 标题 + 搜索栏 -->
<view class="hero">
<view class="hero-overlay" />
<view class="hero-content">
<text class="hero-title">爱上萍乡</text>
<text class="hero-subtitle">赣湘之间 · 发现旅途美好</text>
<view class="search-bar" @click="goSearch"> <view class="search-bar" @click="goSearch">
<text class="search-icon">🔍</text> <text class="search-icon">🔍</text>
<text class="search-placeholder">搜索景区、活动...</text> <text class="search-placeholder">搜索景区、活动...</text>
</view> </view>
</view> </view>
</view>
<scroll-view scroll-y class="scroll-content"> <scroll-view scroll-y class="scroll-content">
<swiper class="banner-swiper" autoplay circular indicator-dots indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#fff"> <!-- 公告栏 -->
<swiper-item v-for="item in banners" :key="item.id" @click="goDetail(item)"> <view class="notice-bar" v-if="announcements.length">
<image class="banner-img" :src="item.image" mode="aspectFill" /> <view class="notice-label">
<text class="notice-icon">📢</text>
<text class="notice-text">公告</text>
</view>
<swiper class="notice-swiper" vertical autoplay circular :interval="3000" :duration="500">
<swiper-item v-for="item in announcements" :key="item.id">
<text class="notice-content">{{ item.content }}</text>
</swiper-item> </swiper-item>
</swiper> </swiper>
<view class="section">
<view class="section-header">
<text class="section-title">热门景区</text>
<text class="section-more" @click="goMore">查看更多 ›</text>
</view> </view>
<view class="spot-list">
<view class="spot-card" v-for="item in hotSpots" :key="item.id" @click="goSpot(item)"> <!-- 卡片销售宣传 -->
<image class="spot-img" :src="item.image" mode="aspectFill" /> <view class="card-section">
<view class="promo-card promo-card-gx" @click="goCardDetail('gx')">
<view class="promo-card-badge">工会会员专属</view>
<text class="promo-card-name">赣湘全域旅游年卡</text>
<view class="promo-card-price-row">
<text class="promo-card-currency">¥</text>
<text class="promo-card-price">300</text>
<text class="promo-card-unit">/套卡(2张)</text>
</view>
<view class="promo-card-features">
<text class="promo-card-feature">✦ 22家景区免门票</text>
<text class="promo-card-feature">✦ 武功山·方特·君澜温泉</text>
<text class="promo-card-feature">✦ 覆盖萍乡/长沙/株洲/湘潭</text>
</view>
<view class="promo-card-btn">
<text class="promo-card-btn-text">立即抢购 →</text>
</view>
</view>
<view class="promo-card promo-card-super" @click="goCardDetail('super')">
<view class="promo-card-badge">大学生·市民专属</view>
<text class="promo-card-name">超级玩家卡</text>
<view class="promo-card-price-row">
<text class="promo-card-currency">¥</text>
<text class="promo-card-price">198</text>
</view>
<view class="promo-card-features">
<text class="promo-card-feature">✦ 40家景区免门票</text>
<text class="promo-card-feature">✦ 武功山·方特·韶山·滴水洞</text>
<text class="promo-card-feature">✦ 含外骨骼登山·青年旅社等</text>
</view>
<view class="promo-card-btn">
<text class="promo-card-btn-text">立即抢购 →</text>
</view>
</view>
</view>
<!-- 热门景点列表 -->
<view class="spots-section">
<view class="spots-header">
<text class="spots-title">热门景点</text>
<text class="spots-more">查看全部 ></text>
</view>
<view class="spot-card" v-for="spot in scenicSpots" :key="spot.id" @click="goSpot(spot)">
<view class="spot-image" :style="{ background: getGradient(spot.region) }">
<view class="spot-region-tag">{{ spot.region }}</view>
</view>
<view class="spot-info"> <view class="spot-info">
<text class="spot-name">{{ item.name }}</text> <text class="spot-name">{{ spot.name }}</text>
<view class="spot-meta"> <view class="spot-meta">
<text class="spot-price">¥{{ item.price }}<text class="spot-price-unit">/人</text></text> <text class="spot-price">¥{{ spot.price }}</text>
<text class="spot-sales">已售{{ item.sales }}</text> <view class="spot-free-tag">持卡免门票</view>
</view>
</view>
</view>
</view>
</view>
<view class="section">
<view class="section-header">
<text class="section-title">精选活动</text>
</view>
<view class="activity-list">
<view class="activity-card" v-for="item in activities" :key="item.id" @click="goActivity(item)">
<image class="activity-img" :src="item.image" mode="aspectFill" />
<view class="activity-info">
<text class="activity-name">{{ item.name }}</text>
<text class="activity-desc">{{ item.desc }}</text>
<text class="activity-price" v-if="item.price">¥{{ item.price }}起</text>
</view> </view>
</view> </view>
</view> </view>
@@ -53,81 +89,151 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref } from 'vue'
import { useUserStore } from '../../store/user' import { scenicSpots } from '../../data/scenic-spots'
import type { ScenicSpotItem } from '../../data/scenic-spots'
const userStore = useUserStore() /** 公告数据(mock,后续接 API) */
const announcements = ref([
/** 轮播图数据 */ { id: 1, content: '赣湘全域旅游年卡限时优惠,工会会员专享¥300/套卡!' },
const banners = ref([ { id: 2, content: '超级玩家卡全新上线,40家景区免门票,仅¥198!' },
{ id: 1, image: 'https://picsum.photos/750/340?random=1', link: '' }, { id: 3, content: '武功山金顶景区已开放,持卡可免费入园1次。' },
{ id: 2, image: 'https://picsum.photos/750/340?random=2', link: '' }
]) ])
/** 热门景区 */ /** 区域对应的渐变色(景点图片占位) */
const hotSpots = ref([ const regionGradients: Record<string, string> = {
{ id: 1, name: '张家界国家森林公园', image: 'https://picsum.photos/200/200?random=3', price: 225, sales: 12580 }, '萍乡': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
{ id: 2, name: '天门山景区', image: 'https://picsum.photos/200/200?random=4', price: 278, sales: 8960 }, '长沙': 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
{ id: 3, name: '凤凰古城', image: 'https://picsum.photos/200/200?random=5', price: 0, sales: 25600 }, '株洲': 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
{ id: 4, name: '岳麓山景区', image: 'https://picsum.photos/200/200?random=6', price: 0, sales: 18200 } '湘潭': 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)',
])
/** 精选活动 */
const activities = ref([
{ id: 1, name: '周末亲子游', desc: '探索自然,快乐成长', image: 'https://picsum.photos/300/200?random=7', price: 199 },
{ id: 2, name: '夜游湘江', desc: '璀璨夜景,浪漫之旅', image: 'https://picsum.photos/300/200?random=8', price: 128 }
])
onMounted(() => {
// 未登录时跳转登录页
if (!userStore.isLogin) {
uni.navigateTo({ url: '/pages/login/login' })
} }
})
/** 跳转搜索 */ function getGradient(region: string): string {
function goSearch() { uni.navigateTo({ url: '/pages/search/search' }) } return regionGradients[region] || regionGradients['萍乡']
}
/** 跳转详情 */ /** 跳转搜索(暂不实现) */
function goDetail(item: any) { /* TODO */ } function goSearch() {
uni.showToast({ title: '搜索功能开发中', icon: 'none' })
}
/** 跳转景区详情 */ /** 跳转卡片详情 */
function goSpot(item: any) { uni.navigateTo({ url: `/pages/spot/detail?id=${item.id}` }) } function goCardDetail(type: 'gx' | 'super') {
uni.navigateTo({ url: '/packages/user/pages/card/list' })
}
/** 查看更多 */ /** 跳转景点详情 */
function goMore() { uni.navigateTo({ url: '/pages/spot/list' }) } function goSpot(spot: ScenicSpotItem) {
uni.showToast({ title: `${spot.name} 详情开发中`, icon: 'none' })
/** 跳转活动 */ }
function goActivity(item: any) { /* TODO */ }
</script> </script>
<style scoped> <style scoped>
.page { display: flex; flex-direction: column; height: 100vh; background: #f5f5f5; } .page { display: flex; flex-direction: column; min-height: 100vh; background: #f5f5f5; }
.header { background: linear-gradient(135deg, #4A90D9, #357ABD); padding: 20rpx 30rpx; padding-top: 80rpx; }
.search-bar { display: flex; align-items: center; background: rgba(255,255,255,0.9); border-radius: 40rpx; padding: 16rpx 24rpx; } /* === 头部区域 === */
.hero { position: relative; height: 360rpx; overflow: hidden; }
.hero-overlay {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(180deg, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.6) 100%);
z-index: 1;
}
.hero::before {
content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(135deg, #1a237e 0%, #4a148c 50%, #e65100 100%);
}
.hero-content {
position: relative; z-index: 2;
padding: 80rpx 30rpx 30rpx;
display: flex; flex-direction: column;
}
.hero-title { font-size: 42rpx; font-weight: bold; color: #fff; margin-bottom: 8rpx; }
.hero-subtitle { font-size: 24rpx; color: rgba(255,255,255,0.8); margin-bottom: 24rpx; }
.search-bar {
display: flex; align-items: center;
background: rgba(255,255,255,0.95); border-radius: 40rpx;
padding: 18rpx 24rpx;
}
.search-icon { font-size: 28rpx; margin-right: 12rpx; } .search-icon { font-size: 28rpx; margin-right: 12rpx; }
.search-placeholder { font-size: 26rpx; color: #999; } .search-placeholder { font-size: 26rpx; color: #999; }
/* === 公告栏 === */
.notice-bar {
display: flex; align-items: center;
background: #fff; padding: 20rpx 24rpx; margin: 0;
border-bottom: 1rpx solid #f0f0f0;
}
.notice-label { display: flex; align-items: center; margin-right: 16rpx; flex-shrink: 0; }
.notice-icon { font-size: 28rpx; margin-right: 6rpx; }
.notice-text { font-size: 24rpx; color: #E74C3C; font-weight: 500; }
.notice-swiper { flex: 1; height: 40rpx; }
.notice-content { font-size: 24rpx; color: #666; line-height: 40rpx; }
/* === 卡片销售宣传 === */
.card-section { padding: 24rpx; display: flex; flex-direction: column; gap: 24rpx; }
.promo-card {
position: relative; border-radius: 24rpx; padding: 40rpx 32rpx;
overflow: hidden;
}
.promo-card-gx {
background: linear-gradient(135deg, #1a237e 0%, #283593 40%, #ffd54f 100%);
}
.promo-card-super {
background: linear-gradient(135deg, #e65100 0%, #bf360c 40%, #7b1fa2 100%);
}
.promo-card-badge {
display: inline-block;
background: rgba(255,255,255,0.2); border-radius: 20rpx;
padding: 6rpx 16rpx; margin-bottom: 16rpx;
font-size: 22rpx; color: rgba(255,255,255,0.9);
}
.promo-card-name { font-size: 34rpx; font-weight: bold; color: #fff; display: block; margin-bottom: 12rpx; }
.promo-card-price-row { display: flex; align-items: baseline; margin-bottom: 20rpx; }
.promo-card-currency { font-size: 32rpx; color: #fff; font-weight: bold; }
.promo-card-price { font-size: 64rpx; color: #fff; font-weight: bold; line-height: 1; }
.promo-card-unit { font-size: 24rpx; color: rgba(255,255,255,0.8); margin-left: 8rpx; }
.promo-card-features { display: flex; flex-direction: column; gap: 8rpx; margin-bottom: 24rpx; }
.promo-card-feature { font-size: 24rpx; color: rgba(255,255,255,0.9); }
.promo-card-btn {
display: flex; align-items: center; justify-content: center;
background: #fff; border-radius: 40rpx;
padding: 16rpx 0;
}
.promo-card-btn-text { font-size: 28rpx; font-weight: bold; color: #333; }
/* === 热门景点列表 === */
.spots-section { padding: 24rpx; }
.spots-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 20rpx;
}
.spots-title { font-size: 34rpx; font-weight: bold; color: #333; }
.spots-more { font-size: 24rpx; color: #999; }
.spot-card {
background: #fff; border-radius: 20rpx; overflow: hidden;
margin-bottom: 20rpx;
}
.spot-image {
width: 100%; height: 320rpx; position: relative;
}
.spot-region-tag {
position: absolute; top: 16rpx; left: 16rpx;
background: rgba(0,0,0,0.5); border-radius: 16rpx;
padding: 4rpx 16rpx;
font-size: 22rpx; color: #fff;
}
.spot-info { padding: 20rpx 24rpx; }
.spot-name { font-size: 30rpx; font-weight: 600; color: #333; margin-bottom: 12rpx; display: block; }
.spot-meta { display: flex; justify-content: space-between; align-items: center; }
.spot-price { font-size: 32rpx; color: #E74C3C; font-weight: bold; }
.spot-free-tag {
background: #E8F5E9; border-radius: 16rpx;
padding: 4rpx 16rpx;
font-size: 22rpx; color: #2E7D32; font-weight: 500;
}
/* === 滚动区域 === */
.scroll-content { flex: 1; } .scroll-content { flex: 1; }
.banner-swiper { height: 340rpx; }
.banner-img { width: 100%; height: 100%; }
.section { padding: 20rpx; }
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
.section-title { font-size: 32rpx; font-weight: bold; color: #333; }
.section-more { font-size: 24rpx; color: #999; }
.spot-list { display: flex; flex-wrap: wrap; gap: 16rpx; }
.spot-card { width: calc(50% - 8rpx); background: #fff; border-radius: 16rpx; overflow: hidden; }
.spot-img { width: 100%; height: 200rpx; }
.spot-info { padding: 16rpx; }
.spot-name { font-size: 26rpx; font-weight: 500; color: #333; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
.spot-meta { display: flex; justify-content: space-between; align-items: baseline; margin-top: 8rpx; }
.spot-price { font-size: 30rpx; color: #E74C3C; font-weight: bold; }
.spot-price-unit { font-size: 20rpx; font-weight: normal; }
.spot-sales { font-size: 22rpx; color: #999; }
.activity-list { display: flex; flex-direction: column; gap: 16rpx; }
.activity-card { display: flex; background: #fff; border-radius: 16rpx; overflow: hidden; }
.activity-img { width: 240rpx; height: 160rpx; flex-shrink: 0; }
.activity-info { flex: 1; padding: 16rpx; display: flex; flex-direction: column; justify-content: center; }
.activity-name { font-size: 28rpx; font-weight: 500; color: #333; margin-bottom: 8rpx; }
.activity-desc { font-size: 24rpx; color: #999; margin-bottom: 8rpx; }
.activity-price { font-size: 28rpx; color: #E74C3C; font-weight: bold; }
</style> </style>