前阵子接了一个基于FastAdmin二次开发的内部管理后台,需求方提了个很实在的问题:“列表页右上角那排按钮太多了,我不想让运营的人随手点导出、点删除,能不能把搜索、导出这几个按钮去掉?”
我一开始以为就是删几行模板代码的事,结果真上手才发现,FastAdmin右上角这些按钮根本不是简单写死在某个文件里的。它们同时受前端JS配置、后台权限节点、模板工具栏容器三个层面的影响。这篇就围绕“FastAdmin怎么去除右上角的搜索、导出等按钮”,把按钮到底从哪来、怎么改最省事、哪些坑必须避开,完整梳理一遍。不管你用的是FastAdmin自带后台还是自己二开的模块,这套思路都能直接套。
1. 先搞清楚:右上角按钮从哪里渲染出来的
1.1 工具栏、搜索框、操作列是三层不同的结构
FastAdmin的列表页,打开之后你能看到的基本上是三块东西:表格右上角一排功能按钮、表格上方的快速搜索框或搜索弹窗、以及每一行数据右侧的“操作”列。
右上角那一排按钮,在FastAdmin里通常叫工具栏(toolbar),里面可能包含搜索、刷新、添加、删除、导入、导出等。快速搜索框是table组件自带的搜索输入框,在前端JS初始化表格时控制开关。操作列则是表格列定义里一个名为“operate”的列,默认放编辑、删除按钮。
先说一个很容易让人走弯路的地方:这些按钮的HTML并不是全部写在index.html模板里的。
我刚接触FastAdmin的时候也以为,去application/admin/view/模块名/index.html里面把按钮删掉就完了。结果模板里只有类似这样的容器:
<div id="toolbar" class="toolbar"> </div> <table id="table" class="table table-bordered table-hover" width="100%"></table>按钮呢?按钮是页面加载之后,由JS动态往#toolbar这个容器里填进去的。所以你光去模板里找,找不到也正常。后面我会具体讲JS是怎么生成的。
1.2 按钮显示的底层逻辑:前端配置叠加后端权限
FastAdmin的列表页,每个后台控制器会配一个对应的前端JS文件,路径基本是public/assets/js/backend/控制器名.js。这个JS入口会调用Table.api.init做初始化,然后调用$("#table").bootstrapTable({...})渲染表格。
在初始化过程中,FastAdmin会根据你在JS里配置的URL地址,以及当前登录管理员是否拥有对应权限节点,来决定哪些按钮渲染、哪些按钮不渲染。比如:
- 配置里给了
add_url,且当前账号有该控制器的添加权限,按钮才显示; - 配置里给了
export_url,且当前账号有导出权限,导出按钮才显示; - 搜索按钮没有独立权限节点,只要你进了列表页,基本都会显示。
所以,真正要搞明白“怎么去除按钮”,必须先理解这个双控机制:前端负责“能不能生成”,后端权限负责“当前账号允不允许生成”。两者都满足,按钮才会出现在页面上。
1.3 我见过的最常见误判:以为改HTML就能彻底解决
刚二开FastAdmin的人,十有八九会陷入一个误区:在模板里把#toolbar删了,或者把#table替换成别的,结果页面直接乱掉。
原因很简单:FastAdmin的JS初始化逻辑会依赖这些容器。你把#toolbar整个删掉,JS没有地方填充按钮,轻则工具栏缺失,重则JS报错,表格都加载不出来。正确的做法不是删容器,而是从配置层面让按钮“不生成”。
如果你用的模块是FastAdmin后台“一键生成CRUD”建出来的,按钮来源基本就是JS动态生成;但如果你接手的是别人手动写的页面,按钮也有可能真的写死在HTML里。拿到一个页面先别急着改,按F12打开开发者工具看一眼按钮DOM,再决定从哪个层面入手。
2. 不想改代码的优先方案:直接调整后台角色权限
2.1 权限节点和按钮的对应关系
FastAdmin后台的权限管理做得很细。默认生成CRUD模块时,会自动创建一批权限节点,常见对应关系如下:
| 按钮 | 权限节点 | 说明 |
|---|---|---|
| 添加 | 模块名/add | 有添加权限才显示 |
| 编辑 | 模块名/edit | 通常作用在操作列 |
| 删除 | 模块名/del | 工具栏批量删除和行内删除都受它控制 |
| 导入 | 模块名/import | 有导入权限才显示 |
| 导出 | 模块名/export | 有导出权限才显示 |
| 搜索 | 无独立节点 | 进列表页就显示 |
| 刷新 | 无独立节点 | 进列表页就显示 |
也就是说,如果你的需求只是“不想让某个角色看到导出、添加、删除按钮”,那完全不需要碰代码。直接在后台权限管理里,把这个角色对应的导出、添加、删除节点取消勾选即可。
2.2 实际配一遍:把某内部项目的导出功能收起来
我之前处理过一个内部订单管理模块,需求是运营组只能看数据,不能导出、不能删除。操作路径大概是这样:
- 进入后台“权限管理 → 角色管理”。
- 找到“运营组”这个角色,点击“编辑”。
- 在权限规则树里找到对应的控制器节点,例如
订单管理。 - 展开节点,只保留“查看”(对应
订单/index),把“添加、编辑、删除、导入、导出”全部取消勾选。 - 保存后,让该角色下的账号退出后台再重新登录一次。
按照这个方式配完,普通运营账号打开列表页的时候,右上角添加、删除、导入、导出按钮就不会出现,但数据仍然可以正常查看。
我当时用这个方案解决的第一个需求,前后没超过五分钟。你要知道,这种方式最大的好处是:一个角色下面有几百个账号,你只需要改一个角色的权限配置,所有账号的按钮显示状态会同步变化,不用挨个去改前端代码,也不用重新走部署流程。
2.3 两个绕不开的注意点:超级管理员和权限缓存
权限方案虽然省事,但有两个坑必须提醒你。
第一个坑是超级管理员。FastAdmin的超级管理员组在权限判断时是直接放行的,也就是说,哪怕你把某个普通角色权限全部取消,超管账号进入页面依然能看到所有按钮。这个设计本身没问题,但会导致你测试的时候误以为“改了没生效”。验证权限效果,务必用一个非超管账号去试。
第二个坑是权限缓存。FastAdmin的权限校验结果会做缓存,你刚改完角色权限,可能不会立刻生效。常规做法是让对应账号退出重新登录,或者去后台“系统 → 控制台”里清一下运行缓存。如果这两个都做了还是不行,就检查一下runtime目录里对应的缓存文件是否真的被删除。
如果你改完权限之后,刷新页面按钮确实消失了,那说明你的需求已经被解决了一大半。但如果你还需要去掉搜索、刷新这种没有权限节点的按钮,请继续往下面看。
3. 要精确到具体页面,还是得改控制器对应的JS
3.1 先定位当前页面加载的JS文件
权限方案适合“按角色区分”,但有些场景下,即使是同一角色,你也想让某个具体页面干净一些,而其他页面保持原样。这时就要去改页面对应的前端JS了。
定位方法很简单:打开列表页,按F12看Network面板,找到backend/xxx.js,其中xxx就是当前模块名称。也可以在后台URL里看到,比如你访问的是admin/order/index,那对应的JS文件往往就是backend/order.js。
这里有个我自己的习惯:改之前先复制一份原JS文件到本地备份,改坏了随时能回退。FastAdmin二开过程中最怕的就是改到一半找不到原始代码。
3.2 用Table.api.init把不需要的URL全部置为false
看一个FastAdmin自动生成模块中比较常见的JS结构:
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { var Controller = { index: function () { // 初始化表格参数 Table.api.init({ extend: { index_url: 'order/index', add_url: 'order/add', edit_url: 'order/edit', del_url: 'order/del', import_url: 'order/import', export_url: 'order/export' } }); var table = $("#table"); // 初始化表格 table.bootstrapTable({ url: $.fn.bootstrapTable.defaults.extend.index_url, pk: 'id', sortName: 'id', search: true, columns: [ [ {field: 'id', title: __('Id')}, {field: 'operate', title: __('Operate'), table: table, ...} ] ] }); // 绑定事件 Table.api.bindevent(table); } }; return Controller; });注意观察extend这一块,里面放了各个动作的URL。FastAdmin渲染按钮的时候,如果发现某个URL被设置成了false或者空值,就不会生成对应的按钮。
所以,我想让这个页面变成一个“纯查看页”,就把这些URL全部置为false:
Table.api.init({ extend: { index_url: 'order/index', add_url: false, edit_url: false, del_url: false, import_url: false, export_url: false } });这样改完之后,右上角的添加、删除、导入、导出按钮就会消失。编辑按钮一般在操作列,也同样因为edit_url为false而不再渲染。
如果你只想去掉导出,保留添加功能,那就只把export_url置为false:
Table.api.init({ extend: { index_url: 'order/index', add_url: 'order/add', edit_url: 'order/edit', del_url: 'order/del', import_url: 'order/import', export_url: false } });这种改法对单个页面非常精准,不会影响其他模块,也不会被角色权限干扰。唯一的缺点是,如果同一个模块下了有好几个页面,你得逐个去改。
3.3 如果你用的版本支持toolbar按钮配置,也可以这样写
有些FastAdmin版本里,Table.api.init支持直接配置工具栏按钮的可见性。大致思路是:
Table.api.init({ extend: { index_url: 'order/index' }, toolbar: { buttons: { search: {visible: false}, refresh: {visible: false}, add: {visible: false}, del: {visible: false}, import: {visible: false}, export: {visible: false} } } });不过这里要特别提醒一下:FastAdmin不同版本之间的配置字段名有调整,这个示例的字段名并不是在所有版本里都能直接生效。最靠谱的办法是打开你自己的public/assets/js/require-table.js,搜索一下Toolbar、buttons这些关键词,看清楚你当前版本支持哪种写法。我早期写配置的时候,就因为在网上抄了一段旧版本代码,结果新版本里压根不识别,按钮纹丝不动。
这个也符合我一直在强调的:框架版本的API会变,代码能跑通才是硬道理,看源码永远是最准的。
3.4 操作列按钮怎么精简
右上角工具栏处理完,接下来是每一行数据右侧的操作列。FastAdmin自动生成模块的操作列,默认会有编辑、删除按钮,有时还有一个“更多”下拉菜单。
如果你希望这个页面完全不允许编辑和删除,直接在列定义里把operate列做一些调整。常见做法是把默认的buttons置为空数组:
{ field: 'operate', title: __('Operate'), table: table, buttons: [], formatter: Table.api.formatter.operate }这样操作列就只剩下一个没有按钮的空白列。更彻底一点,干脆把整个operate列定义删掉,表格右侧就没有操作列了。
但如果你还是想保留一个“详情”入口,只是不想让人编辑和删除,可以自己定义一个按钮:
{ field: 'operate', title: __('Operate'), table: table, buttons: [ { name: 'detail', title: __('详情'), classname: 'btn btn-xs btn-info', icon: 'fa fa-eye', click: function () { // 这里写你自己的详情逻辑,一般是打开一个弹窗或跳转详情页 } } ], formatter: Table.api.formatter.operate }这里的click事件处理需要你自己补充,FastAdmin本身不会帮你去跳转。我的习惯是参考同项目里其他模块已有的详情弹窗写法,直接复制改一下接口地址,省时省力。
3.5 再次强调:先看全局JS再动手
很多人在改FastAdmin按钮时卡住,根本原因是拿旧版本的代码往新版本里套。我的建议是,不管在网上看到什么配置,先打开自己项目里的require-table.js和require-common.js,从源码里找到按钮生成的逻辑。
我见过最典型的情况是,一个按钮在A版本叫export,在B版本叫btnExport,你如果按旧名字写,自然不会生效。所以楼上的代码示例,我只建议当成思路参考,具体字段以你当前正在用的版本源代码为准。
4. 搜索按钮的特殊性:它没有权限节点,得单独处理
4.1 搜索按钮为什么权限方案管不住
如果你仔细看第2部分的权限节点表,会发现搜索和刷新这两个按钮没有对应的权限节点。也就是说,只要当前管理员能访问index列表页面,FastAdmin就默认允许搜索和刷新,这是表格组件的基础行为。
权限方案对这两个按钮基本无效。你就算把角色的权限节点一朵不剩地取消,超级管理员之外的角色也确实看不到添加、导出、删除按钮,但搜索按钮依然在右上角出现。这个设计本身很好理解,因为搜索是列表页的核心查询入口,没有它,运营连筛选数据都做不到。
但需求方往往不在意这些,产品上说了要“右上角干干净净”,那搜索按钮就必须处理。
4.2 处理搜索按钮的几种常用姿势
第一种是JS配置隐藏。在Table.api.init里,如果当前版本支持toolbar.buttons的visible配置,就可以这样写:
toolbar: { buttons: { search: {visible: false}, refresh: {visible: false} } }第二种是CSS隐藏。这种方案比较适合“不想动JS逻辑,就想让某个页面的搜索按钮不显示”的情况。先给需要隐藏按钮的页面容器加上一个独立class,再写CSS:
.order-index #toolbar .btn[data-type="search"] { display: none !important; }CSS方案的好处是不依赖FastAdmin的版本API,只要按钮的DOM属性没变,基本能一直用。坏处是按钮虽然看不见了,但按钮对应的搜索事件和搜索弹窗仍然存在,只是没有入口触发而已。
第三种是模板覆盖。如果你的页面模板里,工具栏按钮是真的写在HTML里面的,那直接在模板里注释或删除对应按钮节点就行。不过就像第1部分说的,FastAdmin自带页面的按钮多半是JS生成的,这一招多数时候用不上。
我自己用得最多的是第一种和第二种结合:优先用JS配置,配置不生效再上CSS。反正最终目的只是让页面显示变干净,用什么方式不重要。
4.3 顺手处理右上角的快速搜索输入框
很多FastAdmin列表页右上角除了“搜索”按钮,还有一个直接输入关键字的快速搜索框。它是bootstrap-table组件的原生搜索,通常由初始化参数里的search字段控制。
如果也想一并去掉,在表格初始化的地方加一个参数即可:
table.bootstrapTable({ url: $.fn.bootstrapTable.defaults.extend.index_url, pk: 'id', sortName: 'id', search: false, columns: [...] });这里有个细节:search: false关闭的是表格快速搜索框,和工具栏上那个“搜索弹窗按钮”是两个东西。我之前改过的一个项目里,客户说“搜索按钮已经没了,但右上角怎么还有一个搜索输入框”,其实就是快速搜索框没关。
把search设为false之后,快速搜索框会消失,但页面的正常搜索功能如果由工具栏搜索弹窗提供,那依然可以通过其他入口打开。如果你希望完全没有任何搜索能力,那就得同时按下葫芦又想按下瓢,把两处都关掉。
还有一类更极端的需求,要求“页面里不允许出现任何搜索UI,但后端筛选接口必须保留”。这种情况我在实操中的做法是:彻底去掉搜索弹窗和快速搜索框,然后在页面上放一个自定义筛选区域,用自定义的查询按钮触发刷新。这种方式虽然麻烦,但能兼顾产品诉求和业务查询能力。
5. 按钮已经看不见了,不等于功能已经断掉了
5.1 界面隐藏和权限隔离是两码事
很多人在做完JS隐藏之后,觉得大功告成。实际上,如果你只是把前端按钮藏起来,而后端接口依然可以访问,那么只要有人知道接口地址,照样能绕过UI直接操作。
举一个我实际碰到过的例子:之前有个模块的导出按钮被我用export_url: false从界面上去掉之后,我以为这个运营角色就无法导出数据了。结果后来发现,只要在浏览器地址栏直接访问admin/order/export,数据照样能下载。原因就是后端导出方法没有任何权限判断,也没有做前置校验。
这就引出我的一个原则:按钮的显示样式是UI层面的事情,接口能不能被调用是权限和功能层面的问题。要做彻底,必须两头一起处理。
5.2 在后端补上权限判断或直接禁用入口
FastAdmin的控制器方法都在application/admin/controller/模块名.php里。对于你不想让普通角色调用的方法,最简单的方式是直接返回错误提示:
public function export() { $this->error('当前角色无权导出数据'); }如果你还是希望某个特定角色可以导出,那就需要结合后台权限节点判断。FastAdmin的权限判断一般是基于当前登录管理员的auth对象。具体写法可以参考控制器基类里的权限判断逻辑,或者在自己项目里已有的权限控制代码中找一个例子,照着改就行。
我的习惯是先在控制器方法里加上权限校验,没有权限直接拒绝,然后前端再把按钮隐藏。这样就算有人猜到URL,也调不动接口。而“拥有权限的管理员”即便按钮被隐藏了,依然可以通过正常权限端点完成操作,两条路径都能闭环。
5.3 一个“只读精简列表页”的完整改法
为了让你更好地落地,我直接给一个组合方案。假设我有一个模块叫内部项目A,控制器是work,需求是:普通管理员只能查看列表,不能添加、编辑、删除、导入、导出,也不能搜索,右上角只保留一个干净的表格。
第一步,后端控制器Work.php里,把不需要开放的方法全部禁掉或做权限判断,例如:
public function add() { $this->error('当前角色无权添加'); } public function edit($ids = null) { $this->error('当前角色无权编辑'); } public function del($ids = null) { $this->error('当前角色无权删除'); } public function import() { $this->error('当前角色无权导入'); } public function export() { $this->error('当前角色无权导出'); }第二步,前端JSbackend/work.js里,把对应URL全部置为false,同时关闭快速搜索:
Table.api.init({ extend: { index_url: 'work/index', add_url: false, edit_url: false, del_url: false, import_url: false, export_url: false } }); var table = $("#table"); table.bootstrapTable({ url: $.fn.bootstrapTable.defaults.extend.index_url, pk: 'id', sortName: 'id', search: false, columns: [ [ {field: 'id', title: __('Id')}, {field: 'title', title: __('标题')}, {field: 'status', title: __('状态'), formatter: Table.api.formatter.status} ] ] }); Table.api.bindevent(table);第三步,如果还需要去掉“搜索”按钮本身,再补一段CSS或者toolbar按钮配置。具体用哪种,看你当前FastAdmin版本和项目的实际代码情况。
第四步,在后台角色管理中,把该角色的添加、编辑、删除、导入、导出节点全部取消勾选。这样角色权限和前端UI就保持了一致。
最后一步,清缓存、重新登录,然后用普通管理员账号进页面看效果。你会发现右上角那排按钮消失了,表格纯展示,非常干净。
这套组合动作我在好几个项目里都验证过,基本能满足“只要看数据,不让你乱操作”这种常规管理需求。
我在实际使用中还有一个体会:不要一上来就改核心公共文件,比如require-table.js。如果你在公共文件里直接删掉按钮定义,那全站所有模块的按钮都会跟着变,后患无穷。宁可多花几分钟找到当前控制器自己的JS,把配置写在自己的模块里,这样以后要恢复也方便。还有一个调试小技巧,特别适合FastAdmin这种按钮动态渲染的框架:在页面上按F12,用选择器点中那个你不想看到的按钮,看一下它的DOM结构和>