JavaScript学习笔记(六)

六、jQuery

1.“jQuery2.x移除了对IE 6、7、8的支持,代码更精简,如果想支持IE6~8,可以选择jQuery1.x。如果想深入研究jQuery源码,就从官网下载uncompressed(未压缩)版本而不是compressed(压缩)版本,这两个版本使用时是一样的。”

2.“jQuery把所有功能全部封装在一个全局变量jQuery中,而$也是一个合法的变量名,它是变量jQuery的别名,本质上是一个函数,也是一个对象”:

window.jQuery; // jQuery(selector, context)
window.$; // jQuery(selector, context)
$ === jQuery; // true
typeof($); // 'function'

“注意看到的$函数名可能不是jQuery(selector, context),因为很多JavaScript压缩工具可以对函数名和参数改名,所以压缩过的jQuery源码$函数可能变成a(b, c)。”

3.在jQuery代码内部window.jQuery = window.$ = jQuery;,如果在调用jquery代码的外部代码中使用了$变量名且不能更改,就不能用$代替jQuery变量来调用jQuery中的函数,需要调用函数jQuery.noConflic()释放jQuey中的$变量,并且之后只能使用jQuery变量来调用jQuery中的函数。如:某些其他 JavaScript 库中的函数(比如 Prototype)同样使用 $ 符号。

4.jQuery查找id为’abc’的元素:

// 查找<div id="abc">:
var div = $('#abc');

“返回的对象(div)是jQuery对象,类似数组而不是数组,它的每个元素都是一个引用了DOM节点的对象,如果存在id为’abc’的元素,以上代码返回的就是[<div id="abc">...</div>,......];如果不存在,返回的就是[],而不是返回undefined或者null

5.“jQuery对象和DOM对象之间可以互相转化”:

var div = $('#abc'); // jQuery对象
var divDom = div.get(0); // 假设存在div,获取第1个DOM元素
var another = $(divDom); // 重新把DOM包装为jQuery对象

6.注意按class查找DOM节点时:

var a = $('.red'); // 所有包含`class="red"`的节点都将返回
// 例如:
// <div class="red">...</div>
// <p class="green red">...</p>

查找class属性同时包含redgreen名称的节点(顺序不一定是red在前,green在后):

var a = $('.red.green'); 
// 符合条件的节点:
// <div class="red green">...</div>
// <div class="blue green red">...</div>

7.按照属性查找节点时,“如果属性的值包含空格等特殊字符时,需要用双引号括起来”:

var email = $('[name=email]'); // 找出<??? name="email">
var a = $('[items="A B"]'); // 找出<??? items="A B">

“按属性查找还可以使用前缀查找和后缀查找”:

var icons = $('[name^=icon]'); // 找出所有name属性值以icon开头的DOM
// 例如: name="icon-1", name="icon-2"
var names = $('[name$=with]'); // 找出所有name属性值以with结尾的DOM
// 例如: name="startswith", name="endswith"

// 找出所有class包含至少一个以`icon-`开头的DOM,不受class包含多个名称的影响,重点理解
var icons = $('[class^="icon-"]'); 
// 例如: class="icon-clock", class="abc icon-home"

8.多项选择器将多个选择器组合起来使用:

$('p,div'); // 把<p>和<div>都选出来
$('p.red,p.green'); // 把<p class="red">和<p class="green">都选出来

“注意选出来的元素是按照它们在HTML中出现的顺序排列的,而且不会有重复元素,例如,<p class="red green">不会被上面的$('p.red,p.green')选择两次”。

9.练习:

使用jQuery选择器分别选出指定元素:

  • 仅选择JavaScript

  • 仅选择Erlang

  • 选择JavaScript和Erlang

  • 选择所有编程语言

  • 选择名字input

  • 选择邮件和名字input

    <!-- HTML结构 -->
    <div id="test-jquery">
        <p id="para-1" class="color-red">JavaScript</p>
        <p id="para-2" class="color-green">Haskell</p>
        <p class="color-red color-green">Erlang</p>
        <p name="name" class="color-black">Python</p>
        <form class="test-form" target="_blank" action="#0" onsubmit="return false;">
            <legend>注册新用户</legend>
            <fieldset>
                <p><label>名字: <input name="name"></label></p>
                <p><label>邮件: <input name="email"></label></p>
                <p><label>口令: <input name="password" type="password"></label></p>
                <p><button type="submit">注册</button></p>
            </fieldset>
        </form>
    </div>
    

