设计和实现 Web 应用程序界面

设计优秀的Web应用程序界面是一项挑战。本文探讨了Web界面设计的关键因素,并提出选择合适技术的建议,重点关注JavaServer Faces(JSF)在定制错误消息方面的应用。

注:本文转自网络 作者不明

设计好的用户界面从来都不是一件容易的事情,而设计 Web 应用程序界面尤其富有挑战性。在本文中,我首先概括地谈一谈关于 web 应用程序界面的设计考虑因素以及关于如何选择合适的实现技术。然后我将深入介绍使用 JavaServer Faces 的 web 应用程序界面的一个方面,特别是如何定制标准的错误消息。

 

设计考虑因素

为了最终获得实用且易用的 web 应用程序界面,需要正确处理许多事情:

  • 导航 。因为 web 应用程序在浏览器中运行,因此用户希望能够使用浏览器的按钮进行导航(例如,使用 Back 按钮返回上一页面)。然而,正确处理这种类型的导航非常棘手,因此设计鼓励用户使用其他方式进行导航的界面非常重要。建议将用户界面设计得看起来尽可能像 传统的图形用户界面 (GUI) — 使用通用 GUI 小部件,如用于选择选项的树、显示所选项不同方面的选项卡以及顶部的菜单栏链接。此外,一定要记住 web 应用程序是由面向任务的页面组成的,这些页面必须以特定的顺序进入,而不像传统 web 站点中那样包含一组形式自由的链接。必须只允许用户直接跳转到特定页面,例如不同任务的主页面。
  • 书签 。书签与导航相关,并且也可能难以在 web 应用程序中支持;例如,您不希望用户收藏仅是提交表单的结果的页面。虽然我不喜欢普通 Web 站点上的 HTML 框架,但框架在应用程序中可能非常有用,因为它们可以防止用户收藏个别页面。
  • Web 应用程序限制 。 让我们来面对这种情况:web 应用程序永远不能拥有和传统的 GUI 应用程序一样的交互性,至少利用当前的浏览器技术无法做到。在 GUI 中,可以容易地让用户选中表中的多行,然后单击一次全部删除它们。相反,在 web 应用程序中,您必须以不同的方式来处理这种情况,例如在每一行放一个复选框,用户可以选中它来选择一行。传统的 GUI 还能轻松地根据用户输入来动态启用或禁用输入小部件,例如当单击了一个单选钮或复选框时。在 web 应用程序中实现动态界面组件一般需要 JavaScript 代码,因此在浏览器中禁用了 JavaScript 的用户将无法使用这种应用程序。除非您对您的用户群有完全的控制权,否则您应当以其他方式将不同选项提供给用户(例如,将单选钮与按钮组合来激活新的选 项,或使用链接来激活选项)。
  • 页面大小 。尽管人们习惯滚动页面来在线阅读完整的文章,但当与每个任务相关的所有信息都包含在一个易读的页面中,或者如果仅在页面的一部分中包含滚动(利用内部框架的帮助)或通过将 Next/Previous 按钮用于大型表,用户将发现 Web 应用程序更易于使用。

 

“尽早验证用户界面是否有意义的一种很好的方式是使用模型和用户指南草稿。”

以我的经验,尽早验证用户界面是否有意义的一种很好的方式是使用模型和用户指南草稿。在编写代码之 前,我会创建纯 HTML 页面,并用它们制作用户指南早期版本中的屏幕截图,然后我请同事(或者在理想情况下请最终用户)来评阅这种早期版本。如果您以前没有试过这种策略,那么您 将吃惊地发现它不仅能够有效地揭示纯界面设计问题,还将揭示被错误理解的需求、缺少的功能和许多其他问题。

实现考虑因素

一旦您对用户界面设计感到满意,就必须决定如何实现该用户界面。使用 Java,您有许多选择。

如果您的应用程序需要高度交互的界面,那么您可能希望开发丰富 GUI 应用程序,而不是 Web 应用程序。得益于 Sun 的 Java Web Start,部署和维护功能全面的 GUI 应用程序几乎与部署和维护 web 应用程序一样容易。然而,许多互联网用户仍然认为 Java Web Start 的入门门槛太高,因此 web 应用程序无疑仍有一席之地。

