經過 13 講的建置,基礎框架已成型。但在實際開發中發現 EasyUI 存在數個體驗與效能問題,且架構層面仍有優化空間。本篇盤點 7 大優化項目,並逐一提供解決方案,將系統推向生產就緒狀態。
/Date(1370000000000)/ 格式DBContainer,缺乏基類封裝// 開啟視窗前計算置中座標
function openCenteredWindow(winSelector, options) {
var $win = $(winSelector);
var winW = options.width || 600;
var winH = options.height || 400;
var left = ($(window).width() - winW) / 2 + $(document).scrollLeft();
var top = ($(window).height() - winH) / 2 + $(document).scrollTop();
$win.window($.extend({}, options, { left: left, top: top })).window('open');
}
// 監聽視窗 resize 事件動態調整
$(window).resize(function() {
$('.window-body:visible').each(function() {
var $win = $(this).closest('.window');
var opts = $win.window('options');
var left = ($(window).width() - opts.width) / 2;
var top = ($(window).height() - opts.height) / 2;
$win.window('move', { left: left, top: top });
});
});
// 初始化時綁定 resize 事件
$('#List').datagrid({
// ... 其他選項
onLoadSuccess: function() {
fixDatagridSize();
}
});
function fixDatagridSize() {
var $dg = $('#List');
var parent = $dg.datagrid('getPanel').parent();
$dg.datagrid('resize', {
width: parent.width(),
height: $(window).height() - 35
});
}
$(window).resize(function() {
clearTimeout(window._dgResizeTimer);
window._dgResizeTimer = setTimeout(fixDatagridSize, 100);
});
在 BundleConfig.cs 中註冊:
bundles.Add(new ScriptBundle("~/bundles/easyui").Include(
"~/Scripts/jquery.min.js",
"~/Scripts/jquery.easyui.min.js",
"~/Scripts/easyui-extend.js" // 自訂擴充
));
bundles.Add(new StyleBundle("~/Content/easyui").Include(
"~/Content/themes/blue/easyui.css",
"~/Content/themes/blue/icon.css",
"~/Content/Site.css"
));
// 啟用最佳化(壓縮)
BundleTable.EnableOptimizations = true;
在 _Layout.cshtml 統一引用:
@Scripts.Render("~/bundles/easyui")
@Styles.Render("~/Content/easyui")
原因:預設 JavaScriptSerializer 將 DateTime 序列化為 /Date(ticks)/ 格式。
方案:自訂 JsonResult 使用 Json.NET 並設定 IsoDateTimeConverter:
public class JsonNetResult : JsonResult {
public JsonSerializerSettings Settings { get; set; }
public override void ExecuteResult(ControllerContext context) {
if (context == null) throw new ArgumentNullException("context");
var response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(ContentType) ? "application/json" : ContentType;
if (ContentEncoding != null) response.ContentEncoding = ContentEncoding;
if (Data != null) {
var settings = Settings ?? new JsonSerializerSettings {
DateFormatHandling = DateFormatHandling.IsoDateFormat, // ISO 8601: "2013-10-15T14:30:00"
DateTimeZoneHandling = DateTimeZoneHandling.Local,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
response.Write(JsonConvert.SerializeObject(Data, settings));
}
}
}
// Controller 基類統一使用
protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) {
return new JsonNetResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding };
}
public abstract class BaseBLL<T> : IDisposable where T : class {
protected DBContainer DbContext { get; private set; }
protected BaseBLL() {
DbContext = new DBContainer();
// 可在此設定 DbContext.Configuration.ProxyCreationEnabled = false 等
}
public void Dispose() {
if (DbContext != null) {
DbContext.Dispose();
DbContext = null;
}
}
// 通用 CRUD 方法可選擇性實作於此
}
具體 BLL 繼承:
public class SysSampleBLL : BaseBLL<SysSample>, ISysSampleBLL {
// 直接使用 DbContext 進行操作
public IQueryable<SysSample> GetList() {
return DbContext.SysSample.AsQueryable();
}
}
在 App.Common 建立 Suggestion.cs:
namespace App.Common {
public static class Suggestion {
public const string PlaseChooseToOperatingRecords = "請選擇要操作的記錄";
public const string AreYouLogOff = "您確定要登出系統嗎?";
public const string YouWantToDeleteTheSelectedRecords = "您確定要刪除所選記錄嗎?";
public const string InsertSucceed = "建立成功";
public const string InsertFail = "建立失敗";
public const string EditSucceed = "修改成功";
public const string EditFail = "修改失敗";
public const string DeleteSucceed = "刪除成功";
public const string DeleteFail = "刪除失敗";
public const string CheckSucceed = "審核成功";
public const string CheckFail = "審核失敗";
public const string UnCheckSucceed = "反審核成功";
public const string UnCheckFail = "反審核失敗";
public const string Export = "匯出";
public const string Import = "匯入";
public const string Loading = "載入中...";
public const string LoginSucceed = "登入成功,歡迎使用!";
public const string UserTimeOver = "登入逾時,請重新登入";
// ... 更多訊息常數
}
}
前端使用:
$.messageBox5s('提示', Suggestion.PlaseChooseToOperatingRecords);
public static class ToolbarExtensions {
public static MvcHtmlString ToolbarButton(this HtmlHelper helper, string id, string text, string iconClass, string onclick) {
var builder = new TagBuilder("a");
builder.MergeAttribute("id", id);
builder.MergeAttribute("class", "l-btn l-btn-plain");
builder.MergeAttribute("onclick", onclick);
builder.MergeAttribute("style", "float:left;");
var spanOuter = new TagBuilder("span");
spanOuter.AddCssClass("l-btn-left");
var spanInner = new TagBuilder("span");
spanInner.AddCssClass("l-btn-text " + iconClass);
spanInner.MergeAttribute("style", "padding-left:20px;");
spanInner.SetInnerText(text);
spanOuter.InnerHtml = spanInner.ToString();
builder.InnerHtml = spanOuter.ToString();
return MvcHtmlString.Create(builder.ToString() + "<div class='datagrid-btn-separator'></div>");
}
}
View 中優雅調用:
@Html.ToolbarButton("btnCreate", "新增", "icon-add", "openCreateWindow()")
@Html.ToolbarButton("btnEdit", "編輯", "icon-edit", "openEditWindow()")
@Html.ToolbarButton("btnDelete", "刪除", "icon-remove", "deleteSelected()")
| 優化項目 | 優化前 | 優化後 |
|---|---|---|
| 視窗置中 | 固定座標,大螢幕偏移 | 動態計算置中,Resize 自適應 |
| DataGrid 自適應 | 固定尺寸,縮放變形 | 100% 容器適應,響應式 |
| JS 管理 | 分散引用,未壓縮 | Bundle 捆綁壓縮,快取優化 |
| 日期格式 | /Date(1370000000000)/ | ISO 8601: 2013-10-15T14:30:00 |
| BLL 重複代碼 | 每類別 new DBContainer() | BaseBLL 統一管理生命週期 |
| 提示訊息 | 硬編碼分散 | 常數類別集中維護 |
| 工具列按鈕 | 重複 HTML 字串 | HtmlHelper 一行調用 |
優化後的完整原始碼將作為本系列最後一個提供下載的版本。後續章節將專注於權限系統、T4 模板、報表引擎等核心功能講解,程式碼將以關鍵片段形式呈現。請妥善保存此版本原始碼作為專案基礎模板。
作者:YmNets
出處:https://ymnets.cnblogs.com/