代码:

'use strict';

var selected = null;

// selected = $('#para-1'); // 仅选择JavaScript
// selected = $('.color-red.color-green'); // 仅选择Erlang
// selected = $('[class^="color-red"]'); // 选择JavaScript和Erlang
// selected = $('[class^="color"]'); // 选择所有编程语言
// selected = $('input[name=name]'); // 选择名字input
selected = $('input[name=name],input[name=email]'); // 选择邮件和名字input

// 高亮结果:
if (!(selected instanceof jQuery)) {
    return console.log('不是有效的jQuery对象!');
}
$('#test-jquery').find('*').css('background-color', '');
selected.css('background-color', '#ffd351');

10.层级选择器可以一步步缩小查找范围,先定位父节点后定位子节点(层级选择器之间用空格隔开)。“子选择器$('parent>child')类似层级选择器,但限定了层级关系必须是父子关系,就是<child>节点必须是<parent>节点的直属子节点”。“过滤器一般不单独使用,而是附加在选择期上,进一步精确定位节点”

11.练习:

// 分别选择所有语言,所有动态语言,所有静态语言,JavaScript,Lua,C等:
// selected = $('div.test-selector li');
// selected = $('ul.test-lang li');
// selected = $('ol.test-lang li');
// selected = $('ul.test-lang li.lang-javascript');
// selected = $('ul.test-lang li:first-child');
selected = $('ul.test-lang>li.lang-javascript');


12.练习:

<form id="test-form" action="#0" onsubmit="return false;">
    <p><label>Name: <input name="name"></label></p>
    <p><label>Email: <input name="email"></label></p>
    <p><label>Password: <input name="password" type="password"></label></p>
    <p>Gender: <label><input name="gender" type="radio" value="m" checked> Male</label> <label><input name="gender" type="radio" value="f"> Female</label></p>
    <p><label>City: <select name="city">
    	<option value="BJ" selected>Beijing</option>
    	<option value="SH">Shanghai</option>
    	<option value="CD">Chengdu</option>
    	<option value="XM">Xiamen</option>
    </select></label></p>
    <p><button type="submit">Submit</button></p>
</form>

对于以上表单,输入值后,用jQuery获取表单的JSON字符串,key和value分别对应每个输入的name和相应的value,例如:{"name":"Michael","email":...},代码;

'use strict';
var json = null;
json = {};
// :input可以选择<input>,<textarea>,<select>和<button>
// not方法从匹配元素集合中删除元素
$('#test-form :input').not('button').filter(function(){
    //console.log(this.type);
    return this.type !== 'radio' || this.checked;
}).map(function(){
    json[this.name] = this.value;
    return true;
});
// 将javascript对象序列化成一个JSON格式的字符串
json = JSON.stringify(json);
// 显示结果:
if (typeof(json) === 'string') {
    console.log(json);
}else {
    console.log('json变量不是string!');
}

13.jQuery对象的css()方法作用于DOM节点的style属性,具有最高优先级。

var div = $('#test-div');
div.css('color'); // '#000033', 获取CSS属性
div.css('color', '#336699'); // 设置CSS属性
div.css('color', ''); // 清除CSS属性

修改class属性:

var div = $('#test-div');
div.hasClass('highlight'); // false, class是否包含highlight
div.addClass('highlight'); // 添加highlight这个class
div.removeClass('highlight'); // 删除highlight这个class

14.练习:分别用css()方法和addClass()方法高亮显示JavaScript

<!-- HTML结构 -->
<style>
.highlight {
    color: #dd1144;
    background-color: #ffd351;
}
</style>

<div id="test-highlight-css">
    <ul>
        <li class="py"><span>Python</span></li>
        <li class="js"><span>JavaScript</span></li>
        <li class="sw"><span>Swift</span></li>
        <li class="hk"><span>Haskell</span></li>
    </ul>
</div>

代码:

