优化展厅讲解卡片视觉
@@ -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;
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -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',
|
||||
|
||||