第十四章 高级特性-海纳百川:BIRT报表扩展点

第七章 BIRT分组报表 7.1 分组的建立,插入,删除 我们先构建报表,空白模板,示例数据库和sql查询数据源,数据集setOrders,如下 select CLASSICMODELS.CUSTOMERS.CUSTOMERNAME, CLASSICMODELS.ORDERS.ORDERNUMBER, CLASSICMODELS.ORDERS.ORDERDATE, CLASSICMODELS.ORDERS.ST 阅读详情

如果仅仅只是前面章节提到的BIRT的设计器的便捷性和BIRT引擎提供的用户自定义BIRT报表展示器等自定义特性,只是表现在BIRT报表作为工具的优势,或者作为服务提供者为系统集成做出的贡献,但这还不足以让BIRT成为eclipse开源社区排名前六的基础插件平台。

众所周知,大凡伟大的IT作品,皆是以开放为基础。ios的app store,android的app market,eclipse的plug-in,maven的osgi,microsoft的windows虽然闭源,但提供了visual studio应用开发平台,而visual studio是一个很优秀的平台;

BIRT同样也提供了扩展点,为它的数据源,数据集,数据操作函数,数据展示,数据导出,图表等等提供多数据源,多种形式的展示,多种格式的导出等等扩展。比如Oracle给multimedia CLOB提供的新特性,只要实现BIRT的数据源,数据集和数据展示,数据导出的新特性,同样可以用BIRT报表进行展示;任何第三方,无论是数据库厂商,还是数据提供商,例如医院,教育机构,航天航空机构等等,都可以实现这些扩展点中提供的接口和实现类,来完成报表的功能。

下面先从一个最简单的扩展例子——扩展BIRT报表的聚合函数,开始着手详细讲解BIRT的扩展点。感谢IBM开源社区提供这个古老的案例。

本节介绍商业智能和报告工具(Business Intelligence and Reporting Tools,BIRT)扩展点模型,并在 BIRT V2.3.x 和 V2.5.x 中实际创建一个聚合扩展。在较早的 BIRT 版本中,创建聚合扩展的方式是扩展 org.eclipse.birt.data.aggregation 扩展点,这会在一个名为 Total 的全局对象中添加一个小函数,您可以在整个报告的任何表达式中使用该函数,其工作原理类似于脚本函数扩展点。

但是,从 BIRT V2.2 到 V2.3,聚合扩展已经发生了变化。新的方式更加复杂,但可以在 Aggregation Widgets 下拉列表中得到一个不错的聚合,还为参数和表达式提供了漂亮的文本框。当创建此扩展时,可以在您的表中以列绑定的形式访问结果。

从总体上看,新的聚合扩展点包含一个对象,该对象是 IAggregationFactory 接口的扩展。可以在此接口中重载方法来完成 3 项操作:

· 初始化您的工厂(在构造函数中)

· 提供由工厂提供的一组聚合(以列表形式提供,包含聚合对象的实际实例)

· 返回聚合对象的单一实例

聚合的每个实例都需要实现 IAggrFunction 接口。需要实现许多含义明显的方法,比如 getName、getDataType 和getParmaeterDefn,还需要实现其他含义不太明显的方法。例如,getNumberOfPasses() 和 getType() 方法是相关的。getType() 方法制定此聚合器的执行方式和类型。有两种聚合类型:SUMMARY_AGGR 表示只为摘要计算该聚合(比如表的表头或表尾),RUNNING_AGGR 表示为表中的每一行或表尾计算聚合。getNumberOfPasses() 方法显示获得结果所需的 pass 数。所有基于评级的聚合器,比如 TopNpercent、PercentSum 和 Percentile,都会返回值 2,其余聚合器返回值 1。

IAggrFunction 接口的实际实现必须返回其 newAccumulator() 方法中的 Accumulator 类的一个扩展。Accumulator 负责执行实际的计算。有一些默认方法需要重载,最重要的是 onRow(),表中的每一行都需要调用该方法。使用此方法,您可以解析函数的参数并执行计算。对于 SUM,可以添加到某个已存储的数字;对于 ave,既可以保存到某个列表中进行存储,也可以添加到一个累计总计并跟踪调用次数。无论您如何执行计算,实际计算都需要在这里完成。getValue() 获取您的计算的最终值或当前值。所以,对于 SUM 操作,您将会返回总数/计数操作。在正在运行的聚合器中,将只返回正在计算的值。

