refactor(cma-frontend): route all API calls through shared axios instance (cma-frontend-axios-unify-20260915)
api/index.ts: add permissionApi.getModules/getConfig/saveConfig + alignmentApi.config/tree. views: replace 13 raw fetch calls and 6 local axios.create instances (each hand-building the Authorization header, without the shared 401 redirect / timeout toast) with the shared instance from api/index.ts. KPIAlignment: fixes a live bug - the header value was the string literal 'Bearer ' + token, so /alignment/tree always returned 401 (reproduced against the production bundle). Unwrap semantics preserved: the shared response interceptor returns response.data, so call sites that used res.data were adjusted accordingly. Verified: scan-frontend-tokenless RAW-TOKENLESS=0; vite build ok; headless A/B (new dist vs production bundle, 8 affected pages, real login) -> identical rendered rows and identical pre-existing error profile, /alignment 401->200. Push not executed: main is ahead 4 with other tickets' gated commits (see cma-main-ahead3-push-gate-notice-20260915).
This commit is contained in:
@@ -271,8 +271,8 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Loading, ArrowRight } from '@element-plus/icons-vue'
|
||||
import { dashboardApi, alertApi, aiSuggestionApi } from '../api/index'
|
||||
import axios from 'axios'
|
||||
import { dashboardApi, alertApi, aiSuggestionApi, notificationApi } from '../api/index'
|
||||
import api from '../api/index'
|
||||
import CountUp from '../components/CountUp.vue'
|
||||
import GrowthQuality from '../components/GrowthQuality.vue'
|
||||
import BEPBreakEvenCard from '../components/BEPBreakEvenCard.vue'
|
||||
@@ -346,12 +346,7 @@ async function loadSuggestions() {
|
||||
suggestionsLoading.value = false
|
||||
}
|
||||
|
||||
const aiApi = axios.create({ baseURL: '/api/cma', timeout: 30000 })
|
||||
aiApi.interceptors.request.use((config: any) => {
|
||||
const token = localStorage.getItem('cma_token')
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`
|
||||
return config
|
||||
})
|
||||
// AI 分析等接口统一走 api/index.ts 的共享实例(此前本地 aiApi 手拼 Bearer 已移除)
|
||||
|
||||
const dimConfig: Record<string, { icon: string; color: string; name: string }> = {
|
||||
finance: { icon: '💰', color: '#409eff', name: '财务维度' },
|
||||
@@ -423,8 +418,8 @@ async function loadKPIAnalysis() {
|
||||
if (!selectedKPI.value?.id) return
|
||||
loadingDetail.value = true
|
||||
try {
|
||||
const r: any = await aiApi.get(`/ai/kpi-analysis/${selectedKPI.value.id}`)
|
||||
selectedKPIDetail.value = r.data?.analysis || r.analysis || '暂无分析结果'
|
||||
const r: any = await api.get(`/ai/kpi-analysis/${selectedKPI.value.id}`)
|
||||
selectedKPIDetail.value = r?.analysis || '暂无分析结果'
|
||||
} catch { selectedKPIDetail.value = 'AI分析请求失败' }
|
||||
loadingDetail.value = false
|
||||
}
|
||||
@@ -432,8 +427,8 @@ async function loadKPIAnalysis() {
|
||||
async function refreshAnalysis() {
|
||||
aiLoading.value = true
|
||||
try {
|
||||
const r: any = await aiApi.get('/ai/dashboard-analysis', { params: { role: userRole.value } })
|
||||
aiAnalysis.value = r.data?.analysis || r.analysis || '暂无分析'
|
||||
const r: any = await api.get('/ai/dashboard-analysis', { params: { role: userRole.value } })
|
||||
aiAnalysis.value = r?.analysis || '暂无分析'
|
||||
// 建议列表从服务端拉取(含dashboard分析自动生成的规则建议)
|
||||
await loadSuggestions()
|
||||
} catch { aiAnalysis.value = 'AI分析暂时不可用' }
|
||||
@@ -482,10 +477,8 @@ async function loadData() {
|
||||
finSummary.value = (r as any)?.summary || {}
|
||||
dataRange.value = (r as any)?.period || ''
|
||||
} else if (userRole.value === 'it') {
|
||||
// 鉴权收口(cma-api-auth-surface-audit-20260914):/notifications/channels 已要求 Bearer
|
||||
const channelRes = await axios.get('/api/cma/notifications/channels', {
|
||||
headers: { 'Authorization': `Bearer ${localStorage.getItem('cma_token')}` },
|
||||
}).then(r => r.data)
|
||||
// 鉴权收口(cma-api-auth-surface-audit-20260914)+ 实例统一(2026-09-15):走共享实例,Bearer 由拦截器注入
|
||||
const channelRes: any = await notificationApi.list()
|
||||
enabledChannels.value = channelRes.data?.filter((c: any) => c.enabled).length || 0
|
||||
}
|
||||
} catch (e) { console.error('加载数据失败:', e) }
|
||||
|
||||
Reference in New Issue
Block a user