ionic热更新的实现以及引导用户下载新版应用功能实现和注意事项

ionic2热更新插件cordova-hot-code-push-plugin更新下载文件 说下需求:在主项目中根据需要下载子项目(或文件),并子项目能利用主项目中的cordova-hot-code-push-plugin的跟随主项目更新。一个是包含全部子项目的页面,一个是选择下载子项目后的页面,都是iOS原生。 一、必要条件 1、首先要搭建个本地服务器,可参考http://blog.youkuaiyun.com/liuyuyefz/article/details/8072485,简单快捷。 阅读详情

在app上线的第一个版本中,就需要加入热更新和外壳更新功能,这样才能尽可能避免损失用户,热更新无需新的apk,直接更新web项目,而外壳更新,通过给用户友好提示,引导用户下载最新版本。两者结合其实是一个功能,两者缺一不可。
建议通读全文之后,再进行实践。

热更新实现步骤

  • 全局安装cordova-hot-code-push-cli插件:$npm install -g cordova-hot-code-push-cli

  • 在项目根目录下安装cordova-hot-code-push-plugin插件:$cordova plugin add cordova-hot-code-push-plugin

  • 在项目根目录下执行 $cordova-hcp server , 然后ctrl / command + z 结束服务器,我们不使用它的服务器来测试。只为了使用它生成的一个配置文件,名为:.chcpenv

  • 好的,我们现在可以开启一个本地服务器,随便找一个地方新建一个文件夹,然后在文件夹根目录下执行 $hs -o -p 1111, (备注,hs命令是全局安装了http-server之后才能使用,$npm i -g http-server), 这样我们可以通过访问 http://127.0.0.1:1111 来访问我们这个本地服务器了。我们在目录中建立几个文件夹目录结构如下:

    /update/hot/app1
    /update/hot/app2
    /update/apks

    说明,app1是你的一个项目用于放置热更新代码的文件夹,而app2则是另一个项目的文件夹,这里假设我们有很多个移动应用项目,而apks文件夹则用于放置我们所有应用的新版apk。(备注:这里的app1和下面用到的app1.apk都只做示例。)

  • 查看电脑ip地址,mac上使用ifconfig命令, windows上使用ipconfig命令,找到我们的电脑ip地址,比如是192.168.1.102, 此处 192.168.1.102 和 127.0.0.1是等同的,但为了手机可访问必须使用192的地址。(手机和电脑需保持在同一个网段上)

  • 开始修改上面的.chcpenv文件,编辑如下:

    {
      "content_url": "http://192.168.1.102:1111/update/hot/app1",
      "config_url": "http://192.168.1.102:1111/update/hot/app1/chcp.json"
    }
    

    说明: 其中app1文件夹里的代码就是你将要放置的热更新后的代码,也就是我们本地项目中的www目录的代码,同样的,www目录中存在chcp.json文件,这个我们即将说道。

  • 我们在项目中新建一个cordova-hcp.json文件,这个文件是生成配置文件的模板,编辑如下:

    {
        "autogenerated": true,
        "content_url": "http://192.168.1.102:1111/update/hot/app1",
        "min_native_interface": 1,
        "update": "now",
        "ios_identifier": ""
    }
    
  • 在项目根目录执行$cordova-hcp build, 这样就会按照上一步的cordova-hcp.json文件为模板在www目录中生成两个文件,chcp.json 和 chcp.manifest 两个文件。chcp.json 文件内容如下:

    {
      "autogenerated": true,
      "content_url": "http://192.168.1.102:1111/update/hot/app1",
      "min_native_interface": 1,
      "update": "now",
      "ios_identifier": "",
      "release": "2017.07.10-22.18.08"
    }

    chcp.json 文件就是按照上面cordova-hcp.json模板生成的。
    里面的字段分别都有意义的,我们在这里做下说明:
    ①. autogenerated 表示是否自动生成的配置
    ②. content_url 表示我们线上热更代码的文件目录
    ③. min_native_interface 表示目前外壳的最小版本号
    ④. update有三个值,分别是 :

    start - app启动时安装更新,默认值
    resume - app从后台切换过来的时候安装更新
    now - web内容下载完毕即安装更新

    ⑤. ios_identifier 则是ios在appStore的应用标识
    ⑥. release 则是build时的时间戳,用于对比更新时间

    而chcp.manifest文件则是www项目中的每一个文件打下一个hash印记,如下:

    [
      {
        "file": "css/style.css",
        "hash": "84a33cf00d95bc1e3617cc35262f7e94"
      },
      {
        "file": "img/adam.jpg",
        "hash": "b445da3cc203f97bce534fdad93b3931"
      },
      ...
    ]

    用以区分改动的文件,通过对比不同hash值而拉下不同的文件,用以热更新。

  • 修改项目根目录中的config.xml文件,添加如下代码:

    <chcp>
     <native-interface version="1" />
     <config-file url="http://192.168.1.102:1111/update/hot/app1/chcp.json" />
    </chcp>
    
  • 目前配置已经完成,开始打包一个新的apk安装到手机,然后修改我们的源码文件,用于标识改动,再次重新运行命令$cordova-hcp build, 将新生成的www目录中的代码放到http://192.168.1.102:1111/update/hot/app1,
    我们退出应用,重新打开后,就会出现闪一下的效果,然后就会看到修改后的代码。这就是热更新。(备注:确保手机和电脑在同一网段访问,使用192.168.1.102只是先作为测试使用)