'use strict';
var div = $('#test-highlight-css');
var js = div.find('ul li.js>span');
// css方法
// js.css('background-color', '#ffd351').css('color', '#dd1144');
// addClass
if(!js.hasClass('highlight')){
    js.addClass('highlight');
}

15.jQuery提供show()hide()方法显示和隐藏DOM元素,“隐藏DOM节点并未改变DOM树的结构,它只影响DOM节点的显示,和删除DOM节点不同”

16.获取DOM信息:

// 浏览器可视窗口大小:
$(window).width(); // 800
$(window).height(); // 600

// HTML文档大小:
$(document).width(); // 800
$(document).height(); // 3500

// 某个div的大小:
var div = $('#test-div');
div.width(); // 600
div.height(); // 300
div.width(400); // 设置CSS属性 width: 400px,是否生效要看CSS是否有效
div.height('200px'); // 设置CSS属性 height: 200px,是否生效要看CSS是否有效

attr()removeAttr()方法用于操作DOM节点的属性”:

// <div id="test-div" name="Test" start="1">...</div>
var div = $('#test-div');
div.attr('data'); // undefined, 属性不存在
div.attr('name'); // 'Test'
div.attr('name', 'Hello'); // div的name属性变为'Hello'
div.removeAttr('name'); // 删除name属性
div.attr('name'); // undefined

prop()方法和attr()类似,但对于属性checked的处理方式不一样“,当prop方法用于返回属性值时,则返回第一个匹配元素的值;用于设置属性值时,则为匹配元素集合设置一个或多个属性/值对:

// 两者等价
<input id="test-radio" type="radio" name="test" checked="checked" value="1">
<input id="test-radio" type="radio" name="test" checked value="1">
var radio = $('#test-radio');
radio.attr('checked'); // 'checked'
radio.prop('checked'); // true

“但用is()方法判断更好”:

var radio = $('#test-radio');
radio.is(':checked'); // true

”处理属性selected和属性checked一样,处理时最好用is(':selected')“。

17.”对于表单元素,jQuery对象统一提供val()方法获取和设置对应的value属性“

18.“为了添加新的DOM节点,除了通过jQuery的html()这种暴力方法外,还可以用append()方法,其可以传入字符串(HTML片段)、原始的DOM对象、jQuery对象和函数对象,传入函数时,要求返回一个字符串、DOM对象或者jQuery对象。因为jQuery的append()可能作用于一组DOM节点,只有传入函数才能针对每个DOM生成不同的子节点。append方法将DOM添加到最后,而且可以移动一个已存在的DOM节点,prepend方法将DOM添加到最前面,同级节点可以用after方法或before方法将新节点添加到某个指定节点的前面或者后面。”

19.练习,除了列出的3种语言外,请再添加Pascal、Lua和Ruby,然后按字母顺序排序节点:

<!-- HTML结构 -->
<div id="test-div">
    <ul>
        <li><span>JavaScript</span></li>
        <li><span>Python</span></li>
        <li><span>Swift</span></li>
    </ul>
</div>

代码:map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值

var ul = $('#test-div>ul');
ul.append('<li><span>Pascal</span></li>');
ul.append('<li><span>Lua</span></li>');
ul.append('<li><span>Ruby</span></li>');
var all = $('#test-div ul li').map(function(){
    return this.innerText;
}).get();
all = all.sort();
$('#test-div ul li').remove();
for(let a of all){
    ul.append('<li><span>'+a+'</span></li>');
}
// 测试:
;(function () {
    var s = $('#test-div>ul>li').map(function () {
        return $(this).text();
    }).get().join(',');
    if (s === 'JavaScript,Lua,Pascal,Python,Ruby,Swift') {
        console.log('测试通过!');
    } else {
        console.log('测试失败: ' + s);
    }
})();

20.jQuery对象的on方法用来绑定一个事件,需要传入事件名称(如点击:click)和对应的处理函数,或者用click方法绑定一个点击事件,传入一个处理函数。

