提交室内导览交互与讲解优化
This commit is contained in:
@@ -59,7 +59,7 @@
|
||||
</view>
|
||||
|
||||
<view v-if="exhibit.audio.status !== 'playable'" class="audio-note">
|
||||
<text class="audio-note-title">音频待开放</text>
|
||||
<text class="audio-note-title">音频待同步</text>
|
||||
<text class="audio-note-desc">
|
||||
{{ exhibit.audio.unavailableReason || '当前仅提供图文讲解,正式音频接入后将显示播放入口。' }}
|
||||
</text>
|
||||
@@ -154,6 +154,9 @@ import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import {
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
toExplainDetailPageViewModel,
|
||||
type ExplainContentType,
|
||||
@@ -171,11 +174,11 @@ const defaultDetail: ExplainDetailPageViewModel = {
|
||||
subtitle: '位置待补充',
|
||||
contentType: 'exhibit',
|
||||
coverImages: ['/static/exhibit-placeholder.jpg'],
|
||||
summary: '该讲解内容待正式内容库补充。',
|
||||
body: '该讲解内容待正式内容库补充。',
|
||||
summary: '该讲解内容待 SGS 场景设置或 CMS 内容补充。',
|
||||
body: '该讲解内容待 SGS 场景设置或 CMS 内容补充。',
|
||||
audio: {
|
||||
status: 'unavailable',
|
||||
unavailableReason: '正式讲解音频尚未接入媒体仓储'
|
||||
unavailableReason: 'SGS 场景设置音频资源尚未同步到当前 H5 项目'
|
||||
},
|
||||
chapters: [],
|
||||
relatedItems: []
|
||||
@@ -194,14 +197,14 @@ const audioStatusText = computed(() => {
|
||||
if (exhibit.value.audio.status === 'none') {
|
||||
return '图文讲解'
|
||||
}
|
||||
return '音频待开放'
|
||||
return exhibit.value.audio.unavailableReason?.includes('包含音频地址') ? '音频待同步' : '图文讲解'
|
||||
})
|
||||
|
||||
const contentTypeText = computed(() => {
|
||||
const map: Record<ExplainContentType, string> = {
|
||||
hall: '展厅讲解',
|
||||
hall: '空间讲解',
|
||||
zone: '展区讲解',
|
||||
exhibit: '讲解点',
|
||||
exhibit: '展品讲解',
|
||||
theme: '主题讲解'
|
||||
}
|
||||
return map[exhibit.value.contentType]
|
||||
@@ -243,7 +246,7 @@ const handlePlayAudio = async () => {
|
||||
isPlaying.value = !isPlaying.value
|
||||
}
|
||||
|
||||
const handleNavigate = () => {
|
||||
const handleNavigate = async () => {
|
||||
if (!exhibit.value.location?.poiId) {
|
||||
uni.showToast({
|
||||
title: '该讲解暂无三维位置数据',
|
||||
@@ -252,6 +255,15 @@ const handleNavigate = () => {
|
||||
return
|
||||
}
|
||||
|
||||
const matchedPoi = await guideUseCase.getPoiById(exhibit.value.location.poiId)
|
||||
if (!matchedPoi) {
|
||||
uni.showToast({
|
||||
title: '该讲解位置尚未映射到当前三维导览资源',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/route/detail?facilityId=${encodeURIComponent(exhibit.value.location.poiId)}&target=${encodeURIComponent(exhibit.value.title)}&state=preview`
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<view class="stats-row">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ hall.exhibitCount }}</text>
|
||||
<text class="stat-label">讲解点</text>
|
||||
<text class="stat-label">讲解内容</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ hall.floorLabel }}</text>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<!-- 讲解列表 -->
|
||||
<view class="exhibits-section">
|
||||
<text class="section-title">展厅讲解内容</text>
|
||||
<text class="section-title">空间讲解内容</text>
|
||||
<view class="exhibits-grid">
|
||||
<ExhibitCard
|
||||
v-for="exhibit in exhibits"
|
||||
@@ -63,6 +63,9 @@ import ExhibitCard from '@/components/content/ExhibitCard.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import {
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
toExplainExhibitViewModel,
|
||||
toHallDetailViewModel,
|
||||
@@ -81,7 +84,7 @@ const hall = ref<HallDetailViewModel>({
|
||||
id: '',
|
||||
name: '展厅内容',
|
||||
floorLabel: '楼层待补充',
|
||||
description: '该展厅介绍待正式内容库补充。',
|
||||
description: '该空间介绍待 SGS 场景设置或 CMS 内容补充。',
|
||||
image: '/static/hall-placeholder.jpg',
|
||||
exhibitCount: 0,
|
||||
area: '待补充'
|
||||
@@ -111,7 +114,7 @@ const handleExhibitClick = (exhibit: any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleNavigate = () => {
|
||||
const handleNavigate = async () => {
|
||||
if (!hall.value.poiId) {
|
||||
uni.showToast({
|
||||
title: '该展厅暂无三维位置数据',
|
||||
@@ -120,6 +123,15 @@ const handleNavigate = () => {
|
||||
return
|
||||
}
|
||||
|
||||
const matchedPoi = await guideUseCase.getPoiById(hall.value.poiId)
|
||||
if (!matchedPoi) {
|
||||
uni.showToast({
|
||||
title: '该空间位置尚未映射到当前三维导览资源',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/route/detail?facilityId=${encodeURIComponent(hall.value.poiId)}&target=${encodeURIComponent(hall.value.name)}&state=preview`
|
||||
})
|
||||
|
||||
@@ -21,16 +21,21 @@
|
||||
:normalize-floor-id="normalizeGuideFloorId"
|
||||
indoor-initial-view="overview"
|
||||
:indoor-view="indoorView"
|
||||
:layer-mode="indoorLayerMode"
|
||||
:active-floor="activeGuideFloor"
|
||||
indoor-view-toggle-top="154px"
|
||||
layer-mode-toggle-top="154px"
|
||||
floor-top="244px"
|
||||
:show-indoor-view-toggle="is3DMode"
|
||||
:show-floor="is3DMode && indoorView === 'floor'"
|
||||
:tools="[]"
|
||||
:show-layer-mode-toggle="is3DMode"
|
||||
:show-floor="is3DMode"
|
||||
:tools="guide3DTools"
|
||||
@search-tap="handleGuideSearchTap"
|
||||
@mode-change="handleModeChange"
|
||||
@floor-change="handleFloorChange"
|
||||
@indoor-view-change="handleIndoorViewChange"
|
||||
@poi-click="handleGuidePoiClick"
|
||||
@selection-clear="handleGuideSelectionClear"
|
||||
@tool-click="handleGuideToolClick"
|
||||
@auto-switch="handleAutoSwitch"
|
||||
>
|
||||
<template #overlay>
|
||||
<view v-if="guideOutdoorState === 'entrance' && !is3DMode" class="entrance-tip">
|
||||
@@ -135,15 +140,22 @@ import {
|
||||
import {
|
||||
toExplainCardViewModel
|
||||
} from '@/view-models/explainViewModels'
|
||||
import type {
|
||||
GuideRenderPoi
|
||||
} from '@/domain/guideModel'
|
||||
import type {
|
||||
MuseumFloor
|
||||
} from '@/domain/museum'
|
||||
|
||||
type GuideIndoorView = 'overview' | 'floor' | 'multi'
|
||||
type LayerDisplayMode = 'single' | 'multi'
|
||||
|
||||
// 3D 模式状态
|
||||
const is3DMode = ref(false)
|
||||
const guideOutdoorState = ref<'home' | 'entrance'>('home')
|
||||
const indoorView = ref<'overview' | 'floor'>('overview')
|
||||
const indoorView = ref<GuideIndoorView>('overview')
|
||||
const activeGuideFloor = ref('1F')
|
||||
const selectedGuidePoi = ref<GuideRenderPoi | null>(null)
|
||||
|
||||
// 状态
|
||||
const searchKeyword = ref('')
|
||||
@@ -157,6 +169,16 @@ const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
|
||||
const indoorModelSource = guideUseCase.getModelSource()
|
||||
const guideFloors = ref<MuseumFloor[]>([])
|
||||
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
|
||||
const indoorLayerMode = computed<LayerDisplayMode>(() => (
|
||||
indoorView.value === 'multi' ? 'multi' : 'single'
|
||||
))
|
||||
const guide3DTools = computed(() => {
|
||||
if (!is3DMode.value) return []
|
||||
|
||||
return selectedGuidePoi.value
|
||||
? ['重置', '俯视', '斜视', '清除']
|
||||
: ['重置', '俯视', '斜视']
|
||||
})
|
||||
|
||||
const shortcutItems = [
|
||||
{ id: 'toilet', name: '卫生间', abbr: '卫' },
|
||||
@@ -351,11 +373,40 @@ const handleCollectMarker = (marker: MarkerDetail) => {
|
||||
const handleFloorChange = (floor: string) => {
|
||||
activeGuideFloor.value = floor
|
||||
indoorView.value = 'floor'
|
||||
selectedGuidePoi.value = null
|
||||
console.log('切换楼层:', floor)
|
||||
}
|
||||
|
||||
const handleIndoorViewChange = (view: 'overview' | 'floor') => {
|
||||
const handleIndoorViewChange = (view: GuideIndoorView) => {
|
||||
indoorView.value = view
|
||||
if (view !== 'floor') {
|
||||
selectedGuidePoi.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' | 'floor'; trigger: string; distance: number }) => {
|
||||
console.log('自动切换视图:', event)
|
||||
indoorView.value = event.to
|
||||
if (event.to !== 'floor') {
|
||||
selectedGuidePoi.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleGuidePoiClick = (poi: GuideRenderPoi) => {
|
||||
selectedGuidePoi.value = poi
|
||||
indoorView.value = 'floor'
|
||||
const matchedFloor = guideFloors.value.find((floor) => floor.id === poi.floorId)
|
||||
activeGuideFloor.value = matchedFloor?.label || poi.floorId
|
||||
}
|
||||
|
||||
const handleGuideSelectionClear = () => {
|
||||
selectedGuidePoi.value = null
|
||||
}
|
||||
|
||||
const handleGuideToolClick = (tool: string) => {
|
||||
if (tool === '清除') {
|
||||
selectedGuidePoi.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 进入 3D 室内模式
|
||||
@@ -364,6 +415,7 @@ const handleEnter3DMode = () => {
|
||||
indoorView.value = 'overview'
|
||||
is3DMode.value = true
|
||||
guideOutdoorState.value = 'home'
|
||||
selectedGuidePoi.value = null
|
||||
}
|
||||
|
||||
const handleModeChange = (mode: '2d' | '3d') => {
|
||||
@@ -372,6 +424,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
||||
if (mode === '2d') {
|
||||
indoorView.value = 'overview'
|
||||
guideOutdoorState.value = 'home'
|
||||
selectedGuidePoi.value = null
|
||||
} else {
|
||||
indoorView.value = 'overview'
|
||||
}
|
||||
@@ -379,7 +432,9 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
||||
|
||||
const guideStatusLabel = computed(() => {
|
||||
if (is3DMode.value) {
|
||||
return indoorView.value === 'overview' ? '室内全馆' : '室内楼层'
|
||||
if (indoorView.value === 'overview') return '建筑外观'
|
||||
if (indoorView.value === 'multi') return '室内多层'
|
||||
return '室内单层'
|
||||
}
|
||||
|
||||
if (guideOutdoorState.value === 'entrance') {
|
||||
@@ -391,16 +446,26 @@ const guideStatusLabel = computed(() => {
|
||||
|
||||
const guideTaskTitle = computed(() => {
|
||||
if (is3DMode.value) {
|
||||
return indoorView.value === 'overview' ? '查看完整建筑' : '查看楼层位置'
|
||||
if (selectedGuidePoi.value) return selectedGuidePoi.value.name
|
||||
if (indoorView.value === 'overview') return '查看建筑外观'
|
||||
if (indoorView.value === 'multi') return '查看多层关系'
|
||||
return '查看单层位置'
|
||||
}
|
||||
return '从室外进入馆内'
|
||||
})
|
||||
|
||||
const guideTaskSubtitle = computed(() => {
|
||||
if (is3DMode.value) {
|
||||
return indoorView.value === 'overview'
|
||||
? '切换到楼层后,可查看 POI 与三维位置预览'
|
||||
: '选择目标设施后,可查看楼层与三维位置预览'
|
||||
if (selectedGuidePoi.value) {
|
||||
return `${activeGuideFloor.value} · ${selectedGuidePoi.value.primaryCategoryZh || '位置预览'} · 已高亮`
|
||||
}
|
||||
if (indoorView.value === 'overview') {
|
||||
return '放大模型可自动进入当前楼层'
|
||||
}
|
||||
if (indoorView.value === 'multi') {
|
||||
return '用于比较楼层关系,选择具体楼层可回到单层'
|
||||
}
|
||||
return '缩小模型可自动回到建筑外观,也可切换多层展示'
|
||||
}
|
||||
|
||||
return ''
|
||||
@@ -408,6 +473,7 @@ const guideTaskSubtitle = computed(() => {
|
||||
|
||||
const guidePrimaryAction = computed(() => {
|
||||
if (is3DMode.value) {
|
||||
if (selectedGuidePoi.value) return '查看相关内容'
|
||||
return '选择目标地点'
|
||||
}
|
||||
|
||||
@@ -416,6 +482,13 @@ const guidePrimaryAction = computed(() => {
|
||||
|
||||
const handleGuidePrimaryAction = () => {
|
||||
if (is3DMode.value) {
|
||||
if (selectedGuidePoi.value) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/search/index?keyword=${encodeURIComponent(selectedGuidePoi.value.name)}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: '/pages/search/index'
|
||||
})
|
||||
@@ -507,7 +580,7 @@ const handleExplainFeaturedClick = async (exhibit: Exhibit) => {
|
||||
await handleAudioClick(exhibit)
|
||||
}
|
||||
|
||||
const handleExplainLocationClick = (exhibit: Exhibit) => {
|
||||
const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
||||
if (!exhibit.poiId) {
|
||||
uni.showToast({
|
||||
title: '该讲解暂无三维位置数据',
|
||||
@@ -516,6 +589,15 @@ const handleExplainLocationClick = (exhibit: Exhibit) => {
|
||||
return
|
||||
}
|
||||
|
||||
const matchedPoi = await guideUseCase.getPoiById(exhibit.poiId)
|
||||
if (!matchedPoi) {
|
||||
uni.showToast({
|
||||
title: '该讲解位置尚未映射到当前三维导览资源',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/route/detail?facilityId=${encodeURIComponent(exhibit.poiId)}&target=${encodeURIComponent(exhibit.title)}&state=preview`
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user