Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
00694c49
提交
00694c49
authored
1月 23, 2026
作者:
朱亚刚
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
完成pdf写报接口接入
上级
3c267bc1
隐藏空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
106 行增加
和
227 行删除
+106
-227
index.vue
src/views/writtingAsstaint/index.vue
+106
-227
没有找到文件。
src/views/writtingAsstaint/index.vue
浏览文件 @
00694c49
...
@@ -294,60 +294,63 @@ const getStreamChat = async (search, inputValue) => {
...
@@ -294,60 +294,63 @@ const getStreamChat = async (search, inputValue) => {
ElMessage
.
error
(
'文件解析失败,请重新选择'
);
ElMessage
.
error
(
'文件解析失败,请重新选择'
);
return
;
return
;
}
}
callSse
Api
(
rawFile
)
callSse
WithPdf
(
rawFile
)
}
else
{
}
else
{
abortController
.
value
=
new
AbortController
();
const
params
=
{
const
params
=
{
query
:
writtingTitle
.
value
,
// "输出一篇报文"
query
:
writtingTitle
.
value
,
// "输出一篇报文"
desc
:
descText
.
value
,
desc
:
descText
.
value
,
topic
:
curTempTitle
.
value
// 政令、智库、法案、清单
topic
:
curTempTitle
.
value
// 政令、智库、法案、清单
};
};
callSseWithAi
(
params
)
}
};
const
callSseWithPdf
=
async
(
selectedFile
)
=>
{
abortController
.
value
=
new
AbortController
();
try
{
// 构造FormData(和后端字段名保持一致)
const
formData
=
new
FormData
();
formData
.
append
(
'pdf'
,
selectedFile
);
fetchEventSource
(
"/sseWrite/api/v1/workflow/invoke"
,
{
// 调用fetchEventSource(核心:支持POST+FormData+SSE)
method
:
"POST"
,
await
fetchEventSource
(
'/pdfSse/api/v1/order/pdf/extract/report/sse'
,
{
method
:
'POST'
,
// 关键:设置POST方法
body
:
formData
,
// 关键:传递PDF文件的FormData
signal
:
abortController
.
value
.
signal
,
// 中断信号
headers
:
{
headers
:
{
"Content-Type"
:
"application/json"
// 禁用默认的SSE协议头(避免和文件上传冲突)
'Accept'
:
'text/event-stream'
,
'Cache-Control'
:
'no-cache'
,
'Connection'
:
'keep-alive'
},
},
body
:
JSON
.
stringify
(
params
),
// 禁用自动重连(可选,根据后端配置)
signal
:
abortController
.
value
.
signal
,
retry
:
0
,
openWhenHidden
:
true
,
// 核心:原生onmessage回调(无需手动分割/解析)
async
onopen
(
res
)
{
async
onopen
(
res
)
{
console
.
log
(
"流式回答开始"
,
res
);
console
.
log
(
"流式回答开始"
,
res
);
isGenerating
.
value
=
true
;
isGenerating
.
value
=
true
;
isShowProcess
.
value
=
true
;
isShowProcess
.
value
=
true
;
},
},
async
onmessage
(
res
)
{
async
onmessage
(
res
)
{
let
msgData
=
JSON
.
parse
(
res
.
data
);
const
{
data
,
event
}
=
res
console
.
log
(
"resss"
,
msgData
.
data
);
const
jsonData
=
JSON
.
parse
(
data
)
console
.
log
(
"msgData"
,
msgData
);
switch
(
event
)
{
let
str
=
msgData
.
data
;
case
"progress"
:
if
(
msgData
.
event_type
===
"stream_agent_out"
)
{
processContent
.
value
+=
`
${
getFormattedTime
()}
:
${
jsonData
.
message
}
\r\n`
;
if
(
str
!==
"[DONE]"
)
{
updateProcess
(
processContent
.
value
,
scrollProcessContainer
.
value
);
reportContent
.
value
+=
str
;
break
;
if
(
reportContent
.
value
.
includes
(
"./out/img"
))
{
case
"result"
:
reportContent
.
value
=
reportContent
.
value
.
replaceAll
(
"./out/img"
,
"http://8.140.26.4:10017/out/img"
);
callSseWithAi
({
// console.log(111, reportContent.value);
query
:
writtingTitle
.
value
,
// "输出一篇报文"
}
desc
:
descText
.
value
,
updateContent
(
reportContent
.
value
,
scrollContainer
.
value
);
topic
:
"政令"
,
}
else
{
result
:
data
// 政令、智库、法案、清单
isGenerating
.
value
=
false
;
})
ElMessage
.
success
(
"报文生成结束"
);
default
:
abortController
.
value
.
abort
();
break
;
abortController
.
value
=
new
AbortController
();
}
}
else
if
(
msgData
.
event_type
===
"workflow_complete"
)
{
ElMessage
.
success
(
"报文生成结束"
);
isGenerating
.
value
=
false
;
abortController
.
value
.
abort
();
abortController
.
value
=
new
AbortController
();
}
else
if
(
msgData
.
event_type
.
toLowerCase
().
includes
(
"error"
))
{
}
else
{
processContent
.
value
+=
str
;
curAgentTool
.
value
=
msgData
.
tool
;
updateProcess
(
processContent
.
value
,
scrollProcessContainer
.
value
);
}
}
},
},
onerror
(
error
)
{
onerror
(
error
)
{
ElMessage
({
ElMessage
({
...
@@ -358,213 +361,89 @@ const getStreamChat = async (search, inputValue) => {
...
@@ -358,213 +361,89 @@ const getStreamChat = async (search, inputValue) => {
abortController
.
value
=
new
AbortController
();
abortController
.
value
=
new
AbortController
();
throw
new
Error
(
error
);
throw
new
Error
(
error
);
}
}
}).
catch
(
error
=>
{
ElMessage
({
message
:
"写报生成报错!"
,
type
:
"warning"
});
abortController
.
value
.
abort
();
abortController
.
value
=
new
AbortController
();
throw
new
Error
(
error
);
});
}
};
const
callSseApi
=
async
(
selectedFile
)
=>
{
abortController
.
value
=
new
AbortController
();
try
{
const
formData
=
new
FormData
();
formData
.
append
(
'pdf'
,
selectedFile
);
const
response
=
await
fetch
(
'/pdfSse/api/v1/order/pdf/extract/report/sse'
,
{
method
:
'POST'
,
body
:
formData
,
signal
:
abortController
.
value
.
signal
,
// 绑定中断信号
});
});
if
(
!
response
.
ok
)
{
throw
new
Error
(
`请求失败:
${
response
.
status
}
${
response
.
statusText
}
`
);
}
console
.
log
(
"流式回答开始"
,
res
);
isGenerating
.
value
=
true
;
isShowProcess
.
value
=
true
;
// 读取流式响应
const
reader
=
response
.
body
.
getReader
();
const
decoder
=
new
TextDecoder
();
let
buffer
=
''
;
// 消息缓冲区
// 定义SSE消息回调(和原生onmessage用法一致)
const
onmessage
=
(
res
)
=>
{
const
{
type
,
data
}
=
res
if
(
type
===
"progress"
)
{
processContent
.
value
+=
data
.
message
;
updateProcess
(
processContent
.
value
,
scrollProcessContainer
.
value
);
}
};
// 错误回调
const
onerror
=
(
error
)
=>
{
};
// 连接关闭回调
const
onclose
=
()
=>
{
};
// 循环读取并解析SSE消息(核心修复换行符兼容)
while
(
true
)
{
try
{
const
{
done
,
value
}
=
await
reader
.
read
();
// 连接正常关闭
if
(
done
)
{
onclose
();
break
;
}
// 1. 解码二进制流为文本,追加到缓冲区
buffer
+=
decoder
.
decode
(
value
,
{
stream
:
true
});
// 2. 分割完整消息(兼容 \r\n\r\n 和 \n\n 换行符)
const
fullMessages
=
buffer
.
split
(
/
(\r\n
|
\n){2}
/
);
// 过滤空字符串,只保留有效消息
const
validMessages
=
fullMessages
.
filter
(
msg
=>
msg
.
trim
()
!==
''
);
// 3. 最后一条可能是不完整消息,放回缓冲区
let
remainingBuffer
=
''
;
if
(
validMessages
.
length
>
0
)
{
remainingBuffer
=
validMessages
.
pop
()
||
''
;
}
// 4. 逐条解析有效消息
validMessages
.
forEach
(
fullMsg
=>
{
const
sseMsg
=
parseSSEMessage
(
fullMsg
);
// 过滤心跳/注释消息(以:开头的消息)
if
(
sseMsg
.
isComment
)
return
;
// 触发onmessage回调
if
(
sseMsg
.
data
)
{
debugger
onmessage
({
type
:
sseMsg
.
event
||
'message'
,
data
:
JSON
.
parse
(
sseMsg
.
data
),
// 解析为JSON对象
timeStamp
:
Date
.
now
()
});
}
});
// 重置缓冲区(只保留不完整的消息)
buffer
=
remainingBuffer
;
}
catch
(
error
)
{
// 排除主动中断的情况
if
(
error
.
name
!==
'AbortError'
)
{
onerror
(
error
);
}
break
;
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
// 捕获请求级别的错误
if
(
error
.
name
!==
'AbortError'
)
{
if
(
error
.
name
!==
'AbortError'
)
{
ElMessage
.
error
(
`请求
异常
:
${
error
.
message
}
`
);
ElMessage
.
error
(
`请求
失败
:
${
error
.
message
}
`
);
isLoading
.
value
=
false
;
isLoading
.
value
=
false
;
}
}
}
}
};
};
// 工具函数:严格按SSE协议解析单条消息
const
callSseWithAi
=
async
(
params
)
=>
{
const
parseSSEMessage
=
(
rawMsg
)
=>
{
abortController
.
value
=
new
AbortController
();
// 兼容 \r\n 和 \n 分割行
fetchEventSource
(
"/sseWrite/api/v1/workflow/invoke"
,
{
const
lines
=
rawMsg
.
split
(
/
\r\n
|
\n
/
);
method
:
"POST"
,
const
result
=
{
headers
:
{
event
:
'progress'
,
// 默认事件类型
"Content-Type"
:
"application/json"
data
:
''
,
// 消息数据
},
isComment
:
false
// 是否是注释/心跳消息
body
:
JSON
.
stringify
(
params
),
};
signal
:
abortController
.
value
.
signal
,
openWhenHidden
:
true
,
lines
.
forEach
(
line
=>
{
async
onopen
(
res
)
{
line
=
line
.
trimEnd
();
// 去掉行尾空白符
console
.
log
(
"流式回答开始"
,
res
);
isGenerating
.
value
=
true
;
// 注释行(以:开头):心跳消息,标记为注释
isShowProcess
.
value
=
true
;
if
(
line
.
startsWith
(
':'
))
{
},
result
.
isComment
=
true
;
async
onmessage
(
res
)
{
return
;
let
msgData
=
JSON
.
parse
(
res
.
data
);
}
console
.
log
(
"resss"
,
msgData
.
data
);
console
.
log
(
"msgData"
,
msgData
);
// 解析event行(event: xxx)
let
str
=
msgData
.
data
;
if
(
line
.
startsWith
(
'event:'
))
{
if
(
msgData
.
event_type
===
"stream_agent_out"
)
{
result
.
event
=
line
.
slice
(
6
).
trim
();
if
(
str
!==
"[DONE]"
)
{
return
;
reportContent
.
value
+=
str
;
}
if
(
reportContent
.
value
.
includes
(
"./out/img"
))
{
reportContent
.
value
=
reportContent
.
value
.
replaceAll
(
"./out/img"
,
"http://8.140.26.4:10017/out/img"
);
// 解析data行(data: xxx),支持多行data拼接
// console.log(111, reportContent.value);
if
(
line
.
startsWith
(
'data:'
))
{
}
result
.
data
+=
line
.
slice
(
5
).
trim
();
updateContent
(
reportContent
.
value
,
scrollContainer
.
value
);
return
;
}
else
{
}
isGenerating
.
value
=
false
;
ElMessage
.
success
(
"报文生成结束"
);
// 空行忽略
abortController
.
value
.
abort
();
if
(
line
===
''
)
return
;
abortController
.
value
=
new
AbortController
();
});
return
result
;
};
const
ele
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
// 转换FormData为Blob(适配SSE的POST请求体)
const
formDataBlob
=
new
Blob
([
// 模拟multipart/form-data的边界符(简化版,实际可复用浏览器自动生成的)
...
Array
.
from
(
formData
.
entries
()).
map
(([
key
,
value
])
=>
{
return
`--boundary\r\nContent-Disposition: form-data; name="
${
key
}
"
${
value
instanceof
File
?
`; filename="
${
value
.
name
}
"\r\nContent-Type:
${
value
.
type
}
`
:
''
}
\r\n\r\n
${
value
instanceof
File
?
value
:
value
}
\r\n`
;
}),
'--boundary--
\
r
\
n'
]);
fetchEventSource
(
'/pdfSse/api/v1/order/pdf/extract/report/sse'
,
{
method
:
'POST'
,
headers
:
{
// 关键:设置multipart/form-data头,包含边界符
'Content-Type'
:
`multipart/form-data; boundary=boundary`
,
},
// 注意:fetchEventSource的body仅支持字符串/ArrayBuffer/Blob,这里传FormData转换后的Blob
body
:
formDataBlob
,
signal
:
abortController
.
value
.
signal
,
openWhenHidden
:
true
,
// SSE连接开启
async
onopen
(
res
)
{
console
.
log
(
"流式回答开始"
,
res
);
isGenerating
.
value
=
true
;
isShowProcess
.
value
=
true
;
},
async
onmessage
(
res
)
{
debugger
const
{
event
,
data
}
=
res
let
jsonData
=
JSON
.
parse
(
data
);
if
(
event
===
"progress"
)
{
processContent
.
value
+=
jsonData
.
message
;
updateProcess
(
processContent
.
value
,
scrollProcessContainer
.
value
);
}
}
},
}
else
if
(
msgData
.
event_type
===
"workflow_complete"
)
{
onerror
(
error
)
{
ElMessage
.
success
(
"报文生成结束"
);
ElMessage
({
isGenerating
.
value
=
false
;
message
:
"写报生成报错!"
,
type
:
"warning"
});
abortController
.
value
.
abort
();
abortController
.
value
.
abort
();
abortController
.
value
=
new
AbortController
();
abortController
.
value
=
new
AbortController
();
throw
new
Error
(
error
);
}
else
if
(
msgData
.
event_type
.
toLowerCase
().
includes
(
"error"
))
{
}
else
{
processContent
.
value
+=
str
;
curAgentTool
.
value
=
msgData
.
tool
;
updateProcess
(
processContent
.
value
,
scrollProcessContainer
.
value
);
}
}
}).
catch
((
error
)
=>
{
},
reject
(
error
);
onerror
(
error
)
{
isProcessing
.
value
=
false
;
ElMessage
({
message
:
"写报生成报错!"
,
type
:
"warning"
});
abortController
.
value
.
abort
();
abortController
.
value
=
new
AbortController
();
throw
new
Error
(
error
);
}
}).
catch
(
error
=>
{
ElMessage
({
message
:
"写报生成报错!"
,
type
:
"warning"
});
});
abortController
.
value
.
abort
();
abortController
.
value
=
new
AbortController
();
throw
new
Error
(
error
);
});
});
}
}
const
getFormattedTime
=
()
=>
{
const
now
=
new
Date
();
// 补零函数:确保单个数字补为两位(如 1 → 01,9 → 09)
const
pad
=
n
=>
n
.
toString
().
padStart
(
2
,
'0'
);
return
`
${
now
.
getFullYear
()}
-
${
pad
(
now
.
getMonth
()
+
1
)}
-
${
pad
(
now
.
getDate
())}
${
pad
(
now
.
getHours
())}
:
${
pad
(
now
.
getMinutes
())}
:
${
pad
(
now
.
getSeconds
())}
`
;
};
const
writtingTitle
=
ref
(
""
);
const
writtingTitle
=
ref
(
""
);
const
descText
=
ref
(
""
);
const
descText
=
ref
(
""
);
const
tabList
=
ref
([
const
tabList
=
ref
([
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论