只需 3 步,在 Access 里实现网页版树控件(含源码)
摘要本文完整演示如何在 Access 窗体里做一个类似资源管理器的树控件不用 TreeView OCX不用注册组件只用 WebBrowser 控件 一个 VBA 标准模块。文章包含控件清单、控件命名、窗体代码、模块源码、从表加载数据的方法复制即可测试。Hi大家好Access 里经常会遇到这种界面需求左边是一个分类树右边是对应明细。比如所有 成品类 通用零部件 标准件 专用零部件 GM电机 025 040 075 150用户点左边的分类右边的数据跟着变化。这个界面很常见产品分类、BOM、组织架构、菜单权限、项目目录都会用到。但 Access 原生控件里没有一个特别省心的树控件。以前很多人会用Microsoft TreeView Control也就是MSCOMCTL.OCX里的 TreeView。它能用但也有几个麻烦点问题表现要注册 OCX换电脑容易出问题32/64 位差异Office 位数不一样时容易踩坑部署麻烦客户电脑环境不统一时不好排查样式不好改想做成自己想要的样子不方便所以这篇文章换一个思路用 Access 自带的 WebBrowser 控件显示一段 HTML 树再用 VBA 和里面的 JS 通信。最终效果是有小三角展开 / 折叠有黄色文件夹图标和文件图标点击节点可以选中VBA 可以读到选中节点 ID 和文字支持搜索、全部展开、全部折叠可以从表或查询直接加载树数据下面直接从零开始做。一、最终要做出来的界面窗体上主要分两块区域控件左侧或主体WebBrowser 控件用来显示树上方工具区搜索框、搜索按钮、展开按钮、折叠按钮下方或旁边Label用来显示当前选中节点树控件本身长这样▾ 公司总部 ▾ 研发中心 ▾ 前端组 张三 李四 ▾ 后端组 王五 ▾ 市场部 ▾ 华北区 北京办事处 ▸ 华南区 ▾ 财务部 账务核算 出纳管理其中显示元素说明▸当前节点可展开但现在是折叠状态▾当前节点已经展开黄色文件夹分类节点白色文件末级节点灰色背景当前选中的节点二、先建窗体放这些控件新建一个空白窗体命名为frmTreeDemo。然后放下面这些控件。控件类型控件名作用WebBrowserWebBrowser0显示树控件文本框txtSearch输入搜索关键字按钮btnSearch搜索按钮按钮btnExpandAll全部展开按钮btnCollapseAll全部折叠按钮btnGetSelected手动读取当前选中节点标签lblSelected显示当前选中节点注意两点WebBrowser 控件可以从 ActiveX 控件里插入选择Microsoft Web Browser。如果 VBE 编译时提示类型相关问题去工具 - 引用里勾选Microsoft Internet Controls。控件大小可以自己调整。一般我会把 WebBrowser 放在窗体左侧宽度 2500 到 3500 twips高度占满主体区域。三、树节点数据怎么写这个树使用最常见的id pid结构。一条节点数据长这样{id:1,pid:0,text:公司总部,icon:folder}字段说明字段说明id节点唯一编号pid父节点编号根节点固定写0text节点显示文字icon图标常用folder或file比如[{id:1,pid:0,text:公司总部,icon:folder},{id:2,pid:1,text:研发中心,icon:folder},{id:3,pid:2,text:前端组,icon:folder},{id:4,pid:3,text:张三,icon:file}]显示出来就是公司总部 研发中心 前端组 张三四、先导入标准模块 Module_Tree打开 VBA 编辑器菜单选择文件 - 导入文件导入下面这个标准模块模块名叫Module_Tree。这个模块提供这些通用函数函数作用TreeInit初始化树控件TreeSetData替换全部节点数据TreeAddNode动态追加节点TreeGetSelected获取选中节点 IDTreeGetText获取选中节点文字TreeExpandAll全部展开TreeCollapseAll全部折叠TreeSearch搜索节点TreeClear清空树TreeFromRecordset从 Recordset 加载树下面是完整源码。Attribute VB_Name Module_Tree Option Compare Database Option Explicit 模块名称: Module_Tree 功能描述: Access Web 控件 - 树控件 (TreeView) 版本: 1.0 Private Sub S(ByRef buf As String, ByVal line As String) buf buf line vbCrLf End Sub Public Sub TreeInit(ByVal wb As Object, _ Optional ByVal sData As String [], _ Optional ByVal sTheme As String light, _ Optional ByVal nHeight As Long 0) Dim html As String html BuildTreeHTML(sData, sTheme, nHeight) wb.Navigate about:blank Do While wb.ReadyState 4 DoEvents Loop wb.Document.Write html wb.Document.Close End Sub Public Sub TreeSetData(ByVal wb As Object, ByVal sData As String) On Error Resume Next wb.Document.parentWindow.execScript treeSetData( sData ), JScript End Sub Public Sub TreeAddNode(ByVal wb As Object, _ ByVal sId As String, _ ByVal sPid As String, _ ByVal sText As String, _ Optional ByVal sIcon As String file) Dim js As String js treeAddNode({id: EscJS(sId) _ ,pid: EscJS(sPid) _ ,text: EscJS(sText) _ ,icon: EscJS(sIcon) }) On Error Resume Next wb.Document.parentWindow.execScript js, JScript End Sub Public Function TreeGetSelected(ByVal wb As Object) As String On Error Resume Next TreeGetSelected wb.Document.getElementById(_treeSelectedId).Value If Err.Number 0 Then TreeGetSelected End Function Public Function TreeGetText(ByVal wb As Object) As String On Error Resume Next TreeGetText wb.Document.getElementById(_treeSelectedText).Value If Err.Number 0 Then TreeGetText End Function Public Sub TreeExpandAll(ByVal wb As Object) On Error Resume Next wb.Document.parentWindow.execScript treeExpandAll(), JScript End Sub Public Sub TreeCollapseAll(ByVal wb As Object) On Error Resume Next wb.Document.parentWindow.execScript treeCollapseAll(), JScript End Sub Public Sub TreeSearch(ByVal wb As Object, ByVal sKeyword As String) On Error Resume Next wb.Document.parentWindow.execScript _ treeSearch( EscJS(sKeyword) ), JScript End Sub Public Sub TreeClear(ByVal wb As Object) On Error Resume Next wb.Document.parentWindow.execScript treeSetData([]), JScript End Sub Public Sub TreeFromRecordset(ByVal wb As Object, _ ByVal rs As Object, _ ByVal sIdField As String, _ ByVal sPidField As String, _ ByVal sTextField As String, _ Optional ByVal sIconField As String , _ Optional ByVal sTheme As String light) Dim sData As String Dim sId As String, sPid As String, sText As String, sIcon As String Dim bFirst As Boolean bFirst True sData [ Do While Not rs.EOF sId Nz(rs.Fields(sIdField).Value, ) sPid Nz(rs.Fields(sPidField).Value, 0) sText Nz(rs.Fields(sTextField).Value, ) If sIconField Then sIcon Nz(rs.Fields(sIconField).Value, file) Else sIcon file End If If Not bFirst Then sData sData , sData sData {id: EscJS(sId) , _ pid: EscJS(sPid) , _ text: EscJS(sText) , _ icon: EscJS(sIcon) } bFirst False rs.MoveNext Loop sData sData ] TreeInit wb, sData, sTheme End Sub Private Function EscJS(ByVal s As String) As String s Replace(s, \, \\) s Replace(s, , \) s Replace(s, , \) s Replace(s, vbCrLf, \n) s Replace(s, vbCr, \n) s Replace(s, vbLf, \n) EscJS s End Function Private Function BuildTreeHTML(ByVal sData As String, _ ByVal sTheme As String, _ ByVal nHeight As Long) As String Dim h As String Dim cBg As String, cFg As String, cHover As String Dim cSelected As String, cLine As String, cSearch As String Dim hStyle As String If sTheme dark Then cBg #202124 cFg #e8eaed cHover #2f3136 cSelected #3a3d42 cLine #696c72 cSearch #6f5a18 Else cBg #ffffff cFg #1f1f1f cHover #f5f7fb cSelected #eeeeee cLine #b8b8b8 cSearch #fff2a8 End If If nHeight 0 Then hStyle height: nHeight px;overflow:auto; Else hStyle height:100%;overflow:auto; End If S h, !DOCTYPE htmlhtmlhead S h, meta http-equivX-UA-Compatible contentIEedge S h, meta charsetutf-8 S h, style S h, html,body{width:100%;height:100%;margin:0;padding:0;background: cBg ;color: cFg ;font-family:Segoe UI,Microsoft YaHei,SimSun,Arial,sans-serif;font-size:12px;} S h, #tree-wrap{ hStyle padding:2px 0 4px 0;white-space:nowrap;} S h, .tree-row{height:22px;line-height:22px;cursor:default;white-space:nowrap;} S h, .tree-row:hover{background: cHover ;} S h, .tree-row.selected{background: cSelected ;} S h, .tree-row.root .node-text{font-weight:600;} S h, .toggle{display:inline-block;width:16px;height:22px;line-height:22px;text-align:center;color:#777;font-size:12px;vertical-align:middle;cursor:pointer;} S h, .toggle.empty{cursor:default;color:transparent;} S h, .guide{display:inline-block;width:18px;height:22px;border-left:1px dotted cLine ;vertical-align:middle;margin-left:8px;} S h, .ico-folder,.ico-file{position:relative;display:inline-block;vertical-align:middle;margin-right:5px;overflow:hidden;} S h, .ico-folder{width:14px;height:10px;margin-left:2px;margin-top:2px;background:#d9b45f;border:1px solid #a47d2d;} S h, .ico-folder span{position:absolute;left:1px;top:0;width:7px;height:3px;background:#f0d27a;border-right:1px solid #a47d2d;border-bottom:1px solid #a47d2d;overflow:hidden;} S h, .ico-file{width:9px;height:12px;margin-left:4px;background:#f7f7f7;border:1px solid #9a9a9a;} S h, .ico-file span{position:absolute;right:0;top:0;width:3px;height:3px;background:#dcdcdc;border-left:1px solid #9a9a9a;border-bottom:1px solid #9a9a9a;overflow:hidden;} S h, .node-text{display:inline-block;vertical-align:middle;padding:0 3px 0 1px;color: cFg ;text-decoration:none;} S h, .tree-row.selected .node-text{color: cFg ;} S h, .hit{background: cSearch ;color:#111;padding:0 1px;} S h, /style S h, /headbody S h, div idtree-wrap/div S h, input typehidden id_treeSelectedId value S h, input typehidden id_treeSelectedText value S h, script typetext/javascript S h, var _nodes[]; S h, var _expanded{}; S h, var _selectedId; S h, var _searchKw; S h, var _initData sData ; S h, function treeSetData(data){ S h, _nodesdata||[]; _expanded{}; _selectedId; _searchKw; S h, for(var i0;i_nodes.length;i){_nodes[i]._idxi;} S h, document.getElementById(_treeSelectedId).value; S h, document.getElementById(_treeSelectedText).value; S h, renderTree(); S h, } S h, function treeAddNode(node){node._idx_nodes.length; _nodes.push(node); renderTree();} S h, function treeExpandAll(){for(var i0;i_nodes.length;i){if(hasChildren(_nodes[i].id)){_expanded[_nodes[i].id]true;}} renderTree();} S h, function treeCollapseAll(){_expanded{}; renderTree();} S h, function treeSearch(kw){ S h, _searchKwtrimText(kw).toLowerCase(); S h, if(_searchKw){for(var i0;i_nodes.length;i){var txtString(_nodes[i].textnull?:_nodes[i].text).toLowerCase(); if(txt.indexOf(_searchKw)0){expandParents(_nodes[i].id);}}} S h, renderTree(); S h, } S h, function expandParents(id){var pidgetPid(id); while(pid pid!0){_expanded[pid]true; pidgetPid(pid);}} S h, function getPid(id){for(var i0;i_nodes.length;i){if(_nodes[i].idid){return _nodes[i].pid;}} return null;} S h, function getChildren(pid){var r[]; for(var i0;i_nodes.length;i){if(_nodes[i].pidpid){r[r.length]_nodes[i];}} return r;} S h, function hasChildren(id){for(var i0;i_nodes.length;i){if(_nodes[i].pidid){return true;}} return false;} S h, function trimText(v){return String(vnull?:v).replace(/^\s|\s$/g,);} S h, function htmlEncode(v){return String(vnull?:v).replace(//g,amp;).replace(//g,lt;).replace(//g,gt;).replace(//g,quot;).replace(//g,#39;);} S h, function highlightText(text){ S h, textString(textnull?:text); if(!_searchKw){return htmlEncode(text);} S h, var lowtext.toLowerCase(); var poslow.indexOf(_searchKw); S h, if(pos0){return htmlEncode(text);} S h, return htmlEncode(text.substring(0,pos))span classhithtmlEncode(text.substr(pos,_searchKw.length))/spanhtmlEncode(text.substring(pos_searchKw.length)); S h, } S h, function renderGuide(depth){var s; for(var i0;idepth;i){sspan classguide/span;} return s;} S h, function renderNode(node,depth){ S h, var hchasChildren(node.id); var open!!_expanded[node.id]; var idxnode._idx; S h, var cls(node.id_selectedId?selected :)(depth0?root :); S h, var togglehc?span classtoggle onclicktreeToggle(idx);return false;(open?#9662;:#9656;)/span:span classtoggle emptynbsp;/span; S h, var iconNameString(node.iconnull?:node.icon).toLowerCase(); S h, var ico(iconNamefile||(!hciconName!folder))?ico-file:ico-folder; S h, var htmldiv classtree-row cls onclicktreeSelect(idx);return false;; S h, htmlrenderGuide(depth)togglespan classicospan/span/spanspan classnode-texthighlightText(node.text)/span/div; S h, if(hc open){var chgetChildren(node.id); for(var i0;ich.length;i){htmlrenderNode(ch[i],depth1);}} S h, return html; S h, } S h, function renderTree(){var rootsgetChildren(0); var html; for(var i0;iroots.length;i){htmlrenderNode(roots[i],0);} document.getElementById(tree-wrap).innerHTMLhtml;} S h, function treeToggle(idx){var ewindow.event; if(e){e.cancelBubbletrue;} var n_nodes[idx]; if(!n){return false;} _expanded[n.id]!_expanded[n.id]; renderTree(); return false;} S h, function treeSelect(idx){ S h, var n_nodes[idx]; if(!n){return false;} S h, _selectedIdn.id; S h, document.getElementById(_treeSelectedId).valuen.id; S h, document.getElementById(_treeSelectedText).valueString(n.textnull?:n.text); S h, renderTree(); S h, try{window.external.TreeNodeSelected(n.id,String(n.textnull?:n.text));}catch(ex){} S h, return false; S h, } S h, treeSetData(_initData); S h, /script S h, /body/html BuildTreeHTML h End Function五、窗体代码完整贴出来标准模块导入以后再回到frmTreeDemo的代码模块。把下面代码复制进去。Option Compare Database Option Explicit Private mLastSelectedId As String Private Sub Form_Load() Dim sData As String sData [ sData sData {id:1,pid:0,text:公司总部,icon:folder}, sData sData {id:2,pid:1,text:研发中心,icon:folder}, sData sData {id:3,pid:2,text:前端组,icon:folder}, sData sData {id:4,pid:3,text:张三,icon:file}, sData sData {id:5,pid:3,text:李四,icon:file}, sData sData {id:6,pid:2,text:后端组,icon:folder}, sData sData {id:7,pid:6,text:王五,icon:file}, sData sData {id:8,pid:1,text:市场部,icon:folder}, sData sData {id:9,pid:8,text:华北区,icon:folder}, sData sData {id:10,pid:9,text:北京办事处,icon:file}, sData sData {id:11,pid:8,text:华南区,icon:folder}, sData sData {id:12,pid:1,text:财务部,icon:folder}, sData sData {id:13,pid:12,text:账务核算,icon:file}, sData sData {id:14,pid:12,text:出纳管理,icon:file} sData sData ] TreeInit Me.WebBrowser0, sData, light TreeExpandAll Me.WebBrowser0 Me.lblSelected.Caption 请点击节点选择... Me.TimerInterval 200 End Sub Private Sub Form_Timer() Dim sId As String Dim sText As String sId TreeGetSelected(Me.WebBrowser0) If sId mLastSelectedId Then mLastSelectedId sId sText TreeGetText(Me.WebBrowser0) If sId Then Me.lblSelected.Caption 请点击节点选择... Else Me.lblSelected.Caption ID: sId | sText End If End If End Sub Private Sub btnSearch_Click() TreeSearch Me.WebBrowser0, Me.txtSearch.Value End Sub Private Sub txtSearch_Change() TreeSearch Me.WebBrowser0, Me.txtSearch.Text End Sub Private Sub txtSearch_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode 13 Then TreeSearch Me.WebBrowser0, Me.txtSearch.Value End If End Sub Private Sub btnExpandAll_Click() TreeExpandAll Me.WebBrowser0 End Sub Private Sub btnCollapseAll_Click() TreeCollapseAll Me.WebBrowser0 End Sub Private Sub btnGetSelected_Click() Dim sId As String Dim sText As String sId TreeGetSelected(Me.WebBrowser0) sText TreeGetText(Me.WebBrowser0) If sId Then Me.lblSelected.Caption 未选中任何节点 Else Me.lblSelected.Caption ID: sId | sText End If End Sub这里我用了一个Form_Timer。原因是节点点击发生在 WebBrowser 内部Access 窗体本身不会像普通按钮那样自动触发事件。所以我用每 200 毫秒读取一次选中 ID 的方式只要发现选中节点变了就更新lblSelected。这个方法简单也够稳定。六、从表里加载树数据实际项目里不建议一直手写 JSON。更常见的做法是建一张树节点表。表名可以叫t_TreeNode。字段设计如下字段名类型说明NodeID短文本节点 IDParentID短文本父节点 ID根节点填0NodeText短文本节点文字Icon短文本folder或fileSortNo数字排序示例数据NodeIDParentIDNodeTextIconSortNo10公司总部folder121研发中心folder232前端组folder343张三file453李四file5然后用下面代码加载Private Sub LoadTreeFromTable() Dim db As DAO.Database Dim rs As DAO.Recordset Set db CurrentDb Set rs db.OpenRecordset( _ SELECT NodeID, ParentID, NodeText, Icon _ FROM t_TreeNode ORDER BY SortNo, dbOpenSnapshot) TreeFromRecordset Me.WebBrowser0, rs, _ NodeID, ParentID, NodeText, Icon, light rs.Close Set rs Nothing Set db Nothing End Sub以后要改树结构就改表数据不用改窗体代码。七、几个容易出错的地方1. WebBrowser 控件名字必须对文章里的代码用的是Me.WebBrowser0如果你的控件叫wbTree那所有地方都要改成Me.wbTree2. 根节点的 pid 必须是字符串 0根节点这样写{id:1,pid:0,text:公司总部,icon:folder}不要写空也不要写 Null。3. WebBrowser 里不要写太新的 JSAccess 的 WebBrowser 还是 IE/JScript 那套环境。所以源码里尽量不用这些东西不建议写法原因let/const老 JScript 不支持箭头函数老 JScript 不支持Array.find兼容性不稳Array.filter兼容性不稳复杂正则拼接容易弹脚本错误这也是为什么上面的 JS 看起来比较老派。在 Access 里稳定比新潮更重要。4. 节点文字里有引号怎么办如果节点文字里有单引号、双引号、换行手工拼 JSON 时要小心。更推荐从表里加载或者用TreeFromRecordset模块里已经做了一层转义处理。八、常用调用方式汇总初始化TreeInit Me.WebBrowser0, sData, light全部展开TreeExpandAll Me.WebBrowser0全部折叠TreeCollapseAll Me.WebBrowser0搜索TreeSearch Me.WebBrowser0, Me.txtSearch.Value读取选中 IDsId TreeGetSelected(Me.WebBrowser0)读取选中文字sText TreeGetText(Me.WebBrowser0)从 Recordset 加载TreeFromRecordset Me.WebBrowser0, rs, NodeID, ParentID, NodeText, Icon九、适合用在哪里这个树控件适合这些界面场景用法产品分类左边分类树右边产品列表BOM左边部件树右边零件明细组织架构部门、岗位、人员分层展示菜单权限菜单层级展示和权限设置项目资料项目、阶段、文档分层最常见的组合是左侧WebBrowser 树控件 右侧连续窗体或子窗体当用户点击左侧树节点时右侧子窗体按节点 ID 筛选。示例Private Sub Form_Timer() Dim sId As String sId TreeGetSelected(Me.WebBrowser0) If sId mLastSelectedId Then mLastSelectedId sId Me.subProduct.Form.Filter CategoryID Replace(sId, , ) Me.subProduct.Form.FilterOn True End If End Sub这样就是一个典型的“左树右表”界面。十、总结这套方案的核心不是 Web 技术而是把 Access 里不好用的树控件封装成一个模块。以后再用树控件只需要做三件事步骤做什么1窗体上放WebBrowser02导入Module_Tree.bas3调用TreeInit Me.WebBrowser0, sData控件多一点没关系关键是命名要对。模块代码长一点也没关系关键是窗体调用要简单。我比较喜欢这种写法TreeInit Me.WebBrowser0, sData, light复杂的 HTML、CSS、JS 都藏在模块里。窗体上只保留几行能看懂的 VBA。这也是 Access 项目长期维护时很重要的一点不要让每个窗体都堆一大段重复代码把通用能力封装成模块。如果你正在做 Access 项目遇到产品分类、BOM、组织架构、菜单权限、左树右表这些需求可以直接照着这篇搭一个出来。后面还可以继续扩展比如节点双击、右键菜单、懒加载、勾选框、多选节点等。需要 Access 开发、培训、框架设计或系统改造的朋友可以添加 edonsoft 交流。

