feat(promotion): social 素材同步 dist、迁移脚本与文档;Brochure 侧栏路由与文案

Made-with: Cursor
This commit is contained in:
whm
2026-03-20 18:18:22 +08:00
parent dfcfb477c5
commit 7336c42af0
12 changed files with 165 additions and 56 deletions

View File

@@ -63,7 +63,7 @@
</el-form-item>
<el-form-item label="保留原文件名">
<el-switch v-model="uploadPreserveFilename" />
<span class="form-hint">开启后覆盖同路径同名文件首页产品视频须上传到 <code>promotion/视频发布/</code> 并开启此项</span>
<span class="form-hint">开启后覆盖同名文件产品视频请上传到 <code>promotion/social/</code>英文文件名见仓库 <code>web/promotion/social/README.md</code></span>
</el-form-item>
<el-form-item label="允许下载">
<el-switch v-model="uploadDownloadable" />

View File

@@ -288,18 +288,18 @@ echo "构建 web 前端 -> deploy/web/dist ..."
run_sudo docker run --rm -v "$ROOT/web:/app" -v "$ROOT/deploy/web/dist:/out" -w /app \
"${REGISTRY_MIRROR}node:20-alpine" sh -c "rm -rf /out/* 2>/dev/null; (npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps) && npm run build && cp -r dist/. /out/"
# 官网访问的是 Nginx 根目录 deploy/web/dist,不是源码 web/;必须把 /promotion/ 静态资源拷进 dist否则 social、logo 等一律 404
echo "同步 web/promotion -> deploy/web/dist/promotion排除 .mov大视频请走后台 API..."
# 官网访问的是 Nginx 根目录 deploy/web/dist;产品视频已放在 social/ 英文文件名,须整目录同步(含 .mov
echo "同步 web/promotion -> deploy/web/dist/promotion排除旧「视频发布」与 PPT 解压,避免重复大文件..."
mkdir -p "$ROOT/deploy/web/dist/promotion"
if command -v rsync >/dev/null 2>&1; then
rsync -a --exclude='*.mov' --exclude='*.MOV' --exclude='_pptx_extract/' \
rsync -a --exclude='_pptx_extract/' --exclude='视频发布/' \
"$ROOT/web/promotion/" "$ROOT/deploy/web/dist/promotion/"
else
mkdir -p "$ROOT/deploy/web/dist/promotion/social"
cp -a "$ROOT/web/promotion/social/." "$ROOT/deploy/web/dist/promotion/social/" 2>/dev/null || true
[ -f "$ROOT/web/promotion/logo.png" ] && cp -a "$ROOT/web/promotion/logo.png" "$ROOT/deploy/web/dist/promotion/" || true
[ -f "$ROOT/web/promotion/index.html" ] && cp -a "$ROOT/web/promotion/index.html" "$ROOT/deploy/web/dist/promotion/" || true
echo "提示: 未检测到 rsync仅复制了 social/logo 等;完整 promotion 请安装 rsync 后重跑本脚本。" >&2
echo "提示: 未检测到 rsync仅复制了 social/logo 等;请安装 rsync 以同步完整 promotion含视频。" >&2
fi
echo "构建 admin 前端 -> deploy/admin/dist ..."

View File

@@ -197,6 +197,18 @@ mkdir -p "$ROOT/deploy/web/dist" "$ROOT/deploy/admin/dist" "$ROOT/deploy/api"
echo "构建 web 前端 -> deploy/web/dist ..."
run_sudo docker run --rm -v "$ROOT/web:/app" -v "$ROOT/deploy/web/dist:/out" -w /app \
"${REGISTRY_MIRROR}node:20-alpine" sh -c "rm -rf /out/* 2>/dev/null; (npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps) && npm run build && cp -r dist/. /out/"
# 与 pull-and-restart 一致:文档根是 deploy/web/dist须把 promotion含 social 视频)拷入 dist
echo "同步 web/promotion -> deploy/web/dist/promotion ..."
mkdir -p "$ROOT/deploy/web/dist/promotion"
if command -v rsync >/dev/null 2>&1; then
rsync -a --exclude='_pptx_extract/' --exclude='视频发布/' \
"$ROOT/web/promotion/" "$ROOT/deploy/web/dist/promotion/"
else
mkdir -p "$ROOT/deploy/web/dist/promotion/social"
cp -a "$ROOT/web/promotion/social/." "$ROOT/deploy/web/dist/promotion/social/" 2>/dev/null || true
[ -f "$ROOT/web/promotion/logo.png" ] && cp -a "$ROOT/web/promotion/logo.png" "$ROOT/deploy/web/dist/promotion/" || true
[ -f "$ROOT/web/promotion/index.html" ] && cp -a "$ROOT/web/promotion/index.html" "$ROOT/deploy/web/dist/promotion/" || true
fi
echo "构建 admin 前端 -> deploy/admin/dist ..."
run_sudo docker run --rm -v "$ROOT/admin:/app" -v "$ROOT/deploy/admin/dist:/out" -w /app \
"${REGISTRY_MIRROR}node:20-alpine" sh -c "rm -rf /out/* 2>/dev/null; (npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps) && npm run build && cp -r dist/. /out/"

