Skip to content

Commit c26567f

Browse files
committed
feat: 接入 Dify AI 答疑助手 - 全局悬浮气泡 + 逐题 AI 解析
1 parent f72a939 commit c26567f

3 files changed

Lines changed: 169 additions & 14 deletions

File tree

index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,11 @@
2323
<body>
2424
<div id="root"></div>
2525
<script type="module" src="/src/main.jsx"></script>
26+
<script>
27+
window.difyChatbotConfig = {
28+
token: 'xg0maoDg7kzrcGT0'
29+
}
30+
</script>
31+
<script src="https://udify.app/embed.min.js" id="xg0maoDg7kzrcGT0" defer></script>
2632
</body>
2733
</html>

src/App.jsx

Lines changed: 119 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ export default function App() {
294294
// 全局警告弹窗状态
295295
const [globalWarning, setGlobalWarning] = useState(null);
296296

297+
// AI 解析弹窗相关状态
298+
const [showAiModal, setShowAiModal] = useState(false);
299+
const [aiQuestionText, setAiQuestionText] = useState('');
300+
const [aiQuestionTitle, setAiQuestionTitle] = useState('');
301+
297302
// 题库状态(从数据库加载)
298303
const [MOCK_QUESTION_BANK, setMOCK_QUESTION_BANK] = useState(DEFAULT_QUESTION_BANK);
299304
const [isLoadingQuestions, setIsLoadingQuestions] = useState(false);
@@ -849,6 +854,35 @@ export default function App() {
849854
};
850855
}, [answeredIds, wrongQuestionIds]);
851856

857+
// --- AI 解析功能 ---
858+
const openAiAnalysis = (question) => {
859+
const title = question.question;
860+
const text = `请帮我解析以下物联网题目:\n\n【题目】${question.question}\n\n【选项】\n${question.options.map((o) => `${o.id}. ${o.text}`).join('\n')}\n\n请给出正确答案并详细解析。`;
861+
setAiQuestionTitle(title);
862+
setAiQuestionText(text);
863+
setShowAiModal(true);
864+
};
865+
866+
const closeAiModal = () => {
867+
setShowAiModal(false);
868+
setAiQuestionText('');
869+
setAiQuestionTitle('');
870+
};
871+
872+
const copyAiQuestion = () => {
873+
navigator.clipboard.writeText(aiQuestionText).then(() => {
874+
}).catch(() => {
875+
const textarea = document.createElement('textarea');
876+
textarea.value = aiQuestionText;
877+
textarea.style.position = 'fixed';
878+
textarea.style.opacity = '0';
879+
document.body.appendChild(textarea);
880+
textarea.select();
881+
document.execCommand('copy');
882+
document.body.removeChild(textarea);
883+
});
884+
};
885+
852886
// --- 组件视图 ---
853887

854888
const WelcomeView = () => (
@@ -1012,13 +1046,22 @@ export default function App() {
10121046
</span>
10131047
)}
10141048
</div>
1015-
<button
1016-
onClick={() => handleFeedback(currentQ)}
1017-
className="text-slate-400 hover:text-orange-500 transition-colors flex items-center gap-1 text-xs font-medium"
1018-
title="题目有误?点击反馈"
1019-
>
1020-
<Flag className="w-4 h-4" /> 纠错
1021-
</button>
1049+
<div className="flex items-center gap-2">
1050+
<button
1051+
onClick={() => openAiAnalysis(currentQ)}
1052+
className="ai-btn-glow text-indigo-500 hover:text-indigo-700 bg-indigo-50 hover:bg-indigo-100 transition-colors flex items-center gap-1 text-xs font-medium px-2.5 py-1.5 rounded-full"
1053+
title="AI 智能解析本题"
1054+
>
1055+
<Sparkles className="w-4 h-4" /> AI 解析
1056+
</button>
1057+
<button
1058+
onClick={() => handleFeedback(currentQ)}
1059+
className="text-slate-400 hover:text-orange-500 transition-colors flex items-center gap-1 text-xs font-medium"
1060+
title="题目有误?点击反馈"
1061+
>
1062+
<Flag className="w-4 h-4" /> 纠错
1063+
</button>
1064+
</div>
10221065
</div>
10231066