本节的示例将展示如何创建一个简单的 Word Count 聚合器。此聚合获取一列中的所有句子并计算字数,返回一个包含该列的字数的整数值。对这种聚合的需求很少,所以还不存在这样的聚合。对于本文中的练习,建议使用 Eclipse BIRT All-in-One 分发版


创建新聚合插件

要创建新聚合插件:

1 单击 File > New > Other 创建一个新插件项目。展开 Plug-In Development 文件夹,然后单击 Plug-in Project。

在 New Plug-in Project 窗口中(如图 1 所示),在 ID 字段中为项目提供一个惟一名称,使用合适的信息完成Version、Name 和 Provider 字段。 

图 1. 项目属性


1 指定 Java™ 2 Platform, Standard Edition (J2SE) V1.5 作为执行环境,以保持与 BIRT 基本兼容。

2 在 Options 区域,清除 This plug-in will make contributions to the UI 复选框。通过清除此复选框,可以限制可用模板的数量。我们不打算为此项目使用模板。

3 在 Rich Client Application 区域,选择 No,因为这不是一个 Eclipse 富客户端项目。单击 Next。

4 在 Templates 页面上,清除 Create a plug-in using one of the templates 复选框,然后单击 Finish。如果现在还未处于 Plug-In 开发透视图中,系统将提示您打开它。

设置扩展点

创建好新项目之后,就可以设置扩展点了。为此,执行以下操作:

1 在打开的清单窗口中,单击 Extensions 选项卡,然后单击 Add。

在 Extension Point Selection 窗口中(如图 2 所示),清除 Show only extension points from the required plug-ins 复选框。 

图 2. 创建新扩展


1 在 Extension Point filter 字段中,键入 org.eclipse.birt.data。将会出现聚合扩展点。当添加此扩展点时,系统将提示您添加依赖关系。

创建必要的聚合类

现在您已经添加了聚合扩展点,接下来需要添加一个新 AggregationFactory。为此,右键单击刚才添加的聚合扩展点,指向 New,然后单击 AggregationFactory,如图 3 所示。注意,您并未添加聚合,聚合是以前的扩展方法,这种方法现在已被淘汰。AggregationFactory 是此插件的主要入口点,负责注册可用的聚合类型,并在运行时创建这些聚合的实例。


图 3. 创建新 AggregationFactory


添加了工厂的定义之后,您将获得一个文本项,其中包含工厂的完全限定包和类名。从图 4 可以看到,该工厂名为 com.digiassn.blogspot.birt.aggregators.wordcount.WordCountFactory。请记住,此工厂可以注册和创建多个聚合类型,但这需要在代码体中操作。在 Extension Element Details 区域,键入或浏览到工厂类的名称,然后单击文本框旁边的 class 超链接。


图 4. 创建新工厂类


New Java Class 向导已经拥有了合适的包和类信息。确认 Java Class 页面上的设置(如图 5 所示),然后单击 Finish。


图 5. 工厂类属性


打开类的源代码,如图 6 所示。如果无法找到 org.eclipse.birt.* imports,请返回并保存清单窗口中的更改。请记住,您需要为类添加必要的继承抽象方法。


图 6. 工厂类框架


您的类包含 3 个函数:一个构造函数、一个 getAggregations() 方法(返回一个 IAggrFunction),以及一个 getAggregations() 方法(返回一个列表)。getAggregations() 方法向调用者返回一个 IAggrFunction 类型列表,使调用者知道此工厂可以生成的各个聚合的类型。调用者负责在列表上进行迭代,并调用 IAggrFunctions 方法来获得描述。对于我们创建的工厂,我们不关心这些描述,工厂将负责返回和维护此列表。

向 getAggregation() 方法传入聚合的名称。该方法获取一个名称并提供一个 IAggrFunction 结果,清单 1 显示了示例工厂。

