微信小程序开发之路④

14、微信小程序数据绑定与数组的运用

01 微信小程序数据绑定

WXML数据绑定

运用 JS 的 data 对象完成的 WXML 数据的动态显示

绑定分类:

1、基础绑定

data: {
    studentName: 'tom'
}
;<text>{{ studentName }}</text>

data 的作用

1、初始始数据

2、我们的所有数据和面页中的内容绑定时的对接程序。

2、组件绑定

属性设定(需要在双引号内)

<view class="{{flag ? 'color-red' : 'color-green'}}">
    <text>name: {{studentName}}</text>

    <view id="item{{id}}"> item-view </view></view
>

运算符的操作

02、微信小程序数组

小程序数组的写法

studentScroes:[90, 70, 85],

和绑定表达式的组合使用

{{studentScroes[0]}}

和绑定表达式的对象组合使用

{{studentClassScroes[0].className}}

扩展运算符(模板时后期讲解)

Js 里的 data 页面的初始数据,让我们所有的数据与页面的内容进行数据绑定时的对接程序

  data: {
    studentName: "tom"
  },

15、微信小程序条件渲染与列表渲染

在微信里所有的字符串都是真

01、微信小程序条件渲染与列表渲染

wx:if 用来做判断是否需要渲染该代码块

也可以用 wx:elif 和 wx:else 来写 if-else 语句

Block wx:if

wx:if --> if(){}
wx:elif -> if else(){}
wx:else --> else(){}
block --> template

特别区分:

wx:if 和 hiiden 相当 display:none(css 隐藏 )

wx:if 是遇 true 显示,hidden 是遇 false 显示。

wx:if 在隐藏的时候不渲染,而 hidden 在隐藏时仍然渲染,只是不呈现。

所以如果频繁切换的话,用 hidden 将会消耗更多资源,因为每次呈现的时候他都会渲染,每次隐藏的时候,他都会销毁。

如果切换并不频繁的话,用 wx:if 相对来说较好些,因为它会避免初始就一下渲染那么多。

02、微信小程序条件渲染与列表渲染

wx:for 的通用写法

修改特定属性:

wx:for-item 修改循环变量

<view class="inner-view margin20" wx:for="{{navigatorScroll}}">
    <navigator url="{{item.url}}">
        <image src="/icon/{{item.img}}"></image>
        <view class="inner-fnt">{{item.title}}</view>
    </navigator>
</view>

wx:for-index 可以指定数组的下标

view wx:for=“{{array}}” wx:for-index=“idx” wx:for-item=“itemName”

指定义: wx:for-index=“scoredID”

指定义: wx:for-item=“studentScore”

<view
    wx:for="{{studentScores}}"
    wx:for-index="scoredID"
    wx:for-item="studentScore"
>
    {{scoredID}}:{{studentScore}}
</view>

数组的嵌套

block wx:for

block 与 view 区别

用在 block 中可以渲染一个包含多节点的结构块。(相当一个 vue 中的 template 标签)

用在 view 中只能应用单个节点。

16、微信小程序模板操作与引用

template 定义模板后可以在不同的位置使用模板

模板的定义 name 起名字

<!-- 模板定义 -->
<template name="studentInfo">
    <view>学号:{{ studentName }}</view>
    <view>姓名: {{ studengXingMing }}</view>
    <view>年龄:{{ age }}</view>
</template>

使用模板 is

<!-- 模板调用 -->
<template is="studentInfo" data="{{studentName, age}}" />

模板的作用域

模板的引用的两种方式import和include

include 引入的模板里面不能包含 template 元素

<include src="/templates/footer.wxml" />

Improt(引入模板)

模板文件 templates 下目录:

studentInfo.wxml
text_student.wxml

引入到 templateDemo.wxml

可以把外部的一个文件导入指定的文件。然后再调用

Import 的作用域:没有继承性
就是 A 引入 B,B 引入 C, 在 A 里面不能展示 C

Include(导入代码)

可以将目标文件整个代码引入,相当于拷贝

17、微信小程序的事件调用

微信小程序事件

事件是用户与微信小程序的交互的通讯接口

事件是视图层到逻辑层的通讯方式。

