feat: 新30号准则P2 — MPM计算器+旧格式双列对比

This commit is contained in:
Hermes CI Fix
2026-07-22 12:23:40 +08:00
parent 748c2da43f
commit cbcc0d0a28
5 changed files with 705 additions and 14 deletions
+92 -9
View File
@@ -92,7 +92,7 @@
</div>
</div>
<div class="new30-net-profit">
<div class="new30-net-profit" v-if="new30Format !== 'dual'">
<div class="new30-profit-row">
<span class="new30-profit-label">合计</span>
<span class="new30-profit-value" :class="{ negative: new30NetProfit < 0 }">
@@ -102,9 +102,57 @@
</div>
</div>
<!-- 双列对比模式 -->
<div v-if="new30Format === 'dual' && dualComparisonData" class="dual-comparison">
<div class="dual-desc">新旧准则双列对比 旧准则一行到底 vs 新准则五板块</div>
<div class="dual-grid">
<!-- 旧准则列 -->
<div class="dual-col">
<div class="dual-col-header old-col">旧准则格式</div>
<el-table :data="dualComparisonData.old_format?.items || []" border stripe size="small" style="width:100%;">
<el-table-column prop="name" label="项目" width="180">
<template #default="{ row }">
<span :style="{ fontWeight: row.is_total ? 700 : row.is_subtotal ? 600 : 400 }">{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column label="金额" width="140" align="right">
<template #default="{ row }">
<span :style="{ fontWeight: row.is_total ? 700 : 400 }">
{{ row.value != null ? '¥ ' + row.value.toLocaleString() : '-' }}
</span>
</template>
</el-table-column>
</el-table>
</div>
<!-- 新准则列 -->
<div class="dual-col">
<div class="dual-col-header new-col">新30号准则格式</div>
<div class="dual-blocks">
<div v-for="block in dualComparisonData.new_format?.blocks || []" :key="block.key" class="dual-block">
<div class="dual-block-header">
<span class="dual-block-name">{{ block.short_name }}</span>
<span class="dual-block-subtotal">{{ formatMoney(block.subtotal) }}</span>
</div>
<div v-for="item in block.items" :key="item.code" class="dual-block-item">
<span class="dual-item-name">{{ item.name }}</span>
<span class="dual-item-amount">{{ item.effective != null ? formatMoney(item.effective) : '-' }}</span>
</div>
</div>
</div>
<div class="dual-net-profit">
<span>净利润</span>
<span class="dual-net-val">{{ formatMoney(dualComparisonData.new_format?.net_profit) }}</span>
</div>
</div>
</div>
</div>
<div class="new30-footer">
<el-button size="small" :type="new30Format === 'new' ? 'primary' : ''" @click="switchNew30Format('new')">新准则</el-button>
<el-button size="small" :type="new30Format === 'old' ? 'primary' : ''" @click="switchNew30Format('old')">旧准则对比</el-button>
<el-button size="small" :type="new30Format === 'old' ? 'primary' : ''" @click="switchNew30Format('old')">旧准则</el-button>
<el-button size="small" :type="new30Format === 'dual' ? 'primary' : ''" @click="switchNew30Format('dual')">双列对比</el-button>
<el-button size="small" @click="exportNew30">导出</el-button>
<span v-if="new30DemoMode" class="new30-demo-hint"> 当前显示示例数据科目打标后显示实际数据</span>
</div>
@@ -357,20 +405,36 @@ const new30Blocks = ref<any[]>([])
const new30NetProfit = ref(0)
const new30Format = ref('new')
const new30DemoMode = ref(false)
const dualComparisonData = ref<any>(null)
async function loadNew30() {
loadingNew30.value = true
dualComparisonData.value = null
try {
const r = await api.get('/reports/profit-statement', {
params: { period: reportPeriod.value, format: 'new' }
})
const params: any = { period: reportPeriod.value }
if (new30Format.value === 'dual') {
params.format = 'dual'
} else {
params.format = 'new'
}
const r = await api.get('/reports/profit-statement', { params })
const d = (r as any).data || {}
new30Blocks.value = d.blocks || []
new30NetProfit.value = d.net_profit ?? 0
new30DemoMode.value = !d.all_items_have_data
if (new30Format.value === 'dual') {
dualComparisonData.value = d
new30Blocks.value = d.new_format?.blocks || []
new30NetProfit.value = d.new_format?.net_profit ?? 0
new30DemoMode.value = !d.new_format?.all_items_have_data
} else {
dualComparisonData.value = null
new30Blocks.value = d.blocks || []
new30NetProfit.value = d.net_profit ?? 0
new30DemoMode.value = !d.all_items_have_data
}
} catch (e) {
new30Blocks.value = []
new30NetProfit.value = 0
dualComparisonData.value = null
ElMessage.error('加载新30号准则利润表失败')
} finally {
loadingNew30.value = false
@@ -387,7 +451,7 @@ function formatMoney(val: number): string {
function switchNew30Format(fmt: string) {
new30Format.value = fmt
if (fmt === 'new') {
if (fmt === 'new' || fmt === 'dual') {
loadNew30()
} else {
// 切换到旧准则tab
@@ -710,4 +774,23 @@ onMounted(() => {
.restate-dot-red { background: #cf1322; }
.restate-dot-green { background: #389e0d; }
.restate-dot-orange { background: #d46b08; }
/* 双列对比样式 */
.dual-comparison { margin-top: 12px; }
.dual-desc { font-size: 13px; color: #888; margin-bottom: 12px; }
.dual-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media screen and (max-width: 1000px) { .dual-grid { grid-template-columns: 1fr; } }
.dual-col { background: #fff; border-radius: 8px; border: 1px solid #ebeef5; overflow: hidden; }
.dual-col-header { padding: 10px 14px; font-weight: 600; font-size: 14px; color: #fff; }
.dual-col-header.old-col { background: #909399; }
.dual-col-header.new-col { background: #409eff; }
.dual-blocks { padding: 8px; }
.dual-block { margin-bottom: 8px; border: 1px solid #ebeef5; border-radius: 4px; overflow: hidden; }
.dual-block-header { display: flex; justify-content: space-between; padding: 6px 10px; background: #f9fafc; font-size: 13px; font-weight: 600; }
.dual-block-subtotal { color: #1a1a2e; }
.dual-block-item { display: flex; justify-content: space-between; padding: 4px 10px 4px 16px; font-size: 12px; color: #555; border-top: 1px dashed #f0f0f0; }
.dual-item-name { flex: 1; }
.dual-item-amount { font-weight: 500; }
.dual-net-profit { padding: 10px 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border-radius: 4px; margin: 8px; display: flex; align-items: center; gap: 8px; font-size: 14px; }
.dual-net-val { font-size: 18px; font-weight: 700; flex: 1; }
</style>