WEB 编程:使用富文本编辑器 Quill 配合 WebBroker 后端

devtools/2024/9/25 19:05:04/

使用 DelphiWebBroker 框架写 Web Server,需要一个前端的富文本编辑器。

评估了好几个,最后选择 Quill 这个开源的。

官方地址:Quill - Your powerful rich text editor

前端代码,存储为一个单独的文本文件,方便随便哪个页面需要的时候可以使用。相当于封装为一个独立的对象,方便代码重用。

Quill 编辑器的代码如下:


<!-- quill 编辑器的封装 -->
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css" rel="stylesheet"><style>.edit_container {font-family: 'Avenir', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;}.ql-editor{height:400px;}</style><form id="myForm" action="ContentUpdate" method="post"><div id="editor"></div><input type="hidden" name="delta" id="deltaInput"><input type="hidden" name="html" id="htmlInput"><input type="submit" value="Submit"></form><script>const quill = new Quill('#editor', {modules: {toolbar: [[{ header: [1, 2, false] }],['bold', 'italic', 'underline'],['image', 'code-block'],],},theme: 'snow'});document.getElementById('myForm').addEventListener('submit', function() {// 获取 Quill 编辑器的内容const delta = JSON.stringify(quill.getContents());const html = quill.root.innerHTML;// 将内容放入隐藏输入框中document.getElementById('deltaInput').value = delta;document.getElementById('htmlInput').value = html;});</script>

把上述代码,保存为文本文件:QuillEditor.txt

然后,我们来做一个测试用的 html 文件,代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Quill Form Submission</title>
</head>
<body><#Editor>
</body>
</html>

这个文件,保存为 index.html;

这个文件里面的 <#Editor> 是给 WebBroker 的 PageProducer 自动替换的标记。Delphi 的后台程序会将这个标记替换为前面的 QuillEditor.txt 的内容。

Delphi 的代码

首先是如何输出页面

Delphi 里面创建一个 WebBroker 工程,

选择 StandAlone 模式,这样方便调试。

在这个工程里面,Delphi 自动帮我们创建了一个 Web 服务器端的核心框架:TWebModule1;

拖一个 PageProducer1 到 TWebModule1 上面。设置它的属性:HTMLFile 为 index.html;

这里的 index.html 就是前面的那个简单的页面框架。这个文件只要放在程序的相同目录下,运行时 PageProducer1 就会自动加载它。当然,也可以指定绝对路径比如:D:\MyHTML\index.html;

然后,选中 PageProducer1 然后把属性面板的页标签切换到 Events,可以看到 PageProducer1 只有一个事件:OnHTMLTag,双击这个事件,Delphi IDE 自动帮我们创建事件的代码框架。在这里我们写代码如下:

Delphi">procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag: TTag;const TagString: string; TagParams: TStrings; var ReplaceText: string);
varSL: TStringList;
beginSL := TStringList.Create;trySL.LoadFromFile('QuillEditor.txt');if TagString = 'Editor' thenReplaceText := SL.Text;finallySL.Free;end;
end;

上述代码的解释:

PageProducer1 在加载 index.html 的时候,会触发上述事件。我们在上述事件里面写代码,首先判断标记:Editor,这个标记就是前面我们写在 index.html 里面的那个 <#Editor>

ReplaceText 则是替换那个标记的内容。这里,我们用前面保存的 Quill Editor 的那个文件的内容来替换掉 Editor 这个标记。这里会将包括尖括号在内的整个 <#Editor> 都替换掉。最终输出的页面内容就是包含 QuillEditor.txt 的内容的一个完整 HTML 页面。

再然后,双击 TWebModule1 这个界面窗口,弹出 Editing WebModule1.Actions 对话框,里面已经有一个默认的 Action,选中它,然后在属性面板里面,把它的 Producer 属性下拉选择为 PageProducer1;这样操作的目的是浏览器访问服务器时,触发这个 Action,这个 Action 最终会把它绑定的 PageProducer1 的内容输出给浏览器。

到此,输出一个包含 Quill Editor 的页面制作完毕。

服务器端接收浏览器提交的内容

在前面提到的 Editing WebModule1.Actions 对话框里面,鼠标右键,下拉菜单:Add,添加一个 Action。

选择这个 Action,设置它的属性 Path 为:ContentUpdate

切换属性面板到事件,它只有一个 OnAction 事件,双击,Delphi IDE 自动产生事件代码框架,在里面写代码如下:

