From 59bab33e9baa2c6c38de8cee87328fff0e6a0acc Mon Sep 17 00:00:00 2001 From: wintsa <770775984@qq.com> Date: Thu, 19 Mar 2026 18:34:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=A7=E9=87=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/data.js | 2 +- src/components/ht/HtBaseInfo.vue | 17 +- src/components/ht/htCard.vue | 5 +- src/components/shared/HtFeeMethodGrid.vue | 23 +- src/components/shared/WorkContentGrid.vue | 35 ++++ .../views/HtFeeMethodTypeLineView.vue | 15 +- src/layout/tab.vue | 198 ++++++++++-------- src/layout/typeLine.vue | 23 +- src/lib/pricingMethodTotals.ts | 64 +++++- src/sql.ts | 2 +- 10 files changed, 270 insertions(+), 114 deletions(-) diff --git a/public/data.js b/public/data.js index dcdd78a..0a4557c 100644 --- a/public/data.js +++ b/public/data.js @@ -196,7 +196,7 @@ let data1 = { ], }, tasks: [{ serviceid: 0, text: ['abc', 'efg'] }, - { serviceid: 2,text: ['abc', 'efg'] } //tasks不分组的时候传单对象[{text: ['abc', 'efg']}],分组的时候传分组的serviceid + { serviceid: 2,text: ['abc', 'efg'] } //tasks不分组的时候传单对象[{text: ['abc', 'efg']}],分组的时候传分组的服务id[{ serviceid: 0, text: ['abc', 'efg'] },...] ],// 工作内容 }, ], diff --git a/src/components/ht/HtBaseInfo.vue b/src/components/ht/HtBaseInfo.vue index aa6f71d..0b6d5cb 100644 --- a/src/components/ht/HtBaseInfo.vue +++ b/src/components/ht/HtBaseInfo.vue @@ -3,13 +3,14 @@ import { onBeforeUnmount, onMounted, ref, watch } from 'vue' import { useZxFwPricingStore } from '@/pinia/zxFwPricing' - interface HtBaseInfoState { +interface HtBaseInfoState { quality: string duration: string } const DEFAULT_QUALITY = '造价咨询服务的综合评价应达到"较好"或综合评分90分' + const DEFAULT_DURATION = '' const props = defineProps<{ @@ -43,13 +44,21 @@ import { const loadForm = async () => { const data = await zxFwPricingStore.loadKeyState(storageKey()) + const hasStoredValue = Boolean( + data && + (Object.prototype.hasOwnProperty.call(data, 'quality') || Object.prototype.hasOwnProperty.call(data, 'duration')) + ) quality.value = typeof data?.quality === 'string' && data.quality ? data.quality : DEFAULT_QUALITY - duration.value = typeof data?.duration === 'string' ? data.duration : - '' + duration.value = typeof data?.duration === 'string' + ? data.duration + : (hasStoredValue ? '' : DEFAULT_DURATION) const payload: HtBaseInfoState = { quality: quality.value, duration: duration.value } lastSavedSnapshot.value = JSON.stringify(payload) + if (!hasStoredValue) { + saveForm(true) + } } watch([quality, duration], () => { saveForm() @@ -99,4 +108,4 @@ import { - \ No newline at end of file + diff --git a/src/components/ht/htCard.vue b/src/components/ht/htCard.vue index 4cccd7c..111f696 100644 --- a/src/components/ht/htCard.vue +++ b/src/components/ht/htCard.vue @@ -6,8 +6,8 @@ :subtitle="`合同段ID:${contractId}`" :meta-text="`合同段预算金额:${formatBudgetAmount(contractBudget)}`" :copy-text="contractId" - :storage-key="`project-active-cat-${contractId}`" - default-category="info" + :storage-key="typeLineStorageKey" + default-category="base-info" :categories="xmCategories" /> @@ -61,6 +61,7 @@ interface QuantityMethodStateLike { } const contractBudget = ref(null) +const typeLineStorageKey = computed(() => `project-active-cat-${props.contractId}`) let budgetRefreshTimer: ReturnType | null = null const toFiniteNumber = (value: unknown): number | null => { diff --git a/src/components/shared/HtFeeMethodGrid.vue b/src/components/shared/HtFeeMethodGrid.vue index cc1b689..e583e6f 100644 --- a/src/components/shared/HtFeeMethodGrid.vue +++ b/src/components/shared/HtFeeMethodGrid.vue @@ -356,7 +356,7 @@ const mergeWithStoredRows = (rowsFromDb: unknown): FeeMethodRow[] => { return fixedNames.value.map((item, index) => { const fromDb = byName.get(item.name) return { - id: item?.id || `fee-method-fixed-${index}`, + id: String(item?.id || `fee-method-fixed-${index}`), name:item.name, rateFee: fromDb?.rateFee ?? null, hourlyFee: fromDb?.hourlyFee ?? null, @@ -424,7 +424,6 @@ const clearRow = async (id: string) => { const editRow = (id: string) => { const row = detailRows.value.find(item => item.id === id) if (!row) return - console.log(id) tabStore.openTab({ id: `ht-fee-edit-${props.storageKey}-${id}`, title: `费用编辑-${row.name || '未命名'}`, @@ -449,16 +448,16 @@ const ActionCellRenderer = defineComponent({ } }, setup(props) { - return () => { - if (isSummaryRow(props.params.data as FeeMethodRow | undefined)) return null - const onActionClick = (action: 'edit' | 'clear') => (event: MouseEvent) => { - event.preventDefault() - event.stopPropagation() - const rowId = props.params.data?.id - if (!rowId) return - if (action === 'edit') { - props.params.context?.onActionEdit?.(rowId) - return + return () => { + if (isSummaryRow(props.params.data as FeeMethodRow | undefined)) return null + const onActionClick = (action: 'edit' | 'clear') => (event: MouseEvent) => { + event.preventDefault() + event.stopPropagation() + const rowId = String(props.params.data?.id || '').trim() + if (!rowId) return + if (action === 'edit') { + props.params.context?.onActionEdit?.(rowId) + return } void props.params.context?.onActionRequestClear?.(rowId, String(props.params.data?.name || '')) } diff --git a/src/components/shared/WorkContentGrid.vue b/src/components/shared/WorkContentGrid.vue index 7c5c365..0aff63f 100644 --- a/src/components/shared/WorkContentGrid.vue +++ b/src/components/shared/WorkContentGrid.vue @@ -34,6 +34,7 @@ interface WorkContentRow { content: string type: WorkType serviceGroup?: string + serviceid?: number | null remark: string checked: boolean custom: boolean @@ -93,6 +94,12 @@ const syncGroupedRowsRender = async () => { }, 16) } +const toServiceId = (value: unknown): number | null => { + const parsed = Number(value) + if (!Number.isSafeInteger(parsed)) return null + return parsed +} + const loadProjectIndustryId = async () => { @@ -193,6 +200,7 @@ const buildDefaultRowsFromDict = async (): Promise => { content, type: typeLabel, serviceGroup, + serviceid: toServiceId(entry.serviceid), remark: '', checked: false, custom: false, @@ -247,9 +255,27 @@ const loadFromStore = async () => { const persistedRows = state.detailRows.map(item => ({ ...item, type: item.custom ? '自定义' : (item.type || '基本工作'), + serviceid: toServiceId(item.serviceid), path: Array.isArray(item.path) && item.path.length ? item.path : ['自定义', item.content || '未命名'] })) as WorkContentRow[] + const defaultGroupServiceIdMap = new Map() + for (const row of defaultRows) { + const groupName = String(row.serviceGroup || '').trim() + const serviceid = toServiceId(row.serviceid) + if (!groupName || serviceid == null) continue + defaultGroupServiceIdMap.set(groupName, serviceid) + } + for (const row of persistedRows) { + if (row.serviceid != null) continue + const groupName = String(row.serviceGroup || '').trim() + if (!groupName) continue + const fallbackServiceId = defaultGroupServiceIdMap.get(groupName) + if (fallbackServiceId != null) { + row.serviceid = fallbackServiceId + } + } + // 按最新词典规则重建默认行,再合并历史勾选/备注,保证分组规则变更后立即生效。 if (defaultRows.length > 0) { const persistedCustomRows = persistedRows.filter(item => item.custom) @@ -462,6 +488,7 @@ const createAddTriggerRow = (groupName?: string): WorkContentRow => { content: '点击添加自定义内容', type: '自定义' as WorkType, serviceGroup: groupName || '', + serviceid: null, remark: '', checked: false, custom: false, @@ -513,11 +540,19 @@ const addCustomRow = (groupName?: string) => { const finalGroupName = isWholeProcessGroupedMode.value ? String(groupName || groupedServiceGroups.value[0] || '').trim() : '' + const finalServiceId = isWholeProcessGroupedMode.value + ? (() => { + const pureRows = getPersistableRows(rowData.value) + const hit = pureRows.find(item => String(item.serviceGroup || '').trim() === finalGroupName && item.serviceid != null) + return hit?.serviceid ?? null + })() + : null const nextRow: WorkContentRow = { id: `custom-${ts}`, content: '', type: '自定义' as WorkType, serviceGroup: finalGroupName, + serviceid: finalServiceId, remark: '', checked: false, custom: true, diff --git a/src/components/views/HtFeeMethodTypeLineView.vue b/src/components/views/HtFeeMethodTypeLineView.vue index 209bd03..3bb3dd2 100644 --- a/src/components/views/HtFeeMethodTypeLineView.vue +++ b/src/components/views/HtFeeMethodTypeLineView.vue @@ -26,19 +26,20 @@ interface TypeLineCategoryItem { const props = defineProps<{ sourceTitle?: string storageKey: string - rowId: string + rowId: string | number rowName?: string contractId?: string contractName?: string }>() const sourceTitleText = computed(() => props.sourceTitle || '费用明细') const rowNameText = computed(() => props.rowName || '未命名') +const rowIdText = computed(() => String(props.rowId || '').trim()) const contractIdText = computed(() => String(props.contractId || '').trim()) const contractNameText = computed(() => String(props.contractName || '').trim() || contractIdText.value || '-') const titleText = computed(() => `合同段:${contractNameText.value} · ${rowNameText.value || sourceTitleText.value}`) -const activeTypeStorageKey = computed(() => `ht-fee-type-active-cat-${props.storageKey}-${props.rowId}`) +const activeTypeStorageKey = computed(() => `ht-fee-type-active-cat-${props.storageKey}-${rowIdText.value}`) const buildMethodStorageKey = (method: 'rate-fee' | 'hourly-fee' | 'quantity-unit-price-fee') => - `${props.storageKey}-${props.rowId}-${method}` + `${props.storageKey}-${rowIdText.value}-${method}` const quantityUnitPricePane = markRaw( defineComponent({ @@ -50,7 +51,7 @@ const quantityUnitPricePane = markRaw( title: '数量单价', storageKey: quantityStorageKey.value, htMainStorageKey: props.storageKey, - htRowId: props.rowId, + htRowId: rowIdText.value, htMethodType: 'quantity-unit-price-fee' }) } @@ -67,7 +68,7 @@ const rateFeePane = markRaw( storageKey: rateStorageKey.value, contractId: props.contractId, htMainStorageKey: props.storageKey, - htRowId: props.rowId, + htRowId: rowIdText.value, htMethodType: 'rate-fee' }) } @@ -84,7 +85,7 @@ const hourlyFeePane = markRaw( title: '工时法明细', storageKey: hourlyStorageKey.value, htMainStorageKey: props.storageKey, - htRowId: props.rowId, + htRowId: rowIdText.value, htMethodType: 'hourly-fee' }) } @@ -109,7 +110,7 @@ const workContentPane = markRaw( }) return () => h(AsyncWorkContentGrid, { title: '工作内容', - storageKey: `work-content-${props.storageKey}-${props.rowId}`, + storageKey: `work-content-${props.storageKey}-${rowIdText.value}`, dictMode: 'additional' }) } diff --git a/src/layout/tab.vue b/src/layout/tab.vue index 13e4e76..93d4439 100644 --- a/src/layout/tab.vue +++ b/src/layout/tab.vue @@ -29,6 +29,7 @@ import { import { decodeZwArchive, encodeZwArchive, ZW_FILE_EXTENSION } from '@/lib/zwArchive' import { PROJECT_TAB_ID, QUICK_TAB_ID, readWorkspaceMode, writeWorkspaceMode } from '@/lib/workspace' import { addNumbers, roundTo } from '@/lib/decimal' +import { getBenchmarkBudgetSplitByScale, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee' import { exportFile, serviceList } from '@/sql' interface DataEntry { @@ -110,6 +111,7 @@ interface WorkContentRowLike { checked?: unknown custom?: unknown serviceGroup?: unknown + serviceid?: unknown isAddTrigger?: unknown } @@ -544,14 +546,11 @@ const finishReportExportProgress = (success: boolean, text: string, blobUrl?: st reportExportStatus.value = success ? 'success' : 'error' reportExportProgress.value = 100 reportExportText.value = text - console.log(blobUrl) reportExportBlobUrl.value = success && blobUrl ? blobUrl : null reportExportToastOpen.value = true - if (!success || !blobUrl) { - reportExportToastTimer = setTimeout(() => { - reportExportToastOpen.value = false - }, success ? 1200 : 1800) - } + reportExportToastTimer = setTimeout(() => { + reportExportToastOpen.value = false + }, success ? 2000 : 1800) } const openExportedReport = () => { @@ -1037,6 +1036,7 @@ const sumNumbers = (values: Array): number => (sum, value) => sum + (typeof value === 'number' && Number.isFinite(value) ? value : 0), 0 ) +const toMoney = (value: unknown): number => roundTo(toFiniteNumber(value) ?? 0, 2) const isNonEmptyString = (value: unknown): value is string => typeof value === 'string' && value.trim().length > 0 @@ -1100,10 +1100,42 @@ const toScaleProNum = (row: ScaleMethodRowLike): number => { } const normalizeTaskText = (value: unknown): string => String(value || '').trim() +const resolveTaskRowServiceId = (row: WorkContentRowLike): number | null => + toSafeInteger((row as { serviceid?: unknown })?.serviceid) +const resolveScaleMethodFee = (row: ScaleMethodRowLike, mode: 'cost' | 'area') => { + const scaleValue = mode === 'cost' ? toFiniteNumber(row.amount) : toFiniteNumber(row.landArea) + const benchmarkSplit = getBenchmarkBudgetSplitByScale(scaleValue, mode) + const computedSplit = benchmarkSplit + ? getScaleBudgetFeeSplit({ + benchmarkBudgetBasic: benchmarkSplit.basic, + benchmarkBudgetOptional: benchmarkSplit.optional, + majorFactor: row.majorFactor, + consultCategoryFactor: row.consultCategoryFactor, + workStageFactor: row.workStageFactor, + workRatio: row.workRatio + }) + : null + const basicFee = toFiniteNumber(row.budgetFee) ?? computedSplit?.total ?? null + const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic) ?? computedSplit?.basic ?? null + const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional) ?? computedSplit?.optional ?? null + const basicFormula = typeof row.basicFormula === 'string' && row.basicFormula.trim() + ? row.basicFormula + : (benchmarkSplit?.basicFormula ?? '') + const optionalFormula = typeof row.optionalFormula === 'string' && row.optionalFormula.trim() + ? row.optionalFormula + : (benchmarkSplit?.optionalFormula ?? '') + return { + basicFee, + basicFeeBasic, + basicFeeOptional, + basicFormula, + optionalFormula + } +} const groupWorkContentTasks = ( rows: WorkContentRowLike[] | undefined, - options?: { forceUngroup?: boolean; serviceLabelToId?: Map } + options?: { forceUngroup?: boolean } ): ExportTaskGroup[] => { const source = Array.isArray(rows) ? rows : [] const selected = source.filter(item => { @@ -1114,7 +1146,7 @@ const groupWorkContentTasks = ( }) if (selected.length === 0) return [] - const hasGroup = !options?.forceUngroup && selected.some(item => normalizeTaskText(item?.serviceGroup).length > 0) + const hasGroup = !options?.forceUngroup && selected.some(item => resolveTaskRowServiceId(item) != null) if (!hasGroup) { const text = selected .map(item => normalizeTaskText(item?.content)) @@ -1122,31 +1154,35 @@ const groupWorkContentTasks = ( return text.length > 0 ? [{ text }] : [] } - const grouped = new Map() - const orderedGroupKeys: string[] = [] + const grouped = new Map() + const orderedServiceIds: number[] = [] + const ungroupedText: string[] = [] for (const item of selected) { - const groupName = normalizeTaskText(item?.serviceGroup) - const key = groupName || '__ungrouped__' - if (!grouped.has(key)) { - grouped.set(key, []) - orderedGroupKeys.push(key) - } const content = normalizeTaskText(item?.content) if (!content) continue - grouped.get(key)?.push(content) + const serviceid = resolveTaskRowServiceId(item) + if (serviceid == null) { + ungroupedText.push(content) + continue + } + if (!grouped.has(serviceid)) { + grouped.set(serviceid, []) + orderedServiceIds.push(serviceid) + } + grouped.get(serviceid)?.push(content) } - const byLabel = options?.serviceLabelToId || new Map() - return orderedGroupKeys - .map(groupName => { - const text = grouped.get(groupName) || [] - if (text.length === 0) return null - const entry: ExportTaskGroup = { text } - const resolvedServiceId = byLabel.get(groupName) - if (resolvedServiceId != null) entry.serviceid = resolvedServiceId - return entry - }) - .filter((item): item is ExportTaskGroup => Boolean(item)) + const groupedTasks: ExportTaskGroup[] = [] + for (const serviceid of orderedServiceIds) { + const text = grouped.get(serviceid) || [] + if (text.length === 0) continue + groupedTasks.push({ serviceid, text }) + } + + if (ungroupedText.length > 0) { + groupedTasks.push({ text: ungroupedText }) + } + return groupedTasks } const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined): ExportServiceCoe[] => { @@ -1205,14 +1241,15 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n const det = rows .map(row => { const major = toScaleMajorId(row) - if (major == null || row.budgetFee == null) return null + if (major == null) return null const proNum = toScaleProNum(row) proSet.add(proNum) const cost = toFiniteNumber(row.amount) - const basicFee = toFiniteNumber(row.budgetFee) + const feeResolved = resolveScaleMethodFee(row, 'cost') + const basicFee = feeResolved.basicFee if (basicFee != null) hasTotalValue = true - const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic) - const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional) + const basicFeeBasic = feeResolved.basicFeeBasic + const basicFeeOptional = feeResolved.basicFeeOptional const remark = typeof row.remark === 'string' ? row.remark : '' const hasValue = cost != null || @@ -1225,16 +1262,16 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n proNum, major, cost: cost ?? 0, - basicFee: basicFee ?? 0, - basicFormula: typeof row.basicFormula === 'string' ? row.basicFormula : '', - basicFee_basic: basicFeeBasic ?? 0, - optionalFormula: typeof row.optionalFormula === 'string' ? row.optionalFormula : '', - basicFee_optional: basicFeeOptional ?? 0, + basicFee: toMoney(basicFee), + basicFormula: feeResolved.basicFormula, + basicFee_basic: toMoney(basicFeeBasic), + optionalFormula: feeResolved.optionalFormula, + basicFee_optional: toMoney(basicFeeOptional), serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor), majorCoe: toFiniteNumberOrZero(row.majorFactor), processCoe: toFiniteNumber(row.workStageFactor) ?? 1, proportion: toFiniteNumber(row.workRatio) ?? 1, - fee: basicFee ?? 0, + fee: toMoney(basicFee), remark } }) @@ -1244,10 +1281,10 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n return { proAmount: proSet.size > 0 ? proSet.size : 1, cost: sumNumbers(det.map(item => item.cost)), - basicFee: sumNumbers(det.map(item => item.basicFee)), - basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)), - basicFee_optional: sumNumbers(det.map(item => item.basicFee_optional)), - fee: sumNumbers(det.map(item => item.fee)), + basicFee: toMoney(sumNumbers(det.map(item => item.basicFee))), + basicFee_basic: toMoney(sumNumbers(det.map(item => item.basicFee_basic))), + basicFee_optional: toMoney(sumNumbers(det.map(item => item.basicFee_optional))), + fee: toMoney(sumNumbers(det.map(item => item.fee))), det } } @@ -1259,14 +1296,15 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n const det = rows .map(row => { const major = toScaleMajorId(row) - if (major == null || row.budgetFee == null) return null + if (major == null) return null const proNum = toScaleProNum(row) proSet.add(proNum) const area = toFiniteNumber(row.landArea) - const basicFee = toFiniteNumber(row.budgetFee) + const feeResolved = resolveScaleMethodFee(row, 'area') + const basicFee = feeResolved.basicFee if (basicFee != null) hasTotalValue = true - const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic) - const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional) + const basicFeeBasic = feeResolved.basicFeeBasic + const basicFeeOptional = feeResolved.basicFeeOptional const remark = typeof row.remark === 'string' ? row.remark : '' const hasValue = area != null || @@ -1279,16 +1317,16 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n proNum, major, area: area ?? 0, - basicFee: basicFee ?? 0, - basicFormula: typeof row.basicFormula === 'string' ? row.basicFormula : '', - basicFee_basic: basicFeeBasic ?? 0, - optionalFormula: typeof row.optionalFormula === 'string' ? row.optionalFormula : '', - basicFee_optional: basicFeeOptional ?? 0, + basicFee: toMoney(basicFee), + basicFormula: feeResolved.basicFormula, + basicFee_basic: toMoney(basicFeeBasic), + optionalFormula: feeResolved.optionalFormula, + basicFee_optional: toMoney(basicFeeOptional), serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor), majorCoe: toFiniteNumberOrZero(row.majorFactor), processCoe: toFiniteNumber(row.workStageFactor) ?? 1, proportion: toFiniteNumber(row.workRatio) ?? 1, - fee: basicFee ?? 0, + fee: toMoney(basicFee), remark } }) @@ -1298,10 +1336,10 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n return { proAmount: proSet.size > 0 ? proSet.size : 1, area: sumNumbers(det.map(item => item.area)), - basicFee: sumNumbers(det.map(item => item.basicFee)), - basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)), - basicFee_optional: sumNumbers(det.map(item => item.basicFee_optional)), - fee: sumNumbers(det.map(item => item.fee)), + basicFee: toMoney(sumNumbers(det.map(item => item.basicFee))), + basicFee_basic: toMoney(sumNumbers(det.map(item => item.basicFee_basic))), + basicFee_optional: toMoney(sumNumbers(det.map(item => item.basicFee_optional))), + fee: toMoney(sumNumbers(det.map(item => item.fee))), det } } @@ -1480,27 +1518,12 @@ const loadHtFeeMethodsByRow = async (mainStorageKey: string, rowId: string) => { } } -const buildServiceGroupLabelToIdMap = (serviceIds: string[]): Map => { - const map = new Map() - for (const serviceId of serviceIds) { - const item = (serviceList as Record)[serviceId] - if (!item) continue - const id = toSafeInteger(serviceId) - if (id == null) continue - const label = `${String(item.code || '').trim()} ${String(item.name || '').trim()}`.trim() - if (!label) continue - map.set(label, id) - } - return map -} - const buildServiceTasks = async ( contractId: string, - serviceId: string, - serviceLabelToId: Map + serviceId: string ): Promise => { const taskState = await zxFwPricingStore.loadKeyState(`work-content-${contractId}-${serviceId}`) - return groupWorkContentTasks(taskState?.detailRows, { serviceLabelToId }) + return groupWorkContentTasks(taskState?.detailRows) } const buildAdditionalRowTasks = async (contractId: string, rowId: string): Promise => { @@ -1631,7 +1654,7 @@ const buildExportReportPayload = async (): Promise => { kvStore.getItem(`zxFW-${contractId}`), kvStore.getItem>(`ht-consult-category-factor-v1-${contractId}`), kvStore.getItem>(`ht-major-factor-v1-${contractId}`), - kvStore.getItem(`ht-base-info-${contractId}`) + zxFwPricingStore.loadKeyState(`ht-base-info-${contractId}`) ]) const contractState = zxFwPricingStore.getContractState(contractId) @@ -1673,8 +1696,6 @@ const buildExportReportPayload = async (): Promise => { const serviceIdTexts = sortServiceIdsByDict( (selectedIds.length > 0 ? selectedIds : fallbackServiceIds).filter(hasServiceId) ) - const serviceLabelToId = buildServiceGroupLabelToIdMap(serviceIdTexts) - const services = ( await Promise.all( serviceIdTexts.map(async serviceIdText => { @@ -1699,7 +1720,7 @@ const buildExportReportPayload = async (): Promise => { const method4 = buildMethod4(method4Raw?.detailRows) const fee = buildServiceFee(sourceRow, method1, method2, method3, method4) const finalFee = buildServiceFinalFee(sourceRow, method1, method2, method3, method4) - const tasks = await buildServiceTasks(contractId, serviceIdText, serviceLabelToId) + const tasks = await buildServiceTasks(contractId, serviceIdText) const process = Number(sourceRow?.process) === 1 ? 1 : 0 const service: ExportService = { id: serviceId, @@ -1717,6 +1738,8 @@ const buildExportReportPayload = async (): Promise => { ) ).filter((item): item is ExportService => Boolean(item)) + const fixedFinalFee = toFiniteNumber(fixedRow?.finalFee) + const serviceFinalFeeSum = sumNumbers(services.map(item => item.finalFee)) const fixedSubtotal = toFiniteNumber(fixedRow?.subtotal) const serviceFeeSum = sumNumbers(services.map(item => item.fee)) const fixedMethodSum = sumNumbers([ @@ -1725,7 +1748,9 @@ const buildExportReportPayload = async (): Promise => { toFiniteNumber(fixedRow?.workload), toFiniteNumber(fixedRow?.hourly) ]) - const serviceFee = fixedSubtotal ?? (serviceFeeSum !== 0 ? serviceFeeSum : fixedMethodSum) + const serviceFee = + fixedFinalFee ?? + (services.length > 0 ? serviceFinalFeeSum : (fixedSubtotal ?? (serviceFeeSum !== 0 ? serviceFeeSum : fixedMethodSum))) const [addtional, reserve] = await Promise.all([ buildAdditionalExport(contractId), buildReserveExport(contractId) @@ -1741,7 +1766,6 @@ const buildExportReportPayload = async (): Promise => { const contractServiceCoesRaw = buildProjectServiceCoes(htConsultCategoryFactorRaw?.detailRows) const contractMajorCoesRaw = buildProjectMajorCoes(htMajorFactorRaw?.detailRows) - contracts.push({ name: isNonEmptyString(contract.name) ? contract.name : `合同段-${index + 1}`, serviceFee, @@ -2291,9 +2315,19 @@ watch( class="pointer-events-auto rounded-xl border border-border bg-card px-4 py-3 text-foreground shadow-lg" @update:open="(val) => { if (!val) dismissReportToast() }" > - - {{ reportExportStatus === 'running' ? '导出报表' : (reportExportStatus === 'success' ? '导出成功' : '导出失败') }} - +
+ + {{ reportExportStatus === 'running' ? '导出报表' : (reportExportStatus === 'success' ? '导出成功' : '导出失败') }} + + +
{{ reportExportText }}