refactor: 优化流式输出与状态管理
- 重构 DeepSeekAPI.js,实现稳健的 SSE 流式解析 - 将核心业务逻辑(生成、分析)移入 appStore.js - 优化 WriterPanel 和 AnalysisPanel 组件,移除冗余逻辑 - 更新文档,补充架构演进说明
This commit is contained in:
@@ -203,81 +203,12 @@ const applyParadigm = (paradigm) => {
|
||||
|
||||
// 分析文章
|
||||
const analyzeArticle = async () => {
|
||||
if (!analysisText.value.trim()) {
|
||||
alert('请输入要分析的文章内容')
|
||||
return
|
||||
}
|
||||
|
||||
isAnalyzing.value = true
|
||||
appStore.analysisResult = {
|
||||
paradigm: '分析中...',
|
||||
paradigmType: null,
|
||||
analysis: '',
|
||||
timestamp: new Date()
|
||||
}
|
||||
|
||||
try {
|
||||
const api = new DeepSeekAPI({
|
||||
url: appStore.apiUrl,
|
||||
key: appStore.apiKey
|
||||
})
|
||||
|
||||
const response = await api.analyzeContent(analysisText.value)
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let fullContent = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
const lines = chunk.split('\n').filter(line => line.trim())
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6)
|
||||
if (data === '[DONE]') continue
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data)
|
||||
const content = parsed.choices?.[0]?.delta?.content || ''
|
||||
if (content) {
|
||||
fullContent += content
|
||||
// 实时更新分析结果
|
||||
appStore.analysisResult = {
|
||||
paradigm: '分析中...',
|
||||
paradigmType: null,
|
||||
analysis: fullContent,
|
||||
timestamp: new Date()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分析完成后检测范式类型
|
||||
const detectedParadigm = detectParadigm(fullContent)
|
||||
appStore.analysisResult = {
|
||||
paradigm: detectedParadigm.name,
|
||||
paradigmType: detectedParadigm.type,
|
||||
analysis: fullContent,
|
||||
timestamp: new Date()
|
||||
}
|
||||
|
||||
const result = await appStore.analyzeArticleAction(analysisText.value, detectParadigm)
|
||||
// 添加到历史记录
|
||||
addToHistory(detectedParadigm.name, analysisText.value, fullContent)
|
||||
addToHistory(result.paradigm, analysisText.value, result.content)
|
||||
} catch (error) {
|
||||
appStore.analysisResult = {
|
||||
error: true,
|
||||
message: error.message
|
||||
}
|
||||
} finally {
|
||||
isAnalyzing.value = false
|
||||
alert(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,9 +130,6 @@
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { buildPrompt } from '../utils/promptBuilder.js'
|
||||
import DeepSeekAPI from '../api/deepseek.js'
|
||||
import { marked } from 'marked'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const {
|
||||
@@ -144,7 +141,6 @@ const {
|
||||
selectedTags,
|
||||
customConstraint,
|
||||
isGenerating,
|
||||
generatedContent,
|
||||
showPromptDebug,
|
||||
apiUrl,
|
||||
apiKey
|
||||
@@ -157,10 +153,7 @@ const presetTags = ['Markdown格式', '总分总结构', '数据支撑', '语气
|
||||
// 添加参考案例
|
||||
const addReference = () => {
|
||||
if (!newRefTitle.value || !newRefContent.value) return
|
||||
references.value.push({
|
||||
title: newRefTitle.value,
|
||||
content: newRefContent.value
|
||||
})
|
||||
appStore.addReferenceFromAnalysis(newRefTitle.value, newRefContent.value)
|
||||
newRefTitle.value = ''
|
||||
newRefContent.value = ''
|
||||
showRefInput.value = false
|
||||
@@ -182,54 +175,10 @@ const toggleTag = (tag) => {
|
||||
|
||||
// 生成内容
|
||||
const generateContent = async () => {
|
||||
if (!apiUrl.value || !apiKey.value || apiKey.value === 'YOUR_KEY') {
|
||||
alert('请先配置 API 地址和 API Key')
|
||||
return
|
||||
}
|
||||
|
||||
isGenerating.value = true
|
||||
generatedContent.value = ''
|
||||
|
||||
try {
|
||||
const api = new DeepSeekAPI({ url: apiUrl.value, key: apiKey.value })
|
||||
const prompt = buildPrompt(inputTask.value, [...selectedTags.value, customConstraint.value].filter(Boolean), references.value)
|
||||
|
||||
const response = await api.generateContent(prompt)
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
const lines = buffer.split('\n')
|
||||
buffer = lines.pop() // 最后一行可能不完整,存入 buffer
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
if (!trimmedLine || !trimmedLine.startsWith('data: ')) continue
|
||||
|
||||
const data = trimmedLine.slice(6)
|
||||
if (data === '[DONE]') break
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data)
|
||||
const content = parsed.choices?.[0]?.delta?.content || ''
|
||||
if (content) {
|
||||
generatedContent.value += content
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('解析流数据失败:', e, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
await appStore.generateContentAction()
|
||||
} catch (error) {
|
||||
console.error('生成内容失败:', error)
|
||||
generatedContent.value = `## 错误\n\n${error.message}\n\n请检查 API 配置是否正确。`
|
||||
} finally {
|
||||
isGenerating.value = false
|
||||
alert(error.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user