hookehuyr

refactor(product): 将产品列表从网格布局改为水平列表布局

- 从 2 列网格布局改为单列水平卡片布局
- 图片尺寸从 200rpx 增加到 220rpx
- 使用圆形文字按钮替代 NutUI 按钮组件
- 优化标签显示逻辑,显示所有标签而非仅前 2 个

技术细节:
- 布局:flex 布局,图片固定宽度 220rpx
- 按钮:纯文字按钮,详情为浅蓝背景,计划书为深蓝背景
- 间距:使用 gap、mt-auto 等优化视觉层次

影响文件:
- src/pages/product-center/index.vue
- components.d.ts (自动更新)
- docs/CHANGELOG.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -35,7 +35,7 @@ declare module 'vue' { ...@@ -35,7 +35,7 @@ declare module 'vue' {
35 PdfPreview: typeof import('./src/components/PdfPreview.vue')['default'] 35 PdfPreview: typeof import('./src/components/PdfPreview.vue')['default']
36 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] 36 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
37 PlanFormContainer: typeof import('./src/components/PlanFormContainer.vue')['default'] 37 PlanFormContainer: typeof import('./src/components/PlanFormContainer.vue')['default']
38 - PlanPopup: typeof import('./src/components/PlanSchemes/PlanPopup.vue')['default'] 38 + PlanPopup: typeof import('./src/components/PlanPopup/index.vue')['default']
39 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] 39 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
40 ProductCard: typeof import('./src/components/ProductCard.vue')['default'] 40 ProductCard: typeof import('./src/components/ProductCard.vue')['default']
41 QrCode: typeof import('./src/components/qrCode.vue')['default'] 41 QrCode: typeof import('./src/components/qrCode.vue')['default']
......
...@@ -5,6 +5,29 @@ ...@@ -5,6 +5,29 @@
5 5
6 --- 6 ---
7 7
8 +## [2026-02-06] - 优化产品中心列表UI (布局调整)
9 +
10 +### 优化
11 +- 增加产品卡片图片和高度(从 200rpx 增加到 220rpx),缓解内容拥挤感
12 +- 调整标题和标签间距,使布局更紧凑
13 +- 使用 `mt-auto` 将操作按钮组沉底,确保与上方标签保持合理间距,并使卡片视觉重心更稳
14 +
15 +### 技术实现
16 +- 图片容器大小:`w-[220rpx] h-[220rpx]`
17 +- 按钮组:添加 `mt-auto`
18 +- 标题间距:`mb-[12rpx]`
19 +- 标签间距:`mb-[8rpx]`
20 +
21 +## [2026-02-06] - 优化产品中心列表UI
22 +
23 +### 优化
24 +- 调整产品列表动态标签的间距,修复 gap 属性拼写错误
25 +- 将产品卡片右侧的图标按钮组改为更直观的纯文字按钮("详情"、"计划书")
26 +
27 +### 技术实现
28 +- 修复 `gap-[8rpx``gap-[8rpx]`,增加标签间距
29 +- 替换 `IconFont` 组件为带样式的 `view` 标签,统一按钮风格
30 +
8 ## [2026-02-06] - 添加 mock 数据支持并修复滚动加载功能 31 ## [2026-02-06] - 添加 mock 数据支持并修复滚动加载功能
9 32
10 ### 新增 33 ### 新增
......
...@@ -51,64 +51,65 @@ ...@@ -51,64 +51,65 @@
51 <text class="text-gray-400 text-[28rpx]">加载中...</text> 51 <text class="text-gray-400 text-[28rpx]">加载中...</text>
52 </view> 52 </view>
53 53
54 - <!-- Product Grid --> 54 + <!-- Product List -->
55 - <view v-else class="flex flex-wrap justify-between px-[40rpx]"> 55 + <view v-else class="px-[40rpx]">
56 <!-- Card Item --> 56 <!-- Card Item -->
57 <view v-for="(item, index) in products" :key="item.id" 57 <view v-for="(item, index) in products" :key="item.id"
58 - class="w-[48%] bg-white rounded-[24rpx] overflow-hidden mb-[24rpx] shadow-sm flex flex-col active:scale-[0.98] transition-transform duration-200" 58 + class="bg-white rounded-[24rpx] overflow-hidden mb-[24rpx] shadow-sm active:scale-[0.98] transition-transform duration-200"
59 :style="{ animationDelay: `${index * 50}ms` }" 59 :style="{ animationDelay: `${index * 50}ms` }"
60 - @tap="handleProductClick(item)"> 60 + >
61 - <!-- Image Container --> 61 + <!-- Product Content (Horizontal Layout) -->
62 - <view class="relative w-full h-[200rpx] product-card-item"> 62 + <view class="flex gap-[24rpx]" @tap="handleProductClick(item)">
63 - <image :src="item.cover_image" class="w-full h-full object-cover bg-gray-100" mode="aspectFill" /> 63 + <!-- Image Container -->
64 - <!-- Tag --> 64 + <view class="relative w-[220rpx] h-[220rpx] flex-shrink-0 product-card-item">
65 - <view v-if="item.recommend === 'hot'" 65 + <image :src="item.cover_image" class="w-full h-full object-cover bg-gray-100" mode="aspectFill" />
66 - class="absolute top-[12rpx] right-[12rpx] bg-red-500 text-white text-[20rpx] px-[12rpx] py-[4rpx] rounded-full"> 66 + <!-- Tag -->
67 - 热卖 67 + <view v-if="item.recommend === 'hot'"
68 + class="absolute top-[12rpx] right-[12rpx] bg-red-500 text-white text-[20rpx] px-[12rpx] py-[4rpx] rounded-full">
69 + 热卖
70 + </view>
68 </view> 71 </view>
69 - </view>
70 72
71 - <!-- Content --> 73 + <!-- Content -->
72 - <view class="p-[20rpx] flex flex-col flex-1"> 74 + <view class="flex-1 flex flex-col py-[20rpx] pr-[20rpx]">
73 - <!-- Title --> 75 + <!-- Title -->
74 - <view class="text-[#1F2937] text-[28rpx] font-medium leading-[1.4] line-clamp-2 mb-[16rpx]"> 76 + <view class="text-[#1F2937] text-[28rpx] font-medium leading-[1.4] line-clamp-2 mb-[12rpx]">
75 - {{ item.product_name }} 77 + {{ item.product_name }}
76 - </view> 78 + </view>
77 79
78 - <!-- 动态标签 --> 80 + <!-- 动态标签 -->
79 - <view v-if="item.tags && item.tags.length > 0" class="flex flex-wrap gap-[8rpx] mb-[16rpx]"> 81 + <view v-if="item.tags && item.tags.length > 0" class="flex flex-wrap gap-[8rpx] mb-[8rpx]">
80 - <view 82 + <view
81 - v-for="tag in item.tags.slice(0, 2)" 83 + v-for="tag in item.tags"
82 - :key="tag.id" 84 + :key="tag.id"
83 - class="text-[20rpx] px-[12rpx] py-[4rpx] rounded-full" 85 + class="text-[20rpx] px-[12rpx] py-[4rpx] rounded-full"
84 - :style="{ 86 + :style="{
85 - backgroundColor: tag.bg_color, 87 + backgroundColor: tag.bg_color,
86 - color: tag.text_color 88 + color: tag.text_color
87 - }" 89 + }"
88 - > 90 + >
89 - {{ tag.name }} 91 + {{ tag.name }}
92 + </view>
90 </view> 93 </view>
91 - </view>
92 94
93 - <!-- 按钮组 --> 95 + <!-- 按钮组 - 靠右对齐(纯文字按钮) -->
94 - <view class="flex gap-[12rpx] mt-auto"> 96 + <view class="flex gap-[16rpx] ml-auto items-center mt-auto" @tap.stop>
95 - <nut-button 97 + <!-- 详情按钮 -->
96 - plain 98 + <view
97 - color="#2563EB" 99 + class="text-[24rpx] text-[#2563EB] bg-blue-50 px-[24rpx] py-[8rpx] rounded-full active:bg-blue-100 transition-colors duration-200"
98 - size="small" 100 + @tap.stop="handleProductClick(item)"
99 - class="flex-1 !h-[56rpx] !rounded-[12rpx] !text-[22rpx] !m-0 !border-blue-600" 101 + >
100 - @tap.stop="handleProductClick(item)" 102 + 详情
101 - > 103 + </view>
102 - 详情 104 +
103 - </nut-button> 105 + <!-- 计划书按钮 -->
104 - <nut-button 106 + <view
105 - color="#2563EB" 107 + class="text-[24rpx] text-white bg-blue-500 px-[24rpx] py-[8rpx] rounded-full active:bg-blue-600 transition-colors duration-200"
106 - size="small" 108 + @tap.stop="openPlanPopup(item)"
107 - class="flex-1 !h-[56rpx] !rounded-[12rpx] !text-[22rpx] !m-0" 109 + >
108 - @tap.stop="openPlanPopup(item)" 110 + 计划书
109 - > 111 + </view>
110 - 计划书 112 + </view>
111 - </nut-button>
112 </view> 113 </view>
113 </view> 114 </view>
114 </view> 115 </view>
......