feat: switch explain flow to API data
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
lyf
2026-07-06 23:18:51 +08:00
parent e2b6a331ba
commit 50902eca82
21 changed files with 1417 additions and 632 deletions

View File

@@ -24,9 +24,14 @@
<text class="hero-placeholder-text">讲解</text>
</view>
<view class="detail-body">
<text class="detail-title">{{ exhibit.title }}</text>
<text v-if="detailMeta" class="detail-meta">{{ detailMeta }}</text>
<view class="detail-body">
<text class="detail-title">{{ exhibit.title }}</text>
<text v-if="detailMeta" class="detail-meta">{{ detailMeta }}</text>
<view v-if="exhibit.linkedExhibitStatusText" class="detail-chip-row">
<view class="detail-chip" :class="{ shared: exhibit.isSharedStop }">
<text class="detail-chip-text">{{ exhibit.linkedExhibitStatusText }}</text>
</view>
</view>
<view
v-if="exhibit.audio.status === 'playable'"
@@ -328,6 +333,8 @@ const handleAudioError = async (_audio: AudioItem | null, message: string) => {
audioLanguage: exhibit.value.audio.language,
playTargetType: exhibit.value.audio.playTargetType,
playTargetId: exhibit.value.audio.playTargetId
}, {
refreshPlayInfo: true
})
if (selection.playable && selection.media?.url) {
@@ -491,6 +498,46 @@ const handleBack = () => {
color: #555c51;
}
.detail-chip-row {
margin-top: 12px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.detail-chip {
max-width: 100%;
height: 28px;
padding: 0 10px;
display: flex;
align-items: center;
box-sizing: border-box;
background: #f4f6ef;
border: 1px solid #dde2d3;
border-radius: 8px;
}
.detail-chip.shared {
background: #151713;
border-color: #151713;
}
.detail-chip-text {
display: block;
max-width: 100%;
font-size: 13px;
line-height: 18px;
font-weight: 700;
color: #555c51;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.detail-chip.shared .detail-chip-text {
color: #e0df00;
}
.audio-panel {
height: 76px;
margin-top: 24px;

View File

@@ -109,7 +109,9 @@ const explainGuideStopItems = computed<ExplainGuideStopSelectItem[]>(() => (
floorId: stop.floorId,
coverImageUrl: stop.coverImageUrl,
description: stop.description,
hasAudio: stop.hasAudio
hasAudio: stop.hasAudio,
audioStatus: stop.audioStatus,
guideLevel: stop.guideLevel
})) || []
))

View File

