@@ -858,6 +914,73 @@ async function loadBudget() {
} catch { budgetItems.value = [] }
}
+// ── 预编报表(预算版三张报表)──
+const loadingProforma = ref(false)
+const proformaType = ref('profit')
+const proformaData = ref
(null)
+
+const proformaBudgetVersion = computed(() => {
+ const v = proformaData.value?.budget_version
+ return Array.isArray(v) ? v.join(', ') : (v || '')
+})
+
+const proformaRows = computed(() => {
+ const d = proformaData.value
+ if (!d) return []
+ const rows: any[] = []
+ if (proformaType.value === 'profit') {
+ for (const b of (d.blocks || [])) {
+ rows.push({ name: b.name, subtotal: true, actual_value: b.subtotal_actual, budget_value: b.subtotal_budget, has_budget: b.has_budget })
+ for (const it of (b.items || [])) rows.push({ ...it, name: it.name })
+ }
+ rows.push({ name: '= 净利润', subtotal: true, actual_value: d.net_profit_actual, budget_value: d.net_profit_budget, has_budget: true })
+ } else if (proformaType.value === 'balance') {
+ for (const s of (d.sections || [])) {
+ rows.push({ name: s.name, subtotal: true, actual_value: s.subtotal_actual, budget_value: s.subtotal_budget, has_budget: s.has_budget })
+ for (const ln of (s.lines || [])) rows.push({ ...ln, name: ln.name })
+ }
+ } else {
+ for (const s of (d.sections || [])) {
+ rows.push({ name: s.name, subtotal: true, actual_value: s.net_actual, budget_value: s.net_budget, has_budget: s.has_budget })
+ for (const ln of (s.lines || [])) rows.push({ ...ln, name: ln.name })
+ }
+ const sm = d.summary || {}
+ rows.push({ name: '= 现金净增加额', subtotal: true, actual_value: sm.net_increase_actual, budget_value: sm.net_increase_budget, has_budget: true })
+ }
+ return rows
+})
+
+async function loadProforma() {
+ loadingProforma.value = true
+ try {
+ const map: Record = {
+ profit: '/reports/proforma/profit-statement',
+ balance: '/reports/proforma/balance-sheet',
+ 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 || {}
+ } catch (e) {
+ proformaData.value = null
+ ElMessage.error('加载预编报表失败')
+ } finally {
+ loadingProforma.value = false
+ }
+}
+
+function proformaRowClass({ row }: any) {
+ if (row.subtotal) return 'proforma-subtotal'
+ if (!row.has_budget) return 'proforma-no-budget'
+ return ''
+}
+
+function proformaRateType(rate: number): string {
+ const abs = Math.abs(rate)
+ if (abs > 20) return 'danger'
+ if (abs > 10) return 'warning'
+ return 'success'
+}
+
async function loadTrends() {
try {
const params: any = { months: trendMonths.value, entity_id: getEntityId() }
@@ -885,7 +1008,7 @@ async function loadKpiOptions() {
function loadAll() {
loading.value = true
- Promise.all([loadProfit(), loadNew30(), loadBudget(), loadTrends(), loadBsc(), loadRestatement()]).finally(() => { loading.value = false })
+ Promise.all([loadProfit(), loadNew30(), loadBudget(), loadProforma(), loadTrends(), loadBsc(), loadRestatement()]).finally(() => { loading.value = false })
}
function openDupont() {
@@ -954,6 +1077,10 @@ onMounted(() => {
.report-tabs { margin-bottom: 20px; }
.report-desc { font-size: 13px; color: #888; padding: 8px 0; }
+/* 预编报表(预算版) */
+:deep(.proforma-no-budget) { opacity: 0.55; }
+:deep(.proforma-subtotal) { background: #f0f7ff !important; font-weight: 600; color: #1f2d3d; }
+
/* 统计卡片 */
.summary-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }
.stat-card { background: #fff; border-radius: 8px; padding: 12px 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border-top: 3px solid #ccc; }