From 5ff300d0f78bc0addf281d004bba703d30a7a6ce Mon Sep 17 00:00:00 2001 From: whm <973418690@qq.com> Date: Sun, 22 Mar 2026 01:15:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E5=85=B3=E6=B3=A8=E6=88=91?= =?UTF-8?q?=E4=BB=AC=E5=9B=BE=E7=89=87=E5=A4=9A=20URL=20=E5=9B=9E=E9=80=80?= =?UTF-8?q?(public=E2=86=92promotion)=EF=BC=8C=E7=A7=BB=E9=99=A4=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E8=B7=AF=E5=BE=84=E6=8F=90=E7=A4=BA=EF=BC=9BNginx=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20/social/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- admin/src/views/files/FileManage.vue | 2 +- deploy/web/default.conf | 6 +++ nginx/web.conf | 5 +++ web/public/README.md | 17 ++++----- web/src/data/promotionSocial.js | 18 +++++---- web/src/views/Home.vue | 56 +++++++++++++++++++++++----- 6 files changed, 76 insertions(+), 28 deletions(-) diff --git a/admin/src/views/files/FileManage.vue b/admin/src/views/files/FileManage.vue index 557492e..7405c9d 100644 --- a/admin/src/views/files/FileManage.vue +++ b/admin/src/views/files/FileManage.vue @@ -63,7 +63,7 @@ - 开启后覆盖同名文件;产品视频请上传到 promotion/social/(英文文件名,见仓库 web/promotion/social/README.md + 开启后将按原文件名保存,同名文件会被覆盖 diff --git a/deploy/web/default.conf b/deploy/web/default.conf index e49832f..39ee35c 100644 --- a/deploy/web/default.conf +++ b/deploy/web/default.conf @@ -27,6 +27,12 @@ server { try_files $uri =404; } + # web/public/social/ → 关注我们二维码等(挂载 /var/www/yh-public) + location ^~ /social/ { + alias /var/www/yh-public/social/; + add_header Cache-Control "public, max-age=300"; + } + # 推广素材:文件必须真实存在,禁止落到 SPA index.html(否则前端误判「静态存在」、视频拿到 HTML) # 宿主机 ./web/promotion 挂载覆盖本路径,改 social 下文件无需重新 npm build location ^~ /promotion/ { diff --git a/nginx/web.conf b/nginx/web.conf index e7d8eff..3ef66ea 100644 --- a/nginx/web.conf +++ b/nginx/web.conf @@ -25,6 +25,11 @@ server { try_files $uri =404; } + location ^~ /social/ { + alias /var/www/yh-public/social/; + add_header Cache-Control "public, max-age=300"; + } + location ^~ /promotion/ { try_files $uri =404; add_header Cache-Control "public, max-age=86400"; diff --git a/web/public/README.md b/web/public/README.md index 0127637..152fcce 100644 --- a/web/public/README.md +++ b/web/public/README.md @@ -1,15 +1,12 @@ -# `public/` 静态资源目录 +# `web/public/` 静态资源 -[Vite 约定](https://cn.vitejs.dev/guide/assets.html#the-public-directory):此目录下的文件会**原样**复制到构建后的站点根目录(`dist/`),访问路径为 **`/<文件名>`**。 +构建后会出现在站点根路径(`/logo.png`);Docker 下整个目录挂载为容器内 `/var/www/yh-public`。 -## 当前文件 +## 可选子目录 `social/` -| 文件 | 用途 | -|------|------| -| `logo.png` | **标签页图标**(`index.html` 的 `rel="icon"` / `apple-touch-icon`)+ **首页与宣传册导航**(`Home.vue`、`BrochurePage.vue`) | +「关注我们」二维码可放在 **`public/social/`**,访问路径为 **`/social/文件名`**。 +若此处没有对应文件,前端会自动尝试 **`/promotion/social/`**(`web/promotion/social/` 挂载)。 -更新品牌时替换本目录下的 `logo.png` 即可。 +## 根目录文件示例 -**Docker 部署**:`docker-compose` 已将 **`web/public` → 容器内 `/var/www/yh-public`** 只读挂载,`deploy/web/default.conf` 中配置了「根路径下单段静态文件名优先读挂载、否则回退 `dist`」。**改宿主机 `web/public/` 内文件后无需 `npm run build`**,刷新即可(注意浏览器缓存)。 - -**注意**:引用时用**根路径** `/logo.png`,不要用 `src/assets` 的 import 方式写 public 资源。 +- `logo.png`:导航栏与 favicon(见 `index.html`、`Home.vue`) diff --git a/web/src/data/promotionSocial.js b/web/src/data/promotionSocial.js index c414c0d..d860957 100644 --- a/web/src/data/promotionSocial.js +++ b/web/src/data/promotionSocial.js @@ -1,34 +1,38 @@ import { promotionUrl } from '../utils/promotionAssets' /** - * 「关注我们」素材统一放在 web/promotion/social/ - * 线上访问:/promotion/social/<文件名>(与视频同源部署) + * 「关注我们」二维码图加载顺序(前者 404 时 img @error 自动换下一项): + * 1. /social/ → web/public/social/(Docker 挂载 /var/www/yh-public/social) + * 2. / → web/public/ 根目录 + * 3. /promotion/social/ → web/promotion/social/(挂载或 dist) */ -const social = (file) => promotionUrl(`social/${file}`) +export function socialFollowImageCandidates(filename) { + return [`/social/${filename}`, `/${filename}`, promotionUrl(`social/${filename}`)] +} export const PROMOTION_SOCIAL_FOLLOW = [ { id: 'xiaohongshu', label: '小红书', - image: social('xiaohongshu.png'), + imageCandidates: socialFollowImageCandidates('xiaohongshu.png'), href: 'https://www.xiaohongshu.com/' }, { id: 'douyin', label: '抖音', - image: social('douyin.png'), + imageCandidates: socialFollowImageCandidates('douyin.png'), href: 'https://www.douyin.com/' }, { id: 'wechat-official', label: '公众号', - image: social('wechat-official.png'), + imageCandidates: socialFollowImageCandidates('wechat-official.png'), href: 'https://mp.weixin.qq.com/' }, { id: 'wechat-channels', label: '视频号', - image: social('wechat-channels.jpg'), + imageCandidates: socialFollowImageCandidates('wechat-channels.jpg'), href: 'https://channels.weixin.qq.com/' } ] diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index 2a1f3fd..8745332 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -135,7 +135,6 @@ -

产品视频

@@ -297,7 +303,13 @@ ({ logo_text: 'YUHENG ONE', @@ -524,7 +539,9 @@ function closeVideoModal() { function openSocialFollow(item) { socialQrTitle.value = item.label - socialQrImage.value = item.image + socialQrCandidates.value = item.imageCandidates || [] + socialQrCandidateIdx.value = 0 + socialQrImage.value = item.imageCandidates?.[0] || '' socialQrHref.value = item.href || '' socialQrOpen.value = true } @@ -533,6 +550,29 @@ function closeSocialFollow() { socialQrOpen.value = false socialQrImage.value = '' socialQrHref.value = '' + socialQrCandidates.value = [] + socialQrCandidateIdx.value = 0 +} + +/** public /social、public 根路径无图时依次尝试 /promotion/social/ */ +function onSocialFollowListImgError(e, item) { + const img = e.target + const list = item.imageCandidates || [] + let i = Number(img.dataset.fi || 0) + if (i + 1 < list.length) { + img.dataset.fi = String(i + 1) + img.src = list[i + 1] + } +} + +function onSocialQrImgError() { + const list = socialQrCandidates.value + let i = socialQrCandidateIdx.value + if (i + 1 < list.length) { + i += 1 + socialQrCandidateIdx.value = i + socialQrImage.value = list[i] + } } function toggleFaq(i) { @@ -949,13 +989,9 @@ onUnmounted(() => { .social-section { padding: 72px 20px; text-align: center; position: relative; z-index: 10; } .social-title { - font-family: 'Exo 2', sans-serif; font-size: clamp(20px, 3vw, 26px); margin-bottom: 12px; + font-family: 'Exo 2', sans-serif; font-size: clamp(20px, 3vw, 26px); margin-bottom: 28px; color: var(--star-white); letter-spacing: 4px; } -.social-desc { - font-size: 12px; color: rgba(255,255,255,0.4); margin-bottom: 28px; max-width: 520px; margin-left: auto; margin-right: auto; -} -.social-code { font-family: ui-monospace, Consolas, monospace; font-size: 11px; color: rgba(0, 212, 255, 0.8); } .social-links { display: flex; justify-content: center; gap: 14px; flex-wrap: wrap; align-items: center; } .social-pill { display: inline-flex; align-items: center; gap: 10px;