Spaces:
Sleeping
Sleeping
import React, { useRef, useState } from 'react'; | |
import { useApp } from '../../contexts/AppContext'; | |
import Button from '../common/Button'; | |
import Modal, { ModalFooter, ModalButton } from '../common/Modal'; | |
import TextArea from '../common/TextArea'; | |
import Input from '../common/Input'; | |
interface DslFileUploaderProps { | |
groupId: string; | |
onUploadComplete?: () => void; | |
} | |
const DslFileUploader: React.FC<DslFileUploaderProps> = ({ | |
groupId, | |
onUploadComplete | |
}) => { | |
const { addDslFile } = useApp(); | |
const fileInputRef = useRef<HTMLInputElement>(null); | |
const [isUploading, setIsUploading] = useState(false); | |
const [showModal, setShowModal] = useState(false); | |
// 用于直接输入 YAML 内容的状态 | |
const [fileName, setFileName] = useState(''); | |
const [content, setContent] = useState(''); | |
const [error, setError] = useState(''); | |
// 处理文件选择 | |
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => { | |
if (!e.target.files || e.target.files.length === 0) return; | |
setIsUploading(true); | |
setError(''); | |
try { | |
const file = e.target.files[0]; | |
// 验证文件类型 | |
const validTypes = ['.yml', '.yaml', '.txt', '.json', '.dsl']; | |
const fileExtension = '.' + file.name.split('.').pop()?.toLowerCase(); | |
if (!validTypes.includes(fileExtension)) { | |
throw new Error('不支持的文件类型。请上传 YAML, JSON, TXT 或 DSL 文件'); | |
} | |
// 读取文件内容为文本 | |
const reader = new FileReader(); | |
reader.onload = async (event) => { | |
if (!event.target || typeof event.target.result !== 'string') { | |
throw new Error('文件读取失败'); | |
} | |
// 获取文件的文本内容 | |
const fileContent = event.target.result; | |
// 修改文件名,确保扩展名为 .yml | |
let fileName = file.name; | |
if (!fileName.toLowerCase().endsWith('.yml')) { | |
fileName = fileName.replace(/\.[^/.]+$/, '') + '.yml'; | |
} | |
await addDslFile(groupId, { | |
name: fileName, | |
content: fileContent | |
}); | |
if (onUploadComplete) { | |
onUploadComplete(); | |
} | |
// 重置 input | |
if (fileInputRef.current) { | |
fileInputRef.current.value = ''; | |
} | |
}; | |
reader.onerror = () => { | |
throw new Error('文件读取错误'); | |
}; | |
reader.readAsText(file); // 直接读取为文本 | |
} catch (error: any) { | |
console.error('文件上传失败:', error); | |
setError(error.message || '上传失败,请重试'); | |
} finally { | |
setIsUploading(false); | |
} | |
}; | |
// 处理直接粘贴 YAML 内容的提交 | |
const handleContentSubmit = async () => { | |
if (!fileName.trim()) { | |
setError('请输入文件名'); | |
return; | |
} | |
if (!content.trim()) { | |
setError('请输入YAML内容'); | |
return; | |
} | |
setIsUploading(true); | |
setError(''); | |
try { | |
// 确保文件名以 .yml 结尾 | |
let finalFileName = fileName; | |
if (!finalFileName.toLowerCase().endsWith('.yml')) { | |
finalFileName = finalFileName.replace(/\.[^/.]+$/, '') + '.yml'; | |
} | |
await addDslFile(groupId, { | |
name: finalFileName, | |
content: content | |
}); | |
if (onUploadComplete) { | |
onUploadComplete(); | |
} | |
// 重置表单并关闭模态框 | |
setFileName(''); | |
setContent(''); | |
setShowModal(false); | |
} catch (error: any) { | |
console.error('添加YAML失败:', error); | |
setError(error.message || '添加失败,请重试'); | |
} finally { | |
setIsUploading(false); | |
} | |
}; | |
const triggerFileInput = () => { | |
if (fileInputRef.current) { | |
fileInputRef.current.click(); | |
} | |
}; | |
return ( | |
<div> | |
<div className="flex space-x-2"> | |
{/* 文件上传按钮 */} | |
<input | |
type="file" | |
ref={fileInputRef} | |
onChange={handleFileChange} | |
style={{ display: 'none' }} | |
accept=".yml,.yaml,.txt,.json,.dsl" | |
/> | |
<Button | |
variant="primary" | |
onClick={triggerFileInput} | |
disabled={isUploading} | |
icon={ | |
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> | |
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
<polyline points="17 8 12 3 7 8"></polyline> | |
<line x1="12" y1="3" x2="12" y2="15"></line> | |
</svg> | |
} | |
> | |
{isUploading ? '上传中...' : '上传 YAML 文件'} | |
</Button> | |
{/* 添加直接粘贴内容的按钮 */} | |
<Button | |
variant="secondary" | |
onClick={() => setShowModal(true)} | |
icon={ | |
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> | |
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path> | |
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path> | |
</svg> | |
} | |
> | |
粘贴 YAML 内容 | |
</Button> | |
</div> | |
{error && ( | |
<div className="mt-2 text-sm text-red-600"> | |
{error} | |
</div> | |
)} | |
{/* 粘贴 YAML 内容的模态框 */} | |
<Modal | |
isOpen={showModal} | |
onClose={() => setShowModal(false)} | |
title="添加 YAML 内容" | |
footer={ | |
<ModalFooter> | |
<ModalButton | |
variant="secondary" | |
onClick={() => setShowModal(false)} | |
> | |
取消 | |
</ModalButton> | |
<ModalButton | |
variant="primary" | |
onClick={handleContentSubmit} | |
> | |
添加 | |
</ModalButton> | |
</ModalFooter> | |
} | |
> | |
<div className="space-y-4"> | |
{error && ( | |
<div className="bg-red-50 text-red-600 p-3 rounded-lg mb-4"> | |
{error} | |
</div> | |
)} | |
<Input | |
label="文件名" | |
value={fileName} | |
onChange={(e) => setFileName(e.target.value)} | |
placeholder="例如: workflow.yml" | |
required | |
/> | |
<TextArea | |
label="YAML 内容" | |
value={content} | |
onChange={(e) => setContent(e.target.value)} | |
placeholder="输入或粘贴 YAML 内容..." | |
rows={12} | |
required | |
/> | |
</div> | |
</Modal> | |
</div> | |
); | |
}; | |
export default DslFileUploader; |