288 lines
10 KiB
TypeScript
288 lines
10 KiB
TypeScript
/**
|
||
* 合同附加费用状态管理 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
|
||
})
|