feat(home-page): add hero sorting function

1. 新增首页工具栏排序控件,支持按职业/星级排序及升降序切换
2. 实现排序逻辑,保留原始数组下标保证编辑定位准确
3. 更新样式文件,添加排序控件组的布局样式
This commit is contained in:
pan
2026-09-07 14:04:49 +08:00
parent 5fc743dbe3
commit 481fdaa450
2 changed files with 38 additions and 3 deletions

View File

@@ -13,11 +13,22 @@
<!-- 首页:英雄卡片列表 -->
<div class="home-toolbar">
<span class="muted">共 {{ heroes.length }} 个英雄</span>
<div class="toolbar-sort">
<el-radio-group v-model="sortKey" size="small">
<el-radio-button value="none">默认</el-radio-button>
<el-radio-button value="role">按职业</el-radio-button>
<el-radio-button value="rarity">按星级</el-radio-button>
</el-radio-group>
<el-radio-group v-if="sortKey !== 'none'" v-model="sortOrder" size="small">
<el-radio-button value="asc">升序</el-radio-button>
<el-radio-button value="desc">降序</el-radio-button>
</el-radio-group>
</div>
<el-button size="small" type="primary" @click="addHero">+ 新建英雄</el-button>
</div>
<section class="hero-rows">
<div v-if="!heroes.length" class="empty">暂无英雄,点击右上角「新建英雄」开始配置</div>
<div v-for="(h, i) in heroes" :key="h.uuid" class="hero-row" @click="openEdit(i)">
<div v-for="{ h, i } in sortedHeroes" :key="h.uuid" class="hero-row" @click="openEdit(i)">
<div class="row-main">
<span class="row-uuid">{{ h.uuid }}</span>
<span class="row-name">{{ h.name || '(未命名)' }}</span>
@@ -178,7 +189,7 @@
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ref, computed, watch, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { api } from './api'
import {
@@ -200,6 +211,28 @@ const editVisible = ref(false)
const current = computed(() => heroes.value[activeIdx.value])
// 首页列表排序:none 原始顺序 / role 按职业枚举序(坦克→辅助)/ rarity 按星级
const sortKey = ref('none')
const sortOrder = ref('desc')
// 切换排序维度时重置方向:职业升序直观(坦克在前),星级降序直观(高星在前)
watch(sortKey, (k) => { sortOrder.value = k === 'role' ? 'asc' : 'desc' })
// 排序视图:携带原始下标,保证点击卡片仍能定位到 heroes 中的编辑对象,且不改写保存顺序
const sortedHeroes = computed(() => {
const list = heroes.value.map((h, i) => ({ h, i }))
if (sortKey.value === 'none') return list
const dir = sortOrder.value === 'asc' ? 1 : -1
const roleIdx = (key) => HERO_ROLES.findIndex((r) => r.key === key)
list.sort((a, b) => {
const d = sortKey.value === 'role'
? roleIdx(a.h.roleKey) - roleIdx(b.h.roleKey)
: (a.h.rarity || 0) - (b.h.rarity || 0)
// 同维度内按 uuid 升序兜底,保证排序稳定
return d !== 0 ? d * dir : a.h.uuid - b.h.uuid
})
return list
})
const skillMap = computed(() => meta.value?.SkillSet || {})
const allSkills = computed(() => Object.values(skillMap.value).sort((a, b) => a.uuid - b.uuid))
const fieldOptions = computed(() => Object.values(meta.value?.FieldSkillSet || {}).sort((a, b) => a.uuid - b.uuid))

View File

@@ -60,7 +60,7 @@ body {
overflow: hidden;
}
/* 首页工具栏:计数 + 新建入口 */
/* 首页工具栏:计数 + 排序 + 新建入口 */
.home-toolbar {
display: flex;
gap: 8px;
@@ -71,6 +71,8 @@ body {
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
/* 排序控件组:维度 + 方向 */
.toolbar-sort { display: flex; align-items: center; gap: 8px; }
/* 首页:一行一个英雄的列表布局 */
.hero-rows {