-
-
diff --git a/src/components/shared/HtFeeMethodGrid.vue b/src/components/shared/HtFeeMethodGrid.vue
index 7e49840..051bf77 100644
--- a/src/components/shared/HtFeeMethodGrid.vue
+++ b/src/components/shared/HtFeeMethodGrid.vue
@@ -624,27 +624,19 @@ onActivated(() => {
})
const storageKeyRef = computed(() => props.storageKey)
-const reserveAdditionalWorkKeyVersion = computed(() => {
- if (!isReserveStorageKey(props.storageKey)) return 0
+const reserveAdditionalWorkStateSignature = computed(() => {
+ if (!isReserveStorageKey(props.storageKey)) return ''
const contractId = String(props.contractId || '').trim()
- if (!contractId) return 0
+ if (!contractId) return ''
const additionalStorageKey = `htExtraFee-${contractId}-additional-work`
- return zxFwPricingStore.getKeyVersion(additionalStorageKey)
+ return JSON.stringify(zxFwPricingStore.htFeeMainStates[additionalStorageKey] || null)
})
watch(storageKeyRef, () => {
scheduleReloadFromStorage()
})
watch(
- () =>
- detailRows.value
- .map(row => {
- const rateKey = zxFwPricingStore.getHtFeeMethodStorageKey(props.storageKey, row.id, 'rate-fee')
- const hourlyKey = zxFwPricingStore.getHtFeeMethodStorageKey(props.storageKey, row.id, 'hourly-fee')
- const quantityKey = zxFwPricingStore.getHtFeeMethodStorageKey(props.storageKey, row.id, 'quantity-unit-price-fee')
- return `${row.id}:${zxFwPricingStore.getKeyVersion(rateKey)}:${zxFwPricingStore.getKeyVersion(hourlyKey)}:${zxFwPricingStore.getKeyVersion(quantityKey)}`
- })
- .join('|'),
+ () => JSON.stringify(zxFwPricingStore.htFeeMethodStates[props.storageKey] || null),
(nextSignature, prevSignature) => {
if (!nextSignature && !prevSignature) return
if (nextSignature === prevSignature) return
@@ -655,19 +647,19 @@ watch(
watch(
() => {
const contractId = String(props.contractId || '').trim()
- if (!contractId) return 0
- return zxFwPricingStore.contractVersions[contractId] || 0
+ if (!contractId) return ''
+ return JSON.stringify(zxFwPricingStore.contracts[contractId] || null)
},
- (nextVersion, prevVersion) => {
- if (nextVersion === prevVersion || nextVersion === 0) return
+ (nextSig, prevSig) => {
+ if (nextSig === prevSig) return
scheduleReloadFromStorage()
}
)
watch(
- reserveAdditionalWorkKeyVersion,
- (nextVersion, prevVersion) => {
- if (nextVersion === prevVersion) return
+ reserveAdditionalWorkStateSignature,
+ (nextSig, prevSig) => {
+ if (nextSig === prevSig) return
scheduleReloadFromStorage()
}
)
diff --git a/src/pinia/zxFwPricing.ts b/src/pinia/zxFwPricing.ts
index d071aa4..8772e9e 100644
--- a/src/pinia/zxFwPricing.ts
+++ b/src/pinia/zxFwPricing.ts
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import { ref } from 'vue'
import { addNumbers } from '@/lib/decimal'
import { toFiniteNumberOrNull } from '@/lib/number'
-import { useKvStore } from '@/pinia/kv'
+import { waitForHydration } from '@/pinia/Plugin/indexdb'
import {
parseHtFeeMainStorageKey,
parseHtFeeMethodStorageKey,
@@ -66,7 +66,6 @@ const STORAGE_PREFIX_METHOD_MAP = new Map(
const toKey = (contractId: string | number) => String(contractId || '').trim()
const toServiceKey = (serviceId: string | number) => String(serviceId || '').trim()
-const dbKeyOf = (contractId: string) => `zxFW-${contractId}`
const serviceMethodDbKeyOf = (contractId: string, serviceId: string, method: ServicePricingMethod) =>
`${METHOD_STORAGE_PREFIX_MAP[method]}-${contractId}-${serviceId}`
const round3 = (value: number) => Number(value.toFixed(3))
@@ -139,7 +138,7 @@ const applyRowSubtotals = (rows: ZxFwDetailRow[]): ZxFwDetailRow[] => {
return {
...row,
subtotal: round3Nullable(subtotal),
- finalFee: round3Nullable(subtotal)
+ finalFee: row.finalFee != null ? round3Nullable(row.finalFee) : round3Nullable(subtotal)
}
})
}
@@ -238,8 +237,9 @@ const parseServiceMethodStorageKey = (keyRaw: string | number) => {
const loadTasks = new Map>()
export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
+ let hydrationReady = false
+ let hydrationTask: Promise | null = null
const contracts = ref>({})
- const contractVersions = ref>({})
const contractLoaded = ref>({})
const servicePricingStates = ref>>({})
@@ -249,18 +249,18 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
const htFeeMainStates = htFeeStore.htFeeMainStates
const htFeeMethodStates = htFeeStore.htFeeMethodStates
const keyedStates = keysStore.keyedStates
- const keyVersions = keysStore.keyVersions
- const touchVersion = (contractId: string) => {
- contractVersions.value[contractId] = (contractVersions.value[contractId] || 0) + 1
- }
-
- const getKvStoreSafely = () => {
- try {
- return useKvStore()
- } catch {
- return null
+ const ensureHydrated = async () => {
+ if (hydrationReady) return
+ if (!hydrationTask) {
+ hydrationTask = waitForHydration('zxFwPricing')
+ .catch(() => undefined)
+ .finally(() => {
+ hydrationReady = true
+ hydrationTask = null
+ })
}
+ await hydrationTask
}
const ensureServicePricingState = (contractIdRaw: string | number, serviceIdRaw: string | number) => {
@@ -552,8 +552,6 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
return keysStore.removeKeyState(key)
}
- const getKeyVersion = (keyRaw: string | number) => keysStore.getKeyVersion(keyRaw)
-
// 对外返回合同咨询服务状态的深拷贝,避免组件直接改写 store 内部引用。
const getContractState = (contractIdRaw: string | number) => {
const contractId = toKey(contractIdRaw)
@@ -567,25 +565,15 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
const loadContract = async (contractIdRaw: string | number, force = false) => {
const contractId = toKey(contractIdRaw)
if (!contractId) return null
+ await ensureHydrated()
if (!force && contractLoaded.value[contractId]) return getContractState(contractId)
if (!force && contracts.value[contractId]) return getContractState(contractId)
if (!force && loadTasks.has(contractId)) return loadTasks.get(contractId) as Promise
const task = (async () => {
- const kvStore = getKvStoreSafely()
- const raw = kvStore
- ? await kvStore.getItem(dbKeyOf(contractId))
- : null
const current = contracts.value[contractId]
- if (raw) {
- const normalized = normalizeState(raw)
- if (!current || !isSameState(current, normalized)) {
- contracts.value[contractId] = normalized
- touchVersion(contractId)
- }
- } else if (!current) {
+ if (!current) {
contracts.value[contractId] = normalizeState(null)
- touchVersion(contractId)
}
contractLoaded.value[contractId] = true
return getContractState(contractId)
@@ -609,12 +597,13 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
if (current && isSameState(current, normalized)) return false
contracts.value[contractId] = normalized
contractLoaded.value[contractId] = true
- touchVersion(contractId)
return true
}
// 只更新某个服务行上的单个汇总字段,适合计费页回写 investScale/landScale/workload/hourly。
- // 这里不会额外重算其它派生字段,调用方需要自行决定是否联动更新 subtotal/finalFee。
+ // 为保证“计价法金额变化 -> 确认金额跟随小计”,这里会同步重算 finalFee:
+ // - 普通行:finalFee = 当前四种计价法小计
+ // - 固定小计行:finalFee = 普通行 finalFee 合计
const updatePricingField = async (params: {
contractId: string
serviceId: string | number
@@ -632,7 +621,7 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
const nextValue = toFiniteNumberOrNull(params.value)
let changed = false
- const nextRows = current.detailRows.map(row => {
+ const updatedRows = current.detailRows.map(row => {
if (String(row.id || '') !== targetServiceId) return row
const oldValue = toFiniteNumberOrNull(row[params.field])
if (oldValue === nextValue) return row
@@ -645,6 +634,37 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
if (!changed) return false
+ const rowsWithSyncedFinalFee = updatedRows.map(row => {
+ if (String(row.id || '') === FIXED_ROW_ID) return row
+ const rowSubtotal = sumNullableNumbers([
+ toFiniteNumberOrNull(row.investScale),
+ toFiniteNumberOrNull(row.landScale),
+ toFiniteNumberOrNull(row.workload),
+ toFiniteNumberOrNull(row.hourly)
+ ])
+ return {
+ ...row,
+ finalFee: round3Nullable(rowSubtotal)
+ }
+ })
+
+ const fixedFinalFee = round3Nullable(
+ sumNullableNumbers(
+ rowsWithSyncedFinalFee
+ .filter(row => String(row.id || '') !== FIXED_ROW_ID)
+ .map(row => toFiniteNumberOrNull(row.finalFee))
+ )
+ )
+
+ const nextRows = rowsWithSyncedFinalFee.map(row =>
+ String(row.id || '') === FIXED_ROW_ID
+ ? {
+ ...row,
+ finalFee: fixedFinalFee
+ }
+ : row
+ )
+
const nextState = normalizeState({
...current,
detailRows: nextRows
@@ -652,7 +672,6 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
if (isSameState(current, nextState)) return false
contracts.value[contractId] = nextState
contractLoaded.value[contractId] = true
- touchVersion(contractId)
return true
}
@@ -693,10 +712,6 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
delete contractLoaded.value[contractId]
changed = true
}
- if (Object.prototype.hasOwnProperty.call(contractVersions.value, contractId)) {
- delete contractVersions.value[contractId]
- changed = true
- }
loadTasks.delete(contractId)
changed = htFeeStore.removeContractHtFeeData(contractId) || changed
@@ -708,20 +723,16 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
changed = keysStore.removeKeysByPrefix(`${prefix}-${contractId}-`) || changed
}
- changed = keysStore.removeKeyState(dbKeyOf(contractId)) || changed
-
return changed
}
return {
contracts,
- contractVersions,
contractLoaded,
servicePricingStates,
htFeeMainStates,
htFeeMethodStates,
keyedStates,
- keyVersions,
getContractState,
loadContract,
setContractState,
@@ -732,7 +743,6 @@ export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
loadKeyState,
setKeyState,
removeKeyState,
- getKeyVersion,
getServicePricingMethodState,
setServicePricingMethodState,
loadServicePricingMethodState,
diff --git a/src/pinia/zxFwPricingKeys.ts b/src/pinia/zxFwPricingKeys.ts
index 94b023e..b5f2db9 100644
--- a/src/pinia/zxFwPricingKeys.ts
+++ b/src/pinia/zxFwPricingKeys.ts
@@ -2,7 +2,7 @@
* 通用键值状态管理 Store
*
* 从 zxFwPricing 拆出,管理 IndexedDB 中的通用键值对状态,
- * 包括版本追踪和快照比对,用于避免不必要的写入。
+ * 通过快照比对避免不必要的写入。
*/
import { defineStore } from 'pinia'
@@ -31,22 +31,9 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
const keyedStates = ref>({})
/** 键是否已从 IndexedDB 加载 */
const keyedLoaded = ref>({})
- /** 键版本号(每次变更递增) */
- const keyVersions = ref>({})
/** 键快照(用于比对是否变更) */
const keySnapshots = ref>({})
- const touchKeyVersion = (key: string) => {
- keyVersions.value[key] = (keyVersions.value[key] || 0) + 1
- }
-
- /** 获取指定键的版本号 */
- const getKeyVersion = (keyRaw: string | number): number => {
- const key = toKey(keyRaw)
- if (!key) return 0
- return keyVersions.value[key] || 0
- }
-
/** 按通用键获取状态 */
const getKeyState = (keyRaw: string | number): T | null => {
const key = toKey(keyRaw)
@@ -80,7 +67,6 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
if (prevSnapshot !== nextSnapshot || !Object.prototype.hasOwnProperty.call(keyedStates.value, key)) {
keyedStates.value[key] = cloneAny(raw)
keySnapshots.value[key] = nextSnapshot
- touchKeyVersion(key)
}
return getKeyState(key)
})()
@@ -108,7 +94,6 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
if (!force && prevSnapshot === nextSnapshot) return false
keyedStates.value[key] = cloneAny(value)
keySnapshots.value[key] = nextSnapshot
- touchKeyVersion(key)
return true
}
@@ -120,7 +105,6 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
delete keyedStates.value[key]
keyedLoaded.value[key] = true
keySnapshots.value[key] = toKeySnapshot(null)
- touchKeyVersion(key)
return hadValue
}
@@ -130,7 +114,6 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
const allKeys = new Set([
...Object.keys(keyedStates.value),
...Object.keys(keyedLoaded.value),
- ...Object.keys(keyVersions.value),
...Object.keys(keySnapshots.value)
])
for (const key of allKeys) {
@@ -140,7 +123,6 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
changed = true
}
delete keyedLoaded.value[key]
- delete keyVersions.value[key]
delete keySnapshots.value[key]
keyLoadTasks.delete(key)
}
@@ -160,16 +142,13 @@ export const useZxFwPricingKeysStore = defineStore('zxFwPricingKeys', () => {
return {
keyedStates,
keyedLoaded,
- keyVersions,
keySnapshots,
- getKeyVersion,
getKeyState,
loadKeyState,
setKeyState,
removeKeyState,
removeKeysByPrefix,
- setKeyStateSilent,
- touchKeyVersion
+ setKeyStateSilent
}
}, {
persist: true