From 17015c0cca092a9173e9b5f21f35d118897c59de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Sat, 22 Nov 2025 22:50:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A1=8C=E6=94=BF=E5=8C=BA?= =?UTF-8?q?=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/worktrees.json | 5 + src/components/Bubble/bubbleItem.module.scss | 2 +- .../DistanceQuickFilterV2/index.scss | 265 ++++++++++++++++++ .../DistanceQuickFilterV2/index.tsx | 176 ++++++++++++ src/components/HomeNavbar/index.tsx | 78 +++++- src/components/Picker/CityPickerV2.tsx | 77 +++++ src/components/Picker/index.ts | 1 + src/main_pages/components/ListPageContent.tsx | 23 +- src/services/listApi.ts | 13 + src/store/listStore.ts | 110 +++++--- types/list/types.ts | 7 +- 11 files changed, 708 insertions(+), 49 deletions(-) create mode 100644 .cursor/worktrees.json create mode 100644 src/components/DistanceQuickFilterV2/index.scss create mode 100644 src/components/DistanceQuickFilterV2/index.tsx create mode 100644 src/components/Picker/CityPickerV2.tsx diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json new file mode 100644 index 0000000..77e9744 --- /dev/null +++ b/.cursor/worktrees.json @@ -0,0 +1,5 @@ +{ + "setup-worktree": [ + "npm install" + ] +} diff --git a/src/components/Bubble/bubbleItem.module.scss b/src/components/Bubble/bubbleItem.module.scss index b916494..4aa7c8b 100644 --- a/src/components/Bubble/bubbleItem.module.scss +++ b/src/components/Bubble/bubbleItem.module.scss @@ -81,7 +81,7 @@ // 标签样式 .bubbleLabel { - font-weight: 500; + font-weight: 600; font-size: 12px !important; } diff --git a/src/components/DistanceQuickFilterV2/index.scss b/src/components/DistanceQuickFilterV2/index.scss new file mode 100644 index 0000000..9a20ec9 --- /dev/null +++ b/src/components/DistanceQuickFilterV2/index.scss @@ -0,0 +1,265 @@ +.distanceQuickFilterWrap { + width: 100%; + font-family: "PingFang SC"; + position: relative; + + + + + // 全局覆盖 NutUI Menu 容器的 overflow 样式 + + + .nut-menu-container-wrap { + position: fixed !important; + left: 0 !important; + right: 0 !important; + width: auto !important; + border-bottom-left-radius: 30px; + border-bottom-right-radius: 30px; + background-color: #fafafa !important; + z-index: 1100 !important; + box-sizing: border-box !important; + max-height: auto !important; + height: 380px !important; + + } + + .nut-menu-container-content { + overflow: visible !important; + padding-left: 0px !important; + padding-right: 0px !important; + padding-bottom: 0px !important; + background-color: transparent !important; + max-height: auto !important; + + } + + .nut-menu-bar { + --nutui-menu-bar-line-height: 30px; + background-color: #fafafa !important; // 明确设置背景色,避免组件默认样式覆盖 + box-shadow: unset; + // padding: 0 15px; + gap: 5px; + justify-content: flex-start; + } + + .nut-menu-title { + flex: unset; + box-sizing: border-box; + display: flex; + height: 28px; + padding: 4px 10px; + justify-content: center; + align-items: center; + gap: 2px; + border-radius: 999px; + border: 0.5px solid rgba(0, 0, 0, 0.06); // 根据设计稿添加边框 + background: #ffffff; + font-family: "PingFang SC"; // 根据设计稿设置字体 + font-size: 14px; + font-weight: 600; + line-height: 20px; // 1.4285714285714286em ≈ 20px + letter-spacing: -0.23px; // -1.6428571726594652% of 14px + } + + .nut-menu-title.active { + color: #000; + } + + + + .positionWrap { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin-bottom: 16px; + padding-left: 20px ; + padding-right: 20px ; + } + + .title { + font-size: 14px; + font-weight: 600; + } + + .cityName { + font-size: 13px; + font-weight: 400; + color: #3c3c43; + } + + .distanceWrap { + margin-bottom: 16px; + width: 100%; + padding-left: 20px; + padding-right: 20px; + } + + .distanceBubbleItem { + display: flex; + width: 80px; + height: 28px; + padding: 4px 10px; + justify-content: center; + align-items: center; + gap: 2px; + border-radius: 999px; + border: 0.5px solid rgba(0, 0, 0, 0.06); + font-weight: 600; + } + + .itemIcon { + width: 20px; + height: 20px; + } + + // 新增:行政区选择区域样式 + .districtWrap2 { + width: 100%; + margin-top: 0; + padding-top: 0; + display: flex; + flex-direction: column; + + .districtContent { + display: flex; + align-items: stretch; // 让子元素高度一致 + flex-direction: row; + width: 100%; + position: relative; + padding: 0; + gap: 0; + flex: 1; // 占满父容器剩余空间 + overflow: hidden; // 确保子元素不会超出圆角 + + .districtTitleBox { + display: flex; + align-items: flex-start; + flex-direction: column; + justify-content: flex-start; + position: relative; + height: 100%; // 占满父容器高度 + width: 82px; + + .districtTitle { + flex-shrink: 0; + padding: 10px 0 10px 20px; // 左侧标题的 padding + z-index: 1; + font-family: "PingFang SC"; + font-size: 14px; + font-weight: 600; + line-height: 1.4285714285714286em; // 20px + letter-spacing: -0.23px; + color: #000; + margin: 0; + white-space: nowrap; + position: relative; + } + + .districtTitleBg { + width: 100%; // 自适应宽度 + height: 100%; // 自适应高度 + background-color: #f2f2f7; // 灰色背景 + border-top-right-radius: 20px; // 右上角圆角 + z-index: 0; // 在文字下方 + } + } + + + .districtOptionsWrapper { + flex: 1; + max-height: 80vh; + overflow-x: hidden; + overflow-y: auto; + height: 290px; + -webkit-overflow-scrolling: touch; + + + .districtOptionsList { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0; + padding: 0; + + .districtItem { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 10px 20px 8px 20px; + height: 38px; + color: rgba(60, 60, 67, 0.6); + font-family: "PingFang SC"; + font-size: 14px; + font-weight: 600; + line-height: 1.2857142857142858em; // 18px + letter-spacing: -0.2px; + box-sizing: border-box; + cursor: pointer; + + &:first-child { + padding-top: 10px; + } + + &:not(:first-child) { + padding-top: 8px; + padding-bottom: 8px; + } + + .districtItemText { + flex: 1; + } + + &.active { + color: #000; + font-weight: 600; + } + } + } + } + } + } + + .quickOptionsWrapper { + width: 100%; + display: flex; + flex-direction: column; + padding-left: 20px; + padding-right: 20px; + + .quickItem { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 0; + color: rgba(60, 60, 67, 0.6); + font-size: 14px; + font-weight: 600; + line-height: 20px; + + &.active { + color: #000; + } + } + } +} + +.distanceQuickFilterWrap_0 .nut-menu-title-0 { + background-color: #000; + color: #fff; + + &.active { + color: #ffffff; + } +} + +.distanceQuickFilterWrap_1 .nut-menu-title-1 { + background-color: #000; + color: #fff; + + &.active { + color: #ffffff; + } +} \ No newline at end of file diff --git a/src/components/DistanceQuickFilterV2/index.tsx b/src/components/DistanceQuickFilterV2/index.tsx new file mode 100644 index 0000000..05fb0d9 --- /dev/null +++ b/src/components/DistanceQuickFilterV2/index.tsx @@ -0,0 +1,176 @@ +import { useRef, useState, useEffect } from "react"; +import { Menu } from "@nutui/nutui-react-taro"; +import { Image, View, ScrollView } from "@tarojs/components"; +import img from "@/config/images"; +import Bubble from "../Bubble"; +import "./index.scss"; + +const DistanceQuickFilterV2 = (props) => { + const { + cityOptions, + quickOptions, + districtOptions = [], // 新增:行政区选项 + onChange, + cityName, + quickName, + districtName = "district", // 新增:行政区字段名 + cityValue, + quickValue, + districtValue, // 新增:行政区选中值 + onMenuVisibleChange, // 菜单展开/收起回调 + } = props; + const cityRef = useRef(null); + const quickRef = useRef(null); + const [changePosition, setChangePosition] = useState([]); + const [isMenuOpen, setIsMenuOpen] = useState(false); + + // 全城筛选显示的标题 - 如果选择了行政区,显示行政区名称 + const getCityTitle = () => { + if (districtValue) { + const selectedDistrict = districtOptions.find((item) => item.value === districtValue); + if (selectedDistrict) { + return selectedDistrict.label; + } + } + return cityOptions.find((item) => item.value === cityValue)?.label; + }; + const cityTitle = getCityTitle(); + + // 快捷筛选显示的标题 + const quickTitle = quickOptions.find( + (item) => item.value === quickValue + )?.label; + + // className + const filterWrapperClassName = changePosition.reduce((pre, cur) => { + return `${pre} distanceQuickFilterWrap_${cur}`; + }, ""); + + // 处理选择变化 + const handleChange = ( + name: string, + value: string | number, + index: number + ) => { + setChangePosition((preState) => { + const newData = new Set([...preState, index]); + return Array.from(newData); + }); + onChange && onChange(name, value); + + // 控制隐藏 + index === 0 && (cityRef.current as any)?.toggle(false); + index === 1 && (quickRef.current as any)?.toggle(false); + }; + + // 监听菜单状态变化,通知父组件 + useEffect(() => { + onMenuVisibleChange?.(isMenuOpen); + }, [isMenuOpen, onMenuVisibleChange]); + + return ( + + setIsMenuOpen(true)} + onClose={() => setIsMenuOpen(false)} + > + } + > +
+

