@@ -47,16 +47,17 @@
|
||||
|
||||
<view v-if="variant === 'home' && !showSearchContent && !isHomeCategoryMode" class="home-category-strip">
|
||||
<view
|
||||
v-for="item in homeCategories"
|
||||
v-for="item in collapsedHomeCategories"
|
||||
:key="item.id"
|
||||
class="home-category-chip"
|
||||
:class="{ active: activeCategoryId === item.id }"
|
||||
:data-testid="`poi-category-${item.id}`"
|
||||
@tap="handleFacilityShortcut(item)"
|
||||
>
|
||||
<view class="home-category-icon line-icon" :class="`icon-${item.icon}`">
|
||||
<view class="icon-part a"></view>
|
||||
<view class="icon-part b"></view>
|
||||
<view class="icon-part c"></view>
|
||||
<view class="home-category-icon">
|
||||
<svg class="poi-category-icon" viewBox="0 0 32 32" aria-hidden="true">
|
||||
<use :href="getPoiCategoryIconHref(item.id)" />
|
||||
</svg>
|
||||
</view>
|
||||
<text class="home-category-label">{{ item.label }}</text>
|
||||
</view>
|
||||
@@ -130,7 +131,7 @@
|
||||
</view>
|
||||
|
||||
<view class="category-scroll">
|
||||
<view class="category-grid">
|
||||
<view class="category-grid" :class="{ 'home-shortcut-grid': variant === 'home' }">
|
||||
<view
|
||||
v-for="item in searchCategoryColumns"
|
||||
:key="item.id"
|
||||
@@ -139,10 +140,10 @@
|
||||
:data-testid="`poi-category-${item.id}`"
|
||||
@tap="handleFacilityShortcut(item)"
|
||||
>
|
||||
<view class="line-icon" :class="`icon-${item.icon}`">
|
||||
<view class="icon-part a"></view>
|
||||
<view class="icon-part b"></view>
|
||||
<view class="icon-part c"></view>
|
||||
<view class="category-icon-shell">
|
||||
<svg class="poi-category-icon" viewBox="0 0 32 32" aria-hidden="true">
|
||||
<use :href="getPoiCategoryIconHref(item.id)" />
|
||||
</svg>
|
||||
</view>
|
||||
<text class="category-label">{{ item.label }}</text>
|
||||
</view>
|
||||
@@ -230,6 +231,7 @@ import {
|
||||
HOME_POI_CATEGORIES,
|
||||
POI_CATEGORIES,
|
||||
findPoiCategoryByKeyword,
|
||||
getPoiCategoryIconHref,
|
||||
getPoiCategoryById,
|
||||
getPoiDataIssues,
|
||||
inspectPoiCollection,
|
||||
@@ -273,8 +275,11 @@ type PreparedPoiResults = {
|
||||
}
|
||||
|
||||
const homeCategories = HOME_POI_CATEGORIES
|
||||
const collapsedHomeCategories = computed(() => homeCategories.slice(0, 5))
|
||||
const searchCategories = POI_CATEGORIES
|
||||
const searchCategoryColumns = searchCategories
|
||||
const searchCategoryColumns = computed(() => (
|
||||
props.variant === 'home' ? homeCategories : searchCategories
|
||||
))
|
||||
|
||||
const searchKeyword = ref('')
|
||||
const searchDraftKeyword = ref('')
|
||||
@@ -1083,89 +1088,76 @@ defineExpose({
|
||||
.home-category-strip {
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.variant-home.is-collapsed .home-category-strip {
|
||||
margin-top: 7px;
|
||||
gap: 5px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.home-category-chip {
|
||||
min-width: 0;
|
||||
height: 60px;
|
||||
min-height: 68px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
padding: 6px 2px;
|
||||
gap: 4px;
|
||||
padding: 6px 2px 5px;
|
||||
box-sizing: border-box;
|
||||
background: #fffef2;
|
||||
border: 1px solid #ecebd2;
|
||||
background: #f5f5ed;
|
||||
border: 1px solid #dfe2d7;
|
||||
border-radius: 8px;
|
||||
box-shadow: none;
|
||||
box-shadow: 0 2px 5px rgba(21, 23, 19, 0.1);
|
||||
transition: background-color 0.18s ease, border-color 0.18s ease, transform 0.14s ease;
|
||||
}
|
||||
|
||||
.variant-home.is-collapsed .home-category-chip {
|
||||
min-width: 0;
|
||||
height: 56px;
|
||||
gap: 4px;
|
||||
padding: 5px 1px;
|
||||
border-radius: 7px;
|
||||
min-height: 68px;
|
||||
}
|
||||
|
||||
.home-category-chip:active {
|
||||
transform: translateY(1px);
|
||||
border-color: #d8d900;
|
||||
background: #eef06d;
|
||||
border-color: #c8ca00;
|
||||
}
|
||||
|
||||
.home-category-chip.active {
|
||||
background: #f2f48d;
|
||||
border-color: #bfc100;
|
||||
}
|
||||
|
||||
.home-category-icon {
|
||||
width: 24px;
|
||||
height: 23px;
|
||||
flex: 0 0 24px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
flex: 0 0 28px;
|
||||
}
|
||||
|
||||
.poi-category-icon {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #151713;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.home-category-icon.line-icon {
|
||||
width: 24px;
|
||||
height: 23px;
|
||||
}
|
||||
|
||||
.home-category-icon .icon-part {
|
||||
transform: scale(0.8);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.variant-home.is-collapsed .home-category-icon,
|
||||
.variant-home.is-collapsed .home-category-icon.line-icon {
|
||||
width: 22px;
|
||||
height: 21px;
|
||||
flex-basis: 22px;
|
||||
}
|
||||
|
||||
.variant-home.is-collapsed .home-category-icon .icon-part {
|
||||
transform: scale(0.74);
|
||||
.home-category-chip.active .poi-category-icon,
|
||||
.category-item.active .poi-category-icon {
|
||||
color: #151713;
|
||||
}
|
||||
|
||||
.home-category-label {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
line-height: 15px;
|
||||
font-weight: 700;
|
||||
color: #151713;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.variant-home.is-collapsed .home-category-label {
|
||||
max-width: 100%;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.home-category-results {
|
||||
@@ -1436,6 +1428,14 @@ defineExpose({
|
||||
touch-action: pan-x;
|
||||
}
|
||||
|
||||
.variant-home .category-scroll {
|
||||
width: calc(100% - 8px);
|
||||
margin: 0 4px;
|
||||
overflow: visible;
|
||||
padding-bottom: 0;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
.category-scroll::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@@ -1455,22 +1455,24 @@ defineExpose({
|
||||
min-width: 0;
|
||||
min-height: 70px;
|
||||
position: relative;
|
||||
padding: 9px 6px 10px;
|
||||
padding: 9px 4px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
background: #fffef2;
|
||||
border: 1px solid #ecebd2;
|
||||
overflow: visible;
|
||||
background: #f5f5ed;
|
||||
border: 1px solid #dfe2d7;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(21, 23, 19, 0.08);
|
||||
transition: background-color 0.18s ease, border-color 0.18s ease, transform 0.14s ease;
|
||||
}
|
||||
|
||||
.category-item.active {
|
||||
background: #fffddc;
|
||||
border-color: #d8d900;
|
||||
background: #f2f48d;
|
||||
border-color: #bfc100;
|
||||
}
|
||||
|
||||
.home-category-chip.disabled,
|
||||
@@ -1482,170 +1484,52 @@ defineExpose({
|
||||
.category-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
bottom: 0;
|
||||
height: 4px;
|
||||
background: var(--museum-accent);
|
||||
}
|
||||
|
||||
.line-icon {
|
||||
width: 26px;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
color: #151713;
|
||||
}
|
||||
|
||||
.category-item.active .line-icon {
|
||||
color: #151713;
|
||||
}
|
||||
|
||||
.icon-part {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.icon-service .icon-part.a,
|
||||
.icon-exhibition .icon-part.a,
|
||||
.icon-cinema .icon-part.a,
|
||||
.icon-ticket .icon-part.a,
|
||||
.icon-drink .icon-part.a,
|
||||
.icon-bag .icon-part.a {
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
top: 5px;
|
||||
bottom: 5px;
|
||||
border: 2px solid currentColor;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.icon-service .icon-part.b,
|
||||
.icon-exhibition .icon-part.b,
|
||||
.icon-cinema .icon-part.b,
|
||||
.icon-ticket .icon-part.b,
|
||||
.icon-drink .icon-part.b,
|
||||
.icon-bag .icon-part.b {
|
||||
left: 9px;
|
||||
right: 9px;
|
||||
top: 1px;
|
||||
height: 5px;
|
||||
border: 2px solid currentColor;
|
||||
border-bottom: 0;
|
||||
border-radius: 6px 6px 0 0;
|
||||
}
|
||||
|
||||
.icon-elevator .icon-part.a,
|
||||
.icon-escalator .icon-part.a,
|
||||
.icon-restroom .icon-part.a,
|
||||
.icon-nursing .icon-part.a,
|
||||
.icon-restaurant .icon-part.a,
|
||||
.icon-stairs .icon-part.a,
|
||||
.icon-entrance .icon-part.a {
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
top: 3px;
|
||||
bottom: 3px;
|
||||
border: 2px solid currentColor;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.icon-elevator .icon-part.b {
|
||||
left: 9px;
|
||||
top: 7px;
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
border-left: 2px solid currentColor;
|
||||
border-right: 2px solid currentColor;
|
||||
}
|
||||
|
||||
.icon-escalator .icon-part.b,
|
||||
.icon-entrance .icon-part.b {
|
||||
left: 7px;
|
||||
top: 12px;
|
||||
width: 13px;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
transform: rotate(-22deg);
|
||||
}
|
||||
|
||||
.icon-restroom .icon-part.b,
|
||||
.icon-nursing .icon-part.b {
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: currentColor;
|
||||
border-radius: 50%;
|
||||
box-shadow: 8px 0 0 currentColor;
|
||||
}
|
||||
|
||||
.icon-exhibition .icon-part.c {
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
bottom: 2px;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.icon-cinema .icon-part.b {
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
top: 4px;
|
||||
height: 4px;
|
||||
border: 0;
|
||||
border-top: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.icon-restaurant .icon-part.b {
|
||||
left: 7px;
|
||||
top: 6px;
|
||||
width: 2px;
|
||||
height: 12px;
|
||||
background: currentColor;
|
||||
box-shadow: 5px 0 0 currentColor, 10px 0 0 currentColor;
|
||||
}
|
||||
|
||||
.icon-restaurant .icon-part.c {
|
||||
right: 3px;
|
||||
top: 5px;
|
||||
width: 7px;
|
||||
height: 15px;
|
||||
border-left: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
border-radius: 0 0 0 5px;
|
||||
}
|
||||
|
||||
.icon-stairs .icon-part.b {
|
||||
left: 6px;
|
||||
top: 7px;
|
||||
width: 14px;
|
||||
height: 12px;
|
||||
border-left: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
}
|
||||
|
||||
.icon-stairs .icon-part.c {
|
||||
left: 10px;
|
||||
top: 11px;
|
||||
width: 10px;
|
||||
height: 8px;
|
||||
border-left: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
right: 10px;
|
||||
bottom: -1px;
|
||||
height: 3px;
|
||||
background: var(--museum-accent);
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.category-icon-shell {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
flex: 0 0 28px;
|
||||
}
|
||||
|
||||
.category-item:active {
|
||||
transform: translateY(1px);
|
||||
background: #eef06d;
|
||||
border-color: #c8ca00;
|
||||
}
|
||||
|
||||
.variant-home .home-shortcut-grid {
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
grid-template-rows: repeat(2, 72px);
|
||||
grid-auto-columns: auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.variant-home .category-item {
|
||||
min-height: 72px;
|
||||
height: 72px;
|
||||
padding: 6px 2px 5px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.category-label {
|
||||
max-width: 100%;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
line-height: 15px;
|
||||
font-weight: 700;
|
||||
color: #151713;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.category-item.active .category-label {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
} from '@/domain/museum'
|
||||
|
||||
export type PoiCategoryId =
|
||||
| 'itinerary'
|
||||
| 'exhibition-hall'
|
||||
| 'cinema'
|
||||
| 'ticket-office'
|
||||
@@ -25,25 +26,37 @@ export interface PoiCategoryDefinition {
|
||||
keywords: readonly string[]
|
||||
categoryIds: readonly string[]
|
||||
iconTypes: readonly string[]
|
||||
excludedCategoryIds?: readonly string[]
|
||||
}
|
||||
|
||||
const definitions: PoiCategoryDefinition[] = [
|
||||
{
|
||||
id: 'exhibition-hall',
|
||||
label: '展厅',
|
||||
icon: 'exhibition',
|
||||
id: 'itinerary',
|
||||
label: '行程',
|
||||
icon: 'itinerary',
|
||||
order: 1,
|
||||
homeVisible: true,
|
||||
keywords: ['展厅', '展览', '展馆', 'exhibition_hall', 'touring_poi', 'hall'],
|
||||
categoryIds: ['exhibition_hall', 'exhibition_hall_entrance', 'touring_poi'],
|
||||
iconTypes: ['exhibition_hall', 'hall_entrance', 'exhibition']
|
||||
keywords: ['行程', '导览', '参观', 'touring_poi'],
|
||||
categoryIds: ['touring_poi'],
|
||||
iconTypes: ['touring', 'guide']
|
||||
},
|
||||
{
|
||||
id: 'exhibition-hall',
|
||||
label: '展览',
|
||||
icon: 'exhibition',
|
||||
order: 2,
|
||||
homeVisible: true,
|
||||
keywords: ['展厅', '展览', '展馆', 'exhibition_hall', 'hall'],
|
||||
categoryIds: ['exhibition_hall', 'exhibition_hall_entrance'],
|
||||
iconTypes: ['exhibition_hall', 'hall_entrance', 'exhibition'],
|
||||
excludedCategoryIds: ['touring_poi']
|
||||
},
|
||||
{
|
||||
id: 'cinema',
|
||||
label: '影院',
|
||||
icon: 'cinema',
|
||||
order: 2,
|
||||
homeVisible: false,
|
||||
order: 3,
|
||||
homeVisible: true,
|
||||
keywords: ['影院', '影厅', '剧场', '报告厅', 'cinema', 'theater'],
|
||||
categoryIds: ['space_theater'],
|
||||
iconTypes: ['cinema', 'theater']
|
||||
@@ -52,8 +65,8 @@ const definitions: PoiCategoryDefinition[] = [
|
||||
id: 'ticket-office',
|
||||
label: '售票处',
|
||||
icon: 'ticket',
|
||||
order: 3,
|
||||
homeVisible: false,
|
||||
order: 6,
|
||||
homeVisible: true,
|
||||
keywords: ['售票处', '售票', '票务', 'ticket_office'],
|
||||
categoryIds: [],
|
||||
iconTypes: ['ticket_office']
|
||||
@@ -62,18 +75,18 @@ const definitions: PoiCategoryDefinition[] = [
|
||||
id: 'service-center',
|
||||
label: '服务中心',
|
||||
icon: 'service',
|
||||
order: 4,
|
||||
homeVisible: false,
|
||||
order: 8,
|
||||
homeVisible: true,
|
||||
keywords: ['服务中心', '服务台', '咨询台', 'service_desk'],
|
||||
categoryIds: [],
|
||||
iconTypes: ['service_desk']
|
||||
},
|
||||
{
|
||||
id: 'dining',
|
||||
label: '餐饮',
|
||||
label: '餐厅',
|
||||
icon: 'restaurant',
|
||||
order: 5,
|
||||
homeVisible: false,
|
||||
homeVisible: true,
|
||||
keywords: ['餐饮', '餐厅', '快餐', '咖啡', 'restaurant', 'dining', 'food', 'cafe'],
|
||||
categoryIds: [],
|
||||
iconTypes: ['restaurant', 'dining', 'food', 'cafe']
|
||||
@@ -82,7 +95,7 @@ const definitions: PoiCategoryDefinition[] = [
|
||||
id: 'souvenir',
|
||||
label: '文创',
|
||||
icon: 'bag',
|
||||
order: 6,
|
||||
order: 11,
|
||||
homeVisible: false,
|
||||
keywords: ['文创', '纪念品', '商店', 'souvenir', 'gift', 'shop'],
|
||||
categoryIds: [],
|
||||
@@ -100,9 +113,9 @@ const definitions: PoiCategoryDefinition[] = [
|
||||
},
|
||||
{
|
||||
id: 'restroom',
|
||||
label: '卫生间',
|
||||
label: '洗手间',
|
||||
icon: 'restroom',
|
||||
order: 8,
|
||||
order: 4,
|
||||
homeVisible: true,
|
||||
keywords: ['卫生间', '洗手间', '厕所', 'toilet', 'accessible_toilet', 'restroom'],
|
||||
categoryIds: [],
|
||||
@@ -122,8 +135,8 @@ const definitions: PoiCategoryDefinition[] = [
|
||||
id: 'stairs',
|
||||
label: '楼梯',
|
||||
icon: 'stairs',
|
||||
order: 10,
|
||||
homeVisible: true,
|
||||
order: 12,
|
||||
homeVisible: false,
|
||||
keywords: ['楼梯', 'stairs', 'stair'],
|
||||
categoryIds: [],
|
||||
iconTypes: ['stairs', 'stair']
|
||||
@@ -132,7 +145,7 @@ const definitions: PoiCategoryDefinition[] = [
|
||||
id: 'escalator',
|
||||
label: '扶梯',
|
||||
icon: 'escalator',
|
||||
order: 11,
|
||||
order: 10,
|
||||
homeVisible: true,
|
||||
keywords: ['扶梯', 'escalator'],
|
||||
categoryIds: [],
|
||||
@@ -145,11 +158,15 @@ export const POI_CATEGORIES = Object.freeze(
|
||||
)
|
||||
|
||||
const homeCategoryOrder: readonly PoiCategoryId[] = [
|
||||
'itinerary',
|
||||
'exhibition-hall',
|
||||
'cinema',
|
||||
'restroom',
|
||||
'dining',
|
||||
'ticket-office',
|
||||
'nursing-room',
|
||||
'service-center',
|
||||
'elevator',
|
||||
'stairs',
|
||||
'escalator'
|
||||
]
|
||||
|
||||
@@ -159,6 +176,25 @@ export const HOME_POI_CATEGORIES = Object.freeze(
|
||||
))
|
||||
)
|
||||
|
||||
const POI_CATEGORY_ICON_SYMBOLS: Readonly<Record<PoiCategoryId, string>> = {
|
||||
itinerary: 'poi-itinerary',
|
||||
'exhibition-hall': 'poi-exhibition-hall',
|
||||
cinema: 'poi-cinema',
|
||||
'ticket-office': 'poi-ticket-office',
|
||||
'service-center': 'poi-service-center',
|
||||
dining: 'poi-dining',
|
||||
souvenir: 'poi-souvenir',
|
||||
'nursing-room': 'poi-nursing-room',
|
||||
restroom: 'poi-restroom',
|
||||
elevator: 'poi-elevator',
|
||||
stairs: 'poi-stairs',
|
||||
escalator: 'poi-escalator'
|
||||
}
|
||||
|
||||
export const getPoiCategoryIconHref = (categoryId: PoiCategoryId) => (
|
||||
`/static/icons/poi/shortcut-icons.svg#${POI_CATEGORY_ICON_SYMBOLS[categoryId]}`
|
||||
)
|
||||
|
||||
type PoiCategorySource = Pick<MuseumPoi, 'name' | 'primaryCategory' | 'categories'>
|
||||
|
||||
const normalizeValue = (value?: string | null) => (value || '').trim().toLowerCase()
|
||||
@@ -177,6 +213,12 @@ export const matchesPoiCategory = (
|
||||
const normalizedCategoryIds = new Set(categories.map((category) => normalizeValue(category.id)))
|
||||
const normalizedIconTypes = new Set(categories.map((category) => normalizeValue(category.iconType)))
|
||||
|
||||
if (definition.excludedCategoryIds?.some((categoryId) => (
|
||||
normalizedCategoryIds.has(normalizeValue(categoryId))
|
||||
))) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (definition.categoryIds.some((categoryId) => normalizedCategoryIds.has(normalizeValue(categoryId)))) {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user