外壳更新实现步骤

  • 为什么要进行外壳更新,比如我们新功能需要加入一些新的cordova插件,而这些插件是内置于apk中的,并非基于www目录,不能进行热更新,新的基于cordova插件的应用逻辑不能被用于旧的apk上,如果强制更新,则会
    出现严重的错误,所以我们需要引导用户下载新的apk或引导用户进入appStore更新应用。

  • 安装必要的cordova插件,如下:

    • $cordova plugin add cordova-hot-code-push-plugin
    • $cordova plugin add cordova-plugin-file-opener2
    • $cordova plugin add cordova-plugin-file-transfer
    • 备注:cordova-plugin-file-transfer安装的过程中会同时安装cordova-plugin-file插件
  • 原理说明:通过min_native_interface属性的设置,如果插件检查发现用户安装的外壳app版本比服务端新的web内容要求的版本要低,就会触发错误事件,错误码:chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW 通过这个错误码可通过弹窗提示用户去升级,跳转到AppStore或下载新安装包

  • 编写引导用户更新,下载逻辑,作为一项服务,包含引导提示界面,和下载功能。

      .factory('setHotPush', [
          '$ionicPopup',
          '$ionicLoading',
          '$timeout',
          'setUtils',
          function($ionicPopup, $ionicLoading, $timeout, setUtils) {
              var AppUpdate = function() {};
              AppUpdate.prototype = {
                  // Application Constructor
                  initialize: function() {
                      this.bindEvents();
                  },
                  // Bind any events that are required.
                  // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
                  bindEvents: function() {
                      document.addEventListener('chcp_updateLoadFailed', this.onUpdateLoadError, false);
                  },
                  onUpdateLoadError: function(eventData) {
                      var error = eventData.detail.error;
                      // 当检测出内核版本过小
                      if (error && error.code === chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW) {
                          var dialogMessage = '有新的版本,请下载更新!';
                          // iOS端 直接弹窗提示升级,点击ok后自动跳转
                          if (ionic.Platform.isIOS()) {
                              chcp.requestApplicationUpdate(dialogMessage, this.userWentToStoreCallback, this.userDeclinedRedirectCallback);
                              // Android端 提示升级下载最新APK文件
                          } else if (ionic.Platform.isAndroid()) {
                              var confirmPopup = $ionicPopup.confirm({
                                  title: '版本更新',
                                  template: '<span class="updatePopup">' + dialogMessage + '</span>',
                                  cssClass: 'popup',
                                  cancelText: '取消',
                                  okText: '<span class="ionicPopupUpdateBtn">' + '升级' + '</span>'
                              });
                              confirmPopup.then(function(res) {
                                  if (res) {
                                      $ionicLoading.show({
                                          template: "已经下载:0%"
                                      });
                                      window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, function(fileEntry) {
                                          fileEntry.getDirectory("com.xxx.app1", {
                                              create: true,
                                              exclusive: false
                                          }, function(fileEntry) {
                                              //下载代码
                                              var fileTransfer = new FileTransfer();
                                              fileTransfer.download("http://192.168.1.102:1111/update/hot/app1/app1.apk", fileEntry.toInternalURL() + "app1.apk", function(entry) {
                                                  // 打开下载下来的APP
                                                  cordova.plugins.fileOpener2.open(
                                                      entry.toInternalURL(), //下载文件保存地址
                                                      'application/vnd.android.package-archive', { //以APK文件方式打开
                                                          error: function(err) {
                                                              setUtils.tips('下载失败!', 1);
                                                          },
                                                          success: function() {
                                                              setUtils.tips('下载成功,请安装后重新打开应用!', 1);
                                                          }
                                                      });
                                              }, function(err) {}, true);
                                              fileTransfer.onprogress = function(progressEvent) {
                                                  $timeout(function() {
                                                      var downloadProgress = (progressEvent.loaded / progressEvent.total) * 100;
                                                      $ionicLoading.show({
                                                          template: "已经下载:" + Math.floor(downloadProgress) + "%"
                                                      });
                                                      if (downloadProgress > 99) {
                                                          $ionicLoading.hide();
                                                      }
                                                  });
                                              };
                                          }, function(err) {
                                              setUtils.tips("创建目录失败原因:" + err, 1)
                                          });
                                      });
                                  }
                              });
                          }
                      }
                  },
                  userWentToStoreCallback: function() {
                      // user went to the store from the dialog
                  },
                  userDeclinedRedirectCallback: function() {
                      // User didn't want to leave the app.
                      // Maybe he will update later.
                  }
              };
              return new AppUpdate();
          }
      ]);
    

    说明,其中setUtils是自己实现的一些通用的函数封装,setUtils.tips则是封装了$cordovaToast服务的提示功能,可自己进行实现。下载文件夹的创建目录com.xxx.app1可以用公司名称的倒序然后加上apk的名称,也可自定义。上文中还加入了一些自定义的样式用于修改提示组件。

  • 将此项服务在应用run方法中注入,并调用上面的initialize方法,下面我们来开始测试。

  • 执行cordova-hcp build 命令,然后安装应用到手机,此处作为原始apk。

  • 修改项目根目录下的cordova-hcp.json文件中的min_native_interface属性,其值加1。

  • 修改项目根目录下的config.xml文件中,找到native-interface标记,其属性version值加1。

  • 修改项目源码,做个标记,然后再次执行cordova-hcp build 命令, 将新的www目录源码更新到http://192.168.1.102:1111/update/hot/app1 目录中,打包新的apk,放到http://192.168.1.102:1111/update/apks中。

  • 打开旧的应用,就会出现提示信息,确认下载,则会走进度条,下载完成则会提示您安装,安装好之后,则更新完毕。

