Skip to content

Commit b822293

Browse files
committed
修复 CORS 问题:改用 iframe 嵌入 Dify
由于浏览器 CORS 限制,前端无法直接调用 Dify API。 现在使用 iframe 嵌入方式,并在顶部显示题目内容和复制按钮。 改进: - 移除 API 直接调用方式(受 CORS 限制) - 使用 iframe 嵌入 Dify WebApp - 在 iframe 上方显示题目提示区域 - 添加醒目的"复制题目"按钮 - 优化 UI 布局,提升用户体验
1 parent 0a094bd commit b822293

1 file changed

Lines changed: 49 additions & 54 deletions

File tree

src/App.jsx

Lines changed: 49 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -952,38 +952,13 @@ export default function App() {
952952
const text = `请帮我解析以下物联网题目:\n\n【题目】${question.question}\n\n【选项】\n${question.options.map((o) => `${o.id}. ${o.text}`).join('\n')}\n\n请给出正确答案并详细解析。`;
953953
setAiQuestionText(text);
954954
setAiResponse('');
955-
setAiIframeUrl('');
956-
setAiConversationId(''); // 重置会话 ID
957-
setAiLoading(true);
955+
setAiLoading(false);
958956
setShowAiModal(true);
959957

960-
// 使用 API 方式调用 Dify
961-
const controller = new AbortController();
962-
aiAbortRef.current = controller;
963-
964-
try {
965-
const resp = await callDifyApi(text, controller.signal, ''); // 新会话
966-
let full = '';
967-
await parseSSEStream(
968-
resp,
969-
(chunk) => {
970-
full += chunk;
971-
setAiResponse(full);
972-
},
973-
(convId) => {
974-
// 保存会话 ID,用于后续追问
975-
setAiConversationId(convId);
976-
},
977-
controller.signal
978-
);
979-
} catch (e) {
980-
console.error('[AI] API 调用失败:', e);
981-
// 失败时显示 iframe 作为备选
982-
setAiIframeUrl(`https://udify.app/chatbot/xg0maoDg7kzrcGT0`);
983-
} finally {
984-
setAiLoading(false);
985-
}
986-
}, [callDifyApi, parseSSEStream]);
958+
// 由于浏览器 CORS 限制,直接使用 iframe 嵌入 Dify WebApp
959+
// 用户需要在 iframe 中手动输入或粘贴题目
960+
setAiIframeUrl(`https://udify.app/chatbot/xg0maoDg7kzrcGT0`);
961+
}, []);
987962

988963
const closeAiModal = useCallback(() => {
989964
if (aiAbortRef.current) aiAbortRef.current.abort();
@@ -1744,35 +1719,55 @@ export default function App() {
17441719
>
17451720
<div className="bg-gradient-to-r from-indigo-500 to-purple-600 px-5 py-3 flex justify-between items-center text-white shrink-0">
17461721
<div className="flex items-center space-x-2.5">
1747-
<Sparkles className={`w-5 h-5 ${aiLoading ? 'animate-spin' : ''}`} />
1722+
<Sparkles className="w-5 h-5" />
17481723
<span className="font-bold text-base">AI 智能解析</span>
17491724
</div>
1750-
<div className="flex items-center gap-2">
1751-
<button
1752-
onClick={copyAiQuestion}
1753-
className="text-white/80 hover:text-white transition-colors bg-white/15 hover:bg-white/25 rounded-lg px-3 py-1.5 text-xs font-medium flex items-center gap-1"
1754-
>
1755-
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
1756-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
1757-
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
1758-
</svg>
1759-
复制题目
1760-
</button>
1761-
<button onClick={closeAiModal} className="text-white/80 hover:text-white transition-colors bg-white/10 hover:bg-white/20 rounded-full p-1.5">
1762-
<X className="w-5 h-5" />
1763-
</button>
1764-
</div>
1725+
<button onClick={closeAiModal} className="text-white/80 hover:text-white transition-colors bg-white/10 hover:bg-white/20 rounded-full p-1.5">
1726+
<X className="w-5 h-5" />
1727+
</button>
17651728
</div>
17661729

1767-
<div className="flex-1 min-h-0 bg-white relative">
1730+
<div className="flex-1 min-h-0 bg-white relative flex flex-col">
17681731
{aiIframeUrl ? (
1769-
<iframe
1770-
ref={iframeRef}
1771-
src={aiIframeUrl}
1772-
style={{ width: '100%', height: '100%', border: 'none' }}
1773-
title="AI 解析助手"
1774-
allow="microphone"
1775-
/>
1732+
<>
1733+
{/* 题目提示区域 */}
1734+
<div className="bg-amber-50 border-b border-amber-200 px-4 py-3 shrink-0">
1735+
<div className="flex items-start gap-3">
1736+
<div className="flex-shrink-0 mt-0.5">
1737+
<svg className="w-5 h-5 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1738+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
1739+
</svg>
1740+
</div>
1741+
<div className="flex-1 min-w-0">
1742+
<p className="text-sm font-medium text-amber-800 mb-1">请将以下题目复制到聊天框中:</p>
1743+
<div className="bg-white rounded-lg p-3 text-xs text-slate-700 whitespace-pre-wrap break-words border border-amber-200 max-h-32 overflow-y-auto">
1744+
{aiQuestionText}
1745+
</div>
1746+
<button
1747+
onClick={copyAiQuestion}
1748+
className="mt-2 inline-flex items-center gap-1.5 text-xs font-medium text-amber-700 hover:text-amber-800 bg-amber-100 hover:bg-amber-200 px-3 py-1.5 rounded-md transition-colors"
1749+
>
1750+
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
1751+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
1752+
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
1753+
</svg>
1754+
复制题目
1755+
</button>
1756+
</div>
1757+
</div>
1758+
</div>
1759+
1760+
{/* iframe 区域 */}
1761+
<div className="flex-1 min-h-0">
1762+
<iframe
1763+
ref={iframeRef}
1764+
src={aiIframeUrl}
1765+
style={{ width: '100%', height: '100%', border: 'none' }}
1766+
title="AI 解析助手"
1767+
allow="microphone"
1768+
/>
1769+
</div>
1770+
</>
17761771
) : aiLoading && !aiResponse ? (
17771772
<div className="flex flex-col items-center justify-center h-full">
17781773
<div className="w-10 h-10 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin mb-4" />

0 commit comments

Comments
 (0)