备份
This commit is contained in:
1 parent
fc26a87bee
commit
8a15587d30
8 files changed
+348
-216
No files matched your search
@@ -21,6 +21,10 @@ import {
|
||||
AlertDialogOverlay,
|
||||
AlertDialogPortal,
|
||||
AlertDialogRoot,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldRoot,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from 'reka-ui'
|
||||
@@ -57,8 +61,8 @@ interface DetailRow {
|
||||
benchmarkBudgetOptional: number | null
|
||||
benchmarkBudgetBasicChecked: boolean
|
||||
benchmarkBudgetOptionalChecked: boolean
|
||||
basicFormula: string
|
||||
optionalFormula: string
|
||||
basicFormula: string |null
|
||||
optionalFormula: string |null
|
||||
consultCategoryFactor: number | null
|
||||
majorFactor: number | null
|
||||
workStageFactor: number | null
|
||||
@@ -79,6 +83,7 @@ interface XmBaseInfoState {
|
||||
}
|
||||
|
||||
interface ServiceLite {
|
||||
mutiple?: boolean | null
|
||||
onlyCostScale?: boolean | null
|
||||
}
|
||||
|
||||
@@ -115,6 +120,11 @@ const isOnlyCostScaleService = computed(() => {
|
||||
const service = getServiceDictItemById(props.serviceId) as ServiceLite | undefined
|
||||
return service?.onlyCostScale === true
|
||||
})
|
||||
const isMutipleService = computed(() => {
|
||||
const service = getServiceDictItemById(props.serviceId) as ServiceLite | undefined
|
||||
return service?.mutiple === true
|
||||
})
|
||||
const projectCount = ref<number>(1)
|
||||
const totalLabel = computed(() => {
|
||||
const industryName = industryNameMap.get(activeIndustryCode.value.trim()) || ''
|
||||
return industryName ? `${industryName}总投资` : '总投资'
|
||||
@@ -896,8 +906,10 @@ const syncComputedValuesToDetailRows = () => {
|
||||
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
|
||||
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
|
||||
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
|
||||
row.basicFormula = benchmarkBudgetRawSplit?.basicFormula ?? ''
|
||||
row.optionalFormula = benchmarkBudgetRawSplit?.optionalFormula ?? ''
|
||||
row.basicFormula =
|
||||
row.benchmarkBudgetBasicChecked === false ? null : (benchmarkBudgetRawSplit?.basicFormula ?? '')
|
||||
row.optionalFormula =
|
||||
row.benchmarkBudgetOptionalChecked === false ? null : (benchmarkBudgetRawSplit?.optionalFormula ?? '')
|
||||
row.budgetFee = budgetFeeSplit?.total ?? null
|
||||
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
|
||||
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
|
||||
@@ -1096,7 +1108,17 @@ const processCellFromClipboard = (params: any) => {
|
||||
|
||||
<div class="rounded-lg border bg-card xmMx flex min-h-0 flex-1 flex-col">
|
||||
<div class="flex items-center justify-between border-b px-4 py-3">
|
||||
<h3 class="text-sm font-semibold text-foreground">投资规模明细</h3>
|
||||
<div class="flex items-center gap-3">
|
||||
<h3 class="text-sm font-semibold text-foreground">投资规模明细</h3>
|
||||
<div v-if="isMutipleService" class="flex items-center gap-2">
|
||||
<span class="text-xs text-muted-foreground">项目数量</span>
|
||||
<NumberFieldRoot v-model="projectCount" :min="1" :step="1" class="inline-flex items-center rounded-md border bg-background">
|
||||
<NumberFieldDecrement class="px-2 py-1 text-xs text-muted-foreground hover:bg-muted">-</NumberFieldDecrement>
|
||||
<NumberFieldInput class="h-7 w-14 border-x bg-transparent px-2 text-center text-xs outline-none" />
|
||||
<NumberFieldIncrement class="px-2 py-1 text-xs text-muted-foreground hover:bg-muted">+</NumberFieldIncrement>
|
||||
</NumberFieldRoot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<AlertDialogRoot>
|
||||
<AlertDialogTrigger as-child>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'v
|
||||
import { AgGridVue } from 'ag-grid-vue3'
|
||||
import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
||||
import localforage from 'localforage'
|
||||
import { getMajorDictEntries, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
||||
import { getMajorDictEntries, getServiceDictItemById, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||
import { addNumbers, decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||
@@ -21,6 +21,10 @@ import {
|
||||
AlertDialogOverlay,
|
||||
AlertDialogPortal,
|
||||
AlertDialogRoot,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldRoot,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from 'reka-ui'
|
||||
@@ -58,8 +62,8 @@ interface DetailRow {
|
||||
benchmarkBudgetOptional: number | null
|
||||
benchmarkBudgetBasicChecked: boolean
|
||||
benchmarkBudgetOptionalChecked: boolean
|
||||
basicFormula: string
|
||||
optionalFormula: string
|
||||
basicFormula: string | null
|
||||
optionalFormula: string | null
|
||||
consultCategoryFactor: number | null
|
||||
majorFactor: number | null
|
||||
workStageFactor: number | null
|
||||
@@ -79,6 +83,10 @@ interface XmBaseInfoState {
|
||||
projectIndustry?: string
|
||||
}
|
||||
|
||||
interface ServiceLite {
|
||||
mutiple?: boolean | null
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
contractId: string,
|
||||
serviceId: string | number
|
||||
@@ -106,6 +114,11 @@ const totalLabel = computed(() => {
|
||||
const industryName = industryNameMap.get(activeIndustryCode.value.trim()) || ''
|
||||
return industryName ? `${industryName}总投资` : '总投资'
|
||||
})
|
||||
const isMutipleService = computed(() => {
|
||||
const service = getServiceDictItemById(props.serviceId) as ServiceLite | undefined
|
||||
return service?.mutiple === true
|
||||
})
|
||||
const projectCount = ref<number>(1)
|
||||
|
||||
const detailRows = ref<DetailRow[]>([])
|
||||
const getDefaultConsultCategoryFactor = () =>
|
||||
@@ -732,20 +745,22 @@ const syncComputedValuesToDetailRows = () => {
|
||||
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByLandArea(row)
|
||||
const budgetFeeSplit = benchmarkBudgetSplit
|
||||
? getScaleBudgetFeeSplit({
|
||||
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
||||
benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
|
||||
majorFactor: row.majorFactor,
|
||||
consultCategoryFactor: row.consultCategoryFactor,
|
||||
workStageFactor: row.workStageFactor,
|
||||
workRatio: row.workRatio
|
||||
})
|
||||
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
||||
benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
|
||||
majorFactor: row.majorFactor,
|
||||
consultCategoryFactor: row.consultCategoryFactor,
|
||||
workStageFactor: row.workStageFactor,
|
||||
workRatio: row.workRatio
|
||||
})
|
||||
: null
|
||||
|
||||
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
|
||||
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
|
||||
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
|
||||
row.basicFormula = benchmarkBudgetRawSplit?.basicFormula ?? ''
|
||||
row.optionalFormula = benchmarkBudgetRawSplit?.optionalFormula ?? ''
|
||||
row.basicFormula =
|
||||
row.benchmarkBudgetBasicChecked === false ? null : (benchmarkBudgetRawSplit?.basicFormula ?? '')
|
||||
row.optionalFormula =
|
||||
row.benchmarkBudgetOptionalChecked === false ? null : (benchmarkBudgetRawSplit?.optionalFormula ?? '')
|
||||
row.budgetFee = budgetFeeSplit?.total ?? null
|
||||
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
|
||||
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
|
||||
@@ -792,10 +807,10 @@ const loadFromIndexedDB = async () => {
|
||||
detailRows.value = hasContractRows
|
||||
? mergeWithDictRows(htData!.detailRows, { includeFactorValues: true })
|
||||
: buildDefaultRows().map(row => ({
|
||||
...row,
|
||||
consultCategoryFactor: getDefaultConsultCategoryFactor(),
|
||||
majorFactor: getDefaultMajorFactorById(row.id)
|
||||
}))
|
||||
...row,
|
||||
consultCategoryFactor: getDefaultConsultCategoryFactor(),
|
||||
majorFactor: getDefaultMajorFactorById(row.id)
|
||||
}))
|
||||
syncComputedValuesToDetailRows()
|
||||
}
|
||||
if (shouldForceDefaultLoad()) {
|
||||
@@ -831,10 +846,10 @@ const importContractData = async () => {
|
||||
detailRows.value = hasContractRows
|
||||
? mergeWithDictRows(htData!.detailRows, { includeFactorValues: true })
|
||||
: buildDefaultRows().map(row => ({
|
||||
...row,
|
||||
consultCategoryFactor: getDefaultConsultCategoryFactor(),
|
||||
majorFactor: getDefaultMajorFactorById(row.id)
|
||||
}))
|
||||
...row,
|
||||
consultCategoryFactor: getDefaultConsultCategoryFactor(),
|
||||
majorFactor: getDefaultMajorFactorById(row.id)
|
||||
}))
|
||||
await saveToIndexedDB()
|
||||
} catch (error) {
|
||||
console.error('importContractData failed:', error)
|
||||
@@ -911,7 +926,17 @@ const processCellFromClipboard = (params: any) => {
|
||||
|
||||
<div class="rounded-lg border bg-card xmMx flex min-h-0 flex-1 flex-col">
|
||||
<div class="flex items-center justify-between border-b px-4 py-3">
|
||||
<h3 class="text-sm font-semibold text-foreground">用地规模明细</h3>
|
||||
<div class="flex items-center gap-3">
|
||||
<h3 class="text-sm font-semibold text-foreground">用地规模明细</h3>
|
||||
<div v-if="isMutipleService" class="flex items-center gap-2">
|
||||
<span class="text-xs text-muted-foreground">项目数量</span>
|
||||
<NumberFieldRoot v-model="projectCount" :min="1" :step="1" class="inline-flex items-center rounded-md border bg-background">
|
||||
<NumberFieldDecrement class="px-2 py-1 text-xs text-muted-foreground hover:bg-muted">-</NumberFieldDecrement>
|
||||
<NumberFieldInput class="h-7 w-14 border-x bg-transparent px-2 text-center text-xs outline-none" />
|
||||
<NumberFieldIncrement class="px-2 py-1 text-xs text-muted-foreground hover:bg-muted">+</NumberFieldIncrement>
|
||||
</NumberFieldRoot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<AlertDialogRoot>
|
||||
<AlertDialogTrigger as-child>
|
||||
@@ -919,7 +944,8 @@ const processCellFromClipboard = (params: any) => {
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay class="fixed inset-0 z-50 bg-black/45" />
|
||||
<AlertDialogContent class="fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-5 shadow-xl">
|
||||
<AlertDialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-5 shadow-xl">
|
||||
<AlertDialogTitle class="text-base font-semibold">确认清空当前明细</AlertDialogTitle>
|
||||
<AlertDialogDescription class="mt-2 text-sm text-muted-foreground">
|
||||
将清空当前用地规模明细,是否继续?
|
||||
@@ -941,7 +967,8 @@ const processCellFromClipboard = (params: any) => {
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay class="fixed inset-0 z-50 bg-black/45" />
|
||||
<AlertDialogContent class="fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-5 shadow-xl">
|
||||
<AlertDialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-5 shadow-xl">
|
||||
<AlertDialogTitle class="text-base font-semibold">确认覆盖当前明细</AlertDialogTitle>
|
||||
<AlertDialogDescription class="mt-2 text-sm text-muted-foreground">
|
||||
将使用合同默认数据覆盖当前用地规模明细,是否继续?
|
||||
@@ -972,5 +999,3 @@ const processCellFromClipboard = (params: any) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { parseNumberOrNull } from '@/lib/number'
|
||||
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||
import {
|
||||
ensurePricingMethodDetailRowsForServices,
|
||||
persistDefaultPricingMethodDetailRowsForServices,
|
||||
getPricingMethodTotalsForService,
|
||||
getPricingMethodTotalsForServices,
|
||||
type PricingMethodTotals
|
||||
@@ -669,7 +670,7 @@ const fillPricingTotalsForServiceIds = async (serviceIds: string[]) => {
|
||||
return
|
||||
}
|
||||
|
||||
await ensurePricingMethodDetailRowsForServices({
|
||||
await persistDefaultPricingMethodDetailRowsForServices({
|
||||
contractId: props.contractId,
|
||||
serviceIds: targetIds,
|
||||
options: PRICING_TOTALS_OPTIONS
|
||||
|
||||
+138
-57
@@ -89,6 +89,28 @@ interface PricingMethodTotalsOptions {
|
||||
excludeInvestmentCostAndAreaRows?: boolean
|
||||
}
|
||||
|
||||
interface PricingMethodDetailDbKeys {
|
||||
investScale: string
|
||||
landScale: string
|
||||
workload: string
|
||||
hourly: string
|
||||
}
|
||||
|
||||
interface PricingMethodDefaultDetailRows {
|
||||
investScale: unknown[]
|
||||
landScale: unknown[]
|
||||
workload: unknown[]
|
||||
hourly: unknown[]
|
||||
}
|
||||
|
||||
interface PricingMethodDefaultBuildContext {
|
||||
htData: StoredDetailRowsState | null
|
||||
consultCategoryFactorMap: Map<string, number | null>
|
||||
majorFactorMap: Map<string, number | null>
|
||||
industryId: string
|
||||
excludeInvestmentCostAndAreaRows: boolean
|
||||
}
|
||||
|
||||
const ONLY_COST_SCALE_ROW_ID = '__only-cost-scale-total__'
|
||||
|
||||
const hasOwn = (obj: unknown, key: string) =>
|
||||
@@ -494,6 +516,105 @@ const resolveScaleRows = (
|
||||
return buildDefaultScaleRows(serviceId, consultCategoryFactorMap, majorFactorMap)
|
||||
}
|
||||
|
||||
export const getPricingMethodDetailDbKeys = (
|
||||
contractId: string,
|
||||
serviceId: string | number
|
||||
): PricingMethodDetailDbKeys => {
|
||||
const normalizedServiceId = String(serviceId)
|
||||
return {
|
||||
investScale: `tzGMF-${contractId}-${normalizedServiceId}`,
|
||||
landScale: `ydGMF-${contractId}-${normalizedServiceId}`,
|
||||
workload: `gzlF-${contractId}-${normalizedServiceId}`,
|
||||
hourly: `hourlyPricing-${contractId}-${normalizedServiceId}`
|
||||
}
|
||||
}
|
||||
|
||||
const loadPricingMethodDefaultBuildContext = async (
|
||||
contractId: string,
|
||||
options?: PricingMethodTotalsOptions
|
||||
): Promise<PricingMethodDefaultBuildContext> => {
|
||||
const htDbKey = `ht-info-v3-${contractId}`
|
||||
const consultFactorDbKey = `ht-consult-category-factor-v1-${contractId}`
|
||||
const majorFactorDbKey = `ht-major-factor-v1-${contractId}`
|
||||
const baseInfoDbKey = 'xm-base-info-v1'
|
||||
|
||||
const [htData, consultFactorData, majorFactorData, baseInfo] = await Promise.all([
|
||||
localforage.getItem<StoredDetailRowsState>(htDbKey),
|
||||
localforage.getItem<StoredFactorState>(consultFactorDbKey),
|
||||
localforage.getItem<StoredFactorState>(majorFactorDbKey),
|
||||
localforage.getItem<XmBaseInfoState>(baseInfoDbKey)
|
||||
])
|
||||
|
||||
return {
|
||||
htData,
|
||||
consultCategoryFactorMap: buildConsultCategoryFactorMap(consultFactorData),
|
||||
majorFactorMap: buildMajorFactorMap(majorFactorData),
|
||||
industryId: typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : '',
|
||||
excludeInvestmentCostAndAreaRows: options?.excludeInvestmentCostAndAreaRows === true
|
||||
}
|
||||
}
|
||||
|
||||
const buildDefaultPricingMethodDetailRows = (
|
||||
serviceId: string,
|
||||
context: PricingMethodDefaultBuildContext
|
||||
): PricingMethodDefaultDetailRows => {
|
||||
const onlyCostScale = isOnlyCostScaleService(serviceId)
|
||||
const scaleRows = resolveScaleRows(
|
||||
serviceId,
|
||||
null,
|
||||
context.htData,
|
||||
context.consultCategoryFactorMap,
|
||||
context.majorFactorMap
|
||||
)
|
||||
|
||||
const investScale = onlyCostScale
|
||||
? buildOnlyCostScaleDetailRows(
|
||||
serviceId,
|
||||
context.htData?.detailRows as Array<Record<string, unknown>> | undefined,
|
||||
context.consultCategoryFactorMap,
|
||||
context.majorFactorMap,
|
||||
context.industryId
|
||||
)
|
||||
: scaleRows.filter(row => {
|
||||
if (!isCostMajorById(row.id)) return false
|
||||
if (context.excludeInvestmentCostAndAreaRows && isDualScaleMajorById(row.id)) return false
|
||||
return true
|
||||
})
|
||||
const landScale = scaleRows.filter(row => isAreaMajorById(row.id))
|
||||
|
||||
return {
|
||||
investScale,
|
||||
landScale,
|
||||
workload: buildDefaultWorkloadRows(serviceId, context.consultCategoryFactorMap),
|
||||
hourly: buildDefaultHourlyRows()
|
||||
}
|
||||
}
|
||||
|
||||
export const persistDefaultPricingMethodDetailRowsForServices = async (params: {
|
||||
contractId: string
|
||||
serviceIds: Array<string | number>
|
||||
options?: PricingMethodTotalsOptions
|
||||
}) => {
|
||||
const uniqueServiceIds = Array.from(new Set(params.serviceIds.map(serviceId => String(serviceId))))
|
||||
if (uniqueServiceIds.length === 0) return
|
||||
|
||||
const context = await loadPricingMethodDefaultBuildContext(params.contractId, params.options)
|
||||
|
||||
await Promise.all(
|
||||
uniqueServiceIds.map(async serviceId => {
|
||||
const dbKeys = getPricingMethodDetailDbKeys(params.contractId, serviceId)
|
||||
const defaultRows = buildDefaultPricingMethodDetailRows(serviceId, context)
|
||||
console.log(dbKeys,defaultRows)
|
||||
await Promise.all([
|
||||
localforage.setItem(dbKeys.investScale, { detailRows: defaultRows.investScale }),
|
||||
localforage.setItem(dbKeys.landScale, { detailRows: defaultRows.landScale }),
|
||||
localforage.setItem(dbKeys.workload, { detailRows: defaultRows.workload }),
|
||||
localforage.setItem(dbKeys.hourly, { detailRows: defaultRows.hourly })
|
||||
])
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export const getPricingMethodTotalsForService = async (params: {
|
||||
contractId: string
|
||||
serviceId: string | number
|
||||
@@ -593,34 +714,16 @@ export const ensurePricingMethodDetailRowsForServices = async (params: {
|
||||
const uniqueServiceIds = Array.from(new Set(params.serviceIds.map(serviceId => String(serviceId))))
|
||||
if (uniqueServiceIds.length === 0) return
|
||||
|
||||
const htDbKey = `ht-info-v3-${params.contractId}`
|
||||
const consultFactorDbKey = `ht-consult-category-factor-v1-${params.contractId}`
|
||||
const majorFactorDbKey = `ht-major-factor-v1-${params.contractId}`
|
||||
const baseInfoDbKey = 'xm-base-info-v1'
|
||||
|
||||
const [htData, consultFactorData, majorFactorData, baseInfo] = await Promise.all([
|
||||
localforage.getItem<StoredDetailRowsState>(htDbKey),
|
||||
localforage.getItem<StoredFactorState>(consultFactorDbKey),
|
||||
localforage.getItem<StoredFactorState>(majorFactorDbKey),
|
||||
localforage.getItem<XmBaseInfoState>(baseInfoDbKey)
|
||||
])
|
||||
|
||||
const consultCategoryFactorMap = buildConsultCategoryFactorMap(consultFactorData)
|
||||
const majorFactorMap = buildMajorFactorMap(majorFactorData)
|
||||
const industryId = typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''
|
||||
const excludeInvestmentCostAndAreaRows = params.options?.excludeInvestmentCostAndAreaRows === true
|
||||
const context = await loadPricingMethodDefaultBuildContext(params.contractId, params.options)
|
||||
|
||||
await Promise.all(
|
||||
uniqueServiceIds.map(async serviceId => {
|
||||
const investDbKey = `tzGMF-${params.contractId}-${serviceId}`
|
||||
const landDbKey = `ydGMF-${params.contractId}-${serviceId}`
|
||||
const workloadDbKey = `gzlF-${params.contractId}-${serviceId}`
|
||||
const hourlyDbKey = `hourlyPricing-${params.contractId}-${serviceId}`
|
||||
const dbKeys = getPricingMethodDetailDbKeys(params.contractId, serviceId)
|
||||
const [investData, landData, workloadData, hourlyData] = await Promise.all([
|
||||
localforage.getItem<StoredDetailRowsState>(investDbKey),
|
||||
localforage.getItem<StoredDetailRowsState>(landDbKey),
|
||||
localforage.getItem<StoredDetailRowsState>(workloadDbKey),
|
||||
localforage.getItem<StoredDetailRowsState>(hourlyDbKey)
|
||||
localforage.getItem<StoredDetailRowsState>(dbKeys.investScale),
|
||||
localforage.getItem<StoredDetailRowsState>(dbKeys.landScale),
|
||||
localforage.getItem<StoredDetailRowsState>(dbKeys.workload),
|
||||
localforage.getItem<StoredDetailRowsState>(dbKeys.hourly)
|
||||
])
|
||||
|
||||
const shouldInitInvest = !Array.isArray(investData?.detailRows) || investData!.detailRows!.length === 0
|
||||
@@ -629,50 +732,28 @@ export const ensurePricingMethodDetailRowsForServices = async (params: {
|
||||
const shouldInitHourly = !Array.isArray(hourlyData?.detailRows) || hourlyData!.detailRows!.length === 0
|
||||
|
||||
const writeTasks: Promise<unknown>[] = []
|
||||
let defaultRows: PricingMethodDefaultDetailRows | null = null
|
||||
const getDefaultRows = () => {
|
||||
if (!defaultRows) {
|
||||
defaultRows = buildDefaultPricingMethodDetailRows(serviceId, context)
|
||||
}
|
||||
return defaultRows
|
||||
}
|
||||
|
||||
if (shouldInitInvest) {
|
||||
const onlyCostScale = isOnlyCostScaleService(serviceId)
|
||||
const investRows = onlyCostScale
|
||||
? buildOnlyCostScaleDetailRows(
|
||||
serviceId,
|
||||
(htData?.detailRows as Array<Record<string, unknown>> | undefined),
|
||||
consultCategoryFactorMap,
|
||||
majorFactorMap,
|
||||
industryId
|
||||
)
|
||||
: resolveScaleRows(
|
||||
serviceId,
|
||||
null,
|
||||
htData,
|
||||
consultCategoryFactorMap,
|
||||
majorFactorMap
|
||||
).filter(row => {
|
||||
if (!isCostMajorById(row.id)) return false
|
||||
if (excludeInvestmentCostAndAreaRows && isDualScaleMajorById(row.id)) return false
|
||||
return true
|
||||
})
|
||||
writeTasks.push(localforage.setItem(investDbKey, { detailRows: investRows }))
|
||||
writeTasks.push(localforage.setItem(dbKeys.investScale, { detailRows: getDefaultRows().investScale }))
|
||||
}
|
||||
|
||||
if (shouldInitLand) {
|
||||
const landRows = resolveScaleRows(
|
||||
serviceId,
|
||||
null,
|
||||
htData,
|
||||
consultCategoryFactorMap,
|
||||
majorFactorMap
|
||||
).filter(row => isAreaMajorById(row.id))
|
||||
writeTasks.push(localforage.setItem(landDbKey, { detailRows: landRows }))
|
||||
writeTasks.push(localforage.setItem(dbKeys.landScale, { detailRows: getDefaultRows().landScale }))
|
||||
}
|
||||
|
||||
if (shouldInitWorkload) {
|
||||
const workloadRows = buildDefaultWorkloadRows(serviceId, consultCategoryFactorMap)
|
||||
writeTasks.push(localforage.setItem(workloadDbKey, { detailRows: workloadRows }))
|
||||
writeTasks.push(localforage.setItem(dbKeys.workload, { detailRows: getDefaultRows().workload }))
|
||||
}
|
||||
|
||||
if (shouldInitHourly) {
|
||||
const hourlyRows = buildDefaultHourlyRows()
|
||||
writeTasks.push(localforage.setItem(hourlyDbKey, { detailRows: hourlyRows }))
|
||||
writeTasks.push(localforage.setItem(dbKeys.hourly, { detailRows: getDefaultRows().hourly }))
|
||||
}
|
||||
|
||||
if (writeTasks.length > 0) {
|
||||
|
||||
+19
-18
@@ -87,16 +87,17 @@ export const serviceList = {
|
||||
26: { code: 'D4-12', name: '调整概算', maxCoe: 0.3, minCoe: 0.15, defCoe: 0.225, desc: '本表系数适用于采用规模计价法基准预算的系数;依据其调整时期所在建设阶段和基础资料的不同,其系数取值不同。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 27, scale: true, onlyCostScale: true, amount: false, workDay: true },
|
||||
27: { code: 'D4-13', name: '造价检查', maxCoe: null, minCoe: null, defCoe: null, desc: '可按照服务工日数量×服务工日人工单价×综合预算系数;也可按照服务工日数量×服务工日综合预算单价。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 28, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
28: { code: 'D4-14', name: '其他专项咨询', maxCoe: null, minCoe: null, defCoe: null, desc: '可参照相同或相似服务的系数。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 29, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
29: { code: 'D4-15-1', name: '造价数据测试验证(估算)', maxCoe: null, minCoe: null, defCoe: 0.04, desc: '委托同一咨询人同时负责D3-1和D3-2时,D3-1和D3-2的合计调整系数为0.25。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 30, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
30: { code: 'D4-15-2', name: '造价数据测试验证(概算)', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 31, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
31: { code: 'D4-15-3', name: '造价数据测试验证(施工图预算)', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '委托同一咨询人同时负责D3-3和D3-4时,D3-3和D3-4的合计调整系数为0.3。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 32, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
32: { code: 'D4-15-4', name: '造价数据测试验证(招标工程量清单及清单预算(或最高投标限价))', maxCoe: null, minCoe: null, defCoe: 0.06, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 33, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
33: { code: 'D4-15-5', name: '造价数据测试验证(清理概算(仅限铁路))', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '本系数适用于铁路工程。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 34, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
34: { code: 'D4-15-6', name: '造价数据测试验证(合同(工程)结算)', maxCoe: null, minCoe: null, defCoe: 0.12, desc: '本系数适用于公路和水运工程。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 35, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
35: { code: 'D4-15-7', name: '造价数据测试验证(合同(工程)结算)', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '本系数适用于铁路工程。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 36, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
36: { code: 'D4-15-8', name: '造价数据测试验证(竣工决算)', maxCoe: null, minCoe: null, defCoe: 0.04, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 37, scale: false, onlyCostScale: null, amount: false, workDay: true },
|
||||
29: { code: 'D4-15-1', name: '造价数据测试验证(估算)', maxCoe: null, minCoe: null, defCoe: 0.04, desc: '委托同一咨询人同时负责D3-1和D3-2时,D3-1和D3-2的合计调整系数为0.25。', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 30, scale: true, onlyCostScale: true, amount: false, workDay: true },
|
||||
30: { code: 'D4-15-2', name: '造价数据测试验证(概算)', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 31, scale: true, onlyCostScale: true, amount: false, workDay: true },
|
||||
31: { code: 'D4-15-3', name: '造价数据测试验证(施工图预算)', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '委托同一咨询人同时负责D3-3和D3-4时,D3-3和D3-4的合计调整系数为0.3。', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 32, scale: true, onlyCostScale: false, amount: false, workDay: true },
|
||||
32: { code: 'D4-15-4', name: '造价数据测试验证(招标工程量清单及清单预算(或最高投标限价))', maxCoe: null, minCoe: null, defCoe: 0.06, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 33, scale: true, onlyCostScale: false, amount: false, workDay: true },
|
||||
33: { code: 'D4-15-5', name: '造价数据测试验证(清理概算(仅限铁路))', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: true, order: 34, scale: true, onlyCostScale: true, amount: false, workDay: true },
|
||||
34: { code: 'D4-15-6', name: '造价数据测试验证(合同(工程)结算)', maxCoe: null, minCoe: null, defCoe: 0.12, desc: '本系数适用于公路和水运工程。', isRoad: true, isRailway: false, isWaterway: true, mutiple: true, order: 35, scale: true, onlyCostScale: false, amount: false, workDay: true },
|
||||
35: { code: 'D4-15-7', name: '造价数据测试验证(合同(工程)结算)', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: true, order: 36, scale: true, onlyCostScale: false, amount: false, workDay: true },
|
||||
36: { code: 'D4-15-8', name: '造价数据测试验证(竣工决算)', maxCoe: null, minCoe: null, defCoe: 0.04, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 37, scale: true, onlyCostScale: true, amount: false, workDay: true },
|
||||
};
|
||||
|
||||
|
||||
export const taskList = {
|
||||
0: { serviceID: 15, code: 'C4-1', name: '工程造价日常顾问', basicParam: '服务月份数', required: true, unit: '万元/月', conversion: 10000, maxPrice: 0.5, minPrice: 0.3, defPrice: 0.4, desc: '' },
|
||||
1: { serviceID: 15, code: 'C4-2', name: '工程造价专项顾问', basicParam: '服务项目的造价金额', required: true, unit: '%', conversion: 0.01, maxPrice: null, minPrice: null, defPrice: 0.01, desc: '适用于涉及造价费用类的顾问' },
|
||||
@@ -550,13 +551,13 @@ async function generateTemplate(data) {
|
||||
// const downTextTmp = { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: '常规' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: '下标' }] };
|
||||
|
||||
data.contracts[0].addtional = {// 附加工作费
|
||||
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'C' }] },
|
||||
code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'C' }] },
|
||||
name: '附加工作',
|
||||
fee: 10000,
|
||||
det: [
|
||||
{
|
||||
id: 0,
|
||||
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' }] },
|
||||
code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' }] },
|
||||
name: '人员驻场服务及其他附加工作',
|
||||
fee: 10000,
|
||||
m4: {
|
||||
@@ -606,7 +607,7 @@ async function generateTemplate(data) {
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'X' }] },
|
||||
code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'X' }] },
|
||||
name: '咨询服务协调工作',
|
||||
fee: 10000,
|
||||
m0: {
|
||||
@@ -661,7 +662,7 @@ async function generateTemplate(data) {
|
||||
]
|
||||
};
|
||||
data.contracts[0].reserve = {// 预备费
|
||||
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'Y' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'B' }] },
|
||||
code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'Y' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'B' }] },
|
||||
name: '预备费',
|
||||
fee: 10000,
|
||||
m0: {
|
||||
@@ -1172,7 +1173,7 @@ async function generateTemplate(data) {
|
||||
endRows++;
|
||||
cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => {
|
||||
targetRow.getCell(1).value = ci.services.length + endRows;
|
||||
targetRow.getCell(2).value = ci.addtional.ref;
|
||||
targetRow.getCell(2).value = ci.addtional.code;
|
||||
targetRow.getCell(3).value = ci.addtional.name;
|
||||
targetRow.getCell(4).value = '';
|
||||
targetRow.getCell(5).value = '';
|
||||
@@ -1184,7 +1185,7 @@ async function generateTemplate(data) {
|
||||
endRows++;
|
||||
cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => {
|
||||
targetRow.getCell(1).value = ci.services.length + endRows;
|
||||
targetRow.getCell(2).value = addobj.ref;
|
||||
targetRow.getCell(2).value = addobj.code;
|
||||
targetRow.getCell(3).value = addobj.name;
|
||||
let tmpArr = [];
|
||||
if (addobj.m0) tmpArr.push(`按上述小计的${addobj.m0.coe}计得${addobj.m0.fee}元`);
|
||||
@@ -1268,7 +1269,7 @@ async function generateTemplate(data) {
|
||||
let siSum = 0;
|
||||
for (let i = 0; i < yz01Num; i++) {
|
||||
targetRow.getCell(i * 7 + 1).value = allServices.length + 1;
|
||||
targetRow.getCell(i * 7 + 2).value = allAddtional[firstNum].ref;
|
||||
targetRow.getCell(i * 7 + 2).value = allAddtional[firstNum].code;
|
||||
targetRow.getCell(i * 7 + 3).value = allAddtional[firstNum].name;
|
||||
targetRow.getCell(i * 7 + 4).value = allAddtional[i * 4]?.fee;
|
||||
targetRow.getCell(i * 7 + 5).value = allAddtional[i * 4 + 1]?.fee;
|
||||
@@ -1288,7 +1289,7 @@ async function generateTemplate(data) {
|
||||
}
|
||||
if (yz01Mod) {
|
||||
targetRow.getCell(yz01Num * 7 + 1).value = allServices.length + 1;
|
||||
targetRow.getCell(yz01Num * 7 + 2).value = allAddtional[firstNum].ref;
|
||||
targetRow.getCell(yz01Num * 7 + 2).value = allAddtional[firstNum].code;
|
||||
targetRow.getCell(yz01Num * 7 + 3).value = allAddtional[firstNum].name;
|
||||
if (yz01Mod == 1) {
|
||||
targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(siSum, 2);
|
||||
@@ -1316,7 +1317,7 @@ async function generateTemplate(data) {
|
||||
let siSum = 0;
|
||||
for (let i = 0; i < yz01Num; i++) {
|
||||
targetRow.getCell(i * 7 + 1).value = allServices.length + endRows;
|
||||
targetRow.getCell(i * 7 + 2).value = allReserve[firstNum].ref;
|
||||
targetRow.getCell(i * 7 + 2).value = allReserve[firstNum].code;
|
||||
targetRow.getCell(i * 7 + 3).value = allReserve[firstNum].name;
|
||||
targetRow.getCell(i * 7 + 4).value = allReserve[i * 4]?.fee;
|
||||
targetRow.getCell(i * 7 + 5).value = allReserve[i * 4 + 1]?.fee;
|
||||
@@ -1336,7 +1337,7 @@ async function generateTemplate(data) {
|
||||
}
|
||||
if (yz01Mod) {
|
||||
targetRow.getCell(yz01Num * 7 + 1).value = allServices.length + endRows;
|
||||
targetRow.getCell(yz01Num * 7 + 2).value = allReserve[firstNum].ref;
|
||||
targetRow.getCell(yz01Num * 7 + 2).value = allReserve[firstNum].code;
|
||||
targetRow.getCell(yz01Num * 7 + 3).value = allReserve[firstNum].name;
|
||||
if (yz01Mod == 1) {
|
||||
targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(siSum, 2);
|
||||
|
||||
Reference in new issue
Block a user