[Ai] feat(page): 品牌改为 91fans 并切回 A 版落地页

- 品牌名:黄果短剧 / 黄果 / HUANGGUO 全量替换为 91fans,涉及 index.html
  的 title / description / keywords / og / twitter,以及 landing-b 与
  mobile 引导组件的展示文案;APK 文件名改为 91fans.apk
- 域名:hgaidj.app -> 91fans01.com(canonical、iOS 引导页、部署脚本注释)
- 图标:新增 app-icon.webp,favicon.ico、logo-h、logo-m 均按新 App 图标
  重制;删除无引用的旧品牌资源 logo-icon-activity / logo-icon-brick /
  mark-white / qr-huangguo
- 接口:APP_ID 与 iOS H5 描述文件 appId 由 2 改为 3;
  getLandingVersion 固定返回 A
- 落地页:恢复 A 版组件(PcDeck / PcLanding / PcLandingB / MobileLanding
  / share.vue),index.vue 按版本分流,PC 走 PcDeck、手机走 MobileLanding,
  /b 路由保留 B 版
- 移除分享页与手机版的 Telegram / X / Instagram / TikTok 入口及对应样式

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
谢宇宁
2026-09-17 11:04:06 +07:00
co-authored by Claude Opus 5
parent cb90ccb2de
commit 35a856b6f2
29 changed files with 1507 additions and 93 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ export const bottonclick = (param: donwloadParam) => {
}
// iOS H5 描述文件的 appId 写死
const H5_CONFIG_APP_ID = 2
const H5_CONFIG_APP_ID = 3
// iOS H5 描述文件:返回 .mobileconfig 原文(非 JSON),不走 useHttp
export const downloadH5 = async (data: H5param): Promise<string> => {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

+32 -32
View File
@@ -109,8 +109,8 @@ const {
<header class="nav" :class="{ scrolled: isScrolled }">
<div class="wrap">
<a class="logo" href="#" @click.prevent="switchView('landing')">
<img class="ico" :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" />
<img class="wm" :src="assetUrl('wordmark-white-1line.png')" alt="黄果短剧" />
<img class="ico" :src="assetUrl('app-icon.webp')" alt="91fans" />
<img class="wm" :src="assetUrl('wordmark-white-1line.png')" alt="91fans" />
</a>
<div class="right">
<button class="btn glass xs" @click="handleDownload()">
@@ -163,7 +163,7 @@ const {
{{ (currentHero?.tags || []).join(' ') }}&nbsp;&nbsp;{{ currentHero?.eps ? `全 ${currentHero.eps} 集 更新至第 ${currentHero.eps} 集` : '连载中' }}&nbsp;&nbsp;2026
</div>
<p class="syn">
{{ currentHero?.syn || '精彩短剧,黄果独家呈现。高清画质与震撼剧情,畅享视听盛宴。' }}
{{ currentHero?.syn || '精彩短剧,91fans独家呈现。高清画质与震撼剧情,畅享视听盛宴。' }}
</p>
<div class="dlhero">
<div class="dlbtns">
@@ -214,7 +214,7 @@ const {
下载 Android 版
</button>
<a class="link brand open-app" href="#" @click.prevent="handleOpenAppDeepLink">
已安装?打开黄果短剧 App
已安装?打开91fans App
<svg class="ic"><use href="#i-arrow-right" /></svg>
</a>
</template>
@@ -353,18 +353,18 @@ const {
<div class="col">
<span class="kicker">关于我们</span>
<h2 class="h2">不是所有短剧,<br />都能被称作作品。</h2>
<p class="sub">黄果短剧,因为专业,所以领先。</p>
<p class="sub">91fans,因为专业,所以领先。</p>
<p>
别人拼命追赶风口,我们定义创造标准。每一支作品都经过精心制作,分镜反复推敲,为一句台词的语气重新生成十次,字幕与剪辑的表情调整到满意为止。这就是我们的匠人精神,也是黄果品质唯一的定义。
别人拼命追赶风口,我们定义创造标准。每一支作品都经过精心制作,分镜反复推敲,为一句台词的语气重新生成十次,字幕与剪辑的表情调整到满意为止。这就是我们的匠人精神,也是91fans品质唯一的定义。
</p>
<p>
我们用 AI 帮助提升作品的品质,把不可能的画面化为可能。题材多元,都会、悬疑、古装、奇幻同步推进;每日更新,精彩不断。短剧与影集双轨并行,仿真与动画齐发。我们只做一件事:把每一支作品,做到值得被记得。
</p>
<p class="slogan">黄果一开,全射出来,每一帧都能解你的色。</p>
<p class="slogan">91fans一开,全射出来,每一帧都能解你的色。</p>
</div>
<div>
<div class="vis">
<img :src="assetUrl('about-visual.png')" alt="黄果短剧 作品视觉" />
<img :src="assetUrl('about-visual.png')" alt="91fans 作品视觉" />
</div>
<div class="tags">
<span class="chip">短剧与影集双轨并行</span>
@@ -376,12 +376,12 @@ const {
</div>
</section>
<!-- ④ 黄果特色 -->
<!-- ④ 91fans特色 -->
<section class="sec" style="padding-top: 0">
<div class="wrap">
<div class="secHead" data-reveal>
<div class="left">
<span class="kicker">黄果特色</span>
<span class="kicker">91fans特色</span>
<h2 class="h2">把最好看的留给你</h2>
</div>
</div>
@@ -415,7 +415,7 @@ const {
<div class="wscrim"></div>
<div class="wrap in">
<div data-reveal>
<h2>现在下载,海量短剧尽在黄果</h2>
<h2>现在下载,海量短剧尽在91fans</h2>
<p class="sub">
支援 iOS / Android&nbsp;&nbsp;双版本 APP,即刻下载,追剧不断档
</p>
@@ -467,7 +467,7 @@ const {
<div class="wrap faq">
<div class="left" data-reveal>
<span class="kicker">常见问题</span>
<h2 class="h2">关于黄果,<br />你想知道的</h2>
<h2 class="h2">关于91fans,<br />你想知道的</h2>
</div>
<div class="acc" data-reveal>
<div
@@ -559,12 +559,12 @@ const {
<footer>
<div class="wrap in">
<div class="lockup">
<img class="ico" :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" />
<img class="wm" :src="assetUrl('wordmark-white-2line.png')" alt="黄果短剧 hgaidj.app" />
<img class="ico" :src="assetUrl('app-icon.webp')" alt="91fans" />
<img class="wm" :src="assetUrl('wordmark-white-2line.png')" alt="91fans 91fans01.com" />
</div>
<div class="fl">
<a href="#works" @click.prevent="switchView('landing', '#works')">作品精选</a>
<a href="#about" @click.prevent="switchView('landing', '#about')">关于黄果</a>
<a href="#about" @click.prevent="switchView('landing', '#about')">关于91fans</a>
<a href="#faq" @click.prevent="switchView('landing', '#faq')">常见问题</a>
<a href="#" @click.prevent="handleDownload()">下载 APP</a>
</div>
@@ -573,16 +573,16 @@ const {
<p>本网站所呈现之所有人员,于拍摄当时均已年满 18 岁,符合 18 U.S.C. 第 2257 章规定。</p>
</div>
<div class="bot">
<span>© 2026 黄果短剧 HUANGGUO&nbsp;&nbsp;All rights reserved.</span>
<span>© 2026 91fans&nbsp;&nbsp;All rights reserved.</span>
<span>仅限 18 岁以上成年人</span>
</div>
</div>
</footer>
<!-- ============ H5 底部常驻下载条 ============ -->
<div class="mdl" :class="{ show: showMobileStickyBar }">
<img :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" />
<img :src="assetUrl('app-icon.webp')" alt="91fans" />
<div class="c">
<b>黄果短剧 App</b>
<b>91fans App</b>
<span>免费下载&nbsp;&nbsp;海量短剧每日更新</span>
</div>
<button class="btn primary" @click="handleDownload()">
@@ -597,10 +597,10 @@ const {
<div class="bd"></div>
<div class="dlg">
<div class="grab" aria-hidden="true"></div>
<img class="age-ico" :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" width="72" height="72" />
<img class="age-ico" :src="assetUrl('app-icon.webp')" alt="91fans" width="72" height="72" />
<h3 id="agegate-title">本站含成人内容</h3>
<p>
黄果短剧仅限 18 岁以上成年人访问。进入即表示您已年满 18 岁、所在地区法律允许浏览成人内容,并同意<a class="privacy" href="#" @click.prevent>《隐私政策》</a>。
91fans仅限 18 岁以上成年人访问。进入即表示您已年满 18 岁、所在地区法律允许浏览成人内容,并同意<a class="privacy" href="#" @click.prevent>《隐私政策》</a>。
</p>
<button class="btn primary md block enter" type="button" @click="confirmAge">
我已年满 18 岁,进入
@@ -616,13 +616,13 @@ const {
:class="{ open: showInstallGuideModal, in: showInstallGuideModal }"
role="dialog"
aria-modal="true"
aria-label="下载黄果短剧 App"
aria-label="下载91fans App"
>
<div class="dlg">
<header class="install-nav">
<button class="back" type="button" aria-label="返回" @click="showInstallGuideModal = false">
<svg class="ic"><use href="#i-chevron-left" /></svg>
<span>下载黄果短剧 App</span>
<span>下载91fans App</span>
</button>
</header>
@@ -630,7 +630,7 @@ const {
<template v-if="installGuideTab === 'ios'">
<div class="install-scroll">
<div class="hd">
<img :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" width="72" height="72" />
<img :src="assetUrl('app-icon.webp')" alt="91fans" width="72" height="72" />
<h3>安装 iOS 版</h3>
<p>
{{
@@ -670,7 +670,7 @@ const {
<svg class="ic"><use href="#i-si-apple" /></svg>
</div>
<div class="store-title">
<b>黄果短剧&nbsp;&nbsp;App Store 版</b>
<b>91fans&nbsp;&nbsp;App Store 版</b>
<span class="tag-rec">推荐</span>
</div>
<p>
@@ -711,7 +711,7 @@ const {
<span class="n">STEP.3</span>
<div class="c">
<b>返回桌面打开</b>
<span>桌面出现「黄果短剧」图标,点击即可观看。建议同时保存账号凭证。</span>
<span>桌面出现「91fans」图标,点击即可观看。建议同时保存账号凭证。</span>
</div>
</div>
<div class="tip">
@@ -738,7 +738,7 @@ const {
</template>
</button>
<a class="link brand open-app" href="#" @click.prevent="handleOpenAppDeepLink">
已安装?打开黄果短剧 App
已安装?打开91fans App
<svg class="ic"><use href="#i-arrow-right" /></svg>
</a>
</div>
@@ -748,7 +748,7 @@ const {
<template v-else>
<div class="install-scroll">
<div class="hd">
<img :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" width="72" height="72" />
<img :src="assetUrl('app-icon.webp')" alt="91fans" width="72" height="72" />
<h3>安装 Android 版</h3>
<p>已识别为 Android 设备,下载 APK 直接安装。</p>
</div>
@@ -758,7 +758,7 @@ const {
<span class="n">STEP.1</span>
<div class="c">
<b>下载 APK 安装包</b>
<span>点击「下载 Android 版」,浏览器开始下载 huangguo.apk,下载完成后在通知栏或下载列表点击打开。</span>
<span>点击「下载 Android 版」,浏览器开始下载 91fans.apk,下载完成后在通知栏或下载列表点击打开。</span>
</div>
</div>
<div class="step">
@@ -771,8 +771,8 @@ const {
<div class="step">
<span class="n">STEP.3</span>
<div class="c">
<b>打开黄果短剧</b>
<span>安装完成后点击「打开」,或在桌面找到「黄果短剧」图标即可观看。</span>
<b>打开91fans</b>
<span>安装完成后点击「打开」,或在桌面找到「91fans」图标即可观看。</span>
</div>
</div>
<div class="tip">
@@ -792,7 +792,7 @@ const {
下载 Android 版
</button>
<a class="link brand open-app" href="#" @click.prevent="handleOpenAppDeepLink">
已安装?打开黄果短剧 App
已安装?打开91fans App
<svg class="ic"><use href="#i-arrow-right" /></svg>
</a>
</div>
@@ -1905,7 +1905,7 @@ section.sec {
margin-top: 20px;
}
/* ============ 黄果特色 ============ */
/* ============ 91fans特色 ============ */
.feat {
margin-top: 40px;
display: flex;
+30 -30
View File
@@ -112,8 +112,8 @@ const {
<header class="nav" :class="{ scrolled: isScrolled }">
<div class="wrap">
<a class="logo" href="#" @click.prevent="switchView('landing')">
<img class="ico" :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" />
<img class="wm" :src="assetUrl('wordmark-white-1line.png')" alt="黄果短剧" />
<img class="ico" :src="assetUrl('app-icon.webp')" alt="91fans" />
<img class="wm" :src="assetUrl('wordmark-white-1line.png')" alt="91fans" />
</a>
<nav class="links">
<a
@@ -128,7 +128,7 @@ const {
:class="{ on: activeAnchor === '#about' && currentView === 'landing' }"
@click.prevent="switchView('landing', '#about')"
>
关于黄果
关于91fans
</a>
<a
href="#faq"
@@ -191,7 +191,7 @@ const {
{{ (currentHero?.tags || []).join(' ') }}&nbsp;&nbsp;{{ currentHero?.eps ? `全 ${currentHero.eps} 集 更新至第 ${currentHero.eps} 集` : '连载中' }}&nbsp;&nbsp;2026
</div>
<p class="syn">
{{ currentHero?.syn || '精彩短剧,黄果独家呈现。高清画质与震撼剧情,畅享视听盛宴。' }}
{{ currentHero?.syn || '精彩短剧,91fans独家呈现。高清画质与震撼剧情,畅享视听盛宴。' }}
</p>
<div class="dlhero">
<div class="qrbox">
@@ -389,18 +389,18 @@ const {
<div class="col">
<span class="kicker">关于我们</span>
<h2 class="h2">不是所有短剧,<br />都能被称作作品。</h2>
<p class="sub">黄果短剧,因为专业,所以领先。</p>
<p class="sub">91fans,因为专业,所以领先。</p>
<p>
别人拼命追赶风口,我们定义创造标准。每一支作品都经过精心制作,分镜反复推敲,为一句台词的语气重新生成十次,字幕与剪辑的表情调整到满意为止。这就是我们的匠人精神,也是黄果品质唯一的定义。
别人拼命追赶风口,我们定义创造标准。每一支作品都经过精心制作,分镜反复推敲,为一句台词的语气重新生成十次,字幕与剪辑的表情调整到满意为止。这就是我们的匠人精神,也是91fans品质唯一的定义。
</p>
<p>
我们用 AI 帮助提升作品的品质,把不可能的画面化为可能。题材多元,都会、悬疑、古装、奇幻同步推进;每日更新,精彩不断。短剧与影集双轨并行,仿真与动画齐发。我们只做一件事:把每一支作品,做到值得被记得。
</p>
<p class="slogan">黄果一开,全射出来,每一帧都能解你的色。</p>
<p class="slogan">91fans一开,全射出来,每一帧都能解你的色。</p>
</div>
<div>
<div class="vis">
<img :src="assetUrl('about-visual.png')" alt="黄果短剧 作品视觉" />
<img :src="assetUrl('about-visual.png')" alt="91fans 作品视觉" />
</div>
<div class="tags">
<span class="chip">短剧与影集双轨并行</span>
@@ -412,12 +412,12 @@ const {
</div>
</section>
<!-- ④ 黄果特色 -->
<!-- ④ 91fans特色 -->
<section class="sec" style="padding-top: 0">
<div class="wrap">
<div class="secHead" data-reveal>
<div class="left">
<span class="kicker">黄果特色</span>
<span class="kicker">91fans特色</span>
<h2 class="h2">把最好看的留给你</h2>
</div>
</div>
@@ -451,7 +451,7 @@ const {
<div class="wscrim"></div>
<div class="wrap in">
<div data-reveal>
<h2>现在下载,海量短剧尽在黄果</h2>
<h2>现在下载,海量短剧尽在91fans</h2>
<p class="sub">
支援 iOS / Android&nbsp;&nbsp;双版本 APP,即刻下载,追剧不断档
</p>
@@ -471,7 +471,7 @@ const {
<span class="qr-lab">扫码下载 App</span>
</div>
<div class="col">
<h4>扫码安装 黄果短剧 App</h4>
<h4>扫码安装 91fans App</h4>
<div class="btns">
<template v-if="hasIosShopPackage">
<button
@@ -532,7 +532,7 @@ const {
<div class="wrap faq">
<div class="left" data-reveal>
<span class="kicker">常见问题</span>
<h2 class="h2">关于黄果,<br />你想知道的</h2>
<h2 class="h2">关于91fans,<br />你想知道的</h2>
</div>
<div class="acc" data-reveal>
<div
@@ -624,12 +624,12 @@ const {
<footer>
<div class="wrap in">
<div class="lockup">
<img class="ico" :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" />
<img class="wm" :src="assetUrl('wordmark-white-2line.png')" alt="黄果短剧 hgaidj.app" />
<img class="ico" :src="assetUrl('app-icon.webp')" alt="91fans" />
<img class="wm" :src="assetUrl('wordmark-white-2line.png')" alt="91fans 91fans01.com" />
</div>
<div class="fl">
<a href="#works" @click.prevent="switchView('landing', '#works')">作品精选</a>
<a href="#about" @click.prevent="switchView('landing', '#about')">关于黄果</a>
<a href="#about" @click.prevent="switchView('landing', '#about')">关于91fans</a>
<a href="#faq" @click.prevent="switchView('landing', '#faq')">常见问题</a>
<a href="#" @click.prevent="handleDownload()">下载 APP</a>
</div>
@@ -638,7 +638,7 @@ const {
<p>本网站所呈现之所有人员,于拍摄当时均已年满 18 岁,符合 18 U.S.C. 第 2257 章规定。</p>
</div>
<div class="bot">
<span>© 2026 黄果短剧 HUANGGUO&nbsp;&nbsp;All rights reserved.</span>
<span>© 2026 91fans&nbsp;&nbsp;All rights reserved.</span>
<span>仅限 18 岁以上成年人</span>
</div>
</div>
@@ -657,7 +657,7 @@ const {
/>
</span>
<div class="c">
<b>扫码下载黄果短剧 App</b>
<b>扫码下载91fans App</b>
<span>支援 iOS / Android</span>
<button class="btn primary" @click="handleDownload()">
<svg class="ic"><use href="#i-download" /></svg>
@@ -676,10 +676,10 @@ const {
<div class="modal agegate" :class="{ open: showAgeGate, in: showAgeGate }" role="dialog" aria-modal="true" aria-labelledby="agegate-title-pc">
<div class="bd"></div>
<div class="dlg">
<img class="age-ico" :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" width="72" height="72" />
<img class="age-ico" :src="assetUrl('app-icon.webp')" alt="91fans" width="72" height="72" />
<h3 id="agegate-title-pc">本站含成人内容</h3>
<p>
黄果短剧仅限 18 岁以上成年人访问。进入即表示您已年满 18 岁、所在地区法律允许浏览成人内容,并同意<a class="privacy" href="#" @click.prevent>《隐私政策》</a>。
91fans仅限 18 岁以上成年人访问。进入即表示您已年满 18 岁、所在地区法律允许浏览成人内容,并同意<a class="privacy" href="#" @click.prevent>《隐私政策》</a>。
</p>
<button class="btn primary lg block enter" type="button" @click="confirmAge">
我已年满 18 岁,进入
@@ -697,7 +697,7 @@ const {
<svg class="ic"><use href="#i-x" /></svg>
</button>
<div class="hd">
<h3>下载黄果短剧 App</h3>
<h3>下载91fans App</h3>
<p>
<template v-if="targetDramaForDownload">
下载后即可观看<b>《{{ targetDramaForDownload.t }}》</b>全 {{ targetDramaForDownload.eps || '' }} 集及更多原创作品&nbsp;&nbsp;支援 iOS / Android
@@ -761,7 +761,7 @@ const {
<div class="bot">
<span>
已安装?
<a class="link brand" href="#" @click.prevent="handleOpenAppDeepLink">打开黄果短剧 App</a>
<a class="link brand" href="#" @click.prevent="handleOpenAppDeepLink">打开91fans App</a>
</span>
<a
class="link guide"
@@ -783,8 +783,8 @@ const {
<svg class="ic"><use href="#i-x" /></svg>
</button>
<div class="hd">
<img :src="assetUrl('logo-icon-activity.png')" alt="黄果短剧" />
<h3>安装黄果短剧 App</h3>
<img :src="assetUrl('app-icon.webp')" alt="91fans" />
<h3>安装91fans App</h3>
<p>三步完成,约 1 分钟。安装后可在桌面直接打开。</p>
</div>
<div class="seg">
@@ -810,7 +810,7 @@ const {
<span class="n">STEP.3</span>
<div class="c">
<b>返回桌面打开</b>
<span>桌面出现「黄果短剧」图标,点击即可观看。建议同时保存账号凭证。</span>
<span>桌面出现「91fans」图标,点击即可观看。建议同时保存账号凭证。</span>
</div>
</div>
</div>
@@ -819,7 +819,7 @@ const {
<span class="n">STEP.1</span>
<div class="c">
<b>下载 APK 安装包</b>
<span>点击「下载 Android 版」,浏览器开始下载 huangguo.apk,下载完成后在通知栏或下载列表点击打开。</span>
<span>点击「下载 Android 版」,浏览器开始下载 91fans.apk,下载完成后在通知栏或下载列表点击打开。</span>
</div>
</div>
<div class="step">
@@ -832,8 +832,8 @@ const {
<div class="step">
<span class="n">STEP.3</span>
<div class="c">
<b>打开黄果短剧</b>
<span>安装完成后点击「打开」,或在桌面找到「黄果短剧」图标即可观看。</span>
<b>打开91fans</b>
<span>安装完成后点击「打开」,或在桌面找到「91fans」图标即可观看。</span>
</div>
</div>
</div>
@@ -851,7 +851,7 @@ const {
{{ installGuideTab === 'ios' ? '下载 iOS 版' : '下载 Android 版' }}
</button>
<a class="link brand open-app" href="#" @click.prevent="handleOpenAppDeepLink">
已安装?打开黄果短剧 App
已安装?打开91fans App
<svg class="ic"><use href="#i-arrow-right" /></svg>
</a>
</div>
@@ -1960,7 +1960,7 @@ section.sec {
margin-top: 20px;
}
/* ============ 黄果特色 ============ */
/* ============ 91fans特色 ============ */
.feat {
margin-top: 40px;
display: flex;
+2 -2
View File
@@ -70,11 +70,11 @@ const GENRES = ['都市', '悬疑', '古装', '奇幻', '穿越', '重生', '武
const FAQS = [
{
q: '黄果短剧 App 需要付费吗?',
q: '91fans App 需要付费吗?',
a: 'App 免费下载。海量短剧与影集每日更新,开通会员可解锁全站作品、免广告与高清画质,随时可取消。',
},
{
q: 'iOS 如何安装黄果短剧 App?',
q: 'iOS 如何安装91fans App?',
a: '用 Safari 打开本页,点击「下载 iOS 版」,允许下载描述文件后,前往 设置 › 已下载描述文件 › 安装,输入锁屏密码即可;桌面出现图标即安装完成。',
},
{
+604
View File
@@ -0,0 +1,604 @@
<template>
<div class="m-page">
<!-- 整页背景(背景-4:hero 紫光 + WHO 人物列 + STAY 剧照,一整张) -->
<div class="page-bg">
<picture>
<source type="image/webp" :srcset="pageBgW" />
<img :src="pageBg" alt="" />
</picture>
</div>
<!-- ① Hero -->
<section class="hero">
<swiper
class="hero-bg"
:modules="modules"
:speed="500"
:loop="true"
:grab-cursor="true"
:allow-touch-move="true"
:autoplay="{ delay: 4000, disableOnInteraction: false }"
@slide-change="onSlide"
>
<swiper-slide v-for="(item, i) in PERSONS" :key="i">
<picture>
<source type="image/webp" :srcset="item.w" />
<img :src="item.p" alt="" />
</picture>
</swiper-slide>
</swiper>
<!-- 顶部 logo -->
<div class="top">
<picture class="logo">
<source type="image/webp" :srcset="logoMW" />
<img :src="logoM" alt="91fans" />
</picture>
</div>
<!-- 底部文案 + 圆点 + CTA -->
<div class="hero-bottom">
<div class="theme">
<h1 class="theme-title">每一帧<br /><span>都能</span><span class="hl">解你的色</span></h1>
<p class="theme-desc">91fans每日更新,海量高清成人短剧、成人影集在线观看,禁忌、诱惑、霸总、逆袭、穿越,精彩黄剧尽在91fans。</p>
</div>
<div class="dots">
<i v-for="i in PERSONS.length" :key="i" class="dot" :class="{ active: i - 1 === activeIndex }" />
</div>
<!-- Figma 743:561:正式发布胶囊 + 下载按钮(间距 25) -->
<div class="download-block">
<p class="launch-pill" role="status">
<span class="launch-dot" aria-hidden="true" />
<span>正式发布 · Officially launched</span>
</p>
<template v-if="isIos">
<div v-if="buttonList.shopIosDownUrl" class="cta-row">
<a class="cta cta-lite" data-track="download" role="button" @click="download(0)">轻量版下载</a>
<a class="cta cta-recommend" data-track="download" role="button" @click="download(1)">
商店包下载
<img class="cta-badge" :src="badgeRecommend" alt="推荐" width="56" height="56" />
</a>
</div>
<a v-else class="cta" data-track="download" role="button" @click="download(0)">轻量版下载</a>
</template>
<a v-else class="cta" data-track="download" role="button" @click="download()">{{ downloadLabel }}</a>
</div>
<div class="ctas">
<p class="install-tip">*下载后开启飞行模式安装*安装包报毒解决方案</p>
</div>
</div>
</section>
<!-- ② WHO WE ARE -->
<section class="who">
<div class="who-inner">
<p class="eyebrow">WHO WE ARE</p>
<h2 class="who-title">91fans一开,全射出来<br />这是我们对每一位用户的承诺。</h2>
<div class="paras">
<p>上千部精彩作品任你挑选,热门主题一应俱全。</p>
<p>从暧昧拉扯到禁忌反转,从碎片时间到深夜独处,随时打开,都能快速沉浸、越看越停不下来。</p>
<p>现在就打开这颗「91fans」,尝尝里面究竟藏着什么让人上瘾的滋味。</p>
</div>
<div class="cards">
<div class="card">
<h3>多元齐全</h3>
<p>多元的题材、海量的短剧与影集,我们把最好看的留给你。</p>
</div>
<div class="card">
<h3>天天上新</h3>
<p>持续更新的内容节奏,每天打开都有新惊喜,追剧永不断档。</p>
</div>
<div class="card">
<h3>沉浸体验</h3>
<p>高清画质、震撼音效,每个动作、每个画面,全心投入剧情。</p>
</div>
</div>
</div>
</section>
<!-- ③ STAY TUNED -->
<section class="stay">
<div class="stay-inner">
<p class="eyebrow">STAY TUNED</p>
<h2 class="stay-title">第一时间 不错过上新</h2>
<div class="foot">
<picture class="logo">
<source type="image/webp" :srcset="logoMW" />
<img :src="logoM" alt="91fans" />
</picture>
<p>© 2026 91fans. All rights reserved.</p>
</div>
</div>
</section>
<!-- 下载后的安装引导:Twitter/IG/TikTok → 复制到 Safari;其它 → Figma 三步轻量版安装引导 -->
<IosSafariOpenGuide v-if="isSocialInApp" :show="guide === 'ios'" :url="iosInstallUrl" @close="guide = null" />
<IosInstallGuide v-else :show="guide === 'ios'" @close="guide = null" />
<AndroidInstallTip :show="guide === 'android'" @close="guide = null" />
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Autoplay } from 'swiper/modules'
import type { Swiper as SwiperClass } from 'swiper/types'
import 'swiper/css'
import { useAppDownload } from '~/utils/useAppDownload'
import IosInstallGuide from '~/components/mobile/IosInstallGuide.vue'
import IosSafariOpenGuide from '~/components/mobile/IosSafariOpenGuide.vue'
import AndroidInstallTip from '~/components/mobile/AndroidInstallTip.vue'
import logoM from '~/assets/image/hg/common/logo-m.png'
import logoMW from '~/assets/image/hg/common/logo-m.webp'
import person1 from '~/assets/image/hg/mobile/person1.png'
import person1W from '~/assets/image/hg/mobile/person1.webp'
import person2 from '~/assets/image/hg/mobile/person2.png'
import person2W from '~/assets/image/hg/mobile/person2.webp'
import person3 from '~/assets/image/hg/mobile/person3.png'
import person3W from '~/assets/image/hg/mobile/person3.webp'
import person4 from '~/assets/image/hg/mobile/person4.png'
import person4W from '~/assets/image/hg/mobile/person4.webp'
import person5 from '~/assets/image/hg/mobile/person5.png'
import person5W from '~/assets/image/hg/mobile/person5.webp'
import pageBg from '~/assets/image/hg/mobile/pagebg.jpg'
import pageBgW from '~/assets/image/hg/mobile/pagebg.webp'
import badgeRecommend from '~/assets/image/hg/mobile/cta/badge-recommend.png'
const modules = [Autoplay]
const activeIndex = ref(0)
// Hero 五张人物图(透明羽化边,叠在固定颜色层上轮播)
const PERSONS = [
{ p: person1, w: person1W },
{ p: person2, w: person2W },
{ p: person3, w: person3W },
{ p: person4, w: person4W },
{ p: person5, w: person5W },
]
function onSlide(sw: SwiperClass) {
activeIndex.value = sw.realIndex
}
// 下载:进页面即拉取下载地址(appId=209,整包 query 作为渠道交后端),点击按平台分流
const { isIos, isSocialInApp, guide, iosInstallUrl, buttonList, init: initDownload, download } = useAppDownload()
const downloadLabel = isIos ? '立即下载 iOS 版' : '立即下载 Android 版'
onMounted(() => {
initDownload()
})
</script>
<style lang="scss" scoped>
@use 'sass:math';
// 设计稿 375 宽,main.js 设 1rem = 屏宽/7.5(375 屏 = 50px);故设计 px → rem 为 /50
@function r($px) {
@return math.div($px, 50) * 1rem;
}
img {
-webkit-user-drag: none;
}
.m-page {
user-select: none;
position: fixed;
inset: 0;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
background: #1a0d1f;
.eyebrow {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 18px;
line-height: 1;
color: #ffb92b;
}
.paras {
display: flex;
flex-direction: column;
gap: r(12);
p {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: r(14);
line-height: 1.6;
color: #fff;
}
&.center p {
text-align: center;
}
}
}
// 整页背景:一整张 背景-4,按 375 宽自然高度铺满、随内容滚动,三段透明叠在上面
.page-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 0;
img {
width: 100%;
height: auto;
display: block;
}
}
// ① Hero
.hero {
position: relative;
width: 100%;
height: 100vh;
height: 100dvh;
overflow: hidden;
// 人物轮播(前景,顶部对齐;紫光由整页背景 .page-bg 提供)
.hero-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
height: r(560);
z-index: 1;
}
:deep(.swiper-slide) {
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center top;
}
}
.top {
position: absolute;
top: 0;
left: 0;
right: 0;
height: r(70);
padding: 0 r(24);
display: flex;
align-items: center;
z-index: 3;
pointer-events: none;
}
// 文案层整块盖在轮播上,默认不接触摸(透给下面的 swiper 横滑),只给按钮恢复
.hero-bottom {
position: absolute;
left: 0;
right: 0;
bottom: r(35);
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
pointer-events: none;
.cta,
.cta-row,
.download-block {
pointer-events: auto;
}
.theme {
align-self: flex-start;
margin: 0 r(40);
width: r(295);
display: flex;
flex-direction: column;
gap: r(16);
.theme-title {
color: #FFF;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-size: 36px;
font-style: normal;
font-weight: 900;
line-height: normal;
.hl {
background: linear-gradient(90deg, #F9D742 -40%, #FF3276 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-size: 36px;
font-style: normal;
font-weight: 900;
line-height: normal;
}
}
.theme-desc {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: r(15);
line-height: 1.6;
color: #fff;
}
}
.dots {
// 标题底 y518 → 圆点 y546 ≈ 28
margin-top: r(28);
display: flex;
align-items: center;
gap: r(10);
.dot {
width: r(8);
height: r(8);
border-radius: 50%;
background: rgba(255, 255, 255, 0.35);
transition: all 0.3s ease;
&.active {
width: r(20);
border-radius: r(4);
background: #fff;
}
}
}
// Figma 743:561:胶囊(29) + 间距25 + 按钮(39) = 93
.download-block {
position: relative;
// 圆点底 y554 → 按钮组 y573 ≈ 19
margin-top: r(19);
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: r(25);
overflow: visible;
}
// Figma 743:562「正式发布 · Officially launched」
.launch-pill {
display: inline-flex;
align-items: center;
gap: r(6);
margin: 0;
padding: r(6) r(10);
box-sizing: border-box;
border-radius: r(999);
border: 1px solid #ffbb2c;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 700;
font-size: r(12);
line-height: normal;
color: #ffba2b;
white-space: nowrap;
pointer-events: none;
.launch-dot {
width: r(5);
height: r(5);
border-radius: 50%;
background: #ffba2b;
flex-shrink: 0;
}
}
.ctas {
margin-top: r(12);
display: flex;
flex-direction: column;
align-items: center;
gap: r(12);
overflow: visible;
}
// 下载按钮下的安装提示(文案与参考版一致)
.install-tip {
margin: 0;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
font-size: r(12);
line-height: 1.4;
color: rgba(255, 255, 255, 0.72);
white-space: nowrap;
}
// iOS 双下载:左白「轻量版」右黄「商店包」(Figma 743:565,宽243 / 左右66)
.cta-row {
display: flex;
align-items: stretch;
gap: r(18);
width: r(243);
margin: 0;
overflow: visible;
.cta {
flex: 1 1 0;
min-width: 0;
display: flex;
align-items: center;
justify-content: center;
height: r(39);
padding: r(8) r(12);
box-sizing: border-box;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
font-weight: 500;
font-size: r(16);
white-space: nowrap;
}
}
.cta-recommend {
position: relative;
// Figma 777:794:相对按钮组 top17 / 按钮 top54 → 相对按钮 top -37
.cta-badge {
position: absolute;
top: r(-37);
right: r(-32);
width: r(56);
height: r(56);
display: block;
pointer-events: none;
z-index: 2;
}
}
.cta {
padding: r(10) r(28);
border-radius: r(999);
background: #ffb92b;
box-shadow: 0 r(8) r(16) rgba(255, 187, 43, 0.25);
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 525;
font-size: r(14);
line-height: normal;
color: #2a0a2e;
cursor: pointer;
}
.cta-lite {
background: #fff;
box-shadow: none;
}
}
}
// logo 图(hero 顶部 + footer 共用):参考站手机版横图(图标 + 91fans短劇 + 副标),原图 252×60,按 30px 高 = 2x 整数缩放
.logo {
display: block;
img {
height: r(30);
width: auto;
display: block;
}
}
// WHO / STAY:本身透明,整页背景 .page-bg 在底层透出,内容叠在上面
.who,
.stay {
position: relative;
width: 100%;
z-index: 1;
}
// ② WHO WE ARE
.who-inner {
position: relative;
z-index: 1;
padding: r(36) r(30);
display: flex;
flex-direction: column;
gap: r(20);
.who-title {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 900;
font-size: r(30);
line-height: 1.4;
color: #fff;
}
.cards {
margin-top: r(4);
display: flex;
flex-direction: column;
gap: r(14);
.card {
display: flex;
flex-direction: column;
gap: r(5);
padding: r(18) r(12);
align-self: stretch;
border-radius: r(24);
border: 1px solid rgba(255, 210, 63, 0.3);
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
h3 {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 700;
font-size: r(20);
line-height: 1.2;
color: #ffb92b;
margin-bottom: 12px;
}
p {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 12px;
line-height: 1.5;
color: rgba(255, 255, 255, 0.85);
}
}
}
}
// ③ STAY TUNED
.stay-inner {
position: relative;
z-index: 1;
padding: r(36) r(30);
display: flex;
flex-direction: column;
align-items: center;
gap: r(24);
.eyebrow {
text-align: center;
}
.stay-title {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 900;
font-size: r(28);
line-height: 1.2;
color: #fff;
text-align: center;
}
.foot {
margin-top: r(8);
display: flex;
flex-direction: column;
align-items: center;
gap: r(12);
.logo {
justify-content: center;
}
p {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 12px;
line-height: 1.5;
color: #fff;
text-align: center;
}
}
}
</style>
+51
View File
@@ -0,0 +1,51 @@
<template>
<div class="pc-deck">
<!-- ① A 版(hero 轮播) -->
<section class="deck-page">
<PcLanding />
</section>
<!-- ② 原 B 版(WHO WE ARE) -->
<section class="deck-page">
<PcLandingB />
</section>
<!-- ③ 分享页 -->
<section class="deck-page">
<Share />
</section>
</div>
</template>
<script lang="ts" setup>
import PcLanding from '~/components/landing/PcLanding.vue'
import PcLandingB from '~/components/landing/PcLandingB.vue'
import Share from '~/pages/share.vue'
</script>
<style lang="scss" scoped>
.pc-deck {
width: 100%;
height: 100vh;
overflow-y: scroll;
overflow-x: hidden;
// 整屏强制翻页:每屏吸附、一次滚动只切一屏
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
// 隐藏滚动条
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.deck-page {
width: 100%;
height: 100vh;
overflow: hidden;
scroll-snap-align: start;
scroll-snap-stop: always;
}
</style>
+358
View File
@@ -0,0 +1,358 @@
<template>
<div class="fit" ref="fitEl">
<div class="hg-pc">
<!-- 五页轮播:只切换背景大图 -->
<swiper
class="hero"
:modules="modules"
effect="fade"
:speed="800"
:loop="true"
:allow-touch-move="false"
:autoplay="{ delay: 4000, disableOnInteraction: false }"
@swiper="onSwiper"
>
<swiper-slide v-for="(item, i) in PC_VARIANTS" :key="i">
<div class="slide-bg">
<!-- 首张是首屏第一眼,提优先级;其余保持默认,避免轮播切过去时还没下完 -->
<picture>
<source type="image/webp" :srcset="item.bgW" />
<img :src="item.bg" alt="" :fetchpriority="i === 0 ? 'high' : 'auto'" />
</picture>
</div>
</swiper-slide>
</swiper>
<!-- 固定 chrome 覆盖层(5 页共用):居中 1920 舞台 -->
<div class="stage">
<!-- 顶部 LOGO(白色横版) -->
<div class="top">
<picture>
<source type="image/webp" :srcset="logoHW" />
<img class="logo-h" :src="logoH" alt="91fans" />
</picture>
</div>
<!-- 主文案 -->
<div class="theme">
<h1 class="theme-title">
<span class="line">每一帧</span>
<span class="line"><span>都能</span><span class="hl">解你的色</span></span>
</h1>
<p class="theme-desc">91fans每日更新,海量高清成人短剧、成人影集在线观看,禁忌、诱惑、霸总、逆袭、穿越,精彩黄剧尽在91fans。</p>
</div>
<!-- 行动区:左 = 二维码框;右 = 扫码说明(Figma 659:2 二维码部分) -->
<div class="cta">
<div class="qr-frame">
<div class="qr-inner">
<DesktopQrcode :size="164" />
</div>
</div>
<div class="qr-copy">
<p class="qr-title">使用相机或者浏览器扫码下载</p>
<p class="qr-tip">
安装提示:下载好安装包以后,请断开网络,由于行业原因,本
APP会被某些杀毒软件视为病毒,请放心下载使用,本APP有广告和会员收益,安装本APP不会对您的手机造成任何损害。
</p>
</div>
</div>
<!-- 右下角封面条:整条图固定不换,5 张封面各盖一个热区,点击切到对应轮播页 -->
<div class="covers">
<picture>
<source type="image/webp" :srcset="PC_COVERS.srcW" />
<img :src="PC_COVERS.src" alt="" fetchpriority="high" />
</picture>
<button
v-for="(item, i) in PC_VARIANTS"
:key="i"
class="cover-hit"
:style="{ left: `${i * COVER_STEP}px` }"
type="button"
:aria-label="`查看第 ${i + 1} 部作品`"
@click="goSlide(i)"
/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, shallowRef, onMounted, onBeforeUnmount } from 'vue'
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Autoplay, EffectFade } from 'swiper/modules'
import type { Swiper as SwiperClass } from 'swiper'
import 'swiper/css'
import 'swiper/css/effect-fade'
import { PC_VARIANTS, PC_COVERS } from '~/config/variants'
import DesktopQrcode from '~/components/desktop/qrcode.vue'
import logoH from '~/assets/image/hg/common/logo-h.png'
import logoHW from '~/assets/image/hg/common/logo-h.webp'
const modules = [Autoplay, EffectFade]
// 封面条几何(同 Figma「轮动」:封面 110 宽、间距 14),热区据此逐张定位
const COVER_STEP = 124
const swiperRef = shallowRef<SwiperClass | null>(null)
function onSwiper(swiper: SwiperClass) {
swiperRef.value = swiper
}
// loop 模式下按真实索引跳转,跳完 autoplay 继续(disableOnInteraction: false)
function goSlide(i: number) {
swiperRef.value?.slideToLoop(i)
}
// 1920×1080 画布按视口 cover 缩放(不再用 body.zoom,避免和翻页滚动冲突)
const fitEl = ref<HTMLElement | null>(null)
function fit() {
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
fitEl.value?.style.setProperty('--fit', String(s))
}
onMounted(() => {
fit()
window.addEventListener('resize', fit)
})
onBeforeUnmount(() => window.removeEventListener('resize', fit))
</script>
<style lang="scss" scoped>
img {
-webkit-user-drag: none;
}
.fit {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #000;
}
.hg-pc {
user-select: none;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
// 轮播铺满整屏(背景全幅,cover 独立铺满,不随舞台缩放)
.hero {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
}
:deep(.swiper-slide) {
width: 100%;
height: 100%;
}
.slide-bg {
position: absolute;
inset: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// 固定 chrome:居中 1920 舞台,超过 1920 不左偏
.stage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(var(--fit, 1));
width: 1920px;
height: 1080px;
z-index: 2;
pointer-events: none;
}
// 顶部 LOGO
.top {
position: absolute;
left: 220px;
top: 38px;
.logo-h {
width: 220px;
height: 77px;
object-fit: contain;
display: block;
}
}
.theme {
position: absolute;
left: 244px;
top: 323px;
width: 630px;
display: flex;
flex-direction: column;
gap: 24px;
.theme-title {
margin: 0;
display: flex;
flex-direction: column;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 900;
font-size: 105px;
line-height: 1.44;
white-space: nowrap;
color: #fff;
.line {
display: flex;
gap: 10px;
}
.hl {
background: linear-gradient(90deg, #f9d742, #ff3276);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}
.theme-desc {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 24px;
line-height: 1.42;
color: #fff;
}
}
// 行动区:二维码 + 文案(Figma 659:2)
.cta {
position: absolute;
left: 244px;
top: 777px;
display: flex;
align-items: center;
gap: 31px;
pointer-events: auto;
// 220 圆角框:渐变描边(稿面自上而下橙 → 玫红)
.qr-frame {
flex-shrink: 0;
width: 220px;
height: 220px;
border-radius: 24px;
padding: 2px;
box-sizing: border-box;
background: linear-gradient(180deg, #f9d742 0%, #cb865f 45%, #f75562 100%);
}
.qr-inner {
width: 100%;
height: 100%;
border-radius: 22px;
background: #000;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
:deep(.qrContent) {
height: 188px;
.left {
width: 188px;
height: 188px;
margin-right: 0;
border-radius: 12px;
overflow: hidden;
.qr {
width: 188px;
height: 188px;
padding: 12px;
box-sizing: border-box;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
.qrcode {
padding: 0;
}
}
}
}
}
.qr-copy {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 20px;
font-family: 'PingFang SC', 'Noto Serif SC', 'Songti SC', sans-serif;
font-style: normal;
line-height: 1.6;
}
.qr-title {
margin: 0;
font-weight: 500;
font-size: 24px;
color: #f75562;
white-space: nowrap;
}
.qr-tip {
margin: 0;
width: 505px;
font-weight: 500;
font-size: 18px;
color: #fff;
}
}
// 右下角封面条(5 页共用同一张)
.covers {
position: absolute;
left: 1184px;
top: 778px;
width: 606px;
height: 152px;
pointer-events: auto;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
// 盖在整条图上的 5 个点击热区,与图内封面一一对齐
.cover-hit {
position: absolute;
top: 0;
width: 110px;
height: 152px;
padding: 0;
border: 1px solid transparent;
border-radius: 12px;
background: transparent;
cursor: pointer;
transition: background 0.2s, border-color 0.2s;
&:hover {
border-color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.16);
}
}
}
}
</style>
+223
View File
@@ -0,0 +1,223 @@
<template>
<div class="fit" ref="fitEl">
<div class="hg-pc-b">
<!-- 整页背景(倾斜剧照拼贴 + 暗化遮罩,已在设计中烘焙为一张图) -->
<div class="bg">
<!-- 第二屏资源:滚到才加载,不占首屏带宽(视口内时 lazy 会立即加载,单独访问不受影响) -->
<picture>
<source type="image/webp" :srcset="bgW" />
<img :src="bg" alt="" loading="lazy" fetchpriority="low" />
</picture>
</div>
<!-- 居中内容:左侧介绍 + 右侧三卡 -->
<div class="stage">
<div class="content">
<div class="intro">
<div class="head">
<p class="eyebrow">WHO WE ARE</p>
<h2 class="title">91fans一开,全射出来<br />这是我们对每一位用户的承诺。</h2>
</div>
<div class="paras">
<p>上千部精彩作品任你挑选,热门主题一应俱全。</p>
<p>从暧昧拉扯到禁忌反转,从碎片时间到深夜独处,随时打开,都能快速沉浸、越看越停不下来。</p>
<p>现在就打开这颗「91fans」,尝尝里面究竟藏着什么让人上瘾的滋味。</p>
</div>
</div>
<div class="cards">
<div class="card">
<h3>多元齐全</h3>
<p>多元的题材、海量的短剧与影集,我们把最好看的留给你。</p>
</div>
<div class="card">
<h3>天天上新</h3>
<p>持续更新的内容节奏,每天打开都有新惊喜,追剧永不断档。</p>
</div>
<div class="card">
<h3>沉浸体验</h3>
<p>高清画质、震撼音效,每个动作、每个画面,全心投入剧情。</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import bg from '~/assets/image/hg/pc/b-bg.jpg'
import bgW from '~/assets/image/hg/pc/b-bg.webp'
// 1920×1080 画布按视口 cover 缩放(不再用 body.zoom)
const fitEl = ref<HTMLElement | null>(null)
function fit() {
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
fitEl.value?.style.setProperty('--fit', String(s))
}
onMounted(() => {
fit()
window.addEventListener('resize', fit)
})
onBeforeUnmount(() => window.removeEventListener('resize', fit))
</script>
<style lang="scss" scoped>
img {
-webkit-user-drag: none;
}
.fit {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #000;
}
.hg-pc-b {
user-select: none;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #000;
.bg {
position: absolute;
inset: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.stage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(var(--fit, 1));
width: 1920px;
height: 1080px;
z-index: 2;
}
.content {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
gap: 52px;
}
.intro {
width: 840px;
display: flex;
flex-direction: column;
gap: 60px;
.head {
display: flex;
flex-direction: column;
gap: 24px;
}
.eyebrow {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 36px;
line-height: 1;
color: #ffb92b;
}
.title {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 900;
font-size: 60px;
line-height: 1.4;
color: #fff;
}
.paras {
display: flex;
flex-direction: column;
gap: 18px;
p {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 20px;
line-height: 1.6;
color: #fff;
}
}
}
.cards {
display: flex;
align-items: center;
gap: 20px;
.card {
position: relative;
width: 230px;
box-sizing: border-box;
padding: 70px 35px;
border-radius: 30px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 15px;
// 外框渐变描边:只画 2px 边框一圈(顶部亮金 → 底部渐隐),内部仍是透明玻璃
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 2px;
background: linear-gradient(180deg, #ffd23f 0%, rgba(255, 210, 63, 0.5) 40%, rgba(255, 210, 63, 0.08) 100%);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
h3 {
margin: 0;
width: 160px;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 700;
font-size: 36px;
line-height: 1.2;
color: #ffb92b;
}
p {
margin: 0;
width: 160px;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 22px;
line-height: 1.5;
text-align: justify;
color: #fff;
opacity: 0.6;
}
}
}
}
</style>
+8 -8
View File
@@ -1,6 +1,6 @@
<template>
<!-- Safari 轻量版安装引导:三步(Figma 63:89569 / 63:89572) -->
<div v-if="show" class="ios-guide" role="dialog" aria-label="黄果短剧轻量版安装引导">
<div v-if="show" class="ios-guide" role="dialog" aria-label="91fans轻量版安装引导">
<header class="guide-head">
<button class="nav-back" type="button" @click="emit('close')">
<img class="nav-back-ico" :src="iconBack" alt="" width="24" height="24" />
@@ -9,16 +9,16 @@
<div class="top">
<picture class="logo">
<source type="image/webp" :srcset="logoHW" />
<img :src="logoH" alt="黄果短剧" width="79" height="28" />
<img :src="logoH" alt="91fans" width="79" height="28" />
</picture>
<span class="domain">hgaidj.app</span>
<span class="domain">91fans01.com</span>
</div>
</header>
<div class="content">
<div class="head">
<p class="kicker">轻量版安装</p>
<h2 class="title">黄果短剧 轻量版安装引导</h2>
<h2 class="title">91fans 轻量版安装引导</h2>
<p class="desc">三步完成,约 1 分钟。安装后可在桌面直接打开。</p>
</div>
@@ -31,7 +31,7 @@
<div class="card">
<div class="ios-alert">
<div class="alert-body">
<p>“hgaidj.app”正尝试下载配置描述文件。要允许吗?</p>
<p>“91fans01.com”正尝试下载配置描述文件。要允许吗?</p>
</div>
<div class="alert-btns">
<div class="alert-btn muted">忽略</div>
@@ -56,7 +56,7 @@
<span class="path-brand">已下载描述文件</span>
</div>
<div class="path-row">
<span class="path-text">黄果短剧</span>
<span class="path-text">91fans</span>
<img class="ico-film" :src="iconFilm" alt="" width="12" height="12" />
<img class="ico-chevron" :src="iconChevron" alt="" width="14" height="14" />
<span class="path-brand">安装</span>
@@ -78,13 +78,13 @@
<span class="step-name">返回桌面打开</span>
</div>
<div class="card">
<p class="card-body">桌面出现「黄果短剧」图标,点击即可观看。建议同时保存账号凭证。</p>
<p class="card-body">桌面出现「91fans」图标,点击即可观看。建议同时保存账号凭证。</p>
</div>
</section>
<button class="cta" type="button" @click="emit('close')">
<img class="cta-ico" :src="iconPlay" alt="" width="16" height="16" />
<span>已安装,打开黄果短剧</span>
<span>已安装,打开91fans</span>
</button>
</div>
</div>
+3 -3
View File
@@ -9,9 +9,9 @@
<div class="top">
<picture class="logo">
<source type="image/webp" :srcset="logoHW" />
<img :src="logoH" alt="黄果短剧" width="79" height="28" />
<img :src="logoH" alt="91fans" width="79" height="28" />
</picture>
<span class="domain">hgaidj.app</span>
<span class="domain">91fans01.com</span>
</div>
</header>
@@ -46,7 +46,7 @@
</div>
</div>
<p class="foot">官方备用地址&nbsp;&nbsp;hgaidj.app</p>
<p class="foot">官方备用地址&nbsp;&nbsp;91fans01.com</p>
</div>
</div>
</template>
+2 -2
View File
@@ -1,2 +1,2 @@
// 黄果 App 在下载接口(blsp01.com /api/stat/version)的 appId
export const APP_ID = 2
// 91fans App 在下载接口(blsp01.com /api/stat/version)的 appId
export const APP_ID = 3
+20 -1
View File
@@ -1,9 +1,28 @@
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import PcDeck from '~/components/landing/PcDeck.vue'
import MobileLanding from '~/components/landing/MobileLanding.vue'
import LandingB from '~/components/landing-b/LandingB.vue'
import { getLandingVersion } from '~/utils/landingVersion'
const route = useRoute()
const isMobile = computed(() =>
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
)
// A/B 分流:与 getButtonList 共用同一份版本(见 landingVersion.ts),当前固定 A
const isVersionB = getLandingVersion(route.query, route.path) === 'B'
const landingComponent = computed(() => {
if (isVersionB) return LandingB
return isMobile.value ? MobileLanding : PcDeck
})
</script>
<template>
<LandingB />
<component :is="landingComponent" />
</template>
<style scoped></style>
+159
View File
@@ -0,0 +1,159 @@
<template>
<div class="fit" ref="fitEl">
<div class="hg-share">
<!-- 背景:剧照网格拼贴 + 暗化遮罩(Figma 导出整图) -->
<div class="bg">
<!-- 作为落地页第三屏时滚到才加载;单独访问 /share 时它在视口内,lazy 会立即加载 -->
<picture>
<source type="image/webp" :srcset="bgW" />
<img :src="bg" alt="" loading="lazy" fetchpriority="low" />
</picture>
</div>
<!-- 居中内容 -->
<div class="stage">
<div class="content">
<div class="head">
<p class="eyebrow">STAY TUNED</p>
<h1 class="title">第一时间 不错过上新</h1>
</div>
<div class="footer">
<picture>
<source type="image/webp" :srcset="logoHW" />
<img class="logo-h" :src="logoH" alt="91fans" />
</picture>
<p>© 2026 91fans. All rights reserved.</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import bg from '~/assets/image/hg/pc/share-bg.jpg'
import bgW from '~/assets/image/hg/pc/share-bg.webp'
import logoH from '~/assets/image/hg/common/logo-h.png'
import logoHW from '~/assets/image/hg/common/logo-h.webp'
// 1920×1080 画布按视口 cover 缩放(不再用 body.zoom)
const fitEl = ref<HTMLElement | null>(null)
function fit() {
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
fitEl.value?.style.setProperty('--fit', String(s))
}
onMounted(() => {
fit()
window.addEventListener('resize', fit)
})
onBeforeUnmount(() => window.removeEventListener('resize', fit))
</script>
<style lang="scss" scoped>
img {
-webkit-user-drag: none;
}
.fit {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #000;
}
.hg-share {
user-select: none;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #000;
.bg {
position: absolute;
inset: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.stage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(var(--fit, 1));
width: 1920px;
height: 1080px;
z-index: 2;
}
.content {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 60px;
}
.head {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
.eyebrow {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 36px;
line-height: 1;
color: #ffb92b;
}
.title {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 900;
font-size: 60px;
line-height: 1.2;
color: #fff;
text-align: center;
}
}
.footer {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
.logo-h {
width: 220px;
height: 77px;
object-fit: contain;
display: block;
}
p {
margin: 0;
font-family: 'Noto Serif SC', 'PingFang SC', 'Songti SC', serif;
font-weight: 400;
font-size: 16px;
line-height: 1.4;
color: #fff;
text-align: center;
}
}
}
</style>
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* 落地页版本:已下线 A 版,固定 B(getButtonList 等接口带 version=B)
* 落地页版本:固定 A(getButtonList 等接口带 version=A)
*/
export type LandingAbVersion = 'A' | 'B'
@@ -7,5 +7,5 @@ export function getLandingVersion(
_query?: Record<string, unknown>,
_path?: string,
): LandingAbVersion {
return 'B'
return 'A'
}
+1 -1
View File
@@ -158,7 +158,7 @@ export function useAppDownload() {
*/
async function download(index = 0, terminal: Terminal = isIos ? 'ios' : 'android') {
const trace_id = renewSessionTraceId()
// 写剪贴板供 App 首次启动读取做安装归因:整包 URL 参数 + tid(黄果 App 按 tid 读)
// 写剪贴板供 App 首次启动读取做安装归因:整包 URL 参数 + tid(91fans App 按 tid 读)
copyToClipboard(JSON.stringify({ ...route.query, tid: trace_id }))
if (terminal === 'ios' && index !== 1) {