From 421912174d027369a2bc8eea538c4adbd95811ec Mon Sep 17 00:00:00 2001 From: "zhonghua.li" Date: Sat, 2 May 2026 16:35:35 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=B8=AD=E6=96=87=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/modules/user.js | 46 ++++++++++++++++++++++++++++++++------- src/utils/request.js | 45 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 1c42d54..fb2620a 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -3,6 +3,26 @@ import { getToken, setToken, removeToken } from '@/utils/auth' import { removeBusinessHeaders } from '@/utils/business-headers' import { resetRouter } from '@/router' +/** 从登录/用户信息 payload 中解析展示名,兼容多种后端字段名 */ +function pickUserDisplayFields(payload, fallbackAccount) { + if (!payload || typeof payload !== 'object') { + return { userName: (fallbackAccount || '').trim(), avatar: '', phone: '' } + } + const userName = + payload.userName || + payload.name || + payload.loginAccount || + payload.user_name || + payload.accountName || + (fallbackAccount || '').trim() || + '' + return { + userName, + avatar: payload.avatar != null ? payload.avatar : '', + phone: payload.phone || payload.mobile || payload.telephone || '' + } +} + const getDefaultState = () => { return { token: getToken(), @@ -39,8 +59,18 @@ const actions = { return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => { const { data } = response + if (!data || !data.token) { + reject(new Error('登录响应缺少 token,请检查接口或联系管理员')) + return + } commit('SET_TOKEN', data.token) setToken(data.token) + // 登录接口已返回用户信息时直接写入 store,避免路由守卫再去调 getUserInfoByToken + //(该接口若失败或未对接,会清除 token 并退回登录页,表现为「登录成功却进不了系统」) + const profile = pickUserDisplayFields(data, username) + commit('SET_NAME', profile.userName) + commit('SET_AVATAR', profile.avatar || '') + commit('SET_PHONE', profile.phone || '') resolve() }).catch(error => { reject(error) @@ -57,14 +87,14 @@ const actions = { if (!data) { return reject('Verification failed, please Login again.') } - console.log('getInfo 0812 :', data) - const { userName, avatar, phone } = data - console.log('getInfo userName 0812 :', userName) - console.log('getInfo avatar 0812 :', avatar) - console.log('getInfo phone 0812 :', phone) - commit('SET_NAME', userName) - commit('SET_AVATAR', avatar) - commit('SET_PHONE', phone || '') + const row = typeof data === 'object' && data !== null && !Array.isArray(data) ? data : {} + const profile = pickUserDisplayFields(row, '') + if (!profile.userName) { + return reject('获取用户信息失败:缺少用户名字段,请检查 getUserInfoByToken 返回结构') + } + commit('SET_NAME', profile.userName) + commit('SET_AVATAR', profile.avatar || '') + commit('SET_PHONE', profile.phone || '') resolve(data) }).catch(error => { reject(error) diff --git a/src/utils/request.js b/src/utils/request.js index 6748d10..800525d 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -26,6 +26,28 @@ function pickValueFromSources(sources, keys) { return '' } +/** 浏览器 XHR 要求请求头值为 ISO-8859-1,含中文等会触发 setRequestHeader 异常 */ +function isHeaderValueLatin1Safe(value) { + if (value == null) return true + const s = String(value) + for (let i = 0; i < s.length; i++) { + if (s.charCodeAt(i) > 255) return false + } + return true +} + +/** 写入 XMLHttpRequest 前的业务头安全化;账号名常用中文展示,需回退到手机号等 ASCII */ +function coerceBusinessHeaderValueForXHR(headerKey, rawValue) { + const v = rawValue == null ? '' : String(rawValue).trim() + if (v === '') return '' + if (isHeaderValueLatin1Safe(v)) return v + if (headerKey === 'X-Account-Name') { + const phone = store.getters.phone ? String(store.getters.phone).trim() : '' + if (phone && isHeaderValueLatin1Safe(phone)) return phone + } + return '' +} + // create an axios instance const service = axios.create({ baseURL: '/api', // 统一使用 /api 前缀 @@ -51,7 +73,10 @@ service.interceptors.request.use( const businessHeaders = getBusinessHeaders() Object.keys(businessHeaders).forEach((key) => { - config.headers[key] = businessHeaders[key] + const safe = coerceBusinessHeaderValueForXHR(key, businessHeaders[key]) + if (safe !== '') { + config.headers[key] = safe + } }) return config }, @@ -87,17 +112,33 @@ service.interceptors.response.use( ), 'X-Account-Name': pickValueFromSources( [response.headers, response.data?.data, response.data], - ['X-Account-Name', 'x-account-name', 'accountName', 'account_name', 'loginAccount', 'userName', 'username'] + [ + 'X-Account-Name', 'x-account-name', + 'phone', 'mobile', 'telephone', + 'loginAccount', 'username', + 'accountName', 'account_name', + 'userName' + ] ) } if (response.config?.url?.includes('/sys/auth/login')) { + const loginPayload = response.data?.data || {} const normalizedFromHeader = { 'X-Role-Name': pickHeaderCaseInsensitive(response.headers, 'X-Role-Name') || loginHeaderValues['X-Role-Name'], 'X-Scenario': pickHeaderCaseInsensitive(response.headers, 'X-Scenario') || loginHeaderValues['X-Scenario'], 'X-Tenant-Id': pickHeaderCaseInsensitive(response.headers, 'X-Tenant-Id') || loginHeaderValues['X-Tenant-Id'], 'X-Account-Name': pickHeaderCaseInsensitive(response.headers, 'X-Account-Name') || loginHeaderValues['X-Account-Name'] } + if (!isHeaderValueLatin1Safe(normalizedFromHeader['X-Account-Name'])) { + const asciiAccount = pickValueFromSources( + [loginPayload], + ['phone', 'mobile', 'telephone', 'loginAccount', 'username'] + ) + if (asciiAccount && isHeaderValueLatin1Safe(asciiAccount)) { + normalizedFromHeader['X-Account-Name'] = asciiAccount + } + } setBusinessHeaders(normalizedFromHeader) }