商品以及商品详情
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
import { useState, useMemo, useEffect } from 'react'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import { Search, Tag } from '@antmjs/vantui'
|
||||
import { shops } from '@/mock/shops'
|
||||
import { forumPosts } from '@/mock/forum'
|
||||
import ShopCard from '@/components/ShopCard'
|
||||
import ForumPostCard from '@/components/ForumPostCard'
|
||||
import type { Article } from '@/types/article'
|
||||
import type { Shop } from '@/types/shop'
|
||||
import './index.less'
|
||||
|
||||
/** 状态栏 + 导航栏高度 */
|
||||
function getNavBarHeight() {
|
||||
try {
|
||||
const sys = Taro.getSystemInfoSync()
|
||||
return { statusBarHeight: sys.statusBarHeight || 20, navBarHeight: 44 }
|
||||
} catch {
|
||||
return { statusBarHeight: 20, navBarHeight: 44 }
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索结果项的联合类型 */
|
||||
type SearchResult =
|
||||
| { kind: 'shop'; data: Shop }
|
||||
| { kind: 'post'; data: Article }
|
||||
|
||||
export default function SearchPage() {
|
||||
const [keyword, setKeyword] = useState('')
|
||||
const [submittedKeyword, setSubmittedKeyword] = useState('')
|
||||
const [history, setHistory] = useState<string[]>(['麻辣烫', '二手 iPad', '考研资料'])
|
||||
const [navBarHeight, setNavBarHeight] = useState({ statusBarHeight: 20, navBarHeight: 44 })
|
||||
|
||||
useEffect(() => {
|
||||
setNavBarHeight(getNavBarHeight())
|
||||
}, [])
|
||||
|
||||
/** 热门搜索 */
|
||||
const hotKeywords = ['麻辣烫', '奶茶', '二手 iPad', '考研资料', '黄焖鸡', '失物招领', '六级', '外卖红包']
|
||||
|
||||
/** 执行搜索(综合匹配商家和帖子) */
|
||||
const results = useMemo<SearchResult[]>(() => {
|
||||
if (!submittedKeyword.trim()) return []
|
||||
const kw = submittedKeyword.toLowerCase().trim()
|
||||
|
||||
const matchedShops: Shop[] = shops.filter(
|
||||
(s) =>
|
||||
s.name.toLowerCase().includes(kw) ||
|
||||
s.description.toLowerCase().includes(kw) ||
|
||||
s.tags.some((t) => t.toLowerCase().includes(kw)),
|
||||
)
|
||||
|
||||
const matchedPosts: Article[] = forumPosts.filter(
|
||||
(p) =>
|
||||
p.title.toLowerCase().includes(kw) ||
|
||||
p.excerpt.toLowerCase().includes(kw) ||
|
||||
p.source.toLowerCase().includes(kw),
|
||||
)
|
||||
|
||||
return [
|
||||
...matchedShops.map((s) => ({ kind: 'shop' as const, data: s })),
|
||||
...matchedPosts.map((p) => ({ kind: 'post' as const, data: p })),
|
||||
]
|
||||
}, [submittedKeyword])
|
||||
|
||||
/** 返回 */
|
||||
const handleBack = () => {
|
||||
Taro.navigateBack()
|
||||
}
|
||||
|
||||
/** 点击搜索 */
|
||||
const handleSearch = () => {
|
||||
const kw = keyword.trim()
|
||||
if (!kw) {
|
||||
Taro.showToast({ title: '请输入搜索关键词', icon: 'none' })
|
||||
return
|
||||
}
|
||||
setSubmittedKeyword(kw)
|
||||
// 保存到历史(去重,最多 8 条)
|
||||
setHistory((prev) => {
|
||||
const next = [kw, ...prev.filter((h) => h !== kw)]
|
||||
return next.slice(0, 8)
|
||||
})
|
||||
}
|
||||
|
||||
/** 点击历史/热门 */
|
||||
const handleQuickKeyword = (kw: string) => {
|
||||
setKeyword(kw)
|
||||
setSubmittedKeyword(kw)
|
||||
setHistory((prev) => {
|
||||
const next = [kw, ...prev.filter((h) => h !== kw)]
|
||||
return next.slice(0, 8)
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空历史 */
|
||||
const handleClearHistory = () => {
|
||||
setHistory([])
|
||||
}
|
||||
|
||||
/** 商家点击 */
|
||||
const handleShopClick = (shop: Shop) => {
|
||||
Taro.navigateTo({ url: `/pages/shop-detail/index?id=${shop.id}` })
|
||||
}
|
||||
|
||||
/** 帖子点击 */
|
||||
const handlePostClick = (post: Article) => {
|
||||
Taro.navigateTo({ url: `/pages/article/index?id=${post.id}` })
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='search-page'>
|
||||
{/* ========== 自定义导航栏(带返回 + 搜索框 + 搜索按钮) ========== */}
|
||||
<View
|
||||
className='custom-navbar'
|
||||
style={{ paddingTop: `${navBarHeight.statusBarHeight}px` }}
|
||||
>
|
||||
<View
|
||||
className='navbar-content'
|
||||
style={{ height: `${navBarHeight.navBarHeight}px` }}
|
||||
>
|
||||
<View className='navbar-back' onClick={handleBack}>
|
||||
<Text className='back-icon'>‹</Text>
|
||||
</View>
|
||||
<View className='navbar-search'>
|
||||
<Search
|
||||
value={keyword}
|
||||
placeholder='搜索商家、菜品、帖子'
|
||||
shape='round'
|
||||
background='#f2f3f5'
|
||||
onChange={(e) => setKeyword(e.detail.value)}
|
||||
onSearch={handleSearch}
|
||||
/>
|
||||
</View>
|
||||
<View className='navbar-search-btn' onClick={handleSearch}>
|
||||
<Text className='search-btn-text'>搜索</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 占位 */}
|
||||
<View style={{ height: `${navBarHeight.statusBarHeight + navBarHeight.navBarHeight}px` }} />
|
||||
|
||||
<ScrollView className='search-scroll' scrollY enhanced showScrollbar={false}>
|
||||
{/* ========== 搜索结果(已提交关键词时显示) ========== */}
|
||||
{submittedKeyword ? (
|
||||
<View className='results-section'>
|
||||
<View className='results-header'>
|
||||
<Text className='results-title'>
|
||||
搜索 "<Text className='results-keyword'>{submittedKeyword}</Text>" 的结果
|
||||
</Text>
|
||||
<Text className='results-count'>共 {results.length} 条</Text>
|
||||
</View>
|
||||
|
||||
{results.length > 0 ? (
|
||||
<View className='results-list'>
|
||||
{results.map((r) =>
|
||||
r.kind === 'shop' ? (
|
||||
<ShopCard
|
||||
key={`shop-${r.data.id}`}
|
||||
shop={r.data}
|
||||
onClick={handleShopClick}
|
||||
/>
|
||||
) : (
|
||||
<View key={`post-${r.data.id}`} className='result-post-wrap'>
|
||||
<ForumPostCard post={r.data} onClick={handlePostClick} />
|
||||
</View>
|
||||
),
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
<View className='empty-tip'>
|
||||
<Text className='empty-icon'>🔍</Text>
|
||||
<Text className='empty-text'>未找到相关内容,换个词试试</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{/* ========== 搜索历史 ========== */}
|
||||
{history.length > 0 && (
|
||||
<View className='section'>
|
||||
<View className='section-header'>
|
||||
<Text className='section-title'>搜索历史</Text>
|
||||
<View className='clear-btn' onClick={handleClearHistory}>
|
||||
<Text className='clear-icon'>🗑</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='tag-list'>
|
||||
{history.map((h) => (
|
||||
<View
|
||||
key={h}
|
||||
className='tag-item'
|
||||
onClick={() => handleQuickKeyword(h)}
|
||||
>
|
||||
<Text className='tag-text'>{h}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* ========== 热门搜索 ========== */}
|
||||
<View className='section'>
|
||||
<View className='section-header'>
|
||||
<Text className='section-title'>热门搜索</Text>
|
||||
</View>
|
||||
<View className='tag-list'>
|
||||
{hotKeywords.map((kw, idx) => (
|
||||
<View
|
||||
key={kw}
|
||||
className={`tag-item ${idx < 3 ? 'hot' : ''}`}
|
||||
onClick={() => handleQuickKeyword(kw)}
|
||||
>
|
||||
{idx < 3 && <Text className='rank'>{idx + 1}</Text>}
|
||||
<Text className='tag-text'>{kw}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<View className='bottom-safe' />
|
||||
</ScrollView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user