清单 1. 完成之后的工厂类

  1. public class WordCountFactory implements IAggregationFactory {  
  2.     HashMap<String, IAggrFunction> aggregateMap;  
  3.       
  4.     public WordCountFactory() {  
  5.         aggregateMap = new HashMap<String, IAggrFunction>();  
  6.           
  7.         BasicWordcount wordCountAggregation = new BasicWordcount();  
  8.           
  9.         aggregateMap.put(wordCountAggregation.getName(), wordCountAggregation);  
  10.     }  
  11.   
  12.     public IAggrFunction getAggregation(String aggregationName) {  
  13.           
  14.         return aggregateMap.get(aggregationName);  
  15.     }  
  16.   
  17.     public List getAggregations() {  
  18.           
  19.         return new ArrayList<IAggrFunction>(aggregateMap.values());  
  20.     }  
  21.   
  22. }  

创建单独的聚合描述类

接下来,在 src 文件夹中创建一个新包,将其命名为 com.digiassn.blogspot.birt.aggregators.implIn。在这个新包之下,创建一个类BasicWordCount。在 Java Class 窗口中,选择 Inherited abstract methods 复选框,以便此类可以继承org.eclipse.birt.data.engine.api.aggregation.IAggrFunction 接口,如图 7 所示。


图 7. 创建新 IAggrFunction 实现


IAggrFunction 类具有两项任务:它将自身描述为工厂可以创建的聚合,它还会创建 Accumulator 类的实例来为您的聚合执行实际工作。清单 2 给出了代码。


清单 2. 完成后的聚合函数和 Accumulator 类

  1. public class BasicWordcount implements IAggrFunction {  
  2.       
  3.     private final static String sDescription =   
  4.         "This aggregation will count all words in a column and return the count.";  
  5.     private final static String sName = "Word Count";  
  6.     private final static String sDisplayName = "Basic Word Count Aggregator";  
  7.       
  8.     public int getDataType() {  
  9.         return DataType.INTEGER_TYPE;  
  10.     }  
  11.   
  12.     public Object getDefaultValue() {  
  13.         return new Integer(0);  
  14.     }  
  15.   
  16.     public String getDescription() {  
  17.         return this.sDescription;  
  18.     }  
  19.   
  20.     public String getDisplayName() {  
  21.         return this.sDisplayName;  
  22.     }  
  23.   
  24.     public String getName() {  
  25.         return this.sName;  
  26.     }  
  27.   
  28.     public int getNumberOfPasses() {  
  29.         return 1;  
  30.     }  
  31.   
  32.     public IParameterDefn[] getParameterDefn() {  
  33.         IParameterDefn paramDef = new IParameterDefn() {  
  34.             public boolean supportDataType(int paramType) {  
  35.                 if (paramType == DataType.STRING_TYPE)  
  36.                 {  
  37.                     return true;  
  38.                 }  
  39.                   
  40.                 return false;  
  41.             }  
  42.               
  43.             public boolean isOptional() {  
  44.                 return false;  
  45.             }  
  46.               
  47.             public boolean isDataField() {  
  48.                 return false;  
  49.             }  
  50.               
  51.             public String getName() {  
  52.                 return "StringColumn";  
  53.             }  
  54.               
  55.             public String getDisplayName() {  
  56.                 return "String Column";  
  57.             }  
  58.               
  59.             public String getDescription() {  
  60.                 return "A column expression that is a String";  
  61.             }  
  62.         };  
  63.           
  64.         IParameterDefn[] parameterDefinitionArray = new IParameterDefn[]   
  65.                                 {paramDef};  
  66.         return parameterDefinitionArray;  
  67.     }  
  68.   
  69.     public int getType() {  
  70.         return IAggrFunction.SUMMARY_AGGR;  
  71.     }  
  72.   
  73.     public boolean isDataOrderSensitive() {  
  74.         return false;  
  75.     }  
  76.   
  77.     public Accumulator newAccumulator() {  
  78.         return new Accumulator()  
  79.         {  
  80.             int sum;  
  81.       
  82.             @Override  
  83.             public Object getValue() throws DataException {  
  84.                 return new Integer(sum);  
  85.             }  
  86.   
  87.             @Override  
  88.             public void onRow(Object[] incomingStrings) throws DataException {  
  89.                 String localString = (String) incomingStrings[0];  
  90.                   
  91.                 sum += localString.split(" ").length;  
  92.             }  
  93.               
  94.         };  
  95.     }  
  96. }  

