fix,去掉大部分indexdb的逻辑

This commit is contained in:
zwgczx committed 2026-03-11 12:01:16 +08:00
1 parent 9a045cfe86
commit 3d26b0b259
19 files changed
+416 -103

No files matched your search

+120 -20
View File
@@ -1,15 +1,14 @@
<script setup lang="ts">
import { computed, defineAsyncComponent, markRaw, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { computed, defineAsyncComponent, markRaw, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
import type { ComponentPublicInstance } from 'vue'
import draggable from 'vuedraggable'
import { useTabStore } from '@/pinia/tab'
import { useZxFwPricingStore } from '@/pinia/zxFwPricing'
import { useKvStore } from '@/pinia/kv'
import { Button } from '@/components/ui/button'
import { ScrollArea } from '@/components/ui/scroll-area'
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from '@/components/ui/tooltip'
import { ChevronDown, CircleHelp, RotateCcw, X } from 'lucide-vue-next'
import { createPinia } from 'pinia';
import localforage from 'localforage'
import {
AlertDialogAction,
@@ -30,12 +29,18 @@ interface DataEntry {
value: any
}
interface ForageStoreSnapshot {
storeName: string
entries: DataEntry[]
}
interface DataPackage {
version: number
exportedAt: string
localStorage: DataEntry[]
sessionStorage: DataEntry[]
localforageDefault: DataEntry[]
localforageStores?: ForageStoreSnapshot[]
}
interface UserGuideStep {
@@ -268,6 +273,9 @@ const PROJECT_INFO_DB_KEY = 'xm-base-info-v1'
const LEGACY_PROJECT_DB_KEY = 'xm-info-v3'
const CONSULT_CATEGORY_FACTOR_DB_KEY = 'xm-consult-category-factor-v1'
const MAJOR_FACTOR_DB_KEY = 'xm-major-factor-v1'
const PINIA_PERSIST_DB_NAME = 'DB'
const PINIA_PERSIST_BASE_STORE_NAME = 'pinia'
const PINIA_PERSIST_STORE_IDS = ['tabs', 'zxFwPricing', 'kv'] as const
const userGuideSteps: UserGuideStep[] = [
{
title: '欢迎使用',
@@ -352,6 +360,7 @@ const componentMap: Record<string, any> = {
const tabStore = useTabStore()
const zxFwPricingStore = useZxFwPricingStore()
const kvStore = useKvStore()
@@ -365,7 +374,7 @@ const dataMenuOpen = ref(false)
const dataMenuRef = ref<HTMLElement | null>(null)
const importFileRef = ref<HTMLInputElement | null>(null)
const importConfirmOpen = ref(false)
const pendingImportPayload = ref<DataPackage | null>(null)
const pendingImportPayload = shallowRef<DataPackage | null>(null)
const pendingImportFileName = ref('')
const userGuideOpen = ref(false)
const userGuideStepIndex = ref(0)
@@ -450,10 +459,10 @@ const shouldAutoOpenGuide = async () => {
if (hasGuideCompleted()) return false
if (hasNonDefaultTabState()) return false
try {
const keys = await localforage.keys()
const keys = await kvStore.keys()
return keys.length === 0
} catch (error) {
console.error('read localforage keys failed:', error)
console.error('read kv keys failed:', error)
return false
}
}
@@ -734,20 +743,49 @@ const writeWebStorage = (storageObj: Storage, entries: DataEntry[]) => {
}
}
const readForage = async (store: typeof localforage): Promise<DataEntry[]> => {
type ForageInstance = ReturnType<typeof localforage.createInstance>
type ForageStore = Pick<ForageInstance, 'keys' | 'getItem' | 'setItem' | 'clear'>
const createForageStore = (storeName: string): ForageInstance =>
localforage.createInstance({
name: PINIA_PERSIST_DB_NAME,
storeName
})
const getPiniaPersistStoreName = (storeId: string) => `${PINIA_PERSIST_BASE_STORE_NAME}-${storeId}`
const getPiniaPersistStores = () =>
PINIA_PERSIST_STORE_IDS.map(storeId => {
const storeName = getPiniaPersistStoreName(storeId)
return {
storeName,
store: createForageStore(storeName)
}
})
const readForage = async (store: ForageStore): Promise<DataEntry[]> => {
const keys = await store.keys()
const entries: DataEntry[] = []
for (const key of keys) {
const value = await store.getItem(key)
entries.push({ key, value })
entries.push({ key, value: toPersistableValue(value) })
}
return entries
}
const writeForage = async (store: typeof localforage, entries: DataEntry[]) => {
const toPersistableValue = (value: unknown) => {
try {
return JSON.parse(JSON.stringify(value))
} catch (error) {
console.error('normalize persist value failed, fallback to null:', error)
return null
}
}
const writeForage = async (store: ForageStore, entries: DataEntry[]) => {
await store.clear()
for (const entry of entries || []) {
await store.setItem(entry.key, entry.value)
await store.setItem(entry.key, toPersistableValue(entry.value))
}
}
@@ -758,6 +796,21 @@ const normalizeEntries = (value: unknown): DataEntry[] => {
.map(item => ({ key: String((item as any).key), value: (item as any).value }))
}
const normalizeForageStoreSnapshots = (value: unknown): ForageStoreSnapshot[] => {
if (!Array.isArray(value)) return []
return value
.filter(item =>
item
&& typeof item === 'object'
&& typeof (item as any).storeName === 'string'
&& Array.isArray((item as any).entries)
)
.map(item => ({
storeName: String((item as any).storeName),
entries: normalizeEntries((item as any).entries)
}))
}
const sanitizeFileNamePart = (value: string): string => {
const cleaned = value
.replace(/[\\/:*?"<>|]/g, '_')
@@ -1058,11 +1111,11 @@ const buildServiceFee = (
const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const [projectInfoRaw, projectScaleRaw, consultCategoryFactorRaw, majorFactorRaw, contractCardsRaw] = await Promise.all([
localforage.getItem<XmInfoLike>(PROJECT_INFO_DB_KEY),
localforage.getItem<XmInfoStorageLike>(LEGACY_PROJECT_DB_KEY),
localforage.getItem<DetailRowsStorageLike<FactorRowLike>>(CONSULT_CATEGORY_FACTOR_DB_KEY),
localforage.getItem<DetailRowsStorageLike<FactorRowLike>>(MAJOR_FACTOR_DB_KEY),
localforage.getItem<ContractCardItem[]>('ht-card-v1')
kvStore.getItem<XmInfoLike>(PROJECT_INFO_DB_KEY),
kvStore.getItem<XmInfoStorageLike>(LEGACY_PROJECT_DB_KEY),
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(CONSULT_CATEGORY_FACTOR_DB_KEY),
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(MAJOR_FACTOR_DB_KEY),
kvStore.getItem<ContractCardItem[]>('ht-card-v1')
])
const projectInfo = projectInfoRaw || {}
@@ -1087,8 +1140,8 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const contract = contractCards[index]
const contractId = contract.id
const [htInfoRaw, zxFwRaw] = await Promise.all([
localforage.getItem<DetailRowsStorageLike<ScaleRowLike>>(`ht-info-v3-${contractId}`),
localforage.getItem<ZxFwStorageLike>(`zxFW-${contractId}`)
kvStore.getItem<DetailRowsStorageLike<ScaleRowLike>>(`ht-info-v3-${contractId}`),
kvStore.getItem<ZxFwStorageLike>(`zxFW-${contractId}`)
])
const zxRows = Array.isArray(zxFwRaw?.detailRows) ? zxFwRaw.detailRows : []
@@ -1179,12 +1232,19 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const exportData = async () => {
try {
const now = new Date()
const piniaForageStores = await Promise.all(
getPiniaPersistStores().map(async ({ storeName, store }) => ({
storeName,
entries: await readForage(store)
}))
)
const payload: DataPackage = {
version: 1,
version: 2,
exportedAt: now.toISOString(),
localStorage: readWebStorage(localStorage),
sessionStorage: readWebStorage(sessionStorage),
localforageDefault: await readForage(localforage),
localforageStores: piniaForageStores
}
const content = await encodeZwArchive(payload)
@@ -1261,9 +1321,44 @@ const confirmImportOverride = async () => {
writeWebStorage(localStorage, normalizeEntries(payload.localStorage))
writeWebStorage(sessionStorage, normalizeEntries(payload.sessionStorage))
await writeForage(localforage, normalizeEntries(payload.localforageDefault))
const piniaSnapshots = normalizeForageStoreSnapshots(payload.localforageStores)
const snapshotMap = new Map(piniaSnapshots.map(item => [item.storeName, item.entries]))
await Promise.all(
getPiniaPersistStores().map(async ({ storeName, store }) => {
const entries = snapshotMap.get(storeName) || []
await writeForage(store, entries)
})
)
tabStore.resetTabs()
await tabStore.$persistNow?.()
const readPersistedState = (storeId: string) => {
const storeName = getPiniaPersistStoreName(storeId)
const entries = snapshotMap.get(storeName) || []
const hit = entries.find(entry => entry.key === storeName)
return hit && hit.value && typeof hit.value === 'object' ? hit.value : null
}
const tabsState = readPersistedState('tabs')
if (tabsState) {
tabStore.$patch(tabsState as any)
} else {
tabStore.resetTabs()
}
const zxFwPricingState = readPersistedState('zxFwPricing')
if (zxFwPricingState) {
zxFwPricingStore.$patch(zxFwPricingState as any)
}
const kvState = readPersistedState('kv')
if (kvState) {
kvStore.$patch(kvState as any)
}
await Promise.all([
tabStore.$persistNow?.(),
zxFwPricingStore.$persistNow?.(),
kvStore.$persistNow?.()
])
dataMenuOpen.value = false
window.location.reload()
} catch (error) {
@@ -1279,6 +1374,11 @@ const handleReset = async () => {
localStorage.clear()
sessionStorage.clear()
await localforage.clear()
await Promise.all(
getPiniaPersistStores().map(async ({ store }) => {
await store.clear()
})
)
localStorage.setItem(USER_GUIDE_COMPLETED_KEY, '1')
} catch (error) {
console.error('reset failed:', error)