事件可以将用户的行为反馈到逻辑层进行处理。

事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。

事件对象可以携带额外信息,如 id, dataset, touches。

在 JS 中就有事件操作的概念,微信小程序的更加简便

事件的使用方式

在组件中绑定一个事件处理函数。

<view bindtap="tapName">Click me</view>

在相应的 Page 定义中写上相应的事件处理函数,参数是 event。

tapName(event) {
    console.log(event);
},

返回对象

{type: “tap”, timeStamp: 2774, target: {…}, currentTarget: {…}, mark: {…}, …}

以“bind+事件类型”或“catch+事件类型” 作为用户的事件绑定监听器,连接事件源和事件处理程序

事件绑定函数可以是一个数据绑定

<view bindtap="{{eventFun}}">Click me</view>

data: {
    eventFun:"tapName"
},

事件分类

#### 事件分为冒泡事件和非冒泡事件:

1、冒泡事件 bind 开头:当一个组件上的事件被触发后,该事件会向父节点传递。

2、非冒泡事件 catch 开头:当一个组件上的事件被触发后,该事件不会向父节点传递

绑定并阻止事件冒泡

除 bind 外,也可以用 catch 来绑定事件。与 bind 不同, catch 会阻止事件向上冒泡。

例如在下边这个例子中,点击 inner view 会先后调用 handleTap3 和 handleTap2(因为 tap 事件会冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父节点传递),点击 middle view 会触发 handleTap2,点击 outer view 会触发 handleTap1。

<view id="outer" bindtap="handleTap1">
    outer view
    <view id="middle" catchtap="handleTap2">
        middle view
        <view id="inner" bindtap="handleTap3">
            {' '}
            inner view{' '}
        </view>
    </view>
</view>
类型	            触发条件	        最低版本

touchstart	       手指触摸动作开始

touchmove	       手指触摸后移动

touchcancel	        手指触摸动作被打断,如来电提醒,弹窗

touchend	        手指触摸动作结束

tap	               手指触摸后马上离开

longpress	        手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发	1.5.0

longtap	            手指触摸后,超过350ms再离开(推荐使用longpress事件代替)

transitionend	    会在 WXSS transition 或 wx.createAnimation 动画结束后触发

animationstart	    会在一个 WXSS animation 动画开始时触发

animationiteration	会在一个 WXSS animation 一次迭代结束时触发

animationend	    会在一个 WXSS animation 动画完成时触发

touchforcechange	在支持 3D Touch 的 iPhone 设备,重按时会触发	1.9.90

注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如 form 的 submit 事件,input 的 input 事件,scroll-view 的 scroll 事件,(详见各个组件)

事件的捕获阶段

事件捕获是先外后里,且优于事件绑定。

<view
id="outer"
bind:touchstart="handleTap1"
capture-bind:touchstart="handleTap2"

>

    outer view
    <view
        id="inner"
        bind:touchstart="handleTap3"
        capture-bind:touchstart="handleTap4"
    >
        inner view
    </view>

</view>

handleTap1() {
console.log("handleTap1")
},
handleTap2() {
console.log("handleTap2")
},
handleTap3() {
console.log("handleTap3")
},
handleTap4() {
console.log("handleTap4")
},

点击 inner view

返回
handleTap2
handleTap4
18 handleTap3
handleTap1

事件对象

每次触发事件时都会传递一个对象给到 JS(逻辑层)

BaseEvent的属性

Type事件类型

Timestamp 时间戳

Target 属性集合(事件源组件)

Id 事件源组件id

Dataset data- 事件源组件上的自定义集合

currentTarget 当前组件的一些属性值集合

dataset data- 事件源组件上的自定义集合

二个盒子:当第二个子级盒子高度用 100%时,外面父盒子一定要有固定的高度。
微信给一个获取屏幕高度、宽度的 API

获取不同屏幕的高度、宽度 px,用一个固定的格式进行 rpx 的转换

第一步
/**
* 生命周期函数--监听页面加载
**/
onLoad: function (options) {
    wx.getSystemInfo({
        success: function(res) {
            let clientHeight = res.windowHeight;
            let clientWidth = res.windowWidth;
            let ratio = 750 / clientWidth;
            let rpxHeight = clientHeight _ ratio;
            console.log(clientHeight);
            console.log(rpxHeight);
        },
    })
},
    // 第二步在 data 定义变量
