This commit is contained in:
Typer_Body
2026-05-27 00:24:07 +08:00
parent 5d4e40459f
commit 804448b6cd
3 changed files with 2154 additions and 164 deletions
+2135 -155
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -45,6 +45,7 @@
"@radix-ui/react-tooltip": "^1.2.7", "@radix-ui/react-tooltip": "^1.2.7",
"@tailwindcss/postcss": "^4.1.5", "@tailwindcss/postcss": "^4.1.5",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@types/js-yaml": "^4.0.9",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"@xyflow/react": "^12.10.2", "@xyflow/react": "^12.10.2",
"axios": "^1.15.0", "axios": "^1.15.0",
@@ -54,6 +55,7 @@
"i18next": "^25.1.2", "i18next": "^25.1.2",
"i18next-browser-languagedetector": "^8.1.0", "i18next-browser-languagedetector": "^8.1.0",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"js-yaml": "^4.1.1",
"lodash": "^4.18.0", "lodash": "^4.18.0",
"lucide-react": "^0.507.0", "lucide-react": "^0.507.0",
"postcss": "^8.5.3", "postcss": "^8.5.3",
@@ -32,6 +32,7 @@ import { Workflow } from '@/app/infra/entities/api';
import { useWorkflowStore } from './store/useWorkflowStore'; import { useWorkflowStore } from './store/useWorkflowStore';
import { toast } from 'sonner'; import { toast } from 'sonner';
import EmojiPicker from '@/components/ui/emoji-picker'; import EmojiPicker from '@/components/ui/emoji-picker';
import yaml from 'js-yaml';
export default function WorkflowDetailContent({ id }: { id: string }) { export default function WorkflowDetailContent({ id }: { id: string }) {
const isCreateMode = id === 'new'; const isCreateMode = id === 'new';
@@ -174,7 +175,7 @@ export default function WorkflowDetailContent({ id }: { id: string }) {
basicInfo, basicInfo,
]); ]);
// Export workflow handler // Export workflow handler - exports as YAML
const handleExport = useCallback(() => { const handleExport = useCallback(() => {
const { nodes, edges } = toWorkflowDefinition(); const { nodes, edges } = toWorkflowDefinition();
@@ -190,13 +191,12 @@ export default function WorkflowDetailContent({ id }: { id: string }) {
exportedAt: new Date().toISOString(), exportedAt: new Date().toISOString(),
}; };
const blob = new Blob([JSON.stringify(exportData, null, 2)], { const yamlStr = yaml.dump(exportData, { indent: 2, lineWidth: -1 });
type: 'application/json', const blob = new Blob([yamlStr], { type: 'text/yaml' });
});
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `${workflow?.name || 'workflow'}.json`; a.download = `${workflow?.name || 'workflow'}.yaml`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
@@ -205,13 +205,21 @@ export default function WorkflowDetailContent({ id }: { id: string }) {
toast.success(t('workflows.exportSuccess')); toast.success(t('workflows.exportSuccess'));
}, [workflow, toWorkflowDefinition, t]); }, [workflow, toWorkflowDefinition, t]);
// Import workflow handler // Import workflow handler - supports both JSON and YAML
const handleImport = useCallback( const handleImport = useCallback(
(file: File) => { (file: File) => {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (e) => { reader.onload = (e) => {
try { try {
const importData = JSON.parse(e.target?.result as string); const content = e.target?.result as string;
let importData: any;
// Try YAML first, then JSON
try {
importData = yaml.load(content);
} catch {
importData = JSON.parse(content);
}
// Validate imported data structure // Validate imported data structure
if (!importData.nodes || !Array.isArray(importData.nodes)) { if (!importData.nodes || !Array.isArray(importData.nodes)) {
@@ -339,7 +347,7 @@ export default function WorkflowDetailContent({ id }: { id: string }) {
<div className="flex gap-2"> <div className="flex gap-2">
<input <input
type="file" type="file"
accept=".json" accept=".yaml,.yml,.json"
onChange={handleFileChange} onChange={handleFileChange}
style={{ display: 'none' }} style={{ display: 'none' }}
ref={fileInputRef} ref={fileInputRef}
@@ -419,7 +427,7 @@ export default function WorkflowDetailContent({ id }: { id: string }) {
{/* Hidden file input for import */} {/* Hidden file input for import */}
<input <input
type="file" type="file"
accept=".json" accept=".yaml,.yml,.json"
onChange={handleFileChange} onChange={handleFileChange}
style={{ display: 'none' }} style={{ display: 'none' }}
ref={fileInputRef} ref={fileInputRef}