hookehuyr

feat: 添加欢迎页开发计划和通用七牛云上传工具

新增欢迎页功能的详细开发计划文档,包括头脑风暴、架构设计和实现步骤
创建通用七牛云上传工具脚本,支持单文件上传和批量上传
添加视频背景组件和欢迎页路由配置的基础框架
实现首次访问检测逻辑,使用localStorage记录用户访问状态
# 欢迎页功能 - 头脑风暴与设计探索
## 项目背景
为 mlaj 平台设计一个欢迎页,作为用户首次进入时的引导页面,展示核心功能入口。
**核心需求:**
- 视频背景循环播放(星空宇宙主题)
- 悬浮的功能入口图标 + 文字介绍
- 持续循环的缩放位移动效
- 首次进入检测,非首次直接跳转主页
---
## 需求探索过程
### 问题 1: 欢迎页的主要目标?
**选项:**
- 品牌展示 - 营造高端/科技感的品牌印象
- 功能引导 - 清晰展示核心功能入口
- 新手引导 - 教育用户如何使用平台
- 混合型 - 品牌展示 + 功能引导 ✅
**决策:** 选择**混合型**,既要有视觉冲击力又要提供实用功能入口。
---
### 问题 2: 视频背景的实现方式?
**选项:**
- 原生 `<video>` 标签
- 简化版组件(参考 StarryBackground)✅
- 页面内直接实现
**决策:** 选择**简化版组件**,理由:
- 组件化便于复用和维护
- 移除 Canvas 星星特效,保留视频播放逻辑
- 支持 Props 配置,灵活性高
---
### 问题 3: 如何判断用户是否首次进入?
**选项:**
- localStorage 标志 ✅
- 后端接口判断
- URL 参数控制
**决策:** 选择 **localStorage 标志**,理由:
- 简单高效,无需额外请求
- 符合前端存储特性
- 开发调试方便(可手动清除)
**权衡:**
- 缺点: 清除缓存后会再次显示
- 解决方案: 文档说明这是预期行为,未来可升级为后端接口
---
### 问题 4: 图标和文字的动效风格?
**选项:**
- 轻量动效(3秒入场 + 悬停交互)
- 持续循环(呼吸/缩放动画)✅
- 一次性入场
**决策:** 选择**持续循环**,理由:
- 符合"星空宇宙"的神秘感主题
- 视觉冲击力更强,吸引用户注意力
- 不同入口设置不同延迟,形成错落感
---
## 架构设计探索
### 整体架构方案
**方案 A: 单页面组件**
```
WelcomePage.vue
├── 直接写 video 标签
└── 内联功能入口
```
- 优点: 简单直接
- 缺点: 代码耦合,难以复用
**方案 B: 组件化设计 ✅**
```
WelcomePage.vue
├── VideoBackground.vue (通用组件)
└── WelcomeContent.vue
└── WelcomeEntryItem.vue
```
- 优点: 组件复用、职责清晰、易维护
- 缺点: 文件稍多
**决策:** 方案 B,组件化设计
---
### 页面层级结构
```
WelcomePage (视图容器)
├── VideoBackground (简化版背景组件)
│ └── <video> 循环播放,覆盖全屏
└── WelcomeContent (内容悬浮层)
├── [待定] Logo/标题区 (等设计稿确认)
├── 中部: 功能入口网格 (2-3列)
└── [待定] CTA 按钮区 (等设计稿确认)
```
**待确认部分:** 顶部和底部元素需等设计稿确定后再规划
---
### 首次访问控制逻辑
**路由守卫方案:**
```javascript
// src/router/guards.js
const HAS_VISITED_WELCOME = 'has_visited_welcome'
router.beforeEach((to, from, next) => {
// 欢迎页功能开关
if (import.meta.env.VITE_WELCOME_PAGE_ENABLED !== 'true') {
return next()
}
// 首次访问检测
if (to.path !== '/welcome' &&
!localStorage.getItem(HAS_VISITED_WELCOME)) {
localStorage.setItem(HAS_VISITED_WELCOME, 'true')
return next({
path: '/welcome',
query: { redirect: to.fullPath }
})
}
next()
})
```
**调试功能:**
- URL 参数 `?reset_welcome=true` 重置标志位
- 控制台 `window.resetWelcomeFlag()` 快捷方法
---
## 核心组件设计
### VideoBackground 组件
**位置:** `src/components/effects/VideoBackground.vue`
**核心特性:**
- 简化版设计,移除 Canvas 特效
- 原生 `<video>` 标签实现
- 移动端和 PC 端自适应
- 可配置视频源、播放速度、覆盖模式
**Props 设计:**
```javascript
{
videoSrc: { type: String, required: true },
poster: { type: String },
autoplay: { type: Boolean, default: true },
loop: { type: Boolean, default: true },
muted: { type: Boolean, default: true },
objectFit: { type: String, default: 'cover' }
}
```
**关键实现点:**
- 使用 `object-fit: cover` 确保全屏覆盖不变形
- 添加 `playsinline` 支持 iOS 内联播放
- 监听 `canplay` 事件移除 loading 状态
- `z-index: -1` 确保在内容层级下方
---
### WelcomeContent 组件
**位置:** `src/components/welcome/WelcomeContent.vue`
**核心布局:** 功能入口网格(2-3列)
**入口项动效设计(持续循环):**
- CSS `@keyframes` 实现呼吸缩放效果
- 动画参数: `scale(1.0) → scale(1.08) → scale(1.0)`
- 周期: 2-3秒
- 不同入口项设置不同延迟(0s, 0.5s, 1s...)形成错落感
- Hover 时加速或高亮
**CSS 动画示例:**
```less
@keyframes breathe {
0%, 100% {
transform: scale(1);
opacity: 0.9;
}
50% {
transform: scale(1.08);
opacity: 1;
}
}
.entry-item {
animation: breathe 2.5s ease-in-out infinite;
animation-delay: calc(var(--index) * 0.3s);
&:hover {
animation-duration: 1s; // 加速
transform: scale(1.1);
}
}
```
---
## 七牛云上传工具设计
### 现有方案分析
**从 deploy.sh 提取的关键逻辑:**
- 使用 `qshell qupload` 命令批量上传
- 依赖配置文件 `~/.qshell/stdj_upload.conf`
- 需要预先安装 qshell 工具
**发现的问题:**
- 用户提到"需要挂代理才能访问"
- 可能是七牛云 API 域名在某些网络环境下被限制
- 需要设置 `HTTP_PROXY`/`HTTPS_PROXY` 环境变量
---
### 通用化设计探索
**原始方案问题:**
- 硬编码了欢迎页特定路径(`/docs/plan/26.1.28-欢迎页开发计划/`
- 配置文件使用项目外的 `~/.qshell/stdj_upload.conf`
- 无法作为通用工具复用
**改进方案:**
1. **项目内配置**
- 配置文件位置: `scripts/qiniu/configs/`
- 账户信息: `scripts/qiniu/account.json`(不入库)
- 配置模板: `scripts/qiniu/templates/*.template`
2. **脚本参数化**
- 支持单文件上传: `./script <local_file> <remote_path>`
- 支持批量上传: `./script <config_file>`
- 环境变量控制代理: `USE_PROXY=true PROXY_HOST=127.0.0.1:7890`
3. **统一账户管理**
- 首次使用时初始化账户
- 账户信息本地加密存储
- 支持多项目共享同一账户
---
### 使用场景设计
**场景 1: 上传欢迎页视频**
```bash
./scripts/upload-to-qiniu.sh video/bg.mp4 mlaj/video/welcome-bg.mp4
```
**场景 2: 批量上传图片**
```bash
# 复制模板并修改配置
cp scripts/qiniu/templates/image-upload.conf.template \
scripts/qiniu/configs/welcome-images.conf
# 执行上传
./scripts/upload-to-qiniu.sh scripts/qiniu/configs/welcome-images.conf
```
**场景 3: 使用代理上传**
```bash
USE_PROXY=true PROXY_HOST="127.0.0.1:7890" \
./scripts/upload-to-qiniu.sh local.mp4 mlaj/video/remote.mp4
```
---
## 风险识别与应对
### 技术风险
**1. 视频播放兼容性**
- iOS Safari 可能禁止自动播放
- 部分浏览器不支持 `playsinline`
**应对:**
```vue
<video
autoplay
loop
muted
playsinline
webkit-playsinline
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
>
```
- 视频加载失败时使用静态背景图降级
---
**2. 首次访问标志位问题**
- 清除 localStorage 后会再次显示
- 不同浏览器无法共享标志位
**应对:**
- 文档说明这是预期行为
- 提供调试工具方便开发
- 未来可升级为后端接口
---
**3. 视频加载性能**
- 文件过大导致加载缓慢
- 弱网环境体验差
**应对:**
- 限制文件大小 < 10MB
- 提供封面图在视频加载前显示
- 添加加载进度提示
- 考虑视频分段加载或 M3U8
---
**4. 七牛云代理问题**
- 需要挂代理才能访问
**应对:**
- 脚本支持 `USE_PROXY` 环境变量
- 提供诊断脚本测试连通性
- 文档说明代理配置方法
---
### 业务风险
**1. 功能入口待确认**
- 设计稿未确定具体入口
**应对:**
- 先实现框架和核心逻辑
- 入口配置化,后续易于调整
- 使用 Mock 数据开发
---
**2. 动效性能影响**
- 持续动画可能消耗 CPU/电量
**应对:**
- 使用 CSS 动画而非 JS(性能更好)
- 提供环境变量控制动画开关
- 低端设备检测后降级为静态效果
---
## 待确认事项
根据需求文档,以下事项需要确认:
1.**背景视频文件** - `video/?.mp4` 文件名未知
2.**页面效果图** - `img/` 文件夹为空
3.**功能入口列表** - 具体跳转地址未知
4.**页面布局细节** - 顶部/底部是否需要元素(Logo、标语、按钮等)
**建议:** 先完成技术框架和上传工具,等设计稿确认后再填充内容。
---
## 关键决策总结
| 决策点 | 选择 | 理由 |
|--------|------|------|
| 页面目标 | 混合型 | 品牌展示 + 功能引导 |
| 视频实现 | 简化版组件 | 组件化,便于复用 |
| 首次检测 | localStorage | 简单高效 |
| 动效风格 | 持续循环 | 符合主题,视觉冲击力强 |
| 架构设计 | 组件化 | 职责清晰,易维护 |
| 上传工具 | 通用化 | 支持多种场景复用 |
---
## 下一步行动
1. ✅ 完成头脑风暴和设计探索
2. ⏳ 编写详细实现计划(plan.md)
3. ⏳ 等待设计稿确认
4. ⏳ 开始实施开发(按优先级分阶段)
---
*文档创建时间: 2026-01-28*
*最后更新: 2026-01-28*
#!/usr/bin/env bash
#
# 部署脚本:构建项目 -> 上传七牛 -> 打包并上传到服务器
# 说明:在 macOS 环境下执行,依赖 npm、qshell、ssh/scp
set -euo pipefail
# 全局变量(按需修改)
repo_root="$(cd "$(dirname "$0")/.." && pwd)"
server_host="zhsy@oa.jcedu.org"
server_port="12101"
remote_dir="/home/www/f"
local_tar="dist.tar.gz"
local_package_dir="stdj"
qshell_dir="$HOME/.qshell"
qshell_conf="stdj_upload.conf"
build_out_dir="dist"
# 打印信息日志
# 说明:输出带前缀的日志,便于观察执行过程
log_info() {
echo "[deploy] $1"
}
# 执行命令并显示日志
# 参数:cmd 命令字符串
# 说明:包装命令执行,便于统一错误处理与日志输出
run_cmd() {
local cmd="$1"
log_info "执行:$cmd"
eval "$cmd"
}
# 检测构建输出目录
# 说明:按优先级读取 .env.production -> .env -> .env.development 中的 VITE_OUTDIR,缺省为 dist
detect_out_dir() {
# POSIX 兼容实现:按顺序检查 .env.production -> .env -> .env.development
# 若未设置,回退为 dist
local outdir="dist"
for env_file in "$repo_root/.env.production" "$repo_root/.env" "$repo_root/.env.development"; do
if [ -f "$env_file" ]; then
# 提取键值:去注释、去空格、去引号
local value
value=$(grep -E '^\s*VITE_OUTDIR\s*=' "$env_file" | head -n1 | cut -d '=' -f2- | sed 's/#.*$//' | tr -d '[:space:]' | sed -e 's/^"//;s/"$//' -e "s/^'//;s/'$//" || true)
if [ -n "${value:-}" ]; then
outdir="$value"
break
fi
fi
done
echo "$outdir"
}
# 构建项目
# 说明:使用 npm 脚本进行构建,产物位于 dist/
build_project() {
log_info "开始构建项目"
cd "$repo_root"
if command -v pnpm >/dev/null 2>&1; then
run_cmd "pnpm run build"
elif command -v npm >/dev/null 2>&1; then
run_cmd "npm run build"
elif command -v yarn >/dev/null 2>&1; then
run_cmd "yarn build"
else
echo "错误:未检测到 pnpm/npm/yarn,请先安装其中之一"
exit 1
fi
build_out_dir="$(detect_out_dir)"
if [ ! -d "$repo_root/${build_out_dir:-}" ]; then
echo "错误:未找到构建输出目录 '${build_out_dir:-}/',请检查 .env/.env.production 的 VITE_OUTDIR 或构建是否成功"
exit 1
fi
log_info "构建完成:${build_out_dir:-}/"
}
# 准备打包目录
# 说明:将 dist 内容复制到 stdj 目录,用于后续打包
prepare_package_dir() {
log_info "准备打包目录:$local_package_dir"
cd "$repo_root"
# 保留 stdj 目录,仅增量同步构建产物
run_cmd "mkdir -p '$local_package_dir'"
if [ "${build_out_dir:-}" = "$local_package_dir" ]; then
# 输出目录与包目录一致,直接使用,无需复制
log_info "输出目录与包目录一致:${build_out_dir:-},跳过复制"
return 0
fi
if command -v rsync >/dev/null 2>&1; then
# 使用 rsync 增量复制,保留 stdj 目录和其他文件(不删除已有文件)
run_cmd "rsync -a '${build_out_dir:-}/' '$local_package_dir/'"
else
# 回退到 cp -R,同样保留 stdj 目录
run_cmd "cp -R '${build_out_dir:-}'/. '$local_package_dir'/"
fi
}
# 上传静态资源到七牛云
# 说明:进入 ~/.qshell 并执行 qupload,同步 upload.conf 中配置的资源
upload_qiniu() {
log_info "检查 qshell 安装与配置"
if ! command -v qshell >/dev/null 2>&1; then
echo "错误:未检测到 qshell,请先安装 https://developer.qiniu.com/kodo/tools/1302/qshell"
exit 1
fi
if [ ! -d "$qshell_dir" ]; then
echo "错误:未找到目录 $qshell_dir"
exit 1
fi
if [ ! -f "$qshell_dir/$qshell_conf" ]; then
echo "错误:未找到七牛配置文件 $qshell_dir/$qshell_conf"
exit 1
fi
log_info "开始上传七牛云资源(qupload)"
cd "$qshell_dir"
# 说明:部分文件已存在会导致 qshell 返回非零退出码,此处不终止流程
log_info "执行:qshell qupload '$qshell_conf'"
if ! qshell qupload "$qshell_conf"; then
log_info "qshell qupload 退出码非零(可能因文件已存在),继续后续部署"
fi
log_info "七牛云资源上传完成"
}
# 打包本地目录并上传到服务器
# 说明:打包 stdj -> 通过 scp 上传到指定目录 -> 远端解压并删除压缩包
upload_server() {
log_info "开始打包:${local_tar:-}"
cd "$repo_root"
run_cmd "tar -czvpf '${local_tar:-}' '${local_package_dir:-}'"
log_info "上传到服务器:${server_host:-}:${remote_dir:-}(端口 ${server_port:-})"
run_cmd "scp -P '${server_port:-}' '${local_tar:-}' '${server_host:-}':'${remote_dir:-}'"
log_info "服务器解压:${remote_dir:-}/${local_tar:-}"
# 说明:构造远端命令,避免变量名意外字符导致的未绑定错误
local remote_cmd
remote_cmd="cd \"${remote_dir:-}\" && tar -xzvf \"${local_tar:-}\" && rm -rf \"${local_tar:-}\""
run_cmd "ssh -p '${server_port:-}' '${server_host:-}' \"$remote_cmd\""
log_info "删除本地压缩包"
run_cmd "rm -f '${local_tar:-}'"
}
# 主流程
# 说明:串行执行:构建 -> 七牛上传 -> 服务器打包上传
main() {
build_project
prepare_package_dir
upload_qiniu
upload_server
log_info "部署完成"
}
main "$@"
This diff is collapsed. Click to expand it.