修复check框保存逻辑

This commit is contained in:
wintsa 2026-03-19 21:44:47 +08:00
parent 35670bb276
commit 32fffa4f89
3 changed files with 109 additions and 27 deletions

View File

@ -616,6 +616,24 @@ const formatReadonlyMoney = (params: any) => {
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked' type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
const updateBudgetCheckState = (rowId: string, checkField: BudgetCheckField, checked: boolean) => {
detailRows.value = detailRows.value.map(row => {
if (row.id !== rowId) return row
if (checkField === 'benchmarkBudgetBasicChecked') {
return {
...row,
benchmarkBudgetBasicChecked: checked,
benchmarkBudgetBasic: checked ? row.benchmarkBudgetBasic : 0
}
}
return {
...row,
benchmarkBudgetOptionalChecked: checked,
benchmarkBudgetOptional: checked ? row.benchmarkBudgetOptional : 0
}
})
}
const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => { const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => {
const valueText = formatReadonlyMoney(params) const valueText = formatReadonlyMoney(params)
const hasValue = params.value != null && params.value !== '' const hasValue = params.value != null && params.value !== ''
@ -635,20 +653,13 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
checkbox.className = 'cursor-pointer' checkbox.className = 'cursor-pointer'
checkbox.checked = params.data[checkField] !== false checkbox.checked = params.data[checkField] !== false
checkbox.addEventListener('mousedown', event => event.stopPropagation())
checkbox.addEventListener('click', event => event.stopPropagation()) checkbox.addEventListener('click', event => event.stopPropagation())
checkbox.addEventListener('change', () => { checkbox.addEventListener('change', () => {
const targetRow = params.data as DetailRow | undefined const targetRow = params.data as DetailRow | undefined
if (!targetRow) return if (!targetRow) return
targetRow[checkField] = checkbox.checked updateBudgetCheckState(targetRow.id, 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() handleCellValueChanged()
params.api?.refreshCells?.({ params.api?.refreshCells?.({
rowNodes: params.node ? [params.node] : undefined, rowNodes: params.node ? [params.node] : undefined,

View File

@ -481,6 +481,24 @@ const formatReadonlyMoney = (params: any) => {
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked' type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
const updateBudgetCheckState = (rowId: string, checkField: BudgetCheckField, checked: boolean) => {
detailRows.value = detailRows.value.map(row => {
if (row.id !== rowId) return row
if (checkField === 'benchmarkBudgetBasicChecked') {
return {
...row,
benchmarkBudgetBasicChecked: checked,
benchmarkBudgetBasic: checked ? row.benchmarkBudgetBasic : 0
}
}
return {
...row,
benchmarkBudgetOptionalChecked: checked,
benchmarkBudgetOptional: checked ? row.benchmarkBudgetOptional : 0
}
})
}
const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => { const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => {
const valueText = formatReadonlyMoney(params) const valueText = formatReadonlyMoney(params)
const hasValue = params.value != null && params.value !== '' const hasValue = params.value != null && params.value !== ''
@ -499,14 +517,17 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
checkbox.type = 'checkbox' checkbox.type = 'checkbox'
checkbox.className = 'cursor-pointer' checkbox.className = 'cursor-pointer'
checkbox.checked = params.data[checkField] !== false checkbox.checked = params.data[checkField] !== false
checkbox.addEventListener('mousedown', event => event.stopPropagation())
checkbox.addEventListener('click', event => event.stopPropagation()) checkbox.addEventListener('click', event => event.stopPropagation())
checkbox.addEventListener('change', () => { checkbox.addEventListener('change', () => {
params.data[checkField] = checkbox.checked const targetRow = params.data as DetailRow | undefined
if (!checkbox.checked) { if (!targetRow) return
const budgetField = checkField === 'benchmarkBudgetBasicChecked' ? 'benchmarkBudgetBasic' : 'benchmarkBudgetOptional' updateBudgetCheckState(targetRow.id, checkField, checkbox.checked)
params.data[budgetField] = 0
}
handleCellValueChanged() handleCellValueChanged()
params.api?.refreshCells?.({
rowNodes: params.node ? [params.node] : undefined,
force: true
})
}) })
const valueSpan = document.createElement('span') const valueSpan = document.createElement('span')

View File

@ -7,7 +7,7 @@ import {
} from '@/sql' } from '@/sql'
import { roundTo, sumByNumber, toDecimal } from '@/lib/decimal' import { roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
import { toFiniteNumberOrNull } from '@/lib/number' import { toFiniteNumberOrNull } from '@/lib/number'
import { getBenchmarkBudgetByScale, getScaleBudgetFee } from '@/lib/pricingScaleFee' import { getBenchmarkBudgetByScale, getBenchmarkBudgetSplitByScale, getScaleBudgetFee, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee'
import { useZxFwPricingStore, type ServicePricingMethod } from '@/pinia/zxFwPricing' import { useZxFwPricingStore, type ServicePricingMethod } from '@/pinia/zxFwPricing'
import { useKvStore } from '@/pinia/kv' import { useKvStore } from '@/pinia/kv'
@ -40,6 +40,8 @@ interface ScaleRow {
id: string id: string
amount: number | null amount: number | null
landArea: number | null landArea: number | null
benchmarkBudgetBasicChecked: boolean
benchmarkBudgetOptionalChecked: boolean
consultCategoryFactor: number | null consultCategoryFactor: number | null
majorFactor: number | null majorFactor: number | null
workStageFactor: number | null workStageFactor: number | null
@ -313,6 +315,8 @@ const buildDefaultScaleRows = (
id, id,
amount: null, amount: null,
landArea: null, landArea: null,
benchmarkBudgetBasicChecked: true,
benchmarkBudgetOptionalChecked: true,
consultCategoryFactor: defaultConsultCategoryFactor, consultCategoryFactor: defaultConsultCategoryFactor,
majorFactor: majorFactorMap?.get(id) ?? getDefaultMajorFactorById(id), majorFactor: majorFactorMap?.get(id) ?? getDefaultMajorFactorById(id),
workStageFactor: 1, workStageFactor: 1,
@ -351,6 +355,14 @@ const mergeScaleRows = (
...row, ...row,
amount: toFiniteNumberOrNull(fromDb.amount), amount: toFiniteNumberOrNull(fromDb.amount),
landArea: toFiniteNumberOrNull(fromDb.landArea), landArea: toFiniteNumberOrNull(fromDb.landArea),
benchmarkBudgetBasicChecked:
typeof (fromDb as { benchmarkBudgetBasicChecked?: unknown }).benchmarkBudgetBasicChecked === 'boolean'
? Boolean((fromDb as { benchmarkBudgetBasicChecked?: unknown }).benchmarkBudgetBasicChecked)
: true,
benchmarkBudgetOptionalChecked:
typeof (fromDb as { benchmarkBudgetOptionalChecked?: unknown }).benchmarkBudgetOptionalChecked === 'boolean'
? Boolean((fromDb as { benchmarkBudgetOptionalChecked?: unknown }).benchmarkBudgetOptionalChecked)
: true,
consultCategoryFactor: consultCategoryFactor:
toFiniteNumberOrNull(fromDb.consultCategoryFactor) ?? toFiniteNumberOrNull(fromDb.consultCategoryFactor) ??
(hasConsultCategoryFactor ? null : defaultConsultCategoryFactor), (hasConsultCategoryFactor ? null : defaultConsultCategoryFactor),
@ -373,14 +385,32 @@ const getBenchmarkBudgetByAmount = (amount: MaybeNumber) =>
const getBenchmarkBudgetByLandArea = (landArea: MaybeNumber) => const getBenchmarkBudgetByLandArea = (landArea: MaybeNumber) =>
getBenchmarkBudgetByScale(landArea, 'area') getBenchmarkBudgetByScale(landArea, 'area')
const getCheckedScaleBudgetSplit = (
value: MaybeNumber,
mode: 'cost' | 'area',
row: Pick<ScaleRow, 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'>
) => {
const split = getBenchmarkBudgetSplitByScale(value, mode)
if (!split) return null
const basic = row.benchmarkBudgetBasicChecked === false ? 0 : split.basic
const optional = row.benchmarkBudgetOptionalChecked === false ? 0 : split.optional
return {
basic,
optional
}
}
const getInvestmentBudgetFee = (row: ScaleRow) => { const getInvestmentBudgetFee = (row: ScaleRow) => {
return getScaleBudgetFee({ const split = getCheckedScaleBudgetSplit(row.amount, 'cost', row)
benchmarkBudget: getBenchmarkBudgetByAmount(row.amount), if (!split) return null
return getScaleBudgetFeeSplit({
benchmarkBudgetBasic: split.basic,
benchmarkBudgetOptional: split.optional,
majorFactor: row.majorFactor, majorFactor: row.majorFactor,
consultCategoryFactor: row.consultCategoryFactor, consultCategoryFactor: row.consultCategoryFactor,
workStageFactor: row.workStageFactor, workStageFactor: row.workStageFactor,
workRatio: row.workRatio workRatio: row.workRatio
}) })?.total ?? null
} }
const getOnlyCostScaleBudgetFee = ( const getOnlyCostScaleBudgetFee = (
@ -409,13 +439,19 @@ const getOnlyCostScaleBudgetFee = (
return sumByNumberNullable(sourceRows, row => { return sumByNumberNullable(sourceRows, row => {
const amount = toFiniteNumberOrNull(row?.amount) const amount = toFiniteNumberOrNull(row?.amount)
if (amount == null) return null if (amount == null) return null
return getScaleBudgetFee({ const split = getCheckedScaleBudgetSplit(amount, 'cost', {
benchmarkBudget: getBenchmarkBudgetByAmount(amount), benchmarkBudgetBasicChecked: typeof row?.benchmarkBudgetBasicChecked === 'boolean' ? row.benchmarkBudgetBasicChecked : true,
benchmarkBudgetOptionalChecked: typeof row?.benchmarkBudgetOptionalChecked === 'boolean' ? row.benchmarkBudgetOptionalChecked : true
})
if (!split) return null
return getScaleBudgetFeeSplit({
benchmarkBudgetBasic: split.basic,
benchmarkBudgetOptional: split.optional,
majorFactor: getRowNumberOrFallback(row, 'majorFactor', defaultMajorFactor), majorFactor: getRowNumberOrFallback(row, 'majorFactor', defaultMajorFactor),
consultCategoryFactor: getRowNumberOrFallback(row, 'consultCategoryFactor', defaultConsultCategoryFactor), consultCategoryFactor: getRowNumberOrFallback(row, 'consultCategoryFactor', defaultConsultCategoryFactor),
workStageFactor: getRowNumberOrFallback(row, 'workStageFactor', 1), workStageFactor: getRowNumberOrFallback(row, 'workStageFactor', 1),
workRatio: getRowNumberOrFallback(row, 'workRatio', 100) workRatio: getRowNumberOrFallback(row, 'workRatio', 100)
}) })?.total ?? null
}) })
} }
@ -431,13 +467,19 @@ const getOnlyCostScaleBudgetFee = (
const majorFactor = getRowNumberOrFallback(onlyRow, 'majorFactor', defaultMajorFactor) const majorFactor = getRowNumberOrFallback(onlyRow, 'majorFactor', defaultMajorFactor)
const workStageFactor = getRowNumberOrFallback(onlyRow, 'workStageFactor', 1) const workStageFactor = getRowNumberOrFallback(onlyRow, 'workStageFactor', 1)
const workRatio = getRowNumberOrFallback(onlyRow, 'workRatio', 100) const workRatio = getRowNumberOrFallback(onlyRow, 'workRatio', 100)
return getScaleBudgetFee({ const split = getCheckedScaleBudgetSplit(totalAmount, 'cost', {
benchmarkBudget: getBenchmarkBudgetByAmount(totalAmount), benchmarkBudgetBasicChecked: typeof onlyRow?.benchmarkBudgetBasicChecked === 'boolean' ? onlyRow.benchmarkBudgetBasicChecked : true,
benchmarkBudgetOptionalChecked: typeof onlyRow?.benchmarkBudgetOptionalChecked === 'boolean' ? onlyRow.benchmarkBudgetOptionalChecked : true
})
if (!split) return null
return getScaleBudgetFeeSplit({
benchmarkBudgetBasic: split.basic,
benchmarkBudgetOptional: split.optional,
majorFactor, majorFactor,
consultCategoryFactor, consultCategoryFactor,
workStageFactor, workStageFactor,
workRatio workRatio
}) })?.total ?? null
} }
const buildOnlyCostScaleDetailRows = ( const buildOnlyCostScaleDetailRows = (
@ -475,6 +517,7 @@ const buildOnlyCostScaleDetailRows = (
{ {
id: onlyCostRowId, id: onlyCostRowId,
amount: totalAmount, amount: totalAmount,
landArea: null,
consultCategoryFactor, consultCategoryFactor,
majorFactor, majorFactor,
workStageFactor, workStageFactor,
@ -486,13 +529,16 @@ const buildOnlyCostScaleDetailRows = (
} }
const getLandBudgetFee = (row: ScaleRow) => { const getLandBudgetFee = (row: ScaleRow) => {
return getScaleBudgetFee({ const split = getCheckedScaleBudgetSplit(row.landArea, 'area', row)
benchmarkBudget: getBenchmarkBudgetByLandArea(row.landArea), if (!split) return null
return getScaleBudgetFeeSplit({
benchmarkBudgetBasic: split.basic,
benchmarkBudgetOptional: split.optional,
majorFactor: row.majorFactor, majorFactor: row.majorFactor,
consultCategoryFactor: row.consultCategoryFactor, consultCategoryFactor: row.consultCategoryFactor,
workStageFactor: row.workStageFactor, workStageFactor: row.workStageFactor,
workRatio: row.workRatio workRatio: row.workRatio
}) })?.total ?? null
} }
const getTaskEntriesByServiceId = (serviceId: string | number) => const getTaskEntriesByServiceId = (serviceId: string | number) =>
@ -650,6 +696,10 @@ const normalizeScopedScaleRows = (
id: resolvedMajorId, id: resolvedMajorId,
amount: toFiniteNumberOrNull(row.amount), amount: toFiniteNumberOrNull(row.amount),
landArea: toFiniteNumberOrNull(row.landArea), landArea: toFiniteNumberOrNull(row.landArea),
benchmarkBudgetBasicChecked:
typeof row.benchmarkBudgetBasicChecked === 'boolean' ? row.benchmarkBudgetBasicChecked : true,
benchmarkBudgetOptionalChecked:
typeof row.benchmarkBudgetOptionalChecked === 'boolean' ? row.benchmarkBudgetOptionalChecked : true,
consultCategoryFactor: consultCategoryFactor:
toFiniteNumberOrNull(row.consultCategoryFactor) ?? toFiniteNumberOrNull(row.consultCategoryFactor) ??
(hasConsultCategoryFactor ? null : defaultConsultCategoryFactor), (hasConsultCategoryFactor ? null : defaultConsultCategoryFactor),