更换线上服务器和安全问题

  • 将上述步骤中的所有192.168.1.102的相关配置替换成自己的服务器地址,请注意访问目录。

  • 热更新和外壳更新是同一项功能,打包apk之前确保两者都可用。

  • 设置服务器访问权限,目录可访问,但替换和删除则需要密码,以确保安全。

  • 在此用linux服务器举例,在更新www目录中的源码时需要删除之前的代码, 在服务端上的相应目录执行rm -rf * 删除服务器上热更新上的所有文件。

  • 然后在上传新的热更新代码, 本地cd到www目录,然后执行scp -r * hot@xxx.xxx.xxx.xxx:update/hot/app1 此处只是作为举例,hot是你服务器上的用户名,xxx.xxx.xxx.xxx代表服务器ip,使用该命令将所有新的代码部署到服务端。

注意事项

  • 在上述测试时,请保持手机和电脑保持在同一网段,确保可访问。

  • 命令cordova-hcp build可以在自定义目录中生成配置文件,如cordova-hcp build www 也可用其他目录,但没有必要,我们使用的就是www目录。

  • 注意开发环境和生产环境的区别,注意src和www的不同,注意结合构建过程。

  • 项目中没有用到 cordova-hot-code-push-local-dev-addon 测试插件,这个只作为测试使用,实际上没有什么价值。

