diff --git a/babel.config.js b/babel.config.js index 8dd05d2..72e84e1 100644 --- a/babel.config.js +++ b/babel.config.js @@ -10,13 +10,12 @@ module.exports = { }] ], plugins: [ - // Taroify 组件按需导入配置 [ "import", { libraryName: "@taroify/core", libraryDirectory: "", - style: false, // 样式已在 app.ts 中全局导入 + style: true, }, "@taroify/core", ], @@ -26,7 +25,7 @@ module.exports = { libraryName: "@taroify/icons", libraryDirectory: "", camel2DashComponentName: false, - style: false, + style: () => "@taroify/icons/style", customName: (name) => name === "Icon" ? "@taroify/icons/van/VanIcon" : `@taroify/icons/${name}`, }, "@taroify/icons", diff --git a/package-lock.json b/package-lock.json index 0f15391..775cbf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@babel/runtime": "^7.24.4", "@taroify/core": "^0.9.0", + "@taroify/hooks": "^0.9.0", "@taroify/icons": "^0.9.0", "@tarojs/components": "4.0.9", "@tarojs/helper": "4.0.9", diff --git a/package.json b/package.json index d9f02cc..12ba100 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "@babel/runtime": "^7.24.4", "@taroify/core": "^0.9.0", + "@taroify/hooks": "^0.9.0", "@taroify/icons": "^0.9.0", "@tarojs/components": "4.0.9", "@tarojs/helper": "4.0.9", diff --git a/src/app.config.ts b/src/app.config.ts index 772474e..58d5929 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -7,6 +7,23 @@ export default defineAppConfig({ 'pages/user/index', 'pages/scanCode/index', // 独立扫码页面(无tabbar) ], + // 分包配置 + subpackages: [ + { + root: 'pageStore', + name: 'store-package', + pages: [ + 'mapStore/index', // 地图找店页面 + ] + } + ], + // 分包预下载规则 + preloadRule: { + 'pages/store/index': { + network: 'all', + packages: ['store-package'] + } + }, window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', diff --git a/src/app.scss b/src/app.scss index b7efbb9..3bafdae 100644 --- a/src/app.scss +++ b/src/app.scss @@ -64,3 +64,12 @@ page { color: #999; font-size: 28px; } + +/* 覆盖 taroify Popup 的 z-index,使其高于自定义 tabbar (z-index: 9999) */ +.taroify-popup { + z-index: 10001 !important; +} + +.taroify-backdrop { + z-index: 10000 !important; +} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index b1735f9..3e1cf5e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,8 +3,6 @@ import { useLaunch } from '@tarojs/taro' import Taro from '@tarojs/taro' import { userStore } from './store' import './app.scss' -// 导入 taroify 全局样式 -import '@taroify/core/index.scss' function App({ children }: PropsWithChildren) { useLaunch(() => { diff --git a/src/assets/icon/locationIcon.png b/src/assets/icon/locationIcon.png new file mode 100644 index 0000000..f195679 Binary files /dev/null and b/src/assets/icon/locationIcon.png differ diff --git a/src/assets/icon/storeIcon.png b/src/assets/icon/storeIcon.png new file mode 100644 index 0000000..6624e56 Binary files /dev/null and b/src/assets/icon/storeIcon.png differ diff --git a/src/custom-tab-bar/index.scss b/src/custom-tab-bar/index.scss index 59e8a54..3e72670 100644 --- a/src/custom-tab-bar/index.scss +++ b/src/custom-tab-bar/index.scss @@ -12,7 +12,7 @@ right: 0; width: 100%; height: 100Px; /* 继续减小高度,给凸起留足空间 */ - z-index: 9999; + z-index: 99; pointer-events: none; // 背景白色层 #0E0D0D diff --git a/src/pageStore/mapStore/index.config.ts b/src/pageStore/mapStore/index.config.ts new file mode 100644 index 0000000..4c1648d --- /dev/null +++ b/src/pageStore/mapStore/index.config.ts @@ -0,0 +1,5 @@ +export default { + navigationBarTitleText: '地图找店', + navigationBarBackgroundColor: '#FFFFFF', + navigationBarTextStyle: 'black' +} diff --git a/src/pageStore/mapStore/index.scss b/src/pageStore/mapStore/index.scss new file mode 100644 index 0000000..b2fb8d0 --- /dev/null +++ b/src/pageStore/mapStore/index.scss @@ -0,0 +1,221 @@ +.map-store-page { + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; + background: #F5F5F5; + position: relative; + + // 地图容器 + .map-container { + width: 100%; + height: 650px; + position: relative; + } + + // 重新定位按钮 + .relocate-btn { + position: absolute; + top: 550px; + right: 30px; + width: 80px; + height: 80px; + background: #FFFFFF; + border-radius: 50%; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + align-items: center; + justify-content: center; + z-index: 10; + } + + // 底部内容区域 + .content-area { + flex: 1; + display: flex; + flex-direction: column; + background: #F2F3F5; + border-radius: 30px 30px 0 0; + margin-top: -30px; + padding: 30px 30px 0; + position: relative; + z-index: 5; + box-shadow: 0 -3px 14px 0 rgba(202, 203, 204, 0.5); + + // 筛选区域 + .filter-area { + // margin-bottom: 30px; + + .filter-left { + display: flex; + align-items: center; + gap: 20px; + + // 城市选择 + .city-select { + flex-shrink: 0; + display: flex; + align-items: center; + gap: 8px; + padding: 16px 24px; + background: #FFFFFF; + border-radius: 8px; + + .city-text { + font-weight: 400; + font-size: 29px; + color: #333333; + } + } + + // 搜索输入框 + .search-input-wrapper { + flex: 1; + display: flex; + align-items: center; + background: #FFFFFF; + border-radius: 8px; + padding: 16px 24px; + gap: 16px; + + .search-input { + flex: 1; + font-size: 29px; + color: #333333; + } + + .search-placeholder { + font-size: 29px; + color: #999999; + } + + .search-icon-btn { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + } + } + } + } + + // 列表头部 + .list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin: 30px 0; + + .list-title { + font-size: 29px; + font-weight: 400; + color: #333333; + } + + .list-count { + font-size: 26px; + color: #999999; + } + } + + // 门店列表 + .store-list { + flex: 1; + margin: 0 -30px; + padding: 0 30px; + + .store-item { + padding: 30px; + background: #FFFFFF; + border-radius: 13px; + margin-bottom: 28px; + transition: all 0.3s; + + &.active { + background: #FFF7E6; + border: 2px solid #FF9900; + box-shadow: 0 4px 12px rgba(255, 153, 0, 0.2); + } + + // 头部:名称和距离 + .store-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 22px; + + .store-name { + flex: 1; + font-weight: 500; + font-size: 33px; + color: #000000; + margin-right: 20px; + } + + .store-distance { + flex-shrink: 0; + font-size: 25px; + color: #333333; + } + } + + // 地址 + .store-address { + margin-bottom: 22px; + + .address-text { + font-size: 25px; + color: #595959; + line-height: 1.5; + word-break: break-all; + } + } + + // 价格信息 + .store-price { + display: flex; + align-items: baseline; + gap: 30px; + + .price-item { + display: flex; + align-items: baseline; + + .price-label { + font-size: 25px; + color: #8B8B8B; + margin-right: 8px; + } + + .price-symbol { + font-size: 25px; + color: #333333; + } + + .price-value { + font-size: 25px; + font-weight: 500; + color: #333333; + } + + .price-unit { + font-size: 25px; + color: #333333; + } + } + } + } + + // 空状态 + .empty-state { + padding: 100px 0; + text-align: center; + + .empty-text { + font-size: 28px; + color: #999999; + } + } + } + } +} diff --git a/src/pageStore/mapStore/index.tsx b/src/pageStore/mapStore/index.tsx new file mode 100644 index 0000000..92ae062 --- /dev/null +++ b/src/pageStore/mapStore/index.tsx @@ -0,0 +1,478 @@ +/** + * 地图找店页面 + */ +import { View, Text, Map, ScrollView, Image, Input } from '@tarojs/components' +import Taro, { useLoad } from '@tarojs/taro' +import { observer } from 'mobx-react' +import { useState, useRef, useEffect } from 'react' +import { Search, Arrow, Location } from '@taroify/icons' +import './index.scss' + +// 模拟门店数据(带经纬度) +const mockStoreData = [ + { + id: '1', + name: '北京黑八大师联盟球厅店', + address: '北京市XXX区XXX街道XXX路19号606', + latitude: 39.910823, + longitude: 116.400470, + price: 16.00, + billiardCount: 6, + billiardEmpty: 6, + chessCount: 2, + chessEmpty: 2, + type: '直营店', + phone: '010-12345678', + distance: 0, + distanceText: '0.05km' + }, + { + id: '2', + name: '北京晶彩桌球世界店', + address: '北京市XXX区XXX街道XXX路XXXXXX大厦101', + latitude: 39.905823, + longitude: 116.395470, + price: 24.00, + billiardCount: 8, + billiardEmpty: 8, + chessCount: 1, + chessEmpty: 1, + type: '加盟店', + phone: '010-87654321', + distance: 0, + distanceText: '0.1km' + }, + { + id: '3', + name: '北京飓风台球店', + address: '北京市XXX区XXX街道106号A01', + latitude: 39.915823, + longitude: 116.405470, + price: 106.00, + billiardCount: 10, + billiardEmpty: 10, + chessCount: 0, + chessEmpty: 0, + type: '加盟店', + phone: '010-11223344', + distance: 0, + distanceText: '1.3km' + }, + { + id: '4', + name: '北京星耀台球俱乐部', + address: '北京市XXX区XXX街道XXX路99号', + latitude: 39.900823, + longitude: 116.390470, + price: 30.00, + billiardCount: 12, + billiardEmpty: 10, + chessCount: 3, + chessEmpty: 2, + type: '直营店', + phone: '010-99887766', + distance: 0, + distanceText: '2.3km' + } +] + +interface Store { + id: string + name: string + address: string + latitude: number + longitude: number + price: number + billiardCount: number + billiardEmpty: number + chessCount: number + chessEmpty: number + type: string + phone: string + distance: number + distanceText: string +} + +interface Marker { + id: number + latitude: number + longitude: number + iconPath: string + width: number + height: number + callout?: any + label?: any +} + +const MapStorePage = observer(() => { + // 用户位置(默认北京天安门) + const [userLocation, setUserLocation] = useState({ + latitude: 39.908823, + longitude: 116.397470 + }) + + // 地图中心点(默认跟随用户位置) + const [mapCenter, setMapCenter] = useState({ + latitude: 39.908823, + longitude: 116.397470 + }) + + const [storeList, setStoreList] = useState([]) + const [markers, setMarkers] = useState([]) + const [activeStoreId, setActiveStoreId] = useState(null) + const [searchValue, setSearchValue] = useState('') + const [selectedCity, setSelectedCity] = useState('选城市') + + const mapContext = useRef(null) + + useLoad(() => { + // 页面加载时获取用户位置 + requestLocationPermission() + }) + + useEffect(() => { + // 初始化地图上下文 + mapContext.current = Taro.createMapContext('storeMap') + }, []) + + // 请求位置权限并获取位置 + const requestLocationPermission = async () => { + try { + // 检查权限 + const setting = await Taro.getSetting() + + if (!setting.authSetting['scope.userLocation']) { + // 请求授权 + await Taro.authorize({ scope: 'scope.userLocation' }) + } + + // 获取位置 + Taro.showLoading({ title: '定位中...' }) + + const location = await Taro.getLocation({ + type: 'gcj02', // 返回国测局坐标,适用于微信小程序地图 + isHighAccuracy: true, + altitude: false + }) + + Taro.hideLoading() + + const userPos = { + latitude: location.latitude, + longitude: location.longitude + } + + setUserLocation(userPos) + setMapCenter(userPos) + + // 获取附近门店(使用模拟数据) + fetchNearbyStores(userPos.latitude, userPos.longitude) + + } catch (error) { + Taro.hideLoading() + console.error('获取位置失败:', error) + + // 使用默认位置并加载门店 + fetchNearbyStores(userLocation.latitude, userLocation.longitude) + + Taro.showModal({ + title: '定位提示', + content: '未能获取您的位置,将使用默认位置展示门店', + showCancel: false + }) + } + } + + // 获取附近门店(模拟接口) + const fetchNearbyStores = async (lat: number, lng: number) => { + try { + Taro.showLoading({ title: '加载中...' }) + + // TODO: 替换为真实接口 + // const res = await Taro.request({ + // url: 'https://your-api.com/api/stores/nearby', + // method: 'GET', + // data: { + // latitude: lat, + // longitude: lng, + // radius: 10000, + // limit: 50 + // } + // }) + + // 模拟网络延迟 + await new Promise(resolve => setTimeout(resolve, 500)) + + // 使用模拟数据并计算距离 + const storesWithDistance = mockStoreData.map(store => ({ + ...store, + distance: calculateDistance(lat, lng, store.latitude, store.longitude), + distanceText: formatDistance( + calculateDistance(lat, lng, store.latitude, store.longitude) + ) + })) + + // 按距离排序 + storesWithDistance.sort((a, b) => a.distance - b.distance) + + setStoreList(storesWithDistance) + + // 生成地图标记 + generateMarkers(storesWithDistance) + + Taro.hideLoading() + + } catch (error) { + Taro.hideLoading() + console.error('获取门店失败:', error) + Taro.showToast({ + title: '获取门店失败', + icon: 'none' + }) + } + } + + // 计算两点之间的距离(米)- Haversine 公式 + const calculateDistance = ( + lat1: number, + lng1: number, + lat2: number, + lng2: number + ): number => { + const R = 6371e3 // 地球半径(米) + const φ1 = (lat1 * Math.PI) / 180 + const φ2 = (lat2 * Math.PI) / 180 + const Δφ = ((lat2 - lat1) * Math.PI) / 180 + const Δλ = ((lng2 - lng1) * Math.PI) / 180 + + const a = + Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2) + + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) + + return R * c // 返回米 + } + + // 格式化距离显示 + const formatDistance = (distance: number): string => { + if (distance < 1000) { + return `${distance.toFixed(0)}m` + } else { + return `${(distance / 1000).toFixed(2)}km` + } + } + + // 生成地图标记 + const generateMarkers = (stores: Store[]) => { + // 使用默认标记图标(微信小程序会提供默认图标) + // 如果需要自定义图标,准备好图片后替换 iconPath + const newMarkers: Marker[] = stores.map((store, index) => ({ + id: index, + latitude: store.latitude, + longitude: store.longitude, + iconPath: '', // 空字符串使用默认图标,或指定自定义图标路径 + width: 30, + height: 30, + callout: { + content: store.name, + color: '#333333', + fontSize: 12, + borderRadius: 8, + bgColor: '#FFFFFF', + padding: 8, + display: 'BYCLICK' // 点击标记时显示 + } + })) + + setMarkers(newMarkers) + } + + // 点击地图标记 + const handleMarkerTap = (e: any) => { + const markerIndex = e.detail.markerId + const store = storeList[markerIndex] + + if (store) { + setActiveStoreId(store.id) + setMapCenter({ + latitude: store.latitude, + longitude: store.longitude + }) + } + } + + // 点击列表项 + const handleStoreItemClick = (store: Store) => { + setActiveStoreId(store.id) + + // 移动地图到对应位置 + setMapCenter({ + latitude: store.latitude, + longitude: store.longitude + }) + + // 使用 moveToLocation 移动地图(需要 mapContext) + if (mapContext.current) { + mapContext.current.moveToLocation({ + latitude: store.latitude, + longitude: store.longitude + }) + } + } + + // 重新定位 + const handleRelocate = () => { + requestLocationPermission() + } + + // 导航到门店 + const handleNavigate = (store: Store, e: any) => { + e.stopPropagation() + + Taro.openLocation({ + latitude: store.latitude, + longitude: store.longitude, + name: store.name, + address: store.address, + scale: 18 + }) + } + + // 搜索门店 + const handleSearch = () => { + if (!searchValue.trim()) { + Taro.showToast({ + title: '请输入门店名称或地址', + icon: 'none' + }) + return + } + + // TODO: 调用搜索接口 + const filtered = mockStoreData.filter( + store => + store.name.includes(searchValue) || store.address.includes(searchValue) + ) + + if (filtered.length > 0) { + setStoreList(filtered) + generateMarkers(filtered) + + // 移动到第一个搜索结果 + setMapCenter({ + latitude: filtered[0].latitude, + longitude: filtered[0].longitude + }) + } else { + Taro.showToast({ + title: '未找到相关门店', + icon: 'none' + }) + } + } + + return ( + + {/* 地图区域 */} + {}} + /> + + {/* 重新定位按钮 */} + + + + + {/* 底部内容区域 */} + + {/* 筛选区域 */} + + + {/* 城市选择 */} + + {selectedCity} + + + + {/* 搜索输入框 */} + + setSearchValue(e.detail.value)} + /> + + + + + + + + {/* 门店列表 */} + + 附近门店 + 共 {storeList.length} 家 + + + + {storeList.map((store) => ( + handleStoreItemClick(store)} + > + {/* 头部:名称和距离 */} + + {store.name} + 距离{store.distanceText} + + + {/* 地址 */} + + {store.address} + + + {/* 价格信息 */} + + + 台球 + ¥ + {store.price.toFixed(2)} + + + + 棋牌 + ¥ + {(store.price + 4).toFixed(2)} + + + + + ))} + + {storeList.length === 0 && ( + + 暂无门店数据 + + )} + + + + ) +}) + +export default MapStorePage diff --git a/src/pages/store/index.scss b/src/pages/store/index.scss index 952373c..915e1a5 100644 --- a/src/pages/store/index.scss +++ b/src/pages/store/index.scss @@ -1,19 +1,299 @@ +/** + * 门店列表页样式 + * 设计稿宽度: 750px + */ + .store-page { - min-height: 100vh; + // 高度为 100vh 减去 tabbar 高度 + height: calc(100vh - 40Px); + height: calc(100vh - 40Px - constant(safe-area-inset-bottom)); + height: calc(100vh - 40Px - env(safe-area-inset-bottom)); background-color: #121212; + position: relative; + overflow-x: hidden; + overflow-y: auto; + box-sizing: border-box; +} - .header { - padding: 32px; - background: #fff; - - .title { - font-size: 36px; - font-weight: bold; - color: #333; - } +/* 内容区域 */ +.content-area { + padding-top: 175px; + padding-left: 30px; + padding-right: 30px; +} + +/* 筛选区域 */ +.filter-area { + height: 84px; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; +} + +.filter-left { + flex: 1; + display: flex; + align-items: center; + background-color: #21212A; + border-radius: 10px; + height: 100%; + padding: 0 20px; +} + +/* 城市选择 */ +.city-select { + display: flex; + align-items: center; + flex-shrink: 0; + padding-right: 20px; + border-right: 1px solid #333; + margin-right: 20px; + + .city-text { + font-weight: 400; + font-size: 29px; + color: #FFFFFF; + margin-right: 8px; + } +} + +/* 搜索输入框包装器 */ +.search-input-wrapper { + flex: 1; + display: flex; + align-items: center; + height: 100%; + position: relative; +} + +/* 搜索输入框 */ +.search-input { + flex: 1; + height: 100%; + font-size: 29px; + color: #FFFFFF; + background: transparent; +} + +.search-placeholder { + font-size: 29px; + color: #999999; +} + +/* 输入框右侧图标按钮(搜索/清除) */ +.input-icon-btn { + display: flex; + align-items: center; + justify-content: center; + width: 50px; + height: 50px; + flex-shrink: 0; +} + +/* 找店按钮 */ +.find-store-btn { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-left: 30px; + flex-shrink: 0; + + .store-icon { + width: 50px; + height: 50px; + margin-bottom: 6px; + } + + .find-text { + font-weight: 400; + font-size: 21px; + color: #E5E5E5; + } +} + +/* 门店列表 */ +.store-list { + margin-top: 30px; +} + +/* 门店项 */ +.store-item { + background-color: #21212A; + border-radius: 13px; + padding: 28px; + margin-bottom: 28px; +} + +/* 门店头部 - 名称和价格 */ +.store-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 24px; +} + +.store-name { + font-weight: 500; + font-size: 33px; + color: #FFFFFF; + flex: 1; + margin-right: 20px; +} + +.store-price { + display: flex; + align-items: baseline; + flex-shrink: 0; + + .price-symbol { + font-size: 25px; + color: #01CA68; + } + + .price-value { + font-size: 33px; + color: #01CA68; + font-weight: 500; + } + + .price-unit { + font-size: 25px; + color: #01CA68; + } +} + +/* 标签区域 */ +.store-tags { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin-bottom: 24px; +} + +.tag { + padding: 8px 16px; + border-radius: 6px; + font-size: 23px; + border-width: 1px; + border-style: solid; +} + +.tag-billiard { + color: #01CA68; + border-color: #01CA68; + background: transparent; +} + +.tag-chess { + color: #FF9900; + border-color: #FF9900; + background: transparent; +} + +.tag-type { + color: #E5E5E5; + border-color: #E5E5E5; + background: transparent; +} + +/* 地址和距离 */ +.store-location { + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.location-left { + display: flex; + align-items: flex-start; + max-width: 389px; +} + +.location-icon { + width: 28px; + height: 28px; + flex-shrink: 0; + margin-right: 8px; + margin-top: 4px; +} + +.address-text { + font-size: 25px; + color: #E5E5E5; + line-height: 1.4; + word-break: break-all; +} + +.distance-text { + font-size: 25px; + color: #E5E5E5; + flex-shrink: 0; + margin-left: 20px; +} + +/* 列表底部 */ +.list-footer { + text-align: center; + padding: 40px 0; + + .footer-text { + font-size: 26px; + color: #666; } +} + +/* 城市选择弹窗 */ +.city-picker { + background: #fff; + border-radius: 20px 20px 0 0; +} + +.picker-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 30px; + border-bottom: 1px solid #eee; +} + +.picker-cancel { + font-size: 30px; + color: #666; +} + +.picker-title { + font-size: 32px; + font-weight: 500; + color: #333; +} + +.picker-confirm { + font-size: 30px; + color: #01CA68; +} + +/* Cascader 选项列表 */ +.cascader-options { + height: 600px; + background: #fff; +} - .content { - padding: 32px; +.cascader-option { + padding: 30px; + border-bottom: 1px solid #f5f5f5; + + &.active { + .option-text { + color: #01CA68; + font-weight: 500; + } } } + +.option-text { + font-size: 28px; + color: #333; +} + diff --git a/src/pages/store/index.tsx b/src/pages/store/index.tsx index 74fc17a..39706fc 100644 --- a/src/pages/store/index.tsx +++ b/src/pages/store/index.tsx @@ -1,26 +1,342 @@ /** * 门店列表页 */ -import { View, Text } from '@tarojs/components' -import { useLoad } from '@tarojs/taro' +import { View, Text, Image, Input, ScrollView } from '@tarojs/components' +import Taro, { useLoad } from '@tarojs/taro' import { observer } from 'mobx-react' +import { useState } from 'react' +import { Popup, Toast, Tabs } from '@taroify/core' +import { useCascader } from '@taroify/hooks' +import { Arrow, Cross, Search } from '@taroify/icons' +import storeIcon from '../../assets/icon/storeIcon.png' +import locationIcon from '../../assets/icon/locationIcon.png' import './index.scss' +// 城市数据 - useCascader 格式(嵌套 children) +const cityOptions = [ + { + label: '天津市', + value: 'tianjin', + children: [{ label: '天津市', value: 'tianjin_city' }], + }, + { + label: '河北省', + value: 'hebei', + children: [ + { label: '赤峰市', value: 'chifeng' }, + { label: '通辽市', value: 'tongliao' }, + ], + }, + { + label: '山西省', + value: 'shanxi', + children: [{ label: '鄂尔多斯市', value: 'eerduosi' }], + }, + { + label: '内蒙古自治区', + value: 'neimenggu', + children: [ + { label: '呼伦贝尔市', value: 'hulunbeier' }, + { label: '巴彦淖尔市', value: 'bayannaoer' }, + { label: '乌兰察布市', value: 'wulanchabu' }, + { label: '锡林郭勒盟', value: 'xilingol' }, + ], + }, + { + label: '辽宁省', + value: 'liaoning', + children: [{ label: '沈阳市', value: 'shenyang' }], + }, + { + label: '吉林省', + value: 'jilin', + children: [{ label: '长春市', value: 'changchun' }], + }, + { + label: '黑龙江省', + value: 'heilongjiang', + children: [{ label: '哈尔滨市', value: 'haerbin' }], + }, +] + +// 模拟门店数据 +const mockStoreList = [ + { + id: '1', + name: '北京黑八大师联盟球厅店', + price: 16.00, + billiardCount: 6, + billiardEmpty: 6, + chessCount: 2, + chessEmpty: 2, + type: '直营店', + address: '北京市XXX区XXX街道XXX路19号606', + distance: '0.05km', + }, + { + id: '2', + name: '北京晶彩桌球世界店', + price: 24.00, + billiardCount: 8, + billiardEmpty: 8, + chessCount: 1, + chessEmpty: 1, + type: '加盟店', + address: '北京市XXX区XXX街道XXX路XXXXXX大厦101', + distance: '0.1km', + }, + { + id: '3', + name: '北京飓风台球店', + price: 106.00, + billiardCount: 0, + billiardEmpty: 0, + chessCount: 0, + chessEmpty: 0, + type: '加盟店', + address: '北京市XXX区XXX街道106号A01', + distance: '100.00km', + }, +] + const StoreIndex = observer(() => { + const [selectedCity, setSelectedCity] = useState('选城市') + const [searchValue, setSearchValue] = useState('') + const [cityPopupOpen, setCityPopupOpen] = useState(false) + const [storeList, setStoreList] = useState(mockStoreList) + const [loading, setLoading] = useState(false) + const [isSearched, setIsSearched] = useState(false) // 是否已执行搜索 + const [cityValue, setCityValue] = useState([]) + const [tabIndex, setTabIndex] = useState(0) + + // 使用 useCascader hook + const { columns } = useCascader({ + value: cityValue, + depth: 2, + options: cityOptions, + }) + useLoad(() => { console.log('门店页面加载') }) + // 打开城市选择 + const handleOpenCityPicker = () => { + setCityPopupOpen(true) + } + + // 关闭城市选择 + const handleCloseCityPicker = () => { + setCityPopupOpen(false) + } + + // 选择选项 + const handleSelectOption = (option: any, columnIndex: number) => { + const newValue = [...cityValue] + newValue[columnIndex] = option.value + // 清除后面的选中值 + newValue.splice(columnIndex + 1) + setCityValue(newValue) + + // 如果选择了最后一级(城市),关闭弹窗 + if (columnIndex === 1 || !option.children || option.children.length === 0) { + setSelectedCity(option.label) + setCityPopupOpen(false) + setTabIndex(0) + } else { + // 切换到下一个 tab + setTabIndex(columnIndex + 1) + } + } + + // 搜索门店 + const handleSearch = () => { + // 无内容时提示输入 + if (!searchValue.trim()) { + Toast.open('请输入门店名称或地址') + return + } + + setLoading(true) + Toast.loading({ + message: '搜索中...', + duration: 0, + }) + + // 模拟接口请求 + setTimeout(() => { + setLoading(false) + Toast.close() + Toast.success('搜索完成') + setIsSearched(true) // 标记已搜索,切换为取消图标 + + // 根据输入内容过滤数据 + setStoreList(mockStoreList.filter(store => + store.name.includes(searchValue) || store.address.includes(searchValue) + )) + }, 1500) + } + + // 输入框变化 + const handleInputChange = (e: { detail: { value: string } }) => { + setSearchValue(e.detail.value) + } + + // 清除搜索并恢复默认数据 + const handleClearSearch = () => { + setSearchValue('') + setIsSearched(false) // 切换回搜索图标 + + // 恢复默认数据 + setLoading(true) + Toast.loading({ + message: '加载中...', + duration: 0, + }) + + setTimeout(() => { + setLoading(false) + Toast.close() + setStoreList(mockStoreList) + }, 500) + } + + // 跳转到地图找店页面(分包页面) + const handleGoToMap = () => { + Taro.navigateTo({ + url: '/pageStore/mapStore/index' + }) + } + return ( - - 附近门店 - - - - 门店列表功能开发中... + {/* 内容区域 */} + + {/* 筛选区域 */} + + + {/* 城市选择 */} + + {selectedCity} + + + {/* 搜索输入框 */} + + + {/* 已搜索时显示取消图标,未搜索时显示搜索图标 */} + {isSearched ? ( + + + + ) : ( + + + + )} + + + {/* 找店按钮 */} + + + 找店 + + + + {/* 门店列表 */} + + {storeList.map((store) => ( + + {/* 标题和价格 */} + + {store.name} + + ¥ + {store.price.toFixed(2)} + 起/小时 + + + + {/* 标签 */} + + + 台球 {store.billiardCount} 桌空闲 + + + 棋牌 {store.chessCount} 桌空闲 + + + {store.type} + + + + {/* 地址和距离 */} + + + + {store.address} + + 距离{store.distance} {'>'} + + + ))} + + {/* 列表底部提示 */} + + 到底了~ + + + {/* 城市选择弹窗 - 使用 useCascader */} + + + {/* 头部 */} + + 取消 + 城市选择 + 确定 + + + {/* Tabs 切换 */} + + {columns.map((column, columnIndex) => ( + opt.value === cityValue[columnIndex])?.label || '请选择' + : '请选择' + } + > + + {column.map((option) => ( + handleSelectOption(option, columnIndex)} + > + {option.label} + + ))} + + + ))} + + + + + {/* Toast 组件 */} + ) })