DataEase 自定义图表开发全流程指南:从架构设计到生产部署
DataEase 自定义图表开发全流程指南从架构设计到生产部署【免费下载链接】dataease 人人可用的开源 BI 工具数据可视化神器。An open-source BI tool alternative to Tableau.项目地址: https://gitcode.com/GitHub_Trending/da/dataeaseDataEase 作为开源 BI 工具其自定义图表开发能力为开发者提供了强大的数据可视化扩展机制。本文深入解析 DataEase 图表插件架构通过完整的实战示例帮助开发者掌握从零构建自定义图表的完整流程。核心关键词DataEase 自定义图表开发。长尾关键词图表插件架构设计、ECharts 集成方案、AntV G2Plot 扩展、图表性能优化策略。架构设计与核心原理DataEase 采用插件化架构设计通过统一的接口规范实现图表引擎的可扩展性。图表插件体系的核心位于 sdk/extensions/extensions-view/ 目录该模块定义了图表插件的完整生命周期管理机制。插件架构分层模型DataEase 的图表插件架构采用三层设计基础抽象层、插件实现层和工厂管理层。基础抽象层由AbstractChartPlugin定义该抽象类规定了图表开发的四个核心生命周期方法public abstract class AbstractChartPlugin { // 处理坐标轴字段配置 public abstract T extends AxisFormatResult T formatAxis(ChartViewDTO view); // 处理过滤条件和交互逻辑 public abstract T extends CustomFilterResult T customFilter( ChartViewDTO view, ListChartExtFilterDTO filterList, AxisFormatResult formatResult); // 计算图表数据结果 public abstract T extends ChartCalcDataResult T calcChartResult( ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, MapString, Object sqlMap, SQLMeta sqlMeta, Provider provider); // 构建最终图表配置 public abstract T extends ChartViewDTO T buildChart( ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult); }插件注册与发现机制图表插件的注册通过PluginsChartFactory实现该工厂类采用双重注册机制静态注册和动态加载。每个图表插件需要继承DataEaseChartPlugin并实现loadPlugin()方法public abstract class DataEaseChartPlugin extends AbstractChartPlugin implements DataEasePlugin { Override public void loadPlugin() { XpackPluginsViewVO viewConfig getConfig(); PluginsChartFactory.loadPlugin( viewConfig.getRender(), viewConfig.getChartValue(), this ); } public abstract XpackPluginsViewVO getConfig(); }图1DataEase 基础柱状图示例展示插件架构处理的数据可视化效果开发环境与工具链配置环境搭建与依赖管理DataEase 采用 Maven 作为后端构建工具前端使用 Vue 3 TypeScript。开发自定义图表需要配置完整的开发环境# 克隆项目仓库 git clone https://gitcode.com/GitHub_Trending/da/dataease.git cd dataease # 安装前端依赖 cd core/core-frontend npm install # 编译后端扩展模块 cd sdk/extensions/extensions-view mvn clean compile -DskipTests开发调试环境配置后端开发环境需要启动 Spring Boot 应用建议使用开发模式cd core/core-backend mvn spring-boot:run -Dspring-boot.run.profilesdev前端开发环境启动命令cd core/core-frontend npm run dev核心模块实现详解ECharts 图表插件实现ECharts 作为 DataEase 支持的主要图表引擎之一其插件实现需要处理数据格式转换和渲染配置生成。以下是 ECharts 柱状图插件的核心实现public class EChartsBarPlugin extends DataEaseChartPlugin { Override public AxisFormatResult formatAxis(ChartViewDTO view) { // 处理坐标轴配置 AxisFormatResult result new AxisFormatResult(); ListChartViewFieldDTO xAxis view.getXAxis(); ListChartViewFieldDTO yAxis view.getYAxis(); // 标准化坐标轴字段 result.setxAxis(standardizeAxisFields(xAxis)); result.setyAxis(standardizeAxisFields(yAxis)); return result; } Override public ChartCalcDataResult calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, MapString, Object sqlMap, SQLMeta sqlMeta, Provider provider) { // 使用 ChartDataUtil 进行数据转换 ChartCalcDataResult result new ChartCalcDataResult(); ListString[] rawData executeQuery(sqlMap, sqlMeta, provider); // 数据排序和格式化 ListString[] sortedData ChartDataUtil.resultCustomSort( formatResult.getxAxis(), formatResult.getyAxis(), view.getSortPriority(), rawData ); result.setSeriesData(sortedData); return result; } Override public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) { // 构建 ECharts 配置选项 MapString, Object echartsOption new HashMap(); // 配置坐标轴 echartsOption.put(xAxis, buildEChartsXAxis(formatResult.getxAxis())); echartsOption.put(yAxis, buildEChartsYAxis(formatResult.getyAxis())); // 配置数据系列 ListMapString, Object series convertToEChartsSeries( calcResult.getSeriesData(), formatResult ); echartsOption.put(series, series); // 应用样式配置 applyStyleConfig(echartsOption, view.getStyleConfig()); // 设置图表配置到视图 view.setChartConfig(JSON.toJSONString(echartsOption)); return view; } }数据格式转换工具DataEase 提供了ChartDataUtil和FieldUtil工具类简化数据格式转换过程public class DataConverterUtil { public static MapString, Object convertToEChartsFormat( ListChartViewFieldDTO xAxis, ListChartViewFieldDTO yAxis, ListString[] seriesData) { MapString, Object result new HashMap(); // 构建 X 轴数据 ListString xAxisData new ArrayList(); for (String[] row : seriesData) { xAxisData.add(FieldUtil.formatValue(xAxis.get(0), row[0], null)); } result.put(xAxisData, xAxisData); // 构建系列数据 ListMapString, Object seriesList new ArrayList(); for (int i 0; i yAxis.size(); i) { MapString, Object series new HashMap(); series.put(name, yAxis.get(i).getName()); ListObject data new ArrayList(); for (String[] row : seriesData) { data.add(FieldUtil.formatValue(yAxis.get(i), row[i 1], null)); } series.put(data, data); seriesList.add(series); } result.put(series, seriesList); return result; } }图2DataEase 多图表组合大屏展示复杂业务场景下的数据整合能力AntV G2Plot 集成方案G2Plot 图表适配器AntV G2Plot 作为另一个主流的可视化引擎其集成方案需要实现特定的适配器接口public class G2PlotChartPlugin extends DataEaseChartPlugin { private static final String RENDER_TYPE antv; Override public void loadPlugin() { // 注册多种图表类型 PluginsChartFactory.loadPlugin(RENDER_TYPE, line, this); PluginsChartFactory.loadPlugin(RENDER_TYPE, bar, this); PluginsChartFactory.loadPlugin(RENDER_TYPE, scatter, this); PluginsChartFactory.loadPlugin(RENDER_TYPE, pie, this); } Override public XpackPluginsViewVO getConfig() { XpackPluginsViewVO config new XpackPluginsViewVO(); config.setRender(RENDER_TYPE); config.setChartValue(g2plot); config.setTypes(Arrays.asList(line, bar, scatter, pie)); config.setIcon(antv-icon); return config; } Override public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) { // 构建 G2Plot 配置 MapString, Object g2plotConfig new HashMap(); g2plotConfig.put(type, view.getType()); g2plotConfig.put(data, convertToG2PlotData(calcResult)); g2plotConfig.put(xField, getXField(formatResult)); g2plotConfig.put(yField, getYField(formatResult)); // 配置交互和动画 g2plotConfig.put(interactions, buildInteractions(view)); g2plotConfig.put(animation, buildAnimationConfig(view)); view.setChartConfig(JSON.toJSONString(g2plotConfig)); return view; } }前端渲染组件实现前端渲染组件位于 core/core-frontend/src/views/chart/components/views/ 目录需要为每个图表类型实现对应的 Vue 组件template div refchartContainer classg2plot-chart-container/div /template script setup langts import { ref, onMounted, watch, onUnmounted } from vue import { G2 } from antv/g2plot const props defineProps{ chartConfig: string width?: number height?: number }() const chartContainer refHTMLElement() let chartInstance: G2.Plot | null null onMounted(() { renderChart() }) watch(() props.chartConfig, () { if (chartInstance) { chartInstance.destroy() } renderChart() }) onUnmounted(() { if (chartInstance) { chartInstance.destroy() } }) const renderChart () { if (!chartContainer.value) return const config JSON.parse(props.chartConfig) const chartType config.type // 根据图表类型创建对应的 G2Plot 实例 switch (chartType) { case line: chartInstance new G2.Line(chartContainer.value, config) break case bar: chartInstance new G2.Bar(chartContainer.value, config) break case scatter: chartInstance new G2.Scatter(chartContainer.value, config) break case pie: chartInstance new G2.Pie(chartContainer.value, config) break } if (chartInstance) { chartInstance.render() } } /script style scoped .g2plot-chart-container { width: 100%; height: 100%; min-height: 300px; } /style集成测试与性能优化单元测试策略图表插件需要完善的单元测试覆盖确保数据转换和渲染逻辑的正确性SpringBootTest class EChartsPluginTest { Autowired private EChartsBarPlugin barPlugin; Test void testFormatAxis() { ChartViewDTO view createTestView(); AxisFormatResult result barPlugin.formatAxis(view); assertNotNull(result); assertEquals(1, result.getxAxis().size()); assertEquals(2, result.getyAxis().size()); } Test void testBuildChart() { ChartViewDTO view createTestView(); ChartCalcDataResult calcResult createTestData(); AxisFormatResult formatResult barPlugin.formatAxis(view); ChartViewDTO result barPlugin.buildChart( view, calcResult, formatResult, null ); assertNotNull(result.getChartConfig()); String config result.getChartConfig(); assertTrue(config.contains(xAxis)); assertTrue(config.contains(series)); } }性能优化策略大数据量场景下的性能优化是图表开发的关键考虑因素public class PerformanceOptimizedPlugin extends DataEaseChartPlugin { private static final int DATA_SAMPLING_THRESHOLD 1000; private static final int MAX_DATA_POINTS 5000; Override public ChartCalcDataResult calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, MapString, Object sqlMap, SQLMeta sqlMeta, Provider provider) { // 添加查询优化 sqlMap.put(enablePagination, true); sqlMap.put(pageSize, MAX_DATA_POINTS); ListString[] rawData executeQuery(sqlMap, sqlMeta, provider); // 数据采样策略 if (rawData.size() DATA_SAMPLING_THRESHOLD) { rawData applySampling(rawData, DATA_SAMPLING_THRESHOLD); } ChartCalcDataResult result new ChartCalcDataResult(); result.setSeriesData(rawData); return result; } Override public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) { MapString, Object option buildBaseOption(view, calcResult); // 性能优化配置 if (calcResult.getSeriesData().size() 1000) { option.put(animation, false); option.put(progressive, 200); option.put(progressiveThreshold, 1000); option.put(sampling, lttb); // Largest Triangle Three Bucket } // 懒加载配置 option.put(lazyUpdate, true); option.put(throttle, 100); view.setChartConfig(JSON.toJSONString(option)); return view; } }部署发布与监控运维插件打包与部署使用 Maven 进行插件打包支持独立部署和集成部署两种模式# 独立打包插件模块 cd sdk/extensions/extensions-view mvn clean package -DskipTests -Pplugin # 生成插件包 ls target/*.jar # 部署到 DataEase 插件目录 cp target/dataease-chart-plugin-*.jar /opt/dataease/plugins/插件监控与日志实现插件健康检查和性能监控public class PluginMonitor { private static final Logger logger LoggerFactory.getLogger(PluginMonitor.class); public void monitorPluginPerformance(String pluginName, long executionTime, int dataSize) { logger.info(Plugin {} executed in {}ms with {} data points, pluginName, executionTime, dataSize); // 性能阈值告警 if (executionTime 1000) { logger.warn(Plugin {} execution time exceeds threshold: {}ms, pluginName, executionTime); } if (dataSize 10000) { logger.warn(Plugin {} processing large dataset: {} records, pluginName, dataSize); } } public void checkPluginHealth(DataEaseChartPlugin plugin) { try { XpackPluginsViewVO config plugin.getConfig(); logger.info(Plugin {} health check passed. Type: {}, Render: {}, plugin.getClass().getSimpleName(), config.getChartValue(), config.getRender()); } catch (Exception e) { logger.error(Plugin health check failed, e); } } }最佳实践与进阶建议图表样式标准化建立统一的样式配置规范确保图表风格一致性public class ChartStyleStandardizer { public static MapString, Object applyStandardStyle( MapString, Object chartConfig, String chartType) { MapString, Object style new HashMap(); // 基础样式配置 style.put(backgroundColor, transparent); style.put(textStyle, Map.of( fontFamily, Microsoft YaHei, Arial, sans-serif, fontSize, 12, color, #333 )); // 图表类型特定样式 switch (chartType) { case line: style.put(lineStyle, Map.of( width, 2, type, solid )); break; case bar: style.put(itemStyle, Map.of( borderRadius, 2, borderWidth, 0 )); break; case pie: style.put(itemStyle, Map.of( borderWidth, 1, borderColor, #fff )); break; } // 合并样式配置 return mergeConfig(chartConfig, style); } public static MapString, Object getColorPalette(String paletteName) { MapString, Object palettes Map.of( default, Arrays.asList(#5470c6, #91cc75, #fac858, #ee6666, #73c0de, #3ba272, #fc8452, #9a60b4), sequential, Arrays.asList(#003f5c, #2f4b7c, #665191, #a05195, #d45087, #f95d6a, #ff7c43, #ffa600), categorical, Arrays.asList(#1f77b4, #ff7f0e, #2ca02c, #d62728, #9467bd, #8c564b, #e377c2, #7f7f7f) ); return Map.of(color, palettes.getOrDefault(paletteName, palettes.get(default))); } }交互功能扩展增强图表的交互能力支持数据钻取、联动过滤等高级功能public class InteractiveChartPlugin extends DataEaseChartPlugin { Override public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) { MapString, Object option buildBaseChartOption(view, calcResult); // 添加交互配置 MapString, Object interactions new HashMap(); // 数据点点击事件 interactions.put(click, Map.of( type, dataPoint, action, drillDown, params, Map.of(level, next) )); // 图表区域缩放 interactions.put(dataZoom, Map.of( type, inside, xAxisIndex, 0, start, 0, end, 100 )); // 图例筛选 interactions.put(legend, Map.of( selectedMode, multiple, type, scroll )); option.put(interactions, interactions); // 工具提示配置 option.put(tooltip, Map.of( trigger, axis, axisPointer, Map.of(type, cross), formatter, buildTooltipFormatter(formatResult) )); view.setChartConfig(JSON.toJSONString(option)); return view; } private String buildTooltipFormatter(AxisFormatResult formatResult) { // 构建自定义工具提示格式 StringBuilder formatter new StringBuilder(); formatter.append({b}br/); for (ChartViewFieldDTO field : formatResult.getyAxis()) { formatter.append(field.getName()) .append(: {c) .append(formatResult.getyAxis().indexOf(field) 1) .append(}br/); } return formatter.toString(); } }图3DataEase 综合报告展示包含时间轴、词云、雷达图等多种图表类型的集成应用总结与未来展望DataEase 自定义图表开发框架为开发者提供了完整的扩展解决方案从基础的数据处理到复杂的可视化渲染每个环节都有清晰的接口定义和实现规范。通过本文的详细解析开发者可以掌握架构理解深入理解 DataEase 插件化架构的设计理念和实现机制开发流程掌握从环境搭建到插件部署的完整开发流程性能优化学习大数据量场景下的性能优化策略和最佳实践扩展能力了解如何扩展图表交互功能和实现复杂业务场景未来DataEase 图表开发将向以下方向发展实时数据支持增强对实时数据流的处理能力支持流式图表渲染AI 增强分析集成机器学习算法提供智能数据洞察和预测分析跨平台适配优化移动端和桌面端的图表渲染性能主题系统建立完整的主题配置系统支持企业级品牌定制通过持续的技术演进和社区贡献DataEase 将为企业级数据可视化提供更加强大和灵活的解决方案。【免费下载链接】dataease 人人可用的开源 BI 工具数据可视化神器。An open-source BI tool alternative to Tableau.项目地址: https://gitcode.com/GitHub_Trending/da/dataease创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Superpaper终极指南:轻松实现多显示器无缝壁纸拼接