不足之处

  • 在热更新的时候,闪一下之后,原来基于cookie的存储全部被删除,一些数据的存储尽量不要使用cookie,可以使用PouchDB 和 SQLite
  • 在项目运行时安装新的apk,没有重新打开,而是退出了,需要手动重新打开应用,这时候会看到新的内容被更新了。

目前环境

在项目根目录下执行$ ionic info 之后

 global packages:

     @ionic/cli-plugin-proxy : 1.3.1
     @ionic/cli-utils        : 1.4.0
     Gulp CLI                : CLI version 3.9.1 Local version 3.9.1
     Ionic CLI               : 3.4.0

 local packages:

     @ionic/cli-plugin-gulp   : 1.0.1
     @ionic/cli-plugin-ionic1 : 2.0.0
     Ionic Framework          : 1.3.2

 System:

     Node       : v7.2.1
     OS         : OS X El Capitan
     Xcode      : Xcode 8.2.1 Build version 8C1002
     ios-deploy : 1.8.6
     ios-sim    : 5.0.8
     npm        : 4.3.0

参考博客

官方文档参考

ionic4.0 详细热更新 自动模式以及手动模式--秒杀网上一切热更新因为网上没有手动更新的详细代码 测试无误上线发布了的东西. 未完待续 阅读详情

相关推荐

【ionic App问题总结系列】ionic 如何更新app版本

