原生JavaScript实现可关闭多窗口Tab组件:零依赖状态管理实战
简介这是一份基于Bootstrap与jQuery实现的可删除Tab多窗口交互示例面向需要快速掌握前端标签页切换与动态增删操作的Web开发初学者、前端实习生或课程设计者。资源包内含完整HTML演示页面与两个JavaScript脚本以tabsDemo形式直观展示nav-tabs与tab-content的联动、data-toggle属性触发切换以及通过jQuery移除对应li与内容面板的完整思路。整个压缩包仅3个文件大小12KB代码精简便于直接阅读、调试和二次改动。已有697人学习使用。示例中针对删除当前激活Tab后的active状态重置、面板同步移除等关键细节做了清晰处理读者可在此基础上轻松扩展新增Tab、状态保存与恢复等能力适合作为日常开发、组件封装或前端课程作业的参考模板。1. 这个Tab面板项目零依赖、可关闭、能扩展的多窗口容器后台管理系统的功能早已不是一屏一页而是多个模块同时在手边开着。这个用原生HTML/CSS/JavaScript实现的Tab多窗口组件允许在同一个页面里打开多个内容窗口随时切换也支持一键删除不需要的标签页没有引入任何前端框架拷贝index.html、tab.css和tab.js三个文件就能跑。它适合需要维护老系统、不想为一个小交互引出整套依赖的开发者也适合拿来练手事件绑定、状态同步和DOM渲染。它可以被改造成浏览器应用内的多页面容器也可以作为独立学习模板。我拆这版代码时最看重的是三件事标签能不能像浏览器一样自然关闭、关闭后焦点会不会丢、二次开发时会不会被一堆绑定事件绊住脚。2. 结构与数据流三个文件和一个数组搭起的Tab骨架这个项目的核心价值不在文件多而在于数据流是单向的。所有操作都修改tabs数组然后调用renderTabs()把整个Tab条重新画一遍。这种写法牺牲了一点点性能换来了逻辑上的绝对清晰很适合中小型页面。如果你以前跳进过这个Tab为什么删不掉的坑多半是因为事件和DOM状态散落在各个回调里而这里把状态都收敛到了一个数组里。2.1 目录结构与职责划分项目拆开来看就是三个普通文件。index.html负责给Tab条和内容区提供挂载点tab.css负责所有视觉表现tab.js则集中处理标签页的状态、渲染和交互。没有构建工具也不需要npm install。这种零依赖形态最大的好处是拿给老项目用的时候不用担心引入某个框架运行时复制过去就能跑。tab-manager/ ├── index.html ├── css/ │ └── tab.css └── js/ └── tab.js我在写这类组件时习惯把css和js独立出去而不是都塞进html里。这样后面要把这个Tab管理器移植到其他项目只需要拷贝目录然后按自己项目的路径修改引用即可。index.html里建议把结构保持得足够简单Tab条只留一个容器内容区只留一个容器所有动态元素都由js生成。举个例子index.html的骨架是这样div idapp div classtab-header div idtabBar classtab-bar/div button idaddBtn classadd-btn title新建标签/button /div div idcontentArea classcontent-area/div /divtab-bar是Tab条addBtn是右侧的新建按钮contentArea是内容容器。实际运行时tab-bar里的每一个标签、每一个关闭按钮都是js动态生成的html里只留空壳。这样做的好处是无论页面打开多少Tab初始结构都是同一段静态代码排查问题的时候能少看一截。tab.css管住了三种视觉状态普通标签、激活标签和关闭按钮。激活标签用border-top加字体颜色变化让用户一眼看出当前焦点。关闭按钮单独放在标签右侧和标题用gap隔开避免和标题文字混在一起。hover效果只加在关闭按钮上用户不会因为鼠标经过标签就被误引导去点关闭。2.2 数据模型用数组驱动视图而不是直接改DOMTab管理器最忌讳的是在各个事件回调里东改一个class、西改一个display最后逻辑全乱。这里的核心状态收敛成一个数组和一个字符串tabs保存所有标签页activeId保存当前激活的标签id。每条tab记录的是id、title和panelKey三个字段panelKey决定这个标签打开后内容区渲染什么内容。let tabs []; // 每个元素{ id, title, panelKey } let activeId ; // 当前激活标签id const panels { dashboard: div classpage控制台面板/div, orders: div classpage订单列表/div, user: div classpage用户管理/div, blank: div classpage空白页/div };这里有一个值得留意的设计细节内容面板不是存放在tab对象里的而是用panelKey去panels映射里取。如果直接把HTML字符串塞进tabs数组数据量会变得很大而且同一个模块被打开两次时两份字符串会同时存在状态容易不一致。用panelKey做映射tabs数组只存轻量的索引内容区渲染时统一从panels里取。有了这个数据模型之后所有对标签的操作都可以归结为一次数组操作加一次重绘。比如打开新标签就是push一条数据并切换activeId关闭标签就是splice删掉一条数据并重新指定activeId。视图层只认tabs和activeId不关心用户是通过按钮、快捷键还是右键菜单触发的操作。function renderTabs() { tabBar.innerHTML ; tabs.forEach(tab { const item document.createElement(div); item.className tab-item (tab.id activeId ? active : ); item.dataset.id tab.id; const title document.createElement(span); title.className tab-title; title.textContent tab.title; item.appendChild(title); const close document.createElement(span); close.className tab-close; close.textContent ×; item.appendChild(close); tabBar.appendChild(item); }); }renderTabs每次都会把tabBar里的所有子节点清空再根据tabs数组重建。很多人会觉得这样频繁操作DOM效率低实际上在几十个标签页的场景里重建成本完全可接受而且它能避免新增状态时忘了更新界面的问题。真正需要担心的不是重绘次数而是事件监听是否重复这一点到第4章避坑部分会细说。2.3 页面骨架与交互从静态结构到可操作入口有了数据和渲染函数还缺两个入口新建按钮和键盘快捷键。这里把它们放在同一个地方初始化是因为它们都指向同一个createTab函数不会产生多入口导致的逻辑分裂。document.getElementById(addBtn).addEventListener(click, () { createTab(新标签, blank); }); document.addEventListener(keydown, e { if (e.ctrlKey e.key.toLowerCase() t) { e.preventDefault(); createTab(新标签, blank); } if (e.ctrlKey e.key.toLowerCase() w activeId) { e.preventDefault(); closeTab(activeId); } });CtrlT新建、CtrlW关闭这两个快捷键是浏览器用户最熟悉的习惯直接映射到Tab管理器里几乎没有学习成本。keydown事件挂在document上即使焦点在输入框里也能触发这是好事也是麻烦如果用户在表单里按CtrlT页面会突然多出一个标签。实际使用时可以在createTab里判断document.activeElement是不是input或者textarea是就忽略这个问题在第4章也会提到。到这里一个最小可用的Tab管理器已经成型点击加号能新建点击标签能切换点击关闭能删除。下一章把这三条链路拆开来看看看每个函数在真实操作时是怎么协作的。3. 核心模块拆解新建、切换、删除的完整链路这一章把TurboTax那套纸面逻辑落到执行层。写完createTab、switchTab、closeTab三个函数再看事件委托怎么跟它们对接这个项目就算真正吃透了。3.1 新建Tab自增id与默认激活策略createTab是整个组件的统一入口不管是点击加号、CtrlT还是后续从业务页面里点击一个打开报表的按钮最终都要走到这里。它需要完成三件事生成唯一id、把tab推进数组、把activeId指向新tab。let seq 0; function createTab(title, panelKey) { const id tab- (seq); tabs.push({ id, title, panelKey }); activeId id; // 新开标签默认激活 renderTabs(); renderContent(); }id使用递增序号拼接tab-前缀保证在单个页面生命周期内不会重复。为什么不直接使用时间戳因为同一毫秒内连续创建两个标签时时间戳可能一样而seq是单线程递增在js的执行模型里绝对不重复。panelKey在调用时由业务方传入比如orders、usercreateTab本身不需要关心panelKey对应什么内容这样组件和业务解耦。renderContent根据activeId找到对应tab的panelKey再从panels里取出HTML填到contentArea。跟renderTabs一样它也是全量重绘。因为面板内容是固定的HTML片段重绘不会丢失用户输入但要注意如果面板里有输入框切换走再切回来输入内容会丢失。这是纯静态HTML片段的限制后面第5章会给一个用iframe或真实组件替代的方案。3.2 切换Tab事件委托与renderTabs的联动标签条上的点击事件不能每次都绑到新建的DOM上否则标签增删几十次后监听器数量会爆炸。我在tabBar这个不变的外层容器上挂了一个click监听用closest找到真正被点中的节点。这个写法能让代码同时兼容点到标题和点到关闭按钮两种情况。tabBar.addEventListener(click, (e) { const closeBtn e.target.closest(.tab-close); if (closeBtn) { const item closeBtn.closest(.tab-item); closeTab(item.dataset.id); return; } const item e.target.closest(.tab-item); if (item) switchTab(item.dataset.id); });closeBtn的判断要放在item判断之前因为关闭按钮也是tab-item的子节点如果先判断item点关闭时就会先触发switchTab标签切到这个即将关闭的页面然后再关闭用户体验很怪。提前判断并用return跳出可以避免冒泡带来的副作用。注意这里我不需要调用stopPropagation因为整个事件委托挂在一个容器上同一个容器内不会二次冒泡。switchTab的逻辑比命名看上去要简单它只做三件事更新activeId、重新画标签条、重新画内容区。重新画内容区时面板被整块替换这既是特性也是坑后面避坑章节会聊到。function switchTab(id) { const tab tabs.find(t t.id id); if (!tab) return; activeId id; renderTabs(); renderContent(); }find用了严格相等判断所以id必须保持字符串类型模板字符串生成的tab-3在开关切换后如果有任何地方转成了数字就会失效。在从后端恢复数据、解析localStorage时特别容易踩统一用String()包一层即可。3.3 关闭Tab邻居焦点继承与右键批量关闭关闭操作是整个项目里最容易翻车的地方。如果只把tab从数组里删掉而不处理焦点关闭后activeId会指向一个不存在的标签内容区渲染出来就是个空壳。我的处理方式是删除后立刻找邻居当备选优先取右侧兄弟右侧没了取左侧兄弟。function closeTab(id) { const index tabs.findIndex(t t.id id); if (index -1) return; tabs.splice(index, 1); if (activeId id) { const fallback tabs[index] || tabs[index - 1]; activeId fallback ? fallback.id : ; } renderTabs(); renderContent(); }这个fallback逻辑最关键的是那个tabs[index]splice把当前元素删掉之后原本index位置上的元素变成了它右侧的兄弟所以数组下标不变时取到的是右边邻居如果删的是最后一个tabs[index]会返回undefined再用tabs[index - 1]拿到左边邻居。两行代码把两种边界都覆盖了。右键菜单是可删除里最好用的批量操作。浏览器原生contextmenu事件会盖掉系统菜单我通过tabBar.addEventListener(contextmenu)拦截然后根据鼠标坐标弹出自己的菜单。菜单项拆成四个操作关闭、关闭其他、关闭左侧、关闭右侧。这四个操作本质上都是数组过滤只是过滤规则不同。function closeOtherTabs(id) { tabs tabs.filter(t t.id id); activeId id; renderTabs(); renderContent(); } function closeLeftTabs(id) { const index tabs.findIndex(t t.id id); tabs tabs.filter((t, i) i index); activeId id; renderTabs(); renderContent(); } function closeRightTabs(id) { const index tabs.findIndex(t t.id id); tabs tabs.filter((t, i) i index); activeId id; renderTabs(); renderContent(); }这里用filter而不是splice是因为批量删除时多个splice会不断改变数组下标容易错删。filter按条件重新组装数组语义更清晰。三个函数共享同一个激活策略最终留下的第一个目标是当前右键点击的那个Tab。closeOtherTabs会直接把其他全部清掉而closeLeftTabs通过i index保留从当前tab开始的右侧所有tab。菜单弹出的位置要避开浏览器边缘我在showContextMenu里做了边界修正。获取菜单自身尺寸后再用Math.min把坐标限制在window.innerWidth和innerHeight之内。这个细节看起来不起眼但做不好会在屏幕右下角看到菜单瞬间被浏览器切掉一块很掉价。到这里三条主链路就全部讲完了。第4章的坑大多也是从这段代码演化出来的。4. 避坑指南从关不掉到菜单飞出屏幕的四次翻车4.1 关闭最后一个标签后整个Tab管理器变沉默现象页面上只剩一个Tab点掉它之后内容区空白再按CtrlT也没有新标签出现控制台也不报错。原因closeTab删除最后一个tab后activeId被置成了空字符串。createTab本身没问题但事件绑定处有一个if (activeId)的限制导致关闭快捷键一直不生效。另外如果addBtn也被同一个判断条件包住点击加号也一样无反应。解决创建标签时不要依赖旧值直接赋id给activeId。关闭时主动做fallback。可以在createTab里加一行console.log验证进入函数时tabs和activeId的状态。具体做法不加判断改完closeTab后跑一遍新建→关闭→再新建焦点能自然恢复。4.2 点击关闭按钮时先切换到当前页才关闭现象用鼠标点Tab上的×页面先是闪了一下切到该Tab然后标签才消失体感上是骗我看一眼再关。原因事件委托里先判断closest(.tab-item)关闭按钮也是tab-item的一部分所以switchTab先执行closeTab后执行。解决把closest(.tab-close)的判断放在最前面命中就直接关闭并return永远不让它走到下方的switchTab分支。不需要用stopPropagation因为监听器只挂在一个容器上这样写最干净。4.3 重复重绘导致监听器爆炸现象连续打开关闭标签几十次页面越来越卡点一个标签要等大几百毫秒浏览器开发者工具里看到tab-item上的listener不断累积。原因早期版本在renderTabs里给每个新建的DOM元素addEventListener标签删除后这些函数引用没被释放旧的DOM虽然从页面上删除了但监听器仍然被事件总线持有形成泄漏。解决把事件绑定全部收敛到tabBar外层用事件委托分发。销毁标签时不需要removeEventListener因为监听器根本没有绑在子元素上。事件委托还有另一个好处即使renderTabs清了内层HTML委托层仍然存活。从那以后我每写一个动态列表组件都会问一句这个监听器绑在谁身上、它会被重建吗。4.4 右键菜单在屏幕边缘被裁切现象在浏览器窗口右下角对一个Tab点右键菜单刚弹出来一大半被切在视口外点击关闭其他时鼠标悬停区域和菜单显示区域对不上。原因contextmenu的clientX和clientY是鼠标视口坐标如果直接把菜单的left和top设为这两个值浏览器会原样渲染。菜单出现在右下角时left加上菜单自身宽度超出了window.innerWidth就会被裁掉。解决显示菜单前用getBoundingClientRect()拿它自身的宽高再通过Math.min把坐标压缩到窗口边界内。还可以预留4px的边距避免菜单挨着边缘导致hover误触。4.5 面板HTML里的id重复导致多开面板后表单串数据现象同一个模块开了两个Tab在第一个里输入了内容切到第二个发现第一个Tab里的信息被覆盖成了第二个的值。原因面板模板里硬编码了id属性比如input idphone。两个Tab同时渲染同一个模板时页面里出现两个idphone的DOM浏览器总是会把操作绑定到第一个出现的节点上于是两个面板共用了一个输入框。解决面板模板内部不要用硬编码id尽量用类名和name属性。如果必须用id就在创建模板时用模板参数填充唯一id比如phone_${tabId}。数据模型阶段使用panelKey映射而不是存储HTML字符串也是为了防止这种串数据的问题。5. 二次开发拖拽排序、状态持久化与路由联动5.1 给Tab加拖拽排序拖拽是原生HTML5的drag相关事件不需要引入库。dragstart记录来源iddragover阻止默认行为drop时根据目标位置重组tabs数组。let dragId null; tabBar.addEventListener(dragstart, e { dragId e.target.closest(.tab-item)?.dataset.id; }); tabBar.addEventListener(dragover, e { e.preventDefault(); }); tabBar.addEventListener(drop, e { const targetId e.target.closest(.tab-item)?.dataset.id; if (!dragId || !targetId || dragId targetId) return; const from tabs.findIndex(t t.id dragId); const to tabs.findIndex(t t.id targetId); const [moved] tabs.splice(from, 1); tabs.splice(to, 0, moved); renderTabs(); });5.2 用localStorage记住标签状态配合createTab、closeTab、switchTab里的saveState调用刷新页面后能恢复上一次打开的所有Tab。const STORAGE_KEY tab-state; function saveState() { localStorage.setItem(STORAGE_KEY, JSON.stringify({ tabs, activeId })); } function restoreState() { const raw localStorage.getItem(STORAGE_KEY); if (!raw) return; try { const state JSON.parse(raw); if (Array.isArray(state.tabs)) { tabs state.tabs; activeId state.activeId; renderTabs(); renderContent(); } } catch (err) {} }恢复后要校验每个tab的panelKey仍然存在于panels映射里否则面板会渲染空。我会写一个recoverPanels函数把失效的panelKey统一替换为blank避免白屏。5.3 与路由联动让Tab地址可分享把activeId同步到location.hash浏览器前进后退也能对应切换标签。window.addEventListener(hashchange, () { const id location.hash.slice(1); if (id tabs.some(t t.id id)) switchTab(id); });switchTab被hashchange触发时不再反向写hash避免循环。路由参数由业务方决定组件只负责响应。有一次在某个后台项目里我图省事只写了关闭逻辑忘记处理焦点继承结果用户每关一次当前页就失明一次差点被当成重大故障处理。从那次之后我每次给这类Tab组件加功能都会强制走一遍新建→切换→关闭→再新建的完整链路然后顺手清掉localStorage再恢复一次确认旧状态不会把已经关闭的页面带回来。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