Superpaper终极指南:轻松实现多显示器无缝壁纸拼接

Superpaper终极指南:轻松实现多显示器无缝壁纸拼接 【免费下载链接】superpaper A cross-platform multi monitor wallpaper manager. 项目地址: https://gitcode.com/gh_mirrors/su/superpaper 你是否曾经为多显示器设置壁纸而烦恼?不同尺寸、分…

2026/10/18 12:20:27 阅读更多 →
Illustrator脚本大全:提升Adobe Illustrator工作效率的10个免费神器

Illustrator脚本大全:提升Adobe Illustrator工作效率的10个免费神器

Illustrator脚本大全:提升Adobe Illustrator工作效率的10个免费神器 【免费下载链接】illustrator-scripts Adobe Illustrator scripts 项目地址: https://gitcode.com/gh_mirrors/il/illustrator-scripts Adobe Illustrator脚本是设计师提升工作效率的秘密武…

2026/10/19 1:17:54 阅读更多 →
SavvyCAN:跨平台CAN总线分析的终极解决方案,3步开启专业级汽车电子调试

SavvyCAN:跨平台CAN总线分析的终极解决方案,3步开启专业级汽车电子调试

SavvyCAN:跨平台CAN总线分析的终极解决方案,3步开启专业级汽车电子调试 【免费下载链接】SavvyCAN QT based cross platform canbus tool 项目地址: https://gitcode.com/gh_mirrors/sa/SavvyCAN 你是否正在寻找一款能够同时在Windows、Linux和ma…

