更新记录

FineUI 自 2008 年起持续迭代,每一次版本更新都源于真实项目的打磨与沉淀。

RECENT RELEASES

最近更新

v16.0.0 #218

新增

  • FineUI.Java 正式发布,为 Spring Boot 应用提供标签式、有状态的企业级 Web UI 开发方式。
    • 标签式声明:一个标签一个控件、一个属性一个功能,页面结构一眼可读、易于维护;统一的声明式标签规范,AI 好生成、人类看得懂。
    • 状态自动往返:控件状态在前后端之间自动保持——后端直接读写控件属性,事件直达处理方法,后端修改的属性自动回传前端,整页不刷新、像写桌面程序一样写 Web。
    • Spring Boot 原生集成:一个 Maven 依赖即可接入,自动装配、业务侧零配置;前端运行时随 jar 内嵌(自带 JS/CSS/主题/语言资源),无需额外部署。
    • 纯 JSON 回发协议:回发携带瘦身状态树,响应为纯 JSON 增量更新,全程无 eval;服务端不保存会话状态,天然支持水平扩展。
    • 数据绑定:表单可声明式绑定到一个数据模型对象,回发自动完成绑定与校验,校验失败自动定位到对应字段并给出提示。
    • 内容安全策略(CSP):默认不产出内联 eval 脚本,可配合严格的 CSP 策略部署。
    • 全局配置项:主题、语言、表单、表格等均可全局设置,也支持单页覆盖。
  • FineUI.Core、FineUI.Pro 社区版发布至 nuget.org,FineUI.Java 社区版发布至 Maven Central;项目可分别依赖 FineUI.Core、FineUI.Pro 和 com.fineui:fineui-java。
  • 官方示例、空项目、快速入门、AppBox 和 fineui-skills 等配套项目在独立仓库开源。
  • [Core] 新增 [HiddenProperty] 特性:页面模型的属性不渲染成控件,也能在回发之间自动保持。
    • 用途:页面自身的状态(当前角色、编辑中的主键、上次的查询条件)不属于表单字段,以前只能额外放一个 HiddenField 占位或者走 Session。
    • 用法:在页面模型的公开属性上标注 [HiddenProperty] 即可,框架在 Page_Load 与事件处理函数之前把值写回;标注在基类上同样有效。
    • 类型不限于简单类型,任何能被 JSON 序列化的类型(含嵌套对象与集合)都可以,long 与 decimal 保持完整精度。
    • 仅 RazorForms 开发模式适用;示例见 数据模型/表单控件(隐藏属性)。
    • 安全提醒:这个值随客户端往返、可以被篡改,不能用来承载权限、金额等可信业务事实,需要可信数据时请在服务端重新读取数据源。
  • [Pro] 新增全局配置项 EnableImplicitPostBack,用于收口动作控件“未声明服务端事件也自动回发”的历史行为。
    • 类库默认 true,未修改配置的老项目升级后行为不变;官方 Pro 示例、Pro 空项目和 AppBoxPro 设置为 false。
    • 新模式下,Button、MenuButton、Tool、LinkButton、TriggerBox、TwinTriggerBox 与 LinkButtonField 只有声明对应服务端事件才自动回发,与 Core、Java 保持一致。
    • 控件显式 EnablePostBack / EnableTrigger1PostBack / EnableTrigger2PostBack 始终优先,可用于无事件回发或覆盖已声明事件;手工回发、AutoPostBack 与 Grid 分页/排序/编辑不受影响。
  • [Pro] 新增全局配置项 EnableImplicitChangeEvents,用于收口 WebForms 变化事件的历史触发方式。
    • 类库默认 true 保持老项目行为;官方 Pro 示例、Pro 空项目和 AppBoxPro 设置为 false。
    • 新模式下,声明 TextChanged、SelectedIndexChanged、CheckedChanged 等服务端事件即可自动回发,不需要重复设置 AutoPostBack=true。
    • 控件显式 AutoPostBack 始终优先;其他控件回发时仍同步当前值,但不会连带触发该控件的变化事件。
  • [Pro] 表格 RenderField 新增 Commands 子标签和 Command 行命令,与 Core-RazorForms 保持一致。
    • Command 只属于 RenderField;LinkButtonField / WindowField 不支持 Command,继续作为兼容旧页面的服务端渲染列保留。
    • 支持文本、图片与字体图标、提示、确认框、命令参数、整体或按行禁用/隐藏、RendererFunction 自定义渲染,以及按行字段格式化 Window 地址和标题。
    • Grid 注册 RowCommand 服务端事件后,自动把客户端 rowcommand 桥接为 WebForms 回发;同时声明客户端 rowcommand 监听时,监听返回 false 可阻止后续回发。
    • 新增 Grid.FindCommand,可按命令名查找声明的行命令;服务端会校验回发中的行、列和命令,伪造或已禁用的命令不会进入 RowCommand。
    • 所有相关 Pro 示例已由 LinkButtonField / WindowField.OnClientClick 和手写行内 onclick 迁移到 RenderField.Commands,原有 22 项例外全部清零;行命令示例扩展到与 Core 主要能力一致,并在菜单中单独分组。
    • 行命令的 <a href="javascript:;"> 只作为按钮型链接占位;共享 Grid 点击委托会先同步阻止默认行为,因此严格 CSP 下不会执行 javascript: URL,也不会改变使用 hash 路由的页面地址。
  • [JS] F.validateForm / F.validateForms 新增 options 形式,后三个位置参数不用再为了够到后面那个而把前面的默认值一一写出来。
    • 以前:F.validateForm('form1', '_self', true, true)
    • 现在:F.validateForm('form1', { messageBoxPlain: true })
    • options 的三个键:target(默认 '_self')、messageBox(默认 true)、messageBoxPlain(默认 false),就是控件属性 ValidateTarget / ValidateMessageBox / ValidateMessageBoxPlain 去掉 Validate 前缀。
    • 旧的位置参数写法继续支持,无需修改现有代码。
    • 大多数场景其实不用手写它:按钮、工具图标、菜单项、链接按钮都有 ValidateForms 属性,点击时自动验证。
  • [Core/Pro] 新增全局配置项 AllowDangerousScriptTag(默认 true),用来关闭「在控件文案里嵌 <script>,由服务端拼成 JS 表达式」这个旧写法。推荐设为 false。
    • 旧写法把文案和代码挤在同一个参数里,服务端只能靠 <script> 标签猜哪段是代码。文案里一旦拼进用户输入,用户输入里的 <script> 也会被当代码执行,就是 XSS。
      • 例如 Alert.Show("无效参数:" + Request.Query["id"]),访问 ?id=<script>eval(name)</script> 即可执行任意脚本。
      • 受影响入口:Alert/Confirm/Notify/Prompt 的 Message、按钮的 ConfirmText、Window.GetShowReference 的地址。
    • 推荐写法:动态取值放客户端。控件标签里用 <Listeners> 声明事件处理函数名,函数体写在页面脚本里,选中行数在那里取;要回发就用自定义回发,把客户端已经拿到的选中行标识一起送上去。
      • 之前:btnDelete.ConfirmTextRawHtml = new RawHtml("确定要删除选中的 <b><script>{0}</script></b> 行吗?", Grid1.GetSelectedCountReference());
      • 现在:页面脚本里 F.confirm({ message: F.rawHtml("确定要删除选中的 <b>" + rows.length + "</b> 行吗?"), ok: function () { F.doPostBack({ eventName: "DeleteRows", eventArgument: rows }); } });
        • 三产品统一用简化形式 F.customEvent("Grid1_DeleteRows", { rowIDs: rows }),服务端写 Page_CustomEvent 接。按钮上的 OnClick 与 ConfirmText 都要去掉。
        • 自定义回发不带 ValidateForms 与回发前禁用按钮这两个内置特性,需要自己实现。
    • 已改造的示例:
      • 表格/表格与表单/表格与表单(弹出窗体,表单中保存)
      • 表格/单元格编辑/新增删除/服务器端删除行
      • 布局/内联框架/表格与编辑窗体;表格与编辑窗体(手工实现行命令);触发器输入框(传值)
      • 其他/第三方组件/WebUploader/WebUploader;WebUploader(取消上传)
  • [Core] 新增 UIHelper.AbortPage(...):在页面处理器里 return 它即可中止本次请求——不渲染业务页面,改渲染一个零控件的极简页面,用标准对话框把消息呈现给用户。
    • 取代 return Content("无效参数!") 这类做法(后者输出一张纯文本页,在 IFrame 窗体里既无样式也关不掉窗体)。
    • 新增示例:其他/数据模型/表单控件(编辑实体),演示按主键加载实体(主键无效时终止请求)、保存时重新读出实体,并只覆盖表单字段。
    • [Java] 新增 AbortPageException 异常类:在 Page_Get 里 throw 它即中止本次请求,行为与 Core 的 UIHelper.AbortPage 一致。
  • [JS] Window 新增公开方法 setSaveStateControlIds(ids):设置窗体关闭时把值回写到哪些控件,供页面脚本调用(此前只能去写内部属性)。

