Jelajahi Sumber

🔍 feat(search): 新增搜索页面和搜索结果页面

## 新增功能
- 新增搜索页面,支持搜索历史和热门搜索功能
- 新增搜索结果页面,支持商品搜索和分页加载
- 更新首页和商品列表页搜索框,支持跳转到搜索页面
- 应用tcb-shop-demo搜索页设计规范

## 技术实现
- 创建 `mini/src/pages/search/index.tsx` 搜索页面
- 创建 `mini/src/pages/search-result/index.tsx` 搜索结果页面
- 集成商品搜索API,支持关键词搜索
- 实现搜索历史和热门搜索功能
- 支持搜索结果分页和加载更多

## 文档更新
- 更新史诗001文档,添加故事17和故事18
- 完成度更新为18/18故事完成

🤖 Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 bulan lalu
induk
melakukan
6d12293245

+ 73 - 4
docs/prd/epic-001-tcb-shop-theme-integration.md

@@ -4,8 +4,8 @@
 将tcb-shop-demo包中的主题、样式和设计规范分析并集成到当前小程序项目中,提升UI一致性和用户体验,同时保持现有系统的完整性。
 
 ## 当前进度
-- **完成度**: 100% (16/16 故事完成)
-- **已集成**: 主题变量、颜色系统、字体系统、布局工具类、组件样式、首页UI重构、首页商品列表数据读取、首页轮播图后台广告数据、用户中心UI重构、商品分类页基础组件开发、商品列表页UI重构、商品详情页UI重构、购物车页面UI重构、订单列表页UI重构、订单详情页UI重构、订单提交页UI重构、收货地址列表页UI重构
+- **完成度**: 100% (18/18 故事完成)
+- **已集成**: 主题变量、颜色系统、字体系统、布局工具类、组件样式、首页UI重构、首页商品列表数据读取、首页轮播图后台广告数据、用户中心UI重构、商品分类页基础组件开发、商品列表页UI重构、商品详情页UI重构、购物车页面UI重构、订单列表页UI重构、订单详情页UI重构、订单提交页UI重构、收货地址列表页UI重构、搜索页面开发、搜索结果页面开发
 
 ## 史诗描述
 
@@ -302,6 +302,47 @@
      - 底部添加按钮功能正常,支持地址数量限制
      - 页面组件TypeScript编译正常,无错误
 
+17. ✅ **故事17:搜索页面开发** - 参照tcb-shop-demo搜索页设计,在mini中新增搜索页面,支持搜索历史和热门搜索功能 (已完成)
+   - **对照文件**:
+     - `tcb-shop-demo/pages/goods/search/index.wxml` - 搜索页结构模板
+     - `tcb-shop-demo/pages/goods/search/index.wxss` - 搜索页样式文件
+     - `tcb-shop-demo/pages/goods/search/index.js` - 搜索页逻辑文件
+   - **目标文件**:
+     - `mini/src/pages/search/index.tsx` - 搜索页面
+     - `mini/src/pages/search/index.css` - 搜索页面样式
+   - **技术要点**:
+     - 实现搜索栏组件,支持输入和提交
+     - 集成搜索历史功能,支持历史记录显示和清空
+     - 集成热门搜索功能,显示热门搜索词
+     - 支持从历史搜索和热门搜索点击直接搜索
+     - 应用tcb-shop-demo搜索页设计规范
+     - 支持空状态显示
+   - **成功标准**:
+     - 搜索页面UI与tcb-shop-demo设计完全一致
+     - 搜索历史和热门搜索功能正常工作
+     - 页面组件TypeScript编译正常,无错误
+
+18. ✅ **故事18:搜索结果页面开发** - 参照tcb-shop-demo搜索结果页设计,在mini中新增搜索结果页面,支持商品搜索和筛选功能 (已完成)
+   - **对照文件**:
+     - `tcb-shop-demo/pages/goods/result/index.wxml` - 搜索结果页结构模板
+     - `tcb-shop-demo/pages/goods/result/index.wxss` - 搜索结果页样式文件
+     - `tcb-shop-demo/pages/goods/result/index.js` - 搜索结果页逻辑文件
+   - **目标文件**:
+     - `mini/src/pages/search-result/index.tsx` - 搜索结果页面
+     - `mini/src/pages/search-result/index.css` - 搜索结果页面样式
+   - **技术要点**:
+     - 实现搜索结果页面布局,包含搜索栏和商品列表
+     - 集成商品搜索API,支持关键词搜索
+     - 实现搜索结果分页和加载更多功能
+     - 支持搜索结果空状态显示
+     - 应用tcb-shop-demo搜索结果页设计规范
+     - 支持下拉刷新功能
+   - **成功标准**:
+     - 搜索结果页面UI与tcb-shop-demo设计完全一致
+     - 商品搜索功能正常工作
+     - 分页和加载更多功能正常
+     - 页面组件TypeScript编译正常,无错误
+
 ## 兼容性要求
 
 - [ ] 现有API保持不变
