优化筛选, 增加记住筛选功能
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { LocationQuery } from 'vue-router'
|
||||
import {
|
||||
decodeHomeQueryJson,
|
||||
encodeHomeQueryJson,
|
||||
isSameQuery,
|
||||
mergeHomeQuery,
|
||||
readQueryNumber,
|
||||
readQueryString,
|
||||
} from '@/features/listings/composables/useHomeFilterQuery'
|
||||
|
||||
describe('useHomeFilterQuery', () => {
|
||||
it('应该从路由 query 中读取字符串和数字', () => {
|
||||
const query: LocationQuery = {
|
||||
keyword: ['测试账号'],
|
||||
min_coin: '100',
|
||||
max_coin: 'abc',
|
||||
}
|
||||
|
||||
expect(readQueryString(query, 'keyword')).toBe('测试账号')
|
||||
expect(readQueryNumber(query, 'min_coin')).toBe(100)
|
||||
expect(readQueryNumber(query, 'max_coin')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('应该合并新筛选并移除旧的托管参数', () => {
|
||||
const query = mergeHomeQuery(
|
||||
{ keyword: '旧关键词', page: '2', region: '上海' },
|
||||
['keyword', 'region', 'sort'],
|
||||
{ keyword: '新关键词', sort: undefined }
|
||||
)
|
||||
|
||||
expect(query).toEqual({
|
||||
page: '2',
|
||||
keyword: '新关键词',
|
||||
})
|
||||
})
|
||||
|
||||
it('应该判断 query 是否语义一致', () => {
|
||||
expect(isSameQuery({ keyword: '测试', empty: '' }, { empty: undefined, keyword: '测试' })).toBe(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('应该安全编码和恢复移动端动态筛选', () => {
|
||||
const filters = { server: ['微信区'], operatorRed: ['红皮肤'] }
|
||||
const encoded = encodeHomeQueryJson(filters)
|
||||
|
||||
expect(encoded).toBe(JSON.stringify(filters))
|
||||
expect(decodeHomeQueryJson({ filters: encoded }, 'filters', {})).toEqual(filters)
|
||||
expect(decodeHomeQueryJson({ filters: '{' }, 'filters', { server: [] })).toEqual({
|
||||
server: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,105 @@
|
||||
import type { LocationQuery, LocationQueryRaw } from 'vue-router'
|
||||
|
||||
export const desktopHomeFilterQueryKeys = [
|
||||
'keyword',
|
||||
'sort',
|
||||
'zone',
|
||||
'server',
|
||||
'region',
|
||||
'login_method',
|
||||
'rank',
|
||||
'insurance',
|
||||
'stamina',
|
||||
'load',
|
||||
'skin_group',
|
||||
'skin_name',
|
||||
'min_coin',
|
||||
'max_coin',
|
||||
'min_price',
|
||||
'max_price',
|
||||
'min_deposit',
|
||||
'max_deposit',
|
||||
'min_total',
|
||||
'max_total',
|
||||
'min_fire_level',
|
||||
'max_fire_level',
|
||||
] as const
|
||||
|
||||
export const mobileHomeFilterQueryKeys = ['keyword', 'sort', 'zone', 'filters', 'ranges'] as const
|
||||
|
||||
export type HomeQueryValue = string | number | undefined
|
||||
|
||||
export function readQueryString(query: LocationQuery, key: string) {
|
||||
const value = query[key]
|
||||
if (Array.isArray(value)) return String(value[0] || '')
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
export function readQueryNumber(query: LocationQuery, key: string) {
|
||||
const value = readQueryString(query, key)
|
||||
if (value === '') return undefined
|
||||
const number = Number(value)
|
||||
return Number.isFinite(number) ? number : undefined
|
||||
}
|
||||
|
||||
export function mergeHomeQuery(
|
||||
currentQuery: LocationQuery,
|
||||
managedKeys: readonly string[],
|
||||
nextValues: Record<string, HomeQueryValue>
|
||||
): LocationQueryRaw {
|
||||
const managedKeySet = new Set(managedKeys)
|
||||
const nextQuery: LocationQueryRaw = {}
|
||||
|
||||
for (const [key, value] of Object.entries(currentQuery)) {
|
||||
if (!managedKeySet.has(key)) nextQuery[key] = value
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(nextValues)) {
|
||||
if (value === undefined || value === '') continue
|
||||
nextQuery[key] = String(value)
|
||||
}
|
||||
|
||||
return nextQuery
|
||||
}
|
||||
|
||||
export function isSameQuery(left: LocationQuery, right: LocationQueryRaw) {
|
||||
return stableQuerySignature(left) === stableQuerySignature(right)
|
||||
}
|
||||
|
||||
export function encodeHomeQueryJson(value: unknown) {
|
||||
if (!hasMeaningfulValue(value)) return undefined
|
||||
return JSON.stringify(value)
|
||||
}
|
||||
|
||||
export function decodeHomeQueryJson<T>(query: LocationQuery, key: string, fallback: T): T {
|
||||
const raw = readQueryString(query, key)
|
||||
if (!raw) return fallback
|
||||
try {
|
||||
const parsed = JSON.parse(raw)
|
||||
return parsed && typeof parsed === 'object' ? (parsed as T) : fallback
|
||||
} catch {
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
function stableQuerySignature(query: LocationQuery | LocationQueryRaw) {
|
||||
const entries: Array<[string, string | string[]]> = Object.entries(query)
|
||||
.filter(([, value]) => value !== undefined && value !== '')
|
||||
.map(([key, value]) => [
|
||||
key,
|
||||
Array.isArray(value) ? value.map(item => String(item ?? '')) : String(value ?? ''),
|
||||
])
|
||||
|
||||
return JSON.stringify(entries.sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)))
|
||||
}
|
||||
|
||||
function hasMeaningfulValue(value: unknown): boolean {
|
||||
if (!value || typeof value !== 'object') return false
|
||||
if (Array.isArray(value)) return value.length > 0
|
||||
|
||||
return Object.values(value).some(item => {
|
||||
if (Array.isArray(item)) return item.length > 0
|
||||
if (!item || typeof item !== 'object') return item !== '' && item !== undefined
|
||||
return Object.values(item).some(nested => nested !== '' && nested !== undefined)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user