- 虚拟业务指南请收好。
在小程序生态中,基于苹果运营规范,小程序内暂不支持iOS端虚拟支付业务。为此小编为大家整理了一份虚拟支付业务指南,希望大家在做虚拟业务时有所帮助: [视频] 那么,到底什么是虚拟支付业务呢? 虚拟支付业务是指购买非实物商品。比如:VIP会员、充值、录制课程、录制音频视频等虚拟产品。目前iOS端暂不支持虚拟支付业务。 我们常见iOS虚拟支付的不合规示例有哪些呢? 示例一 :小程序内存在付费购买虚拟内容或道具。商品多体现为提前编辑好的、录制好的虚拟商品。如录制视频课程、游戏道具。 整改建议 :建议去除小程序内所有付费购买虚拟服务,并根据提示修改相关内容及文案,文案可参照“由于相关规范,iOS功能暂不可用”。 [图片] 示例二 :付费解锁优质服务。多体现为提供虚拟商品的小程序可通过支付购买、开通虚拟会员等形式,体验小程序付费服务。比如:支付阅读章节小说、同城生活服务平台付费发帖/付费置顶等。 整改建议 :建议可以关闭iOS端虚拟支付通道,并将【马上充值】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端会员服务。 [图片] 示例三 :关闭iOS端虚拟支付功能后,虚拟商品页面仍然保留货架价格标签展示、购买/付费/订阅等功能或按钮。 整改建议 :建议去除小程序中的虚拟商品的价格展示,并更改为【免费】;并将【订阅 ¥128】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端虚拟商品购买服务。 [图片] 示例四 :关闭iOS端虚拟支付功能后,提供引导用户前往其他支付的路径/文案,完成虚拟支付闭环。 整 改建议 :建议去除iOS端小程序内引导用户前往其他支付路径/文案,并不再提供iOS端虚拟商品购买服务。 [图片] 示例五 :小程序含需要付费的虚拟商品,并设置限时免费的服务,限时免费结束后需付费才能继续提供服务。 整改建议 :建议将iOS端小程序中所有虚拟付费内容更改为免费,并不再提供iOS端虚拟商品购买服务。 [图片] 示例六 :关闭iOS端虚拟支付功能后,小程序中虚拟产品页面不可以含有付费性质的关键字(如:购买、已购、付费、支付等),包括但不限于功能按钮、功能页面、支付提示及任何商品介绍等。 整改建议 :建议将小程序iOS端虚拟产品页面中的文案/按钮/功能tab含有限制的关键字更改为【免费】或删除。并不再提供iOS端虚拟商品购买服务。 [图片] 如小程序内存在以上不合规的虚拟支付内容,请开发者重视并及时整改。对于首次违规的小程序,平台将下发站内信整改通知,并给予三天整改时间,请开发者按照提示在限期内完成整改。平台将会对到期未完成整改的小程序进行搜索策略调整,并在小程序功能使用上进行一定的限制,直到小程序完成内容整改。
2020-04-23 - 云开发入门
重磅打造的小程序学习路径课,从微信小程序到微信云开发体系化的学习,带来更加顺畅的学习体验。
2021-11-19 - (4)获取用户信息
背景 我们发现大部分小程序都会使用 [代码]wx.getUserInfo[代码] 接口,来获取用户信息。原本设计这个接口时,我们希望开发者在真正需要用户信息的情况下才去调取这个接口,但很多开发者会直接调用这个接口,导致用户在使用小程序的时候产生困扰,归结起来有几点: 开发者在小程序首页直接调用 [代码]wx.getUserInfo[代码] 进行授权,弹框获取用户信息,会使得一部分用户点击“拒绝”按钮。 在开发者没有处理用户拒绝弹框的情况下,用户必须授权头像昵称等信息才能继续使用小程序,会导致某些用户放弃使用该小程序。 用户没有很好的方式重新授权,尽管我们增加了[代码]设置[代码]页面,可以让用户选择重新授权,但很多用户并不知道可以这么操作。 此外,我们发现开发者默认将 [代码]wx.login[代码] 和 [代码]wx.getUserInfo[代码] 绑定使用,这个是由于我们一开始的设计缺陷和实例代码导致的([代码]wx.getUserInfo[代码] 必须通过 [代码]wx.login[代码] 在后台生成 [代码]session_key[代码]后才能调用)。同时,我们收到开发者的反馈,希望用户进入小程序首页便能获取到用户的 [代码]unionId[代码],以便识别到用户是否以前关注了同主体公众号或使用过同主体的App 。 为了解决以上问题,针对获取用户信息我们更新了三个能力: 1.使用组件来获取用户信息 2.若用户满足一定条件,则可以用[代码]wx.login[代码] 获取到的[代码]code[代码]直接换到[代码]unionId[代码] 3.[代码]wx.getUserInfo[代码] 不需要依赖 [代码]wx.login[代码] 就能调用得到数据 获取用户信息组件介绍 [代码][代码] 组件变化: [代码]open-type [代码]属性增加 [代码]getUserInfo[代码] :用户点击时候会触发 [代码]bindgetuserinfo[代码] 事件。 新增事件 [代码]bindgetuserinfo[代码] :当 [代码]open-type[代码]为 [代码]getUserInfo[代码] 时,用户点击会触发。可以从事件返回参数的 [代码]detail[代码] 字段中获取到和 [代码]wx.getUserInfo[代码] 返回参数相同的数据。 示例: [代码]<button open-type="getUserInfo" bindgetuserinfo="userInfoHandler"> Click me button>[代码]和 [代码]wx.getUserInfo[代码] 不同之处在于: 1.API [代码]wx.getUserInfo[代码] 只会弹一次框,用户拒绝授权之后,再次调用将不会弹框; 2.组件 [代码][代码][代码][代码] 由于是用户主动触发,不受弹框次数限制,只要用户没有授权,都会再次弹框。 通过获取用户信息的组件,就可以解决用户再次授权的问题。 直接获取unionId开发者申请 [代码]userinfo[代码] 授权主要为了获取 [代码]unionid[代码],我们鼓励开发者在不骚扰用户的情况下合理获得[代码]unionid[代码],而仅在必要时才向用户弹窗申请使用昵称头像。为此,凡使用“获取用户信息组件”获取用户昵称头像的小程序,在满足以下全部条件时,将可以静默获得 [代码]unionid[代码]: 1.在微信开放平台下存在同主体的App、公众号、小程序。 2.用户关注了某个相同主体公众号,或曾经在某个相同主体App、公众号上进行过微信登录授权。 这样可让其他同主体的App、公众号、小程序的开发者快速获得已有用户的数据。 不依赖登录的用户信息获取某些工具类的轻量小程序不需要登录行为,但是也想获取用户信息,那么就可以在 [代码]wx.getUserInfo[代码] 的时候加一个参数 [代码]withCredentials: false[代码] 直接获取到用户信息,可以少一次网络请求。 这样可以在不给用户弹窗授权的情况下直接展示用户的信息。 最佳实践 1.调用 [代码]wx.login[代码] 获取 [代码]code[代码],然后从微信后端换取到 [代码]session_key[代码],用于解密 [代码]getUserInfo[代码]返回的敏感数据。 2.使用 [代码]wx.getSetting[代码] 获取用户的授权情况 1) 如果用户已经授权,直接调用 API [代码]wx.getUserInfo[代码] 获取用户最新的信息; 2) 用户未授权,在界面中显示一个按钮提示用户登入,当用户点击并授权后就获取到用户的最新信息。 3.获取到用户数据后可以进行展示或者发送给自己的后端。 One More Thing 除了获取用户方案介绍之外,再聊一聊很多初次接触微信小程序的开发者所不容易理解的一些概念: 1.关于OpenId和UnionId [代码]OpenId[代码] 是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户。 [代码]UnionId[代码] 是一个用户对于同主体微信小程序/公众号/APP的标识,开发者需要在微信开放平台下绑定相同账号的主体。开发者可通过[代码]UnionId[代码],实现多个小程序、公众号、甚至APP 之间的数据互通了。 同一个用户的这两个 ID 对于同一个小程序来说是永久不变的,就算用户删了小程序,下次用户进入小程序,开发者依旧可以通过后台的记录标识出来。 2.关于 getUserInfo 和 login 很多开发者会把 [代码]login[代码] 和 [代码]getUserInfo[代码] 捆绑调用当成登录使用,其实 [代码]login[代码] 已经可以完成登录,[代码]getUserInfo[代码] 只是获取额外的用户信息。 在 [代码]login[代码] 获取到 [代码]code[代码] 后,会发送到开发者后端,开发者后端通过接口去微信后端换取到 [代码]openid[代码] 和[代码]sessionKey[代码](现在会将 [代码]unionid[代码] 也一并返回)后,把自定义登录态 [代码]3rd_session[代码]返回给前端,就已经完成登录行为了。而 [代码]login[代码] 行为是静默,不必授权的,用户不会察觉。 [代码]getUserInfo[代码] 只是为了提供更优质的服务而存在,比如展示头像昵称,判断性别,开发者可通过 [代码]unionId[代码] 和其他公众号上已有的用户画像结合来提供历史数据。因此开发者不必在用户刚刚进入小程序的时候就强制要求授权。 可以在官方的文档中看到 [代码]login[代码] 的最佳实践: [图片] Q & A Q1: 为什么 login 的时候不直接返回 openid,而是要用这么复杂的方式来经过后台好几层处理之后才能拿到? A: 为了防止坏人在网络链路上做手脚,所以小程序端请求开发者服务器的的请求都需要二次验证才是可信的。因为我们采取了小程序端只给 [代码]code[代码] ,由服务器端拿着 [代码]code[代码] 和 [代码]AppSecrect[代码] 去微信服务器请求的方式,才会给到开发者对应的[代码]openId[代码] 和用于加解密的 [代码]session_key。[代码] Q2: 既然用户的[代码]openId[代码] 是永远不变的,那么开发者可以使用[代码]openId[代码] 作为用户的登录态么? A: 不行,这是非常危险的行为。因为 [代码]openId[代码] 是不变的,如果有坏人拿着别人的 [代码]openId[代码] 来进行请求,那么就会出现冒充的情况。所以我们建议开发者可以自己在后台生成一个拥有有效期的 [代码]第三方session[代码] 来做登录态,用户每隔一段时间都需要进行更新以保障数据的安全性。 Q3: 是不是用户每次打开小程序都需要重新[代码]login[代码]? A: 不必,可以将登录态存入[代码]storage[代码]中,用户再次登录就可以拿[代码]storage[代码] 里的登录态做正常的业务请求,只有当登录态过期了之后才需要重新[代码]login[代码] 。这样子做一则可以减少用户等待时间,二则可以减少网络带宽。 目前微信的[代码]session_key[代码] 有效期是三天,所以建议开发者设置的登录态有效期要小于这个值。
2018-08-17 - 【笔记】云开发,正确认识数据库自定义权限
【笔记】云开发,数据库权限问题导致更新操作不work,问题复盘 今天在做一个数据库基本的自增操作时,遇到了问题,确切说应该是昨天下午就遇到了,一直没有解决。 代码如下 const db = wx.cloud.database() const _ = db.command db.collection('info').doc('oL-AZ42zPyIlt9-1SaVZd').update({ data: { // 表示指示数据库将字段自增 10 nums: _.inc(1) }, success: function(res) { console.log(res) } }) 问题是这样的,如下图所示,截图右上角有一个数字,表示当前有多少用户正在刷题,我计划在app.js onLaunch里面表示+1,onHide完成-1, 具体集合涉及就是: info集合,只有一条记录:一个固定的_id,一个nums 1、_id,固定写死的,方便update 2、nums [图片] 占位符 [图片] 刚开始的权限是在标记①那里,也就是我在设计完集合之后,默认修改的权限,所有用户可读,仅创建者可读写。但是这次是update写操作,所以代码一直没有生效,最主要的是因为这种由权限导致的代码不work并没有任何提示, 然后改成自定义权限,读:true;写: true [图片] 问题解决 占位符
2020-02-20 - 小程序云开发模糊查询,实现数据库多字段的模糊搜索
最近做小程序云开发时,用到了一个数据库的模糊搜索功能,并且是要求多字段的模糊搜索。 网上也有一大堆资源,但是都是单个字段的搜索。如下图 [图片] 上图只可以实现time字段的模糊搜索。但是我们如果相对数据表里的多个字段做模糊查询呢?该怎么办呢。 多字段模糊搜索 一,如我们的数据表里有以下数据,我们想同时模糊查询name和address字段 [图片] [图片] 如我们搜索“周杰”可以看到我们查询到下面两条数据。 [图片] 二,如我们搜索“编程”,可以搜索到下面数据 [图片] 可以看到我们搜索到的两条数据,一个是name字段为 编程小石头, 一个是address字段里包含“编程“ 字样。 下面把代码贴给大家 [代码] let key = "编程小石头"; console.log("查询的内容", key) const db = wx.cloud.database(); const _ = db.command db.collection('qcl').where(_.or([{ name: db.RegExp({ regexp: '.*' + key, options: 'i', }) }, { address: db.RegExp({ regexp: '.*' + key, options: 'i', }) } ])).get({ success: res => { console.log(res) }, fail: err => { console.log(err) } }) [代码] key就是我们要搜索的关键字。主要是用到了数据库查询的where,or,get方法。 代码都给大家贴出来来,如果对云开发和云数据库还不是很了解的同学可以去翻看下我以前写的文章。
2019-11-06 - 建议web-view 打开公众号页面模板功能
- 需求的场景描述(希望解决的问题) 公众号的文章可以通过web-view直接打开,非常方便,但是公众号中配置的页面模板例如 http://mp.weixin.qq.com/mp/homepage?__biz=MzU2OTQ4NDExMw==&hid=2&sn=560faaca6ff7d87f19482ffad4b9670b&scene=18#wechat_redirect 作为展示公众号历史内容的页面,嵌入web-view却打不开,而且由于是微信官方的页面链接,没有其他办法可以通过web-view打开。 - 希望提供的能力 可以通过web-view直接打开公众号配置的页面模板,有了此功能,公众号和小程序结合起来使用会更加方便!
2019-02-19 - 小程序开发小技巧--webview
小程序开发小技巧 --webview 记录下webview 开发调试相关问题,包括常见的小tip,社区相关问题以及本地调试等基本操作。前一段打算写总结,写着发现没有什么干货,今天看见社区朋友提问 webview调试 难道搞个测试还需要去办理后台配置业务不成吗?是的一开始我就是配业务域名,不停的往服务器传包。。。历历在目所以还是梳理记录一下,希望能对社区小伙伴开发有帮助。 常见的 Bug & Tip 常见的小问题 tip:个人类型的小程序暂不支持使用。 tip: navigationStyle: custom 对 web-view 组件无效,也就是自定义导航栏。 tip: src 指向,可打开关联的公众号的文章,其它网页需登录小程序管理后台配置业务域名。 tip: 不希望webview缓存,配置NG或者src后面加随机数。 tip:保持登陆状态传递cookie、token等,简单的可以拼接URL完成。 tip:webview无法进行分享或者支付,常规操作是触发状态后,传递相关信息跳转回小程序进行下一步分享或者支付。 tip:动态改变web-view的标题,直接[代码]document.title[代码]。 tip:需要刷新webview,简单暴力点[代码]windows.onload[代码]。复杂点操作history。 tip:监听web-view返回。一般场景:分享给朋友后,朋友打开进入webview活动页,如果是直接进入的wenview需要点击返回调转至首页。 tip:先调用[代码]wx.miniProgram.navigateBack[代码]在调用[代码]wx.miniProgram.postMessage[代码]。 tip:src中带有中文字符或者特殊字符的,在 iOS 中会有打开白屏的问题,需要转码,建议加一下 encodeURIComponent。 tip: 玄学问题,引入了[代码]weixin-js-sdk[代码],但是怎么也找不到,我后面用npm安装后就ok了。 tip:网页内 iframe 的域名也需要配置到域名白名单。 tip:开发者工具上,可以在 web-view 组件上通过右键 - 调试,打开 web-view 组件的调试。 tip:每个页面只能有一个 web-view,web-view 会自动铺满整个页面,并覆盖其他组件。 tip:web-view 网页与小程序之间不支持除 JSSDK 提供的接口之外的通信。 tip:在 iOS 中,若存在JSSDK接口调用无响应的情况,可在 web-view 的 src 后面加个#wechat_redirect解决。 tip: web-view 官方文档 webview 本地调试 难道搞个测试还需要去办理后台配置业务不成吗?配业务确实特别麻烦,为什么我最初一直自己往服务器传,,,纯属装X。。。其实本地起个web服务器美滋滋。 快速的起个本地web服务。 下面是常用的几种方法、具体用法操作可以百度。 http-server 编辑器或者浏览器中的插件、例如vscode 中的 [代码]live Server[代码]。等等 动手写一个。。 我自己用的是http-server和live Server,用了很久了,能满足需求,就没有关注其它的方法了。 vue开发单页面。 本地开发完成后在打包、进入dist文件、启动http-server、浏览器打开ok、把链接写死在webview的src,像这样 [代码] let url = 'http://127.0.0.1:8080'; // let url = options.url ? options.url : wx.getStorageSync('bargainURL') [代码] 正常的话我们可以先在chrome中调试,基本完成后在进行真机调试。在chrome中可能会涉及到跨域问题,可以开启一个chrome跨域浏览器进行调试,像这样 我是mac上这样,先创建/Users/jinxm/Documents/ChromeData 目录,然后命令行跑一下下面这个命令就OK了。window自行百度就好了。 [代码]open -n /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir=/Users/jinxm/Documents/ChromeData[代码] 这个跨域用的的还蛮多的,于是我配了个输入法快捷输入,像这样👇,方便了很多。 [图片] 真机测试 上面基本ok后,webview 路径写死,手机上看效果。这个时候会不停的改动,然后就会发现不停的npm run build 、 cd dist 、http-server 也变得很繁琐了,那就简单的配置下cli,像这样👇,修改完点一下server就ok了。 [图片] 具体cil配置也就一段话搞定,像这样 ,修改package.json. 配置有点简单,这个cli主要是做单页面使用,可以忽略。使用vue-cli的自己添加个配置文件就好了 [图片] 完结 简单的问题可以私信下。有关webview相关问题欢迎大家一起讨论👏👏👏
2019-10-21 - 借助云开发10行代码生成小程序码,可以微信扫码快速进入指定小程序页
最近老有同学问我,如何生成小程序二维码,让用户扫码后能快速进入指定页面。经过一番研究,发现用云开发的云调用来实现特别方便,基本上10行代码就可以快速的生成指定页面的二维码。这样我们在做一些线下业务时就能让用户方便快速的进入到指定页面了,比如我们的点餐小程序,我们可以在二维码里绑定桌号,这样用户用微信扫码二维码,就可以快速的进入点餐页面了,并且可以识别到用户当前所在的桌号。 注意事项 我们生成的小程序码指定的页面,必须是你小程序已经发布,如果小程序还没有发布,获取小程序码绑定的页面不存在,扫码后就会出现以下错误 [图片] 所以要想使用这个功能,必须要先发布你的小程序。接下来我们就来讲下具体的实现。 一,先看官方文档 我其实说过很多遍的,官方文档永远是最好的老师。因为官方文档永远是最标准的。所以我们实现这个功能,同样要先去看下官方文档。小程序码官方文旦 [图片] 看上图的官方文档,我们可以看出,有三种方式可以生成小程序码。 A,createQRCode:生成二维码,可接受 path 参数较长,生成个数受限,数量限制 B,get:生成小程序码,可接受 path 参数较长,生成个数受限,数量限制见 C,getUnlimited:生成小程序码,可接受页面参数较短,生成个数不受限 官方提示: 接口只能生成已发布的小程序的二维码 接口 A 加上接口 C,总共生成的码数量限制为 100,000,请谨慎调用。 接口 B 调用分钟频率受限(5000次/分钟),如需大量小程序码,建议预生成 所以我们一定要结合自己的业务场景来使用不同的方法来生成小程序码或者二维码。我们这里以点餐桌号为例。因为我们一个餐厅也没有多少桌,所以我们就用接口B来实现页面和桌号的绑定。 [图片] 看官方文档,我们可以看到,我们主要使用的还是path这个参数,这个参数用来设置页面和参数。具体代码如下图。 [图片] 代码很简单,在第11行,我们给path参数设置了小程序页面和参数name。这样我们用微信扫码后,就会打开对应的页面,并且可以拿到name参数, [图片] 其实到这里我们就成功的生成小程序码了,并且可以用微信直接扫码进入到指定页面,并携带指定参数了。 接下来呢,我就把代码改造成一个简单的工具,这样大家拿到源码后就可以直接使用这个工具快速的生成小程序码了。 创建云函数 关于云开发,云函数的创建和使用,我讲过很多遍了,还不知道的同学,可以去翻看下我之前的文章,或者看下我录的云开发视频:5小时零基础入门小程序云开发 下面我们就创建一个云函数,并编写云函数,如下图 [图片] 细心的同学应该可以看到,我这里传了两个参数进来。event.path就是用来指定我们二维码绑定的页面和携带的参数,event.name就是用来给我们的二维码图片命名的。 原理:我们这里生成小程序码的原理就是使用云开发的云调用,获取二维码,然后把二维码存到云存储,这样我们就可以拿到我们需要的二维码了 再来看下调用的方法。 [图片] 再来看下我们生成的小程序码 [图片] 可以看到我们生成的1号桌对应的二维码,这样顾客用微信扫描一号餐桌的二维码时,就可以直接进入对应的页面,并且把桌号带进去了。 [图片] 这样你只需要改造下我index.js里的path和name就可以生成你想要的小程序二维码了。 [图片] 然后再到云存储里获取对应的二维码, [图片] 下载后打印出来,贴到对应的桌子上,就可以供用户扫码点餐了。还有一点要记住奥,一定是你小程序发布后,才可以使用这个功能奥。 今天就到这里把,祝大家元旦快乐,2020年学有所成,想获取更多小程序相关的知识,请持续关注。
2020-01-01 - 云开发经验总结(展示两种增删改查的方法)
开发工具mpvue官方文档 云开发初始化[代码] [代码] [代码] wx.cloud.init({[代码] [代码] [代码][代码]env: [代码][代码]'wedding-10c111'[代码][代码] })[代码] [代码] [代码] 上面这段代码配置在src目录下的main.js文件中 数据库API(不使用云函数进行增删改查)以下说明均写在对应代码注释里,不清楚的请查看相关注释 查(获取数据)[代码] [代码] [代码] // 获取轮播图列表[代码] [代码] getBannerList () {[代码][代码] [代码][代码]// 获取数据库引用[代码][代码] [代码][代码]const db = wx.cloud.database()[代码][代码] [代码][代码]// 获取名为“banner”的集合引用[代码][代码] [代码][代码]const banner = db.collection([代码][代码]'banner'[代码][代码])[代码][代码] [代码][代码]// 获取集合(Promise 风格)[代码][代码] [代码][代码]banner.get().then(res => {[代码][代码] [代码][代码]this[代码][代码].list = res.data[0].bannerList[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 对应实例如下: [图片] 注意:之所以数据库只有一条数据,而把banner列表当成这条数据的一个字段存储,其目的是为了自己后续换图操作的方便 增(添加数据)[代码][代码][代码] [代码] [代码] // 添加用户[代码] [代码] addUser () {[代码][代码] [代码][代码]// 获取数据库引用[代码][代码] [代码][代码]const db = wx.cloud.database()[代码][代码] [代码][代码]// 获取名为“user”的集合引用[代码][代码] [代码][代码]const user = db.collection([代码][代码]'user'[代码][代码])[代码][代码] [代码][代码]// 向“user”集合中添加一条数据(Promise 风格)[代码][代码] [代码][代码]user.add({[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]user: that.userInfo,[代码][代码] [代码][代码]// 构造一个服务端时间的引用,我的项目中都是取自己转化后的时间,[代码][代码] [代码][代码]// 取这个时间更加合理,可用于查询条件、更新字段值或新增记录时的字段值[代码][代码] [代码][代码]time: db.serverDate()[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]// 添加成功后重新查询列表[代码][代码] [代码][代码]that.getUserList()[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 对应实例如下: [图片] 注意: 可以看出_id和_openid是添加完自动生成的属性 改(修改数据)[代码] [代码] [代码] // 改变某条留言的显示隐藏[代码] [代码] switchMessage (e) {[代码][代码] [代码][代码]// 获取数据库的引用[代码][代码] [代码][代码]const db = wx.cloud.database()[代码][代码] [代码][代码]// 获取名为“message”的集合的引用[代码][代码] [代码][代码]const message = db.collection([代码][代码]'message'[代码][代码])[代码][代码] [代码][代码]// 这里的id是拿到当前操作项对应的id,[代码][代码] [代码][代码]// 这里的show对应change事件传递过来的值[代码][代码] [代码][代码]message.doc(e.mp.target.dataset.id).update({[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]show: e.mp.detail.value[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]console.log(res)[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 对应实例如下: [图片] 注意:这个界面在你们使用的小程序中是看不到的,只有本人才有权限查看 [图片] [代码] [代码] [代码] <[代码][代码]switch[代码] [代码]class[代码][代码]=[代码][代码]"switch"[代码] [代码]:data-id[代码][代码]=[代码][代码]"item._id"[代码] [代码]:checked[代码][代码]=[代码][代码]"item.show"[代码] [代码]@[代码][代码]change[代码][代码]=[代码][代码]"switchMessage"[代码][代码]></[代码][代码]switch[代码][代码]>[代码] [代码] [代码] 注意:上面我们之所以能得到e.mp.target.dataset.id是因为在<switch>标签上加了`:data-id="item._id"`,不然取不到对应id 删(删除数据)正好对应的上图有删除操作 [代码] [代码] [代码] deleteItem (id) {[代码] [代码] [代码][代码]// 记录this指向[代码][代码] [代码][代码]const that = [代码][代码]this[代码][代码] [代码][代码]// 这里之所以使用wx.showModal是防止误操作[代码][代码] [代码][代码]wx.showModal({[代码][代码] [代码][代码]title: [代码][代码]'提示'[代码][代码],[代码][代码] [代码][代码]content: [代码][代码]'你确定要删除这条留言?'[代码][代码],[代码][代码] [代码][代码]success (res) {[代码][代码] [代码][代码]if[代码] [代码](res.confirm) {[代码][代码] [代码][代码]// 获取数据库的引用[代码][代码] [代码][代码]const db = wx.cloud.database()[代码][代码] [代码][代码]// 获取名为“message”集合的引用[代码][代码] [代码][代码]const message = db.collection([代码][代码]'message'[代码][代码])[代码][代码] [代码][代码]// 删除操作(Promise 风格)[代码][代码] [代码][代码]message.doc(id).remove().then(res => {[代码][代码] [代码][代码]// 删除成功后再次请求列表,达到刷新数据的目的[代码][代码] [代码][代码]if[代码] [代码](res.errMsg === [代码][代码]'document.remove:ok'[代码][代码]) {[代码][代码] [代码][代码]that.getList()[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 使用云函数进行增删改查 查(获取数据)[代码] [代码] [代码] // 云函数初始化[代码] [代码] const cloud = require([代码][代码]'wx-server-sdk'[代码][代码])[代码][代码] // 由于文章开始已经讲过初始化步骤,这里init(options)的options可以省略[代码][代码] // options参数定义了云开发的默认配置,该配置会作为之后调用其他所有云 API 的默认配置[代码][代码] cloud.init()[代码][代码] // 获取数据库的引用[代码][代码] const db = cloud.database()[代码][代码] exports.main = async (event, context) => {[代码][代码] [代码][代码]// 将集合名定义成一个变量,方便后续调用[代码][代码] [代码][代码]const dbName = [代码][代码]'message'[代码][代码] [代码][代码]// filter为指定的筛选条件,配合where()使用[代码][代码] [代码][代码]const filter = event.filter ? event.filter : [代码][代码]null[代码][代码] [代码][代码]// pageNum如果小程序端未传入则默认为1[代码][代码] [代码][代码]const pageNum = event.pageNum ? event.pageNum : 1[代码][代码] [代码][代码]// pageSize如果小程序端未传入则默认是10[代码][代码] [代码][代码]const pageSize = event.pageSize ? event.pageSize : 10[代码][代码] [代码][代码]// 数据库满足filter条件的数据总条数[代码][代码] [代码][代码]const countResult = await db.collection(dbName).where(filter).count()[代码][代码] [代码][代码]const total = countResult.total[代码][代码] [代码][代码]// 共多少页[代码][代码] [代码][代码]const totalPage = Math.ceil(total / pageSize)[代码][代码] [代码][代码]// 是否有下一页[代码][代码] [代码][代码]let hasMore[代码][代码] [代码][代码]if[代码] [代码](pageNum >= totalPage) {[代码][代码] [代码][代码]hasMore = [代码][代码]false[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]hasMore = [代码][代码]true[代码][代码] [代码][代码]}[代码][代码] [代码][代码]// 等待所有,orderBy()通过创建时间排序,查询单页数据[代码][代码] [代码][代码]return[代码] [代码]db.collection(dbName).orderBy([代码][代码]'time'[代码][代码], [代码][代码]'desc'[代码][代码]).where(filter).skip((pageNum - 1) * pageSize).limit(pageSize).get().then(res => {[代码][代码] [代码][代码]// 返回结果中顺带注入hasMore和total方便小程序端判断[代码][代码] [代码][代码]res.hasMore = hasMore[代码][代码] [代码][代码]res.total = total[代码][代码] [代码][代码]return[代码] [代码]res[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] [代码] [代码] [代码] getList () {[代码] [代码] [代码][代码]// 记录this指向[代码][代码] [代码][代码]const that = [代码][代码]this[代码][代码] [代码][代码]// 每次调用getList时重新从第一页开始[代码][代码] [代码][代码]that.pageNum = 1[代码][代码] [代码][代码]// 每次调用getList时,先将authorityList置空[代码][代码] [代码][代码]that.authorityList = [][代码][代码] [代码][代码]wx.cloud.callFunction({[代码][代码] [代码][代码]// 云函数名[代码][代码] [代码][代码]name: [代码][代码]'authorityList'[代码][代码],[代码][代码] [代码][代码]// 传入云函数的参数[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]// 查询的默认筛选条件,这里可以参考下面留言审核对应的两张图来看,左上角有个switch开关[代码][代码] [代码][代码]// 当开关开启时,filter:{show:false}生效[代码][代码] [代码][代码]filter: that.checkFlag ? {[代码][代码] [代码][代码]show: [代码][代码]false[代码][代码] [代码][代码]} : [代码][代码]null[代码][代码],[代码][代码] [代码][代码]// 查询页数[代码][代码] [代码][代码]pageNum: that.pageNum,[代码][代码] [代码][代码]// 每页条数[代码][代码] [代码][代码]pageSize: that.pageSize[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]// 配合下拉刷新使用,作用是停止刷新事件[代码][代码] [代码][代码]wx.stopPullDownRefresh()[代码][代码] [代码][代码]// 以下动作为赋值操作[代码][代码] [代码][代码]const temp = res.result[代码][代码] [代码][代码]that.total = temp.total[代码][代码] [代码][代码]that.hasMore = temp.hasMore[代码][代码] [代码][代码]that.authorityList = temp.data[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 上面代码对应实例如下:1.查询未通过审核的留言;2.查询全部的留言 [图片][图片] 增(添加数据)[代码] [代码] [代码] // 前面讲解过的注释之后的代码将不重复说明[代码] [代码] const cloud = require([代码][代码]'wx-server-sdk'[代码][代码])[代码][代码] cloud.init()[代码][代码] const db = cloud.database()[代码][代码] exports.main = async (event, context) => {[代码][代码] [代码][代码]const dbName = [代码][代码]'message'[代码][代码] [代码][代码]// 添加数据[代码][代码] [代码][代码]return[代码] [代码]db.collection(dbName).doc(event.id).add({[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]desc: event.desc,[代码][代码] [代码][代码]type: event.type,[代码][代码] [代码][代码]show: event.show,[代码][代码] [代码][代码]time: event.time,[代码][代码] [代码][代码]url: event.url,[代码][代码] [代码][代码]name: event.name[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] [代码][代码][代码] [代码] [代码] sendMessage () {[代码] [代码] [代码][代码]const that = [代码][代码]this[代码][代码] [代码][代码]if[代码] [代码](that.desc) {[代码][代码] [代码][代码]wx.cloud.callFunction({[代码][代码] [代码][代码]// 云函数名[代码][代码] [代码][代码]name: [代码][代码]'addMessage'[代码][代码],[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]desc: that.desc,[代码][代码] [代码][代码]type: [代码][代码]'message'[代码][代码],[代码][代码] [代码][代码]show: [代码][代码]false[代码][代码],[代码][代码] [代码][代码]time: utils.getNowFormatDate(),[代码][代码] [代码][代码]url: that.userInfo.avatarUrl,[代码][代码] [代码][代码]name: that.userInfo.nickName[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]// 关闭所有页面,打开到应用内的某个页面,跳转到留言列表页[代码][代码] [代码][代码]wx.reLaunch({[代码][代码] [代码][代码]url: [代码][代码]'/pages/message/main'[代码][代码] [代码][代码]})[代码][代码] [代码][代码]})[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]tools.showToast([代码][代码]'说点什么吧~'[代码][代码])[代码][代码] [代码][代码]}[代码][代码] }[代码] [代码] [代码] [代码][代码] 对应实例如下: [图片][图片] 改(修改数据)[代码] [代码] [代码] const cloud = require([代码][代码]'wx-server-sdk'[代码][代码])[代码] [代码] cloud.init()[代码][代码] const db = cloud.database()[代码][代码] exports.main = async (event, context) => {[代码][代码] [代码][代码]const dbName = [代码][代码]'message'[代码][代码] [代码][代码]return[代码] [代码]db.collection(dbName).doc(event.id).update({[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]show: event.show[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] [代码] [代码] [代码] switchMessage (e) {[代码] [代码] [代码][代码]const that = [代码][代码]this[代码][代码] [代码][代码]wx.cloud.callFunction({[代码][代码] [代码][代码]name: [代码][代码]'switchMessage'[代码][代码],[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]id: e.mp.target.dataset.id,[代码][代码] [代码][代码]show: e.mp.detail.value[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]if[代码] [代码](res.result.errMsg === [代码][代码]'document.update:ok'[代码][代码]) {[代码][代码] [代码][代码]that.getList()[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 对应实例如下:(前面没使用云函数也实现了相同的功能,感兴趣的可以对比查阅) [图片] 删(删除数据)[代码] [代码] [代码] const cloud = require([代码][代码]'wx-server-sdk'[代码][代码])[代码] [代码] cloud.init()[代码][代码] const db = cloud.database()[代码][代码] exports.main = async (event, context) => {[代码][代码] [代码][代码]const dbName = [代码][代码]'message'[代码][代码] [代码][代码]return[代码] [代码]db.collection(dbName).doc(event.id).remove()[代码][代码] }[代码] [代码] [代码] [代码] [代码] [代码] deleteItem (id) {[代码] [代码] [代码][代码]// 记录this指向[代码][代码] [代码][代码]const that = [代码][代码]this[代码][代码] [代码][代码]// 这里之所以使用wx.showModal是防止误操作[代码][代码] [代码][代码]wx.showModal({[代码][代码] [代码][代码]title: [代码][代码]'提示'[代码][代码],[代码][代码] [代码][代码]content: [代码][代码]'你确定要删除这条留言?'[代码][代码],[代码][代码] [代码][代码]success (res) {[代码][代码] [代码][代码]if[代码] [代码](res.confirm) {[代码][代码] [代码][代码]wx.cloud.callFunction({[代码][代码] [代码][代码]name: [代码][代码]'deleteMessage'[代码][代码],[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]id[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]if[代码] [代码](res.result.errMsg === [代码][代码]'document.remove:ok'[代码][代码]) {[代码][代码] [代码][代码]that.getList()[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] }[代码] [代码] [代码] 对应实例如下: [图片] 总结掌握上面两种对应的增删改查后,相信大家对云开发会有一个更清晰的认识,也希望大家多多使用云开发做出更多好玩的小程序作品; 如果觉得看完这篇文章让你有想尝试使用云开发的冲动,请不要吝啬你的赞,有什么问题欢迎留言,一起交流学习。 对应小程序 欢迎大家体验: [图片] 日记小程序 [图片] 小程序订制加本人微信:huangbin910419 可按给定UI图订制
2019-11-06 - 总结的一些血与泪的教训
云函数 云函数的上传流程 云函数必须先保存后上传,因为编译器不会在上传的时候自动保存。 保存的方法可以是CTRL+S或者是编译都可以,CTRL+SHIFT+S和编译是等效的;上传的方式可以是对文件的增量更新或者说部署云函数。主要是顺序必须先保存后上传。 云函数部署对于触发器并没有效果,触发器得单独上传(仍然需要先编译)。 如何知道云函数是否执行出错了 云开发→云函数→日志,读取日志,可以读取服务器返回的数据,所以云函数中最好用try{}catch(e){}结构,然后return JSON.stringify(e),当然你不这样也没问题,因为服务器会自动吧错误写进日志 云函数中console.log()的内容会写在日志的末尾。 当然,你也可以使用云函数的本地调试功能(来自Littlesnail大佬的分享),但是本地环境是缺少wx-server-sdk这个包的,需要自己用npm装,大家可以自行衡量。 如何知道云函数是否真正执行完了 之前一直在python平台的我根本没接触过node.js这种异步事件驱动的语言,被异步快整疯了 首先说结论,你得把所有返回Promise类型的方法都加上await关键字,并函数变为async类型。 所谓的Promise类型是指的一个虚的对象,他传递给某些函数后,对那些函数进行一个“保证”,保证过会会给那些函数数据。因此那些函数会先等着,等Promise返回数据后,提醒这些函数开始工作了,然后上一个工作完提醒下一个直到函数执行完。因为有些操作,比如数据库的读取是需要时间的,所以要是一直等着会阻塞整个线程。(node.js似乎是单线程的,这算是一直对多线程的补偿方案吧) Promise对象有三种状态。Pending,表示正在运行中;fulfilled,表示完成然后会自动提醒下一个函数;rejected,表示失败,当然如果你用try catch了就没事。 重点来了,对于async的云函数来说,执行到最后或者return就相当于结束了,然而这个时候如果函数内仍然有处于“保证中”(Pending)的Promise对象,云函数是不会等它执行完才返回的,它会直接返回。所以你必须加一个关键字,await,表示下面的语句都会等着这个异步操作完成再工作,也就是阻塞住。这样才能真正使得云函数完全执行完。 如何判断什么函数返回的是Promise对象,在开发者工具的console里面输入一下这个函数就行了,实在不行typeof。 PS:以前我曾经想过,如果吧云函数的async标签去掉,是否能实现不需要await也能正常运行呢,答案是,你想的美,去掉后人家该Promise还是Promise,不会给你变成现实(特朗普:恶意的zz隐喻,举报了)。所谓的async标签是指的我警告你我这玩意是异步的,你能用then,但是大多数情况你都不会去用then,所以,老老实实await吧。 你们就不要挣扎了,老老实实吧所有出现异步的位置都async和await吧 你标了async的函数,调用它的函数也得加async哦,而且调用的时候也得await 【async-await地狱】 小细节 云函数的运行环境中你不能获取到函数的信息,this.name,this.toString(),或者window.decodePathName都是undefine 数据库 为什么有时候数据库的get没用请确定你的权限是否有误,在 云开发控制台-数据库-权限设置 中可以调整,一般来说选第一个权限 为什么有时候数据库的update没用 首先当然是排查是不是因为在云函数中你却没有加await,数据库的几乎所有操作全是异步的,返回的基本都是Promise对象,另一点是小程序中的db.collection('seats')对象是动态对象,你不能这样写 db_seats=db.collection('seats') db_seats.where({}) 如果你在下面的语句继续用db_seats来直接对数据库做一些操作,相当于你创建了一个数据库的本地副本,你get()数据当然是有用的,但是可能不是最新的数据,但是你add()和update()都是没用的。你必须每次都用db.collection('xxx')来调用xxx数据库。 (实际上后来经过调查发现,add有时候是可以的,但是update保证每次都不行,不知道为啥) ————(以下为更新内容)———— 事实上还有一种更加广泛的错误 一定要注意,doc函数查到的是数据库的索引的_id字段,而不是_openid字段,你拿openid去doc一定找不到东西。 所以如果你一开始只有openid这个数据,那你最好先用where找到对应数据的_id,然后再doc它的id (为什么不直接where后接update?where返回的是一个数组,where接update相当于一次性修改一组数据,只有服务器端,也就是云函数有这个权限,小程序的前端只能先查询再修改) 数据库传入的数据 只有数据库的where(),update(),add(),传入的JSON数据中,只有data是必选属性,其他属性像是success,fail都是非必要的,如果你不用错误处理的话就不用加 then怎么用 数据库的then(),或者说所有的异步操作的then,你把函数放在then()里面表示到时间后完成某项操作,比如 //获取所有data const seats_list = await db.collection('seats').where({ used: true }) .get().then(res => { // return res.data console.log(res.data) }) 相当于吧收到的参数命名为res,然后等res异步返回后,会在=>{}里面处理一些只跟res有关的操作,这当然是有用的,但是问题在于then里面是一个闭包,你console.log当然是可以的,但是你要在里面用this.setData()就不行了,这个闭包的this可不是指的你这个外面的对象,他就是指的它点前面的对象,也就是get()后返回的对象。 所以想在外面用的话,正确操作是吧注释去掉,也就是return res或者return res.data,然后这样的话,外面的seats_list 就能被赋值为res或者res.data了,然后接下来的语句尽情对seats_list 操作,千万别忘了加await,否则你懂的。 WXML 为啥我的wx:if不管用 请注意一件事,那就是。。。WXML里面的任何函数部分,都是不能有空格的,比如这样写 点击选座位 点击选座位 点击选座位 点击选座位 点击选座位 无论你空格加在哪里,只要被""括起来,加空格都会使得wx:if失效,相信小伙伴们此时已经反应过来了,没错,这就是JS那个傻逼机制,非空字符串相当于true > Boolean(" ") > true 我猜wx:if的机制只是简单的做了下拆分,然后配对了下,并不能实际把它当程序运行(甚至可能正则都没用),所以如果遇到了它没拆成功的字符串,它就干脆暴力返回原字符串,当然被识别为true了。 整体 Js的所有变量都需要预先var数据库的Data和云函数的Data JS的Date()函数很迷,它返回的是tm一个String,当前时间的String,你必须用var new才能构造一个真正的Date对象而不是一个字符串,也就是按照下面方法使用,加注释的都是错误的用法。 var nowdate=new Date() // Date().getDate() // Date(一个Date对象).getDate() // Date(一个字符串).getDate() 另外就是无论你在哪调用这个函数,他创建的都是你调用端的时间,而不是云端的时间,就算你在云函数调用也一样,你电脑或者手机是几点他创建的就是几点,我都服了。 云端的时间只能用db.serverDate()来构建,但是问题在于db.serverDate()是tmd一个指令,他返回的不是一个Date,而是类似db.commend那种类型的东西,给传入服务器的data用。 //更新nowdate为服务器时间 await db.collection('seats').where().update({ data:{ nowdate:db.serverDate() } 也就是你平时用是没JB用的,你只能老老实实去用JS的Date方法请求世界时然后倒时区。 总结 以后想到了继续补充,欢迎大佬分享
2020-05-11 - 小程序读取excel表格数据,并存储到云数据库
最近一直比较忙,答应大家的小程序解析excel一直没有写出来,今天终于忙里偷闲,有机会把这篇文章写出来给大家了。 老规矩先看效果图 [图片] 效果其实很简单,就是把excel里的数据解析出来,然后存到云数据库里。说起来很简单。但是真的做起来的时候,发现其中要用到的东西还是很多的。不信。。。。 那来看下流程图 流程图 [图片] 通过流程图,我看看到我们这里使用了云函数,云存储,云数据库。 流程图主要实现下面几个步骤 1,使用wx.chooseMessageFile选择要解析的excel表格 2,通过wx.cloud.uploadFile上传excel文件到云存储 3,云存储返回一个fileid 给我们 4,定义一个excel云函数 5,把第3步返回的fileid传递给excel云函数 6,在excel云函数里解析excel,并把数据添加到云数据库。 可以看到最神秘,最重要的就是我们的excel云函数。 所以我们先把前5步实现了,后面重点讲解下我们的excel云函数。 一,选择并上传excel表格文件到云存储 这里我们使用到了云开发,使用云开发必须要先注册一个小程序,并给自己的小程序开通云开发功能。这个知识点我讲过很多遍了,还不知道怎么开通并使用云开发的同学,去翻下我前面的文章,或者看下我录的讲解视频《5小时入门小程序云开发》 1,先定义我们的页面 页面很简单,就是一个按钮如下图,点击按钮时调用chooseExcel方法,选择excel [图片] 对应的wxml代码如下 [图片] 2,编写文件选择和文件上传方法 [图片] 上图的chooseExcel就是我们的excel文件选择方法。 uploadExcel就是我们的文件上传方法,上传成功以后会返回一个fildID。我们把fildID传递给我们的jiexi方法,jiexi方法如下 3 把fildID传递给云函数 [图片] 二,解下来就是定义我们的云函数了。 1,首先我们要新建云函数 [图片] 如果你还不知道如何新建云函数,可以翻看下我之前写的文章,也可以看我录的视频《5小时入门小程序云开发》 如下图所示的excel就是我们创建的云函数 [图片] 2,安装node-xlsx依赖库 [图片] 如上图所示,右键excel,然后点击在终端中打开。 打开终端后, 输入 npm install node-xlsx 安装依赖。可以看到下图安装中的进度条 [图片] 这一步需要你电脑上安装过node.js并配置npm命令。 3,安装node-xlsx依赖库完成 [图片] 三,编写云函数 我把完整的代码贴出来给大家 [代码]const cloud = require('wx-server-sdk') cloud.init() var xlsx = require('node-xlsx'); const db = cloud.database() exports.main = async(event, context) => { let { fileID } = event //1,通过fileID下载云存储里的excel文件 const res = await cloud.downloadFile({ fileID: fileID, }) const buffer = res.fileContent const tasks = [] //用来存储所有的添加数据操作 //2,解析excel文件里的数据 var sheets = xlsx.parse(buffer); //获取到所有sheets sheets.forEach(function(sheet) { console.log(sheet['name']); for (var rowId in sheet['data']) { console.log(rowId); var row = sheet['data'][rowId]; //第几行数据 if (rowId > 0 && row) { //第一行是表格标题,所有我们要从第2行开始读 //3,把解析到的数据存到excelList数据表里 const promise = db.collection('users') .add({ data: { name: row[0], //姓名 age: row[1], //年龄 address: row[2], //地址 wechat: row[3] //wechat } }) tasks.push(promise) } } }); // 等待所有数据添加完成 let result = await Promise.all(tasks).then(res => { return res }).catch(function(err) { return err }) return result } [代码] 上面代码里注释的很清楚了,我这里就不在啰嗦了。 有几点注意的给大家说下 1,要先创建数据表 [图片] 2,有时候如果老是解析失败,可能是有的电脑需要在云函数里也要初始化云开发环境 [图片] 四,解析并上传成功 如我的表格里有下面三条数据 [图片] 点击上传按钮,并选择我们的表格文件 [图片] 上传成功的返回如下,可以看出我们添加了3条数据到数据库 [图片] 添加成功效果图如下 [图片] 到这里我们就完整的实现了小程序上传excel数据到数据库的功能了。 再来带大家看下流程图 [图片] 如果你有遇到问题,可以在底部留言,我看到后会及时解答。后面我会写更多小程序云开发实战的文章出来。也会录制本节的视频出来,敬请关注。
2019-11-12 - 将小程序原生异步函数promisify后,在async/await中使用
目前,小程序中支持使用async/await有三种模式: 1、不勾选es6转es5,不勾选增强编译;该模式是纯es7的async/await,需要基础库高版本。 2、勾选es6转es5,勾选增强编译;一般是因为调用了第三方的es5插件,通过增强编译支持async/await。 3、勾选es6转es5,不勾选增强编译;手工引入runtime.js支持async/await。 据最近更新情况,原生的函数已经大部分同时原生支持同步化了,不需要本方案转化了,直接加上await即可;比如wx.chooseImage、wx.showModal。。。具体有哪些,可以自己试。 如果只是wx.request的同步化,可参考: https://developers.weixin.qq.com/community/develop/article/doc/0004cc839407a069f77a416c056813 app.js代码: function promisify(api) { return (opt, ...arg) => { return new Promise((resolve, reject) => { api(Object.assign({}, opt, { success: resolve, fail: reject }), ...arg) }) } } App({ globalData: {}, chooseImage: promisify(wx.chooseImage), request: promisify(wx.request), getUserInfo: promisify(wx.getUserInfo), onLaunch: function () { }, }) 某page的index.js代码: const app = getApp() testAsync: async function(){ let res = await app.chooseImage() console.log(res) res = await app.request({url:'url',method:'POST',data:{x:0,y:1}}) console.log(res) }, [图片]
2020-10-20 - 云开发读取数据库中的数据,要等待一会,下面执行的代码获取不到数据,怎么解决?
比如说我要读取云开发数据库里面的数据,把这个数据赋值给数据,在对数据进行其他的操作,可由于是异步的,云开发数据还没获取到,下面的代码就开始执行了,会出错,怎么解决
2019-08-15 - 【学习心得】小程序云端开发——数据库连接
本人UI设计师一枚,熟悉HTML和CSS,为开发小程序大小小小基础,之前也学习好一阵子的JS,摸了摸nodejs,感觉非常迷茫,最近无意中发现微信小程序是基于js开发,内心狂喜,终于有检验学习成果的工具了,决心做一款简单的类新闻发布应用试试水。 界面UI设计和布局基本没难度,只要懂HTML和CSS,对应的文件分别是【index.wxml - index.html】,【index.wxss - style.css】,重要的是处理逻辑功能的index.js文件,可以说是开发微信小程序的核心。 学习过程没什么难的,主要是注意代码结构,括号一多起来容易乱,幸好右键代码块可以格式化。 回归正传,微信小程序开发平台提供一个非常方便前端开发者的工具——云开发,如何使用并且读取数据库数据,是学习后台操作的入门课程。首先注册开发者账号这些不必说,拥有openid才能建立有云开发功能的小程序,点击开发工具左上角的云开发。 [图片] 弹出如下界面[图片] 右边显示当前环境xxxxx,这个相当于独立的一个数据库,数据库内可以创建数据表,和关系型数据库不同,这里的数据如下图打比方 [图片] 通过创建数据库之后,我们需要在app端来读取数据库的数值,这里展示如何读取books这张表里的数据,在读取数据之前,我们有必要先修改一下数据表的权限,要不然无法读取成功。 [图片] 文件结构按照默认的即可 [图片] 具体代码如下 <!–index.wxml–> <view>Database Test</view> <view> <block wx:for="{{books}}" wx:key="{{index}}"> <view>{{item.name}} - {{item.author}} - {{item.price}}</view> </block> </view> <view>标签相当于html中的div,用于块状地显示内容,<block>相当于隐藏版的div,这里用来循环获取到的数据,并且将循环到的数据显示出来。 //index.js // 初始化环境 wx.cloud.init({ env: ‘xbgczj’ }); // 实例化数据库 const db = wx.cloud.database({}); Page({ data: { books: [], // 定义一个books空数组,用于存储数据 blnShow: true }, // 使用onShow或者onLoad函数执行代码,抓取数据 onShow: function () { // 实例化数据表 var _books = db.collection(“books”); // 声明 var that = this; // 将获取到到数据,保存到books[]数组中 _books.get().then(res => { that.setData({ books: res.data, blnShow: false }) }) }, }) 最终结果输出 [图片]
2019-06-11