day11 Ajax&Axios&书城项目第五阶段

day11 Ajax&Axios&书城项目第五阶段

1. Ajax

1.1 服务器端渲染

在这里插入图片描述

1.2 Ajax渲染(局部更新)

在这里插入图片描述

1.3 前后端分离

真正的前后端分离是前端项目和后端项目分服务器部署,在我们这里我们先理解为彻底舍弃服务器端渲染,数据全部通过Ajax方式以JSON格式来传递

1.4 同步与异步

Ajax本身就是Asynchronous JavaScript And XML的缩写,直译为:异步的JavaScript和XML。在实际应用中Ajax指的是:不刷新浏览器窗口,不做页面跳转,局部更新页面内容的技术。

『同步』和『异步』是一对相对的概念,那么什么是同步,什么是异步呢?

1.4.1 同步

多个操作按顺序执行,前面的操作没有完成,后面的操作就必须等待。所以同步操作通常是串行的。

在这里插入图片描述

1.4.2 异步

多个操作相继开始并发执行,即使开始的先后顺序不同,但是由于它们各自是在自己独立的进程或线程中完成,所以互不干扰,谁也不用等谁。
在这里插入图片描述

2. Axios

2.1 Axios简介

使用原生的JavaScript程序执行Ajax极其繁琐,所以一定要使用框架来完成。而Axios就是目前最流行的前端Ajax框架。

Axios官网:http://www.axios-js.com/
在这里插入图片描述

使用Axios和使用Vue一样,导入对应的*.js文件即可。官方提供的script标签引入方式为:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

我们可以把这个axios.min.js文件下载下来保存到本地来使用。

2.2 Axios基本用法
2.2.1 在前端页面引入开发环境
<script src="js/axios.min.js"></script>
<script src="js/vue.js"></script>
2.2.2 发送普通请求参数
① 前端代码

HTML标签:

<div id="box">
    <a href="hello">HelloServlet</a>
    <div v-html="message"></div>
    <hr>
    <button @click="send1()"> 点我发送异步请求1</button>
    <br>
</div>

Vue+axios代码:

 var vue = new Vue({
        "el":"#box",
        "data":{
            "message":""
        },
        "methods":{
            "send1":function () {
                // 发起异步请求
                axios({
                    "url":"s1",  // 请求的地址
                    "method":"post",  // 请求的方式
                    "params":{  // 请求时携带的参数
                        "uname":"zs",
                        "pwd":"123"
                    }
                    // 请求成功 执行then
                }).then(function (success){
                    console.log(success);
                    console.log(success.config);
                    console.log(success.data);
                    // console.log(this);  // axios里面this表示window对象
                    vue.message = success.data;
                    // 请求失败 执行catch
                }).catch(function (reason) {
                    console.log(reason);
                    // 失败的详细信息
                    console.log(reason.response);
                    console.log(reason.response.config);
                    console.log(reason.response.data);
                    vue.message = reason.response.data;
                })
            }
        }
    })

注意:所有请求参数都被放到URL地址后面了,哪怕我们现在用的是POST请求方式。

② 后端代码
package com.atguigu.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/s1")
public class AxiosServlet1 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-------------AxiosServlet1 doGet()---------------");
        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        int i = 10/0;

        resp.getWriter().println("AxiosServlet1 doGet Success");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-------------AxiosServlet1 doPost()---------------");
        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        resp.getWriter().println("AxiosServlet doPost Success");

    }
}

servlet获取axios传递的的参数

html代码:

<button @click="send2()"> 点我发生异步请求2</button>

vue+axios代码:

            "send2":function () {
                axios({
                    "url":"s2",
                    "method":"post",
                    "params":{
                        "username":"李白",
                        "password": "123",
                        "gender": "男"
                    }
                }).then(function (value) {
                    console.log(value);
                    // 获取服务端反馈的数据
                    console.log(value.data);
                })
            }

servlet代码:

package com.atguigu.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

@WebServlet("/s2")
public class AxiosServlet2 extends HelloServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("----------AxiosServlet2 doPost()--------");

        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        // 获取axios传来的数据
        Map<String, String[]> map = req.getParameterMap();

        map.forEach((k,v) -> System.out.println(k + "--->" + Arrays.toString(v)));

        resp.getWriter().println("AxiosServlet2 Success");
    }
}