ionic 如何进行自动更新ionic App更新有两种方式:第一种是普通的从远程下载apk,安装并覆盖旧版本。另外一种就是采用替换www文件夹的内容,实现应用内更新,而无需下载安装apk。 这篇文章讲的是通过第一种方式,来实现app的更新。 App更新流程比较简单,如下图: 这里用到的后端的接口字段如下:{ "update_flag", // 更新的flag,一般取值分为不

前端超人 4401

ionic 配置热更新(cordova-plugin-code-push)

Step 1.安装插件 ionic cordova plugin add cordova-plugin-code-push@latest Step 2.

学无止境! 无休无止! 1123

Ionic热更新插件的完整指南

Ionic热更新插件是一个为移动应用提供无缝更新的工具,能够自动修复程序中的bug并添加新功能,极大地改善了用户体验。开发者利用该插件可以更加灵活地控制应用更新的各个环节,实现更加安全、快捷的更新流程。

weixin_42588672的博客 432

IONIC 自动更新APP版本

1、准备工作,添加插件 如果cordova 未添加请先执行以下命令      1、npm install bower -g //安装bower        2、bower install ngCordova   //安装cordova  1.1、添加获取APP版本信息插件 ionic plugin add cordova-plugin-app-versi

坐在那边看天空 的博客 2013

HFSS螺旋线圈的优化设计与高效仿真实践

本文详细介绍了HFSS螺旋线圈的优化设计与高效仿真实践,涵盖核心参数设置、参数化建模、仿真加速技巧及结果分析。通过HFSS软件实现螺旋线圈的高效仿真,特别适用于无线充电和RFID系统设计,提升设计效率与性能验证准确性。

weixin_30235225的博客 445

Arxml文件可视化:当汽车软件架构遇见交互式设计工具

本文探讨了Arxml文件在汽车电子系统开发中的可视化技术,结合AUTOSAR架构标准,介绍了如何利用现代Web技术构建交互式设计工具。通过三维组件关系图、动态数据流展示和版本对比等功能,显著提升开发效率和架构评审质量,适用于MBD和ASPICE开发流程。

weixin_29050271的博客 429

利用ionic实现IOS和Android端热更新简单搭建

1、热更新的好处? 通常ionic源码包括(html、javaScript css文件和其他资源)往常我们必须通过提交程序到应用市场、经过漫长的审核用户更新、每一个更改的小地方都需要从新打包编译。现在ionic通过使用cordova插件cordova-hot-code-push 实现ios与Android端代码功能、可不必要应用市场平台审核、便可以实现动态的app源码的目的。 2、热更新实现原...

点点的博客 1920

3DGS实战笔记:从相机坐标系到屏幕像素,手把手推导高斯椭球的投影变换矩阵

本文详细解析了3D高斯散射(3DGS)技术中从相机坐标系到屏幕像素的几何变换过程,重点推导了高斯椭球的投影变换矩阵。通过视图变换、投影变换和视口变换的完整流程,结合雅可比矩阵处理协方差矩阵,实现了高效的三维重建与渲染。文章提供了Python代码示例,帮助开发者深入理解3DGS的核心几何变换机制。

weixin_28691741的博客 438

Ionic - 热更新

Ionic3开发时,经常会遇到需要频繁改动打包,如果这时候可以热更新整个APP的变动文件,那么就像web应用一样,在APP热更新。 本文是使用了Cordova-hot-code-push插件去进行对一个打包编译后的静态文件进行加工,部署到服务器的一个过程。 进入正题 一、热更新流程 1. 编译应用成功后,热更新插件对每一个变动的文件生成hashcode 2.将生成后的...

LucioCN 6333

YOLO与3D点云融合:从原理到实践的多模态3D目标检测指南

最近在实验室和几个研究生聊天,发现一个挺有意思的现象:大家一提到“YOLO+3D点云”这个组合,第一反应往往是“顶会热门”、“发论文利器”,但真上手去复现或者想基于这个方向做点创新时,又常常卡在第一步——感觉每个部分都懂一点,但就是串不起来,不知道从哪开始,也不知道核心的“价值点”到底在哪。 这让我想起几年前刚开始接触这个领域时的自己,也经历过同样的困惑。当时看了很多论文,感觉“3D目标检测”这个方向既酷又难,YOLO负责2D图像里的快速定位,点云提供精确的三维空间信息,听起来是天作之合。但真正把代码跑起来

weixin_34267123的博客 416

自适应陷波器的原理与实现

x(t)是叠加有两个频率干扰信号的输入信号,s(t)是需要保留的有用信号,在输入信号中,我们只知道频率w1和w2,但不知道信号的幅度A和相位θ,自适应陷波器可以估计两个未知的幅度和相位值,这需要我们提供两路相互正交的单频信号,如cos(w1t)、sin(w1t),通过调整其权值,从而合成出与干扰信号完全相同的信号A1cos(w1t+θ1),从而实现干扰信号的滤除。采用LMS算法,在保证不损失其他有用信号的情况下,有效地抑制输入信号中某一频率的干扰,如需滤除多个频率,只需要增加相应的输入信号。

Melkiades2333的博客 6894

ionic cordova 热更新

因为项目需要,使用cordova的热更新插件,本地调试很简单,看连接https://github.com/nordnet/cordova-hot-code-push,就几步,这里不说了。  下面两个要装下   npm install -g cordova-hot-code-push-cli(用来动态生成chcp.json和chcp.manifest两个文件)      cordova plu

ioshunjian的博客 3549

喷涂机器人系列编程:ABB IRB 5500_(9).常见故障诊断与排除

ABB IRB 5500 喷涂机器人在实际应用中可能会遇到各种故障,但通过系统的诊断和排除方法,可以确保生产线的连续运行。定期维护和保养是预防故障的重要手段,而操作培训和规范则能有效减少人为因素引起的故障。希望本节内容能帮助操作和维护人员提高故障诊断和排除的效率,确保喷涂机器人的稳定运行。如果您有任何疑问或需要进一步的帮助,请联系 ABB 客户服务中心。

zhubeibei168的博客 1045

ionic实现App动态更新,热更新

ionic实现App动态更新,热更新

兰色的fire 5544

DMC-E 系列总线控制卡----雷赛板卡介绍(十七)

低的速度平稳地接近目标位置,降低运动部件对接触点的冲击。设置插补目标位置,插补方向,以确定插补动作。圆形区域限位,即运动仅在用户设定的圆形范围内进行,超出这个范围,运动会依据设定。首先,用户需要绑定两个轴进而确定该功能所处的坐标系。仅支持平面圆形限位,不支持空间圆形限位。径,可以确定该区域在坐标系中的位置。)运动开始前,用户应当将旋转轴调整到与插补曲线的起点位置相切的角度;可以配合椭圆插补或圆弧插补运动,实现跟随轴的旋转运动角度与曲线当前。外,其它运动形式若开始运动时在限位区域外,则禁止运动;

