148 lines
5.2 KiB
TypeScript
148 lines
5.2 KiB
TypeScript
|
|
import { nextTick, type Reactive, type Ref } from 'vue'
|
||
|
|
import { ElMessage } from 'element-plus'
|
||
|
|
import type {
|
||
|
|
ExternalDataSource,
|
||
|
|
ProcessType,
|
||
|
|
StepId,
|
||
|
|
StructuredProcessOptions,
|
||
|
|
UnstructuredProcessOptions,
|
||
|
|
} from './types'
|
||
|
|
|
||
|
|
export const DATA_PROCESS_DRAFT_STORAGE_KEY = 'yg-data-process-create-draft'
|
||
|
|
export const DATA_PROCESS_DRAFT_SCHEMA_VERSION = 6
|
||
|
|
|
||
|
|
interface DraftSnapshot {
|
||
|
|
schemaVersion?: number
|
||
|
|
currentStepId?: StepId
|
||
|
|
task?: { name?: string; description?: string }
|
||
|
|
processType?: ProcessType
|
||
|
|
structuredOptions?: Partial<StructuredProcessOptions>
|
||
|
|
unstructuredOptions?: Partial<UnstructuredProcessOptions>
|
||
|
|
externalSource?: Partial<ExternalDataSource>
|
||
|
|
}
|
||
|
|
|
||
|
|
interface DraftBindings {
|
||
|
|
currentStepId: Readonly<Ref<StepId>>
|
||
|
|
task: Reactive<{ name: string; description: string }>
|
||
|
|
processType: Ref<ProcessType>
|
||
|
|
structuredOptions: Ref<StructuredProcessOptions>
|
||
|
|
unstructuredOptions: Ref<UnstructuredProcessOptions>
|
||
|
|
externalSource: Reactive<ExternalDataSource>
|
||
|
|
restoringDraft: Ref<boolean>
|
||
|
|
dirty: Ref<boolean>
|
||
|
|
goToStep: (stepId: StepId) => void
|
||
|
|
}
|
||
|
|
|
||
|
|
function sanitizeExternalSource(source: Partial<ExternalDataSource>) {
|
||
|
|
return {
|
||
|
|
type: typeof source.type === 'string' ? source.type : 'mysql',
|
||
|
|
url: typeof source.url === 'string' ? source.url : '',
|
||
|
|
authMode: typeof source.authMode === 'string' ? source.authMode : 'none',
|
||
|
|
username: typeof source.username === 'string' ? source.username : '',
|
||
|
|
limit: Number.isFinite(source.limit) ? Number(source.limit) : 1000,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function isDraftSnapshot(value: unknown): value is DraftSnapshot {
|
||
|
|
return Boolean(value && typeof value === 'object')
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 只持久化可重建的配置。密码、令牌、文件正文、预览与结果都不进入 localStorage。
|
||
|
|
*/
|
||
|
|
export function useDataProcessDraft(bindings: DraftBindings) {
|
||
|
|
function draftSnapshot(): DraftSnapshot {
|
||
|
|
return {
|
||
|
|
schemaVersion: DATA_PROCESS_DRAFT_SCHEMA_VERSION,
|
||
|
|
currentStepId: bindings.currentStepId.value,
|
||
|
|
task: { ...bindings.task },
|
||
|
|
processType: bindings.processType.value,
|
||
|
|
structuredOptions: {
|
||
|
|
...bindings.structuredOptions.value,
|
||
|
|
preprocessOptions: [...bindings.structuredOptions.value.preprocessOptions],
|
||
|
|
datasetSplit: { ...bindings.structuredOptions.value.datasetSplit },
|
||
|
|
},
|
||
|
|
unstructuredOptions: {
|
||
|
|
...bindings.unstructuredOptions.value,
|
||
|
|
preprocessOptions: [...bindings.unstructuredOptions.value.preprocessOptions],
|
||
|
|
datasetSplit: { ...bindings.unstructuredOptions.value.datasetSplit },
|
||
|
|
},
|
||
|
|
externalSource: sanitizeExternalSource(bindings.externalSource),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function writeDraft(showWarning: boolean) {
|
||
|
|
try {
|
||
|
|
localStorage.setItem(DATA_PROCESS_DRAFT_STORAGE_KEY, JSON.stringify(draftSnapshot()))
|
||
|
|
} catch {
|
||
|
|
if (showWarning) ElMessage.warning('草稿保存失败,请检查浏览器存储空间')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function persistDraft() {
|
||
|
|
if (!bindings.restoringDraft.value) writeDraft(true)
|
||
|
|
}
|
||
|
|
|
||
|
|
function restoreDraft() {
|
||
|
|
try {
|
||
|
|
const raw = localStorage.getItem(DATA_PROCESS_DRAFT_STORAGE_KEY)
|
||
|
|
if (!raw) return
|
||
|
|
const snapshot: unknown = JSON.parse(raw)
|
||
|
|
if (!isDraftSnapshot(snapshot)) return
|
||
|
|
|
||
|
|
bindings.restoringDraft.value = true
|
||
|
|
bindings.goToStep('create')
|
||
|
|
bindings.task.name = snapshot.task?.name || ''
|
||
|
|
bindings.task.description = snapshot.task?.description || ''
|
||
|
|
bindings.processType.value = snapshot.processType === 'structured' || snapshot.processType === 'external'
|
||
|
|
? snapshot.processType
|
||
|
|
: 'unstructured'
|
||
|
|
|
||
|
|
if (snapshot.structuredOptions) {
|
||
|
|
bindings.structuredOptions.value = {
|
||
|
|
...bindings.structuredOptions.value,
|
||
|
|
...snapshot.structuredOptions,
|
||
|
|
preprocessOptions: Array.isArray(snapshot.structuredOptions.preprocessOptions)
|
||
|
|
? snapshot.structuredOptions.preprocessOptions
|
||
|
|
: bindings.structuredOptions.value.preprocessOptions,
|
||
|
|
datasetSplit: {
|
||
|
|
...bindings.structuredOptions.value.datasetSplit,
|
||
|
|
...snapshot.structuredOptions.datasetSplit,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (snapshot.unstructuredOptions) {
|
||
|
|
bindings.unstructuredOptions.value = {
|
||
|
|
...bindings.unstructuredOptions.value,
|
||
|
|
...snapshot.unstructuredOptions,
|
||
|
|
preprocessOptions: Array.isArray(snapshot.unstructuredOptions.preprocessOptions)
|
||
|
|
? snapshot.unstructuredOptions.preprocessOptions
|
||
|
|
: bindings.unstructuredOptions.value.preprocessOptions,
|
||
|
|
datasetSplit: {
|
||
|
|
...bindings.unstructuredOptions.value.datasetSplit,
|
||
|
|
...snapshot.unstructuredOptions.datasetSplit,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Object.assign(bindings.externalSource, sanitizeExternalSource(snapshot.externalSource || {}), {
|
||
|
|
password: '',
|
||
|
|
token: '',
|
||
|
|
})
|
||
|
|
bindings.dirty.value = false
|
||
|
|
|
||
|
|
nextTick(() => {
|
||
|
|
bindings.restoringDraft.value = false
|
||
|
|
// 立即覆盖 v5 及更早草稿,清除其中可能存在的敏感值和大段正文。
|
||
|
|
writeDraft(false)
|
||
|
|
})
|
||
|
|
ElMessage.info('已恢复上次的任务配置,请重新上传或拉取源数据')
|
||
|
|
} catch {
|
||
|
|
localStorage.removeItem(DATA_PROCESS_DRAFT_STORAGE_KEY)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return { draftSnapshot, persistDraft, restoreDraft }
|
||
|
|
}
|