Handsontable自定义Select控件开发指南
1. Handsontable 单元格类型扩展实战打造灵活可配的 Select 控件作为一名长期与数据表格打交道的前端开发者我经常遇到需要增强表格交互能力的场景。Handsontable 作为一款功能强大的 JavaScript 电子表格库其 registerCellType 方法为我们提供了无限可能。今天要分享的是如何通过自定义单元格类型实现兼具单选和多选功能的 Select 控件——这个需求在实际项目中出现的频率远超你的想象。去年在为某电商后台系统开发商品属性编辑器时我深刻体会到原生下拉框的局限性。当需要同时处理商品颜色单选和适用人群多选这类字段时标准解决方案往往需要编写大量胶水代码。而通过自定义 CellType我们不仅能统一交互模式还能保持代码的整洁性和可维护性。2. 核心设计思路解析2.1 需求场景拆解在实际业务中Select 控件的使用场景主要分为两类精确单选如状态选择、分类归属等需要严格唯一值的场景灵活多选如标签管理、权限配置等需要复合值的场景传统方案往往需要为这两种场景分别实现不同的控件导致代码冗余。我们的目标是通过一个统一的 Select 单元格类型通过配置参数来切换单选/多选模式。2.2 技术方案选型Handsontable 的自定义单元格类型需要实现三个核心方法{ editor: 负责渲染编辑状态的UI, renderer: 负责单元格的静态展示, validator: 负责数据校验 }对于支持多选的 Select 控件关键点在于编辑状态使用select multiple或自定义多选组件展示状态需要将数组值转换为易读的文本校验逻辑需要区分单选/多选模式3. 完整实现步骤3.1 基础单选 Select 实现我们先从基础的单选版本开始这是后续扩展的基础Handsontable.cellTypes.registerCellType(singleSelect, { editor: { // 使用原生select元素 element: document.createElement(select), // 获取编辑器值 getValue() { return this.element.value; }, // 设置编辑器值 setValue(value) { this.element.value value; }, // 打开编辑器 open() { this.element.focus(); }, // 关闭编辑器 close() { this.element.blur(); } }, renderer: function(instance, td, row, col, prop, value) { // 获取选项配置 const options instance.getCellMeta(row, col).selectOptions || []; // 查找匹配的选项文本 const displayValue options.find(opt opt.value value)?.label || value; // 渲染单元格内容 Handsontable.renderers.TextRenderer.apply(this, arguments); td.textContent displayValue; } });使用示例const hot new Handsontable(container, { data: [ [产品A, active], [产品B, inactive] ], columns: [ { type: text }, { type: singleSelect, selectOptions: [ { value: active, label: 上架中 }, { value: inactive, label: 已下架 } ] } ] });3.2 扩展多选功能现在我们在单选基础上增加多选支持关键修改点包括编辑器改造editor: { element: document.createElement(div), getValue() { return Array.from(this.element.querySelectorAll(input:checked)) .map(el el.value); }, setValue(values) { const checkboxes this.element.querySelectorAll(input); checkboxes.forEach(checkbox { checkbox.checked Array.isArray(values) ? values.includes(checkbox.value) : values checkbox.value; }); }, open() { this.element.style.display block; }, close() { this.element.style.display none; } }渲染器增强renderer: function(instance, td, row, col, prop, value) { const options instance.getCellMeta(row, col).selectOptions || []; let displayValue; if (Array.isArray(value)) { displayValue value.map(v options.find(opt opt.value v)?.label || v ).join(, ); } else { displayValue options.find(opt opt.value value)?.label || value; } Handsontable.renderers.TextRenderer.apply(this, arguments); td.textContent displayValue; }3.3 完整版智能 Select 控件将两种模式整合为一个可配置的智能控件Handsontable.cellTypes.registerCellType(smartSelect, { editor: { element: document.createElement(div), getValue() { const isMultiple this.cellProperties.multiple; const inputs this.element.querySelectorAll(input); if (isMultiple) { return Array.from(inputs) .filter(el el.checked) .map(el el.value); } return inputs[0].checked ? inputs[0].value : null; }, setValue(value) { const isMultiple this.cellProperties.multiple; const inputs this.element.querySelectorAll(input); if (isMultiple) { inputs.forEach(input { input.checked Array.isArray(value) ? value.includes(input.value) : false; }); } else { inputs.forEach(input { input.checked input.value value; }); } }, open() { this.element.style.display block; }, close() { this.element.style.display none; } }, renderer: function(instance, td, row, col, prop, value) { const options instance.getCellMeta(row, col).selectOptions || []; const isMultiple instance.getCellMeta(row, col).multiple; let displayValue; if (isMultiple Array.isArray(value)) { displayValue value.map(v options.find(opt opt.value v)?.label || v ).join(, ); } else { displayValue options.find(opt opt.value value)?.label || value; } Handsontable.renderers.TextRenderer.apply(this, arguments); td.textContent displayValue; } });4. 高级功能与优化技巧4.1 动态选项加载在实际项目中选项数据往往需要异步加载。我们可以通过 Promise 来实现{ // ...其他配置 editor: { // ...其他editor方法 prepare(row, col, prop, td, originalValue, cellProperties) { if (typeof cellProperties.selectOptions function) { return cellProperties.selectOptions().then(options { this.buildOptions(options); return true; }); } this.buildOptions(cellProperties.selectOptions); return true; }, buildOptions(options) { // 清空现有选项 this.element.innerHTML ; // 构建新的选项 options.forEach(option { const div document.createElement(div); const input document.createElement(input); input.type this.cellProperties.multiple ? checkbox : radio; input.value option.value; const label document.createElement(label); label.textContent option.label; div.appendChild(input); div.appendChild(label); this.element.appendChild(div); }); } } }使用示例{ type: smartSelect, multiple: true, selectOptions: () fetch(/api/tags).then(res res.json()) }4.2 样式优化与交互增强默认的 checkbox/radio 样式可能不符合项目设计我们可以通过 CSS 来美化.handsontable .smart-select-container { padding: 8px; background: white; box-shadow: 0 2px 6px rgba(0,0,0,0.1); border-radius: 4px; max-height: 200px; overflow-y: auto; } .handsontable .smart-select-option { display: flex; align-items: center; padding: 4px 0; cursor: pointer; } .handsontable .smart-select-option input { margin-right: 8px; }在编辑器初始化时添加对应的 classeditor: { element: document.createElement(div), init() { this.element.className smart-select-container; }, // ...其他方法 }4.3 性能优化建议当选项数量较大时超过100条需要考虑性能优化虚拟滚动只渲染可视区域内的选项搜索过滤添加搜索框快速定位选项分组展示对选项进行分组归类实现虚拟滚动的简化版本editor: { // ...其他配置 prepare(row, col, prop, td, originalValue, cellProperties) { this.visibleCount 20; // 每次渲染的选项数量 this.scrollTop 0; if (typeof cellProperties.selectOptions function) { return cellProperties.selectOptions().then(options { this.allOptions options; this.renderVisibleOptions(); return true; }); } this.allOptions cellProperties.selectOptions; this.renderVisibleOptions(); return true; }, renderVisibleOptions() { const startIndex Math.floor(this.scrollTop / 30); const endIndex Math.min(startIndex this.visibleCount, this.allOptions.length); this.element.innerHTML ; // 添加占位元素保持滚动高度 const topSpacer document.createElement(div); topSpacer.style.height ${startIndex * 30}px; this.element.appendChild(topSpacer); // 渲染可见选项 for (let i startIndex; i endIndex; i) { const option this.allOptions[i]; // ...创建选项元素的代码 } // 底部占位 const bottomSpacer document.createElement(div); bottomSpacer.style.height ${(this.allOptions.length - endIndex) * 30}px; this.element.appendChild(bottomSpacer); // 监听滚动事件 this.element.onscroll (e) { this.scrollTop e.target.scrollTop; this.renderVisibleOptions(); }; } }5. 常见问题与解决方案5.1 选项更新不生效问题现象修改 selectOptions 后单元格显示没有更新。解决方案// 正确更新选项的方式 hot.setCellMeta(row, col, selectOptions, newOptions); hot.render();5.2 多选值保存格式问题问题现象从服务器获取的多选值无法正确显示。解决方案确保数据格式一致如果是字符串需要转换为数组{ renderer: function(instance, td, row, col, prop, value) { // 处理字符串格式的多选值 let actualValue value; if (instance.getCellMeta(row, col).multiple) { if (typeof value string) { try { actualValue JSON.parse(value); } catch { actualValue value.split(,); } } } // ...其余渲染逻辑 } }5.3 编辑器定位错乱问题现象编辑器出现在错误的位置。解决方案确保编辑器元素使用绝对定位.handsontable .smart-select-container { position: absolute; z-index: 100; /* 其他样式 */ }5.4 移动端兼容性问题问题现象在移动设备上选择不灵敏。解决方案增加触摸事件支持editor: { // ...其他配置 open() { this.element.style.display block; // 添加触摸事件 this.addTouchSupport(); }, addTouchSupport() { const options this.element.querySelectorAll(.smart-select-option); options.forEach(option { option.addEventListener(touchstart, () { const input option.querySelector(input); input.checked !input.checked; }); }); } }6. 实际应用案例6.1 电商商品管理在商品管理后台中一个典型的应用场景是商品属性的编辑const hot new Handsontable(container, { data: products, columns: [ { data: name, type: text }, { data: status, type: smartSelect, selectOptions: [ { value: draft, label: 草稿 }, { value: published, label: 已上架 }, { value: out_of_stock, label: 缺货 } ] }, { data: tags, type: smartSelect, multiple: true, selectOptions: () fetch(/api/tags).then(res res.json()) } ] });6.2 调查问卷系统构建动态调查问卷时灵活处理单选和多选题{ data: questions, columns: [ { data: question, type: text }, { data: options, type: smartSelect, multiple: true, selectOptions: (value, callback) { fetch(/api/option-templates) .then(res res.json()) .then(options callback(options)) } } ] }6.3 权限管理系统在RBAC权限配置界面中的应用{ data: roles, columns: [ { data: roleName, type: text }, { data: permissions, type: smartSelect, multiple: true, selectOptions: permissions, renderer: function(instance, td, row, col, prop, value) { // 特殊渲染逻辑高亮关键权限 const selected Array.isArray(value) ? value : []; const criticalCount selected.filter(p p.startsWith(admin:)).length; Handsontable.dom.empty(td); const wrapper document.createElement(div); wrapper.textContent ${selected.length}个权限; if (criticalCount 0) { const warn document.createElement(span); warn.textContent (含${criticalCount}个高危权限); warn.style.color red; wrapper.appendChild(warn); } td.appendChild(wrapper); } } ] }7. 扩展思路与进阶技巧7.1 与前端框架集成虽然 Handsontable 可以独立使用但与 Vue/React 等框架集成时需要注意Vue 示例// 在Vue组件中 methods: { initHot() { this.hot new Handsontable(this.$refs.container, { data: this.tableData, columns: [ { type: smartSelect, multiple: true, selectOptions: this.selectOptions } // 其他列配置 ] }); // 监听数据变化 this.hot.addHook(afterChange, (changes) { if (!changes) return; this.$emit(change, this.hot.getData()); }); } }, mounted() { this.initHot(); }, beforeDestroy() { this.hot.destroy(); }7.2 添加复杂交互例如实现全选功能editor: { // ...其他配置 buildOptions(options) { this.element.innerHTML ; if (this.cellProperties.multiple) { const selectAll document.createElement(div); selectAll.className smart-select-option select-all; selectAll.innerHTML input typecheckbox idselect-all label forselect-all全选/label ; selectAll.querySelector(input).addEventListener(change, (e) { const checkboxes this.element.querySelectorAll(input:not(#select-all)); checkboxes.forEach(checkbox { checkbox.checked e.target.checked; }); }); this.element.appendChild(selectAll); } // ...渲染普通选项 } }7.3 性能监控与调优对于大型表格添加性能监控很有必要{ // ...表格配置 afterRender: function(isForced) { console.timeEnd(render); console.log(渲染完成行数:, this.countRows()); }, beforeRender: function() { console.time(render); } }优化建议对于超过1000行的表格考虑分页加载使用batch方法批量更新数据对复杂的 renderer 进行缓存优化7.4 无障碍访问支持确保自定义控件符合无障碍标准editor: { // ...其他配置 buildOptions(options) { // 为每个选项添加ARIA属性 optionElement.setAttribute(role, option); optionElement.setAttribute(aria-selected, false); // 键盘导航支持 optionElement.addEventListener(keydown, (e) { if (e.key Enter || e.key ) { input.checked !input.checked; e.preventDefault(); } }); } }8. 版本兼容性与升级指南8.1 Handsontable 版本差异不同版本间的 API 变化需要注意功能点v8.x 及之前v9.x 及之后注册单元格类型registerCellTypecellTypes.registerCellType编辑器定义直接扩展editor属性需要实现Editor类8.2 迁移到新版 APIv9 版本的推荐写法class SmartSelectEditor extends Handsontable.editors.BaseEditor { constructor(hotInstance) { super(hotInstance); this.element document.createElement(div); // ...其他初始化 } getValue() { // ...实现逻辑 } setValue(value) { // ...实现逻辑 } // ...其他必要方法 } Handsontable.cellTypes.registerCellType(smartSelect, { editor: SmartSelectEditor, // ...其他配置 });8.3 多版本兼容方案如果需要支持多个 Handsontable 版本可以这样处理function registerSmartSelect(hot) { if (hot.cellTypes) { // v9 版本 hot.cellTypes.registerCellType(smartSelect, { // ...新版本配置 }); } else { // 旧版本 hot.registerCellType(smartSelect, { // ...旧版本配置 }); } }9. 测试策略与质量保障9.1 单元测试要点针对自定义单元格类型应重点测试编辑器与渲染器的同步性单选/多选模式切换空值处理非法值过滤使用 Jest 的测试示例describe(SmartSelect CellType, () { let hot; beforeEach(() { hot new Handsontable(container, { data: [[null]], columns: [{ type: smartSelect }] }); }); test(should correctly render single select, () { hot.setCellMeta(0, 0, selectOptions, [ { value: 1, label: Option 1 } ]); hot.render(); expect(hot.getCell(0, 0).textContent).toBe(); }); test(should handle array values for multiple, () { hot.setCellMeta(0, 0, multiple, true); hot.setDataAtCell(0, 0, [1, 2]); expect(hot.getDataAtCell(0, 0)).toEqual([1, 2]); }); });9.2 E2E 测试方案使用 Cypress 进行端到端测试describe(SmartSelect Interactions, () { it(should allow multiple selection, () { cy.visit(/table.html); cy.get(.handsontable td).eq(1).click(); cy.get(.smart-select-container input[typecheckbox]).first().click(); cy.get(.smart-select-container input[typecheckbox]).last().click(); cy.get(body).click(); // 关闭编辑器 cy.get(.handsontable td).eq(1).should(contain, Option 1, Option 3); }); });9.3 性能测试指标建立性能基准100个选项的渲染时间应 50ms1000行数据的滚动帧率应 30fps大数据量下的内存增长应 10MB使用 Chrome DevTools 的 Performance 面板进行分析重点关注脚本执行时间布局重排次数内存占用变化10. 总结与最佳实践经过多个项目的实战检验我总结了以下最佳实践配置优先通过 cellProperties 控制行为避免硬编码性能考量对于大型选项集务必实现虚拟滚动状态管理在框架中使用时保持与外部状态同步渐进增强先实现核心功能再逐步添加高级特性测试覆盖特别是边界条件和异常情况一个健壮的生产级实现还应该考虑选项的分组和分类展示搜索和过滤功能懒加载和无限滚动主题和样式的可定制性最后分享一个实用技巧在开发过程中使用 Handsontable 的getCellMeta方法调试单元格配置非常有用hot.addHook(afterSelection, (r, c) { console.log(当前单元格配置:, hot.getCellMeta(r, c)); });

相关新闻

莱达西贝普(Lerodalcibep)用法用量、漏诊处理与给药操作规范梳理

莱达西贝普(Lerodalcibep)用法用量、漏诊处理与给药操作规范梳理

莱达西贝普(商品名Lerochol)作为每月一次皮下注射的长效PCSK9抑制剂,规范给药是保障降脂疗效、降低不良反应风险的关键。基于FDA官方完整处方信息,药物推荐标准剂量为 300mg,每4周(每月)皮下注射…

2026/8/4 13:22:49 阅读更多 →
对CSS中的Position、Float属性的一些深入探讨

对CSS中的Position、Float属性的一些深入探讨

对CSS中的Position、Float属性的一些深入探讨 CSS布局是前端开发的核心技能之一,而position与float则是其中最经典也最容易被误解的两个属性。很多初学者在刚接触它们时,往往只记住了“position: relative是相对定位,float: left是左浮动”这…

2026/8/4 13:21:48 阅读更多 →
计算机毕业设计之大学生身心辅助管理系统

计算机毕业设计之大学生身心辅助管理系统

信息技术是当今社会发展的重要方向之一,它已经深入到各个行业中。随着计算机技术的发展,信息技术已经从传统的数据处理转变为网络信息的处理和交互。在管理方面,通过信息管理技术,系统可以快速的处理大量的数据,并且能…

2026/8/4 13:21:48 阅读更多 →

最新新闻

OpenBMC硬件资产管理:Inventory系统架构与应用实践

OpenBMC硬件资产管理:Inventory系统架构与应用实践

1. OpenBMC与硬件资产管理的关系 OpenBMC作为开源基板管理控制器(Baseboard Management Controller)的实现方案,在现代数据中心和服务器管理中扮演着关键角色。它通过统一的接口提供对服务器硬件的监控和管理能力,而硬件资产信息的…

2026/8/4 14:05:11 阅读更多 →
SpringBoot2+Vue3全栈健康管理系统开发实践

SpringBoot2+Vue3全栈健康管理系统开发实践

1. 项目概述:SpringBoot2Vue3全栈健康管理系统 这套师生健康信息管理系统采用了当前主流的前后端分离架构,后端基于SpringBoot2框架构建RESTful API,前端使用Vue3组合式API开发管理界面,数据持久层采用MyBatis-Plus简化CRUD操作&a…

2026/8/4 14:04:11 阅读更多 →
AI背单词到底有多强?实测12款工具后,这3个被92%用户忽略的核心参数决定成败

AI背单词到底有多强?实测12款工具后,这3个被92%用户忽略的核心参数决定成败

更多请点击: https://kaifayun.com 第一章:AI帮助记忆单词 人工智能正深刻改变语言学习的方式,尤其在词汇记忆环节展现出显著优势。传统死记硬背效率低、遗忘快,而基于认知科学与机器学习的AI工具能动态适配用户记忆曲线&#xf…

2026/8/4 14:03:10 阅读更多 →
Linux系统Oracle 11g R2数据库完整安装与配置实战指南

Linux系统Oracle 11g R2数据库完整安装与配置实战指南

1. 项目概述:为什么要在Linux上安装Oracle 11g?如果你是一名后端开发、DBA或者系统运维,迟早会碰到一个任务:在Linux服务器上部署Oracle数据库。Oracle 11g R2虽然已经不是最新的版本,但在很多传统企业、金融系统和遗留…

2026/8/4 14:03:10 阅读更多 →
2026东莞吉利银河M9音响升级观察:东莞杰之声如何处理11喇叭车内声场

2026东莞吉利银河M9音响升级观察:东莞杰之声如何处理11喇叭车内声场

一台银河M9的二次升级案例,记录器材布局、门板处理与车内声场调整一、这台银河M9为什么进行二次升级银河M9的车厢空间较大,前后排乘员的听音位置也不完全相同。原车系统能够满足日常播放,但当车主希望进一步改善人声密度、乐器层次和低频质感…

2026/8/4 14:03:10 阅读更多 →
人工智能训练师三级·模型生命周期真题40题|训练→评估→调优全链路通关

人工智能训练师三级·模型生命周期真题40题|训练→评估→调优全链路通关

人工智能训练师三级模型生命周期真题40题|训练→评估→调优全链路通关 本文是「真题演练系列」第2篇/共7篇 📊 本篇概览 题量:40道精选真题 预计用时:120分钟 难度分布:基础30% / 进阶50% / 挑战20% 适合人群:有一定机器学习基础,需要系统掌握模型训练评估方法论的备考…

2026/8/4 14:03:10 阅读更多 →

日新闻

AI Agent白手起家26: 使用标准事件驱动大模型实践

AI Agent白手起家26: 使用标准事件驱动大模型实践

纲要 练习目标:掌握大模型标准事件的调用回顾 LangChain 中的核心标准事件 invokestreambatchastream_eventswith_structured_output 环境准备实战代码:多种事件调用对比 同步调用与流式输出批量处理异步事件流监听结构化输出 运行说明与预期结果总结与扩…

2026/8/4 0:00:40 阅读更多 →
dealsea是什么?跨境卖家必知的美国deal站入门指南

dealsea是什么?跨境卖家必知的美国deal站入门指南

说实话,第一次听说美国这个老牌折扣网站的跨境卖家,十个有八个会问同一个问题:这个平台到底是干嘛的?我见过一个做家居出口的朋友,他在亚马逊上月销二十万美金,却从来没用过它。我给他看了首页——一屏一屏…

2026/8/4 0:01:40 阅读更多 →
清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

通讯作者:邓兵、刘建国通讯单位:清华大学DOI:https://doi.org/10.1021/acs.est.6c00603研究背景稀土元素(REEs)是清洁能源技术与电子器件不可或缺的核心原料,然而传统提取方式依赖能耗高、排放大的采矿与强…

2026/8/4 0:01:40 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/4 13:24:41 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/4 11:41:39 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/4 5:26:40 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/4 13:38:24 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/4 11:09:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/4 13:38:40 阅读更多 →