A_nanda的博客 654

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

ionic3 热更新

前提:在ionc3 app项目的基础上 1.设置Android平台 ionic cordova platform add android 修改platforms/android/project.properties和platforms/android/CordovaLib/project.properties 中的 target = "你的sdk版本" 2.安装热更新插件 ...

Amandam2018的博客 739

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

ionic3 实现app版本更新下载并打开进行安装(包括android7+无法自动安装apk问题的解决方法)

记录一下实现app版本一键更新下载的功能。 我的项目的总体的文件: 使用的插件列表如下: 需要纯实现该功能用到的基本上是除了后三个。 首先,安装这些插件,在app.component.ts页面上加入 import { Component, ViewChild } from '@angular/core'; import { Platform, ToastController,...

zuo-yiran的博客 1万+

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

ionic 实现自动升级APP

http://www.cnblogs.com/shachangdianbing/p/6186873.html 最近做移动商城开发,需要一个自动升级APP的功能,在网上搜罗很多,发现有的是下载APK,有的在我这服务无法实现,由于时间原因,都被PASS了,最后找到了一个热更新插件,经过半天的调试,可以使用,很欣喜,记录下来,一起学习。       此插件名称是cordova-hot-cod

方全的专栏 2882

ionic2热更新

热更新的实现主要就是app在启动的时候去服务器检查有没有更新,有则把更新下到本地,这样就实现了热更新。

qq_38191223的博客 478

ionic热更新

参考 http://blog.youkuaiyun.com/lovelyelfpop/article/details/50848524 安装 热更新插件 地址:https://github.com/nordnet/cordova-hot-code-push.git 注意:工程插件目录plugins下的三个json文件为可读写属性,否则安装将失败 CordovaHotCodePush命令...

石头 1529

Cordova ionic3打包 APP在线自动更新升级踩坑问题

做了三天踩了无数的坑,摸了无数的鱼,看了无数的帖子,针对Android版本有很多解决方案,在这里本人用的技术是ionic3+angular5,其他版本的用我的可能不能解决问题但是可以参考。 本贴子参考了好几个大神的技术精炼而成 帖子地址:https://www.jianshu.com/p/56c101672372 主要的就是这个 主要使用插件 file file-transfer ...

qq_24734827的博客 632

ionic应用自动升级

正文 升级是应用最基本的功能,因为很少有一个应用发布后不在进行后期维护! 原生应用的升级比较常见,但是如今混合应用大热,因为项目,我就基于ionic框架实现了一个简单的升级,根据服务器端返回来确定强制还是非强制更新. 插件安装 file(访问文件) ionic cordova plugin add cordova-plugin-file npm install --save @ionic-na...

LLCN的博客 742

ionic 自动更新APP

一、准备工作  1.Cordova插件:    cordova plugin add https://github.com/whiteoctober/cordova-plugin-app-version.git // 获取APP版本    cordova plugin add org.apache.cordova.file // 文件系统    cordova plugin add org.a

pdw2009的专栏 2001
上一篇: ionic后退造成的闪退解决方案
下一篇: 考研英语 - word-list-30
Wang's Blog
博客等级 码龄12年 7434粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

Wang's Blog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值