解决中文编码错误
This commit is contained in:
@@ -3,6 +3,26 @@ import { getToken, setToken, removeToken } from '@/utils/auth'
|
|||||||
import { removeBusinessHeaders } from '@/utils/business-headers'
|
import { removeBusinessHeaders } from '@/utils/business-headers'
|
||||||
import { resetRouter } from '@/router'
|
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 = () => {
|
const getDefaultState = () => {
|
||||||
return {
|
return {
|
||||||
token: getToken(),
|
token: getToken(),
|
||||||
@@ -39,8 +59,18 @@ const actions = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login({ username: username.trim(), password: password }).then(response => {
|
login({ username: username.trim(), password: password }).then(response => {
|
||||||
const { data } = response
|
const { data } = response
|
||||||
|
if (!data || !data.token) {
|
||||||
|
reject(new Error('登录响应缺少 token,请检查接口或联系管理员'))
|
||||||
|
return
|
||||||
|
}
|
||||||
commit('SET_TOKEN', data.token)
|
commit('SET_TOKEN', data.token)
|
||||||
setToken(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()
|
resolve()
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
@@ -57,14 +87,14 @@ const actions = {
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return reject('Verification failed, please Login again.')
|
return reject('Verification failed, please Login again.')
|
||||||
}
|
}
|
||||||
console.log('getInfo 0812 :', data)
|
const row = typeof data === 'object' && data !== null && !Array.isArray(data) ? data : {}
|
||||||
const { userName, avatar, phone } = data
|
const profile = pickUserDisplayFields(row, '')
|
||||||
console.log('getInfo userName 0812 :', userName)
|
if (!profile.userName) {
|
||||||
console.log('getInfo avatar 0812 :', avatar)
|
return reject('获取用户信息失败:缺少用户名字段,请检查 getUserInfoByToken 返回结构')
|
||||||
console.log('getInfo phone 0812 :', phone)
|
}
|
||||||
commit('SET_NAME', userName)
|
commit('SET_NAME', profile.userName)
|
||||||
commit('SET_AVATAR', avatar)
|
commit('SET_AVATAR', profile.avatar || '')
|
||||||
commit('SET_PHONE', phone || '')
|
commit('SET_PHONE', profile.phone || '')
|
||||||
resolve(data)
|
resolve(data)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
|
|||||||
@@ -26,6 +26,28 @@ function pickValueFromSources(sources, keys) {
|
|||||||
return ''
|
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
|
// create an axios instance
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: '/api', // 统一使用 /api 前缀
|
baseURL: '/api', // 统一使用 /api 前缀
|
||||||
@@ -51,7 +73,10 @@ service.interceptors.request.use(
|
|||||||
|
|
||||||
const businessHeaders = getBusinessHeaders()
|
const businessHeaders = getBusinessHeaders()
|
||||||
Object.keys(businessHeaders).forEach((key) => {
|
Object.keys(businessHeaders).forEach((key) => {
|
||||||
config.headers[key] = businessHeaders[key]
|
const safe = coerceBusinessHeaderValueForXHR(key, businessHeaders[key])
|
||||||
|
if (safe !== '') {
|
||||||
|
config.headers[key] = safe
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
@@ -87,17 +112,33 @@ service.interceptors.response.use(
|
|||||||
),
|
),
|
||||||
'X-Account-Name': pickValueFromSources(
|
'X-Account-Name': pickValueFromSources(
|
||||||
[response.headers, response.data?.data, response.data],
|
[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')) {
|
if (response.config?.url?.includes('/sys/auth/login')) {
|
||||||
|
const loginPayload = response.data?.data || {}
|
||||||
const normalizedFromHeader = {
|
const normalizedFromHeader = {
|
||||||
'X-Role-Name': pickHeaderCaseInsensitive(response.headers, 'X-Role-Name') || loginHeaderValues['X-Role-Name'],
|
'X-Role-Name': pickHeaderCaseInsensitive(response.headers, 'X-Role-Name') || loginHeaderValues['X-Role-Name'],
|
||||||
'X-Scenario': pickHeaderCaseInsensitive(response.headers, 'X-Scenario') || loginHeaderValues['X-Scenario'],
|
'X-Scenario': pickHeaderCaseInsensitive(response.headers, 'X-Scenario') || loginHeaderValues['X-Scenario'],
|
||||||
'X-Tenant-Id': pickHeaderCaseInsensitive(response.headers, 'X-Tenant-Id') || loginHeaderValues['X-Tenant-Id'],
|
'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']
|
'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)
|
setBusinessHeaders(normalizedFromHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user