2026/10/20 13:04:04 阅读更多 →

最新新闻

模型量化如何影响推理性能?实战解析原理、选型与问题排查

模型量化如何影响推理性能?实战解析原理、选型与问题排查

在AI推理优化这条路上,量化是我见过投入产出比最高的手段。很多人以为量化就是把模型从FP32变成INT8,体积缩小、推理变快,但实操后才发现,AI模型量化对推理性能的影响并不只有正向:有时速度反而下降,精度却…

2026/10/20 20:20:51 阅读更多 →
基于Simulink的23自由度分布式电驱动整车动力学模型搭建与仿真

基于Simulink的23自由度分布式电驱动整车动力学模型搭建与仿真

做分布式电驱动车辆的控制,最头疼的问题就是你手上的整车模型到底该建多细。我这次在Matlab Simulink里完整搭了一套23自由度整车动力学模型,把车身六个方向的运动、四个车轮的垂向跳动、车轮旋转、转向系统、以及分布式驱动链条的扭转自由度全部拉进来了…

2026/10/20 20:20:51 阅读更多 →
用JSON和Python命令行构建个人技能管理工具:从数据建模到实践

用JSON和Python命令行构建个人技能管理工具:从数据建模到实践

1. 为什么需要技能管理工具:需求拆解与设计取舍上周末想更新简历,结果发现我根本说不清“现在到底会什么”。十几项技能分散在旧简历、脑图、收藏夹和一摞笔记本里,有的早已过期,有的只停留在“看过教程”的阶段,还有的…

