282 lines
7.1 KiB
Vue
282 lines
7.1 KiB
Vue
<template>
|
||
<view class="square-container">
|
||
<!-- 固定背景 -->
|
||
<image class="background-fixed" src="/static/square/squearbj.png" mode="aspectFill" />
|
||
|
||
<!-- 横向瀑布流卡片层(内部自带横向滚动) -->
|
||
<WaterfallGrid
|
||
:screenWidth="screenWidth"
|
||
:screenHeight="screenHeight"
|
||
:bannerBottom="bannerBottomPx"
|
||
:useMockData="USE_MOCK_DATA"
|
||
:category="activeContentTab"
|
||
@cardClick="handleCardClick"
|
||
class="fall-bg"
|
||
/>
|
||
|
||
<!-- Header组件 -->
|
||
<Header
|
||
:showGuideIcon="true"
|
||
:showTaskIcon="true"
|
||
:showStarActivityIcon="true"
|
||
backIconColor="#e6e6e6"
|
||
/>
|
||
|
||
<!-- 轮播图 + 内容分类 Tab -->
|
||
<view class="banner-tabs-wrapper">
|
||
<BannerCarousel
|
||
:bannerActivities="bannerActivities"
|
||
@activityClick="handleActivityClick"
|
||
@top3Click="showRankingModal = true"
|
||
/>
|
||
<ContentTabs class="tabs" v-model="activeContentTab" />
|
||
</view>
|
||
|
||
<!-- 蒙层 - 导航栏展开时显示 -->
|
||
<view v-if="navExpanded" class="nav-mask" @click="navExpanded = false"></view>
|
||
|
||
<!-- 排行榜弹窗 -->
|
||
<RankingModal
|
||
:visible="showRankingModal"
|
||
:parent-active="true"
|
||
:star-id="currentStarId"
|
||
@update:visible="handleRankingModalClose"
|
||
@visit="handleRankingVisit"
|
||
/>
|
||
|
||
<!-- 底部导航栏 -->
|
||
<BottomNav
|
||
:activeTab="0"
|
||
:isExpanded="navExpanded"
|
||
@update:activeTab="handleTabChange"
|
||
@update:isExpanded="navExpanded = $event"
|
||
/>
|
||
|
||
<!-- 全局引导遮罩 -->
|
||
<GuideOverlay />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||
import { useStore } from 'vuex'
|
||
import Header from '../components/Header.vue'
|
||
import BottomNav from '../components/BottomNav.vue'
|
||
import GuideOverlay from '@/components/GuideOverlay.vue'
|
||
import RankingModal from '../components/RankingModal.vue'
|
||
import BannerCarousel from './components/BannerCarousel.vue'
|
||
import WaterfallGrid from './components/WaterfallGrid.vue'
|
||
import ContentTabs from './components/ContentTabs.vue'
|
||
import { clearSubStepProgress, shouldShowGuideStartModal } from '@/utils/guideConfig.js'
|
||
import { useBanner } from './composables/useBanner.js'
|
||
import { USE_MOCK_DATA } from './config/mockData.js'
|
||
|
||
// ========== Store & User Info ==========
|
||
const store = useStore()
|
||
const currentUserNickname = computed(() => store.state.user?.userInfo?.nickname || '')
|
||
const currentStarId = ref(uni.getStorageSync('star_id') || null)
|
||
|
||
// ========== UI State ==========
|
||
const activeContentTab = ref('hot')
|
||
const navExpanded = ref(false)
|
||
const showRankingModal = ref(false)
|
||
|
||
// ========== Screen Info ==========
|
||
const screenWidth = ref(375)
|
||
const screenHeight = ref(812)
|
||
|
||
// ========== Composables ==========
|
||
const {
|
||
bannerActivities,
|
||
loadBannerActivities,
|
||
} = useBanner()
|
||
|
||
// banner(216+360rpx) + tab栏(16+80rpx) + 间距(8rpx) ≈ 680rpx
|
||
const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 716))
|
||
|
||
// ========== Handlers ==========
|
||
const handleCardClick = (card) => {
|
||
// WaterfallGrid 组件内部已处理单击跳转和双击点赞
|
||
}
|
||
|
||
const handleActivityClick = (item) => {
|
||
uni.navigateTo({
|
||
url: `/pages/support-activity/index?id=${item.id}`,
|
||
})
|
||
}
|
||
|
||
const handleRankingVisit = (userId) => {
|
||
showRankingModal.value = false
|
||
uni.navigateTo({
|
||
url: `/pages/exhibition/exhibition?target_uid=${userId}`,
|
||
})
|
||
}
|
||
|
||
const handleRankingModalClose = (visible) => {
|
||
showRankingModal.value = visible
|
||
// 使用 componentMode 判断,因为 isActive 可能在 END_GUIDE 后已变为 false
|
||
if (!visible && store.state.guide.componentMode) {
|
||
uni.$emit('guide:closeComponent')
|
||
}
|
||
}
|
||
|
||
const handleTabChange = (newTab) => {
|
||
if (newTab === 0) {
|
||
navExpanded.value = false
|
||
uni.navigateTo({
|
||
url: '/pages/square/square'
|
||
})
|
||
return
|
||
}
|
||
|
||
const routes = [
|
||
'/pages/square/square',
|
||
'/pages/starbook/index',
|
||
'/pages/castlove/mall',
|
||
'/pages/starcity/index',
|
||
'/pages/friends/index'
|
||
]
|
||
|
||
if (newTab >= 0 && newTab < routes.length) {
|
||
uni.navigateTo({
|
||
url: routes[newTab]
|
||
})
|
||
}
|
||
}
|
||
|
||
// ========== Tile Change Callback ==========
|
||
const handleTileChange = () => {}
|
||
|
||
// ========== Reset Square ==========
|
||
const resetSquare = async () => {}
|
||
|
||
// ========== Watch currentUserNickname ==========
|
||
// (no-op, kept for future use)
|
||
|
||
// ========== Lifecycle ==========
|
||
onMounted(() => {
|
||
const info = uni.getSystemInfoSync()
|
||
screenWidth.value = info.windowWidth
|
||
screenHeight.value = info.windowHeight
|
||
|
||
// 数据加载在后台进行,不阻塞渲染
|
||
resetSquare()
|
||
loadBannerActivities()
|
||
})
|
||
|
||
onShow(() => {
|
||
// 检查是否需要显示引导,如果需要则跳转到引导页面
|
||
if (shouldShowGuideStartModal()) {
|
||
uni.navigateTo({
|
||
url: '/pages/tasks/guide'
|
||
})
|
||
}
|
||
})
|
||
|
||
onLoad((options) => {
|
||
// 调试模式:读取 guide_debug 参数并设置存储
|
||
if (options && 'guide_debug' in options) {
|
||
const debugValue = options.guide_debug
|
||
const isDebug = debugValue === '1' || debugValue === 'true'
|
||
if (isDebug) {
|
||
uni.setStorageSync('guide_debug_mode', true)
|
||
uni.setStorageSync('is_new_user', true)
|
||
console.log('[Guide] 调试模式已开启')
|
||
} else {
|
||
uni.setStorageSync('guide_debug_mode', false)
|
||
uni.removeStorageSync('is_new_user')
|
||
console.log('[Guide] 调试模式已关闭')
|
||
}
|
||
}
|
||
|
||
// 处理引导跳转参数:如果传递了 guide_key,则继续该引导
|
||
if (options && options.guide_key) {
|
||
console.log('[Guide] 收到引导跳转参数, guide_key:', options.guide_key, 'guide_step:', options.guide_step)
|
||
// 使用 resumeGuide 继续引导(不会检查 shouldShowGuide)
|
||
store.dispatch('guide/resumeGuide', options.guide_key).then(res => {
|
||
console.log('[Guide] resumeGuide 结果:', res)
|
||
}).catch(err => {
|
||
console.error('[Guide] resumeGuide 失败:', err)
|
||
})
|
||
}
|
||
})
|
||
|
||
// 监听引导打开组件事件
|
||
uni.$on('guide:openComponent', (componentName) => {
|
||
if (componentName === 'RankingModal') {
|
||
showRankingModal.value = true
|
||
}
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
uni.$off('guide:openComponent')
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.square-container {
|
||
position: relative;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
min-height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* .fall-bg{
|
||
background: rgba(255, 180, 180, 0.25);
|
||
border-radius: 48rpx;
|
||
} */
|
||
|
||
.banner-tabs-wrapper {
|
||
position: fixed;
|
||
top: 216rpx;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 448rpx;
|
||
justify-content: space-between;
|
||
background: rgb(249 159 192 / 45%);;
|
||
/* background: rgba(212, 127, 127, 0.8); */
|
||
border-radius: 48rpx;
|
||
overflow: visible;
|
||
}
|
||
|
||
/* .tabs{
|
||
margin-bottom: 8rpx;
|
||
} */
|
||
|
||
.background-fixed {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
}
|
||
|
||
.nav-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
z-index: 999;
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
</style>
|