12 KiB
12 KiB
热门推荐模块前端设计
一、页面结构
完整页面布局(4个分类区块纵向堆叠)
┌──────────────────────────────────────┐
│ BannerCarousel │
├──────────────────────────────────────┤
│ 热门推荐 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ 热门星卡 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ 热门吧唧 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ 热门海报 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行1 │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ ← 行2 │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│
├──────────────────────────────────────┤
│ CreationGrid(原有组件,不变) │
│ [热门作品] [最新作品] [星卡] [吧唧]... │
└──────────────────────────────────────┘
单个 HotCategoryBlock 内部结构
┌──────────────────────────────────────┐
│ 热门推荐 │ ← block-title
├──────────────────────────────────────┤
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ │
│ └────┘ └────┘ └────┘ └────┘ │ ← block-grid (4×2)
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ │ │ │ │ │ │
│ └────┘ └────┘ └────┘ └────┘ │
│ 🔄 刷新 查看更多 ›│ ← block-actions
└──────────────────────────────────────┘
二、组件结构
HotCategoryBlock.vue
Props:
{
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 卡片样式
.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 标题栏样式
.block-title {
font-size: 30rpx;
font-weight: 600;
color: #fff;
padding: 0 24rpx 16rpx;
}
3.4 操作按钮样式
.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 |
五、骨架屏样式
.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%); }
}
六、完整组件模板
<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 调用
// 单个分类刷新
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 |
拍立得类型 |