☰
C#+EasyUI实战:增删改查、Excel导出与文件上传避坑指南
2026/10/8 8:29:52 网站建设 项目流程

简介:一套基于C#与EasyUI的Web管理功能示例工程,面向需要快速掌握MVC+EasyUI增删改查、分页、导入导出及文件上传的初中级.NET开发者。工程使用VS2013编译,采用MVC+easyui+sqlserver2014架构,围绕单张数据表展示一套完整的业务操作闭环:页面采用EasyUI布局,表格使用datagrid展示与分页,后端提供新增、修改、删除接口,并集成Excel导出与图片上传功能。分页默认基于sqlserver2012关键字实现,同时源码中保留2005/2008数据库的兼容分页方法(UserInfoDAL.cs中的getPage2005),遇到低版本数据库可直接替换调用。压缩包为rar格式,大小28.54MB,内含完整工程源码、前端页面与数据库相关配置,解压后在VS中转换版本即可调试运行,适合以此为基础扩展字段和业务逻辑。已有218人学习下载,可用作企业后台管理模块的参考模板,也适合课程设计或面试前动手练习,能直观理解EasyUI datagrid与后端数据交互、Excel导出及文件上传的常见实现思路。

1. C#+EasyUI这套组合还被项目追着用:先分清谁管数据谁管界面

接手一个内部管理系统,需求单上往往就是这句话:“做几个页面,能新增、修改、删除,数据能导出Excel,还要支持上传附件。”一眼看上去不复杂,但真动手就知道,麻烦全在前后端的对接格式上:表格怎么翻页、表单怎么回填、Excel怎么不出现乱码、上传的配置怎么在IIS和ASP.NET之间不打架。C#提供接口和数据处理能力,EasyUI负责把表格、对话框、表单这些交互元素直接渲染出来,两者分工明确,在C#上位机和中小型管理系统里是出现频率很高的一套组合。这篇内容顺着需求单上的五件事,把方案讲成可以直接照做的步骤:新手能跟得上,熟手可以对照参数和边界条件做取舍,不用再去翻零散的片段。

2. 搭出能用的数据表格:URL绑定、JSON返回格式与增删改的接口链路

2.1 架构上先定规矩:后端只出JSON,EasyUI只负责渲染

做这类需求时我的固定做法是后端用一般处理程序(.ashx)或MVC的Controller输出JSON字符串,前端用EasyUI的datagrid、dialog、form三个组件对接。不建议用WebForms的服务器控件去做交互,因为回发机制把数据格式和页面状态搅在一起,后续接查询、接导出都要额外绕路。选择ashx还是Controller,看项目底子:老项目很多已经是ashx,新项目直接上Controller也没什么学习成本。

EasyUI对后端几乎没有要求,只要接口能返回它认识的JSON。所以这套方案的核心不是框架本身,而是“你返回的JSON结构是否正确”。datagrid认识的结构是固定的:外层要有一个total表示总行数,内层用rows放数据数组。后端的每一行序列化成普通对象,字段名要和前端列定义的field一一对应。

2.2 表格加载与分页:page和rows这对参数是分页的生命线

先看前端初始化datagrid的最小配置。

$('#dg').datagrid({ url: 'list.ashx', method: 'post', pagination: true, pageSize: 20, pageList: [10, 20, 50, 100], fitColumns: true, singleSelect: false, columns: [[ { field: 'ck', checkbox: true }, { field: 'id', title: 'ID', width: 60 }, { field: 'name', title: '姓名', width: 100 }, { field: 'createTime', title: '创建时间', width: 140 } ]], queryParams: { keyword: '' } });

url指向后端接口,method必须写post。分页开启后,datagrid每次加载都会自动带上page和rows两个参数:page是第几页,rows是每页条数。这两个参数不是你想传才传,而是框架固定会传,后端必须按这两个名字取。queryParams里放的是附加查询条件,后面做搜索框时,把输入值塞进去即可。

后端一般处理程序里对应的取法是这样:

