feat: 添加欢迎页开发计划和通用七牛云上传工具
新增欢迎页功能的详细开发计划文档,包括头脑风暴、架构设计和实现步骤 创建通用七牛云上传工具脚本,支持单文件上传和批量上传 添加视频背景组件和欢迎页路由配置的基础框架 实现首次访问检测逻辑,使用localStorage记录用户访问状态
Showing
3 changed files
with
557 additions
and
0 deletions
docs/plan/26.1.28-欢迎页开发计划/brainstorm.md
0 → 100644
| 1 | +# 欢迎页功能 - 头脑风暴与设计探索 | ||
| 2 | + | ||
| 3 | +## 项目背景 | ||
| 4 | + | ||
| 5 | +为 mlaj 平台设计一个欢迎页,作为用户首次进入时的引导页面,展示核心功能入口。 | ||
| 6 | + | ||
| 7 | +**核心需求:** | ||
| 8 | +- 视频背景循环播放(星空宇宙主题) | ||
| 9 | +- 悬浮的功能入口图标 + 文字介绍 | ||
| 10 | +- 持续循环的缩放位移动效 | ||
| 11 | +- 首次进入检测,非首次直接跳转主页 | ||
| 12 | + | ||
| 13 | +--- | ||
| 14 | + | ||
| 15 | +## 需求探索过程 | ||
| 16 | + | ||
| 17 | +### 问题 1: 欢迎页的主要目标? | ||
| 18 | + | ||
| 19 | +**选项:** | ||
| 20 | +- 品牌展示 - 营造高端/科技感的品牌印象 | ||
| 21 | +- 功能引导 - 清晰展示核心功能入口 | ||
| 22 | +- 新手引导 - 教育用户如何使用平台 | ||
| 23 | +- 混合型 - 品牌展示 + 功能引导 ✅ | ||
| 24 | + | ||
| 25 | +**决策:** 选择**混合型**,既要有视觉冲击力又要提供实用功能入口。 | ||
| 26 | + | ||
| 27 | +--- | ||
| 28 | + | ||
| 29 | +### 问题 2: 视频背景的实现方式? | ||
| 30 | + | ||
| 31 | +**选项:** | ||
| 32 | +- 原生 `<video>` 标签 | ||
| 33 | +- 简化版组件(参考 StarryBackground)✅ | ||
| 34 | +- 页面内直接实现 | ||
| 35 | + | ||
| 36 | +**决策:** 选择**简化版组件**,理由: | ||
| 37 | +- 组件化便于复用和维护 | ||
| 38 | +- 移除 Canvas 星星特效,保留视频播放逻辑 | ||
| 39 | +- 支持 Props 配置,灵活性高 | ||
| 40 | + | ||
| 41 | +--- | ||
| 42 | + | ||
| 43 | +### 问题 3: 如何判断用户是否首次进入? | ||
| 44 | + | ||
| 45 | +**选项:** | ||
| 46 | +- localStorage 标志 ✅ | ||
| 47 | +- 后端接口判断 | ||
| 48 | +- URL 参数控制 | ||
| 49 | + | ||
| 50 | +**决策:** 选择 **localStorage 标志**,理由: | ||
| 51 | +- 简单高效,无需额外请求 | ||
| 52 | +- 符合前端存储特性 | ||
| 53 | +- 开发调试方便(可手动清除) | ||
| 54 | + | ||
| 55 | +**权衡:** | ||
| 56 | +- 缺点: 清除缓存后会再次显示 | ||
| 57 | +- 解决方案: 文档说明这是预期行为,未来可升级为后端接口 | ||
| 58 | + | ||
| 59 | +--- | ||
| 60 | + | ||
| 61 | +### 问题 4: 图标和文字的动效风格? | ||
| 62 | + | ||
| 63 | +**选项:** | ||
| 64 | +- 轻量动效(3秒入场 + 悬停交互) | ||
| 65 | +- 持续循环(呼吸/缩放动画)✅ | ||
| 66 | +- 一次性入场 | ||
| 67 | + | ||
| 68 | +**决策:** 选择**持续循环**,理由: | ||
| 69 | +- 符合"星空宇宙"的神秘感主题 | ||
| 70 | +- 视觉冲击力更强,吸引用户注意力 | ||
| 71 | +- 不同入口设置不同延迟,形成错落感 | ||
| 72 | + | ||
| 73 | +--- | ||
| 74 | + | ||
| 75 | +## 架构设计探索 | ||
| 76 | + | ||
| 77 | +### 整体架构方案 | ||
| 78 | + | ||
| 79 | +**方案 A: 单页面组件** | ||
| 80 | +``` | ||
| 81 | +WelcomePage.vue | ||
| 82 | +├── 直接写 video 标签 | ||
| 83 | +└── 内联功能入口 | ||
| 84 | +``` | ||
| 85 | +- 优点: 简单直接 | ||
| 86 | +- 缺点: 代码耦合,难以复用 | ||
| 87 | + | ||
| 88 | +**方案 B: 组件化设计 ✅** | ||
| 89 | +``` | ||
| 90 | +WelcomePage.vue | ||
| 91 | +├── VideoBackground.vue (通用组件) | ||
| 92 | +└── WelcomeContent.vue | ||
| 93 | + └── WelcomeEntryItem.vue | ||
| 94 | +``` | ||
| 95 | +- 优点: 组件复用、职责清晰、易维护 | ||
| 96 | +- 缺点: 文件稍多 | ||
| 97 | + | ||
| 98 | +**决策:** 方案 B,组件化设计 | ||
| 99 | + | ||
| 100 | +--- | ||
| 101 | + | ||
| 102 | +### 页面层级结构 | ||
| 103 | + | ||
| 104 | +``` | ||
| 105 | +WelcomePage (视图容器) | ||
| 106 | +├── VideoBackground (简化版背景组件) | ||
| 107 | +│ └── <video> 循环播放,覆盖全屏 | ||
| 108 | +└── WelcomeContent (内容悬浮层) | ||
| 109 | + ├── [待定] Logo/标题区 (等设计稿确认) | ||
| 110 | + ├── 中部: 功能入口网格 (2-3列) | ||
| 111 | + └── [待定] CTA 按钮区 (等设计稿确认) | ||
| 112 | +``` | ||
| 113 | + | ||
| 114 | +**待确认部分:** 顶部和底部元素需等设计稿确定后再规划 | ||
| 115 | + | ||
| 116 | +--- | ||
| 117 | + | ||
| 118 | +### 首次访问控制逻辑 | ||
| 119 | + | ||
| 120 | +**路由守卫方案:** | ||
| 121 | +```javascript | ||
| 122 | +// src/router/guards.js | ||
| 123 | +const HAS_VISITED_WELCOME = 'has_visited_welcome' | ||
| 124 | + | ||
| 125 | +router.beforeEach((to, from, next) => { | ||
| 126 | + // 欢迎页功能开关 | ||
| 127 | + if (import.meta.env.VITE_WELCOME_PAGE_ENABLED !== 'true') { | ||
| 128 | + return next() | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + // 首次访问检测 | ||
| 132 | + if (to.path !== '/welcome' && | ||
| 133 | + !localStorage.getItem(HAS_VISITED_WELCOME)) { | ||
| 134 | + localStorage.setItem(HAS_VISITED_WELCOME, 'true') | ||
| 135 | + return next({ | ||
| 136 | + path: '/welcome', | ||
| 137 | + query: { redirect: to.fullPath } | ||
| 138 | + }) | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + next() | ||
| 142 | +}) | ||
| 143 | +``` | ||
| 144 | + | ||
| 145 | +**调试功能:** | ||
| 146 | +- URL 参数 `?reset_welcome=true` 重置标志位 | ||
| 147 | +- 控制台 `window.resetWelcomeFlag()` 快捷方法 | ||
| 148 | + | ||
| 149 | +--- | ||
| 150 | + | ||
| 151 | +## 核心组件设计 | ||
| 152 | + | ||
| 153 | +### VideoBackground 组件 | ||
| 154 | + | ||
| 155 | +**位置:** `src/components/effects/VideoBackground.vue` | ||
| 156 | + | ||
| 157 | +**核心特性:** | ||
| 158 | +- 简化版设计,移除 Canvas 特效 | ||
| 159 | +- 原生 `<video>` 标签实现 | ||
| 160 | +- 移动端和 PC 端自适应 | ||
| 161 | +- 可配置视频源、播放速度、覆盖模式 | ||
| 162 | + | ||
| 163 | +**Props 设计:** | ||
| 164 | +```javascript | ||
| 165 | +{ | ||
| 166 | + videoSrc: { type: String, required: true }, | ||
| 167 | + poster: { type: String }, | ||
| 168 | + autoplay: { type: Boolean, default: true }, | ||
| 169 | + loop: { type: Boolean, default: true }, | ||
| 170 | + muted: { type: Boolean, default: true }, | ||
| 171 | + objectFit: { type: String, default: 'cover' } | ||
| 172 | +} | ||
| 173 | +``` | ||
| 174 | + | ||
| 175 | +**关键实现点:** | ||
| 176 | +- 使用 `object-fit: cover` 确保全屏覆盖不变形 | ||
| 177 | +- 添加 `playsinline` 支持 iOS 内联播放 | ||
| 178 | +- 监听 `canplay` 事件移除 loading 状态 | ||
| 179 | +- `z-index: -1` 确保在内容层级下方 | ||
| 180 | + | ||
| 181 | +--- | ||
| 182 | + | ||
| 183 | +### WelcomeContent 组件 | ||
| 184 | + | ||
| 185 | +**位置:** `src/components/welcome/WelcomeContent.vue` | ||
| 186 | + | ||
| 187 | +**核心布局:** 功能入口网格(2-3列) | ||
| 188 | + | ||
| 189 | +**入口项动效设计(持续循环):** | ||
| 190 | +- CSS `@keyframes` 实现呼吸缩放效果 | ||
| 191 | +- 动画参数: `scale(1.0) → scale(1.08) → scale(1.0)` | ||
| 192 | +- 周期: 2-3秒 | ||
| 193 | +- 不同入口项设置不同延迟(0s, 0.5s, 1s...)形成错落感 | ||
| 194 | +- Hover 时加速或高亮 | ||
| 195 | + | ||
| 196 | +**CSS 动画示例:** | ||
| 197 | +```less | ||
| 198 | +@keyframes breathe { | ||
| 199 | + 0%, 100% { | ||
| 200 | + transform: scale(1); | ||
| 201 | + opacity: 0.9; | ||
| 202 | + } | ||
| 203 | + 50% { | ||
| 204 | + transform: scale(1.08); | ||
| 205 | + opacity: 1; | ||
| 206 | + } | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +.entry-item { | ||
| 210 | + animation: breathe 2.5s ease-in-out infinite; | ||
| 211 | + animation-delay: calc(var(--index) * 0.3s); | ||
| 212 | + | ||
| 213 | + &:hover { | ||
| 214 | + animation-duration: 1s; // 加速 | ||
| 215 | + transform: scale(1.1); | ||
| 216 | + } | ||
| 217 | +} | ||
| 218 | +``` | ||
| 219 | + | ||
| 220 | +--- | ||
| 221 | + | ||
| 222 | +## 七牛云上传工具设计 | ||
| 223 | + | ||
| 224 | +### 现有方案分析 | ||
| 225 | + | ||
| 226 | +**从 deploy.sh 提取的关键逻辑:** | ||
| 227 | +- 使用 `qshell qupload` 命令批量上传 | ||
| 228 | +- 依赖配置文件 `~/.qshell/stdj_upload.conf` | ||
| 229 | +- 需要预先安装 qshell 工具 | ||
| 230 | + | ||
| 231 | +**发现的问题:** | ||
| 232 | +- 用户提到"需要挂代理才能访问" | ||
| 233 | +- 可能是七牛云 API 域名在某些网络环境下被限制 | ||
| 234 | +- 需要设置 `HTTP_PROXY`/`HTTPS_PROXY` 环境变量 | ||
| 235 | + | ||
| 236 | +--- | ||
| 237 | + | ||
| 238 | +### 通用化设计探索 | ||
| 239 | + | ||
| 240 | +**原始方案问题:** | ||
| 241 | +- 硬编码了欢迎页特定路径(`/docs/plan/26.1.28-欢迎页开发计划/`) | ||
| 242 | +- 配置文件使用项目外的 `~/.qshell/stdj_upload.conf` | ||
| 243 | +- 无法作为通用工具复用 | ||
| 244 | + | ||
| 245 | +**改进方案:** | ||
| 246 | +1. **项目内配置** | ||
| 247 | + - 配置文件位置: `scripts/qiniu/configs/` | ||
| 248 | + - 账户信息: `scripts/qiniu/account.json`(不入库) | ||
| 249 | + - 配置模板: `scripts/qiniu/templates/*.template` | ||
| 250 | + | ||
| 251 | +2. **脚本参数化** | ||
| 252 | + - 支持单文件上传: `./script <local_file> <remote_path>` | ||
| 253 | + - 支持批量上传: `./script <config_file>` | ||
| 254 | + - 环境变量控制代理: `USE_PROXY=true PROXY_HOST=127.0.0.1:7890` | ||
| 255 | + | ||
| 256 | +3. **统一账户管理** | ||
| 257 | + - 首次使用时初始化账户 | ||
| 258 | + - 账户信息本地加密存储 | ||
| 259 | + - 支持多项目共享同一账户 | ||
| 260 | + | ||
| 261 | +--- | ||
| 262 | + | ||
| 263 | +### 使用场景设计 | ||
| 264 | + | ||
| 265 | +**场景 1: 上传欢迎页视频** | ||
| 266 | +```bash | ||
| 267 | +./scripts/upload-to-qiniu.sh video/bg.mp4 mlaj/video/welcome-bg.mp4 | ||
| 268 | +``` | ||
| 269 | + | ||
| 270 | +**场景 2: 批量上传图片** | ||
| 271 | +```bash | ||
| 272 | +# 复制模板并修改配置 | ||
| 273 | +cp scripts/qiniu/templates/image-upload.conf.template \ | ||
| 274 | + scripts/qiniu/configs/welcome-images.conf | ||
| 275 | + | ||
| 276 | +# 执行上传 | ||
| 277 | +./scripts/upload-to-qiniu.sh scripts/qiniu/configs/welcome-images.conf | ||
| 278 | +``` | ||
| 279 | + | ||
| 280 | +**场景 3: 使用代理上传** | ||
| 281 | +```bash | ||
| 282 | +USE_PROXY=true PROXY_HOST="127.0.0.1:7890" \ | ||
| 283 | + ./scripts/upload-to-qiniu.sh local.mp4 mlaj/video/remote.mp4 | ||
| 284 | +``` | ||
| 285 | + | ||
| 286 | +--- | ||
| 287 | + | ||
| 288 | +## 风险识别与应对 | ||
| 289 | + | ||
| 290 | +### 技术风险 | ||
| 291 | + | ||
| 292 | +**1. 视频播放兼容性** | ||
| 293 | +- iOS Safari 可能禁止自动播放 | ||
| 294 | +- 部分浏览器不支持 `playsinline` | ||
| 295 | + | ||
| 296 | +**应对:** | ||
| 297 | +```vue | ||
| 298 | +<video | ||
| 299 | + autoplay | ||
| 300 | + loop | ||
| 301 | + muted | ||
| 302 | + playsinline | ||
| 303 | + webkit-playsinline | ||
| 304 | + x5-video-player-type="h5" | ||
| 305 | + x5-video-player-fullscreen="true" | ||
| 306 | +> | ||
| 307 | +``` | ||
| 308 | +- 视频加载失败时使用静态背景图降级 | ||
| 309 | + | ||
| 310 | +--- | ||
| 311 | + | ||
| 312 | +**2. 首次访问标志位问题** | ||
| 313 | +- 清除 localStorage 后会再次显示 | ||
| 314 | +- 不同浏览器无法共享标志位 | ||
| 315 | + | ||
| 316 | +**应对:** | ||
| 317 | +- 文档说明这是预期行为 | ||
| 318 | +- 提供调试工具方便开发 | ||
| 319 | +- 未来可升级为后端接口 | ||
| 320 | + | ||
| 321 | +--- | ||
| 322 | + | ||
| 323 | +**3. 视频加载性能** | ||
| 324 | +- 文件过大导致加载缓慢 | ||
| 325 | +- 弱网环境体验差 | ||
| 326 | + | ||
| 327 | +**应对:** | ||
| 328 | +- 限制文件大小 < 10MB | ||
| 329 | +- 提供封面图在视频加载前显示 | ||
| 330 | +- 添加加载进度提示 | ||
| 331 | +- 考虑视频分段加载或 M3U8 | ||
| 332 | + | ||
| 333 | +--- | ||
| 334 | + | ||
| 335 | +**4. 七牛云代理问题** | ||
| 336 | +- 需要挂代理才能访问 | ||
| 337 | + | ||
| 338 | +**应对:** | ||
| 339 | +- 脚本支持 `USE_PROXY` 环境变量 | ||
| 340 | +- 提供诊断脚本测试连通性 | ||
| 341 | +- 文档说明代理配置方法 | ||
| 342 | + | ||
| 343 | +--- | ||
| 344 | + | ||
| 345 | +### 业务风险 | ||
| 346 | + | ||
| 347 | +**1. 功能入口待确认** | ||
| 348 | +- 设计稿未确定具体入口 | ||
| 349 | + | ||
| 350 | +**应对:** | ||
| 351 | +- 先实现框架和核心逻辑 | ||
| 352 | +- 入口配置化,后续易于调整 | ||
| 353 | +- 使用 Mock 数据开发 | ||
| 354 | + | ||
| 355 | +--- | ||
| 356 | + | ||
| 357 | +**2. 动效性能影响** | ||
| 358 | +- 持续动画可能消耗 CPU/电量 | ||
| 359 | + | ||
| 360 | +**应对:** | ||
| 361 | +- 使用 CSS 动画而非 JS(性能更好) | ||
| 362 | +- 提供环境变量控制动画开关 | ||
| 363 | +- 低端设备检测后降级为静态效果 | ||
| 364 | + | ||
| 365 | +--- | ||
| 366 | + | ||
| 367 | +## 待确认事项 | ||
| 368 | + | ||
| 369 | +根据需求文档,以下事项需要确认: | ||
| 370 | + | ||
| 371 | +1. ❌ **背景视频文件** - `video/?.mp4` 文件名未知 | ||
| 372 | +2. ❌ **页面效果图** - `img/` 文件夹为空 | ||
| 373 | +3. ❌ **功能入口列表** - 具体跳转地址未知 | ||
| 374 | +4. ❌ **页面布局细节** - 顶部/底部是否需要元素(Logo、标语、按钮等) | ||
| 375 | + | ||
| 376 | +**建议:** 先完成技术框架和上传工具,等设计稿确认后再填充内容。 | ||
| 377 | + | ||
| 378 | +--- | ||
| 379 | + | ||
| 380 | +## 关键决策总结 | ||
| 381 | + | ||
| 382 | +| 决策点 | 选择 | 理由 | | ||
| 383 | +|--------|------|------| | ||
| 384 | +| 页面目标 | 混合型 | 品牌展示 + 功能引导 | | ||
| 385 | +| 视频实现 | 简化版组件 | 组件化,便于复用 | | ||
| 386 | +| 首次检测 | localStorage | 简单高效 | | ||
| 387 | +| 动效风格 | 持续循环 | 符合主题,视觉冲击力强 | | ||
| 388 | +| 架构设计 | 组件化 | 职责清晰,易维护 | | ||
| 389 | +| 上传工具 | 通用化 | 支持多种场景复用 | | ||
| 390 | + | ||
| 391 | +--- | ||
| 392 | + | ||
| 393 | +## 下一步行动 | ||
| 394 | + | ||
| 395 | +1. ✅ 完成头脑风暴和设计探索 | ||
| 396 | +2. ⏳ 编写详细实现计划(plan.md) | ||
| 397 | +3. ⏳ 等待设计稿确认 | ||
| 398 | +4. ⏳ 开始实施开发(按优先级分阶段) | ||
| 399 | + | ||
| 400 | +--- | ||
| 401 | + | ||
| 402 | +*文档创建时间: 2026-01-28* | ||
| 403 | +*最后更新: 2026-01-28* |
docs/plan/26.1.28-欢迎页开发计划/deploy.sh
0 → 100644
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# | ||
| 3 | +# 部署脚本:构建项目 -> 上传七牛 -> 打包并上传到服务器 | ||
| 4 | +# 说明:在 macOS 环境下执行,依赖 npm、qshell、ssh/scp | ||
| 5 | + | ||
| 6 | +set -euo pipefail | ||
| 7 | + | ||
| 8 | +# 全局变量(按需修改) | ||
| 9 | +repo_root="$(cd "$(dirname "$0")/.." && pwd)" | ||
| 10 | +server_host="zhsy@oa.jcedu.org" | ||
| 11 | +server_port="12101" | ||
| 12 | +remote_dir="/home/www/f" | ||
| 13 | +local_tar="dist.tar.gz" | ||
| 14 | +local_package_dir="stdj" | ||
| 15 | +qshell_dir="$HOME/.qshell" | ||
| 16 | +qshell_conf="stdj_upload.conf" | ||
| 17 | +build_out_dir="dist" | ||
| 18 | + | ||
| 19 | +# 打印信息日志 | ||
| 20 | +# 说明:输出带前缀的日志,便于观察执行过程 | ||
| 21 | +log_info() { | ||
| 22 | + echo "[deploy] $1" | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +# 执行命令并显示日志 | ||
| 26 | +# 参数:cmd 命令字符串 | ||
| 27 | +# 说明:包装命令执行,便于统一错误处理与日志输出 | ||
| 28 | +run_cmd() { | ||
| 29 | + local cmd="$1" | ||
| 30 | + log_info "执行:$cmd" | ||
| 31 | + eval "$cmd" | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +# 检测构建输出目录 | ||
| 35 | +# 说明:按优先级读取 .env.production -> .env -> .env.development 中的 VITE_OUTDIR,缺省为 dist | ||
| 36 | +detect_out_dir() { | ||
| 37 | + # POSIX 兼容实现:按顺序检查 .env.production -> .env -> .env.development | ||
| 38 | + # 若未设置,回退为 dist | ||
| 39 | + local outdir="dist" | ||
| 40 | + for env_file in "$repo_root/.env.production" "$repo_root/.env" "$repo_root/.env.development"; do | ||
| 41 | + if [ -f "$env_file" ]; then | ||
| 42 | + # 提取键值:去注释、去空格、去引号 | ||
| 43 | + local value | ||
| 44 | + 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) | ||
| 45 | + if [ -n "${value:-}" ]; then | ||
| 46 | + outdir="$value" | ||
| 47 | + break | ||
| 48 | + fi | ||
| 49 | + fi | ||
| 50 | + done | ||
| 51 | + echo "$outdir" | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +# 构建项目 | ||
| 55 | +# 说明:使用 npm 脚本进行构建,产物位于 dist/ | ||
| 56 | +build_project() { | ||
| 57 | + log_info "开始构建项目" | ||
| 58 | + cd "$repo_root" | ||
| 59 | + if command -v pnpm >/dev/null 2>&1; then | ||
| 60 | + run_cmd "pnpm run build" | ||
| 61 | + elif command -v npm >/dev/null 2>&1; then | ||
| 62 | + run_cmd "npm run build" | ||
| 63 | + elif command -v yarn >/dev/null 2>&1; then | ||
| 64 | + run_cmd "yarn build" | ||
| 65 | + else | ||
| 66 | + echo "错误:未检测到 pnpm/npm/yarn,请先安装其中之一" | ||
| 67 | + exit 1 | ||
| 68 | + fi | ||
| 69 | + build_out_dir="$(detect_out_dir)" | ||
| 70 | + if [ ! -d "$repo_root/${build_out_dir:-}" ]; then | ||
| 71 | + echo "错误:未找到构建输出目录 '${build_out_dir:-}/',请检查 .env/.env.production 的 VITE_OUTDIR 或构建是否成功" | ||
| 72 | + exit 1 | ||
| 73 | + fi | ||
| 74 | + log_info "构建完成:${build_out_dir:-}/" | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +# 准备打包目录 | ||
| 78 | +# 说明:将 dist 内容复制到 stdj 目录,用于后续打包 | ||
| 79 | +prepare_package_dir() { | ||
| 80 | + log_info "准备打包目录:$local_package_dir" | ||
| 81 | + cd "$repo_root" | ||
| 82 | + # 保留 stdj 目录,仅增量同步构建产物 | ||
| 83 | + run_cmd "mkdir -p '$local_package_dir'" | ||
| 84 | + if [ "${build_out_dir:-}" = "$local_package_dir" ]; then | ||
| 85 | + # 输出目录与包目录一致,直接使用,无需复制 | ||
| 86 | + log_info "输出目录与包目录一致:${build_out_dir:-},跳过复制" | ||
| 87 | + return 0 | ||
| 88 | + fi | ||
| 89 | + if command -v rsync >/dev/null 2>&1; then | ||
| 90 | + # 使用 rsync 增量复制,保留 stdj 目录和其他文件(不删除已有文件) | ||
| 91 | + run_cmd "rsync -a '${build_out_dir:-}/' '$local_package_dir/'" | ||
| 92 | + else | ||
| 93 | + # 回退到 cp -R,同样保留 stdj 目录 | ||
| 94 | + run_cmd "cp -R '${build_out_dir:-}'/. '$local_package_dir'/" | ||
| 95 | + fi | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +# 上传静态资源到七牛云 | ||
| 99 | +# 说明:进入 ~/.qshell 并执行 qupload,同步 upload.conf 中配置的资源 | ||
| 100 | +upload_qiniu() { | ||
| 101 | + log_info "检查 qshell 安装与配置" | ||
| 102 | + if ! command -v qshell >/dev/null 2>&1; then | ||
| 103 | + echo "错误:未检测到 qshell,请先安装 https://developer.qiniu.com/kodo/tools/1302/qshell" | ||
| 104 | + exit 1 | ||
| 105 | + fi | ||
| 106 | + if [ ! -d "$qshell_dir" ]; then | ||
| 107 | + echo "错误:未找到目录 $qshell_dir" | ||
| 108 | + exit 1 | ||
| 109 | + fi | ||
| 110 | + if [ ! -f "$qshell_dir/$qshell_conf" ]; then | ||
| 111 | + echo "错误:未找到七牛配置文件 $qshell_dir/$qshell_conf" | ||
| 112 | + exit 1 | ||
| 113 | + fi | ||
| 114 | + log_info "开始上传七牛云资源(qupload)" | ||
| 115 | + cd "$qshell_dir" | ||
| 116 | + # 说明:部分文件已存在会导致 qshell 返回非零退出码,此处不终止流程 | ||
| 117 | + log_info "执行:qshell qupload '$qshell_conf'" | ||
| 118 | + if ! qshell qupload "$qshell_conf"; then | ||
| 119 | + log_info "qshell qupload 退出码非零(可能因文件已存在),继续后续部署" | ||
| 120 | + fi | ||
| 121 | + log_info "七牛云资源上传完成" | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +# 打包本地目录并上传到服务器 | ||
| 125 | +# 说明:打包 stdj -> 通过 scp 上传到指定目录 -> 远端解压并删除压缩包 | ||
| 126 | +upload_server() { | ||
| 127 | + log_info "开始打包:${local_tar:-}" | ||
| 128 | + cd "$repo_root" | ||
| 129 | + run_cmd "tar -czvpf '${local_tar:-}' '${local_package_dir:-}'" | ||
| 130 | + | ||
| 131 | + log_info "上传到服务器:${server_host:-}:${remote_dir:-}(端口 ${server_port:-})" | ||
| 132 | + run_cmd "scp -P '${server_port:-}' '${local_tar:-}' '${server_host:-}':'${remote_dir:-}'" | ||
| 133 | + | ||
| 134 | + log_info "服务器解压:${remote_dir:-}/${local_tar:-}" | ||
| 135 | + # 说明:构造远端命令,避免变量名意外字符导致的未绑定错误 | ||
| 136 | + local remote_cmd | ||
| 137 | + remote_cmd="cd \"${remote_dir:-}\" && tar -xzvf \"${local_tar:-}\" && rm -rf \"${local_tar:-}\"" | ||
| 138 | + run_cmd "ssh -p '${server_port:-}' '${server_host:-}' \"$remote_cmd\"" | ||
| 139 | + | ||
| 140 | + log_info "删除本地压缩包" | ||
| 141 | + run_cmd "rm -f '${local_tar:-}'" | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +# 主流程 | ||
| 145 | +# 说明:串行执行:构建 -> 七牛上传 -> 服务器打包上传 | ||
| 146 | +main() { | ||
| 147 | + build_project | ||
| 148 | + prepare_package_dir | ||
| 149 | + upload_qiniu | ||
| 150 | + upload_server | ||
| 151 | + log_info "部署完成" | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +main "$@" |
docs/plan/26.1.28-欢迎页开发计划/plan.md
0 → 100644
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment