项目成本测算总合约规划预算产值修改成合同产值(不含增值税)、修复季度分配异常

This commit is contained in:
lzm
2026-05-29 17:41:09 +08:00
parent 0150d1bf9a
commit bacc2f739f
2 changed files with 157 additions and 20 deletions

View File

@@ -301,15 +301,22 @@
<div class="mb-16px flex items-center justify-between gap-16px"> <div class="mb-16px flex items-center justify-between gap-16px">
<div> <div>
<div class="text-14px font-600">季度分配</div> <div class="text-14px font-600">季度分配</div>
<div class="mt-4px text-12px text-[var(--el-text-color-secondary)]"> <div
v-if="guideDetailMode && !historyParentMode"
class="mt-4px text-12px text-[var(--el-text-color-secondary)]"
>
专业所 + 指导价法按指导价法明细分别维护季度分配
</div>
<div v-else class="mt-4px text-12px text-[var(--el-text-color-secondary)]">
总分配已分配待分配和提取进度备注属于合约规划的总体分配控制季度分配表只展示年度季度和分配比例明细 总分配已分配待分配和提取进度备注属于合约规划的总体分配控制季度分配表只展示年度季度和分配比例明细
</div> </div>
</div> </div>
<el-button <el-button
v-hasPermi="['tjt:planning:update', 'tjt:planning-quarter:update', 'tjt:planning-quarter:create']" v-hasPermi="['tjt:planning:update', 'tjt:planning-quarter:update', 'tjt:planning-quarter:create']"
:disabled="guideDetailMode && !historyParentMode && !activeGuideDetail"
plain plain
type="primary" type="primary"
@click="openQuarterDistributionForm" @click="openQuarterDistributionForm(activeGuideDetail?.id)"
> >
编辑季度分配 编辑季度分配
</el-button> </el-button>
@@ -328,7 +335,13 @@
<div class="rounded-8px bg-[var(--el-fill-color-light)] px-16px py-12px"> <div class="rounded-8px bg-[var(--el-fill-color-light)] px-16px py-12px">
<div class="text-12px text-[var(--el-text-color-secondary)]">已分配</div> <div class="text-12px text-[var(--el-text-color-secondary)]">已分配</div>
<div class="mt-6px text-18px font-600"> <div class="mt-6px text-18px font-600">
{{ formatPercentText(currentPlanning.allocatedAmount) }} {{
formatPercentText(
guideDetailMode && activeGuideDetail
? activeGuideDetail.allocatedAmount
: currentPlanning.allocatedAmount
)
}}
</div> </div>
</div> </div>
</el-col> </el-col>
@@ -336,7 +349,13 @@
<div class="rounded-8px bg-[var(--el-fill-color-light)] px-16px py-12px"> <div class="rounded-8px bg-[var(--el-fill-color-light)] px-16px py-12px">
<div class="text-12px text-[var(--el-text-color-secondary)]">待分配</div> <div class="text-12px text-[var(--el-text-color-secondary)]">待分配</div>
<div class="mt-6px text-18px font-600"> <div class="mt-6px text-18px font-600">
{{ formatPercentText(currentPlanning.pendingAmount) }} {{
formatPercentText(
guideDetailMode && activeGuideDetail
? activeGuideDetail.pendingAmount
: currentPlanning.pendingAmount
)
}}
</div> </div>
</div> </div>
</el-col> </el-col>
@@ -350,7 +369,63 @@
</el-col> </el-col>
</el-row> </el-row>
<el-table v-loading="quarterLoading" :data="quarterRows" border> <template v-if="guideDetailMode && !historyParentMode">
<el-empty
v-if="!guideDetailRows.length"
description="暂无指导价法明细,请先维护指导价法明细"
/>
<template v-else>
<el-tabs v-model="activeGuideDetailTab" class="mb-12px" type="card">
<el-tab-pane
v-for="detail in guideDetailRows"
:key="detail.tabKey"
:label="`序号 ${detail.sortNo ?? '-'}`"
:name="detail.tabKey"
/>
</el-tabs>
<el-descriptions
v-if="activeGuideDetail"
:column="4"
border
class="mb-12px"
size="small"
>
<el-descriptions-item label="设计部位">
{{ getDesignPartLabel(activeGuideDetail.designPart) }}
</el-descriptions-item>
<el-descriptions-item label="设计内容/设计类型">
{{ activeGuideDetail.buildingType || '-' }}
</el-descriptions-item>
<el-descriptions-item label="设计面积">
{{ formatAreaText(activeGuideDetail.designArea) }}
</el-descriptions-item>
<el-descriptions-item label="考核产值小计">
{{ formatAmountText(activeGuideDetail.assessmentOutputValue) }}
</el-descriptions-item>
</el-descriptions>
<el-table v-loading="quarterLoading" :data="activeGuideDetailQuarterRows" border>
<el-table-column align="center" label="分配年度" width="150" prop="distributionYear" />
<el-table-column
v-for="quarter in QUARTER_OPTIONS"
:key="quarter.value"
:label="quarter.label"
min-width="220"
>
<template #default="scope">
<div class="flex flex-col gap-8px">
<div class="rounded-6px bg-[var(--el-fill-color-light)] px-10px py-8px text-12px">
分配比例{{ formatQuarterRatio(scope.row, quarter.value) }}
</div>
</div>
</template>
</el-table-column>
</el-table>
</template>
</template>
<el-table v-else v-loading="quarterLoading" :data="quarterRows" border>
<el-table-column align="center" label="分配年度" width="150" prop="distributionYear" /> <el-table-column align="center" label="分配年度" width="150" prop="distributionYear" />
<el-table-column <el-table-column
v-for="quarter in QUARTER_OPTIONS" v-for="quarter in QUARTER_OPTIONS"
@@ -398,6 +473,7 @@ import {
formatPercentText, formatPercentText,
getCalculationMethodLabel, getCalculationMethodLabel,
getCalculationRatioLabel, getCalculationRatioLabel,
getDesignPartLabel,
getDesignStageLabel, getDesignStageLabel,
getOwnershipTypeLabel, getOwnershipTypeLabel,
getVirtualCalculationMethodLabel, getVirtualCalculationMethodLabel,
@@ -415,6 +491,11 @@ interface QuarterYearRow {
quarters: PlanningQuarterApi.ProjectPlanningQuarterVO[] quarters: PlanningQuarterApi.ProjectPlanningQuarterVO[]
} }
interface GuideDetailQuarterRow extends PlanningQuarterApi.ProjectPlanningQuarterGuideDetailVO {
tabKey: string
quarterRows: QuarterYearRow[]
}
const message = useMessage() const message = useMessage()
const loading = ref(false) const loading = ref(false)
@@ -426,9 +507,14 @@ const planningList = ref<PlanningApi.ProjectPlanningVO[]>([])
const currentProject = ref<ProjectApi.ProjectVO>() const currentProject = ref<ProjectApi.ProjectVO>()
const currentPlanning = ref<PlanningApi.ProjectPlanningVO>() const currentPlanning = ref<PlanningApi.ProjectPlanningVO>()
const quarterRows = ref<QuarterYearRow[]>([]) const quarterRows = ref<QuarterYearRow[]>([])
const guideDetailMode = ref(false)
const historyParentMode = ref(false)
const guideDetailRows = ref<GuideDetailQuarterRow[]>([])
const activeGuideDetailTab = ref('')
const queryFormRef = ref() const queryFormRef = ref()
const projectTableRef = ref() const projectTableRef = ref()
const planningTableRef = ref() const planningTableRef = ref()
let planningDetailRequestSeq = 0
const queryParams = reactive<ProjectApi.ProjectPageReqVO>({ const queryParams = reactive<ProjectApi.ProjectPageReqVO>({
pageNo: 1, pageNo: 1,
@@ -478,6 +564,13 @@ const showParentInternalGuidanceUnitPrice = computed(
currentPlanning.value?.internalGuidanceUnitPrice !== undefined && currentPlanning.value?.internalGuidanceUnitPrice !== undefined &&
currentPlanning.value?.internalGuidanceUnitPrice !== null currentPlanning.value?.internalGuidanceUnitPrice !== null
) )
const activeGuideDetail = computed(() => {
return (
guideDetailRows.value.find((item) => item.tabKey === activeGuideDetailTab.value) ||
guideDetailRows.value[0]
)
})
const activeGuideDetailQuarterRows = computed(() => activeGuideDetail.value?.quarterRows || [])
const formatFactorText = (value?: number, digits = 4) => { const formatFactorText = (value?: number, digits = 4) => {
if (value === undefined || value === null) { if (value === undefined || value === null) {
@@ -535,6 +628,20 @@ const buildQuarterRows = (
})) }))
} }
const resetQuarterDetailState = () => {
quarterRows.value = []
guideDetailMode.value = false
historyParentMode.value = false
guideDetailRows.value = []
activeGuideDetailTab.value = ''
}
const cancelQuarterDetailState = () => {
planningDetailRequestSeq += 1
quarterLoading.value = false
resetQuarterDetailState()
}
const getProjectList = async () => { const getProjectList = async () => {
loading.value = true loading.value = true
try { try {
@@ -545,7 +652,7 @@ const getProjectList = async () => {
currentProject.value = undefined currentProject.value = undefined
planningList.value = [] planningList.value = []
currentPlanning.value = undefined currentPlanning.value = undefined
quarterRows.value = [] cancelQuarterDetailState()
return return
} }
const targetProjectId = currentProject.value?.id || projectList.value[0].id const targetProjectId = currentProject.value?.id || projectList.value[0].id
@@ -562,7 +669,7 @@ const getPlanningList = async () => {
if (!currentProject.value?.id) { if (!currentProject.value?.id) {
planningList.value = [] planningList.value = []
currentPlanning.value = undefined currentPlanning.value = undefined
quarterRows.value = [] cancelQuarterDetailState()
return return
} }
planningLoading.value = true planningLoading.value = true
@@ -570,7 +677,7 @@ const getPlanningList = async () => {
planningList.value = await PlanningApi.getProjectPlanningListByProjectId(currentProject.value.id) planningList.value = await PlanningApi.getProjectPlanningListByProjectId(currentProject.value.id)
if (!planningList.value.length) { if (!planningList.value.length) {
currentPlanning.value = undefined currentPlanning.value = undefined
quarterRows.value = [] cancelQuarterDetailState()
return return
} }
const targetPlanningId = currentPlanning.value?.id || planningList.value[0].id const targetPlanningId = currentPlanning.value?.id || planningList.value[0].id
@@ -584,14 +691,35 @@ const getPlanningList = async () => {
} }
const loadPlanningDetail = async (planningId: number) => { const loadPlanningDetail = async (planningId: number) => {
const planning = await PlanningApi.getProjectPlanning(planningId) const requestSeq = ++planningDetailRequestSeq
currentPlanning.value = planning
quarterLoading.value = true quarterLoading.value = true
try { try {
const quarterList = await PlanningQuarterApi.getProjectPlanningQuarterListByPlanningId(planningId) const detail = await PlanningQuarterApi.getProjectPlanningQuarterPlanningDetail(planningId)
quarterRows.value = buildQuarterRows(planning, quarterList) if (requestSeq !== planningDetailRequestSeq) {
return
}
currentPlanning.value = detail?.planning
guideDetailMode.value = !!detail?.guideDetailMode
historyParentMode.value = !!detail?.historyParentMode
if (!detail?.planning) {
resetQuarterDetailState()
return
}
quarterRows.value = buildQuarterRows(detail.planning, detail.quarters || [])
guideDetailRows.value = (detail.guideDetails || []).map((item, index) => ({
...item,
tabKey: String(item.id ?? item.sortNo ?? index),
quarterRows: buildQuarterRows(detail.planning!, item.quarters || [])
}))
const currentTab = activeGuideDetailTab.value
activeGuideDetailTab.value =
guideDetailRows.value.find((item) => item.tabKey === currentTab)?.tabKey ||
guideDetailRows.value[0]?.tabKey ||
''
} finally { } finally {
quarterLoading.value = false if (requestSeq === planningDetailRequestSeq) {
quarterLoading.value = false
}
} }
} }
@@ -606,14 +734,20 @@ const resetQuery = () => {
} }
const handleCurrentProjectChange = async (row?: ProjectApi.ProjectVO) => { const handleCurrentProjectChange = async (row?: ProjectApi.ProjectVO) => {
currentProject.value = row || undefined const previousProjectId = currentProject.value?.id
const nextProject = row || undefined
currentProject.value = nextProject
if (previousProjectId !== nextProject?.id) {
currentPlanning.value = undefined
cancelQuarterDetailState()
}
await getPlanningList() await getPlanningList()
} }
const handleCurrentPlanningChange = async (row?: PlanningApi.ProjectPlanningVO) => { const handleCurrentPlanningChange = async (row?: PlanningApi.ProjectPlanningVO) => {
if (!row?.id) { if (!row?.id) {
currentPlanning.value = undefined currentPlanning.value = undefined
quarterRows.value = [] cancelQuarterDetailState()
return return
} }
await loadPlanningDetail(row.id) await loadPlanningDetail(row.id)
@@ -630,12 +764,15 @@ const openPlanningOutputForm = () => {
planningOutputFormRef.value.open(currentPlanning.value.id) planningOutputFormRef.value.open(currentPlanning.value.id)
} }
const openQuarterDistributionForm = () => { const openQuarterDistributionForm = (guideDetailId?: number) => {
if (!currentPlanning.value?.id) { if (!currentPlanning.value?.id) {
message.warning('请先选择合约规划') message.warning('请先选择合约规划')
return return
} }
quarterDistributionFormRef.value.open(currentPlanning.value.id) quarterDistributionFormRef.value.open(
currentPlanning.value.id,
guideDetailMode.value && !historyParentMode.value ? guideDetailId : undefined
)
} }
const handlePlanningOutputFormSuccess = async () => { const handlePlanningOutputFormSuccess = async () => {

View File

@@ -88,7 +88,7 @@
{{ formatAmountText(scope.row.finalSettlementAmount) }} {{ formatAmountText(scope.row.finalSettlementAmount) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="项目预算产值总计(元)" width="160"> <el-table-column align="center" label="合同产值(不含增值税)(元)" width="190">
<template #default="scope"> <template #default="scope">
{{ formatAmountText(scope.row.effectiveSettlementAmount) }} {{ formatAmountText(scope.row.effectiveSettlementAmount) }}
</template> </template>
@@ -210,7 +210,7 @@
<el-descriptions-item label="结算合同总产值(元)"> <el-descriptions-item label="结算合同总产值(元)">
{{ formatAmountText(budgetDisplay.finalSettlementAmount) }} {{ formatAmountText(budgetDisplay.finalSettlementAmount) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="项目预算产值总计(元)"> <el-descriptions-item label="合同产值(不含增值税)(元)">
{{ formatAmountText(budgetDisplay.effectiveSettlementAmount) }} {{ formatAmountText(budgetDisplay.effectiveSettlementAmount) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="项目开始年度"> <el-descriptions-item label="项目开始年度">
@@ -301,7 +301,7 @@
<el-descriptions-item label="结算合同总产值(元)"> <el-descriptions-item label="结算合同总产值(元)">
{{ formatAmountText(accountingDisplay.finalSettlementAmount) }} {{ formatAmountText(accountingDisplay.finalSettlementAmount) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="项目预算产值总计(元)"> <el-descriptions-item label="合同产值(不含增值税)(元)">
{{ formatAmountText(accountingDisplay.effectiveSettlementAmount) }} {{ formatAmountText(accountingDisplay.effectiveSettlementAmount) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="项目开始年度"> <el-descriptions-item label="项目开始年度">