data: {
    swiperRPXHeight:0
},
    // 第三步修改变量
onLoad: function (options) {
    let that = this;
    wx.getSystemInfo({
        success: function(res) {
            let clientHeight = res.windowHeight;
            let clientWidth = res.windowWidth;
            let ratio = 750 / clientWidth;
            let rpxHeight = clientHeight \* ratio;
            console.log(clientHeight);
            console.log(rpxHeight);

            that.setData({
              swiperRPXHeight: rpxHeight
            })
        },
    })
},
    第四步
    <swiper style="height:{{swiperRPXHeight}}rpx"> </swiper>

18、微信小程序媒体组件 audio

audio 音频

可以播放在线音频资源

支持的属性:参考在线字典

支持 wx.createInnerAudioContext

从内部代码完成播放操作,是 audio 的升级版本

案例演示

<view>
  <audio name="{{name}}" poster="{{poster}}" author="{{author}}" src="{{src}}" id="myAudio"></audio>

<button type="primary" bindtap="audioPlay">播放</button>
<button type="primary" bindtap="audioPause">暂停</button>
<button type="primary" bindtap="audio14">设置当前播放时间为 14</button>
<button type="primary" bindtap="audioStart">回到开头</button>
</view>


/**
   * 播放
   */
    audioPlay() {
    this.audioCtx.play();
    },

  /**
   * 暂停
   */
    audioPause() {
    this.audioCtx.pause();
    },

 /**
   * 指定位置
   */
    audio14() {
    this.audioCtx.seek(14);
    },

  /**
   * 从头开始
   */
    audioStart() {
    this.audioCtx.seek(0);
    },

/**
   * 生命周期函数--监听页面初次渲染完成
   */

    onReady: function () {

        // 创建一个音频对象(音频上下文)
        this.audioCtx = wx.createAudioContext("myAudio")
    },

媒体组件 video

video 视频组件:

该组件为原生组件,需要注意部分事项

小程序中的原生组件有如下

    camera,canvas,input,live-player,live-pusher,map,textarea,video

原生组件的使用限制

1.原生组件的层级是最高层,其他组件无法通过 z-index 来覆盖

2.原生组件可以覆盖原生组件

3.部分样式无法支持原生组件

CSS 动画,position:fixed 4.在 IOS 下,原生组件不支持触摸事件

属性:在线字典查询

案例演示:弹幕制作

19、视图容器 movable-view 与 cover-view 的操作

微信小程序控件 movable-view

movable-area,movable-view

可移动区域

需要设置movable-area高和宽,默认值:10px

movable-view 可拖动区域容器

属性:参考在线字典

案例演示
照片的拖动缩放

cover-view

覆盖在组件上的文本视图

cover-image

覆盖在原生组件智商的图片

案例演示

20、件 rich-text 与 progress 操作

微信小程序控件 rich-text 通过 rich-text 组件可以加载 HTML 的容器

富文本对话框

可以直接导入HTML标签文本

nodes属性

属性值可以是 array或string

在这里插入图片描述

在这里插入图片描述

案例演示 加载逻辑层数据
在这里插入图片描述

下面是wxhtml结构

在这里插入图片描述
在这里插入图片描述

微信小程序控件 progress->进度条

percent 进度多少
stroke-width:宽度

进度条操作

属性:参考在线字典

案例演示

20、表单 form 控件的提交原理与提交操作

微信小程序 form 提交原理

form 表单的提交

和 PC 端的表单提交有所不同

更贴近表单的异步提交模式

两大重要事件

bindsubmit

bindreset

<from bindsubmit="formSubmit" bindreset="formReset">

    <buttom form-type="submit">Submit</button>
    <buttom form-type="reset">Reset</button>
</from>

点击这二按钮后会完成事件的处理:formSubmit formReset

微信小程序提交事件操作

button

size = “mini” 小的
type = “primary” 颜色
plain 镂空
disabled=‘{{false}}’ 不是不可用
loading = ‘{{true}}’ 名称前是否带 loading 图标

