feat(find): 将发现页面重构为网点查询功能

- 修改页面标题为"网点"并设置导航栏样式
- 替换原有文章列表功能为网点搜索和展示功能
- 添加搜索框组件支持城市名称查询
- 实现网点卡片布局显示城市、地址、电话等信息
- 集成模拟数据展示网点列表
- 添加距离计算和导航功能
- 优化页面样式和用户体验
This commit is contained in:
2026-03-01 13:37:53 +08:00
parent eee4644d06
commit c90b69140c
3 changed files with 263 additions and 74 deletions

View File

@@ -1,4 +1,5 @@
export default definePageConfig({ export default definePageConfig({
navigationBarTitleText: '发现', navigationBarTitleText: '网点',
navigationStyle: 'custom', navigationBarTextStyle: 'black',
navigationBarBackgroundColor: '#ffffff'
}) })

View File

@@ -1,4 +1,144 @@
page { page {
background: linear-gradient(to bottom, #e9fff2, #f9fafb); background: #f7f7f7;
background-size: 100%; }
.sitePage {
min-height: 100vh;
background: linear-gradient(180deg, #fde8ea 0%, #f7f7f7 320rpx, #f7f7f7 100%);
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
}
.searchArea {
padding: 22rpx 24rpx 18rpx;
}
.searchBox {
height: 86rpx;
background: #fff;
border: 2rpx solid #b51616;
border-radius: 12rpx;
display: flex;
align-items: center;
overflow: hidden;
}
.searchInput {
flex: 1;
height: 86rpx;
padding: 0 20rpx;
font-size: 30rpx;
color: #222;
}
.searchPlaceholder {
color: #9e9e9e;
font-size: 30rpx;
}
.searchIconWrap {
width: 88rpx;
height: 86rpx;
display: flex;
align-items: center;
justify-content: center;
}
.siteList {
padding: 0 24rpx 24rpx;
}
.siteCard {
background: #fff;
border-radius: 18rpx;
padding: 22rpx 22rpx 18rpx;
margin-top: 18rpx;
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.04);
}
.siteCardInner {
display: flex;
align-items: stretch;
}
.siteInfo {
flex: 1;
padding-right: 10rpx;
}
.siteRow {
display: flex;
align-items: flex-start;
padding: 10rpx 0;
}
.siteRowTop {
padding-top: 2rpx;
padding-bottom: 14rpx;
}
.siteLabel {
width: 170rpx;
flex: 0 0 170rpx;
color: #9a9a9a;
font-size: 30rpx;
line-height: 1.6;
}
.siteValue {
flex: 1;
color: #222;
font-size: 30rpx;
line-height: 1.6;
word-break: break-all;
}
.siteValueStrong {
font-weight: 700;
}
.siteDivider {
height: 2rpx;
background: #ededed;
}
.siteSide {
width: 160rpx;
flex: 0 0 160rpx;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
padding-left: 12rpx;
}
.navArrow {
width: 34rpx;
height: 34rpx;
border-top: 8rpx solid #e60012;
border-right: 8rpx solid #e60012;
border-radius: 4rpx;
transform: rotate(45deg);
margin-right: 8rpx;
}
.distanceText {
margin-top: 18rpx;
font-size: 28rpx;
color: #e60012;
font-weight: 700;
}
.emptyWrap {
padding: 40rpx 0;
display: flex;
justify-content: center;
}
.emptyText {
font-size: 28rpx;
color: #9a9a9a;
}
.bottomSafe {
height: 20rpx;
} }

View File

@@ -1,82 +1,130 @@
import {useEffect, useState} from "react"; import {useMemo, useState} from 'react'
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro'
import {pageCmsArticle} from "@/api/cms/cmsArticle"; import {Input, Text, View} from '@tarojs/components'
import {CmsArticle} from "@/api/cms/cmsArticle/model"; import {Search} from '@nutui/icons-react-taro'
import {NavBar, Cell} from '@nutui/nutui-react-taro';
import {Text} from '@tarojs/components';
import {ArrowRight, ImageRectangle, Coupon, Follow} from '@nutui/icons-react-taro'
import './find.scss' import './find.scss'
import navTo from "@/utils/common";
/** type SiteItem = {
* 文章终极列表 id: string
* @constructor cityName: string
*/ address: string
phone: string
contact: string
distanceMeter: number
}
const MOCK_SITES: SiteItem[] = [
{
id: '1',
cityName: '北京朝阳区网点',
address: '地安门西大街(南门)',
phone: '15878179339',
contact: '刘先生',
distanceMeter: 100
},
{
id: '2',
cityName: '兰州某某区网点',
address: '地安门西大街(南门)',
phone: '15878179339',
contact: '黄先生',
distanceMeter: 150
},
{
id: '3',
cityName: '合肥市某某区网点',
address: '地安门西大街(南门)',
phone: '15878179339',
contact: '黄先生',
distanceMeter: 250
},
{
id: '4',
cityName: '南宁市某某区网点',
address: '广西壮族自治区南宁市良庆区五象新区五象大道403号富雅国际金融中心G1栋高层6006',
phone: '15878179339',
contact: '柳先生',
distanceMeter: 1250
}
]
const Find = () => { const Find = () => {
const [statusBarHeight, setStatusBarHeight] = useState<number>() const [keyword, setKeyword] = useState<string>('')
const [loading, setLoading] = useState<boolean>(false)
const [list, setList] = useState<CmsArticle[]>()
const reload = async () => { const filtered = useMemo(() => {
setLoading(true) const key = keyword.trim()
const article = await pageCmsArticle({categoryId: 4289, status: 0}) if (!key) return MOCK_SITES
if (article) { return MOCK_SITES.filter((it) => it.cityName.includes(key))
setList(article?.list) }, [keyword])
setLoading(false)
} const onNavigate = (item: SiteItem) => {
Taro.showToast({title: `导航至:${item.cityName}(示例)`, icon: 'none'})
} }
useEffect(() => { const onSearch = () => {
Taro.getSystemInfo({ Taro.showToast({title: '查询(示例)', icon: 'none'})
success: (res) => { }
setStatusBarHeight(res.statusBarHeight)
},
})
reload().then(() => {
console.log('初始化完成')
})
}, [])
return ( return (
<> <View className='sitePage'>
{loading && (<div></div>)} <View className='searchArea'>
<NavBar <View className='searchBox'>
fixed={false} <Input
style={{marginTop: `${statusBarHeight}px`, backgroundColor: 'transparent'}} className='searchInput'
onBackClick={() => { value={keyword}
}} placeholder='请输入城市名称查询'
> placeholderClass='searchPlaceholder'
<span></span> confirmType='search'
</NavBar> onInput={(e) => setKeyword(e.detail.value)}
{list && ( onConfirm={onSearch}
<>
<Cell title={
<div style={{display: 'inline-flex', alignItems: 'center'}}>
<ImageRectangle size={18}/>
<Text className={'pl-3'} style={{fontSize: '16px'}}></Text>
</div>
} extra={<ArrowRight color="#cccccc" size={18}/>}/>
<Cell
title={
<div style={{display: 'inline-flex', alignItems: 'center'}}>
<Coupon size={18}/>
<span className={'pl-3'} style={{fontSize: '16px'}}></span>
</div>
}
extra={<ArrowRight color="#cccccc" size={18}/>}
onClick={() => {
navTo('/shop/shopArticle/index', true)
}}
/> />
<Cell title={ <View className='searchIconWrap' onClick={onSearch}>
<div style={{display: 'inline-flex', alignItems: 'center'}}> <Search size={18} color='#b51616' />
<Follow size={18}/> </View>
<span className={'pl-3'} style={{fontSize: '16px'}}></span> </View>
</div> </View>
} extra={<ArrowRight color="#cccccc" size={18}/>}/>
</> <View className='siteList'>
)} {filtered.map((item) => (
</> <View key={item.id} className='siteCard'>
<View className='siteCardInner'>
<View className='siteInfo'>
<View className='siteRow siteRowTop'>
<Text className='siteLabel'></Text>
<Text className='siteValue siteValueStrong'>{item.cityName}</Text>
</View>
<View className='siteDivider' />
<View className='siteRow'>
<Text className='siteLabel'></Text>
<Text className='siteValue'>{item.address}</Text>
</View>
<View className='siteRow'>
<Text className='siteLabel'></Text>
<Text className='siteValue'>{item.phone}</Text>
</View>
<View className='siteRow'>
<Text className='siteLabel'></Text>
<Text className='siteValue'>{item.contact}</Text>
</View>
</View>
<View className='siteSide' onClick={() => onNavigate(item)}>
<View className='navArrow' />
<Text className='distanceText'>{item.distanceMeter}</Text>
</View>
</View>
</View>
))}
{filtered.length === 0 && (
<View className='emptyWrap'>
<Text className='emptyText'></Text>
</View>
)}
</View>
<View className='bottomSafe' />
</View>
) )
} }
export default Find export default Find