在公司的大模型应用项目中,我们要开发一个新功能,实现文档上传到页面可以进行在线编辑、选中文本可以对其进行审核优化的功能。
具体操作步骤如下: 上传word文档,读取内容展示在富文本编辑器中。 用户选中文本点击开始按钮,将选中的文本作为参数访问接口,返回的内容以卡片的形式展示在选中文本的正右方。卡片中包含操作按钮,可以实现替换文本、续写文本等操作。 过程中,可以将选中的文本和卡片进行保存。
这样一个功能,该如何实现?
一、先实现编辑器的文档渲染和下载的功能:
- 创建编辑器
- 使用了成熟的富文本编辑器库 wangEditor。
<template>
<div class="rich-editor">
<Toolbar
ref="toolbarRef"
class="rich-editor-toolbar"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
class="rich-editor-content"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
@onChange="handleChange"
@onBlur="handleBlur"
@keyup="handleKeyUp"
@mouseup="handleMouseup"
/>
</div>
</template>
- Word文档上传和编辑器渲染
- 使用element-plus的上传文件控件,实现文件的上传。
- 文件上传后,使用
readAsArrayBuffer方法读取文件,再用mammoth库将ArrayBuffer转换为HTML,渲染编辑器。
const uploadFile = (data) => {
// 创建 FileReader 对象
// FileReader 是一个Web API,用于读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用文件或Blob对象指定要读取的文件或数据。
const reader = new FileReader();
// 定义 onload 事件处理器: 当 FileReader 读取操作成功完成时,onload 事件会被触发。
reader.onload = (e) => {
// * e.target.result 是一个包含文件内容的 ArrayBuffer。
// * 使用 mammoth 库将 ArrayBuffer 转换为HTML。
// * convertToHtml 方法返回一个Promise,解析为转换后的HTML内容。
// * 如果转换成功,convertedHtml.value 会被设置为转换后的HTML,并使用editorRef.value.setEditor 方法更新编辑器的内容。
// * 如果转换失败,会捕获错误,清空 convertedHtml.value,清空编辑器内容,并显示一个错误消息。
const arrayBuffer = e.target.result;
mammoth
.convertToHtml({ arrayBuffer: arrayBuffer })
.then(async function (result) {
convertedHtml.value = result.value; // 转换后的 HTML 内容
editorRef.value.setEditor(convertedHtml.value);
})
.catch(function (error) {
convertedHtml.value = ""; // 转换后的 HTML 内容
editorRef.value.setEditor("");
ElMessage.error("文件解析失败");
});
};
// 定义 onerror 事件处理器: 如果 FileReader 在读取文件时发生错误,onerror 事件会被触发。
reader.onerror = (e) => {
ElMessage.error("文件解析失败");
};
// 开始读取文件
// readAsArrayBuffer 方法会开始读取指定的Blob或File内容,并在读取操作完成时触发 onload 或 onerror 事件。
reader.readAsArrayBuffer(data.file);
};
- 编辑器HTML文件下载为Word
- 将富文本编辑器中的内容下载为文档,可以使用 wangEditor 提供的 toHtml 方法。再结合
htmlDocx、FileSaver.js等库,可以将HTML文件下载为docx格式。
- 将富文本编辑器中的内容下载为文档,可以使用 wangEditor 提供的 toHtml 方法。再结合
import htmlDocx, { array } from "html-docx-fixed/dist/html-docx";
import saveAs from "file-saver";
// 将 HTML 内容转换为 Word 文档(.docx 格式)并下载
function downloadFile() {
// 将 HTML 内容转换为 Blob 对象
const converted = htmlDocx.asBlob(editorRef.value.getHtml());
saveAs(converted, '文件名称.docx'’);
}
二、基本实现思路:
-
如何监听文本选择事件:
- 通过在编辑器中监听用户的
@keyup和@mouseup,获取当前已选中文本是否为空,从而判断用户选中文本的动作。
- 通过在编辑器中监听用户的
-
创建卡片展示区域,调用接口并展示返回内容:
- 当用户选中文本并点击开始按钮时,创建一个卡片容器,并定位到选中文本的右侧。
- 使用选中的文本等参数访问接口,将接口返回的内容HTML格式化后放入创建的卡片容器中。我这里是调用大模型,解析返回的文本与知识库是否有差异,及优化建议等信息,提供给用户【采纳】【忽略】等操作选择。
-
保存数据和状态及下载文档:
- 可以使用浏览器的localStorage或sessionStorage保存选中的文本和接口返回的内容。我这里是请求接口做的保存,也可以使用其他方式。
- 将富文本编辑器中的内容下载为文档,可以使用 wangEditor 提供的 toHtml 方法。结合
htmlDocx、FileSaver.js等库,可以将HTML文件下载为docx格式。
以下是一个简化的代码示例:
function showCard() {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const selectedText = range.toString();
// 调用接口获取数据,这里使用模拟数据
const cardData =模拟数据或调用API(selectedText);
// 创建卡片并添加到页面
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = cardData; // 假设返回的数据可以直接用HTML显示
document.getElementById('editor').appendChild(card);
// 保存数据到localStorage
saveDataToLocalStorage(selectedText, cardData);
}
}
function saveDataToLocalStorage(text, data) {
// 简单起见,这里直接将数据保存为JSON字符串
const key = `card_${text}`;
const value = JSON.stringify({ text, data });
localStorage.setItem(key, value);
}
// 页面加载时读取localStorage中的数据
window.onload = function() {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key.startsWith('card_')) {
const value = localStorage.getItem(key);
const { text, data } = JSON.parse(value);
// 创建卡片并添加到页面
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = data;
document.getElementById('editor').appendChild(card);
}
}
};
这就完成了基础的功能。
但如果用户下一次进入页面,仍希望卡片可以展示到富文本编辑器对应的位置上,那就需要将卡片的位置信息与选中的文本关联起来,并在页面加载时恢复这些信息。
三、扩展前面示例的实现思路:
-
保存卡片位置信息:
- 当用户创建卡片时,计算卡片应该出现的位置(基于选中文本的位置)。
- 将位置信息(如left和top坐标)与文本一起保存到localStorage或服务器。
-
页面加载时恢复卡片和位置:
- 页面加载时,从localStorage或服务器获取保存的数据。
- 根据保存的位置信息,动态计算并设置卡片的样式,使其出现在正确的位置。
-
处理富文本编辑器内容变化:
- 如果编辑器内容发生变化(如用户添加或删除文本),需要重新计算卡片的正确位置。
- 可以通过监听编辑器的
input或change事件来处理这种变化。
以下是一个简化的代码示例,展示了如何在页面加载时恢复卡片的位置:
// 假设你已经有了showCard函数,现在需要修改它以保存位置信息
function showCard() {
// ...之前的代码
// 创建卡片并添加到页面
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = cardData; // 假设返回的数据可以直接用HTML显示
document.getElementById('editor').appendChild(card);
// 计算并保存卡片位置
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
card.style.left = `${rect.right}px`; // 将卡片定位在选中文本的右侧
card.style.top = `${rect.top}px`;
// 保存数据到localStorage,包括位置信息
saveDataToLocalStorage(selectedText, cardData, rect.left, rect.top);
}
// 修改保存数据的函数以包括位置信息
function saveDataToLocalStorage(text, data, left, top) {
// 简单起见,这里直接将数据保存为JSON字符串
const key = `card_${text}`;
const value = JSON.stringify({ text, data, left, top });
localStorage.setItem(key, value);
}
// 页面加载时读取localStorage中的数据并恢复卡片位置
window.onload = function() {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key.startsWith('card_')) {
const value = localStorage.getItem(key);
const { text, data, left, top } = JSON.parse(value);
// 创建卡片并添加到页面
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = data;
document.getElementById('editor').appendChild(card);
// 设置卡片的位置
card.style.left = `${left}px`;
card.style.top = `${top}px`;
}
}
};
这个示例假设卡片是绝对定位的,并且编辑器的内容不会因为卡片的存在而改变其布局。 但实际应用中,我们需要用更复杂的逻辑来处理编辑器内容的动态变化,以及如何将这些变化反映到卡片的定位上。
四、相关资料及文档:
-
wangEditor: 富文本编辑器插件,主要用于在网页中实现所见即所得的编辑功能。该编辑器提供了丰富的编辑选项,包括文字样式设置、插入图片、插入表格、代码高亮等,且易于集成和使用,支持自定义配置和扩展。 www.wangeditor.com/
-
巨象(Mammoth): 旨在将由Microsoft Word、Google Docs和LibreOffice创建的.docx文档转换为HTML。它通过使用文档中的语义信息并忽略其他细节,生成简单且干净的HTML。 gitcode.com/mwilliamson…
-
htmlDocx:用于将HTML内容转换为Microsoft Word文档格式(.docx)。它允许开发者将HTML的结构、样式以及一些特殊元素(如表格)转换为Word文档中的相应元素。 www.npmjs.com/package/htm…
-
FileSaver.js:用于将 Blob 对象保存为文件并触发浏览器下载。 gitcode.com/eligrey/Fil…