用于 form 组件,点击分别会触发 form 组件的 submit/reset 事件

<button form-type="submit"></button>
<button fro-type ="reset"></button>
radio

用 radio-group 归为一类别

<radio-group name="radio">
    <label>
        <radio value="radio1" />
        选项一
    </label>
    <label>
        <radio value="radio2" />
        选项二
    </label>
</radio-group>
checkbox

用 checkbox 归为一类别
用 e.detail.value 获取 value 值

  /**
   * 提交事件
   */
  checkedChange(e){
    console.log(e.detail.value)
    //获取数据

    //处理数据

    //提交数据
  },

<checkbox-group checkbox='checkedChange'>
    <label>
        <checkbox value="checkbox1" />
        选项一
    </label>
    <label>
        <checkbox value="checkbox2" />
        选项二
    </label>
</checkbox-group>
editor 富文本编辑器
input

type 为键盘类型


<input value='文本' type='tex' maxlength="5" focus="true"></input>

<input value='数字'></input>

<input password placeholder="密码" placeholder-style='color:red'>

confirm-type = send 发送 search 搜索 next 下一个 go 前往 done 完成

label

点击控件的区域大小

21、导航组件 navigator 跳转与 tabbar 的跳转操作

navigator 跳转
主要用于直接在 wxml 的跳转

可跳转其他小程序

具体属性参考:在线微信字典

tabbar 的跳转

通过 app.json 的配置

通过微信接口进行跳转

注意事项

tabbar 跳转与 navigator 跳转不可同时作用在一个对象上

同一对象

<navigator target='self' url="/pages/form/form' open-type='switchTab'>tabbar</navigator>
<navigator target="self" url="/pages/audio/audio" open-type="reLaunch">
    跳转操作
</navigator>
<button bindtap="tabbarNivagtor">tatbar跳转</button>
tabbarNivagtor(){
    wx.switchTab({
        url: '/pages/index/index'  //这个路径一定要在app.json中的
    })
}

其他的跳转方式

<button bindtap="otherNavigator">跳转</button>

otherNavigator(){
    wx.redirectTo({
        url: '/pages/index/index'  //这个路径一定要在app.json中的
    })
}

// open-type 的合法值

navigate 与 reLaunch 的区别

reLaunch可以把前面打开的页面关闭,而navigate前面页面打开着,当前页面跳转。
personCenter
<view class="container">
    <view class="page">
        <form bindsubmit="regFormSubmit" bindreset="regFormReset">
            <view class="area">
                <label class="model">
                    <text>name:</text>
                    <input class="input" bindblur="inputValue"></input>
                </label>
                <label class="model">
                    <text>password:</text>
                    <input
                        class="input"
                        password
                        bindblur="passwordValue"
                    ></input>
                </label>
                <label class="model">
                    <text>password:</text>
                    <radio-group
                        class="control-center"
                        bindchange="genderRadio"
                    >
                        <radio value="1" checked>
                            male
                        </radio>
                        <radio value="0">female</radio>
                    </radio-group>
                </label>
                <label class="model">
                    <text>hobby:</text>
                    <checkbox-group
                        class="control-center"
                        bindchange="hobbyCheckbox"
                    >
                        <checkbox value="tour">tour</checkbox>
                        <checkbox value="book" checked>
                            book
                        </checkbox>
                        <checkbox value="game">game</checkbox>
                    </checkbox-group>
                </label>
                <label class="model">
                    <text>birthday</text>
                    <picker mode="date" bindchange="birthdayValue">
                        <view>[please choose birthday]</view>
                    </picker>
                </label>
                <label class="model">
                    <text>study years:</text>
                    <slider
                        step="1"
                        show-value
                        bindchange="sliderValue"
                    ></slider>
                </label>
                <label class="model">
                    <view>
                        isMember:
                        <switch bindchange="isMemberValue"></switch>
                    </view>
                </label>
                <label class="model">
                    <text>agreemeng:</text>
                    <textarea class="textarea control-center">
                        this is psersonReg.please read it first.
                    </textarea>
                </label>
                <button size="default" form-type="submit">
                    submit
                </button>
                <button size="default" form-type="reset">
                    reset
                </button>
            </view>
        </form>
    </view>