③ axios程序接收到的响应对象结构
属性名作用
config调用axios(config对象)方法时传入的JSON对象
data服务器端返回的响应体数据
headers响应消息头
request原生JavaScript执行Ajax操作时使用的XMLHttpRequest
status响应状态码
statusText响应状态码的说明文本
④ 服务器端处理请求失败后
catch(error => {     // catch()服务器端处理请求出错后,会调用

    console.log(error);         // error就是出错时服务器端返回的响应数据
    console.log(error.response);        // 在服务器端处理请求失败后,获取axios封装的JSON格式的响应数据对象
    console.log(error.response.status); // 在服务器端处理请求失败后,获取响应状态码
    console.log(error.response.statusText); // 在服务器端处理请求失败后,获取响应状态说明文本
    console.log(error.response.data);   // 在服务器端处理请求失败后,获取响应体数据

});

在给catch()函数传入的回调函数中,error对象封装了服务器端处理请求失败后相应的错误信息。其中,axios封装的响应数据对象,是error对象的response属性。response属性对象的结构如下图所示:
在这里插入图片描述

可以看到,response对象的结构还是和then()函数传入的回调函数中的response是一样的:

回调函数:开发人员声明,但是调用时交给系统来调用。像单击响应函数、then()、catch()里面传入的都是回调函数。回调函数是相对于普通函数来说的,普通函数就是开发人员自己声明,自己调用:

function sum(a, b) {
return a+b;
}

var result = sum(3, 2);
console.log("result="+result);
2.3 发送请求体JSON和返回JSON
2.3.1 前端代码

HTML代码:

    <button @click="send3()"> 点我发生异步请求3</button>

Vue+axios代码:

            "send3":function () {
                axios({
                    "url":"s3",
                    "method":"post",  // 如果传递json类型的数据 必须使用post
                    "data":{
                        "username": "李清照",
                        "password":"123456"
                    }
                }).then(function (success){
                    console.log(success.data);
                    console.log(success.data.username);
                })
            }
2.3.2 后端代码
① 加入Gson包

Gson是Google研发的一款非常优秀的JSON数据解析和生成工具,它可以帮助我们将数据在JSON字符串和Java对象之间互相转换。

在这里插入图片描述

② User类
package com.atguigu.pojo;

public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

③ Servlet代码
package com.atguigu.servlet;

import com.atguigu.pojo.User;
import com.google.gson.Gson;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/s3")
public class AxiosServlet3 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("------------AxiosServlet3 doPost()---------");

        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        // 缓冲字符输入流
        BufferedReader reader = req.getReader();

        StringBuffer sbf = new StringBuffer();
        String line = "";

        while ((line = reader.readLine()) != null){
            sbf.append(line);
        }
        System.out.println("sbf = " + sbf);  // sbf = {"username":"李清照","password":"123456"}

        // 创建Gson对象
        Gson gson = new Gson();
        // 将json格式的字符串转为对象
        User user = gson.fromJson(sbf.toString(), User.class);
        System.out.println("user = " + user);  // user = User{username='李清照', password='123456'}


        User user2 = new User("王安石", "123");
        // 将对象转为json格式的字符串
        String json = gson.toJson(user2);
        System.out.println("json = " + json);

        // 返回一个json字符串
        resp.getWriter().println(json);  // json = {"username":"王安石","password":"123"}

//        resp.getWriter().println("AxiosServlet3 Success");
    }
}

P.S.:看着很麻烦是吧?别担心,将来我们有了SpringMVC之后,一个@RequestBody注解就能够搞定,非常方便!

2.4 jackson的使用介绍
2.4.1 jackson的简介

jackson是Java中比较常用的JSON解析的工具包,SpringMVC和SpringBoot中默认支持的就是jackson

2.4.2 jackson的使用
① 引入jar包

在这里插入图片描述

② API介绍
  1. 创建ObjectMapper对象 ObjectMapper objectMapper = new ObjectMapper();
  2. 调用writeValueAsString(obj)方法将对象转成json字符串
  3. 调用mapper.readValue(text, Class);将json字符串转成对象

html代码:

    <button @click="send4()"> 点我发送异步请求4</button>

