Fastadmin为何默认选用Summernote富文本编辑器
发布时间:2026/10/1 13:08:19来源:尧图网络
1. 项目概述为什么Fastadmin里默认用Summernote而不是Vue生态的那些“高大上”编辑器Fastadmin里那个带图片上传、加粗斜体、表格插入的编辑框就是Summernote。它不是什么新潮框架2013年就开源了用jQuery写的但至今仍是Fastadmin后台最稳、最省心的富文本方案。我搭过二十多个Fastadmin项目从v1.0.1到最新版只要没动核心模板Summernote基本开箱即用——这点和现在满屏的Vue PC富文本编辑器比如Tiptap、Quill Vue封装版、或者Element Plus自带的Editor形成鲜明对比后者看着漂亮、响应快、支持Markdown实时预览但真往Fastadmin里塞光是解决Vue2/Vue3兼容、Webpack打包冲突、表单数据双向绑定丢失、提交时内容取不到这几个问题就能耗掉你一整天。Summernote不搞这些花活它就干一件事把用户敲进去的HTML原样存进数据库提交时用$(#content).summernote(code)一句命令就能拿到纯净HTML字符串后端直接接住入库连转义都不用多想。它适合谁适合Fastadmin这种以快速交付、稳定运行为第一要务的中小型后台系统尤其适合PHPMySQL技术栈、前端只求功能完整不追求极致交互体验的场景。如果你的客户明天就要上线今天还在纠结编辑器选型Summernote就是那个能让你今晚安心睡觉的选择。2. 核心设计思路与选型逻辑不是技术落后而是精准匹配业务节奏2.1 为什么Fastadmin没换Vue系编辑器三个硬约束讲透本质Fastadmin的架构决定了它对前端组件的“侵入性”容忍度极低。Summernote之所以成为默认不是因为开发者懒而是被三个现实约束死死卡住第一模板渲染机制不兼容Vue生命周期。Fastadmin所有页面都是PHP模板.html或.tpl直出表单字段靠{:$field}标签动态注入。Vue编辑器必须挂载在有id或ref的DOM节点上且依赖new Vue()实例初始化。但Fastadmin的表单是PHP渲染完才交给JS处理的等Vue实例准备就绪PHP早已把整个HTML吐给浏览器了。我试过强行在$(document).ready()里new Vue({el: #editor})结果发现编辑器初始化时Fastadmin的Form.api.bindevent事件监听器已经跑完了导致表单提交时Vue里的内容根本没同步到Fastadmin的form.serializeArray()数据里——提交的永远是空值。Summernote没这问题它纯jQuery插件$(#editor).summernote()执行完内部自动把内容写进隐藏textareaFastadmin表单序列化时自然能抓到。第二图片上传路径强耦合Fastadmin的Upload API。Summernote的图片上传配置项callbacks.onImageUpload一行代码就能对接Fastadmin的/ajax/upload接口onImageUpload: function(files) { var that this; var formData new FormData(); formData.append(file, files[0]); $.ajax({ url: /ajax/upload, data: formData, type: POST, cache: false, contentType: false, processData: false, dataType: json, success: function(res) { if (res.code 1) { $(that).summernote(insertImage, res.url); } } }); }而Vue编辑器上传得自己写Axios请求、处理Blob、拼接FormData、还要手动把返回的URL插入编辑器内容流——稍有不慎图片就传不上去或者URL插错位置。Fastadmin的/ajax/upload接口返回格式是{code:1,url:https://xxx.com/uploads/2024/05/abc.jpg}Summernote原生适配这种结构Vue编辑器得额外写解析逻辑。第三权限控制粒度太细Vue组件难复用。Fastadmin后台常有“普通编辑员只能加粗/斜体/插入图片管理员才能删表格/改字体颜色”的需求。Summernote通过toolbar配置数组精确控制按钮显隐toolbar: [ [style, [bold, italic, underline]], [insert, [picture, link]], // 普通用户不显示[color, [color]], [table, [table]] ]Vue编辑器要实现同样效果得在组件里写一堆v-if判断角色权限再动态渲染不同工具栏代码量翻倍且易出错。Summernote的配置是扁平JSONFastadmin后台PHP直接根据用户角色生成不同配置数组前端JS拿过来$.extend()一下就完事。提示别被“Vue PC富文本编辑器”这个热词带偏。它适合从零搭建的Vue SPA项目但Fastadmin是传统MVC后台强行嫁接Vue组件就像给拖拉机装F1方向盘——看起来高级实际操作更费劲。2.2 Summernote在Fastadmin中的真实定位不是编辑器是“内容管道”很多新手以为Summernote是个独立功能模块其实它在Fastadmin里是表单数据流中的一环。Fastadmin的Form类会自动识别textarea classsummernote标签并在bindEvent时为其初始化Summernote实例。这意味着你不需要手写任何JS初始化代码——只要HTML里有这个class它就自动工作。它的核心价值不是炫酷UI而是保证HTML内容从用户输入、到前端校验、再到后端接收的全链路一致性。例如当用户粘贴一段带内联样式的Word内容Summernote默认会过滤掉font标签、保留strong而Fastadmin的Validate类在提交前会调用summernote(code)获取HTML再交给PHP的htmlspecialchars()转义最后存进数据库。整条链路没有中间态转换不会出现“前端看到加粗后端收到纯文本”的割裂。Vue编辑器往往需要额外写watch监听内容变化再手动触发this.$emit(input, html)多一层就多一个出错点。3. 核心细节解析与实操要点从初始化到深度定制避开90%的坑3.1 初始化的两种方式模板写法 vs JS手动绑定选错就白忙Fastadmin里Summernote初始化分“声明式”和“命令式”两种新手常混淆导致重复初始化或失效。声明式推荐Fastadmin原生支持在Fastadmin的表单模板如add.html里直接写div classform-group label classcontrol-label col-xs-12 col-sm-2{:__(Content)}/label div classcol-xs-12 col-sm-10 textarea idc-content classsummernote namerow[content] rows5/textarea /div /div关键点有三classsummernote是Fastadmin识别并自动初始化的魔法class缺一不可namerow[content]必须符合Fastadmin的表单命名规范row[字段名]否则提交时后端收不到数据不要加>// 在Fastadmin的requirejs模块里 define([jquery, summernote], function ($, summernote) { $(document).ready(function() { // 先销毁可能存在的实例 $(#c-content).summernote(destroy); // 再重新初始化 $(#c-content).summernote({ height: 300, toolbar: [[style, [bold, italic]]] }); }); });注意如果同时用了声明式classsummernote又手动初始化会导致编辑器创建两次第二次初始化会报错Cannot re-initialize summernote。我踩过这个坑——页面加载时编辑器空白控制台报错查了两小时才发现模板里忘了删class。3.2 图片上传的致命细节URL路径、域名、HTTPS一个都不能错Summernote图片上传看似简单但Fastadmin环境下有三个隐藏雷区雷区一/ajax/upload接口返回的URL必须是绝对路径。Fastadmin的/ajax/upload默认返回相对路径如/uploads/2024/05/abc.jpg但Summernote插入图片时如果当前页面URL是https://admin.example.com/article/add它会把相对路径拼成https://admin.example.com/article/uploads/2024/05/abc.jpg结果404。解决方案是在PHP上传逻辑里强制返回完整URL// Fastadmin的upload.php里修改 $url \think\facade\Url::build(/uploads/ . $filename, , false, true); // 第四个参数true表示生成绝对URL return json([code 1, url $url]);雷区二跨域问题在本地开发环境必现。用php think run启动本地服务时前端地址是http://localhost:8000而后端API是http://localhost:8000看似同源但Summernote的onImageUpload用的是XMLHttpRequest如果Fastadmin的Nginx/Apache没配CORS头会报No Access-Control-Allow-Origin header。临时解法是在Nginx配置里加location /ajax/upload { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods POST; }生产环境必须用同一域名禁用此配置。雷区三HTTPS混合内容警告。如果网站启用了HTTPS但上传接口返回HTTP开头的URL如http://example.com/uploads/abc.jpg现代浏览器会拦截图片加载显示破碎图标。Fastadmin的Url::build默认按当前协议生成URL但某些CDN或反向代理配置错误时会漏掉协议。实测有效检查法在浏览器控制台执行console.log(location.protocol)确保返回https:再确认上传返回的URL协议一致。3.3 工具栏定制删掉“无用按钮”加上“真需要功能”Summernote默认工具栏有15个按钮组但Fastadmin后台90%的场景只需要5个。过度配置不仅增加加载时间还让编辑器变卡尤其低端电脑。我总结出Fastadmin最精简实用的配置$(.summernote).summernote({ height: 250, toolbar: [ [style, [bold, italic, underline, clear]], // 加粗斜体下划线清除格式 [color, [color]], // 字体颜色管理员专用 [para, [ul, ol, paragraph]], // 无序/有序列表段落格式 [insert, [link, picture, hr]], // 链接、图片、水平线 [view, [fullscreen, codeview]] // 全屏、源码模式 ], // 关键禁用自动保存草稿Fastadmin有自己的草稿机制 saveCallback: function (contents) {}, // 禁用自动聚焦避免表单加载时跳到编辑器 focus: false, // 中文语言包需提前引入zh-CN.js lang: zh-CN });为什么去掉这些按钮[table, [table]]后台文章极少需要复杂表格用hr分割更轻量[font, [strikethrough, superscript, subscript]]用户根本不用还占工具栏空间[help, [help]]帮助文档对后台编辑者意义不大Fastadmin有独立帮助中心。实操心得工具栏按钮顺序直接影响用户操作效率。我把picture放在link后面因为用户插入链接频率远高于图片codeview放最后避免新手误点进入源码模式无法退出。4. 实操过程与核心环节实现从零开始配置一个生产级Summernote4.1 完整配置流程五步走每步附验证方法第一步确认Fastadmin版本与Summernote兼容性Fastadmin v1.3.0 默认集成Summernote v0.8.18这是目前最稳定的组合。验证方法打开浏览器开发者工具执行$.fn.summernote.Constructor.VERSION返回0.8.18即正确。如果返回undefined说明Summernote未加载检查public/assets/js/require.js里是否漏了summernote依赖。第二步引入中文语言包避免乱码Summernote默认英文中文按钮名会显示为Bold、Italic。下载summernote-zh-CN.js官方GitHub可得放入public/assets/js/目录然后在public/assets/js/require.js的paths里添加paths: { summernote: ../libs/summernote/summernote.min, summernote-zh-CN: ../libs/summernote/lang/summernote-zh-CN.min }并在模块定义中require([summernote, summernote-zh-CN])。第三步配置图片上传核心步骤在表单JS文件如add.js里找到Controller.api.bindevent函数在success回调后添加// 绑定Summernote上传 $(#c-content).on(summernote.init, function() { $(this).summernote(disable); // 初始化时禁用防止用户乱输 }); $(#c-content).summernote({ callbacks: { onInit: function() { // 初始化完成后再启用 $(this).summernote(enable); }, onImageUpload: function(files) { // 复用Fastadmin的上传逻辑 var editor $(this); var file files[0]; var formData new FormData(); formData.append(file, file); $.ajax({ url: /ajax/upload, type: POST, data: formData, cache: false, contentType: false, processData: false, dataType: json, success: function(res) { if (res.code 1) { editor.summernote(insertImage, res.url); } else { Toastr.error(res.msg || 上传失败); } } }); } } });验证方法在编辑器里点图片按钮选一张图看是否弹出上传进度条成功后图片是否正常显示。第四步表单提交前的内容提取防丢内容Fastadmin默认用$(form).serialize()提交但Summernote内容在隐藏textarea里需手动同步。在Controller.api.bindevent的submit事件里加$(form).on(submit, function () { // 同步Summernote内容到textarea var content $(#c-content).summernote(code); $(#c-content).val(content); });验证方法提交表单后在PHP后端var_dump(input(post.))检查row[content]是否为HTML字符串如pstrong测试/strong/p而非空值。第五步后端内容安全过滤防XSS前端再严谨后端也必须过滤。在Fastadmin的控制器save方法里对content字段做白名单过滤use think\facade\Request; $content Request::post(row.content); // 只允许p,br,strong,em,u,ol,ul,li,a,img,hr标签其他全部删除 $allowedTags pbrstrongemuolulliaimghr; $content strip_tags($content, $allowedTags); // 过滤a标签的href只允许http/https $content preg_replace_callback(/a\shref([^])/i, function($matches) { $url $matches[1]; if (filter_var($url, FILTER_VALIDATE_URL) in_array(parse_url($url, PHP_URL_SCHEME), [http, https])) { return a href . htmlspecialchars($url) . ; } return a href#; }, $content); $data[content] $content;验证方法在编辑器里粘贴scriptalert(1)/script提交后数据库存的应是空字符串粘贴a hrefjavascript:alert(1)坏链接/a应变成a href#坏链接/a。4.2 高级定制让Summernote适配Fastadmin的“中国式需求”需求一限制字数但按中文字符计数Fastadmin后台常要求文章不超过2000字。Summernote原生maximumHeight是高度限制不是字数。解决方案用onKeyup事件实时统计$(#c-content).summernote({ callbacks: { onKeyup: function(e) { var content $(this).summernote(code); // 移除HTML标签只算文字 var text content.replace(/[^]*/g, ); var len text.length; $(#word-count).text(已输入 len 字限2000); if (len 2000) { Toastr.warning(内容超过2000字请删减); // 截断到2000字 var truncated text.substr(0, 2000); $(this).summernote(code, truncated); } } } });在HTML里加span idword-count/span显示字数。需求二一键清空格式保留段落结构用户从Word粘贴内容常带一堆无用样式。Summernote的clear按钮只清内联样式不清span style...。加个自定义按钮$(#c-content).summernote({ toolbar: [ [style, [bold, italic, clear, removeformat]], // 新增removeformat ], buttons: { removeformat: function (context) { var ui $.summernote.ui; var button ui.button({ contents: i classfa fa-eraser/ 清除格式, tooltip: 清除所有样式保留段落和列表, click: function () { var $note context.layoutInfo.note; var content $note.summernote(code); // 用正则清除所有style属性和class content content.replace(/([a-z][a-z0-9]*)[^]*?(?:\/|(.*?)\/\1)/gi, function(match, tag, inner) { if (inner) { return tag inner / tag ; } else { return tag /; } }); content content.replace(/ style[^]*/gi, ).replace(/ class[^]*/gi, ); $note.summernote(code, content); } }); return button.render(); } } });需求三图片点击放大移动端友好Fastadmin后台越来越多用手机审核小图看不清。给图片加>// 初始化Summernote后 $(#c-content).on(summernote.change, function() { // 给所有img加放大属性 var $imgs $(this).find(img); $imgs.each(function() { $(this).attr(data-action, zoom); }); // 初始化lightGallery需提前引入 $(#c-content).lightGallery({ selector: img[data-actionzoom], download: false }); });5. 常见问题与排查技巧实录那些文档里找不到的“血泪经验”5.1 问题速查表症状、原因、三步解决法症状可能原因解决步骤编辑器不显示页面空白1.classsummernote拼写错误如写成sumernote2.textarea标签未闭合3. Fastadmin的Form类未加载1. 检查HTML源码确认class名和标签闭合2. 浏览器控制台执行$(.summernote).length返回0说明未识别3. 查看Network面板确认summernote.min.js是否404图片上传后不显示控制台报4001.formData.append(file, files[0])中files[0]为空2. Fastadmin的/ajax/upload接口要求namefile但前端传了nameimage1. 在onImageUpload函数开头加console.log(files)确认有文件对象2. 查看/ajax/upload接口文档严格按参数名传值提交后数据库存的是空字符串1.$(#c-content).summernote(code)执行时机错误2. 表单name属性不是row[content]格式1. 在submit事件里加console.log($(#c-content).summernote(code))确认能取到内容2. 检查HTML里textarea namerow[content]注意中括号是英文符号编辑器里中文显示方块□□□1. 未引入summernote-zh-CN.js2. 浏览器字体缺失1. 执行$.summernote.langs[zh-CN]返回undefined说明语言包未加载2. 在CSS里加body { font-family: Microsoft YaHei, sans-serif; }5.2 踩过的坑那些让我加班到凌晨的“幽灵Bug”坑一Summernote在Tab页签里初始化失败Fastadmin常用Tab切换不同表单区域如“基本信息”、“SEO设置”、“内容详情”。当Summernote在非首屏Tab里初始化时DOM不可见导致高度计算为0编辑器压成一条线。解决方案不是等Tab显示后再初始化太麻烦而是用tab.show.bs.tab事件监听$(a[data-toggletab]).on(shown.bs.tab, function (e) { var target $(e.target).attr(href); if (target #tab-content) { // 假设内容Tab的ID是tab-content // 延迟100ms确保DOM渲染完成 setTimeout(function() { if (!$(#c-content).data(summernote)) { $(#c-content).summernote({ height: 250, lang: zh-CN }); } }, 100); } });坑二Fastadmin的“快捷键保存”CtrlS触发两次用户按CtrlS想保存结果编辑器内容被清空。原因是Fastadmin的全局快捷键监听和Summernote的onKeydown冲突。Summernote默认拦截CtrlS但Fastadmin又监听了一次。解决法禁用Summernote的快捷键$(#c-content).summernote({ shortcuts: false, // 关键禁用所有快捷键 callbacks: { onKeydown: function(e) { // 手动处理CtrlS if (e.ctrlKey e.keyCode 83) { e.preventDefault(); $(form).submit(); // 触发Fastadmin表单提交 } } } });坑三编辑器内容含nbsp;导致搜索不准用户回车后Summernote自动生成pnbsp;/p数据库存了大量nbsp;后端用LIKE %关键词%搜索时匹配不到。解决方案在提交前替换$(#c-content).summernote({ callbacks: { onBlur: function() { var content $(this).summernote(code); // 将nbsp;替换为普通空格再合并多余空格 content content.replace(/nbsp;/g, ).replace(/\s/g, ); $(this).summernote(code, content); } } });5.3 性能优化让Summernote在老旧电脑上也不卡Summernote默认加载所有插件如video,audio但Fastadmin后台几乎不用。精简加载可提升30%初始化速度// 只加载必需插件 $.summernote.options $.extend($.summernote.options, { modules: { editor: $.summernote.modules.Editor, toolbar: $.summernote.modules.Toolbar, statusbar: $.summernote.modules.Statusbar, codemirror: $.summernote.plugins.codemirror // 仅需codeview时加载 } });再配合懒加载不在首屏的编辑器等用户滚动到视口再初始化function initIfInViewport() { $(.summernote-lazy).each(function() { var $el $(this); var rect $el[0].getBoundingClientRect(); if (rect.top 0 rect.bottom window.innerHeight) { $el.summernote({ height: 200, lang: zh-CN }); $el.removeClass(summernote-lazy); } }); } $(window).on(scroll, initIfInViewport); initIfInViewport(); // 页面加载时检查一次HTML里写textarea classsummernote-lazy即可。6. 后续扩展与演进思考当业务增长Summernote还能撑多久Summernote在Fastadmin里不是终点而是起点。当你的项目从单后台发展为多端协同PC后台小程序APP内容生产方式会倒逼编辑器升级。我经历过三个阶段第一阶段Fastadmin单点作战0-10万内容Summernote完全够用。所有内容由运营人员在后台录入格式简单图片少。此时折腾Vue编辑器纯属浪费时间稳定压倒一切。第二阶段内容多源化10-50万内容开始有外部投稿、API导入、Word批量上传需求。这时Summernote的短板暴露不支持Markdown粘贴、不能解析Word的复杂样式。解决方案不是换编辑器而是加中间层——用Python写个docx2html服务把Word转成Summernote兼容的HTML再通过Fastadmin的/ajax/upload接口存入。成本远低于重构前端。第三阶段协同编辑与版本管理50万内容多人同时编辑同一篇文章需要实时协作、历史版本、变更对比。此时Summernote彻底不够用。我的实践是保留Fastadmin作为“终审发布平台”把内容生产迁移到专业CMS如Strapi用Tiptap做前端编辑器Fastadmin只负责调用Strapi的API拉取最终HTML发布。Summernote退化为“只读预览器”用$(#preview).summernote(code, html)展示不提供编辑能力。个人体会技术选型不是比谁新而是比谁更懂业务节奏。Summernote在Fastadmin里存在十年不是因为它多先进而是因为它足够“笨”——笨到不抢活、不添乱、不制造新问题。当你深夜接到客户电话说“首页文章错了马上要改”打开Fastadmin三秒定位两秒修改一键保存这才是真正的生产力。那些炫酷的Vue编辑器留着给下一个从零开始的项目吧。
网站建设高端定制企业官网