@@ -317,7 +358,7 @@
 
 ## 完成定义
 
-- [ ] 所有故事完成且验收标准满足 (15/16 完成)
+- [x] 所有故事完成且验收标准满足 (18/18 完成)
 - [x] 现有功能通过测试验证
 - [x] 集成点正常工作
 - [x] 文档适当更新
@@ -456,6 +497,8 @@
 - ✅ 订单列表页与tcb-shop-demo设计一致
 - ✅ 订单详情页与tcb-shop-demo设计一致
 - ✅ 收货地址列表页与tcb-shop-demo设计一致
+- ✅ 搜索页面与tcb-shop-demo设计一致
+- ✅ 搜索结果页面与tcb-shop-demo设计一致
 
 ## 故事完成状态
 
@@ -660,4 +703,30 @@
   - 应用了tcb-shop-demo订单列表页设计规范,创建专用CSS文件
   - 实现了订单操作功能(查看详情、去支付、取消订单、确认收货等)
   - 创建了完整的单元测试 `mini/tests/unit/pages/order-list/basic.test.tsx`
-  - 所有8个测试用例通过,页面组件TypeScript编译正常,无错误
+  - 所有8个测试用例通过,页面组件TypeScript编译正常,无错误
+
+### 故事17:搜索页面开发 ✅ (已完成)
+- **完成日期**: 2025-11-22
+- **实施者**: Claude Agent
+- **关键成果**:
+  - 创建了 `mini/src/pages/search/index.tsx` 搜索页面
+  - 创建了专用CSS文件 `mini/src/pages/search/index.css`,应用tcb-shop-demo设计规范
+  - 实现了搜索栏组件,支持输入和提交
+  - 集成了搜索历史功能,支持历史记录显示和清空
+  - 集成了热门搜索功能,显示热门搜索词
+  - 支持从历史搜索和热门搜索点击直接搜索
+  - 支持空状态显示
+  - 页面组件TypeScript编译正常,无错误
+
+### 故事18:搜索结果页面开发 ✅ (已完成)
+- **完成日期**: 2025-11-22
+- **实施者**: Claude Agent
+- **关键成果**:
+  - 创建了 `mini/src/pages/search-result/index.tsx` 搜索结果页面
+  - 创建了专用CSS文件 `mini/src/pages/search-result/index.css`,应用tcb-shop-demo设计规范
+  - 实现了搜索结果页面布局,包含搜索栏和商品列表
+  - 集成了商品搜索API,支持关键词搜索
+  - 实现了搜索结果分页和加载更多功能
+  - 支持搜索结果空状态显示
+  - 支持下拉刷新功能
+  - 页面组件TypeScript编译正常,无错误

+ 14 - 11
mini/src/pages/goods-list/index.tsx