直到目前为止,大多数 Java web 应用程序是使用 JavaServer Pages (JSP) 或类似的开放源代码框架(例如 Apache Velocity)实现的。这些技术是那些用户输入非常有限、动态创建内容的 web 站点的理想选择。

然而,对于具有非常复杂的用户交互的用户界面,类似这些的模板页面技术就开始显露它们的局限了。例 如,即使用复选标记来显示一组复选框以表示当前选择的简单动作都需要在模板页面自身中使用大量的条件逻辑 — 无论用 Java scriptlet、JSP 标准标记库 (JSTL) 操作和表达式语言 (EL) 或 Velocity 模板语言 (VTL) 来实现均是如此。然而,在许多情况下,JSP 或类似的技术却是正确的选择。

2004 年 3 月发布的 JavaServer Faces (JSF) 规范对于复杂的 web 应用程序界面是一种更好的解决方案。JSF 定义了一种基于组件的 web 应用程序开发模型,使供应商和开放源代码项目能够创建复杂的用户界面小部件,开发人员随后即用这些小部件创建易于使用的、在工具和应用服务器之间可移植的 web 应用程序。(Oracle 是 JSF 规范的一个积极贡献者,并通过提供基于该规范的产品(例如 ADF Faces 组件套件的早期试用版)来继续创新。此外,Oracle 承诺在 Oracle JDeveloper IDE 的一个即将推出的版本中提供全面的 JSF 支持。)

利用 JSF 组件模型,所有的逻辑(例如选中复选框来表示当前的选择所需的条件代码)都用组件来实现,而不是用页面内部的代码来实现。JSF 事件模型鼓励将组件和动作分离,并使您能够以类似开发 GUI 应用程序的方式开发 web 应用程序界面。ADF Faces 组件套件和许多其他组件套件(开发源代码和商业套件)为您提供了您所需的大部分组件,但如果您无法找到非常适合您应用程序的组件,那么您可以通过扩展 JSF 类和实现 JSF 接口来实现您自己的组件。

