/** * 合同附加费用状态管理 Store * * 从 zxFwPricing 拆出,管理合同附加费用的主表和子方法状态。 * 包括 additional-work(附加工作)和 reserve(预留金)两种费用类型。 */ import { defineStore } from 'pinia' import { ref } from 'vue' import { useZxFwPricingKeysStore } from '@/pinia/zxFwPricingKeys' import type { HtFeeMainState, HtFeeMethodType, HtFeeMethodPayload } from '@/types/pricing' const HT_FEE_MAIN_KEY_PATTERN = /^htExtraFee-(.+)-(additional-work|reserve)$/ const HT_FEE_METHOD_TYPES: HtFeeMethodType[] = ['rate-fee', 'hourly-fee', 'quantity-unit-price-fee'] const toKey = (keyRaw: string | number) => String(keyRaw || '').trim() const toKeySnapshot = (value: unknown) => JSON.stringify(value ?? null) const cloneAny = (value: T): T => { if (value == null) return value return JSON.parse(JSON.stringify(value)) as T } const normalizeHtFeeMainState = ( payload: Partial | null | undefined ): HtFeeMainState => ({ detailRows: Array.isArray(payload?.detailRows) ? cloneAny(payload.detailRows) : [] }) /** 解析附加费用主表存储键 */ export const parseHtFeeMainStorageKey = (keyRaw: string | number) => { const key = toKey(keyRaw) if (!key) return null const match = HT_FEE_MAIN_KEY_PATTERN.exec(key) if (!match) return null const contractId = String(match[1] || '').trim() const feeType = String(match[2] || '').trim() if (!contractId || !feeType) return null return { key, contractId, feeType, mainStorageKey: key } } /** 解析附加费用子方法存储键 */ export const parseHtFeeMethodStorageKey = (keyRaw: string | number) => { const key = toKey(keyRaw) if (!key) return null for (const method of HT_FEE_METHOD_TYPES) { const suffix = `-${method}` if (!key.endsWith(suffix)) continue const withoutMethod = key.slice(0, key.length - suffix.length) const mainMatch = /^(htExtraFee-.+-(?:additional-work|reserve))-(.+)$/.exec(withoutMethod) if (!mainMatch) continue const mainStorageKey = String(mainMatch[1] || '').trim() const rowId = String(mainMatch[2] || '').trim() if (!mainStorageKey || !rowId) continue return { key, mainStorageKey, rowId, method } } return null } export const useZxFwPricingHtFeeStore = defineStore('zxFwPricingHtFee', () => { /** 附加费用主表状态 */ const htFeeMainStates = ref>({}) /** 附加费用子方法状态 */ const htFeeMethodStates = ref< Record>>> >({}) /* ---------------------------------------------------------------- * 主表操作 * ---------------------------------------------------------------- */ /** 获取附加费用主表状态 */ const getHtFeeMainState = ( mainStorageKeyRaw: string | number ): HtFeeMainState | null => { const mainStorageKey = toKey(mainStorageKeyRaw) if (!mainStorageKey) return null return (htFeeMainStates.value[mainStorageKey] as HtFeeMainState | undefined) || null } /** 设置附加费用主表状态并同步版本 */ const setHtFeeMainState = ( mainStorageKeyRaw: string | number, payload: Partial> | null | undefined, options?: { force?: boolean; syncKeyState?: boolean } ): boolean => { const mainStorageKey = toKey(mainStorageKeyRaw) if (!mainStorageKey) return false const force = options?.force === true const syncKeyState = options?.syncKeyState !== false const normalized = payload == null ? null : normalizeHtFeeMainState(payload) const prevSnapshot = toKeySnapshot(getHtFeeMainState(mainStorageKey)) const nextSnapshot = toKeySnapshot(normalized) if (!force && prevSnapshot === nextSnapshot) return false if (normalized == null) { delete htFeeMainStates.value[mainStorageKey] } else { htFeeMainStates.value[mainStorageKey] = normalized } if (syncKeyState) { const keysStore = useZxFwPricingKeysStore() if (normalized == null) { keysStore.removeKeyState(mainStorageKey) } else { keysStore.setKeyState(mainStorageKey, cloneAny(normalized), { force: true }) } } return true } /** 从缓存或 IndexedDB 加载附加费用主表状态 */ const loadHtFeeMainState = async ( mainStorageKeyRaw: string | number, force = false ): Promise | null> => { const mainStorageKey = toKey(mainStorageKeyRaw) if (!mainStorageKey) return null if (!force) { const existing = getHtFeeMainState(mainStorageKey) if (existing) return existing } const keysStore = useZxFwPricingKeysStore() const payload = await keysStore.loadKeyState>(mainStorageKey, force) if (!payload) { setHtFeeMainState(mainStorageKey, null, { force: true, syncKeyState: false }) return null } setHtFeeMainState(mainStorageKey, payload, { force: true, syncKeyState: false }) return getHtFeeMainState(mainStorageKey) } /** 删除附加费用主表状态 */ const removeHtFeeMainState = (mainStorageKeyRaw: string | number) => setHtFeeMainState(mainStorageKeyRaw, null) /* ---------------------------------------------------------------- * 子方法操作 * ---------------------------------------------------------------- */ const ensureMethodContainer = (mainStorageKey: string, rowId: string) => { if (!htFeeMethodStates.value[mainStorageKey]) { htFeeMethodStates.value[mainStorageKey] = {} } if (!htFeeMethodStates.value[mainStorageKey][rowId]) { htFeeMethodStates.value[mainStorageKey][rowId] = {} } return htFeeMethodStates.value[mainStorageKey][rowId] } /** 获取附加费用子方法存储键 */ const getHtFeeMethodStorageKey = ( mainStorageKeyRaw: string | number, rowIdRaw: string | number, method: HtFeeMethodType ): string => { const mainStorageKey = toKey(mainStorageKeyRaw) const rowId = toKey(rowIdRaw) if (!mainStorageKey || !rowId) return '' return `${mainStorageKey}-${rowId}-${method}` } /** 获取附加费用子方法状态 */ const getHtFeeMethodState = ( mainStorageKeyRaw: string | number, rowIdRaw: string | number, method: HtFeeMethodType ): TPayload | null => { const mainStorageKey = toKey(mainStorageKeyRaw) const rowId = toKey(rowIdRaw) if (!mainStorageKey || !rowId) return null const value = htFeeMethodStates.value[mainStorageKey]?.[rowId]?.[method] return value == null ? null : (cloneAny(value) as TPayload) } /** 设置附加费用子方法状态并同步版本 */ const setHtFeeMethodState = ( mainStorageKeyRaw: string | number, rowIdRaw: string | number, method: HtFeeMethodType, payload: TPayload | null | undefined, options?: { force?: boolean; syncKeyState?: boolean } ): boolean => { const mainStorageKey = toKey(mainStorageKeyRaw) const rowId = toKey(rowIdRaw) if (!mainStorageKey || !rowId) return false const storageKey = getHtFeeMethodStorageKey(mainStorageKey, rowId, method) if (!storageKey) return false const force = options?.force === true const syncKeyState = options?.syncKeyState !== false const prevSnapshot = toKeySnapshot(getHtFeeMethodState(mainStorageKey, rowId, method)) const nextSnapshot = toKeySnapshot(payload ?? null) if (!force && prevSnapshot === nextSnapshot) return false if (payload == null) { const byRow = htFeeMethodStates.value[mainStorageKey]?.[rowId] if (byRow) { delete byRow[method] if (Object.keys(byRow).length === 0) { delete htFeeMethodStates.value[mainStorageKey][rowId] if (Object.keys(htFeeMethodStates.value[mainStorageKey]).length === 0) { delete htFeeMethodStates.value[mainStorageKey] } } } } else { const container = ensureMethodContainer(mainStorageKey, rowId) container[method] = cloneAny(payload) } if (syncKeyState) { const keysStore = useZxFwPricingKeysStore() if (payload == null) { keysStore.removeKeyState(storageKey) } else { keysStore.setKeyState(storageKey, cloneAny(payload), { force: true }) } } return true } /** 从缓存或 IndexedDB 加载附加费用子方法状态 */ const loadHtFeeMethodState = async ( mainStorageKeyRaw: string | number, rowIdRaw: string | number, method: HtFeeMethodType, force = false ): Promise => { const mainStorageKey = toKey(mainStorageKeyRaw) const rowId = toKey(rowIdRaw) if (!mainStorageKey || !rowId) return null if (!force) { const existing = getHtFeeMethodState(mainStorageKey, rowId, method) if (existing != null) return existing } const storageKey = getHtFeeMethodStorageKey(mainStorageKey, rowId, method) const keysStore = useZxFwPricingKeysStore() const payload = await keysStore.loadKeyState(storageKey, force) if (payload == null) { setHtFeeMethodState(mainStorageKey, rowId, method, null, { force: true, syncKeyState: false }) return null } setHtFeeMethodState(mainStorageKey, rowId, method, payload, { force: true, syncKeyState: false }) return getHtFeeMethodState(mainStorageKey, rowId, method) } /** 删除附加费用子方法状态 */ const removeHtFeeMethodState = ( mainStorageKeyRaw: string | number, rowIdRaw: string | number, method: HtFeeMethodType ) => setHtFeeMethodState(mainStorageKeyRaw, rowIdRaw, method, null) /** 清除指定合同的全部附加费用数据 */ const removeContractHtFeeData = (contractId: string): boolean => { const prefix = `htExtraFee-${contractId}-` let changed = false for (const key of Object.keys(htFeeMainStates.value)) { if (!key.startsWith(prefix)) continue delete htFeeMainStates.value[key] changed = true } for (const key of Object.keys(htFeeMethodStates.value)) { if (!key.startsWith(prefix)) continue delete htFeeMethodStates.value[key] changed = true } return changed } return { htFeeMainStates, htFeeMethodStates, getHtFeeMainState, setHtFeeMainState, loadHtFeeMainState, removeHtFeeMainState, getHtFeeMethodStorageKey, getHtFeeMethodState, setHtFeeMethodState, loadHtFeeMethodState, removeHtFeeMethodState, removeContractHtFeeData } }, { persist: true })