@@ -115,17 +115,20 @@ export default function GoodsListPage() {
         <View className="goods-page-container bg-[#f2f2f2] p-[20rpx_24rpx]">
           {/* 搜索栏 */}
           <View className="search-bar-container mb-4">
-            <TDesignSearch
-              placeholder="搜索你想要的商品..."
-              shape="round"
-              value={searchKeyword}
-              onChange={(value) => setSearchKeyword(value)}
-              onSubmit={() => refetch()}
-              onClear={() => {
-                setSearchKeyword('')
-                refetch()
-              }}
-            />
+            <View onClick={() => Taro.navigateTo({ url: '/pages/search/index' })}>
+              <TDesignSearch
+                placeholder="搜索你想要的商品..."
+                shape="round"
+                value={searchKeyword}
+                onChange={(value) => setSearchKeyword(value)}
+                onSubmit={() => refetch()}
+                onClear={() => {
+                  setSearchKeyword('')
+                  refetch()
+                }}
+                disabled={true}
+              />
+            </View>
           </View>
 
           {/* 分类筛选 */}

+ 3 - 1
mini/src/pages/index/index.tsx

@@ -131,7 +131,9 @@ const HomePage: React.FC = () => {
 
   // 搜索框点击
   const handleSearchClick = () => {
-    console.log('点击搜索框')
+    Taro.navigateTo({
+      url: '/pages/search/index'
+    })
   }
 
   return (

+ 108 - 0
mini/src/pages/search-result/index.css

@@ -0,0 +1,108 @@
+.search-result-page {
+  width: 100vw;
+  height: 100vh;
+  background-color: #fff;
+  box-sizing: border-box;
+}
+
+.search-result-content {
+  height: calc(100vh - 88rpx);
+}
+
+.search-input-container {
+  padding: 20rpx 30rpx;
+  background-color: #fff;
+}
+
+.result-container {
+  padding: 0 30rpx;
+}
+
+.result-header {
+  margin-bottom: 24rpx;
+}
+
+.result-title {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+  line-height: 40rpx;
+}
+
+.result-count {
+  font-size: 24rpx;
+  color: #999;
+  line-height: 34rpx;
+  margin-top: 8rpx;
+  display: block;
+}
+
+.goods-list-container {
+  background-color: #f2f2f2;
+  border-radius: 16rpx;
+  padding: 20rpx 24rpx;
+  overflow-y: scroll;
+  -webkit-overflow-scrolling: touch;
+}
+
+.loading-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 100rpx 0;
+}
+
+.loading-text {
+  font-size: 28rpx;
+  color: #999;
+  margin-top: 16rpx;
+}
+
+.empty-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 200rpx 0;
+}
+
+.empty-icon {
+  width: 120rpx;
+  height: 120rpx;
+  color: #ccc;
+  margin-bottom: 32rpx;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  color: #999;
+  margin-bottom: 16rpx;
+}
+
+.empty-subtext {
+  font-size: 24rpx;
+  color: #ccc;
+}
+
+.loading-more-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 32rpx 0;
+}
+
+.loading-more-text {
+  font-size: 24rpx;
+  color: #999;
+  margin-left: 8rpx;
+}
+
+.no-more-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 32rpx 0;
+  color: #999;
+  font-size: 24rpx;
+}

+ 216 - 0
mini/src/pages/search-result/index.tsx

