520 lines
18 KiB
Markdown
520 lines
18 KiB
Markdown
# 黄果短剧落地页(电脑版 5 变体)实施计划
|
||
|
||
> **For agentic workers:** 用 superpowers:executing-plans 或 subagent-driven-development 按 Task 执行。步骤用 `- [ ]` 勾选。
|
||
>
|
||
> **用户规则覆盖(最高优先级):**
|
||
> - 不强制 TDD:本计划不写"先写测试"步骤。
|
||
> - 收尾只做静态检查(`pnpm exec tsc --noEmit` / `pnpm build`),不跑单测/E2E。
|
||
> - 不自动 commit / push / 建 PR:除非用户明确说"提交"。
|
||
|
||
**Goal:** 把 repo 改造为黄果短剧落地页:1 个共用电脑模板 `PcLanding.vue` + 5 变体素材(背景大图 + 底部封面),进站 5 路随机分流 A~E,并更新 index.html 为简体黄果 SEO。
|
||
|
||
**Architecture:** Vue 3 `<script setup lang="ts">` + Vite + SCSS,`zoom` 缩放(基于 1920×1080),绝对定位 + 图片叠加,`<picture>` webp+png。版面只写一次,`variants.ts` 映射每变体的 `bg`/`covers`。共用元素(logo/主文案/二维码文字烤成图,导航程式画)全变体复用。二维码复用 `DesktopQrcode`。
|
||
|
||
**Tech Stack:** Vue 3 + Vite + SCSS + `qrcode.vue`;Figma MCP `get_screenshot`(导出节点 PNG)+ `curl` 下载;`pnpm webp`(sharp q80)生成 webp。
|
||
|
||
**规格来源:** `docs/superpowers/specs/2026-05-29-huangguo-landing-design.md`
|
||
|
||
---
|
||
|
||
## 关键设计参数(Figma `yD7lloED2Okdp1FJTYvhe3`,变体 1 `73:53`,5 变体共用版面)
|
||
|
||
画布 1920×1080,全部绝对坐标。
|
||
|
||
| 元素 | 位置 (x,y) | 尺寸 | 说明 |
|
||
|---|---|---|---|
|
||
| 背景大图 | 0,0 | 1920×1080 | 每变体不同,含底部深色渐变 |
|
||
| 顶部 pill 条 | 360,0 | 1200×70 | 半透明圆角,水平居中(CSS 画) |
|
||
| logo 锁定组 | 420,15 | 154×40 | 烤图(icon+黄果短剧+标语) |
|
||
| 联系方式 容器 | 1094,23 | 406×24 | 3 项 + 圆点分隔(程式画) |
|
||
| 主文案 | 204,308 | 630×381 | 烤图 |
|
||
| 二维码本体 | 204,750 | 180×180 | 白底圆角,内 150×150 动态二维码 |
|
||
| 二维码文字块 | 408,757 | 502×166 | 烤图 |
|
||
| 底部封面行 | 1184,778 | 606×152 | 每变体不同,烤成长条图 |
|
||
|
||
联系方式 3 项(相对 406×24 容器,容器位于顶部内容 (734,8)):
|
||
- 官方群组:item @x=0(icon 24×24 + 文字 @x=30)
|
||
- 圆点分隔 @x=124(竖排 4 点,2×2,间距 4px)
|
||
- 商务合作:item @x=156
|
||
- 圆点分隔 @x=280
|
||
- 渠道合作:item @x=312
|
||
|
||
导出节点 ID:
|
||
|
||
| 变体(版本) | 背景 frame | 轮动 cover row |
|
||
|---|---|---|
|
||
| 1 (A) | `73:54` | `81:766` |
|
||
| 2 (B) | `73:112` | `81:735` |
|
||
| 3 (C) | `73:169` | `81:704` |
|
||
| 4 (D) | `73:225` | `81:673` |
|
||
| 5 (E) | `73:281` | `73:356` |
|
||
|
||
共用元素(从变体 1 导出):logo `73:76`、主文案 `73:62`、二维码文字 `73:69`、官方群组 icon `73:83`、商务合作 icon `73:93`、渠道合作 icon `73:104`。
|
||
|
||
---
|
||
|
||
## File Structure
|
||
|
||
```
|
||
src/
|
||
├── pages/index.vue # 改:设备检测 + 5 路随机 A~E
|
||
├── components/landing/PcLanding.vue # 新:电脑共用模板
|
||
├── config/variants.ts # 新:变体素材映射 + 导航链接常量
|
||
├── components/desktop/qrcode.vue # 复用(必要时微调 .qr-inner 尺寸)
|
||
└── assets/image/hg/
|
||
├── common/ logo / theme / qr-text / icon-tg / icon-biz / icon-channel (.png+.webp)
|
||
└── pc/ bg1..bg5 / covers1..covers5 (.png+.webp)
|
||
index.html # 改:简体黄果 SEO
|
||
```
|
||
|
||
---
|
||
|
||
# Tasks
|
||
|
||
## Task 1:从 Figma 导出全部素材 PNG
|
||
|
||
**Files:**
|
||
- Create: `src/assets/image/hg/common/{logo,theme,qr-text,icon-tg,icon-biz,icon-channel}.png`
|
||
- Create: `src/assets/image/hg/pc/{bg1..bg5,covers1..covers5}.png`
|
||
|
||
导出方式:对每个节点调用 `mcp__figma__get_screenshot`(fileKey `yD7lloED2Okdp1FJTYvhe3`),拿到 `image_url` 后用 `curl -s -o <目标路径> "<url>"` 下载。背景用默认整合渲染;其余叠加元素用 `contentsOnly: true`(透明底)。
|
||
|
||
- [ ] **Step 1.1:建目录**
|
||
|
||
```bash
|
||
mkdir -p src/assets/image/hg/common src/assets/image/hg/pc
|
||
```
|
||
|
||
- [ ] **Step 1.2:导出 5 张背景**(`maxDimension: 1920`,默认模式)
|
||
- 73:54 → `src/assets/image/hg/pc/bg1.png`
|
||
- 73:112 → `bg2.png`
|
||
- 73:169 → `bg3.png`
|
||
- 73:225 → `bg4.png`
|
||
- 73:281 → `bg5.png`
|
||
|
||
- [ ] **Step 1.3:导出 5 张封面长条**(`contentsOnly: true`,`maxDimension: 1212` ≈2×)
|
||
- 81:766 → `covers1.png`,81:735 → `covers2.png`,81:704 → `covers3.png`,81:673 → `covers4.png`,73:356 → `covers5.png`
|
||
|
||
- [ ] **Step 1.4:导出共用元素**(`contentsOnly: true`)
|
||
- 73:76 → `common/logo.png`(`maxDimension: 308`)
|
||
- 73:62 → `common/theme.png`(`maxDimension: 1260`)
|
||
- 73:69 → `common/qr-text.png`(`maxDimension: 1004`)
|
||
- 73:83 → `common/icon-tg.png`(`maxDimension: 72`)
|
||
- 73:93 → `common/icon-biz.png`(`maxDimension: 72`)
|
||
- 73:104 → `common/icon-channel.png`(`maxDimension: 72`)
|
||
|
||
- [ ] **Step 1.5:核对**
|
||
|
||
```bash
|
||
ls -la src/assets/image/hg/common src/assets/image/hg/pc
|
||
```
|
||
Expected:14 个 png,皆非 0 字节。用 Read 抽看 `theme.png`/`qr-text.png`/`logo.png` 确认是透明底、无背景大图穿透;若穿透,改该节点为 `contentsOnly: true` 重导,或退化为 CSS 文字渲染(见 Task 3 备注)。
|
||
|
||
- [ ] **Step 1.6:生成 webp**
|
||
|
||
```bash
|
||
pnpm webp
|
||
```
|
||
Expected:上述 png 同目录生成同名 `.webp`。
|
||
|
||
---
|
||
|
||
## Task 2:建立 `src/config/variants.ts`
|
||
|
||
**Files:**
|
||
- Create: `src/config/variants.ts`
|
||
|
||
- [ ] **Step 2.1:写入完整内容**
|
||
|
||
```ts
|
||
import bg1 from '~/assets/image/hg/pc/bg1.png'
|
||
import bg1W from '~/assets/image/hg/pc/bg1.webp'
|
||
import bg2 from '~/assets/image/hg/pc/bg2.png'
|
||
import bg2W from '~/assets/image/hg/pc/bg2.webp'
|
||
import bg3 from '~/assets/image/hg/pc/bg3.png'
|
||
import bg3W from '~/assets/image/hg/pc/bg3.webp'
|
||
import bg4 from '~/assets/image/hg/pc/bg4.png'
|
||
import bg4W from '~/assets/image/hg/pc/bg4.webp'
|
||
import bg5 from '~/assets/image/hg/pc/bg5.png'
|
||
import bg5W from '~/assets/image/hg/pc/bg5.webp'
|
||
import c1 from '~/assets/image/hg/pc/covers1.png'
|
||
import c1W from '~/assets/image/hg/pc/covers1.webp'
|
||
import c2 from '~/assets/image/hg/pc/covers2.png'
|
||
import c2W from '~/assets/image/hg/pc/covers2.webp'
|
||
import c3 from '~/assets/image/hg/pc/covers3.png'
|
||
import c3W from '~/assets/image/hg/pc/covers3.webp'
|
||
import c4 from '~/assets/image/hg/pc/covers4.png'
|
||
import c4W from '~/assets/image/hg/pc/covers4.webp'
|
||
import c5 from '~/assets/image/hg/pc/covers5.png'
|
||
import c5W from '~/assets/image/hg/pc/covers5.webp'
|
||
|
||
export interface PcVariant {
|
||
bg: string
|
||
bgW: string
|
||
covers: string
|
||
coversW: string
|
||
}
|
||
|
||
export const PC_VARIANTS: PcVariant[] = [
|
||
{ bg: bg1, bgW: bg1W, covers: c1, coversW: c1W },
|
||
{ bg: bg2, bgW: bg2W, covers: c2, coversW: c2W },
|
||
{ bg: bg3, bgW: bg3W, covers: c3, coversW: c3W },
|
||
{ bg: bg4, bgW: bg4W, covers: c4, coversW: c4W },
|
||
{ bg: bg5, bgW: bg5W, covers: c5, coversW: c5W },
|
||
]
|
||
|
||
export const VERSION_LETTERS = ['A', 'B', 'C', 'D', 'E'] as const
|
||
|
||
// 导航链接:官方群组已定,其余为占位,给到网址改这里即可
|
||
export const NAV_LINKS = {
|
||
group: 'https://t.me/huangguoshortdrama',
|
||
biz: '#TODO-business',
|
||
channel: '#TODO-channel',
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Task 3:建立 `src/components/landing/PcLanding.vue`
|
||
|
||
**Files:**
|
||
- Create: `src/components/landing/PcLanding.vue`
|
||
|
||
- [ ] **Step 3.1:写入完整 SFC**
|
||
|
||
```vue
|
||
<template>
|
||
<div class="hg-pc">
|
||
<!-- 背景大图(变体) -->
|
||
<div class="bg">
|
||
<picture>
|
||
<source type="image/webp" :srcset="v.bgW" />
|
||
<img :src="v.bg" alt="" />
|
||
</picture>
|
||
</div>
|
||
|
||
<!-- 顶部 pill 导航条 -->
|
||
<div class="top">
|
||
<div class="logo">
|
||
<picture>
|
||
<source type="image/webp" :srcset="logoW" />
|
||
<img :src="logo" alt="黄果短剧" />
|
||
</picture>
|
||
</div>
|
||
<div class="nav">
|
||
<a class="item" :href="NAV_LINKS.group">
|
||
<picture>
|
||
<source type="image/webp" :srcset="iconTgW" />
|
||
<img :src="iconTg" alt="官方群组" />
|
||
</picture>
|
||
<span>官方群組</span>
|
||
</a>
|
||
<i class="dot" />
|
||
<a class="item" :href="NAV_LINKS.biz">
|
||
<picture>
|
||
<source type="image/webp" :srcset="iconBizW" />
|
||
<img :src="iconBiz" alt="商务合作" />
|
||
</picture>
|
||
<span>商務合作</span>
|
||
</a>
|
||
<i class="dot" />
|
||
<a class="item" :href="NAV_LINKS.channel">
|
||
<picture>
|
||
<source type="image/webp" :srcset="iconChW" />
|
||
<img :src="iconCh" alt="渠道合作" />
|
||
</picture>
|
||
<span>渠道合作</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主文案 -->
|
||
<div class="theme">
|
||
<picture>
|
||
<source type="image/webp" :srcset="themeW" />
|
||
<img :src="theme" alt="每一帧都能解你的色" />
|
||
</picture>
|
||
</div>
|
||
|
||
<!-- 二维码区 -->
|
||
<div class="qr">
|
||
<div class="qr-box">
|
||
<DesktopQrcode />
|
||
</div>
|
||
<div class="qr-text">
|
||
<picture>
|
||
<source type="image/webp" :srcset="qrTextW" />
|
||
<img :src="qrText" alt="" />
|
||
</picture>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部封面行(变体) -->
|
||
<div class="covers">
|
||
<picture>
|
||
<source type="image/webp" :srcset="v.coversW" />
|
||
<img :src="v.covers" alt="" />
|
||
</picture>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { computed, onMounted, onBeforeUnmount } from 'vue'
|
||
import DesktopQrcode from '~/components/desktop/qrcode.vue'
|
||
import { PC_VARIANTS, NAV_LINKS } from '~/config/variants'
|
||
import logo from '~/assets/image/hg/common/logo.png'
|
||
import logoW from '~/assets/image/hg/common/logo.webp'
|
||
import theme from '~/assets/image/hg/common/theme.png'
|
||
import themeW from '~/assets/image/hg/common/theme.webp'
|
||
import qrText from '~/assets/image/hg/common/qr-text.png'
|
||
import qrTextW from '~/assets/image/hg/common/qr-text.webp'
|
||
import iconTg from '~/assets/image/hg/common/icon-tg.png'
|
||
import iconTgW from '~/assets/image/hg/common/icon-tg.webp'
|
||
import iconBiz from '~/assets/image/hg/common/icon-biz.png'
|
||
import iconBizW from '~/assets/image/hg/common/icon-biz.webp'
|
||
import iconCh from '~/assets/image/hg/common/icon-channel.png'
|
||
import iconChW from '~/assets/image/hg/common/icon-channel.webp'
|
||
|
||
const props = defineProps<{ variant: number }>()
|
||
const v = computed(() => PC_VARIANTS[props.variant] ?? PC_VARIANTS[0])
|
||
|
||
function resizeBox() {
|
||
const w = window.innerWidth
|
||
const h = window.innerHeight
|
||
const ratio = w / h
|
||
const pct = ratio >= 1920 / 1080 ? (h / 1080) * 100 : (w / 1920) * 100
|
||
document.body.style.cssText = 'zoom:' + pct + '%'
|
||
}
|
||
|
||
onMounted(() => {
|
||
resizeBox()
|
||
window.addEventListener('resize', resizeBox)
|
||
})
|
||
onBeforeUnmount(() => window.removeEventListener('resize', resizeBox))
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
img { -webkit-user-drag: none; }
|
||
|
||
.hg-pc {
|
||
user-select: none;
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
.bg {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
img { width: 100%; height: 100%; object-fit: cover; }
|
||
}
|
||
|
||
.top {
|
||
position: absolute;
|
||
left: 360px;
|
||
top: 0;
|
||
width: 1200px;
|
||
height: 70px;
|
||
z-index: 3;
|
||
display: flex;
|
||
align-items: center;
|
||
background: rgba(255, 255, 255, 0.08);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 0 0 20px 20px;
|
||
|
||
.logo {
|
||
position: absolute;
|
||
left: 60px;
|
||
top: 15px;
|
||
width: 154px;
|
||
height: 40px;
|
||
img { width: 100%; height: 100%; object-fit: contain; }
|
||
}
|
||
|
||
.nav {
|
||
position: absolute;
|
||
left: 734px;
|
||
top: 23px;
|
||
height: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 32px;
|
||
|
||
.item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
img { width: 24px; height: 24px; display: block; }
|
||
span { color: #fff; font-size: 16px; line-height: 1; white-space: nowrap; }
|
||
}
|
||
|
||
.dot {
|
||
width: 2px;
|
||
height: 14px;
|
||
background-image: radial-gradient(rgba(255,255,255,0.6) 1px, transparent 1px);
|
||
background-size: 2px 4px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.theme {
|
||
position: absolute;
|
||
left: 204px;
|
||
top: 308px;
|
||
width: 630px;
|
||
height: 381px;
|
||
z-index: 2;
|
||
pointer-events: none;
|
||
img { width: 100%; height: 100%; object-fit: contain; }
|
||
}
|
||
|
||
.qr {
|
||
position: absolute;
|
||
left: 204px;
|
||
top: 750px;
|
||
width: 706px;
|
||
height: 180px;
|
||
z-index: 2;
|
||
|
||
.qr-box {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 180px;
|
||
height: 180px;
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.qr-text {
|
||
position: absolute;
|
||
left: 204px;
|
||
top: 7px;
|
||
width: 502px;
|
||
height: 166px;
|
||
img { width: 100%; height: 100%; object-fit: contain; }
|
||
}
|
||
}
|
||
|
||
.covers {
|
||
position: absolute;
|
||
left: 1184px;
|
||
top: 778px;
|
||
width: 606px;
|
||
height: 152px;
|
||
z-index: 2;
|
||
pointer-events: none;
|
||
img { width: 100%; height: 100%; object-fit: contain; }
|
||
}
|
||
}
|
||
</style>
|
||
```
|
||
|
||
> **备注(若 Task 1.5 发现 theme/qr-text/logo 透明导出失败):** 把对应 `<picture>` 换成 CSS 文字。logo 文字「黃果短劇」18px bold + 标语 12px;主文案标题约 60px bold + 副标 18px;二维码文字三行(24px / 18px semibold / 13px regular)。字体用系统 CJK 栈 `"PingFang SC","Microsoft YaHei",sans-serif`。
|
||
|
||
---
|
||
|
||
## Task 4:改造 `src/pages/index.vue` 为 5 路随机分流
|
||
|
||
**Files:**
|
||
- Modify: `src/pages/index.vue`
|
||
|
||
- [ ] **Step 4.1:替换为以下完整内容**
|
||
|
||
```vue
|
||
<script setup>
|
||
import PcLanding from '~/components/landing/PcLanding.vue'
|
||
import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'
|
||
import { useEventTracker, getSessionTraceId } from '~/utils/useEventTracker'
|
||
import { VERSION_LETTERS } from '~/config/variants'
|
||
|
||
const isMobile = computed(() =>
|
||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||
)
|
||
|
||
const variantIndex = ref(0)
|
||
const version = computed(() => VERSION_LETTERS[variantIndex.value])
|
||
const { trackLandingPageView, trackLandingPageClick, generateSessionId } = useEventTracker()
|
||
|
||
const handleClick = (e) => {
|
||
const rawText = e?.target?.alt || e.target.innerText || ''
|
||
const tabKey = rawText.replace(/[^A-Za-z0-9_一-鿿]/g, '_').replace(/_{2,}/g, '_').replace(/^_+|_+$/g, '') || ''
|
||
const isDownload = tabKey.toLowerCase() === 'download'
|
||
trackLandingPageClick({
|
||
landing_page_id: `JHA-126${version.value}`,
|
||
tab_key: tabKey,
|
||
tab_name: rawText,
|
||
click_coordinates_x: e.clientX,
|
||
click_coordinates_y: e.clientY,
|
||
click_x_percent: Math.round((e.clientX / window.innerWidth) * 100),
|
||
click_y_percent: Math.round((e.clientY / window.innerHeight) * 100),
|
||
screen_width: window.innerWidth,
|
||
screen_height: window.innerHeight,
|
||
trace_id: isDownload ? getSessionTraceId() : generateSessionId(),
|
||
})
|
||
}
|
||
|
||
onBeforeMount(() => {
|
||
variantIndex.value = Math.floor(Math.random() * 5)
|
||
trackLandingPageView({ landing_page_id: `JHA-126${version.value}` })
|
||
window.addEventListener('click', handleClick)
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
window.removeEventListener('click', handleClick)
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<!-- 手机版黄果变体待做:暂时桌面/手机都先用 PcLanding 占位 -->
|
||
<PcLanding :variant="variantIndex" />
|
||
</template>
|
||
|
||
<style scoped></style>
|
||
```
|
||
|
||
> **手机过渡说明:** 黄果手机版未做,此处先全部走 `PcLanding`。手机 Figma 到位后新增 `MobileLanding.vue`,模板改为 `isMobile ? MobileLanding : PcLanding`。`isMobile` 已保留备用。
|
||
|
||
---
|
||
|
||
## Task 5:更新 `index.html` 为简体黄果 SEO
|
||
|
||
**Files:**
|
||
- Modify: `index.html`
|
||
|
||
- [ ] **Step 5.1:替换 `<title>` 与各 meta**
|
||
- `<title>`:`黄果短剧官网 - 竖屏短剧APP下载 | 黄果`
|
||
- `description`:`黄果短剧官网 —— 竖屏短剧新世代。强情节、快节奏、随时随地,把最上瘾的好故事装进你的每一个碎片时间。支持安卓/iOS下载,海量都市情感、甜宠恋爱、古装传奇、悬疑反转短剧,免费在线看。`
|
||
- `keywords`:用户提供整串清单(逗号分隔)
|
||
- `og:title`:`黄果短剧 | 竖屏短剧APP下载官方入口`,`og:site_name`:`黄果短剧`,`og:url`:`https://huangguo.com`,`og:description` 同 description
|
||
- `twitter:title/description/site` 同步为黄果
|
||
- `canonical`:`https://huangguo.com`
|
||
- 删除 bili 专用 `rating`(adult / RTA)两行
|
||
- `favicon` / `og:image`:暂留现有(开放项)
|
||
|
||
---
|
||
|
||
## Task 6:静态检查 + 浏览器目检
|
||
|
||
> 不跑测试。先静态检查,再起 dev server 目检。
|
||
|
||
- [ ] **Step 6.1:类型检查** `pnpm exec tsc --noEmit` → 无错误
|
||
- [ ] **Step 6.2:构建** `pnpm build` → 成功输出 dist/
|
||
- [ ] **Step 6.3:dev 目检** `pnpm dev`,开 `http://localhost:3000/`,多刷几次确认随机命中不同变体;核对:背景大图、顶部 logo+3 导航、主文案、二维码(扫码=当前页)、底部封面行位置正确;点官方群组跳 `https://t.me/huangguoshortdrama`;缩放窗口无横向滚动条。
|
||
|
||
---
|
||
|
||
## Self-Review
|
||
|
||
- ✅ Spec §2 架构 → Task 2/3/4。✅ §3 坐标 → Task 3 样式。✅ §4 烤图/程式画 → Task 1+3。✅ §5 分流埋点 → Task 4。✅ §6 导航链接常量 → Task 2。✅ §7 二维码现状 → 复用 DesktopQrcode。✅ §8 SEO → Task 5。✅ §9 压缩 → Task 1.6 + `<picture>` + vsharp。
|
||
- Placeholder:biz/channel 链接为有意占位(规格 §10 已记),非计划缺漏。
|
||
- 类型一致:`PC_VARIANTS`/`NAV_LINKS`/`VERSION_LETTERS` 在 Task 2 定义,Task 3/4 引用一致;`PcVariant` 字段 `bg/bgW/covers/coversW` 与模板用法一致。
|
||
- 风险:Figma 文字节点透明导出可能穿背景 → Task 1.5 已加核对 + Task 3 CSS 退化备注。
|