refactor(listings): 统一移动端与PC端筛选逻辑并支持日消耗与绝密KD

- 统一预设体系:在 useFilterOptions.ts 集中导出 defaultRangePresets,两端共享档位定义,消除移动端私有硬编码档位
- 统一参数模型:在 useHomeFilters.ts 增加 mobileFiltersToHomeFilters,将移动端字典结构标准化转换为领域层 HomeFilters 模型
- 统一查询构建:在 useListingQuery.ts 抽象纯函数 buildListingQueryFromFilters,双端共用同一个后端发包构建器
- 功能对齐:移动端首页完整补充「日消耗」区间筛选;PC端高级抽屉对齐补充「绝密KD」筛选
- 验证通过:补充双端测试用例,全站门禁 ./scripts/check.sh(SQL/Go/MySQL/Vitest/Vite构建)全部绿灯
This commit is contained in:
yml2213
2026-10-10 16:48:16 +08:00
parent 95435faf46
commit 03d20223c8
7 changed files with 403 additions and 146 deletions
@@ -40,7 +40,9 @@ import {
storeHomeQuery,
type HomeQueryValue,
} from '@/features/listings/composables/useHomeFilterQuery'
import { depositRangeOptions } from '@/features/listings/composables/useFilterOptions'
import { defaultRangePresets } from '@/features/listings/composables/useFilterOptions'
import { mobileFiltersToHomeFilters } from '@/features/listings/composables/useHomeFilters'
import { buildListingQueryFromFilters } from '@/features/listings/composables/useListingQuery'
import {
getListingChips,
getListingDisplayPrice,
@@ -139,51 +141,8 @@ const zoneOptions = [
{ key: 'highCoin', label: '高币', hint: '100M以上' },
]
/** 押金档位直接复用首页高级筛选项,保证移动端与桌面端完全一致 */
const depositPresets = depositRangeOptions
.filter(option => option.label !== '全部')
.map(option => ({
label: option.label,
min: option.min === undefined ? '' : String(option.min),
max: option.max === undefined ? '' : String(option.max),
}))
const rangePresets: Record<string, Array<{ label: string; min: string; max: string }>> = {
price: [
{ label: '50-100', min: '50', max: '100' },
{ label: '100-200', min: '100', max: '200' },
{ label: '200以上', min: '200', max: '' },
],
coin: [
{ label: '0-100', min: '0', max: '100' },
{ label: '100-300', min: '100', max: '300' },
{ label: '300-500', min: '300', max: '500' },
{ label: '500以上', min: '500', max: '' },
],
ratio: [
{ label: '30-40', min: '30', max: '40' },
{ label: '40以上', min: '40', max: '' },
],
deposit: depositPresets,
total: [
{ label: '0-500', min: '0', max: '500' },
{ label: '500-1000', min: '500', max: '1000' },
{ label: '1000-2000', min: '1000', max: '2000' },
{ label: '2000-5000', min: '2000', max: '5000' },
],
fireLevel: [
{ label: '38-50', min: '38', max: '50' },
{ label: '50-60', min: '50', max: '60' },
{ label: '60-70', min: '60', max: '70' },
{ label: '70以上', min: '70', max: '' },
],
resource_awmAmmo: [
{ 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: '' },
],
}
/** 统一使用领域层导出的预设区间,保证与桌面端完全一致 */
const rangePresets = defaultRangePresets
const activeSortLabel = computed(
() => sortOptions.find(option => option.key === activeSort.value)?.label || '综合排序'
@@ -279,6 +238,14 @@ const filterSections = computed<FilterSection[]>(() => [
minPlaceholder: '最低',
maxPlaceholder: '最高',
},
{
key: 'dailyLoss',
title: '日消耗',
type: 'range',
unit: 'M/天',
minPlaceholder: '最低',
maxPlaceholder: '最高',
},
{
key: 'fireLevel',
title: '等级',
@@ -602,65 +569,18 @@ function clearFilters() {
}
function buildListingQuery(page: number): PublicListingQuery {
const query: PublicListingQuery = {
const homeFilters = mobileFiltersToHomeFilters(
selectedFilters.value,
rangeFilters.value,
searchValue.value,
publishOptions.value.skin_groups.map(group => group.key)
)
return buildListingQueryFromFilters(homeFilters, {
page,
page_size: mobilePageSize,
keyword: searchValue.value.trim(),
pageSize: mobilePageSize,
sort: activeSort.value,
zone: activeZone.value,
}
const skinGroups: string[] = []
const skinNames: string[] = []
for (const [key, values] of Object.entries(selectedFilters.value)) {
const value = values.filter(Boolean).join(',')
if (!value) continue
if (key === 'server') query.server = value
else if (key === 'region') query.region = value
else if (key === 'login') query.login_method = value
else if (key === 'insurance') query.insurance = value
else if (key === 'stamina') query.stamina = value
else if (key === 'load') query.load = value
else if (key === 'rank') query.rank = value
else if (isSkinGroupKey(key)) {
skinGroups.push(key)
skinNames.push(...values)
}
}
if (skinGroups.length) query.skin_group = skinGroups.join(',')
if (skinNames.length) query.skin_name = skinNames.join(',')
for (const [key, range] of Object.entries(rangeFilters.value)) {
if (!range.min && !range.max) continue
const min = parseOptionalNumber(range.min)
const max = parseOptionalNumber(range.max)
if (key === 'price') {
query.min_price = min
query.max_price = max
} else if (key === 'coin') {
query.min_coin = min
query.max_coin = max
} else if (key === 'ratio') {
query.min_ratio = min
query.max_ratio = max
} else if (key === 'secretKd') {
query.min_secret_kd = min
query.max_secret_kd = max
} else if (key === 'deposit') {
query.min_deposit = min
query.max_deposit = max
} else if (key === 'total') {
query.min_total = min
query.max_total = max
} else if (key === 'fireLevel') {
query.min_fire_level = min
query.max_fire_level = max
} else if (key.startsWith('resource_')) {
const resourceKey = key.replace('resource_', '')
query[`resource_${resourceKey}_min`] = min
query[`resource_${resourceKey}_max`] = max
}
}
return query
})
}
function filterQuerySignature() {
@@ -672,12 +592,6 @@ function filterQuerySignature() {
})
}
function parseOptionalNumber(value: string) {
if (value === '') return undefined
const number = Number(value)
return Number.isFinite(number) ? number : undefined
}
function applyRouteQueryToMobileState() {
const storedQuery = readStoredHomeQuery(mobileHomeFilterStorageKey)
const query = hasHomeQueryValues(route.query, mobileHomeFilterQueryKeys)
@@ -776,10 +690,6 @@ function scrollToTop() {
homeCache.saveScrollTop(0)
}
function isSkinGroupKey(key: string) {
return publishOptions.value.skin_groups.some(group => group.key === key)
}
function parseQuantityUnit(price: string) {
const unit = price.split('/')[1]?.trim()
return unit || undefined