- 违规整改后申诉驳回,没有再次申诉的入口了,求助?
审核员,您好!根据您的建议,我们整改了涉嫌通过中断用户体验、限制用户操作的方式版块,并调整了自身业务,修正了先登录后服务的方式。我司也非常注重用户隐私,在登录页完善了《用户注册协议》和《隐私协议》查阅的入口,并且增加用户是否同意授权的按钮。我司专为C端用户提供优惠券发放服务,为了更好的服务于C端用户,有需要获取用户手机号时,已按照最新用户自愿授权的方式进行询问。当前已及时做出整改调整,目前申诉被驳回,已关闭再次申诉入口,公司业务已经无法正常开展,手机号授权服务被关闭,C端用户无法登录获取服务,现申请恢复,非常感谢。 AppID(小程序ID):wx159f1f09126cdccf 原始ID:gh_059844ed111b
2022-09-28 - 一个正常小程序,被永久封闭广告、停止流量合作?
一、基本信息 名称:体能考核评定 APPID:wx6b0ce297f07a6517 二、经过 前两天毫无征兆的收到微信广告助手的通知: [图片] 然后进入小程序后台,提示如下: [图片] 小程序广告组件关闭原因: 1. 流量主通过违法违规等不正常手段获取流量,包括但不限于通过头像、名称、简介混淆正常搜索结果,影响用户体验,现停止流量合作,未结算收入仍可结算,如有疑问请联系微信广告助手公众号 我就觉得很莫名其妙,我的小程序的头像、名称、简介完全是正正常常的啊,怎么就违规了? 名称:体能考核评定 简介:以最新大纲为依据,提供体能考核成绩评定,以及考核标准查询的服务。 头像: [图片] 到底哪里违规了??而且这个小程序已经正常运营2年了,评分4.7,真不知道官方的判据是什么。。。 [图片] 然后我就向微信广告助手的人工客服申诉: 连续三天,得到的回复永远是一句话: [图片] [图片] [图片] [图片] [图片] 这到底是人工客服还是机器人,这么敷衍了事的吗? 三、结论 申诉至今未果,我只想表达三点: 1、微信官方在没有任何警告、提示的前提下,就永久关闭流量主的广告位,终止流量合作。这样的做法是否欠妥?是不是应该先警告、再整改、拒不整改再停止合作? 2、我自认为小程序没有任何违规的情况,就算有,我向人工客服申诉的时候,能不能指出到底哪里违规了?而不是永远的机械的恢复一句话? 3、据我所知只有微信广告助手公众号这一个渠道来进行申诉,而每次我申诉都无法得到“人”的回答,是否可以认为这是微信官方的一种不作为,真的就以独裁者的姿态自居了?你们就以这样的态度对待开发者的吗? 表述完了,我知道微信官方也不会解决这个问题的。
2022-09-28 - 小程序奇技淫巧之 -- 日志能力
日志与反馈 前端开发在进行某个问题定位的时候,日志是很重要的。因为机器兼容性问题、环境问题等,我们常常无法复现用户的一些bug。而微信官方也提供了较完整的日志能力,我们一起来看一下。 用户反馈 小程序官方提供了用户反馈携带日志的能力,大概流程是: 开发中日志打印,使用日志管理器实例 LogManager。 用户在使用过程中,可以在小程序的 profile 页面(【右上角胶囊】-【关于xxxx】),点击【投诉与反馈】-【功能异常】(旧版本还需要勾选上传日志),则可以上传日志。 在小程序管理后台,【管理】-【反馈管理】,就可以查看上传的日志(还包括了很详细的用户和机型版本等信息)。 这个入口可能对于用户来说过于深入(是的,官方也发现这个问题了,所以后面有了实时日志),我们小程序也可以通过[代码]button[代码]组件,设置[代码]openType[代码]为[代码]feedback[代码],然后用户点击按钮就可以直接拉起意见反馈页面了。利用这个能力,我们可以监听用户截屏的操作,然后弹出浮层引导用户主动进行反馈。 [代码]<view class="dialog" wx:if="{{isFeedbackShow}}"> <view>是否遇到问题?</view> <button open-type="feedback">点击反馈</button> </view> wx.onUserCaptureScreen(() => { // 设置弹窗出现 this.setData({isFeedbackShow: true}) }); [代码] LogManager 关于小程序的 LogManager,大概是非常实用又特别低调的一个能力了。它的使用方式其实和 console 很相似,提供了 log、info、debug、warn 等日志方式。 [代码]const logger = wx.getLogManager() logger.log({str: 'hello world'}, 'basic log', 100, [1, 2, 3]) logger.info({str: 'hello world'}, 'info log', 100, [1, 2, 3]) logger.debug({str: 'hello world'}, 'debug log', 100, [1, 2, 3]) logger.warn({str: 'hello world'}, 'warn log', 100, [1, 2, 3]) [代码] 打印的日志,从管理后台下载下来之后,也是很好懂: [代码]2019-6-25 22:11:6 [log] wx.setStorageSync api invoke 2019-6-25 22:11:6 [log] wx.setStorageSync return 2019-6-25 22:11:6 [log] wx.setStorageSync api invoke 2019-6-25 22:11:6 [log] wx.setStorageSync return 2019-6-25 22:11:6 [log] [v1.1.0] request begin 2019-6-25 22:11:6 [log] wx.request api invoke with seq 0 2019-6-25 22:11:6 [log] wx.request success callback with msg request:ok with seq 0 2019-6-25 22:11:6 [log] [v1.1.0] request done 2019-6-25 22:11:7 [log] wx.navigateTo api invoke 2019-6-25 22:11:7 [log] page packquery/pages/index/index onHide have been invoked 2019-6-25 22:11:7 [log] page packquery/pages/logs/logs onLoad have been invoked 2019-6-25 22:11:7 [log] [v1.1.0] logs | onShow | | [] 2019-6-25 22:11:7 [log] wx.setStorageSync api invoke 2019-6-25 22:11:7 [log] wx.setStorageSync return 2019-6-25 22:11:7 [log] wx.reportMonitor api invoke 2019-6-25 22:11:7 [log] page packquery/pages/logs/logs onShow have been invoked 2019-6-25 22:11:7 [log] wx.navigateTo success callback with msg navigateTo:ok [代码] LogManager 最多保存 5M 的日志内容,超过5M后,旧的日志内容会被删除。基础库默认会把 App、Page 的生命周期函数和 wx 命名空间下的函数调用写入日志,基础库的日志帮助我们定位具体哪些地方出了问题。 实时日志 小程序的 LogManager 有一个很大的痛点,就是必须依赖用户上报,入口又是右上角胶囊-【关于xxxx】-【投诉与反馈】-【功能异常】这么长的路径,甚至用户的反馈过程也会经常丢失日志,导致无法查问题。 为帮助小程序开发者快捷地排查小程序漏洞、定位问题,微信推出了实时日志功能。从基础库 2.7.1 开始,开发者可通过提供的接口打印日志,日志汇聚并实时上报到小程序后台。 使用方式如下: 使用 wx.getRealtimeLogManager 在代码⾥⾯打⽇志。 可从小程序管理后台【开发】-【运维中心】-【实时日志】进入日志查询页面,查看开发者打印的日志信息。 开发者可通过设置时间、微信号/OpenID、页面链接、FilterMsg内容(基础库2.7.3及以上支持setFilterMsg)等筛选条件查询指定用户的日志信息: [图片] 由于后台资源限制,实时日志使用规则如下: 为了定位问题方便,日志是按页面划分的,某一个页面,在onShow到onHide(切换到其它页面、右上角圆点退到后台)之间打的日志,会聚合成一条日志上报,并且在小程序管理后台上可以根据页面路径搜索出该条日志。 每个小程序账号每天限制500万条日志,日志会保留7天,建议遇到问题及时定位。 一条日志的上限是5KB,最多包含200次打印日志函数调用(info、warn、error调用都算),所以要谨慎打日志,避免在循环里面调用打日志接口,避免直接重写console.log的方式打日志。 意见反馈里面的日志,可根据OpenID搜索日志。 setFilterMsg 可以设置过滤的 Msg。这个接口的目的是提供某个场景的过滤能力,例如[代码]setFilterMsg('scene1')[代码],则在 MP 上可输入 scene1 查询得到该条日志。比如上线过程中,某个监控有问题,可以根据 FilterMsg 过滤这个场景下的具体的用户日志。FilterMsg 仅支持大小写字母。如果需要添加多个关键字,建议使用 addFilterMsg 替代 setFilterMsg。 日志开发技巧 既然官方提供了 LogManager 和实时日志,我们当然是两个都要用啦。 log.js 我们将所有日志的能力都封装在一起,暴露一个通用的接口给调用方使用: [代码]// log.js const VERSION = "0.0.1"; // 业务代码版本号,用户灰度过程中观察问题 const canIUseLogManage = wx.canIUse("getLogManager"); const logger = canIUseLogManage ? wx.getLogManager({level: 0}) : null; var realtimeLogger = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : null; /** * @param {string} file 所在文件名 * @param {...any} arg 参数 */ export function DEBUG(file, ...args) { console.debug(file, " | ", ...args); if (canIUseLogManage) { logger!.debug(`[${VERSION}]`, file, " | ", ...args); } realtimeLogger && realtimeLogger.info(`[${VERSION}]`, file, " | ", ...args); } /** * * @param {string} file 所在文件名 * @param {...any} arg 参数 */ export function RUN(file, ...args) { console.log(file, " | ", ...args); if (canIUseLogManage) { logger!.log(`[${VERSION}]`, file, " | ", ...args); } realtimeLogger && realtimeLogger.info(`[${VERSION}]`, file, " | ", ...args); } /** * * @param {string} file 所在文件名 * @param {...any} arg 参数 */ export function ERROR(file, ...args) { console.error(file, " | ", ...args); if (canIUseLogManage) { logger!.warn(`[${VERSION}]`, file, " | ", ...args); } if (realtimeLogger) { realtimeLogger.error(`[${VERSION}]`, file, " | ", ...args); // 判断是否支持设置模糊搜索 // 错误的信息可记录到 FilterMsg,方便搜索定位 if (realtimeLogger.addFilterMsg) { try { realtimeLogger.addFilterMsg( `[${VERSION}] ${file} ${JSON.stringify(args)}` ); } catch (e) { realtimeLogger.addFilterMsg(`[${VERSION}] ${file}`); } } } } // 方便将页面名字自动打印 export function getLogger(fileName: string) { return { DEBUG: function(...args) { DEBUG(fileName, ...args); }, RUN: function(...args) { RUN(fileName, ...args); }, ERROR: function(...args) { ERROR(fileName, ...args); } }; } [代码] 通过这样的方式,我们在一个页面中使用日志的时候,可以这么使用: [代码]import { getLogger } from "./log"; const PAGE_MANE = "page_name"; const logger = getLogger(PAGE_MANE); [代码] autolog-behavior 现在有了日志组件,我们需要在足够多的地方记录日志,才能在问题出现的时候及时进行定位。一般来说,我们需要在每个方法在被调用的时候都打印一个日志,所以这里封装了一个 autolog-behavior 的方式,每个页面(需要是 Component 方式)中只需要引入这个 behavior,就可以在每个方法调用的时候,打印日志: [代码]// autolog-behavior.js import * as Log from "../utils/log"; /** * 本 Behavior 会在小程序 methods 中每个方法调用前添加一个 Log 说明 * 需要在 Component 的 data 属性中添加 PAGE_NAME,用于描述当前页面 */ export default Behavior({ definitionFilter(defFields) { // 获取定义的方法 Object.keys(defFields.methods || {}).forEach(methodName => { const originMethod = defFields.methods![methodName]; // 遍历更新每个方法 defFields.methods![methodName] = function(ev, ...args) { if (ev && ev.target && ev.currentTarget && ev.currentTarget.dataset) { // 如果是事件类型,则只需要记录 dataset 数据 Log.RUN(defFields.data.PAGE_NAME, `${methodName} invoke, event dataset = `, ev.currentTarget.dataset, "params = ", ...args); } else { // 其他情况下,则都记录日志 Log.RUN( defFields.data.PAGE_NAME, `${methodName} invoke, params = `, ev, ...args); } // 触发原有的方法 originMethod.call(this, ev, ...args); }; }); } }); [代码] 我们能看到,日志打印依赖了页面中定义了一个[代码]PAGE_NAME[代码]的 data 数据,所以我们在使用的时候可以这么处理: [代码]import { getLogger } from "../../utils/log"; import autologBehavior from "../../behaviors/autolog-behavior"; const PAGE_NAME = "page_name"; const logger = getLogger(PAGE_NAME); Component({ behaviors: [autologBehavior], data: { PAGE_NAME, // 其他数据 }, methods: { // 定义的方法会在调用的时候自动打印日志 } }); [代码] 页面如何使用 Behavior 看看官方文档:事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用[代码]Component[代码]构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应[代码]json[代码]文件中包含[代码]usingComponents[代码]定义段。 完整的项目可以参考wxapp-typescript-demo。 参考 LogManager 实时日志 Component构造器 behaviors 结束语 使用自定义组件的方式来写页面,有特别多好用的技巧,behavior 就是其中一个比较重要的能力,大家可以发挥自己的想象力来实现很多奇妙的功能。
2019-12-10 - 如何使用scroll-view制作左右滚动导航条效果
最新:2020/06/13。修改为scroll-view与swiper联动效果,新增下拉刷新以及上拉加载效果。。具体效果查看代码片段,以下文章内容和就不改了 刚刚在社区里看到 有老哥在问如何做滚动的导航栏。这里简单给他写了个代码片段,需要的大哥拿去随便改改,先看效果图: [图片] 代码如下: wxml [代码]<scroll-view class="scroll-wrapper" scroll-x scroll-with-animation="true" scroll-into-view="item{{currentTab < 4 ? 0 : currentTab - 3}}" > <view class="navigate-item" id="item{{index}}" wx:for="{{taskList}}" wx:key="{{index}}" data-index="{{index}}" bindtap="handleClick"> <view class="names {{currentTab === index ? 'active' : ''}}">{{item.name}}</view> <view class="currtline {{currentTab === index ? 'active' : ''}}"></view> </view> </scroll-view> [代码] wxss [代码].scroll-wrapper { white-space: nowrap; -webkit-overflow-scrolling: touch; background: #FFF; height: 90rpx; padding: 0 32rpx; box-sizing: border-box; } ::-webkit-scrollbar { width: 0; height: 0; color: transparent; } .navigate-item { display: inline-block; text-align: center; height: 90rpx; line-height: 90rpx; margin: 0 16rpx; } .names { font-size: 28rpx; color: #3c3c3c; } .names.active { color: #00cc88; font-weight: bold; font-size: 34rpx; } .currtline { margin: -8rpx auto 0 auto; width: 100rpx; height: 8rpx; border-radius: 4rpx; } .currtline.active { background: #47CD88; transition: all .3s; } [代码] JS [代码]const app = getApp() Page({ data: { currentTab: 0, taskList: [{ name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, ] }, onLoad() { }, handleClick(e) { let currentTab = e.currentTarget.dataset.index this.setData({ currentTab }) }, }) [代码] 最后奉上代码片段: https://developers.weixin.qq.com/s/nkyp64mN7fim
2020-06-13 - 小程序一个页面触发防截屏,其他页面都不能截图了?
[图片] 对小程序A页面做防截屏处理,B页面可以截屏。 B页面截屏后,返回A页面,截屏弹出不能截屏提示。再到B页面,B页面也不能截屏了。 请问有什么处理办法吗
2022-04-19 - 如何突破一次只能获取20条记录的limit限制?只需要一行代码。
笔者刚遇到需要一次性拉取超过100条(云函数里超过1000条)记录的这种需求。 一般情况下,会有下面两种处理方式: 1、先获取总数,再for循环,每次拉取limit条记录;(可结合Promise.all并发) 2、递归拉取,每次拉取limit条记录,直到拉取的记录数量小于limit。 以上两种方式都比较麻烦,于是动了一脑筋,以最简单的方式实现上面的需求。 极简代码如下: db.collection('order').aggregate() .match({ status:'已付费' }) .addFields({ tempTag:1 //增加一个临时标签;也可以不要addFields这个阶段; }) .group({ _id:'$tempTag', orders:$.push('$$ROOT') //一次性拉取超过100条或者1000条记录 }) .end() .then(res=>{ let orders = res.list[0].orders console.log(orders) }) 一个临时标签,搞定。 小心数据量太大搞崩了,崩溃的极限是多少,需要各位自行摸索了。 需要注意的是,如果是云函数里执行以上代码(比如lookup),返回小程序端的数据量不要超过1M。
2021-03-15 - 请问这两个小程序如何解绑管理员?
无法登录这两个小程序,也不知道从哪里可以解绑。麻烦帮我处理解绑一下。 [图片]
2022-03-08 - 用户生成自己的公众号二维码分享名片有那些方式?
除了https://open.weixin.qq.com/qr/code?username= 这种,有没有别的方法 或者说,有没有什么办法,可以解决前端ajax请求微信接口(上边的接口)跨域的问题。
2022-01-24 - 公众号提取小程序跳转路径及注意事项
前言 今天有个朋友问我,想把自己游戏的微信游戏圈详情页放在公众号菜单里面。于是有了这篇教程。 步骤 找到小程序AppID 查看小程序详情 [图片] 复制AppID [图片] 复制小程序路径 找到插入小程序 [图片] 输入AppID,点击下一步 [图片] 点击「获取更多页面路径」 [图片] 输入自己的微信号,即可找到相关页面,点击右下角「复制小程序路径」 [图片] 注意事项 复制出来的小程序路径会自带「.html」,如果你是公众号菜单跳转就需要去掉.html,然后就大功告成了!
2021-01-17 - 可以注销公众号吗?
1、帐号注销入口 公众号设置->原始ID->注销帐号,点击后即可进入。 2、管理员手机端的注销帐号确认,不小心删除,是否支持补发? 不支持。目前管理员确认注销有效期为7天,且每天都会下发提醒,建议您留意下一次的消息提醒。 注:若已确认,将不会再下发消息提醒。 3、组织类型帐号小额打款有效期多长时间? 10天 4、帐号注销冻结期有多长时间? 冻结时长依据粉丝数而定。粉丝数≤1K,冻结7天;粉丝数大于1k≤1w,冻结15天;粉丝数大于1W≤10w,冻结30天;粉丝数10w以上,冻结60天。 5、帐号注销冻结期,是否可以提前? 不可以 6、注销确认,管理员手机端确认有效期多长时间? 7天 7、注销成功,关联小程序帐号如何展示? 若已发布微信小程序并展示为所属于本微信公众帐号的,也将不再展示该等帐号关系。 8、帐号注销后粉丝端是否会收到提示? 用户端会收到提示:公众号“XXX”已自主申请注销帐号 9、注销成功,粉丝端打开历史消息页面如何提示? 提示该账号已经自主注销
2020-03-18 - FreeUI使用手册,小程序开发神器
FreeUI,一款轻量、开箱即用的UI组件库,内置很多组件和样式,持续更新集成中。 废话不多说,扫码看看就知道了 [图片] 都是一些项目中常用的组件,感兴趣的可以扫码看一下,教程文档正在编写中,有使用中的问题可以联系我或者关注我的公众号【搞文艺的猿】。 喜欢小程序的朋友可以关注下,一起学习进步,交流心得。 文档地址:https://awzks1314.github.io/freeui-web/#/ [图片] [图片]
2021-06-03 - 便携小空调小程序(附源码)
今天看到大家都在转发便携小空调,搜了一下,原版是一个H5版本的,没有小程序版本的,就随手写了一个小程序版本的便携小空调。文末有源码链接,有需要的自取。 先附上正宗原版链接https://ac.yunyoujun.cn/(这个应该是原版吧) 再看下小程序版本的效果图 [图片] [图片] 这个小程序很简单,主要就是:空调开关、温度调节、模式设置、声音控制。 1.空调开关:控制声音、图片、温度的展示/隐藏 2.温度调节:空调可调节温度限制在16-31度之间,并切换数字 3.模式设置:制冷功能及icon、制热功能及icon 4.声音控制:声音的播放加载和循环播放 功能实现起来很简单,重点是素材处理起来比较麻烦,在这里再次感谢便携小空调的原作者。 便携小空调小程序源码:https://gitee.com/dashuaixiaomo/xiaokongtiao 如果觉得还不错,顺手点个👍呗
2021-05-11 - 无对公账号怎么注册、认证和注销公众号?
Q1 无对公账户如何注册公众平台?无对公账户的媒体或其它组织只支持微信认证的方式才可将公众号注册及认证成功。 您选择微信认证后,填写过程中请注意以下几个要点: 1)个体户:若个体工商户无对公账户,可填写营业执照上法人的个人银行卡号; 2)政府类型:在“机构开户银行”、“机构银行账号”栏填写“无”; 3)企业类型:务必提供对公账户,否则无法注册认证; 4)事业单位类型:使用结算中心或财政账户支付验证(如国库集中收付结算中心等)。 Q2 微信认证无对公账户如何填写?1)对公账户填写(个体工商户) 若个体工商户无对公账户,可填写营业执照上法人的个人银行卡号。 温馨提示:填写个人银行卡号时需与营业执照上法人保持一致。 2)对公账户填写(企业类型) 企业是必须要对公账户的,若无对公账户请先办理对公账户; 3)对公账户填写(政府类型) 若政府类型若没有对公账号,可以在“机构开户银行”、“机构银行账号”栏填写“无”。 4)对公账户填写(事业单位) 事业单位如无对公账户,可以使用证书上法人的对私银行卡号及姓名,也可以填写结算中心或财政账户支付验证(如国库集中收付结算中心等)。 5)对公账户填写(其他组织) Q3 无对公账户注销方法:政府无对公账户自主注销方法: https://kf.qq.com/faq/190531qyuuiY190531BjyyEv.html 机构改革后政府微信公众号无法注销?请参考https://kf.qq.com/faq/190531qyuuiY190531BjyyEv.html 因机构改革、单位合并、撤一建一等情况,导致机构主体名称有变更,提供以下材料申请注销: ① 红头文件(有鲜章); ② 变更情况说明书(加盖新主体公章); ③ 变更后新主体的主体证件; ④ 注销申请函(加盖新主体公章); 可提交上面的资料走政府无对公,资料多的建议拼图上传 无对公帐号或者主体已注销申请注销公众号方法(最常见的问题): https://kf.qq.com/faq/171018R3IVBF171018INjUvA.html 手机端打开下面的链接,填写证明材料进行注销: https://kf.qq.com/touch/sappfaq/180727nqAJre1807277F3Iji.html?scene_id=kf172&platform=15
2020-11-26 - 小程序的各种炫酷样式、炫酷动画
收集了上百种的小程序样式,拿来即用,源码公开 各种各样的样式都有,只有你想不到,没有做不到的 什么样的场景都有 源码公开,自行下载 可通过扫描二维码查看样式效果 ## 1、初衷就是收集,后续也会一直的收集并更新下去 *** ## 2、项目只在小程序上测试过,其他平台还需自行测试使用 *** ## 3、如果你有好的样式,可以发送到我的邮箱 1228742150@qq.com *** ## 4、部分样式是从别的地方拿过来的,我也有注明出处,如果你发布到其他的地方,也请尊重别人的劳动成果,注明出处。 *** ## 5、如有任何冒犯的地方,可联系作者 [图片]
2021-07-24 - 云开发实战:实现短信跳小程序
先看效果 [视频] 小程序支持短信跳转小程序了,可以说是打开了一个巨大的流量入口。 效果过程分析 从短信到网页从网页到小程序那么就涉及到两个点 发送短信网页跳转实现步骤分析 先要有个网页,可以跳转到小程序然后发送短信,短信内容包含地址具体实现步骤 1. 先要有个网页,可以跳转到小程序 首先开通静态网页托管 [图片] 创建一个云开发的项目,点击左上方「云开发」按钮 [图片] 点击静态网页进行开通。 然后点击「下载资源包」,解压缩我们会看到 [图片] 第一个是云函数,第二个是跳转的网页。首先我们编辑下跳转的网页 [图片] 打开文件编辑以下 6 处即可(通过“replace”搜索可以快速定位修改的地方): [图片][图片]添加好对应参数后,上传部署到你的静态托管文件目录中 [图片] 这个时候网页这块就已经搞定了,接下来部署下云函数。 刚才的 cloudfunctions 文件夹下面有个 public 文件夹里面的 index.js 复制内容到自己新建的云函数的 index.js 中,然后替换自己小程序的path(友情提示:覆盖完成后别忘记上传部署云函数)[图片]这个云函数的作用,主要是静态网页会调用它生成跳转的URL Scheme。以下为网页调用这个函数的代码区域[图片] 到这里网页显示与网页跳转就只差最后一步了,设置云函数权限。 [图片] [图片] 自定义安全规则配置: { "*": { "invoke": "auth != null" }, "public": { "invoke": true } } 2. 然后发送短信,短信内容包含地址 创建一个sendSms到云函数,复制以下代码: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event, context) => { try { const result = await cloud.openapi.cloudbase.sendSms({ env: 'online-12345678910', // 替换环境ID content: '云开发支持短信跳转小程序了',// 替换短信文案 path: '/index.html',// 替换网页路径 phoneNumberList: [ "+8612345678910" ] }) return result } catch (err) { return err } } 替换以上 3 处内容即可。 环境ID,可以在设置中找到短信内容,这个自己自定义网页路径,在静态网页托管中点击上传到网页即可查看复制[图片] 修改完成后,部署即可。 大功告成 [视频] 小程序就可以调用这个云函数发送短信,短信就会自带网页地址,点击即可跳转到小程序了。
2021-01-08 - 微信小程序答题页——swiper渲染优化及swiper分页实现
前言 swiper的加载太多问题,网上资料好像没有一个特别明确的,就拿这个答题页,来讲讲我的解决方案 这里实现了如下功能和细节: 保证swiper-item的数量固定,加载大量数据时,大大优化渲染效率记录上次的位置,页面初次加载不一定非得是第一页,可以是任何页答题卡选择某一index回来以后的数据替换,并去掉swiper切换动画,提升交互体验示例动图 [图片] 截图 [图片] [图片] 问题原因 当swiper-item数量很多的时候,会出现性能问题 我实现了一个答题小程序,在一次性加载100个swipe-item的时候,低端手机页面渲染时间达到了2000多ms 也就是说在进入答题页的时候,会卡顿2秒多去加载这100个swiper-item 思考问题 那我们能不能让他先加载一部分,然后滑动以后再去改变item的数据,让swiper一直保持一定量的swiper-item? 注意到官方文档有这么两个属性可以利用,我们可以开启衔接滑动,然后再bindchange方法中去修改data [图片] 1、保证swiper-item的数量固定,加载大量数据时,优化渲染效率 假设我们请求到的数据的为list,实际渲染的数据为swiperList 我们现在给他就固定3个swiper-item,前后滑动的时候去替换数据 正向滑动的时候去替换滑动后的下一页数据,反向滑动的时候去替换滑动后的上一页数据 当我们知道了要替换的条件,我们便可以去替换数据了 但是我们应该考虑到临界值的问题,如果当前页是list第一项和最后一项该怎么办,向左向右滑是不是得禁止啊 这边是判断没数据会让它再弹回去 2、记录上次的位置,页面初次加载不一定非得是第一页,可以是任何页 有很多时候,我们是从某一项直接进来的,比如说上次答题答到了第五题,我这次进来要直接做第六题 那么我们需要去初始化这个swiperList,让它当前页、上一页、下一页都有数据 3、答题卡选择某一index回来以后的数据替换,并去掉swiper切换动画,提升交互体验 从答题卡选择index,那就不仅仅是滑动上下页了,它可以跳转到任何页,所以也采用类似初始化swiperList的方法 swiper切换动画我这边是默认250ms,但是发现有时候从答题卡点击回来,你在答题卡点击的下一项不知道会从左还是从右滑过来 体验真的很差,一开始不知道怎么禁掉动画,其实在跳转到答题卡页的时候把duration设为0就可以了 然后在答题卡页的unload方法中恢复 关键点: 在固定3个swiper-item的同时,要保证我们可以有办法来替代微信自带swiper的current属性和change方法 swiper-limited-load使用方法及说明: 将components中的swiper-limited-load复制到您的项目中在需要的页面引用此组件,并且创建自己的自定义组件item-view在初始化数据时,为你的list的每一项指定index属性具体可以参照项目目录start-swiper-limited-load中的用法说明:其它属性和swiper无异,你们可以自己单独添加你们需要的属性总结 一开始很头疼,为什么微信小程序提供的这个swiper,没去考虑这方面 然后在网上和社区找也没有一个特别好的解决方案。 后来想想,遇到需求就静下来解决吧。 项目地址:https://github.com/pengboboer/swiper-limited-load 如果错误,欢迎指出。 如有新的需求也可以提出来,如果有时间的话,我会帮你们完善。 如果能帮到你们,记得给一个star,谢谢。 ---补充 有很多朋友在评论区提到了分页的需求,抽时间写了一个分页的Demo和大家分享一下。 还是以答题为例,比如我们一共有500条数据,一页20条,可能需要如下功能,乍一看不就加了个分页,挺简单的,其实实现起来挺麻烦的,下面说一下思路和一些需要特别注意的点: 1、从其他页面跳转到答题页时,不光只能默认在第一题,可以是任意一题,比如第80题。 跳转到任意一题,那么需要我们根据index算出该数据在第几页,然后需要请求该页数据,最后显示对应的index。我的思路更注重用户体验,不可能是上滑或者下滑才开始去请求数据,一定是要用户滑动前提前请求好数据。所以起码要保证左右两侧在初始化那一刻都有数据。如果此题和它的上一题下一题都在同一页,那么我们只需要请求一页数据(第15题,那么只需请求第1页数据)。如果此题和它的上一题或者下一题不在同一页,那么我们可能需要请求两页数据。(第20题,那么需要请求第1页和第2页数据) 2、左滑、右滑没数据时,都可以加载新数据。直到滑到第一题或者最后一题。 如果我们初始化时是第24题,那么我们左滑到第21题时,就应该去请求第一页的数据。那么用户在看完21题时,再滑到20题,可能就根本不会感知到通过网络请求了数据。但是如果用户此刻滑动特别快:滑到21题时请求了网络,请求还没成功,就又向左滑了。那么我们需要限制用户的滑动,给用户一个提示:数据正在加载中。 3、从答题卡点击任意一题可以跳转到相应的题目,并且左右滑动显示正常数据 比如我们初始化是跳转到了第80题,不一会点击答题卡又要跳转到200题,一会又跳转到150题。各种无序操作,你也不知道用户要往哪里点。 一开始是想着维护一个主list,点到哪道题往list中添加这道题所在的当页的数据,但是还得判断这一页或者左滑右滑请求新一页的数据得往list的哪个位置添加。这来回来去乱七八糟的判断就很麻烦了,很容易出bug。而且list长度太长了以后insert的性能也不好。 后来就去想,要不答题卡点击任意一题都清空旧的list,然后请求新的数据,左右滑动没数据了再请求新的数据呗。但是这样很浪费资源,并且用户体验也不好,用户已经从第1题答到第200题了,这时用户从答题卡选择了一个25题,还得重新请求网络。而且200道题的数据都没了,那再选个26题,再重新请求网络?网络有延时不说,还浪费资源。 最后转念一想,这时候就需要弄一个缓存了。所以最终的解决方法就出来了:我们维护一个map,在网络请求成功后,在map中保存对应页的数据,同时我们维护一个主list来显示对应的题目。当我们在答题卡选择某一题目,就清空list,然后判断map中有没有该页的数据,如果有就直接拿来,没有就再去网络请求。这个处理方式,写法相对来说简单,不需要乱七八糟的判断,也不浪费资源,用户体验也很不错。 总结 以上就是一些思路和要注意的地方。这个Demo断断续续花了好几天时间写出来的。可能我说的比较啰嗦比较细,只是想让需要用到这个分页Demo的同学能理解我是如何实现的。 如果觉得能帮到你,记得给一个star,谢谢。同时如果这个demo有bug或者你们有新想法,欢迎提出来。
2021-01-07 - 【集合】花了 3 个月,写了 40 篇小程序文章
前言 花了3个月,一共输出 40 篇文章,这也算是一个阶段性的总结。在此做个文章分类集合,希望对大家有所帮助。 小程序前端 《专治按钮效果不明显(扩散动画效果)》 《小程序开发必备,这 5 款超实用开源插件!》 《仿抽奖助手奖品详情页面向上翻页效果》 《推荐 5 款高仿知名应用的开源项目!》 《生成海报很复杂?有它轻松搞定!》 《推荐一个自定义导航栏开源库》 《前端开发,必备的学习网站!》 《情侣券-领取动画分析》 《通过玩游戏来学习CSS》 《CSS不规范导致的布局显示问题》 《微信小程序如何引入npm包?》 《情侣券-选中卡片翻转动画》 《CSS:实现卡片洗牌效果》 《情侣券 v2.0 使用的 4 款开源组件》 小程序云开发 《使用聚合函数实现打卡排行榜》 《使用云开发做内容安全检查》 《云开发-实现分页功能》 《云开发-实现维护用户表》 《云开发-实现模糊搜索》 《云开发实战:实现订阅消息推送》 《如何优雅的调用云函数?》 《云开发实战-如何维护用户表?(优化版)》 《推荐 10 款使用云开发的开源项目》 《云开发:CloudBase CMS 实战使用指南》 小程序产品 《如何利用小程序提高10倍活动效果?》 《实战:让数据说话之自定义埋点分析》 《#小程序云开发挑战赛#-情侣券》 《小程序运营必备的 3 款官方小程序》 《小程序云开发挑战赛:情侣券 v1.1 版本迭代》 《云开发挑战赛复赛:情侣券介绍PPT》 《参加#小程序云开发挑战赛#复赛收获》 《云开发挑战赛决赛:情侣券介绍PPT》 通用知识 《如何重构?》 《如何高效学习?》 《如何看懂时序图?》 《为什么优秀的程序员都写博客?》 《我从 Android 转到 微信小程序 的思考》 最后 后续计划会写更多云开发相关的文章以及小程序基础系列学习文章。
2020-11-24 - 开发工具在 macOS Big Sur经常闪退?!
[图片]\ [图片]
2020-11-11 - 小程序云函数和云数据库中的时区必坑笔记
云函数 云函数中默认的时区是UTC +0 参考:注意事项 & FAQ 然而里面有个错误,导致我调试了好久才发现问题 设置云函数时区的两种方式: 在控制台设置: 环境变量 TZ=Asia/Shanghai *注意:TZ大小写敏感,官方文档里写的是错误的! 在代码中设置 process.env.TZ=“Asia/Shanghai” *注意:TZ大小写敏感 云数据库 聚合指令$.dateToString如果不指定时区,默认是UTC +0。所以使用这个指令格式化日期字符串时一定要加上时区属性。 参考:MongoDB参考手册 [代码]$.dateToString({ date: '$closeBookingTime', format: '%Y-%m-%d %H:%M', timezone: 'Asia/Shanghai', }), [代码]
2020-07-16 - 熟练使用微信开发工具的代码块功能提升编码效率,降低误码率
1. 写在前面 自从上次介绍了宇宙第一强集成IDE的微信开发者工具安装插件等隐藏功能后,很多开发者社区哥们老铁感兴趣,一些哥们还私我问我是否支持代码块功能。答案是:支持!而且很完美的支持(此处必须有感恩,感恩微信团队每天的辛苦开发,作为同开发过小编辑器的人知道其中的苦与乐,同时呼吁各位猿少些喷喷喷的负能量,多些宣传和赞美的正能量!)。代码块是小编非常非常常用的一个功能,基本从04年开始建站和开发过程用过的所有IDE中都必备的功能。熟练使用微信开发工具的代码块功能提升编码效率,降低高频代码段的误写率。 2. 什么是代码块 可能有些刚接触开发的哥们不了解什么是「代码块」,这里我仅仅说说我个人对「代码块」功能的理解:代码块,英文名字:Code snippets,代码块的作用就是把比较长的一段代码在编码时只需要输入简写后的几个字母,比如把高频的代码console.log();简写成「clg」,再比如把小程序的页面.js里面的onLoad(),onShow等常用代码简写成「page_init」几个字母,最早提出这个概念和想法的人我也不知道是谁,总之得感谢他。如果说现在很多的编程语言有「语法糖」的说法,那么我个人觉得「代码块」可以称为「代码糖/编码糖」!熟练使用编码糖一定能让你尝到编码中的甜味。他的优点非常明显:极速,0误差输入高频代码段。个人觉得无论你现在处在编码的哪个级别都应该熟练使用Code snippets来提升工作效率。 3. 如何使用 3.1 打开开发者工具的编辑器扩展目录 [图片] 3.2 创建相关文件夹 返回上一级目录到User目下(里面有Workspaces文件夹),创建/进入snippets,此目录mac下完整路径应为:"~/Library/Application Support/微信开发者工具/【当前开发者工具特征码】/Default/Editor/User/snippets" [图片] 3.3 新建/编辑代码块json文件(如上图) [图片] 格式如上面,1,2,3是我比较常用的代码糖,生效后输入图1里面的clg回车就是console.log()(可以用这个来检测你代码糖功能是否生效);并且光标自动定位到()里,如下图: [图片][图片] 3.4 附带自用all.code-snippets 下面附上我常用的粗陋的代码糖块文件all.code-snippets(可以直接复制使用,你可以自行自己添加删除。里面有自己写的java框架常用的代码块,php常用和html常用以及nginx比较常用的,个人习惯不一样建议全部干掉重来,只需按里面的格式来写就可以了,格式如下: 字段名 意义 备注 prefix 简写后的字符串 必填 body 原字符串,可以用转义符 必填 description 备注 可控 [代码]{ // Example: // "Print to console": { // "scope": "javascript,typescript", // "prefix": "log", // "body": [ // "console.log('$0');", // "$2" // ], // "description": "Log output to console" // } //java begin "valueOf": { "prefix": "val", "body": "valueOf($0)", }, "parseInt": { "prefix": "par", "body": "Integer.parseInt($0);", }, "parseLong": { "prefix": "parl", "body": "Long.parseLong($0);", }, "equ": { "prefix": "equ", "body": "equals(\"$0\")", }, "isnull": { "prefix": "isnull", "body": "Tools.myIsNull($0)", }, "setattr": { "prefix": "setattr", "body": "request.setAttribute(\"$0\",$2)", }, "getattr": { "prefix": "getattr", "body": "request.getAttribute(\"$0\")", }, "getparam": { "prefix": "getparam", "body": "request.getParameter(\"$0\")", }, "getpost": { "prefix": "getpost", "body": "TtMap postUrl = Tools.getUrlParam();\r\nTtMap post = Tools.getpostmap(request, true);// 过滤参数,过滤mysql的注入,url参数注入\r\n" }, "tcf": { "prefix": "tcf", "body": "try{\r\n}catch(Exception e){\r\n\tTools.logError(e.getMessage());\r\n}finally{\r\n\t$0.closeConn();\r\n}", }, "777": { "prefix": "777", "body": "Runtime.getRuntime().exec(\"chmod 777 -R \" + strFileFullPath);" }, "go-1": { "prefix": "go-1", "body": "javascript:history.go(-1);" }, "dbctrl": { "prefix": "!dbctrl", "body": "DbCtrl dbCtrl = new DbCtrl(\"$0\");\r\ntry{\r\n\tTtList list = dbCtrl.lists(\"\", \"\");\r\n\tTtMap info = dbCtrl.info($2);\r\n}catch(Exception e){\r\n\tTools.logError(e.getMessage());\r\n\tif (Config.DEBUGMODE) {\r\n\t\te.printStackTrace();\r\n\t}\r\n}finally{\r\n\tdbCtrl.closeConn();\r\n}", "description": "TT-DbCtrl" }, "dbtools": { "prefix": "dbtools", "body": "DbTools dbTools = new DbTools();\r\ntry{\r\n\tTtList list = dbTools.reclist(\"$0\");\r\n\tTtMap info = dbTools.recinfo(\"$2\");\r\n}catch(Exception e){\r\n\tTools.logError(e.getMessage());\r\n}finally{\r\n\tdbTools.closeConn();\r\n}" }, "!recinfo": { "prefix": "rrecinfo", "body": "Tools.recinfo(\"$0\")", "description": "TT-DbTools-recinfo" }, "!reclist": { "prefix": "rreclist", "body": "Tools.reclist(\"$0\")", "description": "TT-DbTools-reclist" }, "ss": { "prefix": "ss", "body": "String[] sS = new String[]{}; ", "description": "new String[]" }, "mss": { "prefix": "mss", "body": "TtMap info = new HashMap<>();", "description": "new Map<String,String>" }, "lss": { "prefix": "lss", "body": "TtList lmss = new ArrayList<>();", "description": "new TtList " }, "<%": { "prefix": "!s", "body": "<%\r\n\t$0\r\n%>", "description": "TT-JSP<%自动完成" }, "<%=": { "prefix": "!ss", "body": "<%=$0%>", "description": "TT-JSP<%=自动完成" }, "jsp_include": { "prefix": "!sss", "body": "<jsp:include page=\"<%=$0%>\">\r\n\t<jsp:param name=\"$3\" value=\"$2\"/>\r\n</jsp:include>" }, "jsp_include2": { "prefix": "<jsp", "body": "<jsp:include page=\"<%=$0%>\">\r\n\t<jsp:param name=\"$3\" value=\"$2\"/>\r\n</jsp:include>" }, "!jsp_requery": { "prefix": "!req", "body": "request.getParameter(\"$0\")", }, "!jsp_requery_getAttribute": { "prefix": "!req2", "body": "request.getAttribute(\"$0\")", }, "!": { "prefix": "!ssss", "body": "<%@page import=\"com.tt.tool\"%>\r\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\"%>\r\n<%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\"%>\r\n<%@ taglib prefix=\"fmt\" uri=\"http://java.sun.com/jsp/jstl/fmt\"%>\r\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/functions\" prefix=\"fn\"%>\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\r\n <title>$0</title>\r\n</head>\r\n<body>\r\n\t$2\r\n</body>\r\n</html>", "description": "jsp-header" }, "div": { "prefix": "<div", "body": "<div id=\"$0\" name=\"$0\">\r\n\t\r\n</div>", }, "p": { "prefix": "<p", "body": "<p id=\"\" name=\"\"></p>", }, "a": { "prefix": "<a", "body": "<a href=\"$0\" id=\"\" name=\"\">$2</a>", }, "head": { "prefix": "<head", "body": "<head>\r\n\t$0\r\n</head>", }, "img": { "prefix": "<img", "body": "<img id=\"\" name=\"\" src=\"$0\">" }, "ul": { "prefix": "<ul", "body": "<ul id=\"\" name=\"\">\r\n\t\r\n</ul>" }, "li": { "prefix": "<li", "body": "<li id=\"\" name=\"\">\r\n\t\r\n</li>" }, "select": { "prefix": "<select", "body": "<select id=\"\" name=\"\">\r\n\t\r\n</select>" }, "option": { "prefix": "<option>", "body": "<option value=\"\">$0</option>" }, "input": { "prefix": "<input", "body": "<input type=\"$0\" id=\"$2\" name=\"$2\" value=\"$3\">" }, "form": { "prefix": "<form", "body": "<form id=\"info_form\" action=\"$${posturl}\" class=\"form-horizontal\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$2\r\n</form>" }, "!i": { "prefix": "!i", "body": "<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\"%>\r\n<%@ page import=\"com.tt.tool.Tools\" %>\r\n<%@ page import=\"com.tt.data.TtMap\" %>\r\n<%@ page import=\"com.tt.data.TtList\" %>\r\n<%@ page import=\"com.tt.tool.JspTools\" %>\r\n<%@ page import=\"java.util.*\" %>\r\n<%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\"%>\r\n<%@ taglib prefix=\"fmt\" uri=\"http://java.sun.com/jsp/jstl/fmt\"%>\r\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/functions\" prefix=\"fn\"%>\r\n<%@ taglib prefix=\"Tools\" uri=\"/tld/manager\" %>\r\n", }, "loginfo": { "prefix": "loginfo", "body": "Tools.logInfo($0)", "description": "loginfo" }, "logerror": { "prefix": "logerror", "body": "Tools.logError($0)", "description": "loginfo" }, //javascript "!go-1": { "prefix": "!go-1", "body": "javascript:history.go(-1);", }, "!tag": { "prefix": "!tag", "body": "<%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\"%>\r\n<%@ taglib prefix=\"fmt\" uri=\"http://java.sun.com/jsp/jstl/fmt\"%>\r\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/functions\" prefix=\"fn\"%>\r\n<%@ taglib prefix=\"Tools\" uri=\"/tld/manager\" %>", }, "$$": { "prefix": "$$", "body": "$(function(){$0\r\n});", "description": "TT-jquery自动运行" }, "$(": { "prefix": "$f", "body": [ "$(function(){", "});" ], "description": "TT-jquery自动运行" }, "timer": { "prefix": "timer", "body": "inv = setInterval(\"$0showtimes();\",1000);", "description": "TT-JS每隔几秒自动运行", }, "!posturl": { "prefix": "!posturl", "body": "<%=Tools.urlKill(\"id$0\")%>", }, "!ifdebug": { "prefix": "ifd", "body": [ "if (Config.DEBUGMODE) {", "\te.printStackTrace();", "}" ], }, "reload":{ "prefix": "reload", "body": "location.reload(true);" }, //PHP "<?php": { "prefix": "php", "body": "<?php $0 ?>", "description": "Global-PHP插入标签" }, "nav":{ "prefix": "nav", "body": "<navigator url=\"car_yy/car_yy_ppuser/car_yy_ppuser\">", "description": "老子的微信小程序的跳转", }, "bind":{ "prefix": "bind", "body": "bindtap=\"$1\"", "description": "老子的bindtap" }, "wx_ra":{ "prefix": "wepy_ra", "body":"let result = await util.wxRequest(httpSet);", "description": "微信小程序阻塞等待请求" }, "wx_r":{ "prefix": "wepy_r", "body":["util.wxRequest(httpSet).then(function(result){\r\n\tif(result.result==\"success\"){$1\r\n\t}\r\n});"], "description": "微信小程序阻塞等待请求" }, "httpset":{ "prefix": "wepy_hs", "body": "let params= {};\r\nlet httpSet={\r\n\turl:'$1',\r\n\tparams:params,\r\n\tmethod:'GET',\r\n\tisShowLoading: false,\r\n}", "description": "httpSet" }, "wepy_global":{ "prefix": "wepy_global", "body": "wepy.$$instance.globalData.$1", }, "wepy_castfail":{ "prefix": "wepy_castfail", "body": "this.$$broadcast('alertHeaderWarning', '$1');" }, "wepy_castOK":{ "prefix": "wepy_castok", "body": "this.$$broadcast('alertHeader', '$1');" }, "wepy_castfail_height":{ "prefix": "wepy_castfailh", "body": "this.$$broadcast('alertHeaderWarningHeight', '$1',60);" }, "wepy_castOK_height":{ "prefix": "wepy_castokh", "body": "this.$$broadcast('alertHeaderHeight', '$1',60);" }, "clg": { "prefix": "clg", "body": "console.log($0);" }, "wepy_tt":{ "prefix": "wepy_tt", "body": "let that = this;" }, "wepy_currtarget":{ "prefix": "wepy_currt", "body": " e.currentTarget.dataset.$1", }, "wepy_gourl":{ "prefix":"wepy_gourl", "body":"@tap=\"goUrl\" data-url=\"$1\" data-memberflag=\"$2\"", }, "wepy_goback":{ "prefix": "wepy_goback", "body":"wepy.navigateBack();" }, "wepy_urlencode":{ "prefix": "wepy_urlencode", "body": "encodeURIComponent($1);" }, "wepy_urldecode":{ "prefix": "wepy_urldecode", "body": "decodeURIComponent($1);" }, "wepy_noimg":{ "prefix": "wepy_noimg", "body": "/images/imgload.png", }, "wepy_errmsg":{ "prefix": "wepy_errmsg", "body": "(result && result.errorMsg) ? result.errorMsg :\"删除失败!\"", }, "wepy_sets":{ "prefix": "wepy_sets", "body": "wepy.setStorageSync($1,$2);" }, "wepy_gets":{ "prefix": "wepy_gets", "body": "wepy.getStorageSync($1);" } } [代码] 4. 相关链接 持续更新:收藏整理官方隐藏的小程序功能/参数/方法/API 动手打造更强更好用的微信开发者工具-编辑器扩展篇 点击wxml里面的绑定事件/变量名/样式名直接跳转到对应文件代码 小程序编码时变量名中文转英文变量名工具,各种驼峰取名 微信开发者工具编辑器支持「书签」功能,快速跳转到指定文件指定行和列 愉快的编写和调试Java:体验新版开发者工具的编辑器扩展功能 5. 其他未公布的隐藏功能 当你觉得有用的时候,就点赞和收藏或者分享,觉得没用的话就投诉,不管点赞还是投诉后的码农写代码永无BUG ,CP设计的产品人见人爱,BOSS每年收入翻番! ↓↓↓↓↓↓__________________________________________________________________________投诉的话点…↓↓↓
2020-05-17 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - 【必收】精心整理!小程序开发资源汇总(附带源码)
很多小伙伴想在春节放假期间学小程序,但是小程序学习的资源和教程可能不太好找。所以小助手精心整理了一期,全是干货!认真学,开启美妙的小程序开发之旅,做一个属于自己的微信小程序。有需要的小伙伴收藏好这期文章哦~ 本文收集整理了微信小程序开发资源,包括官方文档,云开发训练营文档,视频教程以及实战源码推荐,会不间断更新。。 欢迎添加云开发小助手CloudBase微信:Tcloudedu1 ,一起加入技术交流群~ 小程序云开发官方公众号 [图片] 目录 官方文档 云开发训练营 视频教程 小程序·云开发Demo 技术交流群 官方文档 小程序开发者工具 小程序设计指南 小程序开发教程 小程序框架 小程序组件 小程序API 小程序开发者工具 小程序云开发文档 云开发训练营 小程序开发入门 小程序与JavaScript 云开发快速入门 [图片] 视频教程 腾讯云云开发B站:https://space.bilibili.com/447496276 [图片] 小程序·云开发Demo 技术博客小程序 包括文章的发布及浏览、评论、点赞、浏览历史、分类、排行榜、分享、生成海报图等。 网盘小程序 兼具文件存储与分享功能的专属网盘小程序。 教务助手小程序 用完即走,查个成绩和课表,无需下载app或去翻看公众号内的历史内容。 功能日历小程序 既能查看日历又能备注事项,看云开发如何支持功能性日历小程序的快速开发。 客户业务需求收集小程序 用云开发快速制作客户业务需求收集小程序,教你用云开发实现小程序版“朋友圈”的发布与展示。 小程序朋友圈 把朋友圈装进小程序需要几步?借助云开发实现小程序朋友圈的发布与展示。 南苑导览 一款由学生独立开发的以地图为载体,提供中山大学南方学院具体地点的位置信息、导航、校园历史及文化介绍的小程序。 互动打卡小程序 用云开发轻松构建精美互动打卡小程序,交互式双人打卡,快乐加倍。 个性头像小程序 别再@官方啦!云开发教你轻松制作个性头像小程序,趣味挂件、个性icon。 二手书商城小程序 云开发轻松制作二手书交易商城小程序,让智慧延续,让温暖传递。 后台数据批量导出 小程序开发过程中如何将云数据库中的数据批量导出至excel。 发送邮件 初学者福音,手把手教你用小程序云开发实现邮件发送功能。 高考查分小程序 实现高考分数轻松查,小程序源码。 mini论坛 仅需两天轻松搭建mini论坛小程序。 运动圈小程序 打造运动圈小程序(以乒乓球为例),实现球友间高效互动。 心情日记小程序 我能想到最浪漫的事,可能就是“你的心事我全知晓”。 最美恋爱小程序 小程序前端用的是taro框架写的,后台用的云开发。教你用云开发为心爱的人做个小程。 校园约拍小程序 校园场景下,小程序·云开发大显身手,校园约拍小程序源码。 体重记录小程序 只想记录每日体重还得下个APP,不用那么麻烦!用云开发做个专属体重记录小程序,看看你每天瘦了多少。 口袋工具 口袋工具之历史上的今天。一个基于云开发的小程序,看看历史上的今天都发生了啥。 迷你微博 独立做个精简版微博出来让你刷刷刷吗?而且,它还兼具搜索、点赞、主页的功能 多媒体小程序 使用小程序·云开发构建多媒体小程序。 技术交流群 交流技术为主,开发学习工作中遇到问题可以在群内交流,欢迎有需要的朋友加群。 添加小助手微信(Tcloudedu1),回复“技术群”,即可加入云开发技术群。 最后 如果你有关于使用腾讯云云开发相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们~ 关注腾讯云云开发,后台回复【源码】,获取更多微信小程序云开发实战源码。 [图片] [图片] [图片] 关注「腾讯云云开发」,后台回复【 源码 】,获取更多微信小程序云开发实战源码。 持续更新中… [图片]
2020-01-16 - 微信小程序class封装http
config.js [代码]var config = { base_api_url:"https://*****/" } export {config} [代码] utils/http.js [代码]import {config} from "../config"; class HTTP{ request(params) { if (!params.method) { params.method = "GET" } wx.request({ url: config.base_api_url + params.url, data: params.data, method:params.method, header: { 'Content-Type': 'json' }, success: function (res) { let statusCode = res.statusCode.toString(); if(statusCode.startsWith("2")){ params.success(res.data); }else{ wx.showToast({ title:"网络错误", icon:"none" }) } }, fail: function() { wx.showToast({ title:"错误", icon:"none" }) } }) } } export{ HTTP } [代码] models/movie.js [代码]import { HTTP } from "../utils/http"; const movie = "movie/"; class MovieModel extends HTTP { getData(callback) { this.request({ url: movie + "getData", success: res => { callback(res); } }) } } export { MovieModel } [代码] index.js 引用 [代码]import {MovieModel} from "../../models/movie" var movie = new MovieModel(); [代码]
2020-03-13 - 如何实现一个自定义数据版省市区二级、三级联动
社区可能有其他的方案了,但是再分享下吧,给有需要的童鞋。 效果图: [图片] 额,这个视频转GIF因为社区上传不了大图,所以剪了一部分,具体的效果还是直接工具打开代码片段预览吧~ 第一步:你的页面JSON引入该组件: [代码]{ "usingComponents": { "city-picker": "/components/cityPicker/index" } } [代码] 第二步:你的页面WXML引入该组件 [代码]<city-picker visible="{{visible}}" column="2" bind:close="handleClick" bind:confirm="handleConfirm" /> [代码] 第三步:你的页面JS调用 [代码]// 显示/隐藏picker选择器 handleClick() { this.setData( visible: !this.data.visible }) }, // 用户选择城市后 点击确定的返回值 handleConfirm(e) { const { detail: { provinceName = '', provinceId = '', cityName, cityId='', areaName = '', areaId = '' } = {} } = e this.setData({ cityId, cityName, areaId, areaName, provinceId, provinceName }) } [代码] 组件属性 属性 默认值 描述 visible false 是否显示picker选择器 column 3 显示几列,可选值:1,2,3 values [0, 0, 0] 必填,默认回填的省市区下标,可选择具体省市区后查看AppData的regionValue字段 close function 点击关闭picker弹窗 confirm function 点击选择器的确定返回值 confirm: 属性 默认值 描述 provinceName 北京市 省份名称 provinceId 110000 省份ID cityName 市辖区 城市名称 cityId 110100 城市ID areaName 东城区 区域名称 areaId 110000 区域Id 至于怎么获取你想默认城市的下标,可以滑动操作下选中省市区后,点击确定后查看appData里的regionValue的值。 以上就是一个自定义数据版本的省市区二级、三级联动啦,老规矩,结尾放代码片段。 https://developers.weixin.qq.com/s/F9k9cTmT7LAz
2022-07-20 - 使用 MobX 来管理小程序的跨页面数据
在小程序中,常常有些数据需要在几个页面或组件中共享。对于这样的数据,在 web 开发中,有些朋友使用过 redux 、 vuex 之类的 状态管理 框架。在小程序开发中,也有不少朋友喜欢用 MobX ,说明这类框架在实际开发中非常实用。 小程序团队近期也开源了 MobX 的辅助模块,使用 MobX 也更加方便。那么,在这篇文章中就来介绍一下 MobX 在小程序中的一个简单用例! 在小程序中引入 MobX 在小程序项目中,可以通过 npm 的方式引入 MobX 。如果你还没有在小程序中使用过 npm ,那先在小程序目录中执行命令: [代码]npm init -y [代码] 引入 MobX : [代码]npm install --save mobx-miniprogram mobx-miniprogram-bindings [代码] (这里用到了 mobx-miniprogram-bindings 模块,模块说明在这里: https://developers.weixin.qq.com/miniprogram/dev/extended/functional/mobx.html 。) npm 命令执行完后,记得在开发者工具的项目中点一下菜单栏中的 [代码]工具[代码] - [代码]构建 npm[代码] 。 MobX 有什么用呢? 试想这样一个场景:制作一个天气预报资讯小程序,首页是列表,点击列表中的项目可以进入到详情页。 首页如下: [图片] 详情页如下: [图片] 每次进入首页时,需要使用 [代码]wx.request[代码] 获取天气列表数据,之后将数据使用 setData 应用到界面上。进入详情页之后,再次获取指定日期的天气详情数据,展示在详情页中。 这样做的坏处是,进入了详情页之后需要再次通过网络获取一次数据,等待网络返回后才能将数据展示出来。 事实上,可以在首页获取天气列表数据时,就一并将所有的天气详情数据一同获取回来,存放在一个 数据仓库 中,需要的时候从仓库中取出来就可以了。这样,只需要进入首页时获取一次网络数据就可以了。 MobX 可以帮助我们很方便地建立数据仓库。接下来就讲解一下具体怎么建立和使用 MobX 数据仓库。 建立数据仓库 数据仓库通常专门写在一个独立的 js 文件中。 [代码]import { observable, action } from 'mobx-miniprogram' // 数据仓库 export const store = observable({ list: [], // 天气数据(包含列表和详情) // 设置天气列表,从网络上获取到数据之后调用 setList: action(function (list) { this.list = list }), }) [代码] 在上面数据仓库中,包含有数据 [代码]list[代码] (即天气数据),还包括了一个名为 [代码]setList[代码] 的 action ,用于更改数据仓库中的数据。 在首页中使用数据仓库 如果需要在页面中使用数据仓库里的数据,需要调用 [代码]createStoreBindings[代码] 来将仓库中的数据绑定到页面数据中,然后就可以在页面中直接使用仓库数据了。 [代码]import { createStoreBindings } from 'mobx-miniprogram-bindings' import { store } from './store' Page({ onLoad() { // 绑定 MobX store this.storeBindings = createStoreBindings(this, { store, // 需要绑定的数据仓库 fields: ['list'], // 将 this.data.list 绑定为仓库中的 list ,即天气数据 actions: ['setList'], // 将 this.setList 绑定为仓库中的 setList action }) // 从服务器端读取数据 wx.showLoading() wx.request({ // 请求网络数据 // ... success: (data) => { wx.hideLoading() // 调用 setList action ,将数据写入 store this.setList(data) } }) }, onUnload() { // 解绑 this.storeBindings.destroyStoreBindings() }, }) [代码] 这样,可以在 wxml 中直接使用 list : [代码]<view class="item" wx:for="{{list}}" wx:key="date" data-index="{{index}}"> <!-- 这里可以使用 list 中的数据了! --> <view class="title">{{item.date}} {{item.summary}}</view> <view class="abstract">{{item.temperature}}</view> </view> [代码] 在详情页中使用数据仓库 在详情页中,同样可以使用 [代码]createStoreBindings[代码] 来将仓库中的数据绑定到页面数据中: [代码]import { createStoreBindings } from 'mobx-miniprogram-bindings' import { store } from './store' Page({ onLoad(args) { // 绑定 MobX store this.storeBindings = createStoreBindings(this, { store, // 需要绑定的数据仓库 fields: ['list'], // 将 this.data.list 绑定为仓库中的 list ,即天气数据 }) // 页面参数 `index` 表示要展示哪一条天气详情数据,将它用 setData 设置到界面上 this.setData({ index: args.index }) }, onUnload() { // 解绑 this.storeBindings.destroyStoreBindings() }, }) [代码] 这样,这个页面 wxml 中也可以直接使用 list : [代码]<view class="title">{{list[index].date}}</view> <view class="content">温度 {{list[index].temperature}}</view> <view class="content">天气 {{list[index].weather}}</view> <view class="content">空气质量 {{list[index].airQuality}}</view> <view class="content">{{list[index].details}}</view> [代码] 完整示例 完整例子可以在这个代码片段中体验: https://developers.weixin.qq.com/s/YhfvpxmN7HcV 这个就是 MobX 在小程序中最基础的玩法了。相关的 npm 模块文档可参考 mobx-miniprogram-bindings 和 mobx-miniprogram 。 MobX 在实际使用时还有很多好的实践经验,感兴趣的话,可以阅读一些其他相关的文章。
2019-11-01 - 【圣诞节特供】雪花飘落组件
圣诞节到啦! 在写页面的时候想到以前会出现的雪花飘落 用超简单的方法实现了 [代码] [代码] [代码] 代码片段: https://developers.weixin.qq.com/s/2AUMkEmC7cdi[代码] 动画的原理是CSS中的 [代码] [代码] [代码]view { [代码][代码] [代码][代码]transition: [代码][代码]all[代码] [代码]5[代码][代码]s ease-in;[代码] [代码]}[代码] [代码] [代码] 雪花数量以及出现实际的实现的方法是 在data里面放一个数组,用于存雪花的x轴偏移量。 用setTimeOut的方法递归实现 1~2 秒雪花的增量 [代码] let snowInterval = e => {[代码][代码] [代码][代码]this[代码][代码].data.snowArray.push(Math.random() * 750);[代码][代码] [代码][代码]this[代码][代码].setData({ snowOffset: [代码][代码]this[代码][代码].data.snowArray.length - 2, snowArray: [代码][代码]this[代码][代码].data.snowArray });[代码][代码] [代码][代码]setTimeout(snowInterval, Math.random() * 1000 + 300);[代码][代码] [代码][代码]};[代码][代码] [代码][代码]snowInterval();[代码]再以雪花数组的长度以及当前雪花的键名 定义雪花的不同周期(出现→ 飘落 →沉底 → 消失) [代码] [代码] [代码]<[代码][代码]view[代码] [代码] wx:for[代码][代码]=[代码][代码]"{{snowArray}}"[代码] [代码] wx:if="{{index + 30 > snowOffset}}" [代码] [代码] wx:key="index" style="left: {{item}}rpx; {{index < [代码][代码]snowOffset[代码] [代码]? 'top: 100%' : ''}}" [代码] [代码] class[代码][代码]=[代码][代码]"dot"[代码] [代码]></[代码][代码]view[代码][代码]>[代码] [代码] [代码] 肥肠简单的实现方法,想想以前用网页去实现花费好多时间( 不过那个时候的版本会随着鼠标飘动 )
2019-12-16 - 小程序10行代码实现微信头像挂红旗,国庆节个性化头像
最近朋友圈里经常有看到这样的头像 [图片] 既然这么火,大家要图又这么难,作为程序员的自己当然要自己动手实现一个。 老规矩,先看效果图 [图片] 仔细研究了下,发现实现起来并不难,核心代码只有下面10行。 [代码] wx.canvasToTempFilePath({ x: 0, y: 0, width: num, height: num, destWidth: num, destHeight: num, canvasId: 'shareImg', success: function(res) { that.setData({ prurl: res.tempFilePath }) wx.hideLoading() }, fail: function(res) { wx.hideLoading() } }) [代码] 一,首先要创建一个小程序 至于如何创建小程序,我这里就不在细讲了,我也有写过创建小程序的文章,也有路过相关的学习视频,去翻下我历史文章找找就行。 二,创建好小程序后,我们就开始来布局 布局很简单,只有下面几行代码。 [代码]<!-- 画布大小按需定制 这里我按照背景图的尺寸定的 --> <canvas canvas-id="shareImg"></canvas> <!-- 预览区域 --> <view class='preview'> <image src='{{prurl}}' mode='aspectFit'></image> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="1">生成头像1</button> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="2">生成头像2</button> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="3">生成头像3</button> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="4">生成头像4</button> <button type='primary' bindtap='save'>保存分享图</button> </view> [代码] 实现效果图如下 [图片] 三,使用canvas来画图 其实我们实现微信头像挂红旗,原理很简单,就是把头像放在下面,然后把有红旗的相框盖在头像上面 [图片] 下面就直接把核心代码贴给大家 [代码]let promise1 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: "../../images/xiaoshitou.jpg", success: function(res) { console.log("promise1", res) resolve(res); } }) }); let promise2 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: `../../images/head${index}.png`, success: function(res) { console.log(res) resolve(res); } }) }); Promise.all([ promise1, promise2 ]).then(res => { console.log("Promise.all", res) //主要就是计算好各个图文的位置 let num = 1125; ctx.drawImage('../../'+res[0].path, 0, 0, num, num) ctx.drawImage('../../' + res[1].path, 0, 0, num, num) ctx.stroke() ctx.draw(false, () => { wx.canvasToTempFilePath({ x: 0, y: 0, width: num, height: num, destWidth: num, destHeight: num, canvasId: 'shareImg', success: function(res) { that.setData({ prurl: res.tempFilePath }) wx.hideLoading() }, fail: function(res) { wx.hideLoading() } }) }) }) [代码] 来看下画出来的效果图 [图片] 四,头像加红旗画好以后,我们就要想办法把图片保存到本地了 [图片] 保存图片的代码也很简单。 [代码]save: function() { var that = this wx.saveImageToPhotosAlbum({ filePath: that.data.prurl, success(res) { wx.showModal({ content: '图片已保存到相册,赶紧晒一下吧~', showCancel: false, confirmText: '好哒', confirmColor: '#72B9C3', success: function(res) { if (res.confirm) { console.log('用户点击确定'); } } }) } }) } [代码] 来看下保存后的效果图 [图片] 到这里,我的微信头像就成功的加上了小红旗了。 [图片] 源码我也已经给大家准备好了,有需要的同学在文末留言即可。 [图片] 后面我准备录制一门视频课程出来,来详细教大家实现这个功能,敬请关注。
2019-09-26 - 借助云开发实现小程序的登陆注册功能
我们在开发小程序时,难免会用到登陆注册功能。通常小程序有为我们提供用户授权登陆的功能,但是这个只能获取用户的头像和昵称,我们该怎么样来实现小程序账号密码的注册和登陆呢,今天就来手把手的带大家学习小程序登陆注册功能的开发。 老规矩,先看效果图 [图片] 通过上图可以看到我们主要实现了以下功能 1,账号密码登陆 2,账号密码注册 3,退出登陆 下面我们就来看下具体实现 一,原理讲解 因为我们账号密码的注册,就是把用户设置的账号密码存到数据库里,登陆也是从数据库里取账号和密码来校验。所以我们必须要有数据库。如果用传统的数据库来做,比较麻烦,所以我们今天就借助小程序云开发数据库来做。 二,编写一个云开发的小程序 云开发的知识我讲过很多遍了,还不知道云开发是啥的同学可以翻看下我历史文章,或者看下我录制的云开发基础入门视频:《5小时零基础入门小程序云开发》 编写云开发的时候有几点注意的事项给大家说下 1,要先注册小程序获取appid,因为只有appid你才可以使用云开发 2,记得在app.js里初始化云开发环境id,如下图 [图片] 三,设置用户存储用户的数据库(集合) 在云开发管理后台,点击数据库,然后点击 + 号,添加user集合(数据表),如下图 [图片] 四,编写注册代码 代码其实很简单,我这里把对应的代码给大家贴出来。 1,注册页面的wxml文件 [图片] 2,注册页面的js文件 [代码]Page({ data: { name: '', zhanghao: '', mima: '' }, //获取用户名 getName(event) { console.log('获取输入的用户名', event.detail.value) this.setData({ name: event.detail.value }) }, //获取用户账号 getZhangHao(event) { console.log('获取输入的账号', event.detail.value) this.setData({ zhanghao: event.detail.value }) }, // 获取密码 getMiMa(event) { console.log('获取输入的密码', event.detail.value) this.setData({ mima: event.detail.value }) }, //注册 zhuce() { let name = this.data.name let zhanghao = this.data.zhanghao let mima = this.data.mima console.log("点击了注册") console.log("name", name) console.log("zhanghao", zhanghao) console.log("mima", mima) //校验用户名 if (name.length < 2) { wx.showToast({ icon: 'none', title: '用户名至少2位', }) return } if (name.length > 10) { wx.showToast({ icon: 'none', title: '用户名最多10位', }) return } //校验账号 if (zhanghao.length < 4) { wx.showToast({ icon: 'none', title: '账号至少4位', }) return } //校验密码 if (mima.length < 4) { wx.showToast({ icon: 'none', title: '密码至少4位', }) return } //注册功能的实现 wx.cloud.database().collection('user').add({ data: { name: name, zhanghao: zhanghao, mima: mima }, success(res) { console.log('注册成功', res) wx.showToast({ title: '注册成功', }) wx.navigateTo({ url: '../login/login', }) }, fail(res) { console.log('注册失败', res) } }) } }) [代码] 3,注册页面的wxss(样式)页面很简单 [图片] 我这只做下简单的样式美化,主要还是来实现功能的。 五,编写登陆页面的代码 1,登陆页面的wxml文件 [图片] 2,登陆页的js(逻辑编写)页 [代码]Page({ data: { zhanghao: '', mima: '' }, //获取输入的账号 getZhanghao(event) { //console.log('账号', event.detail.value) this.setData({ zhanghao: event.detail.value }) }, //获取输入的密码 getMima(event) { // console.log('密码', event.detail.value) this.setData({ mima: event.detail.value }) }, //点击登陆 login() { let zhanghao = this.data.zhanghao let mima = this.data.mima console.log('账号', zhanghao, '密码', mima) if (zhanghao.length < 4) { wx.showToast({ icon: 'none', title: '账号至少4位', }) return } if (mima.length < 4) { wx.showToast({ icon: 'none', title: '账号至少4位', }) return } //登陆 wx.cloud.database().collection('user').where({ zhanghao: zhanghao }).get({ success(res) { console.log("获取数据成功", res) let user = res.data[0] console.log("user", user) if (mima == user.mima) { console.log('登陆成功') wx.showToast({ title: '登陆成功', }) // wx.navigateTo({ // url: '../home/home?name=' + user.name, // }) wx.navigateTo({ url: '/pages/me/me', }) //保存用户登陆状态 wx.setStorageSync('user', user) } else { console.log('登陆失败') wx.showToast({ icon: 'none', title: '账号或密码不正确', }) } }, fail(res) { console.log("获取数据失败", res) } }) } }) [代码] 3,样式比较简单 [图片] 六,编写个人中心登陆和未登陆状态的展示,含退出登陆功能 1,wxml文件如下 [图片] 2,js文件如下,退出登陆和保存登陆状态也在里面 [代码]Page({ data: { loginOK: false }, //去登陆页 denglu() { wx.navigateTo({ url: '/pages/login/login', }) }, //去注册页 zhuce() { wx.navigateTo({ url: '/pages/index/index', }) }, onShow() { let user = wx.getStorageSync('user') if (user && user.name) { this.setData({ loginOK: true, name: user.name }) } else { this.setData({ loginOK: false }) } }, //退出登陆 tuichu() { wx.setStorageSync('user', null) let user = wx.getStorageSync('user') if (user && user.name) { this.setData({ loginOK: true, name: user.name }) } else { this.setData({ loginOK: false }) } } }) [代码] 3,个人中心登陆成功的状态如下 [图片] 到这里我们就完整的实现了小程序的登陆注册功能了,虽然比较简单,没有做密码加密等一些复杂的操作,但是我们基本的登陆注册原理就是这样实现的,你只有先把最基础的登陆注册功能实现,学习后面复杂的登陆注册,验证码登陆等一系列知识,才会游刃有余。 我把这节登陆注册功能的实现录制了一套课程出来,感兴趣的同学可以去看下,支持下石头哥。 https://edu.csdn.net/course/play/26948/348188
2019-12-09 - weui内置扩展库使用步骤
更新最新的 nightly 版开发者工具 在app.json里新增 “useExtendedLib”: { “weui”: true } 在使用的页面json文件应用组件,比如在index.json里 { “navigationStyle”:“custom”, “usingComponents”: { “mp-navigation-bar”:“weui-miniprogram/navigation-bar/navigation-bar” } } wxml文件使用组件,比如在index.wxml里 <mp-navigation-bar title=“首页”></mp-navigation-bar> 验证有无生效。 示例代码片段:https://developers.weixin.qq.com/s/zB6mFrmc7elr 备注: 文档里说目前暂不支持在分包中引用,但据代码测试,分包也能使用。 各位觉得有用的话,给个赞呗。
2020-10-28 - 云开发中操作数组对象嵌套数据如何修改?
[图片] 比如说,我要修改早退为-20,该用什么api啊,文档都翻了一遍,找不到合适的
2019-11-28 - uni-app使用fly封装网络请求接口
版权说明 本文首发于指尖魔法屋uni-app使用fly封装网络请求接口. 转载请附上原地址 介绍 本文采用[代码]uni-app[代码]框架开发,使用flyio库封装请求。 安装方式 [代码]npm install --save flyio [代码] 引入方式 新建[代码]api.js[代码]文件,编写以下内容 [代码]var Fly = require("flyio/dist/npm/wx") var fly = new Fly; [代码] 配置方式 [代码]// 配置请求根域名 fly.config.baseURL = "http://whisper.wezoz.com" // 配置响应拦截器 fly.interceptors.response.use( (response) => { // 如果请求报错 if (response.data.code != 10000) { uni.showModal({ title:'温馨提示', content:response.data.data }) }else{ //只将请求结果的data字段返回 return response.data.data } }, (err) => { //发生网络错误后会走到这里 return Promise.resolve("网络请求:ERROR!") } ) // 配置请求拦截器 fly.interceptors.request.use((request) => { request.headers["token"] = uni.getStorageSync('token'); return request; }) [代码] 同步封装微信登录 [代码]async function wxLogin() { return await new Promise((resolve, reject) => { wx.login({ success(res) { if (res.code) { resolve(res.code) } } }) }) } [代码] 写一个接口如login [代码]// 登录 export const login = async (params) => { console.log('开始登录...') let code = await wxLogin(); let res = await fly.get('/user/login', {code: code}) uni.setStorageSync('token', res.token) uni.setStorageSync('openid', res.openid) } [代码] 封装普通请求 [代码]export const updateUserInfo = (params) => { return fly.get('/user/update', params) } [代码] 引入方式 在[代码]App.vue[代码] 或者[代码]main.js[代码]添加以下代码 [代码]import * as API from './static/utils/api' Vue.prototype.$api = API [代码] 调用方式 test.vue [代码]this.$api.login() [代码]
2019-11-22 - 请问小程序picker省市区的json数据有没有办法获取到?
现在前端想用picker中的省市区选择组件,但是后端需要有与之匹配的数据格式,请问这个是否开源了,有没有办法获取到正确的数据
2019-11-18 - 小程序开发常见问题及解决方案
一,input不触发bindinput事件 关于input使用bindinput事件时,没法获取用户输入信息的问题。 解决: 这个是官方2.9.3的bug,退到2.9.2后就好了,回退操作如下图 [图片] 二,getImageInfo:fail download image fail 出现这种问题,是因为没有给小程序的downloadFile配置域名白名单。 [图片] 三,获取小程序用户高清图像 [代码]//获取高清微信头像 headimgHD(imageUrl) { console.log('原来的头像', imageUrl); imageUrl = imageUrl.split('/'); //把头像的路径切成数组 //把大小数值为 46 || 64 || 96 || 132 的转换为0 if (imageUrl[imageUrl.length - 1] && (imageUrl[imageUrl.length - 1] == 46 || imageUrl[imageUrl.length - 1] == 64 || imageUrl[imageUrl.length - 1] == 96 || imageUrl[imageUrl.length - 1] == 132)) { imageUrl[imageUrl.length - 1] = 0; } imageUrl = imageUrl.join('/'); //重新拼接为字符串 console.log('高清的头像', imageUrl); return imageUrl; }, [代码]
2019-11-17 - 小程序用Promise简单封装wx.request的POST请求
const inter = ‘主域名’; var obj={}; const post=(url,obj)=>{ return new Promise( (resolve,reject)=>{ wx.showLoading({ title: ‘加载中’,mask:true}) wx.request({ url: inter+url, method:‘post’, data:obj, header: { ‘content-type’: ‘application/x-www-form-urlencoded’, }, success: function (res) { wx.hideLoading(); if (res.statusCode != 200) { reject({ error: ‘服务器忙,请稍后重试’, code: 500 }); return; } resolve(res.data); }, fail: function (res) { reject({ error: ‘网络错误’, code: 0 }); }, complete: function (res) { wx.hideLoading(); } }) }).catch(err=>{ reject(err) console.log(‘请求失败了’,err) }) } module.exports=post;
2019-11-16 - 纯云开发 使用一个小程序访问另一个小程序的云资源
由于工作需要,我需要使用一个小程序与另一个小程序共同享用同一套云资源。这就需要用到'tcb-admin-node'这个sdk来帮我实现这个功能。 这个sdk有详细的教程如下:https://github.com/TencentCloudBase/tcb-admin-node 作为一个新手,刚看这个文档感觉有些懵逼,不过在群友的帮助下,还是慢慢地实现了一小步的功能,就是小程序访问另一个小程序的云函数。 废话不多说,我的使用步骤如下: 1,你要有一个已经有在使用自己开通的云资源的小程序,称为小程序A;还要有一个空的小程序,称为小程序B。 2,为小程序B开通云开发。 3,小程序B创建云函数的方法我就不多说了。按照文档来说,你是需要每建一个云函数就安装一次tcb-admin-node的,但是最新版本的wx-server-sdk貌似已经集成了tcb-admin-node,所以你可以选择安装或者不安装。 4 ,不多说,代码如下图: [图片] 其中secretId和secretKey都是必须的,均为小程序A的secretId和secretKey,获取方式文档中有链接,即从腾讯云中获取你的api密匙。如下图:env为小程序A使用的环境ID [图片] 取一对就可以了,还有必须从你的小程序A进入。 name为你小程序A使用过的云函数,data为参数,与云函数所需参数一致。 5,这就封装完成了一个云函数。别忘记上传。,这时候在前台,就像普通云函数一样调用这个云函数就可以了。我的代码如下: [图片] 访问结果如下: [图片] 这时候小程序B就成功地访问了小程序A。 当然,这只是我实践的结果,成功了,于是把方法分享给大家。你们成功不成功,就看你们自己的实践了。 由于第一次发帖,可能写的有不好的地方,希望大家多多包涵,若有不妥可以纠正一下,谢谢大家。
2019-11-15 - 我想让我的商城给所有的用户发红包,云数据库该如何设计?
我现在是要做类似与发红包的功能,比如,用户刚进去的时候,跳出一个红包,用户点击领取红包,这时候,用户消费的时候可用这些红包抵扣。 现在有一个问题,一个用户只能领一次。我的数据库表该怎么建呢?首先肯定是要先建一个红包表的, 我有两种方法: 第一种是 直接在 红包表里面建立一个用户的list字段。到时候如果用户领了红包,把用户的id存到这个list里面,那就能做唯一标识了。但是这种方法有一种缺陷,就是,如果一种红包领取记录达到上万条,到时候查询的时候不会很卡吗? 第二种就是另外建立一个用户领取红包表,就像mysql一样,每个记录都绑定用户id和红包id,但是这种方法,要查询的时候貌似挺麻烦。好像挺费cdn的。。。 正在纠结哪种方法好,求大神给一点建议?
2019-11-14 - 小程序读取excel表格数据,并存储到云数据库
最近一直比较忙,答应大家的小程序解析excel一直没有写出来,今天终于忙里偷闲,有机会把这篇文章写出来给大家了。 老规矩先看效果图 [图片] 效果其实很简单,就是把excel里的数据解析出来,然后存到云数据库里。说起来很简单。但是真的做起来的时候,发现其中要用到的东西还是很多的。不信。。。。 那来看下流程图 流程图 [图片] 通过流程图,我看看到我们这里使用了云函数,云存储,云数据库。 流程图主要实现下面几个步骤 1,使用wx.chooseMessageFile选择要解析的excel表格 2,通过wx.cloud.uploadFile上传excel文件到云存储 3,云存储返回一个fileid 给我们 4,定义一个excel云函数 5,把第3步返回的fileid传递给excel云函数 6,在excel云函数里解析excel,并把数据添加到云数据库。 可以看到最神秘,最重要的就是我们的excel云函数。 所以我们先把前5步实现了,后面重点讲解下我们的excel云函数。 一,选择并上传excel表格文件到云存储 这里我们使用到了云开发,使用云开发必须要先注册一个小程序,并给自己的小程序开通云开发功能。这个知识点我讲过很多遍了,还不知道怎么开通并使用云开发的同学,去翻下我前面的文章,或者看下我录的讲解视频《5小时入门小程序云开发》 1,先定义我们的页面 页面很简单,就是一个按钮如下图,点击按钮时调用chooseExcel方法,选择excel [图片] 对应的wxml代码如下 [图片] 2,编写文件选择和文件上传方法 [图片] 上图的chooseExcel就是我们的excel文件选择方法。 uploadExcel就是我们的文件上传方法,上传成功以后会返回一个fildID。我们把fildID传递给我们的jiexi方法,jiexi方法如下 3 把fildID传递给云函数 [图片] 二,解下来就是定义我们的云函数了。 1,首先我们要新建云函数 [图片] 如果你还不知道如何新建云函数,可以翻看下我之前写的文章,也可以看我录的视频《5小时入门小程序云开发》 如下图所示的excel就是我们创建的云函数 [图片] 2,安装node-xlsx依赖库 [图片] 如上图所示,右键excel,然后点击在终端中打开。 打开终端后, 输入 npm install node-xlsx 安装依赖。可以看到下图安装中的进度条 [图片] 这一步需要你电脑上安装过node.js并配置npm命令。 3,安装node-xlsx依赖库完成 [图片] 三,编写云函数 我把完整的代码贴出来给大家 [代码]const cloud = require('wx-server-sdk') cloud.init() var xlsx = require('node-xlsx'); const db = cloud.database() exports.main = async(event, context) => { let { fileID } = event //1,通过fileID下载云存储里的excel文件 const res = await cloud.downloadFile({ fileID: fileID, }) const buffer = res.fileContent const tasks = [] //用来存储所有的添加数据操作 //2,解析excel文件里的数据 var sheets = xlsx.parse(buffer); //获取到所有sheets sheets.forEach(function(sheet) { console.log(sheet['name']); for (var rowId in sheet['data']) { console.log(rowId); var row = sheet['data'][rowId]; //第几行数据 if (rowId > 0 && row) { //第一行是表格标题,所有我们要从第2行开始读 //3,把解析到的数据存到excelList数据表里 const promise = db.collection('users') .add({ data: { name: row[0], //姓名 age: row[1], //年龄 address: row[2], //地址 wechat: row[3] //wechat } }) tasks.push(promise) } } }); // 等待所有数据添加完成 let result = await Promise.all(tasks).then(res => { return res }).catch(function(err) { return err }) return result } [代码] 上面代码里注释的很清楚了,我这里就不在啰嗦了。 有几点注意的给大家说下 1,要先创建数据表 [图片] 2,有时候如果老是解析失败,可能是有的电脑需要在云函数里也要初始化云开发环境 [图片] 四,解析并上传成功 如我的表格里有下面三条数据 [图片] 点击上传按钮,并选择我们的表格文件 [图片] 上传成功的返回如下,可以看出我们添加了3条数据到数据库 [图片] 添加成功效果图如下 [图片] 到这里我们就完整的实现了小程序上传excel数据到数据库的功能了。 再来带大家看下流程图 [图片] 如果你有遇到问题,可以在底部留言,我看到后会及时解答。后面我会写更多小程序云开发实战的文章出来。也会录制本节的视频出来,敬请关注。
2019-11-12 - 开发者工具那些你未必知道的console命令
build 编译 相当于点击“编译”按钮 [图片] preview 预览 [图片] upload 上传代码 [图片] cleanAppCache 清除应用缓存 清除完需要重新编译 [图片] showRequestInfo 查看请求过链接的信息 检查证书一大利器 [图片] showSystemInfo 显示当前开发者工具占用内存及其他信息 [图片] checkProxy 检测目标网址是否启用代理 [图片] openToolsLog 打开开发者工具日志目录 openPlugin 打开插件目录 openVendor 打开供应商目录 更多详细的命令使用 help 进行获取 [图片]
2019-11-08 - 内容安全检测图片API:openapi.security.imgSecCheck完美解决方案。
背景需求: 我个人做了一款小程序的小游戏,本质是小程序。里面有个自定义图片的功能。用户从本地相册选一张图片进行裁剪,之后保存到缓存中或者上传到服务器。然后用户再用这张图片作为素材进行其它操作。这里就涉及到内容安全了,提交审核没有通过也是因为这个没有做内容安全。防止一些色情低俗的事情发生。 正文: 思路:相册选图片 --> 裁剪小的图片 --> 内容安全检测 --> 通过 --> 裁剪大的图片 --> 保存。 失败的原因:绝大多数是因为检测图片不能大于1M,而导致超时,或者是errCode:-1,又或者是其它问题。 [图片] [图片] 核心代码图片: [代码]默认裁剪小尺寸图片 (我的业务需求是正方形图片,也可动态计算宽高比例) [代码] [图片] 检测图片 部分iOS不兼容encoding: ‘ucs2’。注释掉就好了 [图片] [图片] 云函数 [图片] 测试情况: 正常图片不含违法违规,测试20次,全部通过。小程序上线后暂无发现检测失败情况。百度搜索的“人体油画”等等均可通过。 PS:第一次写经验分享哈,看不懂可以问我。体验一下我的小程序想问我这个小程序其它的功能点也可以喔! 技术会迭代更新,用到的技术会有时效性,看编辑时间,可能当时的技术现在不适用了
2020-10-22 - 云存储文件目录存在泄露风险
云存储目录文件存在泄露风险,浏览器中输入http://(云存储资源ID).tcb.qcloud.la/,会打开一个xml文件,里面包含云存储里面全部的文件地址等信息。
2019-11-09 - 实战分享: 小程序云开发玩转订阅消息(一)
[图片] 微信官方为提升小程序模板消息能力的使用体验,对模板消息的下发条件进行了调整。原有的小程序模板消息接口于 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 - 请教一个数组存储问题?
let price={}; let dateList = ["2019-10-17", "2019-10-18", "2019-10-19"] let priceList = [100,200,300] // 希望能得到如下结果====> price = { "2019-10-17": 100, "2019-10-18": 200, "2019-10-19":300 } 希望把一个数组'dateList' 的元素(日期)作为key,另外一个数组'priceList '的元素(价格)作为值存进对象(price),最终能通过 .price.2019-10-18 来访问当天价格。请问该如何实现呢?小白先谢过
2019-10-17 - 将小程序原生异步函数promisify后,在async/await中使用
目前,小程序中支持使用async/await有三种模式: 1、不勾选es6转es5,不勾选增强编译;该模式是纯es7的async/await,需要基础库高版本。 2、勾选es6转es5,勾选增强编译;一般是因为调用了第三方的es5插件,通过增强编译支持async/await。 3、勾选es6转es5,不勾选增强编译;手工引入runtime.js支持async/await。 据最近更新情况,原生的函数已经大部分同时原生支持同步化了,不需要本方案转化了,直接加上await即可;比如wx.chooseImage、wx.showModal。。。具体有哪些,可以自己试。 如果只是wx.request的同步化,可参考: https://developers.weixin.qq.com/community/develop/article/doc/0004cc839407a069f77a416c056813 app.js代码: function promisify(api) { return (opt, ...arg) => { return new Promise((resolve, reject) => { api(Object.assign({}, opt, { success: resolve, fail: reject }), ...arg) }) } } App({ globalData: {}, chooseImage: promisify(wx.chooseImage), request: promisify(wx.request), getUserInfo: promisify(wx.getUserInfo), onLaunch: function () { }, }) 某page的index.js代码: const app = getApp() testAsync: async function(){ let res = await app.chooseImage() console.log(res) res = await app.request({url:'url',method:'POST',data:{x:0,y:1}}) console.log(res) }, [图片]
2020-10-20 - 【云开发】用云开发实现订阅消息
背景 10月12日微信官方发布了小程序模板消息能力调整的通知 https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401 相比之前的有了较大的调整,主要的调整就是从开发者主动下发消息改为用户自主订阅,也就是说必须要用户手动订阅才行,而订阅消息又分为了一次性订阅消息和长期性订阅消息,长期性订阅消息需要有特定公共服务业务这里就不介绍了,本文主要讲的是一次性订阅消息。 开发准备 如果还没有开通订阅消息的要在小程序后台里开通订阅消息后添加需要的模板,添加完后的红框中的变量就是订阅消息接口要用到的data了 [图片] [图片] 编写云函数 新建云函数,appid跟secret填你自己的即可, 由于订阅消息需要access_token,所以在使用订阅消息接口前要先获取access_token,这里我把他们都放在一个云函数里了 新建好后要安装依赖,这里用到了[代码]request-promise[代码]模块,所以要在该目录下[代码]npm install request-promise[代码] 代码如下 [图片] [图片] 页面调用 [图片] 传入对应的参数即可 返回结果 [图片] [图片] 总结 总的来说并不难,只是有个地方要注意一下,由于之前没怎么用过云开发,不知道它返回的数据结构会有差异,比如在获取access_token的时候发现一个特别之处,在小程序里进行调用的时候,成功的话返回是下图这种格式的 [图片] 但是本地调试的时候返回又是这种格式的 [图片] 少了外面一层object,导致我判断access_token一直报错,调试了好久才发现,好在最后是可以成功获取到。 再来说这次调整,作为用户的我来说我觉得是很ok的,起码我可以选择不接收订阅消息,不像之前那样莫名其妙的就收到了一个订阅消息还不知道是哪个程序的,我是很反感这种的。但是对于一些企业来说可能就不是那么友好了,降低了触达用户的机会。但是不管怎样,身为开发者的可是有得忙了。 代码就不放了,都在图片里,自己动手敲一遍比较好 相关接口 订阅接口 获取access_token接口 下发消息接口
2019-10-14 - 开发者工具遇到的问题怎么解决?
打开一个目录,想选择里边的一个文件,但是点开后目录后会直接跳转到当前打开的编辑器所在的文件,这个改怎么解决? 全局搜索用不了了,使用ctrl+shift+f 不显示搜索的页面,但是在导航里边打开工具里边的全局搜索就出现,并且里边提示的快捷键也是ctrl+shift+f,这个要怎么解决?(windows并没有和这个冲突的快捷键) 调试器里边的wxml有时候不显示信息?
2019-09-18 - 云开发后台用户头像等基本信息不显示?
如题,小程序云开发,用户已授权,查看云开发后台用户的头像、性别、城市等基本信息不显示。 开发版本和体现版本授权的用户,显示正常;正式版本的新用户,不显示头像等信息! 请问各路大神此issue如何破? [图片]
2019-09-16 - 生成分享卡片 | Canvas 初使用
这是微信小程序开发频率最高的功能,绝大部分的小程序都会做这个,因为小程序页面不可以分享到朋友圈。所以,为了达到分享的目的,大家几乎都是通过生成分享卡片的方式实现。 其本质就是将页面元素,生成一张图片,并且保存到相册中。这块功能的实现需要借助 canvas 画布的 API。这也是「计算日子」这个小工具,一个关键的功能点,最终生成卡片效果如下图所示: [图片] 下面将以它的实现代码为例进行介绍。首先在 pages 目录下,创建 canvas 目录,新建 Page,命名 canvas,目录如下图所示: [图片] 编写 canvas.wxml 文件,结构很简单,一个画布元素,一个按钮,代码如下: [代码]<canvas style="width: 100%; height: 200px;" canvas-id="firstCanvas"></canvas> <view class="bottom_btn"> <button type="warn" bindtap="saveToImg" loading='{{ isLoading }}' disabled='{{ isDisable }}'>保存卡片</button> </view> [代码] 接下来,就是最主要的部分了,使用 js 在画布上画画了,在写代码之前,先来分析一下画布上显示的内容,我们从示例图中,可以发现,有以下几个元素: 矩形白色卡片,带阴影 4 行文本:标题(新中国成立)、数字(25540)、描述(已过天数)以及目标日 一张二维码图片(已经放在 canvas 目录下了) 我们分别将这些元素的绘制方法,放在单独的函数中,打开 canvas.js 文件,在 Page() 函数之前,定义如下几个函数: [代码]const app = getApp(); // 获取屏幕宽度 const windowWidth = wx.getSystemInfoSync().windowWidth; // 使用 wx.createContext 获取绘图上下文 context let context = wx.createCanvasContext('firstCanvas'); function setTitle(title) { // 绘制标题 context.setFontSize(20); context.setFillStyle('#333'); context.fillText(title, 40, 60); } function setLabel(numberLabel) { // 绘制 desc context.setFontSize(10); context.setFillStyle('#999') context.fillText(numberLabel, 43, 95); } function setNumber(number, numberColor) { // 绘制数字 context.setFontSize(40); context.setFillStyle(numberColor) context.fillText(number, 40, 135); } function setDate(date) { // 绘制目标日 context.setFontSize(10); context.setFillStyle('#999') context.fillText('目标日:' + date, 43, 165); } function setRectAndQrcode() { // 绘制背景 context.setFillStyle('#fff'); context.setShadow(5, 5, 12, '#999'); context.fillRect(20, 20, windowWidth - 40, 160); context.setShadow(0, 0, 0, '#fff'); // 绘制二维码 context.drawImage('./bytefactory.jpg', windowWidth - 145, 55, 100, 100); } [代码] 画布定义高度为 200 px,宽度为屏幕宽度(通过 [代码]wx.getSystemInfoSync().windowWidth[代码] API 获取),使用 [代码]wx.createCanvasContext[代码] 创建画布对象,然后就是使用具体的 API 进行绘制了,具体 API 的使用,请打开官方文档,对照查看。 上面的几个函数,只是 context 对象的一些设置,接下来再定义一个函数 [代码]drawCard()[代码] 用来绘制图形,代码如下: [代码]function drawCard(title, date, number, isPast) { var numberLabel = isPast === true ? '已过天数' : '剩余天数'; var numberColor = isPast === true ? '#3cc51f' : '#e64340'; setRectAndQrcode(); setTitle(title); setLabel(numberLabel); setNumber(number, numberColor); setDate(date); // 绘制 context.draw(); } [代码] 将一些变量提取出来,通过参数形式传入。 最后,在 [代码]onShow[代码] 页面监听函数中,加一行代码就搞定了,当显示页面的时候,执行 [代码]drawCard[代码] 函数,绘制图形。 [代码]onShow: function () { drawCard(this.data.title, this.data.date, this.data.number, this.data.isPast); }, [代码] 绘制完卡片后,canvas 有一个导出图片的 API,然后调用保存图片到相册的 API,就可以了。视图中有一个按钮,绑定了 saveToImg 的函数,代码如下: [代码]saveToImg: function () { this.setData({ isLoading: true }); wx.canvasToTempFilePath({ canvasId: 'firstCanvas', fileType: 'jpg', quality: 1, success: res => { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: res => { this.setData({ isLoading: false }); wx.showToast({ title: '保存成功' }) } }); } } [代码] 导出文件 API:wx.canvasToTempFilePath 保存相册 API:wx.saveImageToPhotosAlbum 具体使用方法,请点击查看官方文档,学会查看文档尤其重要。 最后,留一个思考题,假设,我不想在卡片上显示目标日,该如何实现定制卡片的需求? 给个提示:尝试使用 clearRect,当然页面也要有对应的修改。最终效果如下图: [图片] [图片] 假设尝试过了,还是不行,点这里查看源代码。
2019-09-04 - 3分钟教你学会使用路线规划小程序插件
路线规划小程序插件是腾讯位置服务开发的一款为用户规划驾车、公交、步行路线方案的插件。开发者可以直接在小程序内使用这个插件,从而为自己的用户提供多种出行方案选择。 路线规划插件的功能 路线规划插件能为用户规划驾车出行路线(如下图1所示),并且当行车起点和行车终点之间可以规划出多个方案时会展示多个方案及方案耗时。这些不同方案体现了不同的策略,例如根据实时路况时间最短、红绿灯数较少、少收费等策略。 同时驾车路线在地图中会通过不同路线的颜色直观反映道路的拥堵情况,例如红色路线表示那段道路拥堵,这就能够让用户提前规避拥堵路段。 路线规划插件也能为用户规划步行出行路线(如下图2所示),不仅显示了步行路线距离和耗时信息,还显示了用户步行过程中,走过的天桥、人行横道数量,更人性化的显示了步行消耗了多少卡路里。 [图片] [图片] 路线规划插件还能为用户规划公交出行路线(如下图所示),提供多种公交和地铁出行方案,并且用户可以根据自己的实际情况进行方案排序,例如时间短优先排序、少步行优先排序、少换乘优先排序。出行方案上也会有时间短这样的标志信息说明方案特点。 [图片] 路线规划插件的应用场景 路线规划插件应用场景非常丰富,可以直接接入到餐饮、电影等各种类型的小程序中,让消费者在小程序中就能获得到达门店的路线规划方案,方便去门店消费。 设想一个场景,小王周末想要吃一顿大餐,于是打开了某家餐厅小程序,当小王决定去这家餐厅时,不需要再打开地图软件去规划出行路线,通过我们的路线规划插件,在这家餐厅的小程序中就能直接规划小王目前的位置到餐厅的出行路线。小王可以选择开车去餐厅,如果今天车牌号限行,那么小王也可以选择公共交通出行,如果到餐厅的距离很近,那么小王可以选择步行方式到达餐厅。 小程序只需要使用路线规划插件就能拥有这些全面精准规划路线能力。看了这些功能,是不是想马上体验呢?别急!接下来就介绍路线规划插件的使用方法。 路线规划插件的使用方法1、申请路线规划插件在微信公众平台中, “微信小程序官方后台-设置-第三方设置-插件管理” 里点击 “添加插件”(如下图所示),搜索 “腾讯位置服务路线规划” ,选择添加插件,小程序开发者就可以在小程序内使用该插件了。 [图片] 2、申请key调用路线规划插件需要申请腾讯位置服务的服务账号,key是开发者的唯一标识,申请key请点击这里。申请key的具体步骤如下: 2.1 填写申请信息[图片] 2.2 创建key成功[图片] 2.3 授权小程序appid开通微信小程序服务:控制台 -> key管理 -> 设置(使用该功能的key)-> 勾选“微信小程序” -> 填写“授权 APP ID” ->保存。 [图片] 2.4 勾选“WebService API”及“白名单”微信小程序插件需要使用WebService API的部分服务,所以使用该功能的key需要具备相应的权限。 [图片] 如果开发者之前是腾讯位置服务的用户并申请过key,则可以跳过上面2.1、2.2的步骤,直接进行2.3、2.4步骤的设置。 3、在小程序中引入路线规划插件只需要在小程序的app.json文件做如下配置就可以在小程序中引入路线规划插件: [代码]// app.json[代码][代码]{[代码][代码] [代码][代码]"plugins"[代码][代码]: {[代码][代码] [代码][代码]"routePlan"[代码][代码]: {[代码][代码] [代码][代码]"version"[代码][代码]: [代码][代码]"1.0.0"[代码][代码],[代码][代码] [代码][代码]"provider"[代码][代码]: [代码][代码]"wx50b5593e81dd937a"[代码][代码] [代码][代码]}[代码][代码] [代码][代码]},[代码][代码] [代码][代码]"permission"[代码][代码]: {[代码][代码] [代码][代码]"scope.userLocation"[代码][代码]: {[代码][代码] [代码][代码]"desc"[代码][代码]: [代码][代码]"你的位置信息将用于小程序定位"[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码]}[代码]4、在小程序中调用路线规划插件在小程序中调用路线规划插件也非常简单: [代码]let plugin = requirePlugin([代码][代码]'routePlan'[代码][代码]);[代码][代码]let key = [代码][代码]''[代码][代码]; [代码][代码]//使用在腾讯位置服务申请的key[代码][代码]let referer = [代码][代码]''[代码][代码]; [代码][代码]//调用插件的小程序的名称[代码][代码]let startPoint = JSON.stringify({ [代码][代码]//起点[代码][代码] [代码][代码]'name'[代码][代码]: [代码][代码]'中国技术交易大厦'[代码][代码],[代码][代码] [代码][代码]'latitude'[代码][代码]: 39.984154,[代码][代码] [代码][代码]'longitude'[代码][代码]: 116.30749[代码][代码]});[代码][代码]let endPoint = JSON.stringify({ [代码][代码]//终点[代码][代码] [代码][代码]'name'[代码][代码]: [代码][代码]'北京西站'[代码][代码],[代码][代码] [代码][代码]'latitude'[代码][代码]: 39.894806,[代码][代码] [代码][代码]'longitude'[代码][代码]: 116.321592[代码][代码]});[代码][代码]wx.navigateTo({[代码][代码] [代码][代码]url: [代码][代码]'plugin://routePlan/route-plan?key='[代码] [代码]+ key + [代码][代码]'&referer='[代码] [代码]+ referer + [代码][代码]'&endPoint='[代码] [代码]+ endPoint[代码][代码]});[代码]如以上示例代码所示,只需要传4个参数,就能为小程序用户提供驾车、公交、步行路线规划信息了。这4个参数含义如下: key,开发者的唯一标识,第2步申请的key referer,调用插件的小程序的名称 startPoint,起点名称和坐标信息,如果不传起点参数,则起点默认当前用户的真实位置 endPoint,终点名称和坐标信息 怎么样?看了上面的使用方法是不是觉得很简单呢?腾讯位置服务开发路线规划插件的目的就是为了减少开发者开发成本,解放开发者生产力,所以才把这些复杂的路线规划业务封装成了插件,方便小程序开发者使用。 那么还犹豫什么呢?立即点击这里去体验使用吧! 另外,腾讯位置服务还推出了地铁图小程序插件,为用户提供查看各城市地铁线路的功能,还能帮用户检索到最优点地铁出行线路及每个站队的详情信息。 后续,腾讯位置服务还会开发更多的关于地图相关的小程序插件,还请各位开发者持续关注我们的服务商主页!
2019-08-05