此类中的大部分方法只是简单描述要在 BIRT Aggregation Widgets 下拉列表中显示的聚合的各个方面,比如数据类型、标题和描述信息。这 3 个方面有必要进一步解释一下。首先是 getParameterDefn() 方法,它返回一个 IParameterDefn 对象数组,可以在该数组中定义聚合需要的参数。一个聚合可以有多个参数,这就是为什么要以数组的形式返回参数。此方法向 BIRT 引擎简单描述这些参数及其类型。在本例中,仅有一个参数(列表达式),它将是一个字符串。

那么,如果参数在 getParameterDefn() 方法中进行描述,要将它们传入到何处来执行实际工作呢?IAggrFunction 对象还充当着Accumulator 类的工厂,该对象应该在 newAccumulator 方法中创建。Accumulator 是在聚合中执行实际工作的类。它有两个方法需要重载:getValue() 和 onRow()。对于在 BIRT 中处理的每一行,如果使用此聚合,数据绑定将调用 onRow()。作为一个参数,onRow 接收一个包含聚合参数的数组,这些参数由 getParameterDefn() 描述。在更加健壮的方案中,可以调用 getParameterDefn() 并测试传入 onRow() 的参数是否与定义匹配。但是,本文中的简单示例跳过了这一步。onRow() 方法还负责执行处理工作。在上面的聚合代码示例中,它仅用于增加正在计算的字符串总字数。当报告准备好显示值时,它调用 getValue() 方法。

需要进一步说明的另一个元素是 IAggrFunctions getType() 方法。两种聚合类型(SUMMARY_AGGR 和 RUNNING_AGGR)定义该方法属于哪种聚合类型;它出现在报告表的表头或表尾,还是显示在每一行中;它在计算时显示的是累计总计,还是平均值。

测试插件

可以轻松测试此插件,无需象测试任何 Eclipse 插件一样进行部署:只需启动另一个 Eclipse 实例即可。启动另一个实例的最简单方式是从 plugin.xml/manifest 窗口的 Overview 选项卡上启动。在此选项卡上,在 Testing 区域,单击 Launch an Eclipse application 链接,如图 8 所示。


图 8. 测试插件


图 9 显示了一个简单报告,可以根据该报告来测试插件。将一个聚合组件插入到此报告的表尾。

图 9. 向报告添加聚合


在 Aggregation Widgets 下拉列表中,选择 Basic Word Count 聚合器并输入列表达式,以指向包含想要统计的句子的列,如图 10 所示。


图 10. Aggregation Builder


运行报告并验证结果。图 11 显示了生成的报告。


图 11. 报告结果



上面的例子是BIRT扩展点中相对简单的情况,但实现扩展点的过程却是一样,都是需要添加工程类,继承接口或者实现类,完成接口方法,添加插件依赖。后续的章节会讲解实现BIRT-ODA数据源扩展,XML emitter扩展,chat扩展等等。

Eclipse GEF_01_HelloWorld 前提 可能有人会问RCP和GEF有什么关联? 首先,RCP是富客户端开发平台,RCP应用本质上就是一个可独立于Eclipse IDE开发环境运行的Eclipse插件。 而GEF,是一个图形化编辑框架,它允许开发人员以图形化的方式展示和编辑模型,从而提升用户体验。 可以说RCP是搭建GEF的开发环境,而GEF是一个适用于RCP的框架。 这里要讲的 GEF 例子,还是从任何学习编程的最普通例子 Hel... 阅读详情

相关推荐

birt 交叉表实例

你可以通过本实例进入birt交叉表的世界,用birt自带数据源,添加到birt工程中打开就可以用了。

Eclipse RCP应用系统开发方法与实战2-- 定制应用程序窗口属性

写道 5.1.4 定制应用程序窗口属性 向导生成的应用程序主界面并不能满足要求,存在很多问题,例如,主界面运行时没有自动居中,主界面大小没有固定,主窗口标题栏文字应该是“高校经费测算系统”。再看看图5-3、图5-4的标题栏有区别没有?当然有,图5-3所示界面的标题栏是很漂亮的弧线形,而图5-4所示界面的标题栏则不是。如此等等,需要重新设置。 1.闪屏画面 应用程序主界面显示之前往往需要...

