微信小程序相关问题整理(一)

文章目录


本文是在工作中开发小程序遇到的问题或者经验的一个总结,目的在于梳理清楚一些功能细节,加深对流程上的理解;

1、引入 vant-weapp UI

1、打开小程序项目的终端,执行 mpn init,初始化package.json 文件;
2、执行 npm i @vant/weapp -S ,安装 vant-weapp 组件库,成功之后会生成一个 node_module 依赖包;
3、在微信开发者工具上,左上角“工具” 点击 “构建npm” ,成功之后会生成 miniprogram_npm文件;
4、将 app.json 文件的 “style”:"V2" 去掉,因为小程序新版基础组件强行加了许多样式,难以 覆盖;
5、修改 project.config.json 文件:setting 对象下面
在这里插入图片描述
6、使用:
全局引入:在 app.json 中配置

"usingComponents": {
    "van-button":"@vant/weapp/button/index"
  },

局部引入:在页面对应的 json 文件中配置(同上)

2、配置小程序 tabBar

在 app.json 中配置

"tabBar": {
    "color": "#999999",
    "selectedColor": "#0071D0",
    "backgroundColor": "#ffffff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "",
        "selectedIconPath": ""
      },
      {
        "pagePath": "pages/me/index",
        "text": "我的",
        "iconPath": "",
        "selectedIconPath": ""
      }
    ]
  },

3、小程序登录授权流程

1、判断是否存在 token,存在则校验 token 有效性,有效则继续,无效则清空 token;
2、token 失效或者不存在则走登录流程,首先调用 wx:openSetting (打开设置页面授权)判断用户是否已授权,未授权则让用户授权;
3、已授权则调用 wx.login 获取登录凭证 code,将 code 发送给服务器,获取 openid、session_key 等信息,存储在本地;
4、用户点击按钮(根据需要设置open-type)触发事件,获取 iv、encryptedData,可以校验一下 code 的时效性;
5、调取服务器接口,拿到 token 缓存起来;

// 定义 login.js 文件
//获取到openid, session_key等信息
const login = () => {
  return new Promise((resolve, reject) => {
    // 调用微信登录接口获取用户临时登录凭证 code
    wx.login({
      success: (res) => {
        if (res.code) {
          // 登录成功,将 code 发送到开发者服务器
          wx.request({
            url: 'https://example.com/login',
            data: {
              code: res.code
            },
            success: (res) => {
              // 获取用户唯一标识 OpenID 和会话密钥 session_key
              const { openid, session_key } = res.data;
              // 将用户信息存储到缓存中,生成自定义登录态
              wx.setStorageSync('openid', openid);
              wx.setStorageSync('session_key', session_key);
              resolve();
            },
            fail: (err) => {
              reject(err);
            }
          });
        } else {
          reject(res.errMsg);
        }
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};
// 判断用户是否已授权
const checkAuth = () => {
  return new Promise((resolve, reject) => {
    // 获取用户当前设置状态
    wx.getSetting({
      success: (res) => {
        // 判断用户是否已授权
        if (res.authSetting['scope.userInfo']) {
          resolve();
        } else {
           // 用户未授权,引导用户打开设置页面进行授权
		    wx.showModal({
		      title: '提示',
		      content: '检测到您未授权登录,请先进行授权。',
		      showCancel: false,
		      success: (res) => {
		        if (res.confirm) {
		          wx.openSetting({
		            success: (res) => {
		              if (res.authSetting['scope.userInfo']) {
		                // 用户已授权,可进行相关操作
		                resolve();
		              }
		            }
		          });
		        }
		      }
		    });
        }
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};
// 在需要登录的页面调用 login() 方法进行登录授权和判断用户是否已授权
onLoad(){
	login();
}
//下面是用户手动触发的getPhoneNumber方法
getPhoneNumber(e){
	//这里可以使用 wx.checkSession 校验一下code 的有效性
	//e 里面可以拿到iv、encryptedData;发送给服务器获取到登录token
}

我这里上服务器获取用户的信息之后用过接口返回;也可以用个wx.getUserInfo 来获取用户信息;

注意:在调用 wx.openSetting() 方法时,需要在小程序的 app.json 文件中添加相应的权限设置。例如,如果要使用用户信息组件,需要在 app.json 中添加如下代码:

"permission": {
  "scope.userLocation": {
    "desc": "获取你的地理位置信息"
  },
  "scope.userInfo": {
    "desc": "获取你的个人信息"
  }
}

4、小程序支付(微信支付)

资质:

1、小程序已完成实名认证:需要提交个人或企业相关资料进行审核,并通过后才能开通支付功能;
2、小程序已开通微信支付:小程序管理后台中,需要先开通微信支付功能,配置相关参数和密钥;
3、绑定微信商户号:必须关联商户号才能开通支付功能;

流程:

1、需要在微信支付商户平台中注册商户账号,并进行相关的资质认证和审核。通过审核后,可以获得微信支付的商户号、API密钥和证书等信息;
2、在小程序中申请微信支付功能,绑定商户号;
3、使用 wx.request 方法调用统一下单接口,获取预付单信息,将获取到的信息传递给 wx.requestPayment 方法发起支付请求;

wx.requestPayment({
  timeStamp: payParams.timeStamp,
  nonceStr: payParams.nonceStr,
  package: payParams.package,
  signType: payParams.signType,
  paySign: payParams.paySign,
  success(res) {
    console.log('支付成功', res);
  },
  fail(err) {
    console.log('支付失败', err);
  }
});

5、小程序分享

微信小程序提供了 wx.showShareMenu API,可用于开启小程序页面的分享功能。开启分享功能后,用户可以将小程序页面分享给其他用户或群聊。

1、在小程序页面中调用 wx.showShareMenu 方法,开启分享功能。可以在 onReady 生命周期函数中调用该方法。

onReady: function() {
  wx.showShareMenu({
    withShareTicket: true,
    success: (res) => {
      // 分享功能开启成功
    }
  });
},

2、在小程序页面中定义一个自定义按钮,用于触发分享操作。在该按钮的 bindtap 属性中绑定 onShare 方法。

<button type="primary" open-type="share">分享</button>

3、在小程序页面中添加分享统计代码。可以在 onShareAppMessage 生命周期函数中调用后台接口,记录分享事件并上传数据。

onShareAppMessage: function(res) {
  const { from, target, webViewUrl } = res;
  // 调用后台接口记录分享事件
  // ...
  return {
    title: '分享标题',
    path: '/pages/index/index',
    imageUrl: '/images/share.png'
  };
},

第1和第4都可以开启右上角的分享,

6、小程序打开内置地图(腾讯地图)

uni.openLocation({
 	latitude: 31.230416, // 上海的纬度
  	longitude: 121.473701, // 上海的经度
  	name: '上海市', // 地点名称
  	address: '中国上海市黄浦区人民广场', // 地址的详细说明
  	scale: 18, // 缩放比例
  	success: function(res) {
    console.log('打开地图成功');
  },
  fail: function(err) {
    console.log('打开地图失败', err);
  }
});

在地图上显示指定位置的标记点,并且支持调用内置地图进行导航;打开地图后点击右下角导航图标会弹出选项弹窗,让用户选择使用哪一个地图应用程序进行导航;

7、小程序打开app

根据官方说明不能由小程序跳转至任意app,只能跳回app,也就是只能从app跳至小程序,再由小程序跳回app;小程序返回 app 需要用户主动触发,所以不由 API 来调用,需要用 open-type 的值设置为 launchApp 的 button 组件的点击来触发;

如果需要在打开 APP 时向 APP 传递参数,可以设置 app-parameter 为要传递的参数。通过 binderror 可以监听打开 APP 的错误事件。

<button open-type="launchApp" app-parameter="wechat" binderror="launchAppError">打开APP</button>
Page({
  launchAppError (e) {
    console.log(e.detail.errMsg)
  }
})

参考:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/launchApp.html

注意:
1、小程序也不能打开手机默认浏览器;

8、小程序打开其他小程序

需要用户点击、需要确认跳转才可以打开另一个已发布的小程序,写文章时小程序之间跳转没有任何限制,可以随意跳转;

wx.navigateToMiniProgram({
  appId: 'wx1234567890', // 要打开的小程序的appId
  path: '/pages/index', // 打开的页面路径,如果为空则打开首页
  extraData: {
    foo: 'bar' // 需要传递给目标小程序的数据,格式为键值对
  },
  success(res) {
    console.log('打开小程序成功', res);
  },
  fail(err) {
    console.log('打开小程序失败', err);
  }
});

需要保证用户已经授权并登录了目标小程序,否则会导致跳转失败;路径传参可以在打开的页面中 onLoad 生命周期通过 options 获取;extraData 通过小程序的 app.js 通过 onLaunch、onShow 参数 e.referrerInfo 获取;

如果想返回之前小程序可以使用:wx.navigateBackMiniProgram 只有在当前小程序被其他小程序打开的时候才可以调用成功;

9、小程序获取当前地理位置

首先需要在 app.json 里面配置获取位置权限;(uni-app 需要在manifest.json 里面的 mp-weixin 里面设置)

"permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序内的定位服务"
    }
  }

请求授权和获取位置信息都是异步操作,需要在回调函数中处理结果。另外,如果用户拒绝授权或未授权时,会导致 wx.getLocation 方法失败;

// 请求授权
wx.authorize({
  scope: 'scope.userLocation',
  success: function() {
    // 授权成功,获取位置信息
    wx.getLocation({
      type: 'wgs84', // 坐标类型,默认为wgs84,可选gcj02
      success: function(res) {
        const latitude = res.latitude; // 纬度
        const longitude = res.longitude; // 经度
        const speed = res.speed; // 速度,单位m/s
        const accuracy = res.accuracy; // 位置精度
        console.log('获取位置信息成功', res);
      },
      fail: function(err) {
        console.log('获取位置信息失败', err);
      }
    });
  },
  fail: function() {
    // 授权失败,提示用户开启权限
    wx.showModal({
      title: '未授权地理位置信息',
      content: '请在设置中打开地理位置权限',
      showCancel: false,
      confirmText: '确定'
    });
  }
});
额外:选择地址

在小程序管理后台,「开发」-「开发管理」-「接口设置」中自助开通该接口权限;

wx.chooseAddress({
      success(res){
        console.log(res)
      },
      fail(err){
        console.log(err)
      }
    })

可以调起用户编辑收货地址原生界面;可以选择已有地址,也可以添加新地址;

10、自动检测版本更新

在 app.js 中

App({
  onLaunch() {
    this.autoUpdate();
  },
  globalData: {
    userInfo: null
  },
  //检测版本更新
  autoUpdate(){
    //检测当前设备是否可用getUpdateManager api
    if(wx.canIUse('getUpdateManager')){
      const updateManasger = wx.getUpdateManager();
      //检测是否有版本更新
      updateManasger.onCheckForUpdate(function(res){
        if(res.hasUpdate){
          wx.showModal({
            title: '更新提示',
            content: '需要重启小程序完成更新',
            success: (res) => {
              if (res.confirm) {
                //更新成功,强制重启小程序并使用新版本
                updateManasger.onUpdateReady(function(){
                  updateManasger.applyUpdate();
                })
                //更新失败,提示用户删除小程序重新进入
                updateManasger.onUpdateFailed(function(){
                  wx.showModal({
                    title: '更新提示',
                    content: '小程序更新失败,请您删除当前小程序,重新搜索打开!'
                  })
                })
              }
            }
          })
        }
      })
    }
  }
})

11、企业微信中配置小程序

1、登录企业微信后台:应用与小程序-小程序-关联小程序;或者登陆小程序后台:设置-关联设置-关联的企业微信;
2、关联过程中需要小程序管理员授权;
3、对小程序进行可见范围设置;这样企业微信成员就可以在工作台看到小程序了;

12、扫码、拍照、相册

要在小程序中实现扫码、拍照和相册功能,可以使用微信提供的 API 接口。以下是具体实现方法:
1、扫码
需要获取摄像头权限。可以通过 wx.authorize 接口请求用户授权,或者在 app.json 文件中设置 "permission": {"scope.camera": {"desc": "用于扫码"}}

scanCode: function() {
    wx.scanCode({
      onlyFromCamera: true,
      success(res) {
        console.log('扫码成功', res.result)
      },
      fail(res) {
        console.log('扫码失败', res)
      }
    })
  }

2、拍照
同样需要获取摄像头权限;

wx.chooseMedia({
      count: 1,
      sizeType: ['original'],
      sourceType: ['camera'],
      success(res) {
        console.log('选择照片成功', res.tempFilePaths)
      },
      fail(res) {
        console.log('选择照片失败', res)
      }
    })

3、相册
需要获取相册访问权限。可以通过 wx.authorize 接口请求用户授权,或者在 app.json 文件中设置 "permission": {"scope.writePhotosAlbum": {"desc": "用于保存图片到相册"}}

wx.chooseMedia({
      count: 1,
      sizeType: ['original'],
      sourceType: ['album'],
      success(res) {
        console.log('选择照片成功', res.tempFilePaths)
      },
      fail(res) {
        console.log('选择照片失败', res)
      }
    })

13、配置客服

移步到:微信小程序接入客服功能

14、api 封装

跟目录下新建文件夹 api ;
1、新建 http.js :定义 baseUrl 方便后期维护;
2、新建 request.js :

import { baseUrl } from "./http"
import storage from "../utils/storage";
module.exports={
  request:function(url, method, data){
    let fullUrl = `${baseUrl}${url}`;
    let token = storage.get('token');
    wx.showLoading({ title: '数据请求中' });
    return new Promise((resolve,reject)=>{
      wx.request({
        url: fullUrl,
        method,
        data,
        header:{
          'content-type': 'application/json', 
          'Authorization': 'Bearer' + token
        },
        success(res){
          wx.hideLoading();
          if(res.data.status == 200){
            resolve(res.data);
          }else if(res.data.status == 401){
            wx.showToast({
              title: '登录失效',
              icon: "none"
            })
            wx.reLaunch({
              url: '/pages/login/index',
            })
          }else{
            wx.showToast({
              title: res.data.message,
              icon: "none"
            })
            reject(res.data.message);
          }
        },
        fail(err){
          wx.reLaunch({
            url: '/pages/login/index',
          })
          wx.hideLoading();
          wx.showToast({
            title: err.errMsg,
            icon: "none"
          })
          reject(err);
        }
      })
    })
  }
}

3、新建模块 api 文件 (user_api)

import { request } from "./request"
module.exports = {
  user: (data) => request("user","get",data)
}

4、页面调用

const user_api = require("../../api/user_api");
user_api.user(data).then(res=>{
      console.log(res)
})

15、本地存储封装

在 utils 文件夹下新建 storage.js 文件

/**
 - 同步新增:get(key)
 - 同步修改、删除:set(key,value);只传递 key 就可以删除对应的key:value
 - 同步清空:clear()
*/
export default class storage {
  //同步获取
  static get(key){
    if(!key) return null;
    return wx.getStorageSync(key);
  }
  //同步存储、删除
  static set(key, value){
    if(!key) return;
    if(value == null || value == 'undefined'){
      return wx.removeStorageSync(key);
    }else{
      return wx.setStorageSync(key, value);
    }
  }
  //同步清空
  static clear(){
    return wx.clearStorageSync();
  }
}

16、底部安全距离组件

封装一个全局组件 safe,借助 env(safe-area-inset-botton)

<!--全局组件:底部安全距离-->
<view style="padding-bottom: env(safe-area-inset-botton);">
  <solt></solt>
</view>

17、表单校验工具封装

封装校验工具类

// 表单校验工具
export class validator {
  constructor(rules){
    this.rules = rules
    this.error= []
  }
  
  validate(form){
    this.error = [];
    Object.keys(form).forEach(key=>{
      let rule = this.rules[key];
      let required = this.rules[key].required;
      let message = this.rules[key].message;
      let maxLength = this.rules[key].maxLength;
      let fn = this.rules[key].fn;
      if(rule){
        if(required && !this.checkValue(form[key]) && !Array.isArray(form[key])){
          return this.error.push(message || ('请填写'+key));
        }
        //校验key是数组
        if(required && this.checkValueArr(form[key]).isNull && Array.isArray(form[key])){
          return this.error.push(message || (`请完善${this.checkValueArr(form[key]).key}信息`));
        }
        if(maxLength && this.max(form[key], maxLength)){
          return this.error.push(`最多可输入${maxLength}个字!`);
        }
      }
    })
    return this.error;
  }
  checkValueArr(arr){
    let isNull = false;
    let key = null;
    for(let i in arr){
      if(!arr[i].value){
        isNull = true;
        key = arr[i].name;
        break;
      }
    }
    return {isNull,key}
  }
  checkValue(value){
    return value.toString().length > 0;
  }
  max(value, maxLength){
    return value && value.length > maxLength;
  }
}

页面使用

let rules = {
  test:{
    required:true,
    message:"请输入用户名"
  }
}
let fromVal = new validator(rules);

利用 class 类,在new时将规则传入,在提交校验时执行下面操作,将form数据传入进行校验;

let err = fromVal.validate(this.data.form);

上面是我根据前人的思路根据自己的项目封装的一个方法,

18、富文本编辑器 editor

editor 是小程序提供的一个组件,搭配 EditorContext api 来实现工具栏的操作;

<!--富文本编辑器-->
<view style="margin: 20rpx; background: #f0f0f0;">
  <view style="display: flex; justify-content: space-around; padding-bottom: 20rpx;">
    <view class="item" bindtap="format" data-type="bold">加粗</view>
    <view class="item" bindtap="format" data-type="italic">斜体</view>
    <view class="item" bindtap="format" data-type="underline">下划线</view>
    <view class="item" bindtap="undo">撤回</view>
    <view class="item" bindtap="redo">恢复</view>
    <view class="item" bindtap="insertImg">插入图片</view>
    <view class="item" bindtap="clear">删除</view>
  </view>
  <editor 
  style="border-radius: 10rpx; border: 1rpx solid #ccc; background: #fff;"
  id="myEditor"
  placeholder="请输入"
  bindstatuschange="statusChange"
  bindready="onEditorReady"
  ></editor>
</view>
<van-button bindtap="submit">提交</van-button>
Component({
  properties: {

  },
  data: {
    editorCtx:''
  },
  methods: {
    // 初始化,获取到编辑器节点,缓存在data中,方便下面操作
    onEditorReady(){
      let that = this;
      let query = wx.createSelectorQuery();
      query.in(that).select("#myEditor").context();
      query.exec(res=>{
        that.editorCtx = res[0].context;
      })
    },
    undo(){
      console.log(1)
      this.editorCtx.undo();
    },
    redo(){
      this.editorCtx.redo()
    },
    insertImg(){
      let that = this;
      wx.chooseMedia({
        count:1,
        mediaType:["image"],
        sourceType:["album"],
        success(res){
          that.editorCtx.insertImage({
            src:res.tempFiles[0].tempFilePath
          })
        }
      })
    },
    clear(){
      this.editorCtx.clear()
    },
    submit(){
      this.editorCtx.getContents({
        success(res){
          console.log(res)
        }
      })
    },
    format(e){
      let type = e.currentTarget.dataset.type;
      this.editorCtx.format(`${type}`)
    }
  }
})

19、小程序如何操作前一页的内容

back(){
    const pages = getCurrentPages();
    const prePage = pages[pages.length-2];
    console.log(prePage)
    prePage.setData({
      msg:'sssss'
    })
    wx.navigateBack();
  }

想让上一页监听到,可以在后面调用一下前一页的方法;

其他

1、小程序在 IOS 部分机型循环列表的第33个元素上边框消失

1rpx 在IOS部分机型渲染像素点比较低,出现偶尔不显示的情况。可以设置为 1px 然后对边框进行缩放;

2、使用 flex:1进行多行整体布局的时候,页面有点乱

尽量避免多行整体布局使用 flex:1;因为这个1是针对剩余空间进行分配,而不是父元素的宽度;

3、小程序修改对象属性
let c = `arr[${i}].name`
this.setData({
	"obj.a":b,
	[c]:d
})
4、小程序阻止冒泡

用 catchtap 来绑定事件;

5、小程序生命周期顺序

应用:onLaunch onShow
页面:onLoad onShow onReady
组件:attached() -> ready() -> moved() -> detached()

6、小程序动态绑定
class="name {{isOk?'a':'b'}}"
style="height:{{nav}}rpx"
7、获取小程序 dom 节点信息:

页面:

var query = wx.createSelectorQuery();
query.select('.header').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec((res) => {
	var listHeight = res[0].height; // 获取list高度
	this.setData({
		height:listHeight
	})
})

组件:

var query = wx.createSelectorQuery().in(this);
query.select('.header').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec((res) => {
	var listHeight = res[0].height; // 获取list高度
	this.setData({
		height:listHeight
	})
})

方法放到 ready 周期里面,如果还获取不到,在用定时器包裹;

8、小程序模拟器上自定义组件显示没问题,真机上不显示

组件json中添加 “component”: true

9、初次扫码打开小程序接口数据都是空白,打开调试模式就可以正常显示

只需要在小程序后台,将服务端域名配置成合法域名

10、在使用扫码链接打开小程序时,配置的测试帐号,必须与当前请求的地址、参数、参数值完全一致,不然可能会扫码到404;
11、canvas优先级过高,覆盖其他标签问题

1、使用uni-app提供的 cover-view、cover-image等标签;
2、给canvas标签一个判断控制显示隐藏;

12、小程序自定义头部

在配置里面设置 navigationStyle:custom ,原生和uni-app 有区别;获取设备头部信息:uni.getSystemInfo({}),获取状态栏高度,导航栏高度;

13、小程序生命周期和跳转

1、跳转方式区别

navigateTo:跳转到下一页,保留当前页;
navigateBack:关闭当前页面,跳转到前一页;
redirectTo:关闭当前页面,跳转到其他页面;
reLaunch:关闭所有页面,跳转到某个页面;
switchTab:关闭所有非 tabBar 页面,跳转到 tabBar 页面;路径不可携带参数;

js 中使用方法 getCurrentPages() 可以获取当前页面战;

2、生命周期

onLaunch:小程序初始化完成触发,只触发一次;(获取用户信息)
onShow:小程序启动、后台进入前台触发;
onHide:小程序从前台进入后台触发;
onError:收集错误信息;
onPageNotFound:入口页面找不到时,通过它抱错,处理;

onLoad:页面初始化触发,一个页面只调用一次;(发送请求,初始化数据)
onShow:页面显示、后台进入前台触发;
onReady:页面初始化完成触发,只调用一次;
onHide:切入后台触发;
onUnload:页面卸载触发;
onPullDownRefresh:页面下拉刷新触发
onReachButton:

3、二者关系

navigateTo:onHide - onLoad - onShow - onReady
navigateBack:onUnload - onShow
redirectTo:onUnload - onLoad - onShow - onReady

14、小程序生成访问二维码

小程序后台,右上角-工具;输入扫码之后进入的页面路径;

15、微信开发者工具模拟企业微信

下载企业微信插件(设置-扩展设置-模拟器插件-企业微信模拟器),然后将小程序模式调整为企业微信小程序模式;

16、小程序使用van-field点击之后输入框会先失去焦点再次点击才会聚焦,开发工具上是正常的

使用的是 van-field 组件,可以检查组件的配置参数是否正确,例如 auto-focus 或 focus 参数是否设置为 true

17、ios跳转失败问题

页面跳转,ios偶尔会跳转失败,navigateTo成功和完成的回调都能打印出来;具体原因不明,在网络请求拿到结果后跳转时延迟1秒就可以降低失败概率,但是依然会出现跳转失败的情况;
猜测可能是焦点异步有关系;
http://shop.jisuapp.cn/pecial/a79256.html

18、vscode 开发微信小程序

安装插件 “微信小程序插件” ,用微信开发者工具创建小程序项目,然后用 vscode 打开;就可以开发代码了;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值