161 lines
4.3 KiB
TypeScript
161 lines
4.3 KiB
TypeScript
import type {
|
|
MuseumExhibit,
|
|
MuseumHall,
|
|
SearchIndexItem
|
|
} from '@/domain/museum'
|
|
|
|
export type ExplainContentType = 'hall' | 'zone' | 'exhibit' | 'theme'
|
|
export type ExplainAudioStatus = 'playable' | 'unavailable' | 'none'
|
|
|
|
export interface ExplainCardViewModel {
|
|
id: string
|
|
title: string
|
|
subtitle?: string
|
|
summary?: string
|
|
coverImage?: string
|
|
contentType: ExplainContentType
|
|
hallName?: string
|
|
floorLabel?: string
|
|
durationLabel?: string
|
|
languageLabel?: string
|
|
audioStatus: ExplainAudioStatus
|
|
audioStatusText: string
|
|
badges: string[]
|
|
progress?: number
|
|
poiId?: string
|
|
locationActionText?: string
|
|
}
|
|
|
|
export interface ExplainDetailPageViewModel {
|
|
id: string
|
|
title: string
|
|
subtitle?: string
|
|
contentType: ExplainContentType
|
|
coverImages: string[]
|
|
hallName?: string
|
|
floorLabel?: string
|
|
summary: string
|
|
body: string
|
|
audio: {
|
|
status: ExplainAudioStatus
|
|
url?: string
|
|
durationLabel?: string
|
|
language?: string
|
|
unavailableReason?: string
|
|
}
|
|
chapters: Array<{
|
|
id: string
|
|
title: string
|
|
startTime?: number
|
|
}>
|
|
transcript?: string
|
|
location?: {
|
|
poiId: string
|
|
actionText: string
|
|
previewOnly: boolean
|
|
}
|
|
relatedItems: ExplainCardViewModel[]
|
|
}
|
|
|
|
export type ExplainExhibitViewModel = ExplainCardViewModel
|
|
export type ExplainDetailViewModel = ExplainDetailPageViewModel
|
|
|
|
export interface HallDetailViewModel {
|
|
id: string
|
|
name: string
|
|
floorLabel: string
|
|
description: string
|
|
image: string
|
|
exhibitCount: number
|
|
area?: string
|
|
poiId?: string
|
|
}
|
|
|
|
const contentTypeFor = (exhibit: MuseumExhibit): ExplainContentType => {
|
|
if (exhibit.tags?.some((tag) => /主题/.test(tag))) return 'theme'
|
|
if (exhibit.hallName && exhibit.tags?.some((tag) => /展厅/.test(tag))) return 'hall'
|
|
return 'exhibit'
|
|
}
|
|
|
|
const buildSubtitle = (exhibit: MuseumExhibit) => [
|
|
exhibit.hallName,
|
|
exhibit.floorLabel
|
|
].filter(Boolean).join(' · ')
|
|
|
|
const buildBadges = (exhibit: MuseumExhibit) => {
|
|
const badges = [
|
|
exhibit.hallName ? '展厅讲解' : '讲解点',
|
|
exhibit.floorLabel || '',
|
|
...(exhibit.tags || [])
|
|
].filter(Boolean)
|
|
|
|
return Array.from(new Set(badges)).slice(0, 3)
|
|
}
|
|
|
|
export const toExplainCardViewModel = (exhibit: MuseumExhibit): ExplainCardViewModel => ({
|
|
id: exhibit.id,
|
|
title: exhibit.name,
|
|
subtitle: buildSubtitle(exhibit),
|
|
summary: exhibit.description || '讲解内容待正式内容库补充。',
|
|
coverImage: exhibit.image,
|
|
contentType: contentTypeFor(exhibit),
|
|
hallName: exhibit.hallName,
|
|
floorLabel: exhibit.floorLabel,
|
|
durationLabel: undefined,
|
|
languageLabel: undefined,
|
|
audioStatus: 'unavailable',
|
|
audioStatusText: '图文讲解',
|
|
badges: buildBadges(exhibit),
|
|
progress: undefined,
|
|
poiId: exhibit.poiId,
|
|
locationActionText: '查看位置'
|
|
})
|
|
|
|
export const toExplainExhibitViewModel = (exhibit: MuseumExhibit): ExplainExhibitViewModel => toExplainCardViewModel(exhibit)
|
|
|
|
export const toExplainDetailPageViewModel = (exhibit: MuseumExhibit): ExplainDetailPageViewModel => ({
|
|
id: exhibit.id,
|
|
title: exhibit.name,
|
|
subtitle: buildSubtitle(exhibit),
|
|
contentType: contentTypeFor(exhibit),
|
|
coverImages: [exhibit.image || '/static/exhibit-placeholder.jpg'].filter(Boolean),
|
|
hallName: exhibit.hallName,
|
|
floorLabel: exhibit.floorLabel,
|
|
summary: exhibit.description || '该讲解内容待正式内容库补充。',
|
|
body: exhibit.description || '该讲解内容待正式内容库补充。',
|
|
audio: {
|
|
status: 'unavailable',
|
|
unavailableReason: '正式讲解音频尚未接入媒体仓储'
|
|
},
|
|
chapters: [],
|
|
transcript: undefined,
|
|
location: exhibit.poiId
|
|
? {
|
|
poiId: exhibit.poiId,
|
|
actionText: '查看三维位置',
|
|
previewOnly: true
|
|
}
|
|
: undefined,
|
|
relatedItems: []
|
|
})
|
|
|
|
export const toExplainDetailViewModel = (exhibit: MuseumExhibit): ExplainDetailViewModel => toExplainDetailPageViewModel(exhibit)
|
|
|
|
export const toHallDetailViewModel = (hall: MuseumHall): HallDetailViewModel => ({
|
|
id: hall.id,
|
|
name: hall.name,
|
|
floorLabel: hall.floorLabel || '楼层待补充',
|
|
description: hall.description || '该展厅介绍待正式内容库补充。',
|
|
image: hall.image || '/static/hall-placeholder.jpg',
|
|
exhibitCount: hall.exhibitCount || 0,
|
|
area: hall.area,
|
|
poiId: hall.poiId
|
|
})
|
|
|
|
export const toExplainSearchResultViewModel = (item: SearchIndexItem) => ({
|
|
id: item.id,
|
|
name: item.name,
|
|
desc: item.desc,
|
|
type: item.type
|
|
})
|