topMan'blog 213

birt分组小计和自动增加序列号实现方法

该文档介绍了birt中按组进行小计的具体实现办法,同时也介绍了标本中自动编写行需要的办法,希望对你在制作birt报表时能有所帮助.

项目经验总结-Eclipse图表工具Birt的使用技巧(二)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34013044的博客 570

Birt 报表参数的使用

Birt 报表参数的使用,birt中参数的传入,使用.

病理图像处理个人手札(三)WSI & MIL

1.什么是WSI? WSI全称是Whole Slide Image,也就是全视野数字切片,主要应用在病理学细胞图像领域。WSI 是利用数字扫描仪对传统的病理切片进行扫描,采集具有高分辨率的数字图像,再通过计算机将得到的碎片化图像进行无缝拼接整合,制作可视化数字图像的一项技术。与传统载玻片相比,很好地解决了传统的玻璃切片易损坏、易褪色、易丢片、检索困难的问题。 一言以蔽之,WSI是对传统载玻片进行数字化。 由于是全视野,因此它将包含多尺度的切片在一个文件上面。因此WSI将使用到金字塔模型满足其支持不同分辨率的

baiweizeng的博客 1万+

第十四章 高级特性-海纳百川:BIRT报表扩展点(续2)

14.3 报表项的扩展 同上两节所述,BIRT报表扩展点实际上是利用了eclipse的扩展点 BIRT本身提供了显示成水平或者垂直文本的标签和文本,本节扩展报表项,让它能显示有一定旋转角度向四周扩散的标签和文本。 为了实现扩展报表项,我们需要查看以下的报表扩展点,实现其中的接口方法: org.eclipse.birt.report.model.reportItemM

huazaichang的专栏 1056

阿里云网络架构设计

背景 一个企业上云,首先要考虑整个网络的规划,涉及到云上和云下的网络规划,云上VPC的规划,云上不同地域VPC的互通,云上可用区的选择等等,所以一定要提前规划好整体的网络架构,现在阿里云云上的虚拟化网络解决方案已经完全转向 了云企业网,所以所有使用阿里云的企业客户多多少少都会使用阿里云的CEN 支撑自己的业务。 云企业网介绍 云企业网(Cloud Enterprise Network)是承载在阿里云提供的高性能、低延迟的私有全球网络上的一张高可用网络。 IDC专线接入CEN 首先面临的是云上和云下打通,那么

修电脑的码农 4840

第十四章 高级特性-海纳百川:BIRT报表扩展点(续)

14.2 BIRT数据源的扩展 ODA 之所以能够有如此强的灵活性,是因为: ·          它提供了一套完整的接口,开发者可以自己去实现数据源的访问逻辑,使得数据源对数据使用者变得透明。只要开发者遵循编程规范,就可以对任何数据进行驱动。 ·          它基于 Eclipse 的 plugin 框架,可以通过 Eclipse 的扩展点方便地连接数据源并且被数据使用者

huazaichang的专栏 956

避坑指南:sherpa-onnx中文语音识别模型实测对比(2024最新版)

本文实测对比了sherpa-onnx框架下Paraformer、Zipformer、SenseVoice等主流中文语音识别模型。针对不同应用场景,从准确率、延迟、资源占用等维度提供选型建议,并分享部署避坑指南与最佳实践,帮助开发者高效选择本地语音转文字方案。

xray4的博客 2278

华为AR6140E/S5735设备密码策略配置全攻略:从复杂度到锁定机制

本文详细介绍了华为AR6140E/S5735设备的密码策略配置方法,涵盖密码复杂度、时效性和防护性三大核心维度。通过具体的CLI命令示例,指导管理员如何设置密码长度、字符组合、有效期及登录失败锁定等安全机制,为企业网络提供全面的用户名密码防护策略。

weixin_30836759的博客 531

十四、高级特性-海纳百川:BIRT报表扩展点

