uniapp之ios开发及支付整体流程爬坑记录
前言
在写这篇记录的时候,关于ios的支付已经对接的差不多了,下一步就是测试好了直接发版,总共花了好几周的时间,从0到1对于首次做ios支付来说,确实很多坑。
其实业务层面很简单,甚至比安卓支付还简单,因为支付的整体流程uniapp那边已经提供好了,甚至可以直接套模板。主要坑在于不了解ios内购这套东西,及其细节处理。
该APP使用的是uv-ui组件库,uv-ui 破釜沉舟之兼容vue3+2、nvue、app、h5、小程序等多端基于uni-app和uView2.x的生态框架,支持单独导入,开箱即用,利剑出击。
准备工作
最重要的就是准备证书、描述文件等环节。现在uniapp开发ios没有这两样东西,不能真机运行。还需要区分测试证书和正式证书,本地真机运行只能使用测试证书,正式证书只能打包上传后台到TestFlight中下载测试或提App Store审核(即时到了这步,内购也只能使用沙箱环境支付,非正式支付。只有App Store审核通过后才能走真实支付,这里建议开发灰度测试功能,后续会详细讲解)。
注册账号及创建APP等准备工作都是产品去做的,所以对此流程可能会有遗漏,所以只记录大概我所了解。
- 创建你的 Apple ID:https://developer.apple.com/account
- 创建一个App应用:
- 登录iTunes Store,点击我的App
- 新建一个App(如果App已经创建,直接点击App进入就行了)
- 填写App的基本信息
- 创建证书、描述证书等(Certificates, Identifiers & Profiles):Apple后台、uniapp申请证书引导
- 需要mac电脑
- 证书及描述文档要分别创建开发版(Development)和发行版(Distribution),参考uniapp申请证书引导
- 本地调试使用Development,打包上线审核使用Distribution
- 最终需要的东西:
- Bundle ID(AppID)- 创建证书的时候会填写,类似安卓的证书名(推荐反域名+app标识)
- 证书 - xx.p12文件
- 描述文件 - xx.mobileprovision文件
- 证书私钥密码 - 创建证书的时候填写的密码
- 首次运行需要进行基座签名
- 我是使用的 爱思助手 进行的基座签名
- 具体使用请参考uniapp提供的基座签名指南,描述的很详细,跟着操作就OK
- 如果有ios内购项目,添加内购项目:
- 点击我的App进入App Store
- 选择功能/App内购买项目
- 创建App内购买项目:类型、产品 ID等信息
- 最后须知:ios内购的模式是充值,只能是创建固定的金额进行支付,并且平台抽成30%
- 添加沙盒账号
- 回到iTunes Store首页,点击用户和访问权限 进入 用户和访问
- 点击沙箱测试员
- 添加沙箱测试账号信息:沙箱账号的邮件地址是需要没有注册过Apple ID的邮箱,知道这点很重要
- 总结:沙箱测试账号的作用,在后面测试支付的时候会让输入账号和密码,这时候就需要用到这个沙箱账号了,否则其他账号密码是没用的
真机运行
- 完成上面的所有准备,就可以直接使用hbuilderX运行到苹果手机上面
- 总之,在使用hbuilderX运行到真机的时候,提示缺什么,我们就需要按照上面的方法准备什么
正式开发
业务开发
- 使用app-nvue技术开发ios,90%的代码与安卓都是通用的,毕竟多平台跨端开发,只是有些兼容性问题,需要单独处理而已,具体问题具体分析。
- 具体开发的内容就不做详细的介绍,接下来把ios内购买项目做简单的记录。
ios内购开发
- 参考文档:uniapp之苹果应用内支付,在开发之前一定要对该文档进行通读和了解,很多开发代码合流程都在这里面。
- 后端需要准备两个接口:
- 接口1:生成业务订单号,前端需要获取后做相关关联
- 接口2:最后一步,在服务器端请求苹果服务器验证票据
- 前端开发步骤:
- 写好充值页面
- 创建公共文件iap.js,封装的支付相关处理逻辑,方便后续调用,代码是现成的,直接到示例代码复制
- 确认充值相关逻辑,完整示例代码,该示例里面需要完善两个接口的逻辑。注意:示例代码可能造成丢单情况,需要配合本地缓存进行处理,参考下面的完整示例。
- 为了方便理解,我把开发中的完整代码贴在下面:
充值页面混入pay.ios.js:
import { Iap, IapTransactionState } from "@/common/js/iap.js"
export default {data() {return {title: "iap",loadingIOS: false,disabled: true,productId: "",productList: [],isError: false}},methods: {async payInitIOS() {uni.showLoading({mask: true,title: '苹果验证中,请稍等'});this.isError = false;// 创建实例this._iap = new Iap({products: [this.productId] // 苹果开发者中心创建})try {// 初始化,获取iap支付通道await this._iap.init();// 从苹果服务器获取产品列表this.productList = await this._iap.getProduct();this.productList[0].checked = true;this.productId = this.productList[0].productid;// 填充产品列表,启用界面this.disabled = false;} catch (e) {this.isError = true;uni.showModal({title: "init",content: e.message,showCancel: false});} finally {if (this._iap._ready && !this.isError) {this.restore();} else {uni.hideLoading();}}},async restore() {// 检查上次用户已支付且未关闭的订单,可能出现原因:首次绑卡,网络中断等异常// 在此处检查用户是否登陆// uni.showLoading({// mask: true,// title: '苹果验证中,请稍等'// });try {// 从苹果服务器检查未关闭的订单,可选根据 username 过滤,和调用支付时透传的值一致const transactions = await this._iap.restoreCompletedTransactions({username: ''});if (!transactions.length) {return;}// 开发者业务逻辑,从服务器获取当前用户未完成的订单列表,和本地的比较// 此处省略for (let i = 0; i < transactions.length; i++) {const transaction = transactions[i];switch (transaction.transactionState) {case IapTransactionState.purchased:this.isError = true;// 用户已付款,在此处请求开发者服务器,在服务器端请求苹果服务器验证票据uni.showLoading({mask: true,title: '您有一笔订单正在处理中...'})const order_sn = transaction.payment.username || uni.getStorageSync('IOSPAYORDERID');if(!order_sn) {this.isError = false;return await this._iap.finishTransaction(transaction);}let result = await this.validatePaymentResult({product_id: transaction.payment.productid,order_sn: order_sn,receipt: transaction.transactionReceipt, // 不可作为订单唯一标识transactionIdentifier: transaction.transactionIdentifier}, 0);// 验证通过,交易结束,关闭订单if (result) {await this._iap.finishTransaction(transaction);}break;case IapTransactionState.failed:this.isError = false;// 关闭未支付的订单await this._iap.finishTransaction(transaction);break;default:break;}}} catch (e) {// 为了兼容高版本机型在取消订单时候出现的错误,重启后不存在if(e.code == -100 && e.errMsg.indexOf("本地没有响应要移除的事务")>-1){this.isError = false;return;}this.isError = true;uni.showModal({title: `restore${e.errCode}`,content: e.message,showCancel: false});} finally {if (!this.isError) {this.paymentIOS();} else {uni.hideLoading();}}},async paymentIOS() {if (this.loadingIOS == true) {return;}this.loadingIOS = true;uni.showLoading({mask: true,title: '支付处理中...'});try {// 从开发者服务器创建订单const orderId = await this.createOrder({productId: this.productId});// orderId存在本地,防止丢失uni.setStorageSync('IOSPAYORDERID', orderId);// 请求苹果支付const transaction = await this._iap.requestPayment({productid: this.productId,username: orderId,manualFinishTransaction: true,quantity: 1});// 在此处请求开发者服务器,在服务器端请求苹果服务器验证票据await this.validatePaymentResult({product_id: this.productId,order_sn: transaction.payment.username || orderId,receipt: transaction.transactionReceipt, // 不可作为订单唯一标识transactionIdentifier: transaction.transactionIdentifier});// 验证成功后关闭订单await this._iap.finishTransaction(transaction);// 支付成功this.paySccuess();} catch (e) {uni.$uv.toast('支付取消或失败');} finally {this.loadingIOS = false;uni.hideLoading();}},createOrder({ productId }) {return new Promise((resolve, reject) => {this.getOrderInfo({ product_id: productId }).then(res => {resolve(res.order_no);})})},/*** 充值,e.code = 201 或 then返回均代表 处理成功* @param {Object} data 订单数据*/validatePaymentResult(data, type = 1) {return new Promise((resolve, reject) => {const fn = (loading = 1) => {this.validatePayment(data, loading).then(res => {// 处理成功uni.hideLoading();if (type == 0) {this.successTip();}resolve(true);}).catch(e => {if (e.code == 201) { //处理成功-订单已更新this.successTip();uni.hideLoading();resolve(true);} else {setTimeout(() => {fn(0);}, 3000)}})}fn(type == 0 ? 0 : 1);});},applePriceChange(e) {this.productId = e.detail.value;},successTip() {uni.showModal({title: '温馨提示',content: '您的待处理订单已经处理成功,充值金额已到您的账户余额中,请注查收!',showCancel: false,confirmText: '我知道了'});}}
}
测试支付
- 其实ios内购在整个业务逻辑并不复杂,不需要像其他支付进行轮询监听等逻辑,ios内部已经做好了这些事情。
- 开始测试就需要对ios这个后台有所了解,我也是第一次接触,所以更多的时间是摸索后台怎么设置,我就讲讲我到底经历了哪些问题:
- 第一步实例化支付就失败了,后来发现是内购项目未创建,必须先创建购买项目,代码中需要使用产品ID。
- 购买项目创建好后,实例化等逻辑可以走通了,支付之前会弹出一个让输入账号密码的弹窗。一开始我以为是输入自己的AppleID和密码,试了下输入后提交就失败了。在这里也卡了不少时间,后来发现这里是输入沙箱账号,后来创建了沙箱账号成功支付。
- 测试过程中,发现有丢单情况,可能是由于网络或者后端验证失败等其他原因,导致最后断单了,只有重启APP才能补单,但是有些机型发现请求苹果支付this._iap.requestPayment传的username参数也会丢失,导致补单的时候和我们的订单关联不上,所以后端无法做最后的验证票据,我们的处理方式是配合本地缓存进行处理,如果只是单个订单支付,就可以这样处理,这里的坑在后面专门进行说明,上述完整示例代码中也有体现。
- 沙箱环境测试通过,接下来想使用正式支付。开始使用TestFlight测试(测试人员可以在这个软件上安装app进行测试);后来发现在发布App Store审核上线之前,都只能进行沙箱环境支付,这个确实有点坑。经过咨询官方给的解决方案:设置几个账号进行灰度测试,以前没有这个概念,现在终于明白了,灰度测试是这样使用的,经过商量就开发了灰度测试功能,就是固定几个账号才能在上线后支付,等支付没问题后,再放开所有账号支付权限。
开发过程中遇到的坑
首次开发ios及其内购买项目,遇到坑是正常的,感谢这次机会,至少让我得到了成长,接下来就讲讲整个ios开发遇到了哪些坑:
坑一:本地没有响应要移除的事务
如果输入沙箱账号和密码支付后未完成后续验证,杀掉APP进程,重启APP进行补单。这时候肯定会检测到未支付的订单,就需要手动关闭订单this._iap.finishTransaction
。但是某些苹果机型一直反馈错误信息:undefined.Payment_appleiap:本地没有响应要移除的事务,https://ask.dcloud.net.cn/article/282
原因分析:在6s机型没有这问题,在7等机型会有这个问题,导致支付流程不能往下执行
解决方案:捕捉到此错误,然后就当正确的逻辑处理,在上述完整示例代码中也有体现
catch (e) {// 为了兼容高版本机型在取消订单时候出现的错误,重启后不存在if(e.code == -100 && e.errMsg.indexOf("本地没有响应要移除的事务")>-1){this.isError = false;return;}
<!--后面的逻辑在省略-->
坑二:不能真实支付
本地只能沙箱账号进行支付测试,怎么办?
解决方案:根据uni官方的回复,灰度测试,设置几个固定账号进行上线后测试,其他账号暂不支持支付。官方回答:https://ask.dcloud.net.cn/question/179074?notification_id-1321394__rf-false__item_id-254173#!answer_254173
坑三:丢单+补单
输入密码支付过程中,杀掉进程,会造成丢单情况
原因分析:由于网络或者用户主动关闭APP等情况,支付流程断掉,如果根据username进行订单关联,可能有些机型在补单的时候丢失该值,最终导致丢单,这在ios是正常情况
解决方案:
- 根据业务需求,配合本地缓存将订单记录,在补单的时候好做对应
- 可以不使用订单号,据说ios没得订单号的概念,直接后端进行验证,这种方案我们没试过
- 这里有个keep客户端开发也遇到丢单的情况,经过多次测试修改,最终的流程和我们的处理方案一致,这个很有参考意义:根治顽疾:Keep客户端 In-App Purchase 掉单踩坑指南
坑四:打包上传
打包上传到iTunes Store,versionCode每次上传都得高于上一次,versionName可以不变
上传到iTunes Store的工具推荐(必须mac):通过 Transporter App 上传 App 的二进制文件
坑五:打开APP苹果手机发烧严重
同一套代码,在安卓机没问题。但是在ios发现发烧很严重,打开APP就开始发烧。
原因分析:1. 开始以为是本地基座的问题,其实仔细想想不会是这个问题,uniapp不会这么拉胯;2. 经过代码排查,发现是因为image标签使用了@load,我们APP中恰好有很多图片展示,这应该是ios这边的机制比较耗CPU,导致发热严重。
解决方案:去掉image上的@load,取消图片加载效果,只做图片失败效果
坑六:uniapp打包提示:打包时未添加OAuth模块
原因分析:代码中使用了uni.preLogin相关,但是ios并未涉及相关模块,所以在ios端屏蔽掉就OK了。
解决方案:参考文档:http://www.codingwhy.com/view/12174.html
坑七:审核多次驳回
- 后续补充
相关文章:
uniapp之ios开发及支付整体流程爬坑记录
前言 在写这篇记录的时候,关于ios的支付已经对接的差不多了,下一步就是测试好了直接发版,总共花了好几周的时间,从0到1对于首次做ios支付来说,确实很多坑。 其实业务层面很简单,甚至比安卓支付还简单&…...
AutoDL百川大模型体验
文章目录 镜像克隆模型下载测试效果AutoDL自定义服务 感谢AutoDL和CodeWithGPU这两个平台,让我们能低成本,低门槛地部署体验这些大模型 镜像克隆 我是在CodeWithGPU上克隆的这个镜像 模型下载 codewithgpu有介绍 注意这三个文件都需要下载 把那个&quo…...
蓝桥杯每日一题2023.10.8
题目描述 七段码 - 蓝桥云课 (lanqiao.cn) 题目分析 所有的情况我们可以分析出来一共有2的7次方-1种,因为每一个二极管都有选择和不选择两种情况,有7个二极管,但是还有一种都不选的情况需要排除,故-1 枚举每个方案看是否符合要…...
jmeter,性能测试,Locust
一。性能测试的概念 1.性能:就是软件质量属性中的 “ 效率 ” 特性 2.效率特性: 时间特性:指系统处理用户请求的响应时间 资源特性:指系统在运行过程中,系统资源的消耗情况 CPU 内存 磁盘IO(磁盘的写…...
opencv图像的直方图,二维直方图,直方图均衡化
文章目录 opencv图像的直方图,二维直方图,直方图均衡化一、图像的直方图1、什么是图像的直方图:2、直方图的作用:3、如何绘制图像的直方图:(1)cv::calcHist()函数原型:英文单词 calc…...
c++中的map和set
文章目录 1. 关联式容器2. 键值对3. 树形结构的关联式容器3.1 set3.1.1 set的介绍3.1.2 set的使用 3.2 map3.2.1 map的介绍3.2.2 map的使用 3.3 multiset3.3.1 multiset的介绍3.3.2 multiset的使用 3.4 multimap3.4.1 multimap的介绍3.4.2 multimap的使用 1. 关联式容器 在初阶…...
Swagger使用详解
目录 一、简介 二、SwaggerTest项目搭建 1. pom.xml 2. entity类 3. controller层 三、基本使用 1. 导入相关依赖 2. 编写配置文件 2.1 配置基本信息 2.2 配置接口信息 2.3 配置分组信息 2.3.1 分组名修改 2.3.2 设置多个分组 四、常用注解使用 1. ApiModel 2.A…...
ToBeWritten之车联网安全中常见的TOP 10漏洞
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 转移发布平台通知:将不再在CSDN博客发布新文章,敬…...
软考-密码学概述
本文为作者学习文章,按作者习惯写成,如有错误或需要追加内容请留言(不喜勿喷) 本文为追加文章,后期慢慢追加 by 2023年10月 密码学基本概念 密码学的主要目的是保持明文的秘密以防止攻击者获知,而密码分…...
windows 2003、2008远程直接关闭远程后设置自动注销会话
1、2003系统: 按开始—运行—输入“tscc.msc”,打开“终端服务配置”。 单击左边窗口的“连接”项,右边窗口中右击“RDP-TCP”,选择“属性”。 单击“会话”项,勾选“替代用户设置”,在“结束已断开的会话”…...
iOS BUG UIView转UIImage模糊失真
iOS BUG UIView转UIImage模糊失真 ##UIView转成Image - (UIImage *)capture {UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);[self.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *img UIGraphicsGetImageFromCurrentImageContext(…...
如何在10分钟内让Android应用大小减少 60%?
一个APP的包之所以大,主要包括一下文件 代码libso本地库资源文件(图片,音频,字体等) 瘦身就主要瘦这些。 一、打包的時候刪除不用的代码 buildTypes {debug {...shrinkResources true // 是否去除无效的资源文件(如…...
网络代理技术:保障隐私与增强安全
在当今数字化的世界中,网络代理技术的重要性日益凸显。无论您是普通用户还是网络工程师,了解如何使用代理技术来保护隐私和增强网络安全都是至关重要的。本文将深入探讨Socks5代理、IP代理以及它们在网络安全和隐私保护中的关键作用。 1. Socks5代理&am…...
数据结构 | (二) List
什么是 List 在集合框架中, List 是一个接口,继承自 Collection 。 Collection 也是一个接口 ,该接口中规范了后序容器中常用的一些方法,具体如下所示: Iterable 也是一个接口,表示实现该接口的类是可以逐个…...
[NewStarCTF 2023 公开赛道] week1 Crypto
brainfuck 题目描述: [>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<-]>>>>>>>.>----.<-----.>-----.>-----.<<<-.>>..…...
C语言中文网 - Shell脚本 - 0
教程目录如下: 第1章 Shell基础(开胃菜) 1. Shell是什么?1分钟理解Shell的概念! 2. Shell是运维人员必须掌握的技能 3. 常用的Shell有哪些? 4. 进入Shell的两种方式 5. Linux Shell命令的基本格式 6.…...
Transformer预测 | Pytorch实现基于Transformer 的锂电池寿命预测(CALCE数据集)
文章目录 效果一览文章概述模型描述程序设计参考资料效果一览 文章概述 Pytorch实现基于Transformer 的锂电池寿命预测,环境为pytorch 1.8.0,pandas 0.24.2 随着充放电次数的增加,锂电池的性能逐渐下降。电池的性能可以用容量来表示,故寿命预测 (RUL) 可以定义如下: SOH(t…...
2023年【通信安全员ABC证】找解析及通信安全员ABC证考试总结
题库来源:安全生产模拟考试一点通公众号小程序 通信安全员ABC证找解析参考答案及通信安全员ABC证考试试题解析是安全生产模拟考试一点通题库老师及通信安全员ABC证操作证已考过的学员汇总,相对有效帮助通信安全员ABC证考试总结学员顺利通过考试。 1、【…...
前端框架Vue2.0+Vue3.0学习笔记01
一、Vue技术_课程简介 1、前端框架小白 2、熟练掌握Vue2 3、轻松玩转Vue3 ①、vue基础 ②、vue-cli ③、vue-router ④、vuex ⑤、element-ui ⑥、vue3 二、Vue技术_Vue简介 1、Vue是什么? 一套用于构建用户界面(把你拿到手里的数据…...
iOS App上架全流程及相关处理
iOS app上架总体流程: 一、IOS上架整个流程 1、申请开发者账号 2、创建APP ID及申请证书 3、itunes connect 创建APP 4、打包 上传APP 5、提交APP,上线成功 1、申请开发者账号 苹果开发者账号主要分为三种:个人账号、公司账号、企业账…...
解决WordPress升级后提示:无需升级,您的WordPress数据库已经是最新的了
问题描述 当升级了 WordPress 6.3 后,登录后台出现了提示:无需升级,您的WordPress 数据库已经是最新的了。并且无法进入后台了。 出现这个问题的原因可能是你网站开启了 Memcached 缓存。 如何验证是否开启了 Memcached 缓存?检…...
springcloud之项目实战搭建单体
写在前面 在上篇文章 中我们介绍了项目的整体内容以及架构,本文就开始实现一个单体的版本,在之后的文章中,在使用springcloud相关组件将这个单体的版本一步步的拆分为微服务的版本,在开始之前再贴下组件图: 本文我们分…...
Mac 点击桌面 出现黑边框 解决
1、桌面黑框效果 2、解决:设置为 仅在台前调度中...
深度学习(2)---循环神经网络(RNN)
文章目录 一、序列数据和语言模型1.1 序列数据1.2 语言模型 二、循环神经网络(RNN)2.1 概述2.2 门控循环单元(GRU)2.3 长短期记忆网络(LSTM) 一、序列数据和语言模型 1.1 序列数据 1. 在深度学习中,序列数据(Sequence data)是指具有前后顺序…...
[NOIP2010 提高组] 机器翻译
[NOIP2010 提高组] 机器翻译 题目背景 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。 题目描述 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词…...
配置文件生成器-秒杀SSM的xml整合
配置文件生成器-秒杀SSM的xml整合 思路: 通过简单的配置,直接生成对应配置文件。 maven坐标 <dependencies><!-- 配置文件生成 --><dependency><groupId>org.freemarker</groupId><artifactId>freemarker<…...
小黑开始了拉歌训练,第一次进入部室馆,被通知要去当主持人心里有些紧张的leetcode之旅:337. 打家劫舍 III
小黑代码(小黑卡在了bug中,上午一步步探索做出,非常NB!!!) # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left lef…...
flutter开发实战-inappwebview实现flutter与Javascript方法调用
flutter开发实战-inappwebview实现flutter与Javascript方法调用 在使用inappwebview时候,需要flutter端与JS进行交互,调用相应的方法,在inappwebview中的JavaScript Handlers。 一、JavaScript Handlers 要添加JavaScript Handlers&#…...
alsa pcm设备之硬件参数
硬件参数包含了stream描述比如格式,采样率,通道数,和ringbuffer 圆形缓存区大小等. 使用snd_pcm_hw_params_t ,ALSA pcm设备使用了参数重定义系统相关的硬件参数,应用程序首先选择全范围的配置, 然后应用程序设置单个参数,直到所有参数都是基本的(确定的). 格式 量化位數&#…...
websocket拦截
python实现websocket拦截 前言一、拦截的优缺点优点缺点二、实现方法1.环境配置2.代码三、总结现在的直播间都是走的websocket通信,想要获取websocket通信的内容就需要使用websocket拦截,大多数是使用中间人代理进行拦截,这里将会使用更简单的方式进行拦截。 前言 开发者工…...
如何做php网站建设/桂平seo关键词优化
http://www.neoease.com/nginx-virtual-host/...
wish跨境电商平台/seopc流量排行榜企业
方法一 通常使用socket.gethostname()方法即可获取本机IP地址,但有时候获取不到(比如没有正确设置主机名称) import socket#获取计算机名称hostnamesocket.gethostname()#获取本机IPipsocket.gethostbyname(hostname)print(ip) 方法二&#x…...
网站建设列入无形资产管理吗/湖南关键词优化快速
GCC 目前,GCC(GNU Compiler Collection)是Linux社区最好的编译器。GCC也就是以前的GNU C编译器(GNU C Compiler),是由egcs筹划指导委员会维护,他们的目标是让GCC成为标准的C编译器。1999年中&a…...
长春seo排名收费/seo综合查询网站源码
目录 1 问题描述 2 解决方案 1 问题描述 问题描述有n个小朋友围坐成一圈。老师给每个小朋友随机发偶数个糖果,然后进行下面的游戏:每个小朋友都把自己的糖果分一半给左手边的孩子。一轮分糖后,拥有奇数颗糖的孩子由老师补给1个糖果࿰…...
政府网站规范化建设/教育培训网页设计
thinkphp5多文件上传如何实现 一、总结 一句话总结:官方文档,测试一下,一定要测试,打印中间变量,就知道每句话是什么意思,一定要测试一下。又简单有快。 测试一下,你就能确定中间变量和你的是不…...
崇州市微信端网站建/怎样下载优化大师
excel如何读取B列的数据类型和b列里的数据sum(b:b,"工具",a:a)EXCEL如何将表格的数据类型分类提取出来(提取筛选的数据类型)把C列的数据复制,到右边找一列空的列,粘贴进去。然后选中右边粘贴过来的这列、点顶部的:数据---删除重复项…...