if
This commit is contained in:
parent
043e1fc879
commit
303f54bb71
@ -3,7 +3,7 @@ import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'v
|
|||||||
import { AgGridVue } from 'ag-grid-vue3'
|
import { AgGridVue } from 'ag-grid-vue3'
|
||||||
import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { getMajorDictEntries, getServiceDictItemById, isMajorIdInIndustryScope } from '@/sql'
|
import { getMajorDictEntries, getServiceDictItemById, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousands } from '@/lib/numberFormat'
|
||||||
@ -100,6 +100,12 @@ const majorFactorMap = ref<Map<string, number | null>>(new Map())
|
|||||||
let factorDefaultsLoaded = false
|
let factorDefaultsLoaded = false
|
||||||
const paneInstanceCreatedAt = Date.now()
|
const paneInstanceCreatedAt = Date.now()
|
||||||
const ONLY_COST_SCALE_ROW_ID = '__only-cost-scale-total__'
|
const ONLY_COST_SCALE_ROW_ID = '__only-cost-scale-total__'
|
||||||
|
const industryNameMap = new Map(
|
||||||
|
industryTypeList.flatMap(item => [
|
||||||
|
[String(item.id).trim(), item.name],
|
||||||
|
[String(item.type).trim(), item.name]
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
const getDefaultConsultCategoryFactor = () =>
|
const getDefaultConsultCategoryFactor = () =>
|
||||||
consultCategoryFactorMap.value.get(String(props.serviceId)) ?? null
|
consultCategoryFactorMap.value.get(String(props.serviceId)) ?? null
|
||||||
@ -109,6 +115,10 @@ const isOnlyCostScaleService = computed(() => {
|
|||||||
const service = getServiceDictItemById(props.serviceId) as ServiceLite | undefined
|
const service = getServiceDictItemById(props.serviceId) as ServiceLite | undefined
|
||||||
return service?.onlyCostScale === true
|
return service?.onlyCostScale === true
|
||||||
})
|
})
|
||||||
|
const totalLabel = computed(() => {
|
||||||
|
const industryName = industryNameMap.get(activeIndustryCode.value.trim()) || ''
|
||||||
|
return industryName ? `${industryName}总投资` : '总投资'
|
||||||
|
})
|
||||||
|
|
||||||
const loadFactorDefaults = async () => {
|
const loadFactorDefaults = async () => {
|
||||||
const [consultMap, majorMap] = await Promise.all([
|
const [consultMap, majorMap] = await Promise.all([
|
||||||
@ -158,7 +168,14 @@ const shouldForceDefaultLoad = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const detailRows = ref<DetailRow[]>([])
|
const detailRows = ref<DetailRow[]>([])
|
||||||
type majorLite = { code: string; name: string; defCoe: number | null; hasCost?: boolean; hasArea?: boolean }
|
type majorLite = {
|
||||||
|
code: string
|
||||||
|
name: string
|
||||||
|
defCoe: number | null
|
||||||
|
hasCost?: boolean
|
||||||
|
hasArea?: boolean
|
||||||
|
industryId?: string | number | null
|
||||||
|
}
|
||||||
const serviceEntries = getMajorDictEntries().map(({ id, item }) => [id, item] as [string, majorLite])
|
const serviceEntries = getMajorDictEntries().map(({ id, item }) => [id, item] as [string, majorLite])
|
||||||
const majorIdAliasMap = new Map(getMajorDictEntries().map(({ rawId, id }) => [rawId, id]))
|
const majorIdAliasMap = new Map(getMajorDictEntries().map(({ rawId, id }) => [rawId, id]))
|
||||||
|
|
||||||
@ -258,6 +275,21 @@ const buildDefaultRows = (): DetailRow[] => {
|
|||||||
const calcOnlyCostScaleAmountFromRows = (rows?: Array<{ amount?: unknown }>) =>
|
const calcOnlyCostScaleAmountFromRows = (rows?: Array<{ amount?: unknown }>) =>
|
||||||
sumByNumber(rows || [], row => (typeof row?.amount === 'number' ? row.amount : null))
|
sumByNumber(rows || [], row => (typeof row?.amount === 'number' ? row.amount : null))
|
||||||
|
|
||||||
|
const getOnlyCostScaleMajorFactorDefault = () => {
|
||||||
|
const industryId = String(activeIndustryCode.value || '').trim()
|
||||||
|
if (!industryId) return 1
|
||||||
|
const industryMajor = serviceEntries.find(([, item]) => {
|
||||||
|
const majorIndustryId = String(item?.industryId ?? '').trim()
|
||||||
|
return majorIndustryId === industryId && !String(item?.code || '').includes('-')
|
||||||
|
})
|
||||||
|
if (!industryMajor) return 1
|
||||||
|
const [majorId, majorItem] = industryMajor
|
||||||
|
const fromMap = majorFactorMap.value.get(String(majorId))
|
||||||
|
if (typeof fromMap === 'number' && Number.isFinite(fromMap)) return fromMap
|
||||||
|
if (typeof majorItem?.defCoe === 'number' && Number.isFinite(majorItem.defCoe)) return majorItem.defCoe
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
const buildOnlyCostScaleRow = (
|
const buildOnlyCostScaleRow = (
|
||||||
amount: number | null,
|
amount: number | null,
|
||||||
fromDb?: Partial<Pick<DetailRow, 'consultCategoryFactor' | 'majorFactor' | 'workStageFactor' | 'workRatio' | 'remark'>>
|
fromDb?: Partial<Pick<DetailRow, 'consultCategoryFactor' | 'majorFactor' | 'workStageFactor' | 'workRatio' | 'remark'>>
|
||||||
@ -279,7 +311,7 @@ const buildOnlyCostScaleRow = (
|
|||||||
optionalFormula: '',
|
optionalFormula: '',
|
||||||
consultCategoryFactor:
|
consultCategoryFactor:
|
||||||
typeof fromDb?.consultCategoryFactor === 'number' ? fromDb.consultCategoryFactor : getDefaultConsultCategoryFactor(),
|
typeof fromDb?.consultCategoryFactor === 'number' ? fromDb.consultCategoryFactor : getDefaultConsultCategoryFactor(),
|
||||||
majorFactor: typeof fromDb?.majorFactor === 'number' ? fromDb.majorFactor : 1,
|
majorFactor: typeof fromDb?.majorFactor === 'number' ? fromDb.majorFactor : getOnlyCostScaleMajorFactorDefault(),
|
||||||
workStageFactor: typeof fromDb?.workStageFactor === 'number' ? fromDb.workStageFactor : 1,
|
workStageFactor: typeof fromDb?.workStageFactor === 'number' ? fromDb.workStageFactor : 1,
|
||||||
workRatio: typeof fromDb?.workRatio === 'number' ? fromDb.workRatio : 100,
|
workRatio: typeof fromDb?.workRatio === 'number' ? fromDb.workRatio : 100,
|
||||||
budgetFee: null,
|
budgetFee: null,
|
||||||
@ -509,7 +541,6 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
? params.value == null || params.value === ''
|
? params.value == null || params.value === ''
|
||||||
: !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
|
: !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
||||||
valueFormatter: formatEditableMoney
|
valueFormatter: formatEditableMoney
|
||||||
},
|
},
|
||||||
@ -525,10 +556,9 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? params.data?.benchmarkBudgetBasic ?? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
|
: getBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
@ -541,10 +571,9 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? params.data?.benchmarkBudgetOptional ?? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
|
: getBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
@ -557,10 +586,9 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? params.data?.benchmarkBudget ?? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByAmount(params.data)?.total ?? null,
|
: getBenchmarkBudgetSplitByAmount(params.data)?.total ?? null,
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
}
|
}
|
||||||
@ -576,11 +604,21 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
colId: 'consultCategoryFactor',
|
colId: 'consultCategoryFactor',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: params => !params.node?.group && !params.node?.rowPinned,
|
editable: params =>
|
||||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
isOnlyCostScaleService.value
|
||||||
|
? Boolean(params.node?.rowPinned)
|
||||||
|
: !params.node?.group && !params.node?.rowPinned,
|
||||||
|
cellClass: params =>
|
||||||
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: !params.node?.group && !params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: '',
|
||||||
cellClassRules: {
|
cellClassRules: {
|
||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 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: 2 }),
|
||||||
valueFormatter: formatConsultCategoryFactor
|
valueFormatter: formatConsultCategoryFactor
|
||||||
@ -591,11 +629,21 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
colId: 'majorFactor',
|
colId: 'majorFactor',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: params => !params.node?.group && !params.node?.rowPinned,
|
editable: params =>
|
||||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
isOnlyCostScaleService.value
|
||||||
|
? Boolean(params.node?.rowPinned)
|
||||||
|
: !params.node?.group && !params.node?.rowPinned,
|
||||||
|
cellClass: params =>
|
||||||
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: !params.node?.group && !params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: '',
|
||||||
cellClassRules: {
|
cellClassRules: {
|
||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 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: 2 }),
|
||||||
valueFormatter: formatMajorFactor
|
valueFormatter: formatMajorFactor
|
||||||
@ -606,11 +654,21 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
colId: 'workStageFactor',
|
colId: 'workStageFactor',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: params => !params.node?.group && !params.node?.rowPinned,
|
editable: params =>
|
||||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
isOnlyCostScaleService.value
|
||||||
|
? Boolean(params.node?.rowPinned)
|
||||||
|
: !params.node?.group && !params.node?.rowPinned,
|
||||||
|
cellClass: params =>
|
||||||
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: !params.node?.group && !params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: '',
|
||||||
cellClassRules: {
|
cellClassRules: {
|
||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 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: 2 }),
|
||||||
valueFormatter: formatEditableNumber
|
valueFormatter: formatEditableNumber
|
||||||
@ -621,11 +679,21 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
colId: 'workRatio',
|
colId: 'workRatio',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: params => !params.node?.group && !params.node?.rowPinned,
|
editable: params =>
|
||||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
isOnlyCostScaleService.value
|
||||||
|
? Boolean(params.node?.rowPinned)
|
||||||
|
: !params.node?.group && !params.node?.rowPinned,
|
||||||
|
cellClass: params =>
|
||||||
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: !params.node?.group && !params.node?.rowPinned
|
||||||
|
? 'editable-cell-line'
|
||||||
|
: '',
|
||||||
cellClassRules: {
|
cellClassRules: {
|
||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
isOnlyCostScaleService.value && params.node?.rowPinned
|
||||||
|
? 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: 2 }),
|
||||||
valueFormatter: formatEditableNumber
|
valueFormatter: formatEditableNumber
|
||||||
@ -681,13 +749,13 @@ const autoGroupColumnDef: ColDef = {
|
|||||||
},
|
},
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (params.node?.rowPinned) {
|
if (params.node?.rowPinned) {
|
||||||
return isOnlyCostScaleService.value ? '总投资' : '总合计'
|
return totalLabel.value
|
||||||
}
|
}
|
||||||
const nodeId = String(params.value || '')
|
const nodeId = String(params.value || '')
|
||||||
return idLabelMap.get(nodeId) || nodeId
|
return idLabelMap.get(nodeId) || nodeId
|
||||||
},
|
},
|
||||||
tooltipValueGetter: params => {
|
tooltipValueGetter: params => {
|
||||||
if (params.node?.rowPinned) return isOnlyCostScaleService.value ? '总投资' : '总合计'
|
if (params.node?.rowPinned) return totalLabel.value
|
||||||
const nodeId = String(params.value || '')
|
const nodeId = String(params.value || '')
|
||||||
return idLabelMap.get(nodeId) || nodeId
|
return idLabelMap.get(nodeId) || nodeId
|
||||||
}
|
}
|
||||||
@ -696,6 +764,7 @@ const autoGroupColumnDef: ColDef = {
|
|||||||
|
|
||||||
const totalAmount = computed(() => sumByNumber(detailRows.value, row => row.amount))
|
const totalAmount = computed(() => sumByNumber(detailRows.value, row => row.amount))
|
||||||
const visibleDetailRows = computed(() => (isOnlyCostScaleService.value ? [] : detailRows.value))
|
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 totalBenchmarkBudgetBasic = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.basic))
|
||||||
const totalBenchmarkBudgetOptional = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.optional))
|
const totalBenchmarkBudgetOptional = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.optional))
|
||||||
@ -713,18 +782,18 @@ const pinnedTopRowData = computed(() => [
|
|||||||
majorName: '',
|
majorName: '',
|
||||||
hasCost: false,
|
hasCost: false,
|
||||||
hasArea: false,
|
hasArea: false,
|
||||||
amount: totalAmount.value,
|
amount: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.amount : null,
|
||||||
benchmarkBudget: totalBenchmarkBudget.value,
|
benchmarkBudget: null,
|
||||||
benchmarkBudgetBasic: totalBenchmarkBudgetBasic.value,
|
benchmarkBudgetBasic: null,
|
||||||
benchmarkBudgetOptional: totalBenchmarkBudgetOptional.value,
|
benchmarkBudgetOptional: null,
|
||||||
benchmarkBudgetBasicChecked: true,
|
benchmarkBudgetBasicChecked: true,
|
||||||
benchmarkBudgetOptionalChecked: true,
|
benchmarkBudgetOptionalChecked: true,
|
||||||
basicFormula: '',
|
basicFormula: '',
|
||||||
optionalFormula: '',
|
optionalFormula: '',
|
||||||
consultCategoryFactor: null,
|
consultCategoryFactor: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.consultCategoryFactor : null,
|
||||||
majorFactor: null,
|
majorFactor: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.majorFactor : null,
|
||||||
workStageFactor: null,
|
workStageFactor: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.workStageFactor : null,
|
||||||
workRatio: null,
|
workRatio: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.workRatio : null,
|
||||||
budgetFee: totalBudgetFee.value,
|
budgetFee: totalBudgetFee.value,
|
||||||
budgetFeeBasic: totalBudgetFeeBasic.value,
|
budgetFeeBasic: totalBudgetFeeBasic.value,
|
||||||
budgetFeeOptional: totalBudgetFeeOptional.value,
|
budgetFeeOptional: totalBudgetFeeOptional.value,
|
||||||
@ -881,19 +950,28 @@ let persistTimer: ReturnType<typeof setTimeout> | null = null
|
|||||||
|
|
||||||
|
|
||||||
let gridPersistTimer: ReturnType<typeof setTimeout> | null = null
|
let gridPersistTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
const applyOnlyCostScalePinnedAmount = (rawValue: unknown) => {
|
const applyOnlyCostScalePinnedValue = (field: string, rawValue: unknown) => {
|
||||||
const amount = parseNumberOrNull(rawValue, { precision: 2 })
|
const parsedValue = parseNumberOrNull(rawValue, { precision: 2 })
|
||||||
const current = detailRows.value[0]
|
const current = detailRows.value[0]
|
||||||
if (!current) {
|
if (!current) {
|
||||||
detailRows.value = [buildOnlyCostScaleRow(amount)]
|
detailRows.value = [buildOnlyCostScaleRow(field === 'amount' ? parsedValue : null)]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
detailRows.value = [{ ...current, amount }]
|
if (
|
||||||
|
field !== 'amount' &&
|
||||||
|
field !== 'consultCategoryFactor' &&
|
||||||
|
field !== 'majorFactor' &&
|
||||||
|
field !== 'workStageFactor' &&
|
||||||
|
field !== 'workRatio'
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
detailRows.value = [{ ...current, [field]: parsedValue }]
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCellValueChanged = (event?: any) => {
|
const handleCellValueChanged = (event?: any) => {
|
||||||
if (isOnlyCostScaleService.value && event?.node?.rowPinned && event.colDef?.field === 'amount') {
|
if (isOnlyCostScaleService.value && event?.node?.rowPinned && typeof event.colDef?.field === 'string') {
|
||||||
applyOnlyCostScalePinnedAmount(event.newValue)
|
applyOnlyCostScalePinnedValue(event.colDef.field, event.newValue)
|
||||||
}
|
}
|
||||||
syncComputedValuesToDetailRows()
|
syncComputedValuesToDetailRows()
|
||||||
if (gridPersistTimer) clearTimeout(gridPersistTimer)
|
if (gridPersistTimer) clearTimeout(gridPersistTimer)
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'v
|
|||||||
import { AgGridVue } from 'ag-grid-vue3'
|
import { AgGridVue } from 'ag-grid-vue3'
|
||||||
import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
|
import { getMajorDictEntries, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousands } from '@/lib/numberFormat'
|
||||||
@ -96,6 +96,16 @@ const consultCategoryFactorMap = ref<Map<string, number | null>>(new Map())
|
|||||||
const majorFactorMap = ref<Map<string, number | null>>(new Map())
|
const majorFactorMap = ref<Map<string, number | null>>(new Map())
|
||||||
let factorDefaultsLoaded = false
|
let factorDefaultsLoaded = false
|
||||||
const paneInstanceCreatedAt = Date.now()
|
const paneInstanceCreatedAt = Date.now()
|
||||||
|
const industryNameMap = new Map(
|
||||||
|
industryTypeList.flatMap(item => [
|
||||||
|
[String(item.id).trim(), item.name],
|
||||||
|
[String(item.type).trim(), item.name]
|
||||||
|
])
|
||||||
|
)
|
||||||
|
const totalLabel = computed(() => {
|
||||||
|
const industryName = industryNameMap.get(activeIndustryCode.value.trim()) || ''
|
||||||
|
return industryName ? `${industryName}总投资` : '总投资'
|
||||||
|
})
|
||||||
|
|
||||||
const detailRows = ref<DetailRow[]>([])
|
const detailRows = ref<DetailRow[]>([])
|
||||||
const getDefaultConsultCategoryFactor = () =>
|
const getDefaultConsultCategoryFactor = () =>
|
||||||
@ -463,10 +473,9 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? params.data?.benchmarkBudgetBasic ?? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByLandArea(params.data)?.basic ?? null,
|
: getBenchmarkBudgetSplitByLandArea(params.data)?.basic ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
@ -479,10 +488,9 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? params.data?.benchmarkBudgetOptional ?? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByLandArea(params.data)?.optional ?? null,
|
: getBenchmarkBudgetSplitByLandArea(params.data)?.optional ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
@ -495,10 +503,9 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
aggFunc: decimalAggSum,
|
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? params.data?.benchmarkBudget ?? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByLandArea(params.data)?.total ?? null,
|
: getBenchmarkBudgetSplitByLandArea(params.data)?.total ?? null,
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
}
|
}
|
||||||
@ -616,13 +623,13 @@ const autoGroupColumnDef: ColDef = {
|
|||||||
},
|
},
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (params.node?.rowPinned) {
|
if (params.node?.rowPinned) {
|
||||||
return '总合计'
|
return totalLabel.value
|
||||||
}
|
}
|
||||||
const nodeId = String(params.value || '')
|
const nodeId = String(params.value || '')
|
||||||
return idLabelMap.get(nodeId) || nodeId
|
return idLabelMap.get(nodeId) || nodeId
|
||||||
},
|
},
|
||||||
tooltipValueGetter: params => {
|
tooltipValueGetter: params => {
|
||||||
if (params.node?.rowPinned) return '总合计'
|
if (params.node?.rowPinned) return totalLabel.value
|
||||||
const nodeId = String(params.value || '')
|
const nodeId = String(params.value || '')
|
||||||
return idLabelMap.get(nodeId) || nodeId
|
return idLabelMap.get(nodeId) || nodeId
|
||||||
}
|
}
|
||||||
@ -648,11 +655,11 @@ const pinnedTopRowData = computed(() => [
|
|||||||
majorName: '',
|
majorName: '',
|
||||||
hasCost: false,
|
hasCost: false,
|
||||||
hasArea: false,
|
hasArea: false,
|
||||||
amount: totalAmount.value,
|
amount: null,
|
||||||
landArea: null,
|
landArea: null,
|
||||||
benchmarkBudget: totalBenchmarkBudget.value,
|
benchmarkBudget: null,
|
||||||
benchmarkBudgetBasic: totalBenchmarkBudgetBasic.value,
|
benchmarkBudgetBasic: null,
|
||||||
benchmarkBudgetOptional: totalBenchmarkBudgetOptional.value,
|
benchmarkBudgetOptional: null,
|
||||||
benchmarkBudgetBasicChecked: true,
|
benchmarkBudgetBasicChecked: true,
|
||||||
benchmarkBudgetOptionalChecked: true,
|
benchmarkBudgetOptionalChecked: true,
|
||||||
basicFormula: '',
|
basicFormula: '',
|
||||||
|
|||||||
@ -55,6 +55,7 @@ interface MajorLite {
|
|||||||
defCoe: number | null
|
defCoe: number | null
|
||||||
hasCost?: boolean
|
hasCost?: boolean
|
||||||
hasArea?: boolean
|
hasArea?: boolean
|
||||||
|
industryId?: string | number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceLite {
|
interface ServiceLite {
|
||||||
@ -73,6 +74,10 @@ interface ExpertLite {
|
|||||||
manageCoe: number | null
|
manageCoe: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface XmBaseInfoState {
|
||||||
|
projectIndustry?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface PricingMethodTotals {
|
export interface PricingMethodTotals {
|
||||||
investScale: number | null
|
investScale: number | null
|
||||||
landScale: number | null
|
landScale: number | null
|
||||||
@ -125,6 +130,18 @@ const isDualScaleMajorById = (id: string) => {
|
|||||||
return hasCost && hasArea
|
return hasCost && hasArea
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getIndustryMajorEntryByIndustryId = (industryId: string | null | undefined) => {
|
||||||
|
const key = String(industryId || '').trim()
|
||||||
|
if (!key) return null
|
||||||
|
for (const [id, item] of majorById.entries()) {
|
||||||
|
const majorIndustryId = String(item?.industryId ?? '').trim()
|
||||||
|
if (majorIndustryId === key && !String(item?.code || '').includes('-')) {
|
||||||
|
return { id, item }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const resolveFactorValue = (
|
const resolveFactorValue = (
|
||||||
row: { budgetValue?: number | null; standardFactor?: number | null } | undefined,
|
row: { budgetValue?: number | null; standardFactor?: number | null } | undefined,
|
||||||
fallback: number | null
|
fallback: number | null
|
||||||
@ -253,7 +270,9 @@ const getInvestmentBudgetFee = (row: ScaleRow) => {
|
|||||||
const getOnlyCostScaleBudgetFee = (
|
const getOnlyCostScaleBudgetFee = (
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
rowsFromDb: Array<Record<string, unknown>> | undefined,
|
rowsFromDb: Array<Record<string, unknown>> | undefined,
|
||||||
consultCategoryFactorMap?: Map<string, number | null>
|
consultCategoryFactorMap?: Map<string, number | null>,
|
||||||
|
majorFactorMap?: Map<string, number | null>,
|
||||||
|
industryId?: string | null
|
||||||
) => {
|
) => {
|
||||||
const totalAmount = sumByNumber(rowsFromDb || [], row =>
|
const totalAmount = sumByNumber(rowsFromDb || [], row =>
|
||||||
typeof row?.amount === 'number' && Number.isFinite(row.amount) ? row.amount : null
|
typeof row?.amount === 'number' && Number.isFinite(row.amount) ? row.amount : null
|
||||||
@ -263,7 +282,12 @@ const getOnlyCostScaleBudgetFee = (
|
|||||||
toFiniteNumberOrNull(onlyRow?.consultCategoryFactor) ??
|
toFiniteNumberOrNull(onlyRow?.consultCategoryFactor) ??
|
||||||
consultCategoryFactorMap?.get(String(serviceId)) ??
|
consultCategoryFactorMap?.get(String(serviceId)) ??
|
||||||
getDefaultConsultCategoryFactor(serviceId)
|
getDefaultConsultCategoryFactor(serviceId)
|
||||||
const majorFactor = toFiniteNumberOrNull(onlyRow?.majorFactor) ?? 1
|
const industryMajorEntry = getIndustryMajorEntryByIndustryId(industryId)
|
||||||
|
const majorFactor =
|
||||||
|
toFiniteNumberOrNull(onlyRow?.majorFactor) ??
|
||||||
|
(industryMajorEntry ? majorFactorMap?.get(industryMajorEntry.id) ?? null : null) ??
|
||||||
|
toFiniteNumberOrNull(industryMajorEntry?.item?.defCoe) ??
|
||||||
|
1
|
||||||
const workStageFactor = toFiniteNumberOrNull(onlyRow?.workStageFactor) ?? 1
|
const workStageFactor = toFiniteNumberOrNull(onlyRow?.workStageFactor) ?? 1
|
||||||
const workRatio = toFiniteNumberOrNull(onlyRow?.workRatio) ?? 100
|
const workRatio = toFiniteNumberOrNull(onlyRow?.workRatio) ?? 100
|
||||||
return getScaleBudgetFee({
|
return getScaleBudgetFee({
|
||||||
@ -427,24 +451,27 @@ export const getPricingMethodTotalsForService = async (params: {
|
|||||||
const htDbKey = `ht-info-v3-${params.contractId}`
|
const htDbKey = `ht-info-v3-${params.contractId}`
|
||||||
const consultFactorDbKey = `ht-consult-category-factor-v1-${params.contractId}`
|
const consultFactorDbKey = `ht-consult-category-factor-v1-${params.contractId}`
|
||||||
const majorFactorDbKey = `ht-major-factor-v1-${params.contractId}`
|
const majorFactorDbKey = `ht-major-factor-v1-${params.contractId}`
|
||||||
|
const baseInfoDbKey = 'xm-base-info-v1'
|
||||||
const investDbKey = `tzGMF-${params.contractId}-${serviceId}`
|
const investDbKey = `tzGMF-${params.contractId}-${serviceId}`
|
||||||
const landDbKey = `ydGMF-${params.contractId}-${serviceId}`
|
const landDbKey = `ydGMF-${params.contractId}-${serviceId}`
|
||||||
const workloadDbKey = `gzlF-${params.contractId}-${serviceId}`
|
const workloadDbKey = `gzlF-${params.contractId}-${serviceId}`
|
||||||
const hourlyDbKey = `hourlyPricing-${params.contractId}-${serviceId}`
|
const hourlyDbKey = `hourlyPricing-${params.contractId}-${serviceId}`
|
||||||
|
|
||||||
const [investData, landData, workloadData, hourlyData, htData, consultFactorData, majorFactorData] = await Promise.all([
|
const [investData, landData, workloadData, hourlyData, htData, consultFactorData, majorFactorData, baseInfo] = await Promise.all([
|
||||||
localforage.getItem<StoredDetailRowsState>(investDbKey),
|
localforage.getItem<StoredDetailRowsState>(investDbKey),
|
||||||
localforage.getItem<StoredDetailRowsState>(landDbKey),
|
localforage.getItem<StoredDetailRowsState>(landDbKey),
|
||||||
localforage.getItem<StoredDetailRowsState>(workloadDbKey),
|
localforage.getItem<StoredDetailRowsState>(workloadDbKey),
|
||||||
localforage.getItem<StoredDetailRowsState>(hourlyDbKey),
|
localforage.getItem<StoredDetailRowsState>(hourlyDbKey),
|
||||||
localforage.getItem<StoredDetailRowsState>(htDbKey),
|
localforage.getItem<StoredDetailRowsState>(htDbKey),
|
||||||
localforage.getItem<StoredFactorState>(consultFactorDbKey),
|
localforage.getItem<StoredFactorState>(consultFactorDbKey),
|
||||||
localforage.getItem<StoredFactorState>(majorFactorDbKey)
|
localforage.getItem<StoredFactorState>(majorFactorDbKey),
|
||||||
|
localforage.getItem<XmBaseInfoState>(baseInfoDbKey)
|
||||||
])
|
])
|
||||||
|
|
||||||
const consultCategoryFactorMap = buildConsultCategoryFactorMap(consultFactorData)
|
const consultCategoryFactorMap = buildConsultCategoryFactorMap(consultFactorData)
|
||||||
const majorFactorMap = buildMajorFactorMap(majorFactorData)
|
const majorFactorMap = buildMajorFactorMap(majorFactorData)
|
||||||
const onlyCostScale = isOnlyCostScaleService(serviceId)
|
const onlyCostScale = isOnlyCostScaleService(serviceId)
|
||||||
|
const industryId = typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''
|
||||||
|
|
||||||
// 优先使用对应计费页的数据;不存在时回退合同段规模信息,再回退默认字典行。
|
// 优先使用对应计费页的数据;不存在时回退合同段规模信息,再回退默认字典行。
|
||||||
const excludeInvestmentCostAndAreaRows = params.options?.excludeInvestmentCostAndAreaRows === true
|
const excludeInvestmentCostAndAreaRows = params.options?.excludeInvestmentCostAndAreaRows === true
|
||||||
@ -453,7 +480,9 @@ export const getPricingMethodTotalsForService = async (params: {
|
|||||||
serviceId,
|
serviceId,
|
||||||
(investData?.detailRows as Array<Record<string, unknown>> | undefined) ||
|
(investData?.detailRows as Array<Record<string, unknown>> | undefined) ||
|
||||||
(htData?.detailRows as Array<Record<string, unknown>> | undefined),
|
(htData?.detailRows as Array<Record<string, unknown>> | undefined),
|
||||||
consultCategoryFactorMap
|
consultCategoryFactorMap,
|
||||||
|
majorFactorMap,
|
||||||
|
industryId
|
||||||
)
|
)
|
||||||
: (() => {
|
: (() => {
|
||||||
const investRows = resolveScaleRows(
|
const investRows = resolveScaleRows(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user