52
src/view-models/visitorPoiPresentation.ts
Normal file
52
src/view-models/visitorPoiPresentation.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { MuseumPoi } from '@/domain/museum'
|
||||
|
||||
export type VisitorPoiPresentation = Readonly<{
|
||||
displayName: string
|
||||
floorLabel: string
|
||||
locationHint: string
|
||||
}>
|
||||
|
||||
const normalizeDisplayName = (name: string) => {
|
||||
const normalized = name.trim().replace(/\s+/g, ' ')
|
||||
const hallMatch = normalized.match(/^展厅\s*\d+\s*(.+厅)$/)
|
||||
if (hallMatch?.[1]) return hallMatch[1]
|
||||
return normalized.replace(/(卫生间|洗手间)(\d+)$/, '$1 $2')
|
||||
}
|
||||
|
||||
type VisitorPoiSource = Pick<MuseumPoi, 'id' | 'name' | 'floorId' | 'floorLabel' | 'hallName'> & {
|
||||
primaryCategory?: { label?: string }
|
||||
}
|
||||
|
||||
const locationHintFor = (poi: VisitorPoiSource) => [
|
||||
poi.floorLabel?.trim(),
|
||||
poi.hallName?.trim(),
|
||||
poi.primaryCategory?.label?.trim()
|
||||
].filter((value, index, values) => Boolean(value) && values.indexOf(value) === index).join(' · ')
|
||||
|
||||
export const createVisitorPoiPresentations = <TPoi extends VisitorPoiSource>(pois: TPoi[]) => {
|
||||
const baseNames = new Map<string, number>()
|
||||
pois.forEach((poi) => {
|
||||
const name = normalizeDisplayName(poi.name || '') || '未命名点位'
|
||||
baseNames.set(name, (baseNames.get(name) || 0) + 1)
|
||||
})
|
||||
|
||||
const duplicateIndexes = new Map<string, number>()
|
||||
return new Map(pois.map((poi) => {
|
||||
const baseName = normalizeDisplayName(poi.name || '') || '未命名点位'
|
||||
const duplicateCount = baseNames.get(baseName) || 0
|
||||
const nextIndex = (duplicateIndexes.get(baseName) || 0) + 1
|
||||
duplicateIndexes.set(baseName, nextIndex)
|
||||
const locationHint = locationHintFor(poi)
|
||||
const disambiguation = duplicateCount > 1 ? (locationHint || `设施 ${nextIndex}`) : locationHint
|
||||
|
||||
return [poi.id, {
|
||||
displayName: duplicateCount > 1 ? `${baseName} · ${disambiguation}` : baseName,
|
||||
floorLabel: poi.floorLabel || poi.floorId,
|
||||
locationHint: disambiguation
|
||||
} satisfies VisitorPoiPresentation]
|
||||
}))
|
||||
}
|
||||
|
||||
export const presentVisitorPoi = <TPoi extends VisitorPoiSource>(poi: TPoi): VisitorPoiPresentation => (
|
||||
createVisitorPoiPresentations([poi]).get(poi.id)!
|
||||
)
|
||||
Reference in New Issue
Block a user