jQuery EasyUI 教程
jQuery EasyUI 是一组基于 jQuery 的 UI 插件集合,帮助开发者快速搭建后台管理界面。它提供了数据网格(datagrid)、树形菜单(tree)、窗口(window)、对话框(dialog)、表单(form)等几十个开箱即用的组件。
使用 EasyUI 几乎不需要编写复杂的 JavaScript 交互代码:多数组件只需在 HTML 标签上声明 class="easyui-xxx" 或用 data-options 配置属性,即可完成初始化。
本教程结构
| 模块 | 内容 |
|---|---|
| 基础入门 | 环境搭建、第一个组件、CRUD 应用 |
| 布局组件 | layout、accordion、tabs、menus |
| 数据网格 | datagrid 的列、分页、编辑、格式化 |
| 窗口组件 | window、dialog |
| 树形组件 | tree、treegrid |
| 表单组件 | form、validatebox、combobox |
jQuery EasyUI 简介
jQuery EasyUI 是一个用于创建富交互 Web 界面的 jQuery 插件框架。它的设计理念是「用 HTML 声明 UI,用 JavaScript 增强交互」,内置组件覆盖了后台系统几乎全部常用场景。
主要特点
- 上手简单:只需引入 CSS 与 JS,用 class 声明即可使用
- 组件丰富:datagrid、tree、tabs、accordion、dialog、menu 等 30+ 组件
- 主题可换:内置多种皮肤,支持自定义配色
- 中文文档友好:国内使用广泛,社区资料多
环境准备
下载 EasyUI 包后,在页面中引入以下文件:
<link rel="stylesheet" href="themes/default/easyui.css">
<link rel="stylesheet" href="themes/icon.css">
<script src="jquery.min.js"></script>
<script src="jquery.easyui.min.js"></script>
第一个组件
给任意 <div> 加上 easyui-panel 类,就得到了一个面板:
<div class="easyui-panel" title="我的面板" style="width:300px;height:150px;">
欢迎使用 jQuery EasyUI!
</div>
$('#id').panel({...}))。jEasyUI 创建 CRUD 应用
CRUD 指 Create(增)/ Read(查)/ Update(改)/ Delete(删),是后台管理系统最常见的需求。本教程的示例将演示:用 datagrid 展示数据、用 dialog + form 完成新增与编辑、用 linkbutton 触发操作。
整体思路
页面布局:
┌────────────────────────────┐
│ 工具栏:新增 | 编辑 | 删除 │
├────────────────────────────┤
│ 数据网格(datagrid) │
│ ┌──────────────────────┐ │
│ │ 对话框(dialog) │ │
│ │ 表单:名称/价格/分类... │ │
│ └──────────────────────┘ │
└────────────────────────────┘
页面骨架
<table id="dg" class="easyui-datagrid" title="商品列表"
url="data.json" toolbar="#tb"
rownumbers="true" pagination="true">
<thead>
<tr>
<th data-options="field:'id',width:60">ID</th>
<th data-options="field:'name',width:200">名称</th>
<th data-options="field:'price',width:100">价格</th>
</tr>
</thead>
</table>
<div id="tb">
<a class="easyui-linkbutton" data-options="iconCls:'icon-add'">新增</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-edit'">编辑</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-remove'">删除</a>
</div>
- 数据接口统一返回
{total: n, rows: [...]}结构,datagrid 自动分页 - 增删改操作后调用
$('#dg').datagrid('reload')刷新列表 - 对话框点击「保存」后先做表单校验,再提交
jEasyUI 创建 CRUD 数据网格
数据网格(datagrid)是 EasyUI 最核心的组件,负责表格展示与交互。本教程实现一个完整的 CRUD 数据网格:展示商品列表、支持分页、行选中、增删改。
HTML 定义表格
<table id="dg" class="easyui-datagrid" style="width:700px;height:300px;"
url="get_data.php"
toolbar="#tb" rownumbers="true" singleSelect="true" pagination="true">
<thead>
<tr>
<th data-options="field:'id',width:60">ID</th>
<th data-options="field:'name',width:200">商品名</th>
<th data-options="field:'price',width:100,formatter:fmtPrice">价格</th>
<th data-options="field:'stock',width:80">库存</th>
</tr>
</thead>
</table>
工具栏与操作函数
<div id="tb" style="padding:4px;">
<a href="#" class="easyui-linkbutton" iconCls="icon-add" plain="true">新增</a>
<a href="#" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
function fmtPrice(value) {
return "¥" + value;
}
function delRow() {
var row = $("#dg").datagrid("getSelected");
if (!row) { $.messager.alert("提示", "请先选择一行"); return; }
$.post("delete.php", { id: row.id }, function (res) {
if (res.success) { $("#dg").datagrid("reload"); }
}, "json");
}
function addRow() {
// 追加一行空数据(可配合行内编辑)
$("#dg").datagrid("appendRow", { id: 0, name: "", price: 0, stock: 0 });
}
load 方法指定页码参数。jEasyUI 表单的 CRUD 应用
纯数据网格只解决了「展示」,新增与编辑需要表单 + 对话框配合。本教程演示如何用 dialog 弹出表单,实现完整的增改流程。
对话框 + 表单
<div id="dlg" class="easyui-dialog" title="新增商品" style="width:360px;height:220px;"
data-options="closed:true,modal:true,buttons:'#dlg-buttons'">
<form id="fm" method="post">
<table style="margin:10px;">
<tr>
<td>名称:</td>
<td><input name="name" class="easyui-textbox" required="true"></td>
</tr>
<tr>
<td>价格:</td>
<td><input name="price" class="easyui-numberbox" required="true"></td>
</tr>
</table>
</form>
</div>
<div id="dlg-buttons">
<a href="#" class="easyui-linkbutton" iconCls="icon-ok">保存</a>
<a href="#" class="easyui-linkbutton" iconCls="icon-cancel">取消</a>
</div>
新增 / 编辑 / 保存
function addItem() {
$("#fm").form("clear"); // 清空表单
$("#dlg").dialog("open").dialog("setTitle", "新增商品");
}
function editItem() {
var row = $("#dg").datagrid("getSelected");
if (!row) return;
$("#fm").form("load", row); // 回填数据
$("#dlg").dialog("open").dialog("setTitle", "编辑商品");
}
function save() {
if (!$("#fm").form("validate")) return; // 校验必填
$.post("save.php", $("#fm").serialize(), function (res) {
if (res.success) {
$("#dlg").dialog("close");
$("#dg").datagrid("reload");
}
}, "json");
}
form('load', row) 可以自动把对象字段映射到同名表单项,省去手动赋值。jEasyUI 创建 RSS 阅读器
本教程用 datagrid 做一个简单的 RSS 阅读器:加载 RSS 源、展示标题列表、点击展开摘要,体验「数据驱动组件」的用法。
RSS 数据结构
服务端把 RSS 解析成 JSON 返回,字段与表格列一一对应:
{
"total": 20,
"rows": [
{
"title": "如何高效学习 jQuery",
"link": "https://example.com/1",
"pubDate": "2026-08-01",
"description": "一篇关于学习方法论的文章……"
}
]
}
页面与组件
<table id="rss" class="easyui-datagrid" title="我的订阅"
url="rss_feed.php" pagination="true" rownumbers="true"
>
<thead>
<tr>
<th data-options="field:'title',width:300">标题</th>
<th data-options="field:'pubDate',width:120">发布时间</th>
</tr>
</thead>
</table>
<div id="detail" style="margin-top:8px;padding:8px;border:1px solid #ccc;height:150px;">
选中文章后在此显示摘要
</div>
选中展示摘要
function showDetail(index, row) {
$("#detail").html(
"<strong>" + row.title + "</strong><br>" +
"<a href='" + row.link + "' target='_blank'>阅读原文</a><br>" +
row.description
);
}
file_get_contents 或 Node 的 axios)。jEasyUI 基本的拖动和放置
draggable 让元素可以自由拖动,droppable 让元素可以接收被拖放的目标。二者组合可以实现拖拽排序、拖拽到回收站等交互。
使元素可拖动
<div id="box" style="width:120px;height:80px;background:#87ceeb;cursor:move;">
拖动我
</div>
$("#box").draggable({
revert: true, // 拖放后回到原位
proxy: "clone", // 拖动时显示克隆占位
onStartDrag: function () {
$(this).addClass("dragging");
},
onStopDrag: function () {
$(this).removeClass("dragging");
}
});
放置目标
<div id="drop" style="width:200px;height:150px;border:2px dashed #999;">
放这里
</div>
$("#drop").droppable({
accept: "#box", // 只接受 id 为 box 的元素
onDragEnter: function () {
$(this).css("border-color", "#2e8b57");
},
onDrop: function (e, source) {
$(this).html("已收到:" + $(source).text());
$(this).css("border-color", "#999");
}
});
proxy:'clone' 拖动时用的是克隆体,原元素位置不变;拖放回调里通过 source 参数拿到被拖动的原始元素。jEasyUI 创建拖放的购物车
综合运用 draggable + droppable 实现一个迷你购物车:商品可拖入购物车,购物车显示数量与总价。
商品区与购物车
<div style="width:640px;">
<div id="goods">
<div class="item" data-price="99">📱 手机 ¥99</div>
<div class="item" data-price="199">🎧 耳机 ¥199</div>
<div class="item" data-price="49">⌨️ 键盘 ¥49</div>
</div>
<div id="cart" style="margin-top:10px;min-height:100px;border:2px dashed #f60;">
<div style="padding:8px;">把商品拖进来</div>
</div>
<p>共 <span id="count">0</span> 件,合计 ¥<span id="total">0</span></p>
</div>
初始化拖放
$(function () {
$(".item").draggable({
revert: true,
proxy: "clone",
cursor: "move"
});
$("#cart").droppable({
accept: ".item",
onDrop: function (e, source) {
var price = parseInt($(source).attr("data-price"));
var count = parseInt($("#count").text()) + 1;
var total = parseInt($("#total").text()) + price;
$("#count").text(count);
$("#total").text(total);
$("#cart").append("<p>" + $(source).text() + "</p>");
}
});
});
data-price 属性累加,逻辑清晰且易于扩展成真实购物车数据。jEasyUI 创建学校课程表
用 layout 布局 + draggable 做一个可视化的学校课程表:左侧为课程列表,右侧为周时间网格,课程可拖到对应格子。
布局骨架
<div class="easyui-layout" style="width:700px;height:400px;">
<div data-options="region:'west',title:'课程列表',width:180,split:true">
<div class="course">语文</div>
<div class="course">数学</div>
<div class="course">英语</div>
</div>
<div data-options="region:'center',title:'课程表'">
<table id="timetable"></table>
</div>
</div>
生成时间网格
var days = ["周一", "周二", "周三", "周四", "周五"];
var cells = "<tr><th>节次</th>";
$.each(days, function (i, d) { cells += "<th>" + d + "</th>"; });
cells += "</tr>";
for (var h = 1; h <= 6; h++) {
cells += "<tr><td>第" + h + "节</td>";
for (var j = 0; j < 5; j++) {
cells += "<td class='slot' data-day='" + j + "' data-hour='" + h + "'></td>";
}
cells += "</tr>";
}
$("#timetable").html(cells);
课程可拖、格子可收
$(".course").draggable({ revert: true, proxy: "clone" });
$(".slot").droppable({
accept: ".course",
onDrop: function (e, source) {
$(this).text($(source).text()).css("background", "#ffe4b5");
}
});
jEasyUI 创建简单的菜单
menu 组件用于创建右键菜单、下拉菜单。配合 menubutton 可以做成工具栏下拉,配合 onContextMenu 可以实现右键菜单。
定义菜单
<div id="mm" class="easyui-menu" style="width:150px;">
<div data-options="iconCls:'icon-new'">新建</div>
<div data-options="iconCls:'icon-edit'">编辑</div>
<div class="menu-sep"></div>
<div>保存</div>
<div data-options="iconCls:'icon-remove'">删除</div>
</div>
显示菜单
// 点击按钮弹出菜单
$("#btn").menubutton({
menu: "#mm"
});
// 或右键表格弹出
$("#dg").datagrid({
onRowContextMenu: function (e, index, row) {
e.preventDefault();
$(this).datagrid("selectRow", index);
$("#mm").menu("show", {
left: e.pageX,
top: e.pageY
});
}
});
菜单项处理函数
function doNew() { $.messager.alert("菜单", "点击了新建"); }
function doEdit() { $.messager.alert("菜单", "点击了编辑"); }
function doDelete() {
var row = $("#dg").datagrid("getSelected");
$.messager.confirm("确认", "确定删除选中行吗?", function (ok) {
if (ok) $("#dg").datagrid("reload");
});
}
e.preventDefault(),否则浏览器默认菜单也会弹出。jEasyUI 创建链接按钮
linkbutton 把普通的 <a>、<button>、<div> 变成带图标、带样式的按钮,是后台界面最常用的组件之一。
基本用法
<!-- 纯文本按钮 -->
<a class="easyui-linkbutton">确定</a>
<!-- 带图标 -->
<a class="easyui-linkbutton" iconCls="icon-add">新增</a>
<!-- 扁平按钮 -->
<a class="easyui-linkbutton" plain="true" iconCls="icon-edit">编辑</a>
<!-- 禁用按钮 -->
<a class="easyui-linkbutton" disabled="true">已禁用</a>
JS 方式创建
$("#myBtn").linkbutton({
iconCls: "icon-ok",
text: "保存",
onClick: function () {
$.messager.show({ title: "提示", msg: "点击了保存" });
}
});
动态启用 / 禁用
function toggleBtn(disabled) {
$("#myBtn").linkbutton(disabled ? "disable" : "enable");
}
// 未选中行时禁用「编辑」按钮
function onSelectRow() {
$("#editBtn").linkbutton("enable");
}
| 方法 | 作用 |
|---|---|
disable | 禁用按钮 |
enable | 启用按钮 |
resize | 调整按钮尺寸 |
icon-xxx 类;自定义图标可把图片放到 themes/icons 目录后注册。jEasyUI 创建菜单按钮
menubutton 是「按钮 + 下拉菜单」的组合:点击按钮触发默认动作,点击箭头弹出菜单项。
基本定义
<div id="mb" class="easyui-menubutton" data-options="menu:'#mm',iconCls:'icon-edit'">
编辑
</div>
<div id="mm" style="width:150px;">
<div>复制</div>
<div>剪切</div>
<div class="menu-sep"></div>
<div>粘贴</div>
</div>
菜单项带子菜单
<div id="mm2" style="width:150px;">
<div>
<span>导出</span>
<div style="width:120px;">
<div>导出 Excel</div>
<div>导出 CSV</div>
</div>
</div>
</div>
JS 方式创建
$("#mb2").menubutton({
text: "更多操作",
iconCls: "icon-more",
menu: "#mm2",
duration: 200
});
onclick 可以直接写函数名;复杂逻辑建议统一在函数内通过「选中行」等上下文处理,避免在 HTML 中堆业务代码。jEasyUI 创建分割按钮
splitbutton 与 menubutton 类似,但点击按钮主体就触发主动作,点击右侧小箭头才弹出菜单,适合「默认执行 + 可选扩展」的场景。
基本定义
<div id="sb" class="easyui-splitbutton" data-options="menu:'#mm',iconCls:'icon-save'">
保存
</div>
<div id="mm" style="width:150px;">
<div>另存为</div>
<div>保存副本</div>
<div class="menu-sep"></div>
<div>打印</div>
</div>
JS 方式创建
$("#sb2").splitbutton({
text: "发布",
iconCls: "icon-publish",
menu: "#mm",
duration: 100,
onClick: function () {
// 点击按钮主体触发
$.messager.alert("发布", "即将发布当前内容");
}
});
常用属性
| 属性 | 说明 |
|---|---|
menu | 关联的菜单选择器 |
duration | 菜单展开动画时长(ms) |
plain | 是否扁平样式 |
disabled | 是否禁用 |
jEasyUI 创建边框布局
layout 是后台界面的「骨架」组件,把页面分成 north / south / east / west / center 五个区域,配合 split 可拖动调整宽度。
经典后台布局
<div class="easyui-layout" style="width:800px;height:500px;">
<div data-options="region:'north',title:'顶部导航',height:60,split:false">
网站 Logo 与全局菜单
</div>
<div data-options="region:'west',title:'侧边栏',width:180,split:true">
功能列表
</div>
<div data-options="region:'center',title:'内容区'">
主体内容
</div>
<div data-options="region:'south',title:'底部',height:40,split:false">
版权信息
</div>
</div>
区域属性速查
| 属性 | 作用 |
|---|---|
region | 区域位置:north/south/east/west/center |
split | 是否可拖动调整大小 |
collapsible | 是否可折叠 |
minWidth / maxWidth | 宽度限制 |
border | 是否显示边框 |
JS 动态操作
$("#layout").layout("collapse", "west"); // 折叠左侧
$("#layout").layout("expand", "west"); // 展开左侧
$("#layout").layout("resize"); // 整体重算尺寸
center 区域必填,其他区域可以省略;布局容器必须显式设置宽高,否则无法撑开。jEasyUI 创建复杂布局
真实系统中往往需要嵌套布局:外层 layout 划分左右,右侧内容区再嵌套一个 layout 或 tabs,形成「侧边栏 + 多标签页」的常见后台形态。
嵌套布局示例
<div class="easyui-layout" style="width:900px;height:550px;">
<div data-options="region:'west',title:'系统菜单',width:200,split:true">
<div class="easyui-accordion" style="height:100%;">
<div title="用户管理">用户增删改查</div>
<div title="商品管理">商品列表与分类</div>
<div title="系统设置">参数与权限</div>
</div>
</div>
<div data-options="region:'center',title:'工作区'">
<div class="easyui-tabs" style="height:100%;">
<div title="概览">今日数据看板</div>
<div title="明细">数据明细表格</div>
</div>
</div>
</div>
动态添加标签页
function openTab(title, url) {
var tabs = $("#centerTabs");
if (tabs.tabs("exists", title)) {
tabs.tabs("select", title); // 已存在则激活
return;
}
tabs.tabs("add", {
title: title,
href: url,
closable: true,
tools: [{
iconCls: "icon-refresh",
handler: function () {
var tab = tabs.tabs("getSelected");
tabs.tabs("update", { tab: tab, options: { href: url } });
}
}]
});
}
jEasyUI 创建折叠面板
accordion(折叠面板)一次只展开一个面板,适合做侧边栏菜单或分区设置界面,节省垂直空间。
基本用法
<div class="easyui-accordion" style="width:300px;height:250px;">
<div title="面板一" iconCls="icon-home">
第一个面板的内容
</div>
<div title="面板二">
第二个面板的内容
</div>
<div title="面板三">
第三个面板的内容
</div>
</div>
常用配置
$("#acc").accordion({
multiple: false, // 是否允许多个面板同时展开
animate: true, // 展开动画
selected: 0, // 默认选中第几个
onSelect: function (title, index) {
console.log("选中:", title, index);
}
});
JS 控制
$("#acc").accordion("select", 2); // 展开第3个面板
$("#acc").accordion("getSelected"); // 获取当前面板
$("#acc").accordion("add", {
title: "新增面板",
content: "<p>动态添加的内容</p>"
});
<ul> 菜单列表,点击项再通过 JS 打开对应的 tabs 标签页。jEasyUI 创建标签页
tabs 组件把内容分成多个标签页,适合「一个页面多个视图」的场景,如详情/编辑/日志分开展示。
基本定义
<div id="tt" class="easyui-tabs" style="width:700px;height:300px;">
<div title="基本信息">
名称、分类、描述等基础字段
</div>
<div title="价格库存">
价格、成本、库存数量
</div>
<div title="操作日志">
修改记录与审核记录
</div>
</div>
常用属性与事件
$("#tt").tabs({
tabPosition: "top", // top / bottom / left / right
border: true,
onSelect: function (title, index) {
// 切换到某个标签时触发
},
onClose: function (title, index) {
// 关闭标签时触发
}
});
JS 操作
$("#tt").tabs("add", { title: "新页", content: "内容" }); // 添加
$("#tt").tabs("close", "基本信息"); // 关闭
$("#tt").tabs("select", 1); // 激活
$("#tt").tabs("getTab", "日志").panel("refresh"); // 刷新指定页
href 属性可以让标签页内容从独立 URL 异步加载,适合大页面拆分为多个模板。jEasyUI 动态添加标签页
把 tabs 与菜单联动:点击左侧菜单项,动态在右侧工作区新增或激活对应标签页——这是后台系统最经典的交互。
菜单点击触发
<div class="easyui-accordion" style="width:200px;height:400px;">
<div title="商品管理">
<ul class="menu-list">
<li>商品列表</li>
<li>商品分类</li>
</ul>
</div>
</div>
动态开标签页
function openTab(title, url) {
var tabs = $("#tabs");
// 已打开则直接激活
if (tabs.tabs("exists", title)) {
tabs.tabs("select", title);
return;
}
// 未打开则新增
tabs.tabs("add", {
title: title,
href: url,
closable: true,
onLoad: function () {
// 内容加载完成后执行初始化
}
});
}
限制最大标签数
var MAX = 8;
function openTabSafe(title, url) {
var tabs = $("#tabs");
var count = tabs.tabs("tabs").length;
if (count >= MAX) {
$.messager.alert("提示", "最多同时打开 " + MAX + " 个标签页");
return;
}
openTab(title, url);
}
href 加载的标签页,页面内引用的 JS 需要在子页面自身执行;父页面可通过 onLoad 事件做联动初始化。jEasyUI 添加自动播放标签页
实现自动轮播的标签页:每隔几秒自动切换到下一个标签,适合展示轮播图、大屏数据面板等场景。
基础结构
<div id="tt" class="easyui-tabs" style="width:700px;height:300px;">
<div title="页面一">第一屏内容</div>
<div title="页面二">第二屏内容</div>
<div title="页面三">第三屏内容</div>
</div>
<a id="play" class="easyui-linkbutton">开始轮播</a>
<a id="stop" class="easyui-linkbutton">停止轮播</a>
轮播逻辑
var timer = null;
var INTERVAL = 3000; // 3 秒切换一次
function startPlay() {
if (timer) return; // 已在播放
timer = setInterval(function () {
var tabs = $("#tt");
var count = tabs.tabs("tabs").length;
var cur = tabs.tabs("getTabIndex", tabs.tabs("getSelected"));
var next = (cur + 1) % count; // 循环到第一个
tabs.tabs("select", next);
}, INTERVAL);
}
function stopPlay() {
clearInterval(timer);
timer = null;
}
$("#play").click(startPlay);
$("#stop").click(stopPlay);
// 用户手动切换时暂停 5 秒再继续(体验优化)
$("#tt").tabs({
onSelect: function () {
stopPlay();
setTimeout(startPlay, 5000);
}
});
setInterval + 取模实现循环切换;记得在页面卸载时 clearInterval,避免定时器泄漏。jEasyUI 创建 XP 风格左侧面板
模仿经典 Windows XP 的左侧导航样式:把 layout 的 west 区域做成深色标题栏 + 分组链接列表的「资源管理器」风格。
样式定义
.xp-panel { background: #f0f0f0; height: 100%; }
.xp-title {
background: linear-gradient(#4a90d9, #2f6fb2);
color: #fff; padding: 8px 12px; font-weight: bold;
}
.xp-group { margin: 10px 8px; }
.xp-group h4 {
font-size: 12px; color: #666; border-bottom: 1px solid #ddd;
padding-bottom: 4px;
}
.xp-group ul { list-style: none; padding: 4px 0 0 8px; }
.xp-group li { padding: 4px 6px; cursor: pointer; }
.xp-group li:hover { background: #dbe9fb; border-radius: 3px; }
HTML 结构
<div class="easyui-layout" style="width:800px;height:500px;">
<div data-options="region:'west',split:true" style="width:200px;">
<div class="xp-panel">
<div class="xp-title">我的电脑</div>
<div class="xp-group">
<h4>文件夹</h4>
<ul>
<li>📁 文档</li>
<li>🖼️ 图片</li>
</ul>
</div>
<div class="xp-group">
<h4>系统</h4>
<ul>
<li>⚙️ 设置</li>
</ul>
</div>
</div>
</div>
<div data-options="region:'center',title:'内容区'">
<div id="folder-content">请选择左侧文件夹</div>
</div>
</div>
交互函数
function openFolder(name) {
$("#folder-content").html("<h2>" + name + "</h2><p>这里是 " + name + " 的内容……</p>");
}
jEasyUI 转换 HTML 表格为数据网格
已有静态 HTML 表格时,无需重写数据,用 datagrid 的 data 属性或 loadData 方法即可把表格内容直接转成数据网格,立刻获得排序、分页、选择等能力。
静态表格转网格
<table id="dg"></table>
<script>
$(function () {
$("#dg").datagrid({
title: "学生列表",
rownumbers: true,
columns: [[
{ field: "no", title: "学号", width: 100 },
{ field: "name", title: "姓名", width: 150 },
{ field: "score", title: "成绩", width: 100 }
]],
data: [
{ no: "001", name: "张三", score: 92 },
{ no: "002", name: "李四", score: 85 },
{ no: "003", name: "王五", score: 78 }
]
});
});
</script>
从已有 table 结构转换
<table id="oldTable">
<thead>
<tr><th>ID</th><th>城市</th><th>人口(万)</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>北京</td><td>2189</td></tr>
<tr><td>2</td><td>上海</td><td>2487</td></tr>
</tbody>
</table>
// 读取 tbody 数据并转换为 datagrid 数据
function tableToRows($table) {
var rows = [];
$table.find("tbody tr").each(function () {
var cells = $(this).find("td").map(function () {
return $(this).text();
}).get();
rows.push({ id: cells[0], city: cells[1], pop: cells[2] });
});
return rows;
}
$("#oldTable").hide(); // 隐藏原表格
$("#dg").datagrid({
columns: [[
{ field: "id", title: "ID", width: 60 },
{ field: "city", title: "城市", width: 150 },
{ field: "pop", title: "人口(万)", width: 120 }
]],
data: tableToRows($("#oldTable"))
});
jEasyUI 取得选中行数据
操作数据网格前,必须先拿到选中的行数据。本教程梳理 datagrid 的选中 API:单选、多选、按索引取、以及选中事件。
单选模式
// singleSelect=true 时
var row = $("#dg").datagrid("getSelected");
if (row) {
console.log(row.id, row.name);
} else {
$.messager.alert("提示", "请先选择一行");
}
多选模式
// singleSelect=false(默认多选,配合 checkbox 列)
var rows = $("#dg").datagrid("getSelections");
console.log("选中了 " + rows.length + " 行");
var ids = $.map(rows, function (r) { return r.id; });
$.post("batch_delete.php", { ids: ids.join(",") });
按索引取 / 选中事件
// 取第 3 行数据
var row3 = $("#dg").datagrid("getRows")[2];
// 选中事件
$("#dg").datagrid({
onSelect: function (index, row) {
console.log("选中第", index, "行:", row.name);
$("#detail").text(row.name + " / " + row.price);
},
onUnselect: function (index, row) {
console.log("取消选中", row.name);
},
onSelectAll: function (rows) {
console.log("全选", rows.length, "行");
}
});
批量操作前的安全校验
function getChecked() {
var rows = $("#dg").datagrid("getSelections");
if (rows.length === 0) {
$.messager.alert("提示", "请至少选择一行");
return null;
}
return rows;
}
function batchDelete() {
var rows = getChecked();
if (!rows) return;
$.messager.confirm("确认", "确定删除选中的 " + rows.length + " 条吗?", function (ok) {
if (ok) {
// 执行删除并刷新
$("#dg").datagrid("reload");
}
});
}
getSelections 只包含当前页选中的行;跨页选择需要自行用 Map 维护已选 id。jEasyUI 添加查询功能
给 datagrid 加一个搜索工具栏:输入关键字 + 选择条件,把参数传给后端重新加载数据。
查询工具栏
<table id="dg" class="easyui-datagrid" style="width:700px;height:300px;"
url="search.php" toolbar="#tb" pagination="true">
<thead>
<tr>
<th data-options="field:'name',width:200">商品名</th>
<th data-options="field:'price',width:100">价格</th>
</tr>
</thead>
</table>
<div id="tb" style="padding:4px;">
<input id="kw" class="easyui-textbox" prompt="商品名" style="width:150px;">
<input id="minP" class="easyui-numberbox" prompt="最低价" style="width:80px;">
<a href="#" class="easyui-linkbutton" iconCls="icon-search">查询</a>
<a href="#" class="easyui-linkbutton" iconCls="icon-clear">重置</a>
</div>
查询与重置
function doSearch() {
$("#dg").datagrid("load", {
keyword: $("#kw").textbox("getValue"),
minPrice: $("#minP").numberbox("getValue")
});
}
function clearSearch() {
$("#kw").textbox("setValue", "");
$("#minP").numberbox("setValue", "");
$("#dg").datagrid("load", {});
}
// 回车即搜索
$("#kw").textbox("textbox").keydown(function (e) {
if (e.keyCode === 13) doSearch();
});
后端接收(伪代码)
$keyword = $_GET['keyword'] ?? '';
$minPrice = $_GET['minPrice'] ?? 0;
$where = '1=1';
if ($keyword !== '') $where .= " AND name LIKE '%$keyword%'";
if ($minPrice > 0) $where .= " AND price >= $minPrice";
// 拼接分页 LIMIT 并返回 {total, rows}
load 方法传递,datagrid 会自动合并到分页请求中;重置时传空对象即可回到全量数据。jEasyUI 添加工具栏
工具栏(toolbar)是 datagrid 顶部的一组操作按钮,承载新增、编辑、删除、导出等常用动作。本教程演示「内置工具栏」与「自定义工具栏」两种方式。
方式一:内置工具栏
$("#dg").datagrid({
toolbar: [{
text: "新增",
iconCls: "icon-add",
handler: function () { addItem(); }
}, {
text: "编辑",
iconCls: "icon-edit",
handler: function () { editItem(); }
}, "-", // 分隔线
{
text: "删除",
iconCls: "icon-remove",
handler: function () { delItem(); }
}]
});
方式二:HTML 工具栏(推荐)
<div id="tb">
<a class="easyui-linkbutton" data-options="iconCls:'icon-add',plain:true"
>新增</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-edit',plain:true"
>编辑</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-remove',plain:true"
>删除</a>
<span style="margin-left:12px;">
<input id="kw" class="easyui-searchbox" data-options="prompt:'搜索',searcher:doSearch">
</span>
</div>
<table id="dg" class="easyui-datagrid" toolbar="#tb" url="list.php">
<thead><tr><th data-options="field:'name'">名称</th></tr></thead>
</table>
工具栏按钮状态联动
$("#dg").datagrid({
onSelect: function () {
$("#editBtn").linkbutton("enable");
$("#delBtn").linkbutton("enable");
},
onUnselect: function () {
$("#editBtn").linkbutton("disable");
$("#delBtn").linkbutton("disable");
}
});
jEasyUI 创建复杂工具栏
复杂工具栏在基础按钮之上加入按钮组、下拉菜单、搜索框、状态文本等元素,形成完整操作区。本教程演示一个综合示例。
完整工具栏示例
<div id="tb" style="padding:4px;">
<!-- 主操作组 -->
<a class="easyui-linkbutton" data-options="iconCls:'icon-add',plain:true">新增</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-edit',plain:true">编辑</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-remove',plain:true">删除</a>
<span class="tb-sep"></span>
<!-- 下拉操作 -->
<div id="moreMenu" style="width:130px;">
<div>导出 Excel</div>
<div>导出 CSV</div>
</div>
<a class="easyui-menubutton" data-options="menu:'#moreMenu',iconCls:'icon-export',plain:true">导出</a>
<span class="tb-sep"></span>
<!-- 搜索区 -->
<input id="kw" class="easyui-textbox" data-options="prompt:'关键字'" style="width:140px;">
<a class="easyui-linkbutton" data-options="iconCls:'icon-search'">查询</a>
<span style="float:right;line-height:26px;">
共 <span id="totalInfo">0</span> 条记录
</span>
</div>
工具栏样式
.tb-sep {
display: inline-block; width: 1px; height: 16px;
background: #ddd; margin: 0 8px; vertical-align: middle;
}
联动更新统计文本
$("#dg").datagrid({
onLoadSuccess: function (data) {
$("#totalInfo").text(data.total);
}
});
tb-sep 分隔线视觉分组;高频操作放左边,低频/导出类用下拉,右侧放全局统计信息。jEasyUI 设置冻结列
冻结列(frozenColumns)让某些列在水平滚动时保持固定,常用于「序号 / 操作列 / 主键」等需要始终可见的列。
基本用法
$("#dg").datagrid({
title: "冻结列示例",
url: "list.php",
frozenColumns: [[
{ field: "id", title: "ID", width: 60 },
{ field: "name", title: "姓名", width: 120 }
]],
columns: [[
{ field: "math", title: "数学", width: 100 },
{ field: "english", title: "英语", width: 100 },
{ field: "physics", title: "物理", width: 100 },
{ field: "chemistry", title: "化学", width: 100 },
{ field: "biology", title: "生物", width: 100 }
]]
});
在 HTML 中定义
<table id="dg" class="easyui-datagrid" title="冻结列(HTML)" style="width:700px;height:250px;">
<thead>
<tr>
<th data-options="field:'id',width:60,frozen:true">ID</th>
<th data-options="field:'name',width:120,frozen:true">姓名</th>
<th data-options="field:'score',width:200">总分</th>
</tr>
</thead>
</table>
jEasyUI 动态改变列
根据用户需求动态显示/隐藏列、增删列,让表格更灵活。常用于「列设置」功能——用户自选要显示的字段。
显示/隐藏列
// 隐藏某列
$("#dg").datagrid("hideColumn", "price");
// 显示某列
$("#dg").datagrid("showColumn", "price");
// 判断列是否可见
var col = $("#dg").datagrid("getColumnOption", "price");
console.log(col.hidden ? "已隐藏" : "可见");
列设置面板
<div id="colSetting" style="padding:8px;">
<label><input type="checkbox" value="id" checked> ID</label><br>
<label><input type="checkbox" value="name" checked> 名称</label><br>
<label><input type="checkbox" value="price" checked> 价格</label><br>
<label><input type="checkbox" value="stock"> 库存</label><br>
</div>
function applyColumns() {
$("#colSetting input[type=checkbox]").each(function () {
var field = $(this).val();
if ($(this).is(":checked")) {
$("#dg").datagrid("showColumn", field);
} else {
$("#dg").datagrid("hideColumn", field);
}
});
}
// 用 checkboxlist 打开设置
$("#colBtn").linkbutton({
onClick: function () {
$("#colSetting").dialog({
title: "列设置",
buttons: [{
text: "应用",
iconCls: "icon-ok",
handler: function () {
applyColumns();
$("#colSetting").dialog("close");
}
}]
});
}
});
动态增加列
// 追加一列
$("#dg").datagrid("appendColumn", {
field: "remark",
title: "备注",
width: 150
});
jEasyUI 格式化列
formatter 是列的回调函数,用来把原始值渲染成友好内容:金额、状态标签、日期、图片、操作按钮等。
状态格式化
function fmtStatus(value) {
if (value === 1) return "<span class='badge badge-on'>启用</span>";
if (value === 0) return "<span class='badge badge-off'>停用</span>";
return "未知";
}
$("#dg").datagrid({
columns: [[
{ field: "status", title: "状态", width: 80, formatter: fmtStatus }
]]
});
金额与日期格式化
function fmtMoney(value, row, index) {
return "<span style='color:#e4393c;font-weight:bold;'>¥" + Number(value).toFixed(2) + "</span>";
}
function fmtDate(value) {
if (!value) return "-";
var d = new Date(value.replace(/-/g, "/"));
var pad = function (n) { return n < 10 ? "0" + n : n; };
return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
}
$("#dg").datagrid({
columns: [[
{ field: "price", title: "单价", width: 100, formatter: fmtMoney },
{ field: "created", title: "创建时间", width: 120, formatter: fmtDate }
]]
});
行内操作按钮
function fmtAction(value, row, index) {
return "<a href='#'>查看</a> " +
"<a href='#'>删除</a>";
}
function view(index) {
var row = $("#dg").datagrid("getRows")[index];
$.messager.alert("详情", row.name + " / " + row.price);
}
$("#dg").datagrid({
columns: [[
{ field: "op", title: "操作", width: 120, formatter: fmtAction }
]]
});
<、>。jEasyUI 设置排序
datagrid 支持列头点击排序:前端排序(本地数据)或后端排序(远程数据)两种模式。
前端排序(本地数据)
$("#dg").datagrid({
sortName: "score", // 默认排序列
sortOrder: "desc", // 默认排序方向
data: [
{ name: "张三", score: 92 },
{ name: "李四", score: 85 },
{ name: "王五", score: 78 }
]
});
后端排序(远程数据)
$("#dg").datagrid({
url: "list.php",
remoteSort: true, // 默认即 true
sortName: "price",
sortOrder: "asc"
});
// 点击列头时,datagrid 自动把 sort/order/page 参数发给后端
// 后端接收:sort=price&order=asc&page=1&rows=20
$sort = $_GET['sort'] ?? 'id';
$order = $_GET['order'] ?? 'asc';
$page = (int)($_GET['page'] ?? 1);
$rows = (int)($_GET['rows'] ?? 20);
// 白名单校验排序字段,防注入
$allowed = ['id', 'name', 'price', 'stock'];
if (!in_array($sort, $allowed)) $sort = 'id';
$sql = "SELECT * FROM goods ORDER BY $sort $order LIMIT " .
(($page - 1) * $rows) . ", $rows";
指定某列不可排序
// 操作列不需要排序
{ field: "op", title: "操作", width: 100, sortable: false }
sortable: true 的列上显示;本地排序模式无需后端参与,适合数据量小的场景。jEasyUI 自定义排序
当默认排序(字典序/数值序)不满足需求时,用 sorter 自定义排序逻辑:日期字符串、中文拼音、特殊业务规则等。
日期字符串排序
function dateSorter(a, b) {
var da = new Date(a.replace(/-/g, "/")).getTime();
var db = new Date(b.replace(/-/g, "/")).getTime();
return da < db ? -1 : (da > db ? 1 : 0);
}
$("#dg").datagrid({
columns: [[
{ field: "date", title: "日期", width: 120, sortable: true, sorter: dateSorter }
]]
});
混合数据排序(数字+文字)
function smartSorter(a, b) {
// 把 "10 元" 这类文本拆出数字比较
var na = parseFloat(a) || 0;
var nb = parseFloat(b) || 0;
return na - nb;
}
$("#dg").datagrid({
columns: [[
{ field: "priceText", title: "价格", width: 100, sortable: true, sorter: smartSorter }
]]
});
按业务权重排序
var LEVEL_WEIGHT = { "高": 3, "中": 2, "低": 1 };
function levelSorter(a, b) {
return (LEVEL_WEIGHT[a] || 0) - (LEVEL_WEIGHT[b] || 0);
}
$("#dg").datagrid({
columns: [[
{ field: "level", title: "优先级", width: 80, sortable: true, sorter: levelSorter }
]]
});
jEasyUI 创建列组合
列组合(colspan)把多列合并为一个表头,形成分组,例如「成绩」下细分「语文 / 数学 / 英语」。
JS 方式配置列组
$("#dg").datagrid({
columns: [[
{ title: "基本信息", colspan: 2 },
{ title: "成绩", colspan: 3 }
], [
{ field: "name", title: "姓名", width: 120 },
{ field: "class", title: "班级", width: 80 },
{ field: "chinese", title: "语文", width: 80 },
{ field: "math", title: "数学", width: 80 },
{ field: "english", title: "英语", width: 80 }
]]
});
HTML 方式
<table id="dg" class="easyui-datagrid" style="width:600px;height:250px;">
<thead>
<tr>
<th colspan="2">基本信息</th>
<th colspan="3">成绩</th>
</tr>
<tr>
<th data-options="field:'name',width:120">姓名</th>
<th data-options="field:'class',width:80">班级</th>
<th data-options="field:'chinese',width:80">语文</th>
<th data-options="field:'math',width:80">数学</th>
<th data-options="field:'english',width:80">英语</th>
</tr>
</thead>
</table>
jEasyUI 添加复选框
在数据网格最前面加一列复选框,实现多选 + 批量操作,是后台管理列表的标准配置。
启用复选框
$("#dg").datagrid({
// checkbox 列由系统自动生成,无需声明 field
columns: [[
{ field: "ck", checkbox: true },
{ field: "id", title: "ID", width: 60 },
{ field: "name", title: "名称", width: 200 }
]]
});
获取选中行
function batchOp() {
var rows = $("#dg").datagrid("getChecked"); // 推荐:只返回勾选的行
if (rows.length === 0) {
$.messager.alert("提示", "请先勾选数据");
return;
}
var ids = $.map(rows, function (r) { return r.id; }).join(",");
$.messager.alert("已选", "ID: " + ids);
}
// 对比:
// getChecked() —— 只包含 checkbox 勾选的行
// getSelections()—— 包含 click 选中的行 + checkbox 勾选的行
勾选事件
$("#dg").datagrid({
onCheck: function (index, row) {
console.log("勾选:", row.name);
updateBtnState();
},
onUncheck: function (index, row) {
console.log("取消勾选:", row.name);
updateBtnState();
},
onCheckAll: function (rows) {
updateBtnState();
}
});
function updateBtnState() {
var n = $("#dg").datagrid("getChecked").length;
$("#batchBtn").linkbutton(n > 0 ? "enable" : "disable");
}
onLoadSuccess 中回填。jEasyUI 自定义分页
分页组件(pagination)默认显示页码、每页条数。通过配置可自定义:显示总数、跳页输入框、按钮布局等。
分页属性
$("#dg").datagrid({
pagination: true,
pageSize: 20, // 每页条数
pageList: [10, 20, 50, 100],
pageNumber: 1,
loadMsg: "加载中,请稍候..."
});
自定义分页布局
<div id="pp" class="easyui-pagination"
style="width:700px;"
data-options="total:114,pageSize:20,pageList:[10,20,50],
layout:['first','prev','manual','next','last','refresh','sep','info']">
</div>
$("#pp").pagination({
layout: ["first", "prev", "manual", "next", "last", "refresh", "sep", "info"],
pageSize: 20,
total: 114,
onSelectPage: function (pageNumber, pageSize) {
$("#dg").datagrid("load", { page: pageNumber, rows: pageSize });
}
});
分页事件
$("#dg").datagrid({
onLoadSuccess: function (data) {
// 数据加载完成后可更新自定义统计
$("#total").text(data.total);
}
});
// 分页器事件(独立 pagination 时)
$("#pp").pagination({
onRefresh: function (pageNumber, pageSize) {
$("#dg").datagrid("reload");
},
onChangePageSize: function (pageSize) {
// 每页条数改变
}
});
layout 属性支持: first / prev / manual / next / last / refresh / sep / info,可按需组合顺序。jEasyUI 启用行内编辑
行内编辑(editRow)让用户在表格里直接修改数据,无需弹窗,体验更流畅。
开始编辑
function startEdit() {
var row = $("#dg").datagrid("getSelected");
if (!row) return;
var index = $("#dg").datagrid("getRowIndex", row);
$("#dg").datagrid("beginEdit", index); // 进入编辑态
}
function endEdit() {
var rows = $("#dg").datagrid("getRows");
$.each(rows, function (i) {
var editing = $("#dg").datagrid("getEditors", i);
if (editing.length) {
$("#dg").datagrid("endEdit", i); // 提交编辑
}
});
}
列级编辑器配置
$("#dg").datagrid({
columns: [[
{ field: "name", title: "名称", width: 150,
editor: { type: "textbox", options: { required: true } } },
{ field: "price", title: "价格", width: 100,
editor: { type: "numberbox", options: { min: 0, precision: 2 } } },
{ field: "cat", title: "分类", width: 100,
editor: { type: "combobox", options: {
data: [{id:1,text:"图书"},{id:2,text:"数码"}],
valueField: "id", textField: "text"
} } },
{ field: "stock", title: "库存", width: 80,
editor: { type: "numberspinner", options: { min: 0 } } }
]],
onClickRow: function (index) {
// 点击行直接进入编辑
var editing = $("#dg").datagrid("getEditors", index);
if (!editing.length) $("#dg").datagrid("beginEdit", index);
}
});
保存编辑结果
function saveEdit() {
endEdit();
var rows = $("#dg").datagrid("getChanges"); // 获取变更行
if (rows.length === 0) {
$.messager.alert("提示", "没有修改");
return;
}
$.post("save_all.php", JSON.stringify(rows), function (res) {
if (res.success) $("#dg").datagrid("reload");
}, "json");
}
getChanges() 只提交变更过的行,减少请求数据量;配合 onEndEdit 事件做行级校验。jEasyUI 扩展编辑器
内置编辑器不够用时,可以注册自定义编辑器。本教程演示如何把「日期选择、下拉树」等组件变成 datagrid 的编辑器。
注册日期编辑器
$.extend($.fn.datagrid.defaults.editors, {
datebox: {
init: function (container, options) {
var input = $("<input type='text'>").appendTo(container);
input.datebox(options);
return input;
},
destroy: function (target) { $(target).datebox("destroy"); },
getValue: function (target) { return $(target).datebox("getValue"); },
setValue: function (target, value) { $(target).datebox("setValue", value); },
resize: function (target, width) { $(target).datebox("resize", width); }
}
});
使用自定义编辑器
$("#dg").datagrid({
columns: [[
{ field: "name", title: "名称", width: 120 },
{ field: "birth", title: "出生日期", width: 140,
editor: { type: "datebox", options: { required: true } } }
]]
});
注册下拉树编辑器
$.extend($.fn.datagrid.defaults.editors, {
combotree: {
init: function (container, options) {
var input = $("<input type='text'>").appendTo(container);
input.combotree($.extend({ url: "tree_data.php" }, options));
return input;
},
destroy: function (target) { $(target).combotree("destroy"); },
getValue: function (target) { return $(target).combotree("getValue"); },
setValue: function (target, value) { $(target).combotree("setValue", value); },
resize: function (target, width) { $(target).combotree("resize", width); }
}
});
init / destroy / getValue / setValue / resize 五个方法,缺一不可,否则编辑时会报错。jEasyUI 列运算
数据网格底部显示汇总运算(合计/平均/最大值等)是报表类页面常见需求,本教程演示「页脚行 + 列运算」。
数据带 footer 结构
{
"total": 3,
"rows": [
{ "name": "手机", "price": 1999, "qty": 2 },
{ "name": "耳机", "price": 399, "qty": 3 },
{ "name": "键盘", "price": 249, "qty": 1 }
],
"footer": [
{ "name": "合计", "price": 2647, "qty": 6 }
]
}
前端自行运算
$("#dg").datagrid({
url: "list.php",
showFooter: true, // 显示页脚
onLoadSuccess: function (data) {
// 计算合计
var totalPrice = 0, totalQty = 0;
$.each(data.rows, function (i, r) {
totalPrice += r.price * r.qty;
totalQty += r.qty;
});
// 更新页脚(需要在列配置中声明 footer 列)
$("#dg").datagrid("reloadFooter", [{
name: "合计",
price: totalPrice,
qty: totalQty
}]);
}
});
列配置支持 footer
$("#dg").datagrid({
columns: [[
{ field: "name", title: "商品", width: 150 },
{ field: "price", title: "单价", width: 100 },
{ field: "qty", title: "数量", width: 80 },
{ field: "amount", title: "金额", width: 120,
formatter: function (v, r) { return r.price * r.qty; } }
]]
});
// 用 formatter 计算金额列,footer 中对应的 amount 也会自动算
平均 / 最大最小值
function buildFooter(rows) {
var prices = $.map(rows, function (r) { return r.price; });
var avg = prices.reduce(function (a, b) { return a + b; }, 0) / prices.length;
return [{
name: "均价",
price: Math.round(avg * 100) / 100
}];
}
jEasyUI 合并单元格
合并单元格(mergeCells)把多个相邻单元格合并,用于呈现分组数据或报表结构。
基本合并
function mergeCells() {
// 合并第 0 行的 0~1 列
$("#dg").datagrid("mergeCells", {
index: 0, // 起始行
field: "name", // 起始列(field)
colspan: 2, // 横向合并数量
rowspan: 1 // 纵向合并数量
});
}
$(function () {
$("#dg").datagrid({...});
mergeCells();
});
按分组自动合并
// 数据按 dept 分组,自动把同组的第一列合并
function mergeByField(rows, field) {
var i = 0;
while (i < rows.length) {
var j = i + 1;
while (j < rows.length && rows[j][field] === rows[i][field]) j++;
if (j - i > 1) {
$("#dg").datagrid("mergeCells", {
index: i, field: field, rowspan: j - i
});
}
i = j;
}
}
$("#dg").datagrid({
onLoadSuccess: function (data) {
mergeByField(data.rows, "dept");
}
});
合并后取数
// 注意:mergeCells 只是视觉合并,getRows 仍返回每行独立数据
function getMergedValue(index) {
var row = $("#dg").datagrid("getRows")[index];
return row.dept; // 合并列只在该组第一行有值
}
onLoadSuccess 中重新合并;数据应预先按分组字段排序。jEasyUI 创建自定义视图
默认视图是「横排表格」,通过自定义视图可以把数据渲染成卡片、列表、图表等形态。
切换到卡片视图
var view_cards = $.extend({}, $.fn.datagrid.defaults.view, {
render: function (target, container, frozen) {
var opts = $.data(target, "datagrid").options;
var rows = $.data(target, "datagrid").data.rows;
if (frozen) return; // 冻结部分不渲染
var html = [];
$.each(rows, function (i, row) {
html.push(
"<div class='card'>" +
"<div class='card-title'>" + row.name + "</div>" +
"<div class='card-price'>¥" + row.price + "</div>" +
"<div class='card-desc'>" + (row.desc || "") + "</div>" +
"</div>"
);
});
$(container).html(html.join(""));
}
});
$("#dg").datagrid({
view: view_cards,
url: "list.php"
});
卡片样式
.card {
display: inline-block; width: 180px; margin: 8px;
padding: 12px; border: 1px solid #e5e5e5;
border-radius: 8px; background: #fff; cursor: pointer;
}
.card:hover { box-shadow: 0 4px 12px rgba(0,0,0,.12); }
.card-title { font-weight: bold; }
.card-price { color: #e4393c; margin: 6px 0; }
.card-desc { color: #888; font-size: 12px; }
切换视图按钮
function switchView(viewName) {
if (viewName === "table") {
$("#dg").datagrid("options").view = $.fn.datagrid.defaults.view;
} else {
$("#dg").datagrid("options").view = view_cards;
}
$("#dg").datagrid("reload");
}
jEasyUI 创建页脚摘要
页脚摘要(footer)在表格底部显示合计、平均、计数等信息。本教程演示「服务端返回 footer」与「前端计算 footer」两种方式。
方式一:服务端返回 footer
{
"total": 5,
"rows": [
{"name":"手机","price":1999,"qty":2},
{"name":"耳机","price":399,"qty":3}
],
"footer": [
{"name":"合计","price":2398,"qty":5}
]
}
$("#dg").datagrid({
url: "list.php",
showFooter: true,
columns: [[
{ field: "name", title: "商品", width: 150 },
{ field: "price", title: "价格", width: 100 },
{ field: "qty", title: "数量", width: 80 }
]]
});
// 后端返回 footer 字段后,datagrid 自动渲染页脚行
方式二:前端计算
function buildFooter(rows) {
var sumPrice = 0, sumQty = 0;
$.each(rows, function (i, r) {
sumPrice += r.price;
sumQty += r.qty;
});
return [{
name: "合计",
price: sumPrice,
qty: sumQty
}];
}
$("#dg").datagrid({
url: "list.php",
showFooter: true,
onLoadSuccess: function (data) {
if (data.rows) {
$("#dg").datagrid("reloadFooter", buildFooter(data.rows));
}
}
});
页脚列格式化
columns: [[
{ field: "price", title: "价格", width: 100,
formatter: function (v, r, i) {
// 判断是否为 footer 行(footer 行没有 id 等业务字段)
return r.id === undefined ? "¥" + v + " (合计)" : "¥" + v;
} }
]]
jEasyUI 条件设置行背景颜色
根据行数据条件给整行设置背景色,突出异常数据,例如库存不足标红、VIP 客户高亮。
用 rowStyler 设置
$("#dg").datagrid({
url: "list.php",
rowStyler: function (index, row) {
if (row.stock < 10) {
return "background:#ffe4e1;color:#c00;"; // 库存不足标红
}
if (row.vip) {
return "background:#fff7e6;color:#b8860b;"; // VIP 高亮
}
return "";
}
});
按数值区间高亮
$("#dg").datagrid({
rowStyler: function (index, row) {
if (row.score >= 90) return "background:#e8f5e9;";
if (row.score >= 60) return "background:#fffde7;";
return "background:#ffebee;color:#d32f2f;";
}
});
动态刷新样式
// 修改数据后重新渲染行样式
function refreshRow(row) {
var index = $("#dg").datagrid("getRowIndex", row);
// 先删再插入,触发重新渲染
$("#dg").datagrid("deleteRow", index);
$("#dg").datagrid("insertRow", { index: index, row: row });
}
自定义徽标列
function fmtLevel(value) {
var cls = value === "high" ? "bg-red" :
value === "mid" ? "bg-yellow" : "bg-green";
var text = value === "high" ? "高" :
value === "mid" ? "中" : "低";
return "<span class='" + cls + "' style='color:#fff;padding:2px 8px;border-radius:3px;'>" + text + "</span>";
}
columns: [[
{ field: "level", title: "优先级", width: 80, formatter: fmtLevel }
]]
rowStyler 返回的是行内联样式;与列 formatter 配合使用效果最好——一个管行底色,一个管单元格内容。jEasyUI 创建属性网格
propertygrid 是专门展示「键值对属性」的网格:左侧属性名,右侧属性值,支持行内编辑。适合做配置项、对象属性查看器。
基本用法
$("#pg").propertygrid({
url: "props.php",
showGroup: true, // 按 group 分组显示
scrollbarSize: 0
});
数据格式
[
{ "name": "host", "value": "127.0.0.1", "group": "连接", "editor": "text" },
{ "name": "port", "value": "3306", "group": "连接", "editor": "numberbox" },
{ "name": "username", "value": "root", "group": "认证", "editor": "text" },
{ "name": "password", "value": "******", "group": "认证",
"editor": { "type": "passwordbox" } }
]
直接加载数据
$("#pg").propertygrid({
data: [
{ name: "宽度", value: "800px", group: "尺寸", editor: "text" },
{ name: "高度", value: "600px", group: "尺寸", editor: "text" },
{ name: "背景色", value: "#ffffff", group: "样式",
editor: { type: "textbox" } }
],
columns: [[
{ field: "name", title: "属性", width: 120 },
{ field: "value", title: "值", width: 200,
editor: "text" }
]]
});
获取修改后的值
function saveProps() {
// 结束所有编辑
var rows = $("#pg").propertygrid("getRows");
$.each(rows, function (i) {
$("#pg").propertygrid("endEdit", i);
});
// 收集所有属性
var result = {};
$.each(rows, function (i, r) {
result[r.name] = r.value;
});
console.log(result);
// $.post("save.php", result);
}
jEasyUI 扩展行显示细节
expandRow 让选中行展开一个详情区域,显示该行的附加信息,类似「主从表」效果。
HTML 定义
<table id="dg" class="easyui-datagrid" title="订单列表" style="width:700px;height:300px;"
data-options="url:'orders.php',onClickRow:onClickRow">
<thead>
<tr>
<th data-options="field:'id',width:80">订单号</th>
<th data-options="field:'name',width:150">客户</th>
<th data-options="field:'amount',width:100">金额</th>
</tr>
</thead>
</table>
点击展开详情
function onClickRow(index) {
if ($("#dg").datagrid("getRows").length <= 0) return;
if ($("#dg").datagrid("getRowIndex", $("#dg").datagrid("getSelected")) === -1) return;
var row = $("#dg").datagrid("getSelected");
if ($("#dg").datagrid("isExpanded", index)) {
$("#dg").datagrid("collapseRow", index); // 已展开则收起
} else {
$("#dg").datagrid("expandRow", index); // 展开显示 detail
}
}
detail 渲染模板
$("#dg").datagrid({
detailFormatter: function (index, row) {
// 返回 HTML,会被渲染进展开区域
return "<table style='width:90%;margin:8px auto;'>" +
"<tr><td>收货地址</td><td>" + (row.address || "-") + "</td></tr>" +
"<tr><td>下单时间</td><td>" + (row.time || "-") + "</td></tr>" +
"</table>";
}
});
异步加载详情
detailFormatter: function (index, row) {
return "<div id='detail-" + row.id + "'>加载中...</div>";
},
onExpandRow: function (index, row) {
$.getJSON("order_detail.php?id=" + row.id, function (data) {
$("#detail-" + row.id).html(
"<p>商品:" + data.goods + "<br>状态:" + data.status + "</p>"
);
});
}
jEasyUI 创建子网格
在 datagrid 中嵌套第二个数据网格(子网格),实现「主从结构」:左侧主列表,选中后在子网格显示对应明细。
方案一:布局 + 主从网格
<div class="easyui-layout" style="width:800px;height:500px;">
<div data-options="region:'west',title:'订单',width:380,split:true">
<table id="dg1" class="easyui-datagrid"
url="orders.php" singleSelect="true"
>
<thead><tr>
<th data-options="field:'id',width:60">单号</th>
<th data-options="field:'name',width:180">客户</th>
</tr></thead>
</table>
</div>
<div data-options="region:'center',title:'明细'">
<table id="dg2" class="easyui-datagrid">
<thead><tr>
<th data-options="field:'goods',width:200">商品</th>
<th data-options="field:'price',width:100">单价</th>
</tr></thead>
</table>
</div>
</div>
联动加载明细
function loadDetail(index, row) {
// 重新加载子网格数据
$("#dg2").datagrid({
url: "order_items.php?order_id=" + row.id,
onLoadSuccess: function (data) {
// 可顺带更新合计
}
});
// 或使用 load 方法
// $("#dg2").datagrid("load", { order_id: row.id });
}
方案二:expandRow 内嵌子网格
detailFormatter: function (index, row) {
return "<table id='sub-" + row.id + "'></table>";
},
onExpandRow: function (index, row) {
$("#sub-" + row.id).datagrid({
url: "order_items.php?order_id=" + row.id,
singleSelect: true,
height: 120,
columns: [[
{ field: "goods", title: "商品", width: 200 },
{ field: "price", title: "单价", width: 100 }
]]
});
}
jEasyUI 显示海量数据
一次渲染几千行会导致页面卡顿。EasyUI 提供了渲染优化手段:分页、虚拟滚动、按需加载。
方案一:服务端分页(推荐)
$("#dg").datagrid({
url: "big_list.php",
pagination: true,
pageSize: 50,
pageList: [50, 100, 200],
rownumbers: true,
// 大数据量时关闭不必要的交互
singleSelect: true
});
// 后端按 page/rows 分页返回 {total, rows}
方案二:前端分页
$("#dg").datagrid({
data: bigArray, // 全部数据(如 5000 行)
pagination: true,
pageSize: 100,
// 关键:前端分页
loadFilter: function (data) {
return data; // 数据已在本地,直接返回
}
});
方案三:懒加载 + 滚动加载
var page = 1, loading = false;
$("#dg").datagrid({
url: "lazy_list.php?page=" + page,
onBeforeLoad: function () { return false; }, // 阻止默认加载
onLoadSuccess: function (data) {
if (data.rows.length) {
$("#dg").datagrid("appendRow", data.rows);
page++;
} else {
// 没有更多数据
$("#loadMore").linkbutton("disable");
}
}
});
// 点击「加载更多」
function loadMore() {
if (loading) return;
loading = true;
$.getJSON("lazy_list.php?page=" + page, function (data) {
$("#dg").datagrid("appendRow", data.rows);
page++;
loading = false;
});
}
性能小贴士
| 手段 | 说明 |
|---|---|
| 分页 | 最常用,单页 ≤ 100 行 |
| 减少列数 | 列越多渲染越慢,隐藏不常用列 |
| 精简 formatter | 避免每行做昂贵 DOM 操作 |
| 关闭 fitColumns | 大数据量下自适应列宽开销大 |
jEasyUI 添加分页组件
独立的pagination组件可以脱离 datagrid 使用,自定义页脚的分页控件,如底部统计条、跳页、每页条数。
基础分页组件
<div id="pp" class="easyui-pagination" style="width:600px;height:32px;"
data-options="total:114,pageSize:20,pageList:[10,20,50]"
>
</div>
与列表联动
function onSelectPage(pageNumber, pageSize) {
$("#dg").datagrid("load", {
page: pageNumber,
rows: pageSize
});
}
$("#dg").datagrid({
url: "list.php",
onLoadSuccess: function (data) {
// 同步分页组件的总条数与当前页
$("#pp").pagination("refresh", {
total: data.total,
pageNumber: $("#dg").datagrid("options").pageNumber,
pageSize: $("#dg").datagrid("options").pageSize
});
}
});
自定义分页布局
$("#pp").pagination({
total: 0,
layout: ["first", "prev", "manual", "next", "last", "refresh", "info"],
showPageList: true,
showRefresh: false,
beforePageText: "第",
afterPageText: "页,共 {pages} 页",
displayMsg: "显示 {from} 到 {to} 条,共 {total} 条"
});
displayMsg 支持占位符: {from} {to} {total} {pages};用 layout 可自由组合按钮顺序。jEasyUI 创建简单窗口
window 是最基础的浮动容器:支持拖动、缩放、最大化最小化、模态遮罩。对话框(dialog)就是它的子类。
HTML 定义窗口
<div id="win" class="easyui-window" title="简单窗口"
data-options="modal:true,closed:true,width:400,height:250,
minimizable:true,maximizable:true,collapsible:true,
draggable:true,resizable:true,iconCls:'icon-tip'">
<p>这是一个可拖动、可缩放、可最大化的窗口</p>
<p>内容可以放任意 HTML 或 iframe</p>
</div>
JS 方式创建
$("#win").window({
title: "动态窗口",
width: 400,
height: 250,
modal: true, // 模态遮罩
closed: true, // 初始关闭
onClose: function () {
// 关闭时清理
},
onOpen: function () {
// 打开时初始化
}
});
打开 / 关闭 / 定位
$("#win").window("open"); // 打开
$("#win").window("close"); // 关闭
$("#win").window("center"); // 居中
$("#win").window("maximize"); // 最大化
$("#win").window("restore"); // 还原
// 移动到指定位置
$("#win").window("move", { left: 200, top: 100 });
嵌入 iframe
$("#win").window({
content: "<iframe src='detail.html' style='width:100%;height:100%;border:0;'></iframe>"
});
jEasyUI 自定义窗口工具栏
给 window 添加自定义工具栏/按钮区:右上角自定义按钮、底部操作按钮,让窗口更像一个完整的功能模块。
底部按钮(buttons)
<div id="win" class="easyui-window" title="编辑用户" style="width:420px;height:300px;"
data-options="modal:true,closed:true,buttons:'#win-buttons'">
<form id="userForm">
姓名:<input name="name" class="easyui-textbox" style="width:180px;">
</form>
</div>
<div id="win-buttons">
<a class="easyui-linkbutton" data-options="iconCls:'icon-ok'">保存</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-cancel'">取消</a>
</div>
右上角自定义按钮(tools)
$("#win").window({
title: "任务详情",
tools: [
{
iconCls: "icon-refresh",
handler: function () {
$("#win").window("refresh"); // 刷新内容
}
}, {
iconCls: "icon-print",
handler: function () { printWin(); }
}
]
});
JS 方式配置 buttons
$("#win").window({
buttons: [{
text: "保存",
iconCls: "icon-save",
handler: function () {
if ($("#userForm").form("validate")) {
$.post("save_user.php", $("#userForm").serialize());
$("#win").window("close");
}
}
}, {
text: "关闭",
handler: function () { $("#win").window("close"); }
}]
});
buttons 放「保存/取消」;操作类窗口用 tools 放快捷图标,保持标题栏简洁。jEasyUI 窗口与布局
window 与 layout 结合,可以做出「弹窗内嵌复杂布局」的效果:弹窗里再分栏、放 tabs,适用于复杂的编辑页。
窗口内嵌布局
$("#win").window({
title: "复杂编辑",
width: 700,
height: 450,
modal: true,
content: "<div id='innerLayout' class='easyui-layout' style='width:100%;height:100%;'>" +
"<div data-options="region:'west',title:'导航',width:150,split:true">" +
" 左侧菜单</div>" +
"<div data-options="region:'center',title:'内容'">" +
" 编辑区域</div>" +
"</div>",
onOpen: function () {
// 窗口打开后,布局需要重新渲染一次
$("#innerLayout").layout("resize");
}
});
窗口内嵌 tabs
$("#win").window({
title: "商品编辑",
width: 650,
height: 400,
content:
"<div id='innerTabs' class='easyui-tabs' style='width:100%;height:100%;'>" +
" <div title='基本信息'>名称、分类、描述</div>" +
" <div title='价格库存'>价格与库存</div>" +
" <div title='图片'>图片上传</div>" +
"</div>"
});
父子窗口传值
// 打开子窗口并传值
function openChild(row) {
$("#childWin").window({
onOpen: function () {
$("#childForm").form("load", row); // 回填数据
}
}).window("open");
}
// 子窗口保存后刷新父列表
function childSaved() {
$("#childWin").window("close");
$("#dg").datagrid("reload");
}
class="easyui-xxx" 让 EasyUI 自动渲染)。jEasyUI 创建对话框
dialog 是 window 的扩展,自带标题栏、关闭按钮、底部按钮区,是「确认、表单、提示」类弹窗的首选。
基本对话框
<div id="dlg" class="easyui-dialog" title="新增" style="width:380px;height:220px;"
data-options="modal:true,closed:true,
buttons:[{text:'保存',iconCls:'icon-ok',handler:save},
{text:'取消',handler:function(){$(this).dialog('close')}}]">
<form id="fm">
<input name="name" class="easyui-textbox" label="名称:" style="width:200px;">
</form>
</div>
快捷方法
// 打开/关闭
$("#dlg").dialog("open");
$("#dlg").dialog("close");
// 修改标题
$("#dlg").dialog("setTitle", "编辑");
// 调整大小
$("#dlg").dialog("resize", { width: 500, height: 300 });
// 判断是否打开
if ($("#dlg").dialog("options").closed === false) { ... }
加载远程内容
$("#dlg").dialog({
href: "edit_form.php?id=5", // 从远程加载内容
onLoad: function () {
// 内容加载完成后初始化组件
}
});
确认对话框($.messager)
$.messager.confirm("确认删除", "确定删除这条记录吗?", function (ok) {
if (ok) {
// 执行删除
$("#dg").datagrid("reload");
}
});
$.messager.alert("提示", "操作成功", "info");
$.messager.show({
title: "通知",
msg: "保存成功",
timeout: 3000,
showType: "slide"
});
$.messager;需要表单交互用 dialog;需要自由布局用 window——三者按复杂度递增选择。jEasyUI 自定义对话框
自定义对话框在 dialog 基础上扩展:自定义底部按钮、右上角工具、皮肤样式,让弹窗与业务完全匹配。
自定义底部按钮
$("#dlg").dialog({
title: "审核",
width: 400,
height: 240,
modal: true,
closed: true,
buttons: [{
text: "通过",
iconCls: "icon-ok",
handler: function () { audit(1); }
}, {
text: "驳回",
iconCls: "icon-no",
handler: function () { audit(0); }
}, {
text: "取消",
handler: function () { $("#dlg").dialog("close"); }
}]
});
右上角自定义工具
$("#dlg").dialog({
tools: [{
iconCls: "icon-help",
handler: function () {
$.messager.alert("帮助", "填写必填项后点击保存");
}
}]
});
自定义样式
.custom-dialog .panel-title { color: #fff; }
.custom-dialog .panel-header {
background: linear-gradient(90deg, #4a90d9, #2f6fb2);
color: #fff;
}
.custom-dialog .panel-body { background: #f7f9fc; }
$("#dlg").dialog({
cls: "custom-dialog" // 附加自定义 class
});
对话框里嵌套表单校验
function saveFromDialog() {
if (!$("#dlgForm").form("validate")) {
$.messager.alert("提示", "请完善必填项");
return;
}
$.post("save.php", $("#dlgForm").serialize(), function (res) {
if (res.success) {
$("#dlg").dialog("close");
$("#dg").datagrid("reload");
}
}, "json");
}
cls 属性叠加自定义样式;业务相关弹窗建议封装成函数,统一管理打开/保存/关闭逻辑。jEasyUI 使用标记创建树形菜单
tree 组件用来展示层级数据。用 HTML 标记(ul/li)创建树是最直观的方式,适合静态菜单。
HTML 树形菜单
<ul id="tt" class="easyui-tree">
<li>
<span>前端开发</span>
<ul>
<li><span>HTML</span></li>
<li><span>CSS</span></li>
<li>
<span>JavaScript</span>
<ul>
<li><span>ES6</span></li>
<li><span>DOM</span></li>
</ul>
</li>
</ul>
</li>
<li>
<span>后端开发</span>
<ul>
<li><span>Python</span></li>
<li><span>Go</span></li>
</ul>
</li>
</ul>
带图标与状态
<ul id="tt" class="easyui-tree" data-options="animate:true">
<li data-options="iconCls:'icon-folder'">
<span>文档</span>
<ul>
<li data-options="iconCls:'icon-file'"><span>需求.md</span></li>
<li data-options="iconCls:'icon-file'"><span>设计.md</span></li>
</ul>
</li>
</ul>
常用事件
$("#tt").tree({
onClick: function (node) {
console.log("点击:", node.text, "id:", node.id);
// 加载对应内容
$("#content").load(node.url || "about:blank");
},
onExpand: function (node) {
console.log("展开:", node.text);
}
});
jEasyUI 创建异步树形菜单
大型菜单数据量庞大,使用异步加载:节点展开时才向后端请求子节点,减少首屏加载量。
异步加载配置
$("#tt").tree({
url: "tree_data.php",
method: "get",
// 展开节点时自动请求:url?id=父节点id
onBeforeExpand: function (node) {
$("#tt").tree("options").url = "tree_data.php?id=" + node.id;
}
});
后端返回格式
// 顶级节点
[
{ "id": 1, "text": "用户管理", "state": "closed", "children": [] }
]
// 子节点(展开时请求 id=1 返回)
[
{ "id": 11, "text": "用户列表" },
{ "id": 12, "text": "角色管理" }
]
状态说明
| state | 含义 |
|---|---|
open | 节点展开,children 可直接返回 |
closed | 节点收起,展开时才异步加载子节点 |
完整示例
$("#tt").tree({
url: "menu.php",
loadFilter: function (data) {
// 数据清洗:确保每个节点有 state
$.each(data, function (i, n) {
if (n.children && n.children.length === 0) {
n.state = "closed"; // 有子级但未加载
}
});
return data;
},
onSelect: function (node) {
if (node.attributes && node.attributes.url) {
window.open(node.attributes.url);
}
}
});
jEasyUI 树形菜单添加节点
通过 JS API 向树形菜单动态添加/更新/删除节点,实现菜单编辑、分类管理等功能。
添加节点
// 在选中节点下追加子节点
function addChild() {
var node = $("#tt").tree("getSelected");
if (!node) {
$.messager.alert("提示", "请先选中父节点");
return;
}
$("#tt").tree("append", {
parent: node,
data: [{
id: Date.now(), // 生成唯一 id
text: "新节点 " + Math.floor(Math.random() * 100),
state: "open"
}]
});
}
// 在根节点追加
function addRoot() {
$("#tt").tree("append", {
data: [{ id: Date.now(), text: "新根节点" }]
});
}
更新节点文本
function renameNode() {
var node = $("#tt").tree("getSelected");
if (!node) return;
$.messager.prompt("重命名", "输入新名称:", function (val) {
if (val) {
$("#tt").tree("update", {
target: node.target,
text: val
});
}
});
}
删除节点
function removeNode() {
var node = $("#tt").tree("getSelected");
if (!node) return;
$.messager.confirm("确认", "删除节点「" + node.text + "」及其子节点?", function (ok) {
if (ok) {
$("#tt").tree("remove", node.target);
}
});
}
插入到指定位置
// 在选中节点之前/之后插入
$("#tt").tree("insert", {
before: node.target,
data: { id: 99, text: "插入节点" }
});
jEasyUI 创建带复选框的树形菜单
给树节点加复选框,实现权限勾选、多选分类——后台系统权限管理的标配。
启用复选框
$("#tt").tree({
url: "tree_data.php",
checkbox: true, // 关键:显示复选框
cascadeCheck: true // 父子联动(默认 true)
});
获取勾选节点
function getCheckedIds() {
var nodes = $("#tt").tree("getChecked"); // 勾选的节点
var ids = $.map(nodes, function (n) { return n.id; });
return ids;
}
// 只取「叶节点」(父节点勾选通常不单独处理)
function getLeafChecked() {
var nodes = $("#tt").tree("getChecked");
var leaves = $.map(nodes, function (n) {
var children = $("#tt").tree("getChildren", n.target);
return children.length === 0 ? n.id : null;
});
return leaves;
}
设置默认勾选
// 数据加载后,按权限回填勾选
$("#tt").tree({
onLoadSuccess: function () {
var permIds = [1, 5, 9]; // 用户已有权限
$.each(permIds, function (i, id) {
var node = $("#tt").tree("find", id);
if (node) $("#tt").tree("check", node.target);
});
}
});
父子联动与事件
$("#tt").tree({
checkbox: true,
cascadeCheck: true, // 父勾选→子全勾;取消父→子全取消
onCheck: function (node, checked) {
// 勾选/取消时触发,可实时统计
var ids = getCheckedIds();
$("#permInfo").text("已选权限数:" + ids.length);
}
});
getChecked + 手动递归处理半选状态。jEasyUI 树形菜单拖放控制
允许拖拽节点改变树结构,实现分类排序、层级调整。EasyUI 树内置拖放支持,只需开启 dnd 并校验规则。
开启拖放
$("#tt").tree({
url: "tree_data.php",
dnd: true // 开启节点拖放
});
拖放规则校验
$("#tt").tree({
dnd: true,
// 判断能否把节点拖到目标
onDragEnter: function (target, source) {
// 不允许拖到自己的子节点下
if ($(target).parents("#tt").find(source).length) {
return false;
}
return true;
},
onDrop: function (target, source, point) {
// point: 'append' | 'top' | 'bottom'
var targetNode = $(target).data("tree-node");
var sourceNode = $(source).data("tree-node");
console.log(sourceNode.text, "->", targetNode.text, point);
// 把新结构提交给后端
saveTree();
}
});
保存新结构
function saveTree() {
var roots = $("#tt").tree("getRoots");
var data = [];
$.each(roots, function (i, root) {
data.push(buildNode(root));
});
$.post("save_tree.php", { tree: JSON.stringify(data) }, function (res) {
$.messager.show({ title: "提示", msg: "保存成功" });
}, "json");
}
function buildNode(node) {
var item = { id: node.id, children: [] };
var children = $("#tt").tree("getChildren", node.target);
$.each(children, function (i, c) {
item.children.push(buildNode(c));
});
return item;
}
onDragEnter 返回 false 可以禁止非法目标。jEasyUI 树形菜单加载父/子节点
默认展开深度有限,通过预加载父节点 + 惰性加载子节点的组合,兼顾首屏速度与完整导航。
预加载两层
// 后端一次性返回两级结构
// [
// { id:1, text:'A', state:'closed',
// children:[ { id:11, text:'A1', state:'closed' } ] }
// ]
$("#tt").tree({
url: "tree_two_levels.php"
});
节点状态决定加载方式
// state='closed' 的节点展开时才异步请求
$("#tt").tree({
url: "tree_async.php",
onBeforeExpand: function (node) {
// 有 children 说明已加载,不再请求
if (node.children && node.children.length > 0) return;
$("#tt").tree("options").url = "tree_async.php?id=" + node.id;
}
});
手动加载子节点
function loadChildren(nodeId) {
$.getJSON("tree_async.php?id=" + nodeId, function (data) {
var node = $("#tt").tree("find", nodeId);
if (node) {
$("#tt").tree("append", { parent: node, data: data });
}
});
}
全部展开 / 收起
function expandAll() {
var roots = $("#tt").tree("getRoots");
$.each(roots, function (i, root) {
$("#tt").tree("expandAll", root.target);
});
}
function collapseAll() {
var roots = $("#tt").tree("getRoots");
$.each(roots, function (i, root) {
$("#tt").tree("collapseAll", root.target);
});
}
jEasyUI 创建基础树形网格
treegrid 是「树 + 表格」的结合体:行有层级关系,同时具备多列、分页、行内编辑等表格能力。
基本用法
$("#tg").treegrid({
url: "treegrid_data.php",
idField: "id", // 行 id 字段
treeField: "name", // 显示树结构的列
columns: [[
{ field: "name", title: "部门", width: 220 },
{ field: "manager", title: "负责人", width: 100 },
{ field: "member", title: "人数", width: 80 }
]]
});
数据格式
[
{
"id": 1, "name": "总公司",
"manager": "张总", "member": 100,
"children": [
{ "id": 11, "name": "技术部", "manager": "李工", "member": 40,
"children": [
{ "id": 111, "name": "前端组", "manager": "王工", "member": 15 }
]
},
{ "id": 12, "name": "市场部", "manager": "赵总", "member": 60 }
]
}
]
常用操作
// 展开/收起
$("#tg").treegrid("expand", 1);
$("#tg").treegrid("collapse", 1);
// 选中行
var row = $("#tg").treegrid("getSelected");
// 获取子节点
var children = $("#tg").treegrid("getChildren", 1);
// 重新加载
$("#tg").treegrid("reload");
jEasyUI 创建复杂树形网格
在基础 treegrid 上加入复选框、排序、格式化、行操作按钮,构建完整的复杂树形网格。
复选框 + 操作列
$("#tg").treegrid({
url: "data.php",
idField: "id",
treeField: "name",
checkbox: true, // 复选框
columns: [[
{ field: "name", title: "分类", width: 220 },
{ field: "sort", title: "排序", width: 60, sortable: true },
{ field: "status", title: "状态", width: 80,
formatter: function (v) {
return v === 1 ? "<span style='color:green'>启用</span>"
: "<span style='color:gray'>停用</span>";
} },
{ field: "op", title: "操作", width: 120,
formatter: function (v, r) {
return "<a href='#' + r.id + "')">加子级</a> " +
"<a href='#' + r.id + "')">编辑</a>";
} }
]],
onLoadSuccess: function () {
// 加载后展开前两级
var rows = $("#tg").treegrid("getRoots");
$.each(rows, function (i, r) {
$("#tg").treegrid("expand", r.id);
});
}
});
获取勾选的节点
function getChecked() {
var rows = $("#tg").treegrid("getChecked");
return $.map(rows, function (r) { return r.id; });
}
行操作函数
function addChild(pid) {
$("#dlg").dialog("open").dialog("setTitle", "新增子分类");
$("#fm input[name=pid]").val(pid);
}
function editRow(id) {
var row = $("#tg").treegrid("find", id);
$("#fm").form("load", row);
$("#dlg").dialog("open").dialog("setTitle", "编辑分类");
}
function save() {
if (!$("#fm").form("validate")) return;
$.post("save.php", $("#fm").serialize(), function (res) {
if (res.success) {
$("#dlg").dialog("close");
$("#tg").treegrid("reload");
}
}, "json");
}
idField 值必须全局唯一(父子层级间也不能重复),否则展开/选中会错乱。jEasyUI 树形网格动态加载
treegrid 也支持异步加载:展开父节点时才请求子级数据,适合层级深、数据量大的分类树。
异步加载配置
$("#tg").treegrid({
url: "treegrid_data.php",
idField: "id",
treeField: "name",
// 展开前动态设置 url,带上父节点 id
onBeforeExpand: function (row) {
$("#tg").treegrid("options").url =
"treegrid_data.php?parent_id=" + row.id;
},
columns: [[
{ field: "name", title: "分类", width: 240 },
{ field: "count", title: "子类数", width: 80 }
]]
});
后端响应
// 带 children 表示有子级(展开图标显示),前端展开时才请求
[
{ "id": 1, "name": "电脑", "count": 2, "children": [
{ "id": 11, "name": "笔记本", "count": 5, "children": [] }
] }
]
惰性加载标记
// 后端返回的节点若无 children 字段,前端会视为叶节点
// 若「可能有子级但未加载」,应返回 children:[] 或 state 标记
// 前端 loadFilter 统一处理:
$("#tg").treegrid({
loadFilter: function (data) {
$.each(data, function (i, d) {
if (d.has_child && (!d.children || !d.children.length)) {
d.state = "closed"; // 标记为可展开,异步加载
}
});
return data;
}
});
手动刷新子级
function refreshNode(id) {
$("#tg").treegrid("remove", id); // 删除旧节点
$("#tg").treegrid("reload"); // 整体重载
// 或精确:append 新数据
$.getJSON("treegrid_data.php?parent_id=" + id, function (data) {
$("#tg").treegrid("append", { parent: id, data: data });
});
}
remove 再重新加载该分支。jEasyUI 树形网格添加分页
treegrid 支持分页,但层级结构会打散到多页。常见做法:分页只作用于根节点,子级在展开时异步加载。
启用分页
$("#tg").treegrid({
url: "treegrid_page.php",
idField: "id",
treeField: "name",
pagination: true, // 启用分页
pageSize: 10,
pageList: [10, 20, 50],
columns: [[
{ field: "name", title: "部门", width: 220 },
{ field: "count", title: "人数", width: 80 }
]]
});
服务端分页逻辑
// 根节点分页
$page = (int)($_GET['page'] ?? 1);
$rows = (int)($_GET['rows'] ?? 10);
$total = $db->query("SELECT COUNT(*) FROM dept WHERE parent_id=0")->value;
$list = $db->query(
"SELECT * FROM dept WHERE parent_id=0 LIMIT " .
(($page - 1) * $rows) . ", $rows"
);
echo json_encode([
"total" => $total,
"rows" => $list // 每行含 children 供展开
]);
分页后子级异步加载
$("#tg").treegrid({
pagination: true,
onBeforeExpand: function (row) {
$("#tg").treegrid("options").url =
"treegrid_children.php?parent_id=" + row.id;
},
onLoadSuccess: function (data) {
// 更新分页信息
var opts = $("#tg").treegrid("options");
opts.total = data.total;
$("#tg").treegrid("resize");
}
});
前端分页(小数据)
// 数据量小(几千行)时用前端分页
$("#tg").treegrid({
data: allData, // 完整层级数据
pagination: true,
fitColumns: true
});
total 指的是根节点总数;子节点不计入分页计数。展开后子级变化不会影响分页状态。jEasyUI 树形网格惰性加载节点
惰性加载(Lazy Load):初始只加载根节点,展开时才请求子节点。相比一次全量加载,首屏更快、请求更少。
惰性加载示例
$("#tg").treegrid({
url: "lazy_root.php", // 初始只请求根
idField: "id",
treeField: "name",
loadFilter: function (data) {
// 关键:把「有子级的节点」标记为未展开
$.each(data, function (i, d) {
if (d.has_children) d.state = "closed";
});
return data;
},
onBeforeExpand: function (row) {
// 展开时请求该节点子级
$("#tg").treegrid("options").url =
"lazy_children.php?parent_id=" + row.id;
},
columns: [[
{ field: "name", title: "节点", width: 240 },
{ field: "leaf", title: "类型", width: 100,
formatter: function (v) {
return v === 1 ? "叶节点" : "分支";
} }
]]
});
后端两套接口
// lazy_root.php 返回根节点
[
{ "id": 1, "name": "华东区", "has_children": true },
{ "id": 2, "name": "华北区", "has_children": true }
]
// lazy_children.php?parent_id=1 返回子节点
[
{ "id": 11, "name": "上海分公司", "has_children": false },
{ "id": 12, "name": "杭州分公司", "has_children": true }
]
展开状态记忆
// 记录已展开的节点,刷新后恢复
var expanded = {};
$("#tg").treegrid({
onExpand: function (row) { expanded[row.id] = true; },
onCollapse: function (row) { delete expanded[row.id]; },
onLoadSuccess: function () {
$.each(expanded, function (id) {
var row = $("#tg").treegrid("find", id);
if (row) $("#tg").treegrid("expand", id);
});
}
});
jEasyUI 创建异步提交表单
默认表单提交会整页刷新,使用 EasyUI form 组件的 ajax 提交可以无刷新保存,配合成功回调刷新列表。
表单 HTML
<form id="fm" method="post">
<input name="name" class="easyui-textbox" label="名称:" style="width:200px;" required="true">
<br>
<input name="price" class="easyui-numberbox" label="价格:" style="width:200px;" min="0">
</form>
异步提交
function submitForm() {
if (!$("#fm").form("validate")) {
$.messager.alert("提示", "请完善表单");
return;
}
// 方式一:ajax 提交到 url
$("#fm").form("submit", {
url: "save.php",
onSubmit: function () {
return $("#fm").form("validate"); // 提交前校验
},
success: function (res) {
var data = $.parseJSON(res);
if (data.success) {
$.messager.show({ title: "成功", msg: "保存成功" });
$("#dlg").dialog("close");
$("#dg").datagrid("reload");
} else {
$.messager.alert("错误", data.msg || "保存失败");
}
}
});
}
方式二:$.post 手动提交(更灵活)
function submitManual() {
if (!$("#fm").form("validate")) return;
$.post("save.php", $("#fm").serialize(), function (res) {
if (res.success) {
$("#dlg").dialog("close");
$("#dg").datagrid("reload");
} else {
$.messager.alert("错误", res.msg);
}
}, "json");
}
上传文件(带文件域的表单)
// 表单中包含 <input type="file"> 时,必须用 form 的 ajax 提交
$("#fm").form("submit", {
url: "upload.php",
success: function (res) {
var data = $.parseJSON(res);
if (data.success) $("#imgPreview").attr("src", data.url);
}
});
form('submit');需要额外加工数据(如转 JSON、追加参数)用 $.post + serialize(),控制力更强。jEasyUI 表单验证
EasyUI 表单内置校验规则(required、email、url、length 等),还支持自定义规则与实时提示。
内置校验
<form id="fm">
<input class="easyui-validatebox" data-options="required:true,validType:'length[2,20]'"
style="width:200px;"> 用户名(2-20位)
<br>
<input class="easyui-validatebox" data-options="required:true,validType:'email'"
style="width:200px;"> 邮箱
<br>
<input class="easyui-validatebox" data-options="required:true,validType:'url'"
style="width:200px;"> 网址
</form>
校验规则速查
| validType | 说明 |
|---|---|
email | 邮箱格式 |
url | 网址格式 |
length[2,20] | 长度 2-20 |
min[5] | 最小长度 5 |
max[10] | 最大长度 10 |
自定义校验规则
// 注册自定义规则:手机号
$.extend($.fn.validatebox.defaults.rules, {
phone: {
validator: function (value, param) {
return /^1[3-9]\d{9}$/.test(value);
},
message: "请输入正确的手机号"
},
// 确认密码
equalTo: {
validator: function (value, param) {
return $(param[0]).val() === value;
},
message: "两次输入的密码不一致"
}
});
// 使用
// <input class="easyui-validatebox" data-options="required:true,validType:'phone'">
// <input class="easyui-validatebox" data-options="required:true,validType:'equalTo['#pwd']'">
表单级校验与提交
function checkAndSubmit() {
if (!$("#fm").form("validate")) {
// 定位第一个错误项
$.messager.alert("提示", "请检查表单填写");
return;
}
// 校验通过,提交
$("#fm").form("submit", { url: "save.php" });
}
// 实时校验
$("#fm input").each(function () {
$(this).validatebox({
onValidate: function (valid) {
$(this).parent().find(".tip").text(valid ? "" : "有误");
}
});
});
jEasyUI 创建树形下拉框
combotree 是「下拉框 + 树」的组合:点击展开树形选项,选择后回填所选节点文本。常用于分类选择、上级部门选择。
基本用法
<input id="cc" class="easyui-combotree" style="width:220px;"
data-options="url:'tree_data.php',required:true">
JS 方式创建
$("#cc").combotree({
url: "tree_data.php",
valueField: "id", // 值字段
textField: "text", // 文本字段
method: "get",
panelHeight: "auto",
onSelect: function (node) {
console.log("选中节点:", node);
}
});
取值与赋值
// 取值(返回 id)
var selectedId = $("#cc").combotree("getValue");
// 取文本
var text = $("#cc").combotree("getText");
// 赋值(按 id 回显)
$("#cc").combotree("setValue", 5);
// 清空
$("#cc").combotree("clear");
只允许选择叶节点
$("#cc").combotree({
url: "tree_data.php",
onBeforeSelect: function (node) {
// 有子节点则禁止选择(只能选叶子)
if ($(node.target).find("ul").length > 0) {
$.messager.alert("提示", "请选择具体分类");
return false;
}
return true;
}
});
多选模式
$("#cc").combotree({
url: "tree_data.php",
multiple: true, // 多选
cascadeCheck: true
});
// 多选取值(返回 id 数组)
var ids = $("#cc").combotree("getValues");
jEasyUI 格式化下拉框
combobox 的 formatter 可以自定义选项的显示:加图标、加副标题、多字段组合展示。
基础格式化
$("#cc").combobox({
url: "users.php",
valueField: "id",
textField: "name",
formatter: function (row) {
// 返回 HTML,作为选项显示内容
return "<span class='user-opt'>" +
"<span class='avatar'>" + (row.avatar || "👤") + "</span> " +
row.name +
" <span style='color:#999;'>(" + row.dept + ")</span>" +
"</span>";
}
});
带图标选项
$("#cc").combobox({
url: "icons.php",
valueField: "name",
textField: "name",
formatter: function (row) {
return "<span style='display:inline-block;width:24px;'>" + row.icon + "</span> " + row.name;
}
});
显示样式 CSS
.user-opt { display: flex; align-items: center; gap: 6px; }
.user-opt .avatar { font-size: 18px; }
分组显示(optgroup)
$("#cc").combobox({
valueField: "id",
textField: "text",
data: [
{ id: 1, text: "手机", group: "数码" },
{ id: 2, text: "电脑", group: "数码" },
{ id: 3, text: "衬衫", group: "服饰" }
],
groupField: "group" // 按 group 字段分组显示
});
选中后回填自定义文本
$("#cc").combobox({
onSelect: function (row) {
// 在别处显示完整描述
$("#desc").text(row.name + " - " + row.dept);
}
});
panelHeight:'auto' 让面板自适应高度;复杂选项慎用,影响下拉性能。jEasyUI 过滤下拉数据网格
让下拉框与数据网格联动:选中下拉值后,数据网格按条件过滤重新加载——列表页最常见的交互。
下拉 + 网格布局
<div style="padding:8px;background:#f5f5f5;">
分类:
<input id="catFilter" class="easyui-combobox" style="width:180px;"
data-options="url:'cats.php',valueField:'id',textField:'name',
onSelect:filterGrid">
<a class="easyui-linkbutton" data-options="iconCls:'icon-clear'"
>清除筛选</a>
</div>
<table id="dg" class="easyui-datagrid" style="width:100%;height:350px;"
url="goods.php">
<thead><tr>
<th data-options="field:'name',width:200">商品</th>
<th data-options="field:'price',width:100">价格</th>
</tr></thead>
</table>
过滤逻辑
function filterGrid(record) {
// 把分类 id 作为参数传给网格接口
$("#dg").datagrid("load", {
cat_id: record.id
});
}
function clearFilter() {
$("#catFilter").combobox("clear"); // 清空下拉
$("#dg").datagrid("load", {}); // 加载全部
}
多条件组合过滤
function doFilter() {
$("#dg").datagrid("load", {
cat_id: $("#catFilter").combobox("getValue"),
keyword: $("#kw").textbox("getValue"),
in_stock: $("#stockOnly").switchbutton("options").checked ? 1 : 0
});
}
// 任一控件变化都触发
$("#catFilter").combobox({ onChange: doFilter });
$("#kw").textbox({ onChange: doFilter });
记住筛选状态
// 刷新页面后恢复筛选条件
$(function () {
var saved = $.cookie ? $.cookie("filter") : null;
if (saved) {
var f = JSON.parse(saved);
$("#catFilter").combobox("setValue", f.cat_id);
$("#dg").datagrid("load", f);
}
});
page/rows 一起提交;后端记得把 total 按过滤后的结果统计,否则页码会错。jQuery EasyUI 插件
EasyUI 提供了 searchbox、numberbox、switchbutton、progressbar、slider 等丰富的表单与交互插件,覆盖各种输入场景。
常用插件速览
| 插件 | 用途 |
|---|---|
searchbox | 搜索框(带放大镜图标) |
numberbox | 数字输入框(范围/精度) |
numberspinner | 数字微调器(上下箭头) |
switchbutton | 开关按钮(开/关) |
progressbar | 进度条 |
slider | 滑块(区间选择) |
datetimebox | 日期时间选择器 |
searchbox 搜索框
$("#sb").searchbox({
prompt: "搜索商品...",
searcher: function (value, name) {
// 回车或点击搜索按钮触发
$("#dg").datagrid("load", { q: value });
}
});
switchbutton 开关
<input id="sw" class="easyui-switchbutton" data-options="onText:'启用',offText:'停用'">
<script>
$("#sw").switchbutton({
onChange: function (checked) {
// checked: true/false
$.post("toggle.php", { enabled: checked ? 1 : 0 });
}
});
</script>
progressbar 进度条
$("#pb").progressbar({ value: 0 });
var p = 0;
var timer = setInterval(function () {
p += 10;
$("#pb").progressbar("setValue", p);
if (p >= 100) clearInterval(timer);
}, 300);
slider 滑块
$("#sl").slider({
min: 0, max: 100, value: 30,
showTip: true,
onChange: function (v) {
$("#priceRange").text("¥" + v);
}
});
$('#id').插件名('方法', 参数),学会一个就能举一反三。jQuery EasyUI 扩展
EasyUI 支持通过 $.extend 扩展默认行为:自定义验证规则、新组件、主题变量。本教程演示最常用的几种扩展方式。
1. 扩展验证规则
$.extend($.fn.validatebox.defaults.rules, {
idcard: {
validator: function (value) {
return /^\d{15}$|^\d{17}[0-9Xx]$/.test(value);
},
message: "身份证号格式不正确"
}
});
2. 扩展 datagrid 列格式化公共函数
$.extend($.fn.datagrid.defaults, {
formatters: {
yesno: function (v) {
return v ? "<span class='badge-ok'>是</span>"
: "<span class='badge-no'>否</span>";
},
money: function (v) {
return v == null ? "-" : "¥" + Number(v).toFixed(2);
}
}
});
// 使用
// { field: "paid", title: "已付款", formatter: $.fn.datagrid.defaults.formatters.yesno }
3. 自定义主题变量
/* 在引入 easyui.css 后覆盖主题色 */
.panel-header, .tabs-header {
background: #2f6fb2;
}
a.l-btn:hover {
background: #e8f0fb;
}
4. 注册自定义组件(以「星级评分」为例)
$.fn.stars = function (options) {
var defaults = { max: 5, value: 0, readonly: false };
var opts = $.extend({}, defaults, options);
return this.each(function () {
var $el = $(this), html = [];
for (var i = 1; i <= opts.max; i++) {
html.push("<span class='star" + (i <= opts.value ? " on" : "") + "' data-v='" + i + "'>★</span>");
}
$el.html(html.join(""));
if (!opts.readonly) {
$el.on("click", ".star", function () {
var v = $(this).data("v");
$el.find(".star").removeClass("on")
.filter(function (i) { return i < v; }).addClass("on");
});
}
});
};
// 使用:$("#score").stars({ value: 3 });