添加考核产值预算表预览

This commit is contained in:
lpd
2026-05-26 17:04:53 +08:00
parent 0e8dcb1f71
commit 0150d1bf9a
2 changed files with 406 additions and 6 deletions

View File

@@ -79,6 +79,59 @@ export interface ProjectBudgetExportReqVO {
year?: number
}
export interface ProjectBudgetPreviewRespVO {
year?: number
projectCode?: string
projectName?: string
remark?: string
budgetRows?: ProjectBudgetPreviewBudgetRow[]
quarterBudgetRows?: ProjectBudgetPreviewQuarterRow[]
}
export interface ProjectBudgetPreviewBudgetRow {
rowType?: string
planningId?: number
planningContent?: string
displayDesignPart?: string
displayBuildingType?: string
internalGuidanceUnitPrice?: number
designArea?: number
buildingOrUnitCount?: number
drawingSetFactor?: number
scaleFactor?: number
modificationFactor?: number
complexityFactor?: number
subtotalArea?: number
currentDesignStageRatio?: number
assessmentOutputValueWan?: number
}
export interface ProjectBudgetPreviewQuarterRow {
serialNo?: number
totalRow?: boolean
outputType?: string
designContent?: string
budgetYear?: number
totalDesignArea?: number
totalAssessmentArea?: number
totalAssessmentOutputWan?: number
designStageRatio?: number
designStageOutputWan?: number
historicalIssuedRatio?: number
quarterOneRatio?: number
quarterTwoRatio?: number
quarterThreeRatio?: number
quarterFourRatio?: number
currentYearRatio?: number
pendingRatio?: number
quarterOneAmountWan?: number
quarterTwoAmountWan?: number
quarterThreeAmountWan?: number
quarterFourAmountWan?: number
yearTotalAmountWan?: number
ratioRemark?: string
}
export interface ProjectQuarterOutputExportReqVO {
planningId: number
year?: number
@@ -158,6 +211,13 @@ export const exportProjectBudget = (params: ProjectBudgetExportReqVO) => {
return request.download({ url: '/tjt/report/project-budget/export-excel', params })
}
export const getProjectBudgetPreview = (params: ProjectBudgetExportReqVO) => {
return request.get<ProjectBudgetPreviewRespVO>({
url: '/tjt/report/project-budget/preview',
params: { ...params, _t: Date.now() }
})
}
export const exportProjectQuarterOutput = (params: ProjectQuarterOutputExportReqVO) => {
return request.download({
url: '/tjt/report/project-quarter-output/export-excel',

View File

@@ -161,7 +161,9 @@
<el-col :span="8">
<div class="rounded-8px bg-[var(--el-fill-color-light)] px-12px py-12px">
<div class="text-12px text-[var(--el-text-color-secondary)]">分项合同产值合计</div>
<div class="mt-6px text-18px font-600">{{ formatAmountText(totalPlanningAmount) }}</div>
<div class="mt-6px text-18px font-600">{{
formatAmountText(totalPlanningAmount)
}}</div>
</div>
</el-col>
<el-col :span="8">
@@ -189,7 +191,12 @@
<el-button v-if="currentProject" size="small" @click="getPlanningList">刷新</el-button>
</div>
<el-table v-loading="planningLoading" :data="planningList" border>
<el-table-column align="center" label="项目任务包" min-width="180" prop="planningContent" />
<el-table-column
align="center"
label="项目任务包"
min-width="180"
prop="planningContent"
/>
<el-table-column align="center" label="归属类型" width="100">
<template #default="scope">
{{ getOwnershipTypeLabel(scope.row.ownershipType) }}
@@ -217,6 +224,253 @@
</template>
</SplitPane>
<ContentWrap v-if="currentProject" v-loading="budgetPreviewLoading">
<div class="mb-12px flex flex-wrap items-center justify-between gap-12px">
<div>
<div class="text-16px font-600">考核产值预算表预览</div>
<div class="mt-4px text-12px text-[var(--el-text-color-secondary)]">
{{ budgetPreview?.projectName || currentProject.projectName }}
</div>
</div>
<div class="flex items-center gap-8px">
<span class="text-13px text-[var(--el-text-color-secondary)]">年度</span>
<el-date-picker
v-model="previewYearValue"
class="!w-150px"
clearable
placeholder="请选择年度"
type="year"
value-format="YYYY"
@change="getProjectBudgetPreview"
/>
<el-button :loading="budgetPreviewLoading" @click="getProjectBudgetPreview">刷新</el-button>
</div>
</div>
<SplitPane>
<template #left>
<div class="budget-preview-pane">
<div class="mb-12px text-14px font-600">建筑装饰工程项目考核产值预算表</div>
<el-table
:data="budgetPreview?.budgetRows || []"
:empty-text="budgetPreviewEmptyText"
:row-class-name="getBudgetPreviewRowClassName"
border
class="budget-preview-table"
max-height="520"
>
<el-table-column
align="center"
fixed="left"
label="规划内容"
min-width="160"
prop="planningContent"
show-overflow-tooltip
/>
<el-table-column
align="center"
fixed="left"
label="设计部位"
min-width="120"
prop="displayDesignPart"
/>
<el-table-column
align="center"
label="设计内容/设计类型"
min-width="160"
prop="displayBuildingType"
show-overflow-tooltip
/>
<el-table-column align="center" label="内部指导单价(元/㎡)" min-width="140">
<template #default="scope">{{
formatNullableAmount(scope.row.internalGuidanceUnitPrice)
}}</template>
</el-table-column>
<el-table-column align="center" label="设计面积(㎡)" min-width="120">
<template #default="scope">{{ formatNullableAmount(scope.row.designArea) }}</template>
</el-table-column>
<el-table-column
align="center"
label="栋数/户型数"
min-width="110"
prop="buildingOrUnitCount"
/>
<el-table-column align="center" label="套图系数" min-width="100">
<template #default="scope">{{ formatFactor(scope.row.drawingSetFactor) }}</template>
</el-table-column>
<el-table-column align="center" label="规模系数" min-width="100">
<template #default="scope">{{ formatFactor(scope.row.scaleFactor) }}</template>
</el-table-column>
<el-table-column align="center" label="修改系数" min-width="100">
<template #default="scope">{{ formatFactor(scope.row.modificationFactor) }}</template>
</el-table-column>
<el-table-column align="center" label="复杂系数/复杂等级" min-width="140">
<template #default="scope">{{ formatFactor(scope.row.complexityFactor) }}</template>
</el-table-column>
<el-table-column align="center" label="小计(㎡)" min-width="120">
<template #default="scope">{{
formatNullableAmount(scope.row.subtotalArea)
}}</template>
</el-table-column>
<el-table-column align="center" label="设计阶段占比" min-width="120">
<template #default="scope">{{
formatNullablePercent(scope.row.currentDesignStageRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="考核产值小计(万元)" min-width="150">
<template #default="scope">{{
formatNullableAmount(scope.row.assessmentOutputValueWan)
}}</template>
</el-table-column>
</el-table>
<div
v-if="budgetPreview?.remark"
class="mt-10px text-12px text-[var(--el-text-color-secondary)]"
>
备注{{ budgetPreview.remark }}
</div>
</div>
</template>
<template #right>
<div class="budget-preview-pane">
<div class="mb-12px text-14px font-600">项目考核产值年度季度预算计取表</div>
<el-table
:data="budgetPreview?.quarterBudgetRows || []"
:empty-text="budgetPreviewEmptyText"
:row-class-name="getQuarterPreviewRowClassName"
border
class="budget-preview-table"
max-height="520"
>
<el-table-column align="center" fixed="left" label="序号" width="70">
<template #default="scope">{{
scope.row.totalRow ? '' : scope.row.serialNo
}}</template>
</el-table-column>
<el-table-column
align="center"
fixed="left"
label="项目名称"
min-width="180"
show-overflow-tooltip
>
<template #default="scope">{{
scope.row.totalRow ? '项目合计' : budgetPreview?.projectName
}}</template>
</el-table-column>
<el-table-column align="center" label="产值类型" min-width="120" prop="outputType" />
<el-table-column
align="center"
label="设计内容"
min-width="180"
prop="designContent"
show-overflow-tooltip
/>
<el-table-column align="center" label="总建筑设计面积(㎡)" min-width="150">
<template #default="scope">{{
formatNullableAmount(scope.row.totalDesignArea)
}}</template>
</el-table-column>
<el-table-column align="center" label="总考核产值面积(㎡)" min-width="150">
<template #default="scope">{{
formatNullableAmount(scope.row.totalAssessmentArea)
}}</template>
</el-table-column>
<el-table-column align="center" label="总考核产值(万元)" min-width="150">
<template #default="scope">{{
formatNullableAmount(scope.row.totalAssessmentOutputWan)
}}</template>
</el-table-column>
<el-table-column align="center" label="设计阶段考核产值分配">
<el-table-column align="center" label="阶段占比" min-width="110">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.designStageRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="考核产值" min-width="120">
<template #default="scope">{{
formatNullableAmount(scope.row.designStageOutputWan)
}}</template>
</el-table-column>
</el-table-column>
<el-table-column align="center" label="发放情况">
<el-table-column align="center" label="往年已发放比例" min-width="130">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.historicalIssuedRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="一季度" min-width="100">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.quarterOneRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="二季度" min-width="100">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.quarterTwoRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="三季度" min-width="100">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.quarterThreeRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="四季度" min-width="100">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.quarterFourRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="本年度小计" min-width="120">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.currentYearRatio)
}}</template>
</el-table-column>
<el-table-column align="center" label="未发放比例" min-width="120">
<template #default="scope">{{
scope.row.totalRow ? '' : formatNullablePercent(scope.row.pendingRatio)
}}</template>
</el-table-column>
</el-table-column>
<el-table-column align="center" label="本年度项目考核产值(万元)">
<el-table-column align="center" label="一季度" min-width="110">
<template #default="scope">{{
formatNullableAmount(scope.row.quarterOneAmountWan)
}}</template>
</el-table-column>
<el-table-column align="center" label="二季度" min-width="110">
<template #default="scope">{{
formatNullableAmount(scope.row.quarterTwoAmountWan)
}}</template>
</el-table-column>
<el-table-column align="center" label="三季度" min-width="110">
<template #default="scope">{{
formatNullableAmount(scope.row.quarterThreeAmountWan)
}}</template>
</el-table-column>
<el-table-column align="center" label="四季度" min-width="110">
<template #default="scope">{{
formatNullableAmount(scope.row.quarterFourAmountWan)
}}</template>
</el-table-column>
<el-table-column align="center" label="本年度小计" min-width="120">
<template #default="scope">{{
formatNullableAmount(scope.row.yearTotalAmountWan)
}}</template>
</el-table-column>
</el-table-column>
<el-table-column
align="center"
label="备注"
min-width="180"
prop="ratioRemark"
show-overflow-tooltip
/>
</el-table>
</div>
</template>
</SplitPane>
</ContentWrap>
<Dialog v-model="exportDialogVisible" title="导出项目考核产值预算表" width="420">
<el-form label-width="88px">
<el-form-item label="年度">
@@ -261,12 +515,15 @@ const currentYear = new Date().getFullYear()
const loading = ref(false)
const planningLoading = ref(false)
const budgetPreviewLoading = ref(false)
const exportLoading = ref(false)
const exportDialogVisible = ref(false)
const previewYear = ref<number>()
const exportYear = ref<number>()
const total = ref(0)
const projectList = ref<ProjectApi.ProjectVO[]>([])
const planningList = ref<PlanningApi.ProjectPlanningVO[]>([])
const budgetPreview = ref<ReportApi.ProjectBudgetPreviewRespVO>()
const currentProject = ref<ProjectApi.ProjectVO>()
const queryFormRef = ref()
const projectTableRef = ref()
@@ -280,7 +537,7 @@ const queryParams = reactive<ProjectApi.ProjectPageReqVO>({
})
const getProjectRowIndex = (index: number) =>
(queryParams.pageNo - 1) * queryParams.pageSize + index + 1
((queryParams.pageNo || 1) - 1) * (queryParams.pageSize || 10) + index + 1
const queryProjectStartYearValue = computed({
get: () => (queryParams.projectStartYear ? String(queryParams.projectStartYear) : undefined),
@@ -296,6 +553,13 @@ const exportYearValue = computed({
}
})
const previewYearValue = computed({
get: () => (previewYear.value ? String(previewYear.value) : undefined),
set: (value?: string) => {
previewYear.value = value ? Number(value) : undefined
}
})
const totalPlanningAmount = computed(() =>
planningList.value.reduce((sum, item) => sum + Number(item.planningAmount || 0), 0)
)
@@ -303,6 +567,48 @@ const totalAssessmentOutputValue = computed(() =>
planningList.value.reduce((sum, item) => sum + Number(item.assessmentOutputValue || 0), 0)
)
const budgetPreviewEmptyText = computed(() =>
previewYear.value ? '暂无预算表数据' : '请先选择年度'
)
const formatNullableAmount = (value?: number | string | null) => {
if (value === undefined || value === null || value === '') {
return ''
}
return formatAmountText(value)
}
const formatFactor = (value?: number | string | null) => {
if (value === undefined || value === null || value === '') {
return ''
}
const numericValue = Number(value)
if (Number.isNaN(numericValue)) {
return ''
}
return numericValue.toFixed(2)
}
const formatNullablePercent = (value?: number | string | null) => {
if (value === undefined || value === null || value === '') {
return ''
}
const numericValue = Number(value)
if (Number.isNaN(numericValue)) {
return ''
}
return `${(numericValue * 100).toFixed(2)}%`
}
const getBudgetPreviewRowClassName = ({ row }: { row: ReportApi.ProjectBudgetPreviewBudgetRow }) =>
row?.rowType === 'PART_SUBTOTAL' || row?.rowType === 'PLANNING_TOTAL' ? 'budget-total-row' : ''
const getQuarterPreviewRowClassName = ({
row
}: {
row: ReportApi.ProjectBudgetPreviewQuarterRow
}) => (row?.totalRow ? 'budget-total-row' : '')
const getProjectList = async () => {
loading.value = true
try {
@@ -312,6 +618,7 @@ const getProjectList = async () => {
if (!projectList.value.length) {
currentProject.value = undefined
planningList.value = []
budgetPreview.value = undefined
return
}
const targetProjectId = currentProject.value?.id || projectList.value[0].id
@@ -331,12 +638,30 @@ const getPlanningList = async () => {
}
planningLoading.value = true
try {
planningList.value = await PlanningApi.getProjectPlanningListByProjectId(currentProject.value.id)
planningList.value = await PlanningApi.getProjectPlanningListByProjectId(
currentProject.value.id
)
} finally {
planningLoading.value = false
}
}
const getProjectBudgetPreview = async () => {
if (!currentProject.value?.id || !previewYear.value) {
budgetPreview.value = undefined
return
}
budgetPreviewLoading.value = true
try {
budgetPreview.value = await ReportApi.getProjectBudgetPreview({
projectId: currentProject.value.id,
year: previewYear.value
})
} finally {
budgetPreviewLoading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getProjectList()
@@ -349,7 +674,8 @@ const resetQuery = () => {
const handleCurrentProjectChange = async (row?: ProjectApi.ProjectVO) => {
currentProject.value = row || undefined
await getPlanningList()
previewYear.value = row?.projectStartYear || currentYear
await Promise.all([getPlanningList(), getProjectBudgetPreview()])
}
const handleExportProjectBudget = async () => {
@@ -377,7 +703,10 @@ const submitProjectBudgetExport = async () => {
projectId: currentProject.value.id,
year: exportYear.value
})
download.excel(data, `${currentProject.value.projectName}_${exportYear.value}_项目考核产值预算表.xlsx`)
download.excel(
data,
`${currentProject.value.projectName}_${exportYear.value}_项目考核产值预算表.xlsx`
)
exportDialogVisible.value = false
} finally {
exportLoading.value = false
@@ -399,3 +728,14 @@ onActivated(() => {
getProjectList()
})
</script>
<style scoped>
.budget-preview-pane {
min-width: 0;
}
.budget-preview-table :deep(.budget-total-row) {
background: var(--el-fill-color-light);
font-weight: 700;
}
</style>