This commit is contained in:
zwgczx committed 2026-03-03 16:16:16 +08:00
1 parent 62546bc937
commit 33913c29d2
9 files changed
+1153 -239

No files matched your search

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef } from 'ag-grid-community'
import type { ColDef, ColGroupDef } from 'ag-grid-community'
import localforage from 'localforage'
import { majorList } from '@/sql'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
@@ -11,7 +11,7 @@ import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPrici
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
import { parseNumberOrNull } from '@/lib/number'
import { getBenchmarkBudgetByScale, getScaleBudgetFee } from '@/lib/pricingScaleFee'
import { getBenchmarkBudgetSplitByScale, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee'
import { Button } from '@/components/ui/button'
import {
AlertDialogAction,
@@ -49,9 +49,15 @@ interface DetailRow {
majorName: string
amount: number | null
benchmarkBudget: number | null
benchmarkBudgetBasic: number | null
benchmarkBudgetOptional: number | null
basicFormula: string
optionalFormula: string
consultCategoryFactor: number | null
majorFactor: number | null
budgetFee: number | null
budgetFeeBasic: number | null
budgetFeeOptional: number | null
remark: string
path: string[]
}
@@ -197,9 +203,15 @@ const buildDefaultRows = (): DetailRow[] => {
majorName: child.name,
amount: null,
benchmarkBudget: null,
benchmarkBudgetBasic: null,
benchmarkBudgetOptional: null,
basicFormula: '',
optionalFormula: '',
consultCategoryFactor: null,
majorFactor: null,
budgetFee: null,
budgetFeeBasic: null,
budgetFeeOptional: null,
remark: '',
path: [group.id, child.id]
})
@@ -208,7 +220,24 @@ const buildDefaultRows = (): DetailRow[] => {
return rows
}
type SourceRow = Pick<DetailRow, 'id'> & Partial<Pick<DetailRow, 'amount' | 'benchmarkBudget' | 'consultCategoryFactor' | 'majorFactor' | 'budgetFee' | 'remark'>>
type SourceRow = Pick<DetailRow, 'id'> &
Partial<
Pick<
DetailRow,
| 'amount'
| 'benchmarkBudget'
| 'benchmarkBudgetBasic'
| 'benchmarkBudgetOptional'
| 'basicFormula'
| 'optionalFormula'
| 'consultCategoryFactor'
| 'majorFactor'
| 'budgetFee'
| 'budgetFeeBasic'
| 'budgetFeeOptional'
| 'remark'
>
>
const mergeWithDictRows = (
rowsFromDb: SourceRow[] | undefined,
options?: { includeAmount?: boolean; includeFactorValues?: boolean }
@@ -230,6 +259,10 @@ const mergeWithDictRows = (
...row,
amount: includeAmount && typeof fromDb.amount === 'number' ? fromDb.amount : null,
benchmarkBudget: typeof fromDb.benchmarkBudget === 'number' ? fromDb.benchmarkBudget : null,
benchmarkBudgetBasic: typeof fromDb.benchmarkBudgetBasic === 'number' ? fromDb.benchmarkBudgetBasic : null,
benchmarkBudgetOptional: typeof fromDb.benchmarkBudgetOptional === 'number' ? fromDb.benchmarkBudgetOptional : null,
basicFormula: typeof fromDb.basicFormula === 'string' ? fromDb.basicFormula : '',
optionalFormula: typeof fromDb.optionalFormula === 'string' ? fromDb.optionalFormula : '',
consultCategoryFactor:
!includeFactorValues
? null
@@ -247,6 +280,8 @@ const mergeWithDictRows = (
? null
: getDefaultMajorFactorById(row.id),
budgetFee: typeof fromDb.budgetFee === 'number' ? fromDb.budgetFee : null,
budgetFeeBasic: typeof fromDb.budgetFeeBasic === 'number' ? fromDb.budgetFeeBasic : null,
budgetFeeOptional: typeof fromDb.budgetFeeOptional === 'number' ? fromDb.budgetFeeOptional : null,
remark: typeof fromDb.remark === 'string' ? fromDb.remark : ''
}
})
@@ -254,7 +289,7 @@ const mergeWithDictRows = (
const formatEditableNumber = (params: any) => {
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
return '点击输入'
return '请输入'
}
if (params.value == null) return ''
return Number(params.value).toFixed(2)
@@ -281,18 +316,34 @@ const formatReadonlyMoney = (params: any) => {
return formatThousands(roundTo(params.value, 2))
}
const getBenchmarkBudgetByAmount = (row?: Pick<DetailRow, 'amount'>) =>
getBenchmarkBudgetByScale(row?.amount, 'cost')
const getBenchmarkBudgetSplitByAmount = (row?: Pick<DetailRow, 'amount'>) =>
getBenchmarkBudgetSplitByScale(row?.amount, 'cost')
const getBudgetFee = (row?: Pick<DetailRow, 'amount' | 'majorFactor' | 'consultCategoryFactor'>) => {
return getScaleBudgetFee({
benchmarkBudget: getBenchmarkBudgetByAmount(row),
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
if (!benchmarkBudgetSplit) return null
const splitBudgetFee = getScaleBudgetFeeSplit({
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
majorFactor: row?.majorFactor,
consultCategoryFactor: row?.consultCategoryFactor
})
return splitBudgetFee ? splitBudgetFee.total : null
}
const getBudgetFeeSplit = (row?: Pick<DetailRow, 'amount' | 'majorFactor' | 'consultCategoryFactor'>) => {
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
if (!benchmarkBudgetSplit) return null
return getScaleBudgetFeeSplit({
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
majorFactor: row?.majorFactor,
consultCategoryFactor: row?.consultCategoryFactor
})
}
const columnDefs: ColDef<DetailRow>[] = [
const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
{
headerName: '造价金额(万元)',
field: 'amount',
@@ -310,18 +361,7 @@ const columnDefs: ColDef<DetailRow>[] = [
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatEditableMoney
},
{
headerName: '基准预算(元)',
field: 'benchmarkBudget',
headerClass: 'ag-right-aligned-header',
minWidth: 100,
flex: 2,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params => getBenchmarkBudgetByAmount(params.data),
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatReadonlyMoney
},
{
headerName: '咨询分类系数',
field: 'consultCategoryFactor',
@@ -352,17 +392,89 @@ const columnDefs: ColDef<DetailRow>[] = [
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatMajorFactor
},
{
headerName: '基准预算(元)',
marryChildren: true,
children: [
{
headerName: '基本工作',
field: 'benchmarkBudgetBasic',
colId: 'benchmarkBudgetBasic',
headerClass: 'ag-right-aligned-header',
minWidth: 130,
flex: 1,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params =>
params.node?.rowPinned
? params.data?.benchmarkBudgetBasic ?? null
: getBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
valueFormatter: formatReadonlyMoney
},
{
headerName: '可选工作',
field: 'benchmarkBudgetOptional',
colId: 'benchmarkBudgetOptional',
headerClass: 'ag-right-aligned-header',
minWidth: 130,
flex: 1,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params =>
params.node?.rowPinned
? params.data?.benchmarkBudgetOptional ?? null
: getBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
valueFormatter: formatReadonlyMoney
}
]
},
{
headerName: '预算费用',
field: 'budgetFee',
headerClass: 'ag-right-aligned-header',
minWidth: 100,
flex:2,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params => (params.node?.rowPinned ? params.data?.budgetFee ?? null : getBudgetFee(params.data)),
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatReadonlyMoney
marryChildren: true,
children: [
{
headerName: '基本工作',
field: 'budgetFeeBasic',
colId: 'budgetFeeBasic',
headerClass: 'ag-right-aligned-header',
minWidth: 120,
flex: 1,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params =>
params.node?.rowPinned
? params.data?.budgetFeeBasic ?? null
: getBudgetFeeSplit(params.data)?.basic ?? null,
valueFormatter: formatReadonlyMoney
},
{
headerName: '可选工作',
field: 'budgetFeeOptional',
colId: 'budgetFeeOptional',
headerClass: 'ag-right-aligned-header',
minWidth: 120,
flex: 1,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params =>
params.node?.rowPinned
? params.data?.budgetFeeOptional ?? null
: getBudgetFeeSplit(params.data)?.optional ?? null,
valueFormatter: formatReadonlyMoney
},
{
headerName: '合计',
field: 'budgetFee',
colId: 'budgetFeeTotal',
headerClass: 'ag-right-aligned-header',
minWidth: 130,
flex: 1,
cellClass: 'ag-right-aligned-cell',
aggFunc: decimalAggSum,
valueGetter: params => (params.node?.rowPinned ? params.data?.budgetFee ?? null : getBudgetFee(params.data)),
valueFormatter: formatReadonlyMoney
}
]
},
{
headerName: '说明',
@@ -416,8 +528,12 @@ const autoGroupColumnDef: ColDef = {
const totalAmount = computed(() => sumByNumber(detailRows.value, row => row.amount))
const totalBenchmarkBudget = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetByAmount(row)))
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))
const totalBudgetFee = computed(() => sumByNumber(detailRows.value, row => getBudgetFee(row)))
const pinnedTopRowData = computed(() => [
{
@@ -428,21 +544,50 @@ const pinnedTopRowData = computed(() => [
majorName: '',
amount: totalAmount.value,
benchmarkBudget: totalBenchmarkBudget.value,
benchmarkBudgetBasic: totalBenchmarkBudgetBasic.value,
benchmarkBudgetOptional: totalBenchmarkBudgetOptional.value,
basicFormula: '',
optionalFormula: '',
consultCategoryFactor: null,
majorFactor: null,
budgetFee: totalBudgetFee.value,
budgetFeeBasic: totalBudgetFeeBasic.value,
budgetFeeOptional: totalBudgetFeeOptional.value,
remark: '',
path: ['TOTAL']
}
])
const buildPersistDetailRows = () =>
detailRows.value.map(row => {
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
const budgetFeeSplit = benchmarkBudgetSplit
? getScaleBudgetFeeSplit({
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
majorFactor: row.majorFactor,
consultCategoryFactor: row.consultCategoryFactor
})
: null
return {
...row,
benchmarkBudget: benchmarkBudgetSplit?.total ?? null,
benchmarkBudgetBasic: benchmarkBudgetSplit?.basic ?? null,
benchmarkBudgetOptional: benchmarkBudgetSplit?.optional ?? null,
basicFormula: benchmarkBudgetSplit?.basicFormula ?? '',
optionalFormula: benchmarkBudgetSplit?.optionalFormula ?? '',
budgetFee: budgetFeeSplit?.total ?? null,
budgetFeeBasic: budgetFeeSplit?.basic ?? null,
budgetFeeOptional: budgetFeeSplit?.optional ?? null
}
})
const saveToIndexedDB = async () => {
if (shouldSkipPersist()) return
try {
const payload = {
detailRows: JSON.parse(JSON.stringify(detailRows.value))
detailRows: JSON.parse(JSON.stringify(buildPersistDetailRows()))
}
console.log('Saving to IndexedDB:', payload)
await localforage.setItem(DB_KEY.value, payload)