初始提交:91 短剧落地页项目

另外新增两版「糖果短剧」logo 素材(candy_logo、candy_logo_sexy),
参考 pc/logo.webp 的构图制作,暂未在页面中引用。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
谢宇宁
2026-10-08 22:19:01 +07:00
co-authored by Claude Opus 5
commit 3d9f1a7914
298 changed files with 25186 additions and 0 deletions
+12
View File
@@ -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。
+42
View File
@@ -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` 选择器前缀的除外
- 始终用中文回复
+73
View File
@@ -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` |
+72
View File
@@ -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%;
```
+97
View File
@@ -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 拒绝。
+196
View File
@@ -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()` |
+8
View File
@@ -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"]
+6
View File
@@ -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"]
+25
View File
@@ -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,
},
},
],
};
+91
View File
@@ -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/:软链会被当成文件,斜杠规则忽略不掉)
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
+1
View File
@@ -0,0 +1 @@
12
+1
View File
@@ -0,0 +1 @@
# node 版本使用 12.18.1
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
};
Executable
+12
View File
@@ -0,0 +1,12 @@
# sh 执行打包到正式服上
npm run build
zip -q -r dist.zip dist
scp ./dist.zip server@116.212.120.2:/home/server/91porn/ldy/pro
ssh server@116.212.120.2 "/home/server/91porn/ldy/pro/dist.sh"
if [ $? -eq 0 ];then
echo “更新成功!“
else
echo "更新失败!"
fi
rm dist.zip
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
preset: "@vue/cli-plugin-unit-jest"
};
+19969
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
{
"name": "landpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"test-build": "vue-cli-service build --mode test",
"lint": "eslint --fix --ext .js,.vue src",
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"autofit.js": "^2.0.5",
"axios": "^0.19.2",
"core-js": "^3.6.5",
"element-ui": "^2.13.2",
"lib-flexible": "^0.3.2",
"moment": "^2.26.0",
"postcss-plugin-px2rem": "^0.8.1",
"postcss-px2rem": "^0.3.0",
"qrcode": "^1.4.4",
"register-service-worker": "^1.7.1",
"swiper": "^5.4.5",
"vant": "^2.8.4",
"vue": "^2.6.11",
"vue-analytics": "^5.22.1",
"vue-awesome-swiper": "^4.1.1",
"vue-clipboard2": "^0.3.1",
"vue-lottery": "^1.0.3",
"vue-router": "^3.3.1",
"vue-touch-hotfix": "^2.0.0-beta.4",
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.4.1",
"@vue/cli-plugin-eslint": "^4.4.1",
"@vue/cli-plugin-unit-jest": "^4.4.1",
"@vue/cli-service": "^4.4.1",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/test-utils": "1.0.0-beta.31",
"babel-eslint": "^10.0.3",
"babel-plugin-component": "^1.1.1",
"compression-webpack-plugin": "^1.1.12",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^6.1.2",
"node-sass": "^4.14.1",
"prettier": "^1.19.1",
"sass": "~1.26.5",
"sass-loader": "^8.0.2",
"vue-cli-plugin-element": "^1.0.1",
"vue-template-compiler": "^2.6.11",
"webpack-bundle-analyzer": "^4.4.0"
}
}
+196
View File
@@ -0,0 +1,196 @@
---
项目名称: 91短剧 H5 客户端 # 与 DMP 或内部命名一致时请自行替换
一级部门: 北斗 # 请填写一级部门名称
二级部门: 瑞升 # 请填写二级部门名称,无则留空
DMP编码: JHA-214 # 无则留空
项目类型: H5
# 可选值:
# H5 / APP / PC / 服务 / 组件 / 工具 / AI / 中间件
开发语言: Node
# 可选值:
# GO / Java / PHP / Node
项目负责人: DN3634,DN8161 # 员工编号,多个用逗号隔开,这里的负责是这套代码的负责人
运维负责人: DN9460,DN9651,DN4792 # 负责部署、监控、K8S、发布、证书等
产品负责人: YY1145,YY1208 # 项目产品经理
项目成员: DN3634,DN8161
项目状态: 开发中
# 可选值:
# 开发中 / 维护中 / 已停止 / 已归档
项目级别: 中
# 可选值:
# 高 / 中 / 低
核心分支: main
代码仓库:
- https://bd-git.jsyyds.com/ruisheng/jha-214-91/frontend/91dj-landpage-client
最后审计人:
最后审计时间:
告警群:
备注:
- `package.json` 中 `name` 为 `landpage`,仓库/目录名为 `91dj-landpage-client`。
- 业务 `appId` 为 **204**(见 `src/views/land/index.vue`),埋点 `appId` 为 **JHA-204**(见 `public/index.html` 与 `src/utils/eventTracker.js`)。
- 接口路径前缀为 **`/plm`**,开发环境由 `vue.config.js` 代理;选线候选域名见各环境 `.env.*` 中的 `VUE_APP_BASE_HOSTS`(JSON 数组),勿将含密钥、内网地址的未脱敏内容对外发布。
---
# 1. 项目说明
本仓库为面向 **移动端 H5 浏览器**(含 PC 落地页展示)的 **Vue 2 + Vue CLI 4** 单页应用,语言为 **JavaScript**(无 TypeScript)。UI 同时使用 **Vant 2** 与 **Element UI 2**,状态管理为 **Vuex 3**(当前 store 为空壳,业务数据主要在页面组件内维护),路由为 **Vue Router 3**,模式为 **`history`(HTML5 History)**(见 `src/router/index.js`)。入口为 `src/main.js`,挂载 Router、Vuex、Vant、Element、剪贴板、Swiper、Google Analytics(`vue-analytics`)等。
**业务定位:** 91pron **App 下载落地页**(Landing Page),单路由 `/` 对应 `src/views/land/index.vue`。根据设备类型(`userAgent`)与模板版本(`version`: `A` / `B`)切换子组件:
| 场景 | 组件 |
|------|------|
| 移动端 + 版本 A | `mobile1.vue` |
| 移动端 + 版本 B | `mobile2.vue` |
| PC 端 | `pc.vue`(A/B 共用) |
| 特定 PC URL | `tips.vue` |
| 接口失败选线 | `selectLinePopup.vue` |
子组件通过 props 接收 `os`、`configs`、`switchData` 等,通过 `$emit` 向上传递 `getApkInfo`、`recordClick` 等事件。
**启动流程(`land/index.vue`):**
1. 解析 URL 查询参数(`dc`、`ch`、`pc` 等)并写入 sessionStorage。
2. 调用 `getTemplate`(`/plm/okn/ijb/nuS0EDXSczKgf3Xh`)拉取按钮/下载配置(`switchData`、`configs`、各平台下载链接)。
3. 上报落地页展示埋点 `landingPageView`(`eventTracker.js` → `window.tracker` / LandingSDK)。
4. 用户点击下载时:`recordClick` 上报点击并调用 `getApkInfo`;`handleGetApkInfo` 根据终端类型跳转 iOS/Android 下载链接。
5. 接口失败时弹出 **选线弹窗**,用户选择后切换 `axios.defaults.baseURL` 并重试。
**技术栈要点:**
- 构建:**Vue CLI 4**(`vue.config.js`),生产使用 **compression-webpack-plugin** 生成 **gzip** 产物。
- 样式:**Sass/SCSS**(`node-sass` + `sass-loader`),`element-variables.scss` 定制 Element 主题。
- 适配:**lib-flexible** + **postcss-plugin-px2rem**(`rootValue: 37.5`,设计稿基准 375px);带 **`pcm`** 选择器前缀的样式不转 rem(PC 端)。
- 网络:**axios** 封装于 `src/utils/request.js`,默认 `baseURL: '/'`,业务码 `200` 视为成功。
- 埋点:`public/index.html` 异步加载 **`/sdk/landing-sdk-v1.1.1.js`**(LandingSDK),`src/utils/eventTracker.js` 封装 `landing_page_view` / `landing_page_click` 事件及设备信息解析。
- 其他:**qrcode** 生成 PC 扫码图、**vue-clipboard2** 复制渠道参数、**moment**、**swiper**、**vue-analytics**(UA-165940626-2)、**register-service-worker**(PWA 配置于 `vue.config.js`)。
**Node 版本:** 仓库 `README.md` 建议使用 **12.18.1**。
**常用脚本(`package.json`):**
| 命令 | 说明 |
|------|------|
| `npm run dev` | 本地开发(`vue-cli-service serve`) |
| `npm run build` | 生产构建 |
| `npm run test-build` | 测试环境构建(`--mode test`) |
| `npm run lint` | ESLint(`src` 下 `.js` / `.vue`,带 `--fix`) |
| `npm run test:unit` | Jest 单元测试 |
**开发环境代理(`vue.config.js`):**
| 路径 | 说明 |
|------|------|
| `/plm` | 业务接口代理(`target` 以文件内配置为准) |
| `/dcserv` | 代理至 `https://api.shuifeng.cc`(调试用途) |
**项目目录结构(核心):**
```
91pron-landpage-client/
├── public/
│ ├── index.html # 入口 HTML,含 LandingSDK 与 SEO meta
│ └── sdk/landing-sdk-v1.1.1.js
├── src/
│ ├── main.js # 入口:Router、Vuex、Vant、Element、Analytics 等
│ ├── App.vue
│ ├── router/
│ │ └── index.js # 单路由 History 模式
│ ├── store/
│ │ └── index.js # Vuex(当前无业务 state)
│ ├── views/land/
│ │ ├── index.vue # 落地页主入口:设备判断、选线、下载逻辑
│ │ └── components/ # mobile1 / mobile2 / pc / pc2 / tips / selectLinePopup
│ ├── api/ # getApkInfo、getTemplate、getIp、getCustomerServiceUrl
│ ├── utils/ # request、eventTracker、userAgent 等
│ ├── plugins/ # flexible、element、scale_no
│ └── assets/ # 样式、各模板图片(mobile1/mobile2/pc/pc2)
├── vue.config.js
├── .env.development / .env.production
├── build.sh # 正式环境打包发布脚本(示例)
├── test_build.sh # 测试环境打包发布脚本(示例)
└── project.md # 本文件
```
**接口与选线:**
- 业务接口统一走 **`/plm/okn/ijb/...`** 路径(见 `src/api/`),由网关或 devServer 反代。
- **`VUE_APP_BASE_HOSTS`**:各环境 `.env.*` 中为 **JSON 数组字符串**,供选线弹窗展示候选 API 域名;用户选择后写入 `axios.defaults.baseURL` 并重试 `getTemplate`。
- 生产部署需由 **Nginx / 网关** 将 **`/plm`** 转发至实际业务后端;History 模式需配置 **单页回退**(`try_files` → `index.html`)。
---
# 2. 基础设施与中间件
本仓库为 **纯前端工程**,不内置后端;运行时依赖:
- **业务 API**:`/plm` 路径下的配置与下载上报接口;静态部署时需保证 **`/plm` 反代** 与 HTTPS 证书策略一致。
- **选线域名**:`VUE_APP_BASE_HOSTS` 中的候选域名需可达,否则用户只能通过选线弹窗手动切换。
- **LandingSDK**:`public/sdk/landing-sdk-v1.1.1.js`,用于埋点上报(`window.tracker` / `LandingSDK.init`)。
- **静态资源托管**:`npm run build` 产物在 **`dist/`**,由 CDN 或 Web 服务器提供。
- **第三方**:Google Analytics(`UA-165940626-2`)、可选外网 IP 查询(`api.ipify.org`,`getIp` 中,当前主流程未启用)。
---
# 3. 运维部署
**构建产物:** 执行 `npm run build` 或 `npm run test-build` 后,在 **`dist/`** 生成静态资源;可能同时生成 **`.gz`**(依赖服务器是否启用 `gzip_static` 或等价能力)。
**推荐发布流程(示例):**
1. 使用约定 **Node** 版本(建议 12.18.1)安装依赖:`npm ci` 或 `npm install`。
2. 确认各环境 **`.env.*`** 中 `VUE_APP_BASE_HOSTS`、`VUE_APP_BASE_API` 等与当前发布目标一致。
3. 执行 `npm run build` 或 `npm run test-build`。
4. 将 **`dist/`** 同步至静态服务器;确认 **History 路由回退** 与 **`/plm` 反代**。
5. 仓库内 **`build.sh`**(正式)、**`test_build.sh`**(测试)为示例流程(打包 zip → scp → 远程 `dist.sh`),**主机与路径以实际运维规范为准**,避免泄露账号与内网信息。
**环境变量说明(摘录,完整以 `.env.*` 为准):**
| 变量 | 作用 |
|------|------|
| `ENV` | 环境标识(`development` / `production`) |
| `VUE_APP_BASE_API` | API 根路径前缀(生产多为 `/api`) |
| `VUE_APP_APP_API` | 应用 API 前缀(开发环境 `/api`) |
| `VUE_APP_WEB_API` | Web API 前缀(开发环境可为空) |
| `VUE_APP_BASE_HOSTS` | 选线候选 API 域名列表(JSON 数组字符串) |
---
# 4. 风险说明
- **强依赖网关反代**:生产若 `/plm` 代理错误,**配置拉取与下载上报将不可用**;History 模式服务器配置错误会导致 **深链 404**。
- **选线失败**:`getTemplate` 请求失败时弹出选线弹窗;若所有候选域名不可用,用户可能无法获取下载配置。
- **埋点依赖 SDK**:`LandingSDK` 未加载完成前,`window.tracker` 使用队列缓冲;SDK 加载失败会导致埋点丢失。
- **渠道参数**:下载与剪贴板逻辑依赖 URL 参数(`dc`、`ch`、`pc`、`tid` 等),参数缺失或格式不符可能影响跳转与统计。
- **配置敏感信息**:`.env`、选线域名、部署脚本中的主机信息 **勿提交到公开仓库**;供应链上锁定 `package-lock.json` 并定期审计依赖。
---
# 5. 历史事故(可选)
- 暂无(请运维/研发后续补充)
---
# 6. 交接说明(可选)
- 落地页主逻辑集中在 **`src/views/land/index.vue`**;新增模板时参考 `mobile1` / `mobile2` / `pc` 组件模式,并在 `index.vue` 中注册与切换。
- 子组件规范见 **`.cursor/rules/vue-component.mdc`**;样式与 px2rem 见 **`.cursor/rules/scss-styling.mdc`**。
- 接口封装在 **`src/api/`**;HTTP 请求统一走 **`src/utils/request.js`**。
- 埋点上报使用 **`src/utils/eventTracker.js`** 的 `landingPageView` / `landingPageClick`,勿绕过 SDK 直接上报。
- Figma 转落地页工作流见 **`.cursor/skills/figma-to-landpage/SKILL.md`**。
- **请勿将含密钥、生产内网地址的 `.env` 或未脱敏部署脚本提交至对外可见仓库**;YAML 头部元数据若与真实组织不一致,请在内部系统中核对后修改。
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

