添加指导价法明细表
This commit is contained in:
@@ -4,8 +4,6 @@ export interface ProjectPlanningVO {
|
|||||||
id?: number
|
id?: number
|
||||||
projectId: number
|
projectId: number
|
||||||
ownershipType: string
|
ownershipType: string
|
||||||
designPart?: string
|
|
||||||
buildingType?: string
|
|
||||||
calculationMethod: string
|
calculationMethod: string
|
||||||
planningContent: string
|
planningContent: string
|
||||||
planningAmount?: number
|
planningAmount?: number
|
||||||
|
|||||||
59
src/api/tjt/planningGuideDetail/index.ts
Normal file
59
src/api/tjt/planningGuideDetail/index.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ProjectPlanningGuideDetailVO {
|
||||||
|
id?: number
|
||||||
|
planningId: number
|
||||||
|
projectId?: number
|
||||||
|
designPart?: string
|
||||||
|
buildingType?: string
|
||||||
|
designArea?: number
|
||||||
|
internalGuidanceUnitPrice?: number
|
||||||
|
buildingOrUnitCount?: number
|
||||||
|
drawingSetFactor?: number
|
||||||
|
scaleFactor?: number
|
||||||
|
modificationFactor?: number
|
||||||
|
complexityFactor?: number
|
||||||
|
totalAdjustmentFactor?: number
|
||||||
|
designRatio?: number
|
||||||
|
assessmentArea?: number
|
||||||
|
assessmentOutputValue?: number
|
||||||
|
sortNo?: number
|
||||||
|
remark?: string
|
||||||
|
createTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectPlanningGuideDetailBatchSaveVO {
|
||||||
|
planningId: number
|
||||||
|
details: Array<
|
||||||
|
Pick<
|
||||||
|
ProjectPlanningGuideDetailVO,
|
||||||
|
| 'id'
|
||||||
|
| 'designPart'
|
||||||
|
| 'buildingType'
|
||||||
|
| 'designArea'
|
||||||
|
| 'internalGuidanceUnitPrice'
|
||||||
|
| 'buildingOrUnitCount'
|
||||||
|
| 'drawingSetFactor'
|
||||||
|
| 'scaleFactor'
|
||||||
|
| 'modificationFactor'
|
||||||
|
| 'complexityFactor'
|
||||||
|
| 'designRatio'
|
||||||
|
| 'sortNo'
|
||||||
|
| 'remark'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getProjectPlanningGuideDetailListByPlanningId = (planningId: number) => {
|
||||||
|
return request.get({ url: '/tjt/planning-guide-detail/list-by-planning', params: { planningId } })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const batchSaveProjectPlanningGuideDetail = (
|
||||||
|
data: ProjectPlanningGuideDetailBatchSaveVO
|
||||||
|
) => {
|
||||||
|
return request.post({ url: '/tjt/planning-guide-detail/batch-save', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteProjectPlanningGuideDetail = (id: number) => {
|
||||||
|
return request.delete({ url: '/tjt/planning-guide-detail/delete', params: { id } })
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1120">
|
<Dialog v-model="dialogVisible" title="编辑测算参数" width="92%">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
v-loading="formLoading"
|
v-loading="formLoading"
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="归属类型">
|
<el-form-item label="归属类型">
|
||||||
<el-input :model-value="ownershipTypeLabel" disabled />
|
<el-input :model-value="getOwnershipTypeLabel(formData.ownershipType)" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
placeholder="请选择产值计算方式"
|
placeholder="请选择产值计算方式"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in calculationMethodOptions"
|
v-for="item in CALCULATION_METHOD_OPTIONS"
|
||||||
:key="String(item.value)"
|
:key="String(item.value)"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -55,35 +55,6 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="设计部位" prop="designPart">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.designPart"
|
|
||||||
class="!w-1/1"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择设计部位"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in DESIGN_PART_OPTIONS"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="建筑类型" prop="buildingType">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.buildingType"
|
|
||||||
maxlength="100"
|
|
||||||
placeholder="请输入建筑类型"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-divider content-position="left">测算参数</el-divider>
|
<el-divider content-position="left">测算参数</el-divider>
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -99,7 +70,13 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="工程总面积(m²)" prop="planningArea">
|
<el-form-item label="工程总面积(m²)" prop="planningArea">
|
||||||
|
<el-input
|
||||||
|
v-if="showGuideDetailSection"
|
||||||
|
:model-value="formatAmountText(guideDetailSummary.designArea)"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
<el-input-number
|
<el-input-number
|
||||||
|
v-else
|
||||||
v-model="formData.planningArea"
|
v-model="formData.planningArea"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
@@ -119,13 +96,9 @@
|
|||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="设计阶段" prop="designStage">
|
<el-form-item label="设计阶段" prop="designStage">
|
||||||
<el-select
|
<el-select v-model="formData.designStage" class="!w-1/1" placeholder="请选择设计阶段">
|
||||||
v-model="formData.designStage"
|
|
||||||
class="!w-1/1"
|
|
||||||
placeholder="请选择设计阶段"
|
|
||||||
>
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in designStageOptions"
|
v-for="item in DESIGN_STAGE_OPTIONS"
|
||||||
:key="String(item.value)"
|
:key="String(item.value)"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -133,9 +106,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="本次设计阶段比例(%)" prop="currentDesignStageRatio">
|
<el-form-item label="本次设计阶段比例(%)" prop="currentDesignStageRatio">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
@@ -148,6 +118,9 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="16">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="审核审定是否外包" prop="reviewOutsourceFlag">
|
<el-form-item label="审核审定是否外包" prop="reviewOutsourceFlag">
|
||||||
<el-switch
|
<el-switch
|
||||||
@@ -170,9 +143,6 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col v-if="showCalculationRatioField" :span="8">
|
<el-col v-if="showCalculationRatioField" :span="8">
|
||||||
<el-form-item :label="`${calculationRatioLabel}(%)`" prop="calculationRatio">
|
<el-form-item :label="`${calculationRatioLabel}(%)`" prop="calculationRatio">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
@@ -185,7 +155,10 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="showCalculationRatioField ? 8 : 12">
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
<el-form-item label="总分配(%)" prop="totalDistributionAmount">
|
<el-form-item label="总分配(%)" prop="totalDistributionAmount">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="totalDistributionAmountPercent"
|
v-model="totalDistributionAmountPercent"
|
||||||
@@ -199,7 +172,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<template v-if="showMajorFactorFields">
|
<template v-if="showParentMajorFactorFields">
|
||||||
<el-divider content-position="left">专业所测算参数</el-divider>
|
<el-divider content-position="left">专业所测算参数</el-divider>
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -219,7 +192,7 @@
|
|||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="formData.drawingSetFactor"
|
v-model="formData.drawingSetFactor"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="4"
|
||||||
:step="0.01"
|
:step="0.01"
|
||||||
class="!w-1/1"
|
class="!w-1/1"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
@@ -231,7 +204,7 @@
|
|||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="formData.scaleFactor"
|
v-model="formData.scaleFactor"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="4"
|
||||||
:step="0.01"
|
:step="0.01"
|
||||||
class="!w-1/1"
|
class="!w-1/1"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
@@ -245,7 +218,7 @@
|
|||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="formData.modificationFactor"
|
v-model="formData.modificationFactor"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="4"
|
||||||
:step="0.01"
|
:step="0.01"
|
||||||
class="!w-1/1"
|
class="!w-1/1"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
@@ -267,18 +240,171 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="showInternalGuidanceUnitPriceField">
|
<!-- 优化后的指导价法明细块 -->
|
||||||
<el-divider content-position="left">指导价法参数</el-divider>
|
<template v-if="showGuideDetailSection">
|
||||||
<el-row :gutter="16">
|
<el-divider content-position="left">指导价法明细</el-divider>
|
||||||
<el-col :span="8">
|
<div class="mb-12px flex items-center justify-between gap-12px">
|
||||||
<el-form-item label="内部指导单价(元/m²)" prop="internalGuidanceUnitPrice">
|
<el-button plain type="primary" @click="addGuideDetailRow">
|
||||||
|
<template #icon><i class="el-icon-plus"></i></template>新增明细
|
||||||
|
</el-button>
|
||||||
|
<span class="text-12px text-gray-400">💡 提示:数字字段已隐藏加减箭头以优化显示,支持直接输入或复制粘贴</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="guideDetails" border max-height="460" class="optimized-table">
|
||||||
|
<!-- 1. 冻结核心上下文列 -->
|
||||||
|
<el-table-column align="center" label="序号" width="65" fixed="left">
|
||||||
|
<template #default="{ row }">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="formData.internalGuidanceUnitPrice"
|
v-model="row.sortNo"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
:controls="false"
|
||||||
|
class="!w-1/1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="设计部位" min-width="120" fixed="left">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-select v-model="row.designPart" class="!w-1/1" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="item in DESIGN_PART_OPTIONS"
|
||||||
|
:key="String(item.value)"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="建筑类型" min-width="140" fixed="left">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.buildingType" maxlength="100" placeholder="建筑类型" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<!-- 2. 取消 controls 的核心数值列 -->
|
||||||
|
<el-table-column align="center" label="指导单价(元)" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.internalGuidanceUnitPrice"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
:step="1"
|
:controls="false"
|
||||||
class="!w-1/1"
|
class="!w-1/1"
|
||||||
controls-position="right"
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="设计面积(m²)" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.designArea"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
class="!w-1/1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="楼栋/户型数" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.buildingOrUnitCount"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
:controls="false"
|
||||||
|
class="!w-1/1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<!-- 3. 多级表头折叠系数配置 -->
|
||||||
|
<el-table-column label="调整系数配置" align="center">
|
||||||
|
<el-table-column align="center" label="套图" min-width="85">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number v-model="row.drawingSetFactor" :min="0" :precision="4" :controls="false" class="!w-1/1" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="规模" min-width="85">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number v-model="row.scaleFactor" :min="0" :precision="4" :controls="false" class="!w-1/1" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="修改" min-width="85">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number v-model="row.modificationFactor" :min="0" :precision="4" :controls="false" class="!w-1/1" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="复杂(%)" min-width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
:model-value="toPercentValue(row.complexityFactor)"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
class="!w-1/1"
|
||||||
|
@update:model-value="setGuideDetailPercentValue(row, 'complexityFactor', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="合计" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="text-gray-500">{{ formatFactorText(getGuideDetailTotalAdjustmentFactor(row)) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column align="center" label="设计占比(%)" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
:model-value="toPercentValue(row.designRatio)"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
class="!w-1/1"
|
||||||
|
@update:model-value="setGuideDetailPercentValue(row, 'designRatio', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<!-- 4. 结果列靠右显示,用浅色背景区分 -->
|
||||||
|
<el-table-column align="right" label="考核面积(m²)" min-width="110" class-name="bg-gray-50">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="font-bold">{{ formatAmountText(getGuideDetailAssessmentArea(row)) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="right" label="考核产值(元)" min-width="120" class-name="bg-gray-50">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="font-bold text-primary">{{ formatAmountText(getGuideDetailAssessmentOutputValue(row)) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column align="center" label="备注" min-width="130">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.remark" maxlength="500" placeholder="备注信息" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" fixed="right" label="操作" width="70">
|
||||||
|
<template #default="{ $index }">
|
||||||
|
<el-button link type="danger" @click="removeGuideDetailRow($index)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-row :gutter="16" class="mt-16px">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="汇总设计面积(m²)">
|
||||||
|
<el-input :model-value="formatAmountText(guideDetailSummary.designArea)" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="汇总考核面积(m²)">
|
||||||
|
<el-input :model-value="formatAmountText(guideDetailSummary.assessmentArea)" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="汇总考核产值(元)">
|
||||||
|
<el-input
|
||||||
|
:model-value="formatAmountText(guideDetailSummary.assessmentOutputValue)"
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -296,7 +422,7 @@
|
|||||||
placeholder="请选择虚拟产值计算方式"
|
placeholder="请选择虚拟产值计算方式"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in virtualCalculationMethodOptions"
|
v-for="item in VIRTUAL_CALCULATION_METHOD_OPTIONS"
|
||||||
:key="String(item.value)"
|
:key="String(item.value)"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -359,6 +485,7 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">保存</el-button>
|
<el-button :disabled="formLoading" type="primary" @click="submitForm">保存</el-button>
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
@@ -367,19 +494,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
// [原有的 Script 逻辑不变,直接粘贴原本的逻辑即可]
|
||||||
import type { FormRules } from 'element-plus'
|
import type { FormRules } from 'element-plus'
|
||||||
import * as PlanningApi from '@/api/tjt/planning'
|
import * as PlanningApi from '@/api/tjt/planning'
|
||||||
|
import * as PlanningGuideDetailApi from '@/api/tjt/planningGuideDetail'
|
||||||
import {
|
import {
|
||||||
CALCULATION_METHOD_OPTIONS,
|
CALCULATION_METHOD_OPTIONS,
|
||||||
DEFAULT_WORKING_DAY_UNIT_PRICE,
|
DEFAULT_WORKING_DAY_UNIT_PRICE,
|
||||||
|
DESIGN_PART_OPTIONS,
|
||||||
DESIGN_STAGE_OPTIONS,
|
DESIGN_STAGE_OPTIONS,
|
||||||
OWNERSHIP_TYPE_OPTIONS,
|
|
||||||
VIRTUAL_CALCULATION_METHOD_OPTIONS,
|
VIRTUAL_CALCULATION_METHOD_OPTIONS,
|
||||||
formatAmountText,
|
formatAmountText,
|
||||||
formatPercentText,
|
formatPercentText,
|
||||||
fromPercentValue,
|
fromPercentValue,
|
||||||
getCalculationRatioDefaultPercent,
|
getCalculationRatioDefaultPercent,
|
||||||
getCalculationRatioLabel,
|
getCalculationRatioLabel,
|
||||||
|
getOwnershipTypeLabel,
|
||||||
getReviewOutsourceDefaultPercent,
|
getReviewOutsourceDefaultPercent,
|
||||||
isComprehensiveOwnership,
|
isComprehensiveOwnership,
|
||||||
isContractPriceMethod,
|
isContractPriceMethod,
|
||||||
@@ -399,90 +529,64 @@ import {
|
|||||||
|
|
||||||
defineOptions({ name: 'TjtPlanningOutputForm' })
|
defineOptions({ name: 'TjtPlanningOutputForm' })
|
||||||
|
|
||||||
const DESIGN_PART_OPTIONS = [
|
type GuideDetailRow = PlanningGuideDetailApi.ProjectPlanningGuideDetailVO
|
||||||
{ label: '地上部分', value: '地上部分' },
|
|
||||||
{ label: '地下部分', value: '地下部分' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const OWNERSHIP_TYPE_LABELS = ['专业所', '综合所', '专业分包']
|
|
||||||
const CALCULATION_METHOD_LABELS = ['指导价法', '合同价法', '虚拟产值法']
|
|
||||||
const DESIGN_STAGE_LABELS = ['方案', '施工图', '方案+施工图']
|
|
||||||
const VIRTUAL_CALCULATION_METHOD_LABELS = ['指导单价法', '指导总价法', '工日法']
|
|
||||||
|
|
||||||
const createDisplayOptions = (
|
|
||||||
source: Array<{ label: string; value: string }>,
|
|
||||||
labels: string[]
|
|
||||||
) => source.map((item, index) => ({ label: labels[index] || item.label, value: item.value }))
|
|
||||||
|
|
||||||
const ownershipTypeOptions = createDisplayOptions(OWNERSHIP_TYPE_OPTIONS, OWNERSHIP_TYPE_LABELS)
|
|
||||||
const calculationMethodOptions = createDisplayOptions(
|
|
||||||
CALCULATION_METHOD_OPTIONS,
|
|
||||||
CALCULATION_METHOD_LABELS
|
|
||||||
)
|
|
||||||
const designStageOptions = createDisplayOptions(DESIGN_STAGE_OPTIONS, DESIGN_STAGE_LABELS)
|
|
||||||
const virtualCalculationMethodOptions = createDisplayOptions(
|
|
||||||
VIRTUAL_CALCULATION_METHOD_OPTIONS,
|
|
||||||
VIRTUAL_CALCULATION_METHOD_LABELS
|
|
||||||
)
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const dialogTitle = ref('')
|
|
||||||
const formLoading = ref(false)
|
const formLoading = ref(false)
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const formData = ref<PlanningApi.ProjectPlanningVO>({
|
|
||||||
|
const createFormData = (): PlanningApi.ProjectPlanningVO => ({
|
||||||
|
id: undefined,
|
||||||
projectId: 0,
|
projectId: 0,
|
||||||
ownershipType: '',
|
ownershipType: '',
|
||||||
calculationMethod: '',
|
calculationMethod: '',
|
||||||
planningContent: ''
|
planningContent: '',
|
||||||
|
planningAmount: undefined,
|
||||||
|
managementFeeRate: undefined,
|
||||||
|
managementFee: undefined,
|
||||||
|
implementationTeam: '',
|
||||||
|
planningStartYear: undefined,
|
||||||
|
planningArea: undefined,
|
||||||
|
designStage: undefined,
|
||||||
|
currentDesignStageRatio: undefined,
|
||||||
|
reviewOutsourceFlag: false,
|
||||||
|
reviewOutsourceRatio: undefined,
|
||||||
|
totalDistributionAmount: 1,
|
||||||
|
progressRemark: '',
|
||||||
|
allocatedAmount: undefined,
|
||||||
|
pendingAmount: undefined,
|
||||||
|
buildingOrUnitCount: undefined,
|
||||||
|
drawingSetFactor: undefined,
|
||||||
|
scaleFactor: undefined,
|
||||||
|
modificationFactor: undefined,
|
||||||
|
complexityFactor: undefined,
|
||||||
|
internalGuidanceUnitPrice: undefined,
|
||||||
|
virtualCalculationMethod: undefined,
|
||||||
|
workingDayCount: undefined,
|
||||||
|
workingDayUnitPrice: undefined,
|
||||||
|
guidanceUnitPrice: undefined,
|
||||||
|
guidanceTotalPrice: undefined,
|
||||||
|
calculationRatio: undefined,
|
||||||
|
contractUnitPrice: undefined,
|
||||||
|
totalAdjustmentFactor: undefined,
|
||||||
|
assessmentArea: undefined,
|
||||||
|
virtualOutputValue: undefined,
|
||||||
|
assessmentOutputValue: undefined,
|
||||||
|
createTime: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const getOptionLabel = (options: Array<{ label: string; value: string }>, value?: string) =>
|
const formData = ref<PlanningApi.ProjectPlanningVO>(createFormData())
|
||||||
options.find((item) => item.value === value)?.label || value || ''
|
const guideDetails = ref<GuideDetailRow[]>([])
|
||||||
|
|
||||||
const ownershipTypeLabel = computed(() =>
|
|
||||||
getOptionLabel(ownershipTypeOptions, formData.value.ownershipType)
|
|
||||||
)
|
|
||||||
|
|
||||||
const normalizeFormData = (data: PlanningApi.ProjectPlanningVO): PlanningApi.ProjectPlanningVO => ({
|
|
||||||
...data,
|
|
||||||
ownershipType: normalizeOwnershipType(data.ownershipType) || data.ownershipType || '',
|
|
||||||
calculationMethod: normalizeCalculationMethod(data.calculationMethod) || data.calculationMethod || '',
|
|
||||||
designPart: DESIGN_PART_OPTIONS.find((item) => item.value === data.designPart)?.value || undefined,
|
|
||||||
designStage: normalizeDesignStage(data.designStage),
|
|
||||||
virtualCalculationMethod: normalizeVirtualCalculationMethod(data.virtualCalculationMethod),
|
|
||||||
reviewOutsourceFlag: data.reviewOutsourceFlag ?? false,
|
|
||||||
totalDistributionAmount: data.totalDistributionAmount ?? 1,
|
|
||||||
progressRemark: data.progressRemark ?? '',
|
|
||||||
drawingSetFactor: data.drawingSetFactor ?? 1,
|
|
||||||
scaleFactor: data.scaleFactor ?? 1,
|
|
||||||
modificationFactor: data.modificationFactor ?? 1,
|
|
||||||
complexityFactor: data.complexityFactor ?? 1,
|
|
||||||
workingDayUnitPrice:
|
|
||||||
data.workingDayUnitPrice ??
|
|
||||||
(isWorkingDayMethod(data.virtualCalculationMethod) ? DEFAULT_WORKING_DAY_UNIT_PRICE : undefined),
|
|
||||||
guidanceTotalPrice: data.guidanceTotalPrice
|
|
||||||
})
|
|
||||||
|
|
||||||
const planningStartYearValue = computed({
|
const planningStartYearValue = computed({
|
||||||
get: () =>
|
get: () => (formData.value.planningStartYear ? String(formData.value.planningStartYear) : undefined),
|
||||||
formData.value.planningStartYear ? String(formData.value.planningStartYear) : undefined,
|
|
||||||
set: (value?: string) => {
|
set: (value?: string) => {
|
||||||
formData.value.planningStartYear = value ? Number(value) : undefined
|
formData.value.planningStartYear = value ? Number(value) : undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const contractUnitPricePreview = computed(() => {
|
|
||||||
const planningAmount = Number(formData.value.planningAmount || 0)
|
|
||||||
const planningArea = Number(formData.value.planningArea || 0)
|
|
||||||
if (!planningArea) {
|
|
||||||
return formatAmountText(0)
|
|
||||||
}
|
|
||||||
return formatAmountText(planningAmount / planningArea)
|
|
||||||
})
|
|
||||||
|
|
||||||
const createPercentModel = (field: keyof PlanningApi.ProjectPlanningVO, digits = 2) =>
|
const createPercentModel = (field: keyof PlanningApi.ProjectPlanningVO, digits = 2) =>
|
||||||
computed({
|
computed({
|
||||||
get: () => toPercentValue(formData.value[field] as number | undefined, digits),
|
get: () => toPercentValue(formData.value[field] as number | undefined, digits),
|
||||||
@@ -502,23 +606,25 @@ const showCalculationRatioField = computed(
|
|||||||
isComprehensiveOwnership(formData.value.ownershipType) ||
|
isComprehensiveOwnership(formData.value.ownershipType) ||
|
||||||
isSubcontractOwnership(formData.value.ownershipType)
|
isSubcontractOwnership(formData.value.ownershipType)
|
||||||
)
|
)
|
||||||
const calculationRatioLabel = computed(() => getCalculationRatioLabel(formData.value.ownershipType))
|
|
||||||
const showMajorFactorFields = computed(
|
const showGuideDetailSection = computed(
|
||||||
() =>
|
|
||||||
isMajorOwnership(formData.value.ownershipType) &&
|
|
||||||
(isGuidancePriceMethod(formData.value.calculationMethod) ||
|
|
||||||
isContractPriceMethod(formData.value.calculationMethod))
|
|
||||||
)
|
|
||||||
const showInternalGuidanceUnitPriceField = computed(
|
|
||||||
() =>
|
() =>
|
||||||
isMajorOwnership(formData.value.ownershipType) &&
|
isMajorOwnership(formData.value.ownershipType) &&
|
||||||
isGuidancePriceMethod(formData.value.calculationMethod)
|
isGuidancePriceMethod(formData.value.calculationMethod)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const showParentMajorFactorFields = computed(
|
||||||
|
() =>
|
||||||
|
isMajorOwnership(formData.value.ownershipType) &&
|
||||||
|
isContractPriceMethod(formData.value.calculationMethod)
|
||||||
|
)
|
||||||
|
|
||||||
const showVirtualOutputSection = computed(
|
const showVirtualOutputSection = computed(
|
||||||
() =>
|
() =>
|
||||||
isMajorOwnership(formData.value.ownershipType) &&
|
isMajorOwnership(formData.value.ownershipType) &&
|
||||||
isVirtualOutputMethod(formData.value.calculationMethod)
|
isVirtualOutputMethod(formData.value.calculationMethod)
|
||||||
)
|
)
|
||||||
|
|
||||||
const showWorkingDayFields = computed(() => isWorkingDayMethod(formData.value.virtualCalculationMethod))
|
const showWorkingDayFields = computed(() => isWorkingDayMethod(formData.value.virtualCalculationMethod))
|
||||||
const showGuidanceUnitPriceField = computed(
|
const showGuidanceUnitPriceField = computed(
|
||||||
() => isVirtualGuidanceMethod(formData.value.virtualCalculationMethod)
|
() => isVirtualGuidanceMethod(formData.value.virtualCalculationMethod)
|
||||||
@@ -526,13 +632,188 @@ const showGuidanceUnitPriceField = computed(
|
|||||||
const showGuidanceTotalPriceField = computed(
|
const showGuidanceTotalPriceField = computed(
|
||||||
() => isVirtualGuidanceTotalPriceMethod(formData.value.virtualCalculationMethod)
|
() => isVirtualGuidanceTotalPriceMethod(formData.value.virtualCalculationMethod)
|
||||||
)
|
)
|
||||||
|
const calculationRatioLabel = computed(() => getCalculationRatioLabel(formData.value.ownershipType))
|
||||||
|
|
||||||
|
const normalizeFormData = (data: PlanningApi.ProjectPlanningVO): PlanningApi.ProjectPlanningVO => ({
|
||||||
|
...createFormData(),
|
||||||
|
...data,
|
||||||
|
ownershipType: normalizeOwnershipType(data.ownershipType) || data.ownershipType || '',
|
||||||
|
calculationMethod: normalizeCalculationMethod(data.calculationMethod) || data.calculationMethod || '',
|
||||||
|
designStage: normalizeDesignStage(data.designStage),
|
||||||
|
virtualCalculationMethod: normalizeVirtualCalculationMethod(data.virtualCalculationMethod),
|
||||||
|
reviewOutsourceFlag: data.reviewOutsourceFlag ?? false,
|
||||||
|
totalDistributionAmount: data.totalDistributionAmount ?? 1,
|
||||||
|
progressRemark: data.progressRemark ?? '',
|
||||||
|
workingDayUnitPrice:
|
||||||
|
data.workingDayUnitPrice ??
|
||||||
|
(isWorkingDayMethod(data.virtualCalculationMethod) ? DEFAULT_WORKING_DAY_UNIT_PRICE : undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
const createGuideDetailRow = (sortNo: number): GuideDetailRow => ({
|
||||||
|
planningId: formData.value.id || 0,
|
||||||
|
sortNo,
|
||||||
|
designPart: undefined,
|
||||||
|
buildingType: '',
|
||||||
|
designArea: undefined,
|
||||||
|
internalGuidanceUnitPrice: undefined,
|
||||||
|
buildingOrUnitCount: undefined,
|
||||||
|
drawingSetFactor: undefined,
|
||||||
|
scaleFactor: undefined,
|
||||||
|
modificationFactor: undefined,
|
||||||
|
complexityFactor: undefined,
|
||||||
|
totalAdjustmentFactor: undefined,
|
||||||
|
designRatio: undefined,
|
||||||
|
assessmentArea: undefined,
|
||||||
|
assessmentOutputValue: undefined,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalizeGuideDetailList = (
|
||||||
|
list: PlanningGuideDetailApi.ProjectPlanningGuideDetailVO[] | undefined
|
||||||
|
): GuideDetailRow[] =>
|
||||||
|
[...(list || [])]
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
Number(a.sortNo ?? Number.MAX_SAFE_INTEGER) - Number(b.sortNo ?? Number.MAX_SAFE_INTEGER)
|
||||||
|
)
|
||||||
|
.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
planningId: item.planningId || formData.value.id || 0,
|
||||||
|
sortNo: item.sortNo ?? index + 1,
|
||||||
|
buildingType: item.buildingType ?? '',
|
||||||
|
remark: item.remark ?? ''
|
||||||
|
}))
|
||||||
|
|
||||||
|
const addGuideDetailRow = () => {
|
||||||
|
guideDetails.value.push(createGuideDetailRow(guideDetails.value.length + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeGuideDetailRow = (index: number) => {
|
||||||
|
guideDetails.value.splice(index, 1)
|
||||||
|
resetGuideDetailSortNo()
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetGuideDetailSortNo = () => {
|
||||||
|
guideDetails.value = guideDetails.value.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
sortNo: index + 1
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGuideDetailTotalAdjustmentFactor = (row: GuideDetailRow) => {
|
||||||
|
if (
|
||||||
|
row.drawingSetFactor === undefined ||
|
||||||
|
row.scaleFactor === undefined ||
|
||||||
|
row.modificationFactor === undefined ||
|
||||||
|
row.complexityFactor === undefined
|
||||||
|
) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return Number(
|
||||||
|
(
|
||||||
|
Number(row.drawingSetFactor) *
|
||||||
|
Number(row.scaleFactor) *
|
||||||
|
Number(row.modificationFactor) *
|
||||||
|
Number(row.complexityFactor)
|
||||||
|
).toFixed(4)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGuideDetailAssessmentArea = (row: GuideDetailRow) => {
|
||||||
|
const totalAdjustmentFactor = getGuideDetailTotalAdjustmentFactor(row)
|
||||||
|
if (row.designArea === undefined || totalAdjustmentFactor === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return Number((Number(row.designArea) * totalAdjustmentFactor).toFixed(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGuideDetailAssessmentOutputValue = (row: GuideDetailRow) => {
|
||||||
|
const assessmentArea = getGuideDetailAssessmentArea(row)
|
||||||
|
if (
|
||||||
|
row.internalGuidanceUnitPrice === undefined ||
|
||||||
|
row.designRatio === undefined ||
|
||||||
|
assessmentArea === undefined
|
||||||
|
) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return Number(
|
||||||
|
(
|
||||||
|
Number(row.internalGuidanceUnitPrice) *
|
||||||
|
assessmentArea *
|
||||||
|
Number(row.designRatio) *
|
||||||
|
(1 - Number(formData.value.reviewOutsourceRatio || 0))
|
||||||
|
).toFixed(2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const guideDetailSummary = computed(() =>
|
||||||
|
guideDetails.value.reduce(
|
||||||
|
(summary, row) => ({
|
||||||
|
designArea: Number((summary.designArea + Number(row.designArea || 0)).toFixed(2)),
|
||||||
|
assessmentArea: Number(
|
||||||
|
(summary.assessmentArea + Number(getGuideDetailAssessmentArea(row) || 0)).toFixed(2)
|
||||||
|
),
|
||||||
|
assessmentOutputValue: Number(
|
||||||
|
(
|
||||||
|
summary.assessmentOutputValue + Number(getGuideDetailAssessmentOutputValue(row) || 0)
|
||||||
|
).toFixed(2)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
designArea: 0,
|
||||||
|
assessmentArea: 0,
|
||||||
|
assessmentOutputValue: 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const contractUnitPricePreview = computed(() => {
|
||||||
|
const planningAmount = Number(formData.value.planningAmount || 0)
|
||||||
|
const planningArea = showGuideDetailSection.value
|
||||||
|
? Number(guideDetailSummary.value.designArea || 0)
|
||||||
|
: Number(formData.value.planningArea || 0)
|
||||||
|
if (!planningArea) {
|
||||||
|
return formatAmountText(0)
|
||||||
|
}
|
||||||
|
return formatAmountText(planningAmount / planningArea)
|
||||||
|
})
|
||||||
|
|
||||||
|
const formatFactorText = (value?: number, digits = 4) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
return Number(value).toFixed(digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setGuideDetailPercentValue = (
|
||||||
|
row: GuideDetailRow,
|
||||||
|
field: 'complexityFactor' | 'designRatio',
|
||||||
|
value?: number | string | null
|
||||||
|
) => {
|
||||||
|
row[field] = fromPercentValue(value, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasValue = (value: unknown) => value !== undefined && value !== null && value !== ''
|
||||||
|
|
||||||
const formRules = reactive<FormRules>({
|
const formRules = reactive<FormRules>({
|
||||||
calculationMethod: [{ required: true, message: '产值计算方式不能为空', trigger: 'change' }],
|
calculationMethod: [{ required: true, message: '产值计算方式不能为空', trigger: 'change' }],
|
||||||
designPart: [{ required: true, message: '设计部位不能为空', trigger: 'change' }],
|
|
||||||
buildingType: [{ required: true, message: '建筑类型不能为空', trigger: 'blur' }],
|
|
||||||
planningStartYear: [{ required: true, message: '开始年度不能为空', trigger: 'change' }],
|
planningStartYear: [{ required: true, message: '开始年度不能为空', trigger: 'change' }],
|
||||||
planningArea: [{ required: true, message: '工程总面积不能为空', trigger: 'blur' }],
|
planningArea: [
|
||||||
|
{
|
||||||
|
validator: (_rule, value, callback) => {
|
||||||
|
if (showGuideDetailSection.value) {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!hasValue(value)) {
|
||||||
|
callback(new Error('工程总面积不能为空'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
designStage: [{ required: true, message: '设计阶段不能为空', trigger: 'change' }],
|
designStage: [{ required: true, message: '设计阶段不能为空', trigger: 'change' }],
|
||||||
currentDesignStageRatio: [
|
currentDesignStageRatio: [
|
||||||
{ required: true, message: '本次设计阶段比例不能为空', trigger: 'blur' }
|
{ required: true, message: '本次设计阶段比例不能为空', trigger: 'blur' }
|
||||||
@@ -541,7 +822,7 @@ const formRules = reactive<FormRules>({
|
|||||||
calculationRatio: [
|
calculationRatio: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback) => {
|
validator: (_rule, value, callback) => {
|
||||||
if (showCalculationRatioField.value && (value === undefined || value === null || value === '')) {
|
if (showCalculationRatioField.value && !hasValue(value)) {
|
||||||
callback(new Error(`${calculationRatioLabel.value}不能为空`))
|
callback(new Error(`${calculationRatioLabel.value}不能为空`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -550,21 +831,6 @@ const formRules = reactive<FormRules>({
|
|||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
internalGuidanceUnitPrice: [
|
|
||||||
{
|
|
||||||
validator: (_rule, value, callback) => {
|
|
||||||
if (
|
|
||||||
showInternalGuidanceUnitPriceField.value &&
|
|
||||||
(value === undefined || value === null || value === '')
|
|
||||||
) {
|
|
||||||
callback(new Error('内部指导单价不能为空'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
callback()
|
|
||||||
},
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
virtualCalculationMethod: [
|
virtualCalculationMethod: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback) => {
|
validator: (_rule, value, callback) => {
|
||||||
@@ -580,7 +846,7 @@ const formRules = reactive<FormRules>({
|
|||||||
workingDayCount: [
|
workingDayCount: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback) => {
|
validator: (_rule, value, callback) => {
|
||||||
if (showWorkingDayFields.value && (value === undefined || value === null || value === '')) {
|
if (showWorkingDayFields.value && !hasValue(value)) {
|
||||||
callback(new Error('工日不能为空'))
|
callback(new Error('工日不能为空'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -592,7 +858,7 @@ const formRules = reactive<FormRules>({
|
|||||||
workingDayUnitPrice: [
|
workingDayUnitPrice: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback) => {
|
validator: (_rule, value, callback) => {
|
||||||
if (showWorkingDayFields.value && (value === undefined || value === null || value === '')) {
|
if (showWorkingDayFields.value && !hasValue(value)) {
|
||||||
callback(new Error('工日单价不能为空'))
|
callback(new Error('工日单价不能为空'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -604,10 +870,7 @@ const formRules = reactive<FormRules>({
|
|||||||
guidanceUnitPrice: [
|
guidanceUnitPrice: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback) => {
|
validator: (_rule, value, callback) => {
|
||||||
if (
|
if (showGuidanceUnitPriceField.value && !hasValue(value)) {
|
||||||
showGuidanceUnitPriceField.value &&
|
|
||||||
(value === undefined || value === null || value === '')
|
|
||||||
) {
|
|
||||||
callback(new Error('指导单价不能为空'))
|
callback(new Error('指导单价不能为空'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -619,10 +882,7 @@ const formRules = reactive<FormRules>({
|
|||||||
guidanceTotalPrice: [
|
guidanceTotalPrice: [
|
||||||
{
|
{
|
||||||
validator: (_rule, value, callback) => {
|
validator: (_rule, value, callback) => {
|
||||||
if (
|
if (showGuidanceTotalPriceField.value && !hasValue(value)) {
|
||||||
showGuidanceTotalPriceField.value &&
|
|
||||||
(value === undefined || value === null || value === '')
|
|
||||||
) {
|
|
||||||
callback(new Error('指导总价不能为空'))
|
callback(new Error('指导总价不能为空'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -668,31 +928,39 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(showGuideDetailSection, (value) => {
|
||||||
|
if (value && !guideDetails.value.length) {
|
||||||
|
addGuideDetailRow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const buildSavePayload = (): PlanningApi.ProjectPlanningSaveVO => ({
|
const buildSavePayload = (): PlanningApi.ProjectPlanningSaveVO => ({
|
||||||
id: formData.value.id,
|
id: formData.value.id,
|
||||||
projectId: formData.value.projectId,
|
projectId: formData.value.projectId,
|
||||||
ownershipType: formData.value.ownershipType,
|
ownershipType: formData.value.ownershipType,
|
||||||
designPart: formData.value.designPart,
|
|
||||||
buildingType: formData.value.buildingType,
|
|
||||||
calculationMethod: formData.value.calculationMethod,
|
calculationMethod: formData.value.calculationMethod,
|
||||||
planningContent: formData.value.planningContent,
|
planningContent: formData.value.planningContent,
|
||||||
planningAmount: formData.value.planningAmount,
|
planningAmount: formData.value.planningAmount,
|
||||||
managementFeeRate: formData.value.managementFeeRate,
|
managementFeeRate: formData.value.managementFeeRate,
|
||||||
implementationTeam: formData.value.implementationTeam,
|
implementationTeam: formData.value.implementationTeam,
|
||||||
planningStartYear: formData.value.planningStartYear,
|
planningStartYear: formData.value.planningStartYear,
|
||||||
planningArea: formData.value.planningArea,
|
planningArea: showGuideDetailSection.value
|
||||||
|
? guideDetailSummary.value.designArea
|
||||||
|
: formData.value.planningArea,
|
||||||
designStage: formData.value.designStage,
|
designStage: formData.value.designStage,
|
||||||
currentDesignStageRatio: formData.value.currentDesignStageRatio,
|
currentDesignStageRatio: formData.value.currentDesignStageRatio,
|
||||||
reviewOutsourceFlag: formData.value.reviewOutsourceFlag,
|
reviewOutsourceFlag: formData.value.reviewOutsourceFlag,
|
||||||
reviewOutsourceRatio: formData.value.reviewOutsourceRatio,
|
reviewOutsourceRatio: formData.value.reviewOutsourceRatio,
|
||||||
totalDistributionAmount: formData.value.totalDistributionAmount,
|
totalDistributionAmount: formData.value.totalDistributionAmount,
|
||||||
progressRemark: formData.value.progressRemark,
|
progressRemark: formData.value.progressRemark,
|
||||||
buildingOrUnitCount: formData.value.buildingOrUnitCount,
|
buildingOrUnitCount: showGuideDetailSection.value ? undefined : formData.value.buildingOrUnitCount,
|
||||||
drawingSetFactor: formData.value.drawingSetFactor,
|
drawingSetFactor: showGuideDetailSection.value ? undefined : formData.value.drawingSetFactor,
|
||||||
scaleFactor: formData.value.scaleFactor,
|
scaleFactor: showGuideDetailSection.value ? undefined : formData.value.scaleFactor,
|
||||||
modificationFactor: formData.value.modificationFactor,
|
modificationFactor: showGuideDetailSection.value ? undefined : formData.value.modificationFactor,
|
||||||
complexityFactor: formData.value.complexityFactor,
|
complexityFactor: showGuideDetailSection.value ? undefined : formData.value.complexityFactor,
|
||||||
internalGuidanceUnitPrice: formData.value.internalGuidanceUnitPrice,
|
internalGuidanceUnitPrice: showGuideDetailSection.value
|
||||||
|
? undefined
|
||||||
|
: formData.value.internalGuidanceUnitPrice,
|
||||||
virtualCalculationMethod: formData.value.virtualCalculationMethod,
|
virtualCalculationMethod: formData.value.virtualCalculationMethod,
|
||||||
workingDayCount: formData.value.workingDayCount,
|
workingDayCount: formData.value.workingDayCount,
|
||||||
workingDayUnitPrice: formData.value.workingDayUnitPrice,
|
workingDayUnitPrice: formData.value.workingDayUnitPrice,
|
||||||
@@ -701,21 +969,98 @@ const buildSavePayload = (): PlanningApi.ProjectPlanningSaveVO => ({
|
|||||||
calculationRatio: formData.value.calculationRatio
|
calculationRatio: formData.value.calculationRatio
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const buildGuideDetailPayload = (): PlanningGuideDetailApi.ProjectPlanningGuideDetailBatchSaveVO => ({
|
||||||
|
planningId: formData.value.id!,
|
||||||
|
details: guideDetails.value
|
||||||
|
.map((item, index) => ({
|
||||||
|
id: item.id,
|
||||||
|
designPart: item.designPart,
|
||||||
|
buildingType: item.buildingType,
|
||||||
|
designArea: item.designArea,
|
||||||
|
internalGuidanceUnitPrice: item.internalGuidanceUnitPrice,
|
||||||
|
buildingOrUnitCount: item.buildingOrUnitCount,
|
||||||
|
drawingSetFactor: item.drawingSetFactor,
|
||||||
|
scaleFactor: item.scaleFactor,
|
||||||
|
modificationFactor: item.modificationFactor,
|
||||||
|
complexityFactor: item.complexityFactor,
|
||||||
|
designRatio: item.designRatio,
|
||||||
|
sortNo: item.sortNo ?? index + 1,
|
||||||
|
remark: item.remark
|
||||||
|
}))
|
||||||
|
.sort((a, b) => Number(a.sortNo || 0) - Number(b.sortNo || 0))
|
||||||
|
})
|
||||||
|
|
||||||
|
const validateGuideDetails = () => {
|
||||||
|
if (!guideDetails.value.length) {
|
||||||
|
message.warning('请至少维护一条指导价法明细')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (let index = 0; index < guideDetails.value.length; index++) {
|
||||||
|
const row = guideDetails.value[index]
|
||||||
|
const rowText = `第 ${index + 1} 行`
|
||||||
|
if (!row.designPart) {
|
||||||
|
message.warning(`${rowText}设计部位不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!row.buildingType) {
|
||||||
|
message.warning(`${rowText}建筑类型不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.designArea)) {
|
||||||
|
message.warning(`${rowText}设计面积不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.internalGuidanceUnitPrice)) {
|
||||||
|
message.warning(`${rowText}内部指导单价不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.drawingSetFactor)) {
|
||||||
|
message.warning(`${rowText}套图系数不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.scaleFactor)) {
|
||||||
|
message.warning(`${rowText}规模系数不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.modificationFactor)) {
|
||||||
|
message.warning(`${rowText}修改系数不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.complexityFactor)) {
|
||||||
|
message.warning(`${rowText}复杂系数不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!hasValue(row.designRatio)) {
|
||||||
|
message.warning(`${rowText}设计占比不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const open = async (id: number) => {
|
const open = async (id: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
dialogTitle.value = t('action.update')
|
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await PlanningApi.getProjectPlanning(id)
|
const data = await PlanningApi.getProjectPlanning(id)
|
||||||
formData.value = normalizeFormData(data)
|
formData.value = normalizeFormData(data)
|
||||||
|
guideDetails.value = showGuideDetailSection.value
|
||||||
|
? normalizeGuideDetailList(
|
||||||
|
await PlanningGuideDetailApi.getProjectPlanningGuideDetailListByPlanningId(id)
|
||||||
|
)
|
||||||
|
: []
|
||||||
applyCalculationRatioDefault()
|
applyCalculationRatioDefault()
|
||||||
if (formData.value.reviewOutsourceRatio === undefined || formData.value.reviewOutsourceRatio === null) {
|
if (formData.value.reviewOutsourceRatio === undefined || formData.value.reviewOutsourceRatio === null) {
|
||||||
applyReviewOutsourceDefault()
|
applyReviewOutsourceDefault()
|
||||||
}
|
}
|
||||||
|
if (showGuideDetailSection.value && !guideDetails.value.length) {
|
||||||
|
addGuideDetailRow()
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
|
|
||||||
const emit = defineEmits(['success'])
|
const emit = defineEmits(['success'])
|
||||||
@@ -728,10 +1073,17 @@ const submitForm = async () => {
|
|||||||
if (!valid) {
|
if (!valid) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (showGuideDetailSection.value && !validateGuideDetails()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
await PlanningApi.updateProjectPlanning(buildSavePayload())
|
await PlanningApi.updateProjectPlanning(buildSavePayload())
|
||||||
message.success(t('common.updateSuccess'))
|
if (showGuideDetailSection.value && formData.value.id) {
|
||||||
|
await PlanningGuideDetailApi.batchSaveProjectPlanningGuideDetail(buildGuideDetailPayload())
|
||||||
|
}
|
||||||
|
message.success('保存成功')
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
emit('success')
|
emit('success')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -739,3 +1091,17 @@ const submitForm = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 优化无控制按钮的 Input Number 显示样式 */
|
||||||
|
:deep(.optimized-table .el-input-number .el-input__inner) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
/* 给只读计算结果列添加淡淡的背景色用于视觉区分 */
|
||||||
|
:deep(.bg-gray-50) {
|
||||||
|
background-color: #f9fafc !important;
|
||||||
|
}
|
||||||
|
:deep(.text-primary) {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -227,25 +227,25 @@
|
|||||||
{{ formatPercentText(currentPlanning.calculationRatio) }}
|
{{ formatPercentText(currentPlanning.calculationRatio) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
v-if="currentPlanning.buildingOrUnitCount"
|
v-if="showParentBuildingOrUnitCount"
|
||||||
label="楼栋数/户型数"
|
label="楼栋数/户型数"
|
||||||
>
|
>
|
||||||
{{ currentPlanning.buildingOrUnitCount }}
|
{{ currentPlanning.buildingOrUnitCount }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item v-if="showMajorFactorFields" label="套图系数">
|
<el-descriptions-item v-if="showParentMajorFactorFields" label="套图系数">
|
||||||
{{ formatFactorText(currentPlanning.drawingSetFactor, 2) }}
|
{{ formatFactorText(currentPlanning.drawingSetFactor, 2) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item v-if="showMajorFactorFields" label="规模系数">
|
<el-descriptions-item v-if="showParentMajorFactorFields" label="规模系数">
|
||||||
{{ formatFactorText(currentPlanning.scaleFactor, 2) }}
|
{{ formatFactorText(currentPlanning.scaleFactor, 2) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item v-if="showMajorFactorFields" label="修改系数">
|
<el-descriptions-item v-if="showParentMajorFactorFields" label="修改系数">
|
||||||
{{ formatFactorText(currentPlanning.modificationFactor, 2) }}
|
{{ formatFactorText(currentPlanning.modificationFactor, 2) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item v-if="showMajorFactorFields" label="复杂系数/复杂等级">
|
<el-descriptions-item v-if="showParentMajorFactorFields" label="复杂系数/复杂等级">
|
||||||
{{ formatPercentText(currentPlanning.complexityFactor) }}
|
{{ formatPercentText(currentPlanning.complexityFactor) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
v-if="currentPlanning.internalGuidanceUnitPrice"
|
v-if="showParentInternalGuidanceUnitPrice"
|
||||||
label="内部指导单价(元/㎡)"
|
label="内部指导单价(元/㎡)"
|
||||||
>
|
>
|
||||||
{{ formatAmountText(currentPlanning.internalGuidanceUnitPrice) }}
|
{{ formatAmountText(currentPlanning.internalGuidanceUnitPrice) }}
|
||||||
@@ -274,7 +274,7 @@
|
|||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
<el-descriptions :column="2" border class="mt-16px" title="计算结果">
|
<el-descriptions :column="2" border class="mt-16px" title="计算结果">
|
||||||
<el-descriptions-item label="合计调整系数">
|
<el-descriptions-item v-if="!showGuideDetailScene" label="合计调整系数">
|
||||||
{{ formatFactorText(currentPlanning.totalAdjustmentFactor) }}
|
{{ formatFactorText(currentPlanning.totalAdjustmentFactor) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="考核面积(㎡)">
|
<el-descriptions-item label="考核面积(㎡)">
|
||||||
@@ -444,16 +444,32 @@ const showCalculationRatioField = computed(
|
|||||||
isComprehensiveOwnership(currentPlanning.value?.ownershipType) ||
|
isComprehensiveOwnership(currentPlanning.value?.ownershipType) ||
|
||||||
isSubcontractOwnership(currentPlanning.value?.ownershipType)
|
isSubcontractOwnership(currentPlanning.value?.ownershipType)
|
||||||
)
|
)
|
||||||
const showMajorFactorFields = computed(
|
const showGuideDetailScene = computed(
|
||||||
() =>
|
() =>
|
||||||
isMajorOwnership(currentPlanning.value?.ownershipType) &&
|
isMajorOwnership(currentPlanning.value?.ownershipType) &&
|
||||||
(isGuidancePriceMethod(currentPlanning.value?.calculationMethod) ||
|
isGuidancePriceMethod(currentPlanning.value?.calculationMethod)
|
||||||
isContractPriceMethod(currentPlanning.value?.calculationMethod))
|
)
|
||||||
|
const showParentMajorFactorFields = computed(
|
||||||
|
() =>
|
||||||
|
isMajorOwnership(currentPlanning.value?.ownershipType) &&
|
||||||
|
isContractPriceMethod(currentPlanning.value?.calculationMethod)
|
||||||
|
)
|
||||||
|
const showParentBuildingOrUnitCount = computed(
|
||||||
|
() =>
|
||||||
|
showParentMajorFactorFields.value &&
|
||||||
|
currentPlanning.value?.buildingOrUnitCount !== undefined &&
|
||||||
|
currentPlanning.value?.buildingOrUnitCount !== null
|
||||||
|
)
|
||||||
|
const showParentInternalGuidanceUnitPrice = computed(
|
||||||
|
() =>
|
||||||
|
!showGuideDetailScene.value &&
|
||||||
|
currentPlanning.value?.internalGuidanceUnitPrice !== undefined &&
|
||||||
|
currentPlanning.value?.internalGuidanceUnitPrice !== null
|
||||||
)
|
)
|
||||||
|
|
||||||
const formatFactorText = (value?: number, digits = 4) => {
|
const formatFactorText = (value?: number, digits = 4) => {
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return Number(0).toFixed(digits)
|
return '-'
|
||||||
}
|
}
|
||||||
return Number(value).toFixed(digits)
|
return Number(value).toFixed(digits)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,9 +110,6 @@
|
|||||||
<div class="mb-16px flex items-center justify-between gap-12px">
|
<div class="mb-16px flex items-center justify-between gap-12px">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-16px font-600">{{ currentProject.projectName }}</div>
|
<div class="text-16px font-600">{{ currentProject.projectName }}</div>
|
||||||
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
|
|
||||||
页面6:导出 7.1.1 项目考核产值预算表
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<el-button
|
<el-button
|
||||||
v-hasPermi="['tjt:report-budget:export']"
|
v-hasPermi="['tjt:report-budget:export']"
|
||||||
@@ -191,8 +188,6 @@
|
|||||||
{{ getOwnershipTypeLabel(scope.row.ownershipType) }}
|
{{ getOwnershipTypeLabel(scope.row.ownershipType) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="设计部位" width="110" prop="designPart" />
|
|
||||||
<el-table-column align="center" label="建筑类型" min-width="120" prop="buildingType" />
|
|
||||||
<el-table-column align="center" label="开始年度" width="100" prop="planningStartYear" />
|
<el-table-column align="center" label="开始年度" width="100" prop="planningStartYear" />
|
||||||
<el-table-column align="center" label="规划金额(元)" width="130">
|
<el-table-column align="center" label="规划金额(元)" width="130">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|||||||
@@ -93,12 +93,6 @@
|
|||||||
<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-16px font-600">{{ currentPlanning.planningContent }}</div>
|
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||||
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
|
|
||||||
页面7:导出 7.1.2 / 7.1.3,年度:{{ formData.year || '-' }},考核产值:{{
|
|
||||||
formatAmountText(formData.assessmentOutputValue)
|
|
||||||
}}
|
|
||||||
元
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-12px">
|
<div class="flex items-center gap-12px">
|
||||||
<el-button
|
<el-button
|
||||||
|
|||||||
@@ -95,9 +95,6 @@
|
|||||||
<div class="mb-16px flex items-center justify-between gap-12px">
|
<div class="mb-16px flex items-center justify-between gap-12px">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||||
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
|
|
||||||
页面8:导出 7.1.4 当前专业人员年度/季度计取表
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<el-button
|
<el-button
|
||||||
v-hasPermi="['tjt:report-specialty-person:export']"
|
v-hasPermi="['tjt:report-specialty-person:export']"
|
||||||
|
|||||||
Reference in New Issue
Block a user