This commit is contained in:
@@ -1,137 +0,0 @@
|
||||
<template>
|
||||
<GuidePageFrame
|
||||
active-tab="explain"
|
||||
variant="static"
|
||||
:show-top-tabs="false"
|
||||
@tab-change="handleTopTabChange"
|
||||
@back="handleBack"
|
||||
>
|
||||
<view class="explain-business-unit-page">
|
||||
<ExplainHallSelect
|
||||
:business-units="explainBusinessUnitItems"
|
||||
stage="unit"
|
||||
:selected-hall-name="selectedExplainHallName"
|
||||
:loading="explainLoading"
|
||||
:error="explainError"
|
||||
@business-unit-click="handleExplainBusinessUnitClick"
|
||||
@back="handleBack"
|
||||
/>
|
||||
</view>
|
||||
</GuidePageFrame>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import ExplainHallSelect, {
|
||||
type ExplainBusinessUnitSelectItem
|
||||
} from '@/components/explain/ExplainHallSelect.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import type {
|
||||
ExplainBusinessUnit
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
|
||||
const selectedExplainHallId = ref('')
|
||||
const selectedExplainHallName = ref('')
|
||||
const explainBusinessUnits = ref<ExplainBusinessUnit[]>([])
|
||||
const explainLoading = ref(false)
|
||||
const explainError = ref('')
|
||||
|
||||
const explainBusinessUnitItems = computed<ExplainBusinessUnitSelectItem[]>(() => (
|
||||
explainBusinessUnits.value.map((unit) => ({
|
||||
id: unit.id,
|
||||
name: unit.name,
|
||||
hallId: unit.hallId,
|
||||
previewImageUrl: unit.previewImageUrl,
|
||||
guideStopCount: unit.guideStopCount
|
||||
}))
|
||||
))
|
||||
|
||||
const syncPageTitle = (title: string) => {
|
||||
uni.setNavigationBarTitle({ title })
|
||||
if (typeof document !== 'undefined') document.title = title
|
||||
}
|
||||
|
||||
onLoad(async (options: any = {}) => {
|
||||
const hallId = Array.isArray(options.hallId) ? options.hallId[0] : options.hallId
|
||||
const hallName = Array.isArray(options.hallName) ? options.hallName[0] : options.hallName
|
||||
|
||||
selectedExplainHallId.value = hallId || ''
|
||||
selectedExplainHallName.value = hallName || '业务单元'
|
||||
syncPageTitle(selectedExplainHallName.value)
|
||||
|
||||
if (!selectedExplainHallId.value) {
|
||||
explainError.value = '缺少展厅参数,请返回讲解列表后重试'
|
||||
return
|
||||
}
|
||||
|
||||
explainLoading.value = true
|
||||
try {
|
||||
explainBusinessUnits.value = await explainUseCase.loadTemporaryBusinessUnitsByHall(selectedExplainHallId.value)
|
||||
} catch (error) {
|
||||
explainError.value = '展厅讲解点加载失败,请稍后重试'
|
||||
} finally {
|
||||
explainLoading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const handleExplainBusinessUnitClick = (unitId: string) => {
|
||||
const unit = explainBusinessUnits.value.find((item) => item.id === unitId)
|
||||
const params = new URLSearchParams({
|
||||
hallId: selectedExplainHallId.value,
|
||||
hallName: selectedExplainHallName.value,
|
||||
unitId,
|
||||
unitName: unit?.name || '讲解点'
|
||||
})
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/explain/guide-stop-list?${params.toString()}`
|
||||
})
|
||||
}
|
||||
|
||||
const goBackToExplainHallList = () => {
|
||||
uni.redirectTo({
|
||||
url: '/pages/explain/list',
|
||||
fail: () => {
|
||||
uni.reLaunch({ url: '/pages/explain/list' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
const pages = getCurrentPages()
|
||||
const previousPage = pages.length >= 2 ? pages[pages.length - 2] : null
|
||||
|
||||
if (
|
||||
previousPage?.route === 'pages/explain/list'
|
||||
|| previousPage?.route === 'pages/index/index'
|
||||
) {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
fail: goBackToExplainHallList
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
goBackToExplainHallList()
|
||||
}
|
||||
|
||||
const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
navigateToGuideTopTab(tab)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.explain-business-unit-page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #edf0ea;
|
||||
}
|
||||
</style>
|
||||
@@ -1,19 +1,18 @@
|
||||
<template>
|
||||
<GuidePageFrame
|
||||
active-tab="explain"
|
||||
variant="static"
|
||||
:show-top-tabs="false"
|
||||
@tab-change="handleTopTabChange"
|
||||
@back="handleBack"
|
||||
>
|
||||
<GuidePageFrame active-tab="explain" variant="static" :show-top-tabs="false" @tab-change="handleTopTabChange" @back="handleBack">
|
||||
<view class="explain-guide-stop-page">
|
||||
<ExplainHallSelect
|
||||
:guide-stops="explainGuideStopItems"
|
||||
stage="stop"
|
||||
:selected-business-unit-name="selectedExplainBusinessUnitName"
|
||||
:selected-hall-name="selectedExplainHallName"
|
||||
:loading="explainLoading"
|
||||
:loading-more="loadingMore"
|
||||
:has-more="hasMore"
|
||||
:error="explainError"
|
||||
:load-more-error="loadMoreError"
|
||||
@guide-stop-click="handleExplainGuideStopClick"
|
||||
@retry="reloadFirstPage"
|
||||
@retry-more="loadNextPage"
|
||||
@back="handleBack"
|
||||
/>
|
||||
</view>
|
||||
@@ -21,147 +20,158 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onUnmounted, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import ExplainHallSelect, {
|
||||
type ExplainGuideStopSelectItem
|
||||
} from '@/components/explain/ExplainHallSelect.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import type {
|
||||
ExplainGuideStop
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
normalizeExplainDetailTargetFromGuideStop
|
||||
} from '@/domain/explainDetailTarget'
|
||||
import {
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
import ExplainHallSelect, { type ExplainGuideStopSelectItem } from '@/components/explain/ExplainHallSelect.vue'
|
||||
import { explainUseCase } from '@/usecases/explainUseCase'
|
||||
import type { ExplainGuideStop } from '@/domain/museum'
|
||||
import { normalizeExplainDetailTargetFromGuideStop } from '@/domain/explainDetailTarget'
|
||||
import { navigateToGuideTopTab, type GuideTopTab } from '@/utils/guideTopTabs'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
const selectedExplainHallId = ref('')
|
||||
const selectedExplainHallName = ref('')
|
||||
const selectedExplainBusinessUnitId = ref('')
|
||||
const selectedExplainBusinessUnitName = ref('')
|
||||
const explainGuideStopItems = ref<ExplainGuideStopSelectItem[]>([])
|
||||
const explainLoading = ref(false)
|
||||
const loadingMore = ref(false)
|
||||
const explainError = ref('')
|
||||
const loadMoreError = ref('')
|
||||
const hasMore = ref(false)
|
||||
const nextPageNo = ref(1)
|
||||
let requestVersion = 0
|
||||
let disposed = false
|
||||
|
||||
const toGuideStopItems = (stops: ExplainGuideStop[]): ExplainGuideStopSelectItem[] => (
|
||||
stops.map((stop) => ({
|
||||
id: stop.id,
|
||||
name: stop.name,
|
||||
hallId: stop.hallId,
|
||||
hallName: stop.hallName,
|
||||
floorId: stop.floorId,
|
||||
poiId: stop.poiId,
|
||||
mapX: stop.mapX,
|
||||
mapY: stop.mapY,
|
||||
coverImageUrl: stop.coverImageUrl,
|
||||
description: stop.description,
|
||||
hasAudio: stop.hasAudio,
|
||||
audioStatus: stop.audioStatus,
|
||||
guideLevel: stop.guideLevel,
|
||||
playTargetType: stop.playTargetType,
|
||||
playTargetId: stop.playTargetId
|
||||
}))
|
||||
)
|
||||
const toGuideStopItems = (stops: ExplainGuideStop[]): ExplainGuideStopSelectItem[] => stops.map((stop) => ({
|
||||
id: stop.id,
|
||||
name: stop.name,
|
||||
hallId: stop.hallId,
|
||||
hallName: stop.hallName,
|
||||
floorId: stop.floorId,
|
||||
poiId: stop.poiId,
|
||||
mapX: stop.mapX,
|
||||
mapY: stop.mapY,
|
||||
coverImageUrl: stop.imageStatus === 'MISSING' ? undefined : stop.coverImageUrl,
|
||||
description: stop.description,
|
||||
hasAudio: stop.hasAudio,
|
||||
audioStatus: stop.audioStatus,
|
||||
guideLevel: stop.guideLevel,
|
||||
playTargetType: stop.playTargetType,
|
||||
playTargetId: stop.playTargetId
|
||||
}))
|
||||
|
||||
const syncPageTitle = (title: string) => {
|
||||
uni.setNavigationBarTitle({ title })
|
||||
if (typeof document !== 'undefined') document.title = title
|
||||
}
|
||||
|
||||
onLoad(async (options: any = {}) => {
|
||||
selectedExplainHallId.value = Array.isArray(options.hallId) ? options.hallId[0] : options.hallId || ''
|
||||
selectedExplainHallName.value = Array.isArray(options.hallName) ? options.hallName[0] : options.hallName || ''
|
||||
selectedExplainBusinessUnitId.value = Array.isArray(options.unitId) ? options.unitId[0] : options.unitId || ''
|
||||
selectedExplainBusinessUnitName.value = Array.isArray(options.unitName) ? options.unitName[0] : options.unitName || '讲解点'
|
||||
const loadPage = async (pageNo: number, replace: boolean) => {
|
||||
const hallId = selectedExplainHallId.value
|
||||
if (!hallId || disposed || (!replace && (loadingMore.value || !hasMore.value))) return
|
||||
const version = ++requestVersion
|
||||
if (replace) {
|
||||
explainLoading.value = true
|
||||
explainError.value = ''
|
||||
loadMoreError.value = ''
|
||||
} else {
|
||||
loadingMore.value = true
|
||||
loadMoreError.value = ''
|
||||
}
|
||||
try {
|
||||
const page = await explainUseCase.listGuideStopsPageByHall(hallId, pageNo, PAGE_SIZE)
|
||||
if (disposed || version !== requestVersion || hallId !== selectedExplainHallId.value) return
|
||||
const incoming = toGuideStopItems(page.items)
|
||||
if (replace) {
|
||||
explainGuideStopItems.value = incoming
|
||||
} else {
|
||||
const existingIds = new Set(explainGuideStopItems.value.map((item) => item.id))
|
||||
explainGuideStopItems.value = [...explainGuideStopItems.value, ...incoming.filter((item) => !existingIds.has(item.id))]
|
||||
}
|
||||
nextPageNo.value = page.pageNo + 1
|
||||
hasMore.value = page.hasMore && explainGuideStopItems.value.length < page.total
|
||||
} catch (error) {
|
||||
if (disposed || version !== requestVersion) return
|
||||
if (replace) explainError.value = '讲解对象加载失败,请稍后重试'
|
||||
else loadMoreError.value = '加载更多讲解对象失败,请重试'
|
||||
} finally {
|
||||
if (!disposed && version === requestVersion) {
|
||||
explainLoading.value = false
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syncPageTitle(selectedExplainBusinessUnitName.value)
|
||||
const reloadFirstPage = () => {
|
||||
if (!selectedExplainHallId.value) return
|
||||
nextPageNo.value = 1
|
||||
hasMore.value = false
|
||||
void loadPage(1, true)
|
||||
}
|
||||
|
||||
if (!selectedExplainHallId.value || !selectedExplainBusinessUnitId.value) {
|
||||
explainError.value = '缺少业务单元参数,请返回上一级后重试'
|
||||
const loadNextPage = () => void loadPage(nextPageNo.value, false)
|
||||
|
||||
onLoad((options: Record<string, string | string[] | undefined> = {}) => {
|
||||
const first = (value: string | string[] | undefined) => Array.isArray(value) ? value[0] : value
|
||||
selectedExplainHallId.value = first(options.hallId) || ''
|
||||
selectedExplainHallName.value = first(options.hallName) || '讲解对象'
|
||||
syncPageTitle('讲解对象')
|
||||
if (!selectedExplainHallId.value) {
|
||||
explainError.value = '缺少展厅参数,请返回展厅列表后重试'
|
||||
return
|
||||
}
|
||||
reloadFirstPage()
|
||||
})
|
||||
|
||||
explainLoading.value = true
|
||||
try {
|
||||
const unit = await explainUseCase.selectBusinessUnit(
|
||||
selectedExplainHallId.value,
|
||||
selectedExplainBusinessUnitId.value
|
||||
)
|
||||
if (!unit) {
|
||||
explainError.value = '未找到对应业务单元,请返回上一级后重试'
|
||||
return
|
||||
}
|
||||
selectedExplainBusinessUnitName.value = unit.name || selectedExplainBusinessUnitName.value
|
||||
syncPageTitle(selectedExplainBusinessUnitName.value)
|
||||
const stops = await explainUseCase.listGuideStopsByBusinessUnit(
|
||||
selectedExplainHallId.value,
|
||||
selectedExplainBusinessUnitId.value
|
||||
)
|
||||
explainGuideStopItems.value = toGuideStopItems(stops)
|
||||
} catch (error) {
|
||||
explainError.value = '讲解点加载失败,请稍后重试'
|
||||
} finally {
|
||||
explainLoading.value = false
|
||||
}
|
||||
onUnmounted(() => {
|
||||
disposed = true
|
||||
requestVersion += 1
|
||||
})
|
||||
|
||||
const handleExplainGuideStopClick = (stop: ExplainGuideStopSelectItem) => {
|
||||
const target = stop.playTargetType && stop.playTargetId
|
||||
? { targetType: stop.playTargetType, targetId: stop.playTargetId }
|
||||
: normalizeExplainDetailTargetFromGuideStop({ id: stop.id })
|
||||
|
||||
const params = new URLSearchParams({
|
||||
id: stop.id,
|
||||
tab: 'explain',
|
||||
targetType: target.targetType,
|
||||
targetId: target.targetId
|
||||
})
|
||||
const params = new URLSearchParams({ id: stop.id, tab: 'explain', targetType: target.targetType, targetId: target.targetId })
|
||||
if (stop.hallId) params.set('hallId', stop.hallId)
|
||||
if (stop.hallName) params.set('hallName', stop.hallName)
|
||||
if (stop.floorId) params.set('floorId', stop.floorId)
|
||||
if (stop.poiId) params.set('poiId', stop.poiId)
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/exhibit/detail?${params.toString()}`
|
||||
})
|
||||
uni.navigateTo({ url: `/pages/exhibit/detail?${params.toString()}` })
|
||||
}
|
||||
|
||||
const goBackToBusinessUnitList = () => {
|
||||
const params = new URLSearchParams({
|
||||
hallId: selectedExplainHallId.value,
|
||||
hallName: selectedExplainHallName.value
|
||||
})
|
||||
const fallbackToHallList = () => uni.reLaunch({ url: '/pages/explain/list' })
|
||||
|
||||
uni.redirectTo({
|
||||
url: `/pages/explain/business-unit-list?${params.toString()}`,
|
||||
fail: () => {
|
||||
uni.reLaunch({ url: `/pages/explain/business-unit-list?${params.toString()}` })
|
||||
}
|
||||
})
|
||||
type ExplainPageStackEntry = {
|
||||
route?: string
|
||||
options?: {
|
||||
tab?: string | string[]
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
fail: goBackToBusinessUnitList
|
||||
})
|
||||
const returnToHallList = () => {
|
||||
const pages = getCurrentPages()
|
||||
const previousPage = pages[pages.length - 2] as ExplainPageStackEntry | undefined
|
||||
const previousRoute = previousPage?.route
|
||||
const previousTab = Array.isArray(previousPage?.options?.tab)
|
||||
? previousPage.options.tab[0]
|
||||
: previousPage?.options?.tab
|
||||
const canNavigateBackToHallList = (
|
||||
previousRoute === 'pages/explain/list'
|
||||
|| (previousRoute === 'pages/index/index' && previousTab === 'explain')
|
||||
)
|
||||
|
||||
if (canNavigateBackToHallList) {
|
||||
uni.navigateBack({ delta: 1, fail: fallbackToHallList })
|
||||
return
|
||||
}
|
||||
|
||||
fallbackToHallList()
|
||||
}
|
||||
|
||||
const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
navigateToGuideTopTab(tab)
|
||||
}
|
||||
const handleBack = returnToHallList
|
||||
const handleTopTabChange = (tab: GuideTopTab) => navigateToGuideTopTab(tab)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.explain-guide-stop-page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #edf0ea;
|
||||
}
|
||||
.explain-guide-stop-page { position: absolute; inset: 0; background: #edf0ea; }
|
||||
</style>
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
|
||||
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
||||
|
||||
const explainHallItems = ref<ExplainHallSelectItem[]>([])
|
||||
const explainLoading = ref(false)
|
||||
@@ -56,7 +57,6 @@ const buildExplainHallItems = (
|
||||
floorLabel: hall.floorLabel,
|
||||
image: hall.image,
|
||||
explainCount: hall.exhibitCount || 0,
|
||||
businessUnitCount: summaries[hall.id]?.businessUnitCount,
|
||||
guideStopCount: summaries[hall.id]?.guideStopCount,
|
||||
searchText: normalizeExplainKeyword([
|
||||
hall.name,
|
||||
@@ -225,13 +225,8 @@ const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
|
||||
const handleExplainHallClick = (hallId: string) => {
|
||||
const hall = explainHallItems.value.find((item) => item.id === hallId)
|
||||
const params = new URLSearchParams({
|
||||
hallId,
|
||||
hallName: hall?.name || '讲解'
|
||||
})
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/explain/business-unit-list?${params.toString()}`
|
||||
url: explainGuideStopListUrl(hallId, hall?.name || '讲解')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -302,6 +302,7 @@ import type {
|
||||
import ExplainHallSelect, {
|
||||
type ExplainHallSelectItem
|
||||
} from '@/components/explain/ExplainHallSelect.vue'
|
||||
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
||||
import {
|
||||
getLastGuideLocationPreviewUrl,
|
||||
isGuideTopTab,
|
||||
@@ -990,7 +991,6 @@ const buildExplainHallItems = (
|
||||
floorLabel: hall.floorLabel,
|
||||
image: hall.image,
|
||||
explainCount: hall.exhibitCount || 0,
|
||||
businessUnitCount: summaries[hall.id]?.businessUnitCount,
|
||||
guideStopCount: summaries[hall.id]?.guideStopCount,
|
||||
searchText: normalizeExplainKeyword(keywordParts.join(' '))
|
||||
}
|
||||
@@ -1725,13 +1725,8 @@ const guideSearchText = computed(() => {
|
||||
// 讲解页面处理
|
||||
const handleExplainHallClick = async (hallId: string) => {
|
||||
const hall = explainHallItems.value.find((item) => item.id === hallId)
|
||||
const params = new URLSearchParams({
|
||||
hallId,
|
||||
hallName: hall?.name || '讲解'
|
||||
})
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/explain/business-unit-list?${params.toString()}`
|
||||
url: explainGuideStopListUrl(hallId, hall?.name || '讲解')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user