diff --git a/src/components/explain/ExplainHallSelect.vue b/src/components/explain/ExplainHallSelect.vue index 4344cfd..f25bf24 100644 --- a/src/components/explain/ExplainHallSelect.vue +++ b/src/components/explain/ExplainHallSelect.vue @@ -36,11 +36,12 @@ v-for="(hall, index) in filteredHalls" :key="hall.id" class="hall-card hall-overview-card" + :style="{ '--hall-card-color': hallCardTheme(hall).color }" @tap="handleHallClick(hall.id)" > - + {{ hallIconText(hall.name) }} - + {{ hall.name }} - - - {{ hall.floorLabel || '楼层待补充' }} - - {{ hallMetaText(hall) }} - + {{ hallCardTheme(hall).englishName }} + {{ hallFloorMetaText(hall) }} + GO › - - @@ -211,12 +207,24 @@ const hallPreviewMap: Record = { 家园厅: `${HALL_PREVIEW_BASE}/homeland.webp` } +const hallCardThemeMap: Record = { + 宇宙厅: { color: '#57c7d9', englishName: 'Universe Hall' }, + 地球厅: { color: '#99de00', englishName: 'Earth Hall' }, + 演化厅: { color: '#b896f2', englishName: 'Evolution Hall' }, + 恐龙厅: { color: '#c7a15c', englishName: 'Dinosaur Hall' }, + 人类厅: { color: '#e8e500', englishName: 'Human Hall' }, + 动物厅: { color: '#08c4b5', englishName: 'Biology Hall' }, + 生物厅: { color: '#08c4b5', englishName: 'Biology Hall' }, + 生态厅: { color: '#8fe5b8', englishName: 'Ecology Hall' }, + 家园厅: { color: '#ffc2a6', englishName: 'Homeland Hall' } +} + const filteredHalls = computed(() => props.halls) const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram()) const showInternalHeader = computed(() => !shouldUseHostNavigation.value) const headerTitle = computed(() => { if (props.stage === 'stop') return props.selectedHallName || '讲解对象' - return '讲解' + return '展厅讲解' }) const loadingTitle = computed(() => { if (props.stage === 'stop') return '正在加载讲解对象' @@ -271,6 +279,12 @@ const hallMetaText = (hall: ExplainHallSelectItem) => ( : hall.explainCount > 0 ? `${hall.explainCount} 个展项` : '展厅' ) +const hallFloorMetaText = (hall: ExplainHallSelectItem) => `${hall.floorLabel || '楼层待补充'} · ${hallMetaText(hall)}` + +const hallCardTheme = (hall: ExplainHallSelectItem) => ( + hallCardThemeMap[hall.name.trim()] || { color: '#dce5d5', englishName: '' } +) + const handleHallClick = (hallId: string) => { emit('hallClick', hallId) } @@ -318,8 +332,7 @@ const handleBack = () => { } .explain-scroll.hall-stage { - padding: 58px 12px 0; - overflow: hidden; + padding: 76px 16px 16px; } .explain-scroll.stop-stage { @@ -346,34 +359,33 @@ const handleBack = () => { top: 0; left: 0; right: 0; - height: 56px; + height: 64px; display: flex; align-items: center; justify-content: center; padding: 0 20px; box-sizing: border-box; background: #ffffff; - border-bottom: 1px solid #eceee8; z-index: 2001; } .header-back { position: absolute; left: 12px; - top: 0; - height: 56px; - width: 82px; + top: 10px; + height: 44px; + width: 64px; display: flex; align-items: center; justify-content: flex-start; - gap: 6px; + gap: 4px; z-index: 1; } .header-back-icon { position: relative; - width: 18px; - height: 18px; + width: 16px; + height: 16px; display: flex; align-items: center; justify-content: center; @@ -384,10 +396,10 @@ const handleBack = () => { .header-back-icon::before { content: ''; - width: 9px; - height: 9px; - border-left: 2px solid currentColor; - border-bottom: 2px solid currentColor; + width: 8px; + height: 8px; + border-left: 1.5px solid currentColor; + border-bottom: 1.5px solid currentColor; transform: translateX(2px) rotate(45deg); } @@ -395,19 +407,19 @@ const handleBack = () => { display: flex; align-items: center; height: 20px; - font-size: 15px; + font-size: 14px; line-height: 20px; - font-weight: 500; + font-weight: 400; color: #151713; } .header-title { - max-width: calc(100% - 188px); + max-width: calc(100% - 152px); padding: 0 8px; box-sizing: border-box; font-size: 18px; line-height: 25px; - font-weight: 800; + font-weight: 700; color: #151713; text-align: center; overflow: hidden; @@ -422,8 +434,10 @@ const handleBack = () => { } .hall-overview-list { - height: 100%; - gap: 0; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-auto-rows: 154px; + gap: 12px; } .explain-scroll.unit-stage .hall-list, @@ -445,10 +459,15 @@ const handleBack = () => { } .hall-overview-card { - min-height: 0; - flex: 1 1 0; - gap: 7px; - padding: 4px 10px; + position: relative; + min-height: 154px; + display: block; + overflow: hidden; + padding: 0; + border: 0; + border-radius: 8px; + box-shadow: none; + background: var(--hall-card-color); } .hall-card:active { @@ -470,9 +489,44 @@ const handleBack = () => { object-fit: contain; } -.hall-overview-card .hall-thumb { - width: 82px; - height: 56px; +.hall-overview-card:active { + opacity: 0.86; + background: var(--hall-card-color); +} + +.hall-overview-card .hall-card-art { + position: absolute; + top: 46px; + right: 14px; + width: 62px; + height: 62px; + overflow: hidden; + background: transparent; + border: 0; + border-radius: 0; +} + +.hall-card-art-image { + width: 100%; + height: 100%; + opacity: 0; + transition: opacity 0.22s ease; +} + +.hall-overview-card .hall-card-art.is-image-loaded { + background: transparent; +} + +.hall-overview-card .hall-card-art.is-image-loaded .hall-card-art-image { + opacity: 0.96; +} + +.hall-card-art.placeholder { + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.24); + border-radius: 50%; } .hall-thumb-shell.is-image-loaded { @@ -532,6 +586,14 @@ const handleBack = () => { flex: 1; } +.hall-overview-copy { + position: absolute; + top: 14px; + left: 14px; + right: 14px; + min-width: 0; +} + .stop-desc { display: block; margin-top: 5px; @@ -555,12 +617,23 @@ const handleBack = () => { } .hall-overview-card .hall-name { - font-size: 15px; - line-height: 20px; + display: block; + max-width: 94px; + font-size: 19px; + line-height: 25px; + font-weight: 700; } -.hall-overview-card .hall-meta-row { - margin-top: 3px; +.hall-overview-english { + display: block; + max-width: 96px; + margin-top: 1px; + font-size: 12px; + line-height: 17px; + color: #444754; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .stop-title-row { @@ -645,14 +718,37 @@ const handleBack = () => { } .hall-overview-meta { - min-width: 0; - font-size: 11px; - line-height: 15px; + display: block; + max-width: 92px; + margin-top: 5px; + font-size: 9px; + line-height: 13px; + color: #444754; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.hall-go-entry { + position: absolute; + left: 0; + top: 98px; + width: 50px; + height: 25px; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + background: #ffffff; + border-radius: 13px; +} + +.hall-go-entry-text { + font-size: 12px; + line-height: 17px; + color: #262421; +} + .hall-arrow { flex-shrink: 0; font-size: 24px; diff --git a/static/explain/hall-previews/biology.webp b/static/explain/hall-previews/biology.webp index 6a35d90..32aaedc 100644 Binary files a/static/explain/hall-previews/biology.webp and b/static/explain/hall-previews/biology.webp differ diff --git a/static/explain/hall-previews/dinosaur.webp b/static/explain/hall-previews/dinosaur.webp index 46409fe..2c52384 100644 Binary files a/static/explain/hall-previews/dinosaur.webp and b/static/explain/hall-previews/dinosaur.webp differ diff --git a/static/explain/hall-previews/earth.webp b/static/explain/hall-previews/earth.webp index 1dbb11f..3a72823 100644 Binary files a/static/explain/hall-previews/earth.webp and b/static/explain/hall-previews/earth.webp differ diff --git a/static/explain/hall-previews/ecology.webp b/static/explain/hall-previews/ecology.webp index 42666bd..a358d67 100644 Binary files a/static/explain/hall-previews/ecology.webp and b/static/explain/hall-previews/ecology.webp differ diff --git a/static/explain/hall-previews/evolution.webp b/static/explain/hall-previews/evolution.webp index 8b63603..7c7c1d8 100644 Binary files a/static/explain/hall-previews/evolution.webp and b/static/explain/hall-previews/evolution.webp differ diff --git a/static/explain/hall-previews/homeland.webp b/static/explain/hall-previews/homeland.webp index ff95ce5..de3997b 100644 Binary files a/static/explain/hall-previews/homeland.webp and b/static/explain/hall-previews/homeland.webp differ diff --git a/static/explain/hall-previews/human.webp b/static/explain/hall-previews/human.webp index 58a0417..1f91314 100644 Binary files a/static/explain/hall-previews/human.webp and b/static/explain/hall-previews/human.webp differ diff --git a/static/explain/hall-previews/universe.webp b/static/explain/hall-previews/universe.webp index 4dd571d..0bf2b78 100644 Binary files a/static/explain/hall-previews/universe.webp and b/static/explain/hall-previews/universe.webp differ diff --git a/tests/unit/ExplainHallSelect.spec.ts b/tests/unit/ExplainHallSelect.spec.ts index a55b012..c129e6a 100644 --- a/tests/unit/ExplainHallSelect.spec.ts +++ b/tests/unit/ExplainHallSelect.spec.ts @@ -35,6 +35,29 @@ describe('讲解展厅选择列表', () => { expect(wrapper.emitted('hallClick')).toEqual([['hall-evolution']]) }) + it('展厅阶段按卡片网格展示英文名和进入入口', () => { + const wrapper = mount(ExplainHallSelect, { + props: { + stage: 'hall', + halls: [{ + id: 'hall-dinosaur', + name: '恐龙厅', + floorLabel: 'L-2', + explainCount: 35, + guideStopCount: 35, + searchText: '恐龙厅' + }] + } + }) + + const card = wrapper.get('.hall-overview-card') + expect(wrapper.get('.header-title').text()).toBe('展厅讲解') + expect(card.attributes('style')).toContain('--hall-card-color: #c7a15c') + expect(card.text()).toContain('Dinosaur Hall') + expect(card.text()).toContain('L-2 · 35 个讲解对象') + expect(card.get('.hall-go-entry').text()).toBe('GO ›') + }) + it('READY 讲解点不显示可讲解标签,非 READY 讲解点保留图文标签和点击事件', async () => { const readyStop = { id: 'stop-ready',