变更

  • [Core/Pro] 产品名称、程序集和 C# 公共命名空间统一为 FineUI.Core、FineUI.Pro(不兼容提醒)。
    • 原 FineUICore / FineUIPro 命名空间及程序集不再提供;v16 的官方 NuGet 包 ID 分别为 FineUI.Core / FineUI.Pro。
    • 升级时请替换程序集或包引用,更新 using、类型全名及配置中的程序集和类型名;Core 项目还需更新 Razor Tag Helper 的程序集引用,然后重新编译。新旧程序集不能在同一项目中混用。
  • [Core] AddFineUI 自动登记 JArray/JObject 模型绑定器,并在启用 RazorForms 时登记所需过滤器;Startup 不再需要手工添加这两项。
    • 老项目保留原手工登记也不会重复执行,建议升级后删除以简化 Startup;AddNewtonsoftJson 仍需保留。
  • [Java] 普通控件回发不再向浏览器传输后台方法名。
    • 浏览器只提交控件 ID、客户端事件名和业务参数,服务端根据本次页面声明找到对应处理方法。
    • 回发地址保留页面原有的查询参数,Page_Load 和事件处理函数仍可读取页面上下文。
  • [Java] @HiddenProperty 与 [Core]的 [HiddenProperty] 统一为同一套机制:值打包成一个不透明字符串,由隐藏字段 __FHIDDEN 搬运。
    • 浏览器只搬运、不解析它,因此数值不会被 JavaScript 的 double 降级;long 与 decimal 逐位往返。
    • Java 端不再为每个隐藏属性生成一个内部 HiddenField 控件,页面上少了一批 DOM 节点与回发状态节点。
  • [Java/Core/Pro] 客户端点击事件统一使用 ClickHandler,OnClientClick 仅保留旧项目兼容。
    • 推荐写法:在页面脚本区声明 function onXxxClick(event) { ... },控件属性只填写函数名(Pro/Core 用 ClickHandler,Core-MVC 用 .ClickHandler("..."),Java 用 click-handler)。OnClick / on-click 仍表示服务端事件,不要混淆。
    • Button、Tool、菜单项、LinkButton、HyperLink 和 TreeNode 均支持 ClickHandler;除 TreeNode 外,它与 click Listener 等价,同一控件不要同时声明两种写法。
    • 普通控件的 ClickHandler 可在回发中修改,置空即可移除;TreeNode 随节点数据下发,暂不支持运行时增量修改。TreeNode 回调参数为 (event, nodeId),其他控件为 (event)。
    • 点击顺序统一为:表单验证 → beforeclick → OnClientClick(兼容)→ click(含 ClickHandler)→ 确认框与回发。表单验证不通过时不再触发 click;要取消后续动作必须显式 return false。
    • 不兼容提醒:Core/Pro 的 OnClientClick 从 click 之后提前到 click 之前;ClickHandler 返回 false 不会阻止已经执行的 OnClientClick,OnClientClick 返回 false 则会阻止后续 click。菜单项迁移时还需检查父菜单 ItemClick 的差异。
    • [Java] 旧 on-client-click 仍需客户端动态编译,严格 CSP 页面要额外允许 unsafe-eval;ClickHandler 只传函数名,不需要 unsafe-eval。旧脚本只允许开发者可信代码,不能拼入用户输入。
    • [Core/Pro] LinkButton / HyperLink 不再生成 onclick / data-onclick 属性,严格 CSP 下可正常点击;LinkButton 会阻止占位地址的默认导航,HyperLink 仅在处理函数返回 false 时阻止真实导航。
    • [示例] F.js、FineUI.Pro、FineUI.Core(三种开发模式)、FineUI.Java 和五个空项目模板已全部移除 OnClientClick;表格行按钮迁移到 RenderField.Commands,其他客户端点击统一为具名函数。
    • TreeNode.OnClientClick 已标记为“只警告”的过时 API;其他 OnClientClick 本版本继续兼容且不产生过时警告,后续版本再逐步处理。
  • [Pro/Core-RazorForms/Java] 页面级自定义回发统一使用 F.customEvent 与 Page_CustomEvent。
    • Pro 补齐 F.customEvent、F.doPostBack 和 Page_CustomEvent,调用方式与 Core/Java 一致。
    • 支持字符串、数字、布尔值、对象和数组参数;对象与数组自动转换为 JSON。
    • PageManager.CustomEvent(ASPX:OnCustomEvent)与 GetCustomEventReference 已废弃,旧代码仍可运行;新代码使用页面具名函数和 Page_CustomEvent。
  • [Pro/Core-RazorForms/Java] 普通控件回发参数统一以 F.js 客户端事件名开头。
    • 无业务参数只发送 click、change 等事件名;Grid、Tree、Window 的参数格式三端一致。
    • Pro 不再兼容空默认参数和 Command、Page、ClearIcon 等旧标识;Java 首发即采用新协议。
    • Core RazorForms 会检查事件名非空和业务参数格式;Java 还会按本次服务端声明严格校验事件映射。
  • [Core] RazorForms 开发模式下,点击时要给后台带额外数据,推荐用自定义回发(ClickHandler + F.customEvent),不再用 OnClickParameter1..N。
    • 以前:<f:Button ID="Button1" OnClick="Button1_Click" OnClickParameter1="@(new Parameter("inputs", "getUserInputs()"))" />,后台用 Request.Form["inputs"] 取。
    • 现在:<f:Button ID="Button1" ClickHandler="onGetInputsClick" />,页面脚本里写 function onGetInputsClick(event) { F.customEvent('GetInputs', getUserInputs()); },后台用 Page_CustomEvent 按 e.EventName 分派、e.EventArguments 取参。
    • RazorForms 的控件状态本来就自动往返,需要手工回传的只剩客户端临时算出来的那点数据;MVC 与 RazorPages 不受影响,继续使用 OnClickParameter。
    • 需要让数据落进 [BindProperty] 时改用完整形式:F.doPostBack({ eventName: 'GetInputs', params: { inputs: getUserInputs() } }),params 里的键值会作为命名表单字段发出。
    • 已改造的示例:表格/编辑框 全部 10 个;表格/树表格/复选框 全部 8 个。
    • [Core/Java] 自定义回发的发起方式推荐 F.doPostBack,不再用历史全局函数 __doPostBack(后者继续可用);示例「自定义回发(__doPostBack)」与「复选框列(自定义选中改变事件,__doPostBack)」已改名并改用新写法。
  • [Core] 最低运行环境提高到 .NET 8(不兼容提醒!)。
    • 以前:类库编译到 .NET Core 3.1,能被 .NET Core 3.1 以上的项目引用。
    • 现在:类库编译到 .NET 8,能被 .NET 8 以上的项目引用(.NET 8 / 9 / 10 都可以)。
    • 升级影响:项目还跑在 .NET 6 / 7 上的话,升级后会加载不了类库;这两个版本微软已分别于 2024 年 11 月、2024 年 5 月停止支持,请先把项目升到 .NET 8 以上。
    • 项目本身不需要改代码:空项目模板一直就是 .NET 8。
    • 同时 Newtonsoft.Json 依赖从 12.x 提高到 13.x:项目里若还引用 Microsoft.AspNetCore.Mvc.NewtonsoftJson 3.1.x(或直接引用 Newtonsoft.Json 12.x 及更低版本),请一并升级,否则运行时会报找不到 Newtonsoft.Json 13.0.0.0。
    • .NET Core 3.1 微软已于 2022 年 12 月停止支持,继续编译到这个版本既拿不到新的语言特性,也看不到框架给出的过时 API 提示。
  • 示例项目的图片下载与文件下载合并为一个公共下载入口。
    • 以前两个入口:图片走 ImageDownload(只发图片、内联显示),WebUploader 的文件走上传端点自己的下载分支(只发附件);两边的空参、非法文件名、路径穿越、文件不存在这几道校验是各写一份的。
    • 现在一个入口:[Core]MVC 为 HomeController.Download、RazorForms/RazorPages 为 Home/Download;[Pro]common/Download.ashx;[Java]/home/download。
    • 是内联显示还是附件下载,由调用方声明,不按文件扩展名猜。
      • 基类的 GetImageUrl 生成的地址带 inline=1,只有扩展名在图片白名单内才按 image/xxx 内联显示;
      • 新增的 GetFileUrl 不带 inline,一律 application/octet-stream + 附件下载;
      • 白名单以外的扩展名(.html/.svg 等)即使地址上写了 inline=1 也会降级成附件下载,浏览器永远不会把它当页面渲染。
    • 上传仍是各自的逻辑:图片上传保留“扩展名白名单 + 2MB”,WebUploader 仍接受任意文件(它演示的就是任意文件上传,安全性由“存在 web 根之外 + 强制下载”保证)。
    • 顺带修掉:WebUploader 上传端点收到 GET 时返回 500,现在一律 404(四套一致)。
  • [JS] 客户端 AJAX 请求引擎由 jQuery.ajax 切换为浏览器原生 fetch(对已有用法透明,无需改动业务代码)。
  • [JS] 统一手风琴、可折叠面板、表格(行分组、行内展开列)、树控件的展开/折叠动画机制。
    • 状态切换立即生效、动画只做视觉过渡。修复连续快速操作时动画彼此打断、遗留残余样式、动画未触发时清理逻辑不执行等一系列问题。