+99
View File
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="renderer" content="webkit" />
<meta name="author" content="ijh-615" />
<meta name="theme-color" content="rgb(0,0,0)" />
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no, viewport-fit=cover" />
<meta name="screen-orientation" content="portrait" />
<meta name="x5-orientation" content="portrait" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title>91短剧 APP下载 | 国产大神成人短剧平台 百万资源免费</title>
<meta name="description" content="91大神 成人短剧 AI短剧 国产情侣 国产原创 真实自拍 反差婊 性奴母狗 极品学妹 童颜巨乳 宿舍自慰 偷情少妇 学生泄密 探花精选" />
<meta name="keywords" content="91大神 成人短剧 AI短剧 国产情侣 国产原创 真实自拍 反差婊 性奴母狗 极品学妹 童颜巨乳 宿舍自慰 偷情少妇 学生泄密 探花精选" />
<!-- <meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />-->
<!-- <meta name="rating" content="adult" />-->
<!-- <meta name="robots" content="index, follow" />-->
<meta property="og:title" content="91短剧 APP下载 | 国产大神成人短剧平台 百万资源免费" />
<meta property="og:site_name" content="91短剧" />
<meta property="og:type" content="article" />
<!-- <meta property="og:url" content="https://91porn01.cc" />-->
<!-- <meta property="og:image" content="https://91porn01.cc/favicon.ico" />-->
<meta property="og:description" content="91大神 成人短剧 AI短剧 国产情侣 国产原创 真实自拍 反差婊 性奴母狗 极品学妹 童颜巨乳 宿舍自慰 偷情少妇 学生泄密 探花精选" />
<meta name="twitter:title" content="91短剧 APP下载 | 国产大神成人短剧平台 百万资源免费" />
<meta name="twitter:description" content="91大神 成人短剧 AI短剧 国产情侣 国产原创 真实自拍 反差婊 性奴母狗 极品学妹 童颜巨乳 宿舍自慰 偷情少妇 学生泄密 探花精选" />
<meta name="twitter:card" content="summary" />
<!-- <meta name="twitter:image" content="https://91porn01.cc/favicon.ico" />-->
<meta name="twitter:site" content="91短剧" />
<script>
(function(i, s, o, g, r) {
i[r] =
i[r] ||
function() {
var args = Array.prototype.slice.call(arguments);
return new Promise(function(resolve, reject) {
(i[r].q = i[r].q || []).push({
args: args,
resolve: resolve,
reject: reject,
});
});
};
i[r].l = 1 * new Date();
var a = s.createElement(o);
var m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = "/sdk/landing-sdk-v1.1.1.js";
m.parentNode.insertBefore(a, m);
})(window, document, "script", "/sdk/landing-sdk-v1.1.1.js", "tracker");
var config = {
appId: "JHA-214",
domain: "",
channel: (function() {
var params = new URLSearchParams(window.location.search);
return params.get("dc") || params.get("ch") || params.get("pc") || "";
})(),
};
var timer = setInterval(function() {
if (window.LandingSDK) {
clearInterval(timer);
window.LandingSDK.init(config);
var queued = (window.tracker && window.tracker.q) || [];
var real = window.LandingSDK.track;
real.q = [];
window.tracker = real;
queued.forEach(function(item) {
Promise.resolve()
.then(function() {
return real.apply(null, item.args);
})
.then(item.resolve, item.reject);
});
queued.length = 0;
}
}, 30);
</script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
robots.txt
User-agent: *
Disallow: /
View File
File diff suppressed because one or more lines are too long
+48
View File
@@ -0,0 +1,48 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "app",
components: {},
created() {}
};
</script>
<style>
@media screen and (min-width: 620px) {
/* pc */
html,
body,
#app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
max-height: 100vh;
overflow: hidden !important;
background-color: black;
}
}
@media screen and (max-width: 620px) {
/* 手机 */
html,
body,
#app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
max-height: 100vh;
overflow: auto !important;
background-color: black;
}
}
* {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
</style>
+14
View File
@@ -0,0 +1,14 @@
/*
* @Author: 王枫叶
* @Date: 2020-07-20 20:48:00
* @LastEditors: 王枫叶
* @LastEditTime: 2020-10-05 17:23:15
*/
import request from "@/utils/request";
export default data =>
request({
method: "post",
url: "/plm/okn/ijb/vl0gpuBNWklJwcv1",
data
});
+8
View File
@@ -0,0 +1,8 @@
import request from "@/utils/request";
export default data =>
request({
method: "post",
url: "/plm/okn/ijb/fsdfrererdfdf",
data,
});
+8
View File
@@ -0,0 +1,8 @@
import request from "@/utils/request";
export default params =>
request({
method: "get",
url: "https://api.ipify.org/?format=json",
params
});
+14
View File
@@ -0,0 +1,14 @@
/*
* @Author: 王枫叶
* @Date: 2020-08-10 22:55:30
* @LastEditors: 王枫叶
* @LastEditTime: 2020-10-28 22:55:14
*/
import request from "@/utils/request";
export default data =>
request({
method: "post",
url: "/plm/okn/ijb/nuS0EDXSczKgf3Xh",
data
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="&#229;&#149;&#134;&#229;&#138;&#161;&#229;&#144;&#136;&#228;&#189;&#156;">
<g id="&#231;&#159;&#169;&#229;&#189;&#162; 2636">
</g>
<path id="&#231;&#159;&#169;&#229;&#189;&#162; 2554" d="M10.1438 6.78433L17.74 14.3805L12.5269 19.5936C12.3812 19.7393 12.1835 19.8212 11.9774 19.8212C11.7713 19.8212 11.5736 19.7393 11.4278 19.5936L4.38121 12.547C3.61704 11.7828 3.18773 10.7463 3.18773 9.66564C3.18773 8.58494 3.61704 7.5485 4.38121 6.78433C5.14538 6.02016 6.18182 5.59086 7.26252 5.59086C8.34322 5.59086 9.37966 6.02016 10.1438 6.78433Z" fill="white"/>
<path id="&#231;&#159;&#169;&#229;&#189;&#162; 2555" opacity="0.8" d="M19.6187 12.5019L17.7401 14.3805L11.9775 8.61791L13.8561 6.73929C14.6202 5.97512 15.6567 5.54581 16.7374 5.54582C17.8181 5.54582 18.8545 5.97512 19.6187 6.73929C20.3829 7.50346 20.8122 8.5399 20.8122 9.6206C20.8122 10.7013 20.3829 11.7377 19.6187 12.5019Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="&#230;&#184;&#160;&#233;&#129;&#147;&#229;&#144;&#136;&#228;&#189;&#156;">
<g id="&#231;&#159;&#169;&#229;&#189;&#162; 2636">
</g>
<g id="&#229;&#155;&#190;&#229;&#177;&#130;_1">
<path id="Subtract" d="M11.8604 3C15.5711 3 18.6666 5.5284 19.4766 8.91895C19.4883 8.91894 19.4991 8.91699 19.5117 8.91699V8.91797C20.3386 8.91799 21.0088 9.5619 21.0088 10.3555V13.1934C21.0086 13.7901 20.6299 14.3003 20.0918 14.5186C19.4822 17.7713 16.5602 19.8655 12.917 20.1514C12.7502 20.6418 12.2695 20.9979 11.6943 20.998C10.9828 20.998 10.4043 20.4531 10.4043 19.7812C10.4045 19.1086 10.9829 18.5645 11.6943 18.5645C11.9479 18.5611 12.197 18.6321 12.4102 18.7695C12.6235 18.9072 12.792 19.1052 12.8936 19.3379C16.0408 19.0763 18.5822 17.3518 19.208 14.5986C18.8176 14.5211 18.4821 14.2995 18.2666 13.9912C17.7363 15.591 16.5888 16.9216 15.0918 17.7188L15.0303 17.7529C15.0215 17.7556 15.0134 17.7578 15.0029 17.7578C14.993 17.7578 14.9835 17.7556 14.9727 17.7529L14.9365 17.7363C14.9042 17.7156 14.8828 17.6811 14.8828 17.6406C14.8828 17.6245 14.886 17.6084 14.8926 17.5938C14.8993 17.579 14.9097 17.5654 14.9219 17.5547C16.4013 16.2455 16.4914 14.9668 16.3291 13.0986C16.1866 11.4637 15.1979 9.91359 14.6992 9.21289L14.6641 9.23535C14.6343 9.19748 14.5904 9.17188 14.5381 9.17188C14.5017 9.17175 14.4665 9.1848 14.4385 9.20801C14.4106 9.2312 14.3913 9.26317 14.3848 9.29883C14.3325 9.78309 14.2191 10.1192 14.1406 10.2852C13.8043 10.9954 13.2542 11.4218 12.4502 11.5635C11.185 11.7871 9.92538 12.0472 8.66016 12.2744C7.8721 12.4161 7.19452 13.2737 7.32617 14.1475C7.62741 16.1318 9.01595 17.5538 9.40039 17.8301C9.37976 17.8158 9.35516 17.808 9.33008 17.8076C9.3049 17.8078 9.27604 17.8349 9.25 17.8672C9.22202 17.903 9.1969 17.9412 9.17578 17.9814C7.51485 17.2798 6.20333 15.9511 5.55859 14.3027C5.2917 14.621 4.89096 14.829 4.43652 14.8291C3.63044 14.8291 2.97871 14.1915 2.97852 13.4053V10.5986C2.97852 9.89794 3.49793 9.31826 4.17969 9.19922C4.88126 5.66974 8.04777 3.00023 11.8604 3ZM14.2168 15.4873C14.2458 15.4995 14.269 15.5222 14.2822 15.5508C14.2954 15.5797 14.2971 15.6134 14.2871 15.6436C14.0075 16.4755 13.2488 17.0798 12.3555 17.1836C12.2626 17.1935 12.1692 17.1992 12.0781 17.1992C11.6858 17.199 11.2999 17.1002 10.9551 16.9131C10.6104 16.7261 10.3176 16.4565 10.1035 16.1279C10.0869 16.1018 10.08 16.0697 10.085 16.0391C10.09 16.0087 10.1061 15.9814 10.1299 15.9619C10.1544 15.9422 10.1853 15.9314 10.2168 15.9316C10.2484 15.932 10.2795 15.9426 10.3037 15.9629C10.3073 15.9665 10.7395 16.3203 11.708 16.3203C11.899 16.3203 12.1004 16.3063 12.3076 16.2793C13.6889 16.0989 14.0442 15.5436 14.0479 15.5391C14.065 15.5119 14.0914 15.492 14.1221 15.4824C14.153 15.4728 14.1871 15.4744 14.2168 15.4873ZM11.8604 4.24414C8.62586 4.24437 5.94426 6.53515 5.40137 9.54785C5.45547 9.59655 5.51268 9.64526 5.55957 9.70117C6.51914 7.22947 8.97266 5.4668 11.8574 5.4668C14.7539 5.46688 17.2159 7.24281 18.167 9.73047C18.2074 9.65001 18.2599 9.57648 18.3145 9.50488C17.7535 6.51272 15.0798 4.24414 11.8604 4.24414Z" fill="white"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

+6
View File
@@ -0,0 +1,6 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="10.1983" height="10.1983" viewBox="0 0 10.1983 10.1983" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="x">
<path id="Vector 434" d="M7.98311 2.21434L2.21408 7.98337" stroke="white" stroke-width="1.27479" stroke-linecap="round"/>
<path id="Vector 435" d="M7.98415 7.98263L2.21512 2.2136" stroke="white" stroke-width="1.27479" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Some files were not shown because too many files have changed in this diff Show More