10241067
<h2 className="text-xl md:text-2xl font-bold text-slate-800 leading-relaxed mb-8">
@@ -1269,7 +1312,10 @@ export default function App() {
12691312
</div>
12701313
<div className="bg-slate-50 p-3 rounded text-sm text-slate-600 flex justify-between items-start">
12711314
<div>{q.explanation}</div>
1272-
<button onClick={() => handleFeedback(q)} className="ml-4 text-slate-400 hover:text-orange-500 transition-colors"><Flag className="w-4 h-4" /></button>
1315+
<div className="flex items-center gap-1 ml-4">
1316+
<button onClick={() => openAiAnalysis(q)} className="text-indigo-400 hover:text-indigo-600 transition-colors" title="AI 解析"><Sparkles className="w-4 h-4" /></button>
1317+
<button onClick={() => handleFeedback(q)} className="text-slate-400 hover:text-orange-500 transition-colors"><Flag className="w-4 h-4" /></button>
1318+
</div>
12731319
</div>
12741320
</div>
12751321
)
@@ -1389,12 +1435,19 @@ export default function App() {
13891435
) : (
13901436
<>
13911437
<div className="mb-6">
1392-
<div className="flex items-center gap-2 mb-4">
1393-
<span className="bg-slate-100 text-slate-600 text-xs font-bold px-2 py-1 rounded uppercase tracking-wider">{instantQuestion.category}</span>
1394-
{wrongQuestionIds.has(instantQuestion.id) && (
1395-
<span className="bg-red-50 text-red-600 text-xs font-bold px-2 py-1 rounded flex items-center"><AlertTriangle className="w-3 h-3 mr-1"/> 曾做错</span>
1396-
)}
1397-
</div>
1438+
<div className="flex items-center gap-2 mb-4">
1439+
<span className="bg-slate-100 text-slate-600 text-xs font-bold px-2 py-1 rounded uppercase tracking-wider">{instantQuestion.category}</span>
1440+
{wrongQuestionIds.has(instantQuestion.id) && (
1441+
<span className="bg-red-50 text-red-600 text-xs font-bold px-2 py-1 rounded flex items-center"><AlertTriangle className="w-3 h-3 mr-1"/> 曾做错</span>
1442+
)}
1443+
<button
1444+
onClick={() => openAiAnalysis(instantQuestion)}
1445+
className="ai-btn-glow text-indigo-500 hover:text-indigo-700 bg-indigo-50 hover:bg-indigo-100 transition-colors flex items-center gap-1 text-xs font-medium px-2.5 py-1.5 rounded-full ml-auto"
1446+
title="AI 智能解析本题"
1447+
>
1448+
<Sparkles className="w-3.5 h-3.5" /> AI 解析
1449+
</button>
1450+
</div>
13981451
<h3 className="text-xl font-bold text-slate-800 leading-relaxed">{instantQuestion.question}</h3>
13991452
</div>
14001453

@@ -1525,6 +1578,58 @@ export default function App() {
15251578
</div>
15261579
</div>
15271580
)}
1581+
1582+
{/* AI 解析弹窗 */}
1583+
{showAiModal && (
1584+
<div className="fixed inset-0 z-[110] flex items-center justify-center bg-black/60 backdrop-blur-sm" onClick={closeAiModal}>
1585+
<div className="bg-white w-full max-w-3xl mx-4 rounded-2xl shadow-2xl overflow-hidden ai-modal-enter max-h-[90vh] flex flex-col" onClick={(e) => e.stopPropagation()}>
1586+
<div className="bg-gradient-to-r from-indigo-500 to-purple-600 p-4 flex justify-between items-center text-white shrink-0">
1587+
<div className="flex items-center space-x-2">
1588+
<Sparkles className="w-6 h-6" />
1589+
<span className="font-bold text-lg">AI 智能解析</span>
1590+
</div>
1591+
<button onClick={closeAiModal} className="text-white/80 hover:text-white transition-colors bg-white/10 rounded-full p-1">
1592+
<X className="w-5 h-5" />
1593+
</button>
1594+
</div>
1595+
1596+
<div className="p-4 bg-indigo-50 border-b border-indigo-100 shrink-0">
1597+
<p className="text-sm text-slate-700 font-medium line-clamp-2" title={aiQuestionTitle}>{aiQuestionTitle}</p>
1598+
<button
1599+
onClick={copyAiQuestion}
1600+
className="mt-2 flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-800 bg-white px-3 py-1.5 rounded-full border border-indigo-200 hover:bg-indigo-100 transition-colors"
1601+
>
1602+
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
1603+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
1604+
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
1605+
</svg>
1606+
复制题目文本
1607+
</button>
1608+
</div>
1609+
1610+
<div className="flex-1 min-h-0">
1611+
<iframe
1612+
src="https://udify.app/chatbot/xg0maoDg7kzrcGT0"
1613+
style={{ width: '100%', height: '100%', minHeight: '420px', border: 'none' }}
1614+
title="AI 解析助手"
1615+
onLoad={(e) => {
1616+
setTimeout(() => {
1617+
try {
1618+
e.target.contentWindow.postMessage({
1619+
type: 'dify-chatbot-send',
1620+
data: {
1621+
query: aiQuestionText,
1622+
inputs: {}
1623+
}
1624+
}, 'https://udify.app');
1625+
} catch (err) {}
1626+
}, 800);
1627+
}}
1628+
/>
1629+
</div>
1630+
</div>
1631+
</div>
1632+
)}
15281633
</div>
15291634
);
15301635
}

src/index.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,47 @@
2424
}
2525
}
2626
}
27+
28+
/* Dify 全局气泡样式覆盖 */
29+
#dify-chatbot-bubble-button {
30+
background-color: #6366F1 !important;
31+
box-shadow: 0 4px 24px rgba(99, 102, 241, 0.4) !important;
32+
width: 56px !important;
33+
height: 56px !important;
34+
}
35+
36+
#dify-chatbot-bubble-window {
37+
width: 28rem !important;
38+
height: 36rem !important;
39+
max-height: 80vh !important;
40+
}
41+
42+
/* AI 解析自定义弹窗 */
43+
@keyframes aiModalSlideUp {
44+
from {
45+
opacity: 0;
46+
transform: translateY(40px) scale(0.97);
47+
}
48+
to {
49+
opacity: 1;
50+
transform: translateY(0) scale(1);
51+
}
52+
}
53+
54+
.ai-modal-enter {
55+
animation: aiModalSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
56+
}
57+
58+
/* 按钮呼吸灯效果 */
59+
@keyframes aiPulse {
60+
0%, 100% {
61+
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.4);
62+
}
63+
50% {
64+
box-shadow: 0 0 0 8px rgba(99, 102, 241, 0);
65+
}
66+
}
67+
68+
.ai-btn-glow {
69+
animation: aiPulse 2s infinite;
70+
}

0 commit comments

Comments
 (0)