533 lines
18 KiB
Vue
533 lines
18 KiB
Vue
<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>
|