Delphi">procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
varS: string;RequestBody: string;Delta, Html: string;JSONValue: TJSONObject;SL: TStringList;
beginS := Request.ContentFields.Values['html'];SL := TStringList.Create;trySL.Text := S;SL.SaveToFile('abcd.txt');finallySL.Free;end;Response.SendRedirect('Show');Handled := True;
end;

上述代码解释:

这个 Action 的 Path 之所以设置为 ContentUpdate,是因为页面上的 Quill Editor 里面的代码:

<form id="myForm" action="ContentUpdate" method="post">

页面代码指明了提交给服务器的路径是 ContentUpdate,因此 Delphi WebBroker 服务器里面的这个 Action 的 Path 为 ContentUpdate 的 Action 事件就会被触发。

上述代码中:

Delphi">S := Request.ContentFields.Values['html'];

这里的 'html' 是对应的 Quill Editor 的 HTML 代码里面的 name 部分的名称:

<input type="hidden" name="html" id="htmlInput">

假设把上述页面代码的 name="html" 改为 "html2",那么,对应的服务器端 Delphi 的代码就应该改为:

Delphi">S := Request.ContentFields.Values['html2'];

最终,Delphi 把来自浏览器提交的编辑器的内容,保存为一个文本文件 abcd.txt

保存完编辑器提交的内容后,让浏览器跳转到 show 这个路径去:

Delphi">Response.SendRedirect('Show');

假设浏览器访问的是 http://127.0.0.1:8080/ 就会跳转到 http://127.0.0.1:8080/show

为服务器端增加 Show 这个路径的处理方法

在前面提到的 Editing WebModule1.Actions 对话框里面,鼠标右键,下拉菜单:Add,添加一个 Action。

选择这个 Action,设置它的属性 Path 为:Show

选中这个 Action,切换属性面板到 Events,双击它的 OnAction 事件,IDE 创建事件代码框架,写代码如下:

Delphi">procedure TWebModule1.WebModule1WebActionItem2Action(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
beginvar SL := TStringList.Create;trySL.LoadFromFile('abcd.txt');Response.Content := SL.Text;finallySL.Free;end;
end;

上述代码解释:

这里我们加载前面保存的浏览器提交过来的 Quill Editor 的内容,也就是保存的 abcd.txt,然后把这个内容作为 Response 返回给浏览器。

如果在浏览器的地址栏里面输入:http://127.0.0.1:8080/show 就会直接触发服务器端的这个事件方法,浏览器页面上就能看到之前我们在 Quill Editor 里面输入的内容。

比较有意思的是:图片

在浏览器端的富文本编辑器里面插入图片,通常是要上传图片,然后把 URL 插入到编辑器里面。但是这样又会增加不少代码工作。比如服务器端需要有个专门的 Action 来接收上传的图片文件,把图片文件保存到 Web 服务器的某个目录里面,再返回 URL;在前端需要写不少 JavaScript 代码,用来上传图片,获得 URL,然后把 URL 插入到编辑器的对应位置。等等,很麻烦。

对于  Quill Editor 来说,前面我给出的代码,用户点击它的工具栏的图片按钮,选择本地硬盘的图片文件,然后,它会自动把图片贴进编辑框的正确位置,完成图文混排。点击提交按钮后,整个文本编辑框的内容包括图片数据,一起提交给服务器。这里的图片数据是编码为 Base64 的,并且格式复合 HTML 的标准的页面嵌入BASE64图片的格式,不完整但能体现这一点的代码片段数据如下:

src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAYEBQYF

因此,服务器端获得的完整的字符串,就是编辑器的完整内容,包含图片。把这个字符串保存后,作为文本输出给浏览器,图片也能完整地显示出来。

这样一来,图片和文字的混合编辑最终输出的就是一段字符串。在前端不需要写额外的 JavaScript 代码,后端也不需要额外的代码来处理图片文件的保存。

在实际的项目里面,后端可以把前端提交的字符串(包含图片和格式化的文字),直接原本保存到数据库的字段里面。需要显示时,直接从数据库的字段里面读取内容,输出给页面。

One more thing

前面提到的服务器端加载 Quill Editor 的代码,使用了 TStringList,重复一下代码片段:

Delphi">procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag: TTag;const TagString: string; TagParams: TStrings; var ReplaceText: string);
varSL: TStringList;
beginSL := TStringList.Create;trySL.LoadFromFile('QuillEditor.txt');if TagString = 'Editor' thenReplaceText := SL.Text;finallySL.Free;end;
end;

