From ec74735f94f76a6fa2393052692c7590ff8287da Mon Sep 17 00:00:00 2001 From: liulong <18539103286> Date: Mon, 8 Jun 2026 18:00:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=8E=E7=AB=AF=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=BC=80=E5=85=B3=20&=20=E9=A1=B5=E8=84=9AUI=E9=87=8D=E6=9E=84?= =?UTF-8?q?=20&=20=E7=99=BB=E5=BD=95=E8=AE=A4=E8=AF=81=E9=93=BE=E8=B7=AF?= =?UTF-8?q?=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【变更概要】 1. 后端模式开关: 新增 backend-mode.js / USE_NEW_BACKEND 控制走老/新后端 2. token 同步链路: 新增 auth-token-store.js, 改造 request.js 拦截器支持 Bearer token 3. auth 重构 API: 新增 auth-refactor.js (独立模块, 不修改老 login.js) 4. user store: Login action 根据 USE_NEW_BACKEND 切换登录接口 5. login 页: 登录后跳转首页, 错误提示优化 6. dev-server proxy: vue.config.js 动态路由, 支持后端模式切换 7. 页脚 UI 重构: 品牌列 + 4 标题列布局, 响应式适配 8. main.vue 弹窗美化, home2 footer 反向缩放, page-layout CSS 变量调整 9. 双开调试配置: .env.development.new 【生产安全注意点 - 请务必确认】 - 生产构建 CI/CD 不得设置 VUE_APP_USE_NEW_BACKEND=true, 否则 Login 将走新后端 (默认未定义 = false, 走老后端 ry-cloud) - request.js 的 Authorization 头注入仅在 localStorage 有 txw_access_token 时生效, 老用户无此 key, 不会加头, 不影响老后端请求 - 响应拦截器的 token 同步逻辑仅处理含 accessToken 字段的响应体, 老后端不返回该字段, 不会触发 - vue.config.js 仅作用于 dev-server, 生产 Nginx 配置不受影响 --- txw-mhzc-web/.env.development | 19 +- txw-mhzc-web/.env.development.new | 20 + txw-mhzc-web/docs/auth-token-sync.md | 48 +++ txw-mhzc-web/src/config/backend-mode.js | 38 ++ txw-mhzc-web/src/core/request.js | 26 ++ .../src/pages/index/api/auth-refactor.js | 116 ++++++ .../pages/index/components/footer/index.vue | 361 ++++++++++++------ .../src/pages/index/store/modules/user.js | 32 +- .../src/pages/index/styles/page-layout.less | 4 +- .../src/pages/index/views/home2/index.vue | 11 + .../login/components/login/passwordlogin.vue | 26 +- txw-mhzc-web/src/pages/index/views/main.vue | 79 ++-- txw-mhzc-web/src/utils/auth-token-store.js | 74 ++++ txw-mhzc-web/vue.config.js | 104 +++-- 14 files changed, 741 insertions(+), 217 deletions(-) create mode 100644 txw-mhzc-web/.env.development.new create mode 100644 txw-mhzc-web/docs/auth-token-sync.md create mode 100644 txw-mhzc-web/src/config/backend-mode.js create mode 100644 txw-mhzc-web/src/pages/index/api/auth-refactor.js create mode 100644 txw-mhzc-web/src/utils/auth-token-store.js diff --git a/txw-mhzc-web/.env.development b/txw-mhzc-web/.env.development index 67686b7..df7fc57 100644 --- a/txw-mhzc-web/.env.development +++ b/txw-mhzc-web/.env.development @@ -1,16 +1,21 @@ ### - # @Descripttion: - # @Version: 1.0 - # @Author: wjx - # @Date: 2024-02-04 14:09:22 - # @LastEditors: wjx - # @LastEditTime: 2024-04-02 13:48:07 -### + # @Descripttion: 本地开发默认 - 连老后端 (ry-cloud) + # @Usage: yarn serve (默认走这个) + # @Target: txw-gateway(老) 9300 -> Nacos 8848 (namespace: 2fd09a25...) + # @Note: 与 .env.development.new 并存,互不污染;双开时跑两个 dev server +# + # 阶段 1 收尾 BUG-C 配套:后端模式开关 + # VUE_APP_USE_NEW_BACKEND=false → 连老后端 (ry-cloud 9300, 本文件默认) + # VUE_APP_USE_NEW_BACKEND=true → 连新后端 (txw-cloud 8080, 见 .env.development.new) + # 切换无需改代码,改这个值 + 重启 yarn serve 即可。 +### VUE_APP_ENV=dev VUE_APP_MODEL=local VUE_APP_CDN_PATH=/view/mhzc VUE_APP_ROUTER_BASE=/view/mhzc VUE_APP_API_BASE_URL= VUE_APP_DEV_SERVER_PORT=9002 +VUE_APP_MHZC_PROXY=http://localhost:9300 VUE_APP_MOCK=true VUE_APP_AUTO_ROUTER=false +VUE_APP_USE_NEW_BACKEND=false diff --git a/txw-mhzc-web/.env.development.new b/txw-mhzc-web/.env.development.new new file mode 100644 index 0000000..ad6d5f8 --- /dev/null +++ b/txw-mhzc-web/.env.development.new @@ -0,0 +1,20 @@ +### + # @Descripttion: 双开调试专用 - 连新后端 (txw-cloud) + # @Usage: yarn serve --mode development.new + # @Target: txw-gateway(新) 8080 -> Nacos 18848 (namespace: public) + # @Note: 与 .env.development 并存,互不污染 +# + # 阶段 1 收尾 BUG-C 配套:后端模式开关 + # VUE_APP_USE_NEW_BACKEND=true → 走新后端 (本文件) +# 切换回老后端:改 .env.development (默认 VUE_APP_USE_NEW_BACKEND=false) +### +VUE_APP_ENV=dev +VUE_APP_MODEL=local +VUE_APP_CDN_PATH=/view/mhzc +VUE_APP_ROUTER_BASE=/view/mhzc +VUE_APP_API_BASE_URL= +VUE_APP_DEV_SERVER_PORT=9003 +VUE_APP_MHZC_PROXY=http://localhost:8080 +VUE_APP_MOCK=false +VUE_APP_AUTO_ROUTER=false +VUE_APP_USE_NEW_BACKEND=true diff --git a/txw-mhzc-web/docs/auth-token-sync.md b/txw-mhzc-web/docs/auth-token-sync.md new file mode 100644 index 0000000..eced4d3 --- /dev/null +++ b/txw-mhzc-web/docs/auth-token-sync.md @@ -0,0 +1,48 @@ +# 门户前端 token 同步方案(阶段 1 收尾 BUG-C) + +> 范围:`txw-mhzc-web` 门户前端与 `txw-cloud` 网关的鉴权头协商。 +> 基线:本地三后端在跑(auth 9200 / system 9201 / gateway 8080),UUID + JWT 双 Token 模式可工作。 +> 日期:2026-06-07 + +## 1. 问题 + +- `txw-auth` 的 `AuthController.loginByPassword` 把 token 写入 `Cookie: token`(`HttpOnly; Secure`)。 +- 网关 `AuthFilter` 只读 `Authorization: Bearer ` Header,**不读 Cookie**。 +- 后果:门户前端若只依赖 Cookie 携带 token,业务接口经网关会 401。 + +## 2. 决策 + +不改后端(影响网关所有受保护接口),由门户前端在 axios 拦截器把 **响应体中的 token** 同步到 `Authorization` Header。 + +## 3. 实现要点 + +| 点 | 说明 | +|----|------| +| token 来源 | 登录/刷新响应体 `data.accessToken`(不是 Cookie,Cookie 是 HttpOnly,JS 读不到) | +| token 持久化 | `localStorage`(多 tab 共享、刷新不丢) | +| 请求拦截器 | 自动在 `headers.Authorization` 写入 `Bearer ` | +| 响应拦截器 | 登录/刷新成功后 `setTokensFromResponse(data)`;401 失败时 `clearTokens()` | +| 登出 | 不需要前端手动清 Header(后端 expire Cookie 即可,本地 token 由 `clearTokens` 清) | +| 关键文件 | `src/utils/auth-token-store.js`(存取)、`src/core/request.js`(拦截器) | + +## 4. 为什么不用 Cookie 读 + +`HttpOnly` Cookie 是浏览器安全机制,JS 读取会得到空串。若团队坚持用 Cookie,需要: +1. 去掉 `HttpOnly`(安全降级),或 +2. 改后端让网关优先读 Cookie(影响所有受保护接口)。 + +都不采纳。详情见 `refactor-docs/重构方案/阶段1-收尾-问题清单.md` 第 4 节备选。 + +## 5. 联调验收 + +- [ ] 门户登录成功后 `localStorage` 有 `txw_access_token` +- [ ] `Authorization: Bearer ...` 出现在 devtools Network 请求头 +- [ ] 调 `/mhzc/sy/ptgg/list` 等需鉴权接口返回 200 +- [ ] 登出后 `localStorage` 中 `txw_access_token` 被清 +- [ ] 401 时自动清 token 并触发 `showLoginGuide` + +## 6. 不要做的事 + +- 不要在前端代码里直接 `document.cookie` 读 token(HttpOnly 读不到) +- 不要把 token 存到 `sessionStorage`(刷新即丢) +- 不要在 `vuex` 全局状态里存(多 tab 不共享) diff --git a/txw-mhzc-web/src/config/backend-mode.js b/txw-mhzc-web/src/config/backend-mode.js new file mode 100644 index 0000000..4b6b850 --- /dev/null +++ b/txw-mhzc-web/src/config/backend-mode.js @@ -0,0 +1,38 @@ +/** + * 后端模式开关(阶段 1 收尾 BUG-C 配套) + * + *

