应用Myfaces 进行On-demand loading分页

第一章
引入myfaces的其中一个t标签:

<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

<t:dataTable id="data"

styleClass="scrollerTable"

headerClass="standardTable_Header"

footerClass="standardTable_Header" rowClasses="standardTable_Row1,standardTable_Row2" columnClasses="standardTable_Column,standardTable_ColumnCentered,standardTable_Column"

var="car" value="#{scrollerList.list}"

preserveDataModel="false" rows="10"></t:dataTable>

讲解:

t:dataTable标签是用来分页输出List数组

id定义分页表的id,

styleClass定义表的样式css类

headerClass定义表头的样式css类

footerClass定义表尾的样式css类

rowClasses定义行的样式css类

columnClasses定义列的样式css类

var定义每一条记录

value指定要分页的数据

rows指定每页显示数据

<t:dataScroller id="scroll_1" for="data" fastStep="10"

pageCountVar="pageCount" pageIndexVar="pageIndex"

styleClass="scroller" paginator="true" paginatorMaxPages="5"

paginatorTableClass="paginator"

paginatorActiveColumnStyle="font-weight:bold;" immediate="true"

actionListener="#{scrollerList.scrollerAction}">

</t: dataScroller >

讲解:

t:dataScroller显示分页信息

id定义分页格式id

for指向数据源

fastStep向前向后快进的页数

pageCountVar总页数

pageIndexVar当前页数

styleClass定义分页格式的css类

paginator是否显示页数变化轨迹

paginatorMaxPages指定页数变化轨迹中的数量

第二章
应用Myfaces 进行On-demand loading分页

index.jsp>>>

<%@ page session="false" contentType="text/xml;charset=utf-8"%>

<%

response.sendRedirect("dataScroller.jsf");

%>

dataScroller.jsp>>>页面中的图片和css请参见myfaces.apache.org的simple

<%@ page session="false" contentType="text/html;charset=utf-8"%>

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>

<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

<html>

<%@include file="inc/head.inc"%>

<body>



<f:view>

<h:form>



<f:loadBundle

basename="org.apache.myfaces.examples.resource.example_messages"

var="example_messages" />



<h:panelGroup id="body">



<t:dataTable id="data" styleClass="scrollerTable"

headerClass="standardTable_Header"

footerClass="standardTable_Header"

rowClasses="standardTable_Row1,standardTable_Row2"

columnClasses="standardTable_Column,standardTable_ColumnCentered,standardTable_Column"

var="car" value="#{scrollerList.dataModel}" preserveDataModel="true"

rows="10">

<h:column>

<f:facet name="header">

</f:facet>

<h:outputText value="#{car.id}" />

</h:column>



<h:column>

<f:facet name="header">

<h:outputText value="#{example_messages['label_cars']}" />

</f:facet>

<h:outputText value="#{car.type}" />

</h:column>



<h:column>

<f:facet name="header">

<h:outputText value="#{example_messages['label_color']}" />

</f:facet>

<h:outputText value="#{car.color}" />

</h:column>



</t:dataTable>



<h:panelGrid columns="1" styleClass="scrollerTable2"

columnClasses="standardTable_ColumnCentered">

<t:dataScroller id="scroll_1" for="data" fastStep="10"

pageCountVar="pageCount" pageIndexVar="pageIndex"

styleClass="scroller" paginator="true" paginatorMaxPages="5"

paginatorTableClass="paginator"

paginatorActiveColumnStyle="font-weight:bold;"

actionListener="#{scrollerList.scrollerAction}">

<f:facet name="first">

<t:graphicImage url="images/arrow-first.gif" border="1" />

</f:facet>

<f:facet name="last">

<t:graphicImage url="images/arrow-last.gif" border="1" />

</f:facet>

<f:facet name="previous">

<t:graphicImage url="images/arrow-previous.gif" border="1" />

</f:facet>

<f:facet name="next">

<t:graphicImage url="images/arrow-next.gif" border="1" />

</f:facet>

<f:facet name="fastforward">

<t:graphicImage url="images/arrow-ff.gif" border="1" />

</f:facet>

<f:facet name="fastrewind">

<t:graphicImage url="images/arrow-fr.gif" border="1" />

</f:facet>

</t:dataScroller>

<t:dataScroller id="scroll_2" for="data" rowsCountVar="rowsCount"

displayedRowsCountVar="displayedRowsCountVar"

firstRowIndexVar="firstRowIndex" lastRowIndexVar="lastRowIndex"

pageCountVar="pageCount"

pageIndexVar="pageIndex">

<h:outputFormat value="#{example_messages['dataScroller_pages']}"

styleClass="standard">

<f:param value="#{rowsCount}" />

<f:param value="#{displayedRowsCountVar}" />

<f:param value="#{firstRowIndex}" />

<f:param value="#{lastRowIndex}" />

<f:param value="#{pageIndex}" />

<f:param value="#{pageCount}" />

</h:outputFormat>

</t:dataScroller>

</h:panelGrid>

</h:panelGroup>

<t:commandLink value="test" immediate="true" />

</h:form>

</f:view>

</body>

</html>



Car.java>>>

package org.apache.myfaces.examples.listexample;



import java.io.Serializable;



public class Car implements Serializable {

/**

* serial id for serialisation versioning

*/

private static final long serialVersionUID = 1L;



private String id;



private String type;



private String color;



public Car() {



}



public Car(String id, String type, String color) {

this.id = id;

this.type = type;

this.color = color;

}



public String getId() {

return id;

}



public void setId(String id) {

this.id = id;

}



public String getType() {

return type;

}



public void setType(String type) {

this.type = type;

}



public String getColor() {

return color;

}



public void setColor(String color) {

this.color = color;

}

}





DataPage.java>>>

package org.apache.myfaces.examples.listexample;



import java.util.List;



public class DataPage {

/**

* 将需要的页的数据封装到一个DataPage中去, 这个类表示了我们需要的一页的数据,<br>

* 里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。<br>

* datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,<br>

* startRow表示该页的起始行在数据库中所有记录集中的位置

*/

private int datasetSize;



private int startRow;



private List data;



/** */

/**

* Create an object representing a sublist of a dataset.

*

* @param datasetSize

* is the total number of matching rows available.

*

* @param startRow

* is the index within the complete dataset of the first element

* in the data list.

*

* @param data

* is a list of consecutive objects from the dataset.

*/

public DataPage(int datasetSize, int startRow, List data) {

this.datasetSize = datasetSize;

this.startRow = startRow;

this.data = data;

}



/** */

/**

* Return the number of items in the full dataset.

*/

public int getDatasetSize() {

return datasetSize;

}



/** */

/**

* Return the offset within the full dataset of the first element in the

* list held by this object.

*/

public int getStartRow() {

return startRow;

}



/** */

/**

* Return the list of objects held by this object, which is a continuous

* subset of the full dataset.

*/

public List getData() {

return data;

}

}





DataScrollerList.java>>>

package org.apache.myfaces.examples.listexample;



import java.util.ArrayList;

import java.util.List;



import javax.faces.context.FacesContext;

import javax.faces.event.ActionEvent;

import javax.faces.model.DataModel;



import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.apache.myfaces.custom.datascroller.ScrollerActionEvent;

import org.apache.myfaces.examples.dao.CarDAO;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;



public class DataScrollerList extends BasePagedBackingBean {

private final Log log = LogFactory.getLog("DataScrollerList");



private CarDAO carDAO = (CarDAO) getBean("carDAOBean");



public DataScrollerList() {

log.info("创建 DataScroller");

}



public void scrollerAction(ActionEvent event) {

ScrollerActionEvent scrollerEvent = (ScrollerActionEvent) event;

FacesContext.getCurrentInstance().getExternalContext().log(

"scrollerAction: facet: " + scrollerEvent.getScrollerfacet()

+ ", pageindex: " + scrollerEvent.getPageIndex());

}



public int getTotalCount() {

int totalCount = 0;

totalCount = carDAO.getTotalCount();

return totalCount;

}



/**

* 在DataScrollerList这个 Backing Bean中加一些东西,<br>

* 调用业务逻辑,并将数据交给PagedListDataModel,来帮我们完成最后的分页工作。

*/

public DataPage getDataPage(int startRow, int pageSize) {

DataPage dataPage = carDAO.getDataPage(startRow, pageSize);

return dataPage;



}

}