应该把上述代码,封装为一个对象,需要的地方,直接调用对象就好了。避免每个不同的 WEB 项目里面都要重复写上述代码。封装为对象,就可以实现代码重复使用。

代码如下:

Delphi">unit UQuillEditor;interfaceusesSystem.SysUtils, System.Classes, System.IOUtils;typeTQuillEditor = classprivatepublicclass function GetHtml(const QuillEditorFileName: string): string;end;implementation{ TQuillEditor }class function TQuillEditor.GetHtml(const QuillEditorFileName: string): string;
varSL: TStringList;
beginif (not FileExists(QuillEditorFileName)) then raise Exception.Create('文件不存在!' + QuillEditorFileName);SL := TStringList.Create;trySL.LoadFromFile(QuillEditorFileName);Result := SL.Text;finallySL.Free;end;
end;end.

结论

Quill Editor 作为一个前端的开源的富文本编辑器,用起来还是很方便的。后端使用 Delphi WebBroker 框架来开发,可以简单地封装为一个文本文件和一个简单的 Delphi 对象,在需要的地方就可以直接使用了。


http://www.ppmy.cn/devtools/117126.html

相关文章

音频格式不支持如何解决?5个音频转换软件帮你忙!

我们常常会遇到这样的困扰&#xff1a;满心期待地想要播放一段音频&#xff0c;却被告知“音频格式不支持”。不过&#xff0c;不用担心&#xff0c;无论你是遇到MP3、WAV、FLAC还是其他冷门格式的音频文件&#xff0c;我们都有一款合适你的音频转换软件&#xff0c;可以轻松将…

ChartLlama: A Multimodal LLM for Chart Understanding and Generation论文阅读

原文链接&#xff1a;https://arxiv.org/abs/2311.16483 代码与数据集&#xff1a;https://tingxueronghua.github.io/ChartLlama/ 本文启发&#xff1a;文章提出利用GPT-4合成大量图表数据&#xff0c;这些数据包含各种图表类型&#xff0c;包含丰富的instruction data。然后…

云服务器docker中Hbase整合java-api需要放行的接口

在使用 Docker 部署 HBase 并通过 Java API 进行访问时&#xff0c;确保通信畅通非常重要。如果 HBase 部署在云服务器或虚拟机中&#xff0c;你需要在云服务器的安全组中放行一些关键端口&#xff0c;确保外部或本地的 Java 应用可以正确访问 Docker 容器中的 HBase 服务。 1…

主流卷积神经网络CNN总结

ResNet&#xff08;2015&#xff09;残差神经网络 残差结构 ResNet50具体卷积结构图 ResNeXt&#xff08;2016&#xff09;加入了分组卷积的思想&#xff0c;将原ResNet网络中的block替换成由group分组的block&#xff0c;两者得到的feature map一致&#xff0c;只是参数量更少…

缓存预热方案详解

在高性能Web应用中&#xff0c;缓存技术是提升系统响应速度的关键手段之一。然而&#xff0c;在系统启动或重启后&#xff0c;缓存往往是空的&#xff0c;此时来自用户的请求将直接打到数据库上&#xff0c;导致响应时间增加。为了避免这种情况&#xff0c;缓存预热就显得尤为重…

剪映草稿批量自动化导出教程实操演示

如何批量自动导出草稿&#xff1f;今天我来实操演示。首先打开谷歌剪映助手 如果没有安装谷哥剪映助手的可以自行搜索下载&#xff0c;打开后找到批量导出多个草稿自动化导出。接着在右侧输入你要导出草稿的数量&#xff0c;其他的选项根据需求自行选择&#xff0c;最后点击立即…

深度学习02-pytorch-07-张量的拼接操作

在 PyTorch 中&#xff0c;张量的拼接操作主要通过 torch.cat() 和 torch.stack() 两个函数来完成。拼接操作允许你将多个张量沿着指定的维度连接在一起&#xff0c;构建更大的张量。以下是详细解释和举例说明&#xff1a; 1. torch.cat() 功能: 沿着指定的维度连接&#xff…

深入探索 RUM 与全链路追踪:优化数字体验的利器

作者&#xff1a;梅光辉&#xff08;重彦&#xff09; 背景介绍 随着可观测技术的持续演进&#xff0c;多数企业已广泛采用 APM、Tracing 及 Logging 解决方案&#xff0c;以此强化业务监控能力&#xff0c;尤其在互联网行业&#xff0c;产品的体验直接关系着用户的口碑&…