这个示例演示一个常见后台/工作台骨架:左侧 Menu 作为主导航,顶部 Breadcrumb 显示当前位置,右侧 Tabs 动态承载已打开的业务页,底部 Alert 显示当前状态。
示例遵循 aardio 推荐的窗口写法:
/*DSG{{*/ ... /*}}*/ 设计器区域内;custom 宿主;AntdUI.Menu(winform.menuHost)、AntdUI.Breadcrumb(winform.breadcrumbHost)、AntdUI.Tabs(winform.tabsHost) 自然嵌入;SetBounds 绝对布局当作普通窗口模板。import win.ui;
/*DSG{{*/
var winform = win.form(text="AntdUI 导航工作台";right=1040;bottom=640)
winform.add(
menuHost={cls="custom";left=0;top=0;right=220;bottom=640;db=1;dl=1;dt=1;z=1};
breadcrumbHost={cls="custom";left=220;top=0;right=1040;bottom=48;dl=1;dr=1;dt=1;z=2};
tabsHost={cls="custom";left=220;top=48;right=1040;bottom=594;db=1;dl=1;dr=1;dt=1;z=3};
statusHost={cls="custom";left=220;top=594;right=1040;bottom=640;db=1;dl=1;dr=1;z=4}
)
/*}}*/
import dotNet.AntdUI;
import System.Windows.Forms;
import System.Drawing;
// 业务路由表:key 用于菜单、标签页、面包屑之间同步。
var routes = [
{key="dashboard";section="首页";title="运营概览";desc="查看关键指标、待办任务与近期风险。"},
{key="tickets";section="运维中心";title="工单列表";desc="按优先级跟踪设备巡检、维修、复核等工单。"},
{key="devices";section="运维中心";title="设备台账";desc="管理设备基础信息、维保周期与在线状态。"},
{key="reports";section="数据分析";title="运行报表";desc="汇总能耗、告警、工单处理时效等运营数据。"},
{key="settings";section="系统配置";title="角色与权限";desc="维护角色、菜单权限与操作审计策略。"}
];
var menu = AntdUI.Menu(winform.menuHost);
menu.Mode = AntdUI.TMenuMode.Inline;
menu.Unique = true;
menu.Radius = 8;
var breadcrumb = AntdUI.Breadcrumb(winform.breadcrumbHost);
breadcrumb.Separator = "/";
breadcrumb.Gap = 8;
var tabs = AntdUI.Tabs(winform.tabsHost);
tabs.Type = AntdUI.TabType.Card;
tabs.EnablePageScrolling = true;
tabs.CloseDisposePage = true;
var status = AntdUI.Alert(winform.statusHost);
status.TextTitle = "就绪";
status.Text = "请选择左侧菜单打开业务页。";
status.Icon = AntdUI.TType.Info;
status.CloseIcon = false;
var allowMessage = false;
var findRoute = function(key){
if(key === null) return null;
for i,route in routes {
if(route.key == key) return route;
}
}
var findTabPage = function(key){
if(key === null) return null;
for(i=0;tabs.Pages.Count-1;1){
var page = tabs.Pages.get_Item(i);
if(page.Name == key) return page;
}
}
var setBreadcrumb = function(route){
breadcrumb.Items.Clear();
var home = AntdUI.BreadcrumbItem();
home.ID = "home";
home.Text = "工作台";
breadcrumb.Items.Add(home);
var section = AntdUI.BreadcrumbItem();
section.ID = route.section;
section.Text = route.section;
breadcrumb.Items.Add(section);
var current = AntdUI.BreadcrumbItem();
current.ID = route.key;
current.Text = route.title;
breadcrumb.Items.Add(current);
}
var createPage = function(route){
var page = AntdUI.TabPage();
page.Name = route.key;
page.Text = route.title;
page.Tag = route.key;
page.AutoScroll = true;
var header = AntdUI.Alert();
header.Dock = System.Windows.Forms.DockStyle.Top;
header.TextTitle = route.title;
header.Text = route.desc;
header.Icon = AntdUI.TType.Success;
header.CloseIcon = false;
page.Controls.Add(header);
var label = AntdUI.Label();
label.Dock = System.Windows.Forms.DockStyle.Fill;
label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
label.TextMultiLine = true;
label.Text = route.desc ++ '\r\n\r\n页面 key:' ++ route.key
++ '\r\n这里可以继续放置 Table、Form、Chart 或其他 AntdUI 控件。';
page.Controls.Add(label);
return page;
}
var openRoute = function(route){
var page = findTabPage(route.key);
if(!page){
page = createPage(route);
tabs.Pages.Add(page);
}
tabs.SelectTab(page);
setBreadcrumb(route);
status.TextTitle = "已打开";
status.Text = route.section + " / " + route.title;
// 静态 Message 建议以 BaseForm 作为 owner。
// 由 AntdUI.Tabs(winform.tabsHost) 自动创建的父窗体就是 AntdUI.BaseForm。
if(allowMessage){
AntdUI.Message.open(
AntdUI.Message.Config(tabs.Parent,"已打开:" + route.title,AntdUI.TType.Success)
);
}
}
var addLeaf = function(parent,route){
var item = AntdUI.MenuItem(route.title);
item.ID = route.key;
item.IconSvg = "AppstoreOutlined";
parent.Sub.Add(item);
}
var home = AntdUI.MenuItem("首页");
home.ID = "dashboard";
home.IconSvg = "HomeOutlined";
menu.Items.Add(home);
var ops = AntdUI.MenuItem("运维中心");
ops.ID = "ops";
ops.IconSvg = "ToolOutlined";
addLeaf(ops,routes[2]);
addLeaf(ops,routes[3]);
menu.Items.Add(ops);
var analysisMenu = AntdUI.MenuItem("数据分析");
analysisMenu.ID = "analysis";
analysisMenu.IconSvg = "BarChartOutlined";
addLeaf(analysisMenu,routes[4]);
menu.Items.Add(analysisMenu);
var sys = AntdUI.MenuItem("系统配置");
sys.ID = "sys";
sys.IconSvg = "SettingOutlined";
addLeaf(sys,routes[5]);
menu.Items.Add(sys);
menu.ItemClick = function(sender,e){
var item = e.Item;
if(!item) return;
var route = findRoute(item.ID);
if(route) openRoute(route);
}
tabs.SelectedIndexChanged = function(sender,e){
var page = tabs.SelectedTab;
if(!page) return;
var route = findRoute(page.Name);
if(route) setBreadcrumb(route);
}
breadcrumb.ItemClick = function(sender,e){
if(e.Item && e.Item.ID == "home"){
openRoute(routes[1]);
}
}
openRoute(routes[1]);
winform.show();
allowMessage = true;
win.loopMessage();
示例里每个业务页都有唯一 route.key。菜单项的 ID、TabPage.Name、TabPage.Tag 都使用这个 key,便于查找已有标签页、防止重复打开。
item.ID = route.key;
page.Name = route.key;
page.Tag = route.key;
如果 key 来自外部数据,建议只使用字母、数字、下划线等安全字符,避免把完整路径、中文标题或含特殊符号的字符串直接放进 Control.Name。
.NET 集合索引通常从 0 开始,遍历 tabs.Pages 时不要套用 aardio 数组的 1 基习惯。
for(i=0;tabs.Pages.Count-1;1){
var page = tabs.Pages.get_Item(i);
if(page.Name == key) return page;
}
AntdUI.Tabs(winform.tabsHost) 会自动嵌入 AntdUI.BaseForm,因此 tabs.Parent 可作为 Message 的 owner:
AntdUI.Message.open(
AntdUI.Message.Config(tabs.Parent,"已打开:" + route.title,AntdUI.TType.Success)
);
不要把普通 aardio custom 宿主直接当成 Message.Config 的 owner。
复杂业务窗口推荐在 aardio 设计器中拆出多个语义化宿主:
menuHost:左侧导航菜单;breadcrumbHost:顶部面包屑;tabsHost:中间标签页内容区;statusHost:底部状态提示。这样窗口缩放、DPI 缩放和控件锚定由 aardio 设计器负责,比在 .NET 容器内大量手写 SetBounds 更稳。