View File

@@ -0,0 +1,31 @@
# 将 web/promotion/视频发布 中文路径素材复制到 web/promotion/social英文文件名
# 用法:在项目根 powershell 执行 .\scripts\sync-video-assets-to-social.ps1
$ErrorActionPreference = "Stop"
$Root = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot ".."))
$Src = Join-Path $Root "web\promotion\视频发布"
$Dst = Join-Path $Root "web\promotion\social"
New-Item -ItemType Directory -Force -Path $Dst | Out-Null
function Copy-IfExists($fromRel, $toName) {
$from = Join-Path $Src $fromRel
$to = Join-Path $Dst $toName
if (Test-Path -LiteralPath $from) {
Copy-Item -LiteralPath $from -Destination $to -Force
Write-Host "OK $toName"
} else {
Write-Warning "SKIP (缺失): $from"
}
}
Copy-IfExists "宇恒一号操作计算软件实例(一)\宣传片-封面.jpg" "video-calc-demo-1-cover.jpg"
Copy-IfExists "宇恒一号操作计算软件实例(一)\宣传片.mov" "video-calc-demo-1.mov"
Copy-IfExists "宇恒一号操作计算软件实例(二)\宇恒一号操作计算软件实例(二)-封面.jpg" "video-calc-demo-2-cover.jpg"
Copy-IfExists "宇恒一号操作计算软件实例(二)\宇恒一号操作计算软件实例(二).mov" "video-calc-demo-2.mov"
Copy-IfExists "宇恒一号AIWord简介\宇恒一号AIWord简介-封面.jpg" "video-aiword-cover.jpg"
Copy-IfExists "宇恒一号AIWord简介\宇恒一号AIWord简介.mov" "video-aiword.mov"
Copy-IfExists "宇恒一号语音办公实例\宇恒一号语音办公实例-封面.jpg" "video-voice-office-cover.jpg"
Copy-IfExists "宇恒一号语音办公实例\宇恒一号语音办公实例.mov" "video-voice-office.mov"
Copy-IfExists "宇恒一号AI 全自动办发票\宇恒一号AI 全自动办发票-封面.jpg" "video-invoice-ai-cover.jpg"
Copy-IfExists "宇恒一号AI 全自动办发票\宇恒一号AI 全自动办发票.mov" "video-invoice-ai.mov"
Write-Host "完成。Linux 服务器上建议在 social 目录执行: chmod -R a+rX ."

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# 将旧目录「视频发布」中含中文路径的素材复制到 web/promotion/social/,使用与 promotionVideos.js 一致的英文文件名。
# 用法:在项目根执行 ./scripts/sync-video-assets-to-social.sh
# 完成后可设置权限Linuxchmod -R a+rX web/promotion/social
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SRC="$ROOT/web/promotion/视频发布"
DST="$ROOT/web/promotion/social"
mkdir -p "$DST"
copy_if () {
local from="$1" to="$2"
if [[ -f "$from" ]]; then
cp -f "$from" "$to"
echo "OK $to"
else
echo "SKIP (缺失): $from" >&2
fi
}
# 操作与计算软件实例(一)
copy_if "$SRC/宇恒一号操作计算软件实例(一)/宣传片-封面.jpg" "$DST/video-calc-demo-1-cover.jpg"
copy_if "$SRC/宇恒一号操作计算软件实例(一)/宣传片.mov" "$DST/video-calc-demo-1.mov"
# 操作与计算软件实例(二)
copy_if "$SRC/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg" "$DST/video-calc-demo-2-cover.jpg"
copy_if "$SRC/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov" "$DST/video-calc-demo-2.mov"
# AI Word
copy_if "$SRC/宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg" "$DST/video-aiword-cover.jpg"
copy_if "$SRC/宇恒一号AIWord简介/宇恒一号AIWord简介.mov" "$DST/video-aiword.mov"
# 语音办公
copy_if "$SRC/宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg" "$DST/video-voice-office-cover.jpg"
copy_if "$SRC/宇恒一号语音办公实例/宇恒一号语音办公实例.mov" "$DST/video-voice-office.mov"
# 办发票(目录名含全角逗号)
copy_if "$SRC/宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票-封面.jpg" "$DST/video-invoice-ai-cover.jpg"
copy_if "$SRC/宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票.mov" "$DST/video-invoice-ai.mov"
if command -v chmod >/dev/null 2>&1; then
chmod -R a+rX "$DST" 2>/dev/null || true
echo "已执行 chmod -R a+rX $DST"
fi
echo "完成。请确认 deploy 脚本会把 web/promotion 同步到 deploy/web/dist/promotion含 social 下 .mov。"

