news 2026/10/17 7:06:01

PDMS-AVEVA-E3D二次开发教程(10):Command 机制与 UIC——把定制命令挂到菜单与工具栏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PDMS-AVEVA-E3D二次开发教程(10):Command 机制与 UIC——把定制命令挂到菜单与工具栏

版本声明:本篇基线为AVEVA E3D Design 3.1(文档版本线存在 1.1 / 2.1 / 3.1,教程统一以 3.1 为准);前代产品AVEVA PDMS 自 2024 年 4 月 1 日起不再销售、不再提供支持。文中命令基类、状态枚举、事件名、UIC 机制与环境变量均出自官方 .NET 定制手册(NCUG)与 PML 定制手册(PMLCG)。凡标注"按官方 API 签名构造"的代码,均需在本地 E3D Design 环境验证。

一句话结论

把定制功能挂到界面上的最小闭环是**“一个设置过Key的Command子类 + 一次CommandManager.Commands注册 + 一份用同一个Key引用它的.uic”**——三者缺一,菜单就会点了没反应。

〇、认知问题(读完本篇你应该能回答)

  1. 定制命令为什么必须继承Command抽象基类?构造函数里必须设置什么?
  2. Execute()、Execute(ArrayList arguments)、GetState()、Description、Refresh(string context)分别在什么时候被调用?
  3. GetState()返回的CommandState为什么说"可按位组合"?它实际影响什么?
  4. BeforeCommandExecute与CommandExecuted两个事件分别处在执行链的哪个位置?能做什么?
  5. UIC 文件是什么?它和<模块名>Customisation.xml是什么关系?$1会被替换成什么?
  6. 为什么不建议从标签页按钮直接调用 PML 宏?官方给的替代方案是什么?

一、机制解析

1.1Command基类与Key:界面与逻辑解耦

CAF 提供了一个抽象基类Command,官方要求所有定制命令类都必须继承它。继承之后有一条硬性规定:必须在构造函数中设置基类的Key属性。

Key是这套机制的解耦点:

  • 命令类知道自己的Key是什么,但完全不知道自己在界面上的位置;
  • UIC 文件只写"哪个菜单项关联哪个Key",完全不知道命令是怎么实现的;
  • 两边通过Key这个字符串对上号。

所以"菜单点了没反应"的头号原因就是Key没设、设错、或与.uic里写的不逐字符一致。这是本系列铁律 5 的来源:定制命令必须设置Command基类的Key,.uic靠它关联。

1.2 可重写成员与状态属性

官方列出的可重写成员:

成员作用
Execute()无参执行入口,菜单/按钮触发时的主逻辑
Execute(ArrayList arguments)带参数执行入口,用于把参数从界面传进命令
GetState()返回CommandState枚举,决定命令当前的状态
Description命令的描述文本
Refresh(string context)在给定上下文下刷新命令状态

命令实例上可直接读写的状态属性(界面据此呈现):

Checked、Enabled、List、SelectedIndex、Value、ValueChanged、Visible。

把这组属性读懂,就能理解 CAF 命令的"呈现模型":界面元素从命令实例上读状态,而不是命令去改界面。因此:

  • 想让菜单项显示为勾选状态,就操作Checked;
  • 想让菜单项变灰,就通过GetState()返回不含启用位的CommandState;
  • 想让命令在下拉框里表现为"选择项",就用List/SelectedIndex/Value。

注意ValueChanged的存在:它表明 CAF 的命令模型并不只是"点了执行",还包括"值被改了"这类事件。设计界面时不要把它当成只会触发Execute的按钮。

1.3CommandState:按位组合的状态

GetState()的返回值是CommandState枚举,官方明确它可按位组合。这意味着状态不是单选,而是若干标志位的并集——一个命令可以同时是"可见 + 启用 + 已勾选"。

工程含义有两点:

  1. 不要用多个属性分别表达状态,而是把状态收拢到GetState()里统一返回;
  2. 不要写"如果没勾选就不启用"这类耦合逻辑,状态位之间应该是正交的。

另外,GetState()会在界面需要呈现时被反复调用(打开菜单、切换工具栏、刷新上下文),所以它的实现必须快且无副作用——不要在这里面查数据库、不要在这里面改模型。

1.4 事件:BeforeCommandExecute与CommandExecuted

官方给出两个关键事件:

事件位置典型用途
BeforeCommandExecute命令执行之前前置校验、条件不满足时Cancel掉本次执行
CommandExecuted命令执行之后后置审计、日志、界面刷新联动

