富文本编辑器wangEditor (v5) 在 Spring Boot_Thymeleaf 项目中的使用流程
富文本编辑器在“新增”和“修改”两种场景下的流程,一般是“引入资源 → 搭建结构 → 初始化 → 数据同步”。
第一步:引入资源
无论是添加还是编辑页面,首先必须引入 CSS 和 JS 文件。
-
CSS: 决定编辑器的长什么样(边框、按钮样式)。 -
JS: 决定编辑器能做什么(核心逻辑)。
<link rel="stylesheet" th:href="@{/js/wangEditor.min.css}"><script th:src="@{/js/wangEditor.min.js}"></script>
第二步:构建 HTML 结构
wangEditor v5 推荐将 工具栏 和 编辑区 分离,以便更灵活地控制布局。
-
外层容器 (#editor-wrapper): 控制整体高度(如 500px)和边框。 -
工具栏容器 (#toolbar-container): 放置加粗、斜体等按钮。 -
编辑容器 (#editor-container): 用户输入内容的区域,通常设置 overflow-y: auto实现内容滚动。 -
隐藏域 textarea: 关键点。编辑器本身不是表单控件,需要这个隐藏域来存储数据,以便随表单提交。
第三步:CSS 样式控制
通过 CSS 精确控制高度,防止工具栏遮挡内容或高度溢出。
第四步:JavaScript 初始化
这是核心逻辑,分为“配置”和“创建”两个动作。
1. 配置 (editorConfig)
-
placeholder: 提示文字。 -
onChange: 数据同步的核心。每当用户输入内容,触发此事件,将编辑器的 HTML 内容实时赋值给隐藏的 <textarea>。
2. 创建 (createEditor & createToolbar)
-
使用 createEditor绑定编辑区 DOM。 -
使用 createToolbar绑定工具栏 DOM,并关联编辑器实例。
第五步:数据交互 (添加 vs 编辑)
这是你提供的两段代码最大的区别所在,也是实际开发中最容易出错的地方。
场景 A:添加页面 (数据写入)
-
初始内容: 默认为空或
<p><br></p>。 -
目标: 用户输入内容 -> 同步到隐藏域 -> 提交给后端。
-
代码逻辑:
html: '<p><br></p>' // 默认为空
场景 B:编辑页面 (数据回填 + 更新)
-
初始内容: 必须从后端获取。
-
目标: 后端数据 -> 填充到编辑器 -> 用户修改 -> 同步 -> 提交。
-
关键技术: 使用 Thymeleaf 语法
[[${...}]]将后端对象属性注入到 JS 中。 -
代码逻辑:
// 如果后端有数据则显示,否则显示空行html: [[${meetingJl.hynr}]] || '<p><br></p>',注意: 这里的
meetingJl.hynr是后端传过来的富文本 HTML 字符串。
第六步:表单提交
在点击“保存”按钮时(submitHandler),必须确保隐藏域中的数据是最新的。
-
校验: 检查表单其他字段是否合法。 -
强制同步: 虽然 onChange已经做了同步,但在提交瞬间再次执行$('#hynr').val(editor.getHtml())是最稳妥的做法。 -
提交: 使用 AJAX 或表单默认行为提交数据。
functionsubmitHandler() {if ($.validate.form()) { // 1. 获取最新内容 var htmlContent = editor.getHtml(); // 2. 放入隐藏域 $('#hynr').val(htmlContent); // 3. 提交表单 $.operate.save(prefix + "/edit", $('#form-jl-edit').serialize()); }}
总结对照表
|
|
|
|
|---|---|---|
| HTML 结构 |
|
|
| 初始化内容 | html: '<p><br></p>'
|
html: [[${entity.field}]]
|
| 数据流向 |
|
|
| 提交路径 | /add |
/edit |
参考代码:
添加页面:
<link rel="stylesheet" th:href="@{/js/wangEditor.min.css}"> <style> /* 1. 编辑器外层容器样式(包含工具栏和编辑区域) */#editor-wrapper { border: 1px solid #ccc; height: 500px; /* 总高度 */ z-index: 100; } /* 2. 工具栏样式 */#toolbar-container { border-bottom: 1px solid #ccc; /* 工具栏底部边框 */ background-color: #f8f9fa; /* 工具栏背景色,可选 */ } /* 3. 编辑区域样式(内容可滚动区域) */#editor-container { height: calc(100% - 40px); /* 高度 = 总高度 - 工具栏高度(约40px) */ overflow-y: auto; /* 允许内容滚动 */ background-color: #fff; } </style> <div class="form-group"> <label class="col-sm-3 control-label">会议内容:</label> <div class="col-sm-8"> <!-- 注意:这里修改了结构,使用 wrapper 包裹 toolbar 和 editor --> <div id="editor-wrapper"> <!-- 工具栏容器 --> <div id="toolbar-container"></div> <!-- 编辑器容器 --> <div id="editor-container"></div> </div> <!-- 隐藏域用于表单提交 --> <textarea name="hynr" id="hynr" style="display: none;"></textarea> </div> </div><!-- 引入 JS 文件 --><script th:src="@{/js/wangEditor.min.js}"></script><script th:inline="javascript"> var prefix = ctx + "system/jl"; var editor; $(function () {if (typeof window.wangEditor === 'undefined') { console.error('WangEditor 未加载,请检查文件路径');return; } // 解构赋值 const { createEditor, createToolbar } = window.wangEditor; // 1. 编辑器配置 const editorConfig = { placeholder: '请输入会议内容...', onChange: (editor) => { // 实时同步 HTML 到 textarea $('#hynr').val(editor.getHtml()); } }; // 2. 创建编辑器 // 注意:这里不再设置 height,由 CSS 控制高度 editor = createEditor({ selector: '#editor-container', html: '<p><br></p>', config: editorConfig, mode: 'default' }); // 3. 创建工具栏 (这才是官方 Demo 的标准模式) const toolbar = createToolbar({ editor, selector: '#toolbar-container', config: { // 这里可以配置工具栏的按钮组,如果不配置则显示默认全部 // excludeKeys: ['bold', 'italic'] // 例如:排除加粗和斜体 }, mode: 'default' }); });functionsubmitHandler() {if ($.validate.form()) {if (editor) { var htmlContent = editor.getHtml(); // 获取 HTML console.log("准备发送的内容:", htmlContent); // <-- 加这一行 $('#hynr').val(editor.getHtml()); } $.operate.save(prefix + "/add", $('#form-jl-add').serialize()); } }</script>
编辑页面:
<!-- 引入 CSS 文件 --> <link rel="stylesheet" th:href="@{/js/wangEditor.min.css}"> <style> /* 1. 编辑器外层容器样式(包含工具栏和编辑区域) */#editor-wrapper { border: 1px solid #ccc; height: 500px; /* 总高度 */ z-index: 100; } /* 2. 工具栏样式 */#toolbar-container { border-bottom: 1px solid #ccc; /* 工具栏底部边框 */ background-color: #f8f9fa; /* 工具栏背景色,可选 */ } /* 3. 编辑区域样式(内容可滚动区域) */#editor-container { height: calc(100% - 40px); /* 高度 = 总高度 - 工具栏高度(约40px) */ overflow-y: auto; /* 允许内容滚动 */ background-color: #fff; } </style> <div class="form-group"> <label class="col-sm-3 control-label">会议内容:</label> <div class="col-sm-8"> <!-- 注意:这里修改了结构,使用 wrapper 包裹 toolbar 和 editor --> <div id="editor-wrapper"> <!-- 工具栏容器 --> <div id="toolbar-container"></div> <!-- 编辑器容器 --> <div id="editor-container"></div> </div> <!-- 隐藏域用于表单提交 --> <!-- 修改:name 和 id 保持一致,用于 JS 赋值 --> <textarea name="hynr" id="hynr" style="display: none;" th:field="*{hynr}"></textarea> </div> </div><!-- 引入 JS 文件 --><script th:src="@{/js/wangEditor.min.js}"></script><!-- 修改:添加 th:inline="javascript" 以支持后端数据注入 --><script th:inline="javascript"> var prefix = ctx + "system/jl"; var editor; $(function () {if (typeof window.wangEditor === 'undefined') { console.error('WangEditor 未加载,请检查文件路径');return; } // 解构赋值 const { createEditor, createToolbar } = window.wangEditor; // 1. 编辑器配置 const editorConfig = { placeholder: '请输入会议内容...', onChange: (editor) => { // 实时同步 HTML 到 textarea $('#hynr').val(editor.getHtml()); } }; // 2. 创建编辑器 // 注意:这里不再设置 height,由 CSS 控制高度 editor = createEditor({ selector: '#editor-container', // 修改:从后端获取数据并赋值给编辑器 // [[${meetingJl.hynr}]] 是 Thymeleaf 语法,用于获取 Java 对象的属性 html: [[${meetingJl.hynr}]] || '<p><br></p>', config: editorConfig, mode: 'default' }); // 3. 创建工具栏 const toolbar = createToolbar({ editor, selector: '#toolbar-container', config: { // 这里可以配置工具栏的按钮组,如果不配置则显示默认全部 // excludeKeys: ['bold', 'italic'] // 例如:排除加粗和斜体 }, mode: 'default' }); });functionsubmitHandler() {if ($.validate.form()) {if (editor) { var htmlContent = editor.getHtml(); $('#hynr').val(htmlContent); } // 修改:提交路径改为 edit $.operate.save(prefix + "/edit", $('#form-jl-edit').serialize()); } }</script></body></html>
夜雨聆风