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

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

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

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

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

Quill 编辑器的代码如下:

html 复制代码
<!-- 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 文件,代码如下:

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);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.LoadFromFile('QuillEditor.txt');
    if TagString = 'Editor' then
      ReplaceText := SL.Text;
  finally
    SL.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);
var
  S: string;
  RequestBody: string;
  Delta, Html: string;
  JSONValue: TJSONObject;
  SL: TStringList;
begin
  S := Request.ContentFields.Values['html'];
  SL := TStringList.Create;
  try
    SL.Text := S;
    SL.SaveToFile('abcd.txt');
  finally
    SL.Free;
  end;

  Response.SendRedirect('Show');
  Handled := True;
end;

上述代码解释:

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

html 复制代码
<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 部分的名称:

html 复制代码
<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);
begin
  var SL := TStringList.Create;
  try
    SL.LoadFromFile('abcd.txt');
    Response.Content := SL.Text;
  finally
    SL.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图片的格式,不完整但能体现这一点的代码片段数据如下:

html 复制代码
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);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.LoadFromFile('QuillEditor.txt');
    if TagString = 'Editor' then
      ReplaceText := SL.Text;
  finally
    SL.Free;
  end;
end;

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

代码如下:

Delphi 复制代码
unit UQuillEditor;

interface

uses
  System.SysUtils, System.Classes, System.IOUtils;

type
  TQuillEditor = class
  private

  public
    class function GetHtml(const QuillEditorFileName: string): string;
  end;

implementation

{ TQuillEditor }

class function TQuillEditor.GetHtml(const QuillEditorFileName: string): string;
var
  SL: TStringList;
begin
  if (not FileExists(QuillEditorFileName)) then raise Exception.Create('文件不存在!' + QuillEditorFileName);

  SL := TStringList.Create;
  try
    SL.LoadFromFile(QuillEditorFileName);
    Result := SL.Text;
  finally
    SL.Free;
  end;
end;

end.

结论

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

相关推荐
乔峰不是张无忌3309 分钟前
【HTML】动态闪烁圣诞树+雪花+音效
前端·javascript·html·圣诞树
鸿蒙自习室16 分钟前
鸿蒙UI开发——组件滤镜效果
开发语言·前端·javascript
m0_7482507423 分钟前
高性能Web网关:OpenResty 基础讲解
前端·openresty
机器之心43 分钟前
图学习新突破:一个统一框架连接空域和频域
人工智能·后端
前端没钱1 小时前
从 Vue 迈向 React:平滑过渡与关键注意点全解析
前端·vue.js·react.js
NoneCoder1 小时前
CSS系列(29)-- Scroll Snap详解
前端·css
无言非影1 小时前
vtie项目中使用到了TailwindCSS,如何打包成一个单独的CSS文件(优化、压缩)
前端·css
.生产的驴1 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
我曾经是个程序员1 小时前
鸿蒙学习记录
开发语言·前端·javascript
顽疲1 小时前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端