A*与DWA融合:移动机器人路径规划中全局与局部导航协同实战

A*与DWA融合:移动机器人路径规划中全局与局部导航协同实战

1. 为什么要把 A* 和 DWA 放在一起讲先说个现象。很多刚接触机器人路径规划的朋友,最容易掉进的一个坑就是:只用一个算法跑天下。要么迷信 A*,觉得它能找到全局最短路径就万事大吉;要么迷信 DWA,觉得动态窗口法能实时避…

2026/10/18 12:28:25 阅读更多 →
从零部署LinuxCheck:快速搭建Linux安全检测环境全指南

从零部署LinuxCheck:快速搭建Linux安全检测环境全指南

1. 环境准备与整体设计思路拿到“如何快速部署LinuxCheck:从零开始搭建Linux安全检测环境”这个标题,先别急着敲命令。我最初接触这类安全检测工具时踩过一个坑:文件一下载就急着跑,结果环境依赖缺失、脚本权限不对、输出日志乱飞…

2026/10/18 12:28:25 阅读更多 →
企业数字人落地指南:从成本算账到实操避坑全解析

企业数字人落地指南:从成本算账到实操避坑全解析

“老板,在AI时代,您的企业为什么需要数字人?”这个问题,我这几个月几乎每周都会被问到。问的人背景很杂:做本地餐饮连锁的、搞跨境电商的、开设计工作室的、做知识付费的都有。说实话,大部分老板不是来听概…

2026/10/18 12:28:25 阅读更多 →

最新新闻

无代码编辑器:拖拽搭建小程序、H5与网站,企业级应用快速生成

无代码编辑器:拖拽搭建小程序、H5与网站,企业级应用快速生成

简介:这份资源是一套面向无代码开发场景的完整编辑器项目包,适合希望快速搭建小程序、H5页面与网站的企业团队及个人开发者,尤其对缺乏编程基础、又想低成本验证业务想法的人群友好。压缩包共677个文件,约5.67MB,以tsx…

2026/10/18 14:51:49 阅读更多 →
diagram-design:从草图到系统,用信息设计思维画好每一张图

diagram-design:从草图到系统,用信息设计思维画好每一张图

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题第一次看到 “diagram-design” 这个标题,很多人会以为它只是教你怎么把图画得好看一点。但真正做过系统设计、写过技术方案、跟产品经理吵过架构图的人都知道,画图这件事从来不只…

2026/10/18 14:51:49 阅读更多 →
图表设计方法论:从信息分层到可维护资产的工程实践

图表设计方法论:从信息分层到可维护资产的工程实践

1. 从"diagram-design"这个标题里能读出什么第一次看到"diagram-design"这个标题,我脑子里蹦出来的第一个判断是:这大概率不是一个单纯的"画图工具推荐",而是一个关于图表设计方法论的项目。为什么这么说&…

