js实现Md格式 转化成ProseMirror 编辑器的 JSON 格式结构
applyBoldFormatting(text) {
// 使用正则匹配 **加粗** 并转换为 { text: "...", marks: [{ type: "bold" }] }
const boldRegex = /\*\*(.*?)\*\*/g;
const parts = [];
let lastIndex = 0;
let match;
while ((match = boldRegex.exec(text)) !== null) {
const [full, content] = match;
// 添加前面的普通文本
if (match.index > lastIndex) {
const normalText = text.slice(lastIndex, match.index);
parts.push({type: 'text', text: normalText, marks: []});
}
// 添加加粗文本
parts.push({
type: 'text',
text: content,
marks: [{type: 'bold'}]
});
lastIndex = match.index + full.length;
}
// 添加剩余的普通文本
if (lastIndex < text.length) {
const normalText = text.slice(lastIndex);
parts.push({type: 'text', text: normalText, marks: []});
}
return parts;
},
markdownToProseMirrorJSON(markdown) {
const tokens = marked.lexer(markdown);
const content = [];
for (const token of tokens) {
switch (token.type) {
case 'paragraph':
// 处理段落中的换行
const paragraphContent = this.applyBoldFormatting(token.text);
content.push({
type: 'paragraph',
attrs: {textAlign: 'left'},
content: paragraphContent
});
break;
case 'heading':
// 去除内容前后的 ** 并解析加粗
let headingText = token.text;
// 去除开头和结尾的 **
if (headingText.startsWith('**') && headingText.endsWith('**')) {
headingText = headingText.slice(2, -2);
}
const headingContent = this.applyBoldFormatting(headingText);
content.push({
type: 'heading',
attrs: {
id: `heading-${content.filter(n => n.type === 'heading').length + 1}`,
level: token.depth,
textAlign: 'left'
},
content: headingContent
});
break;
case 'list':
// 有序/无序列表
const listType = token.ordered ? 'orderedList' : 'bulletList';
const listItems = token.items.map(item => {
const parsedContent = this.applyBoldFormatting(item.text);
return {
type: 'listItem',
content: [{
type: 'paragraph',
attrs: {textAlign: 'left'},
content: parsedContent
}]
};
});
content.push({
type: listType,
attrs: {start: 1},
content: listItems
});
break;
case 'code':
// 保留代码块原始内容
content.push({
type: 'code_block',
content: [{
type: 'text',
text: token.text,
marks: []
}]
});
break;
case 'blockquote':
// 处理引用块
content.push({
type: 'blockquote',
content: [{
type: 'paragraph',
attrs: {textAlign: 'left'},
content: [{
type: 'text',
text: token.text,
marks: []
}]
}]
});
break;
case 'text':
if (token.text.trim()) {
const textContent = this.applyBoldFormatting(token.text);
content.push({
type: 'paragraph',
attrs: {textAlign: 'left'},
content: textContent
});
}
break;
default:
// 忽略其他类型或添加更多处理逻辑
break;
}
}
return {
prosemirror: {
type: 'doc',
content: content
}
};
},
流式获取转发AI输出接口
export function searchByStream(data, rcall) {
fetchEventSource($config.apiBaseUrl + 'api/chatGpt/searchByStream', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Authorization': getToken()
},
openWhenHidden: true,
body: JSON.stringify(data),
onopen: rcall.onopen,
onmessage: rcall.onmessage,
onclose: rcall.onclose,
onerror: rcall.onerror
});
}
searchChatGPTForSummer(prompt) {
let that = this
let data = {
workCode: this.workCode,
chatGptWord: chatGptWord + ',请以文本内容返回。',
worksType: this.worksType,
conversationId: conversationId,
parentMessageId: parentMessageId,
operationType: null,
chatGptAssistantWord: null
}
let loadingChatGpt = this.$loading({
// fullscreen: true,
text: this.chatGptName + "正在思考中...",
background: 'rgba(0, 0, 0, 0)'
});
this.loadingFlag = true;
excuteAction('Running')
this.chatGptWord = '';
let chatChat = {
name: this.chatGptName
, content: ''
, data: {text: ''}
}
searchByStream(data, {
onmessage(event) {
try {
let parObj = JSON.parse(event.data)
let content = parObj.choices[0].delta.content
console.log(content)
chatChat.content += content
chatChat.data.text += content
} catch (e) {
}
},
onclose(event) {
try {
loadingChatGpt.close();
that.loadingFlag = false;
excuteAction('Idle');
excuteAction('Yes');
console.log(chatChat);
let docContent = that.markdownToProseMirrorJSON(chatChat.content)
let docContentString = JSON.stringify(docContent);
console.log('对应的json格式的数据', docContentString);
that.isErrorOccurred = false; // 请求成功后重置错误标志
} catch (e) {
console.log("catch", e);
}
},
onerror(event) {
console.log("catch", event);
if (!that.isErrorOccurred) {
that.isErrorOccurred = true; // 添加错误标志
that.$message('因未知原因暂时无法回答您的问题.');
loadingChatGpt.close();
that.loadingFlag = false;
excuteAction('Idle');
excuteAction('No');
}
return false;
},
})
},
/**
* 搜索chatGpt
*
* @param chatGptEntity
* @return
*/
@PostMapping(value = "/searchByStream")
public Flux<String> searchByStream(@RequestBody DocChatGptEntity chatGptEntity, HttpServletResponse response) {
String account = getAccount();
response.setHeader("Content-Type", "text/event-stream");
logger.info("用户: {} 访问chat", account);
ChatGptResultEntity entity = new ChatGptResultEntity();
List<ChatGptEntity.Message> messages = BaseUtils.getDefault(chatGptEntity.getMessages(), new ArrayList<>());
chatGptEntity.setMessages(messages);
ChatGptEntity.Message userMessage = new ChatGptEntity.Message(AiMessageRole.USER.getCode(), chatGptEntity.getChatGptWord());
messages.add(userMessage);
if (BaseUtils.strIsNotEmpty(chatGptEntity.getChatGptAssistantWord())) {
ChatGptEntity.Message assistantMessage = new ChatGptEntity.Message(AiMessageRole.ASSISTANT.getCode(), chatGptEntity.getChatGptAssistantWord());
messages.add(assistantMessage);
}
Map<String, Object> paramMap = BaseUtils.createParamMap();
List<ChatGptEntity.Message> requestMessages = chatGptEntity.getMessages();
// 历史数据仅携带需要的量
requestMessages = AiUtils.messageFilterNone(requestMessages);
requestMessages = AiUtils.getLastNElements(requestMessages, aiConfigBean.getHistoryLimit());
paramMap.put("model", "qwen-long");
paramMap.put("temperature", aiConfigBean.getTemperature());
paramMap.put("stream", true);
paramMap.put("messages", requestMessages);
Map<String, String> headerMap = new HashMap<>(16);
headerMap.put("Authorization", aiConfigBean.getAuthorization());
logger.info("请求chatGPt参数: {} header: {}", JSONObject.toJSONString(paramMap), JSONObject.toJSONString(headerMap));
// String url = getChatGptUrl() + CHAT_COMPLETIONS;
String url = "https://afpservice-test.springgroup.cn/largeModel/knowledge2/v1/chat/completions";
log.info(DateUtils.getTime() + "请求开始");
return WebClient
.create()
.post()
.uri(url)
.header("Authorization", "DocsAi")
.bodyValue(paramMap)
.retrieve()
.bodyToFlux(String.class)
.doOnNext(content -> System.out.println(DateUtils.getTime() + ":" + content))
.doFinally(data -> {
log.info(DateUtils.getTime() + "请求结束");
logger.info("用户: {} 访问chat结束", account);
});
}