refactor(pokemon): redesign related pokemon and items layout

Display related Pokemon and items sections side-by-side on desktop
Streamline related Pokemon rows by grouping traits and removing labels
This commit is contained in:
2026-05-02 08:44:03 +08:00
parent 21bbbc7137
commit c2f58fe661
3 changed files with 115 additions and 89 deletions

View File

@@ -223,8 +223,8 @@ Pokemon 详情页展示:
- 特长掉落物品 - 特长掉落物品
- 喜欢的环境 - 喜欢的环境
- 喜欢的东西 - 喜欢的东西
- 相关 Pokemon按相同喜欢的环境优先其次按共同喜欢的东西数量从多到少排序支持按喜欢的环境筛选默认筛选当前 Pokemon 的喜欢的环境,也可切换到其他喜欢的环境或全部;每个筛选视图最多展示 6 个;展示名称、喜欢的环境、特长和喜欢的东西,并高亮共同喜欢的东西 - 相关 Pokemon与关联喜欢的东西的物品在桌面端左右并排展示;按相同喜欢的环境优先,其次按共同喜欢的东西数量从多到少排序;支持按喜欢的环境筛选,默认筛选当前 Pokemon 的喜欢的环境,也可切换到其他喜欢的环境或全部;每个筛选视图最多展示 6 个;每项第一行左侧展示名称,右侧展示特长和喜欢的环境,第二行展示喜欢的东西,并高亮共同喜欢的东西
- 关联喜欢的东西的物品 - 关联喜欢的东西的物品:与相关 Pokemon 在桌面端左右并排展示
- 出现的栖息地 - 出现的栖息地
- 最后编辑信息 - 最后编辑信息
- 编辑历史:通过详情页 Tabs 展示 - 编辑历史:通过详情页 Tabs 展示

View File