如果仅仅只是前面章节提到的BIRT的设计器的便捷性和BIRT引擎提供的用户自定义BIRT报表展示器等自定义特性,只是表现在BIRT报表作为工具的优势,或者作为服务提供者为系统集成做出的贡献,但这还不足以让BIRT成为eclipse开源社区排名前六的基础插件平台。 众所周知,大凡伟大的IT作品,皆是以开放为基础。ios的app store,android的app market,eclipse的plu...

weixin_41961329的博客 473

数学史学习资料:史前数学

著名物理学家伽莫夫(宇宙大爆炸理论以及核聚变理论的提出者)在他的科普书《从1到无穷大》中举了这样一个例子。两个原始部落的酋长比赛,看谁说的数字大。第一个酋长说了三,第二个酋长想了半天,然后说你赢了。你今天听起来觉得这两个人真可笑,至少说出四,岂不就赢了?但是对这两位酋长来讲,所拥有的东西就很少超过三个,比三个多的东西他们就觉得没有必要数清楚了,就用许多来形容了。也就是说,他们生活的环境限制了他们的认知。

howard2005的专栏 902

BIRT修改下拉多选框

深入研究过BIRT运行时的小伙伴们应该对BIRT运行时整体运行流程应该是比较清晰的,BIRT运行端主要是通过prototype的javascript框架进行控制,将每个布局进行展示并且进行事件处理。那么在BIRT运行端显示多选框的时候会是这种样式的:但是这种显示在BIRT报表运行中是可以的,因为在BIRT中是通过参数容器页面来显示所有的参数,当然其中也包括这个多选框,但是如果是将BIRT改成更加适

Q 4139

环境配置与基础教程:当前大厂主流套路:使用 MLflow 全链路管理 YOLO 实验参数、指标与模型版本

YOLO26与MLflow结合实现高效实验管理 YOLO26通过移除DFL模块和实现无NMS端到端推理,在CPU推理速度上提升43%,同时简化了模型导出流程。MLflow 3.10/3.11版本新增多工作区支持、成本追踪和安全序列化等功能,为机器学习实验提供全生命周期管理。两者结合可解决传统"截图+Excel"式实验管理的痛点,实现参数追踪、指标对比和模型版本控制的自动化。 关键优势: YOLO26架构优化带来显著性能提升 MLflow提供实验到生产的全链路管理 原生集成简化了计算机视觉

ZXSXJ的博客 191

飞牛NAS1.1.19更新后SSH连不上?两步快速恢复连接

飞牛NAS1.1.19版本更新后SSH无法连接😟,实则是官方安全策略升级默认关闭了该服务,本文分享两步实操配置恢复SSH连接💻,还同步了官方的设备安全配置建议,帮助用户兼顾使用与设备防护🔒。

XiaoqiangClub的博客 2099

Eclipse BIRT使用之BIRT Designer

<br /><br />在開源的報表工具中,Eclipse的BIRT(Business Intelligence and Reporting Tools ,簡稱BIRT)應該是很重要的一種。BIRT基於Eclipse IDE開發,可以為Java/J2EE應用提供良好的圖形及報表。該報表工具主要包括以下幾大塊組成:<br />l         BIRT Designer:用於進行報表的設計,因為以出報表之前,第一步需的做的就是設計模板,幾乎所有的報表工具都采用這樣的模式,如著名的JasperReports就

技术百宝箱 1784

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

checkbutton应用示例

复选框checkbutton是tkinter库中较常用的控件之一,以下示例主要演示多个复选框的使用方法。

海风的博客 247

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

聚合函数的使用

一、聚合函数介绍 1.概念 聚合函数作用于一组数据,并对一组数据返回一个值。 2.聚合函数的类型 ①AVG和SUM函数 只适用于数值类型的字段(或变量) SELECT AVG(salary),SUM(salary),AVG(salary)*107 FROM employees; #如下的操作是没有意义的 SELECT SUM(last_name),AVG(last_name),SUM(hire_date) FROM employees; ②MIN和MAX函数 适用于数值类型、字符串类型..

博客的博客 4294

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: 第十三章 BIRT报表引擎API及报表API
下一篇: 第十五章 高级特性-开放源码:BIRT报表CVS构建
freedom1993
博客等级 码龄12年 25粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值