Files
hzdyjs/web/src/views/cw/application/summary.vue
T
2025-08-06 16:37:37 +08:00

533 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="application-summary" v-loading="loading">
<!-- 基本信息 -->
<el-card class="summary-card">
<template #header>
<div class="card-header">
<span>项目基本信息</span>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="申报编号">
{{ currentApplication.applicationNo || '系统自动生成' }}
</el-descriptions-item>
<el-descriptions-item label="项目名称">
{{ currentApplication.projectName }}
</el-descriptions-item>
<el-descriptions-item label="项目负责人">
{{ currentApplication.principal }}
</el-descriptions-item>
<el-descriptions-item label="联系电话">
{{ currentApplication.contactPhone }}
</el-descriptions-item>
<el-descriptions-item label="预算年度">
{{ currentApplication.year }}
</el-descriptions-item>
<el-descriptions-item label="项目周期">
{{ formatDateRange(currentApplication.startDate, currentApplication.endDate) }}
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 项目特征 -->
<el-card class="summary-card">
<template #header>
<div class="card-header">
<span>项目特征</span>
</div>
</template>
<el-row :gutter="20">
<el-col :span="8">
<el-tag :type="currentApplication.targetSme ? 'success' : 'info'"> 小微企业发展: {{ currentApplication.targetSme ? '是' : '否' }} </el-tag>
</el-col>
<el-col :span="8">
<el-tag :type="currentApplication.targetMicroSme ? 'success' : 'info'">
微小企业成长: {{ currentApplication.targetMicroSme ? '是' : '否' }}
</el-tag>
</el-col>
<el-col :span="8">
<el-tag :type="currentApplication.importEquipment ? 'success' : 'info'">
进口设备: {{ currentApplication.importEquipment ? '是' : '否' }}
</el-tag>
</el-col>
</el-row>
</el-card>
<!-- 项目内容 -->
<el-card class="summary-card" v-if="hasProjectContent">
<template #header>
<div class="card-header">
<span>项目内容</span>
</div>
</template>
<el-descriptions :column="1" border>
<el-descriptions-item label="申报理由" v-if="currentApplication.applicationReason">
<div class="content-text">{{ currentApplication.applicationReason }}</div>
</el-descriptions-item>
<el-descriptions-item label="建设内容" v-if="currentApplication.constructionContent">
<div class="content-text">{{ currentApplication.constructionContent }}</div>
</el-descriptions-item>
<el-descriptions-item label="总体绩效目标" v-if="currentApplication.totalPerformanceGoal">
<div class="content-text">{{ currentApplication.totalPerformanceGoal }}</div>
</el-descriptions-item>
<el-descriptions-item label="当年完成目标" v-if="currentApplication.currentYearGoal">
<div class="content-text">{{ currentApplication.currentYearGoal }}</div>
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 资金预算汇总 -->
<el-card class="summary-card">
<template #header>
<div class="card-header">
<span>资金预算汇总</span>
<el-tag type="warning" size="small">单位:万元</el-tag>
</div>
</template>
<!-- 总体预算 -->
<div class="budget-section">
<h4>总体预算</h4>
<el-row :gutter="20" class="budget-row">
<el-col :span="6">
<div class="budget-item">
<div class="budget-label">总投资</div>
<div class="budget-value highlight">{{ formatAmount(currentApplication.totalInvestment) }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="budget-item">
<div class="budget-label">市财政资金</div>
<div class="budget-value">{{ formatAmount(currentApplication.totalMunicipalFiscal) }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="budget-item">
<div class="budget-label">单位自筹</div>
<div class="budget-value">{{ formatAmount(currentApplication.totalSelfFunding) }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="budget-item">
<div class="budget-label">其他资金</div>
<div class="budget-value">{{ formatAmount(currentApplication.totalOtherFunding) }}</div>
</div>
</el-col>
</el-row>
</div>
<!-- 分年度预算 -->
<div class="budget-section">
<h4>分年度预算明细</h4>
<el-table :data="budgetBreakdown" border>
<el-table-column prop="year" label="年度" width="120" align="center" />
<el-table-column prop="total" label="总投资" align="right">
<template #default="scope">
{{ formatAmount(scope.row.total) }}
</template>
</el-table-column>
<el-table-column prop="municipal" label="市财政资金" align="right">
<template #default="scope">
{{ formatAmount(scope.row.municipal) }}
</template>
</el-table-column>
<el-table-column prop="self" label="单位自筹" align="right">
<template #default="scope">
{{ formatAmount(scope.row.self) }}
</template>
</el-table-column>
<el-table-column prop="other" label="其他资金" align="right">
<template #default="scope">
{{ formatAmount(scope.row.other) }}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 绩效指标设置 -->
<el-card class="summary-card" v-if="hasPerformanceIndicators">
<template #header>
<div class="card-header">
<span>绩效指标设置</span>
<el-tag type="warning" size="small">分值占比总和:{{ performanceIndicatorTotal }}%</el-tag>
</div>
</template>
<el-table :data="validPerformanceIndicators" border style="width: 100%">
<el-table-column label="指标类型" width="120" align="center">
<template #default="scope">
<el-tag>{{ scope.row.indicatorTypeName }}</el-tag>
</template>
</el-table-column>
<el-table-column label="绩效目标名称" min-width="200">
<template #default="scope">
<div class="content-text">{{ scope.row.goalName }}</div>
</template>
</el-table-column>
<el-table-column label="目标值" width="120" align="center">
<template #default="scope">
<div class="content-text">{{ scope.row.targetValue }}</div>
</template>
</el-table-column>
<el-table-column label="分值占比" width="100" align="center">
<template #default="scope">
<el-tag type="primary">{{ parseFloat(scope.row.weightPercentage) || 0 }}%</el-tag>
</template>
</el-table-column>
<el-table-column label="详细说明" min-width="150">
<template #default="scope">
<div class="content-text" v-if="scope.row.description">{{ scope.row.description }}</div>
<span v-else class="text-muted">-</span>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 备注信息 -->
<el-card class="summary-card" v-if="currentApplication.remark">
<template #header>
<div class="card-header">
<span>备注信息</span>
</div>
</template>
<div class="content-text">{{ currentApplication.remark }}</div>
</el-card>
</div>
</template>
<script setup>
import { computed, ref, watch, onMounted } from 'vue';
import { getProjectApplication } from '@/api/cw/projectApplication';
defineOptions({
name: 'ApplicationSummary'
});
const props = defineProps({
application: {
type: Object,
default: () => ({})
},
applicationId: {
type: [String, Number],
default: null
}
});
// 内部数据状态
const internalApplication = ref({});
const loading = ref(false);
// 计算属性:优先使用传入的application,如果没有则使用内部获取的数据
const currentApplication = computed(() => {
// 如果直接传入了application对象且不为空,则使用它
if (props.application && Object.keys(props.application).length > 0) {
console.log('=== 使用传入的application对象 ===');
console.log('application数据:', props.application);
return props.application;
}
// 否则使用内部获取的数据
console.log('=== 使用内部获取的application数据 ===');
console.log('internalApplication数据:', internalApplication.value);
return internalApplication.value;
});
// 获取申报详情数据
const fetchApplicationData = async (id) => {
if (!id) return;
console.log('=== ApplicationSummary 开始获取数据 ===');
console.log('applicationId:', id);
loading.value = true;
try {
const response = await getProjectApplication(id);
console.log('=== ApplicationSummary API调用成功 ===');
console.log('API响应:', response);
console.log('响应数据:', response.data);
console.log('响应数据类型:', typeof response.data);
// 特别检查绩效指标字段
if (response.data) {
console.log('=== API响应中的绩效指标检查 ===');
console.log('response.data.performanceIndicators:', response.data.performanceIndicators);
console.log('performanceIndicators类型:', typeof response.data.performanceIndicators);
console.log('是否为数组:', Array.isArray(response.data.performanceIndicators));
// 检查所有可能的绩效指标字段名
console.log('可能的字段名检查:');
console.log('- performanceIndicators:', response.data.performanceIndicators);
console.log('- performance_indicators:', response.data.performance_indicators);
console.log('- indicators:', response.data.indicators);
console.log('- performanceIndicatorList:', response.data.performanceIndicatorList);
// 列出所有字段
console.log('响应数据的所有字段:', Object.keys(response.data));
}
internalApplication.value = response.data || {};
} catch (error) {
console.error('=== ApplicationSummary API调用失败 ===');
console.error('错误信息:', error);
internalApplication.value = {};
} finally {
loading.value = false;
}
};
// 监听applicationId变化
watch(
() => props.applicationId,
(newId) => {
if (newId && (!props.application || Object.keys(props.application).length === 0)) {
fetchApplicationData(newId);
}
},
{ immediate: true }
);
// 组件挂载时检查是否需要获取数据
onMounted(() => {
console.log('=== ApplicationSummary 组件挂载 ===');
console.log('props.application:', props.application);
console.log('props.applicationId:', props.applicationId);
// 如果没有传入application但有applicationId,则获取数据
if (props.applicationId && (!props.application || Object.keys(props.application).length === 0)) {
fetchApplicationData(props.applicationId);
}
});
// 判断是否有项目内容
const hasProjectContent = computed(() => {
const app = currentApplication.value;
return app.applicationReason || app.constructionContent || app.totalPerformanceGoal || app.currentYearGoal;
});
// 判断是否有绩效指标
const hasPerformanceIndicators = computed(() => {
const app = currentApplication.value;
console.log('=== 绩效指标详细检查 ===');
console.log('完整app对象:', app);
console.log('app.performanceIndicators类型:', typeof app.performanceIndicators);
console.log('app.performanceIndicators值:', app.performanceIndicators);
console.log('是否为数组:', Array.isArray(app.performanceIndicators));
if (app.performanceIndicators) {
console.log('绩效指标数组长度:', app.performanceIndicators.length);
console.log('绩效指标数组内容:', JSON.stringify(app.performanceIndicators, null, 2));
} else {
console.log('绩效指标数据为空或未定义');
}
const hasIndicators = app.performanceIndicators && app.performanceIndicators.length > 0;
console.log('最终判断 - 是否有绩效指标:', hasIndicators);
return hasIndicators;
});
// 计算绩效指标总分值占比
const performanceIndicatorTotal = computed(() => {
const app = currentApplication.value;
if (!app.performanceIndicators || app.performanceIndicators.length === 0) {
return 0;
}
const total = app.performanceIndicators.reduce((sum, indicator) => {
const weight = parseFloat(indicator.weightPercentage) || 0;
return sum + weight;
}, 0);
return Math.round(total * 100) / 100; // 保留两位小数
});
// 过滤掉无效的绩效指标(分值占比为0的)
const validPerformanceIndicators = computed(() => {
const app = currentApplication.value;
console.log('=== validPerformanceIndicators 计算 ===');
console.log('app.performanceIndicators:', app.performanceIndicators);
if (!app.performanceIndicators) {
console.log('没有绩效指标数据,返回空数组');
return [];
}
const validIndicators = app.performanceIndicators.filter((indicator) => {
const weight = parseFloat(indicator.weightPercentage) || 0;
console.log(`指标 ${indicator.goalName}: 权重=${indicator.weightPercentage}, 解析后权重=${weight}, 是否有效=${weight > 0}`);
return weight > 0;
});
console.log('有效绩效指标数量:', validIndicators.length);
console.log('有效绩效指标详情:', validIndicators);
return validIndicators;
});
// 分年度预算明细
const budgetBreakdown = computed(() => {
const app = currentApplication.value;
const currentYear = app.year || new Date().getFullYear();
console.log('=== 🔍 分年度预算数据详细检查 ===');
console.log('完整application数据:', app);
console.log('当前年度:', currentYear);
console.log('📊 分年度预算字段值详情:');
console.log('📅 当年预算:', {
currentYearTotal: app.currentYearTotal,
currentYearMunicipal: app.currentYearMunicipal,
currentYearSelf: app.currentYearSelf,
currentYearOther: app.currentYearOther
});
console.log('📅 下年预算:', {
nextYearTotal: app.nextYearTotal,
nextYearMunicipal: app.nextYearMunicipal,
nextYearSelf: app.nextYearSelf,
nextYearOther: app.nextYearOther
});
console.log('📅 以后年度预算:', {
futureYearTotal: app.futureYearTotal,
futureYearMunicipal: app.futureYearMunicipal,
futureYearSelf: app.futureYearSelf,
futureYearOther: app.futureYearOther
});
const breakdown = [
{
year: `${currentYear}年`,
total: Number(app.currentYearTotal) || 0,
municipal: Number(app.currentYearMunicipal) || 0,
self: Number(app.currentYearSelf) || 0,
other: Number(app.currentYearOther) || 0
},
{
year: `${parseInt(currentYear) + 1}年`,
total: Number(app.nextYearTotal) || 0,
municipal: Number(app.nextYearMunicipal) || 0,
self: Number(app.nextYearSelf) || 0,
other: Number(app.nextYearOther) || 0
},
{
year: '以后年度',
total: Number(app.futureYearTotal) || 0,
municipal: Number(app.futureYearMunicipal) || 0,
self: Number(app.futureYearSelf) || 0,
other: Number(app.futureYearOther) || 0
}
];
console.log('📋 计算后的budgetBreakdown数据:', breakdown);
console.log('📊 每年度预算汇总:');
breakdown.forEach((item, index) => {
const sum = item.municipal + item.self + item.other;
console.log(`${item.year}: 总投资=${item.total}, 细项合计=${sum}, 财政=${item.municipal}, 自筹=${item.self}, 其他=${item.other}`);
});
// 🔥 移除错误的过滤条件,始终显示所有年度
console.log('✅ 返回完整的分年度预算数据,不进行过滤');
return breakdown;
});
// 格式化金额
const formatAmount = (amount) => {
if (!amount || amount === 0) return '0.00';
return Number(amount).toFixed(2);
};
// 格式化日期范围
const formatDateRange = (startDate, endDate) => {
if (!startDate && !endDate) return '未设置';
if (!startDate) return `截止 ${endDate}`;
if (!endDate) return `${startDate} 开始`;
return `${startDate} 至 ${endDate}`;
};
</script>
<style scoped>
.application-summary {
padding: 0;
}
.summary-card {
margin-bottom: 20px;
}
.summary-card:last-child {
margin-bottom: 0;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 500;
}
.content-text {
line-height: 1.6;
word-break: break-word;
white-space: pre-wrap;
}
.budget-section {
margin-bottom: 24px;
}
.budget-section:last-child {
margin-bottom: 0;
}
.budget-section h4 {
margin: 0 0 16px 0;
font-size: 16px;
color: #333;
border-left: 4px solid #409eff;
padding-left: 12px;
}
.budget-row {
margin-bottom: 16px;
}
.budget-item {
text-align: center;
padding: 16px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
}
.budget-label {
font-size: 14px;
color: #666;
margin-bottom: 8px;
}
.budget-value {
font-size: 18px;
font-weight: 600;
color: #333;
}
.budget-value.highlight {
color: #409eff;
font-size: 20px;
}
.el-tag {
margin-right: 8px;
}
:deep(.el-descriptions__label) {
width: 120px;
font-weight: 500;
}
:deep(.el-table td, .el-table th) {
text-align: center;
}
:deep(.el-table .cell) {
padding: 0 8px;
}
</style>