@@ -0,0 +1,216 @@
+import React, { useState, useEffect } from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import { useInfiniteQuery } from '@tanstack/react-query'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import TDesignSearch from '@/components/tdesign/search'
+import GoodsList from '@/components/goods-list'
+import { goodsClient } from '@/api'
+import { InferResponseType } from 'hono'
+import { useCart } from '@/utils/cart'
+import './index.css'
+
+type GoodsResponse = InferResponseType<typeof goodsClient.$get, 200>
+type Goods = GoodsResponse['data'][0]
+
+const SearchResultPage: React.FC = () => {
+  const [keyword, setKeyword] = useState('')
+  const [searchValue, setSearchValue] = useState('')
+  const { addToCart } = useCart()
+
+  // 获取页面参数
+  useEffect(() => {
+    const params = Taro.getCurrentInstance().router?.params
+    const searchKeyword = params?.keyword || ''
+    setKeyword(searchKeyword)
+    setSearchValue(searchKeyword)
+  }, [])
+
+  const {
+    data,
+    isLoading,
+    isFetchingNextPage,
+    fetchNextPage,
+    hasNextPage,
+    refetch
+  } = useInfiniteQuery({
+    queryKey: ['search-goods-infinite', keyword],
+    queryFn: async ({ pageParam = 1 }) => {
+      const response = await goodsClient.$get({
+        query: {
+          page: pageParam,
+          pageSize: 10,
+          keyword: keyword,
+          filters: JSON.stringify({ state: 1 }) // 只显示可用的商品
+        }
+      })
+      if (response.status !== 200) {
+        throw new Error('搜索商品失败')
+      }
+      return response.json()
+    },
+    getNextPageParam: (lastPage) => {
+      const { pagination } = lastPage
+      const totalPages = Math.ceil(pagination.total / pagination.pageSize)
+      return pagination.current < totalPages ? pagination.current + 1 : undefined
+    },
+    staleTime: 5 * 60 * 1000,
+    initialPageParam: 1,
+    enabled: !!keyword, // 只有有搜索关键词时才执行查询
+  })
+
+  // 合并所有分页数据
+  const allGoods = data?.pages.flatMap(page => page.data) || []
+
+  // 触底加载更多
+  const handleScrollToLower = () => {
+    if (hasNextPage && !isFetchingNextPage) {
+      fetchNextPage()
+    }
+  }
+
+  // 下拉刷新
+  const onPullDownRefresh = () => {
+    refetch().finally(() => {
+      Taro.stopPullDownRefresh()
+    })
+  }
+
+  // 处理搜索提交
+  const handleSubmit = (value: string) => {
+    if (!value.trim()) return
+
+    // 更新搜索关键词并重新搜索
+    setKeyword(value)
+    setSearchValue(value)
+
+    // 重置分页数据
+    refetch()
+  }
+
+  // 跳转到商品详情
+  const handleGoodsClick = (goods: Goods) => {
+    Taro.navigateTo({
+      url: `/pages/goods-detail/index?id=${goods.id}`
+    })
+  }
+
+  // 添加到购物车
+  const handleAddToCart = (goods: Goods) => {
+    addToCart({
+      id: goods.id,
+      name: goods.name,
+      price: goods.price,
+      image: goods.imageFile?.fullUrl || '',
+      stock: goods.stock,
+      quantity: 1
+    })
+    Taro.showToast({
+      title: '已添加到购物车',
+      icon: 'success'
+    })
+  }
+
+  return (
+    <View className="search-result-page">
+      <Navbar
+        title="搜索结果"
+        leftIcon="i-heroicons-chevron-left-20-solid"
+        onClickLeft={() => Taro.navigateBack()}
+        className="bg-white"
+      />
+
+      <ScrollView
+        className="search-result-content"
+        scrollY
+        onScrollToLower={handleScrollToLower}
+        refresherEnabled
+        refresherTriggered={false}
+        onRefresherRefresh={onPullDownRefresh}
+      >
+        {/* 搜索栏 */}
+        <View className="search-input-container">
+          <TDesignSearch
+            placeholder="搜索商品..."
+            shape="round"
+            value={searchValue}
+            onChange={(value) => setSearchValue(value)}
+            onSubmit={() => handleSubmit(searchValue)}
+            onClear={() => {
+              setSearchValue('')
+              setKeyword('')
+            }}
+          />
+        </View>
+
+        {/* 搜索结果 */}
+        <View className="result-container">
+          {/* 搜索结果标题 */}
+          {keyword && (
+            <View className="result-header">
+              <Text className="result-title">
+                搜索结果:"{keyword}"
+              </Text>
+              <Text className="result-count">
+                共找到 {data?.pages[0]?.pagination?.total || 0} 件商品
+              </Text>
+            </View>
+          )}
+
+          {/* 商品列表 */}
+          {isLoading ? (
+            <View className="loading-container">
+              <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+              <Text className="loading-text">搜索中...</Text>
+            </View>
+          ) : allGoods.length === 0 ? (
+            <View className="empty-container">
+              <View className="i-heroicons-magnifying-glass-20-solid empty-icon" />
+              <Text className="empty-text">
+                {keyword ? '暂无相关商品' : '请输入搜索关键词'}
+              </Text>
+              <Text className="empty-subtext">
+                {keyword ? '换个关键词试试吧' : '搜索你想要的商品'}
+              </Text>
+            </View>
+          ) : (
+            <>
+              <View className="goods-list-container">
+                <GoodsList
+                  goodsList={allGoods.map(goods => ({
+                    id: goods.id.toString(),
+                    name: goods.name,
+                    cover_image: goods.imageFile?.fullUrl,
+                    price: goods.price,
+                    originPrice: goods.originPrice,
+                    tags: goods.stock <= 0 ? ['已售罄'] : goods.salesNum > 100 ? ['热销'] : []
+                  }))}
+                  onClick={(goods) => handleGoodsClick(allGoods.find(g => g.id.toString() === goods.id)!)}
+                  onAddCart={(goods) => handleAddToCart(allGoods.find(g => g.id.toString() === goods.id)!)}
+                />
+              </View>
+
+              {/* 加载更多状态 */}
+              {isFetchingNextPage && (
+                <View className="loading-more-container">
+                  <View className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500" />
+                  <Text className="loading-more-text">加载更多...</Text>
+                </View>
+              )}
+
+              {/* 无更多数据状态 */}
+              {!hasNextPage && allGoods.length > 0 && (
+                <View className="no-more-container">
+                  <View className="i-heroicons-check-circle-20-solid w-4 h-4 mr-1" />
+                  <Text className="no-more-text">已经到底啦</Text>
+                </View>
+              )}
+            </>
+          )}
+        </View>
+      </ScrollView>
+    </View>
+  )
+}
+
+export default SearchResultPage

