|
|
|
|
@ -0,0 +1,388 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="big-package-detail-container">
|
|
|
|
|
<!-- 大包基本信息 -->
|
|
|
|
|
<div class="detail-section">
|
|
|
|
|
<div class="section-title">
|
|
|
|
|
<i class="el-icon-info"></i>
|
|
|
|
|
大包基本信息
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="info-grid">
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<label>预算包名称:</label>
|
|
|
|
|
<span>{{ packageData.name || '-' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<label>预算年度:</label>
|
|
|
|
|
<span>{{ packageData.budget_year ? packageData.budget_year.year + '年' : '-' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<label>负责科室:</label>
|
|
|
|
|
<span>{{ getDepartmentName(packageData.department_id) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<label>分配金额:</label>
|
|
|
|
|
<span class="amount">{{ formatCurrency(packageData.allocated_amount) }} 万元</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<label>层级:</label>
|
|
|
|
|
<el-tag :type="getLevelTagType(packageData.level)" size="small">
|
|
|
|
|
{{ getLevelText(packageData.level) }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<label>创建时间:</label>
|
|
|
|
|
<span>{{ formatDate(packageData.created_at) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 下级预算包汇总 -->
|
|
|
|
|
<div class="detail-section" v-if="childPackages.length > 0">
|
|
|
|
|
<div class="section-title">
|
|
|
|
|
<i class="el-icon-s-data"></i>
|
|
|
|
|
下级预算包情况
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<el-table
|
|
|
|
|
:data="childPackages"
|
|
|
|
|
border
|
|
|
|
|
size="small"
|
|
|
|
|
show-summary
|
|
|
|
|
:summary-method="getChildPackageSummaries"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
|
|
<el-table-column label="预算包名称" min-width="200">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span>{{ scope.row.name }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="层级" width="80" align="center">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<el-tag :type="getLevelTagType(scope.row.level)" size="mini">
|
|
|
|
|
{{ getLevelText(scope.row.level) }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="负责科室" width="120" align="center">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span>{{ getDepartmentName(scope.row.department_id) }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="分配金额(万元)" width="120" align="right">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span style="color: #409EFF; font-weight: bold;">{{ formatCurrency(scope.row.allocated_amount) }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="分解金额(万元)" width="120" align="right">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span v-if="scope.row.decomposed_amount" style="color: #67C23A; font-weight: bold;">
|
|
|
|
|
{{ formatCurrency(scope.row.decomposed_amount) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="创建时间" width="160" align="center">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span>{{ formatDate(scope.row.created_at) }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 经济分类汇总 -->
|
|
|
|
|
<div class="detail-section" v-if="economicSummary.length > 0">
|
|
|
|
|
<div class="section-title">
|
|
|
|
|
<i class="el-icon-s-grid"></i>
|
|
|
|
|
经济分类汇总
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<el-table
|
|
|
|
|
:data="economicSummary"
|
|
|
|
|
border
|
|
|
|
|
size="small"
|
|
|
|
|
show-summary
|
|
|
|
|
:summary-method="getEconomicSummaries"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
|
|
<el-table-column label="经济分类" min-width="200">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span>{{ scope.row.name }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="金额(万元)" width="150" align="right">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span style="color: #67C23A; font-weight: bold;">{{ formatCurrency(scope.row.amount) }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="占比" width="100" align="center">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span>{{ getPercentage(scope.row.amount, packageData.allocated_amount) }}%</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 统计信息 -->
|
|
|
|
|
<div class="detail-section">
|
|
|
|
|
<div class="section-title">
|
|
|
|
|
<i class="el-icon-data-analysis"></i>
|
|
|
|
|
统计信息
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="stats-grid">
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-label">下级预算包数量</div>
|
|
|
|
|
<div class="stat-value">{{ childPackages.length }} 个</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-label">经济分类数量</div>
|
|
|
|
|
<div class="stat-value">{{ economicSummary.length }} 个</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-label">总分配金额</div>
|
|
|
|
|
<div class="stat-value amount">{{ formatCurrency(packageData.allocated_amount) }} 万元</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-label">总分解金额</div>
|
|
|
|
|
<div class="stat-value amount">{{ formatCurrency(getTotalDecomposedAmount()) }} 万元</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
export default {
|
|
|
|
|
name: 'BigPackageDetailView',
|
|
|
|
|
props: {
|
|
|
|
|
packageData: {
|
|
|
|
|
type: Object,
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
childPackages: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => []
|
|
|
|
|
},
|
|
|
|
|
economicSummary: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => []
|
|
|
|
|
},
|
|
|
|
|
departmentOptions: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => ({ data: [] })
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
getDepartmentName(departmentId) {
|
|
|
|
|
if (!departmentId || !this.departmentOptions.data) return '-'
|
|
|
|
|
const dept = this.departmentOptions.data.find(d => d.id === departmentId)
|
|
|
|
|
return dept ? dept.name : '-'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getLevelTagType(level) {
|
|
|
|
|
const typeMap = {
|
|
|
|
|
1: 'primary',
|
|
|
|
|
2: 'success',
|
|
|
|
|
3: 'warning'
|
|
|
|
|
}
|
|
|
|
|
return typeMap[level] || 'info'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getLevelText(level) {
|
|
|
|
|
const textMap = {
|
|
|
|
|
1: '大包',
|
|
|
|
|
2: '中包',
|
|
|
|
|
3: '经济分类'
|
|
|
|
|
}
|
|
|
|
|
return textMap[level] || '未知'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getChildPackageSummaries(param) {
|
|
|
|
|
const { columns, data } = param
|
|
|
|
|
const sums = []
|
|
|
|
|
columns.forEach((column, index) => {
|
|
|
|
|
if (index === 0) {
|
|
|
|
|
sums[index] = '合计'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index === 4) { // 分配金额列
|
|
|
|
|
const total = data.reduce((prev, curr) => {
|
|
|
|
|
return prev + (Number(curr.allocated_amount) || 0)
|
|
|
|
|
}, 0)
|
|
|
|
|
sums[index] = this.formatCurrency(total)
|
|
|
|
|
} else if (index === 5) { // 分解金额列
|
|
|
|
|
const total = data.reduce((prev, curr) => {
|
|
|
|
|
return prev + (Number(curr.decomposed_amount) || 0)
|
|
|
|
|
}, 0)
|
|
|
|
|
sums[index] = this.formatCurrency(total)
|
|
|
|
|
} else {
|
|
|
|
|
sums[index] = ''
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return sums
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getEconomicSummaries(param) {
|
|
|
|
|
const { columns, data } = param
|
|
|
|
|
const sums = []
|
|
|
|
|
columns.forEach((column, index) => {
|
|
|
|
|
if (index === 0) {
|
|
|
|
|
sums[index] = '合计'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index === 2) { // 金额列
|
|
|
|
|
const total = data.reduce((prev, curr) => {
|
|
|
|
|
return prev + (Number(curr.amount) || 0)
|
|
|
|
|
}, 0)
|
|
|
|
|
sums[index] = this.formatCurrency(total)
|
|
|
|
|
} else {
|
|
|
|
|
sums[index] = ''
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return sums
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getTotalDecomposedAmount() {
|
|
|
|
|
return this.childPackages.reduce((total, pkg) => {
|
|
|
|
|
return total + (Number(pkg.decomposed_amount) || 0)
|
|
|
|
|
}, 0)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getPercentage(amount, total) {
|
|
|
|
|
if (!total || total === 0) return '0.0'
|
|
|
|
|
const percentage = (amount / total) * 100
|
|
|
|
|
return percentage.toFixed(1)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
formatCurrency(amount) {
|
|
|
|
|
if (amount === null || amount === undefined || amount === '') return '0.0'
|
|
|
|
|
const num = Number(amount)
|
|
|
|
|
return isFinite(num) ? num.toFixed(1) : '0.0'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
formatDate(dateStr) {
|
|
|
|
|
if (!dateStr) return '-'
|
|
|
|
|
const date = new Date(dateStr)
|
|
|
|
|
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', {
|
|
|
|
|
hour12: false,
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
minute: '2-digit'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.big-package-detail-container {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
max-height: 80vh;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-section {
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
border: 1px solid #ebeef5;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #303133;
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title i {
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
gap: 15px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-item label {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #606266;
|
|
|
|
|
width: 100px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-item span {
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-item .amount {
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
gap: 20px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-item {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
border: 1px solid #e9ecef;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: #606266;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value.amount {
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 表格内容样式 */
|
|
|
|
|
.detail-section >>> .el-table {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-section >>> .el-table th {
|
|
|
|
|
background-color: #f8f9fa !important;
|
|
|
|
|
color: #606266;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-section >>> .el-table__footer-wrapper .el-table__footer {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
background-color: #f0f9ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-section >>> .el-table td {
|
|
|
|
|
border-color: #ebeef5;
|
|
|
|
|
}
|
|
|
|
|
</style>
|