14 changed files with 1351 additions and 26 deletions
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
export default { |
||||
navigationBarTitleText: '地图找店', |
||||
navigationBarBackgroundColor: '#FFFFFF', |
||||
navigationBarTextStyle: 'black' |
||||
} |
||||
@ -0,0 +1,221 @@
@@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,478 @@
@@ -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<Store[]>([]) |
||||
const [markers, setMarkers] = useState<Marker[]>([]) |
||||
const [activeStoreId, setActiveStoreId] = useState<string | null>(null) |
||||
const [searchValue, setSearchValue] = useState('') |
||||
const [selectedCity, setSelectedCity] = useState('选城市') |
||||
|
||||
const mapContext = useRef<any>(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 ( |
||||
<View className="map-store-page"> |
||||
{/* 地图区域 */} |
||||
<Map |
||||
id="storeMap" |
||||
className="map-container" |
||||
longitude={mapCenter.longitude} |
||||
latitude={mapCenter.latitude} |
||||
scale={15} |
||||
markers={markers} |
||||
show-location |
||||
enable-zoom |
||||
enable-scroll |
||||
enable-rotate={false} |
||||
onMarkerTap={handleMarkerTap} |
||||
onError={() => {}} |
||||
/> |
||||
|
||||
{/* 重新定位按钮 */} |
||||
<View className="relocate-btn" onClick={handleRelocate}> |
||||
<Location size={24} color="#333" /> |
||||
</View> |
||||
|
||||
{/* 底部内容区域 */} |
||||
<View className="content-area"> |
||||
{/* 筛选区域 */} |
||||
<View className="filter-area"> |
||||
<View className="filter-left"> |
||||
{/* 城市选择 */} |
||||
<View className="city-select"> |
||||
<Text className="city-text">{selectedCity}</Text> |
||||
<Arrow size={16} color="#999999" /> |
||||
</View> |
||||
|
||||
{/* 搜索输入框 */} |
||||
<View className="search-input-wrapper"> |
||||
<Input |
||||
className="search-input" |
||||
placeholder="搜索门店名称/地址" |
||||
placeholderClass="search-placeholder" |
||||
value={searchValue} |
||||
onInput={(e) => setSearchValue(e.detail.value)} |
||||
/> |
||||
<View className="search-icon-btn" onClick={handleSearch}> |
||||
<Search size={18} color="#999999" /> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
|
||||
{/* 门店列表 */} |
||||
<View className="list-header"> |
||||
<Text className="list-title">附近门店</Text> |
||||
<Text className="list-count">共 {storeList.length} 家</Text> |
||||
</View> |
||||
|
||||
<ScrollView scrollY className="store-list"> |
||||
{storeList.map((store) => ( |
||||
<View |
||||
key={store.id} |
||||
className={`store-item ${activeStoreId === store.id ? 'active' : ''}`} |
||||
onClick={() => handleStoreItemClick(store)} |
||||
> |
||||
{/* 头部:名称和距离 */} |
||||
<View className="store-header"> |
||||
<Text className="store-name">{store.name}</Text> |
||||
<Text className="store-distance">距离{store.distanceText}</Text> |
||||
</View> |
||||
|
||||
{/* 地址 */} |
||||
<View className="store-address"> |
||||
<Text className="address-text">{store.address}</Text> |
||||
</View> |
||||
|
||||
{/* 价格信息 */} |
||||
<View className="store-price"> |
||||
<View className="price-item"> |
||||
<Text className="price-label">台球</Text> |
||||
<Text className="price-symbol">¥</Text> |
||||
<Text className="price-value">{store.price.toFixed(2)}</Text> |
||||
<Text className="price-unit">起</Text> |
||||
</View> |
||||
<View className="price-item"> |
||||
<Text className="price-label">棋牌</Text> |
||||
<Text className="price-symbol">¥</Text> |
||||
<Text className="price-value">{(store.price + 4).toFixed(2)}</Text> |
||||
<Text className="price-unit">起</Text> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
))} |
||||
|
||||
{storeList.length === 0 && ( |
||||
<View className="empty-state"> |
||||
<Text className="empty-text">暂无门店数据</Text> |
||||
</View> |
||||
)} |
||||
</ScrollView> |
||||
</View> |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default MapStorePage |
||||
@ -1,19 +1,299 @@
@@ -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; |
||||
} |
||||
|
||||
/* 内容区域 */ |
||||
.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; |
||||
|
||||
.header { |
||||
padding: 32px; |
||||
.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; |
||||
} |
||||
|
||||
.title { |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
.picker-title { |
||||
font-size: 32px; |
||||
font-weight: 500; |
||||
color: #333; |
||||
} |
||||
|
||||
.picker-confirm { |
||||
font-size: 30px; |
||||
color: #01CA68; |
||||
} |
||||
|
||||
.content { |
||||
padding: 32px; |
||||
/* Cascader 选项列表 */ |
||||
.cascader-options { |
||||
height: 600px; |
||||
background: #fff; |
||||
} |
||||
|
||||
.cascader-option { |
||||
padding: 30px; |
||||
border-bottom: 1px solid #f5f5f5; |
||||
|
||||
&.active { |
||||
.option-text { |
||||
color: #01CA68; |
||||
font-weight: 500; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.option-text { |
||||
font-size: 28px; |
||||
color: #333; |
||||
} |
||||
|
||||
|
||||
Loading…
Reference in new issue