mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2025-08-23 07:56:18 +00:00
持久化导入导出支持
This commit is contained in:
@@ -27,7 +27,7 @@ const flowEditorRefs = ref({});
|
||||
const lastActiveFile = ref(filesStore.activeFile);
|
||||
|
||||
const handleTabsEdit = (
|
||||
targetName: String | undefined,
|
||||
targetName: string | undefined,
|
||||
action: 'remove' | 'add'
|
||||
) => {
|
||||
if (action === 'remove') {
|
||||
@@ -60,6 +60,9 @@ onMounted(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
windowHeight.value = window.innerHeight;
|
||||
});
|
||||
// 初始化自动保存功能
|
||||
filesStore.initializeWithPrompt();
|
||||
filesStore.setupAutoSave();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
@@ -80,7 +80,6 @@ import {ref, reactive, onMounted} from 'vue';
|
||||
import html2canvas from "html2canvas";
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import updateLogs from "../data/updateLog.json"
|
||||
import filesStoreExample from "../data/filesStoreExample.json"
|
||||
import {useFilesStore} from "@/ts/useStore";
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import {useGlobalMessage} from "@/ts/useGlobalMessage";
|
||||
@@ -110,7 +109,29 @@ const loadExample = () => {
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
filesStore.$patch({fileList: filesStoreExample});
|
||||
// 使用默认状态作为示例
|
||||
const defaultState = {
|
||||
fileList: [{
|
||||
"label": "示例文件",
|
||||
"name": "example",
|
||||
"visible": true,
|
||||
"type": "FLOW",
|
||||
"groups": [
|
||||
{
|
||||
"shortDescription": "示例组",
|
||||
"groupInfo": [{}, {}, {}, {}, {}],
|
||||
"details": "这是一个示例文件"
|
||||
}
|
||||
],
|
||||
"flowData": {
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"viewport": { "x": 0, "y": 0, "zoom": 1 }
|
||||
}
|
||||
}],
|
||||
activeFile: "example"
|
||||
};
|
||||
filesStore.importData(defaultState);
|
||||
showMessage('success', '数据已恢复');
|
||||
}).catch(() => {
|
||||
showMessage('info', '选择了不恢复旧数据');
|
||||
@@ -139,14 +160,7 @@ const showFeedbackForm = () => {
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
const dataStr = JSON.stringify(filesStore.fileList, null, 2);
|
||||
const blob = new Blob([dataStr], {type: 'application/json;charset=utf-8'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'files.json';
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
filesStore.exportData();
|
||||
};
|
||||
|
||||
const handleImport = () => {
|
||||
@@ -154,27 +168,18 @@ const handleImport = () => {
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
input.onchange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result as string);
|
||||
if (data[0].visible === true) {
|
||||
// 新版本格式:直接替换 fileList
|
||||
filesStore.$patch({fileList: data});
|
||||
} else {
|
||||
// 旧版本格式:仅包含 groups 数组
|
||||
const newFile = {
|
||||
label: `File ${filesStore.fileList.length + 1}`,
|
||||
name: String(filesStore.fileList.length + 1),
|
||||
visible: true,
|
||||
groups: data
|
||||
};
|
||||
filesStore.addFile(newFile);
|
||||
}
|
||||
const target = e.target as FileReader;
|
||||
const data = JSON.parse(target.result as string);
|
||||
filesStore.importData(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to import file', error);
|
||||
showMessage('error', '文件格式错误');
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
|
@@ -5,7 +5,7 @@ import { useFilesStore } from './useStore'
|
||||
let id = 0
|
||||
|
||||
function getId() {
|
||||
return `dndnode_${id++}`
|
||||
return `dndnode_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
|
||||
}
|
||||
|
||||
const state = {
|
||||
|
@@ -1,6 +1,63 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import type { Edge, Node, ViewportTransform } from '@vue-flow/core';
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { useGlobalMessage } from "./useGlobalMessage";
|
||||
|
||||
const { showMessage } = useGlobalMessage();
|
||||
|
||||
function getDefaultState() {
|
||||
return {
|
||||
fileList: [{
|
||||
"label": "File 1",
|
||||
"name": "1",
|
||||
"visible": true,
|
||||
"type": "FLOW",
|
||||
"groups": [
|
||||
{
|
||||
"shortDescription": "",
|
||||
"groupInfo": [{}, {}, {}, {}, {}],
|
||||
"details": ""
|
||||
}
|
||||
],
|
||||
"flowData": {
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"viewport": { "x": 0, "y": 0, "zoom": 1 }
|
||||
}
|
||||
}],
|
||||
activeFile: "1",
|
||||
};
|
||||
}
|
||||
|
||||
function saveStateToLocalStorage(state: any) {
|
||||
try {
|
||||
localStorage.setItem('filesStore', JSON.stringify(state));
|
||||
} catch (error) {
|
||||
console.error('保存到 localStorage 失败:', error);
|
||||
// 如果 localStorage 满了,尝试清理一些数据
|
||||
try {
|
||||
localStorage.clear();
|
||||
localStorage.setItem('filesStore', JSON.stringify(state));
|
||||
} catch (clearError) {
|
||||
console.error('清理 localStorage 后仍无法保存:', clearError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearFilesStoreLocalStorage() {
|
||||
localStorage.removeItem('filesStore');
|
||||
}
|
||||
|
||||
function loadStateFromLocalStorage() {
|
||||
try {
|
||||
const data = localStorage.getItem('filesStore');
|
||||
return data ? JSON.parse(data) : null;
|
||||
} catch (error) {
|
||||
console.error('从 localStorage 加载数据失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 文件相关的类型定义
|
||||
interface FileGroup {
|
||||
@@ -158,6 +215,123 @@ export const useFilesStore = defineStore('files', () => {
|
||||
return file?.flowData;
|
||||
};
|
||||
|
||||
// 初始化时检查是否有未保存的数据
|
||||
const initializeWithPrompt = () => {
|
||||
const savedState = loadStateFromLocalStorage();
|
||||
const defaultState = getDefaultState();
|
||||
|
||||
// 如果没有保存的数据,使用默认状态
|
||||
if (!savedState) {
|
||||
fileList.value = defaultState.fileList;
|
||||
activeFile.value = defaultState.activeFile;
|
||||
return;
|
||||
}
|
||||
|
||||
const isSame = JSON.stringify(savedState) === JSON.stringify(defaultState);
|
||||
if (savedState && !isSame) {
|
||||
ElMessageBox.confirm(
|
||||
'检测到有未保存的旧数据,是否恢复?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '恢复',
|
||||
cancelButtonText: '不恢复',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
fileList.value = savedState.fileList || [];
|
||||
activeFile.value = savedState.activeFile || "1";
|
||||
showMessage('success', '数据已恢复');
|
||||
}).catch(() => {
|
||||
clearFilesStoreLocalStorage();
|
||||
fileList.value = defaultState.fileList;
|
||||
activeFile.value = defaultState.activeFile;
|
||||
showMessage('info', '选择了不恢复旧数据');
|
||||
});
|
||||
} else {
|
||||
// 如果有保存的数据且与默认状态相同,直接使用保存的数据
|
||||
fileList.value = savedState.fileList || defaultState.fileList;
|
||||
activeFile.value = savedState.activeFile || defaultState.activeFile;
|
||||
}
|
||||
};
|
||||
|
||||
// 设置自动保存
|
||||
const setupAutoSave = () => {
|
||||
console.log('自动保存功能已启动,每30秒保存一次');
|
||||
setInterval(() => {
|
||||
try {
|
||||
saveStateToLocalStorage({
|
||||
fileList: fileList.value,
|
||||
activeFile: activeFile.value
|
||||
});
|
||||
console.log('数据已自动保存到 localStorage');
|
||||
} catch (error) {
|
||||
console.error('自动保存失败:', error);
|
||||
}
|
||||
}, 30000); // 设置间隔时间为30秒
|
||||
};
|
||||
|
||||
// 导出数据
|
||||
const exportData = () => {
|
||||
try {
|
||||
const dataStr = JSON.stringify({
|
||||
fileList: fileList.value,
|
||||
activeFile: activeFile.value
|
||||
}, null, 2);
|
||||
const blob = new Blob([dataStr], {type: 'application/json;charset=utf-8'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'yys-editor-files.json';
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
showMessage('success', '数据导出成功');
|
||||
} catch (error) {
|
||||
console.error('导出数据失败:', error);
|
||||
showMessage('error', '数据导出失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 导入数据
|
||||
const importData = (data: any) => {
|
||||
try {
|
||||
if (data.fileList && Array.isArray(data.fileList)) {
|
||||
// 新版本格式:包含 fileList 和 activeFile
|
||||
fileList.value = data.fileList;
|
||||
activeFile.value = data.activeFile || "1";
|
||||
showMessage('success', '数据导入成功');
|
||||
} else if (Array.isArray(data) && data[0]?.visible === true) {
|
||||
// 兼容旧版本格式:直接是 fileList 数组
|
||||
fileList.value = data;
|
||||
activeFile.value = data[0]?.name || "1";
|
||||
showMessage('success', '数据导入成功');
|
||||
} else {
|
||||
// 兼容更旧版本格式:仅包含 groups 数组
|
||||
const newFile = {
|
||||
label: `File ${fileList.value.length + 1}`,
|
||||
name: String(fileList.value.length + 1),
|
||||
visible: true,
|
||||
type: "FLOW",
|
||||
groups: data,
|
||||
flowData: {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
viewport: { x: 0, y: 0, zoom: 1 }
|
||||
}
|
||||
};
|
||||
addFile(newFile);
|
||||
showMessage('success', '数据导入成功');
|
||||
}
|
||||
// 导入后立即保存到 localStorage
|
||||
saveStateToLocalStorage({
|
||||
fileList: fileList.value,
|
||||
activeFile: activeFile.value
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to import file', error);
|
||||
showMessage('error', '数据导入失败');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
fileList,
|
||||
activeFile,
|
||||
@@ -177,5 +351,9 @@ export const useFilesStore = defineStore('files', () => {
|
||||
getFileViewport,
|
||||
updateFileFlowData,
|
||||
getFileFlowData,
|
||||
initializeWithPrompt,
|
||||
setupAutoSave,
|
||||
exportData,
|
||||
importData,
|
||||
};
|
||||
});
|
Reference in New Issue
Block a user