BeforeCommandExecute可以Cancel这一条是很有分量的:它意味着你可以在不修改命令类本身的前提下,给第三方或既有命令加上"权限校验 / 前置条件校验"这类横切逻辑。工程上常见的用法是:

  • 只允许特定权限的用户执行某命令;
  • 当前!!CE类型不符合要求时直接拦住,而不是等命令内部报错。

CommandExecuted则适合做"命令审计日志":谁、什么时候、执行了哪个Key的命令。

1.5 注册:CommandManager.Commands

注册方式官方写得很直接:把命令实例加入CommandManager.Commands集合。

CommandManager位于命名空间Aveva.ApplicationFramework.Presentation(与CommandBarManager同属该命名空间);典型的取法是从ServiceManager里取服务——这正是第 09 篇要求把ServiceManager存下来的原因。

注册与装载是两件事:Addin 被加载 ≠ 命令出现在界面上。Addin 只负责"命令对象被创建并注册进集合",界面出现与否由 UIC 决定。

1.6 UIC 文件与<模块名>Customisation.xml

UIC(User Interface Customisation)文件是 XML 格式的界面定义文件:CAF 通过它定义菜单与工具栏,并与命令对象的Key关联(这就是"解耦 UI 与逻辑"的落地方式)。

模块级配置文件是<模块名>Customisation.xml,其中:

  • <UICustomizationFiles>按顺序列出要加载的 UIC 文件;
  • $1会被替换成当前工程名——这一条非常实用:可以给不同项目加载不同的 UIC 集合,而不用改文件内容。

加载路径由环境变量CAF_UIC_PATH指定(官方说明它是"界面定义文件位置,按<模块名>Customisation.xml查找并列出的.uic")。

两种加载方式:

  1. 配置文件方式:<模块名>Customisation.xml里的<UICustomizationFiles>列出.uic;
  2. 代码方式:commandBarManager.AddUICustomizationFile("AttributeBrowser.uic", "AttributeBrowser")。

另外,CAF 还提供交互式编辑入口:工具栏右键 → Customize。在开发期先用它把菜单摆好、看清生成的Key引用关系,再回写进.uic,比手写 XML 快得多。

1.7 PML 侧的命令机制与"不要从标签页按钮直接调用 PML 宏"

官方在 PML 侧也有一套命令机制:命令可以实现为 PML command object 或 core command object,并通过.uic文件把工具与命令对象关联——这与 .NET 侧的Command+Key是同一套"界面靠引用名找逻辑"的思想。SCRM 的对象类型清单里也确实列有COMMAND对象类型。

同时,官方明确给出了一条否定性建议:不建议从标签页按钮直接调用 PML 宏。理由指向与 command ribbon 的兼容性——直接挂宏绕过了命令体系,状态、权限、上下文都无法参与管理。这就是本系列铁律 7 的由来。官方的替代方案是:把功能做成Command对象(PML command object 或 .NETCommand子类),再用.uic关联。

二、完整代码与逐行剖析

2.1 官方示例转载:ShowAttributeBrowserCommand

下面这段是官方 .NET Command and Menu Customisation 教程中ShowAttributeBrowserCommand的转载版(来源性质:官方教程示例代码转载),它是"命令如何与一个可停靠窗口联动"的标准范例:

usingAveva.ApplicationFramework.Presentation;publicclassShowAttributeBrowserCommand:Command{privateDockedWindow_window;publicShowAttributeBrowserCommand(DockedWindowwindow){this.Key="Aveva.ShowAttributeBrowserCommand";// 必须设置:.uic 靠它引用本命令_window=window;_window.Closed+=newEventHandler(_window_Closed);WindowManager.Instance.WindowLayoutLoaded+=newEventHandler(Instance_WindowLayoutLoaded);}publicoverridevoidExecute(){if(this.Checked){_window.Show();}// 勾选状态 => 显示窗口else{_window.Hide();}// 未勾选 => 隐藏窗口}}

逐行剖析

  • using Aveva.ApplicationFramework.Presentation;:Command基类就在这个命名空间里(该命名空间还包含CommandBarManager、CommandManager)。
  • public class ShowAttributeBrowserCommand : Command:继承是强制的。类名本身没有约束,但Key有约束。
  • private DockedWindow _window;:命令不自己创建窗口,而是从外部接收窗口引用——这是"窗口生命周期由 Addin 管理、命令只负责显示/隐藏"的合理分工。
  • this.Key = "Aveva.ShowAttributeBrowserCommand";:构造函数里第一件事就是设Key。这个字符串会原样出现在.uic里。
  • _window.Closed += …/WindowManager.Instance.WindowLayoutLoaded += …:把窗口关闭与界面布局加载两个事件接上,用来同步Checked状态(窗口被关掉时,菜单项的勾选要跟着取消)。
  • public override void Execute():无参执行入口。
  • if (this.Checked) { _window.Show(); } else { _window.Hide(); }:一个非常典型的"开关型命令"写法——命令自己只判断Checked,界面呈现由 CAF 依据状态完成。注意它没有去改Checked,这通常由GetState()或界面框架负责。

2.2 注册代码(官方示例转载)

CommandManagercommandManager=(CommandManager)serviceManager.GetService(typeof(CommandManager));ShowAttributeBrowserCommandshowCommand=newShowAttributeBrowserCommand(attributeListWindow);commandManager.Commands.Add(showCommand);

逐行剖析

  • serviceManager.GetService(typeof(CommandManager)):命令管理器是从服务容器里取出来的,不是自己 new 的。这说明为什么第 09 篇要在Start里保存ServiceManager——注册动作必须在Start之后、且要用 CAF 给的那个ServiceManager。
  • (CommandManager)强制转换:说明GetService返回的是通用对象类型。
  • commandManager.Commands.Add(showCommand):官方明确的注册方式——把命令实例加入CommandManager.Commands集合。注册之后命令对象就"存在"了,但还没在界面上露面。
  • new ShowAttributeBrowserCommand(attributeListWindow):把窗口实例注入命令。注意此时attributeListWindow必须已经创建完毕,所以 Addin 里的顺序应当是"先建窗口 → 再建命令 → 再注册"。

2.3 一个带状态与参数的自定义命令(构造示例)

下面这个命令演示GetState()、CommandState、Execute(ArrayList)与Value的用法。代码来源性质:按官方 API 签名构造,需在本地 E3D Design 环境验证;其中CommandState的具体枚举成员名请以本机Aveva.ApplicationFramework.Presentation.dll的 IntelliSense 与 CAF API 参考为准。

usingSystem.Collections;usingAveva.ApplicationFramework.Presentation;namespaceMyCompany.E3DAddin{/// <summary>/// 演示型命令:/// 1) 用 GetState() 返回按位组合的 CommandState;/// 2) 用 Execute(ArrayList) 接收界面传来的参数;/// 3) 用 Value 承载一个"当前值"。/// </summary>publicclassAuditModelCommand:Command{privatebool_hasSelection=false;// 是否已选中元素(由外部上下文刷新)privatestring_lastResult="";// 最近一次执行结果摘要publicAuditModelCommand(){this.Key="MyCompany.AuditModelCommand";// 与 .uic 中的引用名逐字符一致}/// <summary>/// 界面每次需要呈现本命令时都会调用:必须快、必须无副作用。/// 返回 CommandState 位组合;具体枚举成员名以本机程序集为准。/// </summary>publicoverrideCommandStateGetState(){CommandStatestate=base.GetState();// 示意:把"可见/启用/勾选"作为相互独立的位来组合,// 而不是用多个布尔属性分散表达。// 实际位名请以本机 CommandState 定义为准确认。returnstate;}/// <summary>无参执行:走同一套逻辑,参数为空。</summary>publicoverridevoidExecute(){Execute(newArrayList());}/// <summary>带参执行:参数由界面传入(例如当前选择集)。</summary>publicoverridevoidExecute(ArrayListarguments){// arguments 的构造方式由调用方决定,使用前必须做空值与数量校验intargCount=(arguments==null)?0:arguments.Count;_lastResult="已处理参数数量:"+argCount;Console.WriteLine("[AuditModelCommand] "+_lastResult);// 执行完毕后通知界面刷新(提示框架状态已变)this.Value=_lastResult;this.ValueChanged=true;}/// <summary>供外部上下文刷新时调用,更新内部状态标记。</summary>publicoverridevoidRefresh(stringcontext){_hasSelection=!string.IsNullOrEmpty(context);base.Refresh(context);}}}

要点提示

  • GetState()里只读内部标记、不做耗时操作——它会被频繁调用。
  • Execute()与Execute(ArrayList)应当收敛到同一段逻辑:Execute()内直接转调带参版本,避免两条路径行为不一致。
  • ValueChanged是命令的一个状态属性,用来向框架表达"值变了";不要把它当成事件处理器去订阅。
  • Refresh(string context)是让命令感知上下文变化的入口,适合更新_hasSelection之类的标记。

2.4.uic片段(构造示例)

<?xml version="1.0" encoding="utf-8"?><!-- 说明:以下节点与属性名仅示意"用命令 Key 关联界面"的组织方式。 .uic 的真实 schema 请以本机随安装提供的 UIC 文件、 或用"工具栏右键 → Customize"导出的结果为准, 不要凭记忆手写。 --><UICustomization><MenuName="MyTools"Caption="MyTools"><!-- Command 属性的值必须与命令构造函数里设置的 Key 逐字符一致 --><MenuItemCommand="MyCompany.AuditModelCommand"Caption="模型巡检"/><MenuItemCommand="Aveva.ShowAttributeBrowserCommand"Caption="属性浏览器"/></Menu></UICustomization>

判定要点:.uic里的Command="…"与命令的Key是同一个字符串。这两处任何一处改动而另一处没改,菜单项就会出现但点了没反应(或根本不出现)。

2.5 两种加载方式对照

方式一:配置文件

<?xml version="1.0" encoding="utf-8"?><!-- <模块名>Customisation.xml 的示意结构;节点名以本机为准 --><Customisation><UICustomizationFiles><!-- 按顺序加载;$1 会被替换为当前工程名 --><File>$1\MyTools.uic</File><File>Common.uic</File></UICustomizationFiles></Customisation>

方式二:代码

// 代码方式加载 UIC:第一个参数是 UIC 文件名,第二个参数是用途/上下文标识// 代码来源性质:按官方 API 签名构造,需在本地环境验证commandBarManager.AddUICustomizationFile("AttributeBrowser.uic","AttributeBrowser");

选择建议:企业统一菜单用配置文件方式(便于项目级$1差异化),单个 Addin 自带的界面用代码方式(随 Addin 一起部署,不用改模块配置)。

三、常见报错与排查

3.1 菜单项出现了,但点了没反应

  • 现象:菜单/工具栏里有这一项,点击无任何响应(不报错、不执行)。
  • 原因:.uic里的Command属性值与命令的Key不一致;或命令对象压根没注册。
  • 排查:三步定位——①打印命令的Key;②在.uic里搜索该字符串,确认存在且逐字符一致;③在Start里确认commandManager.Commands.Add(...)确实执行过(加一行日志)。

3.2 命令执行了,但菜单项完全不出现

  • 现象:日志显示Start执行、注册也成功,但界面里找不到菜单。
  • 原因:UIC 文件没有被加载——CAF_UIC_PATH未生效、<模块名>Customisation.xml没被读到、或<UICustomizationFiles>里没列该文件。
  • 排查:确认CAF_UIC_PATH指向的目录里确实有<模块名>Customisation.xml;确认 XML 语法正确(一个标签没闭合就整份失效);再确认.uic在列表里。

3.3 命令一直灰着,无法点击

  • 现象:菜单项可见但不可点。
  • 原因:GetState()返回的CommandState里没有包含启用位。
  • 排查:检查GetState()的实现是否返回了基类状态、是否在某条分支上漏掉了启用位。注意CommandState是按位组合的,要确认你返回的组合里确实有"启用"。

3.4GetState()一调用界面就卡

  • 现象:打开菜单时明显卡顿。
  • 原因:在GetState()里做了数据库查询或模型写操作;而它会随界面呈现被反复调用。
  • 排查:把耗时逻辑移到Refresh(string context)或Start里做一次,GetState()只读缓存标记。

3.5 事件里Cancel不生效

  • 现象:在BeforeCommandExecute里试图阻止命令执行,但命令照样跑了。
  • 原因:Cancel的位置不对——必须在这个前置事件里取消,且取消的是"本次执行";放到CommandExecuted(后置)里已经晚了。
  • 排查:确认事件挂载的是BeforeCommandExecute而不是CommandExecuted;确认取消操作在该事件的可取消语义范围内(具体 API 以本机 CAF API 参考为准)。

3.6 冲突:旧式事件/工具栏方式与 ribbon 不兼容

  • 现象:用旧式方式挂上去的按钮在 command ribbon 里表现异常。
  • 原因:官方明确指出旧式事件/工具栏方式仍受支持但已不推荐,原因是与 command ribbon 不兼容。
  • 排查:改为官方推荐路径——Command子类 +.uic。同样地,官方也明确不建议从标签页按钮直接调用 PML 宏,也应改成命令对象 +.uic关联。

四、动手练习

  1. 把一个命令挂到菜单上并跑通
    用 2.1 的ShowAttributeBrowserCommand或 2.3 的自定义命令,注册后写一份最小.uic,通过<模块名>Customisation.xml加载。
    判定标准:菜单里出现该项;点击后Execute内的日志被打印且无异常。

  2. 制造并修好"Key 不一致"
    把.uic里的Command值改动一个字符,重启模块,观察现象;再改回来。
    判定标准:能准确描述"菜单项与执行的四种组合"(有项无响应 / 无项有注册 / 两项齐备 / 两项都缺)各自的界面表现,并说出修复动作。

  3. 用BeforeCommandExecute做前置拦截
    给某个命令加前置校验:当不满足条件时取消执行。
    判定标准:不满足条件时命令主体逻辑完全不执行(日志中无主体输出);满足条件时正常执行。同时验证:把同样的拦截逻辑错误地放到CommandExecuted里,拦截不再生效。

  4. 用$1做项目级 UI 差异
    在<模块名>Customisation.xml的<UICustomizationFiles>里使用$1,让不同工程加载不同的 UIC 集合。
    判定标准:能说明$1被替换成什么、以及为什么这种方式比"给每个项目复制一份配置文件"更可维护。

五、小结与下一篇预告

一句话收束:命令体系是"逻辑靠Key自我标识、界面靠Key反向引用"的双向约定。要点四条:

  1. 命令必须继承Command,构造函数里必须设Key;
  2. 状态收拢到GetState()返回的CommandState(按位组合),GetState()必须快且无副作用;
  3. 执行链上两个抓手:BeforeCommandExecute(可Cancel)与CommandExecuted(审计);
  4. 界面出现靠 UIC:<模块名>Customisation.xml列 UIC、CAF_UIC_PATH定位、$1换工程名;旧式事件/工具栏方式不推荐。

下一篇11 数据库 .NET 接口:命令挂上去了,但它要干的活是"读模型、写模型"。下一篇进入Aveva.Core.Database,讲DbElement的三种取元素重载(其中包括那条著名的"无参调用返回null而不是抛异常")、四个数据模型定义类、DbElementType的"每类型单类 + 值语义",以及 Filters/Iterators 与单位体系。

FAQ

Q1:定制命令为什么必须继承Command抽象基类?构造函数里必须设置什么?
CAF 规定所有定制命令类必须继承抽象基类Command,并在构造函数中设置基类的Key属性。Key是整套机制的解耦点:命令类通过它自我标识,.uic文件通过它反向引用命令,界面元素与逻辑之间不需要互相知道对方的存在。构造函数里可以顺带做事件挂接(例如窗口关闭事件、界面布局加载事件),但不要做耗时操作,因为命令对象在启动期就可能被创建。

Q2:Execute()、Execute(ArrayList arguments)、GetState()、Description、Refresh(string context)分别在什么时候被调用?
Execute()是无参执行入口,界面直接触发时走它;Execute(ArrayList arguments)是带参入口,用于把参数从界面传进命令,实践中建议让Execute()转调带参版本以保持行为一致;GetState()在界面需要呈现命令状态时被调用,可能非常频繁,所以必须快且无副作用;Description提供描述文本;Refresh(string context)在上下文变化时被调用,适合更新内部标记(例如是否已有选中元素)。把这五个方法的分工记住,就不会出现"在GetState()里查数据库"这类性能事故。

Q3:GetState()返回的CommandState为什么说"可按位组合"?它实际影响什么?
官方说明GetState()返回CommandState枚举,且该枚举可按位组合。这意味着一个命令的状态不是单选值,而是若干标志位的并集——同一个命令可以同时是"可见、启用、已勾选"。它直接影响界面呈现:菜单项是否出现、是否可点、是否显示勾选。工程上的推论是:把状态统一在GetState()里以位组合的方式返回,不要用多个互不相关的布尔属性分散表达,也不要在状态位之间建立耦合逻辑。

Q4:BeforeCommandExecute与CommandExecuted两个事件分别处在执行链的哪个位置?能做什么?
BeforeCommandExecute在命令执行之前触发,它的关键能力是可以Cancel掉本次执行——适合做权限校验、前置条件校验这类横切逻辑,而且不需要修改命令类本身。CommandExecuted在命令执行之后触发,适合做审计日志(谁在什么时候执行了哪个Key的命令)以及界面联动刷新。两者的顺序不能颠倒:拦截必须放在前置事件里,放到后置事件里命令已经执行完了。

Q5:UIC 文件是什么?它和<模块名>Customisation.xml是什么关系?$1会被替换成什么?
UIC(User Interface Customisation)文件是 XML 格式的界面定义文件,CAF 通过它定义菜单与工具栏,并与命令对象的Key关联,从而实现界面与逻辑解耦。模块级配置文件<模块名>Customisation.xml里的<UICustomizationFiles>按顺序列出要加载的 UIC 文件;文件位置由环境变量CAF_UIC_PATH指定。$1会被替换成当前工程名,因此可以用同一份配置让不同项目加载不同的 UIC 集合。此外也可以用代码方式加载:commandBarManager.AddUICustomizationFile(...);开发期还可以用"工具栏右键 → Customize"交互式地摆好菜单,再回写成.uic。

Q6:为什么不建议从标签页按钮直接调用 PML 宏?官方给的替代方案是什么?
官方明确"不建议从标签页按钮直接调用 PML 宏",原因指向与 command ribbon 的兼容性:直接挂宏绕过了命令体系,命令的状态管理、上下文刷新、权限与审计都无法参与。官方的替代方案是把功能实现为命令对象(可以是 PML command object,也可以是 .NET 的Command子类),再通过.uic文件把工具与命令对象关联起来。官方的 PML 定制手册里也有专门的 Commands 章节与COMMAND对象类型,说明这套机制在 PML 侧同样成立。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 7:05:31

Linux离线安装GCC:依赖拓扑解析与RPM闭包部署指南

简介&#xff1a;本资源是一套专为离线环境设计的GCC编译器完整离线安装方案&#xff0c;面向Linux系统运维人员、嵌入式开发工程师及无网络条件的教学/生产服务器管理员&#xff0c;解决在CentOS/RHEL 6类系统中无法联网安装GCC核心组件的痛点。压缩包共12个文件&#xff0c;含…

作者头像 李华
网站建设 2026/10/17 7:04:32

从成本中心到增长引擎:电商AI客服系统落地与ROI实战指南

很多做电商的朋友跟我聊起客服团队&#xff0c;第一反应就是成本高、人难招、流动性大。特别是大促期间&#xff0c;咨询量翻倍&#xff0c;客服根本接不过来&#xff0c;眼睁睁看着订单流失。大家也都知道AI能帮忙&#xff0c;但多数人把“智能客服”简单理解成一个自动回复机…

作者头像 李华
网站建设 2026/10/17 7:04:02

永磁同步电机参数辨识实战:离线辨识流程与工程疑难点解析

一台陌生电机摆在面前&#xff0c;能让一个做驱动控制的人瞬间头疼的&#xff0c;永远不是那个铭牌上简简单单的额定功率和额定电流&#xff0c;而是矢量控制算法里必须要填、却一个都没写的那些电气参数。定子电阻是多少&#xff0c;d轴电感到底是0.3毫亨还是0.8毫亨&#xff…

作者头像 李华
网站建设 2026/10/17 7:03:41

智驾ADAS全解析:从传感器融合到端到端模型的应用趋势

智驾ADAS这两年的话题热度&#xff0c;从技术圈一路烧到了普通购车用户嘴边。随便刷几条汽车评测&#xff0c;都能看到"城市NOA开城大战""端到端大模型上车"这类说法。但热闹归热闹&#xff0c;作为一个长期跟踪智驾落地情况的人&#xff0c;我更关注的是一…

作者头像 李华
网站建设 2026/10/17 7:03:04

AI Agent Harness工程:从容量规划到弹性扩展的实战指南

做 Agent 系统也快两年了&#xff0c;最深的体会就是&#xff1a;代码跑通只是起点&#xff0c;让一堆 Agent 在线上稳定地跑起来、而且账单不失控&#xff0c;才是真正考验架构功夫的地方。近期被问得最多的不是 Prompt 怎么写、工具怎么接&#xff0c;而是“我们这套 Agent 服…

作者头像 李华
网站建设 2026/10/17 7:02:45

fixed定位被滚动条遮挡?从滚动条占位到Pxtorem适配全解析

好几天前&#xff0c;我在调一个后台管理系统的悬浮操作栏&#xff0c;代码明明写的position: fixed; right: 0; top: 80px;&#xff0c;桌面端预览时却总觉得不对劲&#xff1a;操作栏右边缘和窗口右侧贴得死死的&#xff0c;滚动条一出现&#xff0c;右边一排按钮就被“咬”掉…

作者头像 李华