小程序

官方文档-生命周期

简介

2017年 1 月 9 日,微信正式发布小程序,这是一个不需要下载安装注册登录等步骤,只需要安装微信即可使用的 MINI APP,天生跨平台 成本低

其他小程序与微信小程序类似,微信小程序最具代表性

支付宝小程序
钉钉小程序
百度小程序
qq 小程序
头条系小程序

优秀三方小程序

  1. 拼多多
  2. 滴滴出行
  3. 斗地主
  4. 智行火车票等

使用 WEUI 等 npm 依赖

在微信开发者工具中的菜单栏:工具 --> 项目详情, 勾选“使用 npm 模块”选项

npm init
npm i weui-miniprogram -S

安装完毕后
微信开发者工具中菜单栏:工具 --> 构建 npm
然后会看到根目录的 miniprogram_npm/weui-miniprogram 文件夹

设置 git 忽略依赖文件夹

根目录 + .gitignore 文件

node_modules
miniprogram_npm

引入全局样式

app.wxss

@import '/miniprogram_npm/weui-miniprogram/weui-wxss/dist/style/weui.wxss';

标签

view = div
text = span
小程序说为了安全,只能用框架里的标签
block = template

循环

循环默认了 item 和 index 两个关键字
循环对象时 item = value index = key
可自定义


wx:for="{{}}" wx:key=""
data

小程序提倡 data 内只存放页面内用得到的数据,如果一个对象有很多属性都用不到则最好优化一下

data: {
  initData: {
    
  }
}

通过 this.setData() 这个 API 改变

Page({
  data: { msg: 0 },
  handleChangeMsg() { this.setData({ msg: someValue })}
})
事件绑定

bind 为关键字
bind* = @*

<view bindtap="handleTap" >

传值只能通过自定义属性传,很 tm 恶心

<view bindtap="handleTap(1)"> // 错误
<view bindTap="handleTap" data-operation="{{1}}">

// js
handleTap(e) {
  const opeartion = e.currentTarget.?.value
}
CSS

小程序默认把设备宽给定位 750rpx,即

width(px) = 750rpx
1px = 750rpx / width(px)

当设备为 375px:iphone 6

1px = 750rpx / 375px

当设计稿为 750px 屏幕为 375px,元素宽为 100px

element(100px) / 375px = element(rpx) / 750rpx
element(rpx) = (750 * 100 / 375)rpx
element {
  width: calc(750rpx * 100 / 375)
}

@import 只能是相对路径

@import "../style/common.wxss"

* 通配符不管用:这意味着下面代码失效

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

如果需要初始化

view, text, a, img {
  ...
}
image 标签

小程序包限制 2m,所以所有图片资源都要链入

image 标签有默认宽高 ,320px * 240px

components

小程序支持组件化
需要在 *.json 中配置要使用的组件,相当于 important from

index.json

{
  "usingComponents": {
    "Tabs": "../../components/Tabs/Tabs"
  }
}

组件中的事件要写在 methods

Component({
  methods: {
    handleTap(e) {}
  }
})
setData

在小程序中,已声明的状态不可以像 vue 那样直接修改,必须调用 this.setData() API
官方推荐不要直接处理 this.data 中的数据,需要深拷贝一份出来,然后再通过 API 赋值

someMethods() {
  const { list } = this.data;
  list.forEach((item, index) => {
    item.active = index === activeIndex ? true : false;
  });
  this.setData({ list });
}
组件通讯
  1. 父子通讯
    子组件内 properties 接收传值
// father
<Nav navList="navList" />
data: {
  navList: []
}

// son
properties: {
  list: {
    type: Array,
    value: []
  }
}
  1. 子父通讯
    如果子组件内直接修改父组件传递过来的数据, VUE 会报错,小程序不会报错
properties: {
  list: {
    type: Array,
    value: []
  }
},

methods: {
  changeProps() {
    this.setData({
      list: list.push({})
    })
  }
}

上面的写法不会报错,但是这么写有问题,
相当于在 子组件 data 内声明 list 并赋值
data: {}
=>
data: {
  list: this.properties.list.push({})
}

小程序内正确的子触发父自定义事件的写法:

// father
<Son
  list="{{ sonList }}" 
  bindactiveChange="activeChange"
/>
activeChange(e) {
  const activeIndex = e.detail.activeIndex;
  this.setData(); // do someting...
}

// Son
<view
  bindtap="handleTap"
  data-index="{{1}}"
></>

properties: {
  list: {
    type: Array,
    value: []
  }
},

methods: {
  handleTap(e) {
    const activeIndex = e.currentTarget.dataset.index;
    this.triggerEvent(
      'activeChange',
      { activeIndex }
    )
  }
}

其中,不同于 Vue,this.triggerEvent() 
第二个参数不是原封不动的传递给父组件绑定的函数。而是在 e.detail 中



生命周期
生命周期
1. 应用生命周期( 入口文件 app.js )
  App({})
	onLaunch() {} // 应用第一次启动,获取用户个人信息 共享到所有页面
	onShow() {} // 应用被前台,重置数据,定时器等
	onHide() {} // 应用被后台,停止定时器等性能消耗
	onError(error) {} // 程序报错时,收集错误信息,反馈到后台做异常捕获
	onPageNotFound() {} // 应用第一次启动,找不到路由

  Page({})
	onLoad() {}
	onShow() {}
	onReady() {}
	onHide() {} // 页面被切换,但没被关闭  ps: 小程序的页面被关闭是被替换掉,没关闭是路由被记录,没被清掉。。。
	onUnload() {} // 路由被关闭
	onPullDownRefresh() {} // 下拉刷新钩子
	onReachBottom() {} // 当有滚动条且触底,一般做上拉加载下一页
	onShareAppMessage() {} // 点击右上角分享
	onPageScroll() {} // 页面滚动钩子
	onResize() {} // 用来 横屏 | 竖屏 切换时触发
	onTabItemTap() {} // 当在 tabBar 上点击当前页面的 tab 时触发

  Component({})
数据请求

如果请求报没有权限

  1. 没有添加 APPID 可能会提示没有权限请求接口
  2. 开发时可以在详情勾选配置里的 不校验合法域名 HTTP …,但是上线后还是不合法的(合法域名必须是 https,i 必须手动添加进后台配置 后台 - 开发 - 开发设置 - 服务器域名 - request 合法域名)
onLoad() {
  wx.request({
    url: '',
    success: res => {
      this.setData({})
    }
  })
}
本地存储

wx.getStorageSync('key')
wx.setStorageSync('key', value)
wx.removeStorageSync('key')

滑动组件 scroll-view

MINA 封装好了滚动组件,我们可以直接用
需要先定高,不然和普通的组件没区别
如果需要重置滚动条距离



路由传参
封装网络请求

request.js

let httpTimes = 0;

export default (options = {}) =>
  new Promise((resolve, reject) => {
    ++httpTimes;
    wx.showLoading({
      mask: true,
      icon: 'loading',
      title: '加载中',
    });
    wx.request({
      ...options,
      success: (res) => resolve(res),
      fail: (error) => reject(error),
      complete: () => !--httpTimes && wx.hideLoading(),
    });
  });
一个完整的列表页 demo
  1. 路由传参获取独特 ID
// father.wxml
`<navigator url="/pages/goods_list/index?id={{_id}}">导航</navigator>`
// 列表页.js
import request from '../../utils/request.js';

Page({
  data: {
    goodsList: [],
    total: 0,
    pagenum: 1,
    pagesize: 10,
    activeIndex: 0,
    cid: '',
  },
  onLoad(options) {
    // 获取 url 参数
    const { cid = '' } = options; 
    this.setData({ cid });
    this.getGoodsData();
  },
  async onPullDownRefresh() {
    this.setData({ pagenum: 1 });
    await this.getGoodsData();
    wx.stopPullDownRefresh(); // 数据加载完毕手动停止导航栏的 loading
  },

  async onReachBottom() {
    // 如果数据加载没了 阻止上拉的接口调用
    let { pagenum, pagesize } = this.data;
    let currentTotal = Number(pagenum) * Number(pagesize);
    if (currentTotal >= this.data.total) {
      return wx.showToast({
        title: '到底了',
        icon: 'none',
      });
    }
    this.setData({ pagenum: ++pagenum });
    await this.getGoodsData();
  },
  
  // methods
  handleTap(e) {
    const { index: activeIndex } = e.currentTarget.dataset; // tap 事件传的 dataset 参
    this.setData({ activeIndex });
  },
  async getGoodsData() {
    const { pagenum, pagesize, cid } = this.data;
    await request({
      url: 'https://api-hmugo-web.itheima.net/api/public/v1/goods/search',
      data: {
        cid,
        pagenum,
        pagesize,
      },
    }).then((res) => {
      let { goods: goodsList, total, pagenum } = res.data.message;
      if (pagenum != 1) { // 如果请求非第一页数据数据,则叠加,否则直接用接口返回的第一个数据就好了
        goodsList = [...this.data.goodsList, ...goodsList];
      }
      this.setData({ goodsList, total, pagenum });
    });
  },
});

