简介:针对 jQuery EasyUI 1.3.4 框架的图标与样式整合资源,适合正在开发后台管理系统、数据表格或工具栏界面的前端工程师,能够一次性补齐操作状态、导航箭头、增删改编辑等常用图标,减少在不同图标库之间切换的麻烦。压缩包约 825KB,以 icon.css 样式文件与 icons 图标文件夹中的 PNG、SVG 素材为主,文件分布简单明了,可直接引入项目,也便于按需替换特定图标。目前已有 204 人学习/浏览,对于使用该版本框架的维护者具有一定参考价值。资源覆盖了 CSS 类引用、JavaScript 插件挂载和自定义样式三种常见用法;在按钮中添加 icon-add 等类名即可显示对应图标,也能把图标动态放到对话框或面板标题中,适配工具栏、菜单等多种交互场景。若默认显示效果与项目风格不符,还可修改颜色、尺寸、边距等属性,从而深入理解 1.3.4 的主题定制逻辑,缩短样式调试与切图时间,让界面观感更加统一。
1. 拿到 EasyUI 1.3.4 的图标与样式,先搞明白它到底是什么
如果你手头有一套七八年前起步的后台管理系统,多半见过这种界面:左侧 accordion 菜单、顶部 tabs、datagrid 表格配 toolbar 按钮,按钮上挂着小图标,风格朴素但整齐。这套东西的底子很可能就是 jQuery EasyUI,而 1.3.4 是它生命周期里被用得最广的版本之一。很多人搜“最新全面的图标以及样式”,其实不是想追新,而是拿到了一个还在跑的老项目,需要把按钮、菜单、树节点上的图标摸清楚,或者想把默认样式改成公司想要的风格。
这个版本的一个核心事实是:它的图标体系不是字体图标,而是 CSS Sprite 拼图——所有小图标都在一张大图上,通过 background-position 定位显示。这意味着你没法像用 Font Awesome 那样随手写个 class 就完事,得理解它那套 iconCls 的映射逻辑。样式体系则是按主题目录组织的:一套 easyui.css 管组件布局,一套 icon.css 管图标,换肤靠换 themes 目录下不同的主题文件夹。
本文面向两类人:一是维护老系统的工程师,想在不升级版本的前提下把界面收拾明白;二是刚接触 EasyUI 的新手,想知道这个老框架的样式机制到底怎么运转、自定义图标怎么加、踩坑点集中在哪里。我会按图标体系、样式结构、实际落地、典型坑、进阶技巧的顺序拆开讲。先说结论:1.3.4 的图标和样式设计在今天看不算先进,但它这套基于背景定位的 iconCls 机制和主题覆盖方式,比很多现代框架的“样式隔离”更直白,学会了反而对理解 CSS 优先级有好处。
2. 图标体系拆解:iconCls 是怎么把图标挂到组件上的
2.1 先从 icon.css 和图片文件夹说起
EasyUI 1.3.4 的图标机制,核心依赖两个东西:一个叫 icon.css 的样式文件,一个存放拼图切片的图片目录。默认主题下,它们的路径关系是这样的:
/themes/ ├── default/ │ ├── easyui.css │ ├── icon.css │ └── images/ │ ├── blank.gif │ └── icons/ │ ├── pager_first.png │ ├── pager_last.png │ ├── ... └── ...打开 icon.css 看一下,你会发现里面全是类似这样的规则:
.icon-blank { background: url('icons/blank.gif') no-repeat center; } .icon-add { background: url('icons/add.png') no-repeat center; } .icon-edit { background: url('icons/edit.png') no-repeat center; } .icon-remove { background: url('icons/remove.png') no-repeat center; } .icon-save { background: url('icons/save.png') no-repeat center; } .icon-cut { background: url('icons/cut.png') no-repeat center; } .icon-ok { background: url('icons/ok.png') no-repeat center; } .icon-no { background: url('icons/no.png') no-repeat center; }注意这个写法上的关键点:它用的不是背景拼图定位,而是单独的 PNG 文件。这意味着 1.3.4 的图标体系是“文件碎片化”的——每个图标一个 PNG 小图,路径写在 CSS 里,组件通过 iconCls 把 class 挂上去。这和早期版本里整张拼图定位的做法不太一样,算是 1.3.4 的一个特点。
真正要严格执行的一点:icon.css 里的 url 路径是相对路径,如果你的页面不是在主题目录下打开的,浏览器会找不到图片。最常见的情况是页面在项目根目录,而 CSS 在 themes/default 下,那 url('icons/add.png') 实际是从 themes/default/icons/add.png 加载的,只要 CSS 文件本身没被二次打包、路径没被改写,就没问题。但如果你用打包工具压缩合并了 CSS,又没有把图片路径改成绝对路径,图标就会全部白屏。这是后话,先记住这个路径关系。
2.2 组件上挂图标的三种方式:panel、toolbar、tree
知道了 class 的定义,接下来看怎么用。EasyUI 1.3.4 的组件里,几乎所有能带按钮的区域都支持 iconCls 属性。最常见的三种挂法如下。
第一种是 panel / window / dialog 的标题栏图标。初始化时直接给属性:
$('#myDialog').dialog({ title: '编辑用户', iconCls: 'icon-edit', width: 400, height: 300 });这是最基础的用法,iconCls 会同时出现在标题栏左侧和右上角关闭按钮附近的前缀位置。如果你之后想动态换图标,用组件的方法:
$('#myDialog').dialog('setTitle', '预览模式'); // 只改标题 $('#myDialog').dialog('options').iconCls = 'icon-search'; // 直接改配置不好使这里有个坑要提前说:直接改 options 对象里的 iconCls 不会触发界面刷新。正确做法是通过 panel 的刷新方法或者重新配置:
$('#myDialog').panel('options').iconCls = 'icon-search'; // 或者更直接的做法 $('#myDialog').dialog({ iconCls: 'icon-search' });重新传入配置项会被 dialog 内部的初始化逻辑吃掉,但不一定会马上重绘。我的习惯是用 panel 的setIcon方法——不过 1.3.4 的 panel 并没有这个公开方法。实际的可靠做法是拿到 panel 的标题 DOM,手动改样式类:
var titleEl = $('#myDialog').panel('panel').find('.panel-title').parent(); titleEl.find('.panel-icon').attr('class', 'panel-icon icon-search');这个做法绕过了框架层的局限,直接操作 DOM,在 1.3.4 里是稳妥的。原因是 1.3.4 的 dialog 继承自 window,window 继承自 panel,而 panel 的 head 区域结构是固定的:.panel-header内部有.panel-title和.panel-icon两个关键节点。手动替换样式类相当于做了框架没提供的事。
第二种是 toolbar / linkbutton 的按钮图标。linkbutton 是 EasyUI 里最常见的按钮形态,在 toolbar 里放一排操作按钮时这样写:
<div id="tb"> <a href="javascript:void(0)" class="easyui-linkbutton" ><a href="javascript:void(0)" class="easyui-linkbutton" >$('#menuTree').tree({ data: [{ id: 1, text: '系统管理', state: 'closed', iconCls: 'icon-cog', children: [{ id: 11, text: '用户管理', iconCls: 'icon-user', attributes: { url: '/user/list' } }] }] });这里有个行为细节:state 为 closed 的父节点,在展开和收起时图标会切换。如果你只给了 iconCls,展开状态下会自动用 expandedIconCls,如果没定义就用默认的文件夹类图标。所以想保持自定义图标不变化,得同时设置:
{ id: 1, text: '系统管理', state: 'closed', iconCls: 'icon-cog', expandedIconCls: 'icon-cog' }从数据里看不到图标变化规律时,八成是忘了这个属性。这是 tree 场景下的高频问题,后面避坑章还会讲。
2.3 datagrid 里图标也走 iconCls,但对应的是列头
datagrid 的列头排序箭头、操作列里的按钮,也都能用图标。列头默认自带排序箭头,这是内置行为,不需要配置。操作列里的按钮写法是 formatter 函数里拼 linkbutton 标签:
$('#dg').datagrid({ columns: [[{ field: 'id', title: '操作', width: 120, formatter: function(value, row, index) { return '<a href="javascript:void(0)" class="easyui-linkbutton" ' + 'data-options="iconCls:\'icon-eye-open\', plain:true" ' + 'onclick="viewRow(' + index + ')">查看</a>'; } }]] });formatter 里返回的 HTML 是纯字符串,组件渲染时会让它替换单元格内容。这里有三个注意点。第一,.easyui-linkbutton这个类必须在返回的标签里带上,否则组件不会把它解析成按钮外观——但即使带了,也需要在表格加载完成后手动调用一次解析,因为 datagrid 渲染列时不会自动初始化单元格里的组件:
$('#dg').datagrid({ onLoadSuccess: function() { $(this).find('.easyui-linkbutton').linkbutton(); } });第二,iconCls 的值在字符串拼接中要注意引号嵌套,我习惯用反斜杠转义单引号,或者干脆把 iconCls 改成用双引号写。第三,icon-eye-open这个图标在 1.3.4 内置列表里有没有?有。它的拼图里眼睛相关的图标至少有两个:eye 和 eye-open。但如果你不确定某个图标是否存在,最好先在 icon.css 里 grep 一下,不然会出现“有样式、没图”的白板按钮。
到这里,图标的基本用法链路就清楚了:icon.css 定义 class → class 映射到具体 PNG 文件 → 组件通过 iconCls 接受 class 名 → 渲染时把 class 加到对应 DOM 节点上。链路不复杂,但每一环都有各自的路径、引号、初始化问题。接下来要把样式体系看清楚。
3. 样式体系拆解:主题目录、组件尺寸、优先级逻辑
3.1 easyui.css 里到底装了什么东西
一个典型引入 EasyUI 1.3.4 的页面,head 里大概是这样的:
<link rel="stylesheet" type="text/css" href="themes/default/easyui.css"> <link rel="stylesheet" type="text/css" href="themes/icon.css"> <link rel="stylesheet" type="text/css" href="themes/color.css"> <script type="text/javascript" src="jquery.min.js"></script> <script type="text/javascript" src="jquery.easyui.min.js"></script>注意这里的两个细节。第一个细节:icon.css 在主题目录外面。normal 情况下 easyui.css 在 themes/default/ 下,而 icon.css 在 themes/ 下。这是因为 icon.css 不随主题变化——所有主题共用一套图标,只有 easyui.css 里的组件边框、背景色、圆角值跟着主题走。如果你拿到的项目里 icon.css 放在了 themes/default/ 里面,说明作者做过目录重整,引用路径要相应调整。
第二个细节:color.css 不是 1.3.4 自带的,它是从 1.3.4 之后的某个版本开始引入的。不过 1.3.4 的源码包实际也带了 color.css,里面定义了一堆.color-red、.color-green之类的辅助类,用于给 panel、dialog 设置边框色。这份文件不影响主体样式,可以要也可以不要。
easyui.css 本身是一份非常大的文件,按组件分块:panel、window、dialog、linkbutton、datagrid、tree、tabs、accordion、menu、combo、validatebox、numberbox 等。每一块的样式规则都遵循一个固定套路:.组件名控制外框,.组件名-header、.组件名-body控制内部区块,.组件名-xxx控制更细粒度的节点。
这份 CSS 的结构决定了“覆盖样式”的基本思路:要改某个组件的颜色,不是去改 easyui.css 里的原始规则,而是另写一份覆盖样式,利用 CSS 后加载优先级来覆盖。这几乎是所有 EasyUI 项目的通用做法。
3.2 主题切换的原理:换目录,不换代码
同理,如果你想做一套企业自己的配色,不需要改 CSS 文件里的每条颜色值,而是复制一个主题目录,改里面的 easyui.css,然后在页面里引用新的目录。比如:
/themes/ ├── default/ ├── gray/ ├── bootstrap/ ├── black/ ├── metro/ └── company-blue/ ← 自己复制的定制主题引用时改成:
<link rel="stylesheet" type="text/css" href="themes/company-blue/easyui.css">主题机制的本质是 CSS 文件级的替换,不是 LESS/SASS 变量级的换肤。1.3.4 这个版本没有内置 CSS 变量,也没有动态编译皮肤的能力,所以换肤就等于换文件。这也是为什么很多老项目“换肤”功能做得很痛苦——因为它是整文件切换,而不是变量切换。
做定制主题时,我一般不会直接改 default 里的 easyui.css,而是把 default 的 easyui.css 整份复制成 company-blue/easyui.css,再在文件末尾追加覆盖规则。这样保留了原始规则的完整性,升级或对比时方便 diff。追加的内容集中在几个地方:
/* 自定义主题:覆盖主色调 */ .panel-title { color: #1a5a9e; font-size: 14px; } .panel-header, .window-header { background: #f0f4f9; border-color: #b8c9dc; } .datagrid-header { background: #e8eef5; border-color: #b8c9dc; } .datagrid-row-selected { background: #c8dcf0; color: #0f2f50; }这些覆盖规则放在文件末尾,依靠“同样特异性下,后出现的规则胜出”的 CSS 机制生效。如果你要覆盖的规则原本特异性更高——比如原规则写的是.datagrid-row-selected td而你写的是.datagrid-row-selected——那必须提高自己这边的特异性,比如写成.datagrid .datagrid-row-selected td,否则覆盖不上去。这个优先级问题几乎是所有人都会踩的坑。
主题目录里还有一个易被忽略的文件:easyui.css末尾会把所有组件的字体、字号统一设置到.easyui-*相关类上。如果你页面里的输入框、表格字号看着不统一,问题往往出在这里,而不是你自己的业务样式。
3.3 尺寸体系:行高、边框、间距的“硬编码”现状
EasyUI 1.3.4 的组件尺寸大量使用固定像素值。datagrid 默认行高是 25px 左右,panel 标题栏高度、toolbar 高度、分页条高度也都是固定值。这个特点的好处是界面整齐,坏处是当你的页面字号放大、屏幕密度变化时,组件看起来会很挤。
要系统性地调整尺寸,靠逐条覆盖 CSS 效率太低。我的做法是把关键尺寸提成一组自定义 CSS 变量——注意,这里说的是覆盖样式里的变量,不是框架支持的变量:
:root { --datagrid-row-height: 32px; --panel-header-height: 36px; --toolbar-btn-height: 28px; }然后写覆盖规则把用到这些尺寸的地方统一替换。不过 1.3.4 没有原生的变量机制,所以 CSS 变量只对支持它的浏览器生效,老项目如果还在服务 IE 时代的浏览器,这招可能部分失效。对大部分团队来说,这个版本维护的就是“现代浏览器能跑就行”,用 CSS 变量问题不大。
更关键的一个尺寸点是 datagrid 列宽:fitColumns: true会让表格自动填满容器宽度,但它的宽是算出来的,不是百分比。如果容器宽度变化频繁(比如窗口缩放、左侧菜单折叠),列宽不会自动跟着 flex 布局自适应——需要手动调$('#dg').datagrid('resize')。这和样式尺寸相关,但属于行为维度,专门在这里提醒,是因为很多人以为 fitColumns 会自动响应窗口变化。
3.4 图标和样式的“异类”:validatebox、combo、spinner 的图标坑
1.3.4 里还有一类组件会用到图标,但方式跟 iconCls 不太一样:combo 的下拉箭头、spinner 的加减按钮、validatebox 的校验错误图标。这些用的是内置的纯 CSS 绘制或内置图片,不走 iconCls 体系。比如 combo 的下拉箭头是span.combo-arrow,它没有 background-image,而是用边框画的小三角:
.combo-arrow { background: url('images/combo_arrow.png') no-repeat center; }不同的主题目录下,images/里放的这些组件背景图不同,所以换主题时这些箭头、按钮图标也跟着变。这也就解释了为什么换主题需要整目录替换——因为组件的小图片都散在各自主题的 images 里。
如果你发现某个组件在定制主题下箭头变成空白,第一反应不是去查 iconCls,而是查这个组件的背景图片路径是否在你的新主题目录里存在。很多人复制主题时只拷贝了 easyui.css,漏了整个 images 文件夹,结果组件白屏、箭头消失、按钮没有背景。这类问题的排查方向完全不在样式代码里,而在文件系统里。
4. 从零搭一套带图标和自定义样式的后台骨架:完整步骤
4.1 准备好本地结构并跑通最小页面
动手之前,先在本地建一个干净的目录结构,只放必要文件,排除干扰:
easyui-demo/ ├── index.html ├── jquery.min.js ├── jquery.easyui.min.js └── themes/ ├── icon.css ├── default/ │ ├── easyui.css │ └── images/ └── ...我建议用 1.3.4 的完整源码包来取文件,取这三样:jquery.min.js(注意版本要和 EasyUI 匹配,1.3.4 支持 jQuery 1.8+,但不适合配 jQuery 3.x)、jquery.easyui.min.js、themes 目录。不要自己去 CDN 拼凑,匹配的 jQuery 版本可以省掉很多奇怪报错。
最小页面这样写:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>EasyUI 1.3.4 图标与样式验证页</title> <link rel="stylesheet" type="text/css" href="themes/default/easyui.css"> <link rel="stylesheet" type="text/css" href="themes/icon.css"> <script type="text/javascript" src="jquery.min.js"></script> <script type="text/javascript" src="jquery.easyui.min.js"></script> </head> <body> <div id="tb"> <a href="javascript:void(0)" class="easyui-linkbutton" >$('#dg').datagrid({ toolbar: '#tb', data: [ { id: 1, name: '模拟数据A', status: '启用' }, { id: 2, name: '模拟数据B', status: '停用' } ], columns: ... });4.2 列一个能在 1.3.4 直接用的图标清单
把源码包里 icon.css 里所有图标名列出来,在本地建一个图鉴页面,是 1.3.4 老项目最值得做的第一步。做法如下:
<div id="iconList"></div> <script type="text/javascript"> $(function() { var icons = [ 'icon-add', 'icon-edit', 'icon-remove', 'icon-save', 'icon-cut', 'icon-ok', 'icon-no', 'icon-cancel', 'icon-reload', 'icon-search', 'icon-print', 'icon-help', 'icon-undo', 'icon-redo', 'icon-back', 'icon-sum', 'icon-tip', 'icon-filter', 'icon-man', 'icon-lock', 'icon-more', 'icon-mini-add', 'icon-mini-edit', 'icon-mini-refresh' ]; var html = '<table><tr>'; for (var i = 0; i < icons.length; i++) { html += '<td style="padding:8px;text-align:center;">' + '<span class="' + icons[i] + '" style="display:inline-block;width:16px;height:16px;"></span>' + '<br>' + icons[i] + '</td>'; if ((i + 1) % 8 === 0) html += '</tr><tr>'; } html += '</tr></table>'; $('#iconList').html(html); }); </script>这个页面的逻辑很简单:遍历一个图标名数组,把每个名字渲染成“图标 + 文字标签”的格子。运行时你会总结出三个规律:
第一,基础操作类图标最完整:增删改查、保存、取消、打印、刷新、搜索、导入导出都有,够覆盖大多数后台功能。第二,业务类图标很缺:没有用户、订单、支付、商品这类语义图标,项目用到时得自己补。第三,图标尺寸固定:内置图标都是 16x16 像素,你的自定义图片最好也切成这个尺寸,否则在按钮上对齐会有偏差。
4.3 给不能编辑的默认样式做一套“公司级”覆盖
一旦确认图标可用、页面能跑通,下一步就是把默认的蓝色主题改成自己公司的配色。完整覆盖方案分三步走。
第一步,选定基准主题。如果不打算大改布局,就用 default 做底。如果想走扁平化一点的风格,1.3.4 自带的 metro 主题更接近现代审美。第二步,复制主题目录并改名。第三步,在复制出来的 easyui.css 末尾追加覆盖规则。我给出的具体规则如下:
/* ===== 全局 ===== */ body { font-family: "Microsoft YaHei", "PingFang SC", sans-serif; font-size: 13px; color: #333; } /* ===== 面板 ===== */ .panel-header, .panel-body { border-color: #c5d2de; } .panel-title { font-size: 14px; color: #1f3a5c; } .panel-header { background: linear-gradient(#f5f8fb, #e3eaf1); } /* ===== 按钮 ===== */ a.l-btn { height: 26px; line-height: 26px; padding: 0 12px; border-radius: 3px; } a.l-btn-plain { border: 1px solid transparent; padding: 0 8px; } a.l-btn-plain:hover { background: #eef3f8; border-color: #b7cde0; } /* ===== 数据表格 ===== */ .datagrid-header { background: #eef2f7; border-color: #c5d2de; } .datagrid-header .datagrid-cell { font-weight: 600; color: #1f3a5c; } .datagrid-row { height: 30px; } .datagrid-row-selected { background: #d6e5f5; color: #0f2f50; } /* ===== 分页 ===== */ .pagination { height: 30px; } .pagination-page-list, .pagination .pagination-num { height: 22px; border: 1px solid #c5d2de; border-radius: 2px; }这套覆盖规则有三个特性。第一,它只调颜色和少量尺寸,不动结构布局,所以不管你怎么改,都不会出现结构错乱。第二,它利用了后加载优先级覆盖——但前提是你的覆盖 CSS 必须放在 easyui.css 之后加载。第三,它对有背景图的组件(如链接按钮的图片背景)不会生效,因为按钮的 hover 效果很多依赖图片,而这套规则没有覆盖图片背景,所以按钮 hover 时的样式可能还是旧主题的——如果你在意这个,把覆盖规则写成带!important的背景图替换,但成本较高,极少项目会强求。
第三步的关键检验点是:把datagrid-row-selected的背景色改成浅蓝之后,选中行是否还看得清?很多情况下会因为对比度不足导致选中行和普通行混在一起,所以要调成一套色系,而不是随便选个亮色。
4.4 配置一处全局生效:用 JS 统一注册自定义图标
自定义图标是老项目里最固定的需求。比如需要在按钮上加一个“导出 Excel”的图标、一个“同步数据”的图标,而内置图标里没有。做法不是到处写 inline style,而是先往 icon.css 里追加 class:
.icon-excel { background: url('icons/excel.png') no-repeat center; } .icon-sync { background: url('icons/sync.png') no-repeat center; } .icon-dashboard { background: url('icons/dashboard.png') no-repeat center; }然后把做好的 PNG 放进 themes/default/icons/ 目录,注意尺寸切成 16x16,命名别用中文。png 图片格式建议用 PNG-24,透明背景。切图时不要用带白底的图,不然在灰色按钮背景上会显出一块白斑。
追加完 class 后,每个用到的地方写iconCls: 'icon-excel'就能生效。这样维护成本最低:改图只改文件,改名只改 CSS,页面不用动。如果你有许多图标要加,建议单独建一个 custom-icons.css,在 icon.css 之后引入,避免和 EasyUI 原文件混在一起——将来升级或 diff 时更清爽。
5. 避坑指南:1.3.4 图标与样式最常见的 5 个坑
5.1 图标全部白板,控制台报 404
现象:页面上按钮、菜单、树节点所有图标都不显示,devtools 里 Network 面板能看到一堆 png 请求 404,或者 CSS 文件里 background-url 指向的路径根本不存在。
原因:绝大部分情况是 icon.css 的引用路径和实际目录不一致。icon.css 在 themes/ 下,它内部 url 前缀是icons/xxx.png,这意味着如果 icon.css 本身被移动过,或者页面用绝对路径引用,那这些相对路径就全部失效。还有一种情况是打包工具把 icon.css 合并到了别的文件里,但图片没跟着走。
解决:在页面里直接用完整相对路径检查一下:
# 在项目根目录执行,确认文件存在 ls themes/default/icons/add.png然后看页面实际请求的 URL,比对两个路径。如果是路径层级问题,有两种修法:要么在 icon.css 里把 url 改成themes/default/icons/xxx.png(但这样失去了换主题目录的能力),要么把 icon.css 复制一份到 themes/default/ 下并同步改引用。最省事的做法是页面里不要用绝对路径引用 icon.css,保持相对路径,让浏览器按 CSS 文件所在位置解析图片。
5.2 自定义主题下,linkbutton 的 hover 背景不对
现象:你复制了主题目录并改了 .l-btn 的颜色,但鼠标悬停时按钮的背景要么没变化,要么变成一张旧的背景图,和你的配色明显不搭。
原因:EasyUI 1.3.4 的 linkbutton 在 hover 状态时使用了背景图。查看默认主题的 images 文件夹,能看到linkbutton_plain.png、linkbutton_pressed.png这类图片,它们是白色半透明渐变图,叠在按钮背景之上做出 hover 效果。当你只改了背景色,没改背景图时,hover 时图片会盖住你的新颜色——但它是半透明的,看起来就会呈现旧图片和新颜色混合的奇怪状态。
解决:直接禁用背景图,改用纯色 hover:
a.l-btn-plain:hover, a.l-btn:hover { background: #dbe7f3; background-image: none; }如果你保留背景图,那你需要自己重新画一张同尺寸的同名 PNG 替换掉默认文件。但自定义图形成本高,一般项目不会做,纯色就够了。
5.3 tree 节点图标展开后变为默认图标
现象:给 tree 节点配置了 iconCls,collapsed 状态显示正常,点开箭头后图标变成了默认的文件夹图片。
原因:tree 的展开状态使用的是expandedIconCls,你没给这个属性,所以框架用默认值。默认值通常是icon-folder-open之类的类名,而不是继承你配置的 iconCls。
解决:展示层需求总是要保持节点图标稳定,那就在数据里把两个属性都填上。批量处理时可以在解析数据后统一赋值:
$('#menuTree').tree({ onBeforeLoad: function(node) { // 这个场景下用 onLoadSuccess 更可靠 }, onLoadSuccess: function(node, data) { $(this).tree('getRoots').forEach(function(root) { setTreeIcons(root); }); } }); function setTreeIcons(node) { node.iconCls = node.iconCls || 'icon-folder'; node.expandedIconCls = node.iconCls; if (node.children) { node.children.forEach(setTreeIcons); } }这段代码的逻辑是:递归遍历树节点,给每个节点同时打上 iconCls 和 expandedIconCls,保证展开不跳图。注意在onLoadSuccess回调里执行,不能放在onBeforeLoad,因为数据还没生成。这个坑的本质是 EasyUI 的 tree 把“展开状态图标”当作独立属性对待,很多人只设置了 iconCls 一个值,经验主义地以为够用了。
5.4 datagrid 操作列里按钮没有样式、点击没反应
现象:formatter 里返回的<a>标签看起来就是普通超链接,没有按钮外观,点击也没跳转。
原因:datagrid 渲染单元格时,不会自动初始化单元格里的 EasyUI 组件。返回值只是插入到 HTML 里,框架不认识里面的easyui-linkbutton类。只有当页面启动时对全局执行$.parser.parse()才会逐一解析,但 datagrid 是在 parse 之后渲染的,所以单元格里的标签没有机会被解析。
解决:在onLoadSuccess里手动初始化。上面第 4 章给过一段代码,再补充一个点击事件绑定技巧:
onLoadSuccess: function() { $(this).find('.easyui-linkbutton').linkbutton({ onClick: function() { // 通过行索引找到当前行 var rowIndex = $(this).closest('tr').attr('datagrid-row-index'); var row = $('#dg').datagrid('getRows')[rowIndex]; // 执行你自己的逻辑 } }); }注意这里closest('tr')拿到的行索引属性名是datagrid-row-index,它是字符串类型,要用parseInt转一下。很多人在这个环节拿不到行数据,多半就是忘了转类型或者属性名写错。用 linkbutton 手动初始化还有一个好处:事件不会被重复绑定,因为第二次onLoadSuccess时 linkbutton 已经初始化过,再调用一次是幂等的——但前提是你没有在标签里同时写onclick属性。写了onclick又调linkbutton,会触发两次点击逻辑。
5.5 自定义 theme 改完,状态栏或 hover 仍然显示旧蓝色
现象:复制主题目录并修改 easyui.css 后,大部分界面变了,但某些小部件(如 spinner 按钮、combo 箭头、validatebox 的错误提示)仍然是默认蓝色。
原因:这些组件的样式依赖主题目录里的 images 子目录。spinner 的上下按钮、combo 的下拉箭头、datebox 的日历图标,全部是图片,不是纯 CSS 绘制的。你复制主题时如果只复制了 easyui.css,漏了整个 images 文件夹,或者 images 里的图片还是 default 主题的,这些组件的外观就不会变。
解决:复制主题时把整个 default 目录完整复制,包括 images。然后需要改这些按钮的颜色,要么重新设计图片,要么用 CSS 覆盖 background-image 为纯色或 data URI:
.combo-arrow { background: #f0f4f9; background-image: none; border-left: 1px solid #c5d2de; }这样下拉箭头会变成一个无图标的灰色区域,虽然少了小三角,但不影响功能。更完整的做法是绘制自己的小三角:
.combo-arrow::before { content: ''; display: block; width: 0; height: 0; border-style: solid; border-width: 4px 4px 0; border-color: #1a5a9e transparent transparent; margin: 6px auto; }利用伪元素画出三角形,替代背景图。这个方案只对现代浏览器有效,但对维护老项目的现状来说,够用。
6. 进阶技巧:把 1.3.4 的图标和样式能力榨干
在维护老项目的时候,最终目标不是“用起来”,而是“用起来不难受”。我积累的几个技巧可以让 1.3.4 的图标与样式体验接近现代框架,而不用升级版本。
第一个技巧是建立“图标字典”与“语义别名”。因为内置图标名跟业务语义对不上,我建议在项目里维护一份映射文件,把业务操作名映射到具体图标名:
// iconDict.js var ICON_DICT = { 'add': 'icon-add', 'edit': 'icon-edit', 'delete': 'icon-remove', 'save': 'icon-save', 'search': 'icon-search', 'export': 'icon-excel', // 自定义 'import': 'icon-import', // 自定义 'sync': 'icon-sync', // 自定义 'refresh': 'icon-reload', 'view': 'icon-eye-open', 'approve': 'icon-ok', 'reject': 'icon-no' };所有出现 iconCls 的地方,都优先通过这个字典取:
function getIcon(action) { return ICON_DICT[action] || 'icon-blank'; }这样做的好处是:将来要统一换图标风格,只改字典和自定义 CSS,不需要把所有页面翻一遍。这个思路其实是从组件库的设计模式里借鉴的——把语义层和表现层解耦。
第二个技巧是用装饰类统一调整组件间距和字号。EasyUI 组件的行高、内边距松散分布在各处,逐个覆盖效率低。我的做法是在页面级 CSS 里定义一组“批量调整规则”:
/* 紧凑模式:适合数据密集型列表页 */ .compact .datagrid-row { height: 24px; } .compact .datagrid-cell { padding: 2px 4px; } .compact a.l-btn { height: 22px; line-height: 22px; padding: 0 6px; }使用时在 body 或某个容器上挂compact类,一键切换密度。这比逐个页面改样式方便得多,也方便给不同客户交付不同密度的版本。
第三个技巧是编写一个“图标状态自检”函数,用于在开发阶段发现缺失的图片。它遍历当前页面所有带 iconCls 的元素,去后端拉取每个 class 对应的 background-image 并检查是否加载成功:
function checkMissingIcons() { var missing = []; $('[class*="icon-"]').each(function() { var cls = $(this).attr('class').split(/\s+/); var bg = $(this).css('background-image'); if (bg && bg !== 'none' && bg.indexOf('url(') >= 0) { var url = bg.replace(/^url\(['"]?/, '').replace(/['"]?\)$/, ''); var img = new Image(); img.onerror = function() { missing.push(url); }; img.src = url; } }); if (missing.length) { console.warn('以下图标图片缺失:', missing); } }这个函数在页面加载完成后调用一次,开发阶段能迅速发现图标路径错误,生产环境可以去掉调用。它对性能有轻微影响,因为每个图标都会创建一个 Image 对象去探测加载状态,所以只在开发环境用。
第四个技巧,关于 1.3.4 是否值得继续投入。我的判断是:如果你手里的系统业务稳定、界面不追求视觉刷新,那就继续用 1.3.4,把图标字典和覆盖样式维护好,半年内几乎不用碰样式代码。但如果你在新项目里想用 EasyUI,不要选 1.3.4 了——它的样式机制决定了你很难做出有呼吸感的后台界面,而且对现代浏览器的兼容性也不是很完善。选更新的框架或另一个组件库都更划算。
第五个技巧是保留一份“样式修改日志”。这点听起来不像技术,但在老项目维护里极其重要。我习惯在主题目录下放一个 CHANGELOG.md,简单记录每条覆盖规则的动机:
# 主题覆盖记录 ## 2025-01 - 覆盖 .datagrid-row-selected 背景色:#d6e5f5 原因:默认蓝色选中行与定制主题蓝色标题栏视觉冲突 - 新增 .icon-excel 原因:导出按钮需要 Excel 语义图标,内置图标无 - 禁用 linkbutton hover 背景图 原因:hover 半透明图片叠加后颜色浑浊这个文件不参与任何构建,但它能让后来接手的工程师快速理解“为什么会这样改”,避免有人为了“恢复原样”把你的覆盖规则删掉,结果界面突然回到默认蓝色,然后一脸懵。
最后说一个个人习惯。我维护这类老项目几年,最大的教训是:不要试图用“在页面里临时加一段 style 看看效果”的方式调样式,一定要把覆盖规则集中到主题目录下的一个独立文件里。临时加样式当时看着方便,但三周后你根本记不清那段样式是为哪个需求加的,也找不到哪里删。集中管理后,整个图标与样式的可维护性完全不一样。希望这些拆解和踩坑能帮到你,至少让你在接下来的项目里不再对着白板图标发愁。
本文还有配套的精品资源,点击获取