public void ProcessRequest(HttpContext context) { int page = int.Parse(context.Request["page"] ?? "1"); int rows = int.Parse(context.Request["rows"] ?? "20"); string keyword = context.Request["keyword"] ?? ""; int total = 0; DataTable dt = GetPageData(page, rows, keyword, out total); // 把DataTable转成List<Dictionary<string,object>>,日期在这里统一转成字符串 var list = new List<Dictionary<string, object>>(); foreach (DataRow row in dt.Rows) { var item = new Dictionary<string, object>(); foreach (DataColumn col in dt.Columns) { item[col.ColumnName] = row[col].ToString(); } list.Add(item); } var result = new { total = total, rows = list }; context.Response.ContentType = "application/json; charset=utf-8"; context.Response.Write(new JavaScriptSerializer().Serialize(result)); }

page和rows用字符串取出来再Parse,是因为EasyUI默认通过表单形式提交这两个参数。GetPageData是业务查询部分,按keyword拼条件,按page和rows做分页。total是带条件后的总记录数,这个值直接决定分页栏显示几页,取值错误会导致分页直接错乱,宁可多查一次count,也不要漏条件。

这里有几个细节值得注意:一是DataTable到Dictionary的转换过程中,DateTime默认会带着毫秒序列化,输出前我先ToString,既避免后面避坑章节里那个/Date(...)/问题,也顺便把格式调成“yyyy-MM-dd HH:mm:ss”。二是前端列定义中的field必须和后端返回的字段名完全一致,大小写也要一致,否则表格里永远空着。

2.3 新增对话框:dialog套form,提交时校验再发送

新增和修改我习惯共用一个对话框加一个表单,减少页面上的重复元素。结构如下。

<div id="dlg" class="easyui-dialog" title="编辑" style="width:420px;height:320px;padding:10px" >function save() { $('#fm').form('submit', { url: 'save.ashx', onSubmit: function () { return $(this).form('validate'); }, success: function (data) { var r = JSON.parse(data); if (r.success) { $('#dlg').dialog('close'); $('#dg').datagrid('reload'); } else { $.messager.alert('错误', r.msg); } } }); }

onSubmit里调用form('validate'),easyui-textbox上配了required:true的字段都会被检查,没填就不提交,这是最常见的表单拦截方式。后端save.ashx里只需要读Request.Form["id"]、Request.Form["name"]、Request.Form["remark"],然后按id是否有值决定执行Insert还是Update,最后返回{ success: true }这样的JSON。

这里有个常见误用:有些人直接用$('#fm').form('submit'),没写onSubmit校验,也没在success里做JSON.parse,导致后端返回的字符串被当成HTML处理,弹窗不关闭,表格也不刷新。success回调里拿到的data是字符串,不是对象,先parse再判断是基本盘。

2.4 修改回填:form('load', row)的前提是字段名对得上

修改操作的关键是回填。选中一行,把这一行的数据塞进表单。

function edit() { var row = $('#dg').datagrid('getSelected'); if (!row) { $.messager.alert('提示', '请先选择一行'); return; } $('#fm').form('clear'); $('#fm').form('load', row); $('#dlg').dialog('open').dialog('setTitle', '修改'); }

form('load', row)内部拿着row对象去匹配表单里的name,匹配上就赋值,匹配不上就空着。所以第2.2节里返回的字段名和这里的input name要约定好。比如返回的字段是createTime,表单里如果没有createTime这个name,那时间就不会显示在修改页里,这不算bug而是EasyUI的工作方式。

另外一个容易翻车的地方:如果行里某个字段值为0或false,form('load')可能不赋值,因为源码里对值做了空判断。处理办法是后端在返回JSON时就把这类字段转成字符串“0”“false”,或者回填时单独用textbox('setValue')处理。多数情况下我建议后端统一ToString,省得在前端逐个排查。

2.5 删除要区分单条和多选:getSelected与getSelections的差异

删除按钮常见做法是支持多选批量删除。datagrid开启singleSelect:false后,每行前面出现复选框。取选中行的接口有两个:getSelected只返回第一条选中行,getSelections返回数组。这就是为什么有人写完删除,测试时发现永远只删了一条。

function del() { var rows = $('#dg').datagrid('getSelections'); if (rows.length === 0) { $.messager.alert('提示', '请先选择要删除的记录'); return; } var ids = rows.map(function (row) { return row.id; }).join(','); $.messager.confirm('确认', '确定删除选中的 ' + rows.length + ' 条记录吗?', function (r) { if (!r) return; $.post('delete.ashx', { ids: ids }, function (data) { var d = JSON.parse(data); if (d.success) { $('#dg').datagrid('reload'); } else { $.messager.alert('错误', d.msg); } }, 'text'); }); }

后端delete.ashx按逗号拆分ids,拼成IN条件。这里有个安全细节:id如果是数字,拆分后逐个int.TryParse,过滤掉非法值再拼接SQL;id如果是字符串,至少要做参数化查询,不要把ids直接拼进SQL——这个接口开着会被批量删库。批量删除是给运维用的能力,不是给测试脚本用的玩具。

到这里,新增、修改、删除和数据展示已经串成一条完整的链路,下一步处理导出Excel。

3. 导出Excel:NPOI与EPPlus选型、流式输出和中文文件名编码

3.1 选型:NPOI是默认选项,EPPlus要注意授权

导出Excel在.NET生态里绕不开NPOI和EPPlus两个库。我一般默认用NPOI:免费、支持xls和xlsx两种格式、API稳定、社区资料多,中小型项目的导出需求用它都够。EPPlus操作体验更现代,但5.0之后改了商业授权条款,企业商用需要购买授权,个人项目或者已经确认license许可的可以用,否则容易给自己埋雷。选型的核心原则是:别为了一个“写单元格样式更顺手”的理由,把license问题带进公司项目。

还有一类做法是直接用Office COM组件导出Excel,这是我不推荐的方式:服务器上要装Office,并发导出时COM实例互相抢占,回收进程麻烦不说,翻车概率极高。NuGet上装NPOI包,代码里引用NPOI、NPOI.XSSF.UserModel两个命名空间即可。

3.2 核心代码:从DataTable生成xlsx并输出到响应流

导出功能一般不跟分页绑定,而是把当前查询条件下的所有数据一次性导出。后端代码分两步:查数据、写Excel。

public void ProcessRequest(HttpContext context) { DataTable dt = GetAllData(); // 按当前查询条件取全量数据 IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("数据"); // 表头 IRow headerRow = sheet.CreateRow(0); string[] columns = { "ID", "姓名", "备注", "创建时间" }; for (int i = 0; i < columns.Length; i++) { headerRow.CreateCell(i).SetCellValue(columns[i]); sheet.SetColumnWidth(i, columns[i].Length * 256 + 200); } // 数据行 for (int r = 0; r < dt.Rows.Count; r++) { IRow row = sheet.CreateRow(r + 1); row.CreateCell(0).SetCellValue(dt.Rows[r]["id"].ToString()); row.CreateCell(1).SetCellValue(dt.Rows[r]["name"].ToString()); row.CreateCell(2).SetCellValue(dt.Rows[r]["remark"].ToString()); row.CreateCell(3).SetCellValue(Convert.ToDateTime(dt.Rows[r]["createTime"]).ToString("yyyy-MM-dd HH:mm:ss")); } // 输出 string fileName = "用户数据_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx"; context.Response.Clear(); context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; context.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8)); using (MemoryStream ms = new MemoryStream()) { workbook.Write(ms); context.Response.BinaryWrite(ms.ToArray()); } context.Response.End(); }

XSSFWorkbook对应.xlsx格式,HSSFWorkbook对应旧版.xls格式,二选一,不要混。xls格式单sheet最多65535行,超出后要分sheet写入;xlsx这个上限是1048576行,一般业务很难触顶。SetColumnWidth设置列宽,单位是字符宽度的1/256,直接拿列名字符串长度算出来的宽度只是起步值,中文按两倍字符估算更接近实际效果,你可以按业务列的实际内容微调。

数据行用CreateCell逐个SetCellValue,单元格类型由写入的值自动推导。日期我建议先转成字符串写入,让Excel里显示的是“2025-06-12 10:30:00”这种可读格式,而不是一串序列号。最后用MemoryStream把workbook写进响应流,Response.End收尾,这是最常见的ashx导出写法。

3.3 中文文件名:不转码就会被浏览器截断

导出接口里最容易翻车的细节是文件名。直接写成attachment;filename=用户数据.xlsx,在Chrome下可能正常,在IE和部分国产浏览器里中文会被截断成“_”或乱码,下载后文件名变成“__.xlsx”。解决办法是输出前对文件名做一次UrlEncode,再用UTF-8编码写进Content-Disposition:

context.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8));

