feat(home): 新增AWM子弹区间筛选,重排快速筛选为两列布局(租金移出高级区,登录方式固定右列)
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user