Files
medical-mall/pages/mall/consumer/search.uvue
2026-01-23 16:47:05 +08:00

514 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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