feat(home): 新增AWM子弹区间筛选,重排快速筛选为两列布局(租金移出高级区,登录方式固定右列)

This commit is contained in:
yml2213
2026-08-15 11:56:06 +08:00
parent 7cd6699b84
commit d75ebe4192
7 changed files with 153 additions and 48 deletions
@@ -1,7 +1,9 @@
<script setup lang="ts">
import { computed } from 'vue'
import { Refresh } from '@element-plus/icons-vue'
import { ElButton, ElIcon } from 'element-plus'
import { ElButton } from 'element-plus'
import {
awmRangeOptions,
coinRangeOptions,
ratioRangeOptions,
moneyRangeOptions,
@@ -60,8 +62,22 @@ function toggleSkin(name: string) {
}
function toggleRange(
minKey: 'minCoin' | 'minRatio' | 'minPrice' | 'minDeposit' | 'minTotal' | 'minFireLevel',
maxKey: 'maxCoin' | 'maxRatio' | 'maxPrice' | 'maxDeposit' | 'maxTotal' | 'maxFireLevel',
minKey:
| 'minCoin'
| 'minRatio'
| 'minPrice'
| 'minDeposit'
| 'minTotal'
| 'minFireLevel'
| 'minAwmAmmo',
maxKey:
| 'maxCoin'
| 'maxRatio'
| 'maxPrice'
| 'maxDeposit'
| 'maxTotal'
| 'maxFireLevel'
| 'maxAwmAmmo',
option: RangeOption
) {
const currentMin = props.filters[minKey]
@@ -74,8 +90,22 @@ function toggleRange(
}
function isRangeActive(
minKey: 'minCoin' | 'minRatio' | 'minPrice' | 'minDeposit' | 'minTotal' | 'minFireLevel',
maxKey: 'maxCoin' | 'maxRatio' | 'maxPrice' | 'maxDeposit' | 'maxTotal' | 'maxFireLevel',
minKey:
| 'minCoin'
| 'minRatio'
| 'minPrice'
| 'minDeposit'
| 'minTotal'
| 'minFireLevel'
| 'minAwmAmmo',
maxKey:
| 'maxCoin'
| 'maxRatio'
| 'maxPrice'
| 'maxDeposit'
| 'maxTotal'
| 'maxFireLevel'
| 'maxAwmAmmo',
option: RangeOption
) {
return isRangeSelected(props.filters[minKey], props.filters[maxKey], option.min, option.max)
@@ -84,6 +114,14 @@ function isRangeActive(
function toggleAdvanced() {
emit('update:advancedOpen', !props.advancedOpen)
}
/** 刀皮/红皮作为独立左列字段展示(与全宽皮肤区区分) */
const meleeSkinGroup = computed(() =>
props.skinFilterGroups.find(group => group.key === 'melee')
)
const redSkinGroup = computed(() =>
props.skinFilterGroups.find(group => group.key === 'operatorRed')
)
</script>
<template>
@@ -130,6 +168,22 @@ function toggleAdvanced() {
</div>
</div>
<div class="filter-field">
<label>AWM子弹</label>
<div class="pill-row">
<button
v-for="item in awmRangeOptions.filter(o => o.label !== '全部')"
:key="item.label"
type="button"
class="pill-chip"
:class="{ active: isRangeActive('minAwmAmmo', 'maxAwmAmmo', item) }"
@click="toggleRange('minAwmAmmo', 'maxAwmAmmo', item)"
>
{{ item.label }}
</button>
</div>
</div>
<div class="filter-field">
<label>保险</label>
<div class="pill-row">
@@ -147,17 +201,17 @@ function toggleAdvanced() {
</div>
<div class="filter-field">
<label>体力</label>
<label>租金</label>
<div class="pill-row">
<button
v-for="item in publishOptions.level_options"
:key="`stamina-${item}`"
v-for="item in moneyRangeOptions.filter(o => o.label !== '全部')"
:key="`price-${item.label}`"
type="button"
class="pill-chip"
:class="{ active: filters.stamina.includes(item) }"
@click="toggleChip('stamina', item)"
:class="{ active: isRangeActive('minPrice', 'maxPrice', item) }"
@click="toggleRange('minPrice', 'maxPrice', item)"
>
{{ item }}
{{ item.label }}
</button>
</div>
</div>
@@ -178,7 +232,55 @@ function toggleAdvanced() {
</div>
</div>
<div v-if="meleeSkinGroup" class="filter-field">
<label>{{ meleeSkinGroup.title }}</label>
<div class="pill-row">
<button
v-for="skin in meleeSkinGroup.options"
:key="`melee-${skin}`"
type="button"
class="pill-chip"
:class="{ active: filters.skinNames.includes(skin) }"
@click="toggleSkin(skin)"
>
{{ skin }}
</button>
</div>
</div>
<div class="filter-field">
<label>体力</label>
<div class="pill-row">
<button
v-for="item in publishOptions.level_options"
:key="`stamina-${item}`"
type="button"
class="pill-chip"
:class="{ active: filters.stamina.includes(item) }"
@click="toggleChip('stamina', item)"
>
{{ item }}
</button>
</div>
</div>
<div v-if="redSkinGroup" class="filter-field">
<label>{{ redSkinGroup.title }}</label>
<div class="pill-row">
<button
v-for="skin in redSkinGroup.options"
:key="`red-${skin}`"
type="button"
class="pill-chip"
:class="{ active: filters.skinNames.includes(skin) }"
@click="toggleSkin(skin)"
>
{{ skin }}
</button>
</div>
</div>
<div class="filter-field login-method-field">
<label>登录方式</label>
<div class="pill-row">
<button
@@ -193,26 +295,6 @@ function toggleAdvanced() {
</button>
</div>
</div>
<div
v-for="group in skinFilterGroups"
:key="group.key"
class="filter-field filter-field-full"
>
<label>{{ group.title }}</label>
<div class="pill-row">
<button
v-for="skin in group.options"
:key="`${group.key}-${skin}`"
type="button"
class="pill-chip"
:class="{ active: filters.skinNames.includes(skin) }"
@click="toggleSkin(skin)"
>
{{ skin }}
</button>
</div>
</div>
</div>
<div class="advanced-toggle-row">
@@ -225,22 +307,6 @@ function toggleAdvanced() {
<div v-show="advancedOpen" class="advanced-filter-panel">
<div class="quick-filter-grid">
<div class="filter-field">
<label>租金</label>
<div class="pill-row">
<button
v-for="item in moneyRangeOptions.filter(o => o.label !== '全部')"
:key="`price-${item.label}`"
type="button"
class="pill-chip"
:class="{ active: isRangeActive('minPrice', 'maxPrice', item) }"
@click="toggleRange('minPrice', 'maxPrice', item)"
>
{{ item.label }}
</button>
</div>
</div>
<div class="filter-field">
<label>押金</label>
<div class="pill-row">
@@ -9,6 +9,16 @@ export const coinRangeOptions = [
{ label: '300以上', min: 300, max: undefined },
]
/** AWM 子弹数量区间(与移动端档位保持一致) */
export const awmRangeOptions = [
{ label: '全部', min: undefined, max: undefined },
{ label: '0-20', min: 0, max: 20 },
{ label: '20-50', min: 20, max: 50 },
{ label: '50-100', min: 50, max: 100 },
{ label: '100-200', min: 100, max: 200 },
{ label: '200以上', min: 200, max: undefined },
]
export const ratioRangeOptions = [
{ label: '全部', min: undefined, max: undefined },
{ label: '≤30', min: undefined, max: 30 },
@@ -26,6 +26,8 @@ export const desktopHomeFilterQueryKeys = [
'max_total',
'min_fire_level',
'max_fire_level',
'resource_awmAmmo_min',
'resource_awmAmmo_max',
] as const
export const mobileHomeFilterQueryKeys = ['keyword', 'sort', 'zone', 'filters', 'ranges'] as const
@@ -52,6 +52,9 @@ export interface HomeFilters {
maxTotal: number | undefined
minFireLevel: number | undefined
maxFireLevel: number | undefined
/** AWM 子弹数量区间 */
minAwmAmmo: number | undefined
maxAwmAmmo: number | undefined
}
export interface SkinFilterGroup {
@@ -86,6 +89,8 @@ export function useHomeFilters(
maxTotal: undefined,
minFireLevel: undefined,
maxFireLevel: undefined,
minAwmAmmo: undefined,
maxAwmAmmo: undefined,
})
const activeFilterPopover = ref<FilterPopoverKey | ''>('')
@@ -145,7 +150,6 @@ export function useHomeFilters(
let count = 0
if (filters.region.length) count += 1
if (filters.rank.length) count += 1
if (filters.minPrice !== undefined || filters.maxPrice !== undefined) count += 1
if (filters.minDeposit !== undefined || filters.maxDeposit !== undefined) count += 1
if (filters.minTotal !== undefined || filters.maxTotal !== undefined) count += 1
if (filters.minFireLevel !== undefined || filters.maxFireLevel !== undefined) count += 1
@@ -178,6 +182,8 @@ export function useHomeFilters(
filters.maxTotal = undefined
filters.minFireLevel = undefined
filters.maxFireLevel = undefined
filters.minAwmAmmo = undefined
filters.maxAwmAmmo = undefined
}
function setStringFilter(key: StringFilterKey, value: string | string[]) {
@@ -230,6 +236,11 @@ export function useHomeFilters(
filters.maxFireLevel = max
}
function setAwmAmmoRange(min: number | undefined, max: number | undefined) {
filters.minAwmAmmo = min
filters.maxAwmAmmo = max
}
/** 区间 chip:再点同一档则清空 */
function toggleRange(
currentMin: number | undefined,
@@ -277,6 +288,7 @@ export function useHomeFilters(
setDepositRange,
setTotalRange,
setFireLevelRange,
setAwmAmmoRange,
toggleRange,
setFilterPopover,
closeFilterPopover,
@@ -50,6 +50,8 @@ export function useListingQuery(
max_total: filters.maxTotal,
min_fire_level: filters.minFireLevel,
max_fire_level: filters.maxFireLevel,
resource_awmAmmo_min: filters.minAwmAmmo,
resource_awmAmmo_max: filters.maxAwmAmmo,
}
}
@@ -216,6 +216,8 @@ function applyRouteQueryToHomeState() {
filters.maxTotal = readQueryNumber(query, 'max_total')
filters.minFireLevel = readQueryNumber(query, 'min_fire_level')
filters.maxFireLevel = readQueryNumber(query, 'max_fire_level')
filters.minAwmAmmo = readQueryNumber(query, 'resource_awmAmmo_min')
filters.maxAwmAmmo = readQueryNumber(query, 'resource_awmAmmo_max')
// 无 sort 参数即综合推荐(默认)
sortBy.value = readQueryString(query, 'sort') || 'recommended'
// zone 不从 query/storage 恢复,由用户实时点击决定,不参与筛选持久化
@@ -266,6 +268,8 @@ function buildDesktopHomeQueryValues(): Record<string, HomeQueryValue> {
max_total: filters.maxTotal,
min_fire_level: filters.minFireLevel,
max_fire_level: filters.maxFireLevel,
resource_awmAmmo_min: filters.minAwmAmmo,
resource_awmAmmo_max: filters.maxAwmAmmo,
}
}
+9
View File
@@ -68,6 +68,11 @@
grid-column: 1 / -1;
}
/* 登录方式固定到右侧第二列(与「体力」同行的右列),避免左侧列过挤 */
.login-method-field {
grid-column: 2;
}
.filter-field label {
padding-top: 6px;
font-size: 13px;
@@ -176,6 +181,10 @@
.quick-filter-grid {
grid-template-columns: 1fr;
}
.login-method-field {
grid-column: auto;
}
}
.filter-chip-row {