diff --git a/src/api/tjt/planning/index.ts b/src/api/tjt/planning/index.ts index 62d8ff9..e66d3be 100644 --- a/src/api/tjt/planning/index.ts +++ b/src/api/tjt/planning/index.ts @@ -4,7 +4,7 @@ import type { ProjectPlanningGuideDetailVO } from '@/api/tjt/planningGuideDetail export interface ProjectPlanningVO { id?: number projectId: number - sortNo?: number + sortNo?: string ownershipType: string calculationMethod: string planningContent: string @@ -55,7 +55,6 @@ export type ProjectPlanningSaveVO = Omit< | 'managementFee' | 'vatAmount' | 'projectBudgetOutputValue' - | 'contractUnitPrice' | 'totalAdjustmentFactor' | 'assessmentArea' | 'virtualOutputValue' diff --git a/src/api/tjt/planningQuarter/index.ts b/src/api/tjt/planningQuarter/index.ts index 253fe59..899db39 100644 --- a/src/api/tjt/planningQuarter/index.ts +++ b/src/api/tjt/planningQuarter/index.ts @@ -1,9 +1,12 @@ import request from '@/config/axios' import type { ProjectPlanningVO } from '@/api/tjt/planning' +import type { ProjectPlanningGuideDetailVO } from '@/api/tjt/planningGuideDetail' export interface ProjectPlanningQuarterVO { id?: number planningId: number + guideDetailId?: number + guideDetailSortNo?: number distributionYear: number quarterNo: number distributionRatio?: number @@ -19,6 +22,17 @@ export type ProjectPlanningQuarterSaveVO = Omit< export interface ProjectPlanningQuarterPlanningDetailVO { planning: ProjectPlanningVO quarters: ProjectPlanningQuarterVO[] + guideDetailMode?: boolean + historyParentMode?: boolean + message?: string + parentQuarters?: ProjectPlanningQuarterVO[] + guideDetails?: ProjectPlanningQuarterGuideDetailVO[] +} + +export interface ProjectPlanningQuarterGuideDetailVO extends ProjectPlanningGuideDetailVO { + allocatedAmount?: number + pendingAmount?: number + quarters: ProjectPlanningQuarterVO[] } export const getProjectPlanningQuarter = (id: number) => { diff --git a/src/api/tjt/profit/index.ts b/src/api/tjt/profit/index.ts index 4fe13aa..42eff6f 100644 --- a/src/api/tjt/profit/index.ts +++ b/src/api/tjt/profit/index.ts @@ -3,7 +3,7 @@ import request from '@/config/axios' export interface ProjectProfitVO { projectId: number projectName: string - sortNo?: number + sortNo?: string contractSignedFlag: boolean contractAmount?: number finalSettlementAmount?: number @@ -22,6 +22,47 @@ export interface ProjectProfitVO { profitLossRate?: number projectStartYear?: number createTime?: string + budgetSnapshot?: ProjectProfitSnapshotVO + accountingSnapshot?: ProjectProfitSnapshotVO + settlementSnapshot?: ProjectProfitSnapshotVO +} + +export interface ProjectProfitSnapshotVO { + id?: number + projectId: number + snapshotType: 'budget' | 'accounting' | 'settlement' + lockedFlag?: boolean + actionUserId?: number + actionUserName?: string + actionTime?: string + contractAmount?: number + finalSettlementAmount?: number + effectiveSettlementAmount?: number + comprehensivePlanningAmount?: number + subcontractPlanningAmount?: number + specialSubcontractPlanningAmount?: number + sourceCoopSubcontractPlanningAmount?: number + comprehensiveSubcontractPlanningAmount?: number + majorOutputValue?: number + majorExpectedPerformance?: number + innovationOutputRate?: number + innovationOutputValue?: number + otherCost?: number + profitLossValue?: number + profitLossRate?: number + assessmentResult?: string + assessmentCoefficient?: number + comprehensiveAccountingOutputValue?: number + comprehensiveSettlementOutputValue?: number + majorAccountingOutputValue?: number + majorSettlementOutputValue?: number + remark?: string +} + +export interface ProjectProfitSettlementSaveReqVO { + projectId: number + assessmentResult?: string + remark?: string } export interface ProjectProfitPageReqVO extends PageParam { @@ -38,3 +79,15 @@ export const getProjectProfitPage = (params: ProjectProfitPageReqVO) => { export const getProjectProfit = (projectId: number) => { return request.get({ url: '/tjt/profit/get', params: { projectId } }) } + +export const lockBudgetSnapshot = (projectId: number) => { + return request.post({ url: '/tjt/profit/lock-budget', params: { projectId } }) +} + +export const lockAccountingSnapshot = (projectId: number) => { + return request.post({ url: '/tjt/profit/lock-accounting', params: { projectId } }) +} + +export const saveSettlementSnapshot = (data: ProjectProfitSettlementSaveReqVO) => { + return request.put({ url: '/tjt/profit/save-settlement', data }) +} diff --git a/src/api/tjt/project/index.ts b/src/api/tjt/project/index.ts index 358113c..f559691 100644 --- a/src/api/tjt/project/index.ts +++ b/src/api/tjt/project/index.ts @@ -13,7 +13,7 @@ export interface ProjectRolePersonVO { export interface ProjectVO { id?: number projectName: string - sortNo?: number + sortNo?: string contractSignedFlag: boolean contractAmount?: number totalConstructionArea?: number diff --git a/src/views/tjt/output/PlanningOutputForm.vue b/src/views/tjt/output/PlanningOutputForm.vue index 24b7f79..47e7499 100644 --- a/src/views/tjt/output/PlanningOutputForm.vue +++ b/src/views/tjt/output/PlanningOutputForm.vue @@ -124,7 +124,14 @@ - + @@ -913,17 +920,6 @@ const guideDetailSummary = computed(() => ) ) -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 '-' @@ -1096,6 +1092,7 @@ const buildSavePayload = (): PlanningApi.ProjectPlanningSaveVO => ({ sortNo: formData.value.sortNo, contractValueQuantity: formData.value.contractValueQuantity, contractValueUnitPrice: formData.value.contractValueUnitPrice, + contractUnitPrice: formData.value.contractUnitPrice, managementFeeRate: formData.value.managementFeeRate, vatRate: formData.value.vatRate, implementationTeam: formData.value.implementationTeam, diff --git a/src/views/tjt/output/QuarterDistributionForm.vue b/src/views/tjt/output/QuarterDistributionForm.vue index 86192e7..08861aa 100644 --- a/src/views/tjt/output/QuarterDistributionForm.vue +++ b/src/views/tjt/output/QuarterDistributionForm.vue @@ -51,17 +51,37 @@
季度分配明细
- + 新增年度
- + + +
+ + 清空历史父级分配 + +
+ + - + - + - + - - - + + + @@ -297,6 +497,7 @@ import { fromPercentValue, toPercentValue } from '@/views/tjt/shared/planning' +import { formatDate } from '@/utils/formatTime' defineOptions({ name: 'TjtProfit' }) @@ -305,11 +506,21 @@ const { t } = useI18n() const loading = ref(false) const detailLoading = ref(false) +const actionLoading = ref<'budget' | 'accounting' | 'settlement' | ''>('') const total = ref(0) const list = ref([]) const currentProfit = ref() const queryFormRef = ref() const profitTableRef = ref() +const settlementFormRef = ref() +const settlementForm = reactive({ + projectId: 0, + assessmentResult: '合格', + remark: '' +}) +const settlementRules = { + assessmentResult: [{ required: true, message: '请选择考核结果', trigger: 'change' }] +} const dialogVisible = ref(false) const dialogLoading = ref(false) @@ -354,6 +565,29 @@ const queryProjectStartYearValue = computed({ } }) +const budgetSnapshot = computed(() => currentProfit.value?.budgetSnapshot) +const accountingSnapshot = computed(() => currentProfit.value?.accountingSnapshot) +const settlementSnapshot = computed(() => currentProfit.value?.settlementSnapshot) +const budgetDisplay = computed(() => budgetSnapshot.value || currentProfit.value) +const accountingDisplay = computed(() => accountingSnapshot.value || currentProfit.value) +const settlementCoefficient = computed(() => getAssessmentCoefficient(settlementForm.assessmentResult)) +const settlementComprehensiveAccountingValue = computed(() => + Number( + settlementSnapshot.value?.comprehensiveAccountingOutputValue ?? + accountingSnapshot.value?.comprehensivePlanningAmount ?? + 0 + ) +) +const settlementMajorAccountingValue = computed(() => + Number(settlementSnapshot.value?.majorAccountingOutputValue ?? accountingSnapshot.value?.majorOutputValue ?? 0) +) +const settlementComprehensiveSettlementValue = computed(() => + roundAmount(settlementComprehensiveAccountingValue.value * settlementCoefficient.value) +) +const settlementMajorSettlementValue = computed(() => + roundAmount(settlementMajorAccountingValue.value * settlementCoefficient.value) +) + const getList = async () => { loading.value = true let targetProfit: ProfitApi.ProjectProfitVO | undefined @@ -412,6 +646,60 @@ const refreshCurrentProfit = async () => { await getList() } +const handleLockBudgetSnapshot = async () => { + if (!currentProfit.value?.projectId) { + return + } + await message.confirm('确认下达目标责任书并锁定当前项目成本预算测算吗?锁定后不能重复操作。') + actionLoading.value = 'budget' + try { + currentProfit.value = await ProfitApi.lockBudgetSnapshot(currentProfit.value.projectId) + message.success('目标责任书已下达') + await getList() + } finally { + actionLoading.value = '' + } +} + +const handleLockAccountingSnapshot = async () => { + if (!currentProfit.value?.projectId) { + return + } + if (!budgetSnapshot.value) { + message.warning('请先下达目标责任书,再完成竣工验收') + return + } + await message.confirm('确认竣工验收完成并锁定当前项目成本核算测算吗?锁定后不能重复操作。') + actionLoading.value = 'accounting' + try { + currentProfit.value = await ProfitApi.lockAccountingSnapshot(currentProfit.value.projectId) + message.success('竣工验收已完成') + await getList() + } finally { + actionLoading.value = '' + } +} + +const handleSaveSettlementSnapshot = async () => { + if (!currentProfit.value?.projectId || !accountingSnapshot.value) { + message.warning('请先完成竣工验收,再维护结算测算') + return + } + await settlementFormRef.value?.validate() + actionLoading.value = 'settlement' + try { + currentProfit.value = await ProfitApi.saveSettlementSnapshot({ + projectId: currentProfit.value.projectId, + assessmentResult: settlementForm.assessmentResult, + remark: settlementForm.remark + }) + message.success('结算测算已保存') + await getList() + } finally { + actionLoading.value = '' + } +} + const openProfitEditDialog = async () => { if (!currentProfit.value?.projectId) { return @@ -469,8 +757,46 @@ const profitLossClass = (value?: number) => { return 'text-[var(--el-text-color-primary)]' } -const isUsingContractAmount = (row?: ProfitApi.ProjectProfitVO) => - !!row && Number(row.finalSettlementAmount || 0) <= 0 && Number(row.contractAmount || 0) > 0 +const snapshotActionText = ( + snapshot?: ProfitApi.ProjectProfitSnapshotVO, + emptyText = '当前为实时动态测算值,尚未锁定' +) => { + if (!snapshot) { + return emptyText + } + const actionName = snapshot.actionUserName || '未知操作人' + const actionTime = snapshot.actionTime ? formatDate(snapshot.actionTime as any) : '未知时间' + return `${actionName} 于 ${actionTime} 操作` +} + +const roundAmount = (value: number) => Math.round((Number(value) || 0) * 100) / 100 + +const getAssessmentCoefficient = (assessmentResult?: string) => { + if (assessmentResult === '优秀') { + return 1.02 + } + if (assessmentResult === '待改进') { + return 0.95 + } + return 1 +} + +const formatCoefficientText = (value?: number) => Number(value ?? 1).toFixed(2) + +const syncSettlementForm = () => { + if (!currentProfit.value?.projectId) { + settlementForm.projectId = 0 + settlementForm.assessmentResult = '合格' + settlementForm.remark = '' + return + } + const snapshot = currentProfit.value.settlementSnapshot + settlementForm.projectId = currentProfit.value.projectId + settlementForm.assessmentResult = snapshot?.assessmentResult || '合格' + settlementForm.remark = snapshot?.remark || '' +} + +watch(currentProfit, syncSettlementForm) let activatedOnce = false diff --git a/src/views/tjt/project/PlanningForm.vue b/src/views/tjt/project/PlanningForm.vue index a614935..0add05e 100644 --- a/src/views/tjt/project/PlanningForm.vue +++ b/src/views/tjt/project/PlanningForm.vue @@ -132,13 +132,11 @@ - @@ -174,7 +172,7 @@ const createFormData = ( planningAmount?: number ): PlanningApi.ProjectPlanningVO => ({ projectId: projectId || 0, - sortNo: 0, + sortNo: '', ownershipType: ownershipTypeOptions[0].value, calculationMethod: '', planningContent: '', @@ -285,7 +283,7 @@ const formRules = reactive({ const buildSavePayload = (): PlanningApi.ProjectPlanningSaveVO => ({ id: formData.value.id, projectId: formData.value.projectId, - sortNo: formData.value.sortNo ?? 0, + sortNo: formData.value.sortNo?.trim() || undefined, ownershipType: formData.value.ownershipType, calculationMethod: formData.value.calculationMethod, planningContent: formData.value.planningContent, diff --git a/src/views/tjt/project/ProjectForm.vue b/src/views/tjt/project/ProjectForm.vue index 70dfca2..4fdfa86 100644 --- a/src/views/tjt/project/ProjectForm.vue +++ b/src/views/tjt/project/ProjectForm.vue @@ -155,13 +155,11 @@ - @@ -311,7 +309,7 @@ const createRolePerson = ( const createFormData = (): ProjectApi.ProjectVO => ({ projectName: '', - sortNo: 0, + sortNo: '', contractSignedFlag: true, contractAmount: undefined, totalConstructionArea: undefined, @@ -494,7 +492,7 @@ const emit = defineEmits(['success']) const buildSavePayload = (): ProjectApi.ProjectSaveVO => ({ id: formData.value.id, projectName: formData.value.projectName, - sortNo: formData.value.sortNo ?? 0, + sortNo: formData.value.sortNo?.trim() || undefined, contractSignedFlag: formData.value.contractSignedFlag, contractAmount: formData.value.contractAmount, totalConstructionArea: formData.value.totalConstructionArea,