废弃

  • [Core] 配置项 ExcludedURLs(不使用 FineUI 中间件的网址列表)已废弃,不再需要配置。
    • 中间件现在只缓冲 HTML 响应,图片、Excel、文件流等直接发出,大文件下载与图片输出的内存占用大幅下降(下载一个 200MB 的文件,以前进程内存要多占几百 MB,现在几乎不多占)。
    • 已有配置继续生效,无需修改;示例项目里已全部删除。
  • [Pro] 单元格编辑的改动读取统一到 GetModifiedData(),GetModifiedDict / GetDeletedList / GetNewAddedList 标记为过时(不兼容提醒)。
    • 推荐写法:一个 foreach 遍历 GetModifiedData(),按 status(modified / newadded / deleted)分支处理;每条记录自带 id,不必再用行索引去 DataKeys 里换主键(表格也就不需要为此声明 DataKeyNames 了)。
    • 三个老方法仍可继续使用(只会产生编译警告);FineUI.Core 与 FineUI.Java 一直只提供 GetModifiedData(),统一之后三个产品的写法一致。
    • 注意:数字列被清空时客户端传的是空字符串(老方法由库内部转成 DBNull),写回数据库前需自行处理,可参考示例页的 UpdateDataRow。

移除

  • 移除对 IE 浏览器的支持,清理全部 IE 遗留兼容代码(不兼容提醒!)。
    • 移除公开 API:F.isIE8() / F.isIE89() / F.isIE9() / F.isIE()。
    • 升级影响:支持所有现代浏览器(Chrome、Edge、Firefox、Safari),但不支持任何版本的 IE 浏览器。