HttpUtility.UrlEncode会把中文转成百分号编码,浏览器收到后自动解码,不同浏览器对编码格式的兼容性最好。这个处理不建议省,哪怕你现在只在Chrome里测过,也先加上,免得换台电脑就翻车。还有文件名里别带空格和斜杠,空格在某些浏览器里会截断后半截,斜杠会直接导致下载失败。

3.4 大数据量导出的边界:先算清行数再决定方案

数据量是导出功能的分水岭。几千行到几万行,同步导出没有压力,接口等一两秒,浏览器开始下载,体验可以接受。十万行往上,单个响应里同时做查询、写Excel、输出流,内存和响应时间都会吃紧,直接的表现是接口超时或者服务器内存暴涨。

我一般会在产品层面先挡一道:导出前加一个“导出条数确认”弹窗,显示当前条件下有多少条数据,超过两万行时提示用户缩小时间范围或查询条件。这是性价比最高的做法,不需要动架构。真要支持大文件后台导出,常规方案是后台任务生成Excel文件到服务端,生成完成后给前端一个下载链接,期间用户可以做别的操作。这个方案已经超出“新增、修改、删除、导出Excel”这个标题的最小实现范围,属于后续增强,这里不做展开。

4. 文件上传:easyui-filebox取文件、FormData传输和两处大小限制

