diff --git a/src/components/common/XmFactorGrid.vue b/src/components/common/XmFactorGrid.vue index 04dce77..525b97b 100644 --- a/src/components/common/XmFactorGrid.vue +++ b/src/components/common/XmFactorGrid.vue @@ -35,6 +35,7 @@ type DictSource = Record const props = defineProps<{ title: string storageKey: string + parentStorageKey?: string dict: DictSource disableBudgetEditWhenStandardNull?: boolean excludeNotshowByZxflxs?: boolean @@ -219,13 +220,31 @@ const saveToIndexedDB = async () => { } } +const loadGridState = async (storageKey: string): Promise => { + if (!storageKey) return null + const data = await localforage.getItem(storageKey) + if (!data?.detailRows || !Array.isArray(data.detailRows)) return null + return data +} + const loadFromIndexedDB = async () => { try { - const data = await localforage.getItem(props.storageKey) - if (data?.detailRows) { + const data = await loadGridState(props.storageKey) + if (data) { detailRows.value = mergeWithDictRows(data.detailRows) return } + + const parentStorageKey = props.parentStorageKey?.trim() + if (parentStorageKey) { + const parentData = await loadGridState(parentStorageKey) + if (parentData) { + detailRows.value = mergeWithDictRows(parentData.detailRows) + await saveToIndexedDB() + return + } + } + detailRows.value = buildDefaultRows() } catch (error) { console.error('loadFromIndexedDB failed:', error) diff --git a/src/components/common/xmCommonAgGrid.vue b/src/components/common/xmCommonAgGrid.vue index 3344936..8f1ffc4 100644 --- a/src/components/common/xmCommonAgGrid.vue +++ b/src/components/common/xmCommonAgGrid.vue @@ -28,18 +28,7 @@ interface DictGroup { children: DictLeaf[] } -interface DetailRow { - id: string - groupCode: string - groupName: string - majorCode: string - majorName: string - hasCost: boolean - hasArea: boolean - amount: number | null - landArea: number | null - path: string[] -} + interface XmScaleState { detailRows?: DetailRow[] @@ -151,60 +140,75 @@ const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] => } -const loadFromIndexedDB = async (gridApi: any) => { - +const applyPinnedTotalAmount = ( + api: GridApi | null | undefined, + totalAmount: number | null | undefined +) => { + const normalized = typeof totalAmount === 'number' && Number.isFinite(totalAmount) + ? roundTo(totalAmount, 2) + : null + pinnedTopRowData.value[0].amount = normalized + const pinnedTopNode = api?.getPinnedTopRow(0) + if (pinnedTopNode) { + pinnedTopNode.setDataValue('amount', normalized) + } +} +const loadFromIndexedDB = async (api: GridApi) => { try { + const [baseInfo, contractData] = await Promise.all([ + localforage.getItem(BASE_INFO_KEY), + localforage.getItem(props.dbKey) + ]) - const baseInfo = await localforage.getItem(BASE_INFO_KEY) activeIndustryId.value = typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : '' + if (!activeIndustryId.value) { detailDict.value = [] - return - } - const filteredEntries = majorEntries.filter(([id]) => isMajorIdInIndustryScope(id, activeIndustryId.value)) - detailDict.value = buildDetailDict(filteredEntries) - if (!activeIndustryId.value) { detailRows.value = [] + roughCalcEnabled.value = false + applyPinnedTotalAmount(api, null) return } - const data = await localforage.getItem(props.dbKey) + const filteredEntries = majorEntries.filter(([id]) => + isMajorIdInIndustryScope(id, activeIndustryId.value) + ) + detailDict.value = buildDetailDict(filteredEntries) - roughCalcEnabled.value = data?.roughCalcEnabled || false - const pinnedTopNode = gridApi.getPinnedTopRow(0) - if (pinnedTopNode) { - pinnedTopNode.setDataValue('amount', data?.totalAmount || null) - } - // - if (data?.detailRows) { - detailRows.value = mergeWithDictRows(data.detailRows) + roughCalcEnabled.value = Boolean(contractData?.roughCalcEnabled) + applyPinnedTotalAmount(api, contractData?.totalAmount) + if (contractData?.detailRows) { + detailRows.value = mergeWithDictRows(contractData.detailRows) return } if (props.xmInfoKey) { // 首次创建合同段时,默认继承项目规模信息(同一套专业字典,按 id 对齐) - const xmData = - (await localforage.getItem(props.xmInfoKey)) - roughCalcEnabled.value = xmData?.roughCalcEnabled || false - if (pinnedTopNode) { - pinnedTopNode.setDataValue('amount', xmData?.totalAmount || null) - } + const xmData = await localforage.getItem(props.xmInfoKey) + roughCalcEnabled.value = Boolean(xmData?.roughCalcEnabled) + applyPinnedTotalAmount(api, xmData?.totalAmount) + if (xmData?.detailRows) { - detailRows.value = mergeWithDictRows(xmData.detailRows) + + detailRows.value = mergeWithDictRows(xmData.detailRows.map(e => ({...e, + amount: null, + landArea: null + }))) return } - } - detailRows.value = buildDefaultRows() -saveToIndexedDB() + + void saveToIndexedDB() } catch (error) { console.error('loadFromIndexedDB failed:', error) activeIndustryId.value = '' detailRows.value = [] + roughCalcEnabled.value = false + applyPinnedTotalAmount(api, null) } } @@ -406,17 +410,19 @@ const setDetailRowsHidden = (hidden: boolean) => { } } +let oldValue:number|null const onRoughCalcSwitch = (checked: boolean) => { gridApi.value?.stopEditing(true) roughCalcEnabled.value = checked setDetailRowsHidden(checked) if (!checked) { + oldValue=pinnedTopRowData.value[0].amount syncPinnedTotalForNormalMode() } else { - pinnedTopRowData.value[0].amount = null + pinnedTopRowData.value[0].amount = oldValue const pinnedTopNode = gridApi.value?.getPinnedTopRow(0) if (pinnedTopNode) { - pinnedTopNode.setDataValue('amount', null) + pinnedTopNode.setDataValue('amount', oldValue) } } schedulePersist() diff --git a/src/components/views/Ht.vue b/src/components/views/Ht.vue index 1f3f7c0..7cd162a 100644 --- a/src/components/views/Ht.vue +++ b/src/components/views/Ht.vue @@ -55,6 +55,8 @@ const CONTRACT_SEGMENT_FILE_EXTENSION = '.htzw' const CONTRACT_SEGMENT_VERSION = 1 const CONTRACT_KEY_PREFIX = 'ht-info-v3-' const SERVICE_KEY_PREFIX = 'zxFW-' +const CONTRACT_CONSULT_FACTOR_KEY_PREFIX = 'ht-consult-category-factor-v1-' +const CONTRACT_MAJOR_FACTOR_KEY_PREFIX = 'ht-major-factor-v1-' const PRICING_KEY_PREFIXES = ['tzGMF-', 'ydGMF-', 'gzlF-', 'hourlyPricing-'] const PROJECT_INFO_KEY = 'xm-base-info-v1' @@ -383,6 +385,8 @@ const isContractSegmentPackage = (value: unknown): value is ContractSegmentPacka const isContractRelatedForageKey = (key: string, contractId: string) => { if (key === `${CONTRACT_KEY_PREFIX}${contractId}`) return true if (key === `${SERVICE_KEY_PREFIX}${contractId}`) return true + if (key === `${CONTRACT_CONSULT_FACTOR_KEY_PREFIX}${contractId}`) return true + if (key === `${CONTRACT_MAJOR_FACTOR_KEY_PREFIX}${contractId}`) return true if (PRICING_KEY_PREFIXES.some(prefix => key.startsWith(`${prefix}${contractId}-`))) return true return false } @@ -407,6 +411,12 @@ const readContractRelatedForageEntries = async (contractIds: string[]) => { const rewriteKeyWithContractId = (key: string, fromId: string, toId: string) => { if (key === `${CONTRACT_KEY_PREFIX}${fromId}`) return `${CONTRACT_KEY_PREFIX}${toId}` if (key === `${SERVICE_KEY_PREFIX}${fromId}`) return `${SERVICE_KEY_PREFIX}${toId}` + if (key === `${CONTRACT_CONSULT_FACTOR_KEY_PREFIX}${fromId}`) { + return `${CONTRACT_CONSULT_FACTOR_KEY_PREFIX}${toId}` + } + if (key === `${CONTRACT_MAJOR_FACTOR_KEY_PREFIX}${fromId}`) { + return `${CONTRACT_MAJOR_FACTOR_KEY_PREFIX}${toId}` + } for (const prefix of PRICING_KEY_PREFIXES) { if (key.startsWith(`${prefix}${fromId}-`)) { return key.replace(`${prefix}${fromId}-`, `${prefix}${toId}-`) diff --git a/src/components/views/HtConsultCategoryFactor.vue b/src/components/views/HtConsultCategoryFactor.vue new file mode 100644 index 0000000..0a3bbd3 --- /dev/null +++ b/src/components/views/HtConsultCategoryFactor.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/views/HtMajorFactor.vue b/src/components/views/HtMajorFactor.vue new file mode 100644 index 0000000..26bfbb4 --- /dev/null +++ b/src/components/views/HtMajorFactor.vue @@ -0,0 +1,64 @@ + + + diff --git a/src/components/views/ServiceCheckboxSelector.vue b/src/components/views/ServiceCheckboxSelector.vue index 4caa947..4541a33 100644 --- a/src/components/views/ServiceCheckboxSelector.vue +++ b/src/components/views/ServiceCheckboxSelector.vue @@ -36,7 +36,7 @@ const clearAll = () => {