修复

  • [Java] 修复程序化动态控件的普通属性没有完整跨回发恢复的问题。
  • [示例] 修正示例:两个触发器输入框(清空图标)——输入停顿一秒后的自动搜索一直没生效(脚本里引用了一个不存在的变量)。
  • [Pro] 修复 EnableFStateValidation(__FSTATE 防篡改校验)实际不设防的问题(不兼容提醒!)。
    • 这个配置项给控件状态加一道服务器端签名:状态随页面发到浏览器,回发时服务端重算签名比对,对不上就判定被篡改。它成立的前提是密钥只有服务器端知道。
    • 现在密钥改用 web.config 里 <machineKey> 的 validationKey(ASP.NET 给 ViewState 防篡改用的就是它)。
      • 升级影响:开启此属性的项目必须显式配置 <machineKey>,不能用 AutoGenerate;没配会在页面渲染时直接报错并说明怎么配。默认关闭,没开这个开关的项目不受影响。
      • Web 农场须各节点配置成同一个 validationKey,否则一台机器发出的页面到另一台回发会被误判为篡改。
      • 配了 IsolateApps 时会把应用自身路径掺进密钥,避免共享主机上多个应用共用同一份 machineKey 时互相伪造。
  • [JS] 修复表格列数很多(数百列)且使用多级分组表头时,表头竖线与表体竖线错位、越往右错位越大的问题(STARK11号 - https://t.zsxq.com/sJSCQ)。
    • 根因:分组表头的 colspan 单元格会触发浏览器对表头表格做亚像素精度的列宽重分配,随列数累积,使表头列边界与表体错开(与锁定列、列分割线无关)。
    • 修复:表头渲染后量出每个叶子列的实际渲染宽度,把表体/合计表的 colgroup 换成这组实测宽度,两表逐列精确对齐;新增 JS 示例:表格/多表头/多表头(大量列,列线对齐)。
    • 测量叶子表头自身宽度,避免横向滚动后锁定列的屏幕位置污染校准;六套大量列示例改为月份分组内部混合列宽。
    • 注意:v15.2.0 里曾发布过本问题的不完整修复(给分组表头单元格显式设宽),只在各列等宽时有效;列宽不一时错位仍在,本次为彻底修复。
  • [四栈] 新增表格 ColumnID 全树唯一性检查,覆盖分组列、叶子列和隐藏列。F/Pro/Core 默认仅在控制台警告并保留显式 ID,兼容已有页面;全局启用 gridStrictColumnIdValidation(Pro/Core:GridStrictColumnIDValidation)后抛出配置异常。Java 始终严格检查,不提供兼容开关。缺省 ID 自动避让,严格模式下失败的列更新保留原列。
  • [Core] 修复单元格编辑时清空数字列再保存会报服务端错误的问题。
    • 现象:把一个 FieldType 为 Int/Float/Double 且未标 Required 的单元格清空后保存,客户端上传的是空字符串,写进数字类型的 DataTable 列抛 ArgumentException,整个回发 500。
    • 四套示例的 UpdateDataRow 统一成同一份写法:列不是字符串类型时把空串存为 DBNull,判据取自列类型(rowData.Table.Columns[列名].DataType)而不是当前值的类型。
  • [Pro] 修复页面上放了文件上传控件(FileUpload)时,回发之后表格的列过滤菜单弹不出来的问题(50871362)。
    • 修复:文件上传专用的隐藏表单改在执行回发脚本之前就移除;同时收紧控件渲染容器(renderTo)的解析——选择器命中多个元素时只取第一个。
  • [Core] 修复通过后台代码修改 Accordion(手风琴)的 ActivePaneIndex 属性后,AJAX 回发不会同步激活客户端对应面板的问题。
  • 统一超链接(HyperLink)与链接按钮(LinkButton)禁用后的行为,并修复超链接禁用后仍可点击跳转的问题。
    • 禁用状态下链接不可点击、也不能被键盘 Tab 选中;重新启用后链接地址与点击行为自动恢复。
    • 此前超链接禁用后点击仍会跳转,链接按钮的禁用表现在不同版本间也不一致,现已统一。
    • [Core] 通过 AJAX 清空超链接地址时不再残留空的 href 属性(与首屏渲染及其他版本保持一致)。
  • [Core-RazorForms] 页面过滤器(IAsyncPageFilter)短路后,不再执行控件的事件处理函数。
    • 适用于把权限校验一类的守卫写成页面过滤器的场景:过滤器不调用 next、自行设置 Result 时,页面处理器本来就没有执行,控件的事件处理函数(如 btnSave_Click)也不应再执行。
    • 此前这种情况下事件处理函数仍会被调用,表现为响应里是过滤器给出的提示、但事件里的写操作已经发生。
    • 建议同时给这类守卫过滤器实现 IOrderedFilter 并给一个负的 Order,让它排在框架的全局过滤器之前,整条页面流水线一步都不走。
v15.2.0 #217
  • [安全] 修复 <raw> 标签的 XSS 隐患,换用更安全的方式输出"可信的 HTML"。
    • 老问题:以前想让一段文本按 HTML 显示(不转义),要写成 <raw>...</raw>。但如果这段文本来自用户输入或数据库,就能塞进 <script> 等脚本代码,存在 XSS 风险。
    • 新思路:把"这段 HTML 可信"的判断交给开发者在代码里明确声明,用户填的普通文本永远不会被当成可信 HTML,从根上堵住了这个漏洞。
    • 新增声明"可信 HTML"的写法,用来替代 <raw>。
      • 后台 C#:用 new RawHtml("<b>...</b>") 包裹一下;给控件赋值如 Label1.TextRawHtml = new RawHtml(...);弹出消息框如 Alert.Show(new RawHtml(...))。
      • 页面标签:Pro 版用单属性 TextRawHtml="<b>加粗</b>",Core 版用 _TextRawHtml="<b>加粗</b>",或者 TextRawHtml="@(new RawHtml("<b>加粗</b>"))"。
      • 前端 JS:用 F.rawHtml('<b>...</b>') 传给文本、标题、提示等属性。
    • 新增全局开关,可彻底关闭 <raw> 老写法。
      • 新增配置项 AllowDangerousRawTag,默认开启(兼容老代码);推荐设为 false 把 <raw> 彻底关掉,更安全。
      • 关闭后页面里若还残留 <raw>,浏览器控制台会提醒一次,引导你改用新的写法。
    • 示例项目已全部从 <raw> 改为新写法,并默认关闭了 <raw>,作为安全配置的范例。
    • 新增文档:FineUICore开发指南/安全策略(HTML 编码);安全策略(用 RawHtml / F.rawHtml 安全输出 HTML)。
  • [安全] 修复可信 HTML(F.rawHtml / RawHtml)在跨窗口(iframe)弹层中显示为 [object Object] 的问题(腊八 - https://t.zsxq.com/onDJF)。
    • 现象:iframe 子页里用 F.alert / F.confirm 等弹到父/顶层窗口(target 为 _parent、_top),且标题或内容用 F.rawHtml(...) 声明可信 HTML 时,显示成了 [object Object]。
    • 根源:框架靠 instanceof 判断是不是可信 HTML,而 instanceof 按窗口隔离——子窗口创建的对象拿到父窗口判断会失败,于是被当普通对象输出。同窗口(不带 target)不受影响。
    • 修复:改用不受窗口隔离的判断方式,跨窗口也能正确识别;用户输入仍无法伪造,安全性不降级。
  • [安全] 菜单项文本改为默认 HTML 编码,之前把 HTML 写进菜单项文本的用法需要显式声明为“可信 HTML”(不兼容提醒!)。
    • 涉及控件:菜单项(MenuItem)、菜单按钮(MenuButton)、菜单超链接(MenuHyperLink)、菜单文本(MenuText)、菜单复选框(MenuCheckBox)。
    • 现象:升级后,菜单项文本里写的 HTML(如 <div>、<span>、<i>、<img> 等)不再按 HTML 渲染,而是被当成普通文字原样显示出来。
    • 原因:本次安全加固把菜单项文本纳入“默认 HTML 编码、开发者确信可信时才原样输出”的统一机制;而菜单项文本此前是“首次渲染时原样输出、不编码”的历史特例,故受影响。
    • 升级办法(仅对开发者确信可信的 HTML 这样声明;来自用户输入/数据库的内容不要声明,让其保持 HTML 编码):
      • [JS] 把 text: '<i>...</i>' 改为 text: F.rawHtml('<i>...</i>')。
      • [Pro] 标签写法把 Text="<i>...</i>" 改为 TextRawHtml="<i>...</i>";后置代码把 xxx.Text = "..." 改为 xxx.TextRawHtml = new RawHtml("...")。
      • [Core] 把 .Text("<i>...</i>") 改为 .TextRawHtml(new RawHtml("<i>...</i>"));标签写法(RazorForms/RazorPages)把 Text="<i>...</i>" 改为 _TextRawHtml="<i>...</i>"(或 TextRawHtml="@(new RawHtml("..."))")。
  • [安全] 单选框列表、复选框列表的列表项支持声明“可信 HTML”(谭晓 - https://t.zsxq.com/uDDcT)。
    • 列表项 RadioItem / CheckItem 新增 TextRaw、TextRawHtml 属性;列表控件新增 DataTextRaw 属性——数据绑定(DataBind)时把整列文本按可信 HTML 输出。
    • 三种数据绑定方式在功能和安全性方面等价(DataSource + DataBind + DataTextRaw、OnItemDataBound、手工构建 Items),模版内的数据字段需要自行 HtmlEncode 转义。
    • 新增示例:表单控件/选择框/单选框列表(图标选项,可信 HTML)。
  • 表格分页工具栏新增窄屏自动简洁分页模式(251081362)。
    • [JS] 为表格新增pagerAutoSimpleMode属性
    • [JS] 新增全局配置项gridPagerAutoSimpleMode;分页栏宽度不足时,完整分页器自动降级为“上一页 + 当前/总页 + 下一页”的简洁形态,拉宽后自动还原(默认关闭)。
    • [Core/Pro] 为Grid新增PagerAutoSimpleMode属性,为PageManager和配置文件新增全局配置项GridPagerAutoSimpleMode。
    • 新增示例:表格/分页工具栏/窄屏自动简洁分页。
  • 表格新增客户端事件displaytypechange,显示模式(表格/卡片)切换时触发(木南 - https://t.zsxq.com/iDJb3)。
    • [JS] 签名(event, newDisplayType, oldDisplayType);窄屏自动切换(responsiveCardSize)与手动 setDisplayType 均会触发。
    • [Core/Pro] 该客户端事件通过Listeners集合挂接,无需新增控件属性。
  • 修复可编辑日期列在 storeDateAsString=false 时,用鼠标重选“同一天”(未做任何修改)也被误标记为已修改(左上角小红标)的问题(wmdyj - https://t.zsxq.com/0y50E)。
    • [JS] 根源是时区换算:日期列(FieldType=Date)的值,服务端按 UTC 时间下发(形如 2026-06-19T00:00:00+00:00,即 UTC 的零点),浏览器换算成本地时间后,在东八区就变成了当天的 08:00;
    • [JS] ...而用户点日历重新选一次,选出来的是当天的本地 00:00。同样是 6 月 19 日,一个带 08:00、一个是 00:00,框架按“时间戳”判断两者是否相等时就认为变了,于是误判为已修改。
    • [JS] 修复:日期列“是否被修改”的判定,改为按“列显示的日期格式”比较(本例只显示到“天”,08:00 与 00:00 都算同一天,故不再误判);对含时分秒的日期时间列仍按完整时间比较,能正确识别真正的修改。
    • 新增示例:表格控件/单元格编辑/杂项/日期列编辑(重选同一天不标记为已修改)。
  • 日期范围选择支持左右面板独立切换(放牛娃 - https://t.zsxq.com/OcLDq)。
    • [JS] 为Calendar和DatePicker新增independentRangePanels属性,默认关闭。
    • [JS] 开启后,日期范围(DayRange)、月份范围(MonthRange)、年份范围(YearRange)的左右两个面板可以分别切换,不再强制相邻;左右非法交叉时相关导航按钮自动禁用。
    • [Core/Pro] 为Calendar和DatePicker新增IndependentRangePanels属性。
    • [JS] 统一浮层的落点位置:点击左右任一面板标题栏的年份/月份,浮层在该面板就地展开(独立与联动两种模式行为一致)。
    • 新增示例:日期控件/日期范围控件/日期范围(范围面板独立切换)。
  • 修复点击日期控件标题栏的年份/月份时,弹出浮层里高亮的年月不正确的问题。
    • 浮层里的年/月网格本质是“跳转导航”,应该高亮正在显示的年/月,而不是当前Calendar选中的日期。
    • 高亮改用独立的浅描边样式(弱于选中项的焦点样式),避免与“选中的日期”混淆。
  • 增强示例项目的文件上传校验,作为安全上传的范例。
    • 在示例基类中新增 ValidateUploadFile 方法,统一做“扩展名白名单 + 文件大小上限(默认 2MB)”校验,并返回具体的失败原因(类型不符 / 文件过大 / 文件为空)。
    • 所有上传示例调用基类的公共方法;多文件上传示例改为逐个校验、跳过非法文件并汇总提示。
    • 上传图片回显改为“三步安全上传”范例。
      • 扩展名白名单校验;
      • 用时间戳重命名,避免覆盖与文件名注入;
      • 文件保存到不可直接访问的目录,再通过公共下载处理器带正确的 Content-Type 输出(即使上传了 .html 等文件,浏览器也永远不会把它当页面/脚本执行)。
        • [Core] 保存到 wwwroot 之外的 App_Data/upload
        • [Pro] 保存到 App_Data/upload
    • 新增公共图片下载入口(图片扩展名白名单 + 防路径穿越 + 正确 Content-Type),基类新增 SaveUploadFile / GetImageUrl 辅助方法。
      • [Core] MVC 为 HomeController.ImageDownload
      • [Core] RazorForms/RazorPages 为 Home/ImageDownload
      • [Pro] common/ImageDownload.ashx
  • [Pro] 修复 DropDownList 空值项(Value="")导致的任意回发都会误触发 SelectedIndexChanged 的问题(曹** - https://t.zsxq.com/NUdIK)。
  • 表格新增列属性 showTooltipWhenTruncated:单元格提示仅在文本被列宽截断(出现省略号)时才显示,未截断的短内容鼠标悬停不弹提示(默认 false,向后兼容)(笑疯 - https://t.zsxq.com/pMPsE)。
    • 需配合 showTooltip=true,仅 tooltipType=qtip 时有效(title 类型走浏览器原生、无法根据单元格文本是否被截断动态显示)。
    • [JS] 提示属性仍放在内层 <span> 上并追加 data-qtip-truncated 标记,鼠标悬停时检测省略号容器(.f-grid-cell-text)的宽度以决定是否弹出提示;
    • [Core/Pro] 新增全局配置项 GridShowTooltipWhenTruncated 与列属性 ShowTooltipWhenTruncated,支持 PageManager 全局设置、表格级别和列级别的覆盖。
    • 新增示例:表格/杂项/提示信息(仅截断时显示)。
  • 修复下拉列表多选标签模式的输入框在紧凑/小字体/大字体/大间距等显示模式下高度不正确的问题(Hui - https://t.zsxq.com/FInyc)。
  • 修复日期/时间控件在 Firefox 浏览器下(Chrome 正常)时间选择列(时/分/秒)显示为空白的问题(herry)。
  • 修复 Firefox 浏览器下下拉列表、菜单、日历等区域的自定义细滚动条未生效(显示为浏览器默认滚动条)的问题。
  • 修复表格(例如右侧锁定列)数据由多变少、纵向滚动条消失后,表头仍残留滚动条补偿宽度(margin-right),导致表头与数据列错位的问题(stone磊 - https://t.zsxq.com/DeTXZ)。
v15.0.3 #216
  • [JS] 修正 esbuild 编译目标为 es2022,导致打包后的产物保留 ES2020 新语法(空值合并 ??),在旧版 WebView 解析阶段出错(Alan - https://t.zsxq.com/mG100) 。
    • 修复:编译目标(bundle 与 minify)统一降为 es2015。
  • [JS] 修正老版本Chrome浏览器可能报错 e.columnId.replaceAll is not a function。
    • esbuild 编译目标只降级新语法,不会自动补齐运行时新增的方法/静态函数;老旧 WebView 调用 String.prototype.replaceAll 等方法时报错。
    • 修复:新增运行时 polyfill,补齐 replaceAll、Array.includes、Object.values/entries、padStart/padEnd 等 ES2015+ 运行时 API,兼容旧内核。
v15.0.1 #215
  • 表格数据为空时的空状态改为可选(STARK11号 - https://t.zsxq.com/KKGn0)。
    • 新增全局配置项GridShowEmptyIconText(默认false,兼容之前的空白行为)。
    • 表格数据源为空时,显示效果:优先显示用户自定义的EmptyText文本,其次显示预定义图标+文案(ShowEmptyIconText=true),都不满足时显示空白。
    • [Core/Pro] PageManager与Web.config新增全局配置项GridShowEmptyIconText(默认false),一处开启即可让所有表格默认显示内置空状态。
    • 新增示例:表格/杂项/表格为空的提示(内置图标和文案)。
  • 修正表格锁定列在特定情况下出现的JS问题(一鸣 - https://t.zsxq.com/PA6dg)。
v15.0.0 #214
  • [Core/Pro] 新增泛域名服务器授权,兼容之前的IP+MAC授权方式(企业版)。
    • 生成的授权文件名类似:FineUICore.lic.dep.domain.fineui_com.config
  • 修复表格拖拽改变列宽度后,mouseup触发的click事件误触发列排序的问题(6976935)。
  • Button新增语义颜色:Success(绿色)、Danger(红色)、Warning(橙色)、Info(蓝色),不随主题变化。
  • 主题系统从SCSS预编译重构为CSS自定义属性(CSS Variables)。
    • 消除Sass编译依赖,不再需要sass等构建工具,并新增generate-theme.js构建脚本。
    • 每个主题由theme.config(颜色配置)+ theme-extra.css(可选特有样式)定义。
    • 自定义主题:在res/themes/下新建目录,复制已有theme.config修改颜色值,双击generate-theme.bat即可生成自定义主题。
  • 重构内部构建脚本。
    • 内部打包bat脚本改为npm脚本编排。
    • 使用esbuild统一替代terser(JS压缩混淆)和clean-css-cli(CSS压缩)。
    • FineUI.js组件层迁移到 ESM 模块化(不影响对外 API)。
    • ES2022 class extends 替代 John Resig Class.js(不影响对外 API)。
  • 为Notify新增DisplayProgress属性(bool?),用于关闭倒计时进度条显示(曹** - https://t.zsxq.com/ieFiS)。
    • 为Web.config和PageManager增加全局配置项(MessageBoxDisplayProgress=true)。
  • 修正表格在SummaryPosition=Top/Bottom模式下,数据为空时合计行虽隐藏但仍占据布局空间,导致Grid底部出现空白带的问题。
  • 表格数据为空且未设置EmptyText时,显示内置的「图标+文案」空状态。
  • 表格新增ResponsiveCardSize属性:屏幕宽度小于指定断点时表格自动切换为卡片模式。
    • [JS] 为表格新增属性responsiveCardSize,取值 sm(<768px)/md(<992px)/lg(<1200px);
    • [Core/Pro] 新增枚举属性 GridResponsiveCardSize(None/SM/MD/LG)。
    • 新增示例:布局/Block/响应式表格,并同步改造「响应式首页」中的表格示例。
  • 表格新增ShowPageSizeSelector属性(分页工具栏内置页大小下拉选择器)。
    • [JS] 新增属性showPageSizeSelector(默认false)、pageSizeOptions(默认[10,20,50,100])、pageSizeText(标签文字)、pageSizeChangeResetsPageIndex(默认true)。
    • [JS] 新增客户端事件pagesizechange/beforepagesizechange,签名(event, newPageSize, oldPageSize)。
    • [Core/Pro] 新增相应属性和事件。
    • 新增示例:表格/分页工具栏/页大小选择器(内存分页)、表格/分页工具栏/页大小选择器(数据库分页)。
  • 安全增加。
    • [Core/Pro] 修正当页面的内联脚本中包含</script>字符串时,页面脚本被浏览器提前截断、导致页面无法打开的问题(木南 - https://t.zsxq.com/6KiBx)。
    • 修正表格单元格提示会把单元格数据当作 HTML 处理、导致鼠标悬停时可能执行其中脚本的安全问题(默认启用列的HTML编码,也就是 EncodeText=true)。
    • 当禁用控件的HTML编码(EncodeText=false,涉及 Label、Tree、Menu、Toolbar、Button、表格单元格及提示等),自动移除其中的 <script> 标签作为安全防护。
    • 修正示例项目中展示用户提交/修改数据(如表格 ModifiedData、FilteredData 等)时未做 HTML 编码、直接输出到页面而存在 XSS 风险的问题。
      • 统一改为编码后输出(Pro/Core示例项目的基类新增 EncodeJson 方法、JS端 init.js 新增 encodeJson 方法)。

RELEASE ARCHIVE

历史版本