chore: solidify guide P1 snapshot

This commit is contained in:
lyf
2026-06-11 16:18:57 +08:00
parent a90f63cef0
commit 9790501c3b
32 changed files with 4613 additions and 865 deletions

View File

@@ -1,61 +1,103 @@
<template>
<GuideMapShell
:search-text="searchKeyword || '搜索设施展厅入口'"
floor-top="164px"
@search-tap="handleSearchTap"
@mode-change="handleModeChange"
@floor-change="handleFloorChange"
<GuidePageFrame
active-tab="guide"
variant="overlay"
show-back
:show-cancel="isSearchEditing"
@tab-change="handleTopTabChange"
@back="handlePageBack"
@cancel="handleSearchCancel"
>
<template #overlay>
<view class="filter-row">
<GuideMapShell
:search-text="searchKeyword || '搜索设施展厅入口'"
search-top="60px"
mode-top="104px"
floor-top="208px"
@search-tap="handleSearchTap"
@mode-change="handleModeChange"
@floor-change="handleFloorChange"
>
<template #overlay>
<view
v-for="filter in filters"
:key="filter.id"
class="filter-chip"
:class="[{ active: currentFilter === filter.id }, `filter-${filter.id}`]"
@tap="handleFilterChange(filter.id)"
v-if="isSearchEditing"
class="search-edit-row"
@tap.stop=""
>
<text class="filter-text">{{ filter.label }}</text>
<view class="search-edit-box">
<input
class="search-edit-input"
type="text"
confirm-type="search"
:focus="searchInputFocused"
:placeholder="searchPlaceholder"
:value="searchDraftKeyword"
@input="handleSearchInput"
@confirm="handleSearchConfirm"
/>
<view
v-if="searchDraftKeyword"
class="search-clear-btn"
@tap.stop="handleSearchClear"
>
<text class="search-clear-text">×</text>
</view>
</view>
<view class="search-confirm-btn" @tap="handleSearchConfirm">
<text class="search-confirm-text">搜索</text>
</view>
</view>
</view>
</template>
<view class="results-sheet">
<view class="sheet-header">
<text class="sheet-title">{{ resultTitle }}</text>
<text class="sort-label">按距离排序</text>
</view>
<view class="result-list">
<view
v-for="item in visibleFacilities"
:key="item.id"
class="result-card"
@tap="handleFacilityClick(item)"
>
<view class="facility-mark">
<text class="facility-mark-text"></text>
</view>
<view class="facility-info">
<text class="facility-name">{{ item.name }}</text>
<text class="facility-meta">{{ item.meta }}</text>
</view>
<view class="filter-row">
<view
class="result-action"
:class="{ secondary: item.action === '查看' }"
@tap.stop="handleResultAction(item)"
v-for="filter in filters"
:key="filter.id"
class="filter-chip"
:class="[{ active: currentFilter === filter.id }, `filter-${filter.id}`]"
@tap="handleFilterChange(filter.id)"
>
<text class="result-action-text">{{ item.action }}</text>
<text class="filter-text">{{ filter.label }}</text>
</view>
</view>
</template>
<view class="results-sheet">
<view class="sheet-header">
<text class="sheet-title">{{ resultTitle }}</text>
<text class="sort-label">默认展示</text>
</view>
<view class="result-list">
<view
v-for="item in visibleFacilities"
:key="item.id"
class="result-card"
@tap="handleFacilityClick(item)"
>
<view class="facility-mark">
<text class="facility-mark-text"></text>
</view>
<view class="facility-info">
<text class="facility-name">{{ item.name }}</text>
<text class="facility-meta">{{ item.meta }}</text>
</view>
<view
class="result-action"
:class="{ secondary: item.action === '查看' }"
@tap.stop="handleResultAction(item)"
>
<text class="result-action-text">{{ item.action }}</text>
</view>
</view>
</view>
</view>
</view>
</GuideMapShell>
</GuideMapShell>
</GuidePageFrame>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, nextTick, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
import {
formatNavFloorLabel,
@@ -63,9 +105,13 @@ import {
searchCleanNavPois,
type CleanNavPoi
} from '@/services/navAssets'
import {
navigateToGuideTopTab,
type GuideTopTab
} from '@/utils/guideTopTabs'
interface FilterItem {
id: 'all' | 'accessible' | 'floor' | 'nearest'
id: 'all' | 'accessible' | 'floor'
label: string
}
@@ -79,14 +125,17 @@ interface FacilityResult {
}
const searchKeyword = ref('卫生间')
const searchDraftKeyword = ref('')
const currentFilter = ref<FilterItem['id']>('all')
const isLoading = ref(false)
const isSearchEditing = ref(false)
const searchInputFocused = ref(false)
const searchPlaceholder = '搜索设施、展厅、入口'
const filters: FilterItem[] = [
{ id: 'all', label: '全部' },
{ id: 'accessible', label: '无障碍' },
{ id: 'floor', label: '本楼层' },
{ id: 'nearest', label: '最近' }
{ id: 'floor', label: '1F 点位' }
]
const facilities = ref<FacilityResult[]>([])
@@ -141,11 +190,46 @@ onLoad((options: any) => {
if (options.keyword) {
searchKeyword.value = decodeURIComponent(options.keyword)
}
searchDraftKeyword.value = searchKeyword.value
void loadFacilityResults()
})
const handleSearchTap = () => {
console.log('保持搜索结果页')
const handleSearchTap = async () => {
searchDraftKeyword.value = searchKeyword.value
isSearchEditing.value = true
searchInputFocused.value = false
await nextTick()
searchInputFocused.value = true
}
const handleSearchInput = (event: any) => {
searchDraftKeyword.value = event.detail.value
}
const handleSearchClear = async () => {
searchDraftKeyword.value = ''
searchInputFocused.value = false
await nextTick()
searchInputFocused.value = true
}
const handleSearchCancel = () => {
searchDraftKeyword.value = searchKeyword.value
isSearchEditing.value = false
searchInputFocused.value = false
}
const handleSearchConfirm = async (event?: any) => {
const confirmedKeyword = typeof event?.detail?.value === 'string'
? event.detail.value
: searchDraftKeyword.value
searchKeyword.value = confirmedKeyword.trim()
searchDraftKeyword.value = searchKeyword.value
currentFilter.value = 'all'
isSearchEditing.value = false
searchInputFocused.value = false
await loadFacilityResults()
}
const handleFilterChange = (filterId: FilterItem['id']) => {
@@ -160,7 +244,7 @@ const handleFacilityClick = (facility: FacilityResult) => {
const handleResultAction = (facility: FacilityResult) => {
uni.navigateTo({
url: `/pages/route/detail?facilityId=${facility.id}&target=${encodeURIComponent(facility.name)}&state=planning`
url: `/pages/route/detail?facilityId=${facility.id}&target=${encodeURIComponent(facility.name)}&state=preview`
})
}
@@ -172,12 +256,101 @@ const handleFloorChange = (floor: string) => {
console.log('搜索页切换楼层:', floor)
}
const handleTopTabChange = (tab: GuideTopTab) => {
navigateToGuideTopTab(tab)
}
const handlePageBack = () => {
uni.navigateBack({
delta: 1,
fail: () => {
uni.reLaunch({
url: '/pages/index/index'
})
}
})
}
</script>
<style scoped lang="scss">
.search-edit-row {
position: absolute;
top: 60px;
left: 16px;
right: 16px;
height: 36px;
display: flex;
align-items: center;
gap: 8px;
z-index: 48;
}
.search-edit-box {
flex: 1;
min-width: 0;
height: 36px;
padding: 0 10px;
display: flex;
align-items: center;
box-sizing: border-box;
background: #ffffff;
border: 1px solid rgba(224, 223, 0, 0.55);
border-radius: 6px;
box-shadow: 0 0 0 2px rgba(224, 223, 0, 0.16);
}
.search-edit-input {
flex: 1;
min-width: 0;
font-size: 14px;
line-height: 19px;
color: #1f2329;
background: transparent;
}
.search-clear-btn {
width: 22px;
height: 22px;
margin-left: 6px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: #8b8b84;
border-radius: 11px;
}
.search-clear-text {
font-size: 16px;
line-height: 18px;
font-weight: 500;
color: #ffffff;
}
.search-confirm-btn {
width: 52px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-sizing: border-box;
background: #000000;
border: 1px solid #000000;
border-radius: 8px;
}
.search-confirm-text {
font-size: 14px;
line-height: 19px;
font-weight: 500;
color: var(--museum-accent);
}
.filter-row {
position: absolute;
top: 102px;
top: 146px;
left: 23px;
right: 16px;
display: flex;
@@ -210,10 +383,6 @@ const handleFloorChange = (floor: string) => {
width: 82px;
}
.filter-nearest {
width: 74px;
}
.filter-text {
font-size: 12px;
line-height: 16px;