优化展厅讲解卡片视觉

This commit is contained in:
lyf
2026-07-17 22:53:22 +08:00
parent f4e7a5f147
commit d8e5a84f02
10 changed files with 167 additions and 48 deletions

View File

@@ -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)"
>
<view
v-if="hallPreviewUrl(hall)"
class="hall-thumb hall-thumb-shell"
class="hall-card-art hall-thumb-shell"
:class="{
'is-image-loaded': isHallPreviewLoaded(hall.id),
'is-image-error': isHallPreviewError(hall.id)
@@ -51,29 +52,24 @@
</view>
<image
v-if="!isHallPreviewError(hall.id)"
class="hall-thumb-image"
class="hall-card-art-image"
:src="hallPreviewUrl(hall)"
mode="aspectFill"
mode="aspectFit"
:lazy-load="isHallPreviewLazy(index)"
@load="handleHallPreviewLoad(hall.id)"
@error="handleHallPreviewError(hall.id)"
/>
</view>
<view v-else class="hall-thumb placeholder">
<view v-else class="hall-card-art placeholder">
<text class="hall-thumb-text">{{ hallIconText(hall.name) }}</text>
</view>
<view class="hall-main">
<view class="hall-overview-copy">
<text class="hall-name">{{ hall.name }}</text>
<view class="hall-meta-row">
<view class="floor-badge">
<text class="floor-badge-text">{{ hall.floorLabel || '楼层待补充' }}</text>
<text v-if="hallCardTheme(hall).englishName" class="hall-overview-english">{{ hallCardTheme(hall).englishName }}</text>
<text class="hall-meta-text hall-overview-meta">{{ hallFloorMetaText(hall) }}</text>
<view class="hall-go-entry"><text class="hall-go-entry-text">GO </text></view>
</view>
<text class="hall-meta-text hall-overview-meta">{{ hallMetaText(hall) }}</text>
</view>
</view>
<text class="hall-arrow"></text>
</view>
</view>
@@ -211,12 +207,24 @@ const hallPreviewMap: Record<string, string> = {
家园厅: `${HALL_PREVIEW_BASE}/homeland.webp`
}
const hallCardThemeMap: Record<string, { color: string; englishName: string }> = {
宇宙厅: { 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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -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',