import { onUnmounted, reactive, readonly, watch } from 'vue' import { useBaseConstsStore } from '~/stores/baseConsts' import type { BaseConstsConfig } from '~/types/api' export interface FindPartnerTag { id: string /** * i18n key for the tag label */ labelKey: string } export interface FindPartnerSection { id: 'gender' | 'age' | 'region' | 'rating' | 'price' /** * i18n key for the section title */ titleKey: string tags: FindPartnerTag[] } export interface FindPartnerPopupState { visible: boolean sections: FindPartnerSection[] /** 每个 section 当前选中的 tag id,未选中则为 undefined */ selected: Record } export interface FindPartnerFilterPayload { /** 后端 gender 参数 */ gender: number /** 后端 ageRange 参数 */ ageRange: number /** 后端 areCode 参数 */ areCode: string /** 后端 priceRange 参数 */ priceRange: number /** 后端 sortByStar 参数(结合弹窗与外部排序) */ sortByStar: number } const initialSelected: FindPartnerPopupState['selected'] = { gender: 'unlimited', age: 'unlimited', region: '', rating: 'unlimited', price: 'unlimited', } const state = reactive({ visible: false, sections: [ { id: 'gender', titleKey: 'home.findPartner.sections.gender.title', tags: [ { id: 'unlimited', labelKey: 'home.findPartner.sections.gender.tags.unlimited', }, { id: 'male', labelKey: 'home.findPartner.sections.gender.tags.male', }, { id: 'female', labelKey: 'home.findPartner.sections.gender.tags.female', }, ], }, { id: 'age', titleKey: 'home.findPartner.sections.age.title', tags: [ { id: 'unlimited', labelKey: 'home.findPartner.sections.age.tags.unlimited', }, { id: '15-25', labelKey: 'home.findPartner.sections.age.tags.range_15_25', }, { id: '25-35', labelKey: 'home.findPartner.sections.age.tags.range_25_35', }, { id: '35+', labelKey: 'home.findPartner.sections.age.tags.range_35_plus', }, ], }, { id: 'region', titleKey: 'home.findPartner.sections.region.title', tags: [], }, { id: 'rating', titleKey: 'home.findPartner.sections.rating.title', tags: [ { id: 'unlimited', labelKey: 'home.findPartner.sections.rating.tags.unlimited', }, { id: 'high-to-low', labelKey: 'home.findPartner.sections.rating.tags.highToLow', }, { id: 'low-to-high', labelKey: 'home.findPartner.sections.rating.tags.lowToHigh', }, ], }, { id: 'price', titleKey: 'home.findPartner.sections.price.title', tags: [ { id: 'unlimited', labelKey: 'home.findPartner.sections.price.tags.unlimited', }, { id: '0-10', labelKey: 'home.findPartner.sections.price.tags.range_0_10', }, { id: '10-15', labelKey: 'home.findPartner.sections.price.tags.range_10_15', }, { id: '15-25', labelKey: 'home.findPartner.sections.price.tags.range_15_25', }, { id: '25-50', labelKey: 'home.findPartner.sections.price.tags.range_25_50', }, { id: 'over50', labelKey: 'home.findPartner.sections.price.tags.range_over50', }, ], }, ], selected: { ...initialSelected }, }) const open = () => { state.visible = true } const close = () => { state.visible = false } const toggleTag = (sectionId: FindPartnerSection['id'], tagId: string) => { const current = state.selected[sectionId] state.selected[sectionId] = current === tagId ? undefined : tagId } const resetFilters = () => { state.selected = { ...initialSelected, } } // ===== 弹窗选项 -> 列表接口参数映射 ===== const mapGenderToBackend = (id?: string): number => { // 性别:-1:无限制,0:未知,1:男,2:女 if (!id || id === 'unlimited') return -1 if (id === 'male') return 1 if (id === 'female') return 2 return -1 } const mapAgeToBackend = (id?: string): number => { // 年龄,-1:不限制,0:15-25,1:25-35,2:35以上 if (!id || id === 'unlimited') return -1 if (id === '15-25') return 0 if (id === '25-35') return 1 if (id === '35+') return 2 return -1 } const mapRegionToBackend = (id?: string): string => { // 区域编码,暂无“不限”选项;未选择时传空字符串 if (!id) return '' return id } const mapPriceToBackend = (id?: string): number => { // 价格区间:-1:无限制,0:[0,10],1:(10,15],2:(15,25],3:(25,50],4:over50 if (!id || id === 'unlimited') return -1 if (id === '0-10') return 0 if (id === '10-15') return 1 if (id === '15-25') return 2 if (id === '25-50') return 3 if (id === 'over50') return 4 return -1 } const mapRatingToSortByStar = (id: string | undefined, currentSortByStar: number): number => { // 评分排序,-1:默认排序,0:升序,1:降序 if (!id || id === 'unlimited') return currentSortByStar if (id === 'high-to-low') return 1 if (id === 'low-to-high') return 0 return currentSortByStar } const getFilters = (currentSortByStar: number): FindPartnerFilterPayload => { const gender = mapGenderToBackend(state.selected.gender) const ageRange = mapAgeToBackend(state.selected.age) const areCode = mapRegionToBackend(state.selected.region) const priceRange = mapPriceToBackend(state.selected.price) const sortByStar = mapRatingToSortByStar(state.selected.rating, currentSortByStar) return { gender, ageRange, areCode, priceRange, sortByStar, } } type ApplyListener = () => void const applyListeners = new Set() const onApply = (listener: ApplyListener) => { applyListeners.add(listener) onUnmounted(() => { applyListeners.delete(listener) }) } const applyFilters = () => { applyListeners.forEach(listener => listener()) } const regionTagsInitializedFromBaseConsts = ref(false) const setupRegionTagsFromBaseConsts = () => { if (regionTagsInitializedFromBaseConsts.value) return const baseConstsStore = useBaseConstsStore() const updateRegionTags = (config: BaseConstsConfig | null) => { const regionSection = state.sections.find(section => section.id === 'region') if (!regionSection) return const list = config?.commonAreaConsts if (!Array.isArray(list) || list.length === 0) return regionSection.tags = list .filter(item => item && item.code) .map(item => ({ // areCode 直接取后端提供的区域 code id: String(item.code), // 这里直接使用后端下发的 name,i18n 未配置时会直接展示该文案 labelKey: String(item.name), })) regionTagsInitializedFromBaseConsts.value = true } watch( () => baseConstsStore.config, (config) => { updateRegionTags(config) }, { immediate: true, }, ) } export const useFindPartnerPopup = () => { setupRegionTagsFromBaseConsts() return { state: readonly(state), open, close, toggleTag, resetFilters, getFilters, onApply, applyFilters, } }