514 lines
13 KiB
Plaintext
514 lines
13 KiB
Plaintext
<template>
|
||
<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 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>
|
||
.search-page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background: #f8fafc;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe
|