</view>

Page({
  //表单数据
  personName:'',
  password:'',
  gender:'',
  hobby:'',
  birthday:'',
  studyYears:'',
  isMember:'',

  /**
   * 页面的初始数据
   */
  data: {

  },

  /**
   * 获取姓名
   */
  inputValue(e){
    this.personName = e.detail.value;
    console.log(this.personName)
  },

  /**
   * 获取密码
   */
  passwordValue(e){
    this.password = e.detail.value;
    console.log(this.password)
  },


  /**
   * 获取性别
   */
  genderRadio(e){
    this.gender = e.detail.value == 1 ? "male" : "female";
    console.log(this.gender)
  },

  /**
   * 获取爱好
   */
  hobbyCheckbox(e){
    this.hobby = e.detail.value
    console.log(this.hobby)
  },

  /**
   * 获取生日
   */
  birthdayValue(e){
    this.birthday = e.detail.value;
    console.log(this.birthday)
  },

  /**
   * 获取学习年数
   */
  sliderValue(e){
    this.studyYears = e.detail.value;
    console.log(this.studyYears)
  },

  /**
   * 获取判断会员
   */
  isMemberValue(e){
    this.isMember = e.detail.value;
    console.log(this.isMember)
  },

  /**
   * 提交按钮
   */
  regFormSubmit(){
    let memberData = {
      personName: this.personName,
      password: this.password,
      gender: this.gender,
      hobby: this.hobby,
      birthday: this.birthday,
      isMember: this.isMember
    }

    if(memberData.personName == ""){
      wx.showModal({
        title: 'error',
        content: '姓名没有填写',
      })
    }
})

手机验证码

按钮-> 服务器 -> 电信(移动,联通) 渠道商接(短信下发) -> 在服务器端记录验证号,

用户输入 -> 提交 -> 服务器

用户输入 -> 服务器的记录匹配

24、camera 系统相机。扫码二维码功能,

<view class="page-body">
  <view class="page-body-wrapper">
    <camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;"></camera>
    <view class="btn-area">
      <button type="primary" bindtap="takePhoto">拍照</button>
    </view>
    <view class="btn-area">
      <button type="primary" bindtap="startRecord">开始录像</button>
    </view>
    <view class="btn-area">
      <button type="primary" bindtap="stopRecord">结束录像</button>
    </view>
    <view class="preview-tips">预览</view>
    <image wx:if="{{src}}" mode="widthFix" src="{{src}}"></image>
    <video wx:if="{{videoSrc}}" class="video" src="{{videoSrc}}"></video>
  </view>
</view>

/**
   * 页面的初始数据
   */
  data: {
    src:''
  },

  /**
   * 拍照
   */
  takePhoto(){
    let that = this
    //获取相机对象
    const ctx = wx.createCameraContext();
    //通过接口调用拍照方法
    ctx.takePhoto({
      quality:'high',  //高质量
      success:(res)=>{
        // 设定到缓存中
        wx.setStorage({
          key: 'photoPeople',
          data: restempImagePath, //存在本地的图片临时的路径
        })
        // 跳转页面
        wx.redirectTo({
          url: "/page/case/photo",
        })
        that.setData({
          src:res.tempImagePath
        })
      },
      error(e) {
        console.log(e)
      }
    })
  },

25、map 地图

canvas 组件

画布(原生组件)
画布 canvas 标签默认宽度 300px、高度 225px
同一页面中的 canvas-id 不可重复

<view class="container">
<!-- 画布区域 -->
  <view class='canvas-area'>
  <!-- 注意:同一个页面中的canvas-id不可重复,如果使用了已经出现过的canvase-id,该canvas标签对于的画布将被隐藏病不再正常工作 -->
    <canvas canvas-id='myCanvas' class='myCanvas'
      disable-scroll='false'
      bindtouchstart='touchStart'
      bindtouchmove='touchMove'
      bindtouchend='touchEnd'
    >
    </canvas>
  </view>
  <!-- 画布工具区域 -->
  <view class='canvas-tools'>
    <!-- 细笔绘制 -->
    <view class='box box1' bindtap='penSelect' data-param='5'></view>
    <!-- 粗笔绘制 -->
    <view class='box box2' bindtap='penSelect' data-param='15'></view>
    <!-- 红色笔 -->
    <view class='box box3' bindtap='colorSelect' data-param='#c03'></view>
    <!-- 黄色笔 -->
    <view class='box box4' bindtap='colorSelect' data-param='#f90'></view>
    <!-- 橡皮 -->
    <view class='box box5' bindtap='clearCanvas'></view>
  </view>
</view>


Page({
  data: {
    pen: 3, //画笔粗细默认值
    color: '#000' //画笔颜色默认值
  },
  startX: 0, //保存X坐标轴变量
  startY: 0, //保存X坐标轴变量
  isClear: false, //是否启用橡皮擦标记
  //手指触摸动作开始
  touchStart: function (e) {
    //得到触摸点的坐标
    this.startX = e.changedTouches[0].x
    this.startY = e.changedTouches[0].y
    this.context = wx.createContext()

    if (this.isClear) { //判断是否启用的橡皮擦功能  ture表示清除  false表示画画
      this.context.setStrokeStyle('#F8F8F8') //设置线条样式 此处设置为画布的背景颜色  橡皮擦原理就是:利用擦过的地方被填充为画布的背景颜色一致 从而达到橡皮擦的效果
      this.context.setLineCap('round') //设置线条端点的样式
      this.context.setLineJoin('round') //设置两线相交处的样式
      this.context.setLineWidth(20) //设置线条宽度
      this.context.save();  //保存当前坐标轴的缩放、旋转、平移信息
      this.context.beginPath() //开始一个路径
      this.context.arc(this.startX, this.startY, 5, 0, 2 * Math.PI, true);  //添加一个弧形路径到当前路径,顺时针绘制  这里总共画了360度  也就是一个圆形
      this.context.fill();  //对当前路径进行填充
      this.context.restore();  //恢复之前保存过的坐标轴的缩放、旋转、平移信息
    } else {
      this.context.setStrokeStyle(this.data.color)
      this.context.setLineWidth(this.data.pen)
      this.context.setLineCap('round') // 让线条圆润
      this.context.beginPath()

    }
  },
  //手指触摸后移动
  touchMove: function (e) {

    var startX1 = e.changedTouches[0].x
    var startY1 = e.changedTouches[0].y

    if (this.isClear) { //判断是否启用的橡皮擦功能  ture表示清除  false表示画画

      this.context.save();  //保存当前坐标轴的缩放、旋转、平移信息
      this.context.moveTo(this.startX, this.startY);  //把路径移动到画布中的指定点,但不创建线条
      this.context.lineTo(startX1, startY1);  //添加一个新点,然后在画布中创建从该点到最后指定点的线条
      this.context.stroke();  //对当前路径进行描边
      this.context.restore()  //恢复之前保存过的坐标轴的缩放、旋转、平移信息

      this.startX = startX1;
      this.startY = startY1;

    } else {
      this.context.moveTo(this.startX, this.startY)
      this.context.lineTo(startX1, startY1)
      this.context.stroke()

      this.startX = startX1;
      this.startY = startY1;

    }
    //只是一个记录方法调用的容器,用于生成记录绘制行为的actions数组。context跟<canvas/>不存在对应关系,一个context生成画布的绘制动作数组可以应用于多个<canvas/>
    wx.drawCanvas({
      canvasId: 'myCanvas',
      reserve: true,
      actions: this.context.getActions() // 获取绘图动作数组
    })
  },
  //手指触摸动作结束
  touchEnd: function () {

  },
  //启动橡皮擦方法
  clearCanvas: function () {
    if (this.isClear) {
      this.isClear = false;
    } else {
      this.isClear = true;
    }
  },
  penSelect: function (e) { //更改画笔大小的方法
    console.log(e.currentTarget);
    this.setData({ pen: parseInt(e.currentTarget.dataset.param) });
    this.isClear = false;
  },
  colorSelect: function (e) { //更改画笔颜色的方法
    console.log(e);
    console.log(e.currentTarget);
    this.setData({ color: e.currentTarget.dataset.param });
    this.isClear = false;
  }
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值