# ShortPro · Hadiah 页 Vue 3 工程

> 完整生产级实现:Vue 3 + TypeScript + Vite + Pinia + axios + qrcode
> 100% 还原 设计稿 移动端 H5 页面,可直接接入后端 API。

---

## 🚀 快速启动

```bash
# 1. 安装依赖 (推荐 pnpm,npm/yarn 也可)
pnpm install

# 2. 复制环境变量
cp .env.example .env

# 3. 开发模式启动 (默认端口 5173)
pnpm dev

# 4. 构建生产包
pnpm build

# 5. 本地预览构建结果
pnpm preview
```

无后端时,在 `.env` 中设置 `VITE_USE_MOCK=true`,会用 `src/mock/hadiah.mock.ts` 中的数据。

---

## 📂 目录结构

```
src/
├── api/                   # API 封装 (axios + 拦截器 + 错误兜底)
│   ├── http.ts            # 全局 axios 实例
│   └── hadiah.ts          # Hadiah 页所有 endpoint
├── components/            # 所有 UI 组件 (16 个)
│   ├── PhoneFrame.vue         # 手机外框容器
│   ├── StatusBar.vue          # 状态栏
│   ├── TopTabs.vue            # 顶部 Bonus/Hadiah 切换
│   ├── RankingHero.vue        # 顶部 "Daftar Bonus" hero
│   ├── KarnivalBanner.vue     # AI karnival banner
│   ├── UserStatsCard.vue      # 等级/月奖励/邀请链接卡
│   ├── VoucherStrip.vue       # 横滑 voucher 列表
│   ├── QuickActions.vue       # 2x2 入口块
│   ├── DukungDramaSection.vue # Dukung Drama 大区(组合体)
│   ├── RecentFeed.vue         # 滚动 feed
│   ├── AIInvestCard.vue       # AI 投资产品卡
│   ├── DramaSupportCard.vue   # Drama 支持卡
│   ├── AccordionItem.vue      # 折叠组件 (Aturan/FAQ)
│   ├── BottomNav.vue          # 底部 5 项 nav
│   ├── SharePoster.vue        # 奢华证书风海报
│   ├── SharePreviewModal.vue  # 全屏分享预览
│   ├── QrCode.vue             # 真实 QR 码
│   ├── PageSkeleton.vue       # 骨架屏
│   └── ErrorRetry.vue         # 错误兜底
├── composables/
│   └── useHadiahData.ts       # 数据获取 + abort 控制
├── views/
│   └── HadiahView.vue         # 主视图,组件聚合 + 操作分发
├── types/index.ts             # 全部类型 (API 契约)
├── styles/                    # 样式
│   ├── variables.scss             # 设计令牌
│   ├── global.scss                # 全局 reset + 动画
│   └── share-poster.scss          # 海报独立样式
├── utils/
│   ├── format.ts                  # 格式化(千分位等)
│   └── toast.ts                   # 轻量 toast
├── mock/hadiah.mock.ts            # mock 数据
├── router/index.ts                # vue-router
├── App.vue
└── main.ts
```

---

## 🔌 后端接入指南 (重点)

### 推荐方案:聚合接口

后端最好提供 **1 个聚合接口** `GET /api/hadiah/page` 返回 `HadiahPageData` 完整结构。这能减少 HTTP 往返,首屏快 ~600ms。

```ts
// 类型定义见 src/types/index.ts
interface HadiahPageData {
  user: UserStats
  vouchers: Voucher[]
  aiProducts: AIProduct[]
  dramas: DramaSupport[]
  feed: SupportFeedItem[]
  claim: ClaimPanel
  poster: SharePosterData
  karnivalPeriod: string
  rankingPercent: number
}
```

### 备选方案:并行接口

如果后端暂时没法做聚合,前端已写好 `getHadiahPageDataParallel()` 在 `src/api/hadiah.ts`,会用 `Promise.all` 并发拉取 8 个子接口(并发数受控,移动端弱网也不会卡)。

### 操作类接口

| 方法               | 路径                      | 说明           |
| ------------------ | ------------------------- | -------------- |
| `claimReward`      | `POST /api/claim`         | 领取待领奖励   |
| `redeemCoupon`     | `POST /api/coupon/redeem` | 兑换优惠券     |
| `confirmInvest`    | `POST /api/invest/confirm`| 确认 AI 投资   |
| `supportDrama`     | `POST /api/dramas/:id/support` | 手动支持 drama |
| `trackShare`       | `POST /api/share/track`   | 分享上报       |

### 接入 token

在 `src/api/http.ts` 的请求拦截器中,搜 `TODO[real-auth]` 把注释打开:

```ts
const token = localStorage.getItem('access_token')
if (token) config.headers.set('Authorization', `Bearer ${token}`)
```

### 接入 nginx 反代 (生产)

`.env.production` 配置:

```
VITE_API_BASE=/api
VITE_USE_MOCK=false
VITE_REFERRAL_BASE=https://shortpro.com/r
```

nginx 加上反向代理把 `/api/*` 转到后端。

---

## ⚡ 性能优化清单 (已落地)

- ✅ 路由懒加载 (`HadiahView` 单独 chunk)
- ✅ vendor / qrcode 拆 chunk,主包小
- ✅ 接口聚合优先,减少首屏 RTT
- ✅ `AbortController` 在组件卸载时取消未完成请求
- ✅ Voucher 横滑用 `requestAnimationFrame` 节流,而非高频 `scroll`
- ✅ 操作类 API **乐观更新**,UI 不等接口返回
- ✅ 骨架屏首屏不闪烁
- ✅ 海报截图态 `.frozen` 关闭所有动画/transition
- ✅ `prefers-reduced-motion` 全局适配
- ✅ Bonus/Hadiah 切 tab 不会重发请求

---

## 🎨 设计 Token

所有颜色 / 渐变 / 阴影集中在 `src/styles/variables.scss`,改一个变量全站生效。

字体由 Google Fonts 加载:
- `Bricolage Grotesque` — display / 大数字
- `Plus Jakarta Sans` — body
- `JetBrains Mono` — 代码 / ID
- `Playfair Display` — 海报 serif

---

## 📦 切图说明

本项目采用 **零静态图片** 设计:

- 所有 icon (奖杯、shield、AI 头像、底部 nav、QR 边框、海报边角等) 都是 **inline SVG**
- 所有海报、海报、drama 海报背景都是 **CSS 渐变 + SVG**
- 真实二维码由 `qrcode.js` 运行时生成,内容是 `referralLink`

→ **不需要切图资源**,部署后包体更小,任意分辨率不糊。

如果将来想用真实剧海报照片,把 `DramaSupportCard.vue` 中的 `.drama-poster` 替换为 `<img :src="drama.coverUrl" />` 即可,后端 `DramaSupport` 类型加 `coverUrl: string` 字段。

---

## 🔍 关键代码标记

代码里有 3 类待对接标记,搜索就能找到:

- `TODO[real-api]` — 真实 API 路径需要后端确认
- `TODO[real-auth]` — token 获取/刷新逻辑
- `TODO[router]` — 路由跳转 (其它页面)

---

## 🛠 浏览器兼容

iOS Safari 14+ / Chrome 90+ / Android WebView 90+。需要兼容更老,加 `@vitejs/plugin-legacy`。

---

## 📜 License

ShortPro Internal — 仅授权 ShortPro 团队使用。
