feat(ssa): Complete V11 UI development and frontend-backend integration - Pixel-perfect V11 UI, multi-task support, Word export, input overlay fix, code cleanup. MVP Phase 1 core 95% complete.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
108
frontend-v2/src/modules/ssa/components/SSACodeModal.tsx
Normal file
108
frontend-v2/src/modules/ssa/components/SSACodeModal.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* SSACodeModal - V11 R代码模态框
|
||||
*
|
||||
* 100% 还原 V11 原型图
|
||||
* 调用后端 API 获取真实执行代码
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { X, Download, Loader2 } from 'lucide-react';
|
||||
import { useSSAStore } from '../stores/ssaStore';
|
||||
import { useAnalysis } from '../hooks/useAnalysis';
|
||||
|
||||
export const SSACodeModal: React.FC = () => {
|
||||
const { codeModalVisible, setCodeModalVisible, executionResult, addToast } = useSSAStore();
|
||||
const { downloadCode } = useAnalysis();
|
||||
const [code, setCode] = useState<string>('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (codeModalVisible) {
|
||||
loadCode();
|
||||
}
|
||||
}, [codeModalVisible]);
|
||||
|
||||
const loadCode = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const result = await downloadCode();
|
||||
const text = await result.blob.text();
|
||||
setCode(text);
|
||||
} catch (error) {
|
||||
if (executionResult?.reproducibleCode) {
|
||||
setCode(executionResult.reproducibleCode);
|
||||
} else {
|
||||
setCode('# 暂无可用代码\n# 请先执行分析');
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!codeModalVisible) return null;
|
||||
|
||||
const handleClose = () => {
|
||||
setCodeModalVisible(false);
|
||||
};
|
||||
|
||||
const handleDownload = async () => {
|
||||
try {
|
||||
const result = await downloadCode();
|
||||
const url = URL.createObjectURL(result.blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = result.filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
addToast('R 脚本已下载', 'success');
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
addToast('下载失败', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(code);
|
||||
addToast('代码已复制', 'success');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="code-modal-overlay" onClick={handleClose}>
|
||||
<div className="code-modal pop-in" onClick={(e) => e.stopPropagation()}>
|
||||
<header className="code-modal-header">
|
||||
<h3 className="code-modal-title">
|
||||
<span className="r-icon">R</span>
|
||||
R 源代码交付
|
||||
</h3>
|
||||
<button className="code-modal-close" onClick={handleClose}>
|
||||
<X size={16} />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div className="code-modal-body">
|
||||
{isLoading ? (
|
||||
<div className="code-loading">
|
||||
<Loader2 size={24} className="spin" />
|
||||
<span>加载代码中...</span>
|
||||
</div>
|
||||
) : (
|
||||
<pre className="code-block">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<footer className="code-modal-footer">
|
||||
<button className="copy-btn" onClick={handleCopy} disabled={isLoading}>
|
||||
复制代码
|
||||
</button>
|
||||
<button className="download-btn" onClick={handleDownload} disabled={isLoading}>
|
||||
<Download size={14} />
|
||||
下载 .R 文件
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SSACodeModal;
|
||||
Reference in New Issue
Block a user