+ 105 - 0
mini/src/pages/search/index.css

@@ -0,0 +1,105 @@
+.search-page {
+  width: 100vw;
+  height: 100vh;
+  background-color: #fff;
+  box-sizing: border-box;
+}
+
+.search-page-content {
+  height: calc(100vh - 88rpx);
+  padding: 0 30rpx;
+}
+
+.search-input-container {
+  padding: 20rpx 0;
+}
+
+.search-section {
+  margin-top: 44rpx;
+}
+
+.search-header {
+  display: flex;
+  flex-flow: row nowrap;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.search-title {
+  font-size: 30rpx;
+  font-family: PingFangSC-Semibold, PingFang SC;
+  font-weight: 600;
+  color: rgba(51, 51, 51, 1);
+  line-height: 42rpx;
+}
+
+.search-clear {
+  font-size: 24rpx;
+  font-family: PingFang SC;
+  line-height: 32rpx;
+  color: #999999;
+  font-weight: normal;
+}
+
+.search-content {
+  overflow: hidden;
+  display: flex;
+  flex-flow: row wrap;
+  justify-content: flex-start;
+  align-items: flex-start;
+  margin-top: 24rpx;
+}
+
+.search-item {
+  color: #333333;
+  font-size: 24rpx;
+  line-height: 32rpx;
+  font-weight: normal;
+  margin-right: 24rpx;
+  margin-bottom: 24rpx;
+  background: #f5f5f5;
+  border-radius: 38rpx;
+  padding: 12rpx 24rpx;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.search-item:hover {
+  position: relative;
+  top: 3rpx;
+  left: 3rpx;
+  box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1) inset;
+}
+
+.search-item-text {
+  color: #333333;
+  font-size: 24rpx;
+  line-height: 32rpx;
+  font-weight: normal;
+}
+
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  margin-top: 200rpx;
+}
+
+.empty-icon {
+  width: 120rpx;
+  height: 120rpx;
+  color: #ccc;
+  margin-bottom: 32rpx;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  color: #999;
+  margin-bottom: 16rpx;
+}
+
+.empty-subtext {
+  font-size: 24rpx;
+  color: #ccc;
+}

+ 179 - 0
mini/src/pages/search/index.tsx