4.1 前端取文件:filebox底层就是input[type=file]

EasyUI的filebox是原生文件选择框的封装,初始化后页面里仍然是一个input,只是样式被换成按钮加文本框。取文件不通过val(),而是走filebox自己的方法。

<input id="fileBox" class="easyui-filebox" >$('#btnUpload').click(function () { var file = $('#fileBox').filebox('files')[0]; if (!file) { $.messager.alert('提示', '请先选择文件'); return; } // file.name、file.size、file.type在这里都能拿到 console.log(file.name, file.size); });

filebox('files')返回的是一个数组,即使只选了一个文件,也要取下标0。这里经常有人写成$('#fileBox').filebox('getValue'),拿到的是文件路径字符串,不是文件对象,后面往FormData里塞的时候直接报错。“取文件对象用files(),取显示路径用getValue()”,这个区别值得记住。

4.2 上传动作:FormData加上两个必须写的false

上传文件不能再用form表单序列化,要用XMLHttpRequest级别的FormData。EasyUI没有单独封装上传插件,但ajax和原生FormData配合没有任何障碍。

$('#btnUpload').click(function () { var file = $('#fileBox').filebox('files')[0]; if (!file) return; // 前端先拦一道大小限制,避免无效请求打到后端 if (file.size > 20 * 1024 * 1024) { $.messager.alert('错误', '文件不能超过20MB'); return; } var formData = new FormData(); formData.append('file', file); $.ajax({ url: 'upload.ashx', type: 'POST', data: formData, processData: false, contentType: false, success: function (data) { var r = JSON.parse(data); if (r.success) { $.messager.show({ title: '提示', msg: '上传成功' }); } else { $.messager.alert('错误', r.msg); } }, error: function (xhr) { $.messager.alert('错误', 'HTTP ' + xhr.status); } }); });

processData:false告诉jQuery不要尝试把FormData转成查询字符串,contentType:false告诉jQuery不要覆盖multipart/form-data边界。这两个不写,上传必然失败,是上传代码里最典型的翻车点。error回调里把xhr.status打出来,排错会快很多,别只弹一个“网络错误”让用户猜。

后端接收端用HttpRequest.Files取文件。完整处理包括类型校验、落盘和返回结果:

public void ProcessRequest(HttpContext context) { try { HttpPostedFile file = context.Request.Files["file"]; if (file == null || file.ContentLength == 0) { WriteJson(context, false, "没有收到文件"); return; } // 扩展名校验,注意转成小写再比较 string ext = Path.GetExtension(file.FileName).ToLower(); string[] allowed = { ".xlsx", ".xls", ".png", ".jpg", ".jpeg" }; if (!allowed.Contains(ext)) { WriteJson(context, false, "不支持的文件类型"); return; } // 按月份分目录,GUID重命名,避免重名和单目录文件过多 string dir = "~/Uploads/" + DateTime.Now.ToString("yyyyMM"); string physicalDir = context.Server.MapPath(dir); if (!Directory.Exists(physicalDir)) { Directory.CreateDirectory(physicalDir); } string newName = Guid.NewGuid().ToString("N") + ext; file.SaveAs(Path.Combine(physicalDir, newName)); WriteJson(context, true, newName); } catch (Exception ex) { WriteJson(context, false, ex.Message); } } private void WriteJson(HttpContext context, bool success, string msg) { string json = new JavaScriptSerializer().Serialize(new { success = success, msg = msg }); context.Response.ContentType = "application/json; charset=utf-8"; context.Response.Write(json); }

按扩展名判断类型只是一个前置筛选,严格场景下还要校验文件头字节,防止改名绕过。实际项目里如果上传的是Excel且后续要做解析入库,我会再加一层“打开Excel验证结构”的步骤,把格式问题挡在入库之前。保存路径按月份分目录,物理文件名用GUID,原始文件名存到数据库字段,这是处理重名和目录膨胀的常规做法。

4.3 两处大小限制:web.config里的两个配置都要调

上传文件大小被限制,排错经常卡在两个层面:ASP.NET的httpRuntime和IIS的requestFiltering。先看配置文件:

<system.web> <httpRuntime maxRequestLength="20480" executionTimeout="120" /> </system.web> <system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="20971520" /> </requestFiltering> </security> </system.webServer>
配置项单位示例值超限表现
httpRuntime/maxRequestLengthKB20480(20MB)抛“超出最大请求长度”异常
requestLimits/maxAllowedContentLength字节20971520(20MB)IIS直接返回404.13

这两处是同一道关卡的两道门:第一个限制ASP.NET接受请求体的体积;第二个限制IIS接收的请求内容长度,超了直接返回404.13,请求根本进不到你的处理程序里。只改一处的话,表现很迷惑:本地调试正常,发布到IIS后大文件上传直接404。所以做上传功能时我先确认两处都改了,再拿一个大文件做回归测试。如果你的项目反向代理层还有更严格的内容长度限制,也要一并检查,前端报错时先用Fiddler看响应状态码,404.13基本可以断定是IIS这层的问题。

4.4 保存策略与后续入库:上传只是第一步

上传成功后,返回给前端的是物理文件名或者ID。如果附件只是存证,那么文件名、大小、上传人、时间落在数据库里就行。如果上传的是Excel且业务要求把里面的数据导入数据库,那么上传和导入是两个动作:上传把文件落到服务器,导入用一个后台接口读取Excel并逐行校验入库。

这个需求在中小型项目里出现频率极高,做完“导出Excel”之后,紧接着就有人提“要把填好的Excel模板传上来批量导入”。两个功能共用一套文件处理思路:校验扩展名、限制大小、文件落盘、解析入库。所以第4章这段方案不只是做了“上传”这一个动作,它为后续做Excel导入数据库打好了地基——你已经有文件,有物理路径,有统一的Uploads目录,再接一层解析逻辑即可。

5. EasyUI与C#联调避坑:JSON日期、查询条件、回填与上传的6条记录

以下6条都是实际联调中反复出现的坑,不是玄学,是血泪经验。每一条都按现象、原因、解决三个步骤写,方便你对照排查。

5.1 表格里日期显示成 /Date(1591234567890)/ 怎么办

现象:datagrid的列里直接显示一串类似/Date(1591234567890)/的字符串,完全不是日期。

原因:JavaScriptSerializer序列化DateTime对象时默认输出这种格式,EasyUI拿到后原样渲染。前端没有转换逻辑的接口多半会翻车。

解决:后端在输出JSON前统一处理。最省事的方式是查询后就做ToString转换,我在2.2节代码里已经这么处理。如果想保留DateTime类型,则在EasyUI的columns定义里给该列加formatter,前端用parseInt配合new Date()转换。我一般坚持后端转字符串,因为前端每个日期列都要写一次formatter,容易漏,漏一列就乱一处。

5.2 reload之后查询条件丢了:页数跳了但数据是全量的

现象:第一次加载按关键词过滤正常,点下一页或点刷新后,关键词条件消失,表格又显示了所有数据。

原因:datagrid的queryParams只在初始化和手动指定时生效,内部reload不保留二次修改的条件。很多人把keyword写死在queryParams里,reload时框架只带page和rows,条件就没了。

解决:每次点查询、点刷新都重新加载数据并显式带上条件,不要依赖queryParams被自动记忆:

function reloadGrid() { $('#dg').datagrid('load', { keyword: $('#kw').textbox('getValue') }); }

load方法的参数会合并到当前请求参数里,分页参数page和rows由框架自动补上。这样无论切页还是刷新,条件始终跟着走。

5.3 修改弹窗打开后表单是空的:form('load')的匹配规则

现象:点击编辑,对话框打开了,但姓名、备注都是空的。

原因:form('load', row)是按name去匹配row对象的字段。要么后端返回的字段名和表单name对不上,要么row本身不对。还有一种隐蔽情况:row里的字段值是null或空字符串,form('load')会跳过赋值。

解决:先确认后端返回的JSON字段名和表单name完全一致,包括大小写。再确认字段都有值,后端查询时对可能为null的列用IsNull或“空字符串”兜底。排查时可以临时在edit函数里console.log(row),先确认拿到的数据本身有没有值,再谈赋值问题。

5.4 多选删除只删了一条

现象:勾选了三行,点删除,结果只删掉第一行或者只删掉最后一行。

原因:代码里用了getSelected而不是getSelections。getSelected只返回当前选中的第一行,适合单选场景;多选必须遍历getSelections返回的数组。

解决:按2.5节的写法,用getSelections拼装id列表,一次接口提交所有id。如果你设置了singleSelect:true,那getSelections永远只有一条,多选之前先检查表格配置。

提示:多选前先确认datagrid没有把singleSelect设为true,这个配置在页面初始化时很容易被从示例代码里直接带过来。

5.5 导出Excel文件名在IE和部分浏览器里乱码

现象:Chrome里下载文件名正常,IE或旧内核浏览器里下载下来的文件叫“__.xlsx”或一串乱码。

原因:Content-Disposition的filename参数没有做编码处理,浏览器对中文解释不一致。

解决:输出前对文件名做HttpUtility.UrlEncode并用UTF-8编码写入,这节在3.3节已经给出代码。另一个保险措施是,文件名里不要带冒号、斜杠这类Windows文件名非法字符,生成时间戳文件名时用“yyyyMMddHHmmss”这种纯数字格式,避免麻烦。

5.6 上传十几MB的文件报404.13

现象:小文件上传正常,超过一定大小后就报404.13或者直接返回错误页。

原因:IIS的requestFiltering限制比ASP.NET的maxRequestLength更早生效。发布环境IIS默认对请求内容的长度有上限,超出后请求根本到不了你的处理程序。

解决:把web.config里两个限制同时调大,单位注意区分:maxRequestLength是KB,maxAllowedContentLength是字节。改完后重启应用池再测。顺便检查一下反向代理的客户端请求体大小限制。有三层以上的情况,我会在前端的error回调里打印完整xhr.status,先定位是哪一层拦的再动手改。

6. 验证与进阶:用Fiddler看交互、给表格加行内编辑、把ashx迁移到Controller

6.1 验证三板斧:Fiddler看请求地址、JSON结构和状态码

这套组合联调不顺,绝大多数问题出在接口层面而不是组件用法。我的固定排查顺序是:打开Fiddler,先看请求有没有发出去、URL对不对;再看响应JSON的total和rows结构对不对;最后看状态码,404和500分别指向路由和代码异常。用键盘F12里的Network面板也可以,但Fiddler能看到请求头里的Content-Disposition和JSON原始串,排查上传和导出问题时更直接。接口通了再动前端,顺序反了会让你在弹窗和表格之间来回猜。

6.2 给表格加行内编辑:edatagrid的取舍

如果后面需求从“新增修改删除”进化到“表格里直接改”,EasyUI有一个叫edatagrid的扩展插件,继承自datagrid,多出endEdit、destroyRow等方法。它的做法是编辑完一行自动调用保存接口,对熟练操作的人效率高,但实现时要多处理一个问题:编辑未保存就切页,数据会丢。我一般只在单页、数据量小、不允许批量操作的场景用行内编辑,否则老老实实走对话框提交,交互虽慢但数据安全。

把ashx迁移到MVC Controller的时机:当项目里的接口超过十个、开始需要统一的参数校验、日志、异常过滤时,ashx的重复样板代码会变多。这个迁移是线性成本,接口里面只做数据查询和JSON输出,迁移时把ProcessRequest的逻辑搬到Controller的Action即可,前端URL从list.ashx变成/List/Query,其余不用动。如果你只是维护一个小项目,接口不超过十个,ashx完全够用,不必为了框架升级而升级。

我做这类页面,固定顺序是先定接口返回的JSON字段,再写页面,最后联调。以前总喜欢先把EasyUI表格摆漂亮再回头补接口,结果十个里有八个要改字段名和格式。现在先拿Fiddler确认一道接口返回干净了,再做页面,返工率低了很多。希望这篇C#+EasyUI的实战笔记能帮你少踩几个坑,照着做把这五个需求干净利落地交出去。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询