Files
huangguo_landpage/docs/superpowers/plans/2026-05-29-huangguo-pc-landing.md
2026-09-15 16:25:45 +07:00

18 KiB
Raw Permalink Blame History

黄果短剧落地页(电脑版 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 + SCSSzoom 缩放(基于 1920×1080),绝对定位 + 图片叠加,<picture> webp+png。版面只写一次,variants.ts 映射每变体的 bg/covers。共用元素(logo/主文案/二维码文字烤成图,导航程式画)全变体复用。二维码复用 DesktopQrcode

Tech Stack: Vue 3 + Vite + SCSS + qrcode.vueFigma MCP get_screenshot(导出节点 PNG+ curl 下载;pnpm webpsharp q80)生成 webp。

规格来源: docs/superpowers/specs/2026-05-29-huangguo-landing-design.md


关键设计参数(Figma yD7lloED2Okdp1FJTYvhe3,变体 1 73:535 变体共用版面)

画布 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=0icon 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_screenshotfileKey yD7lloED2Okdp1FJTYvhe3),拿到 image_url 后用 curl -s -o <目标路径> "<url>" 下载。背景用默认整合渲染;其余叠加元素用 contentsOnly: true(透明底)。

  • Step 1.1:建目录
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: truemaxDimension: 1212 ≈2×)

    • 81:766 → covers1.png81:735 → covers2.png81:704 → covers3.png81:673 → covers4.png73:356 → covers5.png
  • Step 1.4:导出共用元素contentsOnly: true

    • 73:76 → common/logo.pngmaxDimension: 308
    • 73:62 → common/theme.pngmaxDimension: 1260
    • 73:69 → common/qr-text.pngmaxDimension: 1004
    • 73:83 → common/icon-tg.pngmaxDimension: 72
    • 73:93 → common/icon-biz.pngmaxDimension: 72
    • 73:104 → common/icon-channel.pngmaxDimension: 72
  • Step 1.5:核对

ls -la src/assets/image/hg/common src/assets/image/hg/pc

Expected14 个 png,皆非 0 字节。用 Read 抽看 theme.png/qr-text.png/logo.png 确认是透明底、无背景大图穿透;若穿透,改该节点为 contentsOnly: true 重导,或退化为 CSS 文字渲染(见 Task 3 备注)。

  • Step 1.6:生成 webp
pnpm webp

Expected:上述 png 同目录生成同名 .webp


Task 2:建立 src/config/variants.ts

Files:

  • Create: src/config/variants.ts

  • Step 2.1:写入完整内容

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

<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:替换为以下完整内容

<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 : PcLandingisMobile 已保留备用。


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:urlhttps://huangguo.comog:description 同 description
    • twitter:title/description/site 同步为黄果
    • canonicalhttps://huangguo.com
    • 删除 bili 专用 ratingadult / RTA)两行
    • favicon / og:image:暂留现有(开放项)

Task 6:静态检查 + 浏览器目检

不跑测试。先静态检查,再起 dev server 目检。

  • Step 6.1:类型检查 pnpm exec tsc --noEmit → 无错误
  • Step 6.2:构建 pnpm build → 成功输出 dist/
  • Step 6.3dev 目检 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。
  • Placeholderbiz/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 退化备注。