@@ -0,0 +1,179 @@
+import React, { useState, useEffect } from 'react'
+import { View, Text, ScrollView } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import TDesignSearch from '@/components/tdesign/search'
+import { useQuery } from '@tanstack/react-query'
+import { searchClient } from '@/api'
+import { InferResponseType } from 'hono'
+import './index.css'
+
+type SearchHistoryResponse = InferResponseType<typeof searchClient.history.$get, 200>
+type SearchPopularResponse = InferResponseType<typeof searchClient.popular.$get, 200>
+
+const SearchPage: React.FC = () => {
+  const [searchValue, setSearchValue] = useState('')
+
+  // 搜索历史查询
+  const {
+    data: historyData,
+    isLoading: isHistoryLoading,
+    refetch: refetchHistory
+  } = useQuery({
+    queryKey: ['search-history'],
+    queryFn: async () => {
+      const response = await searchClient.history.$get()
+      if (response.status !== 200) {
+        throw new Error('获取搜索历史失败')
+      }
+      return response.json()
+    },
+    staleTime: 5 * 60 * 1000,
+  })
+
+  // 热门搜索查询
+  const {
+    data: popularData,
+    isLoading: isPopularLoading
+  } = useQuery({
+    queryKey: ['search-popular'],
+    queryFn: async () => {
+      const response = await searchClient.popular.$get()
+      if (response.status !== 200) {
+        throw new Error('获取热门搜索失败')
+      }
+      return response.json()
+    },
+    staleTime: 5 * 60 * 1000,
+  })
+
+  // 搜索历史
+  const historyWords = historyData?.data?.historyWords || []
+  // 热门搜索
+  const popularWords = popularData?.data?.popularWords || []
+
+  // 页面显示时刷新搜索历史
+  useEffect(() => {
+    refetchHistory()
+  }, [])
+
+  // 处理搜索提交
+  const handleSubmit = (value: string) => {
+    if (!value.trim()) return
+
+    // 跳转到搜索结果页面
+    Taro.navigateTo({
+      url: `/pages/search-result/index?keyword=${encodeURIComponent(value)}`
+    })
+  }
+
+  // 点击历史搜索项
+  const handleHistoryTap = (word: string) => {
+    setSearchValue(word)
+    Taro.navigateTo({
+      url: `/pages/search-result/index?keyword=${encodeURIComponent(word)}`
+    })
+  }
+
+  // 点击热门搜索项
+  const handlePopularTap = (word: string) => {
+    setSearchValue(word)
+    Taro.navigateTo({
+      url: `/pages/search-result/index?keyword=${encodeURIComponent(word)}`
+    })
+  }
+
+  // 清空搜索历史
+  const handleClearHistory = async () => {
+    try {
+      await searchClient.history.$delete()
+      refetchHistory()
+    } catch (error) {
+      console.error('清空搜索历史失败:', error)
+      Taro.showToast({
+        title: '清空失败',
+        icon: 'error'
+      })
+    }
+  }
+
+  return (
+    <View className="search-page">
+      <Navbar
+        title="搜索"
+        leftIcon="i-heroicons-chevron-left-20-solid"
+        onClickLeft={() => Taro.navigateBack()}
+        className="bg-white"
+      />
+
+      <ScrollView className="search-page-content" scrollY>
+        {/* 搜索栏 */}
+        <View className="search-input-container">
+          <TDesignSearch
+            placeholder="搜索商品..."
+            shape="round"
+            value={searchValue}
+            onChange={(value) => setSearchValue(value)}
+            onSubmit={() => handleSubmit(searchValue)}
+            onClear={() => setSearchValue('')}
+            focus
+          />
+        </View>
+
+        {/* 搜索历史 */}
+        {historyWords.length > 0 && (
+          <View className="search-section">
+            <View className="search-header">
+              <Text className="search-title">搜索历史</Text>
+              <Text className="search-clear" onClick={handleClearHistory}>
+                清空
+              </Text>
+            </View>
+            <View className="search-content">
+              {historyWords.map((word: string, index: number) => (
+                <View
+                  key={index}
+                  className="search-item"
+                  onClick={() => handleHistoryTap(word)}
+                >
+                  <Text className="search-item-text">{word}</Text>
+                </View>
+              ))}
+            </View>
+          </View>
+        )}
+
+        {/* 热门搜索 */}
+        {popularWords.length > 0 && (
+          <View className="search-section">
+            <View className="search-header">
+              <Text className="search-title">热门搜索</Text>
+            </View>
+            <View className="search-content">
+              {popularWords.map((word: string, index: number) => (
+                <View
+                  key={index}
+                  className="search-item"
+                  onClick={() => handlePopularTap(word)}
+                >
+                  <Text className="search-item-text">{word}</Text>
+                </View>
+              ))}
+            </View>
+          </View>
+        )}
+
+        {/* 空状态 */}
+        {historyWords.length === 0 && popularWords.length === 0 && (
+          <View className="empty-state">
+            <View className="i-heroicons-magnifying-glass-20-solid empty-icon" />
+            <Text className="empty-text">暂无搜索记录</Text>
+            <Text className="empty-subtext">输入关键词搜索商品</Text>
+          </View>
+        )}
+      </ScrollView>
+    </View>
+  )
+}
+
+export default SearchPage