vue+axios代码:

            "send4":function () {
                axios({
                    "url":"s4",
                    "method":"post",
                    "data":{
                        "username":"杜甫",
                        "password":"abcdefg"
                    }
                }).then(function (value) {
                    console.log(value);
                    console.log(value.data);
                    console.log(value.data.username);
                })
            }

servlet代码:

package com.atguigu.servlet;

import com.atguigu.pojo.User;
import com.atguigu.utils.JSONUtils;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/s4")
public class AxiosServlet4 extends HelloServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        BufferedReader reader = req.getReader();

        StringBuffer sbf = new StringBuffer();
        String line = "";

        while ((line = reader.readLine()) != null){
            sbf.append(line);

        // 创建ObjectMapper对象  对象 <------------> json格式字符串
        ObjectMapper mapper = new ObjectMapper();

        // json格式字符串 -----> 对象
        User user = mapper.readValue(sbf.toString(), User.class);
        }


        System.out.println("user = " + user);  // user = User{username='杜甫', password='abcdefg'}

        // 对象 ----> json格式字符串
        User user1 = new User("安其拉", "123");
        String json = mapper.writeValueAsString(user1);
        System.out.println("json = " + json);  // json = {"username":"安其拉","password":"123"}
        resp.getWriter().println(json);

    }
}

2.5 封装Json工具类用于获取json格式的请求参数以及向客户端响应json字符串
package com.atguigu.utils;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

/*
1.读入json格式字符串转为对象
2.将对象变为json格式字符串进行响应
 */
public class JSONUtils {