2026/10/18 14:51:49 阅读更多 →
Artcraft:意图锚定、过程可见与材料敬畏的创作新范式

Artcraft:意图锚定、过程可见与材料敬畏的创作新范式

1. “Artcraft”不是新词,而是被低估的创作范式重构信号最近在多个设计社区、开源项目讨论区和创意工作坊里,“artcraft”这个词频繁跳出来,但没人给它下过准确定义。它不像“generative design”或“creative coding”那样有清晰的技术边界&…

2026/10/18 14:51:49 阅读更多 →
C++ TCP服务端从零实现:核心API、粘包处理与高并发优化

C++ TCP服务端从零实现:核心API、粘包处理与高并发优化

1. 从零拆解一个TCP服务端到底要写哪些东西很多人第一次接触网络编程,脑子里冒出来的第一个问题不是"怎么写",而是"我到底要写几行代码才能让两个程序说上话"。我当初也是这么想的。后来真正把C的socket API撸了一遍才发现&#xff…

2026/10/18 14:51:49 阅读更多 →
接口安全设计:从Cookie到参数签名防篡改的核心机制

接口安全设计:从Cookie到参数签名防篡改的核心机制

1. 接口安全设计思路拆解:Cookie、签名与“加密函数”到底在防谁先说一个很多人容易混淆的点:我们平时说“参数加密”“签名函数”,其实绝大多数场景下并不是在做那种密文不可见的加密,而是做“篡改可识别”。换句话说&#xff0c…

2026/10/18 14:50:48 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →