优化 PC 首页点击式筛选与排序体验

默认区平铺常用条件并支持刀皮/红皮/枪皮多选,高级筛选折叠展示;修复综合推荐无法选中、筛选排序导致页面回顶等问题。
This commit is contained in:
yml2213
2026-07-18 20:59:21 +08:00
parent 1a4776bba0
commit ed4d3f011c
8 changed files with 667 additions and 230 deletions
@@ -39,20 +39,26 @@ const announcements = ref<string[]>(defaultHomeAnnouncements)
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions)
const sortBy = ref('recommended')
const sortOptions = [
{ value: 'recommended', label: '综合推荐' },
{ value: 'coinDesc', label: '哈夫币' },
{ value: 'awmDesc', label: 'AWM数量' },
{ value: 'priceAsc', label: '价格最低' },
{ value: 'priceDesc', label: '价格最高' },
] as const
const activeZone = ref('all')
const route = useRoute()
const router = useRouter()
const {
filters,
activeFilterPopover,
regionOptions,
loginMethodOptions,
skinFilterGroups,
skinChipLabel,
advancedSkinFilterGroups,
advancedFiltersOpen,
advancedActiveCount,
resetFilters,
setFilterPopover,
closeFilterPopover,
} = useHomeFilters(
publishOptions,
computed(() => listings.value)
@@ -167,9 +173,10 @@ function handleResetFilters() {
function applyRouteQueryToHomeState() {
const storedQuery = readStoredHomeQuery(desktopHomeFilterStorageKey)
const query = hasHomeQueryValues(route.query, desktopHomeFilterQueryKeys)
? route.query
: storedQuery
const routeHasManaged = hasHomeQueryValues(route.query, desktopHomeFilterQueryKeys)
// 路由有托管参数时以路由为准;否则回退 session。
// 注意:综合推荐时 sort 不在 URL,若路由仍有其它筛选参数,也不要用 storage 覆盖 sort。
const query = routeHasManaged ? route.query : storedQuery
filters.keyword = readQueryString(query, 'keyword')
filters.server = readQueryString(query, 'server')
filters.region = readQueryCSV(query, 'region')
@@ -178,8 +185,8 @@ function applyRouteQueryToHomeState() {
filters.insurance = readQueryCSV(query, 'insurance')
filters.stamina = readQueryCSV(query, 'stamina')
filters.load = readQueryCSV(query, 'load')
filters.skinGroup = readQueryString(query, 'skin_group')
filters.skinName = readQueryString(query, 'skin_name')
// 兼容旧单值 skin_name / 新 CSV 多选
filters.skinNames = readQueryCSV(query, 'skin_name')
filters.minCoin = readQueryNumber(query, 'min_coin')
filters.maxCoin = readQueryNumber(query, 'max_coin')
filters.minRatio = readQueryNumber(query, 'min_ratio')
@@ -192,6 +199,7 @@ function applyRouteQueryToHomeState() {
filters.maxTotal = readQueryNumber(query, 'max_total')
filters.minFireLevel = readQueryNumber(query, 'min_fire_level')
filters.maxFireLevel = readQueryNumber(query, 'max_fire_level')
// 无 sort 参数即综合推荐(默认)
sortBy.value = readQueryString(query, 'sort') || 'recommended'
// zone 不从 query/storage 恢复,由用户实时点击决定,不参与筛选持久化
}
@@ -206,8 +214,12 @@ function syncHomeQuery() {
desktopHomeFilterQueryKeys,
buildDesktopHomeQueryValues()
)
// 综合推荐不写 sort;若当前无任何筛选条件,必须清空 storage,
// 否则会回退读到旧的 sort=coinDesc 等,导致无法选回「综合推荐」。
if (hasHomeQueryValues(query, desktopHomeFilterQueryKeys)) {
storeHomeQuery(desktopHomeFilterStorageKey, query)
} else {
clearStoredHomeQuery(desktopHomeFilterStorageKey)
}
if (isSameQuery(route.query, query)) return
router.replace({ path: '/', query })
@@ -224,8 +236,7 @@ function buildDesktopHomeQueryValues(): Record<string, HomeQueryValue> {
insurance: joinQueryCSV(filters.insurance),
stamina: joinQueryCSV(filters.stamina),
load: joinQueryCSV(filters.load),
skin_group: filters.skinGroup || undefined,
skin_name: filters.skinName || undefined,
skin_name: joinQueryCSV(filters.skinNames),
min_coin: filters.minCoin,
max_coin: filters.maxCoin,
min_ratio: filters.minRatio,
@@ -273,12 +284,12 @@ loadHome()
:region-options="regionOptions"
:login-method-options="loginMethodOptions"
:skin-filter-groups="skinFilterGroups"
:skin-chip-label="skinChipLabel"
:active-filter-popover="activeFilterPopover"
:advanced-skin-filter-groups="advancedSkinFilterGroups"
:advanced-open="advancedFiltersOpen"
:advanced-active-count="advancedActiveCount"
@update:filters="updateFilters"
@update:advanced-open="advancedFiltersOpen = $event"
@reset="handleResetFilters"
@set-filter-popover="setFilterPopover"
@close-filter-popover="closeFilterPopover"
/>
<div class="list-head">
@@ -286,14 +297,17 @@ loadHome()
<p class="eyebrow">Account Zone</p>
<h2>账号专区</h2>
</div>
<div class="list-actions">
<el-radio-group v-model="sortBy" size="small">
<el-radio-button label="recommended">综合推荐</el-radio-button>
<el-radio-button label="coinDesc">哈夫币</el-radio-button>
<el-radio-button label="awmDesc">AWM数量</el-radio-button>
<el-radio-button label="priceAsc">价格最低</el-radio-button>
<el-radio-button label="priceDesc">价格最高</el-radio-button>
</el-radio-group>
<div class="list-actions sort-bar" role="group" aria-label="排序方式">
<button
v-for="item in sortOptions"
:key="item.value"
type="button"
class="sort-chip"
:class="{ active: sortBy === item.value }"
@click="sortBy = item.value"
>
{{ item.label }}
</button>
</div>
</div>
@@ -401,6 +415,8 @@ loadHome()
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
}
.zone-head {
@@ -425,6 +441,43 @@ loadHome()
color: #17233d;
}
.sort-bar {
display: inline-flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
padding: 4px;
border-radius: 12px;
background: #f3f5f9;
}
.sort-chip {
min-height: 32px;
padding: 0 14px;
border: none;
border-radius: 9px;
background: transparent;
color: #64748b;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition:
background 0.15s ease,
color 0.15s ease,
box-shadow 0.15s ease;
}
.sort-chip:hover {
color: #334155;
background: rgba(255, 255, 255, 0.7);
}
.sort-chip.active {
background: #ffffff;
color: #ff6a00;
box-shadow: 0 1px 4px rgba(23, 35, 61, 0.08);
}
.enhanced-desktop-list {
display: grid;
gap: 16px;