本文件是切换"老后端 ry-cloud (9300) / 新后端 txw-cloud (8080)"的统一入口。 + * 业务代码根据 {@code USE_NEW_BACKEND} 决定调哪套 API。 + * + *

切换方法(无需改代码): + *

+ * // 老后端 (ry-cloud)
+ * VUE_APP_USE_NEW_BACKEND=false
+ *
+ * // 新后端 (txw-cloud 阶段 2 联调)
+ * VUE_APP_USE_NEW_BACKEND=true
+ * 
+ * + *

环境变量由 webpack 注入(Vue CLI 自动读取 .env / .env.development / .env.[mode]), + * 重启 yarn serve 后生效。 + * + *

参考文档:txw-mhzc-web/docs/auth-token-sync.md + */ + +// webpack DefinePlugin 注入 (process.env.VUE_APP_* 会被静态替换) +// 默认 false,连老后端(兼容老用户) +const rawValue = process.env.VUE_APP_USE_NEW_BACKEND; +const isNew = typeof rawValue === 'string' && rawValue.toLowerCase() === 'true'; + +/** 是否走新后端 (txw-cloud :8080) */ +export const USE_NEW_BACKEND = isNew; + +/** 后端模式: 'legacy' (ry-cloud) | 'new' (txw-cloud) */ +export const BACKEND_MODE = isNew ? 'new' : 'legacy'; + +/** 调试日志: 启动时打印当前后端模式 */ +if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production') { + // 仅 dev 环境提示 + // eslint-disable-next-line no-console + console.info('[backend-mode] current mode:', BACKEND_MODE, '(USE_NEW_BACKEND=' + isNew + ')'); +} diff --git a/txw-mhzc-web/src/core/request.js b/txw-mhzc-web/src/core/request.js index f6f2120..9982db0 100644 --- a/txw-mhzc-web/src/core/request.js +++ b/txw-mhzc-web/src/core/request.js @@ -3,6 +3,7 @@ import { MessagePlugin } from 'tdesign-vue'; import { mhLogout, getRedirectUri } from '@/pages/index/api/login'; import { LoadingPlugin, DialogPlugin } from '@gt4/common-front'; import { showLoginGuide } from '@/pages/index/utils/auth-guard'; +import { getAccessToken, setTokensFromResponse, clearTokens } from '@/utils/auth-token-store'; const SingleLoading = { load: null, @@ -48,6 +49,17 @@ request.interceptors.request.use( if (!url) { return newConf; } + // 阶段 1 收尾 BUG-C:txw-cloud 网关只读 Authorization Header,不读 Cookie token。 + // 从 localStorage 取出 accessToken 写到请求头,未登录态留空。 + if (!newConf.headers) { + newConf.headers = {}; + } + if (!newConf.headers.Authorization) { + const accessToken = getAccessToken(); + if (accessToken) { + newConf.headers.Authorization = `Bearer ${accessToken}`; + } + } if (newConf.method === 'get' && newConf.params) { // 先加时间戳(如果 URL 还没参数) if (url.indexOf('?') === -1) { @@ -112,6 +124,16 @@ request.interceptors.response.use( SingleLoading.endLoading(); } const { code, type } = res.data; + // 阶段 1 收尾 BUG-C:登录/刷新响应同步 token 到 localStorage, + // 供后续请求拦截器写入 Authorization Header。 + // txw-cloud 响应嵌套:{code:200, data:{accessToken, refreshToken}, msg} + // 解包 .data 兼容扁平/嵌套两种形态。 + if (code === 200 && res.data) { + const tokenData = res.data.data || res.data; + if (tokenData.accessToken || tokenData.refreshToken) { + setTokensFromResponse(tokenData); + } + } // 获取错误信息 const msg = res.data.msg || '系统未知错误,请反馈给管理员'; if (code === 401) { @@ -149,6 +171,8 @@ request.interceptors.response.use( } // HTTP 状态码 401 未认证,跳转登录页 if (err.response?.status === 401) { + // 阶段 1 收尾 BUG-C:服务端拒绝 → 清掉本地 token,避免下次请求带过期 token + clearTokens(); // 调用方显式静默 401(如只读列表的公开接口):不弹登录提示, // 让业务层自行 fallback / 提示 / 跳转 const silent = err.config?.__silent401 || err.reqConfig?.__silent401; @@ -254,6 +278,8 @@ request.interceptors.response.use( if (err.reqConfig?.loading || err.config?.loading || SingleLoading.load !== null) { SingleLoading.endLoading(true); } + // 阶段 1 收尾 BUG-C:清掉本地 token + clearTokens(); // 调用方显式静默 401(如只读列表的公开接口):不弹登录提示 const silent = err.config?.__silent401 || err.reqConfig?.__silent401; if (!silent) { diff --git a/txw-mhzc-web/src/pages/index/api/auth-refactor.js b/txw-mhzc-web/src/pages/index/api/auth-refactor.js new file mode 100644 index 0000000..1e2547f --- /dev/null +++ b/txw-mhzc-web/src/pages/index/api/auth-refactor.js @@ -0,0 +1,116 @@ +/** + * 重构测试入口(阶段 1 收尾 BUG-C 联调用) + * + *

