From 761112715d11e0fc23857c6f95e9b3095d27ead3 Mon Sep 17 00:00:00 2001
From: lzm <2316711944@qq.com>
Date: Fri, 22 May 2026 18:05:53 +0800
Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=88=90=E6=9C=AC=E6=B5=8B?=
=?UTF-8?q?=E7=AE=97=E6=96=B0=E5=A2=9E=E9=A1=B9=E7=9B=AE=E6=88=90=E6=9C=AC?=
=?UTF-8?q?=E9=A2=84=E7=AE=97=E3=80=81=E6=A0=B8=E7=AE=97=E3=80=81=E7=BB=93?=
=?UTF-8?q?=E7=AE=97=EF=BC=8C=E5=AD=A3=E5=BA=A6=E5=88=86=E9=85=8D=E8=B0=83?=
=?UTF-8?q?=E6=95=B4=E5=8F=AF=E5=AD=90=E5=90=88=E7=BA=A6=E8=A7=84=E5=88=92?=
=?UTF-8?q?=E5=88=86=E9=85=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/tjt/planning/index.ts | 3 +-
src/api/tjt/planningQuarter/index.ts | 14 +
src/api/tjt/profit/index.ts | 55 +-
src/api/tjt/project/index.ts | 2 +-
src/views/tjt/output/PlanningOutputForm.vue | 21 +-
.../tjt/output/QuarterDistributionForm.vue | 107 +++-
src/views/tjt/output/index.vue | 135 ++++-
src/views/tjt/profit/index.vue | 476 +++++++++++++++---
src/views/tjt/project/PlanningForm.vue | 12 +-
src/views/tjt/project/ProjectForm.vue | 12 +-
10 files changed, 710 insertions(+), 127 deletions(-)
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 @@
-
+
+
+
+
+ 清空历史父级分配
+
+
+
+
- 删除
+
+ 删除
+
- 保存
+ 保存
取消
@@ -139,6 +167,10 @@ const formData = ref({
progressRemark: ''
})
const quarterRows = ref([])
+const guideDetailMode = ref(false)
+const historyParentMode = ref(false)
+const historyMessage = ref('')
+const activeGuideDetail = ref()
const totalDistributionAmountPercent = computed({
get: () => toPercentValue(formData.value.totalDistributionAmount),
@@ -155,9 +187,12 @@ const formRules = reactive({
const buildQuarterCell = (
currentPlanningId: number,
distributionYear: number,
- quarterNo: number
+ quarterNo: number,
+ guideDetail?: PlanningQuarterApi.ProjectPlanningQuarterGuideDetailVO
): PlanningQuarterApi.ProjectPlanningQuarterVO => ({
planningId: currentPlanningId,
+ guideDetailId: guideDetail?.id,
+ guideDetailSortNo: guideDetail?.sortNo,
distributionYear,
quarterNo,
distributionRatio: undefined,
@@ -166,7 +201,8 @@ const buildQuarterCell = (
const buildQuarterRows = (
planning: PlanningApi.ProjectPlanningVO,
- quarters: PlanningQuarterApi.ProjectPlanningQuarterVO[]
+ quarters: PlanningQuarterApi.ProjectPlanningQuarterVO[],
+ guideDetail?: PlanningQuarterApi.ProjectPlanningQuarterGuideDetailVO
) => {
const yearSet = new Set()
if (planning.planningStartYear) {
@@ -190,20 +226,25 @@ const buildQuarterRows = (
const match = quarters.find(
(item) =>
Number(item.distributionYear) === distributionYear &&
- Number(item.quarterNo) === quarterNo
+ Number(item.quarterNo) === quarterNo &&
+ Number(item.guideDetailId || 0) === Number(guideDetail?.id || 0)
)
return match
? { ...match }
- : buildQuarterCell(planning.id!, distributionYear, quarterNo)
+ : buildQuarterCell(planning.id!, distributionYear, quarterNo, guideDetail)
})
}))
}
-const open = async (id: number) => {
+const open = async (id: number, guideDetailId?: number) => {
planningId.value = id
dialogVisible.value = true
loading.value = true
deletedQuarterIds.value = []
+ guideDetailMode.value = false
+ historyParentMode.value = false
+ historyMessage.value = ''
+ activeGuideDetail.value = undefined
try {
const detail = await PlanningQuarterApi.getProjectPlanningQuarterPlanningDetail(id)
if (!detail?.planning) {
@@ -217,6 +258,7 @@ const open = async (id: number) => {
progressRemark: ''
}
quarterRows.value = []
+ activeGuideDetail.value = undefined
deletedQuarterIds.value = []
dialogVisible.value = false
message.warning('合约规划不存在或已被删除')
@@ -231,7 +273,24 @@ const open = async (id: number) => {
totalDistributionAmount: planning.totalDistributionAmount ?? 1,
progressRemark: planning.progressRemark ?? ''
}
- quarterRows.value = buildQuarterRows(formData.value, detail.quarters || [])
+ guideDetailMode.value = !!detail.guideDetailMode
+ historyParentMode.value = !!detail.historyParentMode
+ historyMessage.value = detail.message || ''
+ if (guideDetailMode.value && !historyParentMode.value) {
+ const targetGuideDetail = guideDetailId
+ ? (detail.guideDetails || []).find((item) => Number(item.id) === Number(guideDetailId))
+ : undefined
+ if (!targetGuideDetail) {
+ quarterRows.value = []
+ dialogVisible.value = false
+ message.warning('请先在外层选择指导价法明细')
+ return
+ }
+ activeGuideDetail.value = targetGuideDetail
+ quarterRows.value = buildQuarterRows(formData.value, targetGuideDetail.quarters || [], targetGuideDetail)
+ } else {
+ quarterRows.value = buildQuarterRows(formData.value, detail.quarters || [])
+ }
} finally {
loading.value = false
}
@@ -252,7 +311,7 @@ const addDistributionYear = () => {
quarterRows.value.push({
distributionYear,
quarters: QUARTER_OPTIONS.map((item) =>
- buildQuarterCell(planningId.value!, distributionYear, item.value)
+ buildQuarterCell(planningId.value!, distributionYear, item.value, activeGuideDetail.value)
)
})
}
@@ -267,6 +326,30 @@ const removeDistributionYear = (row: QuarterYearRow) => {
quarterRows.value = quarterRows.value.filter((item) => item !== row)
}
+const clearHistoryParentQuarters = async () => {
+ if (!planningId.value) {
+ return
+ }
+ const ids = quarterRows.value
+ .flatMap((row) => row.quarters)
+ .map((item) => item.id)
+ .filter((id): id is number => typeof id === 'number')
+ if (!ids.length) {
+ message.warning('没有可清空的历史父级分配')
+ return
+ }
+ await message.confirm('确认清空历史父级季度分配吗?清空后需要按指导价法明细重新录入。')
+ loading.value = true
+ try {
+ await PlanningQuarterApi.deleteProjectPlanningQuarterList(Array.from(new Set(ids)))
+ message.success('历史父级分配已清空,请在外层选择序号后重新录入')
+ dialogVisible.value = false
+ emit('success')
+ } finally {
+ loading.value = false
+ }
+}
+
const toYearPickerValue = (value?: number) => (value ? String(value) : undefined)
const updateDistributionYear = (row: QuarterYearRow, value?: string) => {
@@ -303,6 +386,8 @@ const buildQuarterSavePayload = (
): PlanningQuarterApi.ProjectPlanningQuarterSaveVO => ({
id: quarter.id,
planningId: planningId.value!,
+ guideDetailId: quarter.guideDetailId,
+ guideDetailSortNo: quarter.guideDetailSortNo,
distributionYear: row.distributionYear,
quarterNo: quarter.quarterNo,
distributionRatio: quarter.distributionRatio
diff --git a/src/views/tjt/output/index.vue b/src/views/tjt/output/index.vue
index d38e601..25857c8 100644
--- a/src/views/tjt/output/index.vue
+++ b/src/views/tjt/output/index.vue
@@ -307,9 +307,10 @@
编辑季度分配
@@ -328,7 +329,13 @@
已分配
- {{ formatPercentText(currentPlanning.allocatedAmount) }}
+ {{
+ formatPercentText(
+ guideDetailMode && activeGuideDetail
+ ? activeGuideDetail.allocatedAmount
+ : currentPlanning.allocatedAmount
+ )
+ }}
@@ -336,7 +343,13 @@
待分配
- {{ formatPercentText(currentPlanning.pendingAmount) }}
+ {{
+ formatPercentText(
+ guideDetailMode && activeGuideDetail
+ ? activeGuideDetail.pendingAmount
+ : currentPlanning.pendingAmount
+ )
+ }}
@@ -350,7 +363,63 @@
-
+
+
+
+
+
+
+
+
+
+ {{ getDesignPartLabel(activeGuideDetail.designPart) }}
+
+
+ {{ activeGuideDetail.buildingType || '-' }}
+
+
+ {{ formatAreaText(activeGuideDetail.designArea) }}
+
+
+ {{ formatAmountText(activeGuideDetail.assessmentOutputValue) }}
+
+
+
+
+
+
+
+
+
+ 分配比例:{{ formatQuarterRatio(scope.row, quarter.value) }}
+
+
+
+
+
+
+
+
+
([])
const currentProject = ref()
const currentPlanning = ref()
const quarterRows = ref([])
+const guideDetailMode = ref(false)
+const historyParentMode = ref(false)
+const guideDetailRows = ref([])
+const activeGuideDetailTab = ref('')
const queryFormRef = ref()
const projectTableRef = ref()
const planningTableRef = ref()
@@ -479,6 +558,13 @@ const showParentInternalGuidanceUnitPrice = computed(
currentPlanning.value?.internalGuidanceUnitPrice !== undefined &&
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) => {
if (value === undefined || value === null) {
@@ -536,6 +622,14 @@ const buildQuarterRows = (
}))
}
+const resetQuarterDetailState = () => {
+ quarterRows.value = []
+ guideDetailMode.value = false
+ historyParentMode.value = false
+ guideDetailRows.value = []
+ activeGuideDetailTab.value = ''
+}
+
const getProjectList = async () => {
loading.value = true
try {
@@ -546,7 +640,7 @@ const getProjectList = async () => {
currentProject.value = undefined
planningList.value = []
currentPlanning.value = undefined
- quarterRows.value = []
+ resetQuarterDetailState()
return
}
const targetProjectId = currentProject.value?.id || projectList.value[0].id
@@ -563,7 +657,7 @@ const getPlanningList = async () => {
if (!currentProject.value?.id) {
planningList.value = []
currentPlanning.value = undefined
- quarterRows.value = []
+ resetQuarterDetailState()
return
}
planningLoading.value = true
@@ -571,7 +665,7 @@ const getPlanningList = async () => {
planningList.value = await PlanningApi.getProjectPlanningListByProjectId(currentProject.value.id)
if (!planningList.value.length) {
currentPlanning.value = undefined
- quarterRows.value = []
+ resetQuarterDetailState()
return
}
const targetPlanningId = currentPlanning.value?.id || planningList.value[0].id
@@ -589,7 +683,23 @@ const loadPlanningDetail = async (planningId: number) => {
try {
const detail = await PlanningQuarterApi.getProjectPlanningQuarterPlanningDetail(planningId)
currentPlanning.value = detail?.planning
- quarterRows.value = detail?.planning ? buildQuarterRows(detail.planning, detail.quarters || []) : []
+ 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 {
quarterLoading.value = false
}
@@ -613,7 +723,7 @@ const handleCurrentProjectChange = async (row?: ProjectApi.ProjectVO) => {
const handleCurrentPlanningChange = async (row?: PlanningApi.ProjectPlanningVO) => {
if (!row?.id) {
currentPlanning.value = undefined
- quarterRows.value = []
+ resetQuarterDetailState()
return
}
await loadPlanningDetail(row.id)
@@ -630,12 +740,15 @@ const openPlanningOutputForm = () => {
planningOutputFormRef.value.open(currentPlanning.value.id)
}
-const openQuarterDistributionForm = () => {
+const openQuarterDistributionForm = (guideDetailId?: number) => {
if (!currentPlanning.value?.id) {
message.warning('请先选择合约规划')
return
}
- quarterDistributionFormRef.value.open(currentPlanning.value.id)
+ quarterDistributionFormRef.value.open(
+ currentPlanning.value.id,
+ guideDetailMode.value && !historyParentMode.value ? guideDetailId : undefined
+ )
}
const handlePlanningOutputFormSuccess = async () => {
diff --git a/src/views/tjt/profit/index.vue b/src/views/tjt/profit/index.vue
index cf97b05..02cf703 100644
--- a/src/views/tjt/profit/index.vue
+++ b/src/views/tjt/profit/index.vue
@@ -90,14 +90,7 @@
-
- {{ formatAmountText(scope.row.effectiveSettlementAmount) }}
-
- {{ formatAmountText(scope.row.effectiveSettlementAmount) }}
+ {{ formatAmountText(scope.row.effectiveSettlementAmount) }}
@@ -105,31 +98,31 @@
{{ formatAmountText(scope.row.comprehensivePlanningAmount) }}
-
+
{{ formatAmountText(scope.row.specialSubcontractPlanningAmount) }}
-
+
{{ formatAmountText(scope.row.sourceCoopSubcontractPlanningAmount) }}
-
+
{{ formatAmountText(scope.row.comprehensiveSubcontractPlanningAmount) }}
-
-
- {{ formatAmountText(scope.row.majorOutputValue) }}
-
-
{{ formatAmountText(scope.row.majorExpectedPerformance) }}
+
+
+ {{ formatAmountText(scope.row.majorOutputValue) }}
+
+
{{ formatPercentText(scope.row.innovationOutputRate) }}
@@ -180,64 +173,271 @@
-
-
- {{ formatAmountText(currentProfit.contractAmount) }}
-
-
- {{ formatAmountText(currentProfit.finalSettlementAmount) }}
-
-
-
+
+
+
项目成本预算测算表
+
+
+ {{ budgetSnapshot ? '已锁定' : '动态测算' }}
+
+ {{ snapshotActionText(budgetSnapshot) }}
+
+
+
- {{ formatAmountText(currentProfit.effectiveSettlementAmount) }}
-
- {{ formatAmountText(currentProfit.effectiveSettlementAmount) }}
-
-
- {{ currentProfit.projectStartYear || '-' }}
-
-
- {{ formatAmountText(currentProfit.comprehensivePlanningAmount) }}
-
-
- {{ formatAmountText(currentProfit.specialSubcontractPlanningAmount) }}
-
-
- {{ formatAmountText(currentProfit.sourceCoopSubcontractPlanningAmount) }}
-
-
- {{ formatAmountText(currentProfit.comprehensiveSubcontractPlanningAmount) }}
-
-
- {{ formatAmountText(currentProfit.majorOutputValue) }}
-
-
- {{ formatAmountText(currentProfit.majorExpectedPerformance) }}
-
-
- {{ formatPercentText(currentProfit.innovationOutputRate) }}
-
-
- {{ formatAmountText(currentProfit.innovationOutputValue) }}
-
-
- {{ formatAmountText(currentProfit.otherCost) }}
-
-
-
- {{ formatAmountText(currentProfit.profitLossValue) }}
-
-
-
-
- {{ formatPercentText(currentProfit.profitLossRate) }}
-
-
-
+ 目标责任书下达
+
+
+
+
+
+
+
+ {{ formatAmountText(budgetDisplay.contractAmount) }}
+
+
+ {{ formatAmountText(budgetDisplay.finalSettlementAmount) }}
+
+
+ {{ formatAmountText(budgetDisplay.effectiveSettlementAmount) }}
+
+
+ {{ currentProfit.projectStartYear || '-' }}
+
+
+ {{ formatAmountText(budgetDisplay.comprehensivePlanningAmount) }}
+
+
+ {{ formatAmountText(budgetDisplay.specialSubcontractPlanningAmount) }}
+
+
+ {{ formatAmountText(budgetDisplay.sourceCoopSubcontractPlanningAmount) }}
+
+
+ {{ formatAmountText(budgetDisplay.comprehensiveSubcontractPlanningAmount) }}
+
+
+ {{ formatAmountText(budgetDisplay.majorExpectedPerformance) }}
+
+
+ {{ formatAmountText(budgetDisplay.majorOutputValue) }}
+
+
+ {{ formatPercentText(budgetDisplay.innovationOutputRate) }}
+
+
+ {{ formatAmountText(budgetDisplay.innovationOutputValue) }}
+
+
+ {{ formatAmountText(budgetDisplay.otherCost) }}
+
+
+
+ {{ formatAmountText(budgetDisplay.profitLossValue) }}
+
+
+
+
+ {{ formatPercentText(budgetDisplay.profitLossRate) }}
+
+
+
+
+
+
+
+
+
项目成本核算测算表
+
+
+ {{ accountingSnapshot ? '已锁定' : '动态测算' }}
+
+ {{ snapshotActionText(accountingSnapshot) }}
+
+
+
+ 竣工验收完成
+
+
+
+
+
+
+
+
+ {{ formatAmountText(accountingDisplay.contractAmount) }}
+
+
+ {{ formatAmountText(accountingDisplay.finalSettlementAmount) }}
+
+
+ {{ formatAmountText(accountingDisplay.effectiveSettlementAmount) }}
+
+
+ {{ currentProfit.projectStartYear || '-' }}
+
+
+ {{ formatAmountText(accountingDisplay.comprehensivePlanningAmount) }}
+
+
+ {{ formatAmountText(accountingDisplay.specialSubcontractPlanningAmount) }}
+
+
+ {{ formatAmountText(accountingDisplay.sourceCoopSubcontractPlanningAmount) }}
+
+
+ {{ formatAmountText(accountingDisplay.comprehensiveSubcontractPlanningAmount) }}
+
+
+ {{ formatAmountText(accountingDisplay.majorExpectedPerformance) }}
+
+
+ {{ formatAmountText(accountingDisplay.majorOutputValue) }}
+
+
+ {{ formatPercentText(accountingDisplay.innovationOutputRate) }}
+
+
+ {{ formatAmountText(accountingDisplay.innovationOutputValue) }}
+
+
+ {{ formatAmountText(accountingDisplay.otherCost) }}
+
+
+
+ {{ formatAmountText(accountingDisplay.profitLossValue) }}
+
+
+
+
+ {{ formatPercentText(accountingDisplay.profitLossRate) }}
+
+
+
+
+
+
+
+
+
项目成本结算测算表
+
+
+ {{ settlementSnapshot ? '已保存' : '未保存' }}
+
+ {{ snapshotActionText(settlementSnapshot, '保存结算测算后会记录操作人和操作时间') }}
+
+
+
+
+
+
+
+
+ {{ formatAmountText(settlementComprehensiveAccountingValue) }}
+
+
+ {{ formatAmountText(settlementComprehensiveSettlementValue) }}
+
+
+ {{ formatAmountText(settlementMajorAccountingValue) }}
+
+
+ {{ formatAmountText(settlementMajorSettlementValue) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存结算测算
+
+
+
+
+
+
+
+
+
+
+
@@ -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,