JGJS2026/src/pinia/zxFwPricingHtFee.ts
2026-03-18 18:46:58 +08:00

288 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 合同附加费用状态管理 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 = <T>(value: T): T => {
if (value == null) return value
return JSON.parse(JSON.stringify(value)) as T
}
const normalizeHtFeeMainState = (
payload: Partial<HtFeeMainState> | 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<Record<string, HtFeeMainState>>({})
/** 附加费用子方法状态 */
const htFeeMethodStates = ref<
Record<string, Record<string, Partial<Record<HtFeeMethodType, HtFeeMethodPayload>>>>
>({})
/* ----------------------------------------------------------------
* 主表操作
* ---------------------------------------------------------------- */
/** 获取附加费用主表状态 */
const getHtFeeMainState = <TRow = unknown>(
mainStorageKeyRaw: string | number
): HtFeeMainState<TRow> | null => {
const mainStorageKey = toKey(mainStorageKeyRaw)
if (!mainStorageKey) return null
return (htFeeMainStates.value[mainStorageKey] as HtFeeMainState<TRow> | undefined) || null
}
/** 设置附加费用主表状态并同步版本 */
const setHtFeeMainState = <TRow = unknown>(
mainStorageKeyRaw: string | number,
payload: Partial<HtFeeMainState<TRow>> | 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 <TRow = unknown>(
mainStorageKeyRaw: string | number,
force = false
): Promise<HtFeeMainState<TRow> | null> => {
const mainStorageKey = toKey(mainStorageKeyRaw)
if (!mainStorageKey) return null
if (!force) {
const existing = getHtFeeMainState<TRow>(mainStorageKey)
if (existing) return existing
}
const keysStore = useZxFwPricingKeysStore()
const payload = await keysStore.loadKeyState<HtFeeMainState<TRow>>(mainStorageKey, force)
if (!payload) {
setHtFeeMainState(mainStorageKey, null, { force: true, syncKeyState: false })
return null
}
setHtFeeMainState(mainStorageKey, payload, { force: true, syncKeyState: false })
return getHtFeeMainState<TRow>(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 = <TPayload = HtFeeMethodPayload>(
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 = <TPayload = HtFeeMethodPayload>(
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 <TPayload = HtFeeMethodPayload>(
mainStorageKeyRaw: string | number,
rowIdRaw: string | number,
method: HtFeeMethodType,
force = false
): Promise<TPayload | null> => {
const mainStorageKey = toKey(mainStorageKeyRaw)
const rowId = toKey(rowIdRaw)
if (!mainStorageKey || !rowId) return null
if (!force) {
const existing = getHtFeeMethodState<TPayload>(mainStorageKey, rowId, method)
if (existing != null) return existing
}
const storageKey = getHtFeeMethodStorageKey(mainStorageKey, rowId, method)
const keysStore = useZxFwPricingKeysStore()
const payload = await keysStore.loadKeyState<TPayload>(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<TPayload>(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
})