BasePagedBackingBean.java>>>

package org.apache.myfaces.examples.listexample;



import javax.faces.model.DataModel;



import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;



public abstract class BasePagedBackingBean {

private final Log log = LogFactory.getLog("BasePagedBackingBean");



protected abstract DataPage getDataPage(int startRow, int pageSize);



public abstract int getTotalCount();



private DataModel dataModel;



private int i = 0;



// 为什么getDataModel这个方法要调用两次?非常不解啊

public DataModel getDataModel() {

i++;

log.info("第" + i + "次调用 getDataModel.");

if (dataModel == null) {

log.info("创建 DataModel");

dataModel = new LocalDataModel(10);

}

return dataModel;

}



public Object getBean(String beanName) {

ApplicationContext ac = new ClassPathXmlApplicationContext(

"spring/applicationContext.xml");

Object ob = (Object) ac.getBean(beanName);

return ob;

}



private class LocalDataModel extends PagedListDataModel {

public LocalDataModel(int pageSize) {

super(pageSize);

}



public int fetchRowCount() {

return getTotalCount();

}



public DataPage fetchPage(int startRow, int pageSize) {

// call enclosing managed bean method to fetch the data

return getDataPage(startRow, pageSize);

}

}

}



PagedListDataModel.java>>>

package org.apache.myfaces.examples.listexample;



import javax.faces.model.DataModel;



import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;



/** */

/**

* A special type of JSF DataModel to allow a datatable and datascroller to page

* through a large set of data without having to hold the entire set of data in

* memory at once.

* <p>

* Any time a managed bean wants to avoid holding an entire dataset, the managed

* bean should declare an inner class which extends this class and implements

* the fetchData method. This method is called as needed when the table requires

* data that isn\'t available in the current data page held by this object.

* <p>

* This does require the managed bean (and in general the business method that

* the managed bean uses) to provide the data wrapped in a DataPage object that

* provides info on the full size of the dataset.

*/

/**

* 这个类里面的方法被myfaces<t:dataTable>调用顺序为:<br>

* 1.构造函数public PagedListDataModel(int pageSize) <br>

* 2.getRowCount()<br>

* 3.setRowIndex()<br>

* 4.public boolean isRowAvailable()<br>

* 5.public Object getRowData()

*/

