| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- 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<FindPartnerSection['id'], string | undefined>
- }
- 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<FindPartnerPopupState>({
- 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<ApplyListener>()
- 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,
- }
- }
|