- 小程序流量主运营技巧
前言(写给入坑的小白) 本文不涉及任何需要资质的小程序(如:视频类目)。小程序流量主是个人和小微企业主要变现途径之一,满1000人即可开通流量主(登录mp.weixin.qq.com,左侧边栏-推广-流量主-开通即可)。开通后,开发者可从流量主-广告位管理添加广告位,目前有6种广告位。 [图片] 正文(本文约很多字,分为四大主类,手里有1-10个小程序建议全部看完;手里有10个以上小程序,可跳过1、2、3,均为个人观点,不喜使劲喷) 一、小程序定位 小程序定位目前有以下四种,均不需要任何资质,个人(商城除外)/小微企业都可以做,由于本人不擅长文字表达,每个类型只选择一个做分析,谅解。 1、工具类 工具类有很多可以做:题库、技术文档、教程、去水印等。目前最火爆的应该属于疫情相关类的工具,关于疫情数据类小程序不做分析,没资质也没权利,主要说疫情周边可运营的工具。头像口罩,代表小程序:头像加口罩、戴个口罩吧、戴上口罩(每日搜索量约等于2000),可参考以下做法: [图片] 以下为近7日访问数据量 [图片] 盈利方式:流量主 延伸参考:如果仅做头像加口罩的话,那么疫情过后,这个小程序会直线下降,将无任何作用。如果开发者手里目前有类似小程序,可参考“头像加口罩”做法,逐渐去延伸头像周边功能,例: ①、头像加字:头像+数字、头像加V、头像加字、头像加圣诞帽、新年头像边框、头像加福、头像加明星等 ②、聊天背景图、壁纸:武汉加油、卡通、美女(不要漏点太多)、二次元、跑车、科技等 ③、趣味九宫格配图:类似朋友圈9张图,中间获取用户自己头像,周围8张图弄点能吸引用户的等 ④、文字秀:微信昵称下标、上标、个性昵称等 运营分析:如果参考以上4点做法,首先你的程序再疫情结束后,不至于直线下滑,最起码能留住一些用户(UI很重要) 个人建议:工具类的好处就是不需要去长时间盯着后台,建议有想法的开发者,可以入门5-10个左右工具类小程序(功能不要相同)。 推广方式:参考本文第四大板块内容 2、返利类 主流返利平台:淘宝、天猫、拼多多、京东、蘑菇街、唯品会、网易考拉,以下参考 [图片] 盈利方式:返利(主)+流量主(辅) [图片] 基础分析:每个人微信里都会有一个或多个微信群是给你们购物优惠券链接的,他们盈利方式主要是靠每个平台的返利,比如淘宝天猫的叫“阿里妈妈”、拼多多的叫“多多进宝”等 运营分析: ①、平台功能:提供所有优惠券、商品返利、代理入驻、提现(个人可做收款码、企业可对接微信支付到零钱) ②、招代理商、可以给代理商(兼职、宝妈)50%以上的返利 ③、除了商品优惠券之外,可以把返利分给一部分给到用户。首先,用户会花更少的钱买到商品;其次,用户买完东西还会赚点小钱,每个月可提现到微信零钱。这样用户会发生裂变,省钱+赚钱。 个人建议:开发者至少有一个类似的返利小程序,每个月只需运营一天,工作内容一是把用户的返利发给用户&代理商,二是自己去各大平台领取每个月的“工资” 推广方式:参考本文第四大板块内容 3、商城类(个人开发者可跳过) 商城类,本人运营的比较少,每天就10-20单左右,卖啥就不做广告了 盈利方式:差价 基础分析:如果自己手里有一些商品低价资源,可以做一个“综合服务商城类目”,然后去试着用广告主去推一下 运营分析: ①、平台功能:砍价、返利、拼团、回购、入驻、积分、抽奖、游戏营销 ②、广告主曝光&点击报价不要最低,也不要最高,理由就是最低的话,80%的钱会给你推到一些质量很差的微信用户,比如我。 ③、对接圈子,虽然圈子刚起步,不确定能不能做大,万一呢? 个人建议:企业一定要有一个自己的商城,哪怕没人买。这种东西怎么说呢,就好比一个企业站,虽然没什么用,但是得放那儿,万一客户要看呢? 推广方式:参考本文第四大板块内容 4、游戏类(非小游戏) 答题、成语、找茬等类似运营的比较多,可自行搜索,不要认为这是游戏,开发者就望而却步,在线教育类目是可以通过的,这个开发者很多都不知道。以下可参考: [图片] 盈利方式:流量主 基础分析:基本所有的模式都是闯关类型,这种类型的小程序,基本都是用户消磨时间用 运营分析:关卡尽量多,入门、初级、中级、高级,高级模式可以做类比循环,形成无限关卡模式,闯关奖励机制,签到机制等。这种类型的小程序比较方便运营,裂变起来也快。 个人建议:裂变模式一定要有,虽然微信会严格把控这方面功能,但是开发者可以做一些技巧,不要让用户强制或者主动去触发,这样微信对开发者还是很友好的。 推广方式:参考本文第四大板块内容 二、小程序开发 有实力的开发者,自己开发,云开发很快,会前端就可以了,没实力的去正规平台买源码,论坛源码也很多,有部分论坛还是嵌入了比特币勒索,自己做好防护。个人建议:开发者能开发尽量自己开发,后期迭代方便,不要像我一样,50多个小程序80%是买现成去运营的。反正各有各的好处,开发者可自行决定,运营者可选择直接购买源码直接上线运营,前提是自己看好功能是不是和自己要的一样。有些SAAS平台的开发者实力还是可以的,支持定制功能。此处不做广告,自行搜索或者询问朋友。 三、广告位位置及利润 开发者的每个页面广告位一定要分开!一定要分开!一定要分开!这样做的目的是为了分析每个广告位的利润,好去做调整,把收益最大化。 失败案例举例:小程序的主页、个人中心页用同一个banner广告位,这样做出来一点好处都没有,后台只能看到banner收益是多少,看不到是哪个页面收益。极端情况,收益全部再首页,个人中心页没有广告收益,这种情况开发者是不知道的,如果把广告位分开,这种情况可以去优化个人页面,或者主页面换成视频banner。广告位分析页面:流量主--数据统计--广告数据--广告指标明细--细分数据 [图片] [图片] 1、很多人表示,疫情期间流量主收入下滑。这个原因不是因为微信调整流量主收益,根本问题是自己的用户质量。举个例子,当你开通流量主之后,你的用户还是这1000个,假如你第一天收益为100,你很开心,1000用户就能赚100,你第二天就放弃推广了,这样的话,你的用户质量是会逐渐下滑,微信方完全可以认定为你这1000人都是自己的号,去刷广告费的。长此以往下去,你的流量主利润会无限趋向于0。举个栗子: [图片] 2、广告位位置一定要合理好看,但是不代表“流氓”,比如全明星代言的某游戏“元宝无限收一刀999”点哪儿哪充值。开发者需要注意的是小程序的质量,需要用户在每个页面停留的时长最起码30秒,这样一个完整的视频广告才能曝光完。 3、banner广告收益是按有效点击计算的。很多人好几千曝光,但是点击只有几个、十几个,这种情况需要不断去优化接入的场景/位置,提高用户点击意愿。个人技巧:banner广告位尽量不要太多,1-2个就可以。尽量多放几个视频广告位,这样曝光也有收益。格子广告没试过,用过都说不好~ 4、激励广告作为流量主最高收益是有一定道理的,用户为了获取某些奖励是必须观看完整的,所以给开发者建议:用户如果可以获得小程序内某些奖励,可以适当多放一些激励广告位。 5、所有的广告位都是根据用户年龄、爱好等参数去调取相应的广告,开发者不需要去考虑 6、广告收益个人认为:激励》视频》插屏》前贴》banner》格子(格子没试过,暂放倒数第一) 四、小程序推广 尽量做成年人主打的小程序,有些开发者觉得好玩儿,做一些儿童益智类的小程序,你是认为儿童有手机,还是认为家长愿意让孩子玩儿手机呢?这个很不解。没有鄙视的意思,也许是情怀吧~~毕竟我做小程序比较俗,就是为了赚钱。 主流推广方式:公众号引流、截流,由于涉及一些不合常规的内容,本文只说常规操作,剩下的自己领悟,或者可以联系我~ 首先小程序的名字至关重要,一个好的名字可以带来无限的流量,再加上裂变功能(邪恶的微笑)。起名字的时候可以用到的工具:搜索小程序-微信指数,查询关键字,尽量找稳定再1000万以上的搜索量,从关键字中摸索自己的小程序名字。这样用户搜索到你的小程序几率会很高~ 1、工具类核心玩儿法(适用于所有小程序推广):文章引流,截取关键字,火爆主题,比如2019年12月19日庆余年全集泄露、2020年疫情(不要发疫情数据内容,要发一些正能量的有内容文章去引流),我阅读过的文章最低的阅读量8000左右,最高的10万+,据说有好几百万的阅读量。如果你的文章写的好,结尾放一个小广告:为防止疫情蔓延,请给您的头像带上口罩~,啪,一个卡片小程序(或二维码),流量自己想~ 推广对象:18-30岁 2、返利类核心玩儿法: ①、可以参考工具类玩儿法 ②、各大微信群、QQ群,去推广,招代理等方式,或者去买一些基础流量,进行裂变,实际运营看下效果,好继续针对用户群体去推广,建立自己的群体系,群内发商品返利链接。微信好友没人?给你举个例子,我这篇文章发完,如果加个我的二维码,最起码能有100人加我,不是我文章写的有多好,是你永远不知道用户有什么样的目的和需求~ 推广对象:18-60岁 3、商城类核心玩儿法 ①、可参考返利类核心玩儿法,拥有自己的客户群体系,发一些自己的商品还是可以的,一定要带分销体系,你懂得~(最高3级,再高就是传销了) ②、广告主、目前效益个人感觉不明显,每次花1000块钱做广告,利润基本没有,和发广告的钱持平,而且用户留存也不是很高,可能是我的商品比较单一等各方面因素吧,不过赚流量还是不错的。 推广对象:18-30岁(以我的商城为例,还需看商城出售的内容) 4、游戏类核心玩儿法(非小游戏) ①、一个好的名字就够了。举例:精选商品橱窗(腾讯官方),微橱窗(我朋友的)。不得不说,这波流量很高,遗憾的是,他不是火爆的游戏类小程序~ [图片] ②、参考工具类玩儿法,文章引流截流 推广对象:18-40岁 五、小程序矩阵 矩阵一定要有,矩阵一定要有,矩阵一定要有,防截流,底配10个小程序。不是纯矩阵,是微信开发规定,每个小程序可以跳转10个小程序,开发者可以利用这个功能去添加自己的矩阵来获取更多的流量收益,保证自己的用户在自己的矩阵圈活动。 [图片] 写这篇文章主要是给大家传授经验,希望小白能学到点东西,入门后的朋友可领悟到更多运营方法,江湖之大,附月账单有缘再见 [图片]
2020-05-25 - 小程序登录、用户信息相关接口调整更新说明
考虑到近期开发者对小程序登录、用户信息相关接口调整的相关反馈,为优化开发者调整接口的体验,《小程序登录、用户信息相关接口调整说明》公告中关于小程序回收 wx.getUserInfo 接口可获取用户授权的个人信息能力的截止时间调整至2021年4月28日24时。 在此期间,未调整的小程序可能会在微信开发者工具收到“平台 getUserInfo 接口能力调整,请尽快适配”提醒,建议开发者尽快适配 wx.getUserInfo 接口回收场景。 后续开发者可以使用 wx.getUserProfile 接口获取用户授权的个人信息。 关于本次调整的Q&A Q:已经在线上运行的小程序是否会受到影响? A:不会。只有在2021年4月28日24时后,新提交审核发布的小程序调用 wx.getUserInfo 接口才会对齐该新的匿名表现。 Q:此次变更除 wx.getUserInfo 接口回收获取用户授权的个人信息能力还有什么变化? A:通过 wx.login 接口获取的登录凭证可直接换取 unionid,无需再使用 wx.getUserInfo 接口来兼容获取 unionid。 Q:如何保证不同版本的基础库环境下都能正常获取用户信息? A:2.10.4 以下基础库不支持使用 wx.getUserProfile 接口 获取用户信息,采用wx.getUserInfo 接口原有获取流程不受影响,兼容方式可参考 getUserProfile 接口文档中的示例代码 进行适配。 Q:PC版本小程序是否支持? A:PC版本小程序暂未支持,可以直接使用 getUserProfile 接口文档中的示例代码 来判断及适配。 Q:wx.getSetting 接口返回是否有变化? A:wx.getSetting 接口获取的 scope.userInfo 按照调用 wx.getUserInfo 接口返回,返回匿名数据 scope.userInfo 也为 true。此策略将于2021年4月28日24时后生效。 Q:wx.getUserProfile 接口返回的 iv、encryptedData 不可用? A:wx.getUserProfile 接口返回的 iv、encryptedData 解密获取不到 unionid,请使用 wx.login 获取 unionid;wx.getUserProfile 接口在基础库2.16.0以下,无法获取 iv 和 encryptedData ,近期会灰度覆盖到 2.10.4。 Q:在适配过程中遇到其他问题? A:可直接在 微信开放社区 搜索或发帖提问交流。
2021-04-15 - 抽奖类小程序,具体抽奖逻辑如何实现
本文背景本人运营一个抽奖类小程序已步入正轨,期间虽然也出过大大的问题,好在吃一堑长一智,现在一切都比较稳定,特别是在抽奖环节。 本文内容本文依托我运营的小程序,来分享下在具体抽奖环节的逻辑是如何实现的 首先要说下目前小程序的实现机制,目前抽奖小程序主要有三步 (1)开~奖、所谓开奖就是将当前奖项根据时间,从未开奖,标记为可开奖状态 (2)抽~奖、所谓抽奖就是,在可开奖的奖项里面,根据当前奖项参与的用户,以及奖品设置,把具体的奖项给对应的某个参与用户 (3)推~送、所谓推送就是在开奖完成后,推送订阅消息给所有参与抽奖的用户 对应这三步,该小程序有三个核心的云函数 (1)run,触发器,每个整点的1分开始执行,具体逻辑是根据当前时间和开奖时间进行比较,如果当前时间大于开奖时间,那么标记状态位为可开奖 (2)draw,触发器,每个整点的5分开始执行,具体抽奖的逻辑,也是本文具体分享的环节 (3)sendmore,触发器,每个整点的10分开始执行,进行推送订阅消息 f 本文的重点是在上面的第二步 在具体实现抽奖的逻辑,本文分享两个,所谓抽奖无非就是根据奖项设置的奖品个数随机从参与用户那里选取两个用户,这里注意一个关键词,是随机 随机就代表公平,这是该小程序的核心 方法1、云函数的sample 这个是云开发里面提供的随机检索的函数,小程序云开发支持 方法2、第三方库的suffle http://underscorejs.org/ [图片] [图片] 本文总结本文通过分享抽奖类小程序核心逻辑场景,然后给出具体抽奖环节的解决方案以及具体代码
2021-01-10 - 成语答题小程序云开发记录系列一
近来微信成语答题小游戏,已经成了大家休闲娱乐的必打卡点了,在这里有很多个关卡供你挑战,消遣时间还能增长知识, 本文背景在微信成语答题小游戏中排第一位的当属 成语小秀才 《成语小秀才》是一款由非常火爆的微信小游戏成语升官记改名而来的,游戏玩法简单,让人爱不释手 成语小秀才属于小游戏范畴,对于初学者很难驾驭,但是其他成语答题小程序却有很多功能相对简单的,比如微擎版本的成语答题赚小程序 本次学习主要参考该版本小程序 本文内容本文从学习的角度讲新版本成语小程序即微擎成语答题赚小程序用云开发进行仿写, 本次仿写仅为学习,不具有商业价值 仿写的小程序主要功能: 闯关答题,激励视频,图片广告,奖品兑换,由于红包对于个人小程序不支持,所以答题领红包没有在本次的仿写中实现 先来释放几张截图 截图一 [图片] 截图二 [图片] 截图三 [图片] 截图四 [图片] 本文总结最近这个开发任务己接近尾声,其实对于成语答题而言终归属于答题的范畴,所以对我而言,后面的数据库设计其实是很容易切换过来的
2020-10-10 - 开源的抽奖助手小程序
发现开源小程序之美三-抽奖助手小程序 发现开源小程序之美一,个人博客小程序 https://developers.weixin.qq.com/community/develop/article/doc/000a40e13ec550274e2a9addd56413发现开源小程序之美二,微慕WordPress小程序 https://developers.weixin.qq.com/community/develop/article/doc/000c44945dc728ab9c2aff2a55b013发现开源小程序之美三,抽奖助手小程序 https://developers.weixin.qq.com/community/develop/article/doc/0002846854056847b66a2d13451013发现开源小程序之美四,在线答题小程序 https://developers.weixin.qq.com/community/develop/article/doc/00040af07005609a223acee0151413发现开源小程序之美五,营销组件库 https://developers.weixin.qq.com/community/develop/article/doc/000c4235c98740a1dc2a1a6045b013发现开源小程序之美六,酱茄小程序 https://developers.weixin.qq.com/community/develop/article/doc/00040ede6d0388082a3aeb49b57813发现开源小程序之美七,二手书商场 https://developers.weixin.qq.com/community/develop/article/doc/0006ceb61a87182a4b3a1b32a5bc13发现开源小程序之美八,我要戴口罩https://developers.weixin.qq.com/community/develop/article/doc/0006a047b0cee0d5713ad731f5b813发现开源小程序之美九,失物招领小程序 https://developers.weixin.qq.com/community/develop/article/doc/000ca6a3b28ce8857b5a8bb3351c13发现开源小程序之美十,旅游攻略方面的微信小程序 https://developers.weixin.qq.com/community/develop/article/doc/000cc694e9c790ce755aee41556013 这个小程序是身边小伙伴开发的,基于云开发的一个抽奖助手小程序,我今天clone下代码,花了不到10分钟就运行成功了, 值得推荐给大家 先上截图大家参观下 [图片] 1 [图片] 2 [图片] 2 码云地址 https://gitee.com/xiaofeiyang3369/wechatlottery 我在调试过程中做了略微的改动 部署步骤建议按照下面三步走 第一步:创建集合,并将集合权限设置为:所有人可读,仅创建者可读写 第二步:将data里面的lottery.json文件导入到lottery集合 第三步:部署云函数 如没有意外就可以正常运行了,部署过程中遇到任何问题,请评论席留言。 更新记录 2020-07-20 重写了核心逻辑 ①开奖逻辑 ②抽奖逻辑 开奖逻辑目前是按照时间维度,到了时间不管人数有没有凑够都会进行开奖,开奖五分钟后,进行抽奖,确定中奖名额。 具体规则: (1)每个整点的1分去检测,根据当前时间检测是否有需要开奖的 (2)每个整点的5分去检测,是否有开奖未抽奖的,如果有,确定中奖名额 ~~
2021-01-25 - 记录微信8.0对抽奖小程序的一点影响
1 小程序中跳转到公众号文章,无法长按识别个人微信二维码?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/000cea03c20e306a6f9b354535ec00 目前已亲测 小程序跳转到公众号文章,长按已不能识别二维码,包括个人微信号,视频号,群二维码,均不可以,公众号二维码是可以长按识别的。 1 [图片] 1 [图片] 1 [图片] 1 [图片] 1 群二维码 1 [图片] 1 [图片] 1 更新与2021-01-29 升级8.0.0版本后长按识别二维码失效?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/0006645873cdb8f2f19bd411851800[图片] ~
2021-01-29 - 红包封面的N种玩儿法
首先,需要有足够多的红包封面样式,越多越好,而且还得好看。 红包封面申请地址:https://cover.weixin.qq.com,支持个人(认证视频号)和企业认证公众号申请。 需要提供资质:作品源文件+作品著作权,缺一不可。可同时提交三个封面,简单的封面3个工作日审核,复杂的封面30天审核。审核通过后可无限购买,1元一个。 做自媒体朋友,千万不要错过这波流量,赶紧推广,活动可持续到2月底。这波营销可以让你的公众号从0-100万粉丝,一个月完成。可以让你的小程序流量主在一个月之内从日收益1元到日收益万元。 下面说下玩儿法,玩儿法很多,选择合适自己的,一定要看到最后,要不然你的封面怎么没的都不清楚。 红包封面公众号玩儿法: 1、关注赠送红包封面,对接系统以openid发放,按取关率50%来说,就是5块钱一个粉丝,成本较高,不推荐。 2、完成公众号任务送红包封面,比如集字、集卡活动,自己可设置概率。因集卡活动周期比较长,所以取关率较低,大约在20%左右,粉丝成本也不是很高。实验数据:7天新增3300粉丝(数据比较低,因为我只有一个红包封面,而且特别low),支出205个封面,34元现金,粉丝单价7分钱一个。(红包封面以openid发放) [图片] [图片] 3、公众号其他任务,比如邀请关注等等,成本相对都比较低。(红包封面以openid发放) 红包封面小程序玩儿法: 1、签到赠送,比如签到7天+邀请5个好友,赠送一个封面,签到以激励方式进行,配合页面视频、插屏广告等。 我做的活动是7天内签到3天+邀请5个好友,赠送一个封面。7天总收益是3W+,支出数据是500个封面左右,我的签到没设置订阅消息,大部分都是用户自己忘记,或者邀请好友数量不足。(因为做了openid 判断,必须邀请新用户助力) 2、抽奖赠送,比如满X人抽奖,随机中奖者可获得一个红包封面,可以设置激励抽奖,配合视频、插屏广告。 我做的活动是满20人抽奖,随机中奖,邀请好友赠送抽奖次数(非强制)。7天总收益是5W+,支出是300个封面左右。后来添加粉丝好友做了私域流量群,抽奖次数50+不中奖的赠送一个封面,邀请好友超过50+的赠送一个红包封面,这个具体看自己操作。收益绝对高 3、其他类型小程序玩儿法,比如闯关赠送、答题赠送等等,根据自己的系统去做方案,千万不要忘记裂变,这个活动主要靠裂变。 红包封面后期裂变: 当用户使用你的封面发红包时,所有人都可以看到你的封面故事,从而点击进你的公众号、小程序,是给你二次裂变用户,也是一个可观的数据。 [图片] 红包封面注意事项: 不要投机取巧,让用户购买你的会员、商品等赠送红包封面。一旦被微信查到,直接封掉。未经腾讯允许,你也不得因微信红包封面而以任何形式、向最终用户或其他任何主体收取任何费用。也就是说,只要和钱挂钩,那么你的封面就会被封。(能以openid 发放尽量以openid 发) [图片] [图片] 最后祝大家粉丝多多,流量主收益多多,牛年发发发!!!
2021-01-13 - 小程序用户发送的客服消息推送到服务器后,再转发至客服系统,微信客服人员在微信客服小助手能收到不?
1.小程序用户发送的消息推送到配置的服务器后,再转发至客服系统,微信客服小助手能收到不?还是说只有网页版的才能收到? 2.客服人员回复用户的信息,是不会推送到我们自己服务器么,直接发送给用户了么? 麻烦各位大佬帮我看看,先谢过了!
2021-02-24 - 请问小商店标准组件api是否会考虑支持根据openID查询领取的优惠券信息?
现在的优惠券用户领取之后根本没有入口可以再次找到,没有发挥优惠券的最大优势
2021-02-10 - JS关闭退出微信浏览器失败?
WeixinJSBridge.call('closeWindow')安卓和IOS手机怎么关闭退出微信浏览器页面怎么没反应
2020-12-15 - 小程序登录、用户信息相关接口调整说明
公告更新时间:2021年04月15日考虑到近期开发者对小程序登录、用户信息相关接口调整的相关反馈,为优化开发者调整接口的体验,回收wx.getUserInfo接口可获取用户授权的个人信息能力的截止时间由2021年4月13日调整至2021年4月28日24时。为优化用户的使用体验,平台将进行以下调整: 2021年2月23日起,若小程序已在微信开放平台进行绑定,则通过wx.login接口获取的登录凭证可直接换取unionID2021年4月28日24时后发布的小程序新版本,无法通过wx.getUserInfo与<button open-type="getUserInfo"/>获取用户个人信息(头像、昵称、性别与地区),将直接获取匿名数据(包括userInfo与encryptedData中的用户个人信息),获取加密后的openID与unionID数据的能力不做调整。此前发布的小程序版本不受影响,但如果要进行版本更新则需要进行适配。新增getUserProfile接口(基础库2.10.4版本开始支持),可获取用户头像、昵称、性别及地区信息,开发者每次通过该接口获取用户个人信息均需用户确认。具体接口文档:《getUserProfile接口文档》由于getUserProfile接口从2.10.4版本基础库开始支持(覆盖微信7.0.9以上版本),考虑到开发者在低版本中有获取用户头像昵称的诉求,对于未支持getUserProfile的情况下,开发者可继续使用getUserInfo能力。开发者可参考getUserProfile接口文档中的示例代码进行适配。请使用了wx.getUserInfo接口或<button open-type="getUserInfo"/>的开发者尽快适配。开发者工具1.05.2103022版本开始支持getUserProfile接口调试,开发者可下载该版本进行改造。 小游戏不受本次调整影响。 一、调整背景很多开发者在打开小程序时就通过组件方式唤起getUserInfo弹窗,如果用户点击拒绝,无法使用小程序,这种做法打断了用户正常使用小程序的流程,同时也不利于小程序获取新用户。 二、调整说明通过wx.login接口获取的登录凭证可直接换取unionID 若小程序已在微信开放平台进行绑定,原wx.login接口获取的登录凭证若需换取unionID需满足以下条件: 如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用2月23日后,开发者调用wx.login获取的登录凭证可以直接换取unionID,无需满足以上条件。 回收wx.getUserInfo接口可获取用户个人信息能力 4月28日24时后发布的新版本小程序,开发者调用wx.getUserInfo或<button open-type="getUserInfo"/>将不再弹出弹窗,直接返回匿名的用户个人信息,获取加密后的openID、unionID数据的能力不做调整。 具体变化如下表: [图片] 即wx.getUserInfo接口的返回参数不变,但开发者获取的userInfo为匿名信息。 [图片] 此外,针对scope.userInfo将做如下调整: 若开发者调用wx.authorize接口请求scope.userInfo授权,用户侧不会触发授权弹框,直接返回授权成功若开发者调用wx.getSetting接口请求用户的授权状态,会直接读取到scope.userInfo为true新增getUserProfile接口 若开发者需要获取用户的个人信息(头像、昵称、性别与地区),可以通过wx.getUserProfile接口进行获取,该接口从基础库2.10.4版本开始支持,该接口只返回用户个人信息,不包含用户身份标识符。该接口中desc属性(声明获取用户个人信息后的用途)后续会展示在弹窗中,请开发者谨慎填写。开发者每次通过该接口获取用户个人信息均需用户确认,请开发者妥善保管用户快速填写的头像昵称,避免重复弹窗。 插件用户信息功能页 插件申请获取用户头像昵称与用户身份标识符仍保留功能页的形式,不作调整。用户在用户信息功能页中授权之后,插件就可以直接调用 wx.login 和 wx.getUserInfo 。 三、最佳实践调整后,开发者如需获取用户身份标识符只需要调用wx.login接口即可。 开发者若需要在界面中展示用户的头像昵称信息,可以通过<open-data>组件进行渲染,该组件无需用户确认,可以在界面中直接展示。 在部分场景(如社交类小程序)中,开发者需要在获取用户的头像昵称信息,可调用wx.getUserProfile接口,开发者每次通过该接口均需用户确认,请开发者妥善处理调用接口的时机,避免过度弹出弹窗骚扰用户。 微信团队 2021年4月15日
2021-04-15 - 云开发短信跳小程序(自定义开发版)教程
写在前面如果你想要自主开发,但没有云开发相关经验,可以采用演示视频来学习本教程: [视频] 一、能力介绍境内非个人主体的认证的小程序,开通静态网站后,可以免鉴权下发支持跳转到相应小程序的短信。短信中会包含支持在微信内或微信外打开的静态网站链接,用户打开页面后可一键跳转至你的小程序。 这个链接的网页在外部浏览器是通过 URL Scheme 的方式来拉起微信打开主体小程序的。 总之,短信跳转能力的实现分为两个步骤,「配置拉起网页」和「发送短信」。本教程将介绍如何执行操作完成短信跳转小程序的能力。 如果你想要无需写代码就能完成短信跳转小程序的能力,可以参照无代码版教程进行逐步实现。 二、操作指引1、网页创建首先我们需要构建一个基础的网页应用,在任何代码编辑器创建一个 html 文件,在教程这里命名为 index.html 在这个 html 文件中输入如下代码,并根据注释提示更换自己的信息: window.onload = function(){ window.web2weapp.init({ appId: 'wx999999', //替换为自己小程序的AppID gh_ID: 'gh_999999',//替换为自己小程序的原始ID env_ID: 'tcb-env',//替换小程序底下云开发环境ID function: { name:'openMini',//提供UrlScheme服务的云函数名称 data:{} //向这个云函数中传入的自定义参数 }, path: 'pages/index/index.html' //打开小程序时的路径 }) } 以上引入的 web2weapp.js 文件是教程封装的有关拉起微信小程序的极简应用,我们直接引用即可轻松使用。 如果你想进一步学习和修改其中的一些WEB展示信息,可以前往 github 获取源码并做修改。 有关于网页拉起小程序的更多信息可以访问官方文档 如果你只想体验短信跳转功能,在执行完上述文件创建操作后,继续以下步骤。 2、创建服务云函数在上面创建网页的过程中,需要填写一个UrlScheme服务云函数。这个云函数主要用来调用微信服务端能力,获取对应的Scheme信息返回给调用前端。 我们在示例中填写的是 openMini 这个命名的云函数。 我们前往微信开发者工具,定位对应的云开发环境,创建一个云函数,名称叫做 openMini 。 在云函数目录中 index.js 文件替换输入以下代码: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event, context) => { return cloud.openapi.urlscheme.generate({ jumpWxa: { path: '', // 打开小程序时访问路径,为空则会进入主页 query: '',// 可以使用 event 传入的数据制作特定参数,无需求则为空 }, isExpire: true, //是否到期失效,如果为true需要填写到期时间,默认false expire_time: Math.round(new Date().getTime()/1000) + 3600 //我们设置为当前时间3600秒后,也就是1小时后失效 //无需求可以去掉这两个参数(isExpire,expire_time) }) } 保存代码后,在 index.js 右键,选择增量更新文件即可更新成功。 接下来,我们需要开启云函数的未登录访问权限。进入小程序云开发控制台,转到设置-权限设置,找到下方未登录,选择上几步我们统一操作的那个云开发环境(注意:第一步配置的云开发环境和云函数所在的环境,还有此步操作的环境要一致),勾选打开未登录 [图片] 接下来,前往云函数控制台,点击云函数权限,安全规则最后的修改,在弹出框中按如下配置: [图片] 3、本地测试我们在本地浏览器打开第一步创建的 index.html ;唤出控制台,如果效果如下图则证明成功! 需要注意,此处本地打开需要时HTTP协议,建议使用live server等扩展打开。不要直接在资源管理器打开到浏览器,会有跨域的问题! [图片] 4、上传本地创建好的 index.html 至静态网站托管将本地创建好的 index.html 上传至静态网站托管,在这里静态托管需要是小程序本身的云开发环境里的静态托管。 如果你上传至其他静态托管或者是服务器,你仍然可以使用外部浏览器拉起小程序的能力,但会丧失在微信浏览器用开放标签拉起小程序的功能,也不会享受到云开发短信发送跳转链接的能力。 如果你的目标小程序底下有多个云开发环境,则不需要保证云函数和静态托管在一个环境中,无所谓。 比如你有A、B两个环境,A部署了上述的云函数,但是把 index.html 部署到B的环境静态托管中了,这个是没问题的,符合各项能力要求。只需要保证第一步 index.html 网页中的云开发环境配置是云函数所在环境即可。 部署成功后,你便可以访问静态托管的所在地址了,可以通过手机外部浏览器以及微信内部浏览器测试打开小程序的能力了。 5、短信发送云函数的配置在上面创建 openMini 云函数的环境中再来一个云函数,名字叫 sendsms 。 在此云函数 index.js 中配置如下代码: const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV, }) exports.main = async (event, context) => { try { const config = { env: event.env, content: event.content ? event.content : '发布了短信跳转小程序的新能力', path: event.path, phoneNumberList: event.number } const result = await cloud.openapi.cloudbase.sendSms(config) return result } catch (err) { return err } } 保存代码后,在 index.js 右键,选择增量更新文件即可更新成功。 6、测试短信发送能力在小程序代码中,在 app.js 初始化云开发后,调用云函数,示例代码如下: App({ onLaunch: function () { wx.cloud.init({ env:"tcb-env", //短信云函数所在环境ID traceUser: true }) wx.cloud.callFunction({ name:'sendsms', data:{ "env": "tcb-env",//网页上传的静态托管的环境ID "path":"/index.html",//上传的网页相对根目录的地址,如果是根目录则为/index.html "number":[ "+8616599997777" //你要发送短信的目标手机,前面需要添加「+86」 ] },success(res){ console.log(res) } }) } }) 重新编译运行后,在控制台中看到如下输出,即为测试成功: [图片] 你会在发送的目标手机中收到短信,因为短信中包含「退订回复T」字段,可能会触发手机的自动拦截机制,需要手动在拦截短信中查看。 需要注意:你可以把短信云函数和URLScheme云函数分别放置在不同云开发环境中,但必须保证所放置的云开发环境属于你操作的小程序 另外,出于防止滥用考虑,短信发送的云调用能力需要真实小程序用户访问才可以生效,你不能使用云端测试、云开发JS-SDK以及其他非wx.cloud调用方式(微信侧WEB-SDK除外),会提示如下错误: [图片] 如果你想在其他处使用此能力,可以使用服务端API来做正常HTTP调用,具体访问官方文档 7、查看短信监控图表进入 云开发控制台 > 运营分析 > 监控图表 > 短信监控,即可查看短信监控曲线图、短信发送记录。 [图片] 三、总结短信跳转小程序核心是静态网站中配置的可跳转网页,外部浏览器通过URL Scheme 来实现的,这个方式不适用于微信浏览器,需要使用开放标签才可以URL Scheme的生成是云调用能力,需要是目标小程序的云开发环境的云函数中使用才可以。并且生成的URL Scheme只能是自己小程序的打开链接,不能是任意小程序(和开放标签的任意不一致)短信发送能力的体验是每个有免费配额的环境首月100条,如有超过额度的需求可前往开发者工具-云开发控制台-对应按量付费环境-资源包-短信资源包,进行购买。如当前资源包无法满足需求也可通过云开发 工单 提交申请[图片]短信发送也是云调用能力,需要真实小程序用户调用才可以正常触发,其他方式均报错返回参数错误,出于防止滥用考虑云函数和网页的放置可以不在同一个环境中,只需要保证所属小程序一致即可。(需要保证对应环境ID都能接通)如果你不需要短信能力,可以忽略最后两个步骤CMS配置渠道投放、数据统计可参考官方文档
2021-04-07 - 视频号扩展链接生成原理与开发过程
视频号的扩展链接,是目前视频号变现的主要路径。目前,扩展链接仅支持公众号文章,而不能直接挂载小程序,个人猜测主要原因是小程序开发者过于强大,开发出来的小程序五颜六色,一不小心就突破了官方的想象空间,导致为视频号的发展带来负面影响。 所以,对于一般视频号用户而言,如果想带货,或者想发发二维码,那么,申请一个公众号,推送文章,把文章的链接拷贝并保存下来,每次发视频号的时候填上即可。如果自己没有公众号也没关系,使用小程序自主生成即可。 我做的视频号扩展链接生成小程序如下(扫一扫或者长按识别): [图片] 视频扩展链接助手 而对于小程序开发者来讲,如何实现呢? 基本技术原理就是调用认证服务号的高级接口中的群发接口,推送文章。详细流程如下: 1、有一个或多个认证的服务号作为载体; 2、根据用户填写的信息生成页面; 3、调用用户管理接口中的打标签接口创建一个用户组标签; 4、调用群发接口,将第2步中生成的页面,发送给第3步中的标签用户组(这个用户组中并没有用户,所以不占用每月4条额度) 5、通过事件接收接口,获得发送状态以及该页面最终的url,这个url就是扩展链接中可用的url。 6、清理下垃圾,将过程中使用的素材、用户组tag调用删除接口清理掉。 至此,功能全部完成。需要的拿走,自己去实现吧。
2021-02-06 - 企业视频号名称被占用,无法认证,如何解决?
企业视频号认证的时候,需要与公众号名称一致,但是提我们名称已经被占用。 我们的公众号名称【龙岩人才网】已经认证了5年,也有相关的营业执照、政府授权名称书(因为涉及到地方)、电信许可证等资质证明。 现在处理方式: 一、去了占用我们视频号的企业下投诉占用我们的名称(他们也没有发内容,就占用名称) 处理结果:说我们没有提交公众号后台截图等营业执照被驳回了(提交了两次,每次都给截图和资料) 二、去腾讯客服小程序提交了工单 一直没有结果。 大家有没有其他的办法,主要是企业认证,还需要和公众号名称一样,如果不一样我们也就换其他名字了。现在要一样 ,我们被迫就一定需要使用这个名称。
2021-02-20 - 视频号名称被占用,无法进行企业认证,请问要如何解决?
企业认证时需要已认证的“同名”公众号,但我司公众号名称在视频号中已被他人占用,无法进行企业认证,这种情况要怎么解决呢?
2020-11-11 - 视频号扩展链接挂载系统开发原理
腾讯已经将视频号提升为战略部署阶段,用视频号全力阻击短视频风口,借助微信日活10亿用户的流量直接打造下一个自媒体红利风口!你想一起借力来打造自己的短视频红利? 2012年微信公众号让人们知道了自媒体 2017年的微信小程序打通直接变现接口 2020年视频号流量红利你真的还要错过吗? 腾讯流量矩阵(图) [图片] 1、个人微信直接引流到视频号; 2、视频号获得个人微信公域流量扶持; 3、公众号与视频号互相引流; 4、通过视频号挂载公众号与小程序实现直接变现; 视频号变现系统盈利模式 1、自运营视频号赚腾讯广告费,小程序流量主分成,公众号流量主分成! 2、邀请更多人视频系统变现,您获得额外广告佣金收益; 3、用户使用系统缴纳升级会员费; 用户A通过系统,复制下载某音短视频,发布到视频号变现系统,生成视频号挂载链接,将原视频截取视频前十秒,发布视频号,将挂载链接插入到视频号底部; 用户B通过用户A的视频号,观看到未完整版视频,点击该视频底部链接进入观看完整版视频,此时需要观看15秒广告,A获得了广告分成收益,系统获得额外广告分成收益; A用户付费升级开通高级会员获得更多广告分成,系统获得A升级收益,用户B通过A的邀请开通付费会员,A获得推荐佣金奖励,系统获得B升级收益,以后B发布视频可以获得广告收益同时A可以获得B 广告收益分成,平台还可以获得B 的广告分成收益; [图片] 开发中需要用的是是认证服务号的几个接口 [图片] 通过公众号的接口就可以获取到永久链接地址,这样就打破了扩展链接只能插入群发文章的限制了
2020-09-28 - 提示公众号的服务出现故障?
回复关键词次数是有限制吗 还是一段时间内回复多少次会被限制 [图片]
2020-11-30 - open-data中的数据直接展示太不美观了吧?
open-data中的数据全是英文的?直接拿来放到个人信息页面展示不太好看啊。 [图片]
2020-10-19 - 小程序安全风控接口附件
一、风险用户扫描功能简介 为提高微信开放平台生态安全性,针对小程序各应用场景中可能存在的恶意注册、营销作弊等黑产风险和安全问题,平台开放API方式向开发者提供风险用户扫描接口协助开发者应对刷单、虚假交易、恶意骗取补贴等营销作弊风险和批量注册、伪造身份等注册黑产风险,以便开发者维护小程序运营秩序和安全。 二、 风险用户扫描功能应用行业 [图片] 综合 账号、行为、环境 等多维度信息,在注册、登录、营销活动、交易行为等业务场景中,发现羊毛党、黑产、网赚团伙等,有效解决网上商城、旅游OTA、网约车、餐饮外卖等行业中存在的恶意注册、频繁登录、羊毛党刷券、网赚刷单等问题,从而形成完整的小程序安全风控体系。 三、 风险用户扫描能力及其应用场景 营销作弊:在首单优惠和特价优惠等营销活动中有效识别刷单、虚假交易、恶意骗保骗补贴等破坏运营秩序和安全的行为。 恶意注册:识别并拦截机器批量注册、垃圾小号、伪造身份等恶意注册行为。 [图片] 四、产品优势 1、官方渠道 独有能力 小程序的技术框架及管控需求决定了开发者无法获取设备相关底层信息 2、覆盖全面 海量数据 微信全生态行为数据,腾讯全用户账号体系 3、准确可靠 便捷快速 整合腾讯安全相关能力,提供API接口快速接入(注:接口需开通权限后方可使用) 五、如何开通权限? 登录小程序,在【开发→开发管理→安全中心→风险用户扫描】申请开通即可。 [图片] 六、接口介绍可查阅接口文档,如有其他疑问,欢迎随时参与官方社区讨论 七、风险等级判定规则和使用建议 1、基于不同场景有不同的判定规则: 注册场景:根据是否存在恶意注册、盗号等行为,和网络、设备等环境因素的综合判定。 营销作弊场景:根据 是否存在薅羊毛、欺诈等行为,和网络、设备等环境因素的综合判定。 2、开发者可根据接口返回的风险等级数值判别用户的风险程度,数值越大,风险越高,风险等级代表的意义及对应业务的使用,可参考下方的说明及建议,具体的使用可根据业务实际情况动态调整,以达到准确的拦截,保护业务健康有序的开展。 [图片]
2021-03-31 - 社区每周 |接入IP变更通知、接口授权修改公告、社区更新、大赛投票及问题反馈(9.14-9.18)
各位微信开发者: 以下是api.weixin.qq.com接入IP变更通知、小程序收货地址、发票接口授权修改公告、小程序私密消息功能发布、小程序云开发挑战赛初赛投票中、社区支持微信消息提醒及上周我们在社区收到的问题反馈、需求的处理进度,希望同大家一同打造小程序生态。 api.weixin.qq.com接入IP变更通知由于内部网络设备裁撤,api.weixin.qq.com(sz.api.weixin.qq.com)需要更新部分接入点IP。 具体详情,请点击《api.weixin.qq.com接入IP变更通知》详细了解或转发周知。 小程序收货地址、发票接口授权修改公告当前小程序调用收货地址与发票相关接口需要用户进行相应授权: [图片]由于此3个接口均为拉起微信原生页面由用户进行选择,为了方便开发者更好地使用微信开放能力,同时开发者无需进行额外适配,针对以上接口授权进行如下修改: 开发者可以直接调用以上3个接口,无需获取用户授权若开发者调用wx.authorize接口请求以上3个授权,用户侧不会触发授权弹框,直接返回授权成功若开发者调用wx.getSetting接口请求用户的授权状态,会直接读取到以上3个授权为true以上修改从2020年9月25日起生效。 小程序私密消息功能发布小程序私密消息功能是这样一种能力:当分享者分享小程序卡片给其他用户或者微信群后,其他用户点击此小程序卡片时,开发者可以鉴别出点击卡片的用户是否被分享者分享过小程序卡片。能力可详见 文档 小程序云开发挑战赛初赛投票中小程序云开发挑战赛目前已进入初赛阶段: 初赛采用专家评审+投票方式进行,结果计算公式为:专家评分(80%)+ 投票(20%)= 初赛总分。 投票方式:微信开放社区作品介绍文章上点赞,点击参与投票 投票时间:9月21日12:00-9月25日24:00 初赛评选结果公布时间为9月30日,每个赛道将评选出10支队伍进入复赛。 想要了解大赛更多详情,欢迎前往 官网 详细了解。 小程序云开发支持公众号网页开发与环境共享小程序云开发现已支持跨账号环境(资源)共享,也就是一个小程序的云开发资源可以授权给其他小程序 / 公众号使用。 详情可查看 “微信开发者”公众号推文 或 文档 社区支持微信消息提醒问答互动提醒在社区提出问题时,可通过勾选「有回复后微信提醒我」来设置微信客户端接收回复提醒,随时关注问题互动 [图片] 订阅提醒后,也可以在相应问题详情页随时关闭微信提醒 [图片] 公告发布提醒可以在公告的列表页选择微信提醒接收公告发布消息,第一时间了解公告资讯 [图片] 私信接收提醒可以在私信通知栏点击设置图标进入「微信提醒设置页」 [图片] 在「微信提醒设置页」具体配置微信客户端接收私信提醒 [图片] 上周问题反馈和处理进度(09.14-09.18)已修复的问题今天发现短链接无法打开,报 404 的问题 查看详情 小程序在提交修改关联公众号时,显示“系统繁忙,请稍后再试”的问题 查看详情 阅读量清空重新计数的问题 查看详情 微信公众平台管理后台小程序功能设置-其他小程序修改报错的问题 查看详情 修复中的问题视频广告提前关闭,isEnded 返回 true 的问题 查看详情 使用腾讯长地址转短地址之后的短地址,都无法打开的问题 查看详情 iOS新版本下,Map组件的Callout位置向左偏移的问题 查看详情 报错找不到fs模块怎么回事 查看详情 公众号上传视频出现:上传分片错误, ret code:-1 查看详情 scrollview中video全屏退出后,scrollview会回到顶部 查看详情 开发者工具调试器Wxml节点元素是空的 查看详情 编译几次后,调试器选择不了dom元素的问题 查看详情 真机调试看不到日志,每次编译项目都会弹窗“更改appID失败,登录用户不是开发者”的问题 查看详情 ipone7,picker的问题 查看详情 玩小游戏的时候分享出去后游戏概率卡死,整个微信也有几率闪退的问题 查看详情 父级设置缩放后 子级movable-view拖动不到边界的问题 查看详情 iOS14上picker mode=date 选择日期的弹出框变形了的问题 查看详情 picker组件 省市区里面 缺少 四川 - 宜宾 -叙州 这个区的问题 查看详情 使用【新的编译模块】:host 部分样式被过滤掉的问题 查看详情 小程序开发指南文档编辑错误的问题 查看详情 scroll-top和scroll-with-animation冲突bug 查看详情 开发工具要求升级,升级后编译不了的问题 查看详情 公众号里面自带的投票管理功能的问题 查看详情 关于开放域设置数据 setUserCloudStorage 的问题 查看详情 需求反馈需求评估中movable-view组件设置为scale模式后能否设置双指缩放的中心点的需求 查看详情 小程序助手中发布小程序的时候的验证能不能改成反光验证的需求 查看详情 公众号设置视频智能推荐的开关的需求 查看详情 更改公众号显示设置,把原创篇数更改为已发送篇数的需求 查看详情 公众号视频观看更多关闭的需求 查看详情 建议新增文章底部阅读量展示的开关 查看详情 wx.showModal能否新增个open-type属性的需求 查看详情 希望官方能解除【视频消息】下方强制插入的推荐视频的需求 查看详情 input 和 textarea 输入的内容在 iOS 上旁白不朗读的需求 查看详情 微信平台后台素材能否添加删除分组图片的需求 查看详情 定时群发设置更新的需求 查看详情 音频素材批量上传的需求 查看详情 wx.canvasPutImageData 绘制的需求 查看详情 微信团队 2020.09.24
2020-09-24 - 只有三行代码的神奇云函数的功能之二:不用授权获取unionid
这是一个神奇的网站,哦不,神奇的云函数,它只有三行代码:(真的只有三行哦) 云函数:login index.js: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { return { ...event, ...cloud.getWXContext() } } 神奇功能之二:不用授权获取unionid: 不需要弹出授权框,直接获取unionid,但是不保证100%成功获取,有可能unionid为空。 首先了解一下unionid机制,在2,3,5,6情况下可用本方法获得unionid。 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html getUnionid: async function () { app.globalData.unionid = app.globalData.unionid || (await wx.cloud.callFunction({ name: 'login' })).result.UNIONID console.log(app.globalData.unionid) }, 其他功能: 神奇功能之一:获取openid: https://developers.weixin.qq.com/community/develop/article/doc/00080c6e3746d8a940f9b43e55fc13 神奇功能之三:100%成功获取unionid: 保证100%成功获取unionid,需要用户信息授权。 https://developers.weixin.qq.com/community/develop/article/doc/00066a967c4e384949f93fe1151413 神奇功能之四:获取电话号码: 还是这三行代码,获取用户的电话号码。 https://developers.weixin.qq.com/community/develop/article/doc/0006a8ec7ac860c94bf90a34f5d813 神奇功能之五:获取群id: 将小程序分享到某群里,可获得该群的群id, https://developers.weixin.qq.com/community/develop/article/doc/000ea802c00f70894cf9fe72556013 [图片]
2020-10-20 - 只有三行代码的神奇云函数的功能之五:获取群id
这是一个神奇的网站,哦不,神奇的云函数,它只有三行代码:(真的只有三行哦) 云函数:login index.js: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { return { ...event, ...cloud.getWXContext() } } 神奇功能之五:获取群id: 将小程序分享到某群里,可获得该群的群id, page.js: onShareAppMessage: function () { wx.showShareMenu({ withShareTicket: true }) let path = '/pages/xxx/xxx' let title = 'title' let imageUrl = `http://xxx.com/100.jpg` return {title,imageUrl,path} }, app.js: onLaunch: function (options) { options.shareTicket && this.getOpenGId(options.shareTicket) }, getOpenGId: function (shareTicket) { wx.getShareInfo({ shareTicket, success: function (res) { wx.cloud.callFunction({ name: 'login', data: { weRunData: wx.cloud.CloudID(res.cloudID) }, success: res=> { console.log(res.result.weRunData.data.openGId); } }) } }) }, 需要说明一下的是:从群里点击分享卡片进入小程序,必须是重启的小程序,不能是已经打开的小程序,否则得不到shareTicket。 其他功能: 神奇功能之一:获取openid: https://developers.weixin.qq.com/community/develop/article/doc/00080c6e3746d8a940f9b43e55fc13 神奇功能之二:不用授权获取unionid: https://developers.weixin.qq.com/community/develop/article/doc/000a0c6b580338e947f9db0c65b813 神奇功能之三:100%成功获取unionid: https://developers.weixin.qq.com/community/develop/article/doc/00066a967c4e384949f93fe1151413 神奇功能之四:获取电话号码: https://developers.weixin.qq.com/community/develop/article/doc/0006a8ec7ac860c94bf90a34f5d813 [图片]
2020-10-20 - 没有点击浮窗floating的事件api?
需求:点击浮窗floating后给予奖励 问题:没找到相关事件api 【已解决】wx.onShow获得场景值为1131即可判断为从浮窗弹出的
2020-08-21 - 小程序中增加类似Q币功能,用户购买虚拟货币,用于购买其他实物商品或兑换为现金提现需要申请什么类目?
您好,我们打算在小程序中增加类似Q币功能。 服务场景大致如下: 用户可通过微信支付或钱包余额购买相应的虚拟货币。虚拟货币可用于兑换购买实物商品,或者直接兑换为现金提现。请问小程序是否支持此功能开发?如果支持,需要申请什么类目,准备什么资料?期待您的答复!
2020-07-06 - 小程序红包领取和企业付到零钱
用户吐槽:默认就是markdown不好吗? 场景 开发中遇到这样一个需求,用户在小程序里输入密令,点击抽奖,奖品内容分别是,平台积分,平台优惠券,平台线下体验卡,和红包。 怎么发“红包” 现金红包>小程序红包 客户既然说了是红包,搜索微信支付和红包相关的API,然后找到了这篇API,但是,这个API更适合线下。原因是场景值限制,只能“扫一扫”,还是用摄像头的那种扫,多亏了这位仁兄踩雷。但我们的业务需求是用户线上抽到红包就是要立马领取到的,只能另寻思路。 企业付款到零钱 看微信支付官方还能提供到什么文档嘛!果然,我看到这个,企业付,官方描述里,说的是通过APPID+OPENID指定收款用户,那这个openid可不可以是小程序的openid呢?答案是,可以!有人问了 总结一下 如果你要做线下,推荐红包 如果你要做线上,推荐企业付到零钱
2020-07-21 - 小程序流量主运营篇-三招收益翻倍
目前小程序场景可接入banner广告、视频广告、前贴视频广告、插屏广告、格子广告、激励视频广告六种。在多种选择下,流量主可针对不同页面使用合适的广告组件来提升流量主收益。(本文不涉及【前贴视频广告】,作者为个人小程序) [图片] 很多同学收益很低,每天收益都在1-100以内,学会下面三招可以让你目前的收益翻倍,希望能帮助到你。 第一招:广告组件合理接入 1、单页面仅可接入一条信息流广告,即banner、视频、格子,三选一,超过两条,流量主审核会失败。 2、单页面可接入多类型广告,即信息流广告(banner、视频、格子其中之一)、插屏广告、激励视频广告可同时出现在一个页面。 3、广告代码接入,从后台创建好之后,可以获取代码信息,放到合适的位置 3-1、banner、格子、视频广告接入 banner广告代码:<ad unit-id="adunit-99e304189164f191"></ad>放到你wxml页面的合适位置,如下图: [图片] 视频广告、格子广告类似,同样复制代码即可: 视频广告代码:<ad unit-id="adunit-4b35bf8bb66516f7" ad-type="video" ad-theme="white"></ad> 格子广告代码:<ad unit-id="adunit-ad2ee97ce13ffc2a" ad-type="grid" grid-opacity="0.8" grid-count="5" ad-theme="white"></ad> 3-2、插屏广告接入 插屏广告代码需要放到js页面中: 3-2-1、你想让用户打开你的小程序或者打开某个页面直接弹出插屏广告,那么可以把插屏广告代码复制到onload/onshow或者其他方法里面 3-2-2、你想让用户点击某个按钮后弹出插屏广告,那么可以把插屏广告代码复制到触发的事件中。 [图片] 3-3、激励视频广告接入 激励视频不可随意接入,它需要用户看完广告后,要给用户返回一些有价值内容,比如我这个这小程序,是AI写诗的,我设置的激励广告是,用户输入完主体 内容,然后给用户返回AI诗句。同样,代码要放在对应的JS文件中。 [图片] 4、流量主审核失败汇总 诱导用户点击广告、部分机型广告遮挡、单页面出现2个以上信息流广告、激励广告无奖励下发等 点击→小程序审核被拒的情况汇总第二招:挖掘广告接入场景 一个小程序如果广告组件接入场景单一,考虑到平衡用户体验,在该场景下能承载的广告就相对有限。因此,挖掘更多的接入场景,能够为流量主带来广告收入的增长。 举例:单页面多信息流广告,增加遮罩层(与第一招背道而驰,且符合流量主审核标准) [图片] 第三招:广告组件优化,灵活搭配 1、广告位名字 创建完广告位一定要记住这个广告位放在哪儿,为了直观预览,大家可以给给自己的广告起名字如:首页插屏、首页banner、首页激励等 2、分析用户页面停留时间,放合适的广告 [图片] 视频广告的收益包含两种:一是完整曝光收益、二是用户点击收益。一个视频广告的完整曝光在15-30秒之间,也就是说当用户在此页面停留30秒以上,适合放视频广告,前提是视频广告在用户的可视区域内。当用户在此页面停留低于15秒,个人建议可换成banner广告或者取消此广告位。有两个原因:一是视频广告占位比较大影响用户体验,二是视频曝光不完整,没有收益。 3、收益分析放合适广告 流量主-数据统计-广告指标明细-细分数据 [图片] 3-1、首先看拉取/曝光量,当某个广告位拉取/曝光量偏低时,可以根据对应广告位名字去更改/取消页面广告位 3-2、其次看点击率,当某个广告位点击率偏低时,可根据对应广告位名字去更改/删除页面光广告位 3-3、最后看收益,当某个广告位收益偏低时,可根据对应广告位名字去更改/删除页面光广告位 最后希望看到文章的你,收益多多~有缘再见~ 点击→流量主运营技巧机票点击→小程序起名字技巧机票
2020-04-25 - 微信小程序支持调用个人收款码吗?
想在小程序中添加一个功能,调起自己的收款码,由客户扫码支付。 整个支付流程是个人对个人,不涉及商户。 想用小程序实现是因为想记录交易双方的id及该次订单的详情。 不知道如何能实现?
2020-06-22 - 小程序如何调用微信的付款码?
使用 wx.openOfflinePayView时提示没有权限,看了其他人说是需要去db申请,这个该如何操作的呢? [图片] [图片]
2020-05-27 - Failed to load media 错误
在做一个带视频播放的小程序,频繁遇到这个问题,请问有什么解决方法: Failed to load media https://cn.node1.video.driftlife.co/5dd65257596247f5b7f1e5bb0928e945?sign=35a900cfc892bd87061af065ef867f81&t=59f2f867&ext=.mp4 : net::ERR_INSUFFICIENT_RESOURCES
2017-09-22 - 个人版微信小商店,卖家运费险?
个人版微信小商店,在哪里开通卖家运费险?
2020-08-21 - 【交互方案】针对不小心触发返回按钮的交互
17年 因为业务上有该类需求,所以提过一个问题。 想实现监听左上角按钮来做其他操作,比如跳转其他页面。 https://developers.weixin.qq.com/community/develop/doc/92f7cdbacdf724cba640955423a8444f 此交互优化只应对表单等内容提交回填的处理 应用场景: 某个表单提交页,页面挺多内容,用户填写完后,不小心触发了左上角返回,或者不小心点了物理按键,那么用户填写的内容就丢失了,那么最开始大家的想法都是,点击左上角返回按钮或者物理按键返回能触发监听我们再弹窗提醒用户是否退出当前页面。这个交互应该是再正常不过的了,在各种app都有见过这种交互。 网页里: h5里都是通过监听popstate,以及设置pushstate实现。 小程序里: 然而小程序的翻遍官方文档,没找到该方法,发帖询问后也是得到官方童鞋回复,不会提供该方法。 官方童鞋给的原因是:会有某些开发者,阻止用户退出某些页面,以达到一些xxx目的,所以官方为了防止开发者滥用,并不打算开放该功能。 翻了社区大家实现方式,有大佬给了一个方案: https://developers.weixin.qq.com/community/develop/article/doc/000844b537c230b04b999a54f56013 该监听方法的缺点: [图片] 最后确实没发现有什么好的监听方案了,那既然代码无法实现,那么我们可以优化用户体验来达到该效果。 实现操作方案如下: [代码]// app.js下跟onLaunch同级新增个globalData字段。 globalData: { formData: {} // 这里需要默认填写该字段,不然其他地方使用了会报错。 } // 首先用户填写任意字段都存储一个对象到globalData下。 <input type="text" placeholder="请输入用户昵称" bindinput="handleUserName" /> handleUserName(e) { getApp().globalData.formData.userName = e.detail.value } [代码] 这样将用户填写的内容都存到globalData下,而我们最初的交互是,存储后用户点击返回下次进来自动回填。 [代码]onShow() { this.setData({ userName: getApp().globalData.formData.userName || '' }) } [代码] 而最终的交互是这样操作: 如果用户填写完一整页内容,而内容我们都存到了globalData下,用户不小心返回了上一页,那么我们在用户重新进入该页面时,判断globalData的formData下是否存在内容,如果存在,弹窗提醒用户是否回填上次填写的内容,如果用户确认回填那么我们给用户自动回填上次填写的信息,如果用户取消回填,那么我们将globalData下的formData设置为空对象即可。 如此 我们即从交互上规避了不小心点击返回导致需要重新输入的问题,并且交互体验得到极大提升。。
2020-06-04 - 付费功能里能否增加免费名单?
付费文章设置后,设置某些账号免费后,可以不用付费看到付费内容。
2020-07-16 - 可以直接通过房间id跳转到其他小程序的直播间吗?
想做一个类似于看点直播一样的平台级直播,能拉取到多个商家【多个小程序】在进行的直播和回放 , 希望将这些直播列表都整合在一个小程序里,然后通过列表跳转到各个直播间 请问这样是可以实现的吗? 现在有试验提示:传入的房间id有误 [图片]
2020-07-17 - 一个类似探探的高性能卡片滑动组件
cardSwipe - 小程序卡片滑动组件 介绍 此组件是使用原生微信小程序代码开发的一款高性能的卡片滑动组件,无外部依赖,导入即可使用。其主要功能效果类似探探的卡片滑动,支持循环,新增,删除,以及替换卡片。 [图片] 用法 获取: [代码]git clone https://github.com/1esse/cardSwipe.git [代码] 相关文件介绍: /components /card /cardSwipe /pages /index 其中,components文件夹下的card组件是cardSwipe组件的抽象节点,放置卡片内容,需要调用者自己实现。而cardSwipe组件为卡片功能的具体实现。pages下的index为调用组件的页面,可供参考。 功能介绍 亮点: 支持热循环(循环与不循环动态切换),动态新增,动态删除以及动态替换卡片 卡片的wxml节点数不受卡片列表的大小影响,只等于卡片展示数,如果每次只展示三张卡片,那么卡片所代表的节点数只有三个 支持调节各种属性(滑动速度,旋转角度,卡片展示数…等等) 节点数少,可配置属性多,自由化程度高,容易嵌入各种页面且性能高 实现方式: 循环/不循环: 属性circling(true/false)控制 新增: 向卡片的循环数组中添加(不推荐新增,具体原因后面分析。硬要新增的话,如果卡片列表不大,并且需要新增多张卡片,可以直接将数据push到卡片列表中然后setData整个数组。如果是每次只增加一张卡片,推荐使用下面这种方式,以数据路径的形式赋值) [代码]this.setData({ [`cards[${cards.length}]`]: { title: `新增卡片${cards.length + 1}`, // ... } }) [代码] 删除: [代码]// removeIndex: Number,需要删除的卡片的索引,将删除的卡片的值设置为null // removed_cards: Array,收集已删除的卡片的索引,每次删除卡片都需要更新 this.setData({ [`cards[${removeIndex}]`]: null, removed_cards }) [代码] 替换: [代码]// replaceIndex:Number,需要替换的卡片的索引 // removed_cards: Array,收集已删除的卡片的索引,如果replaceIndex的卡片是已删除的卡片的话,需要将该卡片索引移出removed_cards this.setData({ [`cards[${replaceIndex}]`]: { title: `替换卡片${replaceIndex}`, // ... } // removed_cards }) [代码] 注意:删除和替换操作都需要同步removed_cards why?为什么使用removed_cards而不直接删除数组中的元素 由于小程序的机制,逻辑层和视图层的通信需要使用setData来完成。这对大数组以及对象特别不友好。如果setData的数据太多,会导致通信时间过长。又碰巧数组删除元素的操作无法通过数据路径的形式给出,这会导致每次删除都需要重新setData整张卡片列表,如果列表数据过大,将会引发性能问题。 删除的时候,如果删除的卡片索引在当前索引之前,那么当前索引所代表的卡片将会是原来的下一张,导致混乱。 保留删除元素,为卡片列表的替换以及删除提供更方便,快捷,稳定的操作。 优化 由于组件支持动态的删除以及替换,这使得我们可以以很小的卡片列表来展示超多(or 无限)的卡片 场景1:实现一个超多的卡片展示(比如1000张) 实现思路:以分页的形式每次从后台获取数据,先获取第一页和第二页的数据。在逻辑层(js)创建一个卡片列表,把第一页数据赋值给它,用于跟视图层(wxml)通信。开启循环,用户每滑动一次,将划过的卡片替换成第二页相同索引的卡片,第一页卡片全部划过,第二页的内容也已经同步替换完毕,再次请求后台,获取第三页的内容,以此类推。到最后一页的时候,当前索引为0时,关闭循环,删除最后一页替换不到的上一页剩余的卡片 场景2:实现一个无限循环的卡片 实现思路:类似场景1。不关闭循环。 为什么不建议新增卡片 新增卡片会增加卡片列表的长度,由于每次滑动都要重新计算卡片列表中所有卡片的状态,卡片列表越大,预示着每次滑动卡片需要计算状态的卡片越多,越消耗性能。在完全可以开启循环然后用替换卡片操作代替的情况下,不推荐新增卡片。建议卡片列表大小保持在10以内以保证最佳性能。 以下为卡片列表大小在每次滑动时对性能的影响(指再次渲染耗时) 注:不同手机可能结果不同,但是耗时差距的原因是一样的 耗时(ms/毫秒) 10张卡片 100张卡片 1000张卡片 再次渲染1 10 12 116 再次渲染2 12 10 87 再次渲染3 17 16 145 再次渲染4 9 16 112 再次渲染5 9 18 90 平均 11.4 14.4 110
2020-04-20 - 个体户升级成为公司,之前已注册公众号和小程序并授权了第三方平台账号,要怎么把主体升级上去?
我们是深度使用微信生态的,我们有公众号、小程序、微信支付,并且进行了第三方平台的授权,用来获取unionid。个体户是升级成为企业后,营业执照的统一社会编码是不一样的。 我看了官方文档,只有公众号是明确支持的:https://kf.qq.com/faq/1708043yQBnU170804qEFnM7.html 问题有以下几个: 1、小程序要怎么处理?我们找了一下,只说了名称变更可以升级主体,没有说升级成为企业可以。我们知道小程序可以迁移,但是不确定走迁移的方案后unionid会不会变,因为绑定了第三方平台账户的。如果可以直接升级主体那最好。 2、公众号和小程序都是绑定了第三方平台账号的,用来获取unionid。那么主体升级后,第三方平台账号要重新绑定吗?unionid会变吗? 3、主体升级后,小程序、公众号的openid会变吗? 4、公众号和小程序可以同时升级主体吗? 5、微信支付账号怎么升级?这个倒还好说,不升级的话大不了重新申请一个,重新绑定即可。 上面几个问题都是相互关联的,如果都能升级主体并且openid、unionid不变的话,很多问题就没了。其实我们是需要一个完整的主体升级的方案,麻烦各位大神指导一下哈,谢谢!
2020-04-22 - 微信小程序客服分组
现在很多公司有不同的客服组,1,可否在小程序管理后台增加客服分组?2,同时小程序用户前端,用户可直接选择相应分组的客服人员服务?建议增加此功能,很实用;也很有需求的!
2020-07-06 - 手机上为什么不能看到小程序设置的背景?
<view class="panel" style="background:url('/image/1.png')">
2020-02-17 - canvas绘图怎么匹配多个手机型号 使用rpx
let canvas = wx.createCanvasContext("shareCanvas"); //绘制背景 canvas.drawImage(res[1].path, 0, 0, 301, 420); canvas.save(); //绘制二维码 canvas.drawImage(res[0].path, 108, 262, 85, 85); //绘制文字 canvas.font = "normal bold 20px sans-serif"; canvas.fillStyle = "#fff"; canvas.setTextAlign("center"); canvas.fillText("您的好友“" + this.wechat + "”", 150, 40); canvas.fillText("送你20元红包", 150, 65); canvas.draw();
2019-01-04 - 分享测试给予奖励积分算不算滥用分享行为?
我们有个健康测试小程序,引导用户分享给其他人,其他人测试完成后给分享人积分奖励,这个是深度互动吧,是不是不算滥用分享行为?合规么? 附运营规范: 5.1.2 以分享后无需互动或无需深度互动即可获得利益的方式诱导分享至群或好友。“深度互动“是指被分享者理解被分享内容并主动参与活动或业务流程,从而执行的进入页面、点击等一系列相关操作。“利益”包括但不限于:现金奖励、实物奖品、虚拟奖品(红包、优惠券、代金券、积分、话费、流量、信息等)。
2020-06-02 - 激励视频广告bug
[代码]let videoAd = [代码][代码]null[代码] [代码] [代码][代码]// 在页面onLoad回调事件中创建激励视频广告实例[代码][代码] [代码][代码]if[代码] [代码](wx.createRewardedVideoAd) {[代码][代码] [代码][代码]videoAd = wx.createRewardedVideoAd({[代码][代码] [代码][代码]adUnitId: [代码][代码]'adunit-dd94524feaa486ac'[代码][代码] [代码][代码]})[代码][代码] [代码][代码]console.log(123)[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]// 用户触发广告后,显示激励视频广告[代码][代码] [代码][代码]if[代码] [代码](videoAd) {[代码][代码] [代码][代码]videoAd.show().[代码][代码]catch[代码][代码](() => {[代码][代码] [代码][代码]// 失败重试[代码][代码] [代码][代码]videoAd.load()[代码][代码] [代码][代码].then(() => videoAd.show())[代码][代码] [代码][代码].[代码][代码]catch[代码][代码](err => {[代码][代码] [代码][代码]console.log([代码][代码]'激励视频 广告显示失败'[代码][代码])[代码][代码] [代码][代码]})[代码][代码] [代码][代码]})[代码][代码] [代码][代码]videoAd.onError(err=>{[代码][代码] [代码][代码]console.log(err)[代码][代码] [代码][代码]wx.showLoading({[代码][代码] [代码][代码]title: [代码][代码]'未知错误'[代码][代码],[代码][代码] [代码][代码]})[代码][代码] [代码][代码]})[代码][代码] [代码][代码]videoAd.onClose((res)=>{[代码][代码] [代码][代码]console.log(res)[代码] [代码] [代码][代码]if[代码] [代码](res && res.isEnded || res === undefined){[代码][代码] [代码][代码]wx.navigateTo({[代码][代码] [代码][代码]url: [代码][代码]'../friendcircle/friendcircle'[代码][代码],[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码][代码]else[代码][代码]{[代码][代码] [代码][代码]wx.showModal({[代码][代码] [代码][代码]title: [代码][代码]'提示'[代码][代码],[代码][代码] [代码][代码]content: [代码][代码]'只有观看完广告才能提前试用哦!'[代码][代码],[代码][代码] [代码][代码]showCancel: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码]- 当前 Bug 的表现(可附上截图) - 预期表现 - 复现路径 - 提供一个最简复现 Demo 激励视频广告感觉不稳定! 小程序使用者,加入视频广告后,广告会加载不完!加载到一半程序崩溃!清空缓存再次尝试,广告完成,设定会跳转到新页面!然而跳转后的页面变成空白! 请求解答! appid wx45e816a26ccc062c 机型 华为P9 [图片]
2019-05-07 - 商家(B端)给用户(C端)发送红包需要选择什么类目?是否要选择社交红包类目?
管理员您好,我们准备开发一款商家营销工具,商家先往小程序里充值一定金额,商家可以出题让用户来作答,如果答对了用户就可以收到商家的红包,请请问这种功能该选择什么类目?是否有必须要办理的资质证明?我看类目列表中有 社交红包 这个类目,但是该类目描述中说这个类目是用于C端用户之间互相发红包,我们是B端给C端发,其次我们也不是社交行为,所以不知是否该选择这个类目?麻烦管理员解答,谢谢 [图片] [图片]
2020-03-24 - 网页完成充值兑换积分,小程序做积分兑换我们自己编辑的文档的查看权限和我们编辑的视频的查看权限?
你好,我们想做一款小程序,在公众号内通过网页jsapi完成会员充值获取积分,小程序实现会员使用积分兑换我们自己编辑文档的查看权限和我们编辑视频的查看权限,请问这样是否违规
2020-03-21 - 小程序红包配置及开发小结
配置: 1、进入商户平台 在产品中心找到小程序红包 开通小程序红包功能 2、开通后在左边的APPID授权管理中关联该小程序APPID 3、进入小程序后台 在功能==》微信支付中确认关联并授权 4、回到商户平台APPID授权管理中确认关联 5、这是最容易忽略的一点 在商户平台 产品中心 小程序红包的产品设置中 拉到最下面 小程序红包权限中开通该小程序的红包功能 到此小程序红包配置完成 开发: 发送红包 var mdhbhe = Convert.ToInt32(fee * 100); string mch_billno = mdminihb.Mch_id + DateTime.Now.ToString("yyyyMMdd") + GenerateNonceStr(); WxPayData hb = new WxPayData(); hb.SetValue("act_name", mdminihb.Act_name);//活动名称 hb.SetValue("mch_billno", mch_billno);//单号 hb.SetValue("mch_id", mdminihb.Mch_id);//发送红包的商户号 hb.SetValue("nonce_str", GenerateNonceStr()); hb.SetValue("notify_way", "MINI_PROGRAM_JSAPI"); hb.SetValue("re_openid", openid); hb.SetValue("remark", mdminihb.Remark); hb.SetValue("send_name", mdminihb.Send_name);//商户名称 hb.SetValue("total_amount", mdhbhe);//红包金额 单位分 hb.SetValue("total_num", 1);//红包数量 hb.SetValue("wishing", mdminihb.Wishing);//祝福语 hb.SetValue("wxappid", mdminihb.Wxappid);//绑定在商户的小程序的appid 不是公众号的 hb.SetValue("scene_id", mdminihb.Scene_id); var sign = hb.MakeSign2(mdminihb.Mch_key);//商户秘钥 hb.SetValue("sign", sign); string xml = hb.ToXml(); string response = HttpService.HbPost(xml, url, true, 6, mdminihb.Mch_path, mdminihb.Mch_certkey); WxPayData result = new WxPayData(); result.FromXml(response);//将xml格式的结果转换为对象以返回 var package = ""; if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "SUCCESS") { //这边是成功后返回的代码 具体逻辑判断自己处理 package = result.GetValue("package").ToString();//成功后返回的 package = HttpUtility.UrlEncode(package); //这是用于领取红包的代码 WxPayData inputObj = new WxPayData(); inputObj.SetValue("appId", mdminihb.Wxappid);//这边是小程序的appId 这个appId 一定要记住 I要大写 inputObj.SetValue("timeStamp", timeStamp); inputObj.SetValue("nonceStr", nonceStr); inputObj.SetValue("package", package); var paySign = inputObj.HBMakeSign(mdminihb.Mch_key);//商户秘钥 } 签名方法: public string MakeSign2(string key) { //转url格式 string str = ToUrl(); //在string后加入API KEY str += "&key=" + key + ""; var rd = Md5.md5(str, 32); // 所有字符转为大写 return rd.ToUpper(); } 还有记得带证书 写的比较笼统 有不清楚的再补充 补充说明1:目前小程序红包仅支持用户微信扫码打开小程序,进行红包领取。(场景值1011,1025,1047,1124,小程序场景值详情参见文档 这个条件一定要注意 所以特别注意一定要通过wx.getLaunchOptionsSync()先看下场景值对不对 特别说明 体验版的二维码是无法领取红包的(第三方的要注意) 补充说明2:第二次领取红包的签名不需要大写
2020-01-02 - 公众号发送红包的限制试指的个数还是接口调用次数,裂变红包是不是没有个数限制?
1.发送频率限制------默认1800/min 2.发送个数上限------默认1800/min 发送频率-每分钟1800,是1分钟调用接口1800,普通红包是单个发吗? 裂变红包1800也是接口调用次数限制吗,没个数限制吗
2019-11-19 - 小程序审核总是提示失败,涉及账户充值服务?
审核驳回:1:服务类目"商家自营-百货_"与你提交代码审核时设置的功能页面内容不一致:\n(1):小程序页面内容涉及账户充值服务,需补充商家自营-预付卡销售-发行方类目。请在基础信息处申请该类目,通过资质审核并在配置功能页添加符合该类目的功能页面。或自查代码,确保包括前端展示、小程序代码等整体均移除上述内容,再提交代码审核。 目前未添加充值相关的内容的,请问可以审核通过下吗?
2020-03-20 - 公司已经注销,可以注销小程序吗? 可以提供注销证明文件
公司已经注销,可以注销小程序吗? 可以提供注销证明文件。 在客服中心有说明到,主体注销后需要提交申请书,但是没有说明申请书发送到哪里或者哪里可以提交 [图片]
2020-03-21 - 小程序中含积分商城,积分兑换商品全部来自京东自营并由京东配送(与京东签署合同),需要选什么类目?
我公司拟上线发布的小程序中含积分商城模块。小程序积分商城中用户使用系统发放的积分兑换商品,商品全部来自京东自营并由京东配送(与京东签署合同,月结)。那么在小程序注册信息【服务类目】中是否必须选电商平台或商家自营类目? 如果必须选择电商平台或商家自营,那相关资料如何提供?实际商品运营方也不是我们,我们也没有那么多证件。 实在比较迷茫,不知如何选择才能顺利通过审核。请官方给予解惑!谢谢!
2019-12-12 - 微信小程序可以做积分兑换实物吗?积分是签到获取。 没有RMB充值之类的。
微信小程序可以做积分兑换实物吗?积分是签到获取。 没有RMB充值之类的。
2019-07-28 - 小程序充值购买积分,积分购买实物算虚拟支付吗?
求大佬解答。
2019-11-01 - 复用公众号如果注销公众号,是否会影响小程序的使用?
这个是小程序的appid,帮忙看一下是否会受影响,搜了一下都说后期迁移和注销会受影响,想知道具体是什么地方会影响 wx575a058005382780 @官方
2020-02-28 - 服务号模板消息额度不随粉丝量上升?
服务号粉丝数量上涨到10万,100万后,没有自动提示模板消息额度,请帮忙看下怎么回事? gh_34e506684953 超过100万粉丝,额度不提升到1000万。 gh_a2de6ccdd7b6 超过10万了,额度不提升到100万。
2020-02-25 - 小程序添加“餐饮”->"餐饮平台"类目不通过,需要《增值电信业务经营许可证》的问题?
小程序添加“餐饮”->"餐饮平台"类目不通过,需要《增值电信业务经营许可证》。但是在通管局申请《增值电信业务经营许可证》时,通管局下发《行政不予受理通知书》,与之交流告知已与腾讯公司统一交流过此事。但是添加类目时仍然审核不通过。请问腾讯公司,不知如何才能解决此问题? [图片]
2020-01-14 - 电商平台资质问题
[图片]请问这个行政不予受理通知书可以提交使用吗?
2020-02-18 - 小程序类目选择餐饮-外卖/点餐平台需《增值电信业务经营许可证》通信管理局说不能办
- 需求的场景描述(希望解决的问题) 您好,我们公司的小程序需要选择类目餐饮-外卖/点餐平台,但是这两个都需要上传《增值电信业务经营许可证》,我屁颠屁颠地去申请ICP证,除了还差几份材料完善后就可以提交了,但是在填写申请表的时候,我本来以为是用公司网站申请,但是发现公司网站不是经营性的网站并不需要办理ICP证,所以打电话咨询了江苏省通信管理局,说小程序不需要办理ICP证,是在微信平台上的只需要微信的ICP证即可,他们审核的是网站的,但是我们公司的网站又不符合申请的条件。 所以现在咱们微信平台需要我们提供ICP证,但是通信管理局又说不能办不需要办,这自相矛盾的问题,请问该如何去解决呢? - 希望提供的能力 公司的这个项目目前卡在这里,大家都很着急,ICP证也需要很多材料,没有的材料还要去找去办,审核时间也很长,我这也是第一次办,都没有经验,所以特别担心白忙活一场,到头来却不能办?如果申请了等两个月审核下来却无法通过,项目无法上线,这是多么悲惨!也请大家互相理解一下,能给我们解决问题的办法,我看到社交-社区/论坛 类目 目前所需的资质已经从办理《增值电信业务经营许可证》修改为ICP备案,是否可以根据目前的现状也给我们餐饮-外卖/点餐平台放宽权限呢? 非常感谢!
2018-08-13 - 外卖小程序的特殊资质?
本公司做了一个外卖小程序,添加小程序外卖类目的时候就告知需要(增值电商业务经营许可证),但是去办理的时候,工信部告知的是小程序不需要使用这个证件,添加受理截图,类目还是一直审核不通过,这个需要怎么处理?
2019-12-24 - [填坑手册]小程序新版订阅消息+云开发实战与跳坑
[图片] 老版本的订阅消息在2020年1月10日就下线了,相信不少人在接入新版本订阅系统的时候,或多或少会遇到一些问题,这里智库君跟大家介绍下新版订阅的机制和不需要node/后端的情况下 独立完成功能开发。 一、新版订阅的机制 其实开发过程不难,但是要理清楚它里面的机制,智库君还是花了一些时间的,也踩了不少坑 先来看下官方介绍: [图片] 可以设置多个订阅选项 感叹号里面可以看到详情 有个默认不被选中的“总是”选项 这些就是新不同的地方,智库君在开发的时候也有很多疑问,点了“总是”再点“取消”按钮会怎样?部分选择订阅会怎样?下面为大家一一梳理 (1)部分选中 [图片] 比如现在有三个选项 A,B,C,用户**“部分选中”**返回的情况: [图片] 这里用真机调试可以看到,有个返回值状态为“reject”。 如果我们反复几点点击同一个订阅后,这些值是如何计算的呢? 举例: [图片] 从这里看出,微信系统会自动记录用户点击的次数,并且做累加记录,如果用户只允许2次发送,而开发者发送了3次,最后一次将会被拒绝。 (2)点击“总是保持以上选择,不再询问”的情况 [图片] 当用户点击“总是”之后,同一个类型的订阅将不再弹出,那如果有多个订阅选项呢? 举例 订阅AAA 三个订阅模板为 X Y Z 订阅BBB 二个订阅模板为 Y W 这时候如果“订阅AAA”按钮选择了“总是”,那么再点击“订阅BBB”按钮,将只会弹出一个选项“W”,不会有 “Y” 的模板,因为在之前 “订阅AAA” 按钮中已经包含了。 [代码]wx.requestSubscribeMessage({ tmplIds: ["MECDDOdhbC3SrQmMY5XrfqiIGbMTzpEN8Z7ScXJfcd0", "iSb2NIlNnnO60wlI-8Wx5Pe82jR7TRdwjotSXtM1-ww"], success(res) { console.log(res); } }) [代码] 显示内容仅一个选项: [图片] 这里需要注意,“总是”选项是全局有效,不区分页面,选中“总是”的 W,X,Y,Z的模板,在全局任意页面中再次调用,再次调用将不再会显示! [图片] 返回值无提示用户是否选中“总是”。 (3)用户点击“总是”后,获取状态 [图片] [代码]wx.getSetting({ withSubscriptions: true, success(res) { console.log(res.authSetting) // res.authSetting = { // "scope.userInfo": true, // "scope.subscribeMessage": true // } console.log(res.subscriptionsSetting) // res.subscriptionsSetting = { // SYS_MSG_TYPE_INTERACTIVE: 'accept', // SYS_MSG_TYPE_RANK: 'accept', // zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: 'reject', // ke_OZC_66gZxALLcsuI7ilCJSP2OJ2vWo2ooUPpkWrw: 'ban', // } } }); [代码] [图片] 这里可以调用wx.getSetting方法,但是需要注意:如果用户第一次选“总是”后点击“取消”按钮或者订阅模板全部是未选中/reject的,那将获取不到状态(这里可能是BUG,期待官方未来修复)。 (4)用户点击“总是”后,让用户手动修改 前面说到用户点击“总是”后,系统将不再弹窗,但是我们可以通过**“wx.openSetting”**引导用户手动修改。 [代码]wx.openSetting({ success(res) { console.log(res.authSetting) // res.authSetting = { // "scope.userInfo": true, // "scope.userLocation": true // } } }) [代码] [图片] [图片] 当然用户自己也可以修改 [图片] 总结 【重点】选择“总是”,很多人认为就可无限发送订阅消息,这个是错误的,勾选和不勾选唯一的区别就是每次触发订阅的时候会不会弹授权窗口!!! 用户点击次数系统会自动累加,直接影响后台发送通知的次数。 用户选择“总是”后,小程序界面不再弹窗,但仍然有回调/callback。 任意订阅模板在用户选中“总是”(包括接受/拒绝2个状态)后,全局有效,就算其他订阅包含“此模板”也不再显示/弹出 当用户选择“总是”中“accept/选中/接受”的状态后,可以在wx.getSetting查询到用户是否选择“总是”。 当用户选择“总是”中“reject/未选中/拒绝”的状态后,返回值“无感知”(这里可能是BUG) 二、功能开发 使用微信自带的云开发,可以在没有node/后端开发支持下,完成整个订阅流程的开发。 (1)微信后台设置订阅模板和获取模板ID 1、打开小程序后台,找到订阅消息设置 [图片] 2、在公共模板库找模板或者自己申请新模板,建议能用现成模板用现成的,因为申请周期可能较长,且容易被拒 [图片] 3、选好模板后,点击详情 [图片] 4、查看模板内容和发送DATA的结构 [图片] 5、复制模板ID (2)配置云函数 [图片] [图片] 1、新建getOpenId云函数,用于获取用户的openID [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() return { event, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID, } } [代码] 2、新建订阅推送通知云函数 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() //订阅推送通知 exports.main = async (event, context) => { try { const result = await cloud.openapi.subscribeMessage.send({ touser: event.openid, //接收用户的openId page: 'pages/my/index', //订阅通知 需要跳转的页面 data: { //设置通知的内容 thing1: { value: '小程序订阅填坑' }, thing2: { value: '智库方程式' }, thing3: { value: '一起学习,一起进步' } }, templateId: '5Efr7IqIooYO9nPw047Iggxbm9Ge2Km10GQ4amGOUac' //模板id }) console.log(result) return result } catch (err) { console.log(err) return err } } [代码] 写完云函数记得右键部署下!!! (3)小程序代码部分 [代码]<!------------html -------------> <button bindtap="getOpenId" type='primary'>获取openId</button> <view class="subBtn" catch:tap="sub">订阅AAA</view> <view class="subBtn" catch:tap="send">订阅推送测试</view> <view class="subBtn" catch:tap="setting">设置“总是”后,跳转修改</view> [代码] [代码]//JS 部分 //获取用户的openid getOpenId() { wx.cloud.callFunction({ name: "getOpenId" }).then(res => { let openid = res.result.openid console.log("获取openid成功", openid) }).catch(res => { console.log("获取openid失败", res) }) }, //发送模板消息给指定的openId用户 send(openid){ wx.cloud.callFunction({ name: "sendSub", data: { openid: openid } }).then(res => { console.log("发送通知成功", res) }).catch(res => { console.log("发送通知失败", res) }); }, //消息订阅 sub: function () { wx.requestSubscribeMessage({ tmplIds: ["5Efr7IqIooYO9nPw047Iggxbm9Ge2Km10GQ4amGOUac"], success(res) { console.log("订阅授权成功:"+res); }, fail(res){ console.log("订阅授权失败:" + res); } }) }, //帮助用户跳转修改订阅状态 setting:function(){ wx.openSetting({ success(res) { console.log(res.authSetting) // res.authSetting = { // "scope.userInfo": true, // "scope.userLocation": true // } } }) }, [代码] (4)测试流程 点击发送通知后,获得这样的效果: [图片] [图片] 获得对应返回值: [图片] 当errCode为0时,即发送通知成功。 当errCode为43101,说明用户只授权了一次,但是你发送了2次,超过用户授权次数。 [图片] 三、进阶与思考 1、当你有多个订阅模板同时需要用户选择时,你可以通过以下代码记录,用户哪些选了,哪些没选。 [代码]wx.requestSubscribeMessage({ tmplIds: ["5Efr7IqIooYO9nPw047Iggxbm9Ge2Km10GQ4amGOUac", "OBB_Z10eh_Inm9p8EU6Ml_NS_mijXgTz3T07cxgKvX0","5Efr7IqIooYO9nPw047Iggxbm9Ge2Km10GQ4amGOUac"], success(res) { //console.log(res); if (res.errMsg == "requestSubscribeMessage:ok") { let acceptArray = []; //用户授权模板列表 for (let i = 0; i < tmplIds.length; i++) { const element = tmplIds[i]; if (res[element] == "accept") { acceptArray.push(element); } }; console.log(acceptArray); if (acceptArray.length > 0) { //执行下一步函数 } } } }) [代码] 2、一个关于是否需要记录用户对某个“订阅模板授权的次数”,以控制后台“发送的次数”,智库君在实战中认为,其实没有必要,顶多就是你发送返回一个错误码,微信之所有记录用户授权次数,也是为了保护用户不被骚扰。 3、你只需要记录用户点击了哪些需要授权的模板就行,为了是用户点击订阅后,改变按钮的状态,避免订阅按钮反复弹窗的问题,同时当检测到用户点错“总是”按钮后,可以自动跳转到“设置”界面。 4、这次智库君主要给大家简单介绍了下订阅全流程。后面大家可以根据自己的需要,添加和改进这些代码。比如: 配置云函数中的node函数,实现定时发送 配置云函数中的数据库,实现内容的自定义发送 最后,希望这篇文章能帮助到大家,一起学习,一起进步! (官方文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html) 往期回顾: [打怪升级]小程序自定义头部导航栏“完美”解决方案 [填坑手册]小程序Canvas生成海报(一) [拆弹时刻]小程序Canvas生成海报(二)
2021-09-13 - 使用功能-直播还需要再申请相关直播类目嘛?
[图片]在小程序的管理后台中使用这个功能,还需要申请相关类目的直播资质嘛?
2020-02-14 - 《增值电信业务经营许可证》或《电信与信息服务业务经营许可证》申请指南
根据工信部管理规定,电信业务分为第一类基础电信业务、第一类增值电信业务、第二类基础电信业务、第二类增值电信业务等几个大类,如您没有申请过该资质,从事小程序开发运营建议申请“第二类增值电信业务-信息服务业务”的《增值电信业务经营许可证》。 一、【申请地址】https://tsm.miit.gov.cn/ 温馨提示: 若是点击无法跳转,请复制链接更换其他浏览器打开 二、【申请流程图】 [图片] 三、【申请材料】 1、公司法定代表人签署的经营增值电信业务的书面申请。内容包括:申请经营电信业务的种类、业务覆盖范围、公司名称、公司通信地址、邮政编码、联系人、联系电话、电子信箱地址等。 2、公司的企业法人营业执照副本及复印件。 3、公司概况。包括:公司基本情况,拟从事增值电信业务的人员、场地和设施等情况。 4、公司最近经会计师事务所审计的企业法人年度财务会计报告或者验资报告及电信管理机构规定的其他相关会计资料。 5、公司章程、公司股权构及股东的有关情况。 6、申请经营电信业务的业务发展、实施计划和技术方案。 7、为用户提供长期服务和质量保障的措施。 8、信息安全保障措施。 9、证明公司信誉的有关材料。 10、公司法定代表人签署的公司依法经营电信业务的承诺书。 11、其他需提交的申请材料。
2020-03-18 - 虚拟业务指南请收好。
在小程序生态中,基于苹果运营规范,小程序内暂不支持iOS端虚拟支付业务。为此小编为大家整理了一份虚拟支付业务指南,希望大家在做虚拟业务时有所帮助: [视频] 那么,到底什么是虚拟支付业务呢? 虚拟支付业务是指购买非实物商品。比如:VIP会员、充值、录制课程、录制音频视频等虚拟产品。目前iOS端暂不支持虚拟支付业务。 我们常见iOS虚拟支付的不合规示例有哪些呢? 示例一 :小程序内存在付费购买虚拟内容或道具。商品多体现为提前编辑好的、录制好的虚拟商品。如录制视频课程、游戏道具。 整改建议 :建议去除小程序内所有付费购买虚拟服务,并根据提示修改相关内容及文案,文案可参照“由于相关规范,iOS功能暂不可用”。 [图片] 示例二 :付费解锁优质服务。多体现为提供虚拟商品的小程序可通过支付购买、开通虚拟会员等形式,体验小程序付费服务。比如:支付阅读章节小说、同城生活服务平台付费发帖/付费置顶等。 整改建议 :建议可以关闭iOS端虚拟支付通道,并将【马上充值】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端会员服务。 [图片] 示例三 :关闭iOS端虚拟支付功能后,虚拟商品页面仍然保留货架价格标签展示、购买/付费/订阅等功能或按钮。 整改建议 :建议去除小程序中的虚拟商品的价格展示,并更改为【免费】;并将【订阅 ¥128】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端虚拟商品购买服务。 [图片] 示例四 :关闭iOS端虚拟支付功能后,提供引导用户前往其他支付的路径/文案,完成虚拟支付闭环。 整 改建议 :建议去除iOS端小程序内引导用户前往其他支付路径/文案,并不再提供iOS端虚拟商品购买服务。 [图片] 示例五 :小程序含需要付费的虚拟商品,并设置限时免费的服务,限时免费结束后需付费才能继续提供服务。 整改建议 :建议将iOS端小程序中所有虚拟付费内容更改为免费,并不再提供iOS端虚拟商品购买服务。 [图片] 示例六 :关闭iOS端虚拟支付功能后,小程序中虚拟产品页面不可以含有付费性质的关键字(如:购买、已购、付费、支付等),包括但不限于功能按钮、功能页面、支付提示及任何商品介绍等。 整改建议 :建议将小程序iOS端虚拟产品页面中的文案/按钮/功能tab含有限制的关键字更改为【免费】或删除。并不再提供iOS端虚拟商品购买服务。 [图片] 如小程序内存在以上不合规的虚拟支付内容,请开发者重视并及时整改。对于首次违规的小程序,平台将下发站内信整改通知,并给予三天整改时间,请开发者按照提示在限期内完成整改。平台将会对到期未完成整改的小程序进行搜索策略调整,并在小程序功能使用上进行一定的限制,直到小程序完成内容整改。
2020-04-23 - 小程序支持功能为区域内帮人送,找人送的跑腿类型,如何选择服务类目
正在开发但未提交代码的小程序,功能包括在指定区域内找人帮你送,以及跑腿帮人送这两项。小程序本身只是为需要送东西的人跟刚好顺路的人提供一个查询跟联系的平台、渠道,和快递、物流似乎不太相符,生活服务中又无相关的类目。 请问这种情况下,应选择什么服务类目呢? 谢谢。
2019-02-17 - 微信小程序直播资料整理
可以通过此脑图大概了解小程序直播插件发展过程:http://naotu.baidu.com/file/597625fbd8659aa87e54143df1ed7f39?token=5bad276062c05585 [图片] 小程序直播组件是微信给开发者提供的实时视频直播工具,可以帮助开发者快速通过小程序向用户提供优质的直播内容,在小程序内流畅完成购买交易闭环,提升转化率; 小程序直播组件包括观众端、主播端及后台管理端,其中观众端提供拉流、实时互动、订阅提醒、商品购买等能力,主播端提供开播、推流、音视频效果优化等能力,后台管理端则负责直播房间、商品货架以及营销活动配置等。 【开通条件】 https://mp.weixin.qq.com/s/oqNdNEnRblEzwR_61d3vHA 满足以下条件的电商平台、自营商家,即有机会被邀请到小程序直播公测中来: (同时满足以下1、2、3条件,加上4、5、6条件的其中之一即可。) 1. 满足小程序18个开放类目(包括:电商平台、商家自营-百货、食品、初级食用农产品、酒/盐、图书报刊/音像/影视/游戏/动漫、汽车/其他交通工具的配件、服装/鞋/箱包、玩具/母婴用品(不含食品)、家电/数码/手机、美妆/洗护、珠宝/饰品/眼镜/钟表、运动/户外/乐器、鲜花/园艺/工艺品、家居/家饰/家纺、汽车内饰/外饰、办公/文具、机械/电子器件) 2. 主体下小程序近半年没有严重违规 3. 小程序近90天存在支付行为 4. 主体下公众号累计粉丝数大于100 5. 主体下小程序连续7日日活跃用户数大于100 6. 主体在微信生态内近一年广告投放实际消耗金额大于1w 【第三方平台(小程序服务商)】 https://developers.weixin.qq.com/community/develop/doc/000a06014745f00d95f9e03d951401?from=groupmessage&isappinstalled=0 服务商接入指引 具体接入指引请参考《【小程序直播】服务商接入指引》,以下为服务商接入步骤。 1. 权限申请 1) 在问卷《服务商“小程序直播”接入申请》填写相关信息并等待权限开通,发送申请后7个工作日内,可登陆微信开放平台查看第三方平台权限集并勾选 “小程序直播” 能力; 2) 开通后,即可登陆 “微信开放平台” (open.weixin.qq.com)勾选 “小程序直播” 第三方权限集并全网发布; 2.功能开发 小程序直播需要实现【直播组件】与【后台API】两个部分,其中组件部分需要在小程序中进行配置开发。 具体开发文档,请参考《小程序直播组件接入指引》。 【看点直播与小程序直播区别】 看点直播是不是腾讯官方?有什么区别? 小程序直播是腾讯WXG(也就是微信团队)公测推出的能力,为商家提供的经营工具,可在商家自有的小程序中实现直播互动与商品销售闭环。优点是可以直接内嵌到商家小程序,和公众号打通;直播吸引的流量都沉淀在商家自有小程序,不用跳转其他渠道,有利于形成私域流量,转化率高。 腾讯看点直播是腾讯PCG(平台与内容事业群,做QQ、腾讯视频的事业群)推出的直播产品,有App、小程序端、小程序端是基于微信小程序的开发能力开发的,商家必须通服务商才能接入,无法直接在商家自有的小程序内闭环交易。(需要直播服务商提交资料开通,服务费用599半年。) 【2020-05-22更新】 近期已上线新功能:评论管理,支持对单个用户进行禁言,与敏感词设置。 未全面开放新功能:直播间可以直接发放微信代金券,目前内测中,敬请期待! [图片] 【2020-06-02更新】 小程序后台直播功能接口支持进一步提升,可以通过接口添加直播间与商品管理,在没有涉及抽奖情况下及推送商品功能情况下,已经可以完整脱离小程序后台完成直播相关设置,对于一些服务商来说是一个不错的消息,按这个进度,下一步官方应该优先支持推送商品及抽奖活动设置配置接口的开放。 创建直播间文档:https://developers.weixin.qq.com/miniprogram/dev/framework/liveplayer/live-player-plugin.html 拉到三/9 商品接口文档:https://developers.weixin.qq.com/miniprogram/dev/framework/liveplayer/commodity-api.html [图片] 【2020-06-09更新】 直播间优惠券功能已经全面开放,很多玩法将会产生。 直播间领取的券可直接插入微信卡包(代表着可以提醒一次)。 如何配置:http://note.youdao.com/noteshare?id=afbff99580c4540cc011e3ed7ab5fbcf&sub=CDE480AAEC07437CB58902E0FF42C329 [图片][图片]
2020-06-09 - 一眼告诉你什么是订阅消息了,看完就懂订阅消息。
消息通知有两种: 一、A的动作后,发消息给A自己,这种容易解决,不多说明; 二、A动作后,发消息给B(比如管理员、店家、楼主),如何保证B收到消息?这种是本方案要解决的问题。 一张图片一眼告诉你什么是订阅消息,产品经理的设计UI居然让人一眼就知道订阅消息是什么玩意。 [图片] 用户 B (管理员、商家、组长、楼主)在知道订阅数不足后,打开小程序来续订阅数,否则没法收到订阅消息。 [图片] 补充一: 关于勾选按钮,请注意话述是:“总是保持以上选择,不再询问”,而不是:“总是同意接收订阅消息”,不要幻想就成了永久性订阅消息; 相当于你打电话订外卖,对店家说“老样子”,店家只会马上送一次外卖,而不是会以后每天自动给你送外卖了。 勾选和不勾选的区别是什么呢? 区别仅仅是:不勾选时,必须点击订阅10次,弹窗10次;勾选后,仍然必须点击订阅10次,但是不弹窗。无论如何“订阅”这个点击n次的动作少不了。 补充二: 一旦勾选后,就不可逆了,没有任何办法恢复或取消勾选了,除非你小程序MP后台换一次消息模板号(删除模板,重新添加一次)。 补充三: 关于如何保存订阅数。 保存在数据库中,笔者用的是云开发,数据库表user结构如下: { _id:'openid1', nickName:'老张', msg:{ "tempId1":5, "tempId2":7, } } 补充四: 关于如何获取订阅数。两种方式: 一、wx.requestSubscribeMessage的回调success里获取; 二、消息推送机制获取;https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html
2022-09-21 - 小程序流量主、广告位类型和广告收益分析
小程序流量主、广告位类型和广告收益分析 ## 本文介绍 最近在小程序的几个微信群,经常有朋友问到以下几个问题 1、小程序怎么盈利 2、小程序流量主是什么以及怎么开通 3、小程序广告有哪些类型,哪种广告类型相对收益最大 4、 ## 小程序如何盈利 目前对个人小程序开发者而言,只有通过开通流量主,并且按照官方规范要求添加广告位,才能获取收益,当然打赏除外。 ## 什么是流量主如何开通 流量主是微信对外提供的一个服务,通过开通流量主,就可以在小程序合适的位置引入广告位,进而实现收益 登录公众号后台( https://mp.weixin.qq.com/ )在左侧菜单中,找到 推广-流量主,点击进去会看到如下截图 [图片] 小程序流量主: 1.开通条件:小程序累计独立访客(UV)1000以上,且无违规记录,即可开通流量主功能。 温馨提示:如满足条件仍无法开通,可能是数据同步问题,建议等待1-2个工作日后再试。 2.申请方法:进入微信公众平台小程序后台,点击左侧面板“流量主”,满足开通门槛的小程序开发者点击“开通”,提交财务资料,待审核通过后成功开通流量主功能,即可创建相应的广告位。 3.广告接入指引: 广告接入可查看: 微信小程序广告接入指引 在开通流量主的过程中,会绑定个人银行卡,以方便进行后续的广告收益结算,目前结算每月两次,具体官方公告可以查阅 [流量主结算周期及开票规则调整说明][2019-12-03发布] https://mp.weixin.qq.com/promotion/readtemplate?t=notice/detail_page&time=1575340587¬ice_id=634169 ## 广告类型有哪些 Banner激励式视频插屏视频广告前贴视频 以下为各广告类型,截图示例, [banner广告] [图片] [插屏广告] [图片] 视频广告 [图片] 由于插屏广告会影响用户体验,所以不建议放太多场景使用。 具体不同类型广告体验,可以扫码 [图片] 首页模块-->>插屏广告使用说明-->>视频广告关于我们-->>banner广告 ## 哪种广告类型收益相对最大 [图片] 在10月30号,将banner广告同一替换为激励式视频广告和视频广告,收益很明显从30元上升到90元、150元 可以看到视频广告相对于banner广告,对于收益增加是有用的。 下图是某小程序12月4号一天的收益数据 [图片] 12月4号一天,不同广告类型,收益分析 总收益 194.74+23.27+147.82=365.83 具体分拆来看 广告类型点击量总收益单个点击收益(元)banner1956194.740.099插屏广告6223.270.375激励式视频广告152147.820.972 通过上图我们对比分析,不难得出以下结论:激励式视频广告单个点击的收益最大、 当然我们不能通过单一维度来了解哪种收益最好,还要综合考虑,比如哪种广告对用户影响最小,毕竟不管哪种方式,广告的接入肯定会带来交互体验上的障碍, 我们必须在交互体验和广告收益这两者之间做好权衡。 ## 系统公告 激励式广告于7月31日支持30秒视频素材,广告流量将逐步放开,MP后台-广告位管理模块可支持选择6-15秒视频或6-30秒视频素材的功能,请流量主根据产品进行调整。程序视频广告已于9月4日正式全量上线,开通后即按广告曝光获得分成收入,进一步提升流量变现收益。小程序视频前贴广告组件已于8月30日正式全量上线,开通后即按广告曝光获得分成收入,进一步提升流量变现收益。## 官方文档 小程序广告组件流量主操作指引https://wximg.qq.com/wxp/pdftool/get.html?id=BJSyDkLqz&pa=14&name=miniprogramAds_supplier_manual应用规范https://wxa.wxs.qq.com/mpweb/delivery/legacy/pdftool/get.html?id=rynYA8o3f&pa=10&name=miniprogramAds_supplier_guidance小程序流量主应用规范https://wximg.qq.com/wxp/pdftool/get.html?id=rynYA8o3f&pa=10&name=miniprogramAds_supplier_guidance处罚标准https://wxa.wxs.qq.com/mpweb/delivery/legacy/pdftool/get.html?id=BkTGkbs2G&pa=1&name=miniprogramAds_supplier_regulation小程序视频广告流量主指引https://wximg.qq.com/wxp/pdftool/get.html?post_id=1317小程序视频前贴广告流量主指引https://wximg.qq.com/wxp/pdftool/get.html?post_id=1318## 总结三点 从纯收益的角度来讲,在各种广告类型中,视频广告(包含激励式视频广告、视频广告、视频前贴广告)要比banner广告要好,而且好很多从用户体验来讲,插屏广告是首次打开带插屏广告的页面强制弹出的,但是广告过后,在页面是不占空间的,这是区分与其他广告的地方,banner广告、激励式视频广告、视频广告、视频前贴广告都是在页面中占固定的空间的,这一点要小程序运营同学权衡。Banner广告是按点击,激励式视频、视频广告、插屏广告都是按照曝光来收取广告费用的,这一点非常重要,难怪我每次手工点击我的视频广告没有见流量的增加[哭脸.jpg]。[感谢 @ 仙森 补充于2019年12月9号] 虽然对个人开发者而言,我们开发小程序的目的是为了收益(当然也有为了情怀而开发),在了解如何收益的情况下,我们还是应该尽量把精力放在小程序本身的开发上面。 感谢 在此特别感谢,小程序运营讨论群的两位小伙伴,微信号中间两位已打码 1、@迭戈 (yang_##chun) 2、@风猫 (cs##26)
2020-12-25 - wx.requestSubscribeMessage 小程序模版消息升级为订阅消息,
还要从那不久前的炎炎夏日说起, 一位苦逼的前端小妹, 为了加模版消息,熬了好几个加班夜, 动了几十个页面, 修改了几百个按钮, 终于把模版消息都全面埋雷, 不留任何死角. 也就过了才 1 2 3 4 5个月吧, 订阅消息一出. 我们的前端小妹, 那脸色、那眼神、我至今找不到一个合适的词语来形容(主要是笔者词穷) 下面还是主要来说说订阅消息吧, 不然对不起读者. 升级第一步: 注意订阅消息是有最低版本库要求的 (这个主要是需要产品和客户同步, 不是所有人都能订阅哦) 注意:iOS客户端7.0.6版本、Android客户端7.0.7版本之后的一次性订阅/长期订阅才支持多个模板消息,iOS客户端7.0.5版本、Android客户端7.0.6版本之前的一次订阅只支持一个模板消息 升级第二步: 干就完了 官方 api 地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/subscribe-message/wx.requestSubscribeMessage.html wx.requestSubscribeMessage(Object object) [图片] 撸起袖子就是干 小手一抖, 代码全有 [图片] 屏幕一点, 效果立显 [图片] 友情提示: 这里 tmplIds 用的是订阅消息 id 哦, 不要搞错咯 这里必须是在手机上才能看到效果哦 每次弹框, 只能配置最多 3 个订阅消息哦 如果勾选了: “总是保持以上选择, 不再询问” , 真的就是“此生不复相见哦”, 删除了小程序也不管用哦 由于弹出的 3 个订阅消息, 是可以单独勾选, 就会出现如果某 1 个配置的已经“总是保持以上选择, 不再询问”. 那么弹框只会显示其他两个哦, 属于正常情况. 不要以为是哪里错了哈. 并且返回的结果还是 3 个(不要晕哈, 说的啰嗦, 其实不难理解) 小框框弹出来了, 返回也很顺利拿到了, 抿一口手边的枸杞菊花茶, 是不是很舒服? 上面说到了, “总是保持以上选择, 不再询问” , 就是“此生不复相见哦”, 那么如果之前手贱, 点了拒绝. 那如何才能重新订阅呢? 小生也是研究了的: 操作步骤: 右上角点点点, 》 设置 》订阅消息 效果一目了然 [图片] 前方高能: 设置里的订阅消息, 它此生的标签是 “总是保持以上选择, 不再询问”, 不管是你允许还是拒绝, 都不会在弹框里再看到 写在最后 提一个更苦逼的事情, 记得把之前为获取 formid 而写的代码, 统统删掉 写在最最后 给不给点赞? 不点我下一篇还问 未完待续… 以下是补充哦, 持续关注订阅消息 一: 一个模版, 在首页勾选了“总是保持以上选择, 不再询问”按钮. 在别的页面也将不会有弹框. 你懂的, 在产品角度这个是很重要、很关键的交互需求 二: 同意次数是可以累计的. 也就是说, 一个模版, 客户A点击了 10 次允许发送消息. 那我们就可以给他发 10 次模版消息提醒 三: 一次拒绝, 是不会清除之前同意所累计的次数的 这个是针对有网友说: “点一次同意, 再点击一次拒绝,是收不到消息的”. 实践证明: 点一次同意, 就能发一次消息, 后面点击拒绝, 不影响之前点击同意的 四: 有网友问: ”一个弹框有三个模版, 全都勾选并同意. 可以发几条消息? “ 很明显是三个模版每个可以发一次. 这个也是验证了的 干活!! 是不是满满的都是干活!
2019-12-09 - 小程序订阅消息开发指南
2019年10月12日微信开放了小程序订阅消息的功能。按官方的说法,目前的模板消息在实现小程序服务闭环上存在缺陷: 1. 部分开发者在用户无预期或未进行服务的情况下发送与用户无关的消息,对用户产生了骚扰;2. 模板消息需在用户访问小程序后的 7 天内下发,不能满足部分业务的时间要求模板消息确实存在上述的硬伤,不利于小程序的用户留存和用户体验。为了解决这些问题,微信官方推出了用户订阅消息功能。我在微慕专业版上加了订阅消息的功能,并验证了这个功能。这个功能是否能都达到官方的预期,这个我感觉不那么乐观。这里我先说我的感受:目前的订阅消息还不完善,后续还有很大的优化空间。 目前,官方只开放了“一次性订阅消息”,尚未开放“长期性订阅消息”,因此我只尝试了“一次性订阅消息”。 一次性订阅消息:用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。 订阅消息推送位置:服务通知 订阅消息下发条件:用户自主订阅 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面 以下我简单说明订阅消息的开发过程和使用体验。 一.订阅消息的开发1.获取订阅消息的模板ID 在微信小程序的管理后台,在左侧“功能”菜单,选择“订阅消息”,然后点击“添加” [图片] 然后选择你需要的消息模板,并配置关键词。 [图片] 配置完成后,如下图所示。 [图片] 值得关注的是,在配置好的模板详情页面里的“详细内容”很重要,这个就是开发订阅消息时需要遵循的消息格式,这个格式和模板消息有细微的差别 根据微慕小程序的需要,我选用了“新的评论提醒”和“内容更新提醒”这两个消息模版。前者用于提醒发表话题或文章的作者,有新的话题或文章评论,增强作者与读者之间的交流互动;后者是提醒订阅用户,小程序有新的文章发布,引导用户回归小程序。 订阅消息申请模板的时候,需要选择所属类目,只能选择当前小程序相关的类目模板,对于模板消息不需要选择对应类目。如果删除小程序类目,就会把订阅消息模板一起删除。因此删除类目要小心谨慎。 [图片] 2.触发用户订阅,获取下发的权限 触发用户订阅,微信小程序提供的api是: [代码]wx.requestSubscribeMessage[代码],用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。 注意:微信小程序开发工具尚不支持此功能,在开发工具触发订阅的api,会提示: requestSubscribeMessage:fail 开发者工具暂时不支持此 API 调试,请使用真机进行开发 调用api的代码示例如下: [代码]wx.requestSubscribeMessage({[代码] [代码]tmplIds: ["模板A","模板B"],[代码] [代码]success: function (res) {[代码] [代码]//成功[代码] [代码]},[代码] [代码]fail(err) {[代码] [代码]//失败[代码] [代码]console.error(err);[代码] [代码]}[代码] [代码]})[代码] wx.requestSubscribeMessage(Object object) 的回调函数[代码]object.success [代码]参数有两个:errMsg和TEMPLATE_ID; 接口调用成功时errMsg值为’requestSubscribeMessage:ok’。TEMPLATE_ID是动态的键,即模板id,值包括’accept’、’reject’、’ban’。’accept’表示用户同意订阅该条id对应的模板消息,’reject’表示用户拒绝订阅该条id对应的模板消息,’ban’表示已被后台封禁。例如 { errMsg: “requestSubscribeMessage:ok”, zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: “accept”} 表示用户同意订阅zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE这条消息。 个人觉得这个动态键不是特别合理,代码处理起来有些麻烦,如果改成静态键的json格式比较方便处理,例如: [代码]{[代码] [代码] errMsg:"requestSubscribeMessage:ok",[代码] [代码] result: [[代码] [代码] { templateId:"zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE",[代码] [代码]status:"accept"[代码] [代码]}[代码] [代码] ][代码] [代码]}[代码] 在手机上调用此api方法会调出订阅消息的界面,如下图所示: [图片] 关于这个订阅消息的授权有几点要注意: 1) 在确认提示框里,如果用户选择“取消”表示拒绝(取消)订阅消息,选择“允许”表示用户订阅一次消息。 2) 如果用户不勾选“总是保持以上选择,不再询问”,那么每次用户触发都会弹出提示框。 3) 如果用户勾选“总是保持以上选择,不再询问”,那么将再也不会唤起这个对话框。同时,如果选择“取消”,那么以后每次调用这个api的时候,都会自动拒绝;如果选择“允许”,那么以后每次调用此api,都会自动允许授权。 目前小程序没有提供获取用户是否授权订阅消息的方法。通过wx.openSetting 方法无法获取用户是否授权消息订阅的信息,scope 列表没有订阅消息的内容。 如果想从自动拒绝转换到自动自动运行,需要打开小程序的设置去配置。设置方法:点击小程序右上角的三个点,打开如下对话框 [图片] 然后选择“设置”,在设置项里选择“订阅消息” [图片] [图片] 4)对于同一种消息,用户可以订阅多次,订阅多少次,就会收到多少次订阅消息,这个订阅次数是否有上限,官方没有说明,初步判断是不限的。但是,微信不会提供订阅的次数,因此需要在小程序的后端服务里存储用户订阅的次数。因此,我在微慕小程序专业版里,提供了一个给用户多次订阅的设置,并记录用户订阅的次数。 [图片] 如果用户需要某个消息服务,可以订阅多次,当然也可以在点击“订阅”的对话框里选择“取消”,“取消”一次也就减少一次订阅。 5)对于支付的场景,也需要用户确认是否订阅,这个我觉得不合理,支付后给用户一个订单推送消息应该是刚性需求,不需要再询问一遍用户是否订阅。 2.调用接口下发订阅消息 订阅消息下发的接口是小程序后台服务端调用:subscribeMessage.send,此方法类似下发模板消息的方法,详细调用说明见参考官方的链接: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html 订阅消息的下发接口方法和模板消息稍有不同, 模板消息的json格式如下 [代码]"data": {[代码] [代码]"keyword1": {[代码] [代码]"value": "内容1",[代码] [代码]"color": "#000"[代码] [代码]},[代码] [代码]"keyword2": {[代码] [代码]"value": "内容2",[代码] [代码]"color": "#000"[代码] [代码]}[代码] [代码]}[代码] 而订阅消息的json格式如下: [代码]"data": {[代码] [代码]"thing1": {[代码] [代码]"value": "内容"[代码] [代码]},[代码] [代码]"number2": {[代码] [代码]"value": 20[代码] [代码]}[代码] 订阅消息的字段key是和数据类型有关,value的参数需要严格按照设置的类型提交,如果不按类型提交,会导致发送失败。同时如果是文本型的内容,字数也有限制,超过限制也会发送失败,但具体字数是多少,官方没有给出,同时中英文混合计算的长度也有差异,据我目前测试25个中文字符是可以的。希望官方能给出具体的字符长度限制的明确数字。 如果调用下发的次数大于用户的订阅次数,调用接口下发订阅消息会返回失败。报如下错误 [图片] 二.订阅消息使用心得1.订阅消息虽然把订阅的授权的交给了用户,但是也增加了用户使用难度,同时,一次性订阅只能收到一次,操作起来比较繁琐,如果不是刚需用户可能会首次就拒绝了这个服务,要想重新获取授权,需要用户自己打开小程序设置里去配置,颇为麻烦,小程序没有提供更简便的方法去唤起。 2.小程序的服务商为了获得更多给用户发送订阅消息的次数,肯定会想方设法去埋点引诱用户去点击订阅,这种诱导估计也是违规。 3.用户使用门槛和学习比较高,比如某个预约的服务,原来的场景是用户只要有提交表单,小程序就可以推送消息给用户,但是现在需要用户主动去订阅,无形中多了一步,如果用户不熟悉订阅消息或者直接点了“取消”,小程序就没法通知到用户了,用户可能因此错失服务,对商家和用户都是损失。 4.微信小程序将采用订阅消息,并逐步取消模板消息,虽然微信官方试图在方便用户和不打扰用户这两种选择里去寻求平衡,但订阅消息目前的模式恐怕无法达到这个期望,至少在我看来,无论对小程序的服务商,还是小程序的用户,都感到不方便。 update:2020年5月18日,日前订阅消息已经支持微信小程序开发工具。
2020-05-18 - 流量主视频广告ecpm从50 直接掉到 7?为啥突然差这么多,太低了
如题, APPID: wxb1c51d2d831c8db2 为啥突然差这么多?最近针对广告做了一系列优化, 收入没有提升,反而降了好多,ecpm下降和我们的优化难道也会有关系么?(清楚ecpm是微信根据用户画像来推荐的广告所得出来的) 附上图 [图片]
2019-10-29 - 小程序如何发送永久模板消息
大家都很清楚模板消息的使命即将结束,具体可查看下文 10月12日,微信在小程序模板消息能力方面公布了一项重大调整。原有的模板消息将升级为「订阅消息」,支持一次性和长期性订阅消息。而模板消息将于2020年1月10日下线。 查看该通知详情请移步 https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401 [图片] 但是模板消息存在的一次表单只能发一次模板消息的限制,在订阅消息方案中并没有解决,那么有没有一种可永久推送消息的实现方案呢,便是本文接下来要讲的内容 关于模板消息转成订阅消息的各种坑,在下面帖子中很详细 https://developers.weixin.qq.com/community/develop/doc/0006088c2940586de249dffbb5b400 在聊具体方案之前,先看看群里讨论的几张截图 [图片] [图片] [图片] [图片] 该方案在微信记账本中实现,微信记账本是腾讯官方推出的一个用于同步账单的小程序 [图片] 可永久推送模板消息方案是: 通过小程序和订阅号绑定,消息通过公众号的模板消息推送发出,只要用户同意推送,我们就可以无限制的发送模板消息。 [图片] [图片] 关于公众号发送模板消息的文档如下所示 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#5 该方案的弊端就是,在使用小程序的过程中,必须同时关注绑定的订阅号,在小程序用户原大于同主体订阅号的情况下,该方案慎用。 但是为了每天能收到推送我们未尝不可以多做一步,关注下订阅号。
2019-11-28 - 如何获取每天新增的关注者openid,由于数据量较大,可能无法每天都获取一遍全部用户list
如何获取每天新增的关注者openid,由于数据量较大,可能无法每天都获取一遍全部用户list 因而每次串行获取10000条openid的方式不可行 请问是否有其他方案
2019-11-19 - 小程序表单获取用户身份证会被封吗?
各位开发小哥哥, 小姐姐们好. 我最近想开发一个小程序, 有公司主体的. 想要在小程序上面添加一个表单, 获取用户的身份证号码, 上传身份证件, 说白了就是做一个实名认证的功能. 这个通过小程序表单形式获取用户信息可以吗? 会被封吗? 谢谢各位小哥哥, 小姐姐的赐教哈!!
2019-11-04 - 小程序识别身份证,银行卡,营业执照,驾照
最近老是有同学问我小程序ocr识别的问题,就趁机研究了下,实现了小程序识别身份证,银行卡,驾照,营业执照,图片文字的功能。今天来给大家讲讲详细的实现流程。 先画一张流程图出来 [图片] 第一次看到这个流程图,可能有点萌,什么云开发,云函数。。。。 不要着急,我们接下来会一步步带大家实现。 先看下我们的页面和效果图。 [图片] 功能其实很简单,就是我们点对应的按钮后,去拍照或者去相册选择对应的图片。然后把图片上传到云存储,会有一个对应的图片url,然后把这个图片url传递到云函数,然后云函数里使用小程序的开发ocr能力,来识别图片,返回对应的信息回来。如下图所示,我们识别银行卡(身份证什么的就不演示了,涉及到石头哥个人隐私) [图片] 接下来就是代码的实现了。 一,首先要创建一个云开发的小程序项目 这里我前面文章有讲解过,就不再细说了,不会的同学去翻看下我之前的文章。或者看下我录制的 讲解视频 这里有一点需要注意的给大家说下 [图片] 二,创建一个简单的小程序页面 1,index.wxml如下 [图片] 2,index.js完整代码如下 [代码]Page({ //身份证 shenfenzheng() { this.photo("shenfenzheng") }, //银行卡 yinhangka() { this.photo("yinhangka") }, //行驶证 xingshizheng() { this.photo("xingshizheng") }, //拍照或者从相册选择要识别的照片 photo(type) { let that = this wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success(res) { // tempFilePath可以作为img标签的src属性显示图片 let imgUrl = res.tempFilePaths[0]; that.uploadImg(type, imgUrl) } }) }, // 上传图片到云存储 uploadImg(type, imgUrl) { let that = this wx.cloud.uploadFile({ cloudPath: 'ocr/' + type + '.png', filePath: imgUrl, // 文件路径 success: res => { console.log("上传成功", res.fileID) that.getImgUrl(type, res.fileID) }, fail: err => { console.log("上传失败", err) } }) }, //获取云存储里的图片url getImgUrl(type, imgUrl) { let that = this wx.cloud.getTempFileURL({ fileList: [imgUrl], success: res => { let imgUrl = res.fileList[0].tempFileURL console.log("获取图片url成功", imgUrl) that.shibie(type, imgUrl) }, fail: err => { console.log("获取图片url失败", err) } }) }, //调用云函数,实现OCR识别 shibie(type, imgUrl) { wx.cloud.callFunction({ name: "ocr", data: { type: type, imgUrl: imgUrl }, success(res) { console.log("识别成功", res) }, fail(res) { console.log("识别失败", res) } }) } }) [代码] 上面代码注释讲解的很清楚了,再结合我们的流程图,相信你可以看明白。 [图片] 三,重头戏来了,识别的核心代码是下面这个云函数 [图片] 云函数的完整代码也给大家贴出来 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async(event, context) => { let { type, imgUrl } = event switch (type) { case 'shenfenzheng': { // 识别身份证 return shenfenzheng(imgUrl) } case 'yinhangka': { // 识别银行卡 return yinhangka(imgUrl) } case 'xingshizheng': { // 识别行驶证 return xingshizheng(imgUrl) } default: { return } } } //识别身份证 async function shenfenzheng(imgUrl) { try { const result = await cloud.openapi.ocr.idcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } //识别银行卡 async function yinhangka(imgUrl) { try { const result = await cloud.openapi.ocr.bankcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } //识别行驶证 async function xingshizheng(imgUrl) { try { const result = await cloud.openapi.ocr.vehicleLicense({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } [代码] 其实没什么特别的,就是用一个switch方法,根据用户传入的不同的type值,来实现不同的识别效果。 如用传入的type是‘ yinhangka’,我们就调用银行卡识别 [代码]try { const result = await cloud.openapi.ocr.bankcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } [代码] 进而把识别的结果返回给小程序端,如下图 [图片] 到这里我们就完整的实现了,小程序识别身份证,银行卡,行驶证的功能。至于别的更多的ocr识别,可以去看小程序官方文档,结合着我的这篇文章,相信你也可以轻松实现更多的图片识别。 [图片] 源码其实在上面都已经贴给大家了,如果你觉得不完整,想要完整的源码可以在文章底部留言或者私信我。
2019-10-30 - 用云服务定时器推送小游戏订阅消息,错误号501006,请问是什么原因?
我用云服务定时器推送小游戏订阅消息,报错了,详细日志为:Error: errCode: -501006 invalid common parameters | errMsg: subscribeMessage.send:fail param error: -702003 我的appid是:wxf35876b838fdbcda,调用时间是:2019年10月27日23点47分 我上传云函数的开发者工具版本为:Nightly v1.02.1910252 而且这个定时推送订阅消息,我之前逻辑是走通的,并未报错;但我昨天新增了一个云开发环境,切到新的云开发环境之后,就报了这个错误,不知道两者之间会有什么关联,另外我手动推送订阅消息都是正常的,只有走云开发定时推送报这个错误。
2019-10-28 - 「笔记」订阅消息体验踩坑
前言 10月12日夜晚社区发了公告小程序模板消息能力调整通知,正式发布了 一次性订阅消息 这一能力,所以第一时间进行了体验。 本文主要是补充一下官方未提供的使用方法,和使用中与模板消息用法的不同。 文档地址 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html 使用方法 [代码]wx.requestSubscribeMessage({ tmplIds: ["模板id1","模板id2"], success: function (res) { //成功 }, fail(err) { //失败 console.error(err); } }) [代码] 第一个坑 如果不勾选红色方框内的内容,用户每次触发订阅消息功能都会弹出授权窗口,如果用户勾选了则不会出现弹窗。 [图片] 第二个坑 目前开发者工具(v1.02.191012)不支持调试,只能通过真机调试。 [图片] 第三个坑 微信不会为开发者保存订阅次数,需要自己在后台记录用户触发的次数。 超过次数调用接口下发订阅消息会返回失败。 [图片] 第四个坑 发送模板格式和原来的模板消息格式不一致,特别是data内的内容,订阅消息的字段key是和数据类型有关,value的参数需要严格按照设置的类型提交,具体使用参考后台的模板详情。 模板消息的格式: [代码]"data": { "keyword1": { "value": "内容", "color": "#000" }, "keyword2": { "value": "内容", "color": "#000" } } [代码] 订阅消息的格式: [代码]"data": { "thing1": { "value": "内容" }, "number2": { "value": 20 } [代码] 第五个坑 订阅消息申请模板的时候,需要选择所属类目,而且只能是自己小程序相关类目,模板消息是不需要选择对应类目的。 如果删除小程序类目,则会把订阅消息模板一起删除,需谨慎操作。 [图片] 第六个坑 长期订阅消息只针对特定行业开放,所以普通开发者并无法使用。 结束 暂时就先总结这些,有其它坑再补充。
2019-10-13 - 实战分享: 小程序云开发玩转订阅消息(一)
[图片] 微信官方为提升小程序模板消息能力的使用体验,对模板消息的下发条件进行了调整。原有的小程序模板消息接口于 2020 年 1 月 10 日下线,届时将无法使用旧的小程序模板消息接口发送模板消息,取而代之的是新的一次性订阅消息和长期订阅消息。 订阅消息给小程序开发者带来了更好的触达用户的能力,在具体实施过程中,开发者如何把模板消息换成新的订阅消息,是否需要购买服务器来实现服务器鉴权,怎样才能在用户订阅之后一段时间后,给用户发送长期或一次性订阅消息呢? 小程序·云开发最近支持了通过云调用免 access_token 发送订阅消息,还新增支持了在定时触发器中实现云调用,这些能力可以帮助开发者轻松玩转小程序订阅消息。 我们今天会利用小程序·云开发进行一个小程序中实现订阅开课提醒的实战,帮助大家了解如何基于小程序·云开发快速接入小程序订阅消息。 [图片]整体时序图[图片]开课提醒订阅消息时序图环境准备注册小程序帐号[1]开通云开发服务[2]获取订阅消息模板 ID在微信小程序管理后台中,新增一个订阅消息的模板,这里我们新增了一个开课提醒的模板。 [图片]新增模板引导用户订阅微信小程序提供了[代码]wx.requestSubscribeMessage[代码] 接口来发起申请订阅权限界面。 [图片]微信申请订阅权限界面在 "订阅开课提醒" 的按钮上绑定 tap 事件,事件处理器我们这里用的 [代码]onSubscribe[代码] index.wxml [代码]<button class="btn" data-item="{{ item }}" bindtap="onSubscribe" hover-class="btn-hover" > 订阅开课提醒 </button> [代码]在 [代码]onSubscribe[代码] 函数内,我们会调用微信 API [代码]wx.requestSubscribeMessage[代码] 申请发送订阅消息权限,当用户在弹窗同意订阅之后,我们会收到 [代码]success[代码] 回调,将订阅的课程信息调用云函数 [代码]subscribe[代码] 存入云开发数据库,云函数 [代码]subscribe[代码] 的实现在下文会讲。 index.js [代码]onSubscribe: function(e) { // 获取课程信息 const item = e.currentTarget.dataset.item; // 调用微信 API 申请发送订阅消息 wx.requestSubscribeMessage({ // 传入订阅消息的模板id,模板 id 可在小程序管理后台申请 tmplIds: [lessonTmplId], success(res) { // 申请订阅成功 if (res.errMsg === 'requestSubscribeMessage:ok') { // 这里将订阅的课程信息调用云函数存入云开发数据 wx.cloud .callFunction({ name: 'subscribe', data: { data: item, templateId: lessonTmplId, }, }) .then(() => { wx.showToast({ title: '订阅成功', icon: 'success', duration: 2000, }); }) .catch(() => { wx.showToast({ title: '订阅失败', icon: 'success', duration: 2000, }); }); } }, }); [代码][代码] },[代码] 文章字数超出 50000 字,后半部分链接 《实战分享: 小程序云开发玩转订阅消息(二)》
2019-10-23 - 在云函数定时触发器中使用云调用不定时报错
- 当前 Bug 的表现(可附上截图) 我需要在云函数定时触发器中使用云调用发送模板消息,测试阶段使用固定的openId并且使用提前存储的formId,有时可以正常调用,有时报异常: [代码]{[代码][代码]"errorCode"[代码][代码]:1,[代码][代码]"errorMessage"[代码][代码]:[代码][代码]"user code exception caught"[代码][代码],[代码][代码]"stackTrace"[代码][代码]:[代码][代码]"errCode: -501007 invalid parameters | errMsg: openapi.templateMessage.send:fail Invalid request param"[代码][代码]}[代码]通过小程序调用次云函数时,调用正常。怀疑云调用在定时触发器中会出现bug,望解答。 - 预期表现 - 复现路径 - 提供一个最简复现 Demo
2019-04-09 - 云开发定时发送模板消息实现方案
在实际开发过程中,许多需求需要定时发送模板消息通知用户比如说给发起助力3小时未完成用户发送消息,传统开发通过自己发的服务器定时函数很容易就可以实现。但是小程序云开发定时函数不支持云调用,所以通过云开发实现定时发送模板消息也就无法实现。最初想的一个方案是:发送模板消息仍然通过自己的服务器发送。但是这样一来仍然会用到自己的服务器,服务器的部署维护仍需要自己来处理,这样就跟云开发提出的观念背道而驰了。于是想有没有一种方案完全脱离自己的服务器,就像云开发提出的口号让开发者只需要关注代码的逻辑。之前看了一篇文章《云开发如何对外提供URL》不少涉及到支付、订单等异步操作的场景,会需要提供一个回调 URL,以确保在用户完成自己的支付过程后,由支付服务器对回调的 URL 发起请求,确认调用成功。因此,不少用户在使用时提出了自己的疑问,应该如何实现这样的功能? 由于目前云开发云函数尚不支持云调用,所以我们需要借助腾讯云提供的云函数和 定时器,来实现类似的功能。 流程如下: [图片] 具体实现1. 创建云函数访问 https://console.cloud.tencent.com/ ,使用你的腾讯云账号登陆,在顶部菜单栏中找到「云产品」— 「Serverless」—「云函数」 [图片] 在云函数中,选择「函数服务」,并在函数服务页面点击「新建」 [图片] 创建一个新的云函数,其中函数名称根据你的需要填写 [图片] 运行环境选择 Node.js 8.9,创建方式选择空白函数,完成后点击下一步。 在下一页不需要做修改,直接点击完成。 2. 创建 API 网关调用在创建完成云函数以后,我们会进入到这样的界面,在这个界面中选择「触发方式」 [图片] 在触发方式页面新增一个触发方式,使用 「定时触发」,定时任务名字「任意」,触发周期「根据需求设置」,传入参数「可根据需求选择」。 [图片] 填写完成后,点击保存 3,编写小程序云函数 js代码 [代码]// 云函数入口文件[代码][代码]const cloud = require([代码][代码]'wx-server-sdk'[代码][代码])[代码] [代码]cloud.init()[代码] [代码]// 云函数入口函数[代码][代码]exports.main = async(event, context) => {[代码][代码] [代码][代码]const wxContext = cloud.getWXContext()[代码] [代码] [代码][代码]const sendresult = await sendMsg()[代码][代码] [代码][代码]return[代码] [代码]sendresult[代码][代码]}[代码][代码]async [代码][代码]function[代码] [代码]sendMsg() {[代码][代码] [代码][代码]return[代码] [代码]await cloud.openapi.templateMessage.send({[代码][代码] [代码][代码]touser: [代码][代码]"otgvn5YlKfigH0T6pNowJXVmElUk"[代码][代码],[代码][代码] [代码][代码]page: [代码][代码]'pages/pStepInfo/info'[代码][代码],[代码][代码] [代码][代码]template_id: [代码][代码]"LnF85yDgCq44-_ECl3pA_fnddZq9Uhv7J_V-eZf3jZs"[代码][代码],[代码][代码] [代码][代码]form_id: [代码][代码]"c365b4f3cb0b4dd18f3ccbc3d8071349"[代码][代码],[代码][代码] [代码][代码]emphasis_keyword: [代码][代码]'keyword1.DATA'[代码][代码],[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]keyword1: {[代码][代码] [代码][代码]value: [代码][代码]"charb"[代码][代码],[代码][代码] [代码][代码]},[代码][代码] [代码][代码]keyword2: {[代码][代码] [代码][代码]value: [代码][代码]"测试模板消息"[代码][代码],[代码][代码] [代码][代码]},[代码][代码] [代码][代码]keyword3: {[代码][代码] [代码][代码]value: [代码][代码]'拼点已完成'[代码][代码],[代码][代码] [代码][代码]},[代码] [代码] [代码][代码]}[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]return[代码] [代码]res[代码][代码] [代码][代码]}).[代码][代码]catch[代码][代码](err => {[代码][代码] [代码][代码]return[代码] [代码]err[代码][代码] [代码][代码]})[代码][代码]}[代码]json配置[代码]{[代码][代码] [代码][代码]"permissions"[代码][代码]: {[代码][代码] [代码][代码]"openapi"[代码][代码]: [[代码][代码] [代码][代码]"templateMessage.send"[代码][代码] [代码][代码]][代码][代码] [代码][代码]}[代码][代码]}[代码] 保存,上传云函数 4. 编写程序调用当我们完成了云函数的配置以后,接下来我们可以修改云函数,使其完成我们自己想要的功能。 我们希望云函数可以从外部发来的请求中获取到参数,并借助云开发提供的 API,调用云开发的云函数。则我们可以这样操作 我们在本地新建一个目录,并在其中执行如下命令(需要你提前安装了 N ode.js 环境) [代码]cd 新建文件夹[代码][代码]npm install --save request npm install --save request-promise[代码] 然后创建一个文件[代码]index.js[代码],并在其中加入如下代码,并设置其中的 APPID、SECRET、ENV_ID 等字段。 [代码]const rp = require([代码][代码]'request-promise'[代码][代码]);[代码][代码]const APPID = [代码][代码]''[代码][代码]; [代码][代码]// 小程序 APPID[代码][代码]const SECRET = [代码][代码]''[代码][代码]; [代码][代码]// 小程序 Secret[代码][代码]const ENV_ID = [代码][代码]''[代码][代码][代码]// 小程序 云环境 [代码][代码][代码]const NAME = [代码][代码]''//小程序云函数名[代码][代码]const TOKEN_URL = `https:[代码][代码]//api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}`[代码][代码]// const QUERY_URL = 'https://api.weixin.qq.com/tcb/invokecloudfunction'[代码] [代码]exports.main_handler = async (event, context, callback) => {[代码][代码] [代码][代码]let token_resp = await rp(TOKEN_URL)[代码][代码] [代码][代码]let token = JSON.parse(token_resp).access_token[代码][代码] [代码][代码]const QUERY_URL = [代码][代码]'https://api.weixin.qq.com/tcb/invokecloudfunction?access_token='[代码][代码]+token+[代码][代码]"&env="[代码][代码]+ENV_ID+[代码][代码]"&name="[代码][代码]+NAME[代码][代码] [代码][代码]const para = event.queryString||event[代码][代码] [代码][代码]para.time=[代码][代码]new[代码] [代码]Date().getTime()[代码][代码] [代码][代码]const options = {[代码][代码] [代码][代码]method: [代码][代码]'POST'[代码][代码],[代码][代码] [代码][代码]uri: QUERY_URL,[代码][代码] [代码][代码]body: {[代码][代码] [代码][代码]POSTBODY: para,[代码][代码] [代码][代码]},[代码][代码] [代码][代码]headers: {[代码][代码] [代码][代码]'User-Agent'[代码][代码]: [代码][代码]'Request-Promise'[代码][代码] [代码][代码]},[代码][代码] [代码][代码]json: [代码][代码]true[代码] [代码]// Automatically parses the JSON string in the response[代码][代码] [代码][代码]};[代码][代码] [代码][代码]const result = await rp(options)[代码] [代码] [代码][代码]return[代码] [代码]result[代码] [代码]}[代码] 配置完成后,保存文件。 5. 上传代码打包完成后,重新访问腾讯云控制台,找到刚刚创建的云函数,进入到「函数代码」中,选择其中的「本地上传文件夹」,选择你刚刚创建的文件夹,等待其自动压缩完成后,点击保存上传。 [图片] 上传完成后,会自动进行部署。 这样过一会模板消息就会发送成功了 在这里需要感谢一下社区的 白宦成,他的文章《云开发如何对外提供url》给了我灵感,也解决了困扰我多天的问题 至于如何定时发送多个模板消息请结合之前的一篇文章《云开发如何同时发送对个模板消息》
2019-09-23 - 使用PHP处理小程序订阅消息的推送
前端的接口:我们可以预设多个模板ID一起申请,用户勾选的会返回accept,也是就通过,后端就可以记录一次了。 [代码]wx.requestSubscribeMessage({ tmplIds: ["XII_0By8D9WabnUjVPB_8S1itsm2d4_xxx"], success: function (res) { if (res.XII_0By8D9WabnUjVPB_8S1itsm2d4_xxx === 'accept'){ wx.showToast({ title: '订阅OK!', }) } console.log(res) //成功 }, fail(err) { //失败 console.error(err); } }) }, [代码] [图片] 这里主要看一下后端PHP的代码: 其实很简单:一定要注意的是字段的类型要匹配,还有如果显示:xxx blackList 换个词或者模板吧。 [代码] * 发送订阅消息 * @return [type] [description] */ public function sendSubMessage($openId = "openId",$tmpl = "XII_0By8D9WabnUjVPB_8S1itsm2d4_xxx"){ $params['touser'] = $openId; $params['template_id'] = $tmpl; $params['page'] = "/pages/index/index"; $params['data'] = [ 'date2' => ['value' =>'2019-10-12'], // 'character_string7' =>['value'=>'121212'], 'thing1'=>['value'=>'开通完成'] ]; // return $this->success('200',$params); return $this->getCurl(self::$subMessageUrl.self::$accessToken,$params); } [代码] 看一下效果: [图片] 订阅消息现在有很多问题,在小程序端,如果勾选默认,即使把程序删了,也无法弹出,去权限设置那里,只有取消和勾选。。。。还有禁止。
2019-10-15 - 小程序模板消息能力调整通知
小程序模板消息能力在帮助小程序实现服务闭环的同时,也存在一些问题,如: 1. 部分开发者在用户无预期或未进行服务的情况下发送与用户无关的消息,对用户产生了骚扰; 2. 模板消息需在用户访问小程序后的 7 天内下发,不能满足部分业务的时间要求。 为提升小程序模板消息能力的使用体验,我们对模板消息的下发条件进行了调整,由用户自主订阅所需消息。 一次性订阅消息 一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。 [图片] (一次性订阅示例) 长期性订阅消息 一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。 目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。 调整计划 小程序订阅消息接口上线后,原先的模板消息接口将停止使用,详情如下: 1. 开发者可登录小程序管理后台开启订阅消息功能,接口开发可参考文档:《小程序订阅消息》 2. 开发者使用订阅消息能力时,需遵循运营规范,不可用奖励或其它形式强制用户订阅,不可下发与用户预期不符或违反国家法律法规的内容。具体可参考文档:《小程序订阅消息接口运营规范》 3. 原有的小程序模板消息接口将于 2020 年 1 月 10 日下线,届时将无法使用此接口发送模板消息,请各位开发者注意及时调整接口。 微信团队 2019.10.12
2019-10-13 - 批量获取用户基本信息接口调用问题
现在有个需求是: 先获取公众号下的用户列表, 然后通过用户列表获取各个用户对应的UNIONID信息, 现在由于公众号下关注用户达到100000人, 目前批量获取100000个用户对应的用户基本信息发现报错, [图片] 想知道100000人数的关注列表, 通过1000的遍历调用微信批量获取用户基本信息接口 可以成功吗?
2019-03-01 - 公众号流量主点击率太低了,只有0.5%,你们一般都是有多少啊?
[图片] 收入太低了
2019-09-27 - 小程序没流量?也许你忘了做这7件事
2019年,移动用户增速放缓、下沉市场释放增长红利、巨头APP流量优势凸显……商家寻找用户和流量出现,或许就在微信、抖音、百度和支付宝的小程序之中。 8月14日,《中国移动互联网2019半年大报告》显示,微信小程序500万以上月活跃用户的数量正在快速增长,由133个增至180个,超过100 万的达到883个;同时,小程序快速向中老年用户渗透,50岁以上用户增长了22%;此外,小程序的人均使用个数和支付人数分别有156%和109%的增 长,用户使用小程序的习惯正在不断加深。抢占用户流量,小程序已成为各行业商家必争的赛道。 商家使用小程序并不一定需要复杂的运营技巧,只要做好几项基础的引流工作,就能快速吸引线上、线下用户,提升销量。 1、符合搜索习惯的名称 微信搜索和“附近的小程序”是用户获取小程序的两个重要渠道,与来自扫码、分享、有朋友信任背书不同,通过搜索展示的小程序,越是符合用户搜索习惯、直白表达出商品和服务的特点的小程序名称就能吸引用户打开。特别是在“人无我有”阶段,微信搜索导流效果显著。 2、重视小程序页面标题 在新改版的微信中,即将上线“动态”功能,直接可以展示小程序商品、服务的页面。在这种像信息流一样的展示方式下,小程序页面标题越吸引人,就越能吸引用户打开自家的小程序。 3、实体店二维码海报引流 实体店的线下客流是小程序的重要流量池,既然没有额外人力、精力,商家也需要在门店、橱窗、收银台等显著位置放上带有小程序码的易拉宝、海报,并设置新用户优惠券、首单优惠等促销方式,引导用户进入小程序体验购物,从而在非营业时间也能产生订单。 小程序下载:https://www.sucaihuo.com/source/0-0-266-0-0-0 4、公众号菜单和图文 有公众号的商家应当做三件事情:1)小程序绑定公众号;2)在自定义菜单中设置小程序;3)图文消息中插入小程序,最好是以卡片的形式,更容易吸引点击。 5、宣传单页、卡片等物料 无论是线上电商还是线下实体店商家,都应当不放过每次接触用户的机会,来推广小程序。比如在宣传单页、配送车辆、实体会员卡、商品包装等上面印上小程序码,设置扫码优惠活动,吸引用户进入小程序。 6、小程序下拉菜单 下拉菜单是快速进入微信小程序的便捷方式,也是收藏常用小程序的重要入口,但并不是每个用户都熟悉。商家需要做的就是,通过发微信红包等奖励方式,引导用户将自家小程序放进“我的小程序”,这样可以有更高的概率让用户回到小程序。 7、建用户微信群 社交营销是小程序的最大优势,特别是对高频消费的快消品商家来说,创建用户微信群,并在用户群分享小程序活动,是最有效的活动营销方式之一。在微信群中,可以定时推出秒杀引流、拼团裂变、积分兑换福利等活动,培养用户小程序下单习惯。 据悉,微信官方正在基于多维度的小程序质量评估机制,给予优质小程序更多的展现入口。对商家而言,越早投入小程序运营、提供越优质的商品和服务,就越有可能获得更多的微信流量支持。小程序已成为企业最重要的获客渠道,目前来看,没有之一。
2019-09-01 - [打怪升级]小程序评论回复和发贴组件实战(一)
[图片] 在学习成长的过程中,常常会遇到一些自己从未接触的事物,这就好比是打怪升级,每次打倒一只怪,都会获得经验,让自己进步强大。特别是我们这些做技术的,逆水行舟不进则退。下面分享下小程序开发中的打怪升级经历~ [图片] 先来看下实际效果图,小程序开发中有时会要做一些的功能复杂的组件,比如评论回复和发帖功能等,这次主要讲的是关于评论模块的一些思路和实战中的经验,希望能抛砖引玉,给大家一些启发,一同成长~ [代码片段]评论回复组件实战demo demo的微信路径: https://developers.weixin.qq.com/s/oHs5cMma7N9W demo的ID:oHs5cMma7N9W 如果你装了IDE工具,可以直接访问上面的demo路径 通过代码片段将demo的ID输入进去也可添加: [图片] [图片] [图片] 根据这个demo.gif,本人做了一个简单的流程图,帮助大家理解。下面罗列一些开发中需要“打的怪”: 1、组件目录结构 [代码]├─components ---小程序自定义组件 │ ├─plugins --- (重点)可独立运行的大型模块,可以打包成plugins │ │ ├─comment ---评论模块 │ │ │ │ index.js │ │ │ │ index.json │ │ │ │ index.wxml │ │ │ │ index.wxss │ │ │ │ services.js ---(重点)用来处理和清洗数据的service.js,配套模板和插件 │ └─submit ---评论模块子模块:提交评论 index.js index.json index.wxml index.wxss [代码] 为什么要单独做个评论页面页面(submit)? 因为如果是当前页面最下面input输入的形式,会出现一些兼容问题,比如: 不同手机的虚拟键盘高度不同,不好绝对定位和完全适配 弹窗输入框过小输入不方便,如果是大的textare时,容易误触下面评论的交。 注:目录结构,仅供参考。 2、NODE端API接口返回结构和页面结构 [代码]//node:API接口返回 { "data": { "commentTotal": 40, "comments": [ { "contentText": "喜欢就关注我", //评论内容 "createTime": 1560158823647, //评论时间 "displayName": "智酷方程式", //用户名 "headPortrait": "https://blz.nosdn.127.net/1/weixin/zxts.jpg", //用户头像 "id": "46e0fb0066666666", //评论ID 用于回复和举报 "likeTotal": 2, //点赞数 "replyContents": [ //回复评论 { "contentText": "@智酷方程式 喜欢就回复我", //回复评论内容 "createTime": 1560158986524, //回复时间 "displayName": "神秘的前端开发", //回复的用户名 "headPortrait": "https://blz.nosdn.127.net/1/2018cosplay/fourth/tesss.jpg", //回复的用户头像 "id": "46e0fb00111111111", //回复评论的ID "likeTotal": 2, //回复评论的点赞数 "replyContents": [], //回复的回复 盖楼 "replyId": "46e0fb001ec222222222", //回复评论的独立ID,用于统计 }, { "contentText": "@智酷方程式: 威武,学习学习", "createTime": 1560407232814, "displayName": "神秘的前端开发", "headPortrait": "https://blz.nosdn.127.net/1/2018cosplay/fourth/tesss.jpg", "id": "46e0fb00111111111", "likeTotal": 0, "replyContents": [], "replyId": "46e0fb001ec222222222", } ], "replyId": "", "topicId": "46e0fb001ec3333333", } ], "curPage": 1, //当前页面 //通过ID 判断 当前用户点赞了 哪些评论 "likes": [ "46e0fb00111111111", "46e0fb001ec222222222", "46e0fb0066666666", ], "nextPage": null, //下一页 "pageSize": 20, //一页总共多少评论 "total": 7, //总共多少页面 }, "msg": "success", "status": "success" } [代码] [代码]<!-- HTML 部分 --> <block wx:if="{{commentList.length>0}}"> <!-- 评论模块 --> <block wx:for="{{commentList}}" wx:for-item="item" wx:for-index="index" wx:key="idx"> <view class="commentItem" catchtap="_goToReply" data-contentid="{{item.id}}" data-replyid="{{item.id}}" data-battle-tag="{{item.displayName}}"> <view class="titleWrap"> <image class="logo" src="{{item.headPortrait||'默认图'}}"></image> <view class="authorWrap"> <view class="author">{{item.displayName}}</view> <view class="time">{{item.createTime}}</view> </view> <view class="starWrap" catchtap="_clickLike" data-index="{{index}}" data-like="{{item.like}}" data-contentid="{{item.id}}" data-topicid="{{item.topicId}}"> <text class="count">{{item.likeTotal||""}}</text> <view class="workSprite icon {{item.like?'starIconHasClick':'starIcon'}}"></view> </view> </view> <view class="text"> {{item.contentText}} </view> </view> <!-- 评论的评论 --> <block wx:for="{{item.replyContents}}" wx:for-item="itemReply" wx:for-index="indexReply" wx:key="idxReply"> <view class="commentItem commentItemReply" catchtap="_goToReply" data-contentid="{{itemReply.id}}" data-replyid="{{item.id}}" data-battle-tag="{{itemReply.displayName}}"> ... 和上面类似 </view> </block> </block> <!-- 加载更多loading --> <block wx:if="{{isOver}}"> <view class="more">评论加载完成</view> </block> </block> [代码] 通过node提供一个API接口,通过用户的openId来判断是否点赞,这里提供一个参考的JSON结构。 JSON尽量做成array循环的结构方便渲染,根据ID来BAN人和管理。底部加上加载更多的效果,同时,记得做一些兼容,比如默认头像等。 3、评论中的一些微信原生交互 这里建议很多交互如果不是必须要特别定制,可以才用微信原生的组件,效果和兼容性都有保障,而且方便简单。 对评论进行回复/举报 [代码]<!-- HTML部分 通过绑定事件:_goToReply 进行交互--> <view class="commentItem" catchtap="_goToReply" data-contentid="{{item.id}}" data-replyid="{{item.id}}" data-battle-tag="{{item.displayName}}"> ... 内部省略 </view> [代码] [代码]//JS部分 微信原生wx.showActionSheet 显示操作菜单交互 _goToReply(e) { // 上面的各种授权判断省略... let self = this; wx.showActionSheet({ itemList: ['回复', '举报'], success: function (res) { if (!res.cancel) { console.log(res.tapIndex); //前往评论 if (res.tapIndex == 0) { //判断是否是 评论的评论 self._goToComment(replyid); } //举报按钮 if (res.tapIndex == 1) { //弹出框 self.setComplain(contentid); } } else { //取消选择 } }, fail(res) { console.log(res.errMsg) } }); } //当选择“举报”的时候,二次调用 wx.showActionSheet 方法 setComplain(contentid){ let complainJson = ["敏感信息", "色情淫秽", "垃圾广告", "语言辱骂", "其它"]; wx.showActionSheet({ itemList: complainJson, success: async res => { if (!res.cancel) { //选择好后,提交举报 try { let complainResult = await request.postComplainReport(complainJson[index], openid, contentid); if (complainResult.msg == "success") { //提交成功后反馈 } else { } } catch (e) { console.log(e) } } } }); } [代码] 显示操作菜单 wx.showActionSheet 方法说明 属性 类型 说明 itemList Array.<string> 按钮的文字数组,数组长度最大为 6 itemColor string 按钮的文字颜色 success function 接口调用成功的回调函数 fail function 接口调用失败的回调函数 complete function 接口调用结束的回调函数(调用成功、失败都会执行) 使用这个方法,即是主流的做法,也能很好的兼容不同机型,同时给予用户“习惯性体验”。 原生评论排序切换 [图片] [代码]<!-- picker组件 html部分--> <picker bindchange="bindPickerChange" value="{{index}}" range="{{array}}"> <view class="picker"> 当前选择:{{array[index]}} </view> </picker> [代码] [代码]// js部分 Page({ data:{ //查看评论类型切换 array: ["最佳", "最新", "只看自己"], //选择数组中的第几个显示 index:0 }, bindPickerChange(e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ index: e.detail.value }) } }) [代码] picker组件是一个从底部弹起的滚动选择器,这里我们用它来切换不同评论的排序。每次切换都可以通过 bindchange获得对应的变化,通过 e.detail.value获取用户选择的索引值。 官方文档: https://developers.weixin.qq.com/miniprogram/dev/component/picker.html 4、传参跳转写评论页 [代码]let uriData = { logo: "xxx.jpg", type: "commentReply", title: "文章:小程序评论,动态发帖开发指北\n 作者:智酷方程式", openId:"xxxxxxxxxxx", replyId:"aaaaaa" //用户回复的是哪个评论的ID }; wx.navigateTo({ url: `/components/plugins/comment/submit/index?uriData=${encodeURIComponent(JSON.stringify(uriData))}` }); [代码] 这个可以用encodeURIComponent的方式处理下参数中的中文,避免跳转发布评论页接收数据时出现乱码。 5、发表评论页 [图片] 显示和控制评论的字数 [代码]<!-- html部分 关于textarea 的配置 --> <view class='feedback-cont'> <textarea auto-focus="true" value="{{replyName}}" maxlength="200" bindinput="textareaCtrl" placeholder-style="color:#999;" placeholder="留下评论,共同学习,一起进步" /> <view class='fontNum'>{{content.length}}/200</view> </view> <view class='feedback-btn' bindtap='commentSubmit'>提交</view> [代码] [代码]// js部分 Page({ data: { //初始化评论内容,如果是回复则通过传参变成 @xxxx的形式 content: "@xxxx", }, textareaCtrl: function (e) { if (e.detail.value) { this.setData({ content: e.detail.value }) } else { this.setData({ content: "" }) } } }) [代码] textarea 在小程序中改动不大,这个标签原有的一些属性都可以继续使用,通过配置maxlength来控制字数,同时,设置auto-focus="true"可以让用户进到这个发表评论页面时自动弹出虚拟键盘和光标定位在输入的区域。 当然,也可以将发表评论和评论展示区域做在一起,这个就要考虑到要么通过“小程序API”获取键盘高度,要么将“发布评论”置顶区域显示,也是可以做的,只是相对考虑的点会多些。当时开发评论组件的时候,考虑开发时间短和用户体验,权衡后,最终决定以上方案,希望能给到大家一些参考和借鉴,在其他组件开发中触类旁通。 总结,“组件化思想”对于无论做小程序、react/VUE还是其他项目来说,减少重复开发,提高复用性都是一个非常重要的点。评论功能其实只要理清楚整体思路,做起来难度并不大,通过一些原生组件,可以大大提高开发效率,同时保证良好的兼容性。 后面一期还将分享下功能点较多的发帖组件开发。 往期回顾: [填坑手册]小程序Canvas生成海报(一) [拆弹时刻]小程序Canvas生成海报(二) [填坑手册]小程序目录结构和component组件使用心得
2021-09-13 - 小程序名称想改为与公众号同名,但二者认证主体不一致,因为资质问题无法修改,可以通过一下吗?
因为业务需求,想将小程序名称改为与公众号同名,审核被拒,理由是:二者主体不一致。 但问题是,小程序所在主体有需要的资质,而公众号的主体缺乏相应资质,公司内部修改公众号名称和主体流程也很琐碎麻烦,所以还没有办法修改小程序主体,同时小程序还很着急上线。 难道就没有别的办法了吗? 希望官方能通融处理一下,通过我们的名称修改申请。
2019-09-24 - 云开发,获取群ID——调试出来真的很简单。
1 app.js中 onLaunch: function (options) { if (!wx.cloud) console.error(‘请使用 2.2.3 或以上的基础库以使用云能力’) else wx.cloud.init({ traceUser: true, }) [代码]if (options.shareTicket) wx.getShareInfo({ shareTicket: options.shareTicket, success: function (res) { console.log('getShareTiket---shareTicket-->res', res) //获取cloudID let cID=res.cloudID //调用云函数mytest wx.cloud.callFunction({ name: 'mytest', // 这个 CloudID 值到云函数端会被替换 data: { weRunData: wx.cloud.CloudID(cID) }, success: function (res) { console.log('wx cloud mytest fun res', res); } }) } }) [代码] }, 2 云函数mytest const cloud = require(‘wx-server-sdk’) cloud.init() exports.main = (event, context) => { return { event } } /console.log(‘wx cloud mytest fun res’, res);查看打印出来的res, 真是一个惊喜。 不用npm,不用加密解密,不用传数据到自己开发服务器上。哎,一个群ID花了我好多时间啊,最后到底是迎来柳暗花明了。/
2019-09-24 - 几行代码实现小程序云开发提现功能
先看效果: [图片] 纯云开发实现,下面说使用步骤: 一:开通商户的企业付款到领取功能 说明地址: https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1 使用条件 1、商户号(或同主体其他非服务商商户号)已入驻90日 2、截止今日回推30天,商户号(或同主体其他非服务商商户号)连续不间断保持有交易 使用条件是第一难,第二难在下面这里 [图片] 在网上找了很多,感觉是云开发这里的一个不完善地方,如果不填ip,会报这种错 [代码]{"errorCode":1,"errorMessage":"user code exception caught","stackTrace":"NO_AUTH"} [代码] [代码]<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置]]></return_msg> <mch_appid><![CDATA[wx383426ad9ffe1111]]></mch_appid> <mchid><![CDATA[1536511111]]></mchid> <result_code><![CDATA[FAIL]]></result_code> <err_code><![CDATA[NO_AUTH]]></err_code> <err_code_des><![CDATA[此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置]]></err_code_des> </xml> [代码] 云开发没有ip这个概念,所以这里有些无从下手,不过这里我采用了个替代方案,参考了社区帖子: https://developers.weixin.qq.com/community/develop/doc/00088cff3a40d87d80f7267b65b800 之后我也亲自验证了,基本上就是这几个,当然肯定不够,但是可以自己在逻辑上进行处理,ip以下: [代码]172.81.207.12 172.81.212.74 172.81.236.99 172.81.235.12 172.81.245.51 212.64.65.131 212.64.84.22 212.64.85.35 212.64.85.139 212.64.87.134 [代码] 接着,可以动手了 二、云开发部分 1、设置云存储 证书配置地址: [图片] 下载后有三个文件,我们只需要p12结尾的那个 [图片] 然后,将这个apiclient_cert.p12文件上传到你的云存储 [图片] 这里处理完了,我们只需要一个东西,就是fileID也就是常说的云存储ID(上图红框内容) 2、配置云函数 新建云函数ref云函数 [图片] 代码如下: [代码]const config = { appid: 'wx383426ad9ffe1111', //小程序Appid envName: 'zf-shcud', // 小程序云开发环境ID mchid: '1111111111', //商户号 partnerKey: '1111111111111111111111', //此处填服务商密钥 pfx: '', //证书初始化 fileID: 'cloud://zf-shcud.11111111111111111/apiclient_cert.p12' //证书云存储id }; const cloud = require('wx-server-sdk') cloud.init({ env: config.envName }) const db = cloud.database(); const tenpay = require('tenpay'); //支付核心模块 exports.main = async(event, context) => { //首先获取证书文件 const res = await cloud.downloadFile({ fileID: config.fileID, }) config.pfx = res.fileContent let pay = new tenpay(config,true) let result = await pay.transfers({ //这部分参数含义参考https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 partner_trade_no: 'bookreflect' + Date.now() + event.num, openid: event.userinfo._openid, check_name: 'NO_CHECK', amount: parseInt(event.num) * 100, desc: '二手书小程序提现', }); if (result.result_code == 'SUCCESS') { //如果提现成功后的操作 //以下是进行余额计算 let re=await db.collection('user').doc(event.userinfo._id).update({ data: { parse: event.userinfo.parse - parseInt(event.num) } }); return re } } [代码] 需安装的依赖:wx-server-sdk、tenpay 这里只是实现了简单原始的提现操作,关于提现后,比如防止重复提交,提现限额这些,在开源二手书商城上有完整流程,地址: https://github.com/xuhuai66/used-book-pro 这种办法,不是每次都能成功提现,小概率遇到ip未在白名单情况,还是希望,云开发团队能尽快出一个更好的解决方案吧
2019-09-21 - 小程序的留言和评论功能实现
关于留言和评论回复功能,在后台服务器上只存该用户的openid,不存头像和用户名,别人用他的openid获取他的头像和用户名,这样可以么
2019-02-20 - 服务类目"***/***"与你提交代码审核时设置的功能页面内容不一致?怎么解决?
[图片] [图片] 只是一个小小的留言功能,都不行吗?
2019-08-07 - 小程序功能中有一个留言功能,需要选社交类目么?
小程序中有一个简单的留言功能,只能输入文字的那种,这种需要选择社交类目?
2019-08-13 - 物流助手如何解决多商户多家快递公司的绑定问题
你好: 文档: 登录小程序管理后台,点击「物流助手」-「开通」,开通物流助手; 绑定已与你有合作的物流公司帐号 如果是类似拼多多的商户平台,多商户情况下,怎么处理如上的第二步呢?可以说下流程吗
2019-06-05 - 用PHP快速调用快递单号识别查询api接口对接
背景: 不久前,自己对接调用实现了中通快递api的功能,发现如果换了其它快递再重新对接,岂不是会浪费太多的时间,物流这个接口对接是一个难题,要么需要逐一连接多家快递公司进行发货每对接一个快递公司就要开发十余个接口,开发工作量繁琐复杂。 所以选个第三方提供的快递API是最为合理的,下面给出快递鸟的api接口的设计实现。快递鸟是全球最大的第三方快递物流接口服务商, 目前已经集成了418家快递单号查询接口,31家电子面单接口。高实时、高稳定、高并发,支持快递单号自动识别。 《即时查询》的接口: RequestData和DataSign 都是签名后的数据。其他三个都是固定写法。 [图片] 快递鸟 快递鸟api实现物流即时查询 1.首先要有一个快递鸟账号,根据对方的要求,完善用户申请。 2.根据自己的需求,开通服务 3.进入“我的API接口”,根据“接口”中的开发文档了解设计需求,下载官方demo,编辑为适合自己的代码。 【即时查询】代码实现 1.修改官方的demo代码,我在此分离出了一个快递类,提高适用性 2.引入类文件,实例化并进行测试 [代码]package com.zs.app;[代码] [代码]import[代码] [代码]java.io.BufferedReader;[代码][代码]import[代码] [代码]java.io.IOException;[代码][代码]import[代码] [代码]java.io.InputStreamReader;[代码][代码]import[代码] [代码]java.io.OutputStreamWriter;[代码][代码]import[代码] [代码]java.io.UnsupportedEncodingException;[代码][代码]import[代码] [代码]java.net.HttpURLConnection;[代码][代码]import[代码] [代码]java.net.URL;[代码][代码]import[代码] [代码]java.net.URLEncoder;[代码][代码]import[代码] [代码]java.security.MessageDigest;[代码][代码]import[代码] [代码]java.util.HashMap;[代码][代码]import[代码] [代码]java.util.[代码][代码]Map[代码][代码]; [代码] [代码]/[代码][代码]*[代码][代码]*[代码][代码] [代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]快递鸟物流轨迹即时查询接口[代码][代码] [代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]@技术QQ群: [代码][代码]456320272[代码][代码] [代码][代码]*[代码] [代码]@see: http:[代码][代码]/[代码][代码]/[代码][代码]www.kdniao.com[代码][代码]/[代码][代码]YundanChaxunAPI.aspx[代码][代码] [代码][代码]*[代码] [代码]@copyright: 深圳市快金数据技术服务有限公司[代码][代码] [代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]DEMO中的电商[代码][代码]ID[代码][代码]与私钥仅限测试使用,正式环境请单独注册账号[代码][代码] [代码][代码]*[代码] [代码]单日超过[代码][代码]500[代码][代码]单查询量,建议接入我方物流轨迹订阅推送接口[代码][代码] [代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]ID[代码][代码]和Key请到官网申请:http:[代码][代码]/[代码][代码]/[代码][代码]www.kdniao.com[代码][代码]/[代码][代码]ServiceApply.aspx[代码][代码] [代码][代码]*[代码][代码]/[代码] [代码]public [代码][代码]class[代码] [代码]KdniaoTrackQueryAPI {[代码][代码] [代码] [代码] [代码][代码]/[代码][代码]/[代码][代码]DEMO[代码][代码] [代码][代码]public static void main(String[] args) {[代码][代码] [代码][代码]KdniaoTrackQueryAPI api [代码][代码]=[代码] [代码]new KdniaoTrackQueryAPI();[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]String result [代码][代码]=[代码] [代码]api.getOrderTracesByJson([代码][代码]"ANE"[代码][代码], [代码][代码]"210001633605"[代码][代码]);[代码][代码] [代码][代码]System.out.[代码][代码]print[代码][代码](result);[代码][代码] [代码] [代码] [代码][代码]} catch (Exception e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]/[代码][代码]/[代码][代码]电商[代码][代码]ID[代码][代码] [代码][代码]private String EBusinessID[代码][代码]=[代码][代码]"1330422"[代码][代码];[代码][代码] [代码][代码]/[代码][代码]/[代码][代码]电商加密私钥,快递鸟提供,注意保管,不要泄漏[代码][代码] [代码][代码]private String AppKey[代码][代码]=[代码][代码]"7611818b-6279-4398-8747-df2ca39e86b4"[代码][代码];[代码][代码] [代码][代码]/[代码][代码]/[代码][代码]请求url[代码][代码] [代码][代码]private String ReqURL[代码][代码]=[代码][代码]"http://api.kdniao.cc/Ebusiness/EbusinessOrderHandle.aspx"[代码][代码]; [代码][代码] [代码] [代码] [代码][代码]/[代码][代码]*[代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]Json方式 查询订单物流轨迹[代码][代码] [代码][代码]*[代码] [代码]@throws Exception [代码][代码] [代码][代码]*[代码][代码]/[代码][代码] [代码][代码]public String getOrderTracesByJson(String expCode, String expNo) throws Exception{[代码][代码] [代码][代码]String requestData[代码][代码]=[代码] [代码]"{'OrderCode':'','ShipperCode':'"[代码] [代码]+[代码] [代码]expCode [代码][代码]+[代码] [代码]"','LogisticCode':'"[代码] [代码]+[代码] [代码]expNo [代码][代码]+[代码] [代码]"'}"[代码][代码];[代码][代码] [代码] [代码] [代码][代码]Map[代码][代码]<String, String> params [代码][代码]=[代码] [代码]new HashMap<String, String>();[代码][代码] [代码][代码]params.put([代码][代码]"RequestData"[代码][代码], urlEncoder(requestData, [代码][代码]"UTF-8"[代码][代码]));[代码][代码] [代码][代码]params.put([代码][代码]"EBusinessID"[代码][代码], EBusinessID);[代码][代码] [代码][代码]params.put([代码][代码]"RequestType"[代码][代码], [代码][代码]"1002"[代码][代码]);[代码][代码] [代码][代码]String dataSign[代码][代码]=[代码][代码]encrypt(requestData, AppKey, [代码][代码]"UTF-8"[代码][代码]);[代码][代码] [代码][代码]params.put([代码][代码]"DataSign"[代码][代码], urlEncoder(dataSign, [代码][代码]"UTF-8"[代码][代码]));[代码][代码] [代码][代码]params.put([代码][代码]"DataType"[代码][代码], [代码][代码]"2"[代码][代码]);[代码][代码] [代码] [代码] [代码][代码]String result[代码][代码]=[代码][代码]sendPost(ReqURL, params); [代码][代码] [代码] [代码] [代码][代码]/[代码][代码]/[代码][代码]根据公司业务处理返回的信息......[代码][代码] [代码] [代码] [代码][代码]return[代码] [代码]result;[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]/[代码][代码]*[代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]MD5加密[代码][代码] [代码][代码]*[代码] [代码]@param [代码][代码]str[代码] [代码]内容 [代码][代码] [代码][代码]*[代码] [代码]@param charset 编码方式[代码][代码] [代码][代码]*[代码] [代码]@throws Exception [代码][代码] [代码][代码]*[代码][代码]/[代码][代码] [代码][代码]@SuppressWarnings[代码][代码]([代码][代码]"unused"[代码][代码])[代码][代码] [代码][代码]private String MD5(String [代码][代码]str[代码][代码], String charset) throws Exception {[代码][代码] [代码][代码]MessageDigest md [代码][代码]=[代码] [代码]MessageDigest.getInstance([代码][代码]"MD5"[代码][代码]);[代码][代码] [代码][代码]md.update([代码][代码]str[代码][代码].getBytes(charset));[代码][代码] [代码][代码]byte[] result [代码][代码]=[代码] [代码]md.digest();[代码][代码] [代码][代码]StringBuffer sb [代码][代码]=[代码] [代码]new StringBuffer([代码][代码]32[代码][代码]);[代码][代码] [代码][代码]for[代码] [代码]([代码][代码]int[代码] [代码]i [代码][代码]=[代码] [代码]0[代码][代码]; i < result.length; i[代码][代码]+[代码][代码]+[代码][代码]) {[代码][代码] [代码][代码]int[代码] [代码]val [代码][代码]=[代码] [代码]result[i] & [代码][代码]0xff[代码][代码];[代码][代码] [代码][代码]if[代码] [代码](val <[代码][代码]=[代码] [代码]0xf[代码][代码]) {[代码][代码] [代码][代码]sb.append([代码][代码]"0"[代码][代码]);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]sb.append(Integer.toHexString(val));[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]sb.toString().toLowerCase();[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]/[代码][代码]*[代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]base64编码[代码][代码] [代码][代码]*[代码] [代码]@param [代码][代码]str[代码] [代码]内容 [代码][代码] [代码][代码]*[代码] [代码]@param charset 编码方式[代码][代码] [代码][代码]*[代码] [代码]@throws UnsupportedEncodingException [代码][代码] [代码][代码]*[代码][代码]/[代码][代码] [代码][代码]private String base64(String [代码][代码]str[代码][代码], String charset) throws UnsupportedEncodingException{[代码][代码] [代码][代码]String encoded [代码][代码]=[代码] [代码]base64Encode([代码][代码]str[代码][代码].getBytes(charset));[代码][代码] [代码][代码]return[代码] [代码]encoded; [代码][代码] [代码][代码]} [代码][代码] [代码] [代码] [代码][代码]@SuppressWarnings[代码][代码]([代码][代码]"unused"[代码][代码])[代码][代码] [代码][代码]private String urlEncoder(String [代码][代码]str[代码][代码], String charset) throws UnsupportedEncodingException{[代码][代码] [代码][代码]String result [代码][代码]=[代码] [代码]URLEncoder.encode([代码][代码]str[代码][代码], charset);[代码][代码] [代码][代码]return[代码] [代码]result;[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]/[代码][代码]*[代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]电商Sign签名生成[代码][代码] [代码][代码]*[代码] [代码]@param content 内容 [代码][代码] [代码][代码]*[代码] [代码]@param keyValue Appkey [代码][代码] [代码][代码]*[代码] [代码]@param charset 编码方式[代码][代码] [代码][代码]*[代码] [代码]@throws UnsupportedEncodingException ,Exception[代码][代码] [代码][代码]*[代码] [代码]@[代码][代码]return[代码] [代码]DataSign签名[代码][代码] [代码][代码]*[代码][代码]/[代码][代码] [代码][代码]@SuppressWarnings[代码][代码]([代码][代码]"unused"[代码][代码])[代码][代码] [代码][代码]private String encrypt (String content, String keyValue, String charset) throws UnsupportedEncodingException, Exception[代码][代码] [代码][代码]{[代码][代码] [代码][代码]if[代码] [代码](keyValue ![代码][代码]=[代码] [代码]null)[代码][代码] [代码][代码]{[代码][代码] [代码][代码]return[代码] [代码]base64(MD5(content [代码][代码]+[代码] [代码]keyValue, charset), charset);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]base64(MD5(content, charset), charset);[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]/[代码][代码]*[代码][代码]*[代码][代码] [代码][代码]*[代码] [代码]向指定 URL 发送POST方法的请求 [代码][代码] [代码][代码]*[代码] [代码]@param url 发送请求的 URL [代码][代码] [代码][代码]*[代码] [代码]@param params 请求的参数集合 [代码][代码] [代码][代码]*[代码] [代码]@[代码][代码]return[代码] [代码]远程资源的响应结果[代码][代码] [代码][代码]*[代码][代码]/[代码][代码] [代码][代码]@SuppressWarnings[代码][代码]([代码][代码]"unused"[代码][代码])[代码][代码] [代码][代码]private String sendPost(String url, [代码][代码]Map[代码][代码]<String, String> params) {[代码][代码] [代码][代码]OutputStreamWriter out [代码][代码]=[代码] [代码]null;[代码][代码] [代码][代码]BufferedReader [代码][代码]in[代码] [代码]=[代码] [代码]null; [代码][代码] [代码][代码]StringBuilder result [代码][代码]=[代码] [代码]new StringBuilder(); [代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]URL realUrl [代码][代码]=[代码] [代码]new URL(url);[代码][代码] [代码][代码]HttpURLConnection conn [代码][代码]=[代码][代码](HttpURLConnection) realUrl.openConnection();[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]发送POST请求必须设置如下两行[代码][代码] [代码][代码]conn.setDoOutput(true);[代码][代码] [代码][代码]conn.setDoInput(true);[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]POST方法[代码][代码] [代码][代码]conn.setRequestMethod([代码][代码]"POST"[代码][代码]);[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]设置通用的请求属性[代码][代码] [代码][代码]conn.setRequestProperty([代码][代码]"accept"[代码][代码], [代码][代码]"*/*"[代码][代码]);[代码][代码] [代码][代码]conn.setRequestProperty([代码][代码]"connection"[代码][代码], [代码][代码]"Keep-Alive"[代码][代码]);[代码][代码] [代码][代码]conn.setRequestProperty([代码][代码]"user-agent"[代码][代码],[代码][代码] [代码][代码]"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"[代码][代码]);[代码][代码] [代码][代码]conn.setRequestProperty([代码][代码]"MessContent-Type"[代码][代码], [代码][代码]"application/x-www-form-urlencoded"[代码][代码]);[代码][代码] [代码][代码]conn.connect();[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]获取URLConnection对象对应的输出流[代码][代码] [代码][代码]out [代码][代码]=[代码] [代码]new OutputStreamWriter(conn.getOutputStream(), [代码][代码]"UTF-8"[代码][代码]);[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]发送请求参数 [代码][代码] [代码][代码]if[代码] [代码](params ![代码][代码]=[代码] [代码]null) {[代码][代码] [代码][代码]StringBuilder param [代码][代码]=[代码] [代码]new StringBuilder(); [代码][代码] [代码][代码]for[代码] [代码]([代码][代码]Map[代码][代码].Entry<String, String> entry : params.entrySet()) {[代码][代码] [代码][代码]if[代码][代码](param.length()>[代码][代码]0[代码][代码]){[代码][代码] [代码][代码]param.append([代码][代码]"&"[代码][代码]);[代码][代码] [代码][代码]} [代码][代码] [代码][代码]param.append(entry.getKey());[代码][代码] [代码][代码]param.append([代码][代码]"="[代码][代码]);[代码][代码] [代码][代码]param.append(entry.getValue()); [代码][代码] [代码][代码]/[代码][代码]/[代码][代码]System.out.println(entry.getKey()[代码][代码]+[代码][代码]":"[代码][代码]+[代码][代码]entry.getValue());[代码][代码] [代码][代码]}[代码][代码] [代码][代码]/[代码][代码]/[代码][代码]System.out.println([代码][代码]"param:"[代码][代码]+[代码][代码]param.toString());[代码][代码] [代码][代码]out.write(param.toString());[代码][代码] [代码][代码]}[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]flush输出流的缓冲[代码][代码] [代码][代码]out.flush();[代码][代码] [代码][代码]/[代码][代码]/[代码] [代码]定义BufferedReader输入流来读取URL的响应[代码][代码] [代码][代码]in[代码] [代码]=[代码] [代码]new BufferedReader([代码][代码] [代码][代码]new InputStreamReader(conn.getInputStream(), [代码][代码]"UTF-8"[代码][代码]));[代码][代码] [代码][代码]String line;[代码][代码] [代码][代码]while[代码] [代码]((line [代码][代码]=[代码] [代码]in[代码][代码].readLine()) ![代码][代码]=[代码] [代码]null) {[代码][代码] [代码][代码]result.append(line);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]} catch (Exception e) { [代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]/[代码][代码]/[代码][代码]使用[代码][代码]finally[代码][代码]块来关闭输出流、输入流[代码][代码] [代码][代码]finally[代码][代码]{[代码][代码] [代码][代码]try[代码][代码]{[代码][代码] [代码][代码]if[代码][代码](out![代码][代码]=[代码][代码]null){[代码][代码] [代码][代码]out.close();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]if[代码][代码]([代码][代码]in[代码][代码]![代码][代码]=[代码][代码]null){[代码][代码] [代码][代码]in[代码][代码].close();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]catch(IOException ex){[代码][代码] [代码][代码]ex.printStackTrace();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]result.toString();[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码] [代码] [代码][代码]private static char[] base64EncodeChars [代码][代码]=[代码] [代码]new char[] { [代码][代码] [代码][代码]'A'[代码][代码], [代码][代码]'B'[代码][代码], [代码][代码]'C'[代码][代码], [代码][代码]'D'[代码][代码], [代码][代码]'E'[代码][代码], [代码][代码]'F'[代码][代码], [代码][代码]'G'[代码][代码], [代码][代码]'H'[代码][代码], [代码][代码] [代码][代码]'I'[代码][代码], [代码][代码]'J'[代码][代码], [代码][代码]'K'[代码][代码], [代码][代码]'L'[代码][代码], [代码][代码]'M'[代码][代码], [代码][代码]'N'[代码][代码], [代码][代码]'O'[代码][代码], [代码][代码]'P'[代码][代码], [代码][代码] [代码][代码]'Q'[代码][代码], [代码][代码]'R'[代码][代码], [代码][代码]'S'[代码][代码], [代码][代码]'T'[代码][代码], [代码][代码]'U'[代码][代码], [代码][代码]'V'[代码][代码], [代码][代码]'W'[代码][代码], [代码][代码]'X'[代码][代码], [代码][代码] [代码][代码]'Y'[代码][代码], [代码][代码]'Z'[代码][代码], [代码][代码]'a'[代码][代码], [代码][代码]'b'[代码][代码], [代码][代码]'c'[代码][代码], [代码][代码]'d'[代码][代码], [代码][代码]'e'[代码][代码], [代码][代码]'f'[代码][代码], [代码][代码] [代码][代码]'g'[代码][代码], [代码][代码]'h'[代码][代码], [代码][代码]'i'[代码][代码], [代码][代码]'j'[代码][代码], [代码][代码]'k'[代码][代码], [代码][代码]'l'[代码][代码], [代码][代码]'m'[代码][代码], [代码][代码]'n'[代码][代码], [代码][代码] [代码][代码]'o'[代码][代码], [代码][代码]'p'[代码][代码], [代码][代码]'q'[代码][代码], [代码][代码]'r'[代码][代码], [代码][代码]'s'[代码][代码], [代码][代码]'t'[代码][代码], [代码][代码]'u'[代码][代码], [代码][代码]'v'[代码][代码], [代码][代码] [代码][代码]'w'[代码][代码], [代码][代码]'x'[代码][代码], [代码][代码]'y'[代码][代码], [代码][代码]'z'[代码][代码], [代码][代码]'0'[代码][代码], [代码][代码]'1'[代码][代码], [代码][代码]'2'[代码][代码], [代码][代码]'3'[代码][代码], [代码][代码] [代码][代码]'4'[代码][代码], [代码][代码]'5'[代码][代码], [代码][代码]'6'[代码][代码], [代码][代码]'7'[代码][代码], [代码][代码]'8'[代码][代码], [代码][代码]'9'[代码][代码], [代码][代码]'+'[代码][代码], [代码][代码]'/'[代码] [代码]}; [代码][代码] [代码] [代码] [代码][代码]public static String base64Encode(byte[] data) { [代码][代码] [代码][代码]StringBuffer sb [代码][代码]=[代码] [代码]new StringBuffer(); [代码][代码] [代码][代码]int[代码] [代码]len[代码] [代码]=[代码] [代码]data.length; [代码][代码] [代码][代码]int[代码] [代码]i [代码][代码]=[代码] [代码]0[代码][代码]; [代码][代码] [代码][代码]int[代码] [代码]b1, b2, b3; [代码][代码] [代码][代码]while[代码] [代码](i < [代码][代码]len[代码][代码]) { [代码][代码] [代码][代码]b1 [代码][代码]=[代码] [代码]data[i[代码][代码]+[代码][代码]+[代码][代码]] & [代码][代码]0xff[代码][代码]; [代码][代码] [代码][代码]if[代码] [代码](i [代码][代码]=[代码][代码]=[代码] [代码]len[代码][代码]) [代码][代码] [代码][代码]{ [代码][代码] [代码][代码]sb.append(base64EncodeChars[b1 >>> [代码][代码]2[代码][代码]]); [代码][代码] [代码][代码]sb.append(base64EncodeChars[(b1 & [代码][代码]0x3[代码][代码]) << [代码][代码]4[代码][代码]]); [代码][代码] [代码][代码]sb.append([代码][代码]"=="[代码][代码]); [代码][代码] [代码][代码]break[代码][代码]; [代码][代码] [代码][代码]} [代码][代码] [代码][代码]b2 [代码][代码]=[代码] [代码]data[i[代码][代码]+[代码][代码]+[代码][代码]] & [代码][代码]0xff[代码][代码]; [代码][代码] [代码][代码]if[代码] [代码](i [代码][代码]=[代码][代码]=[代码] [代码]len[代码][代码]) [代码][代码] [代码][代码]{ [代码][代码] [代码][代码]sb.append(base64EncodeChars[b1 >>> [代码][代码]2[代码][代码]]); [代码][代码] [代码][代码]sb.append(base64EncodeChars[((b1 & [代码][代码]0x03[代码][代码]) << [代码][代码]4[代码][代码]) | ((b2 & [代码][代码]0xf0[代码][代码]) >>> [代码][代码]4[代码][代码])]); [代码][代码] [代码][代码]sb.append(base64EncodeChars[(b2 & [代码][代码]0x0f[代码][代码]) << [代码][代码]2[代码][代码]]); [代码][代码] [代码][代码]sb.append([代码][代码]"="[代码][代码]); [代码][代码] [代码][代码]break[代码][代码]; [代码][代码] [代码][代码]} [代码][代码] [代码][代码]b3 [代码][代码]=[代码] [代码]data[i[代码][代码]+[代码][代码]+[代码][代码]] & [代码][代码]0xff[代码][代码]; [代码][代码] [代码][代码]sb.append(base64EncodeChars[b1 >>> [代码][代码]2[代码][代码]]); [代码][代码] [代码][代码]sb.append(base64EncodeChars[((b1 & [代码][代码]0x03[代码][代码]) << [代码][代码]4[代码][代码]) | ((b2 & [代码][代码]0xf0[代码][代码]) >>> [代码][代码]4[代码][代码])]); [代码][代码] [代码][代码]sb.append(base64EncodeChars[((b2 & [代码][代码]0x0f[代码][代码]) << [代码][代码]2[代码][代码]) | ((b3 & [代码][代码]0xc0[代码][代码]) >>> [代码][代码]6[代码][代码])]); [代码][代码] [代码][代码]sb.append(base64EncodeChars[b3 & [代码][代码]0x3f[代码][代码]]); [代码][代码] [代码][代码]} [代码][代码] [代码][代码]return[代码] [代码]sb.toString(); [代码][代码] [代码][代码]}[代码][代码]}[代码]
2019-09-16 - 小程序之日历签到积分
[图片] 该示例为纯手写代码,暂无插件,不多说直接上代码 我们的实现思路: JS部分 1、获取当前年月 const date = new Date(); cur_year = date.getFullYear(); cur_month = date.getMonth() + 1; const weeks_ch = [‘日’, ‘一’, ‘二’, ‘三’, ‘四’, ‘五’, ‘六’]; this.setData({ cur_year, cur_month, weeks_ch, }) 2、获取当月共多少天 getThisMonthDays: function (year, month) { return new Date(year, month, 0).getDate() }, 3、获取当月第一天星期几 getFirstDayOfWeek: function (year, month) { return new Date(Date.UTC(year, month - 1, 1)).getDay(); }, 4、计算当月1号前空了几个格子,把它填充在days数组的前面 calculateEmptyGrids: function (year, month) { var that = this; //计算每个月时要清零 that.setData({ days: [] }); const firstDayOfWeek = this.getFirstDayOfWeek(year, month); if (firstDayOfWeek > 0) { for (let i = 0; i < firstDayOfWeek; i++) { var obj = { date: null, isSign: false } that.data.days.push(obj); } this.setData({ days: that.data.days }); //清空 } else { this.setData({ days: [] }); } }, 5、绘制当月天数占的格子,并把它放到days数组中 calculateDays: function (year, month, sign) { var that = this; var isSign; const thisMonthDays = this.getThisMonthDays(year, month); for (var i = 1; i <= thisMonthDays; i++) { var obj = { date: i, isSign: ‘’ } for (var j = 0; j < sign.length; j++) { if (i == parseInt(sign[j].create_time.substr(8, 2))) { obj.isSign = true; break; } } that.data.days.push(obj); } this.setData({ days: that.data.days }); }, 6、切换控制年月,上一个月,下一个月 handleCalendar: function (e) { const handle = e.currentTarget.dataset.handle; const cur_year = this.data.cur_year; const cur_month = this.data.cur_month; if (handle === ‘prev’) { let newMonth = cur_month - 1; let newYear = cur_year; if (newMonth < 1) { newYear = cur_year - 1; newMonth = 12; } this.signRecord(newYear, newMonth); this.setData({ cur_year: newYear, cur_month: newMonth, imgType: ‘cnext.png’ }) } else { if (cur_month + 1 > month) { this.setData({ imgType: ‘next.png’ }) } else { let newMonth = cur_month + 1; let newYear = cur_year; if (newMonth > 12) { newYear = cur_year + 1; newMonth = 1; } this.signRecord(newYear, newMonth); if (cur_month + 1 == month) { this.setData({ cur_year: newYear, cur_month: newMonth, imgType: ‘next.png’ }) } else { this.setData({ cur_year: newYear, cur_month: newMonth, imgType: ‘cnext.png’ }) } } } }, wxml部分: <view class=‘all’> <view class=“bar”> <!-- 上一个月 --> <view class=“previous” bindtap=“handleCalendar” data-handle=“prev”> <image src=‘https://www.***.com/weChatImg/pre.png’></image> </view> <!-- 显示年月 --> <view class=“date”>{{cur_year || “–”}} / {{filter.fill(cur_month) || “–”}}</view> <!-- 下一个月 --> <view class=“next” bindtap=“handleCalendar” data-handle=“next”> <image src=‘https://www.***.com/weChatImg/{{imgType}}’></image> </view> </view> <view class=‘xxian’> <image src=‘weChatImg/huan.png’></image> <image src=‘weChatImg/huan.png’></image> </view> <!-- 显示星期 --> <view class=“week”> <view wx:for="{{weeks_ch}}" wx:key="{{index}}" data-idx="{{index}}">{{item}}</view> </view> <view class=‘days’> <!-- 列 --> <view class=“columns” wx:for="{{days.length/7}}" wx:for-index=“i” wx:key=“i”> <view wx:for="{{days}}" wx:for-index=“j” wx:key=“j”> <!-- 行 --> <view class=“rows” wx:if="{{j/7 == i}}"> <view class=“rows” wx:for="{{7}}" wx:for-index=“k” wx:key=“k”> <!-- 每个月份的空的单元格 --> <view class=‘cell’ wx:if="{{days[j+k].date == null}}"> <text decode="{{true}}"> </text> </view> <!-- 每个月份的有数字的单元格 --> <view class=‘cell’ wx:else> <!-- 当前日期已签到 --> <view wx:if="{{days[j+k].isSign == true}}" style=‘color:#acacac’ class=‘cell’> {{days[j+k].date}} <image src=‘https://www.***.com/weChatImg/sgin.png’></image> </view> <!-- 当前日期未签到 --> <view wx:else> <text>{{days[j+k].date}}</text> </view> </view> </view> </view> </view> </view> </view> </view> 相信大家通过以上思路,再结合自己的需求应该可以自己做出符合自己心目中的日历插件或者签到
2019-09-11 - 真机 view overflow-y下滑会很卡
说明: 为一个view添加CSS样式overflow-y, 在模拟器下一切正常,在真机下很卡, 几十个像素后就立即停止。(不会平滑的从快到慢再停)。100%重现该问题 功能详情: 异步加载了网络图片,形成一个列表,支持下滑操作。 开发版本0.11.112301 MAC版本 真机:iPhone 6, iOS 10.2
2016-12-28 - 如何利用小程序提高10倍活动效果?
前言 大家好,我是一名程序员,从2017年开通了个技术公众号。 从2017年开启了一个长期的活动但是效果很一般,并且是越来越差,而2019年我采用了云开发技术独立开发的小程序后效果比之前的效果好了10倍。 接下来我就来说说这整个运营过程中的变化。 开始 2017年10月,开启帮你养成好习惯活动。 [图片] 活动 规则: 每天在文章下方留言评论,连续60天可获得奖励 奖励: 定制笔记本纪念套装 目的: 增加用户对公众号的粘性 活动刚开始运营的时候效果非常不错,近1个月都是每天基本上都是100条左右,效果超出了我的预期。 [图片] 但是好景不长,过了一个月之后80条左右,再过了一个月50条左右,再过一个月30条左右。。。。慢慢的参与的人就越来越少了。 [图片] 激活 到了2018年,我想再次激活活动加强奖励,于是在2018年的10月份又开启了新的一轮活动,虽然规则一样但是奖励多了。 [图片] 活动 在原来的基础上新增了100天的奖励,定制衣服一件。 本以为效果可以像之前一样,但是没想到效果大不如从前。 [图片] 并且效果比去年失效的更快,一个月的时间留言数量就到了20左右。 [图片] 本次激活策略以失败告终。 重生 在2019年的3月份,我采用云开发制作了一个打卡小程序替代之前的留言活动方式。 [图片] 进入小程序,一键打卡即可。 [图片] 之前的打卡规则:通过留言的形式进行打卡,累计留言达一定天数还送专属福利。 在这个过程中存在3个问题: 对于运营:累计留言天数无法很精确的统计,只能通过看每天的留言数据。 对于读者:参与门槛过高,在通勤的路上不方便打字留言,参与人数很少。 对于读者:很多时候忘记打卡,没有一个提醒机制,不能查看打卡记录。 运营成本过高,参与成本也过高。 做这款小程序的目的就是降低这两个成本,顺便学习下云开发相关技术。 以下是打卡流程图: [图片] 数据对比 参与打卡: 从3月份到9月份,共6个月的时间累计打卡次数57235次。 平均每个月打卡次数9539,平均每天打卡317人参与。 参与留言: 从2017年10月到2019年3月份,共17个月累计留言14749条。 平均每个月打卡次数867,平均每天留言29人参与。 注:按每个月30天来算。 结果 采用小程序后参与活动人数对比之前提高了10倍。
2020-11-23 - 数组去重常用方法
function DuplicateRemovalArray_1(arr) { var resultArr; resultArr = arr.filter(function (item, index, array) { return array.indexOf(item) == index; }); return resultArr; } function DuplicateRemovalArray_2(arr) { /** * Accumulator (acc) (累计器) Current Value (cur) (当前值) Current Index (idx) (当前索引) Source Array (src) (源数组) * */ let result = arr.sort().reduce((accumulator, currentValue, currentIndex, array) => { if (accumulator.length === 0 || accumulator[accumulator.length - 1] !== currentValue) { accumulator.push(currentValue); } return accumulator; }, []); return result; } function DuplicateRemovalArray_3(arr) { let mapArray = Array.from(new Set(arr)); return mapArray; } function DuplicateRemovalArray_4(arr) { let mapArray = [...new Set(arr)] return mapArray; } let p1 = DuplicateRemovalArray_1([1,2,2,3]); let p2 = DuplicateRemovalArray_2([3,2,6,6,6,3]); let p3 = DuplicateRemovalArray_3([1,1,0,1,5]); let p4 = DuplicateRemovalArray_4([1,1,0,1,5]); console.log(p1); console.log(p2); console.log(p3); console.log(p4); let Channel = [ { "key": "其他", "Count": 1638, "Ratio": "2.939300%" }, { "key": "其他", "Count": 23057, "Ratio": "41.374900%" }, { "key": "平面", "Count": 26674, "Ratio": "47.865400%" }, { "key": "工程", "Count": 32, "Ratio": "0.057400%" }, ]; var obj = {}; let arr = Channel.reduce(function (item, next) { obj[next.key] ? '' : obj[next.key] = true && item.push(next); return item; }, []); console.log(arr); // 对象去重 输出 [ 1, 2, 3 ] [ 2, 3, 6 ] [ 1, 0, 5 ] [ 1, 0, 5 ] [ { key: '其他', Count: 1638, Ratio: '2.939300%' }, { key: '平面', Count: 26674, Ratio: '47.865400%' }, { key: '工程', Count: 32, Ratio: '0.057400%' } ] function uniqueArray(list) { list.sort() // [1,101,2,20,21,211,3,1] 注意排序 101 跟 2 const size = list.length let slowP = 0 for( let fastP = 0; fastP < size; fastP++) { if(list[fastP] != list[slowP]) { slowP++ list[slowP] = list[fastP] } } // console.log("slowP", slowP, list) return list.slice(0, slowP + 1) } let t = uniqueArray([1,101,2,20,21,211,3,1]) t=> [ 1, 101, 2, 20, 21, 211, 3 ]
2022-11-09 - 云函数获取用户手机号吗?
小程序通过云函数获得用户手机号码? 思路解析, [图片] 了解了小程序的加密方式,我们就可以自己去解密我们需要的信息。如:最困住我们的用户手机号码? 官方是有案例的,想更多学习可以给与参考,但是估计要多看几遍,有node基础的就比较好理解一些。 [图片] 下面是官网地址: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html#method-cloud 下面开始说我自己的方法; 1.首先构建云函数,需要两个云函数,一个用来解密,session_key,一个用来解密加密手机号码; [代码]//云函数:getSession; // 云函数入口文件 const cloud = require('wx-server-sdk') //npm install request-promise 通过终端下载npm install wx-server-sdk , const rp = require('request-promise'); cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const _JSCODE = event.code const AccessToken_options = { method: 'GET', url: 'https://api.weixin.qq.com/sns/jscode2session', qs: { appid: '', //你的小程序appid; secret: '', //你的秘钥 grant_type: 'authorization_code', js_code: _JSCODE }, json: true }; const resultValue = await rp(AccessToken_options); return { resultValue } } [代码] 下载好需要的两个包,就可以对云函数初始化,执行npm init 有个起名字的环节,用过node的都知道,默认index.js,有个选择 [代码]package name: (gettoken) index.js version: (1.0.0) description: git repository: keywords: author: license: (ISC) About to write to D:\projects\zy_face_id_wxs\server\getToken\package.json: { "name": "index.js", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "request-promise": "^4.2.4", "wx-server-sdk": "^0.8.1" }, "devDependencies": {}, "description": "" } Is this OK? (yes) yes [代码] 这是初始化,终端的代码; 右键点击上传并部署到云端; 下面是客户端的代码; [代码] getPhoneNumber(e) { if (!e.detail.errMsg || e.detail.errMsg != "getPhoneNumber:ok") { wx.showModal({ content: '不能获取手机号码', showCancel: false }) return; } wx.showLoading({ title: '获取手机号中...', }) console.log(e) wx.login({ success(res) { if (res.code) { console.log(res.code) console.log(e.detail.iv) console.log(e.detail.encryptedData) wx.cloud.callFunction({ name: 'getSession', //调用云函数获取session_key; data: { code: res.code, }, success: res => { wx.hideLoading() // console.log(res.result.resultValue) var data = res.result.resultValue console.log(data) console.log(data.session_key) //获取到了session_key的值; }, fail: error => { console.log(error) } }) } } }) }, [代码] 2.构建第二个云函数 GetWX; [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') // const requestpromise = require('request-promise'); var WXBizDataCrypt = require('./RdWXBizDataCrypt') // 用于手机号解密 cloud.init() exports.main = async (event, context) => { const session_key = event.session_key //appid写入你自己的appid,session_key 用第一个云函数的返回值; const pc = new WXBizDataCrypt(appid, session_key ) // -解密第一步 const data = pc.decryptData(event.encryptedData, event.iv) // 解密第二步 return { data } } [代码] 这个同样执行上面的步骤,npm install wx-server-sdk 和初始化npm init; 重点来了,解密所需要的js文件。 [图片]` 这两个文件已经上传到我的网盘里面,需要的请下载; https://pan.baidu.com/s/1VrS1gX_Bw3dKaZkQnNzy2A 提取码:cxnh; 格式和图片的保持一致,并上传到云端。 3.开始前端调用了; 代码如下; [代码]getPhoneNumber(e) { if (!e.detail.errMsg || e.detail.errMsg != "getPhoneNumber:ok") { wx.showModal({ content: '不能获取手机号码', showCancel: false }) return; } wx.showLoading({ title: '获取手机号中...', }) console.log(e) wx.login({ success(res) { if (res.code) { console.log(res.code) console.log(e.detail.iv) console.log(e.detail.encryptedData) wx.cloud.callFunction({ name: 'getSession', //调用云函数获取session_key; data: { code: res.code, }, success: res => { wx.hideLoading() var data = res.result.resultValue console.log(data) console.log(data.session_key) //获取到了session_key的值; const session_key=data.session_key wx.cloud.callFunction({ name:'getWX', //解析秘文,获得手机号码; data:{ session_key: session_key, encryptedData: e.detail.encryptedData, iv: e.detail.iv, }, success:res=>{ console.log(res) }, fail:err=>{ console.log(err) } }) }, fail: error => { console.log(error) } }) } } }) }, [代码] 4.输出的结果: [图片] 遇到问题了在私信问我,一一回答。
2019-07-17 - 云开发怎么同时向多人发送模板消息?
1,找出符合条件的人 [图片] 2,发送模板消息 [图片] 这样消息发不出去,如果只发一条是可以发出去的 求大神指点[图片][图片][图片][图片][图片][图片][图片][图片][图片][图片][图片][图片][图片] [图片][图片]
2019-08-27 - 定时发送模板消息功能实现(云开发实现)。
在准备开发这个功能之前,请确保你已经阅读过云开发文档和以下相关官方文档。 模板消息 获取AccessToken 发送模板消息 云函数定时触发器 我们来设定一个需求场景。以小程序【抽奖助手】为例,用户参与抽奖后,需要在开奖时间发送给用户开奖结果通知。这个通知采用模板消息形式下发。 先整理一下思路,实现这个功能我们需要哪些模块? 定时任务执行器。根据任务类型调用相应任务处理程序。 开奖任务处理程序,开奖后发送模板消息,通知用户结果。 云函数中调用 sendTemplateMessage 后端接口,发送模板消息。 周期获取AccessToken。请求后端接口需要用AccessToken,周期更新AccessToken,放入数据库中,随用随取。 模块实现 1.定时任务执行器 以云数据库形式实现。添加一个定时任务就是在该集合增加一条记录,移除同理。 记录字段设计: [代码]timeingTask{ _id: taskType: //任务类型,决定如何处理这个任务 execTime: // 触发时间。到达这个时间开始执行。 data:{} // 必要数据 } [代码] 然后设置云函数周期执行。每分钟查询一次该定时任务数据库,是否有任务到达执行时间。如果有则根据类型进行处理,并在数据库中移除该任务。 [代码]const cloud = require('wx-server-sdk') cloud.init({ env: '你的云环境ID' }) const db = cloud.database() exports.main = async(event, context) => { const execTasks = []; // 待执行任务栈 // 1.查询是否有定时任务。(timeingTask)集合是否有数据。 let taskRes = await db.collection('timeingTask').limit(100).get() let tasks = taskRes.data; // 2.定时任务是否到达触发时间。只触发一次。 let now = new Date(); try { for (let i = 0; i < tasks.length; i++) { if (tasks[i].execTime <= now) { // 时间到 execTasks.push(tasks[i]); // 存入待执行任务栈 // 定时任务数据库中删除该任务 await db.collection('timeingTask').doc(tasks[i]._id).remove() } } } catch (e) { console.error(e) } // 3.处理待执行任务 for (let i = 0; i < execTasks.length; i++) { let task = execTasks[i]; if (task.taskType == 1) { // 定时开奖任务 const kaiJinag = require('kaiJiang.js') try { await kaiJinag.kai(task.data.activity_id) } catch(e) { console.error(e) } } } } [代码] 使云函数每分钟执行的触发器代码: [代码]{ "triggers": [ { "name": "timeingTaskExecutor", "type": "timer", "config": "0 */1 * * * * *" } ] } [代码] 2.开奖任务处理程序 [代码]kaijiang.js[代码] [代码] const cloud = require('wx-server-sdk') const templateMessage = require('templateMessage.js') const COLL_FIELD_NAME = 'publicField'; const FIELD_NAME = 'ACCESS_TOKEN' const MSGID = '你的模板消息ID'; cloud.init({ env: '你的云环境ID' }) const db = cloud.database() const kai = async activity_id => { // 根据活动id,获取参与用户信息,获取到用户的 openid 和 formid. // 开奖程序省略 // 从数据库中获取AccessToken let tokenRes = await db.collection(COLL_FIELD_NAME).doc(FIELD_NAME).get(); let token = tokenRes.data.token; // access_token let page = '点击模板消息,想要打开的小程序页面'; let msgData = { "keyword1": { "value": activity.prizeName }, "keyword2": { "value": "你参与的抽奖活动正在开奖,点击查看中奖名单" }, }; let openid = '用户openid'; let formid = '用户formid'; await templateMessage.sendTemplateMsg(token, MSGID, msgData, openid, formid, page); } module.exports = { kai: kai, } [代码] 3.发送模板消息 [代码]templateMessage.js[代码] 封装在一个 js 文件里,传入必要参数调用即可。 [代码]const rp = require('request-promise'); const sendTemplateMsg = async (token, msgid, msgData, openid, formid, page) => { await rp({ json: true, method: 'POST', uri: 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=' + token, body: { touser: openid, template_id: msgid, page: page, form_id: formid, data: msgData } }).then(res => { }).catch(err => { console.error(err) }) } module.exports = { sendTemplateMsg: sendTemplateMsg, } [代码] 4.周期获取AccessToken 使用云函数触发器,使云函数每小时请求一次AccessToken,并将AccessToken存入云数据库中。 [代码]const cloud = require('wx-server-sdk') const rq = require('request-promise') const APPID = '你的APPID'; const APPSECRET = '你的APPSECRET'; const COLLNAME = 'publicField'; const FIELDNAME = 'ACCESS_TOKEN' cloud.init({ env: '你的云环境ID' }) const db = cloud.database() exports.main = async(event, context) => { try { let res = await rq({ method: 'GET', uri: "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + APPSECRET, }); res = JSON.parse(res) let resUpdate = await db.collection(COLLNAME).doc(FIELDNAME).update({ data: { token: res.access_token } }) } catch (e) { console.error(e) } } [代码] 使云函数一小时执行一次的触发器代码: [代码]{ "triggers": [ { "name": "pollGetAccessToken", "type": "timer", "config": "0 0 */1 * * * *" } ] } [代码] 这是我做的产品册小程序中的部分代码,目前项目还没有发布,发布后会开源出来。项目中用到了挺多开源组件,给我很大的帮助,希望我的分享也可以帮助到一些人。 有问题可以在公众号后台联系我,我看到都会回复的。 [图片]
2019-03-15 - 云开发同时发送多个模板消息
这段时间做项目用到了云开发,以及模板消息发送,其中有个需求是当助力完成时,需要给发起助力的以及帮助助力的人发送模板消息,因为之前没接触过云开发,所有写的过程中遇到了诸多问题,首先要感谢一下社区染柒, 老张, cc, o0o往后余生 谢谢他们提供的思路,下面分享一下过程中遇到的问题及解决方案: 方案1:建立一个模板任务集合task,里面记录需要发送模板任务的信息(包括openid formid template_id send_time 等等),然后写一个定时函数执行,定时函数每次执行时,把需要发送的任务取出来,发送模板消息, 结果:消息发不出去, 原因:定时函数不支持云调用 方案2:既然云函数不支持云调用那么就在帮助助力的云函数中处理模板消息。当用户帮助助力时,这时候助力刚好够了,然后就给发起人以及参与人发消息, 第一次尝试:[图片] 由于sendMsg未加关键字await ,所有结果消息仍然没发出去,原因:发送模板消息需要耗时,可能模板消息尚在发送中,云函数已经结束了 第二次尝试: [图片] 消息发送成功,一切万事大吉?NO,现在还会存在一个问题,当最后一个助力人帮好友助力的时候,他帮助助力后,该助力就会完成了,就需要给所有参与人发送模板消息了,发送模板消息是一个比较耗时的操作,况且这时候发送的还不止一个消息,如果一直等着模板消息发送完成的时候才把助力结果返回,那么最后一次助力请求要耗费很长一段时间,用户体验上是很不好的,所以产生了第三个方案 方案3:帮助助力逻辑不变,只是把发送模板消息单独写在一个函数中,当小程序端用户帮助助力得到结果时,如果这次帮助助力使得助力完成了,那么就调用一下发送模板消息的方法发送模板消息 [图片] [图片]
2019-08-30 - 前端吧图片二进制流数据 转成可显示的图片 地址
开发一个功能,生成的海报上显示小程序码,后台返回给前端是一个图片二进制数据流,前端怎么把二进制数据流转成可显示的图片地址呢
2019-01-16 - 借助云开发实现小程序模版消息推送(不用搭建服务器就可以实现微信消息推送)
上一节给大家将了借助云开发实现小程序支付功能,那么我们就要想了,能不能借助云开发实现小程序消息推送功能呢? 还别说,云开发还真能实现推送的功能。 一直关注我的同学肯定知道老师之前也写过借助java后台实现小程序消息推送的文章。 我们借助java后台虽然也能轻松的实现消息推送。但是呢?用java开发后台推送,必须要搭建服务器,学习java代码,部署java代码当然你就是做java开发的,或者学习过java,这没什么。 但是作为小程序开发人员来说,用java显得太重了。 传送门: 《借助小程序云开发实现小程序支付功能(含源码)》 《5行代码实现微信小程序模版消息推送 (含推送后台和小程序源码)》 下面就来教大家如何借助云开发实现小程序模版消息的推送功能。 老规矩,先看效果图 [图片] 下面来讲实现步骤 一,定义推送的云函数 由于我们的云推送功能只能在云函数里调用,所以我们这里必须要在云函数里实现推送功能。 1,首先我们定义一个云函数push0524。 如果你还不知道如何使用云开发,如何定义云函数,去翻下老师之前的文章。有写的。 [图片] 把完整的代码贴给大家 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async(event, context) => { console.log(event) return sendTemplateMessage(event) } //小程序模版消息推送 async function sendTemplateMessage(event) { const { OPENID } = cloud.getWXContext() // 接下来将新增模板、发送模板消息、然后删除模板 // 注意:新增模板然后再删除并不是建议的做法,此处只是为了演示,模板 ID 应在添加后保存起来后续使用 const addResult = await cloud.openapi.templateMessage.addTemplate({ id: 'AT0002', keywordIdList: [3, 4, 5] }) const templateId = addResult.templateId //新增的模版id const sendResult = await cloud.openapi.templateMessage.send({ touser: OPENID, templateId, formId: event.formId, page: 'pages/index/index', data: { keyword1: { value: '云开发实现推送', }, keyword2: { value: '2019 年 5 月 24 日', }, keyword3: { value: '编程小石头', }, } }) //删除模版id await cloud.openapi.templateMessage.deleteTemplate({ templateId, }) return sendResult } [代码] 上面代码所实现的就是 1,创建模版,拿到模版id 2,使用模版ID,填充模版消息,发送模版 3,删除模版。 我们正常开发时,模版都是在小程序后台获取到的。这里是为例演示方便。所以正常开发时,只需要实现第二步就行了。 推送的关键代码就是这个方法: cloud.openapi.templateMessage.send 通常我们定义完push0524云函数以后,如果直接调用的话,会报错误的。 [图片] 来看下这个错误,看到红色框里的permission就知道,肯定是权限的问题。所以我们在定义完云函数以后,要在push0524云函数下面添加权限配置页面。如下图 [图片] 重要的就是这个: “templateMessage.send”, 推送权限。因为推送是云开发给我们提供的,我们这里调用时,必须配置相关权限,才能使用的。 到这里我们的推送功能就实现了。下面我们来验证下。 二,验证云开发推送 验证其实很简单,和我们之前的《5行代码实现微信小程序模版消息推送 (含推送后台和小程序源码)》 类似。只不过一个是在java后台推送,一个是在小城里推送。下面我们简单写个小程序里验证推送的demo。 功能很简单 1,获取formid,因为推送必须有formid的 2,点击调用push0524实现推送 [图片] 简单的贴下代码 [图片] [图片] 需要注意的一点:我们测试时,必须要真机测试。因为模拟器没法获取到formid的。 [图片] 我们在推送成功的success回调中打印下log。如果log中出现,send:ok字样,就代表我们推送成功了。来看下推送成功的效果。 微信聊天列表接收到了消息提醒 [图片] 消息内容 [图片] 到这里我们就用云开发实现完整的消息推送功能了。是不是很简单。 有任何关于编程的问题都可以加老师微信 2501902696(备注小程序)也可以找老师索要完整源码。 编程小石头码农一枚,非著名全栈开发人员。分享自己的一些经验,学习心得,希望后来人少走弯路,少填坑 视频讲解地址:https://edu.csdn.net/course/detail/24770
2019-06-11 - 小程序代码审核周期,如何加快小程序审核速度
我们开发小程序的时候,在提交代码审核时有时会等很久,有时会审核的很快,那么小程序的代码审核到底要多久呢?我们该如何加快自己小程序的被审核速度。下面程序猫给大家分享一下。 首先小程序的代码审核目前快的话几分钟就完成了,慢的话2-3天也有可能,如果超过3天,那说明你的小程序有歧义,审核员无法直接判断,尤其要注意审核期间不要撤销,一旦撤销重提,那就要重新排队审核。很多朋友小程序1天没有审就急得要死,这个完全没有必要,因为官方给出的审核时间是1-7天,如果没有超过7天,你再怎么发帖官方都是不会加急的。所以还是和客户说清楚吧,让客户耐心等待。如果是接触小程序很早的同学应该知道,过去审核都是5-7天,现在的审核速度真的算很快了。 另外有些类目是需要二次审核的,比如社交类目,视频类目。由于二次审核是政府主管机构审核,政府很忙的,所以你懂得,审核时间一般都要超过7天。还好微信考虑到实际情况,提出了超过7天审核可以预上线的功能。这里要注意下这个7天是从开始二审开始计算,而不是你提交审核开始计算。 目前能加速程序审核的只有通过小程序评测功能了 我简单介绍一下微信小程序的分项评定主要由运营指标,性能指标,用户指标这三项指标构成的,我们主要看一下这三项指标怎么提高。 运营指标: 运营指标的判定结果和其他两项略有不同的是,这个判定结果只有两种:达标和不达标。运营指标达标比较简单,三个月内没有违规即为达标,但是如果违规一次那么同样意味这至少三个月不达标所以这一点还是要慎重。 性能指标: 性能指标主要和小程序的开发水平有关,主要分为优秀,良好,一般,待优化,急待优化五种。性能指标一般需要达到的是优秀,关于如何达到优秀这个标准我们则可以根据评测的结果进行针对性的优化。 用户指标: 用户指标主要和小程序的用户挂钩,换句话也就是和运营有关,同样分为优秀,良好,一般,待优化,急待优化五种。关于如何提高用户指标这一项,我们可以主要针对两点,用户成长和用户的粘性下功夫即可。
2019-08-26 - 小程序弯低轮播图制作
[图片] wxml [代码]<swiper class='banner' indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}"> <block wx:for="{{imgUrls}}" wx:key=""> <swiper-item> <image src="{{item}}" class="slide-image" /> </swiper-item> </block> </swiper> <view class='wanbox'> <image class='wan' src='http://wechatpx.oss-cn-beijing.aliyuncs.com/qixing/ceng.png'></image> </view> [代码] wxss [代码].slide-image { width: 750rpx; height: 345rpx; } .banner { width: 750rpx; height: 345rpx; } .wanbox{position: relative;z-index: 99} .wan { display: block; width: 750rpx; height: 35rpx; margin-top: -34rpx; } [代码]
2019-07-31 - 小程序性能和体验优化方法
[图片] 小程序应避免出现任何 JavaScript 异常 出现 JavaScript 异常可能导致小程序的交互无法进行下去,我们应当追求零异常,保证小程序的高鲁棒性和高可用性 小程序所有请求应响应正常 请求失败可能导致小程序的交互无法进行下去,应当保证所有请求都能成功 所有请求的耗时不应太久 请求的耗时太长会让用户一直等待甚至离开,应当优化好服务器处理时间、减小回包大小,让请求快速响应 避免短时间内发起太多的图片请求 短时间内发起太多图片请求会触发浏览器并行加载的限制,可能导致图片加载慢,用户一直处理等待。应该合理控制数量,可考虑使用雪碧图技术或在屏幕外的图片使用懒加载 避免短时间内发起太多的请求 短时间内发起太多请求会触发小程序并行请求数量的限制,同时太多请求也可能导致加载慢等问题,应合理控制请求数量,甚至做请求的合并等 避免 setData 的数据过大 setData工作原理 小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。 而 evaluateJavascript 的执行会受很多方面的影响,数据到达视图层并不是实时的。 由于小程序运行逻辑线程与渲染线程之上,setData的调用会把数据从逻辑层传到渲染层,数据太大会增加通信时间 常见的 setData 操作错误 频繁的去 setData Android 下用户在滑动时会感觉到卡顿,操作反馈延迟严重,因为 JS 线程一直在编译执行渲染,未能及时将用户操作事件传递到逻辑层,逻辑层亦无法及时将操作处理结果及时传递到视图层 染有出现延时,由于 WebView 的 JS 线程一直处于忙碌状态,逻辑层到页面层的通信耗时上升,视图层收到的数据消息时距离发出时间已经过去了几百毫秒,渲染的结果并不实时 每次 setData 都传递大量新数据 由setData的底层实现可知,数据传输实际是一次 evaluateJavascript 脚本过程,当数据量过大时会增加脚本的编译执行时间,占用 WebView JS 线程 后台态页面进行 setData 当页面进入后台态(用户不可见),不应该继续去进行setData,后台态页面的渲染用户是无法感受的,另外后台态页面去setData也会抢占前台页面的执行 避免 setData 的调用过于频繁 setData接口的调用涉及逻辑层与渲染层间的线程通过,通信过于频繁可能导致处理队列阻塞,界面渲染不及时而导致卡顿,应避免无用的频繁调用 避免将未绑定在 WXML 的变量传入 setData setData操作会引起框架处理一些渲染界面相关的工作,一个未绑定的变量意味着与界面渲染无关,传入setData会造成不必要的性能消耗 合理设置可点击元素的响应区域大小 我们应该合理地设置好可点击元素的响应区域大小,如果过小会导致用户很难点中,体验很差 避免渲染界面的耗时过长 渲染界面的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要校验下是否同时渲染的区域太大 避免执行脚本的耗时过长 执行脚本的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要确认并优化脚本的逻辑 对网络请求做必要的缓存以避免多余的请求 发起网络请求总会让用户等待,可能造成不好的体验,应尽量避免多余的请求,比如对同样的请求进行缓存 wxss 覆盖率较高,较少或没有引入未被使用的样式 按需引入 wxss 资源,如果小程序中存在大量未使用的样式,会增加小程序包体积大小,从而在一定程度上影响加载速度 文字颜色与背景色搭配较好,适宜的颜色对比度更方便用户阅读 文字颜色与背景色需要搭配得当,适宜的颜色对比度可以让用户更好地阅读,提升小程序的用户体验 所有资源请求都建议使用 HTTPS 使用 HTTPS,可以让你的小程序更加安全,而 HTTP 是明文传输的,存在可能被篡改内容的风险 不使用废弃接口 使用即将废弃或已废弃接口,可能导致小程序运行不正常。一般而言,接口不会立即去掉,但保险起见,建议不要使用,避免后续小程序突然运行异常 避免过大的 WXML 节点数目 建议一个页面使用少于 1000 个 WXML 节点,节点树深度少于 30 层,子节点数不大于 60 个。一个太大的 WXML 节点树会增加内存的使用,样式重排时间也会更长 避免将不可能被访问到的页面打包在小程序包里 小程序的包大小会影响加载时间,应该尽量控制包体积大小,避免将不会被使用的文件打包进去 及时回收定时器 定时器是全局的,并不是跟页面绑定的,当页面因后退被销毁时,定时器应注意手动回收 避免使用 css ‘:active’ 伪类来实现点击态 使用 css ‘:active’ 伪类来实现点击态,很容易触发,并且滚动或滑动时点击态不会消失,体验较差 建议使用小程序内置组件的 ‘hover-*’ 属性来实现 滚动区域可开启惯性滚动以增强体验 惯性滚动会使滚动比较顺畅,在安卓下默认有惯性滚动,而在 iOS 下需要额外设置 [代码]-webkit-overflow-scrolling: touch[代码] 的样式
2019-03-15 - 拥抱更底层技术——从CSS变量到Houdini
0. 前言 平时写CSS,感觉有很多多余的代码或者不好实现的方法,于是有了预处理器的解决方案,主旨是write less &do more。其实原生css中,用上css变量也不差,加上bem命名规则只要嵌套不深也能和less、sass的嵌套媲美。在一些动画或者炫酷的特效中,不用js的话可能是用了css动画、svg的animation、过渡,复杂动画实现用了js的话可能用了canvas、直接修改style属性。用js的,然后有没有想过一个问题:“要是canvas那套放在dom上就爽了”。因为复杂的动画频繁操作了dom,违背了倒背如流的“性能优化之一:尽量少操作dom”的规矩,嘴上说着不要,手倒是很诚实地[代码]ele.style.prop = <newProp>[代码],可是要实现效果这又是无可奈何或者大大减小工作量的方法。 我们都知道,浏览器渲染的流程:解析html和css(parse),样式计算(style calculate),布局(layout),绘制(paint),合并(composite),修改了样式,改的环节越深代价越大。js改变样式,首先是操作dom,整个渲染流程马上重新走,可能走到样式计算到合并环节之间,代价大,性能差。然后痛点就来了,浏览器有没有能直接操作前面这些环节的方法呢而不是依靠js?有没有方法不用js操作dom改变style或者切换class来改变样式呢? 于是就有CSS Houdini了,它是W3C和那几个顶级公司的工程师组成的小组,让开发者可以通过新api操作CSS引擎,带来更多的自由度,让整个渲染流程都可以被开发者控制。上面的问题,不用js就可以实现曾经需要js的效果,而且只在渲染过程中,就已经按照开发者的代码渲染出结果,而不是渲染完成了再重新用js强行走一遍流程。 关于houdini最近动态可点击这里 上次CSS大会知道了有Houdini的存在,那时候只有cssom,layout和paint api。前几天突然发现,Animation api也有了,不得不说,以后很可能是Houdini遍地开花的时代,现在得进一步了解一下了。一句话:这是css in js到js in css的转变 1. CSS变量 如果你用less、sass只为了人家有变量和嵌套,那用原生css也是差不多的,因为原生css也有变量: 比如定义一个全局变量–color(css变量双横线开头) [代码]:root { --color: #f00; } [代码] 使用的时候只要var一下 [代码].f{ color: var(--color); } [代码] 我们的html: [代码]<div class="f">123</div> [代码] 于是,红色的123就出来了。 css变量还和js变量一样,有作用域的: [代码]:root { --color: #f00; } .f { --color: #aaa } .g{ color: var(--color); } .ft { color: var(--color); } [代码] html: [代码] <div className="f"> <div className="ft">123</div> </div> <div className=""> <div className="g">123</div> </div> [代码] 于是,是什么效果你应该也很容易就猜出来了: [图片] css能搞变量的话,我们就可以做到修改一处牵动多处的变动。比如我们做一个像准星一样的四个方向用准线锁定鼠标位置的效果: [图片] 用css变量的话,比传统一个个元素设置style优雅多了: [代码]<div id="shadow"> <div class="x"></div> <div class="y"></div> <div class="x_"></div> <div class="y_"></div> </div> [代码] [代码] :root{ --x: 0px; --y: 0px; } body{ margin: 0 } #shadow{ width: 50%; height: 600px; border: #000 1px solid; position: relative; margin: 0; } .x, .y, .x_, .y_ { position: absolute; border: #f00 2px solid; } .x { top: 0; left: var(--x); height: 20px; width: 0; } .y { top: var(--y); left: 0; height: 0; width: 20px; } .x_ { top: 600px; left: var(--x); height: 20px; width: 0; } .y_ { top: var(--y); left: 100%; height: 0; width: 20px; } [代码] [代码]const style = document.documentElement.style shadow.addEventListener('mousemove', e => { style.setProperty(`--x`, e.clientX + 'px') style.setProperty(`--y`, e.clientY + 'px') }) [代码] 那么,对于github的404页面这种内容和鼠标位置有关的页面,思路是不是一下子就出来了 2. CSS type OM 都有DOM了,那CSSOM也理所当然存在。我们平时改变css的时候,通常是直接修改style或者切换类,实际上就是操作DOM来间接操作CSSOM,而type om是一种把css的属性和值存在attributeStyleMap对象中,我们只要直接操作这个对象就可以做到之前的js改变css的操作。另外一个很重要的点,attributeStyleMap存的是css的数值而不是字符串,而且支持各种算数以及单位换算,比起操作字符串,性能明显更优。 接下来,基本脱离不了window下的CSS这个属性。在使用的时候,首先,我们可以采取渐进式的做法: [代码]if('CSS' in window){...}[代码] 2.1 单位 [代码]CSS.px(1); // 1px 返回的结果是:CSSUnitValue {value: 1, unit: "px"} CSS.number(0); // 0 比如top:0,也经常用到 CSS.rem(2); //2rem new CSSUnitValue(2, 'percent'); // 还可以用构造函数,这里的结果就是2% // 其他单位同理 [代码] 2.2 数学运算 自己在控制台输入CSSMath,可以看见的提示,就是数学运算 [代码]new CSSMathSum(CSS.rem(10), CSS.px(-1)) // calc(10rem - 1px),要用new不然报错 new CSSMathMax(CSS.px(1),CSS.px(2)) // 顾名思义,就是较大值,单位不同也可以进行比较 [代码] 2.3 怎么用 既然是新的东西,那就有它的使用规则。 获取值[代码]element.attributeStyleMap.get(attributeName)[代码],返回一个CSSUnitValue对象 设置值[代码]element.attributeStyleMap.set(attributeName, newValue)[代码],设置值,传入的值可以是css值字符串或者是CSSUnitValue对象 当然,第一次get是返回null的,因为你都没有set过。“那我还是要用一下getComputedStyle再set咯,这还不是和之前的差不多吗?” 实际上,有一个类似的方法:[代码]element.computedStyleMap[代码],返回的是CSSUnitValue对象,这就ok了。我们拿前面的第一部分CSS变量的代码测试一波 [代码]document.querySelector('.x').computedStyleMap().get('height') // CSSUnitValue {value: 20, unit: "px"} document.querySelector('.x').computedStyleMap().set('height', CSS.px(0)) // 不见了 [代码] 3. paint API paint、animation、layout API都是以worker的形式工作,具体有几个步骤: 建立一个worker.js,比如我们想用paint API,先在这个js文件注册这个模块registerPaint(‘mypaint’, class),这个class是一个类下面具体讲到 在html引入CSS.paintWorklet.addModule(‘worker.js’) 在css中使用,background: paint(mypaint) 主要的逻辑,全都写在传入registerPaint的class里面。paint API很像canvas那套,实际上可以当作自己画一个img。既然是img,那么在所有的能用到图片url的地方都适合用paint API,比如我们来自己画一个有点炫酷的背景(满屏随机颜色方块)。有空的话可以想一下js怎么做,再对比一下paint API的方案。 [图片] [代码]// worker.js class RandomColorPainter { // 可以获取的css属性,先写在这里 // 我这里定义宽高和间隔,从css获取 static get inputProperties() { return ['--w', '--h', '--spacing']; } /** * 绘制函数paint,最主要部分 * @param {PaintRenderingContext2D} ctx 类似canvas的ctx * @param {PaintSize} PaintSize 绘制范围大小(px) { width, height } * @param {StylePropertyMapReadOnly} props 前面inputProperties列举的属性,用get获取 */ paint(ctx, PaintSize, props) { const w = (props.get('--w') && +props.get('--w')[0].trim()) || 30; const h = (props.get('--h') && +props.get('--h')[0].trim()) || 30; const spacing = +props.get('--spacing')[0].trim() || 10; for (let x = 0; x < PaintSize.width / w; x++) { for (let y = 0; y < PaintSize.height / h; y++) { ctx.fillStyle = `#${Math.random().toString(16).slice(2, 8)}` ctx.beginPath(); ctx.rect(x * (w + spacing), y * (h + spacing), w, h); ctx.fill(); } } } } registerPaint('randomcolor', RandomColorPainter); [代码] 接着我们需要引入该worker: [代码]CSS && CSS.paintWorklet.addModule('worker.js');[代码] 最后我们在一个class为paint的div应用样式: [代码].paint{ background-image: paint(randomcolor); width: 100%; height: 600px; color: #000; --w: 50; --h: 50; --spacing: 10; } [代码] 再想想用js+div,是不是要先动态生成n个,然后各种计算各种操作dom,想想就可怕。如果是canvas,这可是canvas背景,你又要在上面放一个div,而且还要定位一波。 注意: worker是没有window的,所以想搞动画的就不能内部消化了。不过可以靠外面的css变量,我们用js操作css变量可以解决,也比传统的方法优雅 4. 自定义属性 支持情况 点击这里查看 首先,看一下支持度,目前浏览器并没有完全稳定使用,所以需要跟着它的提示:[代码]Experimental Web Platform features” on chrome://flags[代码],在chrome地址栏输入[代码]chrome://flags[代码]再找到[代码]Experimental Web Platform features[代码]并开启。 [代码]CSS.registerProperty({ name: '--myprop', //属性名字 syntax: '<length>', // 什么类型的单位,这里是长度 initialValue: '1px', // 默认值 inherits: true // 会不会继承,true为继承父元素 }); [代码] 说到继承,我们回到前面的css变量,已经说了变量是区分作用域的,其实父作用域定义变量,子元素使用该变量实际上是继承的作用。如果[代码]inherits: true[代码]那就是我们看见的作用域的效果,如果不是true则不会被父作用域影响,而且取默认值。 这个自定义属性,精辟在于,可以用永久循环的animation驱动一次性的transform。换句话说,我们如果用了css变量+transform,可以靠js改变这个变量达到花俏的效果。但是,现在不需要js,只要css内部消化,transform成为永动机。 [代码]// 我们先注册几种属性 ['x1','y1','z1','x2','y2','z2'].forEach(p => { CSS.registerProperty({ name: `--${p}`, syntax: '<angle>', inherits: false, initialValue: '0deg' }); }); [代码] 然后写个样式 [代码]#myprop, #myprop1 { width: 200px; border: 2px dashed #000; border-bottom: 10px solid #000; animation:myprop 3000ms alternate infinite ease-in-out; transform: rotateX(var(--x2)) rotateY(var(--y2)) rotateZ(var(--z2)) } [代码] 再来看看我们的动画,为了眼花缭乱,加了第二个改了一点数据的动画 [代码]@keyframes myprop { 25% { --x1: 20deg; --y1: 30deg; --z1: 40deg; } 50% { --x1: -20deg; --z1: -40deg; --y1: -30deg; } 75% { --x2: -200deg; --y2: 130deg; --z2: -350deg; } 100% { --x1: -200deg; --y1: 130deg; --z1: -350deg; } } @keyframes myprop1 { 25% { --x1: 20deg; --y1: 30deg; --z1: 40deg; } 50% { --x2: -200deg; --y2: 130deg; --z2: -350deg; } 75% { --x1: -20deg; --z1: -40deg; --y1: -30deg; } 100% { --x1: -200deg; --y1: 130deg; --z1: -350deg; } } [代码] html就两个div: [代码] <div id="myprop"></div> <div id="myprop1"></div> [代码] 效果是什么呢,自己可以跑一遍看看,反正功能很强大,但是想象力限制了发挥。 自己动手改的时候注意一下,动画关键帧里面,不能只存在1,那样子就不能驱动transform了,做不到永动机的效果,因为我的rotate写的是 rotateX(var(–x2))。接下来随意发挥吧 最后 再啰嗦一次 关于houdini最近动态可点击这里 关于houdini在浏览器的支持情况 ENJOY YOURSELF!!!
2019-03-25 - 在onPageScroll监听,到达某个位置置顶,ios真机测试延迟?
onPageScroll监听页面,滑动到某位置置顶元素,但是ios真机测试时滑的很快,会延迟好几秒才置顶?可否改善?有替代方法吗? 以下是代码片段: https://developers.weixin.qq.com/s/u1xqBCmZ7qaQ
2019-08-14 - 通过unionId来判断用户是否有关注公众号问题
一波三折,我就简单的实现小程序判断用户是否有关注关联组件中的公众号。通过了解,想通过unionId来判断用户是否关注公众号,但是现在问题又来了。我测试的时候,关注了公众号能获取到unionId,然后取消关注后,还能获取到unionId,现在我想弄明白是用户关注了公众号后才会有unionId,还是只要关注过,不管现在是够已经关注,进入小程序都能获取到unionId;听说是缓存,但是我清楚缓存还是有unionId;让我怎么玩?
2018-11-15 - 一次性订阅消息 提示跳转失败
如题,有哪位大神给解答一下 SubscribeMessage.Req req = new SubscribeMessage.Req(); req.scene = SUBSCRIBE_MESSAGE_SCENE; req.templateID = SUBSCRIBE_MESSAGE_TEMPLATEID; try { req.reserved = URLEncoder.encode(hnUserID,"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } api.sendReq(req); [图片]
2019-07-15 - 小程序页面吸顶效果、右下角悬浮按钮等隐藏显示切换时不卡顿的实现方法
使用的api及页面方法 api:wx.createSelectorQuery、wx.createIntersectionObserver 页面方法:onPageScroll 为什么使用以上方法? wx.createSelectorQuery:主要解决页面渲染后保证所涉及的元素能百分百渲染到屏幕上,这里打包一个异步方法。 [代码]getElement(elm, component) { const _this = this; return new Promise((resolve, reject) => { let ss = setInterval(function() { let Qy = component ? _this.createSelectorQuery() : wx.createSelectorQuery(); let a = Qy.select(elm).boundingClientRect(function(res) { if (res) { clearInterval(ss) resolve(res) } }).exec(); }, 50); }); } [代码] wx.createIntersectionObserver与onPageScroll的作用: 单纯使用onPageScroll切换隐藏显示状态必然会高频率使用setData导致页面卡顿。如果只是在wx.createIntersectionObserver与onPageScroll中隐藏或者显示,即确保每个方法中只setData一次,那么卡顿的现象就不会出现。 以下wx.createIntersectionObserver仅作显示元素 [代码]onCreateIntersectionObserver(component,elm) { const _this = this; this.getElement(elm||".tr-fixed", component).then(res => { _this.setData({ fixed_top: res.top //记录一直显示的元素的top值 }); _this.IntersectionObserver = component ? _this.createIntersectionObserver() : wx.createIntersectionObserver() _this.IntersectionObserver.relativeTo(".top-transparent", { bottom: 2 }).observe(elm||'.tr-fixed', (res) => { //显示吸顶 const { fixed_show } = _this.data; if (fixed_show === false) { _this.setData({ fixed_show: true }); } //显示吸顶 }) }); } [代码] 上面代码中: .top-transparent是自定义参照区域。 .tr-fixed或elm为切换隐藏与显示的元素(事先写好顶部浮动,隐藏起来,这里并没有css仅作为监听对象) wxml基本代码: [代码]<view class="top-transparent">页面顶部透明参照元素</view> <view class="tr-fixed">一直显示的部分(滚动的scrollTop小于此元素的top值则隐藏,如果监测到与透明的参照元素交叉则显示)</view> <view class="fixed-view" wx:if="{{fixed_show}}">隐藏的部分(与一直显示的部分一模一样)</view> [代码] [代码].top-transparent{ position: fixed; top: 0; left: 0; right: 0; height: 20px; background: transparent;//透明 pointer-events: none; //保证此元素所有点击事件无效,即点击事件都穿透到比它层级低的元素上 } [代码] 以下onPageScroll仅作隐藏元素 [代码]onPageScroll(e) { const { fixed_top, fixed_show } = this.data // 隐藏吸顶头部 if (fixed_top != undefined && fixed_show) { if (e.scrollTop <= fixed_top) { this.setData({ fixed_show: false }) } } // 隐藏吸顶头部 }, [代码] 代码片段: https://developers.weixin.qq.com/s/oUhsfCmP76au
2019-08-14 - 微信服务号带参数二维码(永久)数量不够用
我们的产品是智能硬件,每台设备拥有1个设备ID; 每个ID作为1个参数生成1个永久链接二维码,贴在每个设备上; 用户购买后只需要扫描二维码即可关注服务号,享受服务,开发者也能获取到用户购买的设备ID,进行绑定,以便提供后续服务 但是由于我们已经生成的将要接近10W的上限,也想过其他方案,但是体验均不如现在,所以想跟官方人员交流下,这个问题怎么最佳解决
2019-02-19 - 关于激励视频广告的统计
自己在后台记录了用户看完视频后返回用户相应的奖励,比如一天下来自己的后台记录是50个用户获得奖励,去除重复用户后是45,但是流量主收入的信息显示的点击数却是8,这个点击数难道不应该是45?
2019-04-24 - Swiper中display-multiple-items的妙用
产品需要显示一页加下一页的部分,但是又不需要轮播,如图[图片] 一开始想的是用next-margin,这样能实现功能,但是当翻到最后一页的时候,最右边还会显示next-margin这么多的空白,而并不是靠右对齐,这样看起来很奇怪,缺了一块 [图片] 后来发现原来设置display-multiple-items也是可以实现这个功能的,设置成1.x就可以了,这样最后一页也没有留白。本来以为display-multiple-items是需要设置成整数的,其实不是,目测大于0都是可以的 [图片]
2019-08-07 - 如何解决\n换不了行的问题?
[图片] 如何解决\n换不了行的问题?
2019-08-07 - 小程序页面通信、数据刷新、事件总线 、event bus 终极解决方案之 iny-bus
#### 背景介绍 在各种小程序中,我们经常会遇到 这种情况 有一个 列表,点击列表中的一项进入详情,详情有个按钮,删除了这一项,这个时候当用户返回到列表页时, 发现列表中的这一项依然存在,这种情况,就是一个 `bug`,也就是数据不同步问题,这个时候测试小姐姐 肯定会找你,让你解决,这个时候,你也许会很快速的解决,但过一会儿,测试小姐姐又来找你说,我打开了 四五个页面更改了用户状态,但我一层一层返回到首页,发现有好几个页面数据没有刷新,也是一个 bug, 这个时候你就犯愁了,怎么解决,常规方法有下面几种 #### 解决方法 1. 将所有请求放到 生命周期 `onShow` 中,只要我们页面重新显示,就会重新请求,数据也会刷新 2. 通过用 `getCurrentPages` 获取页面栈,然后找到对应的 页面实例,调用实例方法,去刷新数据 3. 通过设置一个全局变量,例如 App.globalData.xxx,通过改变这个变量的值,然后在对应 onShow 中检查,如果值已改变,刷新数据 4. 在打开详情页时,使用 redirectTo 而不是 navigateTo,这样在打开新的页面时,会销毁当前页面, 返回时就不会回到这个里面,自然也不会有数据不同步问题 #### 存在的问题 1. 假如我们将 所有 请求放到 onShow 生命周期中,自然能解决所有数据刷新问题,但是 onShow 这个生命周期,有两个问题 第一个问题,它其实是在 onLoad 后面执行的,也就是说,假如请求耗时相同,从它发起请求到页面渲染, 会比 onLoad 慢 第二个问题,那就是页面隐藏、调用微信分享、锁频等等都会触发执行,请求放置于 `onShow` 中就会造成 大量不需要的请求,造成服务器压力,多余的资源浪费、也会造成用户体验不好的问题 2. 通过 `getCurrentPages` 获取页面栈,然后找到对应的 页面实例,调用实例方法,去刷新数据,这也 不失为一个办法,但是就如微信官方文档所说 > 不要尝试修改页面栈,会导致路由以及页面状态错误。 > 不要在 App.onLaunch 的时候调用 `getCurrentPages()`,此时 page 还没有生成。 同时、当需要通信的页面有两个、三个、多个呢,这里去使用 `getCurrentPages` 就会比较困难、繁琐 3. 通过设置全局变量的方法,当需要使用的地方比较少时,可以接受,当使用的地方多的时候,维护起来 就会很困难,代码过于臃肿,也会有很多问题 4. 使用 redirectTo 而不是 navigateTo,从用来体验来说,很糟糕,并且只存在一个页面,对于 tab 页面,它也无能为力,不推荐使用 #### 最佳实践 在 Vue 中, 可以通过 new Vue() 来实现一个 event bus作为事件总线,来达到事件通知的功能,在各大 框架中,也有自身的事件机制实现,那么我们完全可以通过同样的方法,实现一个事件中心,来管理我们的事件, 同时,解决我们的问题。iny-bus 就是这样一个及其轻量的事件库,使用 typescript 编写,100% 测试覆 盖率,能运行 js 的环境,就能使用 传送门 [源码](https://github.com/landluck/iny-bus) [NPM](https://www.npmjs.com/package/iny-bus) [文档](https://landluck.github.io/iny-bus/docs/) #### 简单使用 iny-bus 使用及其简单,在需要的页面 onLoad 中添加事件监听, 在需要触发事件的地方派发事件,使监 听该事件的每个页面执行处理函数,达到通信和刷新数据的目的,在小程序中的使用可以参考以下代码 [代码] // 小程序[代码] [代码] import bus from [代码][代码]'iny-bus'[代码] [代码] // 添加事件监听[代码] [代码] // 在 onLoad 中注册, 避免在 onShow 中使用[代码] [代码] onLoad () {[代码] [代码] this[代码][代码].eventId = bus.on([代码][代码]'事件名'[代码][代码], (a, b, c, d) => {[代码] [代码] // 支持多参数[代码] [代码] console.log(a, b, c, d)[代码] [代码] this[代码][代码].setData({ a [代码]}) [代码] // 调用页面请求函数,刷新数据[代码] [代码] this[代码][代码].refreshPageData()[代码] [代码] })[代码] [代码] // 添加只需要执行一次的 事件监听[代码] [代码] this[代码][代码].eventIdOnce = bus.once([代码][代码]'事件名'[代码][代码], () => {[代码] [代码] // do some thing[代码] [代码] })[代码] [代码] }[代码] [代码] // 移除事件监听,该函数有两个参数,第二个事件id不传,会移除整个事件监听,传入ID,会移除该[代码] [代码] // 页面的事件监听,避免多余资源浪费, 在添加事件监[代码][代码]/// 听后,页面卸载(onUnload)时建议移除[代码] [代码] onUnload () {[代码] [代码] bus.remove([代码][代码]'事件名'[代码][代码], [代码][代码]this[代码][代码].eventId)[代码] [代码] }[代码] [代码] // 派发事件,触发事件监听处更新视图[代码] [代码] // 支持多参传递[代码] [代码] onClick () {[代码] [代码] bus.emit([代码][代码]'事件名'[代码][代码], a, b, c)[代码] [代码] }[代码] 更详细的使用和例子可以参考 [Github iny-bus 小程序代码](https://github.com/landluck/iny-bus/tree/master/examples) #### iny-bus 具体实现 * 基本打包工具,这里使用非常优秀的开源库 [typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter),具体细节不展开 * 测试工具 使用 facebook 的 [jest](https://github.com/facebook/jest) * build ci 使用 [travis-ci](https://www.travis-ci.org/) * 测试覆盖率上传使用 [codecov](https://codecov.io/) * 具体的其他细节大家可以看源码中的 [package.json](https://github.com/landluck/iny-bus/blob/master/package.json),这里就一一展开讲了 iny-bus 的核心代码,其实就这么多,总的来说,非常少,但是能解决我们在小程序中遇到的大量 通信 和 数据刷新问题,是采用 各大平台小程序 原生开发时,页面通信的不二之选,同时,100% 的测试覆盖率,确保了 iny-bus 在使用中的稳定性和安全性,当然,每个库都是从简单走向复杂,功能慢慢完善,如果 大家在使用或者源码中发现了bug或者可以优化的点,欢迎大家提 pr 或者直接联系我 最后,如果 iny-bus 给你提供了帮助或者让你有任何收获,请给 作者 点个赞,感谢大家 [点赞](https://github.com/landluck/iny-bus)
2019-08-04 - 如何监听小程序中的手势事件(缩放、双击、长按、滑动、拖拽)
mina-touch [图片] [代码]mina-touch[代码],一个方便、轻量的 小程序 手势事件监听库 事件库部分逻辑参考[代码]alloyFinger[代码],在此做出声明和感谢 change log: 2019.03.10 优化监听和绘制逻辑,动画不卡顿 2019.03.12 修复第二次之后缩放闪烁的 bug,pinch 添加 singleZoom 参数 2020.12.13 更名 mina-touch 2020.12.27 上传 npm 库;优化使用方式;优化 README 支持的事件 支持 pinch 缩放 支持 rotate 旋转 支持 pressMove 拖拽 支持 doubleTap 双击 支持 swipe 滑动 支持 longTap 长按 支持 tap 按 支持 singleTap 单击 扫码体验 [图片] demo 展示 demo1:监听 pressMove 拖拽 手势 查看 demo 代码 [图片] [图片] demo2: 监听 pinch 缩放 和 rotate 旋转 手势 (已优化动画卡顿 bug) 查看 demo 代码 [图片] [图片] demo3: 测试监听双击事件 查看 demo 代码 [图片] [图片] demo4: 测试监听长按事件 查看 demo 代码 [图片] [图片] demo 代码 demo 代码地址 mina-tools-client/mina-touch 使用方法 大致可以分为 4 步: npm 安装 mina-touch,开发工具构建 npm 引入 mina-touch onload 实例化 mina-touch wxml 绑定实例 命令行 [代码]npm install mina-touch[代码] 安装完成后,开发工具构建 npm *.js [代码]import MinaTouch from 'mina-touch'; // 1. 引入mina-touch Page({ onLoad: function (options) { // 2. onload实例化mina-touch //会创建this.touch1指向实例对象 new MinaTouch(this, 'touch1', { // 监听事件的回调:multipointStart,doubleTap,longTap,pinch,pressMove,swipe等等 // 具体使用和参数请查看github-README(底部有github地址 }); }, }); [代码] NOTE: 多类型事件监听触发 setData 时,建议把数据合并,在 touchMove 中一起进行 setData ,以减少短时内多次 setData 引起的动画延迟和卡顿(参考 demo2) *.wxml 在 view 上绑定事件并对应: [代码]<view catchtouchstart="touch1.start" catchtouchmove="touch1.move" catchtouchend="touch1.end" catchtouchcancel="touch1.cancel" > </view> <!-- touchstart -> 实例对象名.start touchmove -> 实例对象名.move touchend -> 实例对象名.end touchcancel -> 实例对象名.cancel --> [代码] NOTE: 如果不影响业务,建议使用 catch 捕获事件,否则易造成监听动画卡顿(参考 demo2) 以上简单几步即可使用 mina-touch 手势库 😊😊😊 具体使用和参数请查看Github https://github.com/Yrobot/mina-touch 如果喜欢mina-touch的话,记得在github点个start哦!🌟🌟🌟
2021-06-24 - 审核举报的时候,能不能认真点!!!
微信官方审核举报的时候能不能认真点呢???? 不管三七二十就把模板消息给封了,"抽奖结果通知"这个模板大家都在用,我们在开奖的时候给参与用户推送一个模板消息。这种推送形式,几乎所有的抽奖小程序都在用,为什么我们推送开奖结果通知就被封模板了呢??? 严重影响我们的业务!
2019-07-29 - 公众号文章中打开小程序,获取appid的问题
我们业务中有个需求,只有通过我们审核的第三方公众号,才能在公众号文章中嵌套我们的小程序,所以我们需要知道是从哪个公众号的文章中打开的小程序 公众号 profile 页相关小程序列表、公众号自定义菜单、公众号模板消息,这三个场景下是可以获取到appid的,建议官方加入"从公众号文章中打开小程序"可以获取到appid,如果不能支持该需求,请说明下原因。
2019-06-12 - 关于createIntersectionObserver的使用
- 当前 Bug 的表现:在实现懒加载的时候 没有使用scroll-view作为内容容器 在分页的时候使用了onReachBottom触底操作 当加载到最后一页时 往上滑动页面 触发了onReachBottom 测试得知 与使用了createIntersectionObserver有关 官方能否给出 createIntersectionObserver 和 onReachBottom 是否可以同时使用的解答 - 预期表现:可以在正常分页加载的同时 利用createIntersectionObserver实现图片懒加载 - 提供一个最简复现 Demo:代码截图如下 在onload中进行数据请求 并且进行图片懒加载判断显示(该页面使用了组件) [图片] 下面是在onreachbottom中的代码 进行了数据的concat 然后重新判断懒加载 结果页面在上滑的时候 会显示 加载到底了~ 这个toast 不知为何会触发 [图片]
2019-07-04 - 云开发·云调用生成小程序码
云开发·云调用生成小程序码 小程序云开发已经支持云调用,开放了很多接口,一直想要的获取小程序码也支持了。这下轻量的小程序也可以有自定义小程序码的功能。 1. 需求 获得一个带参数的小程序码,传播出去以后,用户扫码进入指定页面,根据参数做不同的处理。本文只讲小程序码生成、存储、展示部分。参数处理不多介绍,可以查看 项目代码 了解更多。 2. 开通云开发 新建小程序可以从开发工具的云开发模板初始化项目,根据云开发操作指引新建项目即可。 但是这里有个问题,已发布小程序的页面才能生成小程序码。如果现有的小程序没有开通云开发,需要做以下几步: 开发工具开通云开发,设定云开发的环境; 将原来的代码(除了[代码]project.config.json[代码]以外的所有文件)放到新建的 [代码]miniprogram[代码] 目录; 新增 [代码]cloudfunctions[代码] 目录; [代码]app.json[代码] 新增配置 [代码]"cloud": true[代码]; [代码]project.config.json[代码] 配置 [代码]"miniprogramRoot":"miniprogram/"[代码] 和 [代码]"cloudfunctionRoot":"cloudfunctions/"[代码]; 修改小程序基础库版本,最低要 2.3.0 [代码]"libVersion": "2.3.0"[代码]。 3. 生成小程序码 下面可以开始写代码开发了,开始之前,建议先看完官方教程。特别是开发工具的使用步骤,开发和调试时如果遇到奇怪的问题,可以尝试重启开发工具、重装开发工具,也可以去微信开放社区发帖。(重启和重装都是我在社区中发现的答案,能解决各种不应该存在的问题)。 3.1 准备文件 在 [代码]cloudfunctions[代码]目录右键新建Node.js云函数 [代码]getqr[代码]。 生成小程序码需要单独指定权限。在 [代码]getqr[代码] 目录新建 [代码]config.json[代码] ,里面写以下内容: [代码]{ "permissions": { "openapi": [ "wxacode.getUnlimited" ] } } [代码] 小程序码的获取方式有三种,这里只用到了接口 getUnlimited,选择这个接口的原因是漂亮的圆形小程序码,数量无限制。具体区别可以去 获取小程序码官方文档查看详情。 正常情况下,这个时候云函数可以部署测试了。如果遇到部署不成功、各种权限问题,可以尝试本地部署上传所有文件、重启试试。 3.2 生成小程序码 生成小程序码的代码如下,可以指定页面和页面参数 [代码]scene[代码],还有小程序码的尺寸。 注意这里的 [代码]scene[代码] 有限制: 最大32个可见字符; 只支持数字,大小写英文以及部分特殊字符:[代码]!#$&'()*+,/:;=?@-._~[代码]; 注意参数格式:下面实例代码生成小程序码后,扫码获得 [代码]pages/demo/demo?scene=id%3D6[代码] 。 [代码]try { const result = await cloud.openapi.wxacode.getUnlimited({ page: 'pages/demo/demo', scene: 'id=6', width: 240, }) console.log(result) return result } catch (err) { console.log(err) return err } [代码] 直接调用,比服务端调用少了 access_token 参数。 3.3 上传到云存储 返回值中的 buffer 就是图片内容,直接上传到云存储: [代码]const uploadResult = await cloud.uploadFile({ cloudPath: 'shareqr/' + qr_name_hash + '.jpg', fileContent: result.buffer, }); [代码] 我在云存储新建了 [代码]shareqr[代码] 目录保存小程序码; 图片名根据参数取md5摘要; [代码]getUnlimited[代码] 返回的图像是 [代码]jpeg[代码] 格式,后缀硬编码写 [代码].jpg[代码]。 3.4 获取图片临时路径 直接上代码 [代码]getURLReault = await cloud.getTempFileURL({ fileList: [uploadResult.fileID] }); fileObj = getURLReault.fileList[0] return fileObj [代码] 3.5 直接从存云存储获取 生成过以后图片已经保存在云存储,用同样的参数第二次调用没必要再生成一次,去掉一次网络请求,可以节省不少时间。 前面说到文件名使用请求参数摘要,知道了目录和文件名,再加上文件bucket前缀就可以拼出来 [代码]fileID[代码],用[代码]fileID[代码] 可以查询云存储的文件。 比如我刚刚生成的 fileID 是 [代码]cloud://dev-xxxx.8888-dev-xxxx/qr/44ea42f05091c3bec771123e6e8cd4c2.jpg[代码], 前缀就是 [代码]cloud://dev-xxxx.8888-dev-xxxx/[代码]。再拼上目录、文件名、后缀就是 [代码]fileID[代码]。 注:此处的 [代码]fileID[代码]拼接方法并不是来自官方文档,只是在使用中发现这个前缀不会变。还需要官方解释说明[代码]fileID[代码]规则。 如果会改变,就需要再用云数据库存储[代码]fileID[代码],更麻烦一些。 3.6 云函数完整代码 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk'); const crypto = require('crypto'); const bucketPrefix = 'cloud://dev-xxxx.8888-idc-4d11a4-1257831628/qr/'; // env: 'dev-xxxx' // 云函数入口函数 exports.main = async (event, context) => { const full_path = event.page + '?' + event.scene; const qr_name_hash = crypto.createHash('md5').update(full_path).digest('hex'); const temp_id = bucketPrefix + qr_name_hash + '.jpg'; // return { // full_path, // qr_name_hash, // temp_id // } try { // 先尝试获取文件,存在就直接返回临时路径 let getURLReault = await cloud.getTempFileURL({ fileList: [temp_id] }); // return getURLReault; let fileObj = getURLReault.fileList[0]; if (fileObj.tempFileURL != '') { fileObj.fromCache = true; return fileObj; } // 生成小程序码 const wxacodeResult = await cloud.openapi.wxacode.getUnlimited({ scene: event.scene, page: event.page, width: 280 //二维码的宽度,单位 px,最小 280px,最大 1280px }) // return wxacodeResult; if (wxacodeResult.errCode != 0) { // 生成二维码失败,返回错误信息 return wxacodeResult; } // 上传到云存储 const uploadResult = await cloud.uploadFile({ cloudPath: 'qr/' + qr_name_hash + '.jpg', fileContent: wxacodeResult.buffer, }); // return uploadResult; if (!uploadResult.fileID) { //上传失败,返回错误信息 return uploadResult; } // 获取图片临时路径 getURLReault = await cloud.getTempFileURL({ fileList: [uploadResult.fileID] }); fileObj = getURLReault.fileList[0]; fileObj.fromCache = false; // 上传成功,获取文件临时url,返回临时路径的查询结果 return fileObj; } catch (err) { return err } } [代码] 4. 小程序页面调用 调用页面就比较简单了,在小程序新建一个 [代码]pages/share/share[代码] 在 [代码]onLoad[代码] 函数调用云函数。 [代码]// 使用前记得先初始化云函数,一版放到 app.js onLaunch() 中 // wx.cloud.init({env: 'dev-8888'}) wx.cloud.callFunction({ name: 'getqr', data: { page: 'pages/demo/demo', scene: 'id=6', } }).then(res => { console.log(res.result); if (res.result.status == 0) { _this.setData({ qr_url: res.result.tempFileURL }) }else{ wx.showToast({ icon: 'none', title: '调用失败', }) } }).catch(err => { console.error(err); wx.showToast({ icon: 'none', title: '调用失败', }) }) [代码] 至此完整的调用过程已经全部完成,详细代码可以到 项目代码 查看。 代码中还对入口页面和share页面的参数做了包装,云函数可以直接使用,小程序可以稍做修改适应自己业务。 写在最后 小程序云开发已经开放了很多功能,除了这次提到的生成小程序码,云调用还可以发送模板消息。有需要的开发者又一个理由可以快速上线新功能了。 云开发还开放了[代码]HTTP API[代码],也就是用自己的服务器调用云函数。以前看完云开发介绍文章最大的疑问就是,你说的都很好,可是后台数据怎么管理呢?不能跟自己的服务器结合,只能放一些轻量的小程序。有了 [代码]HTTP API[代码] 以后就可以用自己的服务器做管理后台了。这时候你要问,都用上服务器了,还需要云开发做什么。首先,云开发免费;其次,免费功能已经够强,就差不能做Web管理后台了;最后,获取access_token(小程序及小游戏调用不要求IP地址在白名单内。)
2020-07-10 - 插屏广告页面来回切换控制台会报memory leak
- 当前 Bug 的表现(可附上截图) 见下图 - 预期表现 页面销毁后创建的广告对象也应该同步销毁 - 复现路径 在一个二级页内的onLoad函数内创建插屏广告对象, 然后来回切换页面(即反复执行onLoad) 大约三次左右会提示 [图片] 再切换三次左右会提示 [图片] 再经过三次左右会提示 [图片] emmmmmm 求问如何解决? - 提供一个最简复现 Demo [代码]onLoad(options) {[代码][代码] [代码][代码]let interstitialAd = wx.createInterstitialAd({adUnitId})[代码][代码]}[代码]
2019-06-11 - 显示插屏广告的方法经常报错
- 代码 [代码]let intsetAd = wx.createInterstitialAd({[代码][代码] [代码][代码]adUnitId: [代码][代码]'adunit-6782f5dc36d498e4'[代码][代码]})[代码][代码]intsetAd.show().then(() => {//出错行view.js:749[代码][代码] [代码][代码]if[代码] [代码](!page.data.adFlag){[代码][代码] [代码][代码]page.setData({ adFlag: 1 })[代码][代码] [代码][代码]}[代码][代码]}).[代码][代码]catch[代码][代码](err => {[代码][代码] [代码][代码]console.log(err.errMsg)[代码][代码] [代码][代码]page.setData({ adFlag: 2 })[代码][代码]})[代码] - 当前 Bug 的表现(可附上截图) Object.values is not a function. (In 'Object.values(ub.show)', 'Object.values' is undefined);at pages/item/view page adInsert functionhttps://lib/WAService.js:1:738513Im@https://lib/WAService.js:1:738574value@https://lib/WAService.js:1:745315adInsert@https://pages/item/view.js:749:14https://lib/WAService.js:1:1007171
2019-04-19 - 小程序云开发实现微信支付,小程序支付常见问题汇总及解决方案
近期有比较多的同学反映,使用云开发调取微信支付时,老是提示订单不存在。今天就把这几天大家遇到的问题统一汇总到这里。 一,订单不存在的错误 如下图所示的错误。 [图片] 通常看到这个错误时,最好去看下上面看下,有这么一段日志。 [图片] 其实这里已经很明确的给出了错误信息。 [图片] 二,appid和openid不匹配的问题 通常出现这个问题,是因为你拿到老师的代码以后,没有把project.config.json和pay的config下面的index.js里的appid改成你自己的。 [图片] [图片] 至于如何获取自己的小程序的appid,我在小程序零基础的课程里有讲过的。不知道如何获取自己小程序appid的同学可以去看下老师之前的文章或者视频。 三,appid和mch_id不匹配 [图片] 出现这种文就是因为,你没有给自己的小程序关联商户号。 小程序想要使用微信支付,必须满足下面几个条件的 1,必须是非个人小程序(个人小程序用不了支付) 2,必须申请微信支付的商户号。 3,必须配置商户号密匙。 4,必须在自己小程序里关联你的商户号 商户号的申请和密匙配置,可以参考老师的这篇文章 微信支付商户平台-配置密钥/API安全 上面几个条件都满足了,以后,在下面配置你的商户号和商户密匙 [图片] 四,签名错误 通常出现这错误是因为你的商户密匙没有配置成功,或者你的商户密匙拿的是错误的。商户密匙的配置可以看老师的这篇文章。 微信支付商户平台-配置密钥/API安全 [图片] 这里有一点要注意,这个商户密匙是你微信支付里的密匙,不是你小程序的密匙。 [图片] 五,pay 云函数老是不能上传成功,或者上传成功后是错误的。 [图片] 如果你上传云函数老是报上面的错误,就先关闭开发者工具。然后再打开,开发者工具,进入云开发管理界面,把错误的pay删除了。 [图片] 然后再到你的代码目录里做下同步。 [图片] 这样我们就可以重新上传我们的pay函数了。 [图片] 上传云函数时,一定要记得选择如上图箭头所指的。 上传的时候,会有下面这个提示,可以忽略不管。 [图片] 出现下图就代码你云函数上传成功了。 [图片] 持续更新中。。。 大家在使用云开发实现支付的时候,有任何问题,都可以在我们的付费学习群里讨论,我看到后会把问题汇总起来,方便大家学习使用。 云开发实现微信支付视频教程 《1小时开发商城类小程序》 https://edu.csdn.net/course/detail/24770 有任何关于小程序的问题,都可以加老师微信 2501902696 (备注小程序)
2019-07-16 - 【搜索Widget-自定义模板】后台性能测试(压测),未收到微信请求
小程序appid: wxabcb878857ecb4ac 类目名称: 招聘查询 已按文档中描述做好各处设置: 1.小程序消息推送已设置(在小程序wxabcb878857ecb4ac的管理后台中设置) 2.后台测试接口已设置 3.微信公众平台接口调试工具已可调通(连接的小程序消息推送设置中的url,Token和EncodingAESKey也是复制过来的) 4.我本人微信号已在小程序项目成员中 5.真机调试已通过,可正常展示数据并可跳转到我们的小程序 6.自测用例txt文件,编码UTF-8 示例:{"type": 11036, "slot_list": [{"key": "company_name", "value": "中国中化集团公司"}, {"key": "gps_city", "value": "北京市"}]} 每一行都如示例格式显示,并且有正常换行 7.之前的企业查询服务一开始也收不到请求,然后发贴求助,好像你们那边帮忙解决了一次,之后企业查询服务顺利通过压测,此次是招聘查询服务,遇到类似的问题 问题现象: 压力测试多次,全部显示“请求失败过多,压测异常中止” 我们的服务器在压测时段内未接收到任何请求 希望帮忙看一下我们哪里做得不对?导致压测没有给我么发送请求?如果发送了请求,希望得知微信测请求的url和header和参数信息。不知可否私信把错误信息和请求信息发给我,帮助定位问题,早日完成搜索功能,提供搜索服务 在线等,很急,谢谢~
2019-07-16 - open-type如何阻止冒泡 官方答案是什么啊
<view catchtap='toDetial'> <button open-type='share' >邀好友继续砍价</button> </view> 分享时会冒泡触发 求官方答案 最合理的
2019-03-02 - 云开发不提供redis缓存之类的吗
- 需求的场景描述(希望解决的问题) 1. 用户token。 2. 常用数据快速读取。 - 希望提供的能力 能设置有效期的快速存储,
2019-05-05 - 微信证件OCR识别能力开放
微信证件OCR识别能力是微信团队推出的一套提升移动端快捷信息录入的工具,目前支持身份证、银行卡、行驶证、驾驶证、营业执照 OCR 识别。经过持续迭代优化,现在对外开放接入测试。 使用场景 微信证件OCR广泛适用于政务、医疗、交通、教育、金融等各行各业,涉及到需要用户输入身份信息、银行卡信息、车辆信息场景时,即可通过OCR识快速识别、输入信息,极大提高输入效率,提高用户体验。 以下为开通插件的部分小程序案例:国家发展改革委政务服务、安徽省统一公共支付平台、南航e行、湖南电信网上营业厅、中国国际航空、厦门航空、上海客运总站、沃尔玛、青岛银行信用卡等。以下是部分使用场景的截图: [图片] [图片] [图片] 业界领先的识别水准 微信证件OCR历经v1.0版本(基于传统的版面分析, 单字切分, 字符分类算法),v2.0 (基于深度学习的卡片定位, 文字定位, 单字切分, 字符分类),到现在的v3.0版本(基于深度学习的卡片定位, 文本行定位, 串识别),不断优化OCR的识别精度, 取得了业界领先的识别水准。可以轻松应对在移动端拍照/扫描的情况,诸如拍摄角度、光照、阴影、遮挡等的干扰。 可以扫码体验我们的demo小程序 [图片] 灵活易用的调用方式 目前有两种调用方式:小程序插件方式和云端API的方式。 对于对交互没特殊需求的开发者,可以直接使用我们的小程序插件;有订制化需求的,也可以直接使用我们的云端API方式;后续我们也会把这个升级成小程序的基础能力,做成类扫码的体验,进一步简化接入和调用流程。合作与开放 内测期间,对于已认证的订阅号、服务号、企业号、小程序,我们提供了 500 次/天 的免费额度供开发者使用。如免费额度无法满足使用需求,开发者可以【申请调整 OCR 接口调用次数上限】为主题,发送邮件至:wx_city@tencent.com 申请调整,并在正文中注明小程序帐号 AppID、业务主体、业务背景、服务流程载体、日调用量预估。调整的额度仅在内测期间有效。有任何意见或者反馈,都可以在微信开放社区上联系我们,以便于我们更加了解客户对OCR的需求精确度以及需求场景,从而不断迭代优化OCR版本,拓展OCR服务的范围,如支持更多的场景需求: 发票识别、名片识别、街景识别等等。 微信团队 2019/07/03
2019-09-19 - 小程序做切换主题功能遇到的问题
最近在给一个小程序开发darkmode的功能,虽然已经实现了,但还是希望官方能支持一下。下边是遇到的问题,欢迎探讨: 1、wx.setNavigationBarColor 和 wx.setBackgroundColor 这两个方法,只能支持当前页面的颜色切换,现在处理方法是在每一个页面的 componentDidShow 里做set,结果切换页面时会先白后黑,体验很不好。希望能支持全局的设置。 2、页面的背景色需要给 根page 加className来实现,但是现在加不了。现在的处理办法是自己写一个wrapper元素,把page给撑到全屏,然后给这个wrapper加className来切换背景色。这样很不酷。 希望可以给这个page加className,或者提供一个方法全局动态设置这个背景色: [图片] 现在darkmode也是趋势,希望官方大大考虑一下~~笔芯
2019-06-26 - 通过公众号文章打开小程序,获取公众号appid,这个需求了解下
这个功能是否可以支持下 @官方
2019-06-28 - 另辟蹊径:离开模板消息,如何更优雅的向用户推送消息
适用对象 目前更适用于企业内部管理及报单类应用场景 Q no A Q1:您当前是如何实现您的消息推送的? Q2:您使用模板消息推送是否会遇到: [代码] 1. 需要推送的对象涉及多个场景,需要被提醒多次? 2. 需要推送的时间点超出操作后7天时间范围? [代码] Q3: 收集了足够的formId,最终频繁的推送导致客户无法接收到有效信息? 当然模板消息的推送方式和限制是有问题的么? 不 没有问题!但是依旧会有一些特殊场景需要突破模板消息的限制。 被动接收: A 提交工单到 B平台 , B平台安排 C员工处理工单 需及时通知到C 反复提醒: C员工接收到工单, B平台需向A 及时通告处理进度 [已派单 => 已出发 => 已到达 => 已处理 => 待评价 => 已结单 ] 长时间回复: A 所提交工单为特殊工单,需指定于一周后安排上门实施 这种场景下,C未操作平台B的小程序,B很难推送给C ; A只操作一次,B很难推送多条信息 ; 超过有效期 , B 也很难推送信息给A 。 当然最后您也可以选择收集formId 、 邮件推送、短信提醒的方式。 言归正传 为了在微信小程序下,更好的解决这些问题。我们在小程序内引入了一种新的消息管理方式。通过集中的消息管理让用户自主选择信息。用户可以选择强提醒(app推送,邮件推送,微信推送,立即接收)、弱提醒(消息收取但不提醒,感兴趣再查看)、不提醒(不接收任何消息) [图片] 猝不及防的丢个菊花码给你 是他是他 就是他 干脆再甩个 github吧 强势插入的说明 app为Bark,目前仅支持ios 。是由 Finb 开发并开源的一款软件 Bark是什么? Bark is an iOS App which allows you to push customed notifications to your iPhone 客户端 https://github.com/Finb/Bark 服务器端 https://github.com/Finb/go-tools AppStore https://itunes.apple.com/cn/app/bark-customed-notifications/id1403753865 暂无android端软件,为更好体验。正在寻求接入一款同类型的android端推送类软件。如果有知道的朋友也可以留言提供。谢谢 PS:目前已支持app推送、邮件推送、微信推送、静默推送等方式。可以无需下载app即可体验 流程演示 下面给个Gif 感受一下, 第三方小程序通过授权接入,主动向授权成功用户推送消息的流程。 整个授权流程还是比较简单的。 [图片] 说明:最后的推送是由申请授权的第三方小程序主动触发的,大家可以搜索“Hacker密码”来体验这一功能。 如何接入 免费接入使用,不收取任何费用。鼓励个人开发者接入 1. 接入 添加至app.json [代码] "navigateToMiniProgramAppIdList":["wx74db71d8a9e3b699"] [代码] 微信小程序代码内跳转至授权页 [代码] wx.navigateToMiniProgram({ appId: "wx74db71d8a9e3b699", path: "/pages/bind/app", extraData: { appName: "Bark Helper", // 必填,修改为您当前小程序名称 openid: "" // 必填,修改为当前用户的openid }, envVersion: "release", success(res) { // 打开成功 } }) [代码] 请填写真实有效的openid,以便于调用接口对指定用户发放。虚假的openid将导致信息发送错乱 接收授权结果 用户授权成功或失败后,Bark助手都将返回源小程序 您需要在[代码]App.onLaunch[代码]或[代码]App.onShow[代码]监听来自[代码]appId: 'wx74db71d8a9e3b699'[代码]的[代码]extraData[代码]数据 数据格式为: [代码] "extraData":{ "key":"", //app的授权key "bind":true, //绑定状态 Boolean "errMsg":"" //错误信息 bind为false是会返回 } [代码] 建议在[代码]App.onShow[代码]内监听 [代码] onShow(event){ if(event && event.referrerInfo && event.referrerInfo.appId === 'wx74db71d8a9e3b699'){ const _extraData = ( event && event.referrerInfo && event.referrerInfo.extraData ) || {} if(_extraData.bind){ //绑定成功 console.log(_extraData.key) }else{ //绑定失败 console.log(_extraData.errMsg) } } } [代码] 当bind为true时,表示授权成功 便捷接入 接口文档:github 用户保护 1. 自由选择接收信息的权利 用户可以自由管理已授权的应用,主动屏蔽和解绑已授权的应用,实现消息免打扰和禁收消息 2. 消息推送分离 所有推送不会在微信消息内被提示,只会在小程序内被提示 对于用户想及时了解的应用可以通过app及时获取 消息免打扰开启后,该应用所有推送不会通过用户绑定的Bark进行推送,但消息仍会被小程序接收。需要打开小程序查看 3. 文本过滤 通过微信内容安全[代码]security.msgSecCheck[代码]接口对所有应用推送信息进行过滤 4. 投诉与处罚 用户可在收到推送后,对推送信息发起投诉。投诉成立后,该应用会受到不同程度的处罚(扣分、临时封禁、永久封禁)。 写在最后 1.为什么我们不参照大多数的推送方式去收集formid来共用? 我们希望在腾讯的生态上更好的弥补小程序的约束和不足,而不是希望通过破坏规则来实现所谓的“捷径” 2.是否一定需要安装app? app目前只适配“Bark”,后续将适配更多第三方app。如果您无法或不愿意安装app,也可以选择绑定邮箱。以邮件的形式接收信息
2020-06-11 - swiper 图片抖动问题
小程序顶部banner图在模拟环境中没有问题,但是发布之后,在真机中就一直抖动,如下图 [图片] <scroll-view style="width:100%;height:100%" scroll-y="true" bindscroll="scrollTopFun" scroll-with-animation="true" scroll-top="{{scrollTop}}"> <view class="page-section page-section-spacing" bindtap="goJnh"> <swiper class="swiper" indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" circular="{{circular}}" vertical="{{vertical}}" interval="{{interval}}" duration="{{duration}}" previous-margin="{{previousMargin}}px" next-margin="{{nextMargin}}px" bindchange='bindchange' current="{{current}}" style="height:{{imgheights[current]}}rpx;"> <block wx:for="{{background}}" wx:key="*this"> <swiper-item> <image mode="widthFix" src="{{item}}" bindload="imageLoad" data-index='{{index}}' class="slide-image" width="355" height="200" /> </swiper-item> </block> </swiper> <text class="banner-text">国家AAAA级景区———金牛湖旅游度假区</text> </view>
2019-07-01 - Thor UI组件库,小程序代码片段分享
尊敬的开发者,欢迎体验Thor UI! 该项目主要是一些小程序代码片段的分享,以及基础组件的封装。项目免费开源,源码可在GitHub上下载,会不定期进行更新。 项目可能存在缺陷或者bug,如果您在使用过程中发现问题或者有更好的建议,可反馈给我。您也可以将自己觉得不错的案例分享给我,我会扩展到此项目中。 ThorUI QQ交流群:928308676 扫码体验(一) [图片] 扫码体验(二) [图片] 组件文档地址: http://www.thorui.cn/doc ThorUI uni-app版本GitHub地址: https://github.com/dingyong0214/ThorUI-uniapp ThorUI uni-app版本插件市场地址: https://ext.dcloud.net.cn/plugin?id=556 ThorUI 小程序版本GitHub地址: https://github.com/dingyong0214/ThorUI ThorUI 小程序版本插件市场地址: https://ext.dcloud.net.cn/plugin?id=569 V1.6.5(2021-05-24) 1.tui-validation(表单验证)优化,新增validator自定义验证配置项,具体查看文档。 2.tui-round-progress(圆形进度条)组件优化,修复已知问题。 3.tui-cascade-selection(级联选择器)组件优化,修复已知问题。 4.tui-tabs(标签页)组件优化,选项卡可设置数字角标。 ===================== 【ThorUI示例V1.1.0】更新: 1.tui-org-tree(组织架构树)组件优化,可控制节点内容排版方式、节点选中状态、展开收起子节点,具体查看文档。 2.新增tui-form(表单)组件,主要用于表单验证。 3.新增tui-input(输入框)组件,原生input组件增强。 4.新增tui-textarea(多行输入框)组件。 5.新增tui-label(标签)组件,用来改进表单组件的可用性。 6.新增tui-radio(单项选择器)组件。 7.新增tui-checkbox(多项选择器)组件。 8.新增tui-switch(开关)组件。 9.新增tui-picker(选择器)组件,支持1~3级数据。 10.新增tui-landscape(压屏窗)组件。 11.新增tui-segmented-control(分段器)组件。 12.新增tui-notice-bar(通告栏)组件。 13.新增tui-alerts(警告框)组件。 14.新增tui-request(数据请求)封装,支持Promise,支持请求拦截和响应拦截,支持请求未结束之前阻止重复请求等。 15.tui-utils(工具类)优化,具体查看文档。 16.新增tui-row组件,配合组件tui-col组件使用(24栅格化布局)。 17.新增tui-tree-view(树型菜单)组件。 18.新增tui-charts-column(柱状图-css版)组件。 19.新增tui-charts-bar(横向柱状图-css版)组件。 20.新增tui-charts-line(折线图表-css版)组件。 21.新增tui-charts-pie(饼状图表-css版)组件。 22.tui-lazyload-img(图片懒加载)组件优化,修复已知问题。 23.新增tui-pagination(分页器)组件。 部分功能截图 [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] V1.4.0: 1.新增日期时间选择器组件。 2.H5新增复制文本功能。 3.新增悬浮按钮组件。 4.新增Tabbar组件。 5.新增tabs标签页组件。 6.新增折叠面板组件。 7.新增图片上传组件。 8.NumberBox组件优化调整。 9.Modal组件优化调整。 10.sticky组件优化调整。 11.countdown组件优化调整。 12.商城模板新增购物车、我的、提交订单、支付成功、我的订单、地址列表、新增地址、设置、用户信息等页面。 V1.3.0 1.新增倒计时组件:时分秒倒计时,支持设置大小,颜色等。 2.新增分隔符组件:Divider分隔符,可设置占据高度,线条宽度,颜色等。 3.新增卡片轮播:包含顶部轮播,秒杀商品轮播等。 4.nvue下拉刷新优化。 5.修复已知bug。 V1.2.2 1.新增组件Modal弹框:可设置按钮数,按钮样式,提示文字样式等,还可自定义弹框内容。 2.修复部分已知bug。 ThorUI V1.2.1 1.新增组件Modal弹框:可设置按钮数,按钮样式,提示文字样式等,还可自定义弹框内容。 2.修复已知bug。 3.ThorUI已上线uni-app版本,请移步uni-app插件市场搜索ThorUI。 ThorUI V1.2.0 1.新增组件NumberBox数字框:可设置步长,支持浮点数,支持调整样式(可单独设置)。 2.新增组件Rate评分:可设置星星数,可设置大小颜色。 3.新增聊天模板,包含:消息列表,好友列表,聊天界面等。 4.新增商城模板,包含:商城首页,商城列表,商城详情等。 5.优化部分体验。 ThorUI V1.1.0 1.将基础组件移出扩展,单独出来。 2.扩展改为单独tab bar选项extend。 3.新增滚动消息(extend=>滚动消息):包括顶部通告栏,滚动新闻,以及搜索框中出现的热搜产品。 4.新增弹层下拉选择(extend=>弹层下拉选择):包含顶部下拉选择列表、输入框下拉选择以及底部分享弹层。 5.新增ActionSheet操作菜单(extend=>ActionSheet):可加入提示信息,可单独设置字体样式。 6.新增新闻模板(extend=>新闻模板):包含新闻列表,新闻详情,评论等。 7.部分功能优化,修复已知bug。 ThorUI V1.0.0 1.【地图】新增拖拽定位功能 2.【扩展】新增基础组件,包括:字体图标,按钮,Grid宫格,List列表,Card卡片… 3.【扩展】新增数字键盘 4.【扩展】新增时间轴 5.完善thor(个人中心)功能,包括:关于Thor UI,模拟登录,GitHub地址复制,赞赏,反馈,更新日志等 6.已知bug修复,以及部分功能优化 商城模板部分截图 [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] 新闻模板部分截图 [图片] [图片] [图片] [图片] [图片] [图片] 聊天模板截图 [图片] [图片] [图片] 组件功能部分截图 [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片]
2021-06-01 - 微盟小程序性能优化实践(上)
微盟小程序性能优化要分享的内容分为三部分,启动性能加载、首屏加载的体验建议和渲染性能优化。 今天主要讲启动性能加载的性能优化实践,先看启动加载过程的流程: [图片] · 公共库注入 · 资源准备(基础UI创建,代码包下载) · 业务代码注入和渲染 · 渲染首屏 · 异步请求 优化方案 1、控制代码包大小 · 开启开发者工具中的 “ 上传代码时自动压缩 ” · 及时清理无用代码和资源文件 · 减少代码包中的图片等资源文件的大小和数量 · 将图片等资源文件放到CND中 · 提取公共样式 · 代码压缩,图片格式,压缩,或者外联 · 公共组件提取,代码复用 2、 分包加载 分包加载过程流程 [图片] 在开发小程序分包项目时,会有一个或者多个分包,其中没有分包小程序必须包含一个主包,即放置启动页面或者tabBar页面,以及一些分包都需要用到的公共资源脚本。 在小程序启动时,默认会下载主包并且启动主包内页面,如果用户打开分包内的页面,客户端会把分包下载下来,下载完之后再进行展示。 · 分包加载流程 [图片] 使用分包加载的优点: · 能够增加小程序更大的代码体积,开发更多的功能 · 对于用户,可以更快地打开小程序,同时不影响启动速度 使用分包加载有哪些限制: · 整个小程序所有分包不能超过8M · 单个主包/分包不能超过2M 3、 运行机制优化 · 代码中减少立即执行的代码数量 · 避免高开销和长时间阻塞代码 · 业务代码都写入页面的生命周期中 · 做好缓存策略 4、 数据管理优化 · 首屏请求数量尽量不能超过5个,超过的可以做接口合并(node层,服务端都可以处理) · 对多次提交的数据可以做合并处理 首屏加载的体验建议和渲染性能优化这两部分的内容,将在下次分享给大家。微盟小程序性能优化实践(下)
2018-09-25 - 小程序分包是如何处理静态资源的
- 当前 Bug 的表现(可附上截图) - 预期表现 - 复现路径 - 提供一个最简复现 Demo 由于手上几个项目的体积越来越大(主要是一些图片等静态资源),直逼2M,故打算做分包处理, 看完官方文档中分包部分内容之后还有以下疑问,请前辈们指点一下: 1、原先项目中的图片是放在与pages目录同一级的 imgs 文件中,分包之后,是否需要将每个包用到的静态资源迁移至对应包的目录下 2、如果不需要,小程序是否会只加载对应分包的资源,还是说只是粗暴地下载所有静态资源
2019-05-30 - 如何用小程序实现类原生APP下一条无限刷体验
1.背景 如今信息流业务是各大互联网公司争先抢占的一个大面包,为了提高用户的后续消费,产品想出了各种各样的方法,例如在微视中,用户可以无限上拉出下一条视频;在知乎中,也可以无限上拉出下一条回答。这样的操作方式用户体验更好,后续消费也更多。最近几年的时间,微信小程序已经从一颗小小的萌芽成长为参天大树,形成了较大规模的生态,小程序也拥有了一个很大的流量入口。 2.demo体验 那如何才能在小程序中实现类原生APP效果的下一条无限刷体验? 这篇文章详细记录了下一条无限刷效果的实现原理,以及细节和体验优化,并将相关代码抽象成一个微信小程序代码片段,有需要的同学可查看demo源码。 线上效果请用微信扫码体验: [图片] 小程序demo体验请点击:https://developers.weixin.qq.com/s/vIfPUomP7f9a 3.实现原理 出于性能和兼容性考虑,我们尽量采用小程序官方提供的原生组件来实现下一条无限刷效果。我们发现,可以将无限上拉下一篇的文章看作一个竖向滚动的轮播图,又由于每一篇文章的内容长度高于一屏幕高度,所以需要实现文章内部可滚动,以及文章之间可以上拉和下拉切换的功能。 在多次尝试后,我们最终采用了在[代码]<swiper>[代码]组件内部嵌套一个[代码]<scroll-view>[代码]组件的方式实现,利用[代码]<swiper>[代码]组件来实现文章之间上拉和下拉切换的功能,利用[代码]<scroll-view>[代码]来实现一篇文章内部可上下滚动的功能。 所以页面的dom结构如下所示: [代码]<swiper class='scroll-swiper' circular="{{false}}" vertical="{{true}}" bindchange="bindChange" skip-hidden-item-layout="{{true}}" duration="{{500}}" easing-function="easeInCubic" > <block wx:for="{{articleData}}"> <swiper-item> <scroll-view scroll-top="0" scroll-with-animation="{{false}}" scroll-y > content </scroll-view> </swiper-item> </block> </swiper> [代码] 4.性能优化 我们知道view部分是运行在webview上的,所以前端领域的大多数优化方式都有用。例如减少代码包体积,使用分包,渲染性能优化等。下面主要讲一下渲染性能优化。 4.1 dom优化 由于页面需要无限上拉刷新,所以要在[代码]<swiper>[代码]组件中不断的增加[代码]<swiper-item>[代码],这样必然会导致页面的dom节点成倍数的增加,最后非常卡顿。 为了优化页面的dom节点,我们利用[代码]<swiper>[代码]的[代码]current[代码]和[代码]<swiper-item>[代码]的[代码]index[代码]来做优化,控制是否渲染dom节点。首先,仅当[代码]index <= current + 1[代码]时渲染[代码]<swiper-item>[代码],也就是页面中最多预先加载出下一条,而不是将接口返回的所有后续数据都渲染出来;其次,对于用户已经消费过的之前的[代码]<swiper-item>[代码],不能直接销毁dom节点,否则会导致[代码]<swiper>[代码]的[代码]current[代码]值出现错乱,但是我们可以控制是否渲染[代码]<swiper-item>[代码]内部的子节点,我们设置了仅当[代码]current <= index + 1 && index -1 <= current[代码]时才会渲染[代码]<swiper-item>[代码]中的内容,也就是仅渲染当先文章,及上一篇和下一篇的文章内容,其他文章的dom节点都被销毁了。 这样,无论用户上拉刷新了多少次,页面中最多只会渲染3篇文章的内容,避免了因为上拉次数太多导致的页面卡顿。 4.2 分页时setData的优化 setData工作原理 [图片] 小程序的视图层目前使用[代码]WebView[代码]作为渲染载体,而逻辑层是由独立的 [代码]JavascriptCore[代码] 作为运行环境。在架构上,[代码]WebView[代码] 和 [代码]JavascriptCore[代码] 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 [代码]evaluateJavascript[代码] 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 [代码]JS[代码] 脚本,再通过执行 [代码]JS[代码] 脚本的形式传递到两边独立环境。 而 [代码]evaluateJavascript[代码] 的执行会受很多方面的影响,数据到达视图层并不是实时的。 每次 [代码]setData[代码] 的调用都是一次进程间通信过程,通信开销与 setData 的数据量正相关。 [代码]setData[代码] 会引发视图层页面内容的更新,这一耗时操作一定时间中会阻塞用户交互。 [代码]setData[代码] 是小程序开发中使用最频繁的接口,也是最容易引发性能问题的接口。 避免不当使用setData [代码]data[代码] 应仅包括与页面渲染相关的数据,其他数据可绑定在this上。使用 [代码]data[代码] 在方法间共享数据,会增加 setData 传输的数据量,。 使用 [代码]setData[代码] 传输大量数据,通讯耗时与数据正相关,页面更新延迟可能造成页面更新开销增加。仅传输页面中发生变化的数据,使用 [代码]setData[代码] 的特殊 [代码]key[代码] 实现局部更新。 避免不必要的 [代码]setData[代码],避免短时间内频繁调用 [代码]setData[代码],对连续的setData调用进行合并。不然会导致操作卡顿,交互延迟,阻塞通信,页面渲染延迟。 避免在后台页面进行 [代码]setData[代码],这样会抢占前台页面的渲染资源。可将页面切入后台后的[代码]setData[代码]调用延迟到页面重新展示时执行。 优化示例 无限上拉刷新的数据会采用分页接口的形式,分多次请求回来。在使用分页接口拉取到下一刷的数据后,我们需要调用[代码]setData[代码]将数据写进[代码]data[代码]的[代码]articleData[代码]中,这个[代码]articleData[代码]是一个数组,里面存放着所有的文章数据,数据量十分庞大,如果直接[代码]setData[代码]会增加通讯耗时和页面更新开销,导致操作卡顿,交互延迟。 为了避免这个问题,我们将[代码]articleData[代码]改进为一个二维数组,每一次[代码]setData[代码]通过分页的 [代码]cachedCount[代码]标识来实现局部更新,具体代码如下: [代码]this.setData({ [`articleData[${cachedCount}]`]: [...data], cachedCount: cachedCount + 1, }) [代码] [代码]articleData[代码]的结构如下: [图片] 4.3 体验优化 解决了操作卡顿,交互延迟等问题,我们还需要对动画和交互的体验进行优化,以达到类原生APP效果的体验。 在文章间上拉切换时,我们使用了[代码]<swiper>[代码]组件自带的动画效果,并通过设置[代码]duration[代码]和[代码]easing-function[代码]来优化滚动细节和动画。 当用户阅读文章到底部时,会提示下一篇文章的标题等信息,而在页面上拉时,由于下一篇文章的内容已经加载出来了,这样在滑动过程中会出现两个重复的标题。为了避免这种情况出现,我们通过一个占满屏幕宽高的空白[代码]<view>[代码]来将下一篇文章的内容撑出屏幕,并在滚动结束时,通过[代码]hidden="{{index !== current && index !== current + 1}}"[代码]来隐藏这个空白[代码]<view>[代码],并对这个空白[代码]<view>[代码]的高度变化增加动画,来实现下一篇文章从屏幕底部滚动到屏幕顶部的效果: [代码].fake-scroll { height: 100%; width: 100%; transition: height 0.3s cubic-bezier(0.167,0.167,0.4,1); } [代码] [图片] 而当用户想要上拉查看之前阅读过的文章时,我们需要给用户一个“下滑查看上一条”提示,所以也可以采用同上的方式,通过一个占满屏幕宽高的提示语[代码]<view>[代码]来将上一篇文章的内容撑出屏幕,并在滚动结束时,通过[代码]wx:if="{{index + 1 === current}}"[代码]来隐藏这个提示语[代码]<view>[代码],并对这个提示语[代码]<view>[代码]的透明度变化增加动画,来实现下拉时提示“下滑查看上一条”的效果: [代码].fake-previous { height: 100%; width: 100%; opacity: 0; transition: opacity 1s ease-in; } .fake-previous.show-fake-previous { opacity: 1; } [代码] 至此,这个类原生APP效果的下一条无限刷体验的需求的所有要点和细节都已实现。 记录在此,欢迎交流和讨论。 小程序demo体验请点击:https://developers.weixin.qq.com/s/vIfPUomP7f9a
2019-06-25 - 小说类目需要的资质别的公司给我们授权可以吗
您好,我们公司和别的公司合作开发了一款阅读类小程序,小说的数据源是来自于他们,因为涉及到小说类目需要《互联网出版许可证》,但是我们公司不是出版公司没有相关资质,和我们合作的公司愿意提供给我们数据源以及相关资质证书的授权,请问这样我们可以通过审核吗,麻烦告知一下
2019-06-25 - scroll-view组件中使用bindscrolltoupper上滑加载历史内容
dom结构 [代码]<scroll-view scroll-y bindscrolltoupper="upper" scroll-into-view="{{toView}}" > <view id="green" >1</view> <view id="red" >2</view> <view id="yellow">3</view> <view id="blue" >4</view> </scroll-view> [代码] 可能出现的问题: 使用[代码]scroll-view[代码]中的[代码]bindscrolltoupper[代码]事件监听上拉事件,然后在[代码]scroll-view[代码]里面内容的上面追加内容,滚动条会直接滚动到对顶端,而不是停留在当前的视图窗口(类似微信消息界面上拉会加载以前的消息记录,加载完成后,界面不会自动滚动到最上面) 解决思路 该问题的核心在于需要加载历史数据后,滚动条停留在当前的视图内容,而不是滚动到顶部,关键在于要使用[代码]scroll-into-view[代码]属性,将滚动条滚动进入指定的视图,那么每个[代码]view[代码]都要有一个唯一的id 坑:新添加的view,使用新的id,已经渲染出来的view,它的id不要变更 伪代码 [代码]upper(){ // 给每个新的view指定一个唯一的新id this.list = this.list.map(x => { x.id = XXX }) // 把this.list set 进入页面中 // 滚动进入指定视图 this.toView = toView } [代码]
2019-09-27 - 小程序开发另类小技巧 --用户授权篇
小程序开发另类小技巧 --用户授权篇 getUserInfo较为特殊,不包含在本文范围内,主要针对需要授权的功能性api,例如:wx.startRecord,wx.saveImageToPhotosAlbum, wx.getLocation 原文地址:https://www.yuque.com/jinxuanzheng/gvhmm5/arexcn 仓库地址:https://github.com/jinxuanzheng01/weapp-auth-demo 背景 小程序内如果要调用部分接口需要用户进行授权,例如获取地理位置信息,收获地址,录音等等,但是小程序对于这些需要授权的接口并不是特别友好,最明显的有两点: 如果用户已拒绝授权,则不会出现弹窗,而是直接进入接口 fail 回调, 没有统一的错误信息提示,例如错误码 一般情况而言,每次授权时都应该激活弹窗进行提示,是否进行授权,例如: [图片] 而小程序内只有第一次进行授权时才会主动激活弹窗(微信提供的),其他情况下都会直接走fail回调,微信文档也在句末添加了一句请开发者兼容用户拒绝授权的场景, 这种未做兼容的情况下如果用户想要使用录音功能,第一次点击拒绝授权,那么之后无论如何也无法再次开启录音权限**,很明显不符合我们的预期。 所以我们需要一个可以进行二次授权的解决方案 常见处理方法 官方demo 下面这段代码是微信官方提供的授权代码, 可以看到也并没有兼容拒绝过授权的场景查询是否授权(即无法再次调起授权) [代码]// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope wx.getSetting({ success(res) { if (!res.authSetting['scope.record']) { wx.authorize({ scope: 'scope.record', success () { // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问 wx.startRecord() } }) } } }) [代码] 一般处理方式 那么正常情况下我们该怎么做呢?以地理位置信息授权为例: [代码]wx.getLocation({ success(res) { console.log('success', res); }, fail(err) { // 检查是否是因为未授权引起的错误 wx.getSetting({ success (res) { // 当未授权时直接调用modal窗进行提示 !res.authSetting['scope.userLocation'] && wx.showModal({ content: '您暂未开启权限,是否开启', confirmColor: '#72bd4a', success: res => { // 用户确认授权后,进入设置列表 if (res.confirm) { wx.openSetting({ success(res){ // 查看设置结果 console.log(!!res.authSetting['scope.userLocation'] ? '设置成功' : '设置失败'); }, }); } } }); } }); } }); [代码] 上面代码,有些同学可能会对在fail回调里直接使用wx.getSetting有些疑问,这里主要是因为 微信返回的错误信息没有一个统一code errMsg又在不同平台有不同的表现 从埋点数据得出结论,调用这些api接口出错率基本集中在未授权的状态下 这里为了方便就直接调用权限检查了 ,也可以稍微封装一下,方便扩展和复用,变成: [代码] bindGetLocation(e) { let that = this; wx.getLocation({ success(res) { console.log('success', res); }, fail(err) { that.__authorization('scope.userLocation'); } }); }, bindGetAddress(e) { let that = this; wx.chooseAddress({ success(res) { console.log('success', res); }, fail(err) { that.__authorization('scope.address'); } }); }, __authorization(scope) { /** 为了节省行数,不细写了,可以参考上面的fail回调,大致替换了下变量res.authSetting[scope] **/ } [代码] 看上去好像没有什么问题,fail里只引入了一行代码, 这里如果只针对较少页面的话我认为已经够用了,毕竟**‘如非必要,勿增实体’,但是对于小打卡这个小程序来说可能涉及到的页面,需要调用的场景偏多**,我并不希望每次都人工去调用这些方法,毕竟人总会犯错 梳理目标 上文已经提到了背景和常见的处理方法,那么梳理一下我们的目标,我们到底是为了解决什么问题?列了下大致为下面三点: 兼容用户拒绝授权的场景,即提供二次授权 解决多场景,多页面调用没有统一规范的问题 在底层解决,业务层不需要关心二次授权的问题 扩展wx[funcName]方法 为了节省认知成本和减少出错概率,我希望他是这个api默认携带的功能,也就是说因未授权出现错误时自动调起是否开启授权的弹窗 为了实现这个功能,我们可能需要对wx的原生api进行一层包装了(关于页面的包装可以看:如何基于微信原生构建应用级小程序底层架构) 为wx.getLocation添加自己的方法 这里需要注意的一点是直接使用常见的装饰模式是会出现报错,因为wx这个对象在设置属性时没有设置set方法,这里需要单独处理一下 [代码]// 直接装饰,会报错 Cannot set property getLocation of #<Object> which has only a getter let $getLocation = wx.getLocation; wx.getLocation = function (obj) { $getLocation(obj); }; // 需要做一些小处理 wx = {...wx}; // 对wx对象重新赋值 let $getLocation = wx.getLocation; wx.getLocation = function (obj) { console.log('调用了wx.getLocation'); $getLocation(obj); }; // 再次调用时会在控制台打印出 '调用了wx.getLocation' 字样 wx.getLocation() [代码] 劫持fail方法 第一步我们已经控制了wx.getLocation这个api,接下来就是对于fail方法的劫持,因为我们需要在fail里加入我们自己的授权逻辑 [代码]// 方法劫持 wx.getLocation = function (obj) { let originFail = obj.fail; obj.fail = async function (errMsg) { // 0 => 已授权 1 => 拒绝授权 2 => 授权成功 let authState = await authorization('scope.userLocation'); // 已授权报错说明并不是权限问题引起,所以继续抛出错误 // 拒绝授权,走已有逻辑,继续排除错误 authState !== 2 && originFail(errMsg); }; $getLocation(obj); }; // 定义检查授权方法 function authorization(scope) { return new Promise((resolve, reject) => { wx.getSetting({ success (res) { !res.authSetting[scope] ? wx.showModal({ content: '您暂未开启权限,是否开启', confirmColor: '#72bd4a', success: res => { if (res.confirm) { wx.openSetting({ success(res){ !!res.authSetting[scope] ? resolve(2) : resolve(1) }, }); }else { resolve(1); } } }) : resolve(0); } }) }); } // 业务代码中的调用 bindGetLocation(e) { let that = this; wx.getLocation({ type: 'wgs84', success(res) { console.log('success', res); }, fail(err) { console.warn('fail', err); } }); } [代码] 可以看到现在已实现的功能已经达到了我们最开始的预期,即因授权报错作为了wx.getLocation默认携带的功能,我们在业务代码里再也不需要处理任何再次授权的逻辑 也意味着wx.getLocation这个api不论在任何页面,组件,出现频次如何,**我们都不需要关心它的授权逻辑(**效果本来想贴gif图的,后面发现有图点大,具体效果去git仓库跑一下demo吧) 让我们再优化一波 上面所述大致是整个原理的一个思路,但是应用到实际项目中还需要考虑到整体的扩展性和维护成本,那么就让我们再来优化一波 代码包结构: 本质上只要在app.js这个启动文件内,引用./x-wxx/index文件对原有的wx对象进行覆盖即可 [图片] **简单的代码逻辑: ** [代码]// 大致流程: //app.js wx = require('./x-wxx/index'); // 入口处引入文件 // x-wxx/index const apiExtend = require('./lib/api-extend'); module.exports = (function (wxx) { // 对原有方法进行扩展 wxx = {...wxx}; for (let key in wxx) { !!apiExtend[key] && (()=> { // 缓存原有函数 let originFunc = wxx[key]; // 装饰扩展的函数 wxx[key] = (...args) => apiExtend[key](...args, originFunc); })(); } return wxx; })(wx); // lib/api-extend const Func = require('./Func'); (function (exports) { // 需要扩展的api(类似于config) // 获取权限 exports.authorize = function (opts, done) { // 当调用为"确认授权方法时"直接执行,避免死循环 if (opts.$callee === 'isCheckAuthApiSetting') { console.log('optsopts', opts); done(opts); return; } Func.isCheckAuthApiSetting(opts.scope, () => done(opts)); }; // 选择地址 exports.chooseAddress = function (opts, done) { Func.isCheckAuthApiSetting('scope.address', () => done(opts)); }; // 获取位置信息 exports.getLocation = function (opts, done) { Func.isCheckAuthApiSetting('scope.userLocation', () => done(opts)); }; // 保存到相册 exports.saveImageToPhotosAlbum = function (opts, done) { Func.isCheckAuthApiSetting('scope.writePhotosAlbum', () => done(opts)); } // ...more })(module.exports); [代码] 更多的玩法 可以看到我们无论后续扩展任何的微信api,都只需要在lib/api-extend.js 配置即可,这里不仅仅局限于授权,也可以做一些日志,传参的调整,例如: [代码] // 读取本地缓存(同步) exports.getStorageSync = (key, done) => { let storage = null; try { storage = done(key); } catch (e) { wx.$logger.error('getStorageSync', {msg: e.type}); } return storage; }; [代码] 这样是不是很方便呢,至于Func.isCheckAuthApiSetting这个方法具体实现,为了节省文章行数请自行去git仓库里查看吧 关于音频授权 录音授权略为特殊,以wx.getRecorderManager为例,它并不能直接调起录音授权,所以并不能直接用上述的这种方法,不过我们可以曲线救国,达到类似的效果,还记得我们对于wx.authorize的包装么,本质上我们是可以直接使用它来进行授权的,比如将它用在我们已经封装好的录音管理器的start方法进行校验 [代码]wx.authorize({ scope: 'scope.record' }); [代码] 实际上,为方便统一管理,Func.isCheckAuthApiSetting方法其实都是使用wx.authorize来实现授权的 [代码]exports.isCheckAuthApiSetting = async function(type, cb) { // 简单的类型校验 if(!type && typeof type !== 'string') return; // 声明 let err, result; // 获取本地配置项 [err, result] = await to(getSetting()); // 这里可以做一层缓存,检查缓存的状态,如果已授权可以不必再次走下面的流程,直接return出去即可 if (err) { return cb('fail'); } // 当授权成功时,直接执行 if (result.authSetting[type]) { return cb('success'); } // 调用获取权限 [err, result] = await to(authorize({scope: type, $callee: 'isCheckAuthApiSetting'})); if (!err) { return cb('success'); } } [代码] 关于用户授权 用户授权极为特殊,因为微信将wx.getUserInfo升级了一版,没有办法直接唤起了,详见《公告》,所以需要单独处理,关于这里会拆出单独的一篇文章来写一些有趣的玩法 总结 最后稍微总结下,通过上述的方案,我们解决了最开始目标的同时,也为wx这个对象上的方法提供了统一的装饰接口(lib/api-extend文件),便于后续其他行为的操作比如埋点,日志,参数校验 还是那么一句话吧,小程序不管和web开发有多少不同,本质上都是在js环境上进行开发的,希望小程序的社区环境更加活跃,带来更多有趣的东西
2019-06-14 - 【优化】解决swiper渲染很多图片时的卡顿
相信各位在开发的时候应该有遇到这样一个场景,比如商品的图片浏览,有时图片的浏览会很大,多的时候达几百张或上千张,这样就需要swiper里需要很多swiper-item,如此一来渲染的时候就会很消耗性能,渲染时会有一大段的空白时间,有时还会造成卡顿,体验非常差,下面给大家介绍一下我的解决方案。 首先是wxml结构: [图片] js: [图片] [图片] 主要是利用current属性,swiper里面只放3个swiper-item,要显示的图片放在第二,第一和第三放的是加载的动画背景,步骤如下: 1. 将请求到的数据存入一个数组picListAll内,这里不需要setData,只需要在data外面定义一个变量就行了,以减少渲染性能。 2. 把要显示的图片路径赋值给picUrl, 3. 切换的时候根据bindchange获取current属性,当current改变时判断当前图片在picListAll的index,根据index拿到图片再赋值给picUrl 主要实现步骤就是以上3 步,比较简单,要注意的是当切换到第一张和最后一张的时候要判断一下,把loding动画去掉,请求的时候还可以传入index参数以显示不同的图片,方便从前一页点击图片进入到此页面时能定位到该图片,例子里我是自己mock数据的,只是为了展示,如果你有服务器的话可以弄几百张看看效果,对比直接渲染和用以上方式渲染的差异。当然,这只是我的解决方案,如果各位有更好的方案欢迎一起讨论,一起进步. 系甘先,得闲饮茶 完整代码:https://github.com/HaveYuan/swiper
2019-01-25 - 小程序的全局变量globalData,和 storage,的时效性是一样的吗
小程序的全局变量globalData,和 storage,的时效性是一样的吗?如果不一样谁会先被销毁?
2019-05-24 - WeUI能不能直接在小程序中使用?
[图片] 不能直接在微信小程序中使用,请更改下说明。很容易误导像我这样的新人。 WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。 费解中。。。。。。。。 原来是不能直接使用,需要改成view。。。。。。。。。。。。。
2019-05-14 - 省市区区划代码
希望把所有省市区的区划代码code列出来啊,小程序端只能得到一个,获取不了全部啊。。。后台不知道啊 https://docs.alipay.com/isv/10327 你们可以去看看支付宝小程序的文档,你这边不开源,获取到的省市区和数据库的不匹配
2019-06-01 - wx.getSystemInfo()能否加多数据字段,例如设备像素密度,几寸屏
使用环境 小游戏,cocos creator开发 需求场景 wx拉起键盘 or 读出 右上角菜单栏的位置信息,游戏场景里根据键盘高度 or 右上角的对齐数据 进行UI调整。 尝试: wx接口读出的单位数据到creator并不能直接使用,而是需要转化。参照WCSS的,瞎猜计算如下:[图片] IOS下,X,ipad等完美~ 但是在andorid下,必须 再照 px---dp的转换后 数据才能完全精准(多机型实际参数传入测试)。 问题: 无法撸到DPI / PPI ,或者是几寸屏~~ 希冀 wx返回的设备信息丰富些~~,or creator 能否计算
2018-11-20 - canvas绘制drawImage图片只能画一部分
- 需求的场景描述(希望解决的问题) 需求是将多张图片绘制到canvas上,最后画上一张大尺寸的png图片(2000px左右),让先绘制的图片从png图片镂空处显示,达到组合相册的效果。但是在绘制图片的时候,真机上总是出现图片只能绘制一部分的问题,编辑器上能全绘制出来。希望有人能帮我解决一下这个问题,谢谢! - 希望提供的能力 希望能够将png图片完整的画到canvas上,而不是只画出来一部分,谢谢! 1,正常绘制是这样: [图片] 实际绘制出来是这样: [图片] 2、正常绘制是这样: [图片] 实际绘制是这样: [图片]
2018-10-13 - 将两个canvas中的数据放在一个canvas中合成一张图片
- 当前 Bug 的表现 canvasA、canvasB,在canvasB中进行无规则裁剪操作,使用canvasToTempFilePath API把canvasB指定区域的内容导出生成指定大小的图片,将导出的图片使用drawImage API绘制到canvasA中,再使用drawImage API绘制一张图片到canvasA中,两次都使用draw(true)进行绘制,最后使用canvasToTempFilePath API把canvasA指定区域的内容导出生成指定大小的图片。 // 将裁剪画布内容保存 wx.canvasToTempFilePath({ x: 0, y: 0, width: 320, height: 320, destWidth: 320, destHeight: 320, canvasId: 'clipCanvas', success: res => { //清洗相框画布 //frameCanvas.clearRect(0, 0, 320, 320); console.log('裁剪图:'+res.tempFilePath); // 绘制裁剪图到相框画布 frameCanvas.drawImage(res.tempFilePath, 0, 0, 320, 320); frameCanvas.draw(); // 绘制相框图到相框画布 console.log('相框图:'+this.data.framePic); frameCanvas.drawImage(this.data.framePic, 0, 0, 320, 320); frameCanvas.draw(true); // 保存相框画布内容为图片 wx.canvasToTempFilePath({ x: 0, y: 0, width: 320, height: 320, destWidth: 320, destWidth: 320, canvasId: 'frameCanvas', success: res => { console.log('合成图:'+res.tempFilePath); // 关闭裁剪层,更新数据 this.setData({ clipImagePath: res.tempFilePath, isClip: false }); // 将裁剪图绘制到产品上 this.drawClipImageToGoods(); } }, this); } }, this); [图片][图片] [图片] 真机测试iOS和Android都没有问题,开发工具有问题。
2019-03-06 - Painter 一款轻量级的小程序海报生成组件
生成海报相信大家有的人都做过,但是canvas绘图的坑太多。大家可以试试这个组件。然后附上楼下大哥做的可视化拖拽生成painter代码的工具:链接地址https://developers.weixin.qq.com/community/develop/article/doc/000e222d9bcc305c5739c718d56813
2019-09-27 - 从入门到上线踩坑纪录
从关注小游戏到接触Cocos再到小游戏终于成功上线,历时三个月,中间也踩了不少坑,总结一下,希望可以帮助到新手。 1.软著申请:(没有想象中的复杂,只是时间问题) 1)网上买还是自己申请? 为了亲自走一个全流程,我是自己找的模版写的材料。网上有各式各样的模版,如果有需要可以发我邮箱(文末有),会尽快回复。 在写材料的时候一定要注意细节,比如错别字、图片大小等(一次微信分享的截图上面软件名字和申请的不符被打回)。只要这些没问题,一趟就够了,毕竟只是一个软件登记,还是比较松的。 2)申请时效要多久? 我本人是在深圳,深圳有粤港澳大厅,但是跑过去才知道这边只针对广东户口才能办理。所以第一个申请按照官网上的邮寄过去了。 1月17号邮寄过去的,到2月底都没点音讯,打电话过去问才知道外地邮寄的会先放一个多月才开始受理。晕死...前后加起来近三个月。别的不多说,上图: [图片] 后面又申请了两个,用朋友的名义直接在粤港澳大厅办理的,当天去当天受理,中间还补正了一下,一个月搞定,时间线如下: [图片] 2.设计开发美工 实施过程就没什么可说的了,有问题就各种百度。微信小游戏已经出来一年多了,各式各样的小游戏都有了,作为一名程序猿,最坑爹的就是美工了,所以想在美工上创新基本不可能了,只能在玩法上进行微创新。下面列几个可能忽略的问题: 1)微信打包不能超过5M,Cocos的min.js在打包时只选择自己需要的模块,详细解决方案在下面: https://blog.csdn.net/haibo19981/article/details/80452364 2)微信域名校验时必须时https协议,我用的是阿里云服务器,上面有免费的证书可以申请使用: https://www.jianshu.com/p/638f364e0642?utm_source=oschina-app 3.上线 软著下来后,小游戏上线还需要一个自审自查报告,网上copy一份(需要的私我),还有苹果开发账号,随便填个自己的邮箱就好了。整个审核没有太大的问题,一天时间审核通过上线。 现在,最大的坑来了。 小游戏上线后第二天晚上,用户量暴增,当天有近4000用户访问小游戏。应该是点了申请公测按钮,但是状态一直在已申请,压根不清楚这个状态背后是几个意思。 但是第一次上线是指为了提前走一下小游戏的上线流程,上的只是一个体验版本,还有很多优化没有做完。很显然,这4000的用户最终成了过客。 [图片] 再过两天,因为自己还没有推广运营,用户量骤降。 所以在此提醒各位准备上线的小伙伴们,第一个上线版本也要重视!!!想想怎么尽可能留住这一波种子用户。 以上都是过程中让人记忆深刻不能再踩第二回的坑,与大家分享。 优化了两版之后的小游戏: [图片] 个人邮箱:646146599@qq.com
2019-05-08 - scroll-view怎么判断滚动停止
scroll-view怎么判断滚动停止? 当滚动开始时,悬浮按钮隐藏,停止时,按钮显示
2019-02-20 - 虚拟币能否兑换现金供用户提现
- 你好我们的小程序是一款答题的产品,希望用户在小程序中答题获得虚拟币,累计一定程度的虚拟币后可以在平台兑换成现金提现到微信零钱? 虚拟币用户是无法购买。 - 这样是否可以,请官方给予明示。感谢。
2018-05-28 - 免费抽奖小程序审核问题
我想开发一款类似于“抽奖助手”、“活动抽奖”这样的免费抽奖小程序,里面不包含创建抽奖的工具功能,仅提供面向公众的免费抽奖,由赞助商提供奖品,部分抽奖可能需要观看视频,用户在使用中能够获得积分,也能够获得积分,并可以通过积分兑换现金红包或实物奖品,以下问题需请教: 1、该类小程序应该选择什么服务类目? 2、是否被允许正常提审上线? 3、个人和企业都可以提审该类小程序吗? 4、微信小程序平台对该类小程序还有什么资质要求?
2019-04-13 - 买娃娃机游戏币算不算虚拟支付?
- 需求的场景描述(希望解决的问题) 我们想把H5 的在线买币系统转移至小程序。 买币环节算不算虚拟支付?小程序类目应该怎么选择?
2018-12-11 - 【求问】小程序使用满意度
今天进入一个小程序 发现在页面下方自动弹出一个使用满意度的打分窗口 如下图 给该小程序打分 1-5 分 同一个小程序 并不是每个人进去都会有弹窗 所以想问一下这个评分弹窗的触发条件是什么以及这个评分对后续小程序有哪些影响[图片]
2019-05-22 - 复制任意微信小程序页面路径
以下以微信小程序“虎牙直播”为例,演示如何复制微信小程序页面的路径。 1.进入小程序的“关于虎牙直播”页面 [图片] 2.点击右上角的“…”进入“更多资料”页面 [图片] [图片] [图片] 3.复制AppID:wx74767bf0b684f7d3 4.进入小程序后台输入appid并搜索,然后点下一步 [图片] 5.鼠标移动到“获取更多页面路径”,在弹出窗口输入当前登陆的小程序的任意开发者微信号,然后点击开启,出现顶部的“开启入口成功”就可以使用手机访问“虎牙直播”任意页面进行复制了 [图片] 6.某个直播间的页面路径:pages/main/liveRoom/index.html?anchorUid=1678113423&source=search[图片] PS:复制出来的页面路径在小程序里使用的时候记得删除 .html 才能正常访问。
2020-01-16 - 获取不重复的随机数算法
[图片] 参数count:获取随机数的个数 参数total:总共的个数 返回值:随机的数组 randomtimu: function (count,total){ var sequence = new Array(total) var output = new Array(count) [代码]for (let i = 0; i < total; i++) { sequence[i] = i } let end = total - 1 for (let i = 0; i < count; i++) { var num = Math.floor(Math.random() * (end + 1)) output[i] = sequence[num] sequence[num] = sequence[end] end-- } return output [代码] },
2019-05-18 - 云开发怎么接入腾讯AI的API
请问一下,我现在使用的是云开发。 想接入一个腾讯AI的API,但是找到的相关回答都不是采用云开发,或者是采用PHP等。 那么,用微信小程序怎么样去接入https://ai.qq.com/这些API呢?有没有demo或者代码片段提供参考,谢谢了。
2019-05-08 - 疑似基础库2.7.0的bug,小程序banner广告组件显示异常
- 当前 Bug 的表现(可附上截图) [图片] 如图:左图SDK到了2.7.0,右图SDK还是2.6.6 - 预期表现 [图片] SDK2.6.6下ad组件显示正常 - 复现路径 [图片] SDK2.7.0下ad组件显示异常 - 提供一个最简复现 Demo 基础库2.7.0开发者工具还没有吧……怎么复现demo…… 请官方人员给出回答,为什么在开发者工具未更新2.7.0的时候给部分手机推送了2.7.0?
2019-05-07 - 希望增加组件swiper可以控制指示点的位置
swiper组件确实挺实用的 但是有的时候 需要指示点 但是 指示点的位置没办法调整 希望能增加一个可以调整位置的属性 或者开发者可以自行调整
2019-04-09 - swiper 如何禁止用户手动滑动,只自动播放
swiper 仅用于幻灯片自动播放,但不允许用户手动滑动。有参数设定吗? 我在搜了一下网上用的: <swiper-item catchtouchmove="stopTouchMove"></swiper-item > 这个方法正常用起来感觉是可以的,但我发现一个小bug: 假设 A、B两页 用js代码滑动改变swiper的“current”,从A页滑动到B页的一瞬间(B页面出来十分之一前),手指反方向滑动回去,能将页面换会A页
2019-02-19 - 关于小程序的会员积分
用户在小程序内访问操作,根据业务产生会员积分,积分仅能通过指定操作行为获得,没有充值购买积分的行为。 积分可在实物商城购物时直接抵扣现金,这样的业务需求在类目完善的情况下,是否符合微信小程序的运营规范,烦请审核官回答,谢谢!
2019-01-14 - 早起签到奖励是否违规?
我们计划做一个早起签到打卡的小程序,当a邀请b,b邀请c以后互相绑定好友,当c早起签到以后,a、b也可以获取到一个积分奖励,请问这种模式违规吗?如果这种违规的话,只做a邀请b,ab互为好友,当b早起签到以后,a可以获取到一个积分奖励,这样违规吗?
2019-03-28 - 关于公众号小程序引流违规问题
你好,我们的公众号是一个早起,早睡可以免费打卡签到赚积分兑换商品的。我们想做一个小程序,公众号上面打卡后可以进入小程序去领取积分,请问这种方式违规吗?用户都是自愿的打卡。网上没有看到类似这种方式的,担心这个违规!可以帮我们解答一下吗?
2019-04-15 - 小程序服务类目:社交红包,增值电信业务许可证到底是哪个证?(这个证有两种)
[图片] 这个证有两种 一种叫ICP证,一种叫SP证(而且都叫增值电信业务经营许可证),微信审核的到底是需要哪个证呢?每个证办理都需要2-3个月,我们一个小公司万一办错证,代价承担不起啊! 谢谢回复!!!!!!!!!!
2019-04-17 - 小打卡|如何组件化拆分一个200+页面的小程序
大家好,我是小打卡的前端唐驰。刚才金轩正同学分享了基于原生小程序底层架构,在此基础上我为大家分享下如何拆分一个200+页面的小程序,主要通过以下几点来聊一聊小打卡在组件化路上的一些实践 1.背景 2.组件与方案 3.组件间通讯 4.基于组件我们做了哪些事 [图片] [图片] 1. 其实一开始小打卡是没有引入组件化的,因为微信最开始是不支持组件化的。当时js代码已经4k+行了,各种功能代码,有用的没有用的,不知道干什么的代码就躺在那里,一动不动。举个例子,一个头像点击跳转的逻辑搜索了下,遍布在各个页面。修改起来可想而知的胆战心惊。另一个原因就是当时由于业务功能直线上升,很快我们就遇到了代码包超包了。在微信还没有实现分包之前,我们就只能一个一个页面的去review剔除代码,效率极低。这也是促成我们决定寻求出路的原因之一。可是删代码删功能是不能解决问题,期间我们也考虑过h5的方式,跑了demo之后却发现h5方式的多次渲染, 与加载首页白屏,尽管有各种服务端渲染方案,但是我们一致觉得为了用户体验,放弃了。 [图片] 2. 对于小打卡来说,我们不能再任由项目裸奔了,需要一种开发方式来进行约束,主要是有几个诉求: 在之前的项目上,为了方便。功能与功能之间的耦合程度极其的高,各种为了使用方便而随意修改某一个方法。 1.降低页面上各个功能点的耦合程度 我们不希望同一个功能点同样的代码在页面肆意copy,这样带来了极高的维护成本。以至后面无法维护。并且功能的复用不希望是copy,前端与后端不同的是不仅是单单的逻辑复用、更有布局、样式等。 2.提供代码的可复用性、可维护性 对于一个程序员来说,如果你打开一个代码文件。映入眼帘的是密密麻麻的代码,行数达到好几千K行,我相信大家的第一反应是抗拒的,更别说去修改代码,天知道会改出什么问题。 3.降低单一文件的复杂度 4.如果是公共功能的化我们还希望它能够有自己的作用域,保持自己的独立性。 [图片] 3. 根据以上几点,我们用一个页面举例,如何去拆分一个页面,首先我们需要有以下几点认识: 决定一个页面如何组件化的前提是该页面的功能是否是有全局都需要的功能模块 功能模块是否需要与页面其他模块强耦合 单个功能模块逻辑是否过于复杂(占用代码空间过大)——>单纯是为了页面代码的可读性。 不是全拆成组件就是最好的,不能为了组件而组件化 [图片] 4. 说了这么多,其实我们应该首先应该了解下,组件的特性? 专一性(一个组件只干一件事情,或者某一类事情。)功能的高度内聚 比如说右侧的feed集上的头像、它是一个组件、就负责显示头像跟跳转,其他的事它都不参与 可配置(能够适应通过设置属性值的方式来输出不同的东西)输入影响部分输出 然后我们同时可以设置头像组件上的size属性来设置头像在不同页面下的大小样式 生命周期(组件可以在自身或者说所在页面的生命周期内可以做不同的事情)比如可以在组件生成的时候进行数的初始化、属性值的类型校验、组件销毁时并同时销毁定时器等其他任务 事件传递 (既然要让组件与页面保持独立性,那么组件与页面的通讯交互就得需要一个标准) 右侧的feed组件其实是一个组件集合、我们通过组合不同的组件然后就形成了feed组件。就跟搭积木一样、只需要引入组件就行了。特别方便。 [图片] 5. 说到组件,那么小程序早期的不支持自定义组件开发这就很让人头疼、同样的feed组件我们经历了几乎三个版本的大改动、从最开始的直接写在页面里,后台使用template方式、再到后来的自定义组件方式。所以我们的演进步骤就成了page->template->component, 这儿列了一个表格对比了下几种组件化方式的对比。 可以看到,include的方式其实是最鸡肋的,include的方式其实实际意义上我理解成更多的是代码的切割,并且还不能将(template、wxs)分出去、所以这种方式我们直接pass掉了, 而template的方式其实是我们曾经主力使用的方式、到现在我们也还在使用、相对于include来说,template有了独立的作用域、虽然css、跟js还是与页面共享的。但是已经可以做一些比较简单的事情了。 对于component来说,完完全全的组件,满足了组件的所有要求。 [图片] 6. 先说说template的方式吧,举个列子,这个是我们的使用template构建的头像组件。跟写页面的方式很像、同样是js、wxss、wxml组成。用名称来命名。但是由于微信当时没有很方的方式去引用这些文件,或者说没有一种方法可以打包供我们很方便的使用。但是比起之前直接copy代码的方式、这样通过引用的方式使用其实感觉已经好了很多了。 [图片] [图片] 7. 具体的使用方式我画了张图,对应组件内文件与页面文件的对应方式、这里对于js的引用其实我们是做了一些小动作, 我们在调用Page方法前做了一次page方法与组件方法的check,因为在page代码里我们不能保证所有的方法名不会跟组件内的方法名不会冲突,所以我们做了这个一个检查、 然后mix函数还做了另一个事情就是将page方法与组件方法合并。然后对于mix函数其实我们还可以做很多事情、、比如规范生命周期回调函数放在一个对象内,然后我们自己定义的方法放在另一个对象里,就跟vue一样。 But,在经历了一段template组件化的时间后,我们又觉得这个方式还是有点烂,为什么呢?在使用时仍然不能避免引入众多的文件、虽然我们对js文件做了处理,但是wxss的样式仍然会被污染、js与page仍然共享作用域。并不能成为一个真正的标准组件。好在后来,微信上了自定义组件的功能,接下来聊聊这个标准的微信自定义组件吧。 8. 微信提供了自定义组件的功能后我们也第一时间跟进了,相对于template这种方式来说,现在是真正的独立于页面存在。使用也比之前更为方便与简洁,右图是我们对component的一个项目目录划分。我们将component划分为了公共组件与页面组件、为什么会有页面组件, 1.是为了降低页面代码的复杂度 2.为了好看。 公共组件就不说了,一定是最基础、最通用的组件。 [图片] 9. 转向component方式后有一个问题逐渐便凸显出来了,由于组件的独立作用域,组件间的通讯就成了一个问题,接下来聊一聊组件的事件传递。微信最开始的时候提供了一种triggerEvent的方式,可是这样的方式似乎并不能满足我们某些场景下的需求。后来又提供了page下selectComponent方法来直接操作组件内部的属性与方法。然后还有就是基于我们自己的事件广播机制。这几种方式构成了小打卡现目前最主要的组件与page、组件与组件间的数据交互方式 [图片] 10. 先来说说triggerEvent模式,微信在自定义组件上可以自定义监听函数。我们在组件内将需要向外抛出的事件统一通过this.triggerEvent(‘invoke’,{handler:’fun’,data:{}})这个方法来执行。其中invoke对应了我们绑定在组件标签上的监听函数。而将需要外部执行的方法与数据通过数据的方式传给监听函数。而在page上面我通过统一的监听回调函数去自动执行需要执行的方法、这里的trigger与event都不要我们去手写在组件与page创建的时候底层就已经帮我们预置了,我们只需要关注业务开发就行。这是对于一部分需要page与组件交互的模式。而对于我们想直接操作组件方法而不需要反馈的模式就得使用selectComponent的模式 [图片] 11. 一个简单的列子:全局的toast组件。在需要弹出toast的时候我们想直接调用就行、不用在通过传值给组件、然后由组件来执行显示或隐藏。这类组件我们在组件目录里新增了一个lib的文件。在page里只需要引入这个lib文件然后就可以直接调用toast组件。lib主要是对this.selectCompent与执行逻辑的一个封装。 [图片] 12. 事件发布订阅模式:基于底层的eventBus。简化后我们用在了组件与组件之间的通讯上、特点是简单。 [图片] 13. 解决了组件间的通讯问题,可是对于公共组件的引用仍然让我们觉得麻烦与不畅快、所以我们构建了全局通用模版、它是干什么的呢。它提供给了一些基础的全局组件、比如自定义导航头、toast、loading等等。小打卡所有的页面都通过slot的方式插入到这个模版组件x-page下面。这样就解决了我们需要在每个页面引入公共组件的问题。另一个问题使用自定义导航栏的时定位起点会有状态栏下移动到屏幕左上方。会造成布局的错误。通过x-page可以很好解决这个问题而不用重新布局。并且通信问题也不用担心,都是由x-page组件作为中台来对内对外进行分发与执行。 [图片] [图片] [图片] 14. 通过以上小打卡的开发模式就基本形成。要做的事情还有很多,更多组件的玩儿法,对于现在或者将来我们正在做的。 是构建小打卡的组件与基础sdk的仓库。 拆分组件开发与业务开发。 通过npm包管理的方式来应对越来越多的小程序平台的开发。 或者通过形成小程序插件的方式供其他小伙伴使用。 [图片] [图片] 以上就是我今天分享的内容。谢谢。
2019-04-26 - 关于小程序激励式视频防刷的问题
应用场景描述: 我们公司有一款步数类小程序,计划通过激励式视频广告变现,即用户完整观看视频,奖励金币 问题描述: 我们下发金币奖励的机制是用户完整看完激励式视频广告后,由前端获取到微信的回调(视频播放成功)后,请求后端下发奖励,然后由后端发金币到用户账户。现在的问题是,在前后端通信的过程中,如何避免用户伪造请求?不知官方有提供建议方案没? 另外,若用户观看了一半视频广告后关闭,是否还需要重新检测视频广告加载是否成功的事件?也就是说中途关闭广告是否会导致视频加载事件由成功变为不成功,导致再继续观看广告时,无可看视频? 麻烦官方大神帮忙解答一下,十分感谢!
2019-04-19 - 小程序(非小游戏)激励视频曝光问题
请问 1 小程序激励视频当日不足1000次曝光的情况下有收益吗? 2 激励视频点击有收益吗? 请官网回答一下,因为刚开始做,有些迷茫!
2019-04-18 - 微信小程序下载Excel报表
需求: 微信小程序内,要增加,导出Excel报表的功能。 服务器生成excel报表,在微信小程序内,下载下来。并能够分享给 自己的微信好友~ 在微信小程序内,这个功能可以实现吗? 现在我的解决办法是:点击下载按钮,复制下载的链接,引导用户去,手机浏览器下载,(得到了文件,通过浏览器分享给微信好友) 各位大佬,有没有好的想法,或者建议。 我也用了 API 通过 wx.downloadFile() 下载 wx.saveFile() 保存文件 wx.getSavedFileList() 获取到缓存的文件列表 这也没法做,文件 的分享,求指点
2019-04-12 - 微信小程序开发-常见问题(一)
知晓程序员,一个专注于微信小程序开发的程序员~1、域名必须是HTTPS非HTTPS的域名不被微信小程序允许 2、input组件placeholder字体颜色写在placeholder-class里面的color并不生效,需要写在placeholder-style里面就可以了 3、wx.navigateTo无法跳转到带tabbar的页面带有tabbar的页面,必须使用wx.switchTab进行跳转 4、tabbar在切换时页面数据无法刷新tabbar的实现可能是显示和隐藏view,所以,不会一直调用page.onLoad()方法,可以尝试把代码逻辑写在page.onShow()里面 5、如何获取shareTickets获取shareTickets需要在app.onLaunch或者app.onShow里面才能获取到,而不是page.onShow,请一定要注意。 注:建议在app.onShow里面去获取,app.onLaunch不是一直会执行 6、getPhoneNumber获取手机号目前该接口针对非个人开发者,且完成了认证的小程序开放。个人开发者是没办法调用这个API的 7、wx.previewImage图片预览预览的图片URL必须是HTTPS开头,不能是本地图片 8、wx.playVoice音频播放必须保证音频文件已经在本地,比如在wx.startRecord后,可以获取到filePath。或者提前调用wx.downloadFile来下载资源文件,然后再播放 9、API老版本兼容可以用wx.canIUse或者wx.getSystemInfoSync来进行判断,老版本给出相应提示即可 10、获取系统信息wx.getSystemInfo,可得到系统语言、屏幕宽高、微信版本号、操作系统、设备像素比、客户端甚础库版本等信息 11、如何去掉自定义button灰色的圆角边框主要是button的伪元素设置了样式,去掉即可: button::after{ display: none;} 12、回到页面顶部回到页面顶部,有两种方式: 1、使用scroll-view设置为纵向滚动,然后设置scroll-top值; 2、使用wx.pageScrollTo方法,此方法是1.4.0开始支持,所以要做低版本兼容; 13、input textarea是APP的原生组件,z-index层级最高有做过搜索框的同学,可能会遇到IOS下面,设置icon的z-index后,依然无法显示。建议做显示隐藏效果:点击之前是一个view,点击之后隐藏view,显示input~ 14、小程序如何冷启动小程序的机制,是在退出五分钟内进入,就会显示的是退出前的页面,如果你希望进入小程序都相当于冷启动的方式,直接进入主页面。你可以在page的onUnload里面里面set一个值,然后在app的onShow的时候判断这个值,然后决定是否跳到首页~ 15、一段文字如何换行小程序中唯一可以实现换行的标签组件是text 注:text中不支持<br>,只能使用\n进行换行 16、设置最外层标签的margin-bottom在IOS下不生效margin-bottom在安卓和开发工具里面都正常,就是在IOS下不起效,建议改成padding-bottom 17、小程序中canvas的图片不支持base64格式base64格式图片,在开发工具里面可以正常显示,真机上没有显示。建议修改成带https开头的url形式 待续。。。
2018-01-08 - 配置sitemap后,未配置的页面,是默认不会被检索吗?
1、配置sitemap后,未配置的页面,是默认不会被检索吗? 2、sitemap 检索这个功能,没有版本限制吗? 3、配置好了,是上线后才能检索吗?检索的条件是直接输入关键词吗?如果是,关键词可以自己配置吗? 望解答,谢谢~
2019-04-09 - 分享自媒体运营文章CMS类小程序(阅读分享领红包)
分享微信小程序代码 如果有运营意向的欢迎联系我 资讯文章类小程序 玩法:阅读、点赞文章获得积分(组队满员后当天任务积分翻倍),积分用于在积分商城兑换现金会员等礼品。 获得礼品需要添加微信客服为好友,发送兑换码完成。 通过完成任务获得积分,积分在商城兑换成卷,添加客服微信好友后凭卷密令提现。 日常活动: 签到 10积分 组队 满员后日常奖励翻倍 推荐新用户 10积分/1人(每日最多可推荐100个) 推荐的新用户每日签到 3积分/1人(每日最多获得300次) 日常任务: 阅读文章(要拉到底部) 10篇 每篇1积分,共可获得10积分 点赞文章 5篇 每篇2积分,共可获得10积分 预览地址: [图片] 使用了以下开源项目进行开发 wepy colorUI wemark 其它声明 本项目供个人学习、参考,商用需获得本人授权(限免)。 开源仓库地址: https://github.com/yizenghui/wecont
2020-06-13 - 小程序关联公众号策略调整
各位开发者,大家好。 目前,小程序需要与公众号关联,才可被使用在公众号自定义菜单、模板消息、客服消息等场景中。而公众号关联小程序时,需要小程序管理员确认,该环节增加了开发者之间的沟通成本。 为了降低公众号与小程序间的合作门槛,我们将调整小程序关联公众号策略如下: 公众号关联小程序将无需小程序管理员确认。 取消“小程序最多关联500个公众号”的限制。 若希望小程序在被关联时保留管理员确认环节,可前往“小程序管理后台-设置-基本设置-关联公众号设置”修改设置项。 公众号文章中可直接使用小程序素材,无需关联小程序。 开发者可在“小程序管理后台-设置-关联设置”中管理已关联的公众号。 微信团队 2019.04.04
2019-04-08 - 求官方指导:助力解锁、砍价,属于违规吗?能开发类似功能吗?
很多优质的小程序平台都有类似的功能,助力解锁,转发可减免价格,这样的功能到底是不是许可范围的? 我们小开发商到底能做还是不能做? [图片][图片][图片]
2018-05-29 - 小程序与微信订阅号问题
目前上架了一款小程序,小程序的使用用户会在小程序里提交一些表单等信息,但是我想在小程序里提交完成后单独给某个微信用户主动发送模版消息(此用户非小程序里提交信息的用户),想在订阅号里实现推送消息的功能(服务号应该是可以实现的),请问下,在订阅号里有没有更好的解决方案或者思路呢。不考虑更换成服务号,谢谢各位.
2019-03-27 - 小程序可以在页面弹一个广告吗
[图片] 想问 一下,这种在首页弹窗可以通过审核吗
2018-08-31 - 小程序下发小程序和公众号统一的服务消息
- 当前 Bug 的表现(可附上截图) 小程序下发公众号的服务消息,A用户触发请假申请,公众号消息 touser写的接收者B的openId,为什么还是A用户接受到了,B用户没有收到消息呢? - 预期表现 - 复现路径 - 提供一个最简复现 Demo 用户A触发: var access_token = res.data.access_token; var sendTemplateUrl = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=" + access_token; var sendTemplateData = { touser: '接收者B的openid', mp_template_msg:{ appid:'公众号appid', template_id: 'sK2egqZA7M7uPiMwtmgB0mtzHitn1U12gJlQJe8KwkY', url: "pages/mine/mybatchaudit/mybatchaudit", miniprogram: { appid: "小程序appid", pagepath: "pages/mine/mybatchaudit/mybatchaudit" }, data: { first: { "value": "您有一个待审批事项", "color": "#173177" }, keyword1: { value: '请假申请' }, keyword2: { value: e.detail.value.beginDate + "" + e.detail.value.beginTime }, keyword3: { value: e.detail.value.endDate + "" + e.detail.value.endTime }, keyword4: { value: e.detail.value.reason }, keyword5: { value: that.data.address[e.detail.value.address].address }, } } } function success(res) { console.log(res) } util.getPostRequest(sendTemplateUrl, sendTemplateData, success);
2018-11-08 - sendUniformMessage 接口推送消息不成功问题求解
其中一个同一主体的小程序公众号可以推送统一服务消息,另一个不行 情况一:该小程序的openid推送至公众号中,推送失败,备注:该小程序与公众号为同一主体,且已经关联 [图片] 错误内容: { "errcode": -1, "errmsg": "system error hint: [j1pFma01424125]" } 情况二:该小程序的openid推送至公众号中,可以推送成功,备注:小程序与公众号为同一主体,且已经关联 [图片] 两个公众号和小程序代码都是一样的,就是修改了一下openid,appid,template_id这三个参数,但是一个推送成功一个推送失败。 请问有遇到这种问题的吗 备注:我这里是直接用postman进行测试的,所以而且具体的配置两个公众号和小程序基本相同,而且出现的错误也没有一个具体的提示,所以请问该如何解决?
2018-10-23 - 关于模版消息限额问题
1、小程序·模版消息,即“服务通知”,订单待自提状态下触发。 (1)官方文档查到:【日调用限额100万次。】请问,超过100万次之后,用户是否不再能收到服务通知?能否通过付费提高限额? (2)官方文档查到:【当用户在小程序内完成过支付行为,可允许开发者向用户在7天内推送有限条数的模板消息(1次支付可下发3条,多次支付下发条数独立,互相不影响)。】请问,能否通过付费,提高1次支付可下发3条的限制? 2、微信公众号·模版消息,订单待自提状态下触发。官方文档查到: [代码]当前每个账号的模板消息的日调用上限为10万次,单个模板没有特殊限制。【2014年11月18日将接口调用频率从默认的日1万次提升为日10万次,可在MP登录后的开发者中心查看】。当账号粉丝数超过10W/100W/1000W时,模板消息的日调用上限会相应提升,以公众号MP后台开发者中心页面中标明的数字为准。[代码]请问,能否通过付费,提高限额? 如以上任何一条能够通过付费突破限制,麻烦留下付费服务的联系电话。感谢!
2019-01-17 - 小程序播放第三方网址
-小程序用web-view播放第三方网址 - 小程序内想要播放用户在优酷、腾讯上传的视频。但是小程序提示不支持打开非业务域名,而优酷这种域名是不能加入到我们的配置中的,请问有什么办法吗?
2019-01-03 - 视频播放资质问题
问题:视频播放是否需要文娱-视频 资质? 使用场景:我们是一款茶馆包厢点单的小程序,用户在点单时可以查看点单商品详情,在查看商品详情时,用户可以查看该商品视频介绍,该视频是我们的录播视频,从后台上传的。
2018-12-19 - 自定义导航栏所有机型的适配方案
写在前面的话 大家看到这个文章时一定会感觉这是在炒剩饭,社区中已经有那么多分享自定义导航适配的文章了,为什么我还要再写一个呢? 主要原因就是,社区中大部分的适配方案中给出的大小是不精确的,并不能完美适配各种场景。 社区中大部分文章给到的值是 iOS -> 44px , Android -> 48px 思路 正常来讲,iOS和Android下的胶囊按钮的位置以及大小都是相同且不变的,我们可以通过胶囊按钮的位置和大小再配合 wx.getSystemInfo 或者 wx.getSystemInfoSync 中得到的 [代码]statusBarHeight[代码] 来计算出导航栏的位置和大小。 小程序提供了一个获取菜单按钮(右上角胶囊按钮)的布局位置信息的API,可以通过这个API获取到胶囊按钮的位置信息,但是经过实际测试,这个接口目前存在BUG,得到的值经常是错误的(通过特殊手段可以偶尔拿到正确的值),这个接口目前是无法使用的,等待官方修复吧。 下面是我经过实际测试得到的准确数据: 真机和开发者工具模拟器上的胶囊按钮不一样 [代码]# iOS top 4px right 7px width 87px height 32px # Android top 8px right 10px width 95px height 32px # 开发者工具模拟器(iOS) top 6px right 10px width 87px height 32px # 开发者工具模拟器(Android) top 8px right 10px width 87px height 32px [代码] [代码]top[代码] 的值是从 [代码]statusBarHeight[代码] 作为原点开始计算的。 使用上面数据中胶囊按钮的高度加 [代码]top[代码] * 2 上再加上 [代码]statusBarHeight[代码] 的高度就可以得到整个导航栏的高度了。 为什么 [代码]top[代码] * 2 ?因为胶囊按钮是垂直居中在 title 那一栏中的,上下都要有边距。 扩展 通过胶囊按钮的 [代码]right[代码] 可以准确的算出自定义导航的 [代码]左边距[代码]。 通过胶囊按钮的 [代码]right[代码] + [代码]width[代码] 可以准确的算出自定义导航的 [代码]右边距[代码] 。 通过 wx.getSystemInfo 或者 wx.getSystemInfoSync 中得到的 [代码]windowWidth[代码] - 胶囊按钮的 [代码]right[代码] + [代码]width[代码] 可以准确的算出自定义导航的 [代码]width[代码] 。 再扩展 wx.getSystemInfo 或者 wx.getSystemInfoSync 中得到的 [代码]statusBarHeight[代码] 每个机型都不一样,刘海屏得到的数据也是准确的。 如果是自定义整个页面,iPhone X系列的刘海屏,底部要留 [代码]68px[代码] ,不要问我为什么! 代码片段 https://developers.weixin.qq.com/s/Q79g6kmo7w5J
2019-02-25 - 模板消息快不够用啦,咋办啊~~~~
模板消息快不够用啦,咋办啊~~~~ [图片]
2018-11-16 - setClipboardData getClipboard 复制emoji表情
微信提供的复制api复制以emoji表情为首的字符串的时候,只能复制到第一个emoji表情,之后的表情和文字都是不能复制到的。针对这个问题,我这里在复制以前加了一个零宽字符串编码 即'\u200b' + 你要复制的内容,\u200b 零宽字符用于处理表情开头只能复制第一个表情的问题,加上之后可以复制全部的emoji表情和字符串
2018-11-12 - 图片预览接口会触发onShow,极其不合理
- 当前 Bug 的表现(可附上截图) 图片预览接口会触发onShow - 预期表现 不会触发 - 复现路径 - 提供一个最简复现 Demo
2018-11-12 - 小程序 options.scene 应该怎么配置多参数
由于又十多个二维码扫码进入小程序的入口,需要自己组装一下参数,所以想问一下: 下图应该怎么配置呢?大概有五个左右 [图片] var scene = decodeURIComponent(options.scene) // debugger console.log('打印scene',scene); 2018-11-13 补图,可以通过scene取值的配置模式如下:然后具体的操作也都在后面卢霄霄的回复中了 [图片]
2018-11-13 - 如何动态循环获取view高度
[图片] 如何获取 每一个 .articleBox_school_title <view> 的高度呢 [图片] 这种方法只能获取一个
2018-11-08 - canvas生成的图片第一次保存会全部空白(透明?),第二次才正常。
canvas生成的图片第一次保存会全部空白(透明?),第二次才正常。求解啊!!急得不要不要的!! 代码: createPhoto: function() { let that = this; wx.showLoading({ title: '图片生成中', }) // 获取背景图片本地路径 let promise1 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: that.data.bgi, success: function(res) { console.log(111, res) resolve(res); }, fail: function(res) { reject(res) } }) }) // 获取顶部头像本地路径 let promise2 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: that.data.avatarUrl, success: function(res) { console.log(222, res) resolve(res); }, fail: function(res) { reject(res) } }) }) // 获取底部自己头像本地路径 let promise3 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: that.data.avatarUrl2, success: function(res) { console.log(333, res) resolve(res); }, fail: function(res) { reject(res) } }) }); // 获取底部第一个头像本地路径 let promise4 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: that.data.beforeAvatar, success: function(res) { console.log(444, res) resolve(res); }, fail: function(res) { reject(res) } }) }); // 获取底部第二个头像本地路径 let promise5 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: that.data.afterAvatar, success: function(res) { console.log(555, res) resolve(res); }, fail: function(res) { reject(res) } }) }); // 获取皇冠本地路径 let promise6 = new Promise(function (resolve, reject) { wx.getImageInfo({ src: "https://dreamate.top/2.png", success: function (res) { console.log(666, res) resolve(res); }, fail: function (res) { reject(res) } }) }); // 执行 Promise.all( [promise1, promise2, promise3, promise4, promise5, promise6] ).then(res => { console.log(res) // 获取宽高 let wW = that.data.windowWidth; let wH = that.data.windowHeight; // 定义画布上下文常量 const ctx = wx.createCanvasContext('firstCanvas'); //背景白色 ctx.setFillStyle('white'); //从x=0,y=0开始绘制白色 // ctx.fillRect(0, 0, wW, wH); if (that.data.ranking == 1) { console.log(111) //背景图 ctx.drawImage(res[0].path, 0, 0, wW, wH); //顶部头像 ctx.drawImage(res[1].path, 10, 5, 90, 85); //底部第一张头像 ctx.drawImage(res[2].path, 67, 560, 70, 70); //底部第二张头像 ctx.drawImage(res[3].path, 177, 560, 55, 55); //底部第三张头像 ctx.drawImage(res[4].path, 267, 560, 55, 55); //皇冠 ctx.drawImage(res[5].path, 90, 545, 30, 30); // 绘制文字 ctx.setFontSize(20) ctx.setFillStyle("#fff") ctx.fillText(that.data.userName, 100, 50) // 绘制文字 ctx.setFontSize(26) ctx.setFillStyle("#000") ctx.fillText(that.data.ranking, wW * 0.49, wW * 1.38) /*保存上下文 绘制 */ // ctx.save(); ctx.draw(); //destWidth值越大图片越清晰/大小成正比 解决画布模糊的问题 wx.canvasToTempFilePath({ canvasId: 'firstCanvas', width: wW, height: wH, destWidth: wW * 3, destHeight: wH * 3, success: function success(res) { console.log('转图片结果'); // 关闭loading wx.hideLoading(); wx.showLoading({ title: '图片保存中...', }) // 到page对象的data中 that.setData({ previewImageUrl: res.tempFilePath }) console.log(res) wx.saveImageToPhotosAlbum({ filePath: that.data.previewImageUrl, success(res) { wx.hideLoading(); wx.showToast({ title: '保存成功!', icon: 'success' }) //保存成功 console.log(res); }, fail: function (res) { wx.showToast({ icon: 'fail', title: 'sorry 保存失败,请稍后再试', }) return; } }) }, complete: function complete(e) { console.log(e.errMsg); } }); } }). catch(err => { //error 错误处理 }) },
2018-10-27 - 小程序发布后只对部分人开放应用怎么操作
小程序发布以后,所有人都可以搜索到,但是只针对部分人可以用这个应用要怎么控制呢
2018-10-24