[Ai] chore: 初始化 hjsq_landpage 仓库
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: 用户要求 git commit、提交代码、写 commit message 或创建提交时,必须先加载 commit-convention skill 并严格遵守
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Git 提交规范自动激活
|
||||
|
||||
当用户要求**提交代码**、**git commit**、**写提交说明**或执行提交流程时:
|
||||
|
||||
1. 必须先加载 `.cursor/skills/commit-convention/SKILL.md`。
|
||||
2. Agent 代提交时 subject **必须**以 `[Ai]` 开头,并写清业务变更或 Bug 修复,禁止敷衍 subject。
|
||||
3. 未获用户明确授权不得 commit;不得提交 `.env` 等含密钥文件;默认不 push。
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
description: 项目通用规范,涵盖技术栈、目录结构、编码约定
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# 项目约定
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: Vue 2 + Vue CLI 4
|
||||
- **路由**: Vue Router 3 (history 模式)
|
||||
- **状态管理**: Vuex 3
|
||||
- **语言**: JavaScript (无 TypeScript)
|
||||
- **UI 库**: Element UI + Vant 2
|
||||
- **样式**: SCSS + scoped + PostCSS px2rem (rootValue: 37.5)
|
||||
- **移动端适配**: lib-flexible + postcss-plugin-px2rem
|
||||
- **HTTP**: Axios
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/ # API 请求模块
|
||||
├── assets/ # 静态资源 (图片、样式)
|
||||
├── plugins/ # 插件 (Element UI、flexible 等)
|
||||
├── router/ # 路由配置
|
||||
├── store/ # Vuex store
|
||||
├── utils/ # 工具函数
|
||||
├── views/ # 页面视图
|
||||
│ └── land/ # 落地页
|
||||
│ ├── index.vue # 主入口,根据设备/模板切换组件
|
||||
│ └── components/ # 落地页子组件 (mobile1, pc2 等)
|
||||
```
|
||||
|
||||
## 编码规范
|
||||
|
||||
- 使用 ES6+ 语法,Options API 风格
|
||||
- 组件通过 props 接收 `os`、`configs`、`switchData` 等父级数据
|
||||
- 事件通过 `$emit` 向上传递 (`recordClick`、`getApkInfo`)
|
||||
- 图片资源使用相对路径引用,放在 `assets/images/` 对应子目录下
|
||||
- px 值会被 PostCSS 自动转为 rem,带 `pcm` 选择器前缀的除外
|
||||
- 始终用中文回复
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
description: SCSS 样式编写规范,包含移动端适配和 px2rem 规则
|
||||
globs: "**/*.vue,**/*.scss"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# SCSS 样式规范
|
||||
|
||||
## px2rem 适配
|
||||
|
||||
- 项目使用 `postcss-plugin-px2rem`,rootValue 为 **37.5**
|
||||
- 设计稿以 **375px** 宽度为基准,直接写 px 值即可自动转换
|
||||
- 带 `pcm` 选择器前缀的样式**不会**被转换 (PC 端样式)
|
||||
|
||||
## 样式写法
|
||||
|
||||
- 使用 `<style lang="scss" scoped>` 保证组件样式隔离
|
||||
- 深层覆盖 Vant 组件样式使用 `::v-deep`:
|
||||
|
||||
```scss
|
||||
::v-deep .van-tabs__wrap {
|
||||
.van-tab--active {
|
||||
background: #f68804;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 常用模式
|
||||
|
||||
### 全屏背景图
|
||||
|
||||
```scss
|
||||
.contentBlock {
|
||||
height: 940px;
|
||||
width: 343px;
|
||||
background: url("./../../../assets/images/{template}/content.webp") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
```
|
||||
|
||||
### 固定定位浮层
|
||||
|
||||
```scss
|
||||
.floatingBox {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 187px;
|
||||
z-index: 998;
|
||||
}
|
||||
```
|
||||
|
||||
### 下载按钮
|
||||
|
||||
```scss
|
||||
.downBtn {
|
||||
border-radius: 24px;
|
||||
background: #ff6f2e;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
```
|
||||
|
||||
## 颜色常量 (项目常用)
|
||||
|
||||
| 用途 | 色值 |
|
||||
|------|------|
|
||||
| 页面背景 | `#141414` |
|
||||
| 主按钮 | `#ff6f2e` / `#f68804` |
|
||||
| Tab 背景 | `#2d2c2f` |
|
||||
| 弹窗按钮 | `#5b92ee` |
|
||||
| 文字白色 | `#ffffff` |
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
description: Vue 组件开发规范,适用于落地页组件
|
||||
globs: "**/*.vue"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Vue 组件规范
|
||||
|
||||
## 组件结构
|
||||
|
||||
遵循 `<template>` → `<script>` → `<style>` 顺序:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="pageName">
|
||||
<!-- 内容 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["os", "configs", "switchData"],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
watch: {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pageName {
|
||||
/* 样式 */
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 命名约定
|
||||
|
||||
- 文件名: 小写或 camelCase (`mobile1.vue`, `selectLinePopup.vue`)
|
||||
- 新落地页模板: `mobile{N}.vue` (移动端) / `pc{N}.vue` (PC 端)
|
||||
- CSS 类名: camelCase (`headerBox`, `floatingBox`, `bottomBox`)
|
||||
- 事件名: camelCase (`getApkInfo`, `recordClick`)
|
||||
|
||||
## Props 通信
|
||||
|
||||
落地页组件接收三个核心 props:
|
||||
|
||||
| Prop | 说明 |
|
||||
|------|------|
|
||||
| `os` | 设备信息 (isPhone, userAgent 等) |
|
||||
| `configs` | 配置列表 (tg_group, potato_group 等) |
|
||||
| `switchData` | 开关数据 (下载链接、功能开关) |
|
||||
|
||||
## 事件追踪
|
||||
|
||||
使用 `landingPageClick` 上报用户点击行为,传入 tab 和坐标:
|
||||
|
||||
```js
|
||||
import { landingPageClick } from "@/utils/eventTracker";
|
||||
landingPageClick(this, { tab_key, tab_name, click_coordinates_x, click_coordinates_y });
|
||||
```
|
||||
|
||||
## 图片资源引用
|
||||
|
||||
图片放在 `src/assets/images/{templateName}/` 下,在 SCSS 中通过相对路径引用:
|
||||
|
||||
```scss
|
||||
background: url("./../../../assets/images/mobile1/logo.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: commit-convention
|
||||
description: 为本仓库生成符合团队规范的 Git 提交信息,并在用户要求提交代码时执行完整提交流程。用于 git commit、提交代码、写 commit message、创建提交、push 前整理提交等场景;Agent 代提交时必须使用 [Ai] 标记并写清业务变更。
|
||||
---
|
||||
|
||||
# Git 提交规范(Agent 代提交)
|
||||
|
||||
## 触发条件
|
||||
|
||||
满足任一条件,**必须先加载本 skill**,再执行 `git add` / `git commit`:
|
||||
|
||||
- 用户明确要求:提交、commit、git commit、保存提交、push 前先 commit 等
|
||||
- 用户规则中的「committing-changes-with-git」流程已启动
|
||||
|
||||
**未获用户明确授权时,不得执行 commit。**
|
||||
|
||||
---
|
||||
|
||||
## Agent 代提交:标记规则(强制)
|
||||
|
||||
由 Cursor Agent 发起的提交,**必须**在 subject 前加 `[Ai]`:
|
||||
|
||||
```text
|
||||
[Ai] <type>(<scope>): <subject>
|
||||
|
||||
<body 可选:业务背景、影响范围、关联 Bug>
|
||||
```
|
||||
|
||||
- `type`:`feat` | `fix` | `refactor` | `style` | `perf` | `chore` | `docs` | `test` | `build` | `ci`
|
||||
- `scope`:模块/页面,如 `benefits`、`mine`、`community`、`player`
|
||||
- 可在正文末尾追加 trailer(可选):
|
||||
|
||||
```text
|
||||
Co-Authored-By: Cursor <noreply@cursor.com>
|
||||
```
|
||||
|
||||
用户本人手动提交、且明确说明「人工提交」时,建议使用 `[Human]`;Agent **不要**替用户假标 `[Human]`。
|
||||
|
||||
---
|
||||
|
||||
## Subject 质量(强制)
|
||||
|
||||
提交说明必须让人一眼看懂**改了什么业务**或**修了什么问题**。
|
||||
|
||||
### 必须做到
|
||||
|
||||
1. 先并行执行:`git status`、`git diff`(含 staged)、`git log -5 --oneline` 了解风格与变更范围
|
||||
2. subject 用**完整语义**描述,优先中文;可中英混用 scope
|
||||
3. 多文件、多模块时:一条 commit 只包同一业务目标;若混杂无关改动,先询问是否拆分
|
||||
4. body 写:原因、用户可见变化、风险点(如有)
|
||||
|
||||
### 严禁(subject 不得仅为或等同于)
|
||||
|
||||
`test`、`修改`、`update`、`fix`、`xxx`、`111`、`wip`、`temp`、`提交`、`save`、纯标点、纯数字、单字
|
||||
|
||||
### 示例
|
||||
|
||||
```text
|
||||
# ✅
|
||||
[Ai] feat(benefits): 新增七日签到弹窗与连续签到奖励展示
|
||||
[Ai] fix(player): 修复横屏切换后 HLS 首帧黑屏
|
||||
[Ai] refactor(mine): 签到任务列表抽离 useSignTask composable
|
||||
|
||||
# ❌
|
||||
[Ai] fix: 修改
|
||||
[Ai] feat: test
|
||||
修改播放器
|
||||
111
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 提交流程(与 user rule 对齐)
|
||||
|
||||
1. `git status` + `git diff` + `git log`(可并行)
|
||||
2. 根据 diff **撰写**符合本规范的 message(HEREDOC 传 `-m`)
|
||||
3. 只 `git add` 与本次任务相关的文件
|
||||
4. **不得**提交 `.env`、密钥、凭据类文件;若用户在 staged 中含此类文件,警告并排除
|
||||
5. `git commit` 后 `git status` 确认成功
|
||||
6. hook 失败时:**不要** `commit --amend`,修 message 后**新建 commit**
|
||||
7. **除非用户明确要求,否则不 `git push`**
|
||||
|
||||
---
|
||||
|
||||
## 输出给用户
|
||||
|
||||
提交完成后简要说明:
|
||||
|
||||
- commit hash(若有)
|
||||
- 本次纳入的文件范围
|
||||
- message 摘要(一行)
|
||||
|
||||
---
|
||||
|
||||
## 与本地 Hook / CI 的关系
|
||||
|
||||
本 skill 约束 **Agent 行为**。若仓库后续接入 `husky` + `commitlint` 或 CI,以仓库脚本校验为准;Agent 仍须预先满足 `[Ai]` 与 subject 质量,避免 hook 拒绝。
|
||||
@@ -0,0 +1,196 @@
|
||||
---
|
||||
name: figma-to-landpage
|
||||
description: 将 Figma 设计稿转换为 Vue 2 落地页组件。当用户提供 Figma 链接并要求生成落地页、实现设计稿、创建新模板时触发。适用于移动端和 PC 端落地页开发。
|
||||
---
|
||||
|
||||
# Figma 转落地页
|
||||
|
||||
将 Figma 设计稿翻译为本项目的 Vue 2 落地页组件,保持视觉还原度。
|
||||
|
||||
## 工作流程
|
||||
|
||||
### 第 1 步: 获取设计稿信息
|
||||
|
||||
从 Figma URL 中提取 `fileKey` 和 `nodeId`:
|
||||
- URL 格式: `https://figma.com/design/:fileKey/:fileName?node-id=1-2`
|
||||
- 将 `node-id` 中的 `-` 转为 `:`
|
||||
|
||||
依次调用:
|
||||
1. `get_design_context(fileKey, nodeId)` — 获取布局、颜色、字体等结构化数据
|
||||
2. `get_screenshot(fileKey, nodeId)` — 获取视觉截图作为还原参考
|
||||
|
||||
如果设计太复杂导致返回截断:
|
||||
1. 先调 `get_metadata(fileKey, nodeId)` 获取节点树
|
||||
2. 对各子节点分别调 `get_design_context`
|
||||
|
||||
### 第 2 步: 确定模板编号
|
||||
|
||||
查看 `src/views/land/components/` 下已有的模板文件:
|
||||
- 移动端: `mobile1.vue`, `mobile2.vue`, ...
|
||||
- PC 端: `pc1.vue`, `pc2.vue`, ...
|
||||
|
||||
新模板使用下一个序号。
|
||||
|
||||
### 第 3 步: 下载资源
|
||||
|
||||
将设计稿中的图片、图标导出并放入:
|
||||
```
|
||||
src/assets/images/{templateName}/
|
||||
```
|
||||
|
||||
例如 `mobile3` 模板的资源放在 `src/assets/images/mobile3/`。
|
||||
|
||||
Figma MCP 返回的 localhost 资源链接可直接下载使用。
|
||||
|
||||
### 第 4 步: 创建组件文件
|
||||
|
||||
在 `src/views/land/components/` 下创建新的 `.vue` 文件。
|
||||
|
||||
**必须遵循的组件结构:**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="mobilePage" @click="pageClick">
|
||||
<!-- 顶部固定区域 -->
|
||||
<div class="headerBox" @click.stop="getApkInfo(os.isPhone && switchData.shopIosDownUrl ? 'shopIos' : true, $event)">
|
||||
<div class="header-bg"></div>
|
||||
</div>
|
||||
|
||||
<!-- 主体内容区 -->
|
||||
<div class="main">
|
||||
<!-- Figma 设计内容转换到这里 -->
|
||||
</div>
|
||||
|
||||
<!-- 社交浮窗 -->
|
||||
<div class="floatingBox" v-if="configs && configs.length">
|
||||
<div class="telegram" v-if="cTg" @click="jumpUrl(cTg)"></div>
|
||||
<div class="potato" v-if="cPotato" @click="jumpUrl(cPotato)"></div>
|
||||
<div class="business" v-if="cBusiness" @click="jumpUrl(cBusiness)"></div>
|
||||
<div class="channel" v-if="cChannel" @click="jumpUrl(cChannel)"></div>
|
||||
</div>
|
||||
|
||||
<!-- 下载按钮区 -->
|
||||
<div class="bottomBox" @click.stop="getApkInfo(true, $event)"></div>
|
||||
|
||||
<!-- 安卓/iOS 提示弹窗 -->
|
||||
<van-overlay :show="androidPromptShow" z-index="999" class="androidOverlay">
|
||||
<!-- ... -->
|
||||
</van-overlay>
|
||||
<van-overlay :show="iosPromptShow" z-index="999" class="iosOverlay">
|
||||
<!-- ... -->
|
||||
</van-overlay>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { landingPageClick } from "@/utils/eventTracker";
|
||||
export default {
|
||||
props: ["os", "configs", "switchData"],
|
||||
data() {
|
||||
return {
|
||||
androidPromptShow: false,
|
||||
iosPromptShow: false,
|
||||
showTip: false,
|
||||
currUrl: window.location.href,
|
||||
cTg: "",
|
||||
cPotato: "",
|
||||
cBusiness: "",
|
||||
cChannel: "",
|
||||
showIosTip: true,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
configs(configs) {
|
||||
configs.forEach((item) => {
|
||||
if (item.configType === "tg_group") this.cTg = item.link;
|
||||
else if (item.configType === "potato_group") this.cPotato = item.link;
|
||||
else if (item.configType === "sw_cooperate") this.cBusiness = item.link;
|
||||
else if (item.configType === "qd_cooperate") this.cChannel = item.link;
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
pageClick(event) {
|
||||
landingPageClick(this, {
|
||||
tab_key: "main",
|
||||
tab_name: "主页",
|
||||
click_coordinates_x: event.clientX,
|
||||
click_coordinates_y: event.clientY,
|
||||
});
|
||||
},
|
||||
doCopy() {
|
||||
this.$copyText(this.currUrl).then(() => { this.showTip = false; });
|
||||
},
|
||||
async showInstallTutorial(type) {
|
||||
if (type === "ios") this.iosPromptShow = true;
|
||||
else this.androidPromptShow = true;
|
||||
},
|
||||
async getApkInfo(flag, event) {
|
||||
let type = this.os.isPhone ? "ios" : "android";
|
||||
if (flag === "shopIos") type = "shopIos";
|
||||
if (flag === "androidSpare") type = "androidSpare";
|
||||
if (flag && flag !== "shopIos") await this.showInstallTutorial(type);
|
||||
this.$emit("recordClick", type);
|
||||
setTimeout(() => { this.$emit("getApkInfo", type); }, 500);
|
||||
landingPageClick(this, {
|
||||
tab_name: "下载按钮",
|
||||
tab_key: "download",
|
||||
click_coordinates_x: event.clientX,
|
||||
click_coordinates_y: event.clientY,
|
||||
});
|
||||
},
|
||||
jumpUrl(url) { window.open(url); },
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 样式使用 SCSS + scoped,px 值基于 375 设计稿 */
|
||||
</style>
|
||||
```
|
||||
|
||||
### 第 5 步: 样式转换要点
|
||||
|
||||
将 Figma 设计数据转为 SCSS 时注意:
|
||||
|
||||
1. **尺寸**: 基于 375px 设计稿直接写 px,px2rem 自动转换
|
||||
2. **颜色**: 使用 Figma 给出的精确色值
|
||||
3. **图片**: 大面积背景用 webp 格式,图标用 png
|
||||
4. **字体**: Figma 字体映射到系统字体栈
|
||||
5. **布局**: 优先 Flex 布局,配合 `position: fixed` 实现吸顶/吸底
|
||||
6. **Vant 组件覆写**: 使用 `::v-deep` 覆盖 Vant 默认样式
|
||||
7. **PC 端样式**: 选择器加 `pcm` 前缀以避免 px2rem 转换
|
||||
|
||||
### 第 6 步: 注册组件
|
||||
|
||||
在 `src/views/land/index.vue` 中导入并注册新模板:
|
||||
|
||||
```js
|
||||
import mobileN from "./components/mobileN";
|
||||
// 在 components 中注册
|
||||
```
|
||||
|
||||
并在模板切换逻辑中添加对应条件。
|
||||
|
||||
### 第 7 步: 验证
|
||||
|
||||
- [ ] 视觉与 Figma 截图对比一致
|
||||
- [ ] 移动端/PC 端分别测试
|
||||
- [ ] 下载按钮、社交链接、弹窗功能正常
|
||||
- [ ] 事件追踪正常上报
|
||||
- [ ] 图片资源加载无误
|
||||
|
||||
## Figma 到 Vue 映射参考
|
||||
|
||||
| Figma 属性 | Vue/SCSS 对应 |
|
||||
|------------|--------------|
|
||||
| Auto Layout (横向) | `display: flex` |
|
||||
| Auto Layout (纵向) | `display: flex; flex-direction: column` |
|
||||
| Fill 颜色 | `background-color: #xxx` |
|
||||
| 图片 Fill | `background: url("...") no-repeat; background-size: 100% 100%` |
|
||||
| 固定位置 | `position: fixed` |
|
||||
| 圆角 | `border-radius: Npx` |
|
||||
| 文字样式 | `font-size`, `font-weight`, `line-height`, `color` |
|
||||
| 阴影 | `box-shadow` |
|
||||
| 间距 (Gap) | `gap: Npx` 或 `margin`/`padding` |
|
||||
| 透明度 | `opacity` 或 `rgba()` |
|
||||
@@ -0,0 +1,8 @@
|
||||
# just a flag
|
||||
ENV = 'development'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/'
|
||||
VUE_APP_APP_API='/api'
|
||||
VUE_APP_WEB_API=''
|
||||
VUE_APP_BASE_HOSTS = ["https://d1f02e9hwxyr26.cloudfront.net","https://xa98jupp.ddcu8aw.com"]
|
||||
@@ -0,0 +1,6 @@
|
||||
# just a flag
|
||||
ENV = 'production'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = '/api'
|
||||
VUE_APP_BASE_HOSTS = ["https://d1f02e9hwxyr26.cloudfront.net","https://xa98jupp.ddcu8aw.com"]
|
||||
@@ -0,0 +1,25 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
|
||||
parserOptions: {
|
||||
parser: "babel-eslint",
|
||||
},
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
"**/__tests__/*.{j,t}s?(x)",
|
||||
"**/tests/unit/**/*.spec.{j,t}s?(x)",
|
||||
],
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Node template
|
||||
# Logs
|
||||
/logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# Nuxt generate
|
||||
dist
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# IDE / Editor
|
||||
.idea
|
||||
|
||||
# Service worker
|
||||
sw.*
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Vim swap files
|
||||
*.swp
|
||||
mitaonewapp.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
12
|
||||
@@ -0,0 +1,21 @@
|
||||
# AGENTS.md
|
||||
|
||||
> 本文件用于给 Codex CLI 及兼容 AGENTS.md 的代理 在本仓库工作时提供导航。
|
||||
> 项目业务说明请参考 `README.md`。
|
||||
|
||||
<!-- COMMIT_CONVENTION:BEGIN -->
|
||||
## Commit 规范
|
||||
|
||||
**格式**:`[来源] <type>(<scope>): <中文描述>`
|
||||
|
||||
**来源**(必填):
|
||||
|
||||
- `[Ai]` — AI 代理(Claude / Codex / Cursor)提交,**必须用**
|
||||
- `[Human]` — 人工
|
||||
- `[Migrate]` — 迁移
|
||||
- `[Lib-Upgrade]` — 框架/依赖升级
|
||||
|
||||
**type**:`feat` / `fix` / `perf` / `style` / `docs` / `test` / `refactor` / `build` / `ci` / `chore` / `revert`
|
||||
|
||||
**示例**:`[Ai] feat(page): 新增活动页 V2`
|
||||
<!-- COMMIT_CONVENTION:END -->
|
||||
@@ -0,0 +1,21 @@
|
||||
# CLAUDE.md
|
||||
|
||||
> 本文件用于给 Claude Code(claude.ai/code) 在本仓库工作时提供导航。
|
||||
> 项目业务说明请参考 `README.md`。
|
||||
|
||||
<!-- COMMIT_CONVENTION:BEGIN -->
|
||||
## Commit 规范
|
||||
|
||||
**格式**:`[来源] <type>(<scope>): <中文描述>`
|
||||
|
||||
**来源**(必填):
|
||||
|
||||
- `[Ai]` — AI 代理(Claude / Codex / Cursor)提交,**必须用**
|
||||
- `[Human]` — 人工
|
||||
- `[Migrate]` — 迁移
|
||||
- `[Lib-Upgrade]` — 框架/依赖升级
|
||||
|
||||
**type**:`feat` / `fix` / `perf` / `style` / `docs` / `test` / `refactor` / `build` / `ci` / `chore` / `revert`
|
||||
|
||||
**示例**:`[Ai] feat(page): 新增活动页 V2`
|
||||
<!-- COMMIT_CONVENTION:END -->
|
||||
@@ -0,0 +1,5 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||
@@ -0,0 +1,76 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useId: typeof import('vue')['useId']
|
||||
const useLink: typeof import('vue-router')['useLink']
|
||||
const useModel: typeof import('vue')['useModel']
|
||||
const useRoute: typeof import('vue-router')['useRoute']
|
||||
const useRouter: typeof import('vue-router')['useRouter']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const imgDir = path.join(process.cwd(), 'src/assets/image'); // 图片根目录
|
||||
const imgTypes = ['.png', '.jpg', '.jpeg', '.gif']; // 支持的图片类型
|
||||
|
||||
async function convertImagesToWebp(dir) {
|
||||
try {
|
||||
const files = await fs.readdir(dir);
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
const extname = path.extname(filePath);
|
||||
const itemInfo = await fs.stat(filePath);
|
||||
|
||||
if (itemInfo.isFile() && imgTypes.includes(extname)) {
|
||||
const outputFilePath = path.join(dir, `${path.basename(file, extname)}.webp`);
|
||||
await sharp(filePath)
|
||||
.webp({ quality: 80 }) // 调整质量
|
||||
.toFile(outputFilePath);
|
||||
|
||||
console.log(`Converted: ${filePath} to ${outputFilePath}`);
|
||||
} else if (itemInfo.isDirectory()) {
|
||||
await convertImagesToWebp(filePath); // 递归处理子目录
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
convertImagesToWebp(imgDir);
|
||||
@@ -0,0 +1,117 @@
|
||||
# Commit 规范(北斗治理规范 v1)
|
||||
|
||||
> 团队来源:北斗 / 瑞升
|
||||
> 设计文档:`docs/superpowers/specs/2026-05-14-beidou-git-governance-design.md`(myproject 仓)
|
||||
> 适用范围:JHA-126-bili / JHA-117-pronhub 两个项目编号下所有前端仓库
|
||||
|
||||
---
|
||||
|
||||
## 1. 首次安装
|
||||
|
||||
clone 完仓库后,在仓库根执行一次:
|
||||
|
||||
```bash
|
||||
bash scripts/install-hooks.sh
|
||||
```
|
||||
|
||||
效果:把 `core.hooksPath` 指向仓库内 `.githooks/`,启用本地 commit-msg 校验。
|
||||
|
||||
紧急绕过:
|
||||
|
||||
```bash
|
||||
git commit --no-verify
|
||||
```
|
||||
|
||||
仅用于紧急上线,事后必须 `git commit --amend` 补全合规 prefix 后再 push。
|
||||
|
||||
---
|
||||
|
||||
## 2. Commit Subject 必含 4 个 Prefix 之一
|
||||
|
||||
每条 commit 的 subject(第一行)必须以下列 4 个 prefix 之一开头,**大小写敏感、方括号包裹、后接一个空格**:
|
||||
|
||||
| Prefix | 含义 | 是否计入 AI 占比统计 |
|
||||
|---|---|---|
|
||||
| `[Ai]` | AI 辅助生成 | ✅ 计入 AI 分子 |
|
||||
| `[Human]` | 纯人工 | ✅ 计入人工分子 |
|
||||
| `[Lib-Upgrade]` | 框架/依赖升级(复用) | ❌ 排除(不计入分母) |
|
||||
| `[Migrate]` | 跨项目迁移代码 | ❌ 排除(不计入分母) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 推荐写法
|
||||
|
||||
Prefix 后接 Conventional Commits 风格的 `type(scope): subject`:
|
||||
|
||||
```
|
||||
[Ai] feat(player): 钻石购买确认弹窗 ModalPayDiamondPop
|
||||
[Human] fix(wallet): 修正运营后台金额 currencyType Number 兜底
|
||||
[Lib-Upgrade] chore(deps): 升级 web-sdk 1.0.8 → 1.1.2
|
||||
[Migrate] feat(ui): 从 bili2_h5_client 迁入 AvatarWithBadge 组件
|
||||
```
|
||||
|
||||
常用 type:`feat / fix / refactor / docs / test / chore / perf / ci`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 可选 trailer:Co-Authored-By
|
||||
|
||||
`Co-Authored-By` 仅作元数据,**不参与 hook 校验**。统一格式(不带邮箱):
|
||||
|
||||
```
|
||||
Co-Authored-By: Claude
|
||||
Co-Authored-By: Cursor
|
||||
Co-Authored-By: Copilot
|
||||
Co-Authored-By: Cline
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 豁免列表
|
||||
|
||||
下列自动生成的 commit message,hook 直接放行,无需补 prefix:
|
||||
|
||||
- `Merge branch ...` / `Merge remote-tracking ...`(merge 自动消息)
|
||||
- `Revert "..."`(revert 自动消息)
|
||||
- `fixup! ...` / `squash! ...`(rebase 临时产物)
|
||||
|
||||
---
|
||||
|
||||
## 6. 紧急绕过
|
||||
|
||||
```bash
|
||||
git commit --no-verify
|
||||
```
|
||||
|
||||
适用场景:线上 P0 hotfix、CI 阻塞导致无法启动 hook 时。
|
||||
|
||||
事后补救:
|
||||
|
||||
```bash
|
||||
git commit --amend # 补全 prefix
|
||||
git push --force-with-lease # 仅在尚未被他人 pull 时使用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. FAQ
|
||||
|
||||
**Q1:AI 生成代码我又手工大改了一遍,算 `[Ai]` 还是 `[Human]`?**
|
||||
|
||||
A:看主导工作量。AI 写完后超过 50% 代码由人手改,标 `[Human]`;否则 `[Ai]`。原则:"谁产出主要代码就归谁"。
|
||||
|
||||
**Q2:revert 一个 `[Ai]` commit 算什么?**
|
||||
|
||||
A:revert 自动生成的 `Revert "..."` 消息走豁免分支,无需打 prefix。如果手动写 revert 的 subject,按"撤销动作"的来源标 prefix(自己手工写就 `[Human]`)。
|
||||
|
||||
**Q3:cherry-pick 从其它仓库带来的 commit 算 `[Migrate]` 吗?**
|
||||
|
||||
A:是。跨项目迁移(cherry-pick / 复制代码 / 移植)统一标 `[Migrate]`,不计入 AI/Human 分母。
|
||||
|
||||
---
|
||||
|
||||
## 8. 关联文档
|
||||
|
||||
- 本仓库 `project.md`(项目元信息 + 风险点 + 交接说明)
|
||||
- myproject 仓库 `docs/superpowers/specs/2026-05-14-beidou-git-governance-design.md`(设计 spec)
|
||||
- 北斗治理规范 v1(公司原始文档)
|
||||
@@ -0,0 +1,519 @@
|
||||
# 黄果短剧落地页(电脑版 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://hgaidj.app`,`og:description` 同 description
|
||||
- `twitter:title/description/site` 同步为黄果
|
||||
- `canonical`:`https://hgaidj.app`
|
||||
- 删除 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 退化备注。
|
||||
@@ -0,0 +1,173 @@
|
||||
# 黄果短剧落地页设计规格(电脑版 5 变体)
|
||||
|
||||
> **用户规则覆盖(最高优先级):**
|
||||
> - 不强制 TDD:不写"先写测试"步骤。
|
||||
> - 收尾只做静态检查(`tsc --noEmit` / lint / `pnpm build`),不自动跑单测/E2E。
|
||||
> - 不自动 commit / push / 建 PR:除非用户明确说"提交"。
|
||||
|
||||
**日期:** 2026-05-29
|
||||
**范围:** 本规格只覆盖**电脑版 5 个变体**。手机版 Figma 用户稍后提供,届时另起规格沿用同一套架构。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把当前 repo(残留的"逼哩逼哩 bili"模板)改造为**黄果短剧**落地页。电脑版有 5 个变体,版面结构完全相同,仅**背景大图**与**底部 5 张封面**不同。进站随机分流到其中之一(5 路 A~E,各 20%),并复用现有埋点。
|
||||
|
||||
设计来源 Figma:`yD7lloED2Okdp1FJTYvhe3`
|
||||
- 变体 1 `73:53`、变体 2 `73:111`、变体 3 `73:168`、变体 4 `73:224`、变体 5 `73:280`
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构总览
|
||||
|
||||
沿用现有技术栈:Vue 3 `<script setup lang="ts">` + Vite + SCSS,电脑端 `zoom` 缩放方案(基于 1920×1080)。**不引入新依赖**。
|
||||
|
||||
```
|
||||
src/
|
||||
├── pages/index.vue # 入口:设备检测 + 5 路随机分流 A~E
|
||||
├── components/landing/PcLanding.vue # 电脑版共用模板(本次重点)
|
||||
├── components/landing/MobileLanding.vue # 手机版共用模板(占位,后续做)
|
||||
├── components/desktop/qrcode.vue # 复用现有二维码组件
|
||||
├── config/variants.ts # 变体 → 素材映射表 + 导航链接常量
|
||||
└── assets/image/hg/
|
||||
├── common/ # 5 变体共用素材
|
||||
│ ├── logo.png / .webp # 左上 logo 锁定组(图标+黄果短剧+标语)
|
||||
│ ├── theme.png / .webp # 主文案大图(每一帧都能… + 副标)
|
||||
│ ├── qr-text.png / .webp # 二维码旁文字块(安卓手机 / UC扫码 / 安装提示)
|
||||
│ ├── icon-tg.png / .webp # 官方群组图标
|
||||
│ ├── icon-biz.png / .webp # 商务合作图标
|
||||
│ └── icon-channel.png / .webp # 渠道合作图标
|
||||
└── pc/ # 每变体一份
|
||||
├── bg1.png … bg5.png / .webp # 背景大图 1920×1080
|
||||
└── covers1.png … covers5.png / .webp # 底部封面长条 606×152(静态,5 张烤成一张)
|
||||
```
|
||||
|
||||
**核心思路:版面只写一次。** `PcLanding.vue` 接收 `variant`(0~4)prop,从 `variants.ts` 取该变体的 `bg` 与 `covers` 图,其余元素(导航、主文案、二维码区)5 变体完全共用。
|
||||
|
||||
---
|
||||
|
||||
## 3. 版面坐标(取自 Figma 变体 1 `73:53`,5 变体共用)
|
||||
|
||||
画布 1920×1080,全部为绝对坐标。
|
||||
|
||||
| 元素 | 位置 (x,y) | 尺寸 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 背景大图 | 0,0 | 1920×1080 | **每变体不同**,含底部深色渐变压暗(保证文字可读) |
|
||||
| 顶部 pill 导航条 | 360,0 | 1200×70 | 半透明圆角条,水平居中 |
|
||||
| ├ logo 锁定组 | 420,15 | 154×40 | 图标 40×40 + "黄果短剧" + 标语,烤成一张图 |
|
||||
| └ 联系方式(3 项) | 1094,23 | 406×24 | 官方群组 / 商务合作 / 渠道合作,项间有圆点分隔 |
|
||||
| 主文案 | 204,308 | 630×381 | 标题"每一幀都能 解你的色" + 副标,烤成一张图 |
|
||||
| 二维码区 | 204,750 | 706×180 | 左二维码 + 右文字块 |
|
||||
| ├ 二维码本体 | 204,750 | 180×180 | 白底圆角,内 150×150 动态二维码 |
|
||||
| └ 二维码文字块 | 408,757 | 502×166 | 安卓手机 / 建议使用UC浏览器扫码 / 安装提示,烤成一张图 |
|
||||
| 底部封面行 | 1184,778 | 606×152 | **每变体不同**,5 张 110×152 封面,间距 14px,烤成一张长条图 |
|
||||
|
||||
> 实作计画阶段会逐变体拉取背景与封面的 imageRef / 导出节点。3 个联系方式项的精确间距(item1@0 / 分隔@124 / item2@156 / 分隔@280 / item3@312,相对 406 宽容器)届时核对。
|
||||
|
||||
---
|
||||
|
||||
## 4. 烤图 vs 程式画(沿用 pc2 约定)
|
||||
|
||||
| 元素 | 做法 | 共用/变体 |
|
||||
|---|---|---|
|
||||
| 背景大图 | `<picture>` webp+png | **变体** bg1–bg5 |
|
||||
| 底部 5 封面 | 烤成 606×152 长条图 | **变体** covers1–covers5 |
|
||||
| logo 锁定组 | 烤图 | 共用 |
|
||||
| 主文案 | 烤图 | 共用 |
|
||||
| 二维码旁文字 | 烤图 | 共用 |
|
||||
| 3 个导航项(图标+文字) | **程式画**:可点击、可埋点 | 共用 |
|
||||
| 二维码本体 | 动态 `DesktopQrcode`(编码当前网址) | 共用 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 路由 / 分流 / 埋点
|
||||
|
||||
改造 `src/pages/index.vue`:
|
||||
|
||||
- 设备检测沿用现有 UA 正则。
|
||||
- 把当前 `Math.random() < 0.5 ? "A" : "B"` 扩成 **5 路随机**:`variantIndex = Math.floor(Math.random() * 5)`(0~4),`version = ["A","B","C","D","E"][variantIndex]`。
|
||||
- 桌面 → `<PcLanding :variant="variantIndex" />`。
|
||||
- 埋点照旧:`trackLandingPageView({ landing_page_id: 'JHA-126' + version })`,点击埋点 `handleClick` 逻辑保留不动。
|
||||
- **手机分支(过渡):** 黄果手机版尚未做。本期先让手机分支渲染一个简单的黄果占位(或暂时保留现有 mobile 组件),等手机 Figma 到位后换成 `<MobileLanding :variant="variantIndex" />`。**实作时确认**取哪种过渡(默认:简单占位)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 导航链接(`config/variants.ts` 顶部常量,易替换)
|
||||
|
||||
```ts
|
||||
export const NAV_LINKS = {
|
||||
group: 'https://t.me/huangguoshortdrama', // 官方群组(已定)
|
||||
biz: '#TODO-business', // 商务合作(待用户提供)
|
||||
channel: '#TODO-channel', // 渠道合作(待用户提供)
|
||||
}
|
||||
```
|
||||
|
||||
- 商务合作 / 渠道合作链接**用户尚未提供**,先放占位常量,给到网址后改一行即可。
|
||||
- 点击导航项跳转 `window.location.href = NAV_LINKS.xxx`,并经由现有 `handleClick`(监听 window click,按 alt/innerText 取 tab_key)上报埋点。
|
||||
|
||||
---
|
||||
|
||||
## 7. 二维码行为
|
||||
|
||||
维持现状:`DesktopQrcode` 编码 `window.location.href`(手机扫码 → 打开当前落地页的手机版)。
|
||||
> 用户尚未决定是否改成固定 APK/下载链接。若后续要改,只需把 `qrcode.vue` 的 `URL.value` 来源换成固定值,影响面单一。
|
||||
|
||||
---
|
||||
|
||||
## 8. index.html SEO 改造(简体,黄果短剧)
|
||||
|
||||
| 字段 | 新值 |
|
||||
|---|---|
|
||||
| `lang` | 保持 `zh-CN` |
|
||||
| `<title>` | 黄果短剧官网 - 竖屏短剧APP下载 \| 黄果 |
|
||||
| `description` | 黄果短剧官网 —— 竖屏短剧新世代。强情节、快节奏、随时随地,把最上瘾的好故事装进你的每一个碎片时间。支持安卓/iOS下载,海量都市情感、甜宠恋爱、古装传奇、悬疑反转短剧,免费在线看。 |
|
||||
| `keywords` | 用户提供的整串清单(黄果 / 黄果短剧 / 短剧 / 竖屏短剧 / 下载 / 安卓 / iOS / 各题材 / 竞品词 等,逗号分隔) |
|
||||
| `og:title` | 黄果短剧 \| 竖屏短剧APP下载官方入口 |
|
||||
| `og:site_name` | 黄果短剧 |
|
||||
| `og:url` / `canonical` | https://hgaidj.app |
|
||||
| `og:description` / twitter 同 description |
|
||||
| `theme-color` 等 | 保留 |
|
||||
|
||||
- `favicon` / `og:image`:暂保留现有,等黄果素材到位再换(**实作开放项**)。
|
||||
- 移除 `rating: adult` / RTA 等 bili 专用、与黄果无关的 meta(实作时确认)。
|
||||
|
||||
---
|
||||
|
||||
## 9. 图片压缩流程(用户明确要求)
|
||||
|
||||
1. 从 Figma 导出 PNG:背景用 **1×(1920×1080)**,不用 2×,控制体积;图标/封面按需 1×~2×。
|
||||
2. 跑 `pnpm webp`(`convertToWebp.js`,sharp `quality: 80`)批量生成 `.webp` 同名副本。
|
||||
3. 模板用 `<picture><source type="image/webp" :srcset="xxxW" /><img :src="xxx" /></picture>` 优先吃 webp、png 后备。
|
||||
4. build 时 `vite-plugin-vsharp` 再压一次。
|
||||
5. 背景为最大体积项,重点核对压后单张体积(目标尽量 < 300KB webp,必要时降质量或尺寸)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 开放项(实作前/中确认)
|
||||
|
||||
1. 商务合作 / 渠道合作 链接 — 用户提供后替换占位。
|
||||
2. 二维码是否改固定下载链接 — 默认维持现状。
|
||||
3. 手机分支过渡方案 — 默认简单占位。
|
||||
4. favicon / og:image 黄果素材 — 默认暂留现有。
|
||||
5. 是否删除 / 保留旧 bili 页面(pc/pc2/mobile/mobile2)与 `project.md` — 默认**先保留**,不在本期清理。
|
||||
|
||||
---
|
||||
|
||||
## 11. 不在本期范围
|
||||
|
||||
- 手机版 5 变体(等 Figma)。
|
||||
- 删除旧 bili 页面 / 重写 project.md。
|
||||
- 新增后端接口 / 埋点字段。
|
||||
- 自动 commit / 部署。
|
||||
|
||||
---
|
||||
|
||||
## 12. 自检
|
||||
|
||||
- ✅ 5 变体共用版面 → `PcLanding.vue` 单组件 + `variants.ts` 映射。
|
||||
- ✅ 仅背景+封面随变体变 → 素材分 `common/` 与 `pc/`。
|
||||
- ✅ 5 路随机分流 + 埋点 A~E → `index.vue` 改造。
|
||||
- ✅ 图片压缩 → `pnpm webp` + `<picture>` + vsharp。
|
||||
- ✅ 官方群组链接已定,其余占位易替换。
|
||||
- ⚠️ 占位项(biz/channel 链接、二维码、手机过渡、favicon)已在第 10 节列明,不阻塞电脑版实作。
|
||||
@@ -0,0 +1,81 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-Hant" style="overflow: hidden;">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="HandheldFriendly" content="true" />
|
||||
<meta http-equiv="x-dns-prefetch-control" content="on" />
|
||||
<meta name="referrer" content="same-origin" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700;900&display=swap" rel="stylesheet" />
|
||||
<title>51短剧|成人短剧APP|每日更新|海量高清</title>
|
||||
<meta name="description" content="51短剧每日更新,海量高清成人短剧、成人影集在线观看,禁忌、诱惑、霸总、逆袭、穿越,精彩黄剧尽在51短剧。" />
|
||||
<meta name="keywords" content="51短剧,51dj,51短剧 官网,51短剧落地页,51短剧 落地页,51短剧APP下载,51短剧 APP下载,51短剧 APP,51短剧 下载,51短剧 安卓版,51短剧 安卓下载,51短剧 APK,51短剧 苹果版,51短剧 iOS下载,51短剧 App Store,51短剧 观看,51短剧 怎么看,51短剧 在哪看,51短剧 在线看,51短剧 免费看,51短剧 全集,51短剧 注册,51短剧 登录,51短剧 会员,51短剧 VIP,51短剧 客服,51短剧 官方,51短剧 是什么,51短剧 好看吗,51短剧 评价,51短剧 心得,51短剧 安全吗,51短剧 收费,51短剧 上线时间,51短剧 预订,51短剧 预约,51短剧 通知,短剧,竖屏短剧,直屏短剧,微短剧,迷你剧,短剧集,追剧,在线短剧,短剧平台,短剧 APP,短剧网站,在线看短剧,免费短剧,短剧推荐,热门短剧,短剧下载,逆袭爽剧,小人物逆袭,爽剧,都市情感剧,都市爱情剧,悬疑反转剧,悬疑短剧,甜宠恋爱剧,甜宠剧,古装传奇,古装爽剧,奇幻脑洞,穿越短剧,重生短剧,霸道总裁,通勤看剧,碎片时间追剧,一集十分钟 短剧,上瘾短剧,睡前看剧,短剧 上瘾,什么是短剧,什么是竖屏短剧,红果短剧,ReelShort,DramaBox,番茄短剧,抖音短剧,快手短剧,手机 看短剧,短剧 排行榜,2026 短剧推荐,台湾 短剧,繁体中文 短剧,日本 短剧,短剧 上线通知" />
|
||||
<meta
|
||||
name="robots"
|
||||
content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1"
|
||||
/>
|
||||
<meta
|
||||
name="googlebot"
|
||||
content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1"
|
||||
/>
|
||||
|
||||
<!-- 站长验证:与 hgaidj.net 主站同域,沿用同一组验证码 -->
|
||||
<meta name="google-site-verification" content="r7geXn5kBugzOSLZzyw4v3NBDMQwAmzFCVkqp92odh0" />
|
||||
<meta name="yandex-verification" content="6bb06ed5659790d0" />
|
||||
<meta name="msvalidate.01" content="427637FF44FBAB9D82DB3E5F4E9BA800" />
|
||||
|
||||
<meta property="og:title" content="51短剧 | 竖屏短剧APP下载官方入口" />
|
||||
<meta property="og:site_name" content="51短剧" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:locale" content="zh_CN" />
|
||||
<meta property="og:url" content="https://hgaidj.net/" />
|
||||
<meta property="og:image" content="https://hgaidj.net/favicon.png?v=3" />
|
||||
<meta property="og:description" content="51短剧每日更新,海量高清成人短剧、成人影集在线观看,禁忌、诱惑、霸总、逆袭、穿越,精彩黄剧尽在51短剧。" />
|
||||
|
||||
<meta name="twitter:title" content="51短剧 | 竖屏短剧APP下载官方入口" />
|
||||
<meta name="twitter:description" content="51短剧每日更新,海量高清成人短剧、成人影集在线观看,禁忌、诱惑、霸总、逆袭、穿越,精彩黄剧尽在51短剧。" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:image" content="https://hgaidj.net/favicon.png?v=3" />
|
||||
<meta name="twitter:site" content="51短剧" />
|
||||
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="#000000" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="canonical" href="https://hgaidj.net/" />
|
||||
|
||||
|
||||
<!-- 统计:与 hgaidj.net 主站(web_huangguo_web) 同一套 ID -->
|
||||
<!-- Yandex.Metrika counter -->
|
||||
<script type="text/javascript">
|
||||
(function(m,e,t,r,i,k,a){
|
||||
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
|
||||
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=112967594', 'ym');
|
||||
|
||||
ym(112967594, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
|
||||
</script>
|
||||
<!-- /Yandex.Metrika counter -->
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-B5KGM8S5J1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-B5KGM8S5J1');
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
set -e
|
||||
pnpm run build
|
||||
zip -q -r dist.zip dist
|
||||
#黄果落地页地址 :https://hgaidj.app
|
||||
scp dist.zip root@172.104.166.87:/tmp/dist.zip
|
||||
ssh root@172.104.166.87 "set -e && rm -rf /tmp/extract && unzip -q /tmp/dist.zip -d /tmp/extract && cd /server/51dj/landpage/dist/ && rm -rf ./* && mv /tmp/extract/dist/* . && rm -rf /tmp/extract /tmp/dist.zip && echo DEPLOY_OK"
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "my-vue3-app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"webp": "node convertToWebp.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"ofetch": "^1.4.1",
|
||||
"ohash": "^1.1.6",
|
||||
"process-nextick-args": "^2.0.1",
|
||||
"qrcode.vue": "^3.6.0",
|
||||
"sharp": "^0.34.2",
|
||||
"swiper": "^11.2.6",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.22.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"globals": "^16.0.0",
|
||||
"gulp": "^5.0.1",
|
||||
"gulp-debug": "^5.0.1",
|
||||
"gulp-rename": "^2.1.0",
|
||||
"gulp-webp": "^5.0.0",
|
||||
"sass": "^1.86.0",
|
||||
"typescript": "^5.8.2",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"vite": "^5.4.14",
|
||||
"vite-plugin-vsharp": "^1.8.1"
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.14.1"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 101 KiB |
@@ -0,0 +1,362 @@
|
||||
[
|
||||
{
|
||||
"序": 1,
|
||||
"落地页名称": "废柴杂役",
|
||||
"素材资料夹": "LBSR-005-1-廢材雜役",
|
||||
"封面来源文件": "廢材雜役-總預告-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "廢材雜役-總預告-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d01.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 15,
|
||||
"题材标签(示意)": [
|
||||
"重生",
|
||||
"武侠"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 2,
|
||||
"落地页名称": "最强显示器",
|
||||
"素材资料夹": "MANI-008 最强显示器",
|
||||
"封面来源文件": "MANI-008 最強顯示器 EP0-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "MANI-008 最強顯示器 EP0-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d02.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 10,
|
||||
"题材标签(示意)": [
|
||||
"网游",
|
||||
"穿越"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 3,
|
||||
"落地页名称": "花田里犯的错",
|
||||
"素材资料夹": "ZYB-005-2 花田里犯的错",
|
||||
"封面来源文件": "ZYB-005-2 花田里犯的错 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "ZYB-005-2 花田里犯的错 EP1-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d03.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 10,
|
||||
"题材标签(示意)": [
|
||||
"奇幻",
|
||||
"都市"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 4,
|
||||
"落地页名称": "岁岁莲华",
|
||||
"素材资料夹": "ZYB-011-01 歲歲蓮華-平面",
|
||||
"封面来源文件": "竖版有字体无LOGO版.jpg",
|
||||
"是否原生3比4": false,
|
||||
"横版母版": "横版有字幕无LOGO.jpg",
|
||||
"落地页封面档": "assets/covers2/d04.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 10,
|
||||
"题材标签(示意)": [
|
||||
"古装",
|
||||
"重生"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 5,
|
||||
"落地页名称": "压抑的主妇",
|
||||
"素材资料夹": "SISR-015-压抑的主妇",
|
||||
"封面来源文件": "SISR-015-壓抑的主婦-总预告-竖版封面-母版.jpg",
|
||||
"是否原生3比4": false,
|
||||
"横版母版": "SISR-015-壓抑的主婦-总预告-横版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d05.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 10,
|
||||
"题材标签(示意)": [
|
||||
"都市",
|
||||
"情感"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 6,
|
||||
"落地页名称": "驱魔不成",
|
||||
"素材资料夹": "LBSR-007-1-驱魔不成",
|
||||
"封面来源文件": "驱魔不成-总预告-竖版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "驱魔不成-总预告-横版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d06.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 8,
|
||||
"题材标签(示意)": [
|
||||
"悬疑",
|
||||
"灵异"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 7,
|
||||
"落地页名称": "拳王,别喊我妹妹",
|
||||
"素材资料夹": "LBSR-008-1-拳王,别喊我妹妹",
|
||||
"封面来源文件": "LBSR-008-1 拳王,别喊我妹妹 總預告-豎版封面-母版.png",
|
||||
"是否原生3比4": false,
|
||||
"横版母版": "LBSR-008-1 拳王,别喊我妹妹 總預告-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d07.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 7,
|
||||
"题材标签(示意)": [
|
||||
"逆转",
|
||||
"拳击"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 8,
|
||||
"落地页名称": "双生道侣",
|
||||
"素材资料夹": "MANI-007-1 双生道侣",
|
||||
"封面来源文件": "MANI-007-1 双生道侣海報 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "MANI-007-1 双生道侣海報 EP1-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d08.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 9,
|
||||
"题材标签(示意)": [
|
||||
"修仙",
|
||||
"奇幻"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 9,
|
||||
"落地页名称": "孤注一掷之沉沦妙瓦底",
|
||||
"素材资料夹": "ZYB-005-3 孤注一掷之沉沦妙瓦底",
|
||||
"封面来源文件": "ZYB-005-3 孤注一掷之沉沦妙瓦底 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "ZYB-005-3 孤注一掷之沉沦妙瓦底 EP1-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d09.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": 10,
|
||||
"题材标签(示意)": [
|
||||
"剧情",
|
||||
"悬疑"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 10,
|
||||
"落地页名称": "PRISM:魔盗女团",
|
||||
"素材资料夹": "SISR-001 PRISM:魔盗女团",
|
||||
"封面来源文件": "PRISM:魔盗女团-竖版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "PRISM:魔盗女团-横板封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d10.jpg",
|
||||
"类型": "影集",
|
||||
"集数": 1,
|
||||
"题材标签(示意)": [
|
||||
"科幻",
|
||||
"偶像"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 11,
|
||||
"落地页名称": "琼明神女录",
|
||||
"素材资料夹": "ZYB-017-1 琼明神女录",
|
||||
"封面来源文件": "ZYB-017-1 琼明神女录EP1-豎版封面-母版(無集數).jpg",
|
||||
"是否原生3比4": false,
|
||||
"横版母版": "ZYB-017-1 琼明神女录EP1-橫版封面-母版(無集數).jpg",
|
||||
"落地页封面档": "assets/covers2/d11.jpg",
|
||||
"类型": "影集",
|
||||
"集数": 1,
|
||||
"题材标签(示意)": [
|
||||
"仙侠",
|
||||
"武侠"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 12,
|
||||
"落地页名称": "水浒淫传",
|
||||
"素材资料夹": "LBSR-004-1-水滸淫傳",
|
||||
"封面来源文件": "LBSR-004 水滸淫傳 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "LBSR-004-水滸淫傳 EP1-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d12.jpg",
|
||||
"类型": "影集",
|
||||
"集数": 2,
|
||||
"题材标签(示意)": [
|
||||
"古装",
|
||||
"武侠"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 13,
|
||||
"落地页名称": "欲镜",
|
||||
"素材资料夹": "LBSR-001-1-慾鏡",
|
||||
"封面来源文件": "慾鏡EP1直版封面3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "ZYL-001 慾鏡 總預告-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d13.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"悬疑",
|
||||
"都市"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 14,
|
||||
"落地页名称": "妻夜怪谈",
|
||||
"素材资料夹": "LBSR-003-1-妻夜怪談",
|
||||
"封面来源文件": "妻夜怪談-EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "妻夜怪談-EP1-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d14.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"悬疑",
|
||||
"灵异"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 15,
|
||||
"落地页名称": "触手乐园",
|
||||
"素材资料夹": "LBSR-009-觸手樂園",
|
||||
"封面来源文件": "LBSR-009-1 觸手樂園 總預告-豎版封面-3:4.jpg",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "LBSR-009-1 觸手樂園 總預告-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d15.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"奇幻",
|
||||
"科幻"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 16,
|
||||
"落地页名称": "末雪禁城",
|
||||
"素材资料夹": "MANI-003-1-末雪禁城",
|
||||
"封面来源文件": "末雪禁城-EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "末雪禁城-EP1-横版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d16.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"古装",
|
||||
"悬疑"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 17,
|
||||
"落地页名称": "透明冰棒",
|
||||
"素材资料夹": "SISR-003 透明冰棒",
|
||||
"封面来源文件": "透明冰棒 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "透明冰棒 EP1-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d17.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"都市",
|
||||
"奇幻"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 18,
|
||||
"落地页名称": "快门沦陷",
|
||||
"素材资料夹": "SISR-007 快门沦陷",
|
||||
"封面来源文件": "SISR-007 快门沦陷 總預告-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "SISR-007 快门沦陷 總預告-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d18.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"都市",
|
||||
"情感"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 19,
|
||||
"落地页名称": "修为尽失",
|
||||
"素材资料夹": "SISR-009 修为尽失",
|
||||
"封面来源文件": "SISR-009 修为尽失 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "SISR-009 修为尽失 EP1-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d19.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"仙侠",
|
||||
"武侠"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 20,
|
||||
"落地页名称": "玉女心经",
|
||||
"素材资料夹": "SISR-010 玉女心經",
|
||||
"封面来源文件": "SISR-010 玉女心經 EP1-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "SISR-010 玉女心經 EP1-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d20.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"武侠",
|
||||
"古装"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 21,
|
||||
"落地页名称": "谍影红颜",
|
||||
"素材资料夹": "ZYB-006-3 谍影红颜",
|
||||
"封面来源文件": "ZYB-006-3 谍影红颜 總預告-豎版封面-母版3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "ZYB-006-3 谍影红颜 總預告-橫版封面-母版.jpg",
|
||||
"落地页封面档": "assets/covers2/d21.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"剧情",
|
||||
"悬疑"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 22,
|
||||
"落地页名称": "画皮局:胭脂债",
|
||||
"素材资料夹": "ZYB-006-5 画皮局:胭脂债",
|
||||
"封面来源文件": "豎版封面三比四.jpg",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "橫版封面.jpg",
|
||||
"落地页封面档": "assets/covers2/d22.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"古装",
|
||||
"奇幻"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 23,
|
||||
"落地页名称": "真的她",
|
||||
"素材资料夹": "SISR-021 真的她",
|
||||
"封面来源文件": "SISR-021 真的她 總預告-豎版封面-3*4版.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "SISR-021 真的她 總預告-橫版封面-母版.png",
|
||||
"落地页封面档": "assets/covers2/d23.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"都市",
|
||||
"情感"
|
||||
]
|
||||
},
|
||||
{
|
||||
"序": 24,
|
||||
"落地页名称": "暧昧海滩",
|
||||
"素材资料夹": "SISR-008 暧昧海滩",
|
||||
"封面来源文件": "SISR-008 曖昧海灘 EP1-豎版封面3比4.png",
|
||||
"是否原生3比4": true,
|
||||
"横版母版": "SISR-008 曖昧海灘 EP1-橫版封面.jpg",
|
||||
"落地页封面档": "assets/covers2/d24.jpg",
|
||||
"类型": "短剧",
|
||||
"集数": "未提供(显示 连载中)",
|
||||
"题材标签(示意)": [
|
||||
"都市",
|
||||
"情感"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
| 序 | 落地页名称 | 素材资料夹 | 封面来源文件 | 原生3:4 | 横版母版 |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | 废柴杂役 | LBSR-005-1-廢材雜役 | 廢材雜役-總預告-豎版封面-母版3比4.png | 是 | 廢材雜役-總預告-橫版封面-母版.png |
|
||||
| 2 | 最强显示器 | MANI-008 最强显示器 | MANI-008 最強顯示器 EP0-豎版封面-母版3比4.png | 是 | MANI-008 最強顯示器 EP0-橫版封面-母版.png |
|
||||
| 3 | 花田里犯的错 | ZYB-005-2 花田里犯的错 | ZYB-005-2 花田里犯的错 EP1-豎版封面-母版3比4.png | 是 | ZYB-005-2 花田里犯的错 EP1-橫版封面-母版.jpg |
|
||||
| 4 | 岁岁莲华 | ZYB-011-01 歲歲蓮華-平面 | 竖版有字体无LOGO版.jpg | 否(定锚裁切) | 横版有字幕无LOGO.jpg |
|
||||
| 5 | 压抑的主妇 | SISR-015-压抑的主妇 | SISR-015-壓抑的主婦-总预告-竖版封面-母版.jpg | 否(定锚裁切) | SISR-015-壓抑的主婦-总预告-横版封面-母版.jpg |
|
||||
| 6 | 驱魔不成 | LBSR-007-1-驱魔不成 | 驱魔不成-总预告-竖版封面-母版3比4.png | 是 | 驱魔不成-总预告-横版封面-母版.png |
|
||||
| 7 | 拳王,别喊我妹妹 | LBSR-008-1-拳王,别喊我妹妹 | LBSR-008-1 拳王,别喊我妹妹 總預告-豎版封面-母版.png | 否(定锚裁切) | LBSR-008-1 拳王,别喊我妹妹 總預告-橫版封面-母版.png |
|
||||
| 8 | 双生道侣 | MANI-007-1 双生道侣 | MANI-007-1 双生道侣海報 EP1-豎版封面-母版3比4.png | 是 | MANI-007-1 双生道侣海報 EP1-橫版封面-母版.jpg |
|
||||
| 9 | 孤注一掷之沉沦妙瓦底 | ZYB-005-3 孤注一掷之沉沦妙瓦底 | ZYB-005-3 孤注一掷之沉沦妙瓦底 EP1-豎版封面-母版3比4.png | 是 | ZYB-005-3 孤注一掷之沉沦妙瓦底 EP1-橫版封面-母版.jpg |
|
||||
| 10 | PRISM:魔盗女团 | SISR-001 PRISM:魔盗女团 | PRISM:魔盗女团-竖版封面-母版3比4.png | 是 | PRISM:魔盗女团-横板封面-母版.png |
|
||||
| 11 | 琼明神女录 | ZYB-017-1 琼明神女录 | ZYB-017-1 琼明神女录EP1-豎版封面-母版(無集數).jpg | 否(定锚裁切) | ZYB-017-1 琼明神女录EP1-橫版封面-母版(無集數).jpg |
|
||||
| 12 | 水浒淫传 | LBSR-004-1-水滸淫傳 | LBSR-004 水滸淫傳 EP1-豎版封面-母版3比4.png | 是 | LBSR-004-水滸淫傳 EP1-橫版封面-母版.png |
|
||||
| 13 | 欲镜 | LBSR-001-1-慾鏡 | 慾鏡EP1直版封面3比4.png | 是 | ZYL-001 慾鏡 總預告-橫版封面-母版.jpg |
|
||||
| 14 | 妻夜怪谈 | LBSR-003-1-妻夜怪談 | 妻夜怪談-EP1-豎版封面-母版3比4.png | 是 | 妻夜怪談-EP1-橫版封面-母版.png |
|
||||
| 15 | 触手乐园 | LBSR-009-觸手樂園 | LBSR-009-1 觸手樂園 總預告-豎版封面-3:4.jpg | 是 | LBSR-009-1 觸手樂園 總預告-橫版封面-母版.jpg |
|
||||
| 16 | 末雪禁城 | MANI-003-1-末雪禁城 | 末雪禁城-EP1-豎版封面-母版3比4.png | 是 | 末雪禁城-EP1-横版封面-母版.jpg |
|
||||
| 17 | 透明冰棒 | SISR-003 透明冰棒 | 透明冰棒 EP1-豎版封面-母版3比4.png | 是 | 透明冰棒 EP1-橫版封面-母版.jpg |
|
||||
| 18 | 快门沦陷 | SISR-007 快门沦陷 | SISR-007 快门沦陷 總預告-豎版封面-母版3比4.png | 是 | SISR-007 快门沦陷 總預告-橫版封面-母版.jpg |
|
||||
| 19 | 修为尽失 | SISR-009 修为尽失 | SISR-009 修为尽失 EP1-豎版封面-母版3比4.png | 是 | SISR-009 修为尽失 EP1-橫版封面-母版.jpg |
|
||||
| 20 | 玉女心经 | SISR-010 玉女心經 | SISR-010 玉女心經 EP1-豎版封面-母版3比4.png | 是 | SISR-010 玉女心經 EP1-橫版封面-母版.png |
|
||||
| 21 | 谍影红颜 | ZYB-006-3 谍影红颜 | ZYB-006-3 谍影红颜 總預告-豎版封面-母版3比4.png | 是 | ZYB-006-3 谍影红颜 總預告-橫版封面-母版.jpg |
|
||||
| 22 | 画皮局:胭脂债 | ZYB-006-5 画皮局:胭脂债 | 豎版封面三比四.jpg | 是 | 橫版封面.jpg |
|
||||
| 23 | 真的她 | SISR-021 真的她 | SISR-021 真的她 總預告-豎版封面-3*4版.png | 是 | SISR-021 真的她 總預告-橫版封面-母版.png |
|
||||
| 24 | 暧昧海滩 | SISR-008 暧昧海滩 | SISR-008 曖昧海灘 EP1-豎版封面3比4.png | 是 | SISR-008 曖昧海灘 EP1-橫版封面.jpg |
|
||||
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 774 KiB |
|
After Width: | Height: | Size: 377 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 410 KiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 674 KiB |
|
After Width: | Height: | Size: 305 KiB |
@@ -0,0 +1,24 @@
|
||||
# 首页主打(Hero)素材 · 前端配置说明(2026-09-12)
|
||||
|
||||
轮播顺序(用户指定):01 琼明神女录 → 02 欲镜 → 03 触手乐园 → 04 PRISM:魔盗女团 → 05 废柴杂役。每部 5 个文件,命名 `NN-slug-用途.jpg`:
|
||||
|
||||
| 用途 | 文件 | 尺寸 | 用在哪 |
|
||||
|---|---|---|---|
|
||||
| 横版母版(清晰) | `NN-slug-land.jpg` | 1920×1080 | PC hero 右侧主视觉卡 600×338(object-fit: cover),母版自带金字剧名,不要再叠文字 |
|
||||
| 模糊大图 | `NN-slug-land-blur.jpg` | 1920×1080,高斯模糊 26px | PC hero 全幅氛围底图;前端自行叠遮罩(见下) |
|
||||
| 模糊压暗版 | `NN-slug-land-blur-dark.jpg` | 1920×1080 | 已把页底色 #0B0B0D 85% 混合 + 左/顶/底三层遮罩烘进去,与 Figma 稿完全一致;不想写遮罩时直接用这张 |
|
||||
| H5 顶图 | `NN-slug-port-top.jpg` | 900 宽(约 900×1040) | H5 hero 顶部 375×433 区域(object-fit: cover; object-position: top),已裁掉海报自带剧名,文字区在其下 |
|
||||
| 3:4 封面 | `NN-slug-cover.jpg` | 480×640 | 轮播缩略图 52×70 / 44×58,与作品区封面同一张 |
|
||||
|
||||
## 若用 `-land-blur.jpg` 自己叠遮罩(对齐 Figma)
|
||||
```css
|
||||
.hero{position:relative;background:#0B0B0D}
|
||||
.hero .bg{position:absolute;inset:0;background:url(01-qiongming-land-blur.jpg) center/cover;opacity:.85}
|
||||
.hero .scrim-l{position:absolute;inset:0;background:linear-gradient(90deg,#0B0B0D 0%,rgba(11,11,13,.6) 30%,rgba(11,11,13,0) 62%)}
|
||||
.hero .scrim-b{position:absolute;inset:0;background:linear-gradient(180deg,rgba(11,11,13,0) 50%,#0B0B0D 100%)}
|
||||
.hero .scrim-t{position:absolute;left:0;right:0;top:0;height:24%;background:linear-gradient(180deg,#0B0B0D 0%,rgba(11,11,13,.6) 35%,rgba(11,11,13,0) 100%)}
|
||||
```
|
||||
切换主打时:底图交叉淡入 1s,卡片 / 文字 0.5s 上浮;缩略图选中态 2px #EC237F 描边;进度线 6s 一格。
|
||||
|
||||
## 来源
|
||||
`~/Desktop/AI短劇系列平面/` 各剧的「橫版封面-母版」「豎版封面-母版」「3比4」;欲镜没有 9:16 豎版,H5 顶图取自 3:4 直版上 70%。
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
||||
|
After Width: | Height: | Size: 356 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 314 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-up"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 310 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-award"><circle cx="12" cy="8" r="7"></circle><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 325 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bookmark"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path></svg>
|
||||
|
After Width: | Height: | Size: 287 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-calendar"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
|
||||
|
After Width: | Height: | Size: 410 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 328 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 262 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 269 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-left"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 270 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 270 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-up"><polyline points="18 15 12 9 6 15"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 268 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-clock"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 304 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
|
||||
|
After Width: | Height: | Size: 370 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
|
||||
|
After Width: | Height: | Size: 388 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-eye"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
||||
|
After Width: | Height: | Size: 316 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-film"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><line x1="7" y1="2" x2="7" y2="22"></line><line x1="17" y1="2" x2="17" y2="22"></line><line x1="2" y1="12" x2="22" y2="12"></line><line x1="2" y1="7" x2="7" y2="7"></line><line x1="2" y1="17" x2="7" y2="17"></line><line x1="17" y1="17" x2="22" y2="17"></line><line x1="17" y1="7" x2="22" y2="7"></line></svg>
|
||||
|
After Width: | Height: | Size: 586 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-filter"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
|
||||
|
After Width: | Height: | Size: 290 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
||||
|
After Width: | Height: | Size: 409 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-grid"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
|
||||
|
After Width: | Height: | Size: 404 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-headphones"><path d="M3 18v-6a9 9 0 0 1 18 0v6"></path><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"></path></svg>
|
||||
|
After Width: | Height: | Size: 395 B |