不动老业务:本文件是独立模块,不修改 {@link ./login.js} 的任何老接口。 + * 老业务继续走 {@code /sso/auth/login}(若依老 SSO、cookie + rememberMe), + * 本模块只暴露 txw-cloud 新接口供重构验证使用。 + * + *

用法(前端任意位置 import): + *

+ * import { loginByPassword, getInfo, logoutNew, getCaptcha } from '@/pages/index/api/auth-refactor';
+ *
+ * // 1) 登录(响应拦截器会自动写 localStorage.txw_access_token)
+ * await loginByPassword('admin', 'admin123');
+ * console.log(localStorage.getItem('txw_access_token'));
+ *
+ * // 2) 业务请求(request 拦截器会自动塞 Authorization: Bearer ...)
+ * const info = await getInfo();
+ *
+ * // 3) 登出(401 时也会自动 clearTokens)
+ * await logoutNew();
+ * 
+ * + *

或在浏览器 console 直接跑(dev 模式): + *

+ * const { loginByPassword, getInfo } = await import('/src/pages/index/api/auth-refactor.js');
+ * await loginByPassword('admin', 'admin123');
+ * await getInfo();
+ * 
+ * + *

后端基线:txw-cloud 三后端已启动(auth 9200 / system 9201 / gateway 8080), + * 网关 baseURL 由 {@code window.STATIC_ENV_CONFIG.API_PREFIX} 提供(通常是 {@code /})。 + */ + +import { fetch } from '@/core/request'; +import { getAccessToken, clearTokens } from '@/utils/auth-token-store'; + +const baseURL = ''; + +/** + * 新接口:账号密码登录(OAuth2 UUID Token) + * @param {string} username + * @param {string} password + */ +export function loginByPassword(username, password) { + return fetch({ + url: `${baseURL}/auth/loginByPassword`, + method: 'post', + data: { username, password }, + }); +} + +/** + * 新接口:图形验证码 + */ +export function getCaptcha() { + return fetch({ + url: `${baseURL}/auth/verify/captcha`, + method: 'post', + data: {}, + }); +} + +/** + * 新接口:UUID Token 访问 system 业务接口(验证 Authorization 头是否生效) + */ +export function getInfo() { + return fetch({ + url: `${baseURL}/system/user/getInfo`, + method: 'get', + }); +} + +/** + * 新接口:登出(POST,强收敛后的统一入口) + */ +export function logoutNew() { + return fetch({ + url: `${baseURL}/auth/logout`, + method: 'post', + }); +} + +/** + * 调试辅助:返回当前 localStorage 中的 token 状态(不消耗任何配额) + */ +export function debugTokenState() { + return { + accessToken: getAccessToken(), + accessTokenLength: getAccessToken().length, + localStorageKeys: Object.keys(localStorage).filter((k) => k.startsWith('txw_')), + }; +} + +/** + * 调试辅助:强制清掉 localStorage 中的 token(与 401 触发效果一致) + */ +export function debugClearTokens() { + clearTokens(); + return { cleared: true }; +} + +// 阶段 1 收尾 BUG-C:兜底把整套方法挂到 window.__txwRefactor, +// 让 console 可以直接用 `await __txwRefactor.loginByPassword(...)` 调, +// 彻底避开 webpack 动态 import 生成的懒加载 chunk 在 dev server 下被 +// SPA fallback 兜底成 index.html(text/html MIME)的问题。 +// 仅在 dev / 非生产环境暴露,生产构建会走 terser drop_console 路径清理。 +if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production') { + window.__txwRefactor = { + loginByPassword, + getCaptcha, + getInfo, + logoutNew, + debugTokenState, + debugClearTokens, + }; +} diff --git a/txw-mhzc-web/src/pages/index/components/footer/index.vue b/txw-mhzc-web/src/pages/index/components/footer/index.vue index 4c59910..ce9c45b 100644 --- a/txw-mhzc-web/src/pages/index/components/footer/index.vue +++ b/txw-mhzc-web/src/pages/index/components/footer/index.vue @@ -1,80 +1,90 @@ @@ -83,6 +93,12 @@ import { mapState } from 'vuex'; export default { name: 'Footer', + data() { + return { + // 与页头 nav 同款 logo 资源 + logoIconSrc: require('../../assets/logo-figma-icon.png'), + }; + }, computed: { ...mapState('settings', ['contact']), }, @@ -90,17 +106,23 @@ export default { diff --git a/txw-mhzc-web/src/pages/index/views/login/components/login/passwordlogin.vue b/txw-mhzc-web/src/pages/index/views/login/components/login/passwordlogin.vue index a1b4d1b..fc50389 100644 --- a/txw-mhzc-web/src/pages/index/views/login/components/login/passwordlogin.vue +++ b/txw-mhzc-web/src/pages/index/views/login/components/login/passwordlogin.vue @@ -184,31 +184,37 @@ export default { return; } } - const { href } = window.location; this.loading = true; removeUsername(); removePassword(); removeRememberMe(); // 发起登陆 + // 阶段 1 收尾 BUG-C 改造:dispatch('Login') 内部根据 VUE_APP_USE_NEW_BACKEND + // 自动切老/新后端,业务代码无需关心。详见 src/config/backend-mode.js this.$store .dispatch('Login', this.loginForm) .then(() => { console.log('111111'); - // this.$router.replace('/demo/demo').catch(() => ''); - // 本地之外 - // window.location.href = href.replace('sso/login', 'htgl/mhsy'); // 本地 window.sessionStorage.setItem('sfdl', true); - // this.$router.go(-1); + // 跳到首页 window.location.href = `/view/mhzc/home`; }) .catch((error) => { - console.log('22222'); - return; - // if (error === 1004003) { - // this.reSetSlider(); - // } + console.log('22222', error); + this.loading = false; + // 业务错误提示 + const msg = + (error && error.msg) || + (error && error.message) || + '登录失败,请重试'; + // 重新拿验证码(如果需要) + this.refreshCaptcha(); + // eslint-disable-next-line no-undef + if (typeof MessagePlugin !== 'undefined') { + MessagePlugin.error({ content: msg, duration: 2000 }); + } }); }, handleCounter() { diff --git a/txw-mhzc-web/src/pages/index/views/main.vue b/txw-mhzc-web/src/pages/index/views/main.vue index e32446a..8e00a72 100644 --- a/txw-mhzc-web/src/pages/index/views/main.vue +++ b/txw-mhzc-web/src/pages/index/views/main.vue @@ -18,15 +18,16 @@ - + 问题:txw-cloud 网关 AuthFilter 只读 {@code Authorization: Bearer } Header, + * 不读 {@code Cookie: token}。门户后端(AuthController.loginByPassword)把 token 写入 + * HttpOnly Cookie,JS 无法读取。若前端只依赖 Cookie,业务接口经网关会 401。 + * + *

