fix,去掉大部分indexdb的逻辑
This commit is contained in:
1 parent
9a045cfe86
commit
3d26b0b259
19 files changed
+416
-103
No files matched your search
+120
-20
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user