系数字段修改

This commit is contained in:
zwgczx committed 2026-03-07 17:52:33 +08:00
1 parent 21d3f0379c
commit fc26a87bee
12 files changed
+782 -105

No files matched your search

@@ -5,8 +5,8 @@ import type { ColDef, ColGroupDef } from 'ag-grid-community'
import localforage from 'localforage'
import { getMajorDictEntries, getServiceDictItemById, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
import { formatThousands } from '@/lib/numberFormat'
import { addNumbers, decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
import { formatThousandsFlexible } from '@/lib/numberFormat'
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
@@ -212,8 +212,9 @@ const detailDict: DictGroup[] = (() => {
const hasCost = item.hasCost !== false
const hasArea = item.hasArea !== false
// 特殊规则:投资规模法中,hasCost && hasArea 的专业不参与明细行
if (hasCost && hasArea) continue
// 投资规模法仅保留可按造价计价且非用地规模的专业
if (!hasCost) continue
if (hasArea) continue
groupMap.get(parentCode)!.children.push({
id: key,
@@ -292,7 +293,18 @@ const getOnlyCostScaleMajorFactorDefault = () => {
const buildOnlyCostScaleRow = (
amount: number | null,
fromDb?: Partial<Pick<DetailRow, 'consultCategoryFactor' | 'majorFactor' | 'workStageFactor' | 'workRatio' | 'remark'>>
fromDb?: Partial<
Pick<
DetailRow,
| 'consultCategoryFactor'
| 'majorFactor'
| 'workStageFactor'
| 'workRatio'
| 'remark'
| 'benchmarkBudgetBasicChecked'
| 'benchmarkBudgetOptionalChecked'
>
>
): DetailRow => ({
id: ONLY_COST_SCALE_ROW_ID,
groupCode: 'TOTAL',
@@ -305,8 +317,10 @@ const buildOnlyCostScaleRow = (
benchmarkBudget: null,
benchmarkBudgetBasic: null,
benchmarkBudgetOptional: null,
benchmarkBudgetBasicChecked: true,
benchmarkBudgetOptionalChecked: true,
benchmarkBudgetBasicChecked:
typeof fromDb?.benchmarkBudgetBasicChecked === 'boolean' ? fromDb.benchmarkBudgetBasicChecked : true,
benchmarkBudgetOptionalChecked:
typeof fromDb?.benchmarkBudgetOptionalChecked === 'boolean' ? fromDb.benchmarkBudgetOptionalChecked : true,
basicFormula: '',
optionalFormula: '',
consultCategoryFactor:
@@ -416,7 +430,7 @@ const formatEditableNumber = (params: any) => {
return '请输入'
}
if (params.value == null) return ''
return Number(params.value).toFixed(2)
return formatThousandsFlexible(params.value, 3)
}
const formatConsultCategoryFactor = (params: any) => {
@@ -431,7 +445,7 @@ const formatEditableMoney = (params: any) => {
if (isOnlyCostScaleService.value) {
if (!params.node?.rowPinned) return ''
if (params.value == null || params.value === '') return '点击输入'
return formatThousands(params.value)
return formatThousandsFlexible(params.value, 3)
}
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
return ''
@@ -440,12 +454,12 @@ const formatEditableMoney = (params: any) => {
return '点击输入'
}
if (params.value == null) return ''
return formatThousands(params.value)
return formatThousandsFlexible(params.value, 3)
}
const formatReadonlyMoney = (params: any) => {
if (params.value == null || params.value === '') return ''
return formatThousands(roundTo(params.value, 2))
return formatThousandsFlexible(roundTo(params.value, 3), 3)
}
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
@@ -453,14 +467,14 @@ type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptional
const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => {
const valueText = formatReadonlyMoney(params)
const hasValue = params.value != null && params.value !== ''
if (params.node?.group || params.node?.rowPinned || !params.data || !hasValue) {
if (params.node?.group || (params.node?.rowPinned && !isOnlyCostScaleService.value) || !params.data || !hasValue) {
return valueText
}
const wrapper = document.createElement('div')
wrapper.style.display = 'flex'
wrapper.style.alignItems = 'center'
wrapper.style.justifyContent = 'flex-end'
wrapper.style.justifyContent = 'space-between'
wrapper.style.gap = '6px'
wrapper.style.width = '100%'
@@ -471,8 +485,27 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
checkbox.checked = params.data[checkField] !== false
checkbox.addEventListener('click', event => event.stopPropagation())
checkbox.addEventListener('change', () => {
params.data[checkField] = checkbox.checked
const isOnlyCostScalePinned = isOnlyCostScaleService.value && Boolean(params.node?.rowPinned)
const targetRow =
isOnlyCostScalePinned
? detailRows.value[0]
: (params.data as DetailRow | undefined)
if (!targetRow) return
targetRow[checkField] = checkbox.checked
params.node?.setDataValue?.(checkField, checkbox.checked)
if (!checkbox.checked) {
const budgetField = checkField === 'benchmarkBudgetBasicChecked' ? 'benchmarkBudgetBasic' : 'benchmarkBudgetOptional'
targetRow[budgetField] = 0
params.node?.setDataValue?.(budgetField, 0)
}
handleCellValueChanged()
params.api?.refreshCells?.({
rowNodes: params.node ? [params.node] : undefined,
force: true
})
})
const valueSpan = document.createElement('span')
@@ -485,10 +518,34 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
const getBenchmarkBudgetSplitByAmount = (row?: Pick<DetailRow, 'amount'>) =>
getBenchmarkBudgetSplitByScale(row?.amount, 'cost')
const getBudgetFee = (
row?: Pick<DetailRow, 'amount' | 'majorFactor' | 'consultCategoryFactor' | 'workStageFactor' | 'workRatio'>
const getCheckedBenchmarkBudgetSplitByAmount = (
row?: Pick<DetailRow, 'amount' | 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'>
) => {
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
const split = getBenchmarkBudgetSplitByAmount(row)
if (!split) return null
const basic = row?.benchmarkBudgetBasicChecked === false ? 0 : split.basic
const optional = row?.benchmarkBudgetOptionalChecked === false ? 0 : split.optional
return {
...split,
basic,
optional,
total: roundTo(addNumbers(basic, optional), 2)
}
}
const getBudgetFee = (
row?: Pick<
DetailRow,
| 'amount'
| 'benchmarkBudgetBasicChecked'
| 'benchmarkBudgetOptionalChecked'
| 'majorFactor'
| 'consultCategoryFactor'
| 'workStageFactor'
| 'workRatio'
>
) => {
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByAmount(row)
if (!benchmarkBudgetSplit) return null
const splitBudgetFee = getScaleBudgetFeeSplit({
@@ -503,9 +560,18 @@ const getBudgetFee = (
}
const getBudgetFeeSplit = (
row?: Pick<DetailRow, 'amount' | 'majorFactor' | 'consultCategoryFactor' | 'workStageFactor' | 'workRatio'>
row?: Pick<
DetailRow,
| 'amount'
| 'benchmarkBudgetBasicChecked'
| 'benchmarkBudgetOptionalChecked'
| 'majorFactor'
| 'consultCategoryFactor'
| 'workStageFactor'
| 'workRatio'
>
) => {
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByAmount(row)
if (!benchmarkBudgetSplit) return null
return getScaleBudgetFeeSplit({
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
@@ -517,6 +583,17 @@ const getBudgetFeeSplit = (
})
}
const getMergeColSpanBeforeTotal = (params: any) => {
if (!params.node?.group && !params.node?.rowPinned) return 1
if (isOnlyCostScaleService.value && params.node?.rowPinned) return 1
const displayedColumns = params.api?.getAllDisplayedColumns?.()
if (!Array.isArray(displayedColumns) || !params.column) return 1
const currentIndex = displayedColumns.findIndex((column: any) => column.getColId() === params.column.getColId())
const totalIndex = displayedColumns.findIndex((column: any) => column.getColId() === 'budgetFeeTotal')
if (currentIndex < 0 || totalIndex <= currentIndex) return 1
return totalIndex - currentIndex
}
const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
{
headerName: '造价金额(万元)',
@@ -541,7 +618,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
? params.value == null || params.value === ''
: !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
valueFormatter: formatEditableMoney
},
{
@@ -558,8 +635,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
cellClass: 'ag-right-aligned-cell',
valueGetter: params =>
params.node?.rowPinned
? null
: getBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
? (isOnlyCostScaleService.value ? getCheckedBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null : null)
: getCheckedBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
valueFormatter: formatReadonlyMoney
},
@@ -573,8 +650,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
cellClass: 'ag-right-aligned-cell',
valueGetter: params =>
params.node?.rowPinned
? null
: getBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
? (isOnlyCostScaleService.value ? getCheckedBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null : null)
: getCheckedBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
valueFormatter: formatReadonlyMoney
},
@@ -588,8 +665,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
cellClass: 'ag-right-aligned-cell',
valueGetter: params =>
params.node?.rowPinned
? null
: getBenchmarkBudgetSplitByAmount(params.data)?.total ?? null,
? (isOnlyCostScaleService.value ? getCheckedBenchmarkBudgetSplitByAmount(params.data)?.total ?? null : null)
: getCheckedBenchmarkBudgetSplitByAmount(params.data)?.total ?? null,
valueFormatter: formatReadonlyMoney
}
]
@@ -620,7 +697,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
? params.value == null || params.value === ''
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
valueFormatter: formatConsultCategoryFactor
},
{
@@ -645,7 +722,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
? params.value == null || params.value === ''
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
valueFormatter: formatMajorFactor
},
{
@@ -670,7 +747,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
? params.value == null || params.value === ''
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
valueFormatter: formatEditableNumber
},
{
@@ -695,7 +772,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
? params.value == null || params.value === ''
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
valueFormatter: formatEditableNumber
},
{
@@ -738,8 +815,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
const autoGroupColumnDef: ColDef = {
headerName: '专业编码以及工程专业名称',
minWidth: 250,
pinned: 'left',
flex: 2,
// wrapText: true,
// cellStyle: { whiteSpace: 'normal', lineHeight: '1.5', padding: '2px' },
@@ -747,6 +824,7 @@ const autoGroupColumnDef: ColDef = {
cellRendererParams: {
suppressCount: true
},
colSpan: getMergeColSpanBeforeTotal,
valueFormatter: params => {
if (params.node?.rowPinned) {
return totalLabel.value
@@ -766,9 +844,7 @@ const totalAmount = computed(() => sumByNumber(detailRows.value, row => row.amou
const visibleDetailRows = computed(() => (isOnlyCostScaleService.value ? [] : detailRows.value))
const onlyCostScaleSourceRow = computed(() => detailRows.value[0] ?? buildOnlyCostScaleRow(null))
const totalBenchmarkBudgetBasic = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.basic))
const totalBenchmarkBudgetOptional = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.optional))
const totalBenchmarkBudget = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.total))
const totalBudgetFeeBasic = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.basic))
const totalBudgetFeeOptional = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.optional))
@@ -786,8 +862,8 @@ const pinnedTopRowData = computed(() => [
benchmarkBudget: null,
benchmarkBudgetBasic: null,
benchmarkBudgetOptional: null,
benchmarkBudgetBasicChecked: true,
benchmarkBudgetOptionalChecked: true,
benchmarkBudgetBasicChecked: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.benchmarkBudgetBasicChecked !== false : true,
benchmarkBudgetOptionalChecked: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.benchmarkBudgetOptionalChecked !== false : true,
basicFormula: '',
optionalFormula: '',
consultCategoryFactor: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.consultCategoryFactor : null,
@@ -804,7 +880,8 @@ const pinnedTopRowData = computed(() => [
const syncComputedValuesToDetailRows = () => {
for (const row of detailRows.value) {
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
const benchmarkBudgetRawSplit = getBenchmarkBudgetSplitByAmount(row)
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByAmount(row)
const budgetFeeSplit = benchmarkBudgetSplit
? getScaleBudgetFeeSplit({
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
@@ -819,8 +896,8 @@ const syncComputedValuesToDetailRows = () => {
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
row.basicFormula = benchmarkBudgetSplit?.basicFormula ?? ''
row.optionalFormula = benchmarkBudgetSplit?.optionalFormula ?? ''
row.basicFormula = benchmarkBudgetRawSplit?.basicFormula ?? ''
row.optionalFormula = benchmarkBudgetRawSplit?.optionalFormula ?? ''
row.budgetFee = budgetFeeSplit?.total ?? null
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
@@ -951,7 +1028,7 @@ let persistTimer: ReturnType<typeof setTimeout> | null = null
let gridPersistTimer: ReturnType<typeof setTimeout> | null = null
const applyOnlyCostScalePinnedValue = (field: string, rawValue: unknown) => {
const parsedValue = parseNumberOrNull(rawValue, { precision: 2 })
const parsedValue = parseNumberOrNull(rawValue, { precision: 3 })
const current = detailRows.value[0]
if (!current) {
detailRows.value = [buildOnlyCostScaleRow(field === 'amount' ? parsedValue : null)]