123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- <template>
- <view class="homepage">
- <!-- 轮播图区域 -->
- <view class="banner-section">
- <!-- 状态栏占位 -->
- <view class="status-bar-placeholder" :style="{ height: statusBarHeight + 'px' }"></view>
-
- <!-- 轮播图 - 自动播放,每张停留1秒,支持左右滑动切换 -->
- <swiper
- class="banner-swiper"
- :indicator-dots="true"
- :autoplay="true"
- :interval="1000"
- :duration="300"
- :circular="true"
- indicator-color="rgba(255,255,255,0.5)"
- indicator-active-color="#FFFFFF"
- @change="onBannerChange"
- >
- <swiper-item v-for="(banner, index) in bannerList" :key="index" @click="onBannerClick(banner)">
- <view class="banner-item">
- <image :src="banner.image" class="banner-image" mode="aspectFill"></image>
- <view class="banner-overlay">
- <text class="banner-title">{{ banner.title }}</text>
- </view>
- </view>
- </swiper-item>
- </swiper>
-
- <!-- 轮播图指示器 -->
- <view class="banner-indicator">{{ currentBanner + 1 }}/{{ bannerList.length }}</view>
-
- <!-- 透明搜索框 - 悬浮在轮播图上层 -->
- <view class="transparent-search-container">
- <view class="transparent-search-box" @click="goToSearch">
- <text class="search-icon">🔍</text>
- <text class="search-placeholder">搜索商品名称</text>
- </view>
- </view>
- </view>
-
- <!-- 商品分类区域 -->
- <view class="categories-section">
- <view v-for="category in categoryList" :key="category.id" class="category-block">
- <view class="category-header">
- <text class="category-title">{{ category.name }}</text>
- <view class="more-btn" @click="viewMoreProducts(category)">
- <text class="more-text">更多</text>
- <text class="more-arrow">></text>
- </view>
- </view>
-
- <view class="products-grid">
- <view
- v-for="product in category.products"
- :key="product.id"
- class="product-item"
- @click="goToProductDetail(product)"
- >
- <image :src="product.image" class="product-image" mode="aspectFill"></image>
- <view class="product-info">
- <text class="product-name">{{ product.name }}</text>
- <!-- 按需求不显示价格,只显示商品图和名称 -->
- </view>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 加载状态 -->
- <view v-if="loading" class="loading-container">
- <view class="loading-content">
- <view class="loading-spinner"></view>
- <text class="loading-text">{{ loadingText }}</text>
- </view>
- </view>
-
- <!-- 底部间距,避免被tabBar遮挡 -->
- <view class="bottom-spacer"></view>
-
- <!-- 开发测试区域 (可选显示) -->
- <view v-if="showDebug" class="debug-section">
- <view class="debug-header" @click="toggleDebug">
- <text class="debug-title">🔧 开发测试</text>
- </view>
- <view v-if="debugExpanded" class="debug-content">
- <view class="user-status">
- <text class="status-text">{{ isLoggedIn ? '已登录: ' + userPhone : '游客模式' }}</text>
- <button v-if="!isLoggedIn" class="debug-btn login" @click="goToLogin">登录</button>
- <button v-else class="debug-btn logout" @click="logout">退出</button>
- </view>
- <button class="debug-btn clear" @click="clearAppData">清除数据</button>
- </view>
- </view>
- </view>
- </template>
- <script>
- import { isLoggedIn, getUserInfo, navigateToLogin, clearUserData } from '@/utils/auth.js'
- // TODO: API替换 - 首页数据接口
- import { getHomepageData, handleApiError } from '@/api/homepage.js'
-
- export default {
- data() {
- return {
- statusBarHeight: 0,
- isLoggedIn: false,
- userPhone: '',
- currentBanner: 0,
- showDebug: true, // 开发阶段显示调试面板
- debugExpanded: false,
-
- // 页面数据 - 从API获取
- bannerList: [], // TODO: 替换为 API 调用
- categoryList: [], // TODO: 替换为 API 调用
-
- // 加载状态
- loading: true,
- loadingText: '加载中...'
- }
- },
- onLoad() {
- // 获取系统信息
- const systemInfo = uni.getSystemInfoSync()
- this.statusBarHeight = systemInfo.statusBarHeight || 20
-
- // TODO: API替换 - 加载首页数据
- this.loadHomepageData()
- },
- onShow() {
- this.checkLoginStatus()
- // 更新自定义tabBar选中状态
- this.updateTabBar()
- },
- onPullDownRefresh() {
- // TODO: API替换 - 下拉刷新重新加载数据
- this.loadHomepageData().then(() => {
- uni.stopPullDownRefresh()
- })
- },
- methods: {
- // 更新tabBar选中状态
- updateTabBar() {
- try {
- if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- this.getTabBar().updateSelected(0) // 首页索引为0
- }
- } catch (error) {
- console.log('更新tabBar状态失败:', error)
- }
- },
-
- // 检查登录状态
- checkLoginStatus() {
- this.isLoggedIn = isLoggedIn()
- const userInfo = getUserInfo()
- this.userPhone = userInfo ? userInfo.phone : ''
- },
-
- // 移除登录提示相关方法,游客可正常访问首页
-
- // === 数据加载方法 ===
-
- /**
- * 加载首页数据
- * TODO: API替换 - 将模拟数据替换为真实API调用
- */
- async loadHomepageData() {
- try {
- this.loading = true
- this.loadingText = '加载首页数据...'
-
- console.log('🔄 开始加载首页数据')
-
- // TODO: 替换为真实API调用
- const response = await getHomepageData()
-
- if (response.success) {
- // 处理轮播图数据 - 限制最多6张轮播图
- this.bannerList = response.data.banners
- .filter(banner => banner.status === 1) // 只显示启用的轮播图
- .sort((a, b) => a.sort - b.sort) // 按排序字段排序
- .slice(0, 6) // 限制最多6张
- .map(banner => ({
- id: banner.id,
- title: banner.title,
- subtitle: banner.subtitle,
- image: banner.imageUrl, // 使用素材库URL
- linkType: banner.linkType,
- linkTarget: banner.linkTarget
- }))
-
- // 处理商品分类数据
- this.categoryList = response.data.categories.map(category => ({
- id: category.id,
- name: category.name,
- products: category.products.map(product => ({
- id: product.id,
- name: product.name,
- image: product.imageUrl, // 使用素材库URL
- // 注意:不展示价格,按需求只显示图片和名称
- stock: product.stock,
- sales: product.sales
- }))
- }))
-
- console.log('✅ 首页数据加载成功', {
- banners: this.bannerList.length,
- categories: this.categoryList.length
- })
-
- } else {
- throw new Error(response.message || '数据加载失败')
- }
-
- } catch (error) {
- console.error('❌ 首页数据加载失败:', error)
- handleApiError(error)
-
- // 显示错误提示
- uni.showToast({
- title: '数据加载失败',
- icon: 'none',
- duration: 2000
- })
-
- } finally {
- this.loading = false
- }
- },
-
- // === 新的首页功能方法 ===
-
- // 跳转到搜索页
- goToSearch() {
- uni.navigateTo({
- url: '/pages/search/index'
- })
- },
-
- // 轮播图切换事件 - 支持左右滑动切换
- // 向左滑动:划到下一张图 (current 增加)
- // 向右滑动:返回上一张图 (current 减少)
- onBannerChange(e) {
- this.currentBanner = e.detail.current
- },
-
- // 轮播图点击事件
- onBannerClick(banner) {
- console.log('🖱️ 点击轮播图:', banner)
-
- // TODO: API替换 - 根据linkType和linkTarget进行不同跳转
- if (banner.linkType && banner.linkTarget) {
- switch (banner.linkType) {
- case 'product':
- // 跳转到商品详情
- uni.navigateTo({
- url: `/pages/product/detail?id=${banner.linkTarget}`
- })
- break
- case 'category':
- // 跳转到分类页面
- uni.navigateTo({
- url: `/pages/category/index?id=${banner.linkTarget}`
- })
- break
- case 'activity':
- // 跳转到活动页面
- uni.navigateTo({
- url: `/pages/activity/detail?id=${banner.linkTarget}`
- })
- break
- case 'external':
- // 外部链接
- uni.showModal({
- title: '提示',
- content: '即将跳转到外部链接',
- success: (res) => {
- if (res.confirm) {
- // TODO: 处理外部链接跳转
- console.log('跳转外部链接:', banner.linkTarget)
- }
- }
- })
- break
- default:
- console.log('未知的跳转类型:', banner.linkType)
- }
- }
- },
-
- // 查看更多商品
- viewMoreProducts(category) {
- console.log('查看更多商品:', category)
- uni.navigateTo({
- url: `/pages/category/index?id=${category.id}&name=${category.name}`
- })
- },
-
- // 跳转商品详情
- goToProductDetail(product) {
- console.log('查看商品详情:', product)
- uni.navigateTo({
- url: `/pages/product/detail?id=${product.id}`
- })
- },
-
- // === 开发调试功能 ===
-
- // 切换调试面板
- toggleDebug() {
- this.debugExpanded = !this.debugExpanded
- },
-
- // 跳转登录页
- goToLogin() {
- navigateToLogin()
- },
-
- // 退出登录
- logout() {
- uni.showModal({
- title: '提示',
- content: '确定要退出登录吗?',
- success: (res) => {
- if (res.confirm) {
- clearUserData()
- this.checkLoginStatus()
- uni.showToast({
- title: '已退出登录',
- icon: 'success'
- })
- }
- }
- })
- },
-
- // 清除所有数据
- clearAppData() {
- uni.showModal({
- title: '确认清除',
- content: '这将清除所有本地数据,包括登录状态和首次启动标记',
- confirmText: '确认清除',
- cancelText: '取消',
- success: (res) => {
- if (res.confirm) {
- // 清除所有相关数据
- uni.clearStorageSync()
-
- // 重新初始化全局数据
- try {
- const app = getApp()
- if (app && app.globalData) {
- app.globalData.isLoggedIn = false
- app.globalData.userInfo = null
- }
- } catch (error) {
- console.log('更新全局状态失败')
- }
-
- // 更新页面状态
- this.checkLoginStatus()
-
- uni.showToast({
- title: '数据已清除',
- icon: 'success'
- })
-
- // 延迟重启应用以模拟首次启动
- setTimeout(() => {
- uni.reLaunch({
- url: '/pages/index/index'
- })
- }, 1500)
- }
- }
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .homepage {
- min-height: 100vh;
- background: #ffffff; /* 改为白色背景,符合UI设计 */
- }
- /* 轮播图区域 */
- .banner-section {
- position: relative;
- height: 550rpx; /* 恢复原来的高度 */
- overflow: hidden;
- background: #ffffff; /* 确保背景为白色 */
-
- /* 状态栏占位 */
- .status-bar-placeholder {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- z-index: 1;
- }
-
- .banner-swiper {
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- }
-
- .banner-item {
- width: 100%;
- height: 100%;
- position: relative;
-
- .banner-image {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
-
- .banner-overlay {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- background: linear-gradient(transparent, rgba(0, 0, 0, 0.3)); /* 减少遮罩透明度 */
- padding: 30rpx;
-
- .banner-title {
- color: white;
- font-size: 32rpx;
- font-weight: bold;
- }
- }
- }
-
- .banner-indicator {
- position: absolute;
- bottom: 30rpx; /* 调整到底部右侧,符合UI设计 */
- right: 30rpx;
- background: rgba(255, 255, 255, 0.9); /* 改为白色半透明背景 */
- color: #333; /* 改为深色文字 */
- padding: 8rpx 16rpx;
- border-radius: 20rpx;
- font-size: 24rpx;
- z-index: 3;
- border: 1rpx solid rgba(0, 0, 0, 0.1); /* 添加细边框 */
- }
-
- /* 透明搜索框容器 - 调整位置到轮播图上方 */
- .transparent-search-container {
- position: absolute;
- left: 0;
- right: 0;
- z-index: 10;
- padding: 0 20rpx; /* 减少左右间距,更接近边缘对齐 */
- top: 80rpx; /* 固定位置,在状态栏下方 */
- }
-
- /* 透明搜索框 */
- .transparent-search-box {
- background: rgba(255, 255, 255, 0.6); /* 改为半透明背景 */
- backdrop-filter: blur(10rpx);
- border: 1rpx solid rgba(255, 255, 255, 0.3); /* 改为半透明白色边框 */
- border-radius: 50rpx;
- height: 80rpx; /* 增加高度 */
- display: flex;
- align-items: center;
- padding: 0 30rpx;
- box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1); /* 减少阴影 */
- transition: all 0.3s ease;
-
- .search-icon {
- font-size: 32rpx;
- color: #666; /* 改为深色图标 */
- margin-right: 20rpx;
- }
-
- .search-placeholder {
- font-size: 28rpx;
- color: #999; /* 改为深色占位符 */
- flex: 1;
- }
-
- /* 点击态效果 */
- &:active {
- background: rgba(255, 255, 255, 1);
- transform: scale(0.98);
- }
- }
- }
- /* 商品分类区域 */
- .categories-section {
- padding: 20rpx 20rpx 120rpx; /* 减少左右间距,与搜索框对齐,更接近边缘 */
- background: #ffffff;
-
- .category-block {
- background: #ffffff; /* 保持白色背景 */
- border-radius: 30rpx; /* 减少圆角 */
- margin-bottom: 20rpx; /* 减少间距 */
- overflow: hidden;
- border: 1rpx solid #f0f0f0; /* 添加细边框 */
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); /* 减少阴影 */
-
- .category-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 25rpx 20rpx; /* 减少内边距,与外部容器对齐 */
- border-bottom: 1rpx solid #f5f5f5; /* 更淡的分割线 */
-
- .category-title {
- font-size: 32rpx; /* 减少字体大小 */
- font-weight: 600; /* 减少字重 */
- color: #333;
- }
-
- .more-btn {
- display: flex;
- align-items: center;
- color: #FF6600;
-
- .more-text {
- font-size: 26rpx; /* 减少字体大小 */
- margin-right: 8rpx;
- }
-
- .more-arrow {
- font-size: 22rpx; /* 减少字体大小 */
- }
- }
- }
-
- .products-grid {
- display: flex;
- padding: 20rpx 20rpx; /* 左右内边距与外部容器对齐 */
- gap: 15rpx; /* 使用gap替代margin */
-
- .product-item {
- flex: 1;
- background: #fafafa; /* 更淡的背景色 */
- border-radius: 12rpx; /* 减少圆角 */
- overflow: hidden;
- border: 1rpx solid #f0f0f0; /* 添加细边框 */
-
- .product-image {
- width: 100%;
- height: 180rpx; /* 调整图片高度 */
- background: #f5f5f5;
- object-fit: cover;
- }
-
- .product-info {
- padding: 15rpx 12rpx; /* 减少内边距 */
-
- .product-name {
- font-size: 24rpx; /* 减少字体大小 */
- color: #333;
- display: block;
- text-align: center;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- line-height: 1.3;
- }
- }
-
- /* 点击态效果 */
- &:active {
- transform: scale(0.98);
- opacity: 0.8;
- }
- }
- }
- }
- }
- /* 加载状态 */
- .loading-container {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(255, 255, 255, 0.95);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 9999;
-
- .loading-content {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .loading-spinner {
- width: 60rpx;
- height: 60rpx;
- border: 4rpx solid #f3f3f3;
- border-top: 4rpx solid #FF6600;
- border-radius: 50%;
- animation: loading-spin 1s linear infinite;
- margin-bottom: 20rpx;
- }
-
- .loading-text {
- font-size: 28rpx;
- color: #666;
- }
- }
- }
- @keyframes loading-spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- /* 底部间距 */
- .bottom-spacer {
- height: 100rpx;
- }
- /* 调试区域 */
- .debug-section {
- position: fixed;
- bottom: 120rpx;
- right: 30rpx;
- background: rgba(0, 0, 0, 0.8);
- border-radius: 20rpx;
- overflow: hidden;
- z-index: 999;
-
- .debug-header {
- padding: 20rpx;
-
- .debug-title {
- color: white;
- font-size: 24rpx;
- }
- }
-
- .debug-content {
- border-top: 1rpx solid rgba(255, 255, 255, 0.2);
- padding: 20rpx;
-
- .user-status {
- display: flex;
- align-items: center;
- margin-bottom: 20rpx;
-
- .status-text {
- color: white;
- font-size: 22rpx;
- margin-right: 15rpx;
- flex: 1;
- }
- }
-
- .debug-btn {
- padding: 10rpx 20rpx;
- border-radius: 15rpx;
- border: none;
- font-size: 22rpx;
- margin-right: 10rpx;
- margin-bottom: 10rpx;
-
- &.login {
- background: #007aff;
- color: white;
- }
-
- &.logout {
- background: #ff9500;
- color: white;
- }
-
- &.clear {
- background: #ff3b30;
- color: white;
- width: 100%;
- margin-right: 0;
- }
- }
- }
- }
- /* 点击态效果 */
- .more-btn:active {
- opacity: 0.7;
- }
- .banner-item:active {
- opacity: 0.9;
- }
- </style>
|