From 7b74736980e9f9ce750da4319955c3fd2fcbbe06 Mon Sep 17 00:00:00 2001 From: Hermes CI Fix Date: Tue, 15 Sep 2026 22:21:56 +0800 Subject: [PATCH] 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). --- frontend/src/api/index.ts | 10 +++++ frontend/src/views/Dashboard.vue | 25 +++++------- frontend/src/views/DataClassification.vue | 18 +++------ frontend/src/views/DupontAnalysis.vue | 11 +---- frontend/src/views/KPIAlignment.vue | 30 +++++--------- frontend/src/views/MpmCalculator.vue | 12 ++---- frontend/src/views/ReportCenter.vue | 39 ++++++++---------- frontend/src/views/RolePermissions.vue | 49 +++++++---------------- frontend/src/views/SubjectManage.vue | 12 ++---- 9 files changed, 73 insertions(+), 133 deletions(-) diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 53f0b0e0..a05991ec 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -339,10 +339,20 @@ export const roleApi = { export const permissionApi = { getMenuTree: () => api.get('/permissions/menu-tree'), + getModules: () => api.get('/permissions/modules'), + getConfig: () => api.get('/permissions/config'), + saveConfig: (data: any) => api.put('/permissions/config', data), getUserRoles: () => api.get('/user-roles'), setUserRoles: (data: any) => api.post('/user-roles', data), } +// ── KPI目标对齐(/alignment/* 由路由级 require_auth 保护,统一走本实例)── +export const alignmentApi = { + getConfig: () => api.get('/alignment/config'), + saveConfig: (data: any) => api.post('/alignment/config', data), + getTree: (params?: any) => api.get('/alignment/tree', { params }), +} + export const kpiCausalityApi = { list: (params?: any) => api.get('/kpi-causality', { params }), get: (id: number) => api.get(`/kpi-causality/${id}`), diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue index f4edd380..d637288c 100644 --- a/frontend/src/views/Dashboard.vue +++ b/frontend/src/views/Dashboard.vue @@ -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 = { 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) } diff --git a/frontend/src/views/DataClassification.vue b/frontend/src/views/DataClassification.vue index 6583d874..9a0cb6d5 100644 --- a/frontend/src/views/DataClassification.vue +++ b/frontend/src/views/DataClassification.vue @@ -176,14 +176,8 @@