This commit is contained in:
zwgczx committed 2026-03-09 10:55:08 +08:00
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>
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);