diff --git a/.gitignore b/.gitignore
index 628d67c..f211e46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,6 @@ hookify.*.local.md
# statisticService runtime logs (created by logger when running tests)
backend/services/statisticService/**/logs/
backend/services/statisticService/**/logs/*.log
+
+# superpowers
+.superpowers
\ No newline at end of file
diff --git a/docs/superpowers/plans/2026-06-10-square-stargalaxy-component.md b/docs/superpowers/plans/2026-06-10-square-stargalaxy-component.md
new file mode 100644
index 0000000..0781c53
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-10-square-stargalaxy-component.md
@@ -0,0 +1,1021 @@
+# StarGalaxy 组件实现计划
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 在 square 页面「星河」tab 渲染一个 3D 倾斜椭圆轨道 + 9 item 顺时针旋转 + TOP 1-3 颁奖台的排行榜组件
+
+**Architecture:** 三层拆分 — `index.vue`(容器/数据/装饰)+ `PodiumCard.vue`(TOP 1-3 大卡)+ `ScatteredRanks.vue`(TOP 4-12 9 个散落 item)+ `config.js`(9 slot 位置公式)。单组 `@keyframes orbit` 配合 9 个不同 `animation-delay` 实现旋转。复用现有 `getHotRankingApi` 和 `getAssetCoverRealUrl`。
+
+**Tech Stack:** Vue 3 Composition API + uni-app + CSS keyframes(uni-app 跨端支持 `transform`)。**无单元测试框架**(项目 package.json 只有 vuex),通过 H5 端 `npm run dev:h5` 可视化验证。
+
+**Spec:** `docs/superpowers/specs/2026-06-10-square-stargalaxy-component-design.md`
+
+---
+
+## 文件结构
+
+| 路径 | 类型 | 职责 |
+|----|----|----|
+| `frontend/pages/square/components/StarGalaxy/index.vue` | 新建 | 容器组件:数据加载、装饰层、3D 椭圆轨道 SVG、TOP 1-3 颁奖台 + ScatteredRanks 编排 |
+| `frontend/pages/square/components/StarGalaxy/PodiumCard.vue` | 新建 | TOP 1-3 大卡(钻石渐变外框 + cover + 下方 TOP N 标签 + 可选皇冠) |
+| `frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue` | 新建 | TOP 4-12 9 个散落 item(cover + 上方 TOP N 标签) |
+| `frontend/pages/square/components/StarGalaxy/config.js` | 新建 | 9 slot 位置/translate/scale 公式 + KEYFRAMES 常量 |
+| `frontend/pages/square/square.vue` | 修改 | 在「星河」tab 分支渲染 ``,并设置 onShow 重置 activeContentTab 为 "xinghe" |
+
+每个新文件职责单一,文件之间通过 props/emit 通信。
+
+---
+
+## Task 1: 创建 config.js
+
+**Files:**
+- Create: `frontend/pages/square/components/StarGalaxy/config.js`
+
+- [ ] **Step 1.1: 创建目录**
+
+```bash
+mkdir -p frontend/pages/square/components/StarGalaxy
+```
+
+- [ ] **Step 1.2: 写入 config.js**
+
+```js
+// StarGalaxy 组件配置常量
+// 9 个散落 item 沿 65° 倾斜椭圆轨道排列
+// slot 0 = 最前(底部,TOP 4 起始位置),slot 4-5 = 最后(顶部)
+
+export const RING = {
+ cx: 187, // 椭圆圆心 x
+ cy: 510, // 椭圆圆心 y
+ rx: 130, // 水平半径
+ ry: 55, // 垂直半径(cos(65°) ≈ 0.423,模拟向后倾 65°)
+ startAngle: 180, // 起始角:slot 0 在正下方
+ step: 40, // 间隔角(顺时针 = 负方向 → step = -40 在 CSS 中)
+}
+
+// item 固定尺寸(label 在 cover 上方)
+export const ITEM = {
+ width: 46, // cover + label 宽度
+ labelHeight: 14, // 顶部 label 高度
+ coverHeight: 56, // 底部 cover 高度
+ gap: 2, // label 与 cover 之间的间距
+}
+// total: 14 + 2 + 56 = 72
+
+// TOP 6 / TOP 11 推到边缘,避免与 TOP 5/7、TOP 10/12 重叠
+// TOP 6 推到屏幕右侧 (321, 488),TOP 11 推到屏幕左侧 (8, 488)
+export const OVERRIDES = {
+ 6: { x: 321, y: 488 },
+ 11: { x: 8, y: 488 },
+}
+
+// 计算 y 在椭圆前/后位置的比例(0 = 最后, 1 = 最前)
+function yFactor(y) {
+ // y=458 (back) → 0; y=565 (front) → 1
+ return Math.max(0, Math.min(1, (y - 458) / 107))
+}
+
+// 生成 9 个 item 的位置配置
+export function generateRingPositions() {
+ return Array.from({ length: 9 }, (_, i) => {
+ const rank = i + 4
+ const alpha = (RING.startAngle + i * RING.step) * Math.PI / 180
+ const baseX = RING.cx + RING.rx * Math.sin(alpha) - ITEM.width / 2
+ const baseY = RING.cy - RING.ry * Math.cos(alpha) - (ITEM.labelHeight + ITEM.gap + ITEM.coverHeight) / 2
+ const ovr = OVERRIDES[rank]
+ const y = ovr?.y ?? baseY
+ const x = ovr?.x ?? baseX
+ const f = yFactor(y)
+ return {
+ rank,
+ x,
+ y,
+ scale: 0.75 + 0.40 * f, // 0.75 → 1.15
+ zIndex: Math.round(f * 10), // 0 → 10
+ }
+ })
+}
+
+// 单组 @keyframes(CSS 模板字符串)
+// translate 值是相对 slot 0 中心 (164, 530) 的偏移
+export const ORBIT_KEYFRAMES = `
+@keyframes orbit {
+ 0% { transform: translate(0,0) scale(1.15); }
+ 11.11% { transform: translate(84px,-13px) scale(1.05); }
+ 22.22% { transform: translate(157px,-43px) scale(0.95); }
+ 33.33% { transform: translate(113px,-83px) scale(0.85); }
+ 44.44% { transform: translate(45px,-107px) scale(0.75); }
+ 55.55% { transform: translate(-45px,-107px) scale(0.75); }
+ 66.66% { transform: translate(-113px,-83px) scale(0.85); }
+ 77.77% { transform: translate(-156px,-43px) scale(0.95); }
+ 88.88% { transform: translate(-84px,-13px) scale(1.05); }
+ 100% { transform: translate(0,0) scale(1.15); }
+}
+
+@keyframes crownPulse {
+ 0%, 100% { transform: translateX(-50%) scale(1); }
+ 50% { transform: translateX(-50%) scale(1.15); }
+}
+`
+
+// 各 slot 对应 ring-item 类的 r0..r8 的 delay(负值让 item 起始位置 = slot)
+export const RING_DELAYS = [0, -4, -8, -12, -16, -20, -24, -28, -32]
+```
+
+- [ ] **Step 1.3: 验证文件创建**
+
+```bash
+ls -la frontend/pages/square/components/StarGalaxy/config.js
+```
+
+Expected: 文件存在,~80 行
+
+- [ ] **Step 1.4: 提交**
+
+```bash
+cd frontend
+git add pages/square/components/StarGalaxy/config.js
+git commit -m "feat(stargalaxy): add ring position config and orbit keyframes"
+```
+
+---
+
+## Task 2: 创建 PodiumCard.vue
+
+**Files:**
+- Create: `frontend/pages/square/components/StarGalaxy/PodiumCard.vue`
+
+- [ ] **Step 2.1: 写入 PodiumCard.vue**
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 👑
+
+
+ TOP {{ displayRank }}
+
+
+
+
+
+
+```
+
+- [ ] **Step 2.2: 验证文件创建**
+
+```bash
+ls -la frontend/pages/square/components/StarGalaxy/PodiumCard.vue
+wc -l frontend/pages/square/components/StarGalaxy/PodiumCard.vue
+```
+
+Expected: ~150 行
+
+- [ ] **Step 2.3: 提交**
+
+```bash
+cd frontend
+git add pages/square/components/StarGalaxy/PodiumCard.vue
+git commit -m "feat(stargalaxy): add PodiumCard for TOP 1-3 with gold/silver/bronze labels"
+```
+
+---
+
+## Task 3: 创建 ScatteredRanks.vue
+
+**Files:**
+- Create: `frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue`
+
+- [ ] **Step 3.1: 写入 ScatteredRanks.vue**
+
+```vue
+
+
+
+
+
+
+
+ {{ formatLabel(p.rank) }}
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 3.2: 验证**
+
+```bash
+ls -la frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue
+```
+
+- [ ] **Step 3.3: 提交**
+
+```bash
+cd frontend
+git add pages/square/components/StarGalaxy/ScatteredRanks.vue
+git commit -m "feat(stargalaxy): add ScatteredRanks with 9 ring items + 36s orbit animation"
+```
+
+---
+
+## Task 4: 创建 StarGalaxy/index.vue 容器
+
+**Files:**
+- Create: `frontend/pages/square/components/StarGalaxy/index.vue`
+
+- [ ] **Step 4.1: 写入 index.vue**
+
+```vue
+
+
+
+
+
+
+
+
+
+ ★ 星河 ★
+
+
+
+
+
+
+
+
+
+ 加载失败,点击重试
+ 重试
+
+
+
+
+ 暂无星河数据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 4.2: 验证文件结构**
+
+```bash
+ls -la frontend/pages/square/components/StarGalaxy/
+```
+
+Expected: 4 files (config.js, PodiumCard.vue, ScatteredRanks.vue, index.vue)
+
+- [ ] **Step 4.3: 提交**
+
+```bash
+cd frontend
+git add pages/square/components/StarGalaxy/index.vue
+git commit -m "feat(stargalaxy): add container with data loading, decoration, podium + scattered orchestration"
+```
+
+---
+
+## Task 5: 在 square.vue 中集成 StarGalaxy
+
+**Files:**
+- Modify: `frontend/pages/square/square.vue:42-49`(添加星河分支)
+
+- [ ] **Step 5.1: 导入 StarGalaxy 组件**
+
+在 `square.vue` 顶部 import 区域(约第 71 行附近)添加:
+
+```js
+import StarGalaxy from "./components/StarGalaxy/index.vue";
+```
+
+- [ ] **Step 5.2: 添加星河分支**
+
+在 `` 之前,添加新的星河分支(替换原 `` 注释块):
+
+```vue
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 5.3: 验证 square.vue 语法**
+
+```bash
+cd frontend
+npx --no-install eslint pages/square/square.vue 2>/dev/null || echo "无 eslint,跳过"
+```
+
+- [ ] **Step 5.4: 提交**
+
+```bash
+cd frontend
+git add pages/square/square.vue
+git commit -m "feat(square): wire StarGalaxy component into 星河 tab"
+```
+
+---
+
+## Task 6: H5 端可视化验证
+
+**Files:** 无(仅验证)
+
+- [ ] **Step 6.1: 启动 H5 调试服务**
+
+```bash
+cd frontend
+npm run dev:h5
+```
+
+Expected: 浏览器打开 `http://localhost:8080`(或类似端口),square 页面正常加载。
+
+- [ ] **Step 6.2: 进入「星河」tab 视觉验证**
+
+手动检查清单:
+- [ ] 顶部「★ 星河 ★」标题可见
+- [ ] 3 张 TOP 1-3 颁奖台卡片显示,TOP 1 在中央最大,TOP 2 在左上,TOP 3 在右上
+- [ ] TOP 1 顶部有皇冠,cover 下方有金渐变「TOP 1」标签
+- [ ] TOP 2 下方有银渐变「TOP 2」标签
+- [ ] TOP 3 下方有铜渐变「TOP 3」标签
+- [ ] 下方椭圆轨道上有 9 个 item(TOP 4-12)排成圆环
+- [ ] TOP 4 在最前(最大),TOP 8/9 在最后(最小)
+- [ ] 9 个 item 顺时针缓慢旋转(36s 一圈)
+- [ ] 点击任一 item 跳到 asset-detail 页
+
+- [ ] **Step 6.3: 检查动画**
+
+- [ ] 打开 DevTools 观察 .ring-item 的 transform 在变化
+- [ ] 旋转应该是匀速、连续、无卡顿
+- [ ] 前后 item 的大小差异明显(TOP 4 比 TOP 8 大约 1.5 倍)
+
+- [ ] **Step 6.4: 检查错误状态**
+
+在 DevTools 中断网(Network → Offline),刷新页面,应看到「加载失败,点击重试」+ 重试按钮。恢复网络点击重试,数据应正常加载。
+
+- [ ] **Step 6.5: 提交验证记录(如有问题修复)**
+
+```bash
+cd frontend
+git status
+# 如有修改:
+git add -A
+git commit -m "fix(stargalaxy): visual verification fixes" || echo "无修改需提交"
+```
+
+---
+
+## Task 7: 跨端兼容性烟测
+
+**Files:** 无(仅验证)
+
+- [ ] **Step 7.1: 验证关键 CSS 兼容性**
+
+确认以下 CSS 属性能在 uni-app 跨端运行(小程序、H5、APP):
+- `transform: scale()` + `transform-origin: center` — ✅ uni-app 全支持
+- `filter: blur()` — ✅ H5 + APP 支持,小程序需 `enable-backdrop-filter`
+- `radial-gradient` — ✅ H5 + APP 支持,小程序有限制
+- `@keyframes` + `animation` — ✅ uni-app 全支持
+
+如果小程序有问题,对应的 @media 块需要适配或使用条件编译。
+
+- [ ] **Step 7.2: 文档记录**
+
+在 `frontend/pages/square/components/StarGalaxy/README.md` 写组件使用说明:
+
+```markdown
+# StarGalaxy 组件
+
+square 页「星河」tab 的排行榜组件。
+
+## 视觉特征
+
+- 65° 倾斜椭圆轨道
+- TOP 1-3 颁奖台(cover + 下方标签 + 钻石外框)
+- TOP 4-12 9 个散落 item(cover + 上方标签)
+- 9 item 顺时针 36s 旋转一圈
+- 近大远小(scale 0.75→1.15)
+
+## 数据来源
+
+`getHotRankingApi("displaying", null, 1, 12)` — 与「星榜」共用 API,取前 12 条。
+
+## 文件
+
+- `index.vue` — 容器
+- `PodiumCard.vue` — TOP 1-3 大卡
+- `ScatteredRanks.vue` — TOP 4-12 9 散落 item
+- `config.js` — 位置公式和 keyframes
+
+## 可调参数
+
+- 椭圆倾斜角(config.js: RING.ry)— 改 ry 调整倾斜度
+- 旋转周期(ScatteredRanks.vue: animation duration)— 改 36s 调整速度
+- 近大远小范围(keyframes scale 0.75→1.15)— 改 0.75/1.15 调整
+
+## 可访问性
+
+- `prefers-reduced-motion: reduce` 时关闭旋转动画
+- label 文字使用高对比度的 #FFFABD + 红色 text-shadow
+```
+
+```bash
+cd frontend
+git add pages/square/components/StarGalaxy/README.md
+git commit -m "docs(stargalaxy): add component README"
+```
+
+---
+
+## 验收清单(最终)
+
+- [ ] Task 1-5 全部完成并提交
+- [ ] Task 6.2 视觉验证清单全部 ✅
+- [ ] Task 6.3 动画流畅
+- [ ] Task 6.4 错误状态可重试
+- [ ] Task 7 README 文档完成
+- [ ] 跨端验证(至少 H5 通过;小程序/APP 在后续 PR 验证)
+- [ ] 代码无 ESLint 错误(如有 ESLint 配置)
+- [ ] 所有提交信息遵循 `feat/fix/docs/...` Conventional Commits 规范
+
+---
+
+## 风险与回退
+
+| 风险 | 回退方案 |
+|----|----|
+| 旋转动画在低端机卡顿 | 在 keyframes 中加 `transform: translateZ(0)` 强制 GPU 加速 |
+| 小程序不支持 `radial-gradient` | 改用 `background-image: linear-gradient` 模拟或接受色彩降级 |
+| 椭圆轨道 SVG 在小程序不显示 | 用纯 CSS border 模拟椭圆装饰 |
+| TOP 6/11 推到边缘与不同屏幕宽度冲突 | 在 config.js 用百分比 + 媒体查询自适应 |
+
+---
+
+## 后续 PR(v2 增强,不在本计划范围)
+
+- 缩略图懒加载优化(``)
+- 9 item 悬停暂停动画
+- 减少运动偏好支持进一步增强
+- 双指捏合缩放查看详情
diff --git a/docs/superpowers/specs/2026-06-10-square-stargalaxy-component-design.md b/docs/superpowers/specs/2026-06-10-square-stargalaxy-component-design.md
new file mode 100644
index 0000000..0549ea8
--- /dev/null
+++ b/docs/superpowers/specs/2026-06-10-square-stargalaxy-component-design.md
@@ -0,0 +1,276 @@
+# 星河(StarGalaxy)组件设计
+
+> **日期**: 2026-06-10
+> **状态**: 待评审
+> **目的**: 为 square 页面的「星河」tab 设计一个 3D 倾斜椭圆轨道 + 顺时针旋转的排行榜组件
+> **参考文档**: `docs/figma-analysis-xiaohongshu-stars.md`(Figma 节点 94:78「星河新版本」)
+
+---
+
+## 1. 背景
+
+square 页面现有 3 个内容 tab(星河 / 星榜 / 广场),其中「星榜」是 12 行的横向列表样式(`HotCategoryBlock.vue`)。本次新增「星河」tab,沿用 Figma 中的双层结构:
+
+- **上层**:TOP 1-3 颁奖台(cover + 下方标签 + 钻石渐变外框)
+- **下层**:TOP 4-12 散落在 65° 倾斜椭圆轨道上,绕中心 3D 旋转
+
+设计追求饭圈甜美梦幻风格(粉红渐变 + 玻璃拟态 + 暖黄光晕 + 渐变描边),不显示用户名/点赞数(保持 cover 纯净)。
+
+---
+
+## 2. 文件结构
+
+```
+frontend/pages/square/components/StarGalaxy/
+├── index.vue ← 容器:数据加载、装饰层、TOP 1-3 + ScatteredRanks 编排
+├── PodiumCard.vue ← TOP 1-3 颁奖台卡片(cover + 下方 TOP N 标签 + 钻石外框)
+├── ScatteredRanks.vue ← TOP 4-12 9 个散落 item(cover + 上方 TOP N 标签)
+└── config.js ← 9 slot 位置/translate/scale 公式
+```
+
+`square.vue` 改动:在星河 tab 分支中渲染 ``,复用现有 `handleCardClick`(单击跳详情 + 双击点赞)。
+
+---
+
+## 3. 组件职责
+
+| 组件 | 职责 | 内部状态 | 输入 Props | 事件 |
+|----|----|----|----|----|
+| `StarGalaxy/index.vue` | 数据加载(`getHotRankingApi`)、装饰层渲染、3D 椭圆轨道 SVG、布局编排 | `items`、`loading` | — | `cardClick(item)` |
+| `PodiumCard.vue` | 单张 TOP 1-3 大卡:钻石渐变外框 + cover + 下方 TOP N 标签 + 可选皇冠 | — | `rank: 4\|5\|6` 隐式;`item: HotRankingItem`、`size: {w,h}` | `click(item)` |
+| `ScatteredRanks.vue` | TOP 4-12 共 9 个 item 散落在椭圆轨道 | — | `items: HotRankingItem[]` (length=9) | `cardClick(item)` |
+| `config.js` | 9 个 slot 的绝对位置/translate/scale/zIndex 公式 | — | — | — |
+
+---
+
+## 4. 数据流
+
+```
+StarGalaxy/index.vue (onMounted)
+ │
+ ├── getHotRankingApi("displaying", null, 1, 12)
+ │ │
+ │ └── res.data.items[0..11]
+ │
+ ├── Promise.all → getAssetCoverRealUrl(item.cover_url) // 复用 HotCategoryBlock 的模式
+ │
+ └── items 拆成两段:
+ ├── items.slice(0,3) → PodiumCard × 3
+ └── items.slice(3,12) → ScatteredRanks(9 个 item)
+```
+
+事件冒泡:所有 card 点击 → `emit('cardClick', item)` → `square.vue` 的 `handleCardClick`(已有,含单击跳详情 + 双击点赞)。
+
+---
+
+## 5. 视觉设计
+
+### 5.1 容器尺寸
+- 总宽 750rpx × 高约 1440rpx
+- 适配 iPhone 标准 (375×812)
+- 整体分上下两层:上层颁奖台 y=48~700,下层椭圆轨道 y=800~1440
+
+### 5.2 装饰层(背景 overlay)
+- 粉红渐变:`linear-gradient(179deg, #FFE5E5 0%, #F3A0A1 0%, #FF9C9C 86%, #FF2024 100%)`(覆盖 square 现有渐变之上)
+- 樱花粉光晕:圆形 `#F3D3E3` + `filter: blur(60px)`,居中(top 580)
+- 暖黄光晕:圆形 `#FFFABD` + `filter: blur(50px)`,top 50
+- ⚠️ 不动 square.vue 现有渐变背景,星河只画自己的装饰层
+
+### 5.3 PodiumCard 三个实例
+
+| 排名 | 位置 (left, top) | 卡片尺寸 | 标签尺寸 | 装饰图 |
+|----|----|----|----|----|
+| TOP 1 | (50%, 400) translateX(-50%) | 240×260 (cover 240×260) | 192×44 (金渐变) | 钻石外框 + 皇冠 |
+| TOP 2 | (60, 120) | 200×200 (cover 200×200) | 156×36 (银渐变) | 钻石外框 |
+| TOP 3 | (470, 150) | 192×192 (cover 192×192) | 156×36 (铜渐变) | 钻石外框 |
+
+**PodiumCard 内部层级**(从下到上):
+```
+┌─ 钻石渐变外框(filter: blur(8-12px),不规则圆角 8px 22px 8px 19px)
+│ ├─ 藏品主图(不规则圆角 6px 20px 6px 17px)
+│ ├─ 青绿色高光 overlay(180deg #53F4D3 → 透明)
+│ └─ 钻石渐变边框层(4px 渐变描边)
+└─ TOP N 标签(绝对定位 bottom: -4px,居中)
+```
+
+**皇冠**(仅 TOP 1):绝对定位 `top: -44px; left: 50%; transform: translateX(-50%)`,font-size: 44rpx,2s 循环 pulse 动画。
+
+### 5.4 ScatteredRanks 9 个 slot 位置
+
+9 个 item 在 65° 倾斜椭圆上,等角度 40° 间隔(顺时针),起始角 180°(TOP 4 在正下方 = 最前)。
+
+椭圆参数:
+- 圆心 (cx, cy) = (187, 510)
+- 水平半径 rx = 130
+- 垂直半径 ry = 55(cos(65°) ≈ 0.423,模拟向后倾斜 65°)
+- 起始角 startAngle = 180°(slot 0 在正下方)
+- 间隔角 step = -40°(顺时针 = 负方向)
+
+| Slot | Rank | 中心 (x, y) | 渲染尺寸 (w×h) | scale | z-index |
+|----|----|----|----|----|----|
+| 0 | TOP 4 | (187, 565) | 46×70 | 1.15 | 10 |
+| 1 | TOP 5 | (271, 552) | 46×70 | 1.05 | 9 |
+| 2 | TOP 6 | (321, 488) | 46×70 | 0.95 | 6 |
+| 3 | TOP 7 | (300, 482) | 46×70 | 0.85 | 3 |
+| 4 | TOP 8 | (232, 458) | 46×70 | 0.75 | 0 |
+| 5 | TOP 9 | (142, 458) | 46×70 | 0.75 | 0 |
+| 6 | TOP 10 | (74, 482) | 46×70 | 0.85 | 3 |
+| 7 | TOP 11 | (8, 488) | 46×70 | 0.95 | 6 |
+| 8 | TOP 12 | (80, 518) | 46×70 | 1.05 | 9 |
+
+**OVERRIDES**:TOP 6 / TOP 11 推到边缘 (8, 488) 和 (321, 488),避免与 TOP 5/7、TOP 12/10 重叠。
+
+### 5.5 单个 item 结构(ScatteredRanks)
+
+尺寸 46×70 = label 14 + gap 2 + cover 56:
+
+```vue
+
+ TOP 4
+
+
+```
+
+- **label**(顶部 14rpx):
+ - 背景:`radial-gradient(ellipse, #C8E6FF, #fff 50%, #4D9AF8)`(蓝白钻石)
+ - TOP 4 label 用金渐变:`#FFFFFF, #FFFABD 30%, #4D9AF8 100%`
+ - 圆角 7rpx
+ - 文字:14rpx Baloo Bhai(fallback monospace),色 `#FFFABD`,`text-shadow: -1px 1px 2px rgba(206,9,9,0.84)`
+- **cover**(下方 56rpx):
+ - 圆角 5rpx
+ - box-shadow:`3px 3px 6px rgba(198,13,13,0.45)`(按 scale 调整)
+
+### 5.6 颜色 / 渐变 / 阴影
+
+| Token | 值 | 用途 |
+|----|----|----|
+| 主背景 | `linear-gradient(179deg, #FFE5E5 0%, #F3A0A1 0%, #FF9C9C 86%, #FF2024 100%)` | 装饰层渐变 |
+| 樱花粉光晕 | `#F3D3E3` + `blur(60px)` | 椭圆中心光晕 |
+| 暖黄光晕 | `#FFFABD` + `blur(50px)` | 顶部装饰光晕 |
+| 钻石渐变(外框) | `radial-gradient(ellipse at -10% 5%, #86BEFF, #FF3939 32%, #88FFCE 59%, #4D9AF8)` | PodiumCard 描边 |
+| 蓝白钻石(标签) | `radial-gradient(ellipse, #C8E6FF, #fff 50%, #4D9AF8)` | 9 item label |
+| 金渐变(TOP 4 label) | `radial-gradient(ellipse, #FFFFFF, #FFFABD 30%, #4D9AF8 100%)` | TOP 4 label |
+| 金渐变(TOP 1 标签) | `radial-gradient(ellipse, #FFD700, #FFF6A8 30%, #DAA520 100%)` | TOP 1 标签 |
+| 银渐变(TOP 2 标签) | `radial-gradient(ellipse, #C0C0C0, #E8E8E8 50%, #7A7A7A)` | TOP 2 标签 |
+| 铜渐变(TOP 3 标签) | `radial-gradient(ellipse, #CD7F32, #E8A45C 50%, #A0522D)` | TOP 3 标签 |
+| TOP 数字字色 | `#FFFABD` + `text-shadow: -1px 1px 2px rgba(206,9,9,0.84)` | label 文字 |
+| 卡片阴影 | `3px 3px 6px rgba(198,13,13,0.45)`(随 scale 微调) | cover 阴影 |
+
+### 5.7 字体
+
+| 字体 | 用途 | Fallback |
+|----|----|----|
+| Baloo Bhai | label 数字(TOP 4、TOP 5 等) | monospace |
+| HYQiHei | 不使用(已移除用户名显示) | PingFang |
+| C800 | 不使用(已移除 TOP 排名文字) | system bold sans |
+
+---
+
+## 6. 动画设计
+
+### 6.1 9 item 顺时针旋转
+
+```
+@keyframes orbit {
+ 0% { transform: translate(0,0) scale(1.15); } /* slot 0: front */
+ 11.11% { transform: translate(84px,-13px) scale(1.05); } /* slot 1 */
+ 22.22% { transform: translate(157px,-43px) scale(0.95); } /* slot 2 */
+ 33.33% { transform: translate(113px,-83px) scale(0.85); } /* slot 3 */
+ 44.44% { transform: translate(45px,-107px) scale(0.75); } /* slot 4 */
+ 55.55% { transform: translate(-45px,-107px) scale(0.75); } /* slot 5 */
+ 66.66% { transform: translate(-113px,-83px) scale(0.85); } /* slot 6 */
+ 77.77% { transform: translate(-156px,-43px) scale(0.95); } /* slot 7 */
+ 88.88% { transform: translate(-84px,-13px) scale(1.05); } /* slot 8 */
+ 100% { transform: translate(0,0) scale(1.15); } /* loop */
+}
+```
+
+- 总周期 36s,4s 一步
+- 线性 timing,匀速旋转
+- infinite 循环
+
+### 6.2 9 个 item 的 delay 错开
+
+```
+.ring-item { animation: orbit 36s linear infinite; }
+.r0 { animation-delay: 0s; } /* TOP 4 起始 slot 0 */
+.r1 { animation-delay: -4s; } /* TOP 5 起始 slot 1 */
+.r2 { animation-delay: -8s; } /* TOP 6 起始 slot 2 */
+...
+.r8 { animation-delay: -32s; } /* TOP 12 起始 slot 8 */
+```
+
+负值 delay 让每个 item 起始位置 = 对应的 slot,9 个 item 静态分布在 9 个 slot 上。
+
+### 6.3 皇冠脉冲
+
+```css
+@keyframes crownPulse {
+ 0%, 100% { transform: translateX(-50%) scale(1); }
+ 50% { transform: translateX(-50%) scale(1.15); }
+}
+.crown { animation: crownPulse 2s ease-in-out infinite; }
+```
+
+### 6.4 可访问性
+
+```css
+@media (prefers-reduced-motion: reduce) {
+ .ring-item { animation: none; }
+ .crown { animation: none; }
+ /* item 用 inline transform 写到元素上,作为静态位置 */
+}
+```
+
+---
+
+## 7. 状态管理
+
+| 状态 | 显示 |
+|----|----|
+| Loading | 12 个骨架占位(3 大 + 9 小)+ shimmer 动画 |
+| Success | 完整布局 |
+| Empty (items.length < 3) | 「数据加载中」+ 重试按钮 |
+| Error | 居中提示「加载失败,点击重试」+ 重试按钮 |
+
+---
+
+## 8. 交互细节
+
+- **单击 PodiumCard / ScatteredRank 缩略图**:emit('cardClick', item) → square.vue 已有逻辑 → 跳 `pages/asset-detail/asset-detail`
+- **双击**:同样走 square.vue 的 `handleCardClick` → `doubleTapLike` + 波纹动画
+- **TOP 1 奖牌/皇冠**:CSS keyframes 微脉冲(scale 1.0 → 1.15 → 1.0,2s 循环)
+
+---
+
+## 9. 性能/可访问性
+
+- 图片懒加载用 uni-app ``(与现有 HotCategoryBlock 一致)
+- `prefers-reduced-motion: reduce` 关闭旋转和脉冲
+- 9 item 动画使用 GPU 加速(transform)
+- 椭圆轨道 SVG 装饰层 `pointer-events: none`
+
+---
+
+## 10. 风险与备选
+
+| 风险 | 缓解 |
+|----|----|
+| 旋转动画在低端机上卡顿 | 可降级到 60s 周期;或加 `will-change: transform` |
+| 椭圆轨道的虚线 SVG 在某些小程序不渲染 | 提供 `v-if` 兜底,仅 H5 显示 |
+| 数据 < 9 条时 ScatteredRanks 渲染异常 | 用 v-for 配合 length 校验,< 9 时只渲染实际条数 |
+
+---
+
+## 11. 验收标准
+
+1. ✅ square 页点击「星河」tab,显示 12 个 item 的 3D 排行榜
+2. ✅ TOP 1-3 在上方颁奖台位置,cover 下方有金/银/铜 TOP N 标签
+3. ✅ TOP 4-12 在下方椭圆轨道,9 个 item 等大 46×70,标签在 cover 上方
+4. ✅ 9 item 顺时针连续旋转(36s 一圈)
+5. ✅ 近大远小(scale 0.75→1.15)表达 3D 透视
+6. ✅ 点击任一卡片跳 asset-detail
+7. ✅ 双击点赞 + 波纹动画
+8. ✅ Loading / Empty / Error 状态都正常显示
+9. ✅ `prefers-reduced-motion: reduce` 时关闭旋转
+10. ✅ H5 + 小程序 + APP 三端视觉一致
diff --git a/frontend/pages/square/components/HotCategoryBlock.vue b/frontend/pages/square/components/HotCategoryBlock.vue
index 26a8c10..34926e7 100644
--- a/frontend/pages/square/components/HotCategoryBlock.vue
+++ b/frontend/pages/square/components/HotCategoryBlock.vue
@@ -42,8 +42,8 @@
:key="item.id || index"
class="grid-card"
:class="{
- 'grid-card-top': index < 3,
- [`grid-card-top-${index + 1}`]: index < 3,
+ [`grid-card-top-${index + 1}`]: index < 5,
+ 'grid-card-top-other': index >= 5,
}"
@click="handleCardClick(item)"
>
@@ -147,9 +147,33 @@ const tabs = [
{
key: "hot",
label: "热度榜",
- icon: "/static/square/rementubiao.png",
- iconWidth: 32,
- iconHeight: 40,
+ icon: "/static/square/galaxy/dianzanbang.png",
+ iconWidth: 64,
+ iconHeight: 72,
+ fetch: () => getHotRankingApi("displaying", null, 1, 11),
+ },
+ {
+ key: "new",
+ label: "活跃榜",
+ icon: "/static/square/galaxy/huoyuebang.png",
+ iconWidth: 64,
+ iconHeight: 72,
+ fetch: () => getHotRankingApi("displaying", null, 1, 11),
+ },
+ {
+ key: "trending",
+ label: "曝光榜",
+ icon: "/static/square/galaxy/baoguangbang.png",
+ iconWidth: 64,
+ iconHeight: 72,
+ fetch: () => getHotRankingApi("displaying", null, 1, 11),
+ },
+ {
+ key: "trending",
+ label: "同城榜",
+ icon: "/static/square/galaxy/tongchengbang.png",
+ iconWidth: 64,
+ iconHeight: 72,
fetch: () => getHotRankingApi("displaying", null, 1, 11),
},
];
@@ -279,7 +303,7 @@ onUnmounted(() => {
});
-