This commit is contained in:
2026-01-23 16:47:05 +08:00
parent 73498128dd
commit b634c762b3
15 changed files with 13273 additions and 3019 deletions

View File

@@ -1,13 +1,513 @@
<template>
<view>
<view class="search-page">
<!-- 搜索头部 -->
<view class="search-header" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="search-bar-container">
<!-- 返回按钮 -->
<view class="back-btn" @click="goBack">
<text class="back-icon">←</text>
</view>
<!-- 搜索框 -->
<view class="search-input-container">
<view class="search-icon">🔍</view>
<input
class="search-input"
type="text"
:value="searchKeyword"
@input="onInput"
@confirm="onSearch"
placeholder="请输入药品名称、症状或品牌"
placeholder-class="placeholder"
focus
/>
<view v-if="searchKeyword" class="clear-btn" @click="clearSearch">
<text class="clear-icon">×</text>
</view>
</view>
<!-- 搜索按钮 -->
<view class="search-btn" @click="onSearch">
<text class="search-btn-text">搜索</text>
</view>
</view>
</view>
<!-- 主内容区域 -->
<scroll-view scroll-y class="main-content" :style="{ height: scrollHeight + 'px' }">
<!-- 搜索历史 -->
<view v-if="showHistory && searchHistory.length > 0" class="search-history">
<view class="section-header">
<text class="section-title">搜索历史</text>
<text class="clear-history" @click="clearHistory">清空</text>
</view>
<view class="history-tags">
<view
v-for="(item, index) in searchHistory"
:key="index"
class="history-tag"
@click="searchFromHistory(item)"
>
<text class="history-text">{{ item }}</text>
<text class="delete-icon" @click.stop="deleteHistoryItem(index)">×</text>
</view>
</view>
</view>
<!-- 热门搜索 -->
<view class="hot-search">
<view class="section-header">
<text class="section-title">热门搜索</text>
</view>
<view class="hot-tags">
<view
v-for="(item, index) in hotSearchList"
:key="index"
class="hot-tag"
:class="{ 'hot': index < 3 }"
@click="searchFromHot(item.keyword)"
>
<text class="hot-rank" v-if="index < 3">{{ index + 1 }}</text>
<text class="hot-text">{{ item.keyword }}</text>
<text v-if="item.hot" class="hot-icon">🔥</text>
</view>
</view>
</view>
<!-- 搜索分类 -->
<view class="search-categories">
<view class="section-header">
<text class="section-title">按分类搜索</text>
</view>
<view class="category-grid">
<view
v-for="category in searchCategories"
:key="category.id"
class="category-item"
@click="searchByCategory(category)"
>
<view class="category-icon" :style="{ backgroundColor: category.color }">
<text>{{ category.icon }}</text>
</view>
<text class="category-name">{{ category.name }}</text>
</view>
</view>
</view>
<!-- 搜索建议 -->
<view v-if="searchKeyword && searchSuggestions.length > 0" class="search-suggestions">
<view class="suggestions-list">
<view
v-for="(suggestion, index) in searchSuggestions"
:key="index"
class="suggestion-item"
@click="selectSuggestion(suggestion)"
>
<view class="suggestion-icon">🔍</view>
<text class="suggestion-text">{{ suggestion }}</text>
</view>
</view>
</view>
<!-- 搜索结果 -->
<view v-if="showResults" class="search-results">
<view class="results-header">
<text class="results-title">搜索结果</text>
<text class="results-count">共{{ searchResults.length }}个结果</text>
</view>
<view class="results-list">
<view
v-for="product in searchResults"
:key="product.id"
class="result-item"
@click="viewProductDetail(product)"
>
<image class="product-image" :src="product.image" mode="aspectFill" />
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-spec">{{ product.specification }}</text>
<view class="product-meta">
<text class="manufacturer">{{ product.manufacturer }}</text>
<text class="sales">已售{{ product.sales }}</text>
</view>
<view class="price-section">
<view class="current-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ product.price }}</text>
</view>
<text v-if="product.originalPrice > product.price" class="original-price">
¥{{ product.originalPrice }}
</text>
</view>
<view class="product-tags">
<text v-if="product.tag" class="product-tag">{{ product.tag }}</text>
<text v-if="product.featured" class="featured-tag">{{ product.featured }}</text>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="loading" class="loading-more">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<view v-if="!hasMore && searchResults.length > 0" class="no-more">
<text class="no-more-text">--- 没有更多了 ---</text>
</view>
</view>
<!-- 空状态 -->
<view v-if="showEmpty" class="empty-state">
<view class="empty-icon">🔍</view>
<text class="empty-title">暂无搜索结果</text>
<text class="empty-desc">换个关键词试试吧</text>
</view>
<!-- 安全区域 -->
<view class="safe-area"></view>
</scroll-view>
</view>
</template>
<script setup>
<script setup lang="uts">
import { ref, reactive, onMounted, computed } from 'vue'
// 响应式数据
const statusBarHeight = ref(0)
const scrollHeight = ref(0)
const searchKeyword = ref('')
const showHistory = ref(true)
const showResults = ref(false)
const showEmpty = ref(false)
const loading = ref(false)
const hasMore = ref(true)
// 搜索历史
const searchHistory = ref<string[]>([
'布洛芬',
'感冒药',
'维生素C',
'口罩',
'创可贴'
])
// 热门搜索
const hotSearchList = ref([
{ keyword: '布洛芬缓释胶囊', hot: true },
{ keyword: '连花清瘟胶囊', hot: true },
{ keyword: '维生素C片', hot: true },
{ keyword: '板蓝根颗粒', hot: false },
{ keyword: '阿莫西林胶囊', hot: false },
{ keyword: '口罩', hot: false },
{ keyword: '体温计', hot: false },
{ keyword: '创可贴', hot: false }
])
// 搜索分类
const searchCategories = ref([
{ id: 'cold', name: '感冒发烧', icon: '🤧', color: '#2196F3' },
{ id: 'stomach', name: '肠胃用药', icon: '🤢', color: '#4CAF50' },
{ id: 'pain', name: '止痛消炎', icon: '💊', color: '#F44336' },
{ id: 'skin', name: '皮肤用药', icon: '🤕', color: '#9C27B0' },
{ id: 'vitamin', name: '维生素', icon: '🍊', color: '#FF9800' },
{ id: 'chronic', name: '慢性病', icon: '🫀', color: '#795548' },
{ id: 'child', name: '儿童用药', icon: '👶', color: '#00BCD4' },
{ id: 'external', name: '外用药品', icon: '🧴', color: '#8BC34A' }
])
// 搜索建议
const searchSuggestions = computed(() => {
if (!searchKeyword.value) return []
const keyword = searchKeyword.value.toLowerCase()
const suggestions = [
'布洛芬缓释胶囊',
'布洛芬颗粒',
'布洛芬混悬液',
'感冒灵颗粒',
'感冒清热颗粒',
'维生素C咀嚼片',
'维生素C泡腾片',
'阿莫西林胶囊',
'连花清瘟胶囊',
'板蓝根颗粒'
]
return suggestions.filter(item =>
item.toLowerCase().includes(keyword)
).slice(0, 5)
})
// 搜索结果
const searchResults = ref<any[]>([])
// 模拟搜索结果数据
const mockSearchResults = [
{
id: 'result1',
name: '布洛芬缓释胶囊',
specification: '0.3g*24粒',
price: 18.5,
originalPrice: 25.8,
image: 'https://picsum.photos/300/300?random=search1',
manufacturer: '修正药业',
sales: 2560,
tag: '热销',
featured: '家庭常备'
},
{
id: 'result2',
name: '布洛芬颗粒',
specification: '0.2g*12袋',
price: 15.8,
originalPrice: 20.0,
image: 'https://picsum.photos/300/300?random=search2',
manufacturer: '白云山',
sales: 1890,
tag: '推荐',
featured: '儿童适用'
},
{
id: 'result3',
name: '感冒灵颗粒',
specification: '10g*9袋',
price: 22.5,
originalPrice: 28.0,
image: 'https://picsum.photos/300/300?random=search3',
manufacturer: '999药业',
sales: 3420,
tag: '热销',
featured: '快速缓解'
},
{
id: 'result4',
name: '维生素C咀嚼片',
specification: '100mg*60片',
price: 28.9,
originalPrice: 35.0,
image: 'https://picsum.photos/300/300?random=search4',
manufacturer: '养生堂',
sales: 1250,
tag: '特价',
featured: '增强免疫'
}
]
// 生命周期
onMounted(() => {
initPage()
})
// 初始化页面
const initPage = () => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
// 计算滚动区域高度
const windowHeight = systemInfo.windowHeight
const headerHeight = 100 // 搜索头部高度
scrollHeight.value = windowHeight - headerHeight
// 从本地存储加载搜索历史
loadSearchHistory()
}
// 加载搜索历史
const loadSearchHistory = () => {
try {
const history = uni.getStorageSync('searchHistory')
if (history) {
searchHistory.value = JSON.parse(history)
}
} catch (error) {
console.error('加载搜索历史失败:', error)
}
}
// 保存搜索历史
const saveSearchHistory = () => {
try {
uni.setStorageSync('searchHistory', JSON.stringify(searchHistory.value))
} catch (error) {
console.error('保存搜索历史失败:', error)
}
}
// 输入处理
const onInput = (event: any) => {
searchKeyword.value = event.detail.value
showHistory.value = !searchKeyword.value
showResults.value = false
showEmpty.value = false
}
// 清除搜索
const clearSearch = () => {
searchKeyword.value = ''
showHistory.value = true
showResults.value = false
showEmpty.value = false
}
// 执行搜索
const onSearch = () => {
if (!searchKeyword.value.trim()) {
uni.showToast({
title: '请输入搜索关键词',
icon: 'none'
})
return
}
// 添加到搜索历史
addToSearchHistory(searchKeyword.value.trim())
// 显示搜索结果
performSearch()
}
// 添加到搜索历史
const addToSearchHistory = (keyword: string) => {
// 移除重复项
const index = searchHistory.value.indexOf(keyword)
if (index !== -1) {
searchHistory.value.splice(index, 1)
}
// 添加到开头
searchHistory.value.unshift(keyword)
// 限制历史记录数量
if (searchHistory.value.length > 10) {
searchHistory.value = searchHistory.value.slice(0, 10)
}
// 保存到本地存储
saveSearchHistory()
}
// 从历史记录搜索
const searchFromHistory = (keyword: string) => {
searchKeyword.value = keyword
performSearch()
}
// 从热门搜索搜索
const searchFromHot = (keyword: string) => {
searchKeyword.value = keyword
addToSearchHistory(keyword)
performSearch()
}
// 按分类搜索
const searchByCategory = (category: any) => {
searchKeyword.value = category.name
addToSearchHistory(category.name)
performSearch()
}
// 选择搜索建议
const selectSuggestion = (suggestion: string) => {
searchKeyword.value = suggestion
addToSearchHistory(suggestion)
performSearch()
}
// 执行搜索逻辑
const performSearch = () => {
showHistory.value = false
showEmpty.value = false
loading.value = true
// 模拟搜索延迟
setTimeout(() => {
if (searchKeyword.value.toLowerCase().includes('布洛芬') ||
searchKeyword.value.toLowerCase().includes('感冒') ||
searchKeyword.value.toLowerCase().includes('维生素')) {
// 模拟搜索结果
searchResults.value = [...mockSearchResults]
showResults.value = true
showEmpty.value = false
} else {
// 无结果
searchResults.value = []
showResults.value = false
showEmpty.value = true
}
loading.value = false
hasMore.value = true
// 滚动到顶部
uni.pageScrollTo({
scrollTop: 0,
duration: 300
})
}, 500)
}
// 查看商品详情
const viewProductDetail = (product: any) => {
uni.navigateTo({
url: `/pages/medicine/detail?id=${product.id}`
})
}
// 清空搜索历史
const clearHistory = () => {
uni.showModal({
title: '提示',
content: '确定要清空搜索历史吗?',
success: (res) => {
if (res.confirm) {
searchHistory.value = []
saveSearchHistory()
}
}
})
}
// 删除单个历史记录
const deleteHistoryItem = (index: number) => {
searchHistory.value.splice(index, 1)
saveSearchHistory()
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
// 加载更多
const loadMore = () => {
if (loading.value || !hasMore.value) return
loading.value = true
// 模拟加载更多数据
setTimeout(() => {
const newResults = [...mockSearchResults].map((item, idx) => ({
...item,
id: `more${idx}`,
price: Math.floor(item.price * 0.9 + Math.random() * 10)
}))
searchResults.value = [...searchResults.value, ...newResults]
loading.value = false
hasMore.value = searchResults.value.length < 20
}, 1000)
}
</script>
<style>
</style>
.search-page {
width: 100%;
min-height: 100vh;
background: #f8fafc;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe