【微信小程序开发小白零基础入门】微信小程序框架【建议收藏】

微信小程序框架



一、逻辑层

1.注册程序

1.App()函数

小程序通过使用App(OBJECT)函数来进行应用注册,用其指定小程序的生命周期函数。 App()函数只能写在小程序根目录下的app.js文件中,且只能注册1个。
属性类型描述触发时机备注
onLaunchFunction生命周期函数—监听小程序初始化当小程序初始化完成时触发全局只触发1次
onShowFunction生命周期函数—监听小程序显示小程序启动、或从后台进入前台显示时触发
onHideFunction生命周期函数—监听小程隐藏当小程序从前台进入后台隐藏序隐藏时触发
onErrorFunction错误监听函数当小程序发送脚本错误、或API调用失败时触发
其他自定义参数Any开发者可以添加自定义名称的函数或数据到OBJECT参数中用this可以访问

在这里插入图片描述

2.onPageNotFound()函数

当需要打开的页面不存在时,微信客户端会有一个原生模板页面进行提示。 如果开发者不希望跳转到此页面,想自行处理则需用到onPageNotFound()监听器。
字段类型说明
pathstring不存在页面的路径
queryobject打开不存在页面的query
isEntryPageBoolean是否本次启动的首个页面(例如从分享等入口进来,首个页面是开发者配置的分享页面)
App({
  onPageNotFound: function(res) {
     // 小程序需要打开的页面不存在时需要执行的代码
     wx.redirectTo({
       url: 'pages/test/test'
     }) // 如果是 tabbar 页面,请使用 wx.switchTab
  }
})

3.getApp()函数

在小程序的其他JS文件中均可以使用全局的getApp()函数获取小程序实例。 此时就可以在test.js页面拿到app.js中保存的公共数据并在console控制台打印输出了。
//app.js
App({
  globalData: {
    userInfo: null  //这是一个全局变量
  }
})

App({
  globalData: {
    userInfo: null  //这是一个全局变量
  }
  onLoad:function(options){
      console.log(this.globalData.userInfo) //使用this获得全局变量
  }
})

2.注册页面

小程序在每个页面JS文件中通过使用Page(OBJECT)函数来进行页面注册,该函数可以用于指定小程序页面的生命周期函数。
注:Page()函数只能写在小程序每个页面对应的JS文件中,且每个页面只能注册1个。

属性类型描述
dataObject页面的初始数据
onLoadFunction生命周期函数–监听页面加载
onReadyFunction生命周期函数–监听页面初次渲染完成
onshowFunction生命周期函数–监听页面显示
onHideFunction生命周期函数–监听页面隐藏
onUnloadFunction生命周期函数–监听页面卸载
onPullDownRefreshFunction页面相关事件处理函数–监听用户下拉动作
onReachBottomFunction页面上拉触底事件的处理函数
onShareAppMessageFunction用户点击右上角转发
onPageScrollFunction页面滚动触发事件的处理函数
onTabltemTapFunction当前是tab页时,点击tab时触发
其他Any开发者可以添加任意的函数或数据到object参数中,在页面的函数中用this可以访问
// pages/test/test.js
Page({
    data: {},
    onLoad: function (options) {},
    onReady: function () {},
    onShow: function () {},
    onHide: function () {},
    onUnload: function () {},
    onPullDownRefresh: function () {},
    onReachBottom: function () {},
    onShareAppMessage: function () {}
})

1.初始数据

例如,在data中放置2个自定义数据。 此时{{msg01}}和{{msg02}}不会显示字面内容,而是会查找data中的初始数据,然后显示出“Hello 2018”字样。
Page({
  data:{
      msg01: 'Hello',
      msg02: 2021
  }
})

<view>{{msg01}} {{msg02}}</view>

2.生命周期回调函数

Page()函数中默认生成的onLoad、onShow、onReady、onHide以及onUnload均属于页面的生命周期回调函数。具体解释如下: onLoad:格式为onLoad(Object query),只在页面加载时触发一次,可以在onLoad的参数中获取打开当前页面路径附带的参数。 onShow():页面显示或从小程序后台切入前台时触发。 onReady():页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。 onHide():页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其他页面,小程序切入后台等。 onUnload():页面卸载时触发。如redirectTo或navigateBack到其他页面时。

3.页面事件处理函数

Page()函数中默认生成的onPullDownRefresh、onReachBottom、onShareAppMessage以及未自动生成的onPageScroll、onTabItemTap均属于页面的事件处理函数。具体解释如下: onPullDownRefresh():监听用户下拉刷新事件。 onReachBottom():监听用户上拉触底事件。 onPageScroll(Object):监听用户滑动页面事件。
//JS
Page({
  btnTap: function() {
    console.log(![在这里插入图片描述](https://img-blog.csdnimg.cn/aec61a7ef1164a89aab343b953ea9d7b.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5pm65oWn5LqR5bel5YW3566x,size_16,color_FFFFFF,t_70,g_se,x_16)
'按钮被点击。')
  }
})

4.组件事件处理函数cont

例如tap就是点击事件,可以使用bindtap属性在组件上进行绑定。 以按钮组件为例,为其绑定点击事件
<!WXML-->
<button bindtap="btnTap">点击此处</button>

//JS
Page({
  btnTap: function() {
    console.log('按钮被点击。')
  }
})

5.route

在Page()函数中,可以使用this.route查看当前页面的路径地址。
Page({
  onShow: function() {
    console.log(this.route)
  }
})

6.setData

在Page()函数中setData()可以用来同步更新data属性中的数据值,也会异步更新相关数据到WXML页面上去。

例如,在Page()的data中定义初始数据,JS文件代码如下

Page({
  data:{
      today: '2021-10-04'
  }
})

为组件追加自定义点击事件changeData。
在Page()中追加自定义函数changeData的具体内容。
用户如果触发了该组件的点击事件,WXML中的{{today}}值将立刻更新成2021-10-04。

<!WXML-->
<view bindtap="changeData">{{today}} </view>

//JS
Page({
  changeData:function{
      this.setData({today: '2021-10-04'})
  }
})

7.生命周期

当小程序应用生命周期调用完onShow以后就准备触发小程序页面生命周期了。页面初次打开会依次触发onLoad-->onShow-->onReady这三个函数。同样如果被切换到后台,会调用页面onHide,从后台被唤醒会调用页面onShow。直到页面关闭会调用onUnload,下次打开还会照样触发onLoad-->onShow-->onReady这三个函数。

在这里插入图片描述

3.页面路由

1.页面栈

在小程序中页面之间的切换路由均由框架统一进行管理,框架以栈的形式维护了当前的所有页面。当发生路由切换的时候,页面栈的表现如表所示。
路由方式页面栈表现
初始化新页面入栈
打开新页面新页面入栈
页面重定向当前页面出栈,新页面入栈
页面返回页面不断出栈,直到目标返回页
Tab切换页面全部出栈,只留下新的Tab页面
重加载页面全部出栈,只留下新的页面

2.获取页面栈

小程序使用getCurrentPages()函数获取当前页面栈的实例,实例将以数组形式按栈的顺序给出。其中第一个元素为首页,最后一个元素为当前页面。 ### 3.路由方式 对于路由的触发方式以及页面生命周期函数如表所示。
路由方式触发时机路由前页面路由后页面
初始化小程序打开的第一个页面onLoad,onShow
打开新页面调用API wx.navigateTo或使用组件onHideonLoad,onShow
页面返回调用API wx.navigateBack 或使用组件或用户按左上角返回按钮onUnloadonShow
重启动调用API wx.reLaunch 或使用组件onUnloadonload,onShow

4.模块化

1.文件作用域

在小程序的任意JS文件中声明的变量和函数只在该文件中有效,不同的JS文件中可以声明相同名字的变量和函数,不会互相影响。 如果需要跨页面进行数据共享,可以在app.js中定义全局变量,然后在其他JS文件中使用getApp()来获取和更新。例如在app.js设置全局变量msg。
//app.js
App({
  globalData: {
    msg: ' Goodbye 2018'  //这是一个全局变量
  }
})

//test.js
var app = getApp()
app.globalData.msg = 'Hello 2019' //全局变量被更新

2.模块化

程序支持将一些公共JavaScript代码放在一个单独的JS文件中,作为一个公共模块可以被其他JS文件调用。模块只能通过module.exports或者exports才能对外提供接口。 例如,在根目录下新建utils文件夹并创建公共JS文件common.js。
//common.js
function sayHello(name) {
  console.log('Hello ${name} ! ')
}
function sayGoodbye(name) {
  console.log('Goodbye ${name} ! ')
}
module.exports.sayHello = sayHello //推荐使用这种
exports.sayGoodbye = sayGoodbye

//test.js
var common = require('../../utils/common.js') //目前暂时不支持绝对路径地址
Page({
  hello: function() {
    common.sayHello('2019')
  },
  goodbye: function() {
    common.sayGoodbye('2018')
  }
})

5.API

小程序开发框架提供丰富的微信原生API接口,可以方便的调起微信提供的能力,如获取用户信息、本地存储、地理定位等。常用的API列举如下: 网络:实现小程序与服务器端的网络交互; 媒体:实现图片、录音、音/视频和相机管理; 文件:实现临时文件和本地文件的管理; 数据:实现小程序本地数据缓存; 位置:使用小程序获取地理位置和控制地图组件; 设备:获得手机内存、网络、传感器、扫码、剪贴板、振动等设备能力; 界面:实现交互反馈、导航条设置、页面导航、动画、绘图等能力。 这些API及其相关标签会在后续章节陆续介绍。

二、视图层

1.WXML

WXML的全称是WeiXin Markup Language(微信标记语言),类似于HTML也是一种使用<标签>和

1.数据绑定

1.简单绑定

在WXML页面中可以使用双花括号{{变量名}}的形式表示动态数据

<view>{{msg}}<view>

Page({
  data: {
    msg: '你好!'
  }
})

2.属性绑定

属性绑定
组件的属性也可以使用动态数据,例如组件的id、class等属性值

<view id='{{id}}'>测试</view>

Page({
  data: {
    id: 'myView'
  }
})

3.控制属性绑定

控制属性绑定
控制属性也可以使用动态数据,但必须在引号内。

<view wx:if='{{condition}}'>测试</view>

Page({
  data: {
     condition: false
  }
})

4.关键字绑定

关键字绑定
如果直接在引号内写布尔值也必须用双花括号括起来

<view wx:if='{{false}}'>测试1</view>
<view wx:if='{{true}}'>测试2</view>

5.运算绑定

运算绑定
双花括号内部还可以进行简单的运算,支持的运算方式包括:
三元运算
算数运算
逻辑判断
字符串运算
数据路径运算

算数运算示例代码

<!--WXML页面代码-->
<view> {{a + b}} + {{c}} + d </view> <!--显示的结果是3+3+d-->

//JS文件代码
Page({
    data: {
      a : 1, b : 2, c : 3
    }
})

6.组合绑定

组合绑定
还可以在双花括号内直接进行变量和值的组合,构成新的对象或者数组

数组组合示例代码

<!--WXML页面代码-->
<view wx:for='{{[1,2,x,4]}}'>{{item}}</view> <!--最终组合成数组[1, 2, 3, 4]-->

//JS文件代码
Page({
    data: {
      x : 3
    }
})

2.列表渲染

简单列表
也可以使用wx:for-item和wx:for-index自定义当前元素和下标的变量名。

<!WXML修改后-->
<view wx:for='{{array}}' wx:for-index='stuID' wx:for-item='stuName'>
      学生{{stuID}}{{stuName}}
</view>

//JS
Page({
    data:{
        array:[ '张三', '李四', '王五']
    }
})

嵌套列表
wx:for还可以嵌套出现,例如九九乘法口诀表代码。

<!--WXML-->
<view wx:for="{{array}}" wx:for-item="i">
  <view wx:for="{{array}}" wx:for-item="j">
    <view wx:if="{{i <= j}}">
      {{i}} * {{j}} = {{i * j}}
    </view>
  </view>
</view>

//JS
Page({
    data:{
        array:[1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
})

多节点列表
可以将wx:for用在标签上,以渲染一个包含多节点的结构块。
注:并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染

<block wx:for="{{[ '张三', '李四', '王五']}}"> 
<!--数组也可以直接写在wx:for-->
  <view> 学号:{{index}} </view>
  <view> 姓名:{{item}} </view>
</block>

<!--效果等同于-->
  <view> 学号:0 </view>
  <view> 姓名:张三 </view>
  <view> 学号:1 </view>
  <view> 姓名:李四 </view>
  <view> 学号:2 </view>
  <view> 姓名:王五 </view>

3.条件渲染

简单条件
也可以组合使用0-N个wx:elif加上1个wx:else来添加其他条件

<!--WXML-->
<view wx:if="{{x > 5}}"> A </view>
<view wx:elif="{{x > 2}}"> B </view>
<view wx:else> C </view>

//JS
Page({
    data:{
          x: 3
    }
})

多节点条件
如果要一次性判断多个组件标签,可以使用标签将多个组件包装起来,并在上使用wx:if控制属性。

<!--WXML-->
<block wx:if="{{true}}">
  <view> view1 </view>
  <view> view2 </view>
</block>

4.模板

小程序使用在WXML文件中定义代码片段作为模板使用,每个都使用name属性自定义模板名称。
<!--WXML-->
<template name="myTemp">
  <view>
    <text> Name: {{name}} </text>
    <text> Age: {{age}} </text>
  </view>
</template>

5.事件

事件是视图层到逻辑层的通讯方式,有以下特点: 可以将用户的行为反馈到逻辑层进行处理; 可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数; 对象可以携带额外信息,如 id, dataset, touches。

事件使用方式
展开输出内容前面的箭头可以查看详情,整理后节选信息如右边代码框所示。
例如想获得data-my中的数据值,可以描述为e.currentTarget.dataset.my,开发者未来可以利用这些信息进行后续的代码编写。

{
  type:"tap",
  timeStamp:2859,
  currentTarget: {
    id: "myBtn",
    dataset:  {
      my:"hello"
    }
  },
  detail: {x: 214, y: 17.5},
  touches:[{
    identifier:0,
    pageX:214,
    pageY:17.5,
    clientX:214,
    clientY:17.5
  }]
}

6.引用

1.import 需要注意的是,有作用域的概念,即只会引用目标文件自己定义的template,而不会引用目标文件里面用引用的第三方模板。
<!--A页面-->
<template name="A">
  <text> A页面模板</text>
</template>

<!--B页面-->
<import src="a.wxml"/>
<template name="B">
  <text> B页面模板</text>
</template>

<!--C页面-->
<import src="b.wxml"/>
<template is="A"/>  <!-- 引用模板失败!C必须自己import A才可以 -->
<template is="B"/>  <!-- 引用模板成功!C页面有import B -->

2.include
例如,为页面制作统一的页眉页脚,示例如下。
页眉header.wxml代码:

<view>这是小程序的页眉</view>

首页index.wxml代码:

<view>这是小程序的页脚</view>

页脚footer.wxml代码:

<include src="header.wxml"/>
<view>正文部分</view>
<include src="footer.wxml"/>

2.WXSS

尺寸单位 小程序规定了全新的尺寸单位rpx(responsive pixel),可以根据屏幕宽度进行自适应。其原理是无视设备原先的尺寸大小,统一规定屏幕宽度为750rpx。 rpx不是固定值,屏幕越大,1rpx对应的像素就越大。例如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

1.尺寸单位

设备rpx换算px(屏幕宽度750)px换算rpx (750/屏幕宽良)
iPhone51rpx = 0.42px1px = 2.34rpx
iPhone61rpx = 0.5px1px = 2rpx
iPhone6 Plus1rpx = 0.552px1px = 1.81rpx

2.样式导入

小程序在wxss样式表中使用@import语句导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。

3.常用属性其中颜色可以用以下几种方式来表示:

RBG颜色:用RGB红绿蓝三通道色彩表示法,例如rgb(255,0,0)表示红色; RGBA颜色:在RGB的基础上加上颜色透明度,例如rgba(255,0,0,0.5)表示半透明红色; 16进制颜色:又称为HexColor,用#加上6位数字表示,例如#FF0000表示红色; 预定义颜色:使用颜色英文单词的形式表示,例如red表示红色。小程序目前共预设了148种颜色名称,见附录。
样式属性含义参考值
background-color背景色颜色名,例如red表示红色
color前景色同上
font-size字体大小例如16px表示16像素大小的字体
border边框例如3px solid blue表示宽度为3像素的蓝色实线
width宽度例如20px表示20像素的宽度
height高度例如100px表示100像素的高度

4.内联样式

小程序使用class属性指定样式规则,其属性值是一个或多个自定义样式类名组成,多个样式类名之间用空格分隔。
.style01{
    color: red;  //文字为红色
}
.style02{
    font-size: 20px;  //字体大小为20像素
    font-weight: bold;  //字体加粗
}

<view class="style01 style02">测试</view>
<!--上述代码表示组件同时接受.style01和.style02的样式规则。注意在class属性值的引号内部不需要加上类名前面的点。-->

5.选择器

小程序目前在wxss样式表中支持的选择器如表所示。
选择器样例样例描述
.class.demo选择所有拥有class="demo"属性的组件
#id#test选择拥有id="test"属性的组件
elementview选择所有view组件
element, elementview, text选择所有文档的view组件和所有的text组件
afterviewafter在view组件后边插入内容
beforeview::before在view组件前边插入内容

6.全局样式和局部样式

小程序wxss样式表中规定的样式根据其作用范围分为两类: 全局样式:在app.wxss中的样式,作用于每一个页面; 局部样式:在页面wxss文件中定义的样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。

三、基本布局方法-flex模型

1.基本概念

1.Flex布局

小程序使用了flex模型来提高页面布局效率。这是一种灵活的布局模型,当页面需要适应不同屏幕大小以及设备类型时该模型可以确保元素在恰当的位置。

2.容器和项目

在flex布局中,用于包含内容的组件称为容器(container),容器内部的组件称为项目(item)。容器允许包含嵌套
<view id="A">
    <view id="B">
        <view id="C"></view>
    </view>
</view>

3.坐标轴

flex布局的坐标系是以容器左上角的点为原点,自原点往右、往下两条坐标轴。默认情况下是水平布局,即水平方向从左往右为主轴(main axis),垂直方向自上而下为交叉轴(cross axis),也可以使用样式属性flex-direction: column将主轴与交叉轴位置互换。

4.Flex属性

在小程序中,与flex布局模型相关的样式属性根据其属标签的类型可以分为容器属性和项目属性。 容器属性:用于规定容器布局方式从而控制内部项目的排列和对齐方式。 项目属性:用于设置容器内部项目的尺寸、位置以及对齐方式。

2.容器属性

1.flex-direction属性

flex-direction属性用于设置主轴方向,通过设置坐标轴可以规定项目的排列方向。
其语法格式如下

.container{
  flex-direction: row(默认值)| row-reverse | column | column-reverse
}

属性值解释如下: row:默认值,主轴在水平方向从左到右。项目按照主轴方向从左到右排列; row-reverse:主轴是row的反方向。项目按照主轴方向从右到左排列; column:主轴在垂直方向从上而下。项目按照主轴方向从上往下排列; column-reverse:主轴是column的反方向。项目按照主轴方向从下到上排列。

2.flex-wrap属性

flex-wrap属性用于规定是否允许项目换行,以及多行排列时换行的方向。
其语法格式如下:

.container{
  flex-wrap: nowrap(默认值)| wrap | wrap-reverse
}

属性值解释如下: nowrap:默认值,表示不换行。如果单行内容过多,项目宽度可能会被压缩。 wrap:容器单行容不下所有项目时允许换行排列。 wrap-reverse:容器单行容不下所有项目时允许换行排列。换行方向为wrap的反方向。

3.justify-content属性

justify-content属性用于设置项目在主轴方向上对齐方式,以及分配项目之间及其周围多余的空间。其语法格式如下:

.container{
justify-content: flex-start (默认值)| flex-end| center| space-between| space-around| space-evenly
}

属性值解释如下: flex-start:默认值,表示项目对齐主轴起点,项目间不留空隙; center:项目在主轴上居中排列,项目间不留空隙。 flex-end:项目对齐主轴终点,项目间不留空隙; space-between:项目间间距相等,第一个和最后一个项目分别离起点/终点距离为0; space-around:与space-between相似。不同点为,第一个项目离主轴起点和最后一个项目离终点距离为中间项目间间距的一半; space-evenly:项目间距、第一个项目离主轴起点以及最后一个项目离终点距离均相等。

4.align-items属性

align-items属性用于设置项目在行中的对齐方式。其语法格式如下:

.container{
  align-items:stretch(默认值)| flex-start | center | flex-end | baseline
}

属性值解释如下: stretch(默认值):未设置项目尺寸时将项目拉伸至填满交叉轴; flex-start:项目顶部与交叉轴起点对齐; center:项目在交叉轴居中对齐; flex-end:项目底部与交叉轴终点对齐; baseline:项目与行的基线对齐,在未单独设置基线时等同于flex-start。

5.align-content属性

align-content属性用于多行排列时,设置项目在交叉轴方向上的对齐方式,以及分配项目之间及其周围多余的空间。其语法格式如下:

.container{
  align-content: stretch(默认值) | flex-start | center | flex-end | space-between |space-around | space-evenly
}

属性值解释如下: stretch:默认值,未设置项目尺寸时将各行中的项目拉伸至填满交叉轴。当设置了项目尺寸,项目尺寸不变,项目行拉伸至填满交叉轴。 flex-start:首行在交叉轴起点开始排列,行间不留间距。 center:行在交叉轴中点排列,行间不留间距,首行离交叉轴起点和尾行离交叉轴终点距离相等。 flex-end:尾行在交叉轴终点开始排列,行间不留间距。 space-between:行与行间距相等,首行离交叉轴起点和尾行离交叉轴终点距离为0。 space-around:行与行间距相等,首行离交叉轴起点和尾行离交叉轴终点距离为行与行间间距的一半。 space-evenly:行间间距、以及首行离交叉轴起点和尾行离交叉轴终点距离相等。 注:多行排列要设置flex-wrap属性值为wrap,表示允许换行。

3.项目属性

1.order属性

order属性用于设置项目沿主轴方向上的排列顺序,数值越小,排列越靠前。属性值为整数。其语法格式如下:

.item{
  order: 0(默认值)| <integer>
}

2.flex-shrink属性

flex-shrink属性用于设置项目收缩因子。当项目在主轴方向上溢出时,通过项目收缩因子的规定比例来压缩项目以适应容器。其语法格式如下:

.item{
flex-shrink: 1(默认值) | <number>
}

3.flex-grow属性

flex-grow属性用于设置项目扩张因子。当项目在主轴方向上还有剩余空间时,通过设置项目扩张因子进行剩余空间的分配。。其语法格式如下:

.item{
  flex-grow: 0(默认值)| <number>
}

4.flex-basis属性

flex-basis属性
其语法格式如下:

.item{
  flex-basis: auto(默认值)| <number>px
}

5.flex属性

flex属性
flex属性是flex-grow,flex-shrink,flex-basis的简写方式,其语法格式如下:

.item{
flex: none | auto | @flex-grow @flex-shrink@flex-basis
}

6.align-self属性

align-self属性设置项目在行中交叉轴方向上的对齐方式,用于覆盖容器的align-items,这么做可以对项目的对齐方式做特殊处理。其语法格式如下:

.item{
  align-self: auto(默认值) | flex-start | center | flex-end | baseline |stretch
}

四、推荐小程序(欢迎各位大佬指导)

在这里插入图片描述

  • 8
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智慧云工具箱

你的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值