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

12 KiB
Raw Blame History

热门推荐模块前端设计

一、页面结构

完整页面布局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 拍立得类型