21.“$(function () {…})形式的写法表示这是document对象的ready`事件处理函数,另外的写法:”

$(document).on('ready', function () {
            $('#testForm).on('submit', function () {
                alert('submit!');
            });
        });

// 或者

$(document).ready(function () {
    // on('submit', function)也可以简化:
    $('#testForm).submit(function () {
        alert('submit!');
    });
});

ready事件仅作用于document对象,由于ready事件在DOM完成初始化后触发,且只触发一次,所以非常适合用来写其他的初始化代码,而且可以反复绑定事件处理函数,它们会依次执行”

“有些事件,如mousemovekeypress,传入函数时需要传入Event对象作为参数,可以从Event对象上获取到更多的信息”

22.“一个已被绑定的事件可以解除绑定,通过off('click', function)实现,注意参数function必须和之前绑定的函数同名,而不是内容相同的匿名函数,使用off('click')可以一次性移除已绑定的click事件的所有处理函数,无参数调用off()一次性移除已绑定的所有类型的事件处理函数。”

23.“注意事件的触发总是由用户操作引发的,当用户在文本框中输入时,就会触发change事件,但是如果用JavaScript代码去改动文本框的值,将不会触发change事件:”

var input = $('#test-input');
input.change(function () {
    console.log('changed...');
});

var input = $('#test-input');
input.val('change it!'); // 无法触发change事件

“如果希望用代码触发change事件,可以直接调用无参数的change()方法来触发该事件,input.change()相当于input.trigger('change'),它是trigger()方法的简写,手动触发一个事件有时可以避免写重复的一段代码(绑定change事件)”

var input = $('#test-input');
input.val('change it!');
input.change(); // 触发change事件

24.“在浏览器中,有些JavaScript代码只有在用户触发下才能执行,例如,window.open('/')函数,打开新的窗口,如果延迟执行该函数,将被浏览器拦截”

25.注意:

(1)forEach()方法按升序为数组中含有效值的每一项执行一次callback 函数,那些已删除或者未初始化的项将被跳过forEach()的返回值为undefinedforEach()对于空数组是不会执行回调函数的;没有办法中止或者跳出 forEach()循环,除了抛出一个异常。

语法:

arr.forEach(callback(currentValue, index, arr), thisArg)

示例代码:

let arr = [1, 2, , 3]
arr.forEach((item, index) => {
    console.log(`arr[${index}] = ${item}`)
})

结果:

a[0] = 1
a[1] = 2
a[3] = 3
// a[2]被跳过了,因为数组在这个位置没有项(为空)

(2)map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。map()方法不会对空数组进行检测,并且不会改变原数组。

语法:

array.map(function(currentValue, index, arr), thisIndex)

示例代码:

let array = [1, 2, 3, 4, 5];

let newArray = array.map((item) => {
    return item * item;
})

console.log(newArray)  // [1, 4, 9, 16, 25]

(3)each方法用于对jQuery对象(可能包含0个或多个DOM对象,jQuery对象调用get(0)会得到第一个DOM对象)进行遍历,语法:

$(selector).each(function(index,element))

参数分别是选择器的 index 位置和当前的元素(也可使用 “this” 选择器,即$(this),把当前DOM对象this包装为jQuery对象)

输出每个 li 元素的文本,示例代码:

$("button").click(function(){
  $("li").each(function(){
    alert($(this).text())
  });
});

(4)jQuery对象的map() 方法把每个元素通过函数传递到当前匹配集合中,生成包含返回值的新的 jQuery 对象。

(5)jQuery对象的get方法得到jQuery对象匹配得到的所有元素组成的数组,适用于想要直接操作 DOM 对象而不是 jQuery 对象的情况。

(6)join方法用于数组中元素的连接。

26.练习,对以下HTML表单:

<!-- HTML结构 -->
<form id="test-form" action="test">
    <legend>请选择想要学习的编程语言:</legend>
    <fieldset>
        <p><label class="selectAll"><input type="checkbox"> <span class="selectAll">全选</span><span class="deselectAll">全不选</span></label> <a href="#0" class="invertSelect">反选</a></p>
        <p><label><input type="checkbox" name="lang" value="javascript"> JavaScript</label></p>
        <p><label><input type="checkbox" name="lang" value="python"> Python</label></p>
        <p><label><input type="checkbox" name="lang" value="ruby"> Ruby</label></p>
        <p><label><input type="checkbox" name="lang" value="haskell"> Haskell</label></p>
        <p><label><input type="checkbox" name="lang" value="scheme"> Scheme</label></p>
		<p><button type="submit">Submit</button></p>
    </fieldset>
</form>

绑定合适的事件处理函数,实现以下逻辑:

当用户勾上“全选”(点击全选前的checkbox)时,自动选中所有语言,并把“全选”变成“全不选”;

当用户去掉“全不选”(再次点击全选前的checkbox)时,自动不选中所有语言;

当用户点击“反选”时,自动把所有语言状态反转(选中的变为未选,未选的变为选中);

当用户把所有语言都手动勾上时,“全选”被自动勾上,并变为“全不选”;

当用户手动去掉选中至少一种语言时,“全不选”自动被去掉选中,并变为“全选”。

代码:

'use strict';

var
    form = $('#test-form'),
    langs = form.find('[name=lang]'),
    selectAll = form.find('label.selectAll :checkbox'),
    selectAllLabel = form.find('label.selectAll span.selectAll'),
    deselectAllLabel = form.find('label.selectAll span.deselectAll'),
    invertSelect = form.find('a.invertSelect');

// 重置初始化状态:
form.find('*').show().off();
form.find(':checkbox').prop('checked', false).off();
deselectAllLabel.hide();
// 拦截form提交事件:
form.off().submit(function (e) {
    e.preventDefault();
    alert(form.serialize());
});

// 绑定事件
selectAll.click(function(){
    if(selectAll.is(':checked')){ // 当用户勾上“全选”时,自动选中所有语言,并把“全选”变成“全不选”;
        langs.each(function(){
            $(this).prop('checked', true);
        });
        // 和上面写法意义相同
        // langs.prop('checked', true);
        selectAllLabel.hide();
        deselectAllLabel.show();
    }else{ // 当用户去掉“全不选”时,自动不选中所有语言
        langs.each(function(){
            $(this).prop('checked', false);
        });
        // 和上面写法意义相同
        // langs.prop('checked', true);
        selectAllLabel.show();
        deselectAllLabel.hide();
    }
});
// 当用户点击“反选”时,自动把所有语言状态反转(选中的变为未选,未选的变为选中)
invertSelect.click(function(){
    langs.each(function(){
        $(this).prop('checked',!$(this).prop('checked'));
    });
});

// 当<input>、<select>或<textarea>的内容改变时触发change事件
var num = 0;
langs.each(function(){
    $(this).change(function(){ // 当用户手动去掉选中至少一种语言时,“全不选”自动被去掉选中,并变为“全选”。
        if($(this).prop('checked')===false){ // 或者将判断条件变为if(!this.checked)
            selectAll.prop('checked', false);
            selectAllLabel.show();
            deselectAllLabel.hide();
        }else if($(this).prop('checked')===true){ // 当用户把所有语言都手动勾上时,“全选”被自动勾上,并变为“全不选”
            num++;
            console.log(num);
            console.log(langs.size());
            if(num===langs.size()){
                selectAll.prop('checked', true);
                selectAllLabel.hide();
                deselectAllLabel.show();
            }
        }
    });
});
// 测试:
console.log('请测试功能是否正常。');

27.“hide或者show函数传入参数就可以实现动画效果,将DOM元素从左上角开始展开或者收缩,toggle()方法则根据当前状态决定是show()还是hide()”:

var div = $('#test-show-hide');
div.hide(3000); // 在3秒钟内逐渐消失
// div.show('slow'); // 在0.6秒钟内逐渐显示,或者用fast字符串

slideUp()slideDown()将DOM元素在垂直方向逐渐收缩或展开,slideToggle()根据元素是否可见来决定下一步动作。”

fadeIn()fadeOut()的动画效果是淡入淡出,通过不断设置DOM元素的opacity属性(不透明度,1为最不透明)来实现,而fadeToggle()则根据元素是否可见来决定下一步动作。”

animate()方法可以实现任意动画效果,需要传入的参数就是DOM元素最终的CSS状态和时间,jQuery在时间段内不断调整CSS直到达到我们设定的值:”

var div = $('#test-animate');
div.animate({
    opacity: 0.25,
    width: '256px',
    height: '256px'
}, 3000); // 在3秒钟内CSS过渡到设定值

animate()还可以再传入一个函数,当动画结束时,该函数将被调用,这种回调函数参数对于hide等可以实现基本动画的方法也是适用的:”

var div = $('#test-animate');
div.animate({
    opacity: 0.25,
    width: '256px',
    height: '256px'
}, 3000, function () {
    console.log('动画已结束');
    // 恢复至初始状态:
    $(this).css('opacity', '1.0').css('width', '128px').css('height', '128px');
});

“jQuery的动画效果还可以串行执行,通过delay()方法还可以实现暂停。”

28.一个jQuery对象如果包含了不止一个DOM节点,first()last()slice()方法可以返回一个新的jQuery对象,把不需要的DOM节点去掉:

var langs = $('ul.lang li'); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var js = langs.first(); // JavaScript,相当于$('ul.lang li:first-child')
var haskell = langs.last(); // Haskell, 相当于$('ul.lang li:last-child')
var sub = langs.slice(2, 4); // Swift, Scheme, 参数和数组的slice()方法一致

29.练习:在执行删除操作时,给用户显示一个动画比直接调用remove()要更好。请在表格删除一行的时候添加一个淡出的动画效果:

'use strict';

function deleteFirstTR() {
    // tr是一个jQuery对象
    var tr = $('#test-table>tbody>tr:visible').first();
    tr.fadeOut('slow',function(){
        tr.remove(); // 删除调用remove方法的对象
    });
}
deleteFirstTR();

30.“对于不是block性质的DOM元素,动画效果不起作用;jQuery没有实现对background-color的动画效果,可以使用CSS3的transition实现动画效果”

31.“jQuery在全局对象jQuery(也就是$)绑定了ajax()函数,可以处理AJAX请求,ajax(url, settings)函数需要接收一个URL和一个可选的settings对象。jQuery的AJAX完全封装的是JavaScript的AJAX操作,其安全限制和用JavaScript写AJAX完全一样。如果需要使用JSONP,可以在ajax()settings参数中设置jsonp: 'callback',让jQuery实现JSONP跨域加载数据。”

32.“给jQuery对象绑定一个新方法是通过扩展$.fn对象实现的,示例代码:”

$.fn.highlight1 = function () {
    // this在调用时被绑定为当前jQuery对象
    this.css('backgroundColor', '#fffceb').css('color', '#d85030');
    return this;
}

“因为jQuery对象支持链式操作,自己写的扩展方法也要能继续链式下去,因此在上述代码中要return this”。

对扩展的方法传入参数,示例代码如下,注意符号&&||的使用:

$.fn.highlight2 = function (options) {
    // 要考虑到各种情况:
    // options为undefined
    // options只有部分key
    var bgcolor = options && options.backgroundColor || '#fffceb';
    var color = options && options.color || '#d85030';
    this.css('backgroundColor', bgcolor).css('color', color);
    return this;
}

33.“jQuery的方法$.extend(target, obj1, obj2, ...),它把多个object对象的属性合并到第一个target对象中并返回该对象,遇到同名属性,总是使用靠后的对象的值,也就是越往后优先级越高:”

// 把默认值和用户传入的options合并到对象{}中并返回:
var opts = $.extend({}, {
    backgroundColor: '#00a8e6',
    color: '#ffffff'
}, options);

以下写法使得用户可以一次性设定默认值,而不是调用highlight方法的时候传入参数:

$.fn.highlight = function (options) {
    // 合并默认值和用户设定值:
    var opts = $.extend({}, $.fn.highlight.defaults, options);
    this.css('backgroundColor', opts.backgroundColor).css('color', opts.color);
    return this;
}

// 设定默认值:
$.fn.highlight.defaults = {
    color: '#d85030',
    backgroundColor: '#fff8de'
}

“编写jQuery插件的原则:”

(1)给$.fn绑定函数,实现插件的代码逻辑;

(2)插件函数最后要return this;以支持链式调用;

(3)插件函数要有默认值,绑定在$.fn.<pluginName>.defaults上;

(4)用户在调用时可传入设定值以便覆盖默认值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值