View File

@@ -1,7 +1,7 @@
# 推广素材(首页与视频源)
- `index.html`:静态完整落地页参考;线上 Vue 首页已与之对齐,素材路径以本目录为准。
- `视频发布/`:产品视频与封面。**`.mov` 等大文件默认不入 Git**;生产环境请在 **后台 → 文件管理** 上传到 `promotion/视频发布/…`,勾选 **保留原文件名**路径与文件名见 `视频发布/README.md`)。官网解析到站点后,首页通过 `/api/web/sites/{site_id}/promotion-media/视频发布/...` 拉取。本地开发仍可将文件放在本目录,走 `/promotion/视频发布/...`
- `social/`:产品视频与封面已与 **二维码图** 同目录,使用 **英文文件名**`social/README.md`)。线上 URL`/promotion/social/video-*.mov`。后台上传到 `promotion/social/`,勾选 **保留原文件名**。旧「视频发布」中文路径可用 `scripts/sync-video-assets-to-social.sh` 一键复制并改名
- `social/`**关注我们** 统一资源包(建议只用此目录上线),首页读取:
- `social/xiaohongshu.png``social/douyin.png``social/wechat-official.png``social/wechat-channels.jpg`
- 若需换图,直接替换 `social/` 下对应文件ASCII 文件名利于网关与日志)。
@@ -9,9 +9,9 @@
## 生产部署
1. **关键**:线上 Docker/Nginx 的文档根是 **`deploy/web/dist/`**浏览器请求 `https://域名/promotion/social/xxx.png` 实际读的是 **`deploy/web/dist/promotion/social/xxx.png`**。仅把文件放在源码 **`web/promotion/`**(例如 `/www/yh_web/web/promotion/`**不会出现**,必须同步进 `dist``pull-and-restart.sh``npm run build` 后会自动 `rsync web/promotion``deploy/web/dist/promotion`,并排除 `.mov`
2. **权限**若手动拷贝,目录建议 `755`、文件至少 `644`,避免 `root` 拥有且 `640` 导致 Nginx(非 root 用户)读不到
3. **视频**推荐 **后台上传**`data/uploads/.../promotion/`,由 `/api/web/sites/.../promotion-media/` 提供;勿只放在源码目录指望自动对外访问
1. **关键**:线上文档根是 **`deploy/web/dist/`**`/promotion/social/...` 对应 **`deploy/web/dist/promotion/social/...`**。`pull-and-restart.sh` 会把 **`web/promotion/`** 整体 rsync 到 dist**包含 `social/*.mov`**;仅排除 **`视频发布/`** 与 **`_pptx_extract/`**(避免重复大文件)。改完素材后务必重新执行部署脚本或手动 rsync
2. **权限**:目录建议 `755`、文件 `644`(或 `chmod -R a+rX promotion/social`),避免 `640` 导致 Nginx worker 读不到(表现为 404/403
3. **视频**可静态放在 **`social/`** 英文文件名;也可用 **后台上传** `promotion/social/`,由 `/api/web/sites/.../promotion-media/` 提供,二者与 `promotionVideos.js` 的路径一致即可
---

View File

@@ -0,0 +1,38 @@
# `promotion/social/`
统一放 **关注我们二维码****首页产品视频** 的封面/视频,**仅用英文名、小写、连字符**,避免 URL 编码与网关问题。
## 关注我们(已有)
| 文件 | 说明 |
|------|------|
| `xiaohongshu.png` | 小红书 |
| `douyin.png` | 抖音 |
| `wechat-official.png` | 公众号 |
| `wechat-channels.jpg` | 视频号 |
## 产品视频(与 `promotionVideos.js` 一致)
| 文件 | 说明 |
|------|------|
| `video-calc-demo-1-cover.jpg` / `video-calc-demo-1.mov` | 操作与计算(一) |
| `video-calc-demo-2-cover.jpg` / `video-calc-demo-2.mov` | 操作与计算(二) |
| `video-aiword-cover.jpg` / `video-aiword.mov` | AI Word |
| `video-voice-office-cover.jpg` / `video-voice-office.mov` | 语音办公 |
| `video-invoice-ai-cover.jpg` / `video-invoice-ai.mov` | 办发票 |
线上访问示例:`https://你的域名/promotion/social/douyin.png`
(须将 `web/promotion` 同步到 **`deploy/web/dist/promotion`**,见 `pull-and-restart.sh`。)
## 从旧「视频发布」目录迁移
若本地仍有中文子目录下的素材,在项目根执行:
```bash
./scripts/sync-video-assets-to-social.sh
```
## 后台上传
目录:`promotion/social/`,上传上表文件名,勾选 **保留原文件名**。API 路径为
`/api/web/sites/<site_id>/promotion-media/social/<文件名>`

View File

@@ -1,26 +1,11 @@
# 产品视频目录
# 视频发布(旧结构,可选)
大体积 **`.mov` 视频不入 Git**。线上由官网 **后台 → 文件管理** 上传到站点下的 `promotion/视频发布/...`,与首页「产品视频」使用的路径一致。
产品视频已迁移到 **`../social/`** 下 **英文文件名**,与 `web/src/data/promotionVideos.js` 一致。
## 上传步骤
迁移命令(在项目根):
1. 打开 **后台**,进入 **文件管理 → 功能模块**
2. 选择 **官网站点**(与系统设置中的「官网」站点一致)。
3. 新建或使用目录:`promotion/视频发布/<子目录名>/`(与下列清单一致)。
4. 上传对应 **封面 `.jpg`****视频 `.mov`** 时,请勾选 **「保留原文件名」**,文件名必须与下列清单完全一致(否则首页无法匹配)。
```bash
./scripts/sync-video-assets-to-social.sh
```
## 文件清单(相对 `promotion/`
| 子目录 | 封面文件 | 视频文件 |
|--------|----------|----------|
| `视频发布/宇恒一号操作计算软件实例(一)` | `宣传片-封面.jpg` | `宣传片.mov` |
| `视频发布/宇恒一号操作计算软件实例(二)` | `宇恒一号操作计算软件实例(二)-封面.jpg` | `宇恒一号操作计算软件实例(二).mov` |
| `视频发布/宇恒一号AIWord简介` | `宇恒一号AIWord简介-封面.jpg` | `宇恒一号AIWord简介.mov` |
| `视频发布/宇恒一号语音办公实例` | `宇恒一号语音办公实例-封面.jpg` | `宇恒一号语音办公实例.mov` |
| `视频发布/宇恒一号AI 全自动办发票` | `宇恒一号AI 全自动办发票-封面.jpg` | `宇恒一号AI 全自动办发票.mov` |
(若表内「封面」文件名与代码中 `web/src/data/promotionVideos.js` 不一致,以代码中的 `relCover` / `relVideo` 为准。)
## 本地开发
若仅在本地调试仍可将视频放在本目录Vite 开发服务器会通过 `/promotion/...` 直接读本地文件。
之后请使用 **`promotion/social/`** 维护素材;本目录可仅作本地备份或留空。

View File

@@ -1,43 +1,46 @@
import { promotionUrl, promotionMediaApiUrl } from '../utils/promotionAssets'
const ROOT = '视频发布'
const SOCIAL = 'social'
/** 相对 `promotion/` 的路径;与后台上传目录 promotion/视频发布/… +「保留原文件名」一致 */
/**
* 产品视频与封面统一使用 promotion/social/ 下英文文件名(无空格、无中文路径),便于线上 URL 与 Nginx。
* 静态:/promotion/social/xxx.mov后台上传路径promotion/social/ + 下列文件名,勾选「保留原文件名」。
*/
export const PROMOTION_VIDEOS_BASE = [
{
id: 'calc-demo-1',
title: '操作与计算软件实例(一)',
desc: '宇恒一号宣传片',
relCover: `${ROOT}/宇恒一号操作计算软件实例(一)/宣传片-封面.jpg`,
relVideo: `${ROOT}/宇恒一号操作计算软件实例(一)/宣传片.mov`
relCover: `${SOCIAL}/video-calc-demo-1-cover.jpg`,
relVideo: `${SOCIAL}/video-calc-demo-1.mov`
},
{
id: 'calc-demo-2',
title: '操作与计算软件实例(二)',
desc: '进阶操作与计算演示',
relCover: `${ROOT}/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg`,
relVideo: `${ROOT}/宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov`
relCover: `${SOCIAL}/video-calc-demo-2-cover.jpg`,
relVideo: `${SOCIAL}/video-calc-demo-2.mov`
},
{
id: 'aiword',
title: '宇恒一号 AI Word 简介',
desc: 'AI Word 能力介绍',
relCover: `${ROOT}/宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg`,
relVideo: `${ROOT}/宇恒一号AIWord简介/宇恒一号AIWord简介.mov`
relCover: `${SOCIAL}/video-aiword-cover.jpg`,
relVideo: `${SOCIAL}/video-aiword.mov`
},
{
id: 'voice',
title: '语音办公实例',
desc: '语音驱动办公流程',
relCover: `${ROOT}/宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg`,
relVideo: `${ROOT}/宇恒一号语音办公实例/宇恒一号语音办公实例.mov`
relCover: `${SOCIAL}/video-voice-office-cover.jpg`,
relVideo: `${SOCIAL}/video-voice-office.mov`
},
{
id: 'invoice',
title: 'AI 全自动办发票',
desc: '发票场景自动化演示',
relCover: `${ROOT}/宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票-封面.jpg`,
relVideo: `${ROOT}/宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票.mov`
relCover: `${SOCIAL}/video-invoice-ai-cover.jpg`,
relVideo: `${SOCIAL}/video-invoice-ai.mov`
}
]

View File

@@ -12,7 +12,7 @@ export function promotionUrl(relativePath) {
/**
* 官网产品视频/封面:读取站点 uploads 下 `promotion/` 目录(与后台上传路径一致)
* @param {string} siteId Mongo 站点 id与 /api/web/routes 返回的 site_id 一致)
* @param {string} relativePath 相对 promotion/ 的路径,如 `视频发布/xxx/yyy.mov`
* @param {string} relativePath 相对 promotion/ 的路径,如 `social/video-aiword.mov`
*/
export function promotionMediaApiUrl(siteId, relativePath) {
if (!siteId) return promotionUrl(relativePath)

View File

@@ -29,21 +29,15 @@
<aside id="brochure-toc" class="brochure-toc">
<h2>宣传册目录</h2>
<p class="toc-note">点击章节即<strong>切换路由页面</strong>可前进/后退</p>
<nav>
<nav class="toc-nav">
<router-link
v-for="item in BROCHURE_NAV"
:key="item.topic"
:to="{ name: 'Brochure', params: { topic: item.topic } }"
custom
v-slot="{ href, navigate, isExactActive }"
>
<a
:href="href"
class="toc-item"
:class="{ active: isExactActive }"
@click="(e) => { e.preventDefault(); navigate(e) }"
>{{ item.label }}</a>
</router-link>
active-class="active"
exact-active-class="active"
>{{ item.label }}</router-link>
</nav>
<router-link class="toc-back-home" :to="{ path: '/', hash: '#contact' }">需要帮助去首页联系我们</router-link>
</aside>
@@ -532,6 +526,7 @@ watch(
text-decoration: none;
font-size: 13px;
transition: background 0.2s, color 0.2s;
cursor: pointer;
}
.toc-item:hover {
background: rgba(0, 212, 255, 0.1);

View File

@@ -209,7 +209,7 @@
</div>
</section>
<!-- 产品视频 web/promotion/视频发布 目录一致 -->
<!-- 产品视频web/promotion/social/ 下英文文件名 promotionVideos.js -->
<section class="video-section" id="videos">
<div class="section-title">
<h2>产品视频</h2>