当前位置

+

上海市

+
+
+ { + const singleValue = Array.isArray(value) ? value[0] : value; + handleChange(name, singleValue, 0); + }} + layout="grid" + size="small" + columns={4} + itemClassName="distanceBubbleItem" + name={cityName} + /> +
+ {/* 新增:行政区选择区域 */} + {districtOptions.length > 0 && ( +
+
+
+

行政区

+
+
+ + + {districtOptions.map((item) => { + const active = districtValue === item?.value; + return ( + handleChange(districtName, item.value, 0)} + > + {item?.label} + {active && ( + + + + )} + + ); + })} + + +
+
+ )} +
+ + + {quickOptions.map((item) => { + const active = quickValue === item?.value; + return ( + handleChange(quickName, item.value, 1)} + > + {item?.label} + {active && ( + + + + )} + + ); + })} + + +
+
+ ); +}; +export default DistanceQuickFilterV2; + diff --git a/src/components/HomeNavbar/index.tsx b/src/components/HomeNavbar/index.tsx index 7c739ac..b69d592 100644 --- a/src/components/HomeNavbar/index.tsx +++ b/src/components/HomeNavbar/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import { View, Text, Image } from "@tarojs/components"; import img from "@/config/images"; import { useGlobalState } from "@/store/global"; @@ -8,7 +8,10 @@ import { Input } from "@nutui/nutui-react-taro"; import Taro from "@tarojs/taro"; import "./index.scss"; import { getCurrentFullPath } from "@/utils"; -import { CityPicker as PopupPicker } from "@/components/Picker"; +import { CityPickerV2 as PopupPicker } from "@/components/Picker"; + +// 城市缓存 key +const CITY_CACHE_KEY = "USER_SELECTED_CITY"; interface IProps { config?: { @@ -81,6 +84,7 @@ const HomeNavbar = (props: IProps) => { statusNavbarHeightInfo || {}; const [cityPopupVisible, setCityPopupVisible] = useState(false); + const hasShownLocationDialog = useRef(false); // 防止重复弹窗 // 监听城市选择器状态变化,通知父组件 useEffect(() => { @@ -88,13 +92,59 @@ const HomeNavbar = (props: IProps) => { }, [cityPopupVisible]); const userInfo = useUserInfo(); - const province = (userInfo as any)?.province || ""; - const city = (userInfo as any)?.city || ""; - // const district = (userInfo as any)?.district || ""; + const locationProvince = (userInfo as any)?.province || ""; // 定位获取的省份 + // 只使用省份,不使用区域(city、district) + // 初始化城市:优先使用缓存的定位信息,其次使用当前定位 useEffect(() => { - updateArea(["中国", province, city]); - }, [province, city]); + // 1. 尝试从缓存中读取上次的定位信息 + const cachedCity = (Taro as any).getStorageSync(CITY_CACHE_KEY); + + if (cachedCity && cachedCity.length === 2) { + // 如果有缓存的定位信息,使用缓存 + console.log("使用缓存的定位城市:", cachedCity); + updateArea(cachedCity); + + // 如果当前定位省份已获取且与缓存不同,弹窗询问是否切换 + if (locationProvince && cachedCity[1] !== locationProvince && !hasShownLocationDialog.current) { + hasShownLocationDialog.current = true; + showLocationConfirmDialog(locationProvince, cachedCity); + } + } else if (locationProvince) { + // 如果没有缓存但有定位信息,直接使用定位并保存到缓存 + console.log("没有缓存,使用当前定位省份:", locationProvince); + const newArea: [string, string] = ["中国", locationProvince]; + updateArea(newArea); + // 保存定位信息到缓存 + (Taro as any).setStorageSync(CITY_CACHE_KEY, newArea); + } + }, [locationProvince]); + + // 显示定位确认弹窗 + const showLocationConfirmDialog = (detectedProvince: string, cachedCity: [string, string]) => { + (Taro as any).showModal({ + title: "提示", + content: `检测到您当前位置在${detectedProvince},是否切换到${detectedProvince}?`, + confirmText: "是", + cancelText: "否", + success: (res) => { + if (res.confirm) { + // 用户选择"是",切换到当前定位城市 + const newArea: [string, string] = ["中国", detectedProvince]; + updateArea(newArea); + // 更新缓存为新的定位信息(只有定位的才缓存) + (Taro as any).setStorageSync(CITY_CACHE_KEY, newArea); + console.log("切换到当前定位城市并更新缓存:", detectedProvince); + + // 刷新数据 + handleCityChangeWithoutCache(); + } else { + // 用户选择"否",保持缓存的定位城市 + console.log("保持缓存的定位城市:", cachedCity[1]); + } + } + }); + }; // const currentAddress = city + district; @@ -123,6 +173,7 @@ const HomeNavbar = (props: IProps) => { duration: 300, }); } + return; // 已经在列表页,只滚动到顶部,不需要跳转 } (Taro as any).redirectTo({ url: "/main_pages/index", // 列表页 @@ -147,8 +198,8 @@ const HomeNavbar = (props: IProps) => { const area_city = area.at(-1); - // 处理城市切换 - const handleCityChange = async (_newArea: any) => { + // 处理城市切换(仅刷新数据,不保存缓存) + const handleCityChangeWithoutCache = async () => { // 切换城市后,同时更新两个列表接口获取数据 if (refreshBothLists) { await refreshBothLists(); @@ -159,6 +210,15 @@ const HomeNavbar = (props: IProps) => { } }; + // 处理城市切换(用户手动选择) + const handleCityChange = async (_newArea: any) => { + // 用户手动选择的城市不保存到缓存 + console.log("用户手动选择城市(不保存缓存):", _newArea); + + // 切换城市后,同时更新两个列表接口获取数据 + await handleCityChangeWithoutCache(); + }; + return ( void; + options?: PickerOption[][]; // 两级数据:[国家列表, 省份/城市列表] + value?: (string | number)[]; // [国家value, 省份/城市value] + onChange?: (value: (string | number)[]) => void; + style?: React.CSSProperties; +} + +const CityPickerV2 = ({ + visible, + setvisible, + value = [], + onChange, + options = [], + style, +}: CityPickerV2Props) => { + const [defaultValue, setDefaultValue] = useState<(string | number)[]>(value); + + // 当外部 value 变化时同步更新 + useEffect(() => { + if (value && value.length > 0) { + setDefaultValue(value); + } + }, [value]); + + const changePicker = (_options: any[], values: any, _columnIndex: number) => { + setDefaultValue(values); + }; + + const handleConfirm = () => { + onChange?.(defaultValue); + setvisible(false); + }; + + const dialogClose = () => { + setvisible(false); + }; + + return ( + <> + + + + + ); +}; + +export default CityPickerV2; + diff --git a/src/components/Picker/index.ts b/src/components/Picker/index.ts index 74e86a0..64a4a61 100644 --- a/src/components/Picker/index.ts +++ b/src/components/Picker/index.ts @@ -5,5 +5,6 @@ export type { PickerCommonRef } from './PickerCommon' export { default as CalendarUI } from './CalendarUI/CalendarUI' export { default as DialogCalendarCard } from './CalendarDialog/DialogCalendarCard' export { default as CityPicker } from './CityPicker' +export { default as CityPickerV2 } from './CityPickerV2' export { default as DayDialog } from './DayDialog'; export { default as HourDialog } from './HourDialog'; diff --git a/src/main_pages/components/ListPageContent.tsx b/src/main_pages/components/ListPageContent.tsx index f97207b..90768d7 100644 --- a/src/main_pages/components/ListPageContent.tsx +++ b/src/main_pages/components/ListPageContent.tsx @@ -7,7 +7,7 @@ import { useListStore } from "@/store/listStore"; import { useGlobalState } from "@/store/global"; import { View, Image, Text, ScrollView } from "@tarojs/components"; import ListContainer from "@/container/listContainer"; -import DistanceQuickFilter from "@/components/DistanceQuickFilter"; +import DistanceQuickFilter from "@/components/DistanceQuickFilterV2"; import { updateUserLocation } from "@/services/userService"; import { useDictionaryStore } from "@/store/dictionaryStore"; import { saveImage, navigateTo } from "@/utils"; @@ -56,8 +56,11 @@ const ListPageContent: React.FC = ({ updateDistanceQuickFilter, getCities, getCityQrCode, + getDistricts, area, cityQrCode, + districts, + gamesNum, // 新增:获取球局数量 } = store; const { @@ -190,9 +193,10 @@ const ListPageContent: React.FC = ({ useEffect(() => { // 分批异步执行初始化操作,避免阻塞首屏渲染 - // 1. 立即执行:获取城市和二维码(轻量操作) + // 1. 立即执行:获取城市、二维码和行政区列表(轻量操作) getCities(); getCityQrCode(); + getDistricts(); // 新增:获取行政区列表 // 2. 移除 fetchUserInfo 调用,因为父组件 main_pages/index.tsx 已经在授权成功后调用了 // 这里直接使用 store 中的用户信息即可 @@ -324,10 +328,12 @@ const ListPageContent: React.FC = ({ initDictionaryData(); }, []); - const area_city = area?.at(-2) || "上海"; + // 获取省份名称(area 格式: ["中国", "省份"]) + const province = area?.at(1) || "上海"; function renderCityQrcode() { - let item = cityQrCode.find((item) => item.city_name === area_city); + // 根据省份查找对应的二维码 + let item = cityQrCode.find((item) => item.city_name === province); if (!item) item = cityQrCode.find((item) => item.city_name === "其他"); return ( @@ -362,9 +368,13 @@ const ListPageContent: React.FC = ({ ); } + // 判定是否显示"暂无球局"页面 + // 条件:省份不是上海 或 (已加载完成且球局数量为0) + const shouldShowNoGames = province !== "上海" ; + return ( <> - {area_city !== "上海" ? ( + {shouldShowNoGames ? ( renderCityQrcode() ) : ( @@ -404,10 +414,13 @@ const ListPageContent: React.FC = ({ cityOptions={distanceData} quickOptions={quickFilterData} onChange={handleDistanceOrQuickChange} + districtOptions={districts || []} cityName="distanceFilter" quickName="order" + districtName="district" cityValue={distanceQuickFilter?.distanceFilter} quickValue={distanceQuickFilter?.order} + districtValue={distanceQuickFilter?.district} onMenuVisibleChange={handleDistanceFilterVisibleChange} /> diff --git a/src/services/listApi.ts b/src/services/listApi.ts index 82debd5..2210850 100644 --- a/src/services/listApi.ts +++ b/src/services/listApi.ts @@ -132,3 +132,16 @@ export const getCityQrCode = async () => { throw error; } } + +// 获取行政区列表 +export const getDistricts = async (params: { country: string; state: string }) => { + try { + // 调用HTTP服务获取行政区列表 + return httpService.post('/cities/cities', params) + } catch (error) { + // 捕获并打印错误信息 + console.error("行政区列表获取失败:", error); + // 抛出错误以便上层处理 + throw error; + } +} \ No newline at end of file diff --git a/src/store/listStore.ts b/src/store/listStore.ts index b753d9e..44134f6 100644 --- a/src/store/listStore.ts +++ b/src/store/listStore.ts @@ -9,6 +9,7 @@ import { getGamesCount, getCities, getCityQrCode, + getDistricts, } from "../services/listApi"; import { ListActions, @@ -20,12 +21,23 @@ import { function translateCityData(dataTree) { return dataTree.map((item) => { const { children, ...rest } = item; + // 只保留两级:国家和省份,去掉第三级(区域) + const processedChildren = children?.length > 0 + ? children.map(child => ({ + ...child, + text: child.name, + label: child.name, + value: child.name, + children: null, // 去掉第三级 + })) + : null; + return { ...rest, text: rest.name, label: rest.name, value: rest.name, - children: children?.length > 0 ? translateCityData(children) : null, + children: processedChildren, }; }); } @@ -48,6 +60,7 @@ const defaultFilterOptions: IFilterOptions = { const defaultDistanceQuickFilter = { distanceFilter: "", order: "0", + district: "", // 新增:行政区筛选 }; const defaultPageOption = { @@ -155,7 +168,8 @@ const commonStateDefaultValue = { ], cities: [], cityQrCode: [], - area: ['', '', ''] as [string, string, string], + area: ['', ''] as [string, string], // 改为两级:国家、省份 + districts: [], // 新增:行政区列表 } // 创建 store @@ -176,42 +190,45 @@ export const useListStore = create()((set, get) => ({ const filterOptions = currentPageState?.filterOptions || {}; // 全城和快捷筛选 const distanceQuickFilter = currentPageState?.distanceQuickFilter || {}; - const { distanceFilter, order } = distanceQuickFilter || {}; + const { distanceFilter, order, district } = distanceQuickFilter || {}; - // 从 area 中获取省份和城市名称(area 格式: ["中国", 省份, 城市]) + // 从 area 中获取省份名称(area 格式: ["中国", 省份]) const province = state.area?.[1] || ""; // area[1] 是省份 - const city = state.area?.at(-1) || ""; // area[2] 是城市(虽然参数名是 city,但实际是城市名称) - - // 处理 dateRange,确保始终有两个值 - let dateRange: [string, string] = defaultDateRange; - const filterDateRange = filterOptions?.dateRange; - if (Array.isArray(filterDateRange)) { - if (filterDateRange.length === 0) { - // 如果没有选择日期,使用默认值 - dateRange = defaultDateRange; - } else if (filterDateRange.length === 1) { - // 如果只选择了一个日期,转换为两个相同的值 - const singleDate = filterDateRange[0] as string; - dateRange = [singleDate, singleDate]; - } else if (filterDateRange.length >= 2) { - // 如果选择了两个或更多日期,取前两个 - // 如果两个日期相同,保持两个相同的值 - dateRange = [filterDateRange[0] as string, filterDateRange[1] as string]; + + // city 参数逻辑: + // 1. 如果选择了行政区(district 有值),使用行政区的名称(label) + // 2. 如果是"全城"(distanceFilter 为空),不传 city + let city: string | undefined = undefined; + if (district) { + // 从 districts 数组中查找对应的行政区名称 + const selectedDistrict = state.districts.find(item => item.value === district); + if (selectedDistrict) { + city = selectedDistrict.label; // 传递行政区名称,如"静安" } } + // 如果是"全城"(distanceFilter 为空),city 保持 undefined,不会被传递 + + // 使用 filterOptions 中的 dateRange + const dateRange: [string, string] = filterOptions?.dateRange || defaultDateRange; + + const searchOption: any = { + ...filterOptions, + title: state.searchValue, + ntrpMin: filterOptions?.ntrp?.[0], + ntrpMax: filterOptions?.ntrp?.[1], + dateRange: dateRange, // 确保始终是两个值的数组 + distanceFilter: distanceFilter, + province: province, // 添加省份参数 + }; + + // 只在有值时添加 city 参数 + if (city) { + searchOption.city = city; + } const params = { pageOption: currentPageState?.pageOption, - seachOption: { - ...filterOptions, - title: state.searchValue, - ntrpMin: filterOptions?.ntrp?.[0], - ntrpMax: filterOptions?.ntrp?.[1], - dateRange: dateRange, // 确保始终是两个值的数组 - distanceFilter: distanceFilter, - province: province, // 添加省份参数 - city: city, // 添加区县参数(虽然叫 city,但实际是区县) - }, + seachOption: searchOption, order: order, lat: state?.location?.latitude, lng: state?.location?.longitude, @@ -618,7 +635,36 @@ export const useListStore = create()((set, get) => ({ }) }, - updateArea(payload: [string, string, string]) { + // 新增:获取行政区列表 + async getDistricts() { + try { + const state = get(); + // 从 area 中获取省份,area 格式: ["中国", 省份, 城市] + const country = "中国"; + const province = state.area?.at(1) || "上海"; // area[1] 是省份 + + const res = await getDistricts({ + country, + state: province + }); + + if (res.code === 0 && res.data) { + const districts = res.data.map((item) => ({ + label: item.cn_city, + value: item.id.toString(), + id: item.id, + })); + set({ districts }); + return districts; + } + return []; + } catch (error) { + console.error("获取行政区列表失败:", error); + return []; + } + }, + + updateArea(payload: [string, string]) { const state = get(); set({ ...state, diff --git a/types/list/types.ts b/types/list/types.ts index 47b9ac9..2875776 100644 --- a/types/list/types.ts +++ b/types/list/types.ts @@ -30,6 +30,7 @@ export interface PageState { distanceQuickFilter: { distanceFilter: string; order: string; + district?: string; // 新增:行政区筛选 }; filterCount: number; pageOption: { @@ -89,7 +90,8 @@ export interface ListState { gamesNum: number; cities: CityTree[]; cityQrCode: CityQrCodeItem[]; - area: [string, string, string]; + area: [string, string]; // 两级:国家、省份 + districts: BubbleOption[]; // 新增:行政区列表 } export interface ListActions { @@ -119,7 +121,8 @@ export interface ListActions { updateDistanceQuickFilter: (payload: Record) => void; getCities: () => Promise; getCityQrCode: () => Promise; - updateArea: (payload: [string, string, string]) => void; + getDistricts: () => Promise; // 新增:获取行政区 + updateArea: (payload: [string, string]) => void; refreshBothLists: () => Promise; }