2026/10/20 20:20:51 阅读更多 →
企业微信回调验证与AES加解密:.NET开发者接入指南

企业微信回调验证与AES加解密:.NET开发者接入指南

接手过几个企业微信自建应用的项目,每次新人对接时几乎都会卡在同一个地方:管理后台保存“接收消息服务器”配置时提示URL验证失败,或者明明验证过了,消息却一直推送不进来。这个环节看着只是填几个参数,背后实际上是一…

2026/10/20 20:20:51 阅读更多 →
vLLM Prefix Caching 原理:如何用 Paged KV 消除重复计算

vLLM Prefix Caching 原理:如何用 Paged KV 消除重复计算

先说一个我遇到过的情况。某个线上推理服务,所有请求都带着同一段两千多字的 system prompt,并发一上来,GPU 利用率看着很高,但真正对用户有用的计算没多少——大部分算力在一遍遍重复计算同一段内容的 K 和 V。很长一段时间里&am…

2026/10/20 20:20:51 阅读更多 →
用Design.md构建符合产品风格的原型设计:设计令牌实战指南

用Design.md构建符合产品风格的原型设计:设计令牌实战指南

上周跟某团队聊原型评审,对方把三个页面的设计稿拍在我面前,主色蓝就出现了五种,没有两个颜色值相同。评审会开了一个小时,快一半时间在争论“这个蓝到底是不是我们品牌那个蓝”。原型阶段最让人心累的从来不是画得快不快&#xf…

2026/10/20 20:19:47 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →