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:
Hermes CI Fix
2026-09-15 22:21:56 +08:00
parent e5eb092d20
commit 7b74736980
9 changed files with 73 additions and 133 deletions
+17 -22
View File
@@ -619,7 +619,8 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import axios from 'axios'
// 统一走 api/index.ts 的 axios 实例(拦截器自动带 Bearer / 统一 401 跳登录 / 超时提示)
import api from '../api/index'
import { getFixedEntityId } from '../utils/entity'
// ── 状态 ──
@@ -644,13 +645,7 @@ function getEntityId() {
return Number(localStorage.getItem('cma_entity_id') || currentEntityId.value || 1)
}
// API 配置
const api = axios.create({ baseURL: '/api/cma', timeout: 30000 })
api.interceptors.request.use((config: any) => {
const token = localStorage.getItem('cma_token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
// API 配置:统一使用 api/index.ts 的共享实例(本地 axios 实例 + 手拼 Bearer 已移除)
// ── 报表1:管理利润表 ──
const profitItems = ref<any[]>([])
@@ -688,7 +683,7 @@ async function loadNew30() {
params.format = 'new'
}
const r = await api.get('/reports/profit-statement', { params })
const d = (r as any).data || {}
const d = (r as any) || {}
if (new30Format.value === 'dual') {
dualComparisonData.value = d
@@ -749,7 +744,7 @@ async function loadStatutory() {
statutoryLoading.value = true
try {
const r = await api.get('/reports/statutory', { params: { period: reportPeriod.value, entity_id: getEntityId() } })
const d = (r as any).data || {}
const d = (r as any) || {}
statutoryData.value = d
statutoryDemoMode.value = !d.cash_flow?.all_items_have_data
} catch (e) {
@@ -762,11 +757,11 @@ async function loadStatutory() {
async function exportStatutory() {
exporting.value = true
try {
const token = localStorage.getItem('cma_token') || ''
const url = `/api/cma/reports/statutory/export?period=${reportPeriod.value}&entity_id=${getEntityId()}`
const resp = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } })
if (!resp.ok) throw new Error('导出失败')
const blob = await resp.blob()
// 统一走 api 实例(responseType=blob;Bearer 由拦截器注入,非 2xx 直接 reject)
const blob: any = await api.get('/reports/statutory/export', {
params: { period: reportPeriod.value, entity_id: getEntityId() },
responseType: 'blob',
})
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = `对外法定报表_${reportPeriod.value}.xlsx`
@@ -811,7 +806,7 @@ async function loadRestatement() {
loadingRestatement.value = true
try {
const r = await api.get('/reports/restatement', { params: { period: reportPeriod.value, entity_id: getEntityId() } })
const d = (r as any).data || {}
const d = (r as any) || {}
restatementItems.value = d.items || []
restatementSummary.value = d.summary || null
restatementNetProfit.value = d.net_profit_comparison || null
@@ -864,7 +859,7 @@ function onPeriodChange() {
async function loadProfit() {
try {
const r = await api.get('/reports/profit-summary', { params: { period: reportPeriod.value, entity_id: getEntityId() } })
profitItems.value = (r as any).data?.items || []
profitItems.value = (r as any)?.items || []
} catch { profitItems.value = [] }
}
@@ -874,7 +869,7 @@ async function loadBudget() {
if (budgetDim.value) params.dimension = budgetDim.value
if (budgetLevel.value) params.alert_level = budgetLevel.value
const r = await api.get('/reports/budget-execution', { params })
const d = (r as any).data || {}
const d = (r as any) || {}
budgetSummary.value = d.summary || {}
budgetItems.value = d.items || []
} catch { budgetItems.value = [] }
@@ -925,7 +920,7 @@ async function loadProforma() {
cash: '/reports/proforma/cash-flow',
}
const r = await api.get(map[proformaType.value], { params: { period: reportPeriod.value, entity_id: getEntityId() } })
proformaData.value = (r as any).data || {}
proformaData.value = (r as any) || {}
} catch (e) {
proformaData.value = null
ElMessage.error('加载预编报表失败')
@@ -953,7 +948,7 @@ async function loadTrends() {
if (trendKpiId.value) params.kpi_id = trendKpiId.value
else if (trendDim.value) params.dimension = trendDim.value
const r = await api.get('/reports/kpi-trends', { params })
trendData.value = (r as any).data?.data || []
trendData.value = (r as any)?.data || []
nextTick(() => renderTrendCharts())
} catch { trendData.value = [] }
}
@@ -961,14 +956,14 @@ async function loadTrends() {
async function loadBsc() {
try {
const r = await api.get('/reports/bsc-scorecard', { params: { period: reportPeriod.value, entity_id: getEntityId() } })
bscData.value = (r as any).data || {}
bscData.value = (r as any) || {}
} catch { bscData.value = {} }
}
async function loadKpiOptions() {
try {
const r = await api.get('/kpis', { params: { page_size: 100, entity_id: getEntityId() } })
kpiOptions.value = (r as any).data?.data || []
kpiOptions.value = (r as any)?.data || []
} catch {}
}