    /**
     * 将对象百年未json格式字符串进行响应
     * @param resp
     * @param obj
     */
    public static void writeJson(HttpServletResponse resp, Object obj){
        try {
            ObjectMapper mapper = new ObjectMapper();
            // 转为json格式字符串
            String s = mapper.writeValueAsString(obj);
            // 进行反馈
            resp.getWriter().println(s);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 读入json格式字符串转为对象
     * @param req
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T parseJsonToObject(HttpServletRequest req, Class<T> clazz){
        BufferedReader br = null;
        try {
            br = req.getReader();
            StringBuffer sbf = new StringBuffer();
            String line = "";

            while ((line = br.readLine()) != null){
                sbf.append(line);
            }
            ObjectMapper mapper = new ObjectMapper();
            T value = mapper.readValue(sbf.toString(), clazz);
            return value;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
    }
}

2.6 总结
2.6.1 同步请求和异步请求的区别
  1. 是否刷新、跳转页面
  2. 是否局部刷新
  3. 是否能同时发送多个请求
2.6.2 axios的使用
  1. 发送请求
axios({
	"method":"POST",
    "url":"路径",
	"params"/"data":{}//请求参数
})
  1. 处理响应数据(服务器端没有异常的情况下)
.then(response => {//处理响应数据})
  1. 处理服务器端的异常
.catch(error => {//处理异常数据})
2.6.3 请求参数的类型
  1. 普通类型参数
/项目名/demo01?username=aobama&password=123456&gender=male
  1. JSON类型(请求体)
{"username":"aobama","password":"123456","gender":"male"}
2.6.4 Servlet中获取不同类型的参数
  1. 获取普通类型参数
1. request.getParameter(name)
2. request.getParameterValues(name)
3. request.getParameterMap()
  1. 获取JSON请求体类型的参数
第一步: 通过字符输入流,获取json请求体(字符串)
第二步: 解析json,将json字符串的内容封装到JavaBean对象
其实使用工具类就很简单了: JsonUtils.parseJsonToObject(request,JavaBean.class);
2.6.5 Servlet中将对象转成json字符串响应给客户端
在Servlet中使用工具类: JsonUtils.writeJson(response,object);

在前端js代码.then(response => {
	//1. response.data 就相当于获取了服务器端响应过来的整个json字符串
	//2. 想要获取json中的哪个值就使用response.data.key就能获取了
})

3. 书城项目第五阶段

3.1 注册页面用户名唯一性检查优化
3.1.1 准备工作同步请求
  • 复制module
3.1.2 加入Ajax开发环境
① 前端所需axios库

在这里插入图片描述

② 后端所需jackson库
3.1.3 拷贝Json工具类
3.2 封装CommonResult
3.1 模型的作用

在整个项目中,凡是涉及到给Ajax请求返回响应,我们都封装到CommonResult类型中。

3.2 模型的代码
package com.atguigu.pojo;

/*
ajax通用的反馈结果
 */
public class CommonResult {
    /*
    服务器端处理请求的标识
    true:成功
    false:失败
     */
    private boolean flag;

    // 当服务器端处理请求失败的时候要响应给客户端的错误信息
    private String message;

    /*
    结果集数据
     */
    private Object resultData;

    // 请求失败
    public static CommonResult error(){
        return new CommonResult().setFlag(false);
    }

    // 请求失败 携带错误信息
    public static CommonResult error(String message){
        return new CommonResult().setFlag(false).setMessage(message);
    }

    // 请求成功
    public static CommonResult ok(){
        return new CommonResult().setFlag(true);
    }

    // 请求成功 携带data数据
    public static CommonResult ok(Object data){
        return new CommonResult().setFlag(true).setResultData(data);
    }

    private CommonResult() {
    }

    public boolean isFlag() {
        return flag;
    }

    public CommonResult setFlag(boolean flag) {
        this.flag = flag;
        return this;
    }

    public String getMessage() {
        return message;
    }

    public CommonResult setMessage(String message) {
        this.message = message;
        return this;
    }

    public Object getResultData() {
        return resultData;
    }

    public CommonResult setResultData(Object resultData) {
        this.resultData = resultData;
        return this;
    }
}

各个属性的含义:

属性名含义
flag服务器端处理请求的结果,取值为true或者false
message服务器端处理请求失败之后,要响应给客户端的数据
resultData服务器端处理请求成功之后,需要响应给客户端的数据
3.3 模型的好处
  • 作为整个团队开发过程中,前后端交互时使用的统一的数据格式
  • 有利于团队成员之间的协助,提高开发效率
3.3 功能实现
3.3.1 定位功能的位置

在用户输入用户名之后,立即检查这个用户名是否可用。

3.3.2 思路
① 给用户名输入框绑定的事件类型

结论:不能在针对username设定的watch中发送Ajax请求。

原因:服务器端响应的速度跟不上用户输入的速度,而且服务器端异步返回响应数据,无法保证和用户输入的顺序完全一致。此时有下面几点问题:

  • 给服务器增加不必要的压力
  • 用户输入的数据在输入过程中是不断发生变化的
  • 响应数据和输入顺序不对应,会发生错乱

解决办法:绑定的事件类型使用change事件。

② 流程图

在这里插入图片描述

3.3.3 代码实现
① 在当前页面引入axios库文件
 <script src="static/script/axios.min.js"></script>
② 给用户名输入框绑失去焦点事件
<input type="text"  name="username" placeholder="请输入用户名" v-model.trim="username" @blur="checkUserName()"/>

③ JavaScript代码
    var vue = new Vue({
      "el": "#box",
      "data":{
        // 用户名信息
        "username":"[[${param.username}]]",
        "usernameErrMsg":"用户名应为3~16位数字和字母组成",
        "usernameCss":{
              "visibility":"hidden"
        },
        // 密码信息
        "password":"",
        "passwordErrMsg":"密码的长度至少为5位",
        "passwordCss":{
              "visibility":"hidden"
        },
        // 确认密码信息
        "dbpassword":"",
        "dbpasswordErrMsg":"密码两次输入不一致",
        "dbpasswordCss":{
              "visibility":"hidden"
        },
        // 邮箱信息
        "email":"[[${param.email}]]",
        "emailErrMsg":"请输入正确的邮箱格式",
        "emailCss":{
              "visibility":"hidden"
        },
        // 验证码信息
        "code":"",
        "imgPath":"KaptchaServlet",
        "codeErrMsg":"验证码为4~6位数字和字母组成",
        "codeCss":{
          "visibility":"hidden"
        }

      },
      "methods":{
        "changeImg":function (){
          //Math.random();目的是让data中 imgPath的值每次点击都不一样 这样才会重新赋值 才会发起新的请求
          this.imgPath = "KaptchaServlet?a=" + Math.random();
        },

        "checkUserName":function (){
          // 显示提示信息
          this.usernameCss = {"visibility":"visible"};
          // 1.获取用户名的值
          var username = this.username;
          // 2.定义正则表达式
          var reg = /^[0-9a-zA-Z]{3,16}/;

          // 3.使用正则校验
          if (reg.test(username)){
            // 3.1 发起异步请求
            axios({
              "url":"user",
              "method":"post",
              "params":{
                "method":"checkUserName",
                "username":this.username
              }
            }).then(function (value) {
              console.log(value.data);
              if (value.data.flag){
                vue.usernameErrMsg = "√";
              }else{
                vue.usernameErrMsg = value.data.message;
              }
              vue.usernameFlag = value.data.flag;
            })

          }else{
            // 3.2 不满足
            this.usernameErrMsg = "用户名应为3~16位数字和字母组成";
            vue.usernameFlag = false;
          }
        },
        "checkPassWord":function (){
          // 显示提示信息
          this.passwordCss = {"visibility": "visible"};
          // 1.获取密码的值
          var password = this.password;
          // 2.定义正则表达式
          var reg = /\w{5,}/;
          // 3.使用正则校验
          if (reg.test(password)){
            // 3.1 满足条件
            this.passwordErrMsg = "√";
            return true;
          }else{
            this.passwordCss = "密码的长度至少为5位"
          }
        },
        "checkDbPassWord":function (){
          // 显示提示信息
          this.dbpasswordCss = {"visibility":"visible"}
          // 1.获取确认密码的值
          var dbpassword = this.dbpassword;
          // 2.判断确认密码是否为空
          if (dbpassword == ""){
            this.dbpasswordErrMsg = "密码长度不能为空";
            return false;
          }
          if (this.password == dbpassword){
            this.dbpasswordErrMsg = "√";
            return true;
          }else{
            this.dbpasswordErrMsg = "密码两次输入不一致";
            return false;
          }
        },
        "checkEmail":function (){
          // 显示提示信息
          this.emailCss = {"visibility": "visible"}
          // 1.获取邮箱的值
          var email = this.email;
          // 2.定义正则表达式
          var reg = /^[a-zA-Z0-9_\.-]+@([a-zA-Z0-9-]+[\.]{1})+[a-zA-Z]+$/;
          // 3.使用正则校验
          if (reg.test(email)){
            this.emailErrMsg = "√";
            return true;
          }else{
            this.emailErrMsg = "请输入正确的邮箱格式";
            return false;
          }
        },
        "checkCode":function (){
          // 显示提示信息
          this.codeCss = {"visibility": "visible"}
          // 1.获取验证码的值
          var code = this.code;
          // 2.定义正则表达式
          var reg = /^[0-9a-zA-Z]{4,6}/;
          // 3.使用正则校验
          if (reg.test(code)){
            this.codeErrMsg = "√";
            return true;
          }else{
            this.codeErrMsg = "验证码为4~6位数字和字母组成";
            return false;
          }
        },
        "register":function (){
          // 使用单& 会全部执行一边 提示所有报错
          var flag = this.usernameFlag & this.checkPassWord() & this.checkDbPassWord() & this.checkEmail() & this.checkCode();

          //只要有一个校验失败 则 不能提交 阻止默认事件
          if(!flag){
            event.preventDefault();
          }
        }

      }
    })
④ UserServlet
    // 校验用户名是否可以使用
    protected void checkUserName(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        CommonResult result = null;
        try {
            // 获取请求参数username的值
            String username = req.getParameter("username");
            // 调用业务层方法校验用户名是否可以使用
            service.checkUserName(username);
            // 没有异常 表示用户名可用
            result = CommonResult.ok();
            // 将json对象转换为json字符串反馈给浏览器
        } catch (Exception e) {
            e.printStackTrace();
            // 出现异常 表示用户名已存在
            result = CommonResult.error(e.getMessage());
        }
        
        // 将result对象转成json字符串 响应给客户端
        JSONUtils.writeJson(resp, result);
    }
⑤ UserService
@Override
public void checkUserName(String username) {
    // 1.创建持久层对象
    UserDao userDao = new UserDaoImpl();
    User user = userDao.findUserByName(username);

    if (user != null){
        throw new RuntimeException("用户名【 "+ username +"】已存在");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值