feat: 抽离AI请求为hooks

This commit is contained in:
Shu Guang 2025-04-20 22:24:32 +08:00
parent c3950fd4c7
commit c8f251a4a2
5 changed files with 171 additions and 189 deletions

51
src/hooks/useAIRequest.ts Normal file
View File

@ -0,0 +1,51 @@
import { useState } from 'react';
import useAddress from './useAddress';
interface AIRequestOptions {
model?: string;
maxTokens?: number;
systemPrompt?: string;
}
const useAIRequest = () => {
const [loading, setLoading] = useState(false);
const { NewAPiAddress } = useAddress();
const sendRequest = async (content: any, options: AIRequestOptions = {}) => {
setLoading(true);
try {
const response = await fetch(`${NewAPiAddress}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-mw9ekhJlSj3GeGiw0hLRSHlwdkDFst8q6oBfQrW0L15QilbY'
},
body: JSON.stringify({
model: options.model || 'gpt-4o-mini',
messages: [
{
role: 'system',
content: options.systemPrompt || '你是一个智能助手,请根据用户输入进行分析并给出专业的见解。'
},
{
role: 'user',
content
}
],
max_tokens: options.maxTokens || 2000
})
});
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
throw error;
} finally {
setLoading(false);
}
};
return { loading, sendRequest };
};
export default useAIRequest;

5
src/hooks/useAddress.tsx Normal file
View File

@ -0,0 +1,5 @@
const useAddress = () => {
const NewAPiAddress = "https://openai.933999.xyz"
return {NewAPiAddress};
}
export default useAddress;

View File

@ -1,11 +1,12 @@
import React, { useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import { CopyOutlined, DownloadOutlined } from '@ant-design/icons';
import { CopyOutlined, DownloadOutlined } from '@ant-design/icons';
import { Upload, Card, Button, Tabs, message, Radio, Space, Tooltip } from 'antd';
import type { UploadFile } from 'antd/es/upload/interface';
import Mermaid from '@/components/Mermaid';
import MonacoEditor from '@/components/MonacoEditor';
import html2canvas from 'html2canvas';
import useAIRequest from '@/hooks/useAIRequest';
const { Dragger } = Upload;
const { TabPane } = Tabs;
@ -28,86 +29,64 @@ const CodeAnalysisPage: React.FC = () => {
const [codeContent, setCodeContent] = useState<string>('');
const [activeTab, setActiveTab] = useState<string>('editor');
// 处理代码编辑
const handleCodeChange = (value: string) => {
setCodeContent(value);
};
const handleExportDiagram = async () => {
try {
const chartElement = document.querySelector('.mermaid') as HTMLElement;
if (!chartElement) {
message.warning('未找到图表元素');
return;
try {
const chartElement = document.querySelector('.mermaid') as HTMLElement;
if (!chartElement) {
message.warning('未找到图表元素');
return;
}
const canvas = await html2canvas(chartElement, {
useCORS: true,
scale: 2,
backgroundColor: '#ffffff'
});
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = `diagram_${new Date().getTime()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
message.success('导出成功');
} catch (error) {
console.error('导出失败:', error);
message.error('导出失败,请重试');
}
const canvas = await html2canvas(chartElement, {
useCORS: true,
scale: 2, // 提高导出图片质量
backgroundColor: '#ffffff'
});
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = `diagram_${new Date().getTime()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
message.success('导出成功');
} catch (error) {
console.error('导出失败:', error);
message.error('导出失败,请重试');
}
};
// 处理代码分析
const { loading: aiLoading, sendRequest } = useAIRequest();
const handleAnalyze = async () => {
if (!codeContent.trim()) {
message.warning('请输入或上传代码');
return;
}
setLoading(true);
try {
const response = await fetch('https://openai.933999.xyz/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-mw9ekhJlSj3GeGiw0hLRSHlwdkDFst8q6oBfQrW0L15QilbY'
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: analysisType === 'er'
? '请分析这段代码并生成对应的ER图使用mermaid语法,用于计算机科学与技术毕业论文。分析要点1. 实体关系 2. 属性定义 3. 关系类型'
: '请分析这段代码并生成功能模块图使用mermaid语法,用于计算机科学与技术毕业论文。。分析要点1. 模块划分 2. 依赖关系 3. 调用流程'
},
{
type: 'text',
text: codeContent
}
]
}
],
max_tokens: 2000
})
});
const data: AnalysisResponse = await response.json();
const content = data.choices[0].message.content;
// 解析返回的内容,提取 mermaid 图表代码和分析建议
try {
const content = await sendRequest([
{
type: 'text',
text: analysisType === 'er'
? '请分析这段代码并生成对应的ER图使用mermaid语法,用于计算机科学与技术毕业论文。分析要点1. 实体关系 2. 属性定义 3. 关系类型'
: '请分析这段代码并生成功能模块图使用mermaid语法,用于计算机科学与技术毕业论文。。分析要点1. 模块划分 2. 依赖关系 3. 调用流程'
},
{
type: 'text',
text: codeContent
}
]);
const [diagramPart, analysisPart] = content.split('分析建议:').map(part => part.trim());
// 提取 mermaid 代码块
const mermaidCode = diagramPart.match(/```mermaid\n([\s\S]*?)\n```/)?.[1] || diagramPart;
setDiagramCode(mermaidCode);
setAnalysisResult(analysisPart || '暂无具体分析建议');
setActiveTab('result');
@ -115,27 +94,24 @@ const CodeAnalysisPage: React.FC = () => {
} catch (error) {
console.error('分析失败:', error);
message.error('分析失败,请重试');
} finally {
setLoading(false);
}
};
// 处理文件上传
const handleUpload = async (file: File) => {
setLoading(true);
try {
// 读取文件内容
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target?.result as string;
setCodeContent(content);
setActiveTab('editor');
message.success('文件上传成功');
};
reader.readAsText(file);
return false; // 阻止默认上传行为
} catch (error) {
message.error('文件读取失败');
console.error('文件读取失败:', error);
}
setLoading(false);
};
return (
@ -176,7 +152,7 @@ const CodeAnalysisPage: React.FC = () => {
>
<Button></Button>
</Dragger>
<Button type="primary" onClick={handleAnalyze} loading={loading}>
<Button type="primary" onClick={handleAnalyze} loading={aiLoading}>
</Button>
</Space>

View File

@ -28,6 +28,7 @@ import ReactECharts from 'echarts-for-react';
import styles from './index.less';
import * as XLSX from 'xlsx';
import { marked } from 'marked';
import useAIRequest from '@/hooks/useAIRequest';
const { Dragger } = Upload;
const { TextArea } = Input;
@ -105,7 +106,9 @@ const AnalysisCenter: React.FC = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
const handleSend = async () => {
const { loading: aiLoading, sendRequest } = useAIRequest();
const handleSend = async () => {
if (!inputValue.trim()) return;
const userMessage: Message = {
@ -119,46 +122,27 @@ const AnalysisCenter: React.FC = () => {
setLoading(true);
try {
const response = await fetch('https://openai.933999.xyz/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-mw9ekhJlSj3GeGiw0hLRSHlwdkDFst8q6oBfQrW0L15QilbY'
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: '你是一个数据分析专家,请根据用户输入进行分析并生成分析报告和 ECharts 图表配置。图表配置需要包含在 ```json 代码块中。'
},
{
role: 'user',
content: [
{
type: 'text',
text: `请对以下内容进行${analysisOptions.find(opt => opt.value === analysisType)?.label},并给出专业的分析见解。
ECharts 使 \`\`\`json 包裹),配置中需要包含:
1.
2. 线
3.
4.
5.
const content = await sendRequest([
{
type: 'text',
text: `请对以下内容进行${analysisOptions.find(opt => opt.value === analysisType)?.label},并给出专业的分析见解。
ECharts 使 \`\`\`json 包裹),配置中需要包含:
1.
2. 线
3.
4.
5.
${inputValue}`
}
]
}
],
max_tokens: 2000
})
${inputValue}`
}
], {
systemPrompt: '你是一个数据分析专家,请根据用户输入进行分析并生成分析报告和 ECharts 图表配置。图表配置需要包含在 ```json 代码块中。'
});
const result = await response.json();
let chartOption;
try {
const matches = result.choices[0].message.content.match(/```json\n([\s\S]*?)\n```/);
const matches = content.match(/```json\n([\s\S]*?)\n```/);
if (matches && matches[1]) {
chartOption = JSON.parse(matches[1]);
}
@ -169,7 +153,7 @@ const AnalysisCenter: React.FC = () => {
const assistantMessage: Message = {
type: 'assistant',
content: result.choices[0].message.content.replace(/```json\n[\s\S]*?\n```/g, '').trim(),
content: content.replace(/```json\n[\s\S]*?\n```/g, '').trim(),
timestamp: Date.now(),
charts: chartOption,
};
@ -181,7 +165,7 @@ const AnalysisCenter: React.FC = () => {
message.error('分析请求失败');
setLoading(false);
}
}
};
const handleFileAnalysis = async (file: File) => {

View File

@ -8,6 +8,7 @@ import styles from './index.less';
import { Document, Packer, Paragraph as DocxParagraph, TextRun } from 'docx';
import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx';
import useAIRequest from '@/hooks/useAIRequest';
const { Dragger } = Upload;
const { TextArea } = Input;
@ -96,37 +97,22 @@ const ReportPage: React.FC = () => {
}
};
const analyzeData = async (content: any) => {
try {
const response = await fetch('https://openai.933999.xyz/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-mw9ekhJlSj3GeGiw0hLRSHlwdkDFst8q6oBfQrW0L15QilbY'
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: '请分析这些数据的趋势和关键信息,并给出专业的分析见解。'
},
content
]
}
],
max_tokens: 1000
})
});
const { loading: aiLoading, sendRequest } = useAIRequest();
const data: AnalysisResponse = await response.json();
const analyzeData = async (content: any) => {
setLoading(true);
try {
const result = await sendRequest([
{
type: 'text',
text: '请分析这些数据的趋势和关键信息,并给出专业的分析见解。'
},
content
]);
setPreviewData({
columns: ['分析结果'],
data: [[data.choices[0].message.content]]
data: [[result]]
});
message.success('数据分析成功');
@ -135,7 +121,38 @@ const ReportPage: React.FC = () => {
message.error('数据分析失败');
console.error('API调用失败:', error);
} finally {
setLoading(false);
setLoading(false);
}
};
const generateReport = async () => {
try {
const result = await sendRequest([
{
type: 'text',
text: `请基于以下分析目标和数据生成一份详细的markdown格式分析报告包含标题、概述、详细分析等章节${goal}\n${previewData?.data[0][0]}`
}
], {
maxTokens: 2000
});
// 解析 markdown 内容
const titleMatch = result.match(/^#\s+(.+)$/m);
const title = titleMatch ? titleMatch[1] : '数据分析报告';
setReportData({
title,
summary: '',
sections: [],
charts: [],
markdown: result
});
setWordUrl('https://example.com/report.docx');
message.success('报告生成成功');
setCurrentStep(2);
} catch (error) {
message.error('报告生成失败');
}
};
@ -186,57 +203,6 @@ const handleDownload = async () => {
}
};
const generateReport = async () => {
setLoading(true);
try {
const response = await fetch('https://openai.933999.xyz/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-mw9ekhJlSj3GeGiw0hLRSHlwdkDFst8q6oBfQrW0L15QilbY'
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: `请基于以下分析目标和数据生成一份详细的markdown格式分析报告包含标题、概述、详细分析等章节${goal}\n${previewData?.data[0][0]}`
}
]
}
],
max_tokens: 2000
})
});
const data: AnalysisResponse = await response.json();
const markdownContent = data.choices[0].message.content;
// 解析 markdown 内容
const titleMatch = markdownContent.match(/^#\s+(.+)$/m);
const title = titleMatch ? titleMatch[1] : '数据分析报告';
setReportData({
title,
summary: '',
sections: [],
charts: [],
markdown: markdownContent
});
setWordUrl('https://example.com/report.docx');
message.success('报告生成成功');
setCurrentStep(2);
} catch (error) {
message.error('报告生成失败');
} finally {
setLoading(false);
}
};
const renderPreview = () => {
if (!previewData) return null;
return (