231 lines
5.7 KiB
Vue
231 lines
5.7 KiB
Vue
<template>
|
||
<view class="search-page">
|
||
<!-- 自定义导航栏 -->
|
||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="custom-navbar__content" :style="{ height: navBarHeight + 'px' }">
|
||
<image class="custom-navbar__back" src="/static/ic_back.png" mode="aspectFit" @click="goBack" />
|
||
<text class="custom-navbar__title">全局搜索:{{ keyword }}</text>
|
||
<view class="custom-navbar__placeholder" />
|
||
</view>
|
||
</view>
|
||
<view :style="{ height: (statusBarHeight + navBarHeight) + 'px' }" />
|
||
|
||
<!-- 搜索结果统计 -->
|
||
<view class="result-tip" v-if="!loading && products.length > 0">
|
||
<text>共计搜到:{{ total }}条结果</text>
|
||
</view>
|
||
|
||
<!-- 结果列表 -->
|
||
<view class="result-list">
|
||
<view
|
||
v-for="item in products"
|
||
:key="item.id"
|
||
class="result-item"
|
||
@click="goDetail(item.id)"
|
||
>
|
||
<image class="result-item__img" :src="fullUrl(item.thumb || (item.bannerImages && item.bannerImages[0]) || '')" mode="aspectFill" />
|
||
<view class="result-item__info">
|
||
<text class="result-item__name">{{ item.name }}</text>
|
||
<view class="result-item__meta">
|
||
<text v-if="item.styleNo">款号:{{ item.styleNo }}</text>
|
||
<text v-if="item.barcode">条码号:{{ item.barcode }}</text>
|
||
</view>
|
||
<view class="result-item__bottom">
|
||
<text class="result-item__price">¥{{ item.basePrice }}元</text>
|
||
<text class="result-item__stock">×{{ item.stock || 1 }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view v-if="loading" class="loading-tip">
|
||
<text>加载中...</text>
|
||
</view>
|
||
<view v-if="!loading && noMore && products.length > 0" class="loading-tip">
|
||
<text style="color: #FF6D9B">没有更多商品了</text>
|
||
</view>
|
||
<view v-if="!loading && products.length === 0" class="empty-tip">
|
||
<image class="empty-tip__img" src="/static/ic_empty.png" mode="aspectFit" />
|
||
<text>没有搜索到相关商品</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue'
|
||
import type { Product } from '../../types'
|
||
import { getProducts } from '../../api/product'
|
||
import { BASE_URL } from '../../utils/request'
|
||
|
||
const keyword = ref('')
|
||
const products = ref<Product[]>([])
|
||
const loading = ref(false)
|
||
const total = ref(0)
|
||
const page = ref(1)
|
||
const pageSize = 20
|
||
const noMore = ref(false)
|
||
|
||
const statusBarHeight = ref(20)
|
||
const navBarHeight = ref(44)
|
||
try {
|
||
const sysInfo = uni.getSystemInfoSync()
|
||
statusBarHeight.value = sysInfo.statusBarHeight || 20
|
||
// #ifdef MP-WEIXIN
|
||
const menuBtn = uni.getMenuButtonBoundingClientRect()
|
||
navBarHeight.value = (menuBtn.top - (sysInfo.statusBarHeight || 20)) * 2 + menuBtn.height
|
||
// #endif
|
||
} catch { /* fallback */ }
|
||
|
||
function goBack() {
|
||
uni.navigateBack({ delta: 1 })
|
||
}
|
||
|
||
function fullUrl(path: string): string {
|
||
if (!path) return ''
|
||
if (path.startsWith('http')) return path
|
||
return BASE_URL + path
|
||
}
|
||
|
||
function goDetail(id: number) {
|
||
uni.navigateTo({ url: `/pages/product/detail?id=${id}` })
|
||
}
|
||
|
||
async function search(reset = false) {
|
||
if (reset) { page.value = 1; products.value = [] }
|
||
loading.value = true
|
||
try {
|
||
const data = await getProducts({ page: page.value, pageSize, keyword: keyword.value } as any)
|
||
if (reset) {
|
||
products.value = data.list
|
||
} else {
|
||
products.value.push(...data.list)
|
||
}
|
||
total.value = data.total || data.list.length
|
||
noMore.value = data.list.length < pageSize
|
||
} catch {
|
||
uni.showToast({ title: '搜索失败', icon: 'none' })
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
const pages = getCurrentPages()
|
||
const currentPage = pages[pages.length - 1] as { options?: { keyword?: string } }
|
||
keyword.value = decodeURIComponent(currentPage.options?.keyword || '')
|
||
if (keyword.value) search(true)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.search-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
}
|
||
.custom-navbar {
|
||
background: linear-gradient(to right, #FFCFDE, #FFA6C4);
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
}
|
||
.custom-navbar__content {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 24rpx;
|
||
}
|
||
.custom-navbar__back {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
}
|
||
.custom-navbar__title {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-size: 34rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
.custom-navbar__placeholder {
|
||
width: 44rpx;
|
||
}
|
||
.result-tip {
|
||
text-align: center;
|
||
padding: 20rpx 0;
|
||
font-size: 26rpx;
|
||
color: #FF6D9B;
|
||
}
|
||
.result-list {
|
||
padding: 0 24rpx;
|
||
}
|
||
.result-item {
|
||
display: flex;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
.result-item__img {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
border-radius: 12rpx;
|
||
flex-shrink: 0;
|
||
background: #f5f5f5;
|
||
}
|
||
.result-item__info {
|
||
flex: 1;
|
||
margin-left: 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
.result-item__name {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
.result-item__meta {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 8rpx;
|
||
}
|
||
.result-item__bottom {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 12rpx;
|
||
}
|
||
.result-item__price {
|
||
font-size: 32rpx;
|
||
color: #FF6D9B;
|
||
font-weight: bold;
|
||
}
|
||
.result-item__stock {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
.empty-tip {
|
||
text-align: center;
|
||
padding: 120rpx 0 60rpx;
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.empty-tip__img {
|
||
width: 300rpx;
|
||
height: 300rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
.loading-tip {
|
||
text-align: center;
|
||
padding: 60rpx 0;
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
}
|
||
</style>
|