public abstract class PagedListDataModel extends DataModel {

private final Log log = LogFactory.getLog("PagedListDataModel");



int pageSize;



int rowIndex;



private int rowCount = -1;



DataPage page;



/** */

/**

* Create a datamodel that pages through the data showing the specified

* number of rows on each page.

*/

public PagedListDataModel(int pageSize) {

super();

this.pageSize = pageSize;

this.rowIndex = -1;

this.page = null;

}



/** */

/**

* Not used in this class; data is fetched via a callback to the fetchData

* method rather than by explicitly assigning a list.

*/

public void setWrappedData(Object o) {

if (o instanceof DataPage) {

this.page = (DataPage) o;

} else {

throw new UnsupportedOperationException("setWrappedData");

}

}



public int getRowIndex() {

return rowIndex;

}



/** */

/**

* Specify what the "current row" within the dataset is. Note that the

* UIData component will repeatedly call this method followed by getRowData

* to obtain the objects to render in the table.

*/

public void setRowIndex(int index) {

rowIndex = index;

}



/**

* Return the total number of rows of data available (not just the number of

* rows in the current page!).

*/

public int getRowCount() {

if (rowCount < 0) {

log.info("默认rowCount:" + rowCount);

rowCount = fetchRowCount();

log.info("初始化rowCount:" + rowCount);

}

return rowCount;

}



/**

* Return a DataPage object; if one is not currently available then fetch

* one. Note that this doesn\'t ensure that the datapage returned includes

* the current rowIndex row; see getRowData.

*/

private DataPage getPage(String name) {

if (page != null) {

return page;

}

int rowIndex = getRowIndex();

int startRow = rowIndex;

if (rowIndex == -1) {

// even when no row is selected, we still need a page

// object so that we know the amount of data available.

startRow = 0;

}

// invoke method on enclosing class

log.info("getPage:" + name + "创建page");

page = fetchPage(startRow, pageSize);

return page;

}



/** */

/**

* Return the object corresponding to the current rowIndex. If the DataPage

* object currently cached doesn\'t include that index then fetchPage is

* called to retrieve the appropriate page.

*/

public Object getRowData() {

if (rowIndex < 0) {

throw new IllegalArgumentException(

"Invalid rowIndex for PagedListDataModel; not within page");

}

// ensure page exists; if rowIndex is beyond dataset size, then

// we should still get back a DataPage object with the dataset size

// in it

if (page == null) {

page = fetchPage(rowIndex, pageSize);

rowCount = page.getDatasetSize();//

log.info("getRowData:创建page");

}

int datasetSize = page.getDatasetSize();

int startRow = page.getStartRow();

int nRows = page.getData().size();

int endRow = startRow + nRows;

if (rowIndex >= datasetSize) {

throw new IllegalArgumentException("Invalid rowIndex");

}

if (rowIndex < startRow) {

log.info("fetchPage:向前取数据,getRowData:创建page,rowIndex:" + rowIndex);

page = fetchPage(rowIndex, pageSize);

log.info("翻页之前rowCount:" + rowCount);

rowCount = page.getDatasetSize();//

log.info("翻页之后rowCount:" + rowCount);

startRow = page.getStartRow();

} else if (rowIndex >= endRow) {

log.info("fetchPage:向后取数据,getRowData:创建page,rowIndex:" + rowIndex);

page = fetchPage(rowIndex, pageSize);

log.info("翻页之前rowCount:" + rowCount);

rowCount = page.getDatasetSize();//

log.info("翻页之后rowCount:" + rowCount);

startRow = page.getStartRow();

}

return page.getData().get(rowIndex - startRow);

}



public Object getWrappedData() {

return page.getData();

}



/** */

/**

* Return true if the rowIndex value is currently set to a value that

* matches some element in the dataset. Note that it may match a row that is

* not in the currently cached DataPage; if so then when getRowData is

* called the required DataPage will be fetched by calling fetchData.

*/

public boolean isRowAvailable() {

DataPage page = getPage("isRowAvailable");

if (page == null) {

return false;

}

int rowIndex = getRowIndex();



if (rowIndex < 0) {

return false;

} else if (rowIndex >= page.getDatasetSize()) {

return false;

} else {

return true;

}

}



/** */

/**

* Method which must be implemented in cooperation with the managed bean

* class to fetch data on demand.

*/

public abstract DataPage fetchPage(int startRow, int pageSize);



public abstract int fetchRowCount();

}



CarDAO.java>>>

package org.apache.myfaces.examples.dao;



import org.apache.myfaces.examples.listexample.DataPage;



public interface CarDAO {

public DataPage getDataPage(int startRow, int pageSize);



public int getTotalCount();

}



CarDAOImpl.java>>>

package org.apache.myfaces.examples.dao.impl;



import java.util.List;



import org.apache.myfaces.examples.dao.CarDAO;

import org.apache.myfaces.examples.listexample.DataPage;

import org.hibernate.Query;

import org.hibernate.Session;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;



public class CarDAOImpl extends HibernateDaoSupport implements CarDAO {



public DataPage getDataPage(int startRow, int pageSize) {

DataPage page = new DataPage(getCountCars(), startRow, getCars(

startRow, pageSize));

return page;

}



public int getTotalCount() {

return getCountCars();

}



public List getCars(int startRow, int pageSize) {

Session session = this.getSession();

String sql = "from Car";

Query query = session.createQuery(sql);

query.setFirstResult(startRow);

query.setMaxResults(pageSize);

List list = query.list();

return list;

}



public int getCountCars() {

String sql = "select count(*) from Car";

List list = getHibernateTemplate().find(sql);

int count = 0;

if (list.size() > 0) {

count = ((Long) list.get(0)).intValue();

}

return count;

}

}

配置文件:

web.xml>>>

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<listener>

<listener-class>

org.apache.myfaces.webapp.StartupServletContextListener

</listener-class>

</listener>

<listener>

<listener-class>

org.springframework.web.util.Log4jConfigListener

</listener-class>

</listener>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/classes/spring/applicationContext.xml

</param-value>

</context-param>

<context-param>

<param-name>log4jConfigLocation</param-name>

<param-value>/WEB-INF/classes/log4j.properties</param-value>

</context-param>

<context-param>

<param-name>log4jRefreshInterval</param-name>

<param-value>60000</param-value>

</context-param>

<servlet>

<servlet-name>context</servlet-name>

<servlet-class>

org.springframework.web.context.ContextLoaderServlet

</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet>

<servlet-name>Faces Servlet</servlet-name>

<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>

<load-on-startup>0</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>Faces Servlet</servlet-name>

<url-pattern>*.jsf</url-pattern>

</servlet-mapping>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

<error-page>

<error-code>500</error-code>

<location>/error.jsp</location>

</error-page>

</web-app>



Car.hbm.xml>>>

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<!--

Mapping file autogenerated by MyEclipse - Hibernate Tools

-->

<hibernate-mapping>

<class name="org.apache.myfaces.examples.listexample.Car" table="CAR">

<id name="id" type="java.lang.String">

<column name="ID" precision="22" scale="0" />

<generator class="assigned" />

</id>

<property name="type" type="java.lang.String">

<column name="TYPE" length="20" />

</property>

<property name="color" type="java.lang.String">

<column name="COLOR" length="20" />

</property>

</class>

</hibernate-mapping>

faces-config.xml>>>

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">



<faces-config>

<!-- Managed Beans for dataScroller.jsp -->

<managed-bean>

<managed-bean-name>scrollerList</managed-bean-name>

<managed-bean-class>

org.apache.myfaces.examples.listexample.DataScrollerList

</managed-bean-class>

<managed-bean-scope>session</managed-bean-scope>

</managed-bean>

<navigation-rule>

<navigation-case>

<from-outcome>go_datascroller</from-outcome>

<to-view-id>/dataScroller.jsp</to-view-id>

</navigation-case>

</navigation-rule>

</faces-config>

applicationConfig.xml>>>

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<!--

<bean id="dataSource"

class="org.springframework.jndi.JndiObjectFactoryBean">

<property name="jndiName">

<value>java:comp/env/VSPN18DATASOURCE</value>

</property>

</bean>

-->

<bean id="dataSource"

class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

<property name="driverClassName"

value="oracle.jdbc.driver.OracleDriver" />

<property name="url"

value="jdbc:oracle:thin:@localhost:1521:oracle" />

<property name="username" value="vst" />

<property name="password" value="vst" />

<property name="maxActive" value="100" />

<property name="maxIdle" value="30" />

<property name="maxWait" value="1000" />

</bean>

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

<property name="dataSource">

<ref bean="dataSource" />

</property>

<property name="mappingResources">

<list>

<value>org\apache\myfaces\examples\listexample\Car.hbm.xml</value>

</list>

</property>

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">

org.hibernate.dialect.OracleDialect

</prop>

<prop key="hibernate.show_sql">true</prop>

</props>

</property>

</bean>

<bean id="transactionManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<property name="sessionFactory">

<ref local="sessionFactory" />

</property>

</bean>

<bean id="carDAO"

class="org.apache.myfaces.examples.dao.impl.CarDAOImpl">

<property name="sessionFactory">

<ref local="sessionFactory" />

</property>

</bean>

<bean id="carDAOBean"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property name="transactionManager">

<ref bean="transactionManager" />

</property>

<property name="target">

<ref local="carDAO" />

</property>

<property name="transactionAttributes">

<props>

<prop key="insert*">PROPAGATION_REQUIRED</prop>

<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>

</props>

</property>

</bean>

</beans>



log4j.properties>>>

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

#log4j.logger.uk.ltd.getahead.dwr = ERROR

log4j.logger.org.hibernate = ERROR

log4j.logger.org.springframework= ERROR

log4j.logger.org.apache.myfaces= ERROR
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值