@@ -708,7 +708,9 @@ const explainGuideStopItems = computed<ExplainGuideStopSelectItem[]>(() => (
floorId: stop.floorId,
coverImageUrl: stop.coverImageUrl,
description: stop.description,
hasAudio: stop.hasAudio
hasAudio: stop.hasAudio,
audioStatus: stop.audioStatus,
guideLevel: stop.guideLevel
})) || []
))
@@ -995,19 +997,26 @@ const resolveSelectedPoiHall = async (): Promise<MuseumHall | null> => {
if (!poi) return null
const halls = await explainUseCase.listHalls()
const hallId = poi.hallId
if (hallId) {
const matchedById = halls.find((hall) => hall.id === hallId || hall.poiId === hallId)
if (matchedById) return matchedById
}
const matchedByPoiId = halls.find((hall) => (
hall.poiId === poi.id
|| hall.location?.poiId === poi.id
|| (poi.entrances || []).some((entrance) => hall.poiId === entrance.sourcePlaceId)
))
if (matchedByPoiId) return matchedByPoiId
const hallId = poi.hallId
if (hallId) {
const matchedById = halls.find((hall) => hall.id === hallId)
if (matchedById) return matchedById
}
const matchedByEntrance = halls.find((hall) => (
(poi.entrances || []).some((entrance) => (
hall.poiId === entrance.sourcePlaceId
|| hall.poiId === entrance.id
))
))
if (matchedByEntrance) return matchedByEntrance
const poiHallName = normalizeHallMatchText(poi.hallName || poi.name)
return halls.find((hall) => {
const hallName = normalizeHallMatchText(hall.name)
@@ -1932,17 +1941,30 @@ const handleExplainBack = () => {
left: 50%;
right: auto;
width: calc(var(--museum-h5-page-width) - 24px);
bottom: calc(env(safe-area-inset-bottom) + 16px);
padding: 12px;
bottom: calc(env(safe-area-inset-bottom) + 14px);
padding: 10px;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.96);
border: 1px solid rgba(255, 255, 255, 0.96);
border-radius: 16px;
box-shadow: 0 10px 28px rgba(36, 49, 42, 0.16);
overflow: hidden;
background:
linear-gradient(180deg, rgba(224, 225, 0, 0.22) 0, rgba(224, 225, 0, 0) 58px),
#060704;
border: 1px solid rgba(224, 225, 0, 0.34);
border-radius: 8px;
box-shadow: 0 18px 38px rgba(0, 0, 0, 0.34);
transform: translateX(-50%);
z-index: 1003;
}
.guide-home-dock::before {
content: '';
position: absolute;
left: 10px;
right: 10px;
top: 0;
height: 4px;
background: var(--museum-accent);
}
.guide-home-dock.expanded {
position: fixed;
left: 50%;
@@ -1964,10 +1986,14 @@ const handleExplainBack = () => {
transform: translateX(-50%);
overflow: hidden;
overscroll-behavior: contain;
touch-action: pan-y;
touch-action: pan-x pan-y;
z-index: 1100;
}
.guide-home-dock.expanded::before {
display: none;
}
/* #ifdef H5 */
.guide-home-dock.expanded {
min-height: 100vh;
@@ -1988,18 +2014,18 @@ const handleExplainBack = () => {
.guide-quick-actions {
position: absolute;
top: clamp(96px, 14vh, 118px);
right: 18px;
width: 48px;
top: clamp(88px, 13vh, 112px);
right: 14px;
width: 52px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 7px;
z-index: 1002;
}
.guide-quick-action {
width: 48px;
min-height: 56px;
width: 52px;
min-height: 58px;
padding: 7px 4px 6px;
display: flex;
flex-direction: column;
@@ -2007,15 +2033,16 @@ const handleExplainBack = () => {
justify-content: center;
gap: 4px;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.96);
border: 1px solid rgba(230, 233, 224, 0.96);
background: rgba(245, 245, 237, 0.94);
border: 1px solid rgba(255, 255, 255, 0.72);
border-radius: 8px;
box-shadow: 0 8px 22px rgba(36, 49, 42, 0.14);
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
}
.guide-quick-action.primary {
background: #151713;
border-color: #151713;
background: var(--museum-accent);
border-color: var(--museum-accent);
}
.guide-quick-action:active {
@@ -2031,7 +2058,7 @@ const handleExplainBack = () => {
}
.guide-quick-action.primary .guide-quick-icon {
color: var(--museum-accent);
color: #0a0b08;
}
.guide-quick-icon-floor {
@@ -2113,7 +2140,7 @@ const handleExplainBack = () => {
}
.guide-quick-action-text.primary {
color: var(--museum-accent);
color: #0a0b08;
}
.route-sim-top-pill {

View File

@@ -80,16 +80,15 @@ onUnmounted(() => {
<style scoped lang="scss">
.poi-search-page {
--museum-h5-page-width: min(100vw, 430px);
position: relative;
width: 100%;
height: 100vh;
height: 100dvh;
min-height: 100vh;
min-height: 100dvh;
padding: calc(env(safe-area-inset-top) + 28px) 14px calc(env(safe-area-inset-bottom) + 16px);
padding: calc(env(safe-area-inset-top) + 18px) 0 0;
box-sizing: border-box;
background:
linear-gradient(180deg, rgba(224, 225, 0, 0.22) 0, rgba(245, 245, 237, 0) 146px),
#f5f5ed;
background: #f5f5ed;
color: #262421;
font-family: '鸿蒙黑体', 'HarmonyOS Sans SC', 'HarmonyOS Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
display: flex;
@@ -97,6 +96,19 @@ onUnmounted(() => {
overflow: hidden;
}
.poi-search-page::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: 0;
height: 210px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0 64px, rgba(0, 0, 0, 0.18) 65px, rgba(0, 0, 0, 0.5) 100%),
url('/static/guide/app-launch-loading.webp') center top / cover no-repeat;
pointer-events: none;
}
/* #ifdef H5 */
.poi-search-page {
height: var(--poi-search-page-height, 100dvh);
@@ -108,20 +120,29 @@ onUnmounted(() => {
/* #endif */
.search-header {
min-height: 46px;
position: relative;
z-index: 1;
min-height: 48px;
flex: 0 0 auto;
display: flex;
align-items: flex-start;
align-items: center;
justify-content: space-between;
gap: 12px;
margin: 0 2px 16px;
margin: 0 18px 118px;
}
.search-panel-shell {
position: relative;
z-index: 1;
min-height: 0;
flex: 1;
display: flex;
padding: 18px 14px calc(env(safe-area-inset-bottom) + 14px);
box-sizing: border-box;
background: #fffef0;
border-radius: 22px 22px 0 0;
overflow: hidden;
box-shadow: 0 -10px 28px rgba(0, 0, 0, 0.08);
}
.search-header-copy {
@@ -132,31 +153,31 @@ onUnmounted(() => {
}
.page-title {
font-size: 26px;
line-height: 31px;
font-size: 24px;
line-height: 30px;
font-weight: 800;
letter-spacing: 0;
color: #000000;
color: #050704;
}
.page-subtitle {
font-size: 11px;
line-height: 15px;
font-weight: 500;
color: #5d5b50;
color: #4f5149;
white-space: nowrap;
}
.map-link {
min-width: 74px;
height: 32px;
min-width: 76px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10px;
box-sizing: border-box;
background: #000000;
border: 1px solid rgba(0, 0, 0, 0.88);
background: #050704;
border: 1px solid #050704;
border-radius: 8px;
}
@@ -177,13 +198,16 @@ onUnmounted(() => {
@media (max-width: 360px) {
.poi-search-page {
padding-top: calc(env(safe-area-inset-top) + 22px);
padding-left: 12px;
padding-right: 12px;
padding-top: calc(env(safe-area-inset-top) + 14px);
}
.search-header {
margin-bottom: 12px;
margin: 0 14px 108px;
}
.search-panel-shell {
padding-left: 12px;
padding-right: 12px;
}
.page-title {