437 lines
9.2 KiB
Vue
437 lines
9.2 KiB
Vue
<script setup>
|
||
import { ref, onMounted, computed } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { useSceneStore } from '../../src/store/scene'
|
||
import { useCollectionStore } from '../../src/store/collection'
|
||
import LongImageViewer from '../components/LongImageViewer.vue'
|
||
import MediaPlayer from '../components/MediaPlayer.vue'
|
||
|
||
const router = useRouter()
|
||
const sceneStore = useSceneStore()
|
||
const collectionStore = useCollectionStore()
|
||
|
||
// 场景数据
|
||
const sceneData = ref({
|
||
id: 'qianmen',
|
||
name: '前门商圈',
|
||
description: '北京最具历史文化底蕴的商圈之一',
|
||
image: 'https://picsum.photos/id/1015/750/1600', // 长图占位图
|
||
thumbnail: 'https://picsum.photos/id/1015/200/100',
|
||
location: '北京市东城区前门大街',
|
||
history: '前门大街形成于明代,已有600多年历史,是北京最古老的商业街之一。',
|
||
attractions: [
|
||
{ name: '前门大街', description: '北京著名的商业街,汇集众多老字号' },
|
||
{ name: '大栅栏', description: '明清时期的商业中心,保留大量传统建筑' },
|
||
{ name: '天安门广场', description: '世界最大的城市广场,中国的象征' }
|
||
],
|
||
audio: 'https://example.com/audio/qianmen.mp3', // 音频占位符
|
||
video: 'https://example.com/video/qianmen.mp4' // 视频占位符
|
||
})
|
||
|
||
// 交互状态
|
||
const isInteractive = ref(false)
|
||
const sealCollected = ref(false)
|
||
const showInfoPanel = ref(false)
|
||
|
||
// 福印收集状态
|
||
const sealStatus = computed(() => {
|
||
return collectionStore.isSceneSealCollected(sceneData.value.id)
|
||
})
|
||
|
||
// 组件挂载后初始化
|
||
onMounted(() => {
|
||
// 激活当前场景
|
||
sceneStore.activateScene(sceneData.value.id)
|
||
|
||
// 设置当前场景索引
|
||
const sceneIndex = sceneStore.scenes.findIndex(scene => scene.id === sceneData.value.id)
|
||
if (sceneIndex !== -1) {
|
||
sceneStore.setCurrentScene(sceneIndex)
|
||
}
|
||
|
||
// 检查福印是否已收集
|
||
sealCollected.value = sealStatus.value
|
||
})
|
||
|
||
// 点击场景交互区域
|
||
const handleSceneInteraction = () => {
|
||
if (!isInteractive.value) {
|
||
isInteractive.value = true
|
||
|
||
// 模拟交互过程
|
||
setTimeout(() => {
|
||
collectSeal()
|
||
}, 1000)
|
||
}
|
||
}
|
||
|
||
// 收集福印
|
||
const collectSeal = () => {
|
||
if (!sealCollected.value) {
|
||
const success = collectionStore.collectSealBySceneId(sceneData.value.id)
|
||
if (success) {
|
||
sealCollected.value = true
|
||
showToast({
|
||
message: '恭喜获得前门福印!',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
} else {
|
||
showFailToast({
|
||
message: '福印收集失败',
|
||
duration: 2000
|
||
})
|
||
}
|
||
} else {
|
||
showToast({
|
||
message: '福印已收集',
|
||
duration: 1500
|
||
})
|
||
}
|
||
}
|
||
|
||
// 查看场景信息
|
||
const showSceneInfo = () => {
|
||
showInfoPanel.value = true
|
||
}
|
||
|
||
// 关闭信息面板
|
||
const closeInfoPanel = () => {
|
||
showInfoPanel.value = false
|
||
}
|
||
|
||
// 跳转到下一个场景
|
||
const goToNextScene = () => {
|
||
router.push('/chongwen')
|
||
}
|
||
|
||
// 跳转到上一个场景
|
||
const goToPreviousScene = () => {
|
||
Toast('已经是第一个场景了')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="scene-page">
|
||
<!-- 场景标题 -->
|
||
<div class="scene-header">
|
||
<h1 class="scene-title">{{ sceneData.name }}</h1>
|
||
<p class="scene-description">{{ sceneData.description }}</p>
|
||
</div>
|
||
|
||
<!-- 长图查看器 -->
|
||
<div class="long-image-container">
|
||
<LongImageViewer
|
||
:image-url="sceneData.image"
|
||
:description="sceneData.name"
|
||
@click="handleSceneInteraction"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 交互提示 -->
|
||
<div class="interaction-tip" v-if="!isInteractive">
|
||
<div class="tip-icon">👆</div>
|
||
<p>点击图片查看详情</p>
|
||
</div>
|
||
|
||
<!-- 福印收集提示 -->
|
||
<div class="seal-collect-tip" v-if="sealCollected">
|
||
<div class="seal-icon">🏮</div>
|
||
<p>已收集前门福印</p>
|
||
</div>
|
||
|
||
<!-- 场景信息面板 -->
|
||
<div class="info-panel" v-if="showInfoPanel">
|
||
<div class="info-panel-content">
|
||
<div class="info-panel-header">
|
||
<h2>场景信息</h2>
|
||
<button class="close-btn" @click="closeInfoPanel">×</button>
|
||
</div>
|
||
|
||
<div class="info-panel-body">
|
||
<div class="info-item">
|
||
<label>名称:</label>
|
||
<span>{{ sceneData.name }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>位置:</label>
|
||
<span>{{ sceneData.location }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>历史:</label>
|
||
<span>{{ sceneData.history }}</span>
|
||
</div>
|
||
|
||
<div class="attractions-section">
|
||
<h3>主要景点:</h3>
|
||
<ul class="attractions-list">
|
||
<li v-for="(attraction, index) in sceneData.attractions" :key="index">
|
||
<div class="attraction-name">{{ attraction.name }}</div>
|
||
<div class="attraction-description">{{ attraction.description }}</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 媒体播放器 -->
|
||
<div class="media-player-container">
|
||
<MediaPlayer
|
||
:audio-url="sceneData.audio"
|
||
:video-url="sceneData.video"
|
||
:scene-name="sceneData.name"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 场景导航 -->
|
||
<div class="scene-navigation">
|
||
<button class="nav-btn prev-btn" @click="goToPreviousScene">
|
||
上一个
|
||
</button>
|
||
<button class="nav-btn info-btn" @click="showSceneInfo">
|
||
场景信息
|
||
</button>
|
||
<button class="nav-btn next-btn" @click="goToNextScene">
|
||
下一个
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.scene-page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
padding-top: 0.88rem;
|
||
padding-bottom: 1.2rem;
|
||
}
|
||
|
||
.scene-header {
|
||
padding: 0.2rem;
|
||
text-align: center;
|
||
background-color: #fff;
|
||
margin-bottom: 0.1rem;
|
||
}
|
||
|
||
.scene-title {
|
||
font-size: 0.4rem;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 0.1rem;
|
||
}
|
||
|
||
.scene-description {
|
||
font-size: 0.28rem;
|
||
color: #666;
|
||
}
|
||
|
||
.long-image-container {
|
||
width: 100%;
|
||
height: 5rem;
|
||
overflow: hidden;
|
||
background-color: #000;
|
||
position: relative;
|
||
}
|
||
|
||
.interaction-tip {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: rgba(0, 0, 0, 0.7);
|
||
color: #fff;
|
||
padding: 0.3rem 0.5rem;
|
||
border-radius: 0.2rem;
|
||
z-index: 10;
|
||
animation: pulse 1.5s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% {
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
50% {
|
||
transform: translate(-50%, -50%) scale(1.1);
|
||
}
|
||
100% {
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
}
|
||
|
||
.tip-icon {
|
||
font-size: 0.6rem;
|
||
margin-bottom: 0.1rem;
|
||
}
|
||
|
||
.seal-collect-tip {
|
||
position: absolute;
|
||
top: 1rem;
|
||
right: 0.2rem;
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: rgba(255, 107, 53, 0.9);
|
||
color: #fff;
|
||
padding: 0.15rem 0.3rem;
|
||
border-radius: 0.2rem;
|
||
font-size: 0.28rem;
|
||
z-index: 10;
|
||
}
|
||
|
||
.seal-icon {
|
||
font-size: 0.4rem;
|
||
margin-right: 0.1rem;
|
||
}
|
||
|
||
.info-panel {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 100;
|
||
}
|
||
|
||
.info-panel-content {
|
||
width: 90%;
|
||
max-width: 500px;
|
||
background-color: #fff;
|
||
border-radius: 0.2rem;
|
||
overflow: hidden;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.info-panel-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0.3rem;
|
||
background-color: #ff6b35;
|
||
color: #fff;
|
||
}
|
||
|
||
.info-panel-header h2 {
|
||
font-size: 0.36rem;
|
||
margin: 0;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
color: #fff;
|
||
font-size: 0.4rem;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.info-panel-body {
|
||
padding: 0.3rem;
|
||
}
|
||
|
||
.info-item {
|
||
margin-bottom: 0.2rem;
|
||
font-size: 0.28rem;
|
||
}
|
||
|
||
.info-item label {
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.info-item span {
|
||
color: #666;
|
||
}
|
||
|
||
.attractions-section {
|
||
margin-top: 0.3rem;
|
||
}
|
||
|
||
.attractions-section h3 {
|
||
font-size: 0.32rem;
|
||
margin-bottom: 0.2rem;
|
||
color: #333;
|
||
}
|
||
|
||
.attractions-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
.attractions-list li {
|
||
margin-bottom: 0.2rem;
|
||
padding: 0.2rem;
|
||
background-color: #f9f9f9;
|
||
border-radius: 0.1rem;
|
||
}
|
||
|
||
.attraction-name {
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 0.05rem;
|
||
}
|
||
|
||
.attraction-description {
|
||
color: #666;
|
||
font-size: 0.26rem;
|
||
}
|
||
|
||
.media-player-container {
|
||
margin: 0.2rem;
|
||
background-color: #fff;
|
||
border-radius: 0.1rem;
|
||
padding: 0.2rem;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.scene-navigation {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 0.2rem;
|
||
margin-top: 0.2rem;
|
||
}
|
||
|
||
.nav-btn {
|
||
flex: 1;
|
||
padding: 0.2rem;
|
||
margin: 0 0.1rem;
|
||
font-size: 0.3rem;
|
||
border: none;
|
||
border-radius: 0.15rem;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s ease;
|
||
}
|
||
|
||
.prev-btn {
|
||
background-color: #e0e0e0;
|
||
color: #666;
|
||
}
|
||
|
||
.prev-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.info-btn {
|
||
background-color: #4caf50;
|
||
color: #fff;
|
||
}
|
||
|
||
.next-btn {
|
||
background-color: #2196f3;
|
||
color: #fff;
|
||
}
|
||
|
||
.nav-btn:hover:not(:disabled) {
|
||
opacity: 0.8;
|
||
}
|
||
</style> |