@@ -2630,22 +2630,50 @@ button:disabled,
justify-content: flex-end; justify-content: flex-end;
} }
.pokemon-related-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
align-items: start;
}
.pokemon-related-grid > .detail-section {
min-width: 0;
}
.related-pokemon-list li { .related-pokemon-list li {
display: block; display: block;
} }
.related-pokemon-row { .related-pokemon-row {
display: grid; display: grid;
gap: 12px; gap: 8px;
min-width: 0; min-width: 0;
} }
.related-pokemon-row__header { .related-pokemon-row__summary {
display: grid;
grid-template-columns: minmax(112px, 1fr) minmax(0, auto);
align-items: center;
gap: 8px 14px;
min-width: 0;
}
.related-pokemon-row__traits {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
justify-content: space-between; justify-content: flex-end;
gap: 8px 12px; gap: 8px;
min-width: 0;
}
.related-pokemon-row__skills {
flex: 0 1 auto;
min-width: 0;
}
.related-pokemon-row__skills.chips {
min-width: 0; min-width: 0;
} }
@@ -2653,7 +2681,9 @@ button:disabled,
min-width: 0; min-width: 0;
color: var(--ink); color: var(--ink);
font-weight: 900; font-weight: 900;
overflow-wrap: anywhere; overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.related-pokemon-row__environment { .related-pokemon-row__environment {
@@ -2667,26 +2697,6 @@ button:disabled,
color: #172036; color: #172036;
} }
.related-pokemon-row__content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
gap: 12px;
min-width: 0;
}
.related-pokemon-row__group {
display: grid;
align-content: start;
gap: 6px;
min-width: 0;
}
.related-pokemon-row__label {
color: var(--muted);
font-size: 0.76rem;
font-weight: 900;
}
.related-favourite-chip { .related-favourite-chip {
gap: 6px; gap: 6px;
max-width: 100%; max-width: 100%;
@@ -2694,6 +2704,10 @@ button:disabled,
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.related-pokemon-row__favourites {
min-width: 0;
}
.detail-text { .detail-text {
margin: 0; margin: 0;
color: var(--ink-soft); color: var(--ink-soft);
@@ -3374,6 +3388,7 @@ button:disabled,
.detail-grid, .detail-grid,
.pokemon-profile-grid, .pokemon-profile-grid,
.pokemon-profile-row, .pokemon-profile-row,
.pokemon-related-grid,
.admin-layout { .admin-layout {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
@@ -3509,6 +3524,18 @@ button:disabled,
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.related-pokemon-row {
grid-template-columns: 1fr;
}
.related-pokemon-row__summary {
grid-template-columns: 1fr;
}
.related-pokemon-row__traits {
justify-content: flex-start;
}
.appearance-summary div { .appearance-summary div {
grid-template-columns: 68px minmax(0, 1fr); grid-template-columns: 68px minmax(0, 1fr);
} }

View File

@@ -346,77 +346,76 @@ watch(
<EntityChips :items="pokemon.favorite_things" /> <EntityChips :items="pokemon.favorite_things" />
</DetailSection> </DetailSection>
<DetailSection :title="t('pages.pokemon.relatedPokemon')"> <div class="pokemon-related-grid">
<template v-if="pokemon.relatedPokemon.length"> <DetailSection :title="t('pages.pokemon.relatedPokemon')">
<Tabs <template v-if="pokemon.relatedPokemon.length">
v-if="relatedHabitatTabs.length" <Tabs
id="pokemon-related-habitats" v-if="relatedHabitatTabs.length"
v-model="relatedHabitatTab" id="pokemon-related-habitats"
:tabs="relatedHabitatTabs" v-model="relatedHabitatTab"
:label="t('pages.pokemon.relatedHabitat')" :tabs="relatedHabitatTabs"
/> :label="t('pages.pokemon.relatedHabitat')"
<ul v-if="relatedPokemonRows.length" class="row-list related-pokemon-list"> />
<li v-for="related in relatedPokemonRows" :key="related.id"> <ul v-if="relatedPokemonRows.length" class="row-list related-pokemon-list">
<div class="related-pokemon-row"> <li v-for="related in relatedPokemonRows" :key="related.id">
<div class="related-pokemon-row__header"> <div class="related-pokemon-row">
<RouterLink class="related-pokemon-row__name" :to="`/pokemon/${related.id}`">#{{ related.id }} {{ related.name }}</RouterLink> <div class="related-pokemon-row__summary">
<span <RouterLink class="related-pokemon-row__name" :to="`/pokemon/${related.id}`">#{{ related.id }} {{ related.name }}</RouterLink>
class="chip related-pokemon-row__environment" <div class="related-pokemon-row__traits">
:class="{ 'related-pokemon-row__environment--match': related.environment.id === pokemon.environment.id }" <EntityChips
> v-if="related.skills.length"
{{ related.environment.name }} class="related-pokemon-row__skills"
</span> :items="related.skills"
</div> />
<div class="related-pokemon-row__content">
<div class="related-pokemon-row__group">
<span class="related-pokemon-row__label">{{ t('pages.pokemon.skills') }}</span>
<EntityChips v-if="related.skills.length" :items="related.skills" />
<span v-else class="meta-line">{{ t('common.none') }}</span>
</div>
<div class="related-pokemon-row__group">
<span class="related-pokemon-row__label">{{ t('pages.pokemon.favoriteThings') }}</span>
<div v-if="related.favorite_things.length" class="chips">
<span <span
v-for="thing in related.favorite_things" class="chip related-pokemon-row__environment"
:key="thing.id" :class="{ 'related-pokemon-row__environment--match': related.environment.id === pokemon.environment.id }"
class="chip related-favourite-chip"
:class="{ 'related-favourite-chip--match': thing.matches }"
> >
{{ thing.name }} {{ related.environment.name }}
</span> </span>
</div> </div>
<span v-else class="meta-line">{{ t('common.none') }}</span> </div>
<div
v-if="related.favorite_things.length"
class="chips related-pokemon-row__favourites"
>
<span
v-for="thing in related.favorite_things"
:key="thing.id"
class="chip related-favourite-chip"
:class="{ 'related-favourite-chip--match': thing.matches }"
>
{{ thing.name }}
</span>
</div> </div>
</div> </div>
</div> </li>
</li> </ul>
</ul> <p v-else class="meta-line">{{ t('common.none') }}</p>
</template>
<p v-else class="meta-line">{{ t('common.none') }}</p> <p v-else class="meta-line">{{ t('common.none') }}</p>
</template> </DetailSection>
<p v-else class="meta-line">{{ t('common.none') }}</p>
</DetailSection>
<DetailSection :title="t('pages.pokemon.relatedItems')"> <DetailSection :title="t('pages.pokemon.relatedItems')">
<template v-if="pokemon.favoriteThingItems.length"> <template v-if="pokemon.favoriteThingItems.length">
<Tabs <Tabs
v-if="itemCategoryTabs.length" v-if="itemCategoryTabs.length"
id="pokemon-favorite-items" id="pokemon-favorite-items"
v-model="itemCategoryTab" v-model="itemCategoryTab"
:tabs="itemCategoryTabs" :tabs="itemCategoryTabs"
:label="t('pages.pokemon.relatedItemCategory')" :label="t('pages.pokemon.relatedItemCategory')"
/> />
<ul v-if="favoriteThingItems.length" class="row-list"> <ul v-if="favoriteThingItems.length" class="row-list">
<li v-for="item in favoriteThingItems" :key="item.id"> <li v-for="item in favoriteThingItems" :key="item.id">
<RouterLink :to="`/items/${item.id}`">{{ item.name }}</RouterLink> <RouterLink :to="`/items/${item.id}`">{{ item.name }}</RouterLink>
<EntityChips :items="item.tags" /> <EntityChips :items="item.tags" />
</li> </li>
</ul> </ul>
<p v-else class="meta-line">{{ t('common.none') }}</p>
</template>
<p v-else class="meta-line">{{ t('common.none') }}</p> <p v-else class="meta-line">{{ t('common.none') }}</p>
</template> </DetailSection>
<p v-else class="meta-line">{{ t('common.none') }}</p> </div>
</DetailSection>
<DetailSection :title="t('pages.pokemon.habitats')"> <DetailSection :title="t('pages.pokemon.habitats')">
<ul class="row-list appearance-list"> <ul class="row-list appearance-list">