xpage调用java_XPages 开发实践:开发基于 XPages 的复合应用程序

简介: 本文详细介绍了如何使用 Lotus Domino Designer 8.5 中的 XPages 技术开发一个复合应用程序。

这个部分将会对本文中所使用的相关技术做简要的介绍。读者如需了解更详尽的内容,可以访问 IBM 或相关组织的网站。

复合应用程序是面向服务体系结构(Service Oriented Architecture,SOA)和上下文协作策略 (contextual collaboration strategy) 中的关键元素。复合应用程序由松散耦合的用户界面组件组成,支持组件之间的通信。组件可以在多个复合应用程序中重用。能够将多种技术组合成一个应用程序,这种能力可以提供巨大的业务价值(参见“参考资源”)。

在 Lotus Notes/Domino 8 众多新特性中,复合应用(Composite Application)是最具有革命性的一次突破。其实我们可以从多个角度来理解这项技术:从企业 Web 2.0 的观点来看,你可以说它代表着混搭 Mashup 的思想;站在企业集成的角度来看,它是 SOA 在用户前端的具体实现;对用户而言,它是一种全新的界面体验;而对传统的 Lotus 开发者来说,复合应用带给了他们新的挑战和机遇(参见“参考资源”)。

本文实际上是结合实例介绍如何开发一个复合应用程序。本文的实例有两个组件组成,一个是 Managed Browser 的组件 (IBM DeveloperWorks/ wiki / forum 上有很多文章介绍如何基于 Lotus Notes/Domino 8 或者 IBM Expeditor 产品开发 Managed Browser 组件应用,本文略叙之 )。另一个是基于 XPages 技术开发的组件,本文的重点在于介绍如何开发使用这个基于 XPages 技术的组件。

XPages 技术是 IBM Lotus Domino 8.5 中新增的一个功能强大的设计元素。XPages 使用户可以利用所有 Web 必备的功能,可在现有应用程序上构建或创建新的设计元素,可以充分利用它为我们的项目呈现 Web 客户端用户界面。 Domino 8.5 为用户提供了集成 Eclipse IDE 的可视化开发环境。使用 XPages 开发基于 Domino 的 Web 应用,可以获得“所见即所得”的用户体验。对 XPages 中的每一个页面、控件、对象,我们都可以直接以可视化的方式修改它们的属性,并且即时预览;相应的,对象所支持的事件,XPages 也提供了分门别类的向导,帮助用户编辑和管理事件的响应脚本(参见“参考资源”)。

在 Lotus Domino ® Designer 8.5 中,开发者能够利用 XPage 技术创建一流的组件并感受完美的用户体验。 本文重点介绍如何创建 XPages 组件,定义输入事件,发布事件,编写 XPage 用户界面,以及如何应用 Javascript,Domino Java API 完成相应的业务逻辑功能等等。

Domino 的 Java APIs 为用户提供了在 Java 程序中访问 Domino 数据的能力。用户可以在 Domino Agent、Applet、Servlet 中通过 Domino 的 Java API 读取和操作 Domino 数据。现在,通过 Designer 集成环境,用户可以在 Domino 数据库中编写 Java 函数库,然后在 XPages 页面中使用 Javascript. 调用 Java 函数,完成各种数据操作(参见“参考资源”)。 如果读者想了解更多有关 Domino Java APIs 的知识,请参考 Notes Client 帮助文档信息。

本文试图通过一个简单的应用示例 - 用户可以把自己感兴趣的 2010 年南非足球世界杯的比赛日程加入到 Notes 的日历中来,进而介绍如何基于 XPages 技术开发复合应用程序的组件的。

首先我们需要创建一个 NSF 数据库,命名为 WorldCupCalendarHelper。 如图 1 所示:

接下来我们生成一个新的 XPages 页面: CalendarHelper,如图 2 所示:

下面我们要生成组件的定义。在 WorldCupCalendarHelper 数据库中右键点击 Components 元素创建一个新的组件 CalendarComponent。当创建的组件是为了复合应用的程序时(也可以是为了 lotus Mushups 的应用需要,开发者应该以视图(View)模式关联 Xpages 页面和相应的组件,如图 4 所示。

同时我们在 XPages 组件中定义了三个类型是 String 的监听事件 WccwEvent(这个事件是用来监听比赛对阵的);WcDateEvent(这个事件是用来监听比赛日期的);WcTimeEvent( 这个事件是用来监听比赛时间的 )。本例中,XPages 组件正是通过这个这些监听事件同 Managed Browser 组件进行通信的。当然 Xpages 组件也能够发布事件(publish event)。那么如何在 Xpages 组件中发布事件呢?有两种方式。

其一,在 XPages 中,开发人员可以使用客户端 Javascript. 角本发布一个已经定义的组件事件。具体步骤如下:

首先要实现一个 Javascript. 角本的触发机制,比如创建一个 XPages Button,该按钮的客户端 onclick 事件触发 Javascript. 角本的执行。

然后调用 XSP.publishEvent(name, value, type) 方法发布事件。Name 参数就是已定义的事件的名称;type 是该事件的事件类型;value 值则必须要复合事件类型的定义。如清单 1 所示:

清单 1. 应用客户端 javascript. 发布事件

XSP.publishEvent("publishString", "hello dragon", "string");

XSP.publishEvent("publishNumber", "30", "number");

XSP.publishEvent("publishBoolean", "false", "boolean");

XSP.publishEvent("publishJson", {"userId": "dragonli"}, "JSON");

// 开发者也可以把服务器端 JavaScript™ 作为第二个参数来发布经由服务器计算的结果值

XSP.publishEvent("publishString", "#{javascript.:document.thefield}", "string");

其二,开发人员也可以通过简单的动作(simple action)来发布 XPages 组件事件。具体步骤如下:

首先要实现一个 simple action 的触发机制,比如创建一个 XPages Button,该按钮的客户端 onclick 事件触发 simple action 的执行。

选择客户端的 simple action。 如图中所示,下拉列表中有两个 simple action 值:发布组件属性(Publish Component Property)和发布视图列(Publish View Column)。前者适用于所有的事件,后者仅适用于视图列的 onclick 事件。

接下来填写已经定义好的事件的名称,其值以及对应的事件类型。清单 2 是对应的源代码。

图 5. Simple action 发布事件

80a7dfd9e8245d27934a45f2ecf80087.png

清单 2. 客户端 simple action 发布事件示例代码

refreshMode="complete">

type="string">

关于事件的类型,XPages 提供内置的 String, Number, Boolean, JSON 四种类型。当然开发者可以根据业务逻辑需要通过 Custom Types 配置页面来定制事件类型。

我们已经定义了这些监听事件,接下来,我们介绍 XPages 组件是如何从别的组件获取信息的。打开 CalendarXpage 页面,双击“New Event …”,然后输入我们已经定义的这些接收事件的名字(注意:名字需要对应一致),如下图所示。

我们定义了三个对应的组件事件,现在我们需要为他们做些编写代码的工作。代码是在服务器端执行的。我们将把提交上来的值存储在一个会话(session)的属性里面。该属性的名字分别是 counterworkerEntry,dateEntry 和 timeEntry。这样我们就可以在 XPage 的其它部分使用这些属性值了。代码仅包含一行语句,比如对于 WccwEvent,我们增加如下一行代码:context.setSessionProperty ("counterworkerEntry", context. getSubmittedValue()); 其余类似。

如上图所示,我们定义了一个按钮和一些标签界面元素。其中用花括号括起来的标签元素是会根据业务逻辑动态生成显示内容的。比如,红色矩形框所对应的逻辑代码是:

接下来,我们通过 onclick 事件实现了 Add to Calendar 按钮的功能。该功能就是生成 Notes Calendar 的记录信息。(服务器端的 Javascript. 通过调用 Domino APIs 完成了创建比赛日程表的任务。)

右键点击 Applications 元素创建 Composite 定义。如下图所示:

与此同时,我们需要确认该 NSF 数据库应以复合应用的方式启动。(右键点击数据库-> 选择属性-> 打开 Launch Tab 栏进行编辑)如下图所示:

通过以上的步骤,我们就轻松的完成了 XPage 组件的开发工作。对于 XPages 技术 , Lotus ®Domino Designer 提供了非常强大的平台技术支持。下面我们把 Managed Browser 组件加进来,进而完善此复合应用程序。

通过本文背景知识的介绍,读者知道复合应用程序体现了混搭 Mashup 的思想。是各个不同组件之间的相互通信,相互协作的一种用户体验。基于 XPages 技术的组件也需要同其它组件通信才能体现它的价值。

现在关掉 Notes Domino Designer,在 Notes Client 中打开 WorldCupCalendarHelper 数据库,根据前文的配置,它会默认显示一张空白页面。点击 Actions – > Edit Application 编辑此复合应用程序。

接下来,我们将按照如下的步骤完成该示例复合应用的集成部署工作:

首先更改复合应用程序的标题为:World Cup Calendar Helper

更改空白页面的标题为:Calendar Helper

然后把前面我们所创建的基于 XPages 技术的组件添加到复合应用中来,如下图所示:

图 12. 加入 Xpages 组件 1

2c18b1c7b4d14a1c07d49a09ccad79c0.gif

点击 “Browser …” 按钮,加入 CalendarComponent 组件。

图 13. 加入 Xpages 组件 2

085d30154cad6d0d0a3d1bd44d5e750f.gif

把生成的 WorldCup Calendar Helper 组件拖拽到右边的边栏(Sidebar)中。

图 14. 放置 XPages 组件

f635be5526353adfa8c92529f15645e2.gif

拖拽 Managed Browser 组件到复合应用的主页面,并输入http://sports.sohu.com/s2009/2547/s268698101/(来自 Sohu 世界杯官方网站)。并生成三个类型是 Map HTML Table 的活动。分别对应比赛对阵,比赛日期和比赛时间。当用户选中一场比赛对阵的时候,点击并触发该活动,通过复合应用的 wiring 机制把该比赛对阵的信息传给了 XPages 组件。

图 15. 配置 Managed Browser 组件 1

16ca31aa301c13918888cb57080a34be.gif

a26f3481334d69b5f216bb0b12830331.gif

在完成所有设定后,需要及时保存设定信息,并把当前页设为主页面。

接下来,右键点击 Managed Browser,打开 Edit Component Property,如下图所示,把蓝色矩形框内 XPath 表达式的固定行号换成通配符“*”,这样点击其它行对应的列的时候,也能触发该事件。

图 16. 配置 Managed Browser 组件 2

cc996bd2c2ade5ff960a91e7d938c725.gif

组件 Wiring

把 Managed Browser 定义好的活动和 Xpages 组件的事件关联起来。这样就实现了组件之间的通信了,当然本应用程序逻辑比较简单,Xpages 组件没有发布事件出来。

图 17. 关联组件事件

a43b67056fc90f6137a8a3d774b3a747.gif

下图是我们的复合应用程序的显示效果图。该复合应用有两部分组成,中间的是基于 Managed Browser 容器的组件,当用户喜欢某一场比赛的时候,点击对应的比赛对阵,日期和时间,相应的信息会出现在边栏的 XPages 组件中,点击 Add to Calendar 按钮,服务器端 Javascript. 调用 Domino APIs 生成一条 Notes 日历记录信息。

图 18. 世界杯复合应用示例程序

4d26ac368f40103a0bae4f384ebe6fb6.gif

图 19. 生成的 Reminder 信息

1780ab4f1536687eb3ea7a3db8be27bb.gif

本文的应用程序不能简单的单击一个 HTML Table 的 CELL 就能生成全部行的信息,所以读者需要点击同一行的这三个单元格触发三个活动来获得相关信息。当然我们开发人员可以定制这个活动。把整个 HTML Table 的信息以 XML 或者 JSON, ICal 的格式传给 XPages 应用组件,然后在 XPages 的应用组件中再进行解析操作,获得各个参数,并根据业务逻辑的需要而使用之。

本文比较系统的介绍了如何基于 Lotus® Domino Designer 8.5 XPages 技术开发组件,并构建简单有意义的复合应用程序。文章浅显易懂,作者希望本文能起到一个抛砖引玉的作用,更多的开发人员开始关注 XPages 技术,喜欢 XPages 技术。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XPAGE学习笔记 1 Theme 2 2 在Xpage中使用Dojo 2 2.1 加载dojo.js 2 2.2 设置应用程序主题引入tundra.css 2 2.3 设置xpage属性引入dojo模块 3 2.4 Xpage的页面html代码 4 2.5 在xpage中使用dojo编程 4 2.5.1 按钮(dijit.form.Button) 4 2.5.1.1 通过插入<div>标签生成dojo按钮 5 2.5.1.2 使用xpage按钮控件 7 2.5.1.3 在按钮控件上使用事件处理程序控件 7 2.5.2 Xpage中使用 Spinner(dijit.form.NumberSpinner) 9 2.5.3 在xpage中使用ProgressBar(dijit.ProgressBar) 10 2.5.4 在Xpage中使用树(dijit.Tree) 10 2.5.4.1 JSON与XML数据源 11 2.5.4.2 Box树与Order树 12 2.5.4.3 对树进行添加、删除及重命名操作 13 2.5.4.4 将树的操作记录到后台数据库 14 2.5.4.5 树的拖放 15 3 在Xpage中如何查找控件 16 3.1 理解document.getElementById 16 3.2 理解dojo.byId 17 3.3 理解dijit.byId 17 3.4 理解XSP.getElementById 17 3.5 理解getComponent 17 4 Xpage VS 表单 18 5 使用oneui主题 18 6 参考 18 6.1 JSON与String转换 18 6.2 XPage中调试代码 18 1 Theme 主题在Domino中的路径:D:\IBM\Lotus\Domino\xsp\nsf\themes 2 在Xpage中使用Dojo  Xpage自动加载dojo.js  通过设置应用程序主题将tundra.css引用到xpage  设置xpage属性,引用dojo模块  在xpage中使用dojo编程 2.1 加载dojo.js Xpage会自动加载dojo.js,默认情况下xpage自动查找data\domino\js下的最新版本。也可以通过设置xsp.properties文件来修改dojo版本,重命名xsp.properties.sample 文件(D:\IBM\Lotus\Domino\data\properties\ xsp.properties.sample) 2.2 设置应用程序主题引入tundra.css 通过设置应用程序主题将tundra.css引用到xpage 2.3 设置xpage属性引入dojo模块 2.4 Xpage的页面html代码 2.5 在xpage中使用dojo编程 2.5.1 按钮(dijit.form.Button)  通过插入<div>标签生成dojo按钮  使用xpage按钮控件  在按钮控件上使用事件处理程序控件 2.5.1.1 通过插入<div>标签生成dojo按钮 第一步:在xpage中插入<div dojoType=”dijit.form.Button” id=”dojoBtn”></div> 第二步:在xpage中插入<xp:scriptBlock></xp:scriptBlock> 第三步:编写客户端javascript <div dojoType="dijit.form.Button" id="dojoBtn"></div> <xp:scriptBlock> <xp:this.value><![CDATA[function showDia(){ var dia = new dijit.Dialog({title:"dojo按钮演示",style:"width:150px"}); var pane = document.createElement("div"); pane.innerHTML = "<span>Hello Dojo!</span>"; dia.setContent(pane); dia.show(); } function btnDemo(){ var dojoBtn = dijit.byId("dojoBtn"); var dojoBtnNode = dojo.byId("dojoBtn"); dojoBtn.setLabel("Dojo Button"); // dojo.connect(dojoBtn.domNode,"onclick","showDia"); dojo.connect(dojoBtnNode,"onclick","showDia"); } dojo.addOnLoad(btnDemo);]]></xp:this.value> </xp:scriptBlock> 2.5.1.2 使用xpage按钮控件 <xp:button id="button1" value="按钮控件"> <xp:this.onclick> <![CDATA[ var dia = new dijit.Dialog({title:"提示:",style:"width:150px;"}); var pane = document.createElement("div"); pane.innerHTML = "<span>dojo按钮演示!</span>"; dia.setContent(pane); dia.show(); ]]> </xp:this.onclick> </xp:button> 2.5.1.3 在按钮控件上使用事件处理程序控件 将事件处理程序控件拖放到按钮控件上 2.5.2 Xpage中使用 Spinner(dijit.form.NumberSpinner) <xp:inputText id="numSales"></xp:inputText> <xp:scriptBlock> <xp:this.value><![CDATA[ dojo.addOnLoad(function(){ new dijit.form.NumberSpinner({ name:"#{id:numSales}", value:50, smallDelta:1, min:0, places:0},XSP.getElementById("#{id:numSales}")); }); ]]></xp:this.value> </xp:scriptBlock> var myvalue = "#{javascript: var value; if(getComponent("numSales").getValue() != null){ value = getComponent("numSales").getValue().intValue(); }else{ value = 50; } return value.toFixed(0);}"; new dijit.form.NumberSpinner({name:"#{id:numSales}", value:myvalue, smallDelta:1, constraints:{min:0,places:0}}, XSP.getElementById("#{id:numSales}")); 2.5.3 在xpage中使用ProgressBar(dijit.ProgressBar) <div dojoType="dijit.ProgressBar" style="width:300px" jsId="jsProgress" id="div1"> </div> <xp:button value="Start" id="button2"> <xp:eventHandler event="onclick" submit="false"> <xp:this.handlers> <xp:handler type="text/javascript"> <xp:this.script><![CDATA[ var numParts = Math.floor(100/7); jsProgress.update({ maximum: numParts, progress:0 }); for (var i=0; i<=numParts; i++){ setTimeout("jsProgress.update({ progress: " + i + " })",(i+1)*100 + Math.floor(Math.random()*100)); } ]]></xp:this.script> </xp:handler> </xp:this.handlers> </xp:eventHandler> </xp:button> 2.5.4 在Xpage中使用树(dijit.Tree)  JSON与XML数据源  Box树与Order树  对树进行添加、删除及重命名操作  将树的操作记录到后台数据库  树的拖放 2.5.4.1 JSON与XML数据源 box.json文件 { identifier:'id', label:'name', items:[ {id:1,name:"盒子1",type:"box",weight:0} ] } order.xml文件 <orders> <order> <orderNumber>001</orderNumber> <description>订单001</description> <priority>航运</priority> <line> <orderNumber>001-1</orderNumber> <qty>1</qty> <sku>11761</sku> <description>精通DOJO</description> </line> <line> <orderNumber>001-2</orderNumber> <qty>3</qty> <sku>11789</sku> <description>项目管理那些事儿</description> </line> </order> <order> <orderNumber>002</orderNumber> <description>订单002</description> <priority>快递</priority> <line> <orderNumber>002-1</orderNumber> <qty>10</qty> <sku>11763</sku> <description>精通J2EE</description> </line> <line> <orderNumber>002-2</orderNumber> <qty>30</qty> <sku>11799</sku> <description>超越对手</description> </line> </order> </orders> 2.5.4.2 Box树与Order树 <div dojoType="dojo.data.ItemFileWriteStore" jsId="boxStore" url="/tree/box.json"> </div> <div dojoType="dojox.data.XmlStore" jsId="orderStore" label="description" keyAttribute="orderNumber" url="/tree/order.xml"> </div> <div dojoType="dijit.tree.ForestStoreModel" jsId="boxModel" store="boxStore" rootLabel="退货物品" childrenAttrs="orders,items"> </div> <div dojoType="dijit.tree.ForestStoreModel" jsId="orderModel" store="orderStore" rootLabel="订单" childrenAttrs="line,order"> </div> <xp:td style="height:200.0px" valign="top"> <xp:panel> <div dojoType="dijit.Tree" id="boxTree" model="boxModel"> <script type="dojo/method" event="onClick" args="item"> lastBoxSelected = item; </script> </div> </xp:panel> </xp:td> <xp:td style="height:200.0px" valign="top"> <xp:panel> <div dojoType="dijit.Tree" id="orderTree" model="orderModel"> </div> </xp:panel> </xp:td> 2.5.4.3 对树进行添加、删除及重命名操作 <xp:table> <xp:tr> <xp:td> <div dojoType="dijit.form.Button" id="addBtn"> 添加 <script type="dojo/method" event="onClick"> boxStore.newItem({id:2,name:"盒子2",type:"box",weight:0,orders:[]}); </script> </div> </xp:td> <xp:td> <div dojoType="dijit.form.Button" id="delBtn"> 删除 <script type="dojo/method" event="onClick"> if(boxStore.isItem(lastBoxSelected)){ boxStore.deleteItem(lastBoxSelected); } </script> </div> </xp:td> <xp:td> <div dojoType="dijit.form.Button" id="reBtn">重命名 <script type="dojo/method" event="onClick"> if(boxStore.isItem(lastBoxSelected)){ boxStore.setValue(lastBoxSelected,"name","新盒子"); } </script> </div> </xp:td> </xp:tr> </xp:table> 2.5.4.4 将树的操作记录到后台数据库 <div dojoType="dojo.data.ItemFileWriteStore" jsId="boxStore" url="/tree/box.json"> <script type="dojo/connect" event="onNew" args="newItem"> var itemId = this.getValue(newItem,"id"); var itemName = this.getValue(newItem,"name"); dojo.xhrPost({ url:"agCreateNewBox?OpenAgent", content:{id:itemId,name:itemName}, timeout:10000, error:function(){alert("对不起,出错啦!");}, load:function(){alert("数据保存成功!");console.debug("数据保存成功!");} }); </script> <script type=”dojo/connect” event=”onDelete” args=”delItem”> </script> </div> 2.5.4.5 树的拖放 要使树具有拖放功能,仅仅需要设置dndController属性(dijit._tree.dndSource已废弃): 要限制拖放的行为,仅仅需要实现checkAcceptance或checkItemAcceptance处理函数即可: 以上已经实现了树的拖放功能了! 但是,如果要将拖放的结果保存起来,也就是说要改变树的模型并将模型数据保存,那么应该编写onDndDrop的处理函数(通过增加jsId获得dndController会出现this.avaria is null 错误,所以下面通过小部件初始化时将dndController传递给变量controller): 在boxDrop中与checkAcceptance一样可以得到target与source,另外,onDndDrop中的source与nodes有些相似source.selection[something]=nodes[somethin] 结合ItemFileWriteStore 可以实现对模型数据的保存 3 在Xpage中如何查找控件 3.1 理解document.getElementById 得到node,创建node可以用document.createElement 这样访问或设置节点的所有属性 node.innerHTML = “” node.value=”” var id = node.id var style = node.style 3.2 理解dojo.byId 得到node,也可以通过dijit.byId().domNode得到节点 dojo.byId(“myid”).value dojo.byid(“myid”).id dojo.attr(node,attribute) 如读取dojo.attr(node,”id”) dojo.attr(node,attribute,value) 如设置dojo.attr(node,”id”,”myid”) dojo.hasAttr(node,attribute) 如判断 dojo.hasAttr(node,”id”) dojo.connect(node,”onclick”,handler) dojo.removeClass(node,”myclass”) 3.3 理解dijit.byId 得到dojo控件对象,也可以通过dijit.byNode(dojo.byId())得到对象 不同的控件的getValue()返回值类型不一样 在xpage中对于<div>标签可能使用期dijit.byId(“myid”),对于<xp:>标签是不可以的 dijit.byId().getValue() dijit.byId().setValue() 3.4 理解XSP.getElementById 这是XSPClientDojo 库中定义的,XSP全局变量相当于document对象,为客户端访问xpage控件提供方法 XSP.getElementById(“#{id:myid}”)使用JSF表达式查询控件 var xid = XSP.getElementById(“#{id:myid}”) xid.value = “myvalue” xid.name = “myname” xid.disabled = false 3.5 理解getComponent javascript服务器端访问控件 getComponet(“myid”).getValue(); getComponet(“myid”).getSubmittedValue(); getComponent(“myid”).setValue(); getComponent(“myid”).setSubmittedValue(); 4 Xpage VS 表单 5 使用oneui主题 6 参考 6.1 JSON与String转换 dojo.fromJson(string) 返回json对象 dojo.toJson(object) 返回string对象 eval(‘(‘+string+’)’) 返回json对象 6.2 XPage中调试代码 window.alert 函数不可用于服务器脚本。 使用以下函数写入服务器上的 console.log(例如 C:\Notes\Data\IBM_TECHNICAL_SUPPORT\console.log): print(string) 写入一个字符串。 _dump(object) 写入表示对象的字符串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值