192 lines
6.0 KiB
TypeScript
192 lines
6.0 KiB
TypeScript
import { i18n } from '@/i18n'
|
|
import localforage from 'localforage'
|
|
|
|
export type WorkspaceMode = 'project' | 'quick'
|
|
|
|
export const PROJECT_TAB_ID = 'ProjectCalcView'
|
|
export const QUICK_TAB_ID = 'QuickCalcView'
|
|
export const LEGACY_PROJECT_TAB_ID = 'ProjectCalcView'
|
|
export const FIXED_WORKSPACE_TAB_IDS = [PROJECT_TAB_ID, QUICK_TAB_ID] as const
|
|
|
|
export const WORKSPACE_MODE_STORAGE_KEY = 'jgjs-workspace-mode-v1'
|
|
export const PROJECT_ID_QUERY_KEY = 'projectId'
|
|
export const NEW_PROJECT_QUERY_KEY = 'newProject'
|
|
export const OPEN_PROJECT_DIALOG_QUERY_KEY = 'openProjectDialog'
|
|
export const FORCE_HOME_QUERY_KEY = 'forceHome'
|
|
export const DEFAULT_PROJECT_ID = 'default'
|
|
export const QUICK_PROJECT_ID = 'quick'
|
|
export const PROJECT_DB_NAME_PREFIX = 'DB'
|
|
|
|
export const QUICK_CONTRACT_ID = 'quick-contract-default'
|
|
export const QUICK_CONTRACT_META_KEY = 'quick-contract-meta-v1'
|
|
export const QUICK_CONTRACT_FALLBACK_NAME = i18n.global.t('quickCalc.projectName')
|
|
export const QUICK_CONTRACT_TAB_ID = `contract-${QUICK_CONTRACT_ID}`
|
|
|
|
export const QUICK_PROJECT_INFO_KEY = 'quick-xm-base-info-v1'
|
|
export const QUICK_PROJECT_SCALE_KEY = 'quick-xm-info-v3'
|
|
export const QUICK_CONSULT_CATEGORY_FACTOR_KEY = 'quick-xm-consult-category-factor-v1'
|
|
export const QUICK_MAJOR_FACTOR_KEY = 'quick-xm-major-factor-v1'
|
|
|
|
const HOME_IMPORT_TEMP_DB_NAME = 'JGJS-HOME-IMPORT-TEMP'
|
|
const HOME_IMPORT_TEMP_STORE_NAME = 'home-import'
|
|
const HOME_IMPORT_TEMP_FILE_KEY = 'pending-file'
|
|
const HOME_IMPORT_SKIP_CONFIRM_KEY = 'jgjs-home-import-skip-confirm'
|
|
const homeImportTempForage = localforage.createInstance({
|
|
name: HOME_IMPORT_TEMP_DB_NAME,
|
|
storeName: HOME_IMPORT_TEMP_STORE_NAME
|
|
})
|
|
|
|
export interface QuickContractMeta {
|
|
id: string
|
|
name: string
|
|
updatedAt: string
|
|
}
|
|
|
|
|
|
|
|
export const readWorkspaceMode = (): WorkspaceMode => {
|
|
try {
|
|
return window.localStorage.getItem(WORKSPACE_MODE_STORAGE_KEY) as WorkspaceMode
|
|
} catch {
|
|
return 'project'
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export const createDefaultQuickContractMeta = (): QuickContractMeta => ({
|
|
id: QUICK_CONTRACT_ID,
|
|
name: QUICK_CONTRACT_FALLBACK_NAME,
|
|
updatedAt: new Date().toISOString()
|
|
})
|
|
export const writeWorkspaceMode = (mode: WorkspaceMode) => {
|
|
try {
|
|
window.localStorage.setItem(WORKSPACE_MODE_STORAGE_KEY, mode)
|
|
} catch {
|
|
// 忽略只读或隐私模式下的写入失败。
|
|
}
|
|
}
|
|
|
|
export const setPendingHomeImportFile = (
|
|
file: File | null,
|
|
options?: { skipWorkspaceConfirm?: boolean }
|
|
) => {
|
|
return (async () => {
|
|
try {
|
|
if (file) {
|
|
await homeImportTempForage.setItem(HOME_IMPORT_TEMP_FILE_KEY, {
|
|
name: file.name,
|
|
type: file.type,
|
|
lastModified: file.lastModified,
|
|
blob: file
|
|
})
|
|
} else {
|
|
await homeImportTempForage.removeItem(HOME_IMPORT_TEMP_FILE_KEY)
|
|
}
|
|
} catch {
|
|
// ignore temp file persistence errors
|
|
}
|
|
try {
|
|
window.sessionStorage.setItem(HOME_IMPORT_SKIP_CONFIRM_KEY, options?.skipWorkspaceConfirm ? '1' : '0')
|
|
} catch {
|
|
// ignore session storage errors
|
|
}
|
|
})()
|
|
}
|
|
|
|
export const consumePendingHomeImportFile = async () => {
|
|
try {
|
|
const payload = await homeImportTempForage.getItem<{
|
|
name?: string
|
|
type?: string
|
|
lastModified?: number
|
|
blob?: Blob
|
|
}>(HOME_IMPORT_TEMP_FILE_KEY)
|
|
await homeImportTempForage.removeItem(HOME_IMPORT_TEMP_FILE_KEY)
|
|
if (!payload?.blob) return null
|
|
const name = String(payload.name || 'import.zw').trim() || 'import.zw'
|
|
return new File([payload.blob], name, {
|
|
type: typeof payload.type === 'string' ? payload.type : '',
|
|
lastModified: typeof payload.lastModified === 'number' ? payload.lastModified : Date.now()
|
|
})
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export const consumePendingHomeImportSkipConfirm = () => {
|
|
try {
|
|
const skip = window.sessionStorage.getItem(HOME_IMPORT_SKIP_CONFIRM_KEY) === '1'
|
|
window.sessionStorage.removeItem(HOME_IMPORT_SKIP_CONFIRM_KEY)
|
|
return skip
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export const normalizeProjectId = (value: unknown) => {
|
|
const raw = String(value || '').trim()
|
|
if (!raw) return DEFAULT_PROJECT_ID
|
|
return raw.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 64) || DEFAULT_PROJECT_ID
|
|
}
|
|
|
|
export const readCurrentProjectId = () => {
|
|
try {
|
|
const url = new URL(window.location.href)
|
|
return normalizeProjectId(url.searchParams.get(PROJECT_ID_QUERY_KEY))
|
|
} catch {
|
|
return DEFAULT_PROJECT_ID
|
|
}
|
|
}
|
|
|
|
export const writeProjectIdToUrl = (projectIdRaw: string) => {
|
|
try {
|
|
const projectId = normalizeProjectId(projectIdRaw)
|
|
const url = new URL(window.location.href)
|
|
url.searchParams.set(PROJECT_ID_QUERY_KEY, projectId)
|
|
window.history.replaceState({}, '', `${url.pathname}${url.search}${url.hash}`)
|
|
} catch (error) {
|
|
console.error('write project id to url failed:', error)
|
|
}
|
|
}
|
|
|
|
export const ensureProjectIdInUrl = () => {
|
|
const id = readCurrentProjectId()
|
|
writeProjectIdToUrl(id)
|
|
return id
|
|
}
|
|
|
|
export const buildProjectUrl = (
|
|
projectIdRaw: string,
|
|
options?: { newProject?: boolean; openProjectDialog?: boolean; forceHome?: boolean }
|
|
) => {
|
|
try {
|
|
const url = new URL(window.location.href)
|
|
url.searchParams.set(PROJECT_ID_QUERY_KEY, normalizeProjectId(projectIdRaw))
|
|
if (options?.newProject) {
|
|
url.searchParams.set(NEW_PROJECT_QUERY_KEY, '1')
|
|
if (options?.openProjectDialog === false) {
|
|
url.searchParams.set(OPEN_PROJECT_DIALOG_QUERY_KEY, '0')
|
|
} else {
|
|
url.searchParams.delete(OPEN_PROJECT_DIALOG_QUERY_KEY)
|
|
}
|
|
} else {
|
|
url.searchParams.delete(NEW_PROJECT_QUERY_KEY)
|
|
url.searchParams.delete(OPEN_PROJECT_DIALOG_QUERY_KEY)
|
|
}
|
|
if (options?.forceHome) {
|
|
url.searchParams.set(FORCE_HOME_QUERY_KEY, '1')
|
|
} else {
|
|
url.searchParams.delete(FORCE_HOME_QUERY_KEY)
|
|
}
|
|
return `${url.pathname}${url.search}${url.hash}`
|
|
} catch {
|
|
return `/?${PROJECT_ID_QUERY_KEY}=${encodeURIComponent(normalizeProjectId(projectIdRaw))}`
|
|
}
|
|
}
|
|
|
|
export const getProjectDbName = (projectIdRaw: string) => {
|
|
const projectId = normalizeProjectId(projectIdRaw)
|
|
return `${PROJECT_DB_NAME_PREFIX}-${projectId}`
|
|
}
|