h5 拉微信登陆

https://segmentfault.com/a/1190000019025598

微信小程序框架

小程序不再是H5,有自己的框架和标签。API可以调用原生设备接口,服务端由腾讯云提供。小程序内嵌在微信内,但是大小只能有2M,所以开发周期短,项目小。
注意:

  1. 特别的是小程序不在基于HTML+JS,html有自己的标签,但在小程序内叫内置的组件
  2. 小程序组件的属性区分大小写,html 大小写不敏感
  3. 在微信小程序中,图片一般不放在当前开发环境下
  4. 小程序不操作DOM
  5. 变量使用{{}}语法,事件绑定不需要
  6. 云开发有免费服务器
框架
  1. wepy
  2. mpvue(美团)
  3. taro(较少使用)
UI库
  1. WEUI

开发流程
  1. 获取开发者ID
    登录微信公众平台,点击小程序。邮箱登录,微信扫码,进入后台后找到:开发 – 开发者设置 – 开发者ID
  2. 开发工具
    1. 官方工具(难用,可以预览)
      开发版本不太稳定
      预发布版本就是即将发布版本,有新功能较稳定
      优先选择稳定版,但也有很多BUG
    2. VScode(好用,不能预览)
  3. 打开工具,创建小程序项目,填写 AppId ,选一个新的空文件夹
  4. 可以用公司自己的服务器,对于个人也可以进行云开发(小程序提供的服务器)
工具栏
  1. 添加编译模式可以自定义启动页
  2. 通过预览按钮生成二维码来进行真机预览,用微信扫二维码就能在手机上使用。预览只有添加为开发成员扫二维码才管用
  3. 真机调试扫二维码后,在电脑打开调试工具,此时手机上的操作在调试工具里可以看到
  4. 切后台可以监听进入小程序的方式
  5. 上传是发布小程序的时候用
  6. 版本控制是内置的git
  7. 社区打开小程序开发社区web端
  8. 详情中是对小程序具体的配置设置
文件目录
components                  自定义组件
		components1
			xx.wxml xx.js xx.json xx.wxss
pages						pages下每个文件夹就是一个页面
	index					页面--路由。
		index.js  			交互逻辑
		index.json   	 	页面配置文件
		index.wxml  		布局,相当于html
		index.wxss			与css完全一致
	logs					页面--路由
	
utils
	utils.js				日期格式化文件
	
app.js						入口性文件。里边的app相当于new vue根实例
app.json					全局配置
app.wxss					样式重置

project.config.json			整个项目工具配置文件,比如项目名称,APPID
	https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html
sitemap.json				搜索相关
	https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html
APP结构
APP
  pages
    components
      内置组件
        button text swiper 等
      自定义组件
APP 自己有三部分
  app.json			全局配置
  app.js			全局逻辑
  app.wxss			全局样式

pages
  pages.js
  pages.json
  pages.wxml
  pages.wxss
components

app.json

在 app.json 的 pages 数组内写路径,微信开发工具会自动建立对应路径的四个文件,很方便