如果您决定使用 JSF,那么您应当了解这些并不那么显而易见的问题:

  • 命令按钮或命令链接 。JSF 提供提交表单的两个标准组件 — 一个命令按钮和一个命令链接。命令链接使用 JavaScript 代码提交表单,因此如果您不能确保您的所有用户将始终启用 JavaScript,那么您应当坚持使用命令按钮而非链接。
  • 使用页面片断 。对于复杂的用户界面,您应当为各个部分使用单独的文件并用一个主文件将它们组合起来。这种方法使开发和维护应用程序变得更容易,还令您可以在多个页面中重用相同的部分。例如,如果您使用 JSP 开发 JSF 应用程序,那么您可以使用动态的 <jsp:include><c:import> 操作,或者静态的 <%@ include %> 指令来创建主文件,以获得这些片断的内容。(建议您尽可能使用静态 include 以避免产生与使用动态 include 相关的额外需求和问题。)
  • 是否使用 JSP 。 虽然 JSP 是用该规范中描述的全部 JSF 组件组建 Web 应用程序界面的唯一技术,但并不是您的唯一选择。JSF API 非常灵活,足以使您(或其他人)使用其他技术(例如纯 XML 文件)。JSP 对许多 web 应用程序开发人员而言非常熟悉,但当和 JSF 一起使用时,它将给您带来一些麻烦。(关于这些问题的更多信息,请参考我的文章“通过转储 JSP 改善 JSF ”(http://www.onjava.com/pub/a/onjava/2004/06/09/jsf.html))。

在简要讨论了在设计和实现 web 应用程序界面时需要考虑的一些事情后,我将深入讨论 web 应用程序界面在使用 JavaServer Faces 时的一个方面,特别是如何定制标准的错误消息。让我们看看通用属性和 PhaseListener 实现可以如何帮助您定制由 JSF 标准转换器和验证器创建的错误消息。

添加有意义的域引用

JSF 定义了许多转换器和验证器,您可以将它们与组件相连以验证用户输入。当输入无效时,它们将错误消息入队,然后消息组件把消息显示给用户。例如,这个 JSP 页面创建了一个需要 1 到 10 之间的一个数字值的输入组件,以及用于提供详细消息和摘要消息的两种消息组件:

 

<%@ page contentType="text/html" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

<f:view>
<h:messages layout="table" showDetail="true" showSummary="false" />
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Number of passengers:"/>
<h:inputText id="noOfPass" size="8" required="true">
<f:convertNumber integerOnly="true" />
<f:validateLongRange minimum="1" maximum="10"/>
</h:inputText>
<h:message for="noOfPass" showDetail="false" showSummary="true"
style="color:red" />

</h:panelGrid>
<h:commandButton value="Submit"/>
</h:form>
</f:view>

 

验证错误消息通常一起显示在页面顶部,但这只有在用户能够轻松地将每一条消息与相应的无效域匹配时 才起作用。标准的 JSF 错误消息仅提供问题的描述,而没有与消息引用的域相关的任何信息 — 正如向上面例子中的验证器提供一个超出范围的值时 Sun 的 JSF 1.0 引用实现 (RI) 所返回的消息那样:

 

Validation error:Specified attribute is not between the expected values of 1 and 10

 

要解决这个问题,我们需要两个东西:一种将用户友好的域引用与组件关联的方式以及一种将这种引用添加到消息文本中的方式。我们来使用一个通用组件属性来指定组件的域引用:

 

  ...
<f:view>
<h:messages layout="table" showDetail="true" showSummary="false" />
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Number of passengers:"/>
<h:inputText id="noOfPass" size="8" required="true">
<f:convertNumber integerOnly="true" />
<f:validateLongRange minimum="1" maximum="10"/>
<f:attribute name="fieldRef" value="Number of passengers" />
</h:inputText>
...
</h:panelGrid>
<h:commandButton value="Submit"/>
</h:form>
</f:view>

 

通用属性是组件的一个命名定制值,对该组件有访问权的其他代码段可以使用这个值。 <f:attribute> 动作元素将一个名为 fieldRef 的通用属性设置为与最终用户看到的输入域的标签匹配的一个值。

接下来我们需要能够获取通用属性并将其插入消息中的东西。最适合于这个任务的是定制的 PhaseListener 。应用程序可以注册一个或多个 PhaseListener 实现:

 

<faces-config>
<lifecycle>
<phase-listener>
com.mycompany.jsf.listeners.MessageListener1
</phase-listener>

</lifecycle>
</faces-config>

 

JSF 在它感兴趣的请求处理生命周期阶段之前或之后调用 PhaseListener 。这个 PhaseListener 实现处理我们的消息定制需求:

 

package com.mycompany.jsf.listeners;

import java.util.Iterator;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

public class MessageListener1 implements PhaseListener {

public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}

public void beforePhase(PhaseEvent e) {

FacesContext fc = e.getFacesContext();
UIViewRoot root = fc.getViewRoot();
Iterator i = fc.getClientIdsWithMessages();
while (i.hasNext()) {
String clientId = (String) i.next();
UIComponent c = root.findComponent(clientId);
String fieldRef =
(String) c.getAttributes().get("fieldRef");

if (value != null)
Iterator j = fc.getMessages(clientId);
while (j.hasNext()) {
FacesMessage fm = (FacesMessage) j.next();
fm.setDetail(fieldRef + ":" + fm.getDetail());
}
}
}
}

public void afterPhase(PhaseEvent e) {
}
}

 

MessageListener1 类从 getPhaseId() 方法返回 PhaseId.RENDER_RESPONSE ,因此 JSF 就在提交响应之前调用它的 beforePhase() 方法。 beforePhase() 方法首先获取所有要为其进行消息入队的组件的客户 ID。然后它借助 findComponent() 方法找到每一个这种组件,并获取分配给 JSP 页面中组件的 fieldRef 属性的值。最后,该方法获得组件的所有消息,并在开头添加对详细消息文本的域引用。当 JSF 继续输出响应时,JSP 页面顶部的 <h:messages> 组件输出修改后的详细消息,包括在图 1 中显示的用户友好的域引用