解决:登录成功后由 axios 响应拦截器从响应体 {@code data.accessToken} 写入本工具, + * 后续请求拦截器读取并塞到 {@code Authorization} Header。登出由后端清 Cookie, + * 前端无需手动清 Header。 + * + *

为什么不用 Cookie:HttpOnly 标志是浏览器安全机制,JS 读取会得到空串。 + * 为什么不用 sessionStorage:刷新即丢,体验差;多 tab 共享需 localStorage。 + */ + +const ACCESS_TOKEN_KEY = 'txw_access_token'; +const REFRESH_TOKEN_KEY = 'txw_refresh_token'; + +export function getAccessToken() { + try { + return localStorage.getItem(ACCESS_TOKEN_KEY) || ''; + } catch (e) { + return ''; + } +} + +export function getRefreshToken() { + try { + return localStorage.getItem(REFRESH_TOKEN_KEY) || ''; + } catch (e) { + return ''; + } +} + +/** + * 从登录/刷新响应体里同步 token;要求形如 + *

{ accessToken: '...', refreshToken: '...' }
+ * 注意:调用方需保证传入的是 token 字段**直接所在**的对象。 + * 如果是嵌套响应体(如 txw-cloud 的 {code, data:{accessToken}, msg}), + * 调用方应先解包 .data 再传入。 + * + * @param {Object} data 含 accessToken/refreshToken 字段的对象 + * @returns {string} 写入后的 accessToken(可能为空) + */ +export function setTokensFromResponse(data) { + if (!data || typeof data !== 'object') { + return ''; + } + const { accessToken, refreshToken } = data; + if (accessToken) { + try { + localStorage.setItem(ACCESS_TOKEN_KEY, accessToken); + } catch (e) { + // 隐私模式可能写不进去,降级到内存 + } + } + if (refreshToken) { + try { + localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken); + } catch (e) { + // 同上 + } + } + return accessToken || ''; +} + +export function clearTokens() { + try { + localStorage.removeItem(ACCESS_TOKEN_KEY); + localStorage.removeItem(REFRESH_TOKEN_KEY); + } catch (e) { + // 同上 + } +} diff --git a/txw-mhzc-web/vue.config.js b/txw-mhzc-web/vue.config.js index c2f5e32..9d86f2a 100644 --- a/txw-mhzc-web/vue.config.js +++ b/txw-mhzc-web/vue.config.js @@ -287,48 +287,68 @@ module.exports = { }, // Vue CLI prepareProxy 用 pathname.match(代理键) 判断;键写 '/mhzc' 会变成匹配路径里任意位置的 /mhzc, // 会误伤 SPA 路由 /view/mhzc/...,刷新时整页请求被转发到后端导致 Proxy error。必须用 ^ 限定为路径前缀。 - proxy: { - // 阶段 1 收尾 BUG-C:auth-refactor.js 调 /auth/loginByPassword(txw-cloud 新栈)。 - // 8080 是新 gateway(auth 9200 / system 9201 都在它后面),不配这条会落到 SPA fallback。 - '^/auth': { - target: 'http://localhost:8080', - changeOrigin: true, - }, - '^/sso/did/pub': { - // 阶段 2 BUG-D:login.vue 的 DID 扫码轮询(每 2s 一次)打 /sso/did/pub/backresult/login, - // 老栈 target 是 cciw.com.cn 生产,本地 reqId/cookie 校验失败会一直返回"密码错误"。 - // 新栈 nacos 白名单有 /auth/did/pub/**,把 /sso/did/pub/** 改写到 8080 的 /auth/did/pub/**。 - // 必须放在 '^/sso' 之前,否则会被广匹配的 '^/sso' 截走。 - target: 'http://localhost:8080', - changeOrigin: true, - pathRewrite: { '^/sso/did/pub': '/auth/did/pub' }, - }, - '^/sso': { - // target: 'http://localhost:9300', - // target: 'http://carbon.liantu.tech', - target: 'https://www.cciw.com.cn', - // target: 'http://10.23.20.13:94/', - changeOrigin: true, - }, - '^/mhzc': { - target: process.env.VUE_APP_MHZC_PROXY || 'https://www.cciw.com.cn', - changeOrigin: true, - }, - '^/gxzx': { - // target: 'http://localhost:9300', - // target: 'http://carbon.liantu.tech', - target: 'https://www.cciw.com.cn', - // target: 'http://10.23.20.13:94/', - changeOrigin: true, - }, - '^/yygl': { - // target: 'http://localhost:20010', - // target: 'http://carbon.liantu.tech', - target: 'https://www.cciw.com.cn', - // target: 'http://10.23.20.13:94/', - changeOrigin: true, - }, - }, + // + // 阶段 1 收尾 BUG-C:/auth /system proxy 以前硬编码 8080 (txw-cloud 新栈), + // 老栈模式 (USE_NEW_BACKEND=false) 时也照打 8080,导致切换不彻底。 + // 现在根据 VUE_APP_USE_NEW_BACKEND 动态选 target: + // true → 新栈 txw-cloud 8080 + // false → 老栈,走 VUE_APP_MHZC_PROXY (默认 localhost:9300) + proxy: (() => { + const useNew = process.env.VUE_APP_USE_NEW_BACKEND === 'true'; + const newTarget = 'http://localhost:8080'; + const legacyTarget = process.env.VUE_APP_MHZC_PROXY || 'http://localhost:9300'; + const gatewayTarget = useNew ? newTarget : legacyTarget; + // eslint-disable-next-line no-console + console.log( + `====>> 后端模式: ${useNew ? 'new (txw-cloud 8080)' : 'legacy (' + legacyTarget + ')'}`, + ); + return { + // auth-refactor.js 调 /auth/loginByPassword;txw-cloud 8080 网关后面挂着 auth 9200。 + // 老栈模式: 走 VUE_APP_MHZC_PROXY (默认 9300 老 gateway)。 + '^/auth': { + target: gatewayTarget, + changeOrigin: true, + }, + // getInfo 调 /system/user/getInfo;txw-system 9201 业务接口在 8080 网关后面。 + '^/system': { + target: gatewayTarget, + changeOrigin: true, + }, + // DID 扫码轮询 (login.vue 每 2s 一次) 改写到新栈的 /auth/did/pub/**。 + // 隔离原则: 老栈模式 (USE_NEW_BACKEND=false) 也打老栈 gatewayTarget (9300), + // 即使老栈没这个接口 → 502/404 → 前端 catch 兜底, 不让老栈 dev server 跨栈打新栈。 + // 历史: 重构前 (B984406 之前) 这条根本没配, /sso/did/pub 走 ^/sso 打 cciw.com.cn 生产, + // 一直"密码错误"。BUG-D 修复后才配。隔离之后, 老栈的扫码自然 502, 行为收敛。 + '^/sso/did/pub': { + target: gatewayTarget, + changeOrigin: true, + pathRewrite: { '^/sso/did/pub': '/auth/did/pub' }, + }, + '^/sso': { + // 老栈 target 走 cciw.com.cn 生产 (历史行为保留)。 + target: 'https://www.cciw.com.cn', + changeOrigin: true, + }, + '^/mhzc': { + target: process.env.VUE_APP_MHZC_PROXY || 'https://www.cciw.com.cn', + changeOrigin: true, + }, + '^/gxzx': { + // target: 'http://localhost:9300', + // target: 'http://carbon.liantu.tech', + target: 'https://www.cciw.com.cn', + // target: 'http://10.23.20.13:94/', + changeOrigin: true, + }, + '^/yygl': { + // target: 'http://localhost:20010', + // target: 'http://carbon.liantu.tech', + target: 'https://www.cciw.com.cn', + // target: 'http://10.23.20.13:94/', + changeOrigin: true, + }, + }; + })(), before(app) { if (process.env.VUE_APP_MOCK === 'true') { // 是否开启MOCK,默认开启,检查项目根目录下是否存在.env.development文件 内容为VUE_APP_MOCK=true