相关新闻

从零开始手写一个协程库(一)

从零开始手写一个协程库(一)

引言从本篇文章开始,我会逐一介绍协程库的一些重要代码和知识点~~~什么是协程库?简单来说就是一个超级轻量级的线程,这个线程还是用户态线程。那么我们为什么要实现协程库呢?在高并发的环境下,如果因为一个任务的阻塞而…

2026/7/1 4:17:19阅读更多 →
上班族10分钟轻养生挑战!7天亲测:气血感/睡眠/脾胃全记录

上班族10分钟轻养生挑战!7天亲测:气血感/睡眠/脾胃全记录

为什么是"10分钟轻养生"很多人听到"养生",第一反应是:没时间、太复杂、坚持不下来。但养生的本质,是在日常习惯里做微小但持续的改变。每天10分钟,做满7天,身体的反馈往往比想象中明显。这次挑战&…

2026/7/1 4:17:19阅读更多 →
2026 年芝加哥自动化展聚焦人形机器人:工业应用重任务解决能力而非外形!

2026 年芝加哥自动化展聚焦人形机器人:工业应用重任务解决能力而非外形!

真正重要的,不是像不像人,而是会不会解决问题人形机器人因外形熟悉易让人将“像人”与“聪明”联系,适合展示和吸引投资。但企业应用时更关注其认知能力,具备该能力的机器人不一定像人,人形设计有优势但特定任务下轮式…

