diff --git a/src/hooks/useAIRequest.ts b/src/hooks/useAIRequest.ts new file mode 100644 index 0000000..5ba35e3 --- /dev/null +++ b/src/hooks/useAIRequest.ts @@ -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; \ No newline at end of file diff --git a/src/hooks/useAddress.tsx b/src/hooks/useAddress.tsx new file mode 100644 index 0000000..7c8176e --- /dev/null +++ b/src/hooks/useAddress.tsx @@ -0,0 +1,5 @@ +const useAddress = () => { + const NewAPiAddress = "https://openai.933999.xyz" + return {NewAPiAddress}; +} +export default useAddress; \ No newline at end of file diff --git a/src/pages/Code/index.tsx b/src/pages/Code/index.tsx index 24ec98d..efac552 100644 --- a/src/pages/Code/index.tsx +++ b/src/pages/Code/index.tsx @@ -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(''); const [activeTab, setActiveTab] = useState('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 = () => { > - diff --git a/src/pages/Forecast/index.tsx b/src/pages/Forecast/index.tsx index 6f30cc3..c4ab780 100644 --- a/src/pages/Forecast/index.tsx +++ b/src/pages/Forecast/index.tsx @@ -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) => { diff --git a/src/pages/Report/index.tsx b/src/pages/Report/index.tsx index e15ec07..972173a 100644 --- a/src/pages/Report/index.tsx +++ b/src/pages/Report/index.tsx @@ -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 (