资讯动态

Delphi Web 界面骨架:IntraWeb 与 EasyUI 集成与回调实战

发布时间:2026/10/26 22:00:31 来源:尧图企业网站定制
简介面向使用 Delphi 进行 Web 开发的程序员与团队这份资料围绕 IntrawebIW与 EasyUI 的协同开发展开重点解决服务端业务逻辑与前端交互界面如何衔接的问题。压缩包约 4.22MB文件清单与类型明细暂未完整提供从内容构成看包含 EasyUI 1.4 版 API 中文文档以及 IW 项目中集成 EasyUI 的示例或教程线索便于按组件查询方法、理解前后端数据传递方式。已有 479 人学习说明其在 Delphi Web 与 jQuery 前端组合场景中具有一定参考价值。读者可借此梳理 IW 的服务器控件、数据库连接、动态内容生成与自定义模板用法并结合 EasyUI 的数据网格、下拉树、日期选择器等组件掌握 JSON/AJAX 无刷新交互、排序过滤编辑等常见实现思路。资料适合需要快速搭建后台管理界面、迁移桌面开发经验或补齐前后端协作认知的中级开发者查漏补缺。1. IntraWeb 与 EasyUI 拼装一份能直接编译的 Delphi Web 界面骨架IntraWeb下文按社区习惯简称 IW加上 EasyUI 这套组合在 Delphi 圈子里出现的场景高度集中手里有一套跑了很多年的服务端 Pascal 逻辑客户要求搬到浏览器上界面不能再是原生控件堆出来的样子。这份源码包解决的正是这个衔接面——服务端仍然用 IWForm 的事件模型写业务前端用 EasyUI 拿现成的数据表格、树、对话框和表单校验省掉手写 CSS 和造组件的成本。工程里能直接看到页面模板、静态资源注册、服务端 JSON 回调、datagrid 出数这几条主线的完整实现。适合两类人一是手上已有 IW 工程、界面还停在原生控件的二是想把 EasyUI 接进现有 Delphi Web 项目却不知道从哪下手的。动手前先记住一条边界IW 管状态EasyUI 管长相这条线划清楚后面改起来才不会互相踩。2. 选型与骨架IW 的事件回传模型如何和 EasyUI 组件层对上号2.1 两套渲染模型的对位关系IW 的核心假设是「窗体在服务端」。一个 TIWAppForm 实例活在会话里浏览器拿到的只是服务端控件树渲染出来的一段 HTML。用户点按钮浏览器 POST 回服务端服务端找到对应会话里的窗体和控件执行 OnClick改完属性后再把变化的部分刷回去。整套机制靠 Session 挂载靠隐藏字段传递控件 ID 和事件名。EasyUI 是另一个世界。它是一层 jQuery 插件拿到页面上已有的 DOM读>procedure TIWServerController.IWServerControllerBaseConfig(Sender: TObject); begin // 端口和会话本地调试够用上线前务必改 Port : 8888; SessionTimeout : 20 * 60; // 单位秒20 分钟 // 顺序很重要jQuery 必须排在 easyui 之前 ExtraHeader : link relstylesheet typetext/css href/www/themes/default/easyui.css sLineBreak link relstylesheet typetext/css href/www/themes/icon.css sLineBreak script typetext/javascript src/www/js/jquery.min.js/script sLineBreak script typetext/javascript src/www/js/jquery.easyui.min.js/script sLineBreak script typetext/javascript src/www/js/iw-easyui-bridge.js/script; end;Port是独立运行模式下的监听端口走 ISAPI 部署时这个值会被忽略。SessionTimeout决定会话多久回收设太短用户填一半表单就掉线设太长服务器内存吃不住20 分钟是个折中。ExtraHeader里的字符串会原样出现在每个页面的/head之前所以 jQuery 必须写在 EasyUI 前面顺序反了 EasyUI 初始化时报$ is not defined。路径前缀/www/对应的是可执行文件同级的www目录。发布时如果只拷了 exe 没拷目录页面上所有资源都是 404控制台一片红——这是最常见的第一次翻车方式。3.2 第二步给 IWForm 挂上 HTML 模板不挂模板的 IWForm 会自己生成一整页 HTML你想插 DOM 结构只能靠嵌套容器控件很难看。挂上TIWTemplateProcessorHTML之后页面的骨架来自你写的 HTML 文件IW 控件按名字填进占位符。procedure TIWFormMain.IWAppFormCreate(Sender: TObject); begin // 模板处理器接管整页布局 FTemplate : TIWTemplateProcessorHTML.Create(Self); FTemplate.Templates.Default : templates/main.html; LayoutMgr : FTemplate; // 页面里 {IWURL} 这类标签由 MasterForm 或处理器统一替换 FTemplate.Templates.Master : ; end;Templates.Default指向www下的相对路径注意是相对www而不是相对 exe。LayoutMgr赋值后IW 不再自己生成 body而是读模板文件把{IWLABEL1}、{IWREGION1}这种占位符替换成对应控件的渲染结果。占位符名字必须和窗体上控件的Name完全一致大小写也一致错一个字符就原样输出到页面上页面顶着一行花括号看着像模板没生效。模板文件推荐存成 UTF-8 无 BOM。带 BOM 的话某些 IW 版本会把 BOM 当成正文输出页面最前面冒出三个不可见字符HTML 解析器进入怪异模式EasyUI 的布局立刻错位。3.3 第三步用 IWCallBack 出 JSON让 datagrid 自己渲染到了数据层这里思路是让 IW 完全不参与表格结构只吐 JSON。IW 里的异步回调组件可以拦截一次请求让你直接往响应流里写内容绕开页面渲染流程。procedure TIWFormGrid.IWCallBack1Execute(Sender: TObject; EventParams: TStringList); var Arr: TJSONArray; Obj: TJSONObject; I: Integer; begin Arr : TJSONArray.Create; try for I : 0 to FRepo.Count - 1 do begin Obj : TJSONObject.Create; Obj.AddPair(orderNo, FRepo.Items[I].OrderNo); Obj.AddPair(customer, FRepo.Items[I].Customer); Obj.AddPair(amount, TJSONNumber.Create(FRepo.Items[I].Amount)); Obj.AddPair(status, FRepo.Items[I].StatusText); Arr.AddElement(Obj); end; // 直接写响应跳过 IW 的页面渲染 WebApplication.Response.ContentType : application/json; charsetutf-8; WebApplication.Response.StatusCode : 200; WebApplication.Response.Write(Arr.ToString); finally Arr.Free; end; end;EventParams是前端传上来的查询参数做分页和筛选时从这里取比如EventParams.Values[page]。WebApplication.Response.Write是关键的一句它直接往输出流写IW 检测到已经写过内容就不再渲染页面。回调地址有两种拿法一是 IW 在页面里生成的固定格式 URL二是自己在模板里留个隐藏域服务端OnRender时把地址填进去。我更推荐后者URL 格式在不同 IW 版本间变过写死在前端 JS 里升级时必炸。版本差异要有心理准备回调组件的类名和事件签名在 14 和 15 之间改过Execute的参数列表不完全一样。拿到源码先看uses里引的是哪个单元再对着那版的声明改。3.4 第四步前端拿到地址后初始化 datagrid前端这边就回归 EasyUI 的常规写法了唯一特殊的是数据地址来自服务端注入。// iw-easyui-bridge.js (function ($) { function initGrid() { var url $(#IW_CB_URL).val(); // 服务端渲染时填进来的回调地址 $(#dg).datagrid({ url: url, method: post, singleSelect: true, rownumbers: true, columns: [[ { field: orderNo, title: 单号, width: 140 }, { field: customer, title: 客户, width: 200 }, { field: amount, title: 金额, width: 100, align: right }, { field: status, title: 状态, width: 90 } ]], onLoadError: function () { $.messager.alert(提示, 数据加载失败检查回调地址, error); } }); } window.IWEasyUI { init: function () { $.parser.parse(document.body); initGrid(); }, reparse: function (scope) { $.parser.parse(scope || document.body); } }; $(function () { window.IWEasyUI.init(); }); })(jQuery);$.parser.parse负责把页面里带easyui-class 和># 假设源码解压在 D:\work\EasyUIWeb # 确认静态资源目录与编译输出同级 D:\work\EasyUIWeb\Win32\Debug\EasyUIWeb.exe D:\work\EasyUIWeb\Win32\Debug\www\js\jquery.min.js D:\work\EasyUIWeb\Win32\Debug\www\themes\default\easyui.css如果不想每次手动拷在工程设置里把输出目录改成和源码同级或者加一个编译后事件把www复制过去。我一般在.dproj的 PostBuild 里加一行xcopy /E /Y /I $(PROJECTDIR)www $(OUTPUTDIR)www省得每次改完前端忘了同步。接着核对 IW 的搜索路径。工程里uses引了IWInit、IWServerController、IWAppForm这些单元路径没配好会报找不到单元。Delphi 装了 IW 之后一般会自动加入库路径但换个 Delphi 版本就要重新确认一次。4.2 编译与独立运行工程入口文件是 IW 的标准写法指定启动窗体即可。program EasyUIWeb; uses Forms, IWInit, ServerController in ServerController.pas {IWServerController: TDataModule}, UserSessionUnit in UserSessionUnit.pas {IWUserSession: TDataModule}, IWMainForm in Forms\IWMainForm.pas {frmMain: TIWAppForm}, IWGridDemo in Forms\IWGridDemo.pas {frmGrid: TIWAppForm}; {$R *.res} begin IWRun(frmMain); end.IWRun是 IW 的启动入口参数是首个页面窗体。它会初始化 HTTP 服务、注册会话控制器、把ServerController实例化出来。注意这里传的是窗体类型而不是实例实例是每个会话创建时新建的。编译目标选 Win32 还是 Win64 都行独立运行模式下两者都能起来。点击运行控制台窗口弹出显示监听端口浏览器访问http://localhost:8888/就能看到第一个页面。如果访问超时先确认端口没被占。IW 独立模式启动失败时控制台窗口会一闪而过看不到错误用命令行方式跑一次就能看到异常堆栈cd /d D:\work\EasyUIWeb\Win32\Debug EasyUIWeb.exe # 端口冲突时会直接抛 EIdSocketError换 Port 属性即可4.3 换成部署模式ISAPI 模块与路径修正开发阶段独立运行最省事上线一般要挂到 IIS 下走 ISAPI。切换方式是把工程类型从独立 exe 改成 ISAPI 模块编译出 dll在 IIS 里建应用、配好处理程序映射。这个切换最麻烦的是一堆路径会变。独立模式下/www/js/...是相对根的挂在子应用下就变成/myapp/www/js/...。稳妥的做法是把ExtraHeader里的路径统一改成带波浪号的相对路径或者干脆用一个服务端属性拼出应用根路径。还有权限。IIS 的应用程序池如果跑在受限账户下www目录必须给读权限模板文件也一样。表现是页面能打开但布局全乱、图标全丢控制台一堆 403。换主题也得在这一步做。EasyUI 的皮肤在themes/下有多个目录把default换成别的名字同时把ExtraHeader里两处 css 路径一起改掉。只改一处的结果是布局生效但图标样式丢了看着像半成品。5. 避坑与排查IW 与 EasyUI 联调时最容易翻车的五处5.1 现象页面能开但所有 EasyUI 组件退化成裸 HTML原因几乎都是资源没加载成功或者加载顺序错。控制台里$ is not defined、easyui is not defined或者 css 404 导致组件只有结构没有样式。还有一种隐蔽情况同一页面被引入了两份 jQueryIW 某个版本自带一份你又从www里引了一份后引入的把前者覆盖之前注册的插件全丢。排查顺序是固定的F12 看 Network确认三个 js 和两个 css 全部 200再看 Console 有没有$未定义最后在 Console 里敲jQuery.fn.datagrid看是不是函数。三步走完基本能定位。解决上把ExtraHeader里的引入顺序钉死jQuery 永远第一EasyUI 第二桥接脚本第三确认 IW 自带的那份不冲突冲突就把自己引的那份换成 IW 提供的版本。5.2 现象datagrid 第一次有数据点一下 IW 按钮后表格消失或变形这是最典型的一个坑。IW 的异步回传只刷新变化的那部分 DOM如果表格所在的容器在刷新范围内EasyUI 生成的嵌套结构被整块替换掉剩下的就是你写在模板里的原始table看起来像表格「塌了」。原因在于 IW 认为那个区域的 HTML 需要更新它按自己的控件树重新渲染不知道 EasyUI 在上面加过东西。解决办法有两条一是把 datagrid 挪到 IW 不参与刷新的区域比如模板里独立的一块 div服务端不往里放控件二是在回传完成后手动 reparse把被替换的节点重新初始化。procedure TIWFormGrid.IWButtonRefreshAsyncClick(Sender: TObject; EventParams: TStringList); begin FRepo.Reload; // 重新查库 WebApplication.CallBackResponse.AddJavaScriptToExecute( IWEasyUI.reparse(#gridWrap);); // 只 repair 表格所在容器 end;AddJavaScriptToExecute把一段 JS 塞进回传响应浏览器收到后执行。这里只 parse#gridWrap范围全页 parse 会把已经正常的组件重来一遍弹窗状态会丢。这条经验是踩出来的全页 reparse 一次用户填了一半的对话框内容直接清空。5.3 现象EasyUI 弹窗打开后遮罩层盖住整个页面且点不掉弹窗的 modal 遮罩是一个全屏 divz-index 设得很高。IW 的某些容器控件渲染出来也带定位和层级两边叠在一起遮罩压住了弹窗本身或者关掉弹窗后遮罩没销毁。表现是页面变灰、点什么都没反应。根因是两层框架各自管理 z-index没人协调。EasyUI 的 dialog 默认 z-index 从 9000 起IW 的浮动面板可能更高。常见做法是在打开弹窗前手动设一次层级并在关闭事件里确保销毁。$(#dlg).dialog({ modal: true, closed: true, onOpen: function () { $(this).dialog(center); }, onClose: function () { $(this).dialog(clear); } // 关闭即清空避免复用脏数据 });另外弹窗里的表单如果同时挂了 EasyUI 的 validatebox 和 IW 的服务端校验要注意提交路径只能选一条。走 IW 的 postback前端校验结果带不回去走 Ajax服务端又拿不到会话里的控件状态。我一般让 EasyUI 只做格式校验业务规则全部放服务端两边都做就会打架。5.4 现象中文全是问号或方块模板文件编码、响应头、数据库连接字符集三处任一没对上都会出这个。IW 默认响应是 UTF-8但如果模板文件存成了 ANSI读进来就是乱码如果ContentType里没写charsetutf-8某些浏览器会按本地编码猜中文变问号。排查顺序先用编辑器的十六进制模式看模板文件开头有没有 BOM再把响应头打出来确认 charset。在ServerController里设一次全局的WebApplication.Response.CharSet : utf-8能兜住大部分情况。数据库那边如果是老版本驱动连接串里的字符集参数别漏。5.5 现象本地一切正常部署到服务器后静态资源全 404区别就在路径和权限。独立运行时相对路径以 exe 所在目录为基准挂到 IIS 子应用下就变成以站点根为基准。或者www目录压根没跟着 dll 一起发布本地有、服务器没有。我的做法是发布脚本里强制校验拷完 dll 之后用 curl 拉一遍关键资源状态码不是 200 就直接失败别等用户反馈页面难看。顺手把 IIS 的 MIME 类型配上.woff、.svg这类文件在某些服务器上默认不给图标会缺。6. 二次开发模板改造与自定义 EasyUI 组件的落地套路改这套代码最常见的需求是加一个新页面。流程上不要从零写复制一份IWGridDemo改。窗体继承已经配好的模板处理器和回调组件剩下的就是换 SQL、换 columns 配置。模板文件也复制一份占位符名字对齐新窗体的控件Name这样能躲开手写占位符拼错的问题。再往上一层是封装自己的组件。EasyUI 的组件初始化靠参数对象重复写很啰嗦我习惯在桥接脚本里加一层注册函数把常用配置固化下来// 注册一个带默认分页的查询表格 window.IWEasyUI.regGrid function (selector, url, columns, opts) { var cfg $.extend({ url: url, method: post, pagination: true, pageSize: 20, rownumbers: true, singleSelect: true, columns: [columns] }, opts || {}); return $(selector).datagrid(cfg); };这样服务端只要把列定义以 JSON 吐出来前端一行调用就出表。列定义跟着业务走、配置跟着界面走两边不搅在一起后面换皮肤或者换框架都好挪。验证环节我一般走三步改完模板先不接数据纯静态打开确认占位符全部替换成功再接回调用 curl 直接打回调地址看返回的 JSON 结构对不对最后才在浏览器里点。跳步省时间但一旦出问题就分不清是模板、接口还是前端配置。改造的时候有个东西别动——ExtraHeader里的引入顺序和路径前缀。这两处看起来随手可改改错了整个站都白屏。从那以后我每次动这段配置都强制在三个浏览器里各开一次 Network 面板确认资源全绿再往下写业务。希望这些内容帮到你少走几段弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