{
	pages 数组内每一项为页面路径,顺序决定启动页,谁在第一个谁就是启动页
    也可以通过上方工具栏-普通编译-添加编译模式 ,来指定启动页
  "pages": [
  	所有的页面都必须注册,否则不显示
    "pages/index/index",		
    "pages/logs/logs"
  ],
  "window": {
    "navigationBarBackgroundColor": "#f00",    小程序头部背景色(包含状态栏),必须十六进制颜色
    "navigationBarTextStyle": "white",         头部字体颜色 black/white
    "navigationBarTitleText": "小程序",         全局小程序标题
    "backgroundColor": "#eeeeee",              苹果系统下拉的背景色,测试手机只能用16进制颜色,模拟器上可以用单词
    "backgroundTextStyle": "light",
    "enablePullDownRefresh": false             全局下拉刷新
  },
  "tabbar":{
    如果页面没在tabbar
    "selectedColor":"#ff6777",                   标题acitveColor
    "list":[
      "pagePath":"pages/xx/xx",                   路径
      "text":"标题",						      标题
      "iconPath":"assets/tabbar/home.png",	      默认图标
      "selectedIconPath":"assets/tabber/home_acitve/png"    激活图标
    ]
  }
}



.wxss

在微信中,编写 css 不可以用 less 或者 sass 等工具,但在 mpvue 库中可以用


web-wiew:在小程序里嵌入另一个webapp

服务器域名:自动反向代理获取数据,相当跨域
如果要在VScode里编辑代码,需要安装插件wxml
小程序内置git,叫版本管理


生命周期

小程序启动流程

  1. 下载小程序包
  2. 启动小程序
  3. 加载解析app.json
  4. 注册App()
  5. 执行App生命周期函数
  6. 加载自定义组件代码,注册自定义组件
  7. 加载解析page.jison
  8. 注册Page()
  9. 执行Page生命周期
  10. 渲染完毕
注册App( app.js的App()函数 )
  1. 判断小程序进入从场景
  2. 监听生命周期函数,执行业务逻辑
  3. 放置单例共享数据
  4. 全局样式
数据共享
app.js
APP({
  globalData:{
    name:'zhangsan'
  }
})


page.js
const app = getApp()
console.log(app)

onLaunch(opitons){}:小程序初始化完成时
onLaunch只会在2小时内执行一次,或者手动删除小程序使用记录,普通的关闭小程序后在进入只执行 onShow 和 onHide 两个函数

onShow(options){}:小程序显示时

  1. 获取用户信息方式
1.
wx.getUserInfo({
  success:function(res){
  }
})

2.
<button open-type="getUserInfo" bindgetUserInfo="getUserInfo"></button>
getUserInfo(e){
  console.log(e)
}

3.只展示
<open-data type="userNickName"></open-data>
type有很多值,可以在官方文档中查找
https://developers.weixin.qq.com/miniprogram/dev/component/open-data.html

onHide(options){}:小程序隐藏时

onError:小程序产生错误时
捕获错误函数

onPageNotFound:页面不存在时监听

其他:可以添加任意函数或数据变量到 object 参数内,通过 this 访问

注册Page( Page()函数 )
  1. 请求数据
  2. 绑定初始化数据
  3. 事件监听
页面滚动:onPageScroll
到底部:onReachBottom
下拉刷新:onPullDownRefresh

onLoad:页面被加载

数据请求必须配置,否则报不在合法域名列表中。可以在详情-不校验合法域名…临时配置

onLoad(){
  wx.request({
    url:'',
    success:res => {
      if(res.code==1){
        const list = res.data.list
        this.setData({
          list
        })
      }
    }
  })
}

onShow:页面渲染时,关闭打开执行多次

onReady:初次渲染完成时,只执行一次,关闭打开不执行

onHide:页面隐藏时

onUnload:页面跳转时


列表渲染

wx:for="{{ }}"
区别:

  1. 小程序为:而非 -
  2. 绑定数据要用 {{}} 语法,事件监听绑定处理函数不需要{{}}
  3. 不用 item in 变量,而是直接使 item
<view wx:for="{{students}}">
  {{item.name}}{{item.age}}
</view>
事件

事件冒泡绑定:bind*
事件捕获绑定:capture-bind:*
bind 与 catch 的区别:bind 会冒泡,catch 阻止冒泡

  1. 绑定事件不用 {{}} 语法
  2. 处理函数写在 data 同级
  3. 变量为 this.data.xx,而非 vue 中 this.xx
  4. 直接对 data 操作不会更新视图,需要使用 this.setData()
  5. 不能直接在绑定事件的引号内写逻辑,报错

绑定形式

  1. bindtap=“handleChooseImg”
  2. bind:tap=""
  3. catch:tap=""
wxml:
<view>count为{{count}}</view>
<button size="mini" bindtap="handleButtonTap">+</button>

js:
Page({
  data:{ count:0 },
  handleButtonTap(){
    this.setData({
      count:this.data.count + 1
    })
  }
})

组件共有事件类型
10. tap 和 longpress 只会触发一个
11. 轻触时触发:touchstart tap touchend
12. 长按时触发:touchstart longpress touchend

13. touchstart:开始触摸
14. touchmove:手指移动
15. touchcancel:触摸被中断(电话,短信等)
16. touchend:触摸结束
17. tap:轻触
18. longpress:长按 350ms 以上

特有组件特有事件类型

???

事件对象

event:
1. type:事件类型
2. timeStamp:页面打开到触发事件经历的时间戳
3. target:当前组件的一些属性集合
4. currentTarget:当前组件的一些属性集合
5. detail:触发时距离页面顶部坐标
6. touches:所有触摸点的数组
7. changedTouches:所有触摸点的数组

touches:有几个手指在小程序上触摸了就有几个
changedTouches:记录变化
例子: 在单手指 touchend 事件中,手指离开,所以 touches 为0 ,changedTouches为1

target 与 currentTarget 的区别:
target:产生事件的组件
currentTarget:记录触发事件的 view
例子: 事件冒泡时大盒子有区别

事件传参数
小程序的事件传参数不能直接在事件绑定上括号内传入,需要用到 data- 属性。
获取参数需要在 event.target.dataset 对象内获取(currentTarget)

<view 
  bindtap="handleTap"
  data-index="{{index}}"
  data-title="{{item.title}}"
>
  {{item.title}}
</view>

组件

组件共有属性

  1. hidden — 相当于 v-show,通过 display 控制
  2. data-*
  3. bind*
  4. catch*

系统组件:

text
6. selectable
7. space
8. decode
https://developers.weixin.qq.com/miniprogram/dev/component/text.html

text:默认不能长安选中
换行用/n br 不管用
<text selectable></text>
除了文本节点以外的其他节点都无法长按选中

button

hover-class:当手指按下时
<button hover-class="btn_click"></botton>

open-type:

view

image

  1. image 单双标签都可以,inline-block 可以设置宽高
  2. 默认有自己大小 320x240
  3. 绝对路径/相对路径/网络地址
bindload:图片加载完成事件,配合懒加载
lazy-load:上下三屏懒加载
show-menu-by-longpress:长按图片菜单,识别小程序

<img 
  wx:for="imgArr"
  lazy-load
  bindload="handleImgLoad"
/>

input

  1. type:弹出键盘类型( text number idcard digit )
  2. password:暗文
  3. 事件监听
wxml:
<input 
  bindinput="handleInput"
/>
js:
handleInput(e){
  console.log(e.detail.value)
}

scroll-view

  1. scroll-view 中的需要滑动的元素不可以用 float 浮动
  2. scroll-view 中的包裹需要滑动的元素的大盒子用 display:flex; 没用
  3. scroll-view 中的需要滑动的元素要用 dislay:inline-block进行元素的横向编排
  4. 包裹 scroll-view 的大盒子有明确的宽和加上样式–> overflow:hidden;white-space:nowrap;
滚动到最左/顶触发
bindscrolltoupper(){}
upper-threshold:距左/顶多少距离时触发scrolltoupper

滚动到最右/底部触发
bindscrolltolower(){}
lower-threshold:距右/底多少距离时触发scrolltoupper

自定义组件:

小程序起初不支持组件化,在1.6.3版本后支持。组件化要我们将页面拆分成一个个小的,可复用的组件,这样我们的代码更加方便管理和维护,拓展性更强,所以组件化不管在哪里都是非常重要的概念
通常在根目录下创建 components 文件夹,存放公共组件

组件名只能是小写字母,下划线和中划线,不能用 wx-*。
组件的 class 与页面的样式不会造成影响,自定义组件不能用 id/属性/标签选择器

components.js