2026/7/1 4:12:19阅读更多 →
别再手动算排名了!用Matlab实现TOPSIS评价模型,5分钟搞定水质评估案例

别再手动算排名了!用Matlab实现TOPSIS评价模型,5分钟搞定水质评估案例

别再手动算排名了!用Matlab实现TOPSIS评价模型,5分钟搞定水质评估案例水质评估是环境监测中的常见需求,但面对pH值、溶解氧、氨氮含量等多项指标时,如何科学量化不同采样点的综合水质水平?传统方法往往依赖专家经验或简…

2026/7/1 5:17:22阅读更多 →
爬虫开发实战:识别与规避反爬蜜罐(Web陷阱)的技术指南

爬虫开发实战:识别与规避反爬蜜罐(Web陷阱)的技术指南

1. 项目概述:当爬虫遇上“甜蜜的陷阱”做爬虫开发的朋友,估计没少和“反爬虫”斗智斗勇。从简单的User-Agent校验、IP频率限制,到复杂的验证码、动态加密参数,这些明面上的对抗大家已经习以为常。但今天要聊的,是一种更…

2026/7/1 5:17:22阅读更多 →
西安军工科研院所首选:满足信创要求的国产数字孪生仿真引擎有哪些?

西安军工科研院所首选:满足信创要求的国产数字孪生仿真引擎有哪些?

