Files
WAI_Project_UNIX/pages/mall/search.uvue
2026-01-21 01:37:34 +08:00

199 lines
4.1 KiB
Plaintext

<template>
<cl-page>
<cl-topbar title="搜索" />
<view class="search-page">
<view class="search-bar">
<cl-icon class="search-icon" name="search" :size="32" color="#999" />
<input class="search-input" v-model="keyword" type="text" placeholder="请输入商品名称" @confirm="doSearch" />
<view v-if="keyword.length > 0" class="clear-btn" @click="clearKeyword">
<cl-icon name="close-circle" :size="32" color="#999" />
</view>
</view>
<view v-if="searched == false" class="history-section">
<view class="section-header">
<text class="section-title">搜索历史</text>
<view class="clear-history" @click="clearHistory">
<cl-icon name="delete" :size="28" color="#999" />
</view>
</view>
<view class="history-tags">
<view v-for="(item, index) in history" :key="index" class="history-tag" @click="searchByHistory(item)">
<text>{{ item }}</text>
</view>
</view>
</view>
<view v-if="searched == true" class="result-section">
<view class="product-grid">
<view
v-for="product in results"
:key="product.id"
class="product-card"
@click="goDetail(product.id)"
>
<image class="product-image" :src="product.mainImage != null ? product.mainImage : '/static/images/product.png'" mode="aspectFill" />
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-price">¥{{ product.price }}</text>
</view>
</view>
</view>
<cl-empty v-if="results.length === 0" text="未找到商品" />
</view>
</view>
</cl-page>
</template>
<script setup lang="uts">
import { ref } from 'vue';
import { router, useCool } from '@/cool';
const { service } = useCool();
const keyword = ref('');
const searched = ref(false);
const history = ref<string[]>(['番茄', '草莓', '苹果']);
const results = ref<any[]>([]);
function clearKeyword() {
keyword.value = '';
searched.value = false;
}
async function doSearch() {
if (keyword.value.trim().length == 0) return;
// 添加到历史
if (history.value.includes(keyword.value) == false) {
history.value.unshift(keyword.value);
if (history.value.length > 10) history.value.pop();
}
searched.value = true;
try {
const res = await service.nongchuang.product.list({ keyword: keyword.value });
if (res != null) {
const data = res as UTSJSONObject;
const list = data.getArray<any>("list");
results.value = list != null ? list : (res as any[]);
}
} catch (e) {
results.value = [];
}
}
function searchByHistory(item: string) {
keyword.value = item;
doSearch();
}
function clearHistory() {
history.value = [];
}
function goDetail(id: number) {
router.push({ path: '/pages/mall/detail', query: { id } });
}
</script>
<style lang="scss" scoped>
.search-page {
padding: 20rpx;
}
.search-bar {
display: flex;
flex-direction: row;
align-items: center;
padding: 16rpx 24rpx;
background: #f5f5f5;
border-radius: 40rpx;
.search-icon {
margin-right: 16rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
}
}
.history-section {
margin-top: 30rpx;
.section-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
}
.history-tags {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-top: 20rpx;
.history-tag {
padding: 12rpx 24rpx;
background: #f5f5f5;
border-radius: 30rpx;
font-size: 26rpx;
color: #666;
margin-right: 16rpx;
margin-bottom: 16rpx;
}
}
}
.result-section {
margin-top: 20rpx;
}
.product-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.product-card {
width: 48%;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
margin-bottom: 20rpx;
.product-image {
width: 100%;
height: 240rpx;
}
.product-info {
padding: 16rpx;
.product-name {
font-size: 28rpx;
color: #333;
}
.product-price {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
margin-top: 10rpx;
}
}
}
</style>