topfans/docs/superpowers/specs/2026-05-28-热门推荐模块前端设计.md

388 lines
12 KiB
Markdown
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.

# 热门推荐模块前端设计
## 一、页面结构
### 完整页面布局4个分类区块纵向堆叠
```
┌──────────────────────────────────────┐
│ BannerCarousel │
├──────────────────────────────────────┤
│ 热门推荐 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ 热门星卡 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ 热门吧唧 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ 热门海报 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ CreationGrid原有组件不变
│ [热门作品] [最新作品] [星卡] [吧唧]... │
└──────────────────────────────────────┘
```
### 单个 HotCategoryBlock 内部结构
```
┌──────────────────────────────────────┐
│ 热门推荐 │ ← block-title
├──────────────────────────────────────┤
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ │
│ └────┘ └────┘ └────┘ └────┘ │ ← block-grid (4×2)
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│ ← block-actions
└──────────────────────────────────────┘
```
---
## 二、组件结构
### HotCategoryBlock.vue
**Props**:
```javascript
{
categoryType: String, // 后端返回的 type: "hot_recommend" / "hot_star_card" / "hot_badge" / "hot_poster"
title: String // 后端返回的标题: "热门推荐"
}
```
**状态**:
- `items: Array` — 作品列表
- `loading: Boolean` — 首次加载中(显示骨架屏)
- `refreshing: Boolean` — 刷新中(按钮旋转动画)
- `hasMore: Boolean` — 是否有更多可加载
**事件**:
- `cardClick(item)` — 点击卡片,跳转详情
---
## 三、样式规范
### 3.1 布局
| 属性 | 值 |
|------|-----|
| 区块间距 | 上下 `32rpx`,左右 `24rpx` |
| 网格 | 4列 × 2行间距 `16rpx` |
| 卡片宽 | `=(屏宽 - 48rpx - 48rpx) / 4` |
| 卡片高 | `卡片宽 × 1.3` |
| 圆角 | `16rpx` |
### 3.2 卡片样式
```css
.hot-card {
border-radius: 16rpx;
overflow: hidden;
position: relative;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10rpx);
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
}
.hot-card-image {
width: 100%;
height: 100%;
display: block;
}
/* 底部渐变遮罩 */
.hot-card-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 64rpx;
background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
display: flex;
align-items: flex-end;
justify-content: space-between;
padding: 0 12rpx 12rpx;
}
.hot-card-user {
display: flex;
align-items: center;
}
.user-avatar {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
margin-right: 6rpx;
}
.user-name {
font-size: 20rpx;
color: #fff;
max-width: 120rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hot-card-likes {
display: flex;
align-items: center;
}
.like-icon {
width: 20rpx;
height: 20rpx;
margin-right: 4rpx;
}
.like-count {
font-size: 20rpx;
color: #fff;
}
```
### 3.3 标题栏样式
```css
.block-title {
font-size: 30rpx;
font-weight: 600;
color: #fff;
padding: 0 24rpx 16rpx;
}
```
### 3.4 操作按钮样式
```css
.block-actions {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 16rpx 24rpx 0;
gap: 24rpx;
}
.refresh-btn {
display: flex;
align-items: center;
}
.refresh-btn .action-icon {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.7);
}
.refresh-btn.spinning .action-icon {
animation: spin 0.8s linear;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.more-btn {
display: flex;
align-items: center;
}
.more-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.7);
}
.more-arrow {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.7);
margin-left: 4rpx;
}
```
---
## 四、状态处理
| 状态 | 表现 |
|------|------|
| `loading=true` | 显示 8 个骨架屏卡片,操作按钮禁用 |
| `loading=false && items.length=0` | 空状态提示:「暂无{{title}}作品」 |
| `loading=false && items.length>0` | 正常显示 8 张卡片 |
| `refreshing=true` | 刷新按钮旋转动画,卡片 fade-out |
| `refreshing=false` | 刷新完成,卡片 fade-in |
---
## 五、骨架屏样式
```css
.skeleton-card {
border-radius: 16rpx;
background: #3a3a4a;
overflow: hidden;
position: relative;
}
.skeleton-shimmer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.1) 50%,
transparent 100%
);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
```
---
## 六、完整组件模板
```vue
<template>
<view class="hot-category-block">
<!-- 标题 -->
<text class="block-title">{{ title }}</text>
<!-- 网格区域 -->
<view v-if="loading" class="block-grid">
<view v-for="i in 8" :key="i" class="skeleton-card">
<view class="skeleton-shimmer"></view>
</view>
</view>
<view v-else-if="items.length === 0" class="empty-state">
<text class="empty-text">暂无{{ title }}作品</text>
</view>
<view v-else class="block-grid">
<view
v-for="(item, index) in items"
:key="item.asset_id || index"
class="hot-card"
@click="handleCardClick(item)"
>
<image class="hot-card-image" :src="item.cover_url" mode="aspectFill" />
<view class="hot-card-overlay">
<view class="hot-card-user">
<image class="user-avatar" :src="item.owner_avatar" mode="aspectFill" />
<text class="user-name">{{ item.owner_nickname }}</text>
</view>
<view class="hot-card-likes">
<image class="like-icon" src="/static/icon/heart-icon.png" mode="aspectFit" />
<text class="like-count">{{ formatCount(item.likes) }}</text>
</view>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="block-actions">
<view class="refresh-btn" :class="{ spinning: refreshing }" @click="handleRefresh">
<text class="action-icon">↻</text>
</view>
<view class="more-btn" @click="handleViewMore">
<text class="more-text">查看更多</text>
<text class="more-arrow"></text>
</view>
</view>
</view>
</template>
```
---
## 七、API 调用
```javascript
// 单个分类刷新
export function getHotInspirationFlowApi(type) {
return request({
url: '/api/v1/inspiration-flow/hot',
method: 'GET',
data: { type }
})
}
// 查看更多分页
export function getHotInspirationFlowMoreApi(type, cursor = '', limit = 20) {
return request({
url: '/api/v1/inspiration-flow/hot/more',
method: 'GET',
data: { type, cursor, limit }
})
}
```
---
## 八、hot-category-more.vue 页面
**路由参数**:
- `type` — 分类类型
- `title` — 标题URL 编码)
**页面结构**:
```
┌──────────────────────────────────────┐
│ ← 返回 热门星卡 │
├──────────────────────────────────────┤
│ [全部] [光栅卡] [镭射卡] [撕拉卡] [拍立得] │ ← 仅星卡显示
├──────────────────────────────────────┤
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ │
│ └────┘ └────┘ └────┘ └────┘ │
│ ... │
└──────────────────────────────────────┘
```
**星卡子标签**(仅 `hot_star_card` 显示):
| 子标签 | value | 说明 |
|-------|-------|------|
| 全部 | `all` | 全部星卡 |
| 光栅卡 | `raster` | 光栅卡类型 |
| 镭射卡 | `holographic` | 镭射卡类型 |
| 撕拉卡 | `tear_off` | 撕拉卡类型 |
| 拍立得 | `polaroid` | 拍立得类型 |