一、 西安军工产业的“信创”与“数字孪生”双重挑战西安,作为我国重要的国防科技工业基地,聚集了大量的军工科研院所和装备制造企业。这些单位在进行数字化转型,尤其是构建数字孪生系统时,面临着两个极为特殊且必须解决的核心矛盾…

2026/7/1 5:17:22阅读更多 →
周报日报生成与办公效率工具应用指南

周报日报生成与办公效率工具应用指南

一、职场周报日报场景的效率痛点 职场中,周报日报是高频但耗时的工作环节。多数职场人写周报时,需从零散的工作记录、会议纪要、项目数据中汇总内容,回忆与整理占据大量时间;日报则因每日重复撰写,易陷入格式固化、内…

2026/7/1 5:17:22阅读更多 →
保姆级教程:用LLaMA Factory的Web UI,在单张V100上微调Yi-6B模型(附完整参数配置)

保姆级教程:用LLaMA Factory的Web UI,在单张V100上微调Yi-6B模型(附完整参数配置)

零门槛实战:在单卡V100上通过Web UI高效微调Yi-6B模型当大模型技术席卷全球时,许多开发者和研究者却被复杂的命令行操作和晦涩的参数配置挡在门外。本文将带你用最直观的Web界面,在一张V100显卡上完成Yi-6B模型的完整微调流程。无需担心显存不…

2026/7/1 5:17:22阅读更多 →
CAD二次开发中DoubleCollection用法详解

CAD二次开发中DoubleCollection用法详解

在 AutoCAD .NET API 中,DoubleCollection 是一个用于存储双精度浮点数集合的类,常用于定义多段线(Polyline)的顶点坐标或样条曲线的拟合点等。 核心用法 DoubleCollection 通常作为参数传递给需要一系列连续数值的构造函数或方…

2026/7/1 5:12:22阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/1 4:42:14阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/1 5:19:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →