☰
Fastadmin为何默认选用Summernote富文本编辑器
2026/10/1 13:08:13 网站建设 项目流程

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这种以快速交付、稳定运行为第一要务的中小型后台系统,尤其适合PHP+MySQL技术栈、前端只求功能完整不追求极致交互体验的场景。如果你的客户明天就要上线,今天还在纠结编辑器选型,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()执行完,内部自动把内容写进隐藏<textarea>,Fastadmin表单序列化时自然能抓到。

第二,图片上传路径强耦合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的配置是扁平JSON,Fastadmin后台PHP直接根据用户角色生成不同配置数组,前端JS拿过来$.extend()一下就完事。

提示:别被“Vue PC富文本编辑器”这个热词带偏。它适合从零搭建的Vue SPA项目,但Fastadmin是传统MVC后台,强行嫁接Vue组件,就像给拖拉机装F1方向盘——看起来高级,实际操作更费劲。

2.2 Summernote在Fastadmin中的真实定位:不是编辑器,是“内容管道”

很多新手以为Summernote是个独立功能模块,其实它在Fastadmin里是表单数据流中的一环。Fastadmin的Form类会自动识别<textarea class="summernote">标签,并在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 class="form-group"> <label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label> <div class="col-xs-12 col-sm-10"> <textarea id="c-content" class="summernote" name="row[content]" rows="5"></textarea> </div> </div>

关键点有三:

  1. class="summernote"是Fastadmin识别并自动初始化的魔法class,缺一不可;
  2. name="row[content]"必须符合Fastadmin的表单命名规范(row[字段名]),否则提交时后端收不到数据;
  3. 不要加>// 在Fastadmin的requirejs模块里 define(['jquery', 'summernote'], function ($, summernote) { $(document).ready(function() { // 先销毁可能存在的实例 $('#c-content').summernote('destroy'); // 再重新初始化 $('#c-content').summernote({ height: 300, toolbar: [['style', ['bold', 'italic']]] }); }); });

    注意:如果同时用了声明式(class="summernote")又手动初始化,会导致编辑器创建两次,第二次初始化会报错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. 实操过程与核心环节实现:从零开始配置一个生产级Summernote

    4.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字符串(如<p><strong>测试</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 = '<p><br><strong><em><u><ol><ul><li><a><img><hr>'; $content = strip_tags($content, $allowedTags); // 过滤a标签的href,只允许http/https $content = preg_replace_callback('/<a\s+href="([^"]+)"/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;

    验证方法:在编辑器里粘贴<script>alert(1)</script>,提交后数据库存的应是空字符串;粘贴<a href="javascript: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 id="word-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 class="fa 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-action="zoom"]', download: false }); });

    5. 常见问题与排查技巧实录:那些文档里找不到的“血泪经验”

    5.1 问题速查表:症状、原因、三步解决法

    症状可能原因解决步骤
    编辑器不显示,页面空白1.class="summernote"拼写错误(如写成sumernote)
    2.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接口要求name="file",但前端传了name="image"
    1. 在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 name="row[content]">,注意中括号是英文符号
    编辑器里中文显示方块(□□□)1. 未引入summernote-zh-CN.js
    2. 浏览器字体缺失
    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-toggle="tab"]').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的“快捷键保存”(Ctrl+S)触发两次
    用户按Ctrl+S想保存,结果编辑器内容被清空。原因是Fastadmin的全局快捷键监听和Summernote的onKeydown冲突。Summernote默认拦截Ctrl+S,但Fastadmin又监听了一次。解决法:禁用Summernote的快捷键:

    $('#c-content').summernote({ shortcuts: false, // 关键!禁用所有快捷键 callbacks: { onKeydown: function(e) { // 手动处理Ctrl+S if (e.ctrlKey && e.keyCode === 83) { e.preventDefault(); $('form').submit(); // 触发Fastadmin表单提交 } } } });

    坑三:编辑器内容含&nbsp;导致搜索不准
    用户回车后,Summernote自动生成<p>&nbsp;</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 class="summernote-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编辑器,留着给下一个从零开始的项目吧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询