UIDesigner(腾讯公司出品) 在软件设计阶段,交互设计师或者产品经理等常常需要使用一些工具(比如Visio、Axure RP、Flash或者OmniGraffle等)制作出静态稿或者原型来表达设计思想。这些工具是各有各的优势,当然也各有各的缺点啦。那么腾讯CDC是如何设计软件原型的呢?这里向大家介绍我们正在研发的原型设计利器UIDesigner。   首先,先让我们来体验一下UIDesigner制作出来的原型效果。这个原型是一个设置窗体,主要由按钮、复选框、分组框文本框等控件构成,其中一些按钮还具有响应的动作(如打开另一个界面,关闭窗体等)。如图一所示,这是使用Firework制作出来的交互图。 图一、使用Firework制作出来的设计稿   这种交互图的特点是简单明了,能够表示出各种控件的基本属性(如文本、位置选中状态等),但它只是一个静态的图片,不能真实表现出每个控件的响应动作,如仅看这张图,是不清楚点击代理服务器设置分组框上的“设置…”按钮会有什么响应的。   那再看看你若使用UIDesigner制作出来的效果:最终输出的是一个EXE可执行文件(见图二左上角),双击运行后就会出现一个与最终实现效果完全一致的窗体(见图二右)。另外,它还是一个具有响应动作的真实原型,例如你若点击了“设置…”按钮,那就会弹出一个新的窗体(见图三)。 图二、使用UIDesinger制作出来的原型 图三、点击“设置…”按钮后弹出的另一窗体   实际上,这个新弹出的窗体都是一个独立的原型,也是由UIDesigner制作出来的。由于它里面的控件同样可以设置下一步的响应动作,所以从总体上来看,众多原型就像一个树状结构,而其中的父节点就是图二中的设置窗体了。这种结构具有一个很大的好处:无论你完成了多个界面的原型,只需要它们之间有链接关系,最后输出的只是一个EXE可执行文件。这样,你可以很方便地与其它人员表达自己的设计思路,让沟通变得更加快捷。   接下来,我们看看这个原型是如何制作出来的。   第一步:建立空白窗体,调整它的大小、风格、标题Icon 图四、使用UIDesigner制作原型—建立窗体   第二步:从工具箱中拖曳控件到窗体上,修改这些控件的属性(如Text等) 图五、使用UIDesigner制作原型—加入控件   由于UIDesigner具有自动对齐的功能,所以在拖动控件到窗体时就可以完成排版工作,不需要再逐个像素去调整。另外,不单单在加入控件到窗体时,在改变已存在于窗体内的控件的位置或者大小时,自动对齐功能一样会生效。这样,设计者在调整控件大小或者位置时(包括控件与控件,控件与窗体边缘等的距离等情况),工作变得很简单。 图六、使用UIDesigner制作原型—自动对齐功能   第三步:几分钟后,所有的控件都放到窗体设置好属性了,如下图。 图七、使用UIDesigner制作原型—整体界面效果   此时,设计者就可以使用软件的导出图片或者导出原型功能分享你的设计成果了。当然,现在这种效果还只是一个没有响应动作效果的原型。接下来,我们再给“设置…”按钮加入响应动作。首先,选中“设置…”按钮,然后点击工具栏的“响应”按钮,你将会看到如图八的响应设置窗口。此时,你可以选择打开窗体、打开网页、弹出对话框、修改属性值关闭本身窗体等五种效果,这里我们选择“打开窗体”,然后在弹出的对话框里指向弹出窗体的文件。最后,点击“完成”按钮即可。 图八、使用UIDesigner制作原型—设置控件的响应动作   到此为止,你就完成了一个与最终实现效果完全一致的高保真原型了。产品经理开发人员等若想了解你的设计思想就不需要看着长长几页的说明文档了,而只需要运行你输出的原型文件,就可以对你的设计思路一目了然。   UIDesigner除了能让使用者能够快速地搭建起软件界面的高保真原型外,还提供了项目管理,让使用者能够方便地管理工程文件;提供了图片库模板库功能,让使用者可以方便地重用以前的设计资源;提供了32个Windows客户端软件常用控件,满足使用者的设计需求;提供了多个属性设置入口,分别实现最常用属性设置、一般属性设置高级属性设置功能。当然,UIDesigner不是专用来设计QQ的,其它的Windows客户端产品都在它的工作范围之内。总的来说呢,使用UIDesigner来制作原型是很高效的,而制作出来的原型也能够实现设计师、产品经理程序开发工程师三者间的快速沟通,减少不必要的工作内耗。 ------------------------------------------------------------------ GUI_Design_Studio_3.5.94.0 在2008年,这款软件的作者从1,322位付费用户那获得了162,302美元的收入(其中仅12月份就有39,000美元),这令人鼓舞地证明了只要是提供真正价值的服务软件,就能够创造很好的收入,即使在经济萧条的寒冬里。 GUI Design Studio是一款图形用户界面设计工具,您能用它在不需要编写任何代码或脚本的情况下快速地创建演示原型。使用标准元素绘制个人化的屏幕、窗口以及控件;将它们整合以展示操作工作流然后运行模拟程序测试您的设计。 当您需要绘制一款应用程序的外观或显示怎样将程序的各个部分连接起来时,您就可以使用GUI Design Studio来实现,如: 将产品创意文档化 制作项目提案 需求记录 创建屏幕图样 为开发人员制作详细的规格 为现有产品提出加强方案 以及更多其它用途 为用户以及股东甚至您自己制作展示文件以: 验证设计 找出替代项 评估多个使用场景 系统需求 Microsoft Windows Vista/XP/NT/2000 至少15MB的硬盘空间 推荐不少于256MB的内存 工程 将您的工作组织放到工程里。 每个工程都拥有它自己的文件夹结构。 可连接您计算机或网络上任何地方的其它库工程。 可连接到您计算机或网络上任何地方使您能够方便地获取图片以及其它文件。 创建您想要的独立于所有工程的个人化设计文件。 创建能重复使用的设计库以及工程之间的一致性。 将图片直接从剪贴板中粘贴到工程文件夹的文件中。 屏幕设计程序 可同时打开多个设计文档能使用分页界面在文档间快速切换。 可使用标准Windows元素创建图形用户界面(GUI)屏幕,包括框架窗口、会话、菜单、工具栏、标签、按钮、复选框、单选按钮、滚动条、滑动调节框、微调框、组合框、树列表、列表框、编辑框以及静态文本等。 通过现有元素或其它自定义控件创建自定义控制组件。 在其它设计中创建将要使用的控件设计。 以常用的文件格式添加图标与图片。 从能够显示您将获得什么的控件面板中进行拖放操作。 属性编辑程序使您能将每个元素进行自定义。 元素的自动生成功能。 您能在任何地方放置任何元素。 Edge snapping能通过“点击”对屏幕元素进行统一记录。 设计网格向您提供了简单的制作具有一致性设计布局的方法。 只需单键点击就能排列元素组。 均衡地隔开元素组。 调整元素大小以适应其它元素或测量一组其它元素。 聚焦缩小以查看细节设计或聚焦放大以浏览设计概况。使用工具栏按键或键盘或鼠标的滑动缩小放大功能聚焦增量。 使用鼠标的滚动以及滚轮功能快速浏览设计的概况。 剪切、复制与粘贴。 使用可选的canvas向导显示可用的屏幕空间。 通过文本框与书签对您的设计进行注释。 为每个设计添加说明。 选择色彩主题以避免实际功能窗口的混淆。 图标 为图标面板提供快速的访问方式。 工程间可共享常用图标。 工程也能拥有它们独享的图标集。 使用将来在实际产品开发中可能用到的标准ICO文件。 包含的Icon Express编辑程序支持16色、256色以及最大尺寸为127 x 127的真彩图标。 如果愿意同样能整合任意其它的图标编辑应用程序。 展示与原型 在可导航元素(如按键与窗口)之间创建连接以显示控制流程。 为图象添加覆盖保护以及添加其它元素以创建可导航的热点。 支持形式化与非模态的窗口支持显示、隐藏或微调窗口,以及窗口替换以实现切换效果。 锚点使您能对窗口进行准确定位。 能立即在模拟程序中测试或展示您的设计。 添加消息框以描述可能在真实应用程序中出现的功能。 可创建多种场景,如正常条件下的场景与多个出错场景。 选择要激活的场景或让模拟程序为您选择。 更改有效的屏幕分辨率以查看您的设计如何适应不同的屏幕大小。 在没有进行脚本或代码编写的情况下,所有操作都是以图形模式完成。 ------------------------------------------------------------------ Balsamiq_Mockups_1.8.4 1. 首先安装 AdobeAIRInstaller.exe 2. 安装主程序 MockupsForDesktop.air 3. 使用 keygen 进行注册 在Balsamiq Mockups中绘制界面原型,就像在纸张白板上手工绘画一样方便快速,而且不用担心出错,因为你完全可以进行回复/重做。在Balsamiq Mockups中设计原型比在纸张白板可有趣多了! 丰富的控件支持 Balsamiq Mockups预先提供了丰富的控件,如浏览器窗口、媒体播放器组件、圆形图表等,让你信手拈来! 丰富的控件 方便的属性设置 当你选择一个控件时,会立刻出现该控件的属性选项,你可以随时编辑修改属性。 属性设定 无限的恢复/重做,不用担心出错,你可以无限制的进行回复重做。 开放,可移植的数据 Balsamiq Mockups所生成的数据是易于阅读的XML格式数据,你可以进行方便的移植重用集成。 开放的数据 多种方式的集成应用,你可以将Balsamiq Mockups集成倒Confluence, Jira, XWiki中. 集成应用 Balsamiq Mockups是一款免费的带有手绘风格的原型设计软件,可以帮助你设计桌面应用软件,Web 2.0 站点, RIA富网络应用程序, Web站点Web应用软件。 功能亮点: 操作方面:拖拽,控件分组,甚至元素之间的对齐都做得很到位; 预制了六十多个界面元素,从简单的输入框,下拉框,到经常用得到的导航条,日历,表格,到复杂的Tag Cloud,Cover Flow, 地图,WYSWYG的格式工具栏等,有了这些不用从头画起,其实比用白板都快; 界面元素的修改很简单,比如导航条的几个标签页的label,就是用逗号分隔的文字,下拉框的选项就是分行的文字; 使用xml语言来记录保存界面元素布局,从而使其能够快速的导入到你所需的任何一个项目中,或其他工具中。 可以将设计导出成PNG格式的图片; 随着使用的熟练,快捷键便派上用场,超过一半的元素均有快捷方式,这更有助于原型的快速构造,几乎几分钟便可实现一个满意的而复杂的原型设计; 跨平台,Balsamiq Mokups是用FlexAir实现的,所以在Mac OS, LinuxWindows下都能使用; 不仅仅有桌面版本,还有能集成在Confluence,JIRA,XWiki中的版本,使得异地在线协作更方便有效。 可以用命令行进行导出操作,这样就能让我写个脚本,从svn里checkout某个目录下的所有设计文件后,导出图片,打包后用邮件发到项目经理,工程师甚至客户那; 跨平台,Balsamiq Mokups是用FlexAir实现的,所以在Mac OS, LinuxWindows下都能使用;不仅仅有桌面版本,还有能集成在Confluence,JIRA,XWiki中的版本,使得异地在线协作更方便有效;据作者说,现在这款软件的设计就是用它自己来设计的,经典的“吃自己的狗粮”,这也让我对其更有信心,因为它是开发者为开发者写的软件。 还有值得一提的是Balsamiq Mokups的在GetSatisfaction上的用户支持服务,作者Peldi对问题报告,新需求的回应很积极及时。而且根据这个讨论上看,到月底就会有一个专门用来分享界面控件设计的社区网站了,很期待。 再要说的一点是这款软件是要付费的,79美元(也可以免费,具体如何免费,请看网站上的说明),相对于它能节省下来的时间提高的效率,是很值得的。Peldi说在2008年,这款软件就从1,322位付费用户那获得了162,302美元的收入(其中仅12月份就有39,000美元),这令人鼓舞地证明了只要是提供真正价值的服务软件,就能够创造很好的收入,即使在经济萧条的寒冬里。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值