Component({
  options:{
    sttyleIsolation:"apply-shared"
  }
})
  1. 编写组件
    需要在自定义组件的 json 中将 component 设置为 true
component.json
{
  "component": true
}
  1. 注册自定义组件
1. 全局注册 app.json
{
  "usingComponents": {
    "标签名" : "绝对/相对",
    "标签名" : "路径"
  }
}



2. 单页面内注册 index.json:
{
  "navigationBarTitleText": "首页",
  "usingComponents": {
    "标签名" : "绝对/相对",
    "标签名" : "路径"
  }
}
  1. 使用
<my-com/>

组件通讯

                         页面
数据:properties  样式:externalClasses  标签:slot  自定义事件
                         组件

父子通讯与 vue 类似

Page.wxml:
<my-com title="自定义标题"/>

component.js

properties: {
  与 vue 类似,这两种都可以
  title:String,
  title: {
    type: String,
    value: '默认值',
    observer: function (oldValue, newValue) {    // 观察者
      console.log(oldValue, newValue)
    }
  }
}

自定义模板

以前小程序不支持自定义组件,为了代码复用,用 template 很多,现在小程序支持自定义组件,template 用的比较少了。

  1. 定义模板使用 name 属性
  2. 模板在没有使用时不会被渲染
  3. 通过 is 属性使用模板
  4. 通过 data 属性传入数据
单 wxml 内:
<template name="myTemplate">
  <view>{{viewData}}</view>
  <text>{{textData}}</text>
  code... 
</template>

<template is="myTemplate" data="{{ viewData:'viewData' , key:value }}" />

wxml 模板文件封装 -- template.wxml
login.wxlm:
<import scr="绝对/相对路径" />
引用

https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/import.html
WXML 提供两种文件引用方式 import 和 include

  1. import 主要导入 template,不可以递归导入
  2. include 引入除了 <template/> <wxs/>外的所有代码
wxs脚本

wxml 中不能直接调用函数(除了事件绑定),但有些时候需要函数去处理一些数据,类似 vue 的 filter。在小程序中只能用 wxs

  1. wxs 不支持 ES6
  2. 使用 module 属性,模块化导出
  3. 只能用相对路径,不能用绝对路径
  4. wxs 的运行环境与正常 js 隔离,不能调用其他 js 文件中的函数,也不能调用小程序的 API
  5. 不可作为事件回调
  6. 在 IOS 设备上 wxs 运行速度比 js 快2-20 倍,在安卓上无差别

wxs 可以在 wxml 中直接书写

<wxs module="info">
  var name = '张三',age = 18;
  function showInfo() {
    return name + ':' + age
  }
  module.exports = {
    showInfo: showInfo
  }
</wxs>

<view>
  {{info.showInfo()}}
</view>

也可以封装为单独的 wxs 文件

info.wxs:
var name = 'zhangsna'
module.exports = {
  name:name
}

wxml:
<wxs src="../../wxs/info.wxs" module="info" />  不能用绝对路径
<view>{{info.name}}</view>

rpx
block

block不是组件,只是一个不被渲染的标签,它只接受控制类型的属性,有两种好处

  1. 将需要渲染或判断的内容包裹起来
  2. 将属性放在block标签,提高代码可读性
  3. 不被渲染,性能更好
<block wx:if={{isActive}}">
  <button>xx</button>
  <view>xxx</view>
</block>

指令
  1. wx:if wx:elif wx:else
<text wx:if="{{score>=90">优秀</text>
<text wx:elif="{{score>=60}}">及格</text>
<text wx:else>不及格></text>
  1. wx:for
    需要设置 key
    与 vue 不同的是,item 和 index 默认,不用写在{{}}内,如果多层便利可以自定义名字。但是二层直接用item就可以渲染出来了
index,item:
<view wx:for="{{['a','b','c']}}"> {{item , index}} </view>

多层循环:
<wx:for="list">
  <wx:for="item">{{item{}}</>
</>

或者更改名字
wx:for="list" wx:for-item="v" wx:for-index="i"

mpvue(vue语法)

在小程序内 所有 DOM/BOM 都不能操作,所以在vue中使用第三方实例化会不起作用。因此只能使用小程序自己的swiper

Wepy(react语法)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值