From 198175975b1f87cfab40f6480538f71a2b4ce688 Mon Sep 17 00:00:00 2001 From: chenxing Date: Tue, 8 Sep 2026 17:18:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(env-config):=20=E8=BF=90=E8=A1=8C=E6=97=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=94=B9=E9=80=A0-index.html=E5=86=85?= =?UTF-8?q?=E8=81=94=E5=BC=95=E5=AF=BC=E8=84=9A=E6=9C=AC=E5=90=8C=E6=AD=A5?= =?UTF-8?q?XHR=E6=8B=89/{ctx}/api/env-config=3Ftarget=3Dadmin=E5=86=99=5F?= =?UTF-8?q?=5FAPP=5FCONFIG=5F=5F,host.config=E4=B8=89=E7=BA=A7=E5=90=88?= =?UTF-8?q?=E5=B9=B6(=E8=BF=90=E8=A1=8C=E6=97=B6JSON>VITE=5F*(=E4=BB=85dev?= =?UTF-8?q?/mock)>=E5=90=8C=E5=9F=9F=E6=8E=A8=E5=AF=BC),=E5=88=A0.env.base?= =?UTF-8?q?/.env.prod=E4=BA=A7=E7=89=A9=E9=9B=B6=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E5=80=BC,vitest=2056/56+build=E4=BA=A7=E7=89=A9=E9=9B=B6?= =?UTF-8?q?=E7=83=A7=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.base | 8 ----- .env.dev | 6 ++-- .env.prod | 6 ---- index.html | 24 +++++++++++++++ src/assets/config/host.config.ts | 50 +++++++++++++++++++++++++------- src/vite-env.d.ts | 4 +++ 6 files changed, 72 insertions(+), 26 deletions(-) delete mode 100644 .env.base delete mode 100644 .env.prod diff --git a/.env.base b/.env.base deleted file mode 100644 index d9dc213..0000000 --- a/.env.base +++ /dev/null @@ -1,8 +0,0 @@ -# 正式环境 - base -VITE_BASE_HOST=http://base.yunhaixingtong.com -VITE_SSO_HOST=http://sso.yunhaixingtong.com -# API 基址用相对 "/yschcms"(同源 + context-path):projman 部署的服务上下文为 /yschcms, -# base 域网关按路径反代 /yschcms → 同源恒成立; -# 写死绝对域名会导致 localhost 直连、换域名等场景全部"加载中"卡死(day2 修复) -VITE_SERVICE_HOST=/yschcms -VITE_IS_MOCK=false diff --git a/.env.dev b/.env.dev index f7af9e0..ce36881 100644 --- a/.env.dev +++ b/.env.dev @@ -1,5 +1,7 @@ -# 开发环境 (dev):API 走 /api 同源相对路径(vite proxy 改写转发到本地后端 8072 的 /cms/api), -# 规避后端 CAS 过滤器不回 CORS 头导致的跨域拦截;SSO/基础平台用真实环境 +# 开发环境 (dev):仅 vite dev --mode dev 加载;build 产物不读任何 .env.*(线上由 env-config 下发)。 +# API 走 /api 同源相对路径(serviceHost 留空 + vite proxy 改写转发到本地后端 8072 的 /cms/api), +# 规避后端 CAS 过滤器不回 CORS 头导致的跨域拦截;SSO/基础平台用真实环境。 +# ⚠️ VITE_* 仅本地开发生效,线上一律走 /api/env-config?target=admin 运行时配置。 VITE_BASE_HOST=http://base.yunhaixingtong.com VITE_SSO_HOST=http://sso.yunhaixingtong.com VITE_SERVICE_HOST= diff --git a/.env.prod b/.env.prod deleted file mode 100644 index a13f549..0000000 --- a/.env.prod +++ /dev/null @@ -1,6 +0,0 @@ -# 生产环境(与 base 同源域名) -VITE_BASE_HOST=http://base.yunhaixingtong.com -VITE_SSO_HOST=http://sso.yunhaixingtong.com -# 相对 "/yschcms"(同源 + context-path,projman 部署注入),理由见 .env.base 注释 -VITE_SERVICE_HOST=/yschcms -VITE_IS_MOCK=false diff --git a/index.html b/index.html index 44cac8b..3c76fae 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,30 @@ + + <%- title %> diff --git a/src/assets/config/host.config.ts b/src/assets/config/host.config.ts index 336d6d4..03e6da1 100644 --- a/src/assets/config/host.config.ts +++ b/src/assets/config/host.config.ts @@ -1,24 +1,54 @@ import { BaseConf } from "@/types/Basics"; /** - * 通过 Vite 环境变量管理配置(Notice front 同模式) - * 环境变量定义在项目根目录的 .env.dev / .env.base / .env.prod 中 - * serviceHost = 源 + 后端上下文路径(如 http://base.yunhaixingtong.com/cms), - * dev 模式留空走 vite 同源代理(规避 CAS 过滤器不回 CORS 头) + * 配置优先级(高 → 低): + * ① window.__APP_CONFIG__:线上运行时配置,index.html 引导脚本在应用模块加载前同步拉取 + * //api/env-config?target=<部署目录名>(默认 admin,projman 配置管理维护)写入 + * ② import.meta.env.VITE_*:仅本地开发/演示(vite dev --mode dev 读 .env.dev、--mode mock 读 .env.mock); + * build 产物不读任何 .env.*(.env.base/.env.prod 已删),VITE_* 恒为 undefined,本层自动跳过, + * 产物不含任何环境值 + * ③ 同域推导兜底:env-config 未配置/拉取失败时框架至少能渲染并调通同源 API; + * ssoHost 为空 → 登录跳转不可用——与后端未就绪状态一致,可接受。 */ const env = import.meta.env; +// ③ 同域推导兜底:context 段取当前路径首段(/{contextPath}/admin/ → contextPath,即后端上下文), +// 与服务端 context-path 对齐(本地 /cms、base/prod /yschcms);本地 dev/mock 在根路径运行(无段), +// serviceHost 留空走 vite 同源代理(规避 CAS 过滤器不回 CORS 头) +const seg = window.location.pathname.split("/").filter(Boolean); +const origin = window.location.origin; + let conf: BaseConf = { - baseHost: env.VITE_BASE_HOST, - ssoHost: env.VITE_SSO_HOST, - serviceHost: env.VITE_IS_MOCK === "true" ? "" : env.VITE_SERVICE_HOST, - isMock: env.VITE_IS_MOCK === "true", + baseHost: origin, + ssoHost: "", + serviceHost: seg.length >= 2 ? `${origin}/${seg[0]}` : "", + isMock: false, isDebug: env.DEV, }; +// 逐键覆盖:只覆盖来源里实际出现且非空的键,未出现的键沿用底层值 +const applyConf = (target: any, source: any) => { + if (!source || typeof source !== "object") return; + Object.keys(source).forEach(key => { + if (source[key] !== undefined && source[key] !== null && source[key] !== "") { + (target as any)[key] = source[key]; + } + }); +}; + +// ② 本地开发/演示(build 产物中恒 undefined → 自动跳过) +applyConf(conf, { + baseHost: env.VITE_BASE_HOST, + ssoHost: env.VITE_SSO_HOST, + serviceHost: env.VITE_SERVICE_HOST, + isMock: env.VITE_IS_MOCK === "true", +}); + +// ① 线上运行时配置(最高优先,覆盖前面所有) +applyConf(conf, (window as any).__APP_CONFIG__); + // HTTPS 自动升级:当前页面是 https 时,将所有 http:// 配置项升级为 https:// -var ishttps = "https:" == document.location.protocol ? true : false; -if (ishttps) { +if ("https:" === document.location.protocol) { Object.keys(conf).forEach(element => { const val = (conf as any)[element]; if (typeof val === "string" && val.indexOf("http://") > -1) { diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 90284a4..9e7e117 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,5 +1,9 @@ /// +// import.meta.env 类型声明:VITE_* 仅供本地开发/演示(.env.dev、.env.mock,vite dev 对应 mode)读取; +// build 不读任何 .env.*(.env.base/.env.prod 已删),VITE_* 恒为 undefined,不会进构建产物。 +// 线上环境差异由 env-config 运行时配置承担(index.html 引导脚本 → window.__APP_CONFIG__,见 +// src/assets/config/host.config.ts)。 interface ImportMetaEnv { readonly VITE_BASE_HOST: string; readonly VITE_SSO_HOST: string;