- 芒果在线小程序webview展示公众号文章-快速滑动时候会崩溃闪退?Android
使用webview加载公众号文章 会闪退 有啥办法规避一下吗? 公众号文章地址: https://mp.weixin.qq.com/s?__biz=MzUyNzM3MTU3Ng==&mid=2247579669&idx=1&sn=7aabbb2902e0eedd8c98f7241591a7b4&chksm=fa036442cd74ed5402d9a896dfa738cb768c6b0c292f7a534a1c85e6365981bc2362351a1b0e#rd
01-31 - 能不能提供小程序跳转视频号小店的功能?
希望可以支持小程序跳转视频号小店!
02-25 - 小程序菜单复制链接功能复制出来的是永久链接还是临时链接?
wx.navigateToMiniProgram(Object object)小程序链接,当传递该参数后,可以不传 appId 和 path。链接可以通过【小程序菜单】->【复制链接】获取。
2023-09-19 - 解锁小程序中使用SVG新姿势
SVG 的优势 清晰度: 可以进行放大,而不失真 更小的文件体积 可扩展性,可以动态颜色 动效 可以添加动效 在小程序中使用 目前小程序 的image标签已经支持了 svg 的显示 [代码] <image src="./xx.svg"/> [代码] 如何动态的改变 svg 属性呢? 大体思路:把svg转成 base64 然后通过 image标签 src设置图片,再动态赋值svg颜色 把svg转成base64 如下一个svg 代码文件 [代码]<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="#eeeeee" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg> [代码] 转成base64,其实就是 对这个svg进行 encodeURIComponent 得到 如下代码 [代码]%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23eeeeee%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E [代码] 拼接base64 [代码] data:image/svg+xml;charset=utf-8,encodeURIComponent后的代码 [代码] 在对应svg属性上动态设置颜色,比如这里用到的是填充颜色 在js文件 data中定义 color 状态 在wxml中动态渲染 [代码] <image src="data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23{{color}}%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E" /> [代码] [代码]注意:这里的颜色 由于是已经被编码了,所以# 已经被转义了 %23, 直接写颜色数字即可[代码] 当然你也可以 去掉%23 自己实现一个内部方法 [代码] if (color && color.startsWith('#')) { return `%23${color.slice(1)}`; } [代码] 这样其实就实现了 svg的动态渲染,可是这种写法,写在wxml中 不是特别的优雅,那么如何重构下让我们的代码看起来更优雅呢? 把 svg 单独存放 支持动态返回 动态赋值 image src 属性 svg 动态函数 loading.svg.js 文件 [代码]export const loadingSvg = (color='#ddd') =>{ const svgXml = `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="${color}" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg>` return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgXml)}` } [代码] 逻辑层引入,setData [代码] onLoad(){ const { loadingSvg } = require('./loading.svg.js') const svgImg = loadingSvg('#eee') this.setData({svgImg}) }, [代码] 渲染层使用 [代码] <image src="{{svgImg}}"/> [代码] github 使用案例 demoFormpSvg
2022-04-30 - URL Scheme 和 URL Link 优化公告
为了帮助开发者更好地为用户提供服务,降低开发者使用 URL Scheme 和 URL Link 的成本,自 2023 年 12 月 19 日起,微信团队将对 URL Scheme 和 URL Link 进行如下优化: 1、新增明文 URL Scheme,开发者无需调用接口可自行拼接并且生成明文 Scheme; 2、取消 URL Scheme 和 URL Link 打开端一人一链的限制,支持同一条链接被多名用户访问; 3、新增打开端限制:每个小程序每天 URL Scheme 和 URL Link 总打开次数上限为 300 万次。 一、新增明文 URL Scheme开发者无需调用平台接口,可自行根据如下格式拼接 appid 和 path 等参数,作为 URL Scheme 链接。 weixin://dl/business/?appid=*APPID*&path=*PATH*&query=*QUERY*&env_version=*ENV_VERSION* 其中各参数含义如下: [图片] 注意: 1、为保护开发者,通过明文 URL Scheme 拉起的小程序(页面)必须要提前在「小程序管理后台 -> 设置 -> 隐私与安全 -> 明文 scheme 拉起此小程序」中进行声明; 小程序:配置能够通过明文 scheme 进入的小程序页面[图片] 小游戏:打开开关即可通过明文 scheme 拉起小游戏[图片] 2、通过明文 URL Scheme 打开小程序的场景值为 1286; 3、明文 URL Scheme 不受每天 50 万次的生成量限制; 4、明文 URL Scheme 没有有效期的概念,可长期有效; 5、明文 URL Scheme 没有一人一链的打开限制,支持一条链接同时被多名用户访问。 二、原 URL Scheme 升级为加密 URL Scheme,并支持自行拼接参数目前已对外提供的 URL Scheme 能力平滑升级为加密 URL Scheme,取消一人一链的限制,支持开发者自行在链接后拼接参数*CUSTOM PARAMETER*。 注意:之前通过平台接口生成的 URL Scheme 可继续使用,链接自动可支持多人打开。 URL Scheme格式 weixin://dl/business/?t=*TICKET*&cq=*CUSTOM PARAMETER* 其中参数含义如下: [图片] 注意:加密 URL Scheme 打开小程序的场景值保持不变,仍为 1065。 三、原 URL Link 升级为加密 URL Link,并支持自行拼接参数目前已对外提供的 URL Link 能力平滑升级为加密 URL Link,取消一人一链的限制,支持开发者自行在链接后拼接参数*CUSTOM PARAMETER*。 注意:之前通过平台接口生成的 URL Link 可继续使用,链接自动可支持多人打开。 URL Link格式: https://wxaurl.cn/*TICKET*?cq=*CUSTOM PARAMETER* 其中参数含义如下: [图片] 注意:加密 URL Link 打开小程序的场景值保持不变,微信外打开的场景值为 1194;微信内打开会调整为开放标签打开小程序,场景值为 1167。 四、调用规则调整1、加密 URL Scheme 和 URL Link 取消一人一链,支持一条链接同时被多名用户访问,生效后之前生成的链接被多名用户访问时,不会再报错; 2、每个小程序每天能够生成加密 URL Scheme 和 URL Link 共计 50 万条的限制不变,额外增加每个小程序每天在微信外,能够通过链接打开小程序共计 300 万次的打开量限制,其中链接包括加密 URL Scheme、加密 URL Link 和明文 URL Scheme ;若链接打开小程序的次数超过 300 万次/天,则无法通过链接在微信外拉起小程序; 3、URL Scheme (加密和明文)和 URL Link (加密)仅支持非个人主体小程序使用; 4、注意事项:平台有安全策略防止开发者的链接被黑灰产大量打开,可能导致达到访问上限无法正常通过链接打开小程序的问题; 5、查询方式:开发者可复用现有的查询方式对 URL Scheme 和 URL Link 进行打开额度查询和链接状态查询。 [图片]
2023-12-19 - 常驻云函数怎么设置 有啥用啊,压根看不到设置常驻云函数功能是不是废弃了?
常驻云函数有啥用
2022-10-17 - wx-open-launch-weapp开放标签,path 后面的参数怎样动态传给小程序?
请问,wx-open-launch-weapp开放标签,需要通过h5跳转小程序,并带流水号参数给小程序,path 这个值小程序页面后拼接的参数怎样动态传入呢?看文档只能是固定的么?也不像wx-open-launch-app标签还有个额外信息的属性。这里用的普通html5页面,求解答,谢谢,麻烦了
2021-05-21 - H5免鉴权跳转小程序常见问题解答
看到很多开发者在H5免鉴权跳转小程序这处于懵逼状态!!!! 我下边解释一下什么叫免鉴权????? 下方是官方文档内对免鉴权跳转能力的解释 [图片] 注意第二段话 静态网站网页在微信客户端打开时 也就相当于什么,相当于在微信中打开这个开放按钮时候才会免鉴权,此时是通过什么跳转的呢 没错是通过上边代码中 username="小程序原始账号 ID(gh_ 开头的)" path="要跳转到的页面路径" 此时根据这两个参数跳转的,此时和云函数半毛钱关系没得!!!! 此时相当于什么 相当与小程序内的 wx.navigateToMiniProgram(Object object) 此时你想说我就想单纯实现微信内H5的跳转,且想在路径传参怎么解决??? js获取静态网站url后的参数,原生js去替换username与path的值呀 let launchBtn = document.getElementById('launch-btn') launchBtn.setAttribute("path", "XXXXXXXXXX"); //HTML 属性 launchBtn.setAttribute("username", "XXXXXXXXXX"); //HTML 属性 在微信以外的渠道中都需要走云函数去请求拿到 openlink 或自建网站鉴权调用接口获取openlink 那些发帖想在小程序A云函数跳转B小程序的别想了不可以 在uniapp里腾讯云函数搞的也别想了,那边没有内置小程序的sdk,调用不动云函数的 乖乖去云开发里上传静态网站,并打开允许访问,云函数打开未登录允许调用 或者自建网站鉴权获取 下边发一条我自己开发的H5跳小程序链接,你们可以去测试 https://u.imvp.top/?s=jlqwyBFN ——本链接由微信小程序【链接工具】生成 [图片] 看到这里有人问我,我这个链接后边的参数是干什么的?这个参数是控制跳转哪一篇文章的加密id。 在任何情况下访问网站我都会去解析真实对应的文章链接是什么? 微信内我会将真实链接拼接在wx-open-launch-weapp属性内 if(res.result.url){ launchBtn.setAttribute("path", `/pages/basics/web_view.html?url=${encodeURIComponent(res.result.url)}`); //HTML 属性 } [图片] 此时文章链接已经拼接在属性path上了。微信内点击也会跳转到指定位置, 非微信内我会拿到openlink 重定向Url唤醒微信,实现外链跳转。 云函数端代码同样采用了官方示例代码,增加了openlink 入库绑定对应文章链接与加密参数,免得多次生成浪费! 2021年1月26日补充 因为目前URL Scheme进入小程序仅可进入正式版本,无法进入测试版,自己在开发时候专门做了参数埋点,上次测试后才二次对接参数提版,为此我将参数格式说明一下 //小程序端首页onLoad onLoad(options) { if(options.s=='u'){ //openlink解析后的参数标志位 uni.navigateTo({ url:`/pages/basics/web_view?id=${options.id}` }) } } //云函数端,生成openlink const result = await cloud.openapi.urlscheme.generate({ jumpWxa: { path: `/pages/index/index`, // 替换自己的url路径 query: id?'s=u&id='+id:'', // s=u 作为我自己的参数标志位 }, // 如果想不过期则置为 false,并可以存到数据库 isExpire: false, // 一分钟有效期 expireTime: parseInt(Date.now() / 1000 + 60), }) //存储跳转链接 saveOpenlink(id,result.openlink) return { ...result, //urlscheme返回的所有参数 主要使用result.openlink s:id, //加密文章ID url:articleData.data.url //文章链接 } //html端 供小程序环境访问跳转使用 let launchBtn = document.getElementById('launch-btn') launchBtn.setAttribute("path", `/pages/basics/web_view.html?url=${encodeURIComponent(res.result.url)}`); //HTML 属性
2021-01-26 - 使用代码加固后还能取消吗?对项目有影响吗 会导致项目不可用吗?编辑器执行加固后需要重新提审并发布吗
使用代码加固后还能取消吗?对项目有影响吗 会导致项目不可用吗?编辑器执行加固后需要重新提审并发布吗
2022-11-21 - 云开发控制台新出的内容安全功能如何使用?为何配置之后也没有作用
云开发新出了内容安全功能但是按照文档操作完全没有反应。 官方文档:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/Consecurity.html [图片] [图片] [图片] 而且在小程序管理后台中的内容风控也没作用,到底应该如何使用? [图片] [图片][图片] [图片] 请教一下,到底该如何正确使用内容安全功能,让他发挥作用
2021-11-27 - #小程内容安全 小程内容安全躺过的坑-已躺过-自带的内容安全接口不灵敏。
2023-08-28 更新,补个后续 问题是小程序内容安全接口4个场景识别都是通过,但却是敏感内容,而且很多都识别不出来。 最后搞了个收费的(百度内容安全),然后做了个后台可以标记的功能,方便标记那些内容是安全、危险、或者错杀。 敢说90%以上小程序如果没有做人工审核或者没有接入第三方,都应该注意。 2023-08-14 更新,还是未通过,我就不懂这个涉政到底怎么个涉法? [图片] 一、起因->2023-07-03小程序能力被封禁 [图片] 详情截图: [图片] --------------------------------------------------------------------------割一刀----------------------------------------------------------------------------------------- 看到以上内容没有明白为什么违规了,如实去客服核实了一下。 [图片] [图片][图片] 第一次没有理解到位,只验证了输入上联页面做了处理,然后第一次申诉直接提交了过去,说做了验证的,被打回; 后面看了再次看客服消息,调整了下,文本框改为失去光标调用内容安全验证,验证只要不是pass提示并清空用户输入的内容, 第二次申诉提交了过去,还是被打回; [图片] [图片] 【材料】--检测到输入内容有问题就提示,并清空用户输入内容 [图片][图片] 二、两次申请机会没了,没有入口了,找客服了解,并重新改了一版。 [图片] [图片] [图片] 【改版后页面】 [图片] 要了代码截图,然后让我等3个工作日,要等三个工作日,我也没有嫌着,我找了下之前警告的提醒可以发起申诉,如是也提起了申诉,但不管用,还是为通过。 [图片] 三、等了三天后,再来找客服,一个是反馈问题,一个我也试了其他小程序,内容检测没有我做的到位为啥没有事?? [图片] [图片] [图片] [图片] [图片] [图片] 得到的结果还是没有通过,因为没有明说哪里的问题, 我对这三句话反复琢磨,看看我的小程序还有哪些地方可以改进。 四、于是又改了一版,分享的页面,小程序卡片的图片都不用默认截图,小程序内容安全接口各个场景验证一遍,只要一个没有过,就提示内容敏感, 最终结果如下: 1检查文本内容安全接口把各个场景(1 资料;2 评论;3 论坛;4 社交日志)都调用一遍,发现输入敏感词失去光标后验证提示,并清空违规内容; 2.分享页卡片改为指定图片处理; [图片][图片] 然后今天2023-08-03 ,我又去找客服, [图片] [图片] 这次为7个工作日内核实,等吧! 未完待续.....
2023-08-28 - 云开发控制台的“内容安全”,按照指示添加规则后,没有生效
[图片][图片][图片]
2022-11-23 - 在云开发控制台配置了内容安全规则,怎么过了一段时间不生效了?
平台监测小程序存在信息安全风险的提醒,然后我在这里配置了内容安全规则,测试的时候没有问题,可以重写违规内容。但过了几天平台第二次检查时又不生效了?[图片]
2023-05-15 - 小程序中图片二维码、小程序码,长按识别支持的情况
因为看到最近还有人刷到这篇文章还有收藏的,所以特别说明一下: 以下是2021年5月31日时候测试的结果,并不一定与现在的情况相符。现在啥情况,我也不知道,已经不咋做小程序了。所以大家实际使用时候,请大家还是再测测。 上面这段话更新于2021年10月11日 下面是原文 ==================================================================================================================== 最近小程序中的图片支持长按识别了,总结一下几种情况下: 测试时间:2021-5-31 微信版本:8.0.6 当前时间最新 image标签 + show long press menu <image src="https://img.qr.com/qr.jpg" style="width: 100%;" mode="widthFix" show-menu-by-longpress="{{true}}"></image> ✅ 识别小程序码 - ✅ 跳转小程序 ✅ 识别群二维码 - ❌ 跳转到加群页面 ✅ 识别名片二维码 - ❌ 跳转到加好友页面 ❌ 识别小程序二维码 wx.previewImage ✅ 识别小程序码 - ✅ 跳转小程序 ✅ 识别群二维码 - ✅ 跳转到加群页面 ✅ 识别名片二维码 - ✅ 跳转到加好友页面 ❌ 识别小程序二维码 web-view ✅ 识别小程序码 - ✅ 跳转小程序 ✅ 识别群二维码 - ✅ 跳转到加群页面 ✅ 识别名片二维码 - ✅ 跳转到加好友页面 ❌ 识别小程序二维码 总结,目前微信已经开放了在小程序中长按识别。但是似乎还有一些bug,image标签可以识别到,但是点了没反应。
2021-10-11 - 目前小程序同一Url Link能否支持多人访问?
[图片]
2023-10-23 - 小程序胶囊里面的复制链接 可不可以用代码生成?
需要跳转到一个页面传不同的参数展示不同的票详情,可不可以通过动态传参数的方式生成这个链接,有这个api吗? [图片][图片]
2023-11-14 - 获取云存储永久有效url地址?
[图片] 在使用cloud.getTempFileURL获取的url地址过一段时间后失效了,查看了获取的字符串和云存储的下载地址比较后发现,少了sign之后的内容。想问下怎么才能在一开始就获得云存储的下载地址?
2022-04-23 - 如何获取云存储内照片的永久有效url链接地址?
wx.cloud.getTempFileURL 这个API 获取的好像是临时URL 如何获取云存储内的图片详情的下载地址!
2021-01-25 - getTempFileURL设置maxAge为30秒不生效?
https://developers.weixin.qq.com/community/develop/doc/0008004a2a84004119a863f2956c00 参考以上链接,设置maxAge为30秒,但是一直不过期(浏览器缓存已清除,也换了浏览器测试),所以无法测试,如果我设置了30天,是否真的也不过期,还是会提前过期。 有官方更明确的答案吗? [图片] 后面认真看了下getTempFileURL的官方描述,在核对下我的存储权限“所有用户可读”,所以算“公有读”,能不能因此判断我的文件就是不需要设置maxAge,因为本身就不会过期。 [图片] [图片]
2023-05-31 - 多张图片(数组里)怎么上传至云存储并获取它们的真实链接(永久有效)
多张图片(数组里)怎么上传至云存储并获取它们的真实链接(永久有效)
2018-09-16 - 关于微信小程序备案填写证件住所不通过
https://developers.weixin.qq.com/miniprogram/product/record/record_faq.html [图片] 小程序备案,在【验证备案类型】界面下的【证件住所】一直提示“【主体证件住所】工商数据对比不通过,如带有标点符号或字母,请检查中、英文标点符号和字母大小写”的信息,但是截图文中填写的都是中文及数字,并未包含标点或特殊符号,麻烦检查下代码是否有bug?
2023-10-19 - 云开发实战:实现短信跳小程序
先看效果 [视频] 小程序支持短信跳转小程序了,可以说是打开了一个巨大的流量入口。 效果过程分析 从短信到网页从网页到小程序那么就涉及到两个点 发送短信网页跳转实现步骤分析 先要有个网页,可以跳转到小程序然后发送短信,短信内容包含地址具体实现步骤 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 - 云开发环境静态H5页面,跳转到不同小程序不同页面的实现
客户有需求,要短信或者其它场景中的链接静态H5页面,触发打开自家的小程序,官方也提供了 静态网站 H5 跳小程序技术文档(https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/jump-miniprogram.html ),不过里面只描述跳转到小程序本身绑定的云开发环境,那么是多个小程序共享的云开发环境如何实现跳转呢,该文档并没有说。 为每个小程序购买一个云开发环境当然是可以,然而,成本似乎似乎太高,不合算,为了给客户节省成本,就给一个小程序买了云开发环境,按照跨账号环境共享设置,实现共享给同主体下的不同小程序(参考:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/resource-sharing/)。 接下来我把最终结果和解决方案,贴出来共享。 场景:首先同公司名下有4个小程序:A、B、C、D,只为A小程序购买了最基础的云开发环境19块,开通静态网站,添加跨账号共享功能,实现访问同一个静态H5页面携带不同参数,打开B、C、D不同小程序的指定页面,无论是微信里还是在手机浏览器均可以跳转。 实现代码: 1、云函数public编写(参考并改进 https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/jump-miniprogram.html ) // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() switch (event) { case 'getUrlScheme': { return getUrlScheme(event) } } return 'action not found' } async function getUrlScheme(event) { if(event.appid) { var clb = cloud.openapi({ appid: event.appid }); } else{ var clb = cloud.openapi; } return clb.urlscheme.generate({ jumpWxa: { path: event.path, query: event.query, }, // 如果想不过期则置为 false,并可以存到数据库 isExpire: false, // 一分钟有效期 expireTime: parseInt(Date.now() / 1000 + 60), }) } 2、静态H5页面:jump.html <html> <head> <title>H5打开小程序</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <script> window.onerror = e => { console.error(e);alert('发生错误:' + e);} </script> <!-- 调试用的移动端 console --> <!-- <script src="https://cdn.bootcss.com/eruda/1.2.4/eruda.min.js"></script> --> <!-- <script>eruda.init();</script> --> <script> function getQueryParam(key) { const reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i'); const r = window.location.search.substr(1).match(reg); if (r != null) { return decodeURI(r[2]); } return null; } //设置 资源环境ID以及绑定的appid var resAppId = '环境宿主Appid';// <!-- replace --> var resEnv = '资源环境ID'; // <!-- replace --> //资源方其它小程序组【AppID,原始id,名称,缺省打开路径】 var appIDs = [ ['wxe6ddf673521da8f0','gh_e4025e37c422','小程序A','pages/webview'], // <!-- replace --> ['wxb1abf1b1fe25f8c6','gh_a4cbe6b9f17f','小程序B',''], // <!-- replace --> ['wx24911b4d9b6971c9','gh_e544c578a3ef','小程序C',''], // <!-- replace --> ['wx6fecac42503a957b','gh_81a6106a84ce','小程序D',''] // <!-- replace --> ]; ////////////////////////////// var launchIdx = getQueryParam('id') || 0; var pagepath = launchIdx== 0 ? appIDs[launchIdx][3] : ""; pagepath = pagepath ? pagepath+(getQueryParam('url') ? "?url="+encodeURIComponent(getQueryParam('url')):"") : ""; pagepath = pagepath ? pagepath : (getQueryParam('page') ? getQueryParam('page'):""); </script> <!-- weui 样式 --> <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css" /> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script> <style>.hidden{display:none}.full{position:absolute;top:0;bottom:0;left:0;right:0}.public-web-container{display:flex;flex-direction:column;align-items:center}.public-web-container p{position:absolute;top:25%}.public-web-container a{position:absolute;bottom:40%}.wechat-web-container{display:flex;flex-direction:column;align-items:center}.wechat-web-container p{position:absolute;top:40%}.wechat-web-container wx-open-launch-weapp{position:absolute;bottom:40%;left:0;right:0;display:flex;flex-direction:column;align-items:center}.desktop-web-container{display:flex;flex-direction:column;align-items:center}.desktop-web-container p{position:absolute;top:40%} </style> </head> <body> <div class="page full"> <div id="public-web-container" class="hidden"> <p>正在唤起微信小程序...</p> <a id="public-web-jump-button" href="javascript:" class="weui-btn weui-btn_primary weui-btn_loading" onclick="openWeapp()"> <span id="public-web-jump-button-loading" class="weui-primary-loading weui-primary-loading_transparent"><i class="weui-primary-loading__dot"></i></span>点击唤起小程序</a> </div> <div id="wechat-web-container" class="hidden"> <script> document.write('<p>请点击下方按钮</p>'); document.write('<wx-open-launch-weapp id="launch-btn" username="'+appIDs[launchIdx][1]+'" path="'+pagepath+'">'); document.write(' <template><button style="width: 240px; height: 45px; text-align: center; font-size: 17px; display: block; margin: 0 auto; padding: 8px 24px; border: none; border-radius: 4px; background-color: #07c160; color:#fff;">打开微信小程序</button></template>'); document.write('</wx-open-launch-weapp>'); </script> </div> <div id="desktop-web-container" class="hidden"><p class="font-size:26px;">请在手机上打开本链接</p></div> </div> <script> function docReady(fn) { if (document.readyState === 'complete' || document.readyState === 'interactive') { fn();} else {document.addEventListener('DOMContentLoaded', fn);} } docReady(async function() { var ua = navigator.userAgent.toLowerCase() var isWXWork = ua.match(/wxwork/i) == 'wxwork'; var isWeixin = !isWXWork && ua.match(/MicroMessenger/i) == 'micromessenger'; var isMobile = isDesktop = false; if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) { isMobile = true } else { isDesktop = true } var isAndroid = ua.indexOf('android') > -1 || ua.indexOf('Adr') > -1; var isOS = ua.indexOf('iPhone') > -1 || ua.indexOf('iPad') > -1 || ua.indexOf('Mac') > -1; if (isWeixin) { var containerEl = document.getElementById('wechat-web-container'); containerEl.classList.remove('hidden'); containerEl.classList.add('full', 'wechat-web-container'); var launchBtn = document.getElementById('launch-btn'); launchBtn.addEventListener('ready', function (e) { console.log('开放标签 ready'); }); launchBtn.addEventListener('launch', function (e) { console.log('开放标签 success'); }); launchBtn.addEventListener('error', function (e) { console.log('开放标签 fail', e.detail);}); wx.config({ debug: false, appId: appIDs[launchIdx][0], // <!-- replace --> timestamp: 0, // 必填,填任意数字即可 nonceStr: 'nonceStr', // 必填,填任意非空字符串即可 signature: 'signature', // 必填,填任意非空字符串即可 jsApiList: ['chooseImage'], // 必填,随意一个接口即可 openTagList:['wx-open-launch-weapp'], // 填入打开小程序的开放标签名 }) } else if (isDesktop) { // 在 pc 上则给提示引导到手机端打开 var containerEl = document.getElementById('desktop-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'desktop-web-container') } else { //腾讯云开发的免鉴权调用 var containerEl = document.getElementById('public-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'public-web-container') var c = new cloud.Cloud({ identityless: true, resourceAppid: resAppId, // 资源方宿主 小程序的AppID resourceEnv: resEnv, // 资源方环境ID }) await c.init(); window.c = c; var buttonEl = document.getElementById('public-web-jump-button') var buttonLoadingEl = document.getElementById('public-web-jump-button-loading') try { await openWeapp(() => { buttonEl.classList.remove('weui-btn_loading'); buttonLoadingEl.classList.add('hidden'); }) } catch (e) { console.log('error',e) buttonEl.classList.remove('weui-btn_loading') buttonLoadingEl.classList.add('hidden'); throw e } } } ) async function openWeapp(onBeforeJump) { var c = window.c const res = await c.callFunction({ name: 'public', // 宿主环境中的云函数,注意开启权限 data: { action: 'getUrlScheme', appid: appIDs[launchIdx][0], path : pagepath }, }) if (onBeforeJump) { onBeforeJump(); } location.href = res.result.openlink; } </script> </body> </html> 调用方式:https://xxxx.xxx.xxx/jump.html?id=1&path=my/xwt-apply/xwt-apply (修改成你云环境静态网页的域名) id=1 表示jump.html页面中小程序B,path 表示小程序B中的具体页面。 你可以尝试修改成自己的,实现传参跳转到不同小程序页面。希望对你有帮助!
2023-07-10 - 小程序开发——微信外环境静态h5跳转小程序
写在前面如果你想要自主开发,但没有云开发相关经验,看官方文档仍旧无法从浏览器环境h5页面调起小程序,那么可以来学习下本教程。 可以看下本文发表时间,所贴示例代码均为实际demo代码,能成功调起小程序的。 不太方便录制视频,这里就以图文形式全流程介绍如何实现微信外环境静态h5跳转小程序。 一.先贴下官方开发文档,虽没能成功调起小程序,也是可以先看下的1.官方开发文档参考:静态网站 h5跳小程序 官方文档只提供了关键代码,但是没说怎么上传静态文件,怎么上传云函数。 2. 微信开放社区知识库:云开发短信跳小程序(自定义开发版)教程 有视频教程,有demo,有介绍怎么上传静态网站、云函数,但是视频demo跟文中提供的github源码不一致,反正我是看的有点懵。 二.自己摸索,博采众长,终于在浏览器中拉起小程序了工单提不了,提问没人回,做微信开发最头大的就是这个。后边百度搜索了好久,找到了比较简单可行的方法,这里梳理了下,即使没用过云函数的也可以参考本文操作。 经验证,本示例可以在微信内外环境中调起微信小程序。 三. 需要准备的工具、材料 微信开发者工具、非个人主体并且已认证的(微信认证)小程序。 四. 操作步骤: 1.开通云开发服务、云开发权限设置、开通静态网站功能: 1-1 开通云开发服务,微信开发者工具——云开发 [图片] 创建云开发环境,设置环境名称、付费方式(默认预付费,可以选按量付费,有一定免费额度的,无论个人开发调试还是公司项目使用,选按量付费就好了) [图片] 开通成功之后,概览界面右侧,能看到环境id,记住这个id,后边配置h5页面会用到。 [图片] 1-2 设置云开发权限,设置——权限设置,未登录用户访问云资源权限设置,勾选未登录用户访问权限: [图片] 1-3 开通镜头网站,菜单栏”更多“——静态网站——开通 [图片] [图片] 扫码确认,等初始化,可能需要点时间,无需等待,开通成功会有短信通知,可以先进行后边步骤 [图片] 2.创建云开发小程序、设置云开发环境录用户访问云资源权限设置 2-1 创建云开发小程序: 如果小程序已经是云开发服务了,那就可以直接用了。如果不是,那就创建一个云开发小程序。 这个小程序只是用来上传云函数的,appid要填要开通云开发功能的小程序,即h5要跳转的小程序。另外后端服务要选微信云开发,至于模板,随便选个及就行。只是为了实现跳转小程序这个功能,上传云函数而已,上传之后就没用了(除非要更新云函数)。 [图片] 云开发小程序创建好之后,可以看到有 cloudfunctions 和 miniprogram 两个目录。cloudfunctions 目录就是我们创建、上传云函数要用的目录了。 [图片] 2-2 设置云开发环境: cloudfunctions 目录上边单击鼠标右键,设置当前环境,选择前边创建的云环境 [图片] 3.创建云函数 请注意,这里的创建云函数不是在云开发控制台直接创建的,是需要通过小程序创建并部署的。虽然控制台也可以直接创建,但是不能直接用。别问我怎么知道,踩过这个坑而已。 3-1 下载官方云函数示例代码(点击下载),源码目录如下: [图片] 3-2 将 cloudfunctions 目录中的 public 文件夹复制到云开发小程序项目 cloudfunctions 目录下: [图片] 3-3 编辑 public/index.js 文件,将 getUrlscheme函数中的path改成要调起小程序的页面路径。 [图片] 相关代码: // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() switch (event.action) { case 'getUrlScheme': { return getUrlScheme() } } return 'action not found' } async function getUrlScheme() { return cloud.openapi.urlscheme.generate({ jumpWxa: { path: '/pages/login/index', // query: 'i=a', }, // 如果想不过期则置为 false,并可以存到数据库 isExpire: false, // 一分钟有效期 expireTime: parseInt(Date.now() / 1000 + 60), }) } 4.安装依赖 鼠标放到public目录上,单击鼠标右键,快捷菜单选中"在内建终端中打开",然后运行 npm install 安装依赖 [图片] 5.上传部署云函数: public 目录上,单击鼠标右键,选择”上传并部署:云端安装依赖(不上传node_modules), [图片] 部署后,打开云开发控制台——云函数,就能看到前边创建部署的云函数了: [图片] 等几秒钟,云函数就能部署成功了。 6.修改云函数权限: 云开发控制台——云开发——云开发权限,自定义安全规则,点击右侧的“修改” [图片] 点允许所有用户访问,这样所有用户都可以免鉴权通过h5调起小程序了 [图片] 可选的进阶的云函数安全规则模板(只放开了让 public 云函数支持未登录访问) { // * 为通配符,表示对所有函数适用 "*": { // invoke 表示调用权限控制 // auth 包含鉴权信息,如果是未登录模式,则 auth == null "invoke": "auth != null" }, // 函数名,该规则优先级会高于通配符 "public": { // 表示允许所有来源调用,包括未登录用户 "invoke": true } } 7.编写及上传h5静态页面到云开发环境 编辑前边下载的 h5/jump-mp.html 文件,将所有标注<!-- replace -->的地方都修改成真实的内容就好了。示例代码如下: <html> <head> <title>打开小程序</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <script> window.onerror = e => { console.error(e) alert('发生错误' + e) } </script> <!-- weui 样式 --> <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css"> </link> <!-- 调试用的移动端 console --> <script src="https://cdn.jsdelivr.net/npm/eruda"></script> <script>eruda.init();</script> <!-- 公众号 JSSDK --> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <!-- 云开发 Web SDK --> <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script> <script> function docReady(fn) { if (document.readyState === 'complete' || document.readyState === 'interactive') { fn() } else { document.addEventListener('DOMContentLoaded', fn); } } docReady(async function() { var ua = navigator.userAgent.toLowerCase() var isWXWork = ua.match(/wxwork/i) == 'wxwork' var isWeixin = !isWXWork && ua.match(/micromessenger/i) == 'micromessenger' var isMobile = false var isDesktop = false if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) { isMobile = true } else { isDesktop = true } if (isWeixin) { var containerEl = document.getElementById('wechat-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'wechat-web-container') var launchBtn = document.getElementById('launch-btn') launchBtn.addEventListener('ready', function(e) { console.log('开放标签 ready') }) launchBtn.addEventListener('launch', function(e) { console.log('开放标签 success') }) launchBtn.addEventListener('error', function(e) { console.log('开放标签 fail', e.detail) }) wx.config({ debug: false, // 调试时可开启 appId: 'wxad8exxxx', // <!-- replace --> timestamp: 0, // 必填,填任意数字即可 nonceStr: 'nonceStr', // 必填,填任意非空字符串即可 signature: 'signature', // 必填,填任意非空字符串即可 jsApiList: ['chooseImage'], // 必填,随意一个接口即可 openTagList: ['wx-open-launch-weapp'], // 填入打开小程序的开放标签名 }) } else if (isDesktop) { // 在 pc 上则给提示引导到手机端打开 var containerEl = document.getElementById('desktop-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'desktop-web-container') } else { var containerEl = document.getElementById('public-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'public-web-container') var c = new cloud.Cloud({ // 必填,表示是未登录模式 identityless: true, // 资源方 AppID resourceAppid: 'wxad8exxxx', // <!-- replace --> // 资源方环境 ID resourceEnv: 'cloud-mall-2gs0uxxxx', // <!-- replace --> }) await c.init() window.c = c var buttonEl = document.getElementById('public-web-jump-button') var buttonLoadingEl = document.getElementById('public-web-jump-button-loading') try { await openWeapp(() => { buttonEl.classList.remove('weui-btn_loading') buttonLoadingEl.classList.add('hidden') }) } catch (e) { buttonEl.classList.remove('weui-btn_loading') buttonLoadingEl.classList.add('hidden') throw e } } }) async function openWeapp(onBeforeJump) { var c = window.c const res = await c.callFunction({ name: 'public', data: { action: 'getUrlScheme', }, }) console.warn(res) if (onBeforeJump) { onBeforeJump() } location.href = res.result.openlink } </script> <style> .hidden { display: none; } .full { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .public-web-container { display: flex; flex-direction: column; align-items: center; } .public-web-container p { position: absolute; top: 40%; } .public-web-container a { position: absolute; bottom: 40%; } .wechat-web-container { display: flex; flex-direction: column; align-items: center; } .wechat-web-container p { position: absolute; top: 40%; } .wechat-web-container wx-open-launch-weapp { position: absolute; bottom: 40%; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; } .desktop-web-container { display: flex; flex-direction: column; align-items: center; } .desktop-web-container p { position: absolute; top: 40%; } </style> </head> <body> <div class="page full"> <div id="public-web-container" class="hidden"> <p class="">正在打开 “xxxx”...</p> <!-- replace --> <a id="public-web-jump-button" href="javascript:" class="weui-btn weui-btn_primary weui-btn_loading" onclick="openWeapp()"> <span id="public-web-jump-button-loading" class="weui-primary-loading weui-primary-loading_transparent"><i class="weui-primary-loading__dot"></i></span> 打开小程序 </a> </div> <div id="wechat-web-container" class="hidden"> <p class="">点击以下按钮打开 “xxxx”</p> <!-- replace --> <!-- 跳转小程序的开放标签。文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html --> <wx-open-launch-weapp id="launch-btn" username="gh_783bxxxx" path="/pages/login/index"> <!-- replace --> <template> <button style="width: 200px; height: 45px; text-align: center; font-size: 17px; display: block; margin: 0 auto; padding: 8px 24px; border: none; border-radius: 4px; background-color: #07c160; color:#fff;">打开小程序</button> </template> </wx-open-launch-weapp> </div> <div id="desktop-web-container" class="hidden"> <p class="">请在手机打开网页链接</p> </div> </div> </body> </html> 关键修改点在这三处: [图片] [图片] [图片] 8上传h5页面到云开发环境: 云开发控制台——更多——静态网站——文件管理,点击上传文件,选择上边改好的h5页面即可 [图片] 上传成功: [图片] 9.测试验证、配置域名: 静态网站窗口,选择”网站配置“,可以看到已经分配了测试域名,可以直接复制这个页面到手机浏览器中直接打开,如果前边操作没有遗漏的话,就能在浏览器中调起微信小程序了。 [图片] 四.开发中遇到的异常情况排雷: 1.-501000:environment not found 未找到环境,检查html文件中云环境id配置,直接复制云开发控制平台概览中的环境ID就好了,如下: [图片] [图片] 2.-501023: permission denied 没有权限,云开发控制平台——设置——权限设置——勾选”未登录用户访问权限“就好了 [图片]
2022-06-28 - H5页面分享不显示自定义标题和图片?
原因是H5用旧的方式申请JSAPI分享权限,后台返回无权限,因此分享的时候不能自定义,建议开发者接入新的分享方式:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html ,使用新的wx.updateAppMessageShareData和wx.updateTimelineShareData接口来设置分享数据。
2020-04-23 - 请问从外部直接跳转微信小程序时,怎样可以不出现中间页面
1,我们在外部做了个H5,需要跳转到微信小程序,但是现在跳转时会出现一个中间页面:请问如何才能不出现这个页面,直接跳转到微信小程序 [图片] 2,我看有个应用是在别的APP里,直接选择是否跳转,然后点击就跳过去了,没有我们的那个中间页面 [图片] 求大神解救!
2021-06-24 - 打开另一个小程序,需要用户确认跳转
请问同一个企业认证的多个小程序跳转,如何做到相互打开,不需要用户确认跳转
2023-10-19 - 社区每周 | 小程序链接生成与使用规则调整、公益编程挑战赛报名及上周问题反馈(2.28-3.04)
各位微信开发者: 以下是小程序链接生成与使用规则调整公告、公益编程挑战赛报名及上周我们在社区收到的问题反馈的处理进度,希望与大家一同打造更好的小程序生态! 小程序链接生成与使用规则调整公告 为确保小程序链接合理使用,自 2022 年 4 月 11 日起,URL Scheme 和 URL Link (以下统称为 “链接”)接口能力规则将进行以下调整: 每个 URL Scheme 或 URL Link 有效期最长 30 天,均不再支持永久有效的链接、不再区分短期有效链接与长期有效链接;链接生成后,若在微信外打开,用户可以在浏览器页面点击进入小程序。每个独立的链接被用户访问后,仅此用户可以再次访问并打开对应小程序,其他用户无法再次通过相同链接打开该小程序;单个小程序每天生成链接数(URL Scheme 和 URL Link 总数)上限为 50 万条。调整详情可点击查看原公告:《小程序链接生成与使用规则调整公告》 公益编程挑战赛报名开启 公益编程挑战赛以云开发系列产品(包含云开发、云托管、微搭低代码)为技术平台,以社会公益为主题,招募广大开发者组成 3~4 人的团队,创作具有应用潜力和社会价值的小程序/公众号网页项目,最高可获得 50 万奖励。预选报名将于 2022 年 4 月 1 日截止,快来报名,用技术创造更大的影响力! 赛事报名: [图片] 点此了解赛事详细规则 上周问题反馈和处理进度(2.28-3.04) 已修复的问题某场景下,数组中被删除的元素没有删除而是变成 null 的问题 查看详情 开发者工具开启按需注入后,组件样式污染页面样式的问题 查看详情 云开发单日出现多项系统异常错误的问题 查看详情 修复中的问题 getLatestUserKey 密钥过期后仍返回过期密钥的问题 查看详情 昵称填写在页面跳转时没有消失的问题 查看详情 微信团队 2022.3.11
2022-03-11 - 如何生成可跳转到小程序的外链?
请问各位大佬,微信或腾讯生态下有相关生成可跳转到小程序的外链工具吗?
2023-07-05 - 发货信息管理怎么关闭?
请问一下为什么我们正儿八经的开店,给我们小程序来了一出发货信息管理,怎么关闭? 如果不能关闭,那么请问一下怎么样才能和我们小程序商城的订单物流信息同步?? 现在的问题是我们商城快递签收了,客户也确认收货了,你们腾讯的小程序后台还是没任何物流状态更新。把我们的货款卡着好玩是吧?? 再不处理我直接报警,再不行直接起诉,妈的真的特么火大,什么个垃圾!!
2023-05-10 - 小程序发货信息管理服务 有预售商品 订单很多 如果通过服务器API请求 特殊发货报备?
我的小程序 会有很多订单属于预售订单,有的订单得等1-2月等东西做好了才会发货,此时需要 小程序发货信息管理服务 特殊发货报备 ,此时需想问下如何通过API请求向微信后台发送这些单子。如果由用户复制这些单子再去小程序后台页面填写这些很麻烦且容易操作失误。想问下我该如何解决这个问题。
2023-09-10 - 关于小程序待接入订单发货管理的通知?
今天客户莫名其妙出现了一个通知,提示要对接发货管理。导致客户小程序无法正常运营。 [图片] 我们是三方服务,请问如何给客户关闭这个发货管理,让客户小程序能力恢复正常。急急急!!!! 客户appid:wx13fb7ca5f171bc42
2023-09-21 - 小程序的发货管理的订单,必须在48小时内发货吗?
最近被开通了发货管理,我看运营规范是48小时内,可以超过48小时吗?我们货物时间周期长。
2023-09-04 - 实体物品电商都需要接入订单发货管理?
我们小程序是第三方的电商平台,现在微信已经把我们的支付功能封了,需要我们对接直连商户号才行。 我刚才测试了下微店等第三方电商平台的微信小程序,他们仍然使用的是微信支付的间联(服务商)方式啊,为什么只封我们?
2023-09-25 - Skyline | 快速搞定复杂的分享海报
在小程序中生成海报是一种非常有效的推广方式 用户可以使用小程序的过程中生成小程序海报并分享给他人 通过海报的形式,用户可以直观地了解产品或服务的特点和优势 [图片] 常见绘制海报方式 目前,小程序海报有两种常见的实现方式: · canvas 绘制海报 · 服务端绘制海报 这两种方式各有千秋 canvas 绘制海报使用 canvas 绘制海报主要有以下几个步骤 1、创建 [代码]canvasContext[代码] 2、获取网络图片的本地路径 3、绘制图片、文字等到 [代码]canvas[代码] 4、调用 [代码]wx.canvasToTempFilePath[代码] 导出图片 尽管 canvas 绘制功能强大,但实际使用中,这些操作看似简单,但调试起来却比较麻烦 而且面对一些复杂的排版时,使用 canvas 绘制相较于使用 CSS 绘制来说困难许多 除此之外,canvas 的宽高有最大限制,超出限制则会绘制空白 服务端绘制 小程序也可以通过调用服务端接口,将需要生成海报的数据传递给服务端, 由服务端使用 Canvas API 等第三方库来生成图片。 然而,这种绘制方式需要走网络请求,如果量大会给服务器带来一定的成本压力。 此外,对于复杂排版的实现,使用 Canvas 绘制也有一定的难度。 尽管小程序海报虽然好用,但是当遇到要求比较高的设计稿需要还原海报时,对小程序开发者来说是一个十分让人头疼的问题 考虑到海报在小程序中使用的广泛性,我们把开发者的烦恼交给官方来处理~ 小程序官方推出了 [代码]snapshot[代码] 组件,可以直接将小程序 wxml 导出图片。 snapshot 生成海报 当使用 canvas 或 服务端绘制海报遇到复杂排版时,如 圆角、百分比、自定义字体 等等,实现比较困难。 但是使用 wxml 实现却很简单 👇 下面的例子我们使用 wxml 实现海报 <view class="snapshot-box"> <view class="poster-container"> <view class="poster-header"> <image /> ... </view> <view class="description"> ... </view> <view class="footer"> ... </view> </view> </view> [图片] 接着,我们就可以导出海报啦,使用非常简单: 1、用 [代码]snapshot[代码] 组件包裹海报的 wxml 2、调用 [代码]takeSnapshot[代码] 获取图片数据 3、调用 [代码]fs.writeFileSync[代码] 将海报数据写入本地文件 4、调用 [代码]wx.saveImageToPhotosAlbum[代码] 将海报保存到本地 <snapshot id="view"> <!-- 这里是要海报的 wxml --> </snapshot> <button bindtap="tap">保存海报</button> tap() { this.createSelectorQuery().select("#view") .node().exec(res => { const node = res[0].node // 保存海报 node.takeSnapshot({ type: 'arraybuffer', format: 'png', success: (res) => { const f = `${wx.env.USER_DATA_PATH}/hello.png` const fs = wx.getFileSystemManager(); // 将海报数据写入本地文件 fs.writeFileSync(f, res.data, 'binary') this.setData({ img: f }) // 把海报图片保存到本地 wx.saveImageToPhotosAlbum({ filePath: f }) } }) }) } 最后我们来看看使用 [代码]snapshot[代码] 组件生成海报的效果吧~ [图片] 除了普通尺寸分享海报之外,对于 canvas 无法搞定的超长海报,[代码]snapshot[代码] 后续也会支持超长海报的导出~ [图片] 你的小程序也有海报生成需求吗? 赶紧 mark 下这个 代码片段 来接入使用吧~
2023-09-06 - 小程序渲染引擎Skyline小试牛刀--快书
今年年初,在官方文档上看到小程序团队要推出一款性能逼近原生的渲染引擎Skyline,就一直在关注。刚好最近打算做一款新的阅读小程序,作为一名独立开发者,对于性能和用户体验的追求是永无止境的,于是我决定用纯Skyline打造这款小程序。 当然,这个项目里面所用到的skyline特性只是冰山一角,并非全部,更多酷炫的特性请前往官方文档查阅。 接下来,我会结合快书小程序,从以下几个方面,逐条阐述关于skyline特性(快书项目中所用到的)的理解与应用: 效果演示。如何开启Skyline。新版组件swiper。新版组件scroll-view。全新组件snapshot。增强特性worklet动画。增强特性手势系统。增强特性自定义路由。增强特性共享元素动画。希望对于刚接触Skyline,或者想要了解Skyline的同学有所帮助。当然,如有错误或遗漏,欢迎在评论区批评指正,不胜感激。 一、效果演示 [图片] 二、如何开启Skyline 开启Skyline的方式非常简单,只需要在app.json文件中,加入以下配置即可(这里是全局Skyline,若只打算指定页面开启,则在指定页面的json文件中配置即可): "renderer": "skyline", "lazyCodeLoading": "requiredComponents", "rendererOptions": { "skyline": { "defaultDisplayBlock": true, } }, "componentFramework": "glass-easel", 三、新版组件-Swiper 旧版的Swiper基于webview的,在性能上有所局限,特别是当swiper-item的数量动态不断增加的情况下。当然,也可以自己想办法去优化,比如做懒加载和缓存,但相对来说比较麻烦。而Skyline版本的Swiper性能会大幅度提升,首先渲染引擎本身的性能提升了,另外官方也做了缓存的功能,只需要通过定义cache-extent的值,就能轻松定义缓存区域大小,例如值为 1 则表示提前渲染上下各一屏区域。 [图片] 用法上,和webview版本没有太大区别(这里就不放代码了),只需注意不要使用某些webview独有的特性即可。 四、新版组件-Scroll-view 同样,旧版的scroll-view也基于webview的,滚动元素过多的时候会有明显卡顿,当然也是可以通过虚拟Dom的方式自行优化。然而,Skyline版本的scroll-view官方已经实现了只会渲染在屏节点的特性,大大提升了滚动的流畅度,真正做到了开箱即用。 用法上,有以下几个点要注意的。 指定type属性,有2个可选值,分别为:list和custom,对应的是列表模式和自定义模式。如是普通列表,list即可,如果是稍微复杂的列表,比如常见的瀑布流表现形式(类似小红书那样),则可使用custom。只有直接子节点才能根据是否在屏来按需渲染。即你不能把你的列表项,都放在同一个父级view中,而是应该直接放在scroll-view组件下。 // 错误的方式: <scroll-view type="list" scroll-y> <view> <view class="item" wx:for="{{dataList}}" wx:key="id"></view> </view> </scroll-view> // 正确的方式 <scroll-view type="list" scroll-y> <view class="item" wx:for="{{dataList}}" wx:key="id"></view> </scroll-view> // 正确的方式 <scroll-view type="custom" scroll-y> <list-view> <view class="item" wx:for="{{dataList}}" wx:key="id"></view> <list-view> </scroll-view> 另外,上面提到了瀑布流的问题,实现方式也很简单,官方提供了一个叫做grid-view的组件,只需定义它的type="masonry"即可,但若是在webview下,除了性能不理想以外,还会有一些小BUG,比如我在社区提的这个问题:grid-view masonry 在webview模式下经常会出现大块区域的空白。在Skyline下,就不会出现此类问题。 [图片] <scroll-view type="custom" scroll-y> <grid-view type="masonry" main-axis-gap="15" cross-axis-gap="15"> <view wx:for="{{dataList}}" wx:key="id"></view> </grid-view> </scroll-view> 五、全新组件Snapshot 我们常常会有分享精美海报的需求,但由于海报上的内容是动态,仅仅使用一张图片分享达不到我们的目的。在以往,我们可能会使用到wxml-to-canvas,通过绘制 canvas ,导出图片。现在,在Skyline下(基础库3.0.0以上),实现此需求就非常简单。只需要将我们要分享的内容包裹在snapshot组件下就行。 [图片] // wxml: <snapshot id="target"> <view>content</view> </snapshot> // page: Page({ onReady() { this.createSelectorQuery() .select("#target") .node() .exec(res => { const node = res[0].node node.takeSnapshot({ type: 'arraybuffer', format: 'png', success: (res) => { fs.writeFileSync(savePath,res.data,'binary'); //图片保存至本地 wx.showShareImageMenu({ //唤起分享图片的界面 path:savePath }) }, fail(res) {} }) } }) 六、增强特性-worklet动画 worklet动画相比传统的方式,流畅度提升了不少,但如何使用呢?常见的普通动画无非是对于页面元素的平移,缩放,旋转等变换。那么,要让一个元素动起来,只需要做以下2件事: 将页面元素的样式与某个变量进行绑定,变量值的变化会自动触发样式的更新。实时动态地改变这个变量。结合快书的例子(下拉时,让页面缩小,松手后,页面弹回),来看一下具体的实现步骤。 [图片] 首先,如何绑定样式与参数呢?通过官方提供的一个applyAnimatedStyle函数: // Wxml: <view id="#box">content</box> // Page: this.scale = shared(1); //这里是定义一个共享变量,即可在UI线程和JS线程间同步的变量。 this.applyAnimatedStyle(`#box`, () => { 'worklet'; // 声明这是一个worklet函数 return { transform: `scale(${this.scale.value})`, }; }); // 1、这里使用共享变量是为了让后续改变这个变量时,worklet的函数能捕获到。 // 2、#box你要动起来的元素 // 3、当this.scale.value变化时,会自动触发函数体的执行,从而改变#box的样式 第二步,下拉时,根据下拉的偏移量,改变这个scale的值。 this.scale.value = (evt.deltaY / 100) * 0.15; // 这里的evt.deltaY是下拉时的位置偏移量,然后根据偏移量按比例计算缩放的值 // 如何获取这个下拉偏移量?下一小节讲手势系统时会讲到 第三步,松手时,复原scale的值。 this.scale.value = timing(1, { duration: 300, easing: Easing.ease }); // timing函数表示:在300毫秒内,scale.value会逐渐变成1 // easing: Easing.ease 表示缓动的方式,具体可参考https://easings.net // 如何知道已经松手了?下一小节讲手势系统时会讲到 更多动画参考请查阅官方文档。 七、增强特性-手势系统 还是上面那个例子,我们只说了下拉时根据下拉的偏移量改变scale的值,那如何得到下拉的偏移量呢?这里就涉及到了手势系统。下面讲讲如何让一个元素能响应拖动,缩放等手势。 说回上一小结的例子,我们只需要讲#box元素包裹在手势组件vertical-drag-gesture-handler即可。更多示例可查阅官方文档 // wxml: <vertical-drag-gesture-handler worklet:ongesture="handlePan"> <view id="box"></view> <vertical-drag-gesture-handler> // page: handlePan(evt) { 'worklet' if (evt.state === GestureState.ACTIVE) { // 拖拽时 if (evt.deltaY > 0) { // 下拉 this.scale.value = Math.max(this.scale.value - (evt.deltaY / 100) * 0.15, 0.85); } else { // 上拉 this.scale.value = Math.min(this.scale.value - (evt.deltaY / 100) * 0.15, 1); } } else if (evt.state === GestureState.END || evt.state === GestureState.CANCELLED) { // 拖拽结束或取消 this.scale.value = timing(1, { duration: 300, easing: Easing.ease }); } }, 然而,当手势组件与scroll-view等可以滚动的组件嵌套时,会出现冲突的问题。比如,同上一小节的示例,为了让文章内容过长时可以滚动,我们需要将文章的内容放在scroll-view中。当scroll-view已经滚动到顶部,再继续下拉的话,应当触发手势组件的拖拽事件,即缩放页面。相反,则继续滚动scroll-view。 [图片] // wxml: <vertical-drag-gesture-handler tag="pan" worklet:ongesture="handlePan" shouldResponseOnMove="shouldPanResponse" simultaneousHandlers="{{['scroll']}}"> <vertical-drag-gesture-handler tag="scroll" native-view="scroll-view" shouldResponseOnMove="shouldScrollResponse" simultaneousHandlers="{{['pan']}}"> <scroll-view type="list" scroll-y bindscroll="handleContentScroll">文章内容</scroll-view> </vertical-drag-gesture-handler> </vertical-drag-gesture-handler> // page: // 处理scroll-view的滚动事件,获取scrollTop的值 handleContentScroll(evt) { 'worklet' this.scrollTop.value = evt.detail.scrollTop; }, // return false 则表示scroll-view不再响应滚动事件 shouldScrollResponse(evt) { 'worklet'; const { deltaY } = evt if (this.scrollTop.value <= 0 && deltaY > 0) { //scroll-view已经滚动到顶部,继续下拉时 this.pan.value = true; return false; } if (this.scale.value < 1 && deltaY < 0) { //#box已经被缩放,继续上拉时 this.pan.value = true; return false; } this.pan.value = false; return true; }, shouldPanResponse() { 'worklet' return this.pan.value; // true表示响应手势组件的拖拽事件,false则不响应 }, 八、增强特性-自定义路由 以往在webview中,路由的的过渡动画仅支持从右到左,较为单调。在skyline之后,我们可以自定义路由的过渡动画了,比如常见的淡入淡出,从底部弹起等。比如以下这个例子,从首页点击图片,会跳转到分享的页面,这里就是用自定义路由实现的淡入效果。 [图片] 自定义路由的使用相比前几个特性稍微复杂一点,这里官方讲的更为具体和清晰,可查阅官方文档。唯一要注意的一点是,只有连续的skyline页面跳转时,才会有效果。 九、增强特性-共享元素动画 还是上面的例子,当从首页点击图片跳转到分享页面时,图片看起来像是从首页飞到了分享页,这里便是使用到了共享元素动画。我同时也做Flutter的开发,所以这里看起来非常类似Flutter的hero动画或者叫飞行动画。 使用方式也类似于Flutter。将2个页面的相似组件都用share-element组件包括起来,并且使用相同的key即可。再配合自定义路由,可使得飞行动画看起来非常的丝滑。 // A页面: <share-element key="唯一key"> <image src="imagePath" mode="aspectFill" /> </share-element> // B页面: <share-element key="唯一key"> <image src="imagePath" mode="aspectFill" /> </share-element> // 有几个要注意的地方 // 1、两个个页面的share-element组件必须使用相同的key。 // 2、key是唯一的,即同一个页面中,不能出现重复的key。 // 3、image不要写死宽高,应百分比100%,具体宽高数值写在share-element组件上。 有一个常见的问题,A页面是一个列表,B页面是详情页,列表中的数据都是通过接口从后台返回的,由于共享元素的key又不能重复,那么这个key怎么定义?一般后台返回的数据都会有一个唯一标识,假设为ID,我们可以用这个ID当作Key。 但是,另一个问题来了,如果数据是后台接口返回的,然后通过setData的方式响应到页面,那么很有可能B页面的首帧获取不到这个Key,因为这时候接口请求可能还未完成,那么动画也是不会生效的。针对这种情况,官方也提供了一种方式: 共享元素动画需保证下一个页面首帧即创建好 [代码]share-element[代码] 节点,并设置了 key,用于计算目标位置。如果是通过 [代码]setData[代码] 设置的,可能会错过首帧。针对这种情况,可以 使用 Component 构造器构造下一个页面,只要在组件 [代码]attached[代码] 生命周期前(含)通过 [代码]setData[代码] 设置上去,就会在首帧渲染 十、总结 Skyline还有一些其他有趣的特性,大家感兴趣的话可以查阅官方文档。总的来说,相比起webview,skyline对性能的提升是显而易见的,并且,一些在webview很难实现的效果,在skyline的基础上,也能轻易实现,开箱即用。目前,skyline还在不断地迭代中,还有许多的新特性还在评估和开发中,相信之后的版本会更完善更好用。 最后,大家多多使用快书呀,球球了~ [图片]
2023-09-01 - 云开发怎么实现商家转账到零钱的功能,有demo吗?
以前叫企业转账到零钱,当时网上能找到比较完整的demo,现在换成了v3接口了,找不到完整的资料,能找到的都是其他开发语言的demo,试问下有没有像下面这种方法在云开发中实现的例子 [图片]
2022-06-14 - 微信支付的文档、工具升级,我们需要你的反馈!
亲爱的微信支付商户: 大家好!微信支付官网团队一直在不懈努力,致力于提供更优质的文档和开发工具,助力开发者更高效地接入微信支付。此次,我们以【商家转账到零钱】为例,进行了一次全面的优化改版,期待借此机会能够听到您的宝贵意见和建议。 首先,我们重新设计了文档的内容架构,使其更加清晰、易于理解;同时,对所有文档进行了整理和优化,确保它们都符合新的内容架构,提升了文档的可读性和易用性;此外,我们还重点对开发工具进行了优化,帮助开发者提升接入效率。 在此向您介绍一下主要的改动点: [图片] 商家转账到零钱文档体验地址:新版 丨 旧版 问卷链接:https://wj.qq.com/s2/12944401/f109/ 期望收集的问题方向: 文档结构的优化:文档的组织方式是否清晰,是否容易找到所需信息?文档的易读性和准确性:文档的语言是否清晰易懂且准确无误,是否有过于复杂或难以理解的部分?开发者教程和指南:关键概念、开发指引、开发工具和库、最佳实践这些内容是否能够满足开发需求,是否需要更多的教程和指南,以帮助开发者更好地使用和理解我们的产品?示例Demo的质量:示例Demo是否足够清晰,是否能有效地帮助理解文档内容?文档的反馈机制:文档的反馈机制是否有效,是否容易提交反馈和建议?其他的意见和反馈 我们深知,每一次的改进和优化,都离不开您的支持和建议。我们期待您的反馈,一起让微信支付变得更好!
2023-08-25 - 更新3.0基础库,上传图片文件之类的api 不触发了?
更新3.0基础库后,wx.chooseImage、wx.chooseMedia、wx.chooseMessageFile 这些上传之类的api,在真机和模拟器上都不触发了。2.33.0基础库正常
2023-08-16 - 关于小程序隐私保护指引设置的公告
为规范开发者的用户个人信息处理行为,保障用户的合法权益,自2023年9月15日起,对于涉及处理用户个人信息的小程序开发者,微信要求,仅当开发者主动向平台同步用户已阅读并同意了小程序的隐私保护指引等信息处理规则后,方可调用微信提供的隐私接口。 开发者首先需确定小程序是否涉及处理用户个人信息,如涉及,则需配置用户隐私授权弹窗,且仅有在平台《小程序用户隐私保护指引》中声明了所处理的用户个人信息,才可以调用平台提供的对应接口或组件。(隐私相关接口) 隐私协议设置整体流程参考下方指引: 一、设置《小程序用户隐私保护指引》 开发者需在「小程序管理后台」设置《小程序用户隐私保护指引》 [图片] [图片] 二、填写《小程序用户隐私保护指引》 [图片] 只有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将无法调用成功。隐私接口与对应的处理的用户个人信息关系可见:小程序用户隐私保护指引内容介绍 三、配置用户隐私授权弹窗 微信提供了wx.onNeedPrivacyAuthorization(function callback) 接口,意为用户触发了一个微信侧未记录过同意的隐私接口调用,开发者可通过响应该事件选择提示用户的时机。此外,微信还提供了 wx.requirePrivacyAuthorize(Object object) 接口,可用于模拟触发 onNeedPrivacyAuthorization 事件。 小程序开发者可自行设计提示方式与触发时机,详细文档可查看隐私协议开发指南。 仅有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将直接禁用。 [图片] (参考样例) 四、如要进行代码提审,开发者需先自行声明是否有采集用户隐私,如有,则需在提审页面-「用户隐私保护设置」选择“采集用户隐私” [图片]
2023-09-18 - chooseMedia和chooseImage特定手机都失败?
报错信息:chooseMedia:fail privacy permission is not authroized 我的手机测试突然之间一直报这个错,之前都是好的。试了其他机型也都是正常的。 微信最新版 机型:nova10
2023-08-15 - 刚收到通知获取手机号收费开始了?
[图片] https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getRealtimePhoneNumber.html https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/paymentManage.html 未来:旧版本接口依然可以使用,只是需要收费而已,不想做改动的交钱就行了。如果想用新API方法就去改吧,多花一分钱。 可能支持的省钱办法: 授权手机号后,服务端将openId、手机号进行绑定。用户onLaunch打开小程序的时候通过wx.login获取code去解密openId,同时由于服务端已经绑定过手机号,所以可以使用该手机号进行登录,并同步返回token、jwtToken等登录态。这样可以做到用户冷启动小程序时自动登录上,减少使用授权的逻辑。业务按钮点击后 先调用wx.login,如果返回token则进行后续业务,如果没返回则弹出自定义弹窗,弹窗内点击按钮再进行手机号授权。(也可以在部分页面onLoad里wx.login),这个场景因为会延长流程,所以产品说不考虑,先直接打开页面就登录上,你们的各自看各自的业务场景吧。然后有四个疑问: 充值购买次数后会,如果小程序被封禁了,充值的金额是否可退款。购买数量是否支持按量付费?如果次数用完了,未购买新的次数,用户端的表现是什么?如果次数用完了,之前文档说的余量20%、10%、5%时会发模板消息提醒,文档相关现在已经删除了,是否还会发?[图片] ———————————————————————————————————————————————— 今天看了下文档做了改动: 退款规则:若购买有误,且未正式开始使用资源包前,可以在支付成功后的7天内申请退款。款项将在3-5个工作日内从原支付路径返回;若资源包已经开始使用(使用1次及以上),则不能申请退款;若支付成功后超过7天,未发起退款申请,亦不能再申请退款。 那么小程序被封了应该是不退的。不确定,等官方回复次数用完了,用户授权不会弹出授权弹窗,会返回一个errNo:1400001,用户判断等于这个errNo的时候跳转到自己的账密登录页面。不确定,等官方回复———————————————————————————————————————————————— 据了解老版本的快速验证组件(获取手机号),180天才会发送短信验证一次,为啥能每次授权都收费0.03元。 社区搜了一张图,180天没验证的应该会弹这个,不是说是短信运营成本么?为啥不是第180天验证那次费用让我们付,而是每次授权都付? [图片] 手机号授权改造后的效果: 打开职位详情页:优先调用接口判断openId是否绑定过。 如果未绑定:使用button的open-type=“getPhoneNumber”,点击报名弹出手机号授权,授权成功后与openId进行绑定落库。 如果已绑定,页面通过变量判断使用wx.login静默授权,同时服务端拿到绑定的手机号后进行登录操作,同步返回登录态(token/jwtToken)。 退出登录页面增加解绑操作(服务端解除openId与手机号的绑定),此时用户再次点击报名,就会弹出手机号授权,方便用户切换手机号。 [视频]
2023-07-27 - 小程序提交代码报错 Error: cannot push because a reference ?
[图片]
2021-07-07 - 【重要】关于11月25日商家转账到零钱新产品策略正式上线的通知
尊敬的微信支付商户: 为保障商户资金安全、优化商户体验,商家转账到零钱产品将于11月25日起对现有产品能力逐步调整,主要包括: 1、批次转账的明细数量上限调整; 2、商户基于转帐场景提交资料,平台将基于不同场景确定商家转账限额及使用方式; 3、支持下载5年内账单并优化业务账单格式,同时支持下载2年内电子回单。 详情请参考:商家转账产品文档 特别提示:其中使用转账给员工、付款给合作伙伴的场景,需要在商户平台-商家转账到零钱添加收款用户openid及姓名,仅可向列表中已添加且姓名一致性校验通过的用户转账。 为了不影响您的使用,请尽快在商户平台添加需要付款的用户openid及姓名,否则调用转账接口会返回报错信息“未配置收款用户列表,请前往商户平台-产品中心 - 商家转账到零钱 - 转账场景中添加”或“该openid不在收款用户列表中,请前往商户平台-商家转账到零钱-转账场景中添加”。 财付通支付科技有限公司 2022年11月25日
2022-11-29 - 企业付款到银行卡已下线?
微信支付商户经营工具-「企业付款到银行卡」, 这个功能是不是没有拉? 后台都没有显示了,只有付款到零钱
2022-08-05 - 几行代码实现小程序云开发提现功能
先看效果: [图片] 纯云开发实现,下面说使用步骤: 一:开通商户的企业付款到领取功能 说明地址: 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 - 云开发企业付款到零钱IP地址如何填
[图片] [图片] [图片] 我用的是云开发,由于没有IP ,在申请开通企业付款到零钱时,用公网IP,ping api.mch.weixin.qq.com的IP后就出来这个结果,能不能给个指导
2019-01-16 - 境外小程序支付攻略(云开发篇),看这一篇就够了。
一、前言 境外微信支付的实现方式,与国内支付的方式大不同,即不是V2,也不是V3,而是经由海外支付服务商,通过间连的方式来实现的。(香港地区融合钱包除外)。 以下专门介绍具体的实现攻略: 二、开通 首先,你需要先了解当地有哪些第三方支付服务商,然后选择其中一家,接入他们提供的支付服务,从而实现微信支付的开通。 1、全球第三方支付服务商: https://pay.weixin.qq.com/index.php/public/wechatpay_en/partner_search#/ 找到当地服务商的联系方式,与他们联系,他们会告诉你所需提供哪些资料,并帮你开通商户号,接入微信支付; 资料信息包括: 营业执照 翻译认证 公司银行账户 法人及联系人信息 其他信息等。 2、香港融合钱包 融合钱包以下不做介绍,技术方面和v3支付完全一样。 技术方案可以参考: https://pay.weixin.qq.com/wiki/doc/api/wxpay/en/pages/MiniProgramPay_fw.shtml 三、第三方服务商提供的支付服务 每个第三方服务商所提供的支付服务各不相同,以下分别举例: Yaband Pay (Europe PayPro B.V.): 微信支付, 支付宝, PayPal, SOFORT, IDEAL, 银联支付, List 2paynow (Europe Sepay B.V.): 微信支付, 支付宝 VR Payment (Europe CardProcess GmbH): 微信支付, 支付宝 Supay (Australia): 微信支付, 信用卡支付 其他地区第三方支付服务商,提供哪些服务,需要向他们咨询。 注: 1、境外小程序对除微信支付之外的支付方式,卡得并不象国内这样严,一般情况下,PayPal, 信用卡等方式,都支持并审核通过,支付宝会稍难点; 2、小程序里实现其他支付方式的唯一方案:生成支付链接,复制到剪贴板,在手机浏览器或微信浏览器中打开,完成支付流程。 四、第三方支付账号和接入文档 第三方帮你接入微信支付后,会回复邮件给你,并提供相关支付信息: 1. 一个属于第三方的支付账号 比如Yaband pay,支付账号类似如下: ID: yourname@yoursite.com Key: f4f70db6107xxxxxxxb75efa1f1490b4 2paynow,支付账号类似如下: ID: 541102252 Key: osVx_0POxxxxF9h3JSCls 2. 该第三方支付服务商支付接入文档 比如Yaband pay 支付文档: https://www.yabandmedia.com/api/cn-api.html 比如2paynow支付文档,是由技术支持人员发送给你的PDF文档,具体内容暂不介绍。 3. 第三方支付服务商的商家管理后台 你可以登录该后台进行配置,并查看详细的订单数据。 [图片] 五、云开发实现境外支付 因为商家可以自主选择第三方支付服务商,所以我们在实现支付功能时,需要同时支付多个第三方支付服务商。 用云开发来实现,一般会有两种方式: (1)、每个第三方的支付功能,分别用一个云函数实现; (2)、一个云函数集成所有第三方支付功能;(我们采用的是该方式) 1、云函数pay.js: 按支付接口、支付服务商、支付方式分流: const cloud = require('wx-server-sdk') const rp = require('request-promise') const CryptoJS = require('crypto-js') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() exports.main = async (event, context) => { switch (event.action) {//根据小程序端传入的支付相关接口分流 case 'unifiedorder': return await unifiedorder(event)//统一下单 case 'queryorder': return await queryorder(event)//订单查询 case 'refund': return await refund(event)//退款 default: return 'wrong action' } } async function unifiedorder(event) { let res = await db.collection('config').doc('payment').get().catch() if (res && res.data) { } else return 'wrong action'//注意:res?.data在云函数中不支持 let config = res.data//云数据库中保存了支付配置信息 switch (config.pay_sp) {//根据支付服务商pay_sp来分流 case '2paynow': return await unifiedorder_2paynow(event, config) case 'yaband': return await unifiedorder_yaband(event, config) case 'cloudpay': return await unifiedorder_cloudpay(event, config) case 'supay': return await unifiedorder_supay(event, config) case 'vr': return await unifiedorder_vr(event, config) case 'v2': return await unifiedorder_v2(event, config) case 'v3': return await unifiedorder_v3(event, config) case 'v2sp': return await unifiedorder_v2(event, config)//v2服务商模式 case 'v3sp': return await unifiedorder_v3(event, config)//v3服务商模式 default: return 'wrong action' } } async function queryorder(event) { let res = await db.collection('config').doc('payment').get().catch() if (res && res.data) { } else return 'wrong action' let config = res.data switch (config.pay_sp) { case '2paynow': return await queryorder_2paynow(event, config) case 'supay': return await queryorder_supay(event, config) case 'vr': return await queryorder_vr(event, config) default: return 'wrong action' } } async function refund(event) { let res = await db.collection('config').doc('payment').get() if (res && res.data) { } else return 'wrong action' let config = res.data switch (res.data.pay_sp) { case '2paynow': return await refund_2paynow(event, config) case 'yaband': return await refund_yaband(event, config) case 'cloudpay': return await refund_cloudpay(event, config) case 'supay': return await refund_supay(event, config) case 'vr': return await refund_vr(event, config) default: return 'wrong action' } } 2、统一下单(Yaband pay为例) 接口文档:https://www.yabandmedia.com/api/cn-api.html // -------------------yaband----------------------------------------------------------- async function unifiedorder_yaband(event, config) { let notify_url = config.notify_url let user = config.mchid let key = config.key let timeStamp = parseInt(Date.now() / 1000) let payMethod = event.pay_method if (payMethod == 'wechat') {//微信支付 const wxContext = cloud.getWXContext() let param = { "user": user, "method": "v3.CreatePaymentsWechatMiniPay", "time": timeStamp, } let data = { "pay_method": "online", "sub_pay_method": "WeChat Pay", "order_id": event.out_trade_no, "amount": event.total_fee || "0.1", "currency": event.fee_type || config.currency, "description": event.body || "YabandPay pay", "demo": event.demo || "order", "timeout": "0", "notify_url": notify_url, "sub_app_id": wxContext.FROM_APPID || wxContext.APPID, "sub_open_id": wxContext.FROM_OPENID || wxContext.OPENID } let sign = getSign_yaband({ ...param, ...data }, key) let body = { ...param, sign, data } let res = await rp({ url: "https://mapi.yabandpay.com/Payments", method: 'POST', body, json: true }) let payment = res.data.parameters return { payment, sp_trade_no: res.data.trade_id } } if (payMethod == 'sofort') {//省略 } if (payMethod == 'alipay') {//省略 } if (payMethod == 'paypal') {//Paypal支付 let param = { "user": user, "method": "v3.CreatePayments", "time": timeStamp, } let data = { "pay_method": "online", "sub_pay_method": "PAYPAL/RECURRING", "order_id": event.out_trade_no, "amount": event.total_fee || "0.1", "currency": event.fee_type || config.currency, "description": event.body || "YabandPay pay", "demo": event.demo || "order", "post_email": "xin.liu@elbsino.com", "timeout": "0", "redirect_url": "https://www.klarna.com/de/", "notify_url": notify_url } let sign = getSign_yaband({ ...param, ...data }, key) let body = { ...param, sign, data } let res = await rp({ url: "https://mapi.yabandpay.com/Payments", method: 'POST', body, json: true }) let url = res.data.url return { url }//除微信支付外,都必须返回一个url:支付链接 } } async function refund_yaband(event, config) { console.log('refund_yaband') let notify_url = config.notify_url let user = config.mchid let key = config.key let timeStamp = parseInt(Date.now() / 1000) let param = { "user": user, "method": "v3.CreateRefund", "time": timeStamp, } let data = { "trade_id": event.trade_id, "refund_amount": event.refund_amount || "0.1", "refund_currency": config.refund_currency, "refund_description": event.refund_description || "test", "notify_url": notify_url } let sign = getSign_yaband({ ...param, ...data }, key) let body = { ...param, sign, data } console.log(body) return await rp({ url: "https://mapi.yabandpay.com/Payments", method: 'POST', body, json: true }) } function getSign_yaband(args, key, sa = []) {//每个支付服务商的签名方式都不一样 for (let k in args) sa.push(k + '=' + args[k]) sa = sa.sort() let signStr = sa.join('&') let hash = CryptoJS.HmacSHA256(signStr, key) return CryptoJS.enc.Hex.stringify(hash) } function getSign_2paynow(args, key) {//2paynow的签名算法。 let signStr = args.function + args.mid + args.timestamp + key let hash = CryptoJS.MD5(signStr, key) return CryptoJS.enc.Hex.stringify(hash) } 踩坑实录: 1、每次统一下单,都会产生三种订单号: 商家订单号 支付服务商的支付订单号 微信支付订单号 记住:一定要保存支付服务商的支付订单号,因为有些支付服务商在订单查询和退款接口中,只认该订单号。 2、每个服务商的签名方案是不同的,有的甚至不需要签名,这个需要分别对待。 六、支付成功异步通知 多个服务商的异步通知,用一个云函数来实现。 云函数:notify.js const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const _ = db.command exports.main = async (event, context) => { let res = await db.collection('config').doc('payment').get() switch (res.data.pay_sp) { case '2paynow': return await notify_2paynow(event) case 'yaband': return await notify_yaband(event) case 'cloudpay': return await notify_cloudpay(event) case 'vr': return await notify_vr(event)//vr暂时不支持回调通知 case 'supay': return await notify_supay(event) case 'v2': return await notify_v2(event) case 'v3': return await notify_v3(event) default: return 'wrong action' } } async function notify_2paynow(event) { console.log('notify_2paynow') let qs = event.queryStringParameters if (qs.merchant_trade_no) { } else return 'success' await db.collection('payment').add({ data: qs }).then(res => console.log(res)) if (qs.merchant_trade_no && qs.trade_status == 'TRADE_SUCCESS') { await onPaymentSuccess(qs.merchant_trade_no, qs.trade_no, qs.original_trade_no) } return 'success' } async function notify_yaband(event) { console.log('notify_yaband') if (event.body) { } else return { statusCode: 200, body: 'ok' } let body = JSON.parse(event.body) let pay = body.data await db.collection('payment').add({ data: pay }).then(res => console.log(res)) if (pay.transaction_id && pay.state == 'paid') { await onPaymentSuccess(event.outTradeNo) } return { statusCode: 200, body: 'ok' } } async function notify_cloudpay(event) {//云支付pay_cb console.log('notify_cloudpay') if (event.outTradeNo) { await db.collection('payment').add({ data: event }).then(res => console.log(res)) if (event.returnCode == 'SUCCESS' && event.resultCode == 'SUCCESS') { await onPaymentSuccess(event.outTradeNo) } } return { "errcode": 0, "errmsg": 'SUCCESS' } } 踩坑实录: 1、有些国外支付服务商,并不提供异步通知功能,比如VR Payment,这就很尴尬了,因为我们业务的流程里,是非常依赖这个功能的,因此,后期造成了大量的代码修改工作,建议大家从一开始就考虑这种情况。 七、币种选择 统一下单时,支持两种币种: 1、CNY 2、当地币种,比如EUR 境外退款,一般只能使用当地币退款,不支持RMB。 八、境外小程序云开发禁用后的选择 目前境外小程序的云开发功能已经被禁用,可以通过环境共享的模式支持以上云开发境外支付方案; 1、共享在禁用令之前的同主体小程序的云环境; 2、我们是采用开放平台第三方服务商批量云开发模式; 九、境外小程序相关文章 1、https://developers.weixin.qq.com/community/develop/article/doc/000aec921e4fd8a320ec0f9795bc13 2、https://developers.weixin.qq.com/community/develop/article/doc/0000e805af0900b37f6c900c356c13 3、https://developers.weixin.qq.com/community/develop/article/doc/000a68f1a24a687242eb2427556013 4、https://developers.weixin.qq.com/community/develop/article/doc/000860b434cd180a749b9268f51c13
2022-05-31 - 云支付的回调函数会一直被调用,这是正常的吗?
云支付的回调函数会一直被调用,这是正常的吗?没有调用支付,回调函数也会一直调用 // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database(); // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() if(event.returnCode == 'SUCCESS' && event.resultCode == 'SUCCESS') //两个都成功,支付完成 { var time = "444"; var order = await db.collection('orderlist').where({outTradeNo : event.outTradeNo}) .update({ data:{ info:{ paystate:'已支付', orderstate:'生产中', payTime:time } } }) } const res = {errcode:0, errmsg:'SUCCESS'} return res } [图片]
2022-02-09 - 首次调用云函数特别慢,怎么解决?
云函数只是一个很简单的加解密工具类,由于把密钥放在前端不安全,就打算把密钥和加密算法写在云函数中。 但是首次调用云函数慢的令人发指,在开发工具上倒还好,勉强能接受 [图片] 但是到手机上调用首次基本需要1s左右,然后后续接连调用就会很快了100-200ms左右吧。但是过一段时间再次调用又会出现1s左右的等待。 然后云函数调用日志中实际运行之间只有 2-3ms。 有什么好的建议吗 06/30 15:05:08:192 [KkcRsa][2] START 06/30 15:05:08:361 [KkcRsa][2] END 06/30 15:05:08:361 [KkcRsa][2] REPORT RequestId:80e256bc-d971-11eb-a778-525400e5615d Duration:3ms Memory:128MB MemUsage:12.101562MB 06/30 15:16:21:039 [KkcRsa][1] START 06/30 15:16:21:435 [KkcRsa][1] END 06/30 15:16:21:435 [KkcRsa][1] REPORT RequestId:11ec1fe4-d973-11eb-975b-525400c2bfee Duration:2ms Memory:128MB MemUsage:12.429688MB 06/30 15:17:01:080 [KkcRsa][0] START 06/30 15:17:01:445 [KkcRsa][0] END 06/30 15:17:01:445 [KkcRsa][0] REPORT RequestId:29cc1ac9-d973-11eb-975b-525400c2bfee Duration:1ms Memory:128MB MemUsage:12.750000MB
2021-06-30 - 为什么云函数第一次调用会很慢
为什么云函数第一次调用会很慢 ~ 因为前几天在群里问大家,同一个操作是写云函数还是答题小程序里面直接写 ~ 该问题源于我的答题小程序收藏和取消收藏的场景 ~ 另一个被我忽略的问题就是云函数第一次调用和非首次调用的效果是不一样的? ~ 一个云函数超过半小时左右没有用户访问,再次访问就要冷启动3秒左右,老问题了,一直没优化 ~ [图片] ~ 如上图所示 云函数存在冷启动和热启动的问题,这样就会导致不同的启动机制,响应用户的时间是不一样的,用户的直接感受就是第一次调用云函数会十分慢,而非首次调用的效果会好很多 更新于2021-09-07 ~ [图片] ~ ~
2021-09-07 - 一个云函数五行代码搞定云调用openapi
云调用接口如下: https://developers.weixin.qq.com/miniprogram/dev/api-backend/ 1、该文档中的几十个接口,全部可由下面5行代码实现: 2、同时支持共享环境下的云调用 云函数名:openapi index.js代码: const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async event => { let appid = cloud.getWXContext().FROM_APPID || cloud.getWXContext().APPID return await cloud.openapi({appid})[event.action](event.body) } 小程序端调用代码: onOpenapi: function () { wx.cloud.callFunction({ name: 'openapi', data: { action: 'urlscheme.generate', body: {} } }).then(res => { console.log(res) }) }, 将云调用相关的云函数合并成一个。 而且,极简。。。
2022-09-29 - 云函数调用反应速度很慢,如何优化?
云函数调用反应速度很慢,如何优化?云函数中只写了一个简单的加法,第一次调用Waiting(TTFB)2s多一点,后续几次也在600ms~1000ms浮动,
2020-07-28 - 云函数调用非常慢?
[图片] 如果TTFB达到5.64s,云函数代码里面也没啥复杂的计算,就一个上传,加iimgSecCheck云调用 [代码]exports.main = async (event, context) => {[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]const wxContext = cloud.getWXContext();[代码][代码] [代码][代码]const ret = await cloud.openapi.security.imgSecCheck({[代码][代码] [代码][代码]media: {[代码][代码] [代码][代码]header: {[代码][代码] [代码][代码]'Content-Type'[代码][代码]: [代码][代码]'application/octet-stream'[代码][代码] [代码][代码]},[代码][代码] [代码][代码]contentType: [代码][代码]'image/png'[代码][代码],[代码][代码] [代码][代码]value: Buffer.from(event.imageBuffer)[代码][代码] [代码][代码]}[代码][代码] [代码][代码]});[代码] [代码] [代码][代码]return[代码] [代码]ret;[代码] [代码] [代码][代码]} [代码][代码]catch[代码] [代码](err) {[代码][代码] [代码][代码]console.error(err);[代码][代码] [代码][代码]return[代码] [代码]err;[代码][代码] [代码][代码]}[代码][代码]}[代码]
2019-10-08 - [ios] map放进swiper中,第二个map里面内嵌的view不见了?
<swiper style="height: 600rpx;"> <swiper-item wx:for="{{arr}}" wx:key="*this"> <map style="height: 400rpx;"> <view style="width:100rpx;height:100rpx;background-color:yellow;">1{{item}}</view> </map> <view>这里用来滑动</view> </swiper-item> </swiper> 如上面代码所示 一共三个地图,第一张左上角还能看到1aa,切换swiper,到第二张就什么都看不见了,安卓一切正常。期望ios的表现能和安卓一样。 [图片] [图片]
2023-02-07 - swiper-item中定义absolute元素,安卓端该元素被swiper-item遮挡
[代码]<[代码][代码]view[代码] [代码]class[代码][代码]=[代码][代码]"main"[代码][代码]>[代码] [代码] [代码][代码]<[代码][代码]swiper[代码] [代码]style[代码][代码]=[代码][代码]"height:100vh"[代码] [代码]vertical[代码][代码]=[代码][代码]"true"[代码] [代码]bindchange[代码][代码]=[代码][代码]"swiperChange"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]class[代码][代码]=[代码][代码]"aelImage"[代码] [代码]src[代码][代码]=[代码][代码]"https://linker-automake-1256733046.file.myqcloud.com/1/1575599065.jpg"[代码][代码]></[代码][代码]image[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]class[代码][代码]=[代码][代码]"mImage"[代码] [代码]style[代码][代码]=[代码][代码]"display:block"[代码] [代码]src[代码][代码]=[代码][代码]"https://linker-automake-1256733046.file.myqcloud.com/1/1575599072.jpg"[代码][代码]></[代码][代码]image[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]view[代码] [代码]style[代码][代码]=[代码][代码]"position: relative;"[代码] [代码]class[代码][代码]=[代码][代码]"lSwipe"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]swiper[代码] [代码]circular[代码][代码]=[代码][代码]"false"[代码] [代码]class[代码][代码]=[代码][代码]"lSwipe"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]swiper-item[代码] [代码]style[代码][代码]=[代码][代码]"background-color: red;z-index:100;"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]class[代码][代码]=[代码][代码]"lSwipe"[代码] [代码]src[代码][代码]=[代码][代码]"https://linker-automake-1256733046.file.myqcloud.com/1/1575599077.jpg"[代码][代码]> </[代码][代码]image[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]swiper[代码][代码]>[代码] [代码] [代码][代码]<[代码][代码]view[代码] [代码]style[代码][代码]=[代码][代码]"position:absolute;width:84rpx;top:41%;left:10rpx;z-index:99999;background-color:red;min-height: 100rpx;"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]bindtap[代码][代码]=[代码][代码]"clickLeft"[代码] [代码]mode[代码][代码]=[代码][代码]"widthFix"[代码] [代码]style[代码][代码]=[代码][代码]"width: 100%;"[代码] [代码]src[代码][代码]=[代码][代码]"../../images/left.png"[代码] [代码]bindload[代码][代码]=[代码][代码]"imageLoad"[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]image[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]view[代码][代码]>[代码] [代码] [代码][代码]<[代码][代码]view[代码] [代码]style[代码][代码]=[代码][代码]"position:absolute;width:84rpx;top:41%;right:10rpx;z-index:999999;background-color:yellow;min-height: 100rpx;"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]bindtap[代码][代码]=[代码][代码]"clickRight"[代码] [代码]mode[代码][代码]=[代码][代码]"widthFix"[代码] [代码]style[代码][代码]=[代码][代码]"width: 100%;"[代码] [代码]src[代码][代码]=[代码][代码]"../../images/left.png"[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]image[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]view[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]view[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]swiper[代码][代码]>[代码][代码]</[代码][代码]view[代码][代码]>[代码][代码].main{[代码][代码] [代码][代码]width[代码][代码]: [代码][代码]100%[代码][代码];[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]100[代码][代码]vh;[代码][代码] [代码][代码]overflow[代码][代码]: [代码][代码]hidden[代码][代码];[代码][代码] [代码][代码]position[代码][代码]: [代码][代码]relative[代码][代码];[代码][代码]}[代码] [代码]image{[代码][代码] [代码][代码]width[代码][代码]: [代码][代码]100%[代码][代码];[代码][代码]}[代码] [代码]/*小轮播尺寸*/[代码][代码].mSwipe{[代码][代码] [代码][代码]width[代码][代码]:[代码][代码]750[代码][代码]rpx;[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]424[代码][代码]rpx[代码][代码]}[代码] [代码]/*大轮播尺寸*/[代码][代码].lSwipe{[代码][代码] [代码][代码]width[代码][代码]:[代码][代码]750[代码][代码]rpx;[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]1052[代码][代码]rpx[代码][代码]}[代码] [代码]/*小图片尺寸*/[代码][代码].mImage{[代码][代码] [代码][代码]width[代码][代码]:[代码][代码]750[代码][代码]rpx;[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]424[代码][代码]rpx[代码][代码]}[代码] [代码]/*大图片尺寸*/[代码][代码].lImage{[代码][代码] [代码][代码]width[代码][代码]:[代码][代码]750[代码][代码]rpx;[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]1052[代码][代码]rpx[代码][代码]}[代码] [代码]/*小视频尺寸*/[代码][代码].video{[代码][代码] [代码][代码]width[代码][代码]:[代码][代码]750[代码][代码]rpx;[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]424[代码][代码]rpx [代码][代码]}[代码] [代码]/*整屏图尺寸*/[代码][代码].aelImage{[代码][代码] [代码][代码]width[代码][代码]:[代码][代码]750[代码][代码]rpx;[代码][代码] [代码][代码]height[代码][代码]: [代码][代码]1476[代码][代码]rpx[代码][代码]}[代码]上面的代码仅仅是复现bug所用。left.png可以可以直接去掉,在第二页的siwper-item中,红色框以及黄色框会被swiper-item遮挡的?是我姿势不对,还是其他? 测试图片仅仅用于测试。
2019-12-09 - ios端 swiper组件下的定位元素会被图片给覆盖,如何解决?
[图片] 理想效果是这样子的,但是滑动后(切换到下一件商品时) [图片][图片][图片] 就会变成如下问题,图片将本是定位在整个屏幕下方的文字给覆盖了,大佬们,如何解决啊?
2020-10-19 - scroll-view 横向滚动内容使用absolute定位第一个不显示出?
ios上面横向滚动定位在真机上面第一个显示不全[图片]
2023-03-13 - swiper组件内的position:fixed失效
求解:在swiper中使用position: fixed失效? 搜索过相关问题,官方没有回复。所以我生成了个代码片段,求解答...
2018-12-12 - 动态预加载 swiper-item 中的图片(延迟加载)
在使用 swiper 时,有些场景需要一次载入的图片太多,消耗资源的同时也会影响用户体验。 [图片] 自己的解决方法是这样的: 创建图片【地址数组】根据需要展示图片的总数创建一个空的数组,即用来页面绑定的【页面数组】初始化【页面数组】,为第 1、2 项赋值当向右滑动时,接着为【页面数组】的第 3 项赋值直到全部展示完毕[图片] 如果用户提前离开,就能避免未展示项图片的加载。 代码如下: <view class="s-page"> <swiper class="s-swiper" snap-to-edge="false" indicator-dots="true" bindchange="swiperChange"> <block wx:for="{{pageList}}" wx:key="*this"> <swiper-item class="s-swiper_item"> <view>{{item}}</view> </swiper-item> </block> </swiper> </view> Page({ /** * 页面的初始数据 */ data: { imageUrlList: ['url 1', 'url 2', 'url 3', 'url 4', 'url 5', 'url 6', 'url 7'], pageList: [], currentPageIndex: 0 }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { // 根据图片总数,创建需要渲染的空数组 let pageList = new Array(this.data.imageUrlList.length).fill(''); // 初始化渲染数组,载入第1张,并且预载入下一张 pageList[this.data.currentPageIndex] = this.data.imageUrlList[this.data.currentPageIndex]; pageList[this.data.currentPageIndex + 1] = this.data.imageUrlList[this.data.currentPageIndex + 1]; // 渲染页面 this.setData({ pageList: pageList }) console.log(this.data.pageList); }, swiperChange: function (e) { let oldPageIndex = this.data.currentPageIndex, // 之前展示的页面索引 newPageIndex = e.detail.current; // 滑动后新展示的页面索引 // 判断是否由用户触摸引起的 if (e.detail.source == 'touch') { console.log(newPageIndex); // 判断滑动方向 if (oldPageIndex < newPageIndex) { console.log('向右滑动'); // 判断是否到最后一张,并且地址信息是否为空 if (newPageIndex < this.data.imageUrlList.length - 1 && !this.data.pageList[newPageIndex + 1]) { this.data.pageList[newPageIndex + 1] = this.data.imageUrlList[newPageIndex + 1] console.log('预加载成功'); } // 渲染页面 this.setData({ currentPageIndex: newPageIndex, pageList: this.data.pageList }) } else if (oldPageIndex > newPageIndex) { console.log('向左滑动'); this.data.currentPageIndex = newPageIndex; } } console.log(this.data.pageList); } }) 如果不显示“面板指示点”,那么【页面数组】直接创建一个空数组即可。 自己的笨办法,如果有更好的方法,请不吝赐教!
2021-07-07 - 在swiper组件中的子元素想要使用position:fixed;bottom:0;,无效
在swiper组件中的子元素想要使用position:fixed;bottom:0;,固定在窗口的最下边,但是无效,加了z-index也无效,求解决
2021-06-03 - scroll-view组件下swiper的层级大于view层级在ios真机上
问题:在scroll-view组件下,设置view层级比swiper层级大,但是滑动过程中的swiper的层级会大于view层级 在机型ios13.2.2真机上有问题,在ios12.4.1真机没有问题
2019-11-12 - 做了个轮播 swiper-item层级覆盖问题 有大佬解答下吗?
[图片] 轮播的时候想让中间的图片层级最上面 现在被兄弟级的swiper-item覆盖了
2019-11-26 - 如何解决组件中轮播图swiper层级太高?
我想把轮播图置于底部但是发现,z-index 和cover-view都是失效
2022-01-23 - 双层 swiper 最内层 image 元素层级 ,iOS下有时候image层级会特别高,偶发性的?
swiper 全屏竖向滚动 ,元素是 swiper ,内层swiper元素是 图片横向滑动,iOS image下图片层级会特别高,偶发性的,有的image是正常的,有的image就会直接编程顶层,覆盖在了最顶层,好奇怪,如何解决? ----iOS真机可以复现 录屏如下, https://app.brainco.cn/starkids_mini/images/temp/RPReplay_Final1661244798.MP4 [图片]
2022-08-23 - swiper-item内有一个元素需要固定在底部,同时跟随swipe滑动,求思路
预期: 左右滑动的swiper,有一个元素需要在每个swiper-item内部固定底部位置(每个swiper-item的高度比较长,有上下滚动),同时这个元素要跟随每个swiper-item左右滑动。 内部元素设置position:fixed无效,查了一下,说是父元素有transform属性,子元素fixded就无效,但是需求摆在这里,求解决思路~~
2019-05-16 - 建议:swiper组件可以增加item划动位置的传入参数吗?
在页面swiper组件上方有遮罩层(如固定位置的文字等)时,可以跟踪手指在遮罩层的位置并传入swiper组件,从而实现透过遮罩层划动swiper-item。
2023-02-03 - 【优化】解决swiper渲染很多图片时的卡顿
最近再用swiper,当有超过20个SwiperItem就会出现卡顿。 查看了各种资料 比如 https://developers.weixin.qq.com/community/develop/doc/000068ff25ccf0bae4e76eab156c04 这个方案不错,但是切换的时候会出现一个突然的跳跃,因为swiper的current会变。 经过我自己很多尝试,发现一个完美解决方案。 核心思想就是把没有显示出来的dom元素尽量简化 比如用空 <SwiperItem/> 代替 const ITEM_LIMIMT = 3; <Swiper current ={currentIndex} onChange={onChangeSwiper} displayMultipleItems={1} duration={300} > {items.map((item: any, index: number) => currentIndex < index + ITEM_LIMIMT && currentIndex > index - ITEM_LIMIMT ? ( <SwiperItem key={index}> <ComplexComponent item={item}/> </SwiperItem> ) : ( /* 没有显示出来的元素用空SwiperItem 代替*/ <SwiperItem key={index} /> ), )} </Swiper>
2020-05-28 - 关于微信原生组件swiper 在IOS中可能造成滑动时swiper-item上内容消息的问题处理方案
最近在做一款类似小红书的小程序时,有使用到swiper及嵌套功能。 经过开发测试发现IOS在滑动时容易造成swipter-item上的内容丢失。 经过真机测试和调试发现,原生组件中存在IOS兼容的BUG。 具体操作看下图: [图片] 从上图中我们看到原生的swiper-item 真机调试时自带了position:abolute; IOS测试过程中发现,只要在真机调试上鼠标移动到对应的VIEW标签上文字就出现了。 于是在页面样式中加上: swiper-item{ position:fixed !import; } 问题成功解决。无论怎么上下左右滑动都不会出现swiper-item上文字消失的情况。 建议官方及时完善这个BUG。
2022-06-28 - swiper ,live-player+view,在iOS上划两下,view就被盖住,如何解决?
swiper 里有live-player,然后盖了一层view,在iOS上划两下,view就被live-player覆盖了!android和开发工具上好的😓 下面是swiper-item里的布局 <view class="player-container"> <live-player class="player-fill" id="{{member}}" object-fit="contain" src="rtmpLinkPull" autoplay="true" mode="RTC"> </live-player> <view class="userInfo-small"> <block wx:if="{{!mic}}"> <image src="/assets/eshare/mic-off-view.svg" class="mic-off"></image> </block> <view class="name">{{memberInfo[member].name}}</view> </view> </view>
2022-10-28 - wxml-to-canvas没有fontWeight的相关支持?
wxml-to-canvas怎么设置fontWeight?还不支持透明度?作者你粗来 : (
2020-10-26 - [开盖即食]利用“云函数”生成小程序码和将buffer流转化图片
[图片] 为什么要用云函数来做这个? 前端一条龙不求人(后端) 利用官方资源来快速解决问题,白piao不香吗? 之前也有不少人分享过云函数生成小程序码的方法,这里我为大家总结下不同方法的区别和优缺点~ [图片] 开盖食用思路: 小程序端请求 --> 云函数API小程序 --> 返回图片的buffer --> 把buffer转化成图片 1、云函数部分 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event, context) => { try { const result = await cloud.openapi.wxacode.get({ path: event.url, //传入需要配置的url,但是新版本不能传参param width: 300 }) console.log(result) return result } catch (err) { console.log(err) return err } } [代码] 新建一个云函数名字为getWxacode,示例是openapi.wxacode.get的方法,这个参数可以直接跳转小程序内部对应的page。 注意: 与 wxacode.createQRCode 总共生成的码数量限制为 100,000,如果你需要大量生成码,请谨慎调用。且新版本只能传path,不能传参param~ 2、页面部分 [代码]Page({ data: { imgUrl: "" //图片地址 }, getWxacode() { wx.cloud.init(); let self = this; wx.showLoading({ title: '请求云函数' }) // 调用云函数 获取 内容 wx.cloud.callFunction({ name: 'getWxacode', data: { url: "pages/home/index" }, success: res => { console.log('云函数调用成功', res); let bufferImg = "data:image/png;base64," + wx.arrayBufferToBase64(res.result.buffer); self.setData({ //imgUrl: res.result.fileID imgUrl: bufferImg }); wx.hideLoading(); }, fail: err => { console.error('云函数调用失败', err) } }) } }) [代码] [图片] 可以获得一个返回值,里面有个图片的buffer,转化buffer即可展示图片内容。 3、转化buffer流成图片的三种方法 3.1 直接将buffer转化Base64 [代码]console.log('云函数调用成功返回值:', res); let bufferImg = "data:image/png;base64," + wx.arrayBufferToBase64(res.result.buffer); self.setData({ imgUrl: bufferImg }); [代码] 这里用到的方法是 wx.arrayBufferToBase64(buffer)转化,加好base64头,即可食用~ 优点: 方便简单 确定: 阅后即焚,无法保存,个别场景可能需要缓存或者拼接canvas海报 3.2 在云函数直接将Buffer上传到云存储 [代码]await cloud.uploadFile({ cloudPath: 'test/' + event.userInfo.openId + '.jpg', //这里如果可以重复就用openId,如果不可能重复就用 fileContent: result.buffer, //处理buffer 二进制数据 success: res => { // 文件地址 console.log(res.fileID) }, fail: err =>{ console.log(err) } }) [代码] [图片] 将生成的小程序码上传到自带的云存储上,可以长期永久保存 优点: 长期保存,合适只要生成一次反复使用的场景 缺点: 生成量大的话,比较占用有限云存储资源 3.3 将图片转化保存在手机本地 [代码]let { buffer } = res.result; const wxFile = wx.getFileSystemManager(); const filePath = wx.env.USER_DATA_PATH + '/test.jpg'; //把图片写在本地 wxFile.writeFile({ filePath, encoding: "binary", data: buffer, success: res => { console.log(res); //writeFile:ok self.setData({ imgUrl: filePath }); } }) [代码] 这里用到的是wx.getFileSystemManager()的方法,将图片buffer转化后保存一个本地地址~ 优点: 生成实体地址,有时候图片太大,base64会出现一些诡异的BUG 缺点: 耗时 4、最终方案 经过综合考虑,这里使用的是不限次数的 openapi.wxacode.getUnlimited方法。 云函数部分: [代码]const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event, context) => { try { const result = await cloud.openapi.wxacode.getUnlimited({ scene: event.scene }); console.log(result) return await cloud.uploadFile({ cloudPath: 'test/' + event.userInfo.openId + '.jpg', fileContent: result.buffer, //处理buffer 二进制数据 success: res => { // 文件地址 console.log(res.fileID) }, fail: console.error }) } catch (err) { console.log(err) return err } } [代码] 页面代码: [代码]Page({ data: { imgUrl: "" //图片地址 }, getWxacode() { wx.cloud.init(); let self = this; wx.showLoading({ title: '请求云函数' }) // 调用云函数 获取 内容 wx.cloud.callFunction({ name: 'getWxacode', data: { scene: "goTo:pages/home/index" }, success: res => { console.log('云函数调用成功', res); self.setData({ imgUrl: res.result.fileID }); wx.hideLoading(); }, fail: err => { console.error('云函数调用失败', err) } }) }, }) [代码] 5、注意事项 5.1 生成码一共有三种方法: openapi.wxacode.get 小程序码,可以直接生成path,但不能传参,有次数限制 openapi.wxacode.createQRCode 二维码,有次数限制 openapi.wxacode.getUnlimited 特定scene传参,无次数限制 (推荐使用) 1和2方法累积10w次限制 5.2 代码报错,跑不了等 [图片] 记得初始化 wx.cloud.init(); [图片] 记得部署云函数~ ctrl+s 是没用的~~~ [图片] 记得先本地调试好,再上传~~~ 官方文档地址: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.get.html 看完觉得有帮助记得点个赞哦~ 你的赞是我继续分享的最大动力!^-^
2021-09-13 - 小程序云开发生成二维码并保存到文件
小程序云开发 云开发已经出来很久的时间了,但是一直没有使用,原因是一些基本框架都还在原来的服务。这次想参考礼物小盲盒做一个小程序。内容比较简单,刚好适合拿来做云开发练手,就从此开启云开发之路。 云开发整体使用还是比较方便的,这里不作过多的介绍,重点说下今天开发遇到的第一个小小的环节,生成一张二维码分享图可以保存分享到朋友圈。 页面效果 [图片] 实现方法 先来看看官方提供的文档:云开发获取小程序码 接口方法:[代码]openapi.wxacode.getUnlimited[代码] 需在 config.json 中配置 wxacode.getUnlimited API 的权限 属性 类型 默认值 必填 说明 scene string 是 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&’()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) page string 主页 否 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 1. 在 config.json 中配置 wxacode.getUnlimited API 的权限 在你云函数下的config.json文件中,增加以下代码: [代码]{ "permissions": { "openapi": [ "wxacode.getUnlimited" ] } } [代码] 2. scene只支持32个可见字符 如果你的参数过长,则需要将参数进行缩短,可以通过短码,短参数名的方式。我这里只需要一个boxId,刚好32位,这里直接使用boxId。 3. 云端生成二维码并保存 分享图除了二维码,还需要一些其他信息,这些信息是通过本地使用canvas进行绘制,而二维码需要从服务端生成。因为需要请求云函数,获取生成的二维码链接。 由于[代码]wxacode.getUnlimited[代码]返回结果图片buffer,这里使用云文件管理的方法,将获取到的buffer 写入本地文件,然后返回云文件ID给小程序端。 来看代码: [代码]async function getQrCode(scene, page, fileName) { try { var fileName = 'qrcode/' + fileName + '.png'; const result = await cloud.openapi.wxacode.getUnlimited({ scene: scene, page: page }) if (result && result.buffer) { var res = await cloud.uploadFile({ cloudPath: fileName, fileContent: result.buffer, }) if (res.fileID) { return res.fileID } } return false } catch (err) { console.error(err) return false } } [代码] 这里没有对二维码是否已经存在做检查,每次调用都会重新生成。因此在外部调用的地方需要检查是否已经生成,提高性能。 云开发文件存储相关API :https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/storage/api.html 将fileID 直接返回给小程序端,即可展示二维码。 但是这里还需要在小程序端画出分享图 4. 生成分享图 这里使用canvas绘制图片,但是canvas绘制图片需要为本地图片,先通过 [代码]wx.getImageInfo[代码]获取本地临时File地址。 [代码] loading: function (qrcode, avatarUrl, qrBackground) { var _this = this; var a = new Promise(function (r, j) { wx.getImageInfo({ src: qrcode, success: function (t) { r(t); } }); }), b = new Promise(function (r, j) { wx.getImageInfo({ src: avatarUrl, success: function (t) { r(t); } }); }), c = new Promise(function (r, j) { wx.getImageInfo({ src: qrBackground, success: function (n) { r(n); } }); }); Promise.all([a, b, c]).then(function (t) { _this.createNewImg(t[0].path, t[1].path, t[2].path); }); }, createNewImg: function (qrcode, avatarUrl, qrBackground) { var _this = this, config = _this.data.config, canvas = wx.createCanvasContext("myCanvas"); canvas.drawImage(qrcode, 260, 393, 150, 150), canvas.drawImage(qrBackground, 0, 0, 670, 670), canvas.font = "normal bold 28px simhei", canvas.fillStyle = "#000000"; var s = 335 - canvas.measureText("礼物份数:" + _this.data.num + "份").width / 2; canvas.fillText("礼物份数:" + _this.data.num + "份", s, 173), canvas.font = "normal bold 50px simhei", canvas.fillStyle = "#000000"; var qrTxt = config.giftConfig.qrTxt; if (qrTxt.length > 10) { var u = 335 - canvas.measureText(qrTxt.substr(0, 10)).width / 2, r = 335 - canvas.measureText(qrTxt.substr(10, 100)).width / 2; canvas.fillText(qrTxt.substr(0, 10), u, 250), canvas.fillText(qrTxt.substr(10, 100), r, 325); } else { var f = 335 - canvas.measureText(qrTxt).width / 2; canvas.fillText(qrTxt, f, 250); } canvas.arc(335, 468, 35, 0, 2 * Math.PI, !0), canvas.clip(), canvas.drawImage(avatarUrl, 300, 433, 70, 70), canvas.stroke(), canvas.draw(), setTimeout(function () { wx.canvasToTempFilePath({ canvasId: "myCanvas", success: function (n) { var e = n.tempFilePath; wx.hideLoading(), _this.setData({ url: e }); }, fail: function (t) { } }); }, 500); }, [代码] 再通过[代码]wx.canvasToTempFilePath[代码]函数将canvas 保存为本地临时文件,将url设置并展示即可。 wxml代码示例: [代码]<image id="wenan" mode="widthFix" src="{{url}}"></image> <canvas canvasId="myCanvas" style="width:670px;height:670px;margin-top:1000px;position:fixed"></canvas> [代码] 还有保存按钮申请存储权限这里就不说了,属于小程序基本操作。
2021-03-29 - wxml-to-canvas最新版本(1.1.1)控制台会给出“canvas 2d 接口...“警告
wxml-to-canvas最新版本(1.1.1)在控制台会警告“canvas 2d 接口支持同层渲染且性能更佳,建议切换使用” 官方给出的代码片段中wxml-to-canvas版本是1.0.1就不会出现警告
2020-09-07 - weapp-qrcode-canvas-2d在微信小程序中生成二维码,新版canvas-2d接口
weapp-qrcode-canvas-2d weapp-qrcode-canvas-2d 是使用新版canvas-2d接口在微信小程序中生成二维码(外部二维码)的js包。canvas 2d 接口支持同层渲染且性能更佳,建议切换使用,可大幅提升生成图片的速度。 仓库地址 weapp-qrcode-canvas-2d【码云gitee】 weapp-qrcode-canvas-2d【github】 [图片] 测试环境 微信小程序基础库版本:2.10.4 开发者工具版本:Stable 1.03.2101150 Usage 先在 wxml 文件中,创建绘制的 [代码]canvas[代码],并定义好 [代码]width[代码], [代码]height[代码], [代码]id[代码] , [代码]type[代码] ,其中type的值必须为[代码]2d[代码] [代码]<canvas type="2d" style="width: 260px; height: 260px;" id="myQrcode"></canvas> [代码] 安装方法1:直接引入 js 文件 直接引入 js 文件,使用 [代码]drawQrcode()[代码] 绘制二维码 [代码]// 将 dist 目录下,weapp.qrcode.esm.js 复制到项目中。路径根据实际引用的页面路径自行改变 import drawQrcode from '../../utils/weapp.qrcode.esm.js' [代码] 安装方法2:npm安装 [代码]npm install weapp-qrcode-canvas-2d --save [代码] // 然后需要在小程序开发者工具中:构建npm [代码]import drawQrcode from 'weapp-qrcode-canvas-2d' [代码] 安装完成后调用 例子1:没有使用叠加图片 [代码]const query = wx.createSelectorQuery() query.select('#myQrcode') .fields({ node: true, size: true }) .exec((res) => { var canvas = res[0].node // 调用方法drawQrcode生成二维码 drawQrcode({ canvas: canvas, canvasId: 'myQrcode', width: 260, padding: 30, background: '#ffffff', foreground: '#000000', text: 'abc', }) // 获取临时路径(得到之后,想干嘛就干嘛了) wx.canvasToTempFilePath({ canvasId: 'myQrcode', canvas: canvas, x: 0, y: 0, width: 260, height: 260, destWidth: 260, destHeight: 260, success(res) { console.log('二维码临时路径:', res.tempFilePath) }, fail(res) { console.error(res) } }) }) [代码] 例子2:使用叠加图片(在二维码中加logo) [代码]const query = wx.createSelectorQuery() query.select('#myQrcode') .fields({ node: true, size: true }) .exec((res) => { var canvas = res[0].node var img = canvas.createImage(); img.src = "/image/logo.png" img.onload = function () { // img.onload完成后才能调用 drawQrcode方法 var options = { canvas: canvas, canvasId: 'myQrcode', width: 260, padding: 30, paddingColor: '#fff', background: '#fff', foreground: '#000000', text: '123456789', image: { imageResource: img, width: 80, // 建议不要设置过大,以免影响扫码 height: 80, // 建议不要设置过大,以免影响扫码 round: true // Logo图片是否为圆形 } } drawQrcode(options) // 获取临时路径(得到之后,想干嘛就干嘛了) wx.canvasToTempFilePath({ x: 0, y: 0, width: 260, height: 260, destWidth: 600, destHeight: 600, canvasId: 'myQrcode', canvas: canvas, success(res) { console.log('二维码临时路径为:', res.tempFilePath) }, fail(res) { console.error(res) } }) }; }) [代码] API drawQrcode([options]) options Type: Object 参数 必须 说明 示例 canvas 必须 画布标识,传入 canvas 组件实例 canvasId 非 绘制的[代码]canvasId[代码] [代码]'myQrcode'[代码] text 必须 二维码内容 ‘123456789’ width 非 二维码宽度,与[代码]canvas[代码]的[代码]width[代码]保持一致 260 padding 非 空白内边距 20 paddingColor 非 内边距颜色 默认与background一致 background 非 二维码背景颜色,默认值白色 [代码]'#ffffff'[代码] foreground 非 二维码前景色,默认值黑色 [代码]'#000000'[代码] typeNumber 非 二维码的计算模式,默认值-1 8 correctLevel 非 二维码纠错级别,默认值为高级,取值:[代码]{ L: 1, M: 0, Q: 3, H: 2 }[代码] 1 image 非 在 canvas 上绘制图片,层级高于二维码,v1.1.1+版本支持。具体使用见:例子2 [代码]{imageResource: '', width:80, height: 80, round: true}[代码]
2023-04-02 - 云开发实现企业付款到零钱
云开发实现企业付款到零钱 ~ [图片] 一、前言 本博客实现的功能需求很单一,就是实现微信商户号中的企业付款到零钱的功能。 简单的来说,就是提现功能,最为普遍的使用场景大概是小程序/APP中举办一些活动,然后给予用户现金的奖励,由用户从小程序/APP提现到微信钱包中。 但是本文比较特殊,传统实现此功能避免不了使用服务器,但是微信小程序已经推出了云开发这一能力,那么能不能在云开发的云函数中实现提现这一功能呢?即免服务器实现。 答案是可以的。(不然我还写啥?) 二、实现的可行性分析 目前云开发基本成为未来小程序开发模式的一大趋势,对于全栈开发者/中小型项目开发来说,无疑是提供了巨大的便捷性。 但是请求外网API,例如文章中要讲的提现这种需要调用外网API的功能来说,云函数环境是否能够胜任呢? 需要满足什么样的条件的外网接口能够使用云函数环境代替传统服务器进行开发呢? 或许看到这里, 大家会觉得这一"实现的可行性分析”是啰嗦之语,但是因为我希望能把这种思路提供给大家。 让大家能够在项目开发前进行需求分析时对采用云开发是否能满足类似的需求有一个判断。进而减少开发的错误。 其实: 对于大部分无需证书(*.pem),普通的数据获取接口,都是可以通过云函数进行数据请求的。例如:需要爬取某网站的数据/请求、接收第三方API的数据 。 当接口请求的文档里出现: 需要配置请求IP白名单、需要携带证书、需要提供服务器回调地址 这些关键字眼的时候就要特别注意了。 因为可能云函数环境没办法实现。 [图片] 1、配置请求IP地址白名单 目前云函数是可以实现固定一个IP请求接口,在云函数设置-高级里面可以设置以及查看请求的ip地址。所以这个是可以实现的。 2、需要携带证书访问(*.pem文件) 一般需要证书访问的接口,文档里面都会提到提供存放证书的绝对路径。 例如支付宝的付款到用户余额。 云函数其实也是可以拿到这个绝对的路径的。绝对路径为:/var/user/证书名称.pem。 也可以通过 __dirname() 方法获取到云函数运行时的绝对路径。具体用法百度即可,注意是两个_。 所以这个基本也可以满足。 3、需要提供服务器回调地址 有些接口这个参数是非必填的,但是如果文档里提示是必填的,那就需要考虑一下了。云函数目前没办法提供这个地址,所以要想其他办法实现。这个满足不了。 三、实际分析 根据上面的可行性分析,我们来实际分析一下微信的企业付款到零钱的接口,是否能够满足在云函数实现的条件。打开企业付款到微信的开发文档 调用要求里面说需要携带证书进行访问。 关于API证书 其实这里不熟悉的开发者(例如我)一开始看可能会有些迷惑,以为是需要携带3个证书才可以成功访问接口。 就会很疑惑,因为.p12证书云函数没办法安装,仔细看了几次才发现,其实是两种选择。 3个证书中: 要么请求环境安装了.p12证书(仅限windows系统) 要么是请求的时候携带两个.pem证书。 这样看来,云函数其实是可以实现的这一接口调用的。 可行性分析完毕,接下来就开始实现代码了。 四、实现过程 实现前提 开通产品 要实现这个功能,前提当然是企业的商户号成功开通了企业付款到零钱这一功能。 申请开通的门槛还是稍微有点高的。 如果满足了其他条件,唯独不满足连续三十日交易流水这个条件的话,可以试一下每天随机支付金额,刷一下流水,有几率可以开通成功。 如果是比较老的商户号,有一定的支付记录了,大概率是可以开通的。 如果是新的商户号的话,自测。 检查证书以及API密钥 请求过程中需要携带API证书,以及需要API密钥生成签名组成请求参数。所以二者缺一不可。 具体的可以直接参照文档去操作。这个没有坑。生成的证书以及设置的API密钥记得保存好。 代码实现 完成了上面的前提之后,就可以进行代码的编写了。 1、生成签名 签名生成算法文档 可以不看文档,直接看代码。 请求参数 具体你的业务逻辑需要什么参数,直接去文档里看就行,然后放到这个参数对象里就行。 需要注意的是,提现的金额的单位是分,也就是说,假设提现1块钱,需要传入的参数数值则是 100。 openid、随机字符串、随机订单号这些变量,自行填入即可,测试时可以写死。 上面是需要提交的请求数据对象,需要将其进行排序、加密,得出签名。 计算签名模块 const crypto = require('crypto') // 引入MD5加密模块 /** * 根据传入的参数对象以及密钥,经过处理以及加密, 生成并返回sing参数 * @param {object} obj 参数组成的对象 * @param {String} mchKey 商户平台设置设置的密钥 * @returns */ const getSign = (obj, mchKey) => { // 对传入的对象进行排序 let arr = new Array(); let num = 0; for (let i in obj) { arr[num] = i; num++; } let sortArr = arr.sort(); let sortObj = {}; for (let i in sortArr) { sortObj[sortArr[i]] = obj[sortArr[i]]; } // 对传入的对象进行拼接 let sortStr = '' sortArr.forEach(key => { sortStr += key + "=" + sortObj[key] + "&" }); // 减去最后一个参数的&连接符 // sortStr = sortStr.substring(0, sortStr.length - 1) // 拼接密钥 sortStr += "key=" + mchKey // 使用MD5进行加密, 并将加密结果的英文字母全部转换成大写 const sign = crypto.createHash('md5').update(sortStr, 'utf8').digest('hex').toUpperCase(); return sign; } module.exports = { getSign: getSign } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 调用签名模块,组成最终的提交数据对象(json格式) const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 将签名放入请求参数对象 1 2 3 发起请求函数 这里需要引入两个模块。 const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 1 2 证书放置位置 请求代码逻辑 const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '<xml>\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '</' + key + '>\n' }); xml += '</xml>' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求数据,放入API证书 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 请求结果 如果没有出现什么问题,此时应该会返回接口的请求结果。可能会出现以下几种情况: 签名错误 使用在线密钥检查工具检测以下是否密钥生成有误。 直接把代码中生成的xml打印出来,整个粘贴进去,输入密钥,然后校验就行。看看签名是否出现问题。按文档中操作即可。 API证书错误 检查一下API密钥以及API证书是否有误,如果确认无误还是不行,尝试一下更换新的证书。 余额不足 扣款有两种情况: 1、如果没有开通运营账户,则在基本账户余额扣除。 2、如果开通了运营账户,则在运营账户扣除。 余额不足,直接充值进对应得账户即可。 低于多少元下限或高于多少元上限制 查看企业付款到零钱的设置,提现最低门槛可手动设置至0.3元。 在设置的提现金额范围内发起提现即可。 解析返回结果xml为json格式 该接口返回的结果的格式是XML格式,对于交易结果的判断比较麻烦,因此为了便利,将结果转成JSON的格式。 xml解析模块 直接将返回的xml数据整个传入即可。 const xml2js = require('xml2js') //引入xml解析模块 /** * 解析付款到零钱返回的xml为JSON * @param xmlText 返回的xml */ const parseXMLToJSON = (xmlText) =>{ let xmlJsonText = '' xml2js.parseString(xmlText, (err, result)=>{ xmlJsonText = result }) xmlJsonText = JSON.stringify(xmlJsonText) const xmlJson = JSON.parse(xmlJsonText) let resultObj = xmlJson.xml let resultJson = {} Object.keys(resultObj).forEach((key)=>{ resultJson[key] = resultObj[key][0] }) return resultJson } module.exports = { parseXMLToJSON: parseXMLToJSON } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 调用 打印输出结果示例(云函数日志) 到账提示 目录结构 五、结语 主要的请求逻辑基本就在上面了。至于请求后的业务逻辑处理,例如数据入库等操作,基本就是可以自行实现了。这个没有什么可说的。 希望以上的文章可以帮助到大家,有不当的地方或者我没写明白的地方, 欢迎大家能够在评论区提出来。 六、完整的index.js代码 // 云函数入口文件 const cloud = require('wx-server-sdk') const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 const getSign = require('./utils/getSing.js') // 计算签名模块 const xmlToJson = require('./utils/xmlToJson.js') // xml转json模块 cloud.init({ evn: '云环境id' }) const db = cloud.database() const _ = db.command // 云函数入口函数 exports.main = async (event) => { const partner_trade_no = getOrderId('商户号') // 随机订单号 const nonce_str = getNonceStr() // 随机字符串 let paramObj = { mchid: "商户号", mch_appid: 'mch_appid', device_info: 1000, nonce_str: nonce_str, partner_trade_no: partner_trade_no, openid: event.openid, check_name: 'FORCE_CHECK', // 是否校验实名,可选,详看文档 re_user_name: event.name, // 详看文档 amount: event.amount * 100, // 注意金额的单位,我传入的是以元为单位的,所以需要乘以100 desc: event.desc } const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 发起提现请求 const xmlText = await requestFun(paramObj) // 将返回的xml解析成JSON格式 const xmlJson = xmlToJson.parseXMLToJSON(xmlText.body) console.log(xmlJson) // 判断提现状态 // 通讯成功 if (xmlJson.return_code == 'SUCCESS') { // 交易成功 if (xmlJson.result_code == 'SUCCESS') { // 提现成功后的业务逻辑 } // 交易失败 else if (xmlJson.result_code == 'FAIL') { // 提现失败后的业务逻辑 } } return xmlJson } /** * 请求函数 * @param paramObj 请求时提交的参数对象,JSON格式 */ const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '\n' }); xml += '' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求体 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } /** * 获取32位随机支付订单号 */ const getOrderId = (mchid) => { let randomStr = "" const timeStamp = new Date().getTime() for (let i = 0; i < (32 - 13 - mchid.length); i++) { let randomNum = Math.floor(Math.random() * 10) // 获取 0-9随机整数 randomStr += randomNum } // 拼接 商户号 + 随机数字 + 时间戳 返回 return mchid + randomStr + timeStamp } /** * 获取32位随机字符串 */ const getNonceStr = () => { let str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let randomStr = ''; for (let i = 32; i > 0; --i) { randomStr += str[Math.floor(Math.random() * str.length)]; } return randomStr } ~云开发实现企业付款到零钱 ~ [图片] 一、前言 本博客实现的功能需求很单一,就是实现微信商户号中的企业付款到零钱的功能。 简单的来说,就是提现功能,最为普遍的使用场景大概是小程序/APP中举办一些活动,然后给予用户现金的奖励,由用户从小程序/APP提现到微信钱包中。 但是本文比较特殊,传统实现此功能避免不了使用服务器,但是微信小程序已经推出了云开发这一能力,那么能不能在云开发的云函数中实现提现这一功能呢?即免服务器实现。 答案是可以的。(不然我还写啥?) 二、实现的可行性分析 目前云开发基本成为未来小程序开发模式的一大趋势,对于全栈开发者/中小型项目开发来说,无疑是提供了巨大的便捷性。 但是请求外网API,例如文章中要讲的提现这种需要调用外网API的功能来说,云函数环境是否能够胜任呢? 需要满足什么样的条件的外网接口能够使用云函数环境代替传统服务器进行开发呢? 或许看到这里, 大家会觉得这一"实现的可行性分析”是啰嗦之语,但是因为我希望能把这种思路提供给大家。 让大家能够在项目开发前进行需求分析时对采用云开发是否能满足类似的需求有一个判断。进而减少开发的错误。 其实: 对于大部分无需证书(*.pem),普通的数据获取接口,都是可以通过云函数进行数据请求的。例如:需要爬取某网站的数据/请求、接收第三方API的数据 。 当接口请求的文档里出现: 需要配置请求IP白名单、需要携带证书、需要提供服务器回调地址 这些关键字眼的时候就要特别注意了。 因为可能云函数环境没办法实现。 [图片] 1、配置请求IP地址白名单 目前云函数是可以实现固定一个IP请求接口,在云函数设置-高级里面可以设置以及查看请求的ip地址。所以这个是可以实现的。 2、需要携带证书访问(*.pem文件) 一般需要证书访问的接口,文档里面都会提到提供存放证书的绝对路径。 例如支付宝的付款到用户余额。 云函数其实也是可以拿到这个绝对的路径的。绝对路径为:/var/user/证书名称.pem。 也可以通过 __dirname() 方法获取到云函数运行时的绝对路径。具体用法百度即可,注意是两个_。 所以这个基本也可以满足。 3、需要提供服务器回调地址 有些接口这个参数是非必填的,但是如果文档里提示是必填的,那就需要考虑一下了。云函数目前没办法提供这个地址,所以要想其他办法实现。这个满足不了。 三、实际分析 根据上面的可行性分析,我们来实际分析一下微信的企业付款到零钱的接口,是否能够满足在云函数实现的条件。打开企业付款到微信的开发文档 调用要求里面说需要携带证书进行访问。 关于API证书 其实这里不熟悉的开发者(例如我)一开始看可能会有些迷惑,以为是需要携带3个证书才可以成功访问接口。 就会很疑惑,因为.p12证书云函数没办法安装,仔细看了几次才发现,其实是两种选择。 3个证书中: 要么请求环境安装了.p12证书(仅限windows系统) 要么是请求的时候携带两个.pem证书。 这样看来,云函数其实是可以实现的这一接口调用的。 可行性分析完毕,接下来就开始实现代码了。 四、实现过程 实现前提 开通产品 要实现这个功能,前提当然是企业的商户号成功开通了企业付款到零钱这一功能。 申请开通的门槛还是稍微有点高的。 如果满足了其他条件,唯独不满足连续三十日交易流水这个条件的话,可以试一下每天随机支付金额,刷一下流水,有几率可以开通成功。 如果是比较老的商户号,有一定的支付记录了,大概率是可以开通的。 如果是新的商户号的话,自测。 检查证书以及API密钥 请求过程中需要携带API证书,以及需要API密钥生成签名组成请求参数。所以二者缺一不可。 具体的可以直接参照文档去操作。这个没有坑。生成的证书以及设置的API密钥记得保存好。 代码实现 完成了上面的前提之后,就可以进行代码的编写了。 1、生成签名 签名生成算法文档 可以不看文档,直接看代码。 请求参数 具体你的业务逻辑需要什么参数,直接去文档里看就行,然后放到这个参数对象里就行。 需要注意的是,提现的金额的单位是分,也就是说,假设提现1块钱,需要传入的参数数值则是 100。 openid、随机字符串、随机订单号这些变量,自行填入即可,测试时可以写死。 上面是需要提交的请求数据对象,需要将其进行排序、加密,得出签名。 计算签名模块 const crypto = require('crypto') // 引入MD5加密模块 /** * 根据传入的参数对象以及密钥,经过处理以及加密, 生成并返回sing参数 * @param {object} obj 参数组成的对象 * @param {String} mchKey 商户平台设置设置的密钥 * @returns */ const getSign = (obj, mchKey) => { // 对传入的对象进行排序 let arr = new Array(); let num = 0; for (let i in obj) { arr[num] = i; num++; } let sortArr = arr.sort(); let sortObj = {}; for (let i in sortArr) { sortObj[sortArr[i]] = obj[sortArr[i]]; } // 对传入的对象进行拼接 let sortStr = '' sortArr.forEach(key => { sortStr += key + "=" + sortObj[key] + "&" }); // 减去最后一个参数的&连接符 // sortStr = sortStr.substring(0, sortStr.length - 1) // 拼接密钥 sortStr += "key=" + mchKey // 使用MD5进行加密, 并将加密结果的英文字母全部转换成大写 const sign = crypto.createHash('md5').update(sortStr, 'utf8').digest('hex').toUpperCase(); return sign; } module.exports = { getSign: getSign } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 调用签名模块,组成最终的提交数据对象(json格式) const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 将签名放入请求参数对象 1 2 3 发起请求函数 这里需要引入两个模块。 const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 1 2 证书放置位置 请求代码逻辑 const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '<xml>\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '</' + key + '>\n' }); xml += '</xml>' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求数据,放入API证书 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 请求结果 如果没有出现什么问题,此时应该会返回接口的请求结果。可能会出现以下几种情况: 签名错误 使用在线密钥检查工具检测以下是否密钥生成有误。 直接把代码中生成的xml打印出来,整个粘贴进去,输入密钥,然后校验就行。看看签名是否出现问题。按文档中操作即可。 API证书错误 检查一下API密钥以及API证书是否有误,如果确认无误还是不行,尝试一下更换新的证书。 余额不足 扣款有两种情况: 1、如果没有开通运营账户,则在基本账户余额扣除。 2、如果开通了运营账户,则在运营账户扣除。 余额不足,直接充值进对应得账户即可。 低于多少元下限或高于多少元上限制 查看企业付款到零钱的设置,提现最低门槛可手动设置至0.3元。 在设置的提现金额范围内发起提现即可。 解析返回结果xml为json格式 该接口返回的结果的格式是XML格式,对于交易结果的判断比较麻烦,因此为了便利,将结果转成JSON的格式。 xml解析模块 直接将返回的xml数据整个传入即可。 const xml2js = require('xml2js') //引入xml解析模块 /** * 解析付款到零钱返回的xml为JSON * @param xmlText 返回的xml */ const parseXMLToJSON = (xmlText) =>{ let xmlJsonText = '' xml2js.parseString(xmlText, (err, result)=>{ xmlJsonText = result }) xmlJsonText = JSON.stringify(xmlJsonText) const xmlJson = JSON.parse(xmlJsonText) let resultObj = xmlJson.xml let resultJson = {} Object.keys(resultObj).forEach((key)=>{ resultJson[key] = resultObj[key][0] }) return resultJson } module.exports = { parseXMLToJSON: parseXMLToJSON } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 调用 打印输出结果示例(云函数日志) 到账提示 目录结构 五、结语 主要的请求逻辑基本就在上面了。至于请求后的业务逻辑处理,例如数据入库等操作,基本就是可以自行实现了。这个没有什么可说的。 希望以上的文章可以帮助到大家,有不当的地方或者我没写明白的地方, 欢迎大家能够在评论区提出来。 六、完整的index.js代码 // 云函数入口文件 const cloud = require('wx-server-sdk') const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 const getSign = require('./utils/getSing.js') // 计算签名模块 const xmlToJson = require('./utils/xmlToJson.js') // xml转json模块 cloud.init({ evn: '云环境id' }) const db = cloud.database() const _ = db.command // 云函数入口函数 exports.main = async (event) => { const partner_trade_no = getOrderId('商户号') // 随机订单号 const nonce_str = getNonceStr() // 随机字符串 let paramObj = { mchid: "商户号", mch_appid: 'mch_appid', device_info: 1000, nonce_str: nonce_str, partner_trade_no: partner_trade_no, openid: event.openid, check_name: 'FORCE_CHECK', // 是否校验实名,可选,详看文档 re_user_name: event.name, // 详看文档 amount: event.amount * 100, // 注意金额的单位,我传入的是以元为单位的,所以需要乘以100 desc: event.desc } const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 发起提现请求 const xmlText = await requestFun(paramObj) // 将返回的xml解析成JSON格式 const xmlJson = xmlToJson.parseXMLToJSON(xmlText.body) console.log(xmlJson) // 判断提现状态 // 通讯成功 if (xmlJson.return_code == 'SUCCESS') { // 交易成功 if (xmlJson.result_code == 'SUCCESS') { // 提现成功后的业务逻辑 } // 交易失败 else if (xmlJson.result_code == 'FAIL') { // 提现失败后的业务逻辑 } } return xmlJson } /** * 请求函数 * @param paramObj 请求时提交的参数对象,JSON格式 */ const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '<xml>\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '</' + key + '>\n' }); xml += '</xml>' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求体 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } /** * 获取32位随机支付订单号 */ const getOrderId = (mchid) => { let randomStr = "" const timeStamp = new Date().getTime() for (let i = 0; i < (32 - 13 - mchid.length); i++) { let randomNum = Math.floor(Math.random() * 10) // 获取 0-9随机整数 randomStr += randomNum } // 拼接 商户号 + 随机数字 + 时间戳 返回 return mchid + randomStr + timeStamp } /** * 获取32位随机字符串 */ const getNonceStr = () => { let str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let randomStr = ''; for (let i = 32; i > 0; --i) { randomStr += str[Math.floor(Math.random() * str.length)]; } return randomStr } ~
2022-06-18 - 共享云环境时代来了,解决fileID带来不兼容问题。
云开发收费了,不管你怎么选择,只要你还继续使用云开发,共享云环境的课题就不可避免。 我们知道,共享云环境下,fileID是无法使用的,怎么兼容,一个最简的方法如下: <wxs module="wxs"> module.exports = { getUrl: function (link) { if (link) { } else return '' if (link.substring(0, 5) == 'cloud') { } else return link var arr = link.split('/') arr[0] = 'https:' arr[2] = arr[2].split('.')[1] + '.tcb.qcloud.la' return arr.join('/') } } </wxs> <image src="{{wxs.getUrl(link)}}"></image> 可见:只要将原项目所有的fileID换成wxs.getUrl(link) 其他代码可以一分不动,也不需要用到wx.cloud.getTempFileURL 可以将wxs.getUrl放在lib.wxs里,任何wxml引用即可。
2022-10-28 - # 使用小程序云开发API更新数组中的单个数组元素
使用小程序云开发API更新数组中的单个数组元素 看了看mongoDB的更新数据方式,找到了解决办法,解决方法如下,亲测可用: 第一种方法:使用位置操作符$ [代码]代码,条件更新写在云函数中 [代码] [图片] [代码]test_api集合原始数据如下 [代码] [图片] [代码]在云函数中执行1中的代码,数组users中id为1001的用户添加了一个新的属性test [代码] [图片] [代码]原理分析 [代码] where条件是查找数组中id属性为1001的用户 update中的使用’users.$.test’: ‘test’ 注意里面的$符号,在mongoDB中,这个符号叫做位置操作符,代表数组的下标,如下引自《mongoDB实战》 [图片] 第二种方法:直接使用数组下标 云函数代码 [图片] test_api集合原始数据如下 [图片] 代码执行后 [图片] 相对于第一种方法,这种方法更加简单,只不过users.1.test这种写法有点颠覆js和java中的属性书写规则,让人感觉怪怪的,在mongoDB中,也支持点数字这种写法。 一个可能的疑惑 可不可以写作’users[1].test’:‘test’,测试结果如下: [图片] [图片] 可以看到’user[1]'无法被识别为数组的第二个元素,而是作为了属性名新增了一个属性,结论:必须写成”点数字“不能写成“中括号” 结论: 经过测试,使用这两种种方法可以更新数组中的一个元素。 方法一适合在不知道数组元素下标的情况下根据查询条件更新元素; 方法二适合在知道数组元素下标的情况下更新元素; 当然也存在既知道元素下标也可以通过属性查到的情况,想用哪个就看心情了-.- 但是暂未找到查询返回数组中的一个元素的方法,再探索探索吧 ——。——
2019-03-06 - 【小程序技巧】如何让长文本超过限定行数自动折叠,并且可以展开收起
这是去年在校做项目遇到的一个需求,文章沉在草稿箱里一直没写完,主要分享一下如何实现长文本的折叠展开。 长文本超过限定行数自动折叠,点击长文本或者按钮,实现展开收起效果。这类效果其实在平时的app中或者网站中很常见,举几个栗子: 微信朋友圈: [图片] 新浪微博: [图片] 分析需求 1、文本超长省略,主要是通过 line-clamp 实现: [代码].text-clamp2 { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } [代码] 文本效果: [图片] 2、如何判断文本是否超出两行,显示「全文」「收起」按钮呢? [图片] 通过上图我们可以发现,当文本区域省略时,它的高度会相对变小,那么我们只需要获取到不省略和省略时的文本区域高度,进行比较就能知道是否超出了两行。 [图片] 思路解决了,怀着喜悦的心情翻看了一下文档:咦?为什么小程序没有像 js 那样操作 dom 节点的接口?那还怎么获取元素的尺寸高度!好在功夫不负有心人,终于在文档找到类 DOM 操作的 API「SelectQuery」。 实现需求 3、什么是 SelectQuery?如何去使用它? 从文档(传送门)描述来看 SelectQuery 是一个查询节点信息的对象,它可以选择匹配选择器的所有节点以及显示区域内的节点信息。既然它可以类似 jQuery 那样去匹配选择器,那么我们可以获取到需要的高度信息了。 [代码]// wxml <view class="contentInner1 text-clamp2">小程序是一种新的开放能力,开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。</view> <view class="contentInner2">小程序是一种新的开放能力,开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。</view> [代码] [代码]// js wx.createSelectorQuery().selectAll(".contentInner1, .contentInner2").boundingClientRect(res => { console.log(res) }).exec() [代码] 查询结果(文本区域省略时高度为 52px、不省略时为 104px,只要 res[0].height < res[1].height,此时就应该显示展开收起按钮 ) [图片] 4、逻辑设计上的优化 由于论坛帖子不只一个,我们得匹配对应的两个长文本节点,如果都给一个唯一的选择器,那么在页面中一次性查询这么多节点,很明显这不是最优的。 实际上我们可以将这封装成一个自定义组件,可供每个页面循环复用,在组件内我们只需要关注 单个 长文本的节点信息,不需要一次性获取当前页面的所有长文本节点,更重要的是:在组件内每个长文本的展开与收起状态都是独立的,也省去了在页面内定义字段去标识每个帖子的展开状态。 5、实现效果 [图片] [图片] 6、参数说明 属性 类型 默认值 说明 content String “示例文本” 长文本内容 maxline Number 1 最多展示行数[只允许 1-5 的正整数] position String “left” 展开收起按钮位置[可选值为 left right] foldable Boolean true 点击长文本是否展开收起 最后附上代码片段,有疑问欢迎在下方留言或者发社区私信(三连暗示) [图片]
2021-12-30 - 云存储到对象存储COS的迁移
云开发计费调整后,对存储和CDN用量较大的业务成本影响较大。把文件从云存储迁移到对象存储(下文简称 COS ),是一个成本优化的选择。 我自己的小程序比较下来,每月的费用大约是不迁的三分之一(刚好也赶上双11的活动攒了一波优惠资源);如果不考虑活动,正常买资源包,大约也能降低一半。 但相比从零开始就是「云函数 + 云数据库 + COS」的架构,就急需一个高效稳定的迁移方案,迁移云存储中的文件,并同时更新数据库记录。最好还无需停服。 这里就不赘述那些尝试过的失败方案,仅记录并分享最后跑通的流程,供有类似需求的开发者朋友参考。 一、开通并设置COS、CDN 在腾讯云创建COS的存储桶(最好和云存储同地域,之后迁移既快又经济),「权限管理」设置为“私有读写”,避免外网的直接下行访问;注册或找个域名,设置为 COS 的「自定义CDN加速域名」,开启「回源鉴权」;进入「内容分发网络」的「域名管理」,在「管理」的「访问控制」,开启「防盗链配置」——类型选「白名单」,填入小程序域名“servicewechat.com”。这里需要开启「允许空 referer 访问」,如不开启,手机端将无法访问 video 组件的视频文件(这个问题至今尚未修复)。二、准备小程序的更新版,提交审核(增删改查指向COS新路径) 在腾讯云控制台「访问管理」中创建一个子用户角色,获取 secretId 和 secretKey;写一个云函数,通过 secretId 和 secretKey 生成临时密钥;客户端上传文件时,先触发这个云函数获取COS的临时密钥,用于客户端 wx.uploadFile 文件上传;上传完成后,回调函数里获取 COS 文件路径,写入云数据库;以上是“增”,“删改查”同理,不赘述(访问 COS 的云函数,需要通过 npm 给云函数装 COS 的 Node.js SDK)。详细实践可参考腾讯云文档:小程序直传实践。 新版本准备好了便提交小程序审核。 三、写迁移云函数 因云函数默认内存256M、超时时间3s,最大也只可设置为 1024M、60s,如不够用,可将迁移过程拆为任务发起函数和执行函数;发起函数获取需要迁移的文件数据(如数据量大,可根据数据结构再拆分云函数读取),异步调用执行函数,不等执行结果;每个执行函数只执行一个文件的迁移、及更新数据记录的任务。这样执行时云函数都不会超过内存和时间限制;执行函数里,先 cos.downloadFile 云存储的文件到云函数的缓存(注意路径是“/tmp/文件名”,不是“__dirname/文件名”),再从缓存 cos.uploadFile 到 COS。四、发布小程序、执行迁移 小程序审核通过、迁移云函数都准备好后: 小程序新版发布上线;通过IDE编译,触发迁移云函数里的发起函数。迁移全程同地域内网执行(不要用本地调试。公网执行慢很多,容易超时,且会产生公网流量费);因为执行函数并发量大,容易受到云函数并发数的500/分钟的限制,500以上的都会执行失败(500是入门版到团队进阶版的每分钟并发数,个人版100,更贵的版本更高)。因此执行函数里只能执行一个文件迁移就立即更新相应记录,不能汇总了再更新;如数据量很大、发起函数拆分后读取仍超时,可在数据库记录任务发起情况,多触发几次,总会迁移完。在这个尝试迁移的过程中,有幸得到腾讯云工单同学、客户经理、云开发产品经理的支持和点拨,多次在挠头时刻解惑。感谢。 才疏学浅,如有谬误,请多多指正~
2022-12-20 - 轻松实现小程序直接上传图片至腾讯云对象存储
概念介绍 对象存储(Cloud Object Storage,COS)是腾讯云提供的一种存储海量文件的分布式存储服务,用户可通过网络随时存储和查看数据。腾讯云 COS 使所有用户都能使用具备高扩展性、低成本、可靠和安全的数据存储服务。 前期准备 登录腾讯云对象存储控制台创建存储桶,获取 Bucket(存储桶名称) 和 Region(地域名称)。 通过管理控制台的 密钥管理 获取您的项目 SecretId 和 SecretKey 下载对象存储SDK,至小程序对象存储gitHub项目目录下载https://github.com/tencentyun/cos-wx-sdk-v5/tree/master/demo/lib下的cos-auth.js文件,添加至项目目录 项目实践 一、引用 [代码]var CosAuth = require('cos-auth');//引入对象存储SDK var config = require('../utils/api.js');//定义了项目的一些配置内容,例如存储桶名称、地区、请求域名等 var stsCache; //存储临时秘钥及秘钥过期时间内容 //定义上传接口 var uploadFile = function(filePath, cb) { // 请求用到的参数 var prefix = 'https://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com/'; // 对更多字符编码的 url encode 格式 var camSafeUrlEncode = function(str) { return encodeURIComponent(str) .replace(/!/g, '%21') .replace(/'/g, '%27') .replace(/\(/g, '%28') .replace(/\)/g, '%29') .replace(/\*/g, '%2A'); }; // 获取临时密钥 // 全局变量stsCache 存储临时秘钥及过期时间内容 var getCredentials = function(callback) { //判断临时秘钥未过期 if (stsCache && Date.now() / 1000 + 30 < stsCache.expiredTime) { callback(stsCache && stsCache.credentials); return; } //过期,服务器重新请求获取临时秘钥 wx.request({ method: 'GET', url: 'https://baidu.com/Api/Cos/getCosTempKeys', // 服务端签名,参考 server 目录下的两个签名例子 dataType: 'json', success: function(result) { var data = result.data.result; var credentials = data.credentials; if (credentials) { stsCache = data } else { wx.showModal({ title: '临时密钥获取失败', content: JSON.stringify(data), showCancel: false }); } callback(stsCache && stsCache.credentials); }, error: function(err) { wx.showModal({ title: '临时密钥获取失败', content: JSON.stringify(err), showCancel: false }); } }); }; // 计算签名 var getAuthorization = function(options, callback) { getCredentials(function(credentials) { callback({ XCosSecurityToken: credentials.sessionToken, Authorization: CosAuth({ SecretId: credentials.tmpSecretId, SecretKey: credentials.tmpSecretKey, Method: options.Method, Pathname: options.Pathname, }) }); }); }; // 上传文件 var uploadFile = function(filePath, cb) { var Key = filePath.substr(filePath.lastIndexOf('/') + 1); // 这里指定上传的文件名 getAuthorization({ Method: 'POST', Pathname: '/' }, function(AuthData) { var requestTask = wx.uploadFile({ url: prefix, name: 'file', filePath: filePath, formData: { 'key': Key, 'success_action_status': 200, 'Signature': AuthData.Authorization, 'x-cos-security-token': AuthData.XCosSecurityToken, 'Content-Type': '', }, success: function(res) { var url = prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/'); if (res.statusCode === 200) { if (cb) { cb(url); } } else { wx.showModal({ title: '上传失败', content: JSON.stringify(res), showCancel: false }); } }, fail: function(res) { wx.showModal({ title: '上传失败', content: JSON.stringify(res), showCancel: false }); } }); requestTask.onProgressUpdate(function(res) { }); }); }; // 触发上传文件方法,按步骤调用执行 uploadFile(filePath, cb); }; module.exports = { uploadFile }; [代码] 2、调用以上SDK实现上传 1、 在需调用的文件,首先引入以上文件 var demoNoSdk = require(‘sdk文件路径’);//引入上述的对象存储SDK文件 2、 获取到上传文件的临时路径(备注:可以是直接调用wx.chooseImag方法获取的临时文件,也可以是调用wx.canvasToTempFilePath将画布导出的临时图片路径等等其他方式获取的到的临时图片文件路径) 这里调用wx. chooseImage为例 [代码]wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success: function(res) { //临时文件路径 var tempFilePath= res.tempFilePaths[0]; //定义上传图片成功后的回调函数 var callback = function(url) { //url为上传至对象存储成功后返回的云存储文件路径 //do other something …… }; //调用对象存储SDK,实现文件上传 //传递参数临时文件路径及上传成功后的回调函数,若不需要回调可不传此参数 demoNoSdk.uploadFile(tempFilePath, callback); }, }) [代码] 实现上传的过程如下 获取图片临时文件路径 ->调用SDK的uploadFile方法 ->服务器请求获取请求对象存储功能所需要的临时秘钥(并将秘钥结果及过期时间存储至全局变量,方便后面直接调用已缓存的未过期的临时秘钥即可,无需重复请求服务器获取) ->调用wx.uploadFile接口上传图片 ->上传成功后调用我们自定义的回调函数实现我们自己的业务。 至此,小程序直接上传图片至对象存储完成!
2019-04-27 - [Component] property received type-uncompatible
给子组件传的参是从 computed 里获取的值,第一次会报警告(看得难受) [图片] 2.18.1 以下的基础库不会有此问题 相关问题可见:https://developers.weixin.qq.com/community/develop/doc/0002628cac8fb0a8158c3713f5bc00?page=2#comment-list
2022-03-17 - 如何解决SharedArrayBuffer需要跨源隔离?
[图片]
2022-11-14 - wx.navigateTo() 出现警告[Deprecation] ?
[Deprecation] SharedArrayBuffer will require cross-origin isolation as of M92, around July 2021. See https://developer.chrome.com/blog/enabling-shared-array-buffer/ for more details. e.onmessage @ worker.js?libName=WAAccelerateWorker.js:1 [worker] reportRealtimeAction:fail not support e.workerInvokeJsApi @ worker.js?libName=WAAccelerateWorker.js:1 (anonymous) @ WAWorker.js:2 k @ WAWorker.js:2 invoke @ WAWorker.js:2 f @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 re @ WAWorker.js:2 y @ WAWorker.js:2 l @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 setTimeout (async) globalThis.setTimeout @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 Q @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 S @ WAWorker.js:2 eval @ VM10:1 e.onmessage @ worker.js?libName=WAAccelerateWorker.js:1
2022-06-21 - 【跨账号共享环境】云开发cloudfunctions无法指定环境?
操作系统:macOS,微信开发工具:1.05.2105170 Stable,云开发控制台:v1.3.89 按云开发模版新建项目后,无法指定云开发环境,云开发环境是另一个小程序共享过来的(同一主题),试了很多办法都不行,在新建项目的时候,明明显示有绑定的云开发环境 [图片] 云函数本地调试,也显示未指定环境[图片] 但这app.js和云函数index.js内都指定了云开发环境 [图片] [图片]
2021-05-29 - 使用weui组件库修改内部样式不起作用?
[图片][图片][图片] 应用在另一个组件中的
2020-08-13 - 极简代码之云开发的触底无限加载
js: [代码]const db = wx.cloud.database() const _ = db.command const col = "test" const sql = { _id: _.neq(1) } //获取所有记录 Page({ data: { isEndOfList: false, list: [], limit: 20 //每次拉取数量 }, onLoad: function(options) { this.getData() }, getData: function() { db.collection(col) .where(sql) .skip(this.data.list.length) .limit(this.data.limit) .get() .then(res => { this.setData({ list: [...this.data.list, ...res.data], //合并数据 isEndOfList: res.data.length < this.data.limit ? true : false //判断是否结束 }) }) }, onReachBottom: function() { this.data.isEndOfList || this.getData() } }) [代码] wxml [代码]<view style="height:100px" wx:for='{{list}}' wx:key='none'>{{index}}</view> <view style="padding:15px;text-align:center;color:grey" wx:if='{{list.length>limit}}'> <view wx:if='{{(!isEndOfList)}}'>正在加载数据...</view> <view wx:else>----END----</view> </view> [代码]
2020-06-16 - 免费ICP备案攻略。不花1分钱拥有一台云服务器并顺利ICP备案。
写在前面: 大家不要将ICP证和ICP备案搞混了。 ICP证指的是【电信增值业务经营许可证】,这个资质需要企业主体至少100万注金,去工信部办理,比较难办理;社交-交友需要ICP证。 而ICP备案,【非经营性互联网信息服务备案核准】仅仅是指企业主体的域名备案,可以简单的按以下步骤免费办理成功,其他社交类目如社区、论坛、笔记等,只需要ICP备案即可。 1、在腾讯云注册一个账号并认证企业主体(不吹不黑,开发小程序当然首选腾讯云,好用)。http://www.qcloud.com/ 如果你是个人主体,就不要往下看了,没必要折腾了。 2、找到腾讯云免费活动页:https://cloud.tencent.com/act/free?from=10107 3、选择一款云服务器,180天免费试用。 云服务器申请成功后,它的使命就完成了,没用了,让它自生自灭吧。 在整个备案过程中,也不需要部署网站(域名都没有备案,哪来的网站?)。 [图片] 云服务器180天到期后,可以自己决定是否续费,每个月也才99元,促销期甚至更低,完全可以接受吧。 备案成功后,该服务器就没什么作用了,让它180天后自然欠费销毁得了。 服务器销毁后会有什么影响?答:没有任何影响。 但是。。。。。 你备案的域名最后还得指向一个网站,因为腾讯云会应工信部的要求定期检查网站是否合规,所以你还是要建一个简单的网站,(备案期间,可以暂时不管网站的事,等将来需要的时候再管理)。 至于有多简单,答,多简单都行。此时你可以在七牛、腾讯云、阿里云租点免费的对象存储空间,做个简单的网站。 4、在进行ICP备案之前,你需要在腾讯云注册你的域名地址,如果你已有域名,但不在腾讯云,建议先将要域名过户到腾讯云的账号上。 5、进入控制台,开始ICP备案,这个流程就不介绍了,因为完全一看就懂。而且现在使用备案小程序后,不需要幕布或现场拍照了,极其方便,大家跟着流程走就一点问题没有,有人脸识别和在线拍一段小视频。另外,大家可以随便作,随便填,填错或者填得不合适也不用怕,会有专门的备案客服打电话告诉你哪哪要改,还会告诉你应该怎么填才更容易通过工信部的审核,客服的态度好得发指。 仅说一点其中的几个小坑: a、人脸识别的时候,白色背景、白色背景、白色背景,笔者在人脸识别的时候,满世界找白墙,结果还被打回来重拍了3次。 b、网站用途一律写:公司官网,好通过工信部审核。 6、腾讯云提交资料到工信部审核。这是一个漫长的让人无语的等待,20-30天。笔者最近两次都是20天才过审;不要幻想会有可能提前完成审核,这是政府部门在审核,提前完成说明某政府人员的工作安排有问题,会犯错误的。 7、备案成功后,会有短信通知你,但是,你需要去工信部网站查询结果,并将结果切屏拷贝下来,因为小程序类目审核需要上传这张图片。http://beian.miit.gov.cn/publish/query/indexFirst.action [图片] 把上面这张图片保存好,小程序类目审核的时候需要上传。收到通知后,如果在这里查不到结果,也别急,据说需要24小时。 8、接下来是小程序上线审核。 因为ICP备案的小程序内容肯定涉及到社交,最后小程序上线时还要提交到工信部审核,还需要7天左右的时间,加上前面ICP备案的时间,加起来怎么也得30-40天。大家估计时间,别影响小程序上线。这7天也是政府部门在审核,不要幻想会提前。 9、计算一下时间: 腾讯云注册账号和认证:1-3天; 域名备案:腾讯云环节:1-3天; 域名备案:工信部环节:20-30天; 小程序添加服务类目:社交类目审核:1-3天; 小程序上线审核:腾讯环节:1-2天; 小程序上线审核:工信部环节:7+天; 总天数:30-40天; 10、节省时间的一些建议: 在开发小程序之前,就开始备案工作,小程序可以同时开发,相互不影响; 在开发完成之前一、两星期之内,先发布一版小程序,别管功能是不是完整,能通过审核就行,这样会有7天的等待类目审核的时间,这个时间里,小程序可以照常开发,不影响进度; 只要是社交类,基本需要有文字和图片安全检查功能,别忘了加上,别到时审核通过不了。 11、结束。 [图片]
2021-01-19 - 小程序 font-family 设置自定义字体
小程序 font-family 设置自定义字体 现在 模拟器里有 设置字体成功[图片] 但在真机上无效 [图片] 请问是什么原因?是小程序不支持还是代码问题? wx.loadFontFace({ family: 'webfont', source: 'url("../../DINCond-Regular.otf")', success(e){ console.log(e) }, fail(e){ console.log(e) } })
2018-12-14 - [已解决]利用云开发解决小程序加载自定义字体无效果问题
终极效果 效果要求:使得最终页面效果在开发者模拟器、安卓、iOS都可正常显示字体效果。 [图片] 问题背景 如何在微信小程序上利用wx.loadFontFace方法加载自己要上传的字体的文件一直是困扰很多开发者的问题,很多人反映使用该方法在开发者工具模拟器上是可以显示的,有的开发者说在模拟器和iOS系统上可以正常显示自定义字体,但是安卓机上无法正常显示。 很多人为了能够实现加载自定义字体想了很多办法,比如 方法1: 字体文件转base64代码(如网站https://www.transfonter.org) ,然后存储在wxss文件中。这也是我曾使用的方法之一,确实很好用,安卓/苹果机上都可以正常实现效果。 缺点:但是面对字体文件包一般都很大的中文字体来说,转换成base64代码是极容易出现仅用来引用字体的wxss代码就超过2M的情况。 根据小程序的开发规矩,即使采用分包形式开发一个小程序,单个分包的代码量也是不能超过2M的,此时就是该方法的鸡肋。 方法2: 也有开发者利用iconfont上的在线引用方法,把自己会用到的字输进入然后点击生成字体,引用一个url即可实现。这种方法也是可行的。 缺点:但是对于部分字多或者可能出现用户输入文字的情况,这种方法就不太方便。 解决方案 今天,和大家讲的就是wx.loadFontFace方法,也是很多人一直在尝试,执着于想要利用这种方法实现。 在小程序开发者文档中,这一段文字是你不陌生的。 wx.loadFontFace(Object object) 基础库 2.1.0 开始支持,低版本需做兼容处理。 本接口从基础库版本 2.15.0 起支持在小程序插件中使用 动态加载网络字体,文件地址需为下载类型。‘2.10.0’起支持全局生效,需在 app.js 中调用。 注意: 字体文件返回的 contet-type 参考 font,格式不正确时会解析失败。 字体链接必须是https(ios不支持http) 字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com canvas等原生组件不支持使用接口添加的字体 工具里提示 Faild to load font可以忽略 ’2.10.0’ 以前仅在调用页面生效。 ——来源于《微信小程序开发者文档》 分析: 注意点1是大家一般无需担心的,我们下载到的字体文字很多是ttf文件,都是符合相关要求的。 注意点2也是一般不会犯错的,大家都会避开这个。 注意点3是非常重要的,也是很多开发者不明白的,下面,我会着重讲如何解决这个问题,解决了这个问题,无论调试还是真机都能正常显示。 在开发者文档中,有这样一段代码: [代码]wx.loadFontFace({ family: 'Bitstream Vera Serif Bold', source: 'url("https://sungd.github.io/Pacifico.ttf")', success: console.log }) [代码] 可以看出使用的是https链接,或许你曾经拿这段代码测试过。 或者你也曾经把字体文件上传到一些存储空间然后得到上传后文件的地址,或者说你曾经在开发者社区看到别人上传的字体文件的https链接然后来调用。 在开发者工具模拟器上正常显示,一到真机就显示不了。 当然为了想要使用我自己喜欢的字体,得到该字体的文件链接,我也突然脑洞大开,尝试把字体文件上传到云开发的云存储上,然后得到一个FileID链接和一个下载链接。 随后在页面或者全局的JS中加载该字体文件: 代码: [代码]loadFontFace() { wx.loadFontFace({ family: 'XXX', source: 'url("xxxxxx.ttf")', success(res){ console.log('res', res) }, fail(err){ console.log('err', err) } }) }, [代码] 遗憾的是,也是能在模拟器中显示出自定义字体效果,在安卓的真机上无法正常显示,iOS未知(我没有iOS系统故无法测试)。 到底是怎么回事,问题出在哪呢?于是一遍遍琢磨开发文档中的注意事项“字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com”。 后来琢磨后面一句话,我也是不明白啥是cors,于是就搜索了,记得其中一篇文章就讲到腾讯云的cors之类的。 操作步骤 1 进入腾讯云的官网(https://cloud.tencent.com),然后登录账号,选择公众号小程序登录,扫码并选择需要实现自定义字体的小程序进行登录 2 在顶部菜单栏找到“对象存储” [图片] 3 存储桶列表中新建一个存储桶 [图片] 4 给存储桶设置配置,跨域访问中设置好“cors规则”,把https://servicewechat.com域名填写到来源Origin的方框内。 [图片] 现在再来读读“字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com”是不是有了新的体会。 5 把需要使用的字体文件上传到该存储桶,然后点击文件右边的“详情”,你就会看到“对象地址”,这个地址就可以粘贴到JS文件的wx.loadFontFace使用。 [图片] 6 不要忘了设置刚刚上传的字体文件“访问权限”为“公有读私有写”,这样每个用户都可以读取这个字体文件了。 源码分享 JS文件 [代码]loadFontFace() { wx.loadFontFace({ family: 'XXX(你给字体拟的名称,最好英文如kaiti)', source: 'url("对象地址")', success(res){ console.log('res', res) }, fail(err){ console.log('err', err) } }) }, [代码] WXSS文件 [代码]page{ font-family: 'XXX'(你给字体拟的名称,最好英文如kaiti); } [代码] 或者 [代码].xxx(class名){ font-family:'xxx(你给字体拟的名称,最好英文如kaiti)' } [代码]
2021-04-13 - 虚拟业务指南请收好。
在小程序生态中,基于苹果运营规范,小程序内暂不支持iOS端虚拟支付业务。为此小编为大家整理了一份虚拟支付业务指南,希望大家在做虚拟业务时有所帮助: [视频] 那么,到底什么是虚拟支付业务呢? 虚拟支付业务是指购买非实物商品。比如:VIP会员、充值、录制课程、录制音频视频等虚拟产品。目前iOS端暂不支持虚拟支付业务。 我们常见iOS虚拟支付的不合规示例有哪些呢? 示例一 :小程序内存在付费购买虚拟内容或道具。商品多体现为提前编辑好的、录制好的虚拟商品。如录制视频课程、游戏道具。 整改建议 :建议去除小程序内所有付费购买虚拟服务,并根据提示修改相关内容及文案,文案可参照“由于相关规范,iOS功能暂不可用”。 [图片] 示例二 :付费解锁优质服务。多体现为提供虚拟商品的小程序可通过支付购买、开通虚拟会员等形式,体验小程序付费服务。比如:支付阅读章节小说、同城生活服务平台付费发帖/付费置顶等。 整改建议 :建议可以关闭iOS端虚拟支付通道,并将【马上充值】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端会员服务。 [图片] 示例三 :关闭iOS端虚拟支付功能后,虚拟商品页面仍然保留货架价格标签展示、购买/付费/订阅等功能或按钮。 整改建议 :建议去除小程序中的虚拟商品的价格展示,并更改为【免费】;并将【订阅 ¥128】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端虚拟商品购买服务。 [图片] 示例四 :关闭iOS端虚拟支付功能后,提供引导用户前往其他支付的路径/文案,完成虚拟支付闭环。 整 改建议 :建议去除iOS端小程序内引导用户前往其他支付路径/文案,并不再提供iOS端虚拟商品购买服务。 [图片] 示例五 :小程序含需要付费的虚拟商品,并设置限时免费的服务,限时免费结束后需付费才能继续提供服务。 整改建议 :建议将iOS端小程序中所有虚拟付费内容更改为免费,并不再提供iOS端虚拟商品购买服务。 [图片] 示例六 :关闭iOS端虚拟支付功能后,小程序中虚拟产品页面不可以含有付费性质的关键字(如:购买、已购、付费、支付等),包括但不限于功能按钮、功能页面、支付提示及任何商品介绍等。 整改建议 :建议将小程序iOS端虚拟产品页面中的文案/按钮/功能tab含有限制的关键字更改为【免费】或删除。并不再提供iOS端虚拟商品购买服务。 [图片] 如小程序内存在以上不合规的虚拟支付内容,请开发者重视并及时整改。对于首次违规的小程序,平台将下发站内信整改通知,并给予三天整改时间,请开发者按照提示在限期内完成整改。平台将会对到期未完成整改的小程序进行搜索策略调整,并在小程序功能使用上进行一定的限制,直到小程序完成内容整改。
2020-04-23 - 三个按钮宽度总和750,为什么不能铺满屏幕宽?
.b1{ position:absolute;bottom:0;left:0;width:100rpx;height:160rpx;line-height:160rpx;border-radius:0; } .b2{ position:absolute;bottom:0;left:100rpx;width:550rpx;height:160rpx;line-height:160rpx;border-radius:0; } .b3{ position:absolute;bottom:0;right:0;width:100rpx;height:160rpx;line-height:160rpx;border-radius:0; } [图片] <button class='b1' type='primary'>b1</button> <button class='b2' type='primary'>b2</button> <button class='b3' type='primary'>b3</button> 很奇怪的一个问题,总和750rpx, 但是中间为什么还有缝隙?有人知道吗?
2021-01-22 - 什么小程序需要社交相关类目?
什么内容或服务的小程序需要补充社交相关类目? [视频] [图片] 1、陌生人交友:小程序内涉及提供在线陌生人交友服务,需补充社交-陌生人交友类目。 所需资质:《增值电信业务经营许可证》(核准服务项目包含“互联网信息服务业务”) 案例:如下图小程序涉及通过展示用户微信号、电话等信息,提供陌生人交友服务,需补充社交-陌生人交友类目。 [图片] 2、熟人交友:小程序内涉及提供在线熟人交友服务,需补充社交-陌生人交友类目。 所需资质:《增值电信业务经营许可证》(核准服务项目包含“互联网信息服务业务”) 案例:如下图小程序通过用户分享好友关系群,提供熟人交友服务,需补充社交-熟人交友类目。 [图片] 3、社区/论坛:小程序内提供社区/论坛服务,包括UGC内容的发布与交流,如:论坛、贴吧、社群等,需补充社交-社区/论坛类目。 所需资质(2选1): ①《非经营性互联网信息服务备案核准》 ②《组织机构代码证》或《统一社会信用代码证》(适用于政府主体) 案例:如下图小程序用户可通过自定义发布帖子功能,提供论坛服务,需补充社交-社区/论坛类目。 [图片] 4、直播:小程序内涉及提供在线直播服务,需补充社交-直播类目。 所需资质(3选1): ①《信息网络传播视听节目许可证》 ②《网络文化经营许可证》(经营范围含网络表演)(适用于含表演性质的直播) ③《统一社会信用代码》《情况说明函件》(适用于政府主体) 案例:如下图小程序某个模块涉及提供在线产品直播服务,需补充社交-直播类目。 [图片] 5、笔记:小程序内涉及提供自定义内容的记录及分享,包括文字、图片、视频、音频等服务,需补充社交-笔记类目。 所需资质(2选1): ①《非经营性互联网信息服务备案核准》 ②《组织机构代码证》或《统一社会信用代码证》(适用于政府主体) 案例:如下图小程序提供自定义音频录制,需补充社交-笔记类目。 [图片] 6、婚恋:小程序内涉及提供婚恋交友服务,需补充社交-婚恋类目。 所需资质:《增值电信业务经营许可证》(核准服务项目包含“互联网信息服务业务”) 案例:如下图小程序涉及通过发布婚恋需求者个人信息提供婚恋交友服务,需补充社交-婚恋类目。 [图片] 7、问答:小程序内涉及提供用户可以根据自身的需求,有针对性地发布、提出和解决问题,或者搜索其他用户解答的平台服务,需补充社交-问答类目。 所需资质(2选1): ①《非经营性互联网信息服务备案核准》 ②《组织机构代码证》或《统一社会信用代码证》(适用于政府主体) 案例:如下图小程序支持用户自定义发布问题或答案,提供平台服务,需补充社交-问答类目。 [图片] 8、直播答题:小程序内涉及提供直播答题服务,需补充社交-直播答题类目。 所需资质:《信息网络传播视听节目许可证》 案例:如下图小程序涉及提供在规定时间点提供在线答题直播服务,需补充社交-直播答题类目。 [图片]
2020-12-11 - 小程序简单两栏瀑布流效果
瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式。视觉表现为参差不齐的多栏布局,即多行等宽元素排列,后面的元素依次添加到其后,等宽不等高,根据图片原比例缩放直至宽度达到我们的要求,依次放入到高度最低的那一栏。 先上代码:https://developers.weixin.qq.com/s/Fgm5s1mz7Wdm 所谓简单,是指只考虑图片,图片之外的其他元素高度固定,不在考虑范围内。 说一下基本的实现思路: 1、加载列表数据 2、在一个隐藏的view中加载图片,通过image组件的bindload获取图片的实际宽高并存储 3、等所有图片加载完成后遍历列表,将图片插入到高度低的那一栏,同时更新该栏高度 我也考虑过在第二步bindload获取到宽高后就直接插入到栏位中,但是会出现小的图片先加载完先出现到页面中,虽然瀑布流不是普通的列表那样的排序,但是也不能小的图片在上面这样太乱顺序,所以就改成了获取宽高先存储,等所有图片加载完成后再往页面上渲染。 来看看实际的代码 不需要渲染到wxml中的数据,我放到了jsData中,主要是两栏的高度和是否在加载数据的标记。 tempPics是第一次加载的数据,临时存放,用于加载图片宽高 columns是两个栏位的实际展示数据 [代码]jsData: { columnsHeight: [0, 0], isLoading: false }, data: { columns: [ [], [] ], tempPics: [] } [代码] 1、加载列表数据 这一步没什么好说的,主要是触发方式,我的代码里是放在页面加载以及拉到页面底部时触发 [代码]onLoad: function() { this.loadData() }, onReachBottom: function() { this.loadData() } [代码] 加载后将列表数据存到tempPics中,用于页面加载获取宽高 2、在一个隐藏的view中加载图片,通过image组件的bindload获取图片的实际宽高并存储 [代码]<view class="hide"> <image wx:for="{{tempPics}}" src="{{item.pic}}" bindload="loadPic" binderror="loadPicError" data-index="{{index}}" /> </view> [代码] 主要是image组件的bindload来获取实际宽高,这里还增加了binderror,防止出现图片加载出错的时候卡死 [代码]loadPic: function(e) { var that = this, data = that.data, tempPics = data.tempPics, index = e.currentTarget.dataset.index if (tempPics[index]) { //以750为宽度算出相对应的高度 tempPics[index].height = e.detail.height * 750 / e.detail.width tempPics[index].isLoad = true } that.setData({ tempPics: tempPics }, function() { that.finLoadPic() }) } [代码] 获取到宽高后,以750为宽度计算出相对应的高度并存储,然后增加一个加载完成的标记。加载出错后就强制高度为750,这样展示的时候就是一个正方形。 单个图片加载完成并存储后调用finLoadPic方法来判断所有图片是否都加载完成。 遍历列表,只要有一个图片没有加载完成的标记,就判断为没有加载完成。 加载完成后进入下一步。 [代码]finLoadPic: function() { var that = this, data = that.data, tempPics = data.tempPics, length = tempPics.length, fin = true for (var i = 0; i < length; i++) { if (!tempPics[i].isLoad) { fin = false break } } if (fin) { wx.hideLoading() if (that.jsData.isLoading) { that.jsData.isLoading = false that.renderPage() } } } [代码] 3、等所有图片加载完成后遍历列表,将图片插入到高度低的那一栏,同时更新该栏高度 这里需要再便利一遍列表,根据当前栏位的高度情况,将图片插入到高度底的那一栏,同时把这一栏高度加上当前图片的高度(不是实际高度,是上一步以750为宽度算出来的高度) [代码]renderPage: function() { var that = this, data = that.data, columns = data.columns, tempPics = data.tempPics, length = tempPics.length, columnsHeight = that.jsData.columnsHeight, index = 0 for (var i = 0; i < length; i++) { index = columnsHeight[1] < columnsHeight[0] ? 1 : 0 columns[index].push(tempPics[i]) columnsHeight[index] += tempPics[i].height } that.setData({ columns: columns, tempPics: [] }) that.jsData.columnsHeight = columnsHeight } [代码] 在wxml中展示的时候image组件的mode要使用widthFix,同时wxss中图片的高度和宽度一样,这样加载出错的图片可以正方形展示 11月21日增加: 根据@杨泉的建议,也尝试了使用wx.getImageInfo来获取图片的宽高(具体代码可以参考评论区),代码也精简了很多。但是实际比较下来速度要比用image组件慢,初步推测原因是[代码]wx.getImageInfo[代码]会返回本地路径,多了写本地临时文件的时间 ps:用到瀑布流的地方,最好能后端直接返回图片的宽高,省去小程序端获取宽高的麻烦 再ps:我个人并不建议小程序端使用瀑布流
2020-01-14 - 「分享」高性能双列瀑布流极简实现(附示例)❤️
前言 在日常开发过程中,经常会有双列瀑布流场景的需求出现,如商品列表、文章列表等,本文将简单介绍这种情景下如何高效、精准的实现双列瀑布流场景,支持刷新、加载更多等,实现效果如下。 [图片] [图片] 开发思路 瀑布流视图有一种参差的美感,常规列表布局如 flex wrap 等由于存在行高度限制,无法让第二行的 item 对齐上一行最矮处,因此,瀑布流布局时采用双列 scrollview 的 flex 布局。 参差布局的实现,采用代码计算左右两列的高度,然后对左右两列总高度进行比较,新加入的 item 总是排在总高度较小的那列后面。 计算时可以尽可能的缓存高度,例如左右两列高度在每次计算时都缓存起来,有新的 item 加入列表时直接增加左右两列高度即可,不需要重新从头计算。 index.js [代码]const tplWidth = (750 - 24 - 8) / 2; const tplHeight = 595; // plWidth * 1.66 newPhotos.forEach(photo => { const { height, width } = photo let photoHeight = tplWidth if (height > width) { photoHeight = tplHeight photo.display = 'long' } else { photo.display = 'short' } if (leftHeight < rightHeight) { leftList.push(photo) leftHeight += photoHeight } else { rightList.push(photo) rightHeight += photoHeight } }) [代码] index.wxml [代码]<!-- list --> <view class='list'> <!-- left --> <view class='left-list'> <block wx:for="{{leftList}}" wx:key="{{item._id}}"> <cell photo="{{item}}" bindclick='onCellClicked' /> </block> </view> <!-- right --> <view class='right-list'> <block wx:for="{{rightList}}" wx:key="{{item._id}}"> <cell photo="{{item}}" bindclick='onCellClicked' /> </block> </view> </view> [代码] index.css [代码].list { display: flex; flex: 1; position: relative; flex-direction: row; justify-content: space-between; padding-left: 12rpx; padding-right: 12rpx; padding-top: 8rpx; } .left-list { display: flex; position: relative; flex-direction: column; width: 359rpx; } .right-list { display: flex; position: relative; flex-direction: column; width: 359rpx; } [代码]
2019-02-27 - 一种新颖的小程序内引导关注公众号的方式
一种新颖的小程序内关注公众号的方式 本文背景今天中午在某大佬的前端群,看到一个抽奖的活动,体验了下,发现一件很有意思的事情,具体听我我娓娓道来 本文内容小程序内引导关注公众号是一个永恒的热门话题,毕竟每个产品各有侧重,各有所长,各有千秋 截图一 [图片] 截图二 [图片] 截图三 [图片] 截图四 [图片] 截图五 [图片] 截图六 [图片] 本文内容本文主要分享了一种非常新颖的小程序关注公众号的方式,该方式很特别的地方是通过公众号文章内嵌公众号二维码的方式。
2020-09-17 - 使用云开发CMS能力实现简易商场
源码 点此领取 技术栈 云开发 CloudBase:云端一体化的 Serverless 后端服务解决方案。Taro:一套遵循 React 语法规范的 多端开发 解决方案开发工具 建议提前安装好 微信开发者工具Node LTS 版本VS Code 编辑器CloudBase VS Code 插件需求分析 只考虑基本的功能: 商品列表与下单:展示商品信息,创建订单订单列表:展示订单列表 资源准备 1. 在微信开发者工具中开通云开发,请选择按量付费 如果你的环境是预付费,请到设置中,将支付方式转换为按量付费 [图片] 2. 安装 CMS 系统 (1)更新到最新的 Nightly 版本工具,在工具顶部 Tab 栏中,点击「更多」-「内容管理」。 [图片] (2)点击开通,勾选同意协议后,点击确定。 [图片] (3)开通内容管理需要填写管理员账号,填写账号后,点击「确定」完成。 [图片] (4)开通拓展需要一定时间,请耐心等待。 (5)完成后,点击「更多」-「内容管理」,即可看到内容管理的入口和相关信息。点击访问地址,即可在弹出的窗口中进行内容管理的相关配置。 [图片] 3. 登录 CMS 系统,创建资源 CloudBase CMS 已经部署在当前环境下的静态网站托管中,访问地址的格式如下:云开发静态托管默认域名/部署路径,例如 https://envid.ap-shanghai.app.tcloudbase.com/tcb-cms/(结尾有 / 符号)。默认域名可以访问控制台查看。 打开 CloudBase CMS 后,你需要先登录,账号密码为安装时设置的管理员账号和密码。 在开始管理内容数据前,我们需要先创建一个项目。CloudBase CMS 使用项目划分不同类的内容,便于区分内容数据用途,进行权限管理。 首先,我们需要点击新建项目下方的创建新项目按钮,创建一个名为小商店,Id 为 shop 的项目。 [图片] 创建完项目后,点击项目卡片,进入项目的管理页面,我们会看到项目的欢迎页面。 [图片] 创建商品类型,管理商品信息 创建一个名称为商品的内容模型,数据库名为 goods,即将商品数据存储到 goods 数据集合中。如果新建内容的时候指定的集合不存在,CloudBase CMS 会自动新建集合。 [图片] 在创建完内容模型后,我们会得到一个空的内容模型。接下来,我们需要为商品添加商品名称,商品图片,价格,库存数量等字段。 为商品添加商品名称属性,因为商品名称通常是比较短的文字,所以我们可以选择单行字符串字段,点击右侧的单行字符串卡片,填写商品名称的字段信息。除了基本的名称,数据库字段名之外,我们还可以为此字段添加其他的限制,如最大长度,限制填写商品名称时的最大长度,创建商品时,是否必需填写商品等。 [图片] 类似的,我们可以创建数字类型的价格字段以及库存数量,图片类型的商品图片字段。在创建图片字段时,考虑到商品的图片可能有多张,我们可以打开允许多个内容按钮,表明可以上传多张图片。 [图片] 创建的 goods 数据库集合的结构如下: [图片] 同上,类似的创建一个名称为订单列表,数据库集合名为 order 的内容模型,来管理订单信息。创建的 order 数据库集合的结构如下: [图片] 添加一个商品 [图片] 创建项目 1、拉取模板 # 安装 taro cli 工具 npm install -g @tarojs/cli@2.2.7 # 拉取模板 git clone https://github.com/TencentCloudBase/cloudbase-minishop.git 使用微信开发者工具导入项目,进入 client 目录,安装依赖: npm i 项目目录 cloud/functions 包含写好的微信支付的两个云函数, pay 和接收支付消息推送的 pay-callback 云函数。使用时需使用微信开发者工具上传这两个云函数。 2、项目目录 . ├── client // 小程序源码 │ ├── config │ └── src │ ├── assets │ ├── components │ └── pages │ ├── index │ └── order-list └── cloud // 云开发相关源码 │ └── functions │ ├── pay │ └── pay-callback ├── cloudbaserc.json // 云开发配置 ├── project.config.json // 小程序配置 微信支付下单流程 1、小程序调用云函数,在云函数中调用统一下单接口,参数中带上接收异步支付结果的云函数名和其所在云环境 Id。 const cloud = require("wx-server-sdk"); const res = await cloud.cloudPay.unifiedOrder({ envId: '', subMchId: '', body: "商品名", totalFee: 100, outTradeNo: '订单号', spbillCreateIp: "127.0.0.1", functionName: "pay-callback" }); // 返回 res.payment 支付结果回调的云函数必须返回如下一个对象,否则会视为回调不成功,云函数会收到重复的支付回调。 { errcode: '', errmsg: '', } 2、统一下单接口返回的成功结果对象中有 payment 字段,该字段即是小程序端发起支付的接口(wx.requestPayment)所需的所有信息。 3、小程序端拿到云函数结果,调用 wx.requestPayemnt 发起支付 wx.requestPayment({ ...payment, success (res) { }, fail (res) { }tt })https://docs.cloudbase.net/ 4、支付完成后,在统一下单接口中配置的云函数将收到支付结果通知。 多端支持 - 跨平台 小程序Web 相关文献 云开发文档 云开发微信支付 支付接口
2021-09-10 - 订阅信息恶心至极?没有服务器的我们也能直接发消息到微信(公众号)! [即抄即用,拎包入住]
更新时间(2020/12/2) 大家好,众所周知,今年左右新出的订阅消息对商家和用户的友好度都极低,少了一个直接发信息到微信的最重要的渠道。 [图片] 那么我们动动歪心思,直接发消息到公众号(其实这是不是wx的本意???)。 所有服务号都可以在功能->添加功能插件处看到申请模板消息功能的入口,但只有认证后的服务号才可以申请模板消息的使用权限并获得该权限。 为方便公众号用户方便、快捷地接入小程序服务,公众号用户可复用公众号资质创建小程序。当前每个账号的模板消息的日调用上限为10万次,单个模板没有特殊限制。当账号粉丝数超过10W/100W/1000W时,模板消息的日调用上限会相应提升。这还不美滋滋吗,对于小商家来说等于无限次了。 所以我们一开始最好就在公众号进行微信认证,复用公众号的资质来注册小程序(注意,可以复用5个(好像))。 (我付出了¥300的惨痛代价)。 -------------------------------------------------------------------------------- 以上搞定后,流程走起 重点中的重点:为您的函数申请公网固定ip 有了这个ip之后,没有服务器的我们(穷)就能绕过ip白名单,这不正是真正的云开发精神吗? [图片] 0.用你的小程序账号登陆腾讯云,并在里面新建一个云函数 [图片]>>[图片] 选择你要发消息到公众号的小程序 [图片] [图片] -----------------我是2020/10/23的拎包哥----------------------- 创建并选择角色 记得在 https://console.cloud.tencent.com/cam/role 创建角色 [图片] 勾选tcb,scf [图片] 在搜索框里搜scf,tcb后,有什么策略就勾选什么策略 [图片] [图片] 创建角色名 [图片] 在云函数启用刚刚新建的角色 [图片] ps. 记得做好这一步,不然各种报错 missing authorationo key // 报错:缺失授权键 you are not authorized to xxx // 你没有权限去xxx 1.打开 https://cloud.tencent.com/document/product/583/38198 ,申请白名单 [图片] 2.审核通过后,再跟着步骤走。 [图片] 最后你的云函数会得到一个公网固定ip [图片] 3.开始码云函数的代码 如下,记得把wx-server-sdk和request-promise的包都npm下来。 依赖包可以通过这里上传上来 [图片] 我的做法 把获取的accessToken储存在云开发的数据库里。这样就不用担心access token的生成次数超过限制了 'use strict'; const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() const resInfo = db.collection('resInfo') const appid = '公众号的APPID'; // APPID const secret = '公众号的密钥'; // Secret const rp = require('request-promise') exports.main_handler = async (event, context, callback) => { var that = this var options = { url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + appid + '&secret=' + secret, json: true } return await rp(options) .then(async res1 => { return await resInfo.where({name:'outInfo'}).update({ data: { access_token: res1.access_token } }).then(res2 => { return res2 }) }).catch(err => { return err }) } 公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。4.设置触发器(触发器偶尔会失灵,所以最好是59min触发一次)。 [图片] 权限设置 由于云函数的访问不存在openid,所以安全规则必须为任何人可读可写。 [图片] 5.有了可以稳定刷新的access_token后,根据需求挑选你的公众号模板消息,开始你的表演。 例如:做餐饮小程序的朋友都想用户下单后发送订单信息到商户。那么就需要 获取商户的openid 步骤 注:公众号的openid在小程序开发工具就可以查出来 5.1 获取公众号openid列表 https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN 注意我没有加上next_openid = NEXT_OPENID,是为了取出公众号的所有的openidopenid列表在 res.data.openid5.2 for循环openid列表,根据商户的微信信息(nickname,city等等)找出只属于商户的openid https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN --------------------------------------- var openidList = openid列表 for(var i in openidList){ wx.request({ url:'https://api.weixin.qq.com/cgi-bin/user/info?access_token='+ openList[i] + '&lang=zh_CN', success(){ console.log(....) } }) } 这里就不用unionid了,不用浪费时间在上面), 就可以使用只发给商户的模板消息了。 公众号对应文档链接 https://developers.weixin.qq.com/doc/offiaccount/User_Management/Getting_a_User_List.htmlhttps://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId, -----------------------------------------效果图--------------------------------------- [图片] -------------------------------------------------------------------------------- 哎,差不多了,感觉是有点折腾,如果感觉还是不够直白的,可以指出来我继续补充 。 求点赞,你的评论就是对拎包哥最大的支持。 [图片][图片] ===================更新于2020/10/23======================
2020-12-02 - 公众号获取用户基本信息(UnionID机制)接口返回sex字段3
公众号获取用户基本信息(UnionID机制)接口返回 sex=3 依据官方文档只有用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 这几个值 麻烦看下 appid 和openid 私信我要 [图片]
2018-08-16 - 平时我们在往数据库里面存日期信息的时候,是倾向于时间戳还是日期呢?
平时我们在往数据库里面存的时候,是倾向于时间戳还是日期呢? 另外,有没有好用的转化工具或者npm第三方包推荐 万分感谢
2020-05-15 - 云数据库查询怎么将查询的date结果转换格式?或者dateToString怎么在条件查询中使用?
云数据库查询怎么将查询的date类型日期结果转换格式? 或者dateToString怎么在条件查询中使用?
2020-04-18 - 云开发数据库使用时间戳怎么转换格式?
云开发数据库使用时间戳怎么转换格式? 创建一个订单集合 order,小程序前端代码为 :orderData: new Date()//ld_date db.collection('order').add({ data: { order_id: this.data.order_id, orderData: new Date()//ld_date } 上传到云端格式为:date 类型 [图片] 预览:[图片] 小程序前端读取集合代码: db.collection('order').orderBy('timeStamp', 'asc').limit(10).where({ _openid: app.globalData.openid, // 填入当前用户 openid orderPayed:true }).get().then(res => { this.setData({ arrDate:res.data }) console.log(res) console.log(new Date(res.data[0].orderData)) }) 控制台输出: [图片] orderData为时间戳格式 wxml中的代码 <view class="card short-card"> arrDate[0].orderData = {{arrDate[0].orderData}} </view> 显示为:[图片] [图片] 输出结果为对象 如何解决,将orderData 时间戳对象 格式化输出为 2020-11-10 12:20 这种格式呢?
2020-11-09 - 微信小程序云开发教程-时间存储、转换、比较
一、背景 许多功能都需要记录时间,比如发表博文、创建商品等。 小程序的云开发中云数据库提供了一种专门的时间类型: Date:时间 字符串格式如下: create_time: Fri Aug 14 2020 12:11:56 GMT+0800 (China Standard Time) 云开发中云数据中的格式如下: [图片] 优点:后台可以直接操作,方便修改 缺点:较差的可读性 二、存储 // 创建的时间 create_time: new Date() // create_time: new Date('2020-10-10') 三、转换 var date = new Date(create_time); // 格式化创建时间为 2020-05-09 21:30 creat_date_time = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() 这里使用的的是Date对象,用于处理日期和时间。 getFullYear() 使用 getFullYear() 获取年份。 getTime() getTime() 返回从 1970 年 1 月 1 日至今的毫秒数。 setFullYear() 如何使用 setFullYear() 设置具体的日期。 toUTCString() 如何使用 toUTCString() 将当日的日期(根据 UTC)转换为字符串。 getDay() 如何使用 getDay() 和数组来显示星期,而不仅仅是数字。 四、比较 可以直接使用时间对象进行比较 [图片]
2020-08-25 - 新能力 | 云开发CMS内容管理系统,5分钟搞定小程序管理后台
小程序·云开发的云调用能力,让用户可以免鉴权快速调用微信的开放能力,极大节约了开发成本。现在,大家期待已久的云开发 CMS 内容管理系统,终于上线啦!顺便提示,接下来还可以二次开发哦! 云开发 CMS 管理系统是什么? 云开发 CMS 内容管理系统是云开发提供的一个扩展程序,可以在云开发控制台一键安装在自己的云开发环境中,方便开发人员和内容运营者随时随地管理小程序 / Web 等多端云开发内容数据。不用编写代码就可以使用,还提供了 PC /移动端浏览器访问支持,支持文本、富文本、图片、文件、关联类型等多种类型的可视化编辑。 [图片] 先来看看云开发CMS的"庐山真面目" 首先我们通过几张截图来直观感受一下 CMS 内容管理系统扩展: 图1 云开发控制台的安装界面截图 [图片] 图2 安装并配置好内容的 CMS 内容管理系统界面演示 [图片] 图3 CMS 内容管理系统界面的移动端演示 [图片] 云开发 CMS 内容管理系统有哪些功能特性 ? 特性 介绍 免开发 基于后台建模配置生成内容管理界面,无须编写代码 多端适配 支持 PC/移动端访问和管理内容 功能丰富 支持文本、富文本、图片、文件 等多种类型内容的可视化编辑,并且支持内容关联 权限控制 系统基于管理员/运营者两种身份角色的访问控制 外部系统集成 支持 Webhook 接口,可以用于在运营修改修改内容后通知外部系统,如自动构建静态网站、发送通知等 数据源兼容 支持管理小程序/ Web / 移动端的云开发数据,支持管理已有数据集合,也可以在 CMS 后台创建新的内容和数据集合 部署简单 可在云开发控制台扩展管理界面一键部署和升级 什么场景下适合使用 CMS ? 1. 适用于需要为小程序应用增加一个运营管理后台的业务 小程序应用有偏运营方面的文章编辑和发布、运营活动配置、素材管理等数据管理需求,使用 CMS 扩展之后,不用手动线上修改 db 数据,也不用投入人力物力开发管理后台,可以随时随地使用自己环境下部署的 CMS 内容管理系统来管理,同时还支持区分管理员和运营者的身份权限。 2. 适用于快速开发内容型的网站应用、小程序应用等场景 CMS 内容管理系统还可以帮助开发者提升开发网站应用、小程序应用的效率,省去一部分后端开发工作。例如安装了CMS 扩展之后,解决了内容和数据的管理和生产问题,直接可以结合前端应用框架读取 db 数据进行渲染。例如基于 CMS 可以快速开发博客、企业官网等小程序/网站应用,最后悄悄透露一下,云开发的官网 (http://cloudbase.net/) 就是基于 CMS 扩展 + Next.js + 云开发静态托管搭建和部署的。 如何安装和使用 CMS ? 第一步:切换为按量付费 由于 CMS 扩展需要用到静态网站托管资源,必须在按量计费的环境下才可以部署,因此首先要切换计费方式为按量付费。 1. 微信小程序开发者 登录微信开发者工具-云开发控制台 在【云开发控制台】-【设置】-【环境设置】-【支付方式】中点击切换【按量付费】即可。 注意:这里需要先保证腾讯云账户中是有充值金额的哦~ [图片] 2. 腾讯云开发者 登录腾讯云云开发控制台 在【云开发 CloudBase 控制台】-【环境】-【资源购买】-【计费模式】中点击【切换按量付费】即可。 [图片] 第二步:在腾讯云控制台安装扩展 登录腾讯云控制台 微信小程序开发者需要使用微信公众号登录! [图片] 在【云开发 CloudBase 控制台】-【扩展能力】-【扩展管理】中找到 CMS内容管理系统 扩展进行安装 安装时需要进行资源的授权和扩展程序的配置,比如管理员和运营者的账号密码配置等,同时需要提供自定义登录的密钥,可以点击自定义登录密钥旁边的小图标了解如何填写。 [图片] 第三步:使用 CMS 内容管理系统 完成【CMS内容管理系统】的安装以后,然后访问该扩展的管理页,可以在【扩展运行方式】Tab 查看使用指引,依照文档完成 CMS 的使用,下面简单介绍一下快速上手的步骤,更多细节可以参考运行方式。 [图片] 访问 CMS 系统 CMS 扩展已经部署在当前环境下的静态网站托管中,访问路径为“静态托管的默认域名+安装设置的部署路径” 访问地址的格式如下: [代码]云开发静态托管默认域名/部署路径[代码],例如 [代码]https://xxxx.tcloudbaseapp.com/tcb-cms/[代码] 账号登录 打开 CMS 系统后首先会提示需要登录,我们首先使用使用安装扩展时设置的管理员账号和密码进行登录 内容建模 登录成功后,首先需要进行内容的建模设置,例如我们想为自己的博客应用(小程序/网站)来生成管理界面。 假设当前已有一个管理 文章的数据库集合 [代码]articles[代码],我们可以在 CMS 管理后台新建一个 “文章” 内容(如果新建内容的时候指定的集合名不存在,CMS 扩展会自动新建集合)来生成“文章”类型的内容管理界面。 假设数据库集合 [代码]articles[代码] 的结构如下: 字段名 类型 描述 _id ID 文章唯一 id name String 文章标题 cover String 封面图,这里存放云开发的存储的文件的 cloudID content String 文章内容,采用 markdown 格式 author ID 作者的用户 id createTime DateTime 创建时间 updateTime DateTime 更新时间 tag String[] 标签,例如 [代码]["serverless","cms"][代码] category String[] 分类,例如 [代码]["前端","开发"][代码] 我们在“内容设置”中点击“新建”来创建“文章”类型时,可以对照上面的集合数据把字段类型和字段的限制进行配置,例如封面图可以直接选择 “图片”字段类型,文章内容可以直接选择 “Markdown” 类型,这样在生成的管理界面里可以直接上传图片和通过编辑器编写文章,保存在数据库集合的时候,依然会保存为数据库支持的类型,图片会存储为云存储的 CloudID, 内容会存储为字符串等。 [图片] 创建并保存之后会自动刷新生成”文章“的运营界面 管理内容 接下来就可以进行运营管理内容操作了,可以使用运营者身份登录,对新创建的“文章”进行操作,我们可以新建一篇文章。 [图片] 文章发布成功后,即可在文章列表中看到这篇文章 [图片] 使用内容数据 采用 CMS 管理的内容,依然可以通过云开发各端 SDK 进行访问(需要注意的是在前端访问时,需要正确设置数据库的安全规则设置,例如设置为所有用户可读,仅创建者可写)。 例如,在上面的例子里,我们需要在云函数中获取文章的标签是 [代码]CloudBase[代码] 的最新 10 条文章,可以采用以下代码来获取数据: [代码]db.collection("articles") .where({ tag: "CloudBase" }) .orderBy("createTime", "desc") .limit(10) .get(); [代码] 获取到内容数据就可以在各种场景使用了,比如在小程序/ Web 中构建应用和网站,具体的CMS + 应用开发的实践可以关注后期我们的实践教程。 [图片] 后续,云开发CMS内容管理系统将支持二次开发,用户可以自由定制自己的管理后台。云开发将始终坚持,为开发者提供一站式云服务! [图片] 最后,小编赠上《5分钟部署云开发CMS系统》教程,帮助大家快快上车! 视频链接: https://v.qq.com/x/page/f09687on1qv.html 文档链接 :(CMS 内容管理系统链接) https://cloud.tencent.com/document/product/876/44547
2020-09-14 - 如何优雅的调用云函数?
前言 这是在做《#小程序云开发挑战赛#-情侣券-想做就做》这个项目过程中学习到了一个新知识。 能够优雅的调用云函数,极大程度的减少维护成本。 在此之前我都是业务代码层面直接调用云函数,而且一个云函数就一个方法。 如下: 首页 [代码]wx.cloud.callFunction({ name: 'login' }).then(res => { wx.setStorageSync('openid', res.result.openid) }) [代码] login 云函数 [代码]const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = (event, context) => { const wxContext = cloud.getWXContext() return { event, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID, env: wxContext.ENV, } } [代码] 这种代码写起来很爽,但是要维护起来的时候就很头疼,云函数越写越多,找起来就比较麻烦,无法快速定位。 如何解决呢? 从两个维度来解决: 从小程序代码来说,可以加一个api层来管理所有云函数的调用。 从云函数代码来说,可以在一个云函数里面做个简单路由写多个方法。 1.云函数调用统一管理 从之前的顺序:小程序业务层=>云函数 改成 小程序业务层=>API层=>云函数 业务层:调用API [代码]// 先导入api的user类 import { login } from 'api/user.js' login().then(res => { wx.setStorageSync('openid', res.result.openid) }) [代码] API层:调用云函数 [代码]async function login() { return wx.cloud.callFunction({ // 云函数名称 name: 'user', // 传给云函数的参数 data: { action: 'login' } }); } module.exports = { login:login } [代码] 上面案例中我们只用了login方法,其实在实战中user里面会有所有的相关操作,引入也可以引入多个方法。 所有的云函数都会在api层进行管理,这样改云函数就不需要去业务代码里面去找了。 业务层只负责传参与回调,调用过程不需要关心,同时还可以支持多个页面复用api方法。 2. 一个云函数多个方法 从上面api层的案例不难看出,data里面传入了一个action参数,这个参数就是你要调用的方法名称。 云函数: [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init(); // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() // 简单路由 if (event.action && userHelper[event.action]) { const result = await userHelper[event.action](wxContext, event) return result } return { message: 'This action was not found', error: -1, } } const db = cloud.database(); const userHelper = { async getOpenId(context, params) { return { openid: context.OPENID, } } } [代码] 通过userHelper来管理所有的方法,你如果需要添加方法可以直接在userHelper下面添加即可,action传入方法名称就可以调用了。 这样便于查找与管理,同一类业务之需要创建一个云函数即可。 还有一点就是减少云函数数量,云开发不同的收费套餐是有云函数数量限制的。 套餐配合可见:配额说明 总结 全部写完了,如果觉得不错就给我的《#小程序云开发挑战赛#-情侣券-想做就做》点个赞,谢谢。
2020-09-22 - 如何只使用一个云函数搞定一个庞大而复杂的系统
吐槽 翻遍社区的文章,关于云开发的干货,少之又少,大部分都还是官方文档的搬来搬去,没啥营养,是时候放出一点技术"干货"了(有经验的开发者都能想到的方案)! 正题 小程序云开发的云函数的最大限制是 [代码]50[代码] 个,假设每个接口都使用 [代码]1[代码] 个云函数的话,有 [代码]10[代码] 张表,每张表都有 [代码]增删改查[代码] 四个接口,那么就会有 [代码]40[代码] 个接口,再加上一些其他接口,差不多刚刚好够用,那如果有 [代码]20[代码] 张表,甚至更多的表、更多的接口呢?对于中小型的小程序来说足够使用,那如果一个非常庞大而复杂的系统该怎么办呢? 而且每一个云函数的运行环境是独立的,想要共享一些数据也不是特别方便,那么有没有什么办法突破这样的限制呢? 其实解决方案很简单,只需要一点点的 [代码]OOP[代码] 思想和利用 [代码]JavaScript[代码] 的特性,一个云函数就可以搞定所有的接口。 具体的实现请往下看。 思路 云函数的运行环境是 [代码]Nodejs[代码] , 那么使用的语言就是 [代码]JavaScript[代码] ,可以充分的利用 [代码]JavaScript[代码] 的特性。 [代码]JavaScript[代码] 中的 [代码]属性访问表达式[代码] 有两种语法 [代码]expression . identifier expression [ expression ] [代码] 第一种写法是一个表达式后跟随一个句点 [代码].[代码] 和一个标识符。表达式指定对象,标识符则指定需要访问的属性的名称。 第二种写法是使用方括号 [代码][][代码],方括号内是另一个表达式(这种方法适用于对象和数组)。第二个表达式指定要访问的属性的名称或者代表要访问数组元素的索引。 不管使用哪种形式的属性访问表达式,在 [代码].[代码] 和 [代码][][代码] 之前的表达式总是会首先计算。 虽然 [代码].[代码] 的写法更加简单,但这种方式只适用于要访问的属性名称的合法标识符,并需要准确知道访问的属性的名字,如果属性的名称是一个保留字或者包含空格和标点符号,或者是一个数字(对于数组来说),则必须使用方括号 [代码][][代码] 的写法。当属性名是通过运算得出的值而不是固定值的时候,这时也必须使用方括号 [代码][][代码] 写法。 感谢社区大神 @卢霄霄 提供参考资料,详见 [代码]JavaScript权威指南[代码] (犀牛书)4.4章节。 可以使用 [代码][][代码] 的形式来完成动态的属性访问。具体实现请往下看。 实现 上面说了太多废话了,下面直接开干吧。 新建云函数 在云开发目录中新建一个云函数,我这里命名为 [代码]cloud[代码]。 打开 [代码]index.js[代码] 文件你会看到下面这段代码 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') // 初始化 cloud cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() return { event, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID, } } [代码] 这个云函数仅作为入口使用,上面提到了云函数的运行环境是 [代码]Nodejs[代码] 那么 [代码]Nodejs[代码] 的特性也是可以使用的,这里主要用到的是全局对象 [代码]global[代码],详见文档 在文件中,写入一些必要的全局变量,主要还是云数据库方面的,方便后面使用。 在初始化后面插入代码 [代码]global.cloud = cloud global.db = cloud.database() global._ = db.command global.$ = _.aggregate [代码] 这样就可以在同一个云函数环境中直接访问这些全局变量。 创建公共类 然后新建一个文件夹,我这里命名为 [代码]controllers[代码] ,这个文件夹用于存放所有的接口。 在 [代码]controllers[代码] 中新建一个 [代码]base-controller.js[代码] 文件,创建一个叫做 [代码]BaseController[代码] 的类,用于提供一些公用的方法。 内容如下: [代码]class BaseController { /** * 调用成功 */ success (data) { return { code: 0, data } } /** * 调用失败 */ fail (erroCode = 0, msg = '') { return { erroCode, msg, code: -1 } } } module.exports = BaseController [代码] 看到这里大家可能有点没看懂在做什么,那么请继续往下看。 创建接口 假设创建一些要操作用户相关的的接口,可以在 [代码]controllers[代码] 文件夹中新建一个 [代码]user-controller.js[代码] 的文件,创建一个名为 [代码]UserController[代码] 的类,并继承上面的 [代码]BaseController[代码] 类,内容如下: [代码]const BaseController = require('./base-controller.js') class UserController extends BaseController { // ... } module.exports = UserController [代码] 可以在这个类中编写所有关于 [代码]user[代码] 的接口方法。 编写接口 假设要分页查询用户信息,可以在 [代码]UserController[代码] 类中创建一个 [代码]list[代码] 方法。 代码如下: [代码]async list (data) { const { pageIndex, pageSize } = data let result = await db.collection('users') .skip((pageIndex - 1) * pageSize) .limit(pageSize) .get() .then(result => this.success(result.data)) .catch(() => this.fail([])) return result } [代码] 由于上面已经定义了全局变量 [代码]db[代码] 所以在 [代码]UserController[代码] 中无需引入 [代码]wx-server-sdk[代码] 引入接口类 写到这里接口已经完成了,还需要再引入这些接口类才可以进行访问。在 [代码]index.js[代码] 中引入 [代码]user-controller.js[代码] [代码]const User = require('./controllers/user-controller.js') [代码] 然后创建一个 [代码]api[代码] 变量,[代码]new[代码] 一个 [代码]User[代码] 实例 [代码]const api = { user: new User() } [代码] 在 [代码]main[代码] 方法中调用 [代码]UserController[代码] 中的方法。 [代码]exports.main = async (event, context) => { const { data } = event let result = await api['user']['list'](data) return result } [代码] 写到这里基本已经完成了接口的调用,但想要一个云函数动态调用所有接口还需要做一些改动。 动态调用接口 刚开始的时候介绍了 [代码]属性访问表达式[代码],限制稍微改动一下 [代码]main[代码] 方法 [代码]exports.main = async (event, context) => { const { controller, action, data } = event const result = await api[controller][action](data) return result } [代码] 在小程序调用云函数时,需要传入 [代码]controller[代码]、[代码]action[代码] 和 [代码]data[代码] 参数即可 [代码]const result = await wx.cloud.callFunction({ name: 'cloud', data: { controller, action, data } }) [代码] 完整 [代码]index.js[代码] 文件的代码 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') const User = require('./controllers/user-controller.js') const api = { user: new User() } // 初始化 cloud cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) global.cloud = cloud global.db = cloud.database() global._ = db.command global.$ = _.aggregate // 云函数入口 exports.main = async (event, context) => { exports.main = async (event, context) => { const { controller, action, data } = event const result = await api[controller][action](data) return result } } [代码] 其他实现 云开发官方团队打造的轮子 tcb-router
2020-05-26 - 只有三行代码的神奇云函数的功能之一:获取openid
这是一个神奇的网站,哦不,神奇的云函数,它只有三行代码:(真的只有三行哦) 云函数:login index.js: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { return { ...event, ...cloud.getWXContext() } } 神奇功能之一:获取openid: 任何页面运行下面代码,已解决异步问题。 onLoad: async function (options) { app.globalData.openid=app.globalData.openid||(await wx.cloud.callFunction({name:'login'})).result.OPENID console.log(app.globalData.openid) }, 其他功能: 神奇功能之二:不用授权获取unionid: 不需要弹出授权框,直接获取unionid,但是不保证100%成功获取,有可能unionid为空。 https://developers.weixin.qq.com/community/develop/article/doc/000a0c6b580338e947f9db0c65b813 神奇功能之三: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 - 微信小程序云开发教程-分页查询
本小节,咱们来学一点难的,如何对云数据库进行分页查询 [图片] 仔细看过开发文档的同学应该都知道: 第一点,云数据库每次最多能够查询100条记录,即使你查询的集合中有超过100条记录; 第二点,云函数通过limit方法可以指定每次需要获取的记录数量,如果你指定为10,就每次只能查询出10条结果,但是你指定为101,也只能返回100条记录。我们可以通过limit函数进行分页查询。 [图片] 分页之前,我们得先知道集合中总共有多少条符合条件的记录,我们可以使用count操作。 第一步,实例化数据库连接; 第二步,指定要查找哪个集合; 第三步,使用count操作; 第四步,结果中的total字段表示的就是符合查询条件的记录总数 [图片] 计算出符合查询条件的记录总数之后,我们需要计算我们总共需要分多少次查询,也就是分页的数量。 第一步,设置每页至多获取多少条记录,最小值为1,最大值为100; 第二步,计算分页总数,我们使用总记录数除以每页数量,然后向上取整,就可以知道总共需要查询多少次了。 [图片] 知道总共需要查询多少次之后,我们现在就可以开始查询了。 第一步,定义一个数组接收和汇总每次查询的记录; 第二步,使用for循环,分批次查询,每次只查一页,也就是说,假设我们每次查100个记录,那么第一次查第一页,也就是获得前1-100,第二次查第二页,也就是获得101-200,以此类推; 第三步,查询语句需要指定查询的是哪个集合; 第四步,一定要加排序语句,这样才能保证我们每次查出来的结果都是按顺序的; 第五步,每次查询都需要跳过之前几次查询过的,这里使用skip操作,参数是跳过多少个,如果我们查了i次,那么说明之前已经查过i-1次了,所以需要跳过(i-1)*MAX_LIMIT次。 第六步,限定每次最多查询多少条记录; 第七步,使用get操作进行查询; 第八步,查询成功之后,将查询结果合并到一个数组中。 循环结束后,我们的feedbacks数组就是符合我们查询条件的所有数据了。 下面,请根据教学视频进行学习和操作
2020-09-02 - 我的小程序开通了腾讯视频插件的去广告服务功能,但小程序运行时,还是会显示广告
我们这边在对接“腾讯视频插件”,小程序开发工具里调试看到有“腾讯视频”广告,开通了去广告功能,但广告还是出现,具体应该怎么操作
2020-11-04 - 为什么官方组件库weui用的单位是px而不是rpx?
其实我是想交流下逻辑像素单位和视口单位的优缺权衡问题。 逻辑像素单位px使得同一份样式文件在不同分辨率的设备中有大致相同的表现,但是不同设备的逻辑分辨率并不一致,所以px不能统一所有设备的表现。 因此rpx和vw这类视口单位的初衷一致,是为了统一所有设备的表现。 但通常设备的逻辑像素都是物理像素的整倍数,而视口单位并不是,所以展现效果上逻辑像素单位优于视口单位?我对屏幕显示技术不了解,不知道非整数倍物理像素是如何显示的。 所以weui权衡利弊之后还是选择了逻辑像素单位px?或是其他什么原因? 那么既然vw这类视口单位不能解决所有问题,那它意义何在啊?
2020-09-14 - lookup联表查询,如何遍历数组,匹配对应的数据?
表1 "_id":"2a0398605f1114*****d69a167ebf9ed" "name":"A_TL" "alistArray":[100,101,200,201] 表二 "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"301" "time":"20-10-10" ------------------------------------------------------ "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"100" "time":"21-10-10" ------------------------------------------------------ "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"200" "time":"20-12-10" ------------------------------------------------------ "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"201" "time":"10-10-10" ------------------------------------------------------ "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"501" "time":"25-10-10" 遍历【表1】里”alistArray“字段里的所有值,联表查询【表2】里的”alistID“相匹配的数据, 实现结果: "_id":"2a0398605f1114*****d69a167ebf9ed" "name":"A_TL" "alistArray":[100,101,200,201] "newlist":[ { "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"100" "time":"21-10-10" }, { "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"200" "time":"20-12-10" }, { "_id":"2a0398605f1114*****d69a167ebf9ed" "alistID":"201" "time":"10-10-10" } ]
2020-07-17 - 使用聚合函数实现打卡排行榜
效果展示 先上图,有图有真相。 [图片] 需求实现分析 需求:根据打卡天数进行排序,实现累积排行榜,查询前100名。 这里涉及到两张表:用户表,打卡记录表 实现思路 用户表和打卡记录表通过openid进行联合查询 统计每个用户到打卡次数 根据次数进行排序 查询前100名 代码实现 根据以上思路实现代码如下: exports.main = async (event, context) => { // 获取操作符 const $ = db.command.aggregate // 用户表和打卡记录表联合查询 let res = await db.collection(‘用户表’).aggregate() .lookup({ from: ‘打卡记录表’, localField: ‘_openid’, foreignField: ‘_openid’, as: ‘list’, }) // 统计每个人打卡次数 .project({ userInfo:1, _openid:1, size: $.size(’$list’) }) // 进行排序 .sort({ size: -1, }) // 限制100名 .limit(100) .end() return res } 总结 这里面用到了4个关键函数:lookup、project、sort、limit。 lookup:官方文档传送门 project:官方文档传送门 sort:官方文档传送门 limit:官方文档传送门
2020-08-13 - 请问lookup可以对联表排序吗?
我主要想对联表查询到的数据进行排序,如何做呢? [图片] 这样写好像不对,主要相对 zhoumuL_table 中的字段进行排序 [图片] 以便对这个序号进行排序,谢谢!
2020-05-20 - 聚合查询lookup如何排序呢?
尝试着在collection()后边加.orderBy,然后就会在调用该云函数的地方报一个错: [图片]
2019-11-19 - 最新Mac Big Sur11.0.1稳定系统,工具一直无故闪退,无法进入开发!!!紧急哇
无故闪退,一进去时好时坏,但是闪退很稳定,过一会必定闪退,现在竟然都成了只要打开开发者工具,一定会闪退。。。。 写了几十行代码,闪退一下全没了
2020-11-14 - 腾讯视频插件支持竖版视频吗?
现在使用腾讯视频插件,播放的视频都是默认横屏的横屏的。如果是竖版视频,也都是缩在视频中央。
2020-11-23 - 数据库分页limit和联表查询lookup的时候报错?
现在做一个分页查询文章(文章collection)并联表查询每一篇文章的用户信息(用户collection),写法如下: db.collection('article') .orderBy('createTime', 'desc') .where(where) .skip((page - 1) * pageSize) .limit(pageSize)// limit返回的是collection .aggregate() // 在这里报了一个...limit is not a function,从面无法执行 .lookup({ from: 'user', localField: '_openid', foreignField: '_openid', as: 'userInfo' }) .end() 请问哪里出错了,还是说要将article的所有数据都关联一下user再做分页查询?
2020-08-22 - 云开发lookup聚合联表查询as新字段内有无数量限制?
例如文档的例子 db.collection('orders').aggregate() .lookup({ from: 'books', localField: 'book', foreignField: 'title', as: 'bookList', }) .end() 如果bookList里有一千或者一万条记录,需要分步提取还是一次就可以?尝试过50条记录可以一次性显示。
2020-06-28 - 请问lookup联表查询中,能否通过条件控制被联的表的输入数量?
[图片] 游戏每天都有几千人注册 register表和donateLog表都是生数据,也就是逐条记录 donateLog中每条数据都长这样 [图片] 现在我想获得一段时间内,注册玩家中的捐款玩家的openid,然后干点别的事情 但是报错了,把时间缩到一两天没问题,数据一多就炸了 我在想是不是因为联表的时候,donateLog表是全表输入,导致数据量太大了,大部分都是无效数据 我想让donateLog表也从规定的时间段内截取部分数据,和register进行联表 可以做到吗? 谢谢各位大哥了~
2020-08-17 - 小程序云开发lookup连表查询怎么加where?
[代码]const res = await db.collection([代码][代码]'book_lend'[代码][代码]).where({[代码][代码] [代码][代码]user: wxContext.OPENID[代码][代码] [代码][代码]}).aggregate()[代码][代码] [代码][代码].lookup({[代码][代码] [代码][代码]from: [代码][代码]'books'[代码][代码],[代码][代码] [代码][代码]localField: [代码][代码]'isbn'[代码][代码],[代码][代码] [代码][代码]foreignField: [代码][代码]'isbn'[代码][代码],[代码][代码] [代码][代码]as: [代码][代码]'bookDetail'[代码][代码],[代码][代码] [代码][代码]})[代码][代码] [代码][代码].end()[代码]以上代码会报错,‘collection().where().aggregate().lookup().end() is not a function ’,怎么写才对呢?
2019-11-06 - 实战分享: 小程序云开发玩转订阅消息(二)
[图片]这是实战分享: 小程序云开发玩转订阅消息的第二部分 第一部分链接 《实战分享: 小程序云开发玩转订阅消息(一)》 将订阅消息存入云开发数据库接下来我们创建一个云函数 [代码]subscribe[代码] ,这个云函数的作用是将用户的订阅信息存入云开发数据库的集合 [代码]messages[代码] 中,等待将来需要通知用户时进行调用。 在微信开发者工具的云开发面板中创建数据库集合 [代码]messages[代码] [图片]微信开发者工具新增数据库集合 创建一个 [代码]subscribe[代码] 云函数,在云函数中我们将小程序端发送过来的课程订阅信息,存储在云开发数据库集合中,开发完成后,在微信开发者工具中右键上传并部署云函数。 cloudfunctions/subscribe/index.js [代码]const cloud = require('wx-server-sdk'); cloud.init(); const db = cloud.database(); exports.main = async (event, context) => { try { const {OPENID} = cloud.getWXContext(); // 在云开发数据库中存储用户订阅的课程 const result = await db.collection('messages').add({ data: { touser: OPENID, // 订阅者的openid page: 'index', // 订阅消息卡片点击后会打开小程序的哪个页面 data: event.data, // 订阅消息的数据 templateId: event.templateId, // 订阅消息模板ID done: false, // 消息发送状态设置为 false }, }); return result; } catch (err) { console.log(err); return err; } }; [代码]利用定时触发器来定期发送订阅消息接下来我们需要实现一个定时执行的云函数[代码]send[代码],来检查数据库中是否有需要发送给用户的订阅消息。如果有需要发送的订阅消息,会通过云调用 [代码]cloud.openapi.subscribeMessage.send[代码] 将订阅消息发送给用户。 创建一个名叫 [代码]send[代码] 的云函数,首先要配置云函数,在 [代码]config.json[代码] 的 [代码]permissions[代码] 中新增 [代码]subscribeMessage.send[代码]的云调用权限,然后新增一个 [代码]sendMessagerTimer[代码] 的定时触发器,定时触发器的语法和 [代码]linux[代码] 的 [代码]crontab[代码] 类似,比如,我们配置的 [代码]"0 * * * * * *"[代码] 代表每分钟执行一次云函数。 cloudfunctions/send/config.json [代码]{ "permissions": { "openapi": ["subscribeMessage.send"] }, "triggers": [ { "name": "sendMessagerTimer", "type": "timer", "config": "0 * * * * * *" } ] } [代码]接下来是实现发送订阅消息的云函数,这个云函数会从云开发数据库集合[代码]messages[代码]中查询等待发送的消息列表,检查数据库中是否有需要发送给用户的订阅消息,发送条件可以根据自己的业务实现,比如开课提醒可以根据课程开课日期来检查是否需要发送订阅消息,在我们下面的代码示例里做了简化,筛选条件只检查了状态为未发送。 查询到待发送的消息列表之后,我们会循环消息列表,依次发送每条订阅消息,发送成功后将数据库中消息的状态改为已发送。 cloudfunctions/send/index.js [代码]const cloud = require('wx-server-sdk'); exports.main = async (event, context) => { cloud.init(); const db = cloud.database(); try { // 从云开发数据库中查询等待发送的消息列表 const messages = await db .collection('messages') // 查询条件这里做了简化,只查找了状态为未发送的消息 // 在真正的生产环境,可以根据开课日期等条件筛选应该发送哪些消息 .where({ done: false, }) .get(); // 循环消息列表 const sendPromises = messages.data.map(async message => { try { // 发送订阅消息 await cloud.openapi.subscribeMessage.send({ touser: message.touser, page: message.page, data: message.data, templateId: message.templateId, }); // 发送成功后将消息的状态改为已发送 return db .collection('messages') .doc(message._id) .update({ data: { done: true, }, }); } catch (e) { return e; } }); return Promise.all(sendPromises); } catch (err) { console.log(err); return err; } }; [代码]最终效果 [图片]开课提醒订阅消息截图 源代码https://github.com/binggg/tcb-subscribe-demo[3] 参考资料 [1]注册小程序帐号: https://tencentcloudbase.github.io/2019-09-03-wx-dev-guide-register/ [2]开通云开发服务: https://tencentcloudbase.github.io/2019-09-03-wx-dev-guide-service/ [3]https://github.com/binggg/tcb-subscribe-demo: https://github.com/binggg/tcb-subscribe-demo
2019-10-23 - 小程序到底现在支持云开发定时推送订阅消息吗?
下载了最新的nightly build v1.0.2 1912252,[图片] [图片],每次点击按钮调阅上图云函数可以发送订阅消息,但是上图设置了触发器,想每5秒发送一个测试,结果没法应,求回答
2019-12-27 - 直播教程-矩阵小程序流量主广告怎么赚钱
一篇文章成了2月突出贡献 [图片] 原文如下: 小程序流量主运营技巧 文章发布后,收到很多私信,也有很多朋友想让我教怎么做小程序流量主赚钱,说实话这个东西没有系统的教程,不知道从何教起,于是整理了一周的资料准备开个直播给大家演示一下,全部从0开始免费做自己的流量,直播的内容主要有: 1、小程序申请、起名、上线(代码已购买) 2、公众号申请、起名、文章引流到小程序 3、聊一些小程序你不知道的小技巧 4、互动问题解答 (我的骨子里是一个程序员,大家不要太期待直播的激情) 直播大约2个小时左右,具体时间群里通知(本来想用直播插件,后来发现直播插件不支持OBS,大家看不清楚我的操作,准备用第三方直播) 个人简介: 本人现在有58个小程序,其中个人小程序有10个,企业小程序48个。 1个名字测试小程序(测试名字是否可以注册)2个空小程序(抢注名字用),3个测试小程序(名字测试自然搜索量) 其中工具类11个,答题类13个,视频类(腾讯视频插件)12个,游戏类5个,返利类1个,商城1个,其他类6个(高流量小程序给其他引流) 我不是什么大佬,可以教大家一些小技巧,避免绕弯路。 下图是半月结算单,2月份收益都比较高,正是疫情期间都在家没事儿干,加上文章推广引流,效果还不错 [图片] 个人主页有微信
2020-03-22 - 使用第三方腾讯视频插件播放视频,是否需要添加 文娱-视频 类目?
若小程序已接入文娱-视频插件,暂不需补充文娱-视频类目。审核将以实际提交的小程序服务内容为准。
2019-12-27 - 腾讯视频插件如何获取视频封面图片?
使用的是腾讯视频插件播放视频,分享视频页面到微信时,需要获取当前视频封面图片作为分享图片,请问有什么方法可以获取得到吗?
2020-06-22 - 小程序引用腾讯视频提示not in plugin's domain list?
[图片] 小程序后台也添加了腾讯视频插件为什么还会出现这个问题,怎么解决呢?
2020-03-23 - 小程序流量主如何赚钱
小程序开通流量主的门槛不高,需要1000个独立 UV ,也就是说有1000个微信注册用户进入过你的小程序。一般来说,通过自己朋友圈转发二维码,或者通过在微信大群分享你的小程序,差不多一周就可以达到门槛要求。最重要的是,个人是可以发布小程序的,千万不要以为只有企业可以发布小程序。 登录小程序mp.weixin.qq.com,左边栏选择流量主-开通即可。当日满1000用户,次日可开通(好像是早晨8点以后) [图片] 小程序项目的核心,在于批量化操作,只做一两个赚钱是不容易的(千万不要以为自己是大神,自己开发一个小程序就觉得这个小程序无敌了),把自己的位置摆正,一步一步走,循序渐进。小程序矩阵项目赚钱的方式,并不在于运营单个小程序赚取服务佣金,而在于批量运营多个小程序下,基于流量赚取的广告佣金。 小程序项目的盈利来源,主要来自于微信流量主的广告费。具体玩法,即建立多个小程序,形成流量矩阵,小程序与小程序之间互相导入流量(小程序A>小程序B>小程序C>小程序A),这样收益就能实现倍增。小程序项目的优势:收益稳定、启动成本低、长期稳定。 [图片] [图片] [图片] 小程序最大的问题,就在于留存,虽然打开即代表关注,但能否使关注用户二次打开,要看小程序是否满足用户需求,所以在小程序源码购买前,一定要先扫码体验,深入分析用户需求及产品体验。(小程序代码是不具备唯一性的,同一套代码可以不限主体,不限数量上线) 其次就是用户来源问题,一个好的名字会给你不断的创造惊喜,名字注册推荐工具“微信指数小程序”,可自行查看关键字的搜索指数,个人建议稳定再1000W以上的搜索量可以作为小程序名字关键字。 [图片] http://aldwx.com(阿拉丁小程序平台),提供了排行榜,在这里可以看到各分类下,排行靠前的小程序,以此可以给我们一些选择模块时的参考。这里切忌,不要选择电商类的小程序,因为这类小程序后面需要运营,会花费很多的时间精力,且价格较高,游戏类与工具类的小程序是我们可以优先选择的。 [图片] 那么小程序初期如何推广,首先利用自身朋友圈与微信群的资源,多数小程序开发者在开发时已考虑过,小程序的分享裂变机制,基于小程序的特性,有一定用户数后,也很容易一传十,十传百。 本文部分文字内容转载来自公众号:黑帽星球,以及个人见解。
2020-02-27 - 小程序流量主运营技巧
前言(写给入坑的小白) 本文不涉及任何需要资质的小程序(如:视频类目)。小程序流量主是个人和小微企业主要变现途径之一,满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 - 流量主一天你们能赚多少钱?
[图片] 小程序流量主的出现,无疑是解决小程序变现难的一个绝佳渠道。其意义不下于公众号流量主广告的上线。“有流量、变现难”本来是小程序开发者面前挥之不去的重重迷雾,而现在腾讯开放了小程序流量主,瞬间将小程序开发者们面前的迷雾拨开,他们能放心大胆地往前走。 我自认为我运营到很高了,但是有人一天1.9W+,将近2万,他们是怎么运营的?粉丝都是怎么来的? 有没有大神一起来探讨一下?
2020-01-06 - (14)腾讯视频插件
想要在自己的电商小程序中增加商品介绍视频,但自己搭建视频服务开发成本太大? 想要在餐饮服务中添加菜品视频,但苦于带宽成本过高? 想要在门票预订服务中用视频打动用户,但视频资质申请流程太长? 腾讯视频插件来帮你~ 今天,我们跟大家分享腾讯视频插件的故事。 腾讯视频插件提供了完整视频播放能力,方便开发者可以给用户提供更好的视频体验。 插件基于腾讯视频的CDN节点和成熟视频方案,可以做到为不同地点的用户提供更流畅的播放服务,更清晰的视频,更稳定的播放质量。只需要视频的vid,开发者即可直接在小程序内播放腾讯视频上的内容! 腾讯视频插件使用场景 场景一:电商类小程序 除了对商品的语言描述,实惠的价格,精美的图片,也需要生动直观、360度动态介绍商品,比单一的图片更能引起消费者的好感。 现在,在商品介绍里直接链入相关商品视频。比如,买衣服的电商小程序,可以把电视或电影中明星穿过的同款视频链入,在满足用户感官享受的同时,提升转化率。 场景二:文娱推荐类小程序 不局限于苍白的文字描述,推荐类小程序也可在小程序中添加电影视频、预告片,为用户带来完美的购票、观影体验。开发者无需独立开发视频功能,直接使用“腾讯视频”插件,即可实现视频播放功能。 [图片] 场景三:资讯类小程序 “腾讯视频”插件还可以是内容创作者的一大利器,以游戏攻略类小程序为例。小程序就可以直接用视频的形式展现,一方面清晰明了地展示攻略流程,同时也增加了用户在小程序内的停留时长,进一步引导用户做更多动作。 或者生活居家小技巧类的小程序,比如想教大家一种打结的方式,纯文字的形式即很难说清楚,又浪费内容运营者的时间,这时候直接使用插件链入视频,就能很快地教会大家,给予用户更好的内容体验。 轻松接入腾讯视频插件 “腾讯视频”提供了这么多的功能,使用起来也同样很方便: 1 在“小程序管理后台→设置→第三方服务→插件管理”中查找插件名称“腾讯视频”,并申请使用。 [图片] 2 参考详情页中的 [开发文档],在小程序代码中使用插件。 了解完“腾讯视频”插件后,还想看看其他小程序是如何实际运用的?一起来体验一下 腾讯+ ! [图片] 页面视频服务是由腾讯视频插件提供
2018-08-17 - 视频小程序如何不需要资质发布
大家好,我又来给你们秀一些骚操作了! 前言:看着小年糕等类似的小视频流量爆表,老年流量是一波高质量的用户,他们会情不自禁的去转发。想做又做不了,低配代办广播5000起,看着这块蛋糕望而却步...... 正文: [图片] 主要用到到的就是“腾讯视频”插件 [图片] 所有视频均由“腾讯视频”提供,属于安全内容 [图片] 温馨提示:仅供企业/个体户小程序使用,个人主体不能使用~ 开发者们,加油!
2020-09-03 - 实现小程序分享朋友圈功能
一大早在各科技公众号有看到小程序支持直接分享到朋友圈,这么好的功能,忍不住要实践下。 根据官方文档走起,文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html 然后根据官方文档的意思,目前之支持Android版本,为什么IOS版本总是慢一些呢,(╯▔皿▔)╯ 接下来先上代码(需要注意的是得是最新版的预发布版本才行,下载链接:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html) [图片] [图片] 在需要分享的页面对应的js文件里面写入"onShareTimeline"即可,需要注意的是需要先编写"onShareAppMessage"方法。 [图片] 点击右上角三个点,出来的效果如下图所示 [图片][图片] 再次点击右下角得"查看小程序分享页"就可以看到类似官方文档上的效果,具体如下图所示: [图片][图片] 在Android手机端下载最新版本的小程序示例,也未发现对应的分享朋友圈功能,静候微信团队的更新
2020-07-08 - 微信支付云调用拼夕夕版尝鲜踩坑教程 [拎包哥]
微信支付竟然跟云开发发生了关系,手残党又迎来福音喽! [图片] 但!本着跟着官方教程复制粘贴就能跑的心态,拎包哥慢慢发现事情并不简单。。。 [图片] 下面列出几个填过的小坑。 1. 退款API权限 [图片] 1.1 如果只是想进行微信支付,退款API权限不需要授权的, 只要在微信支付商家助手(公众号)上授权了JSAPI权限就可以进行微信支付。 1.2 如果要对退款API进行授权,就得在登录商户平台后,再打开 https://pay.weixin.qq.com/index.php/extend/product/submch ,才能看到“我授权的产品" 这他喵竟然是在首页你敢信? [图片] [图片] 所以产品中心根本就没有的,不要再瞎找了。 [图片] 2. 两个云函数 不要被官方文档所蒙蔽了,其实我们需要写两个云函数。 [图片] [图片] functionName就是官方文档没有写的第二个云函数的名字。如果你不写这个函数, 就会出现各种莫名其妙的报错,不要试图从这些报错上找出答案,都是扯淡。 functionName对应函数代码 exports.main = async (event, context) => { return(event,context) } 踩过这两个坑,再跟着教程走基本上就可以进行微信支付的云调用了。 欢迎大哥们批评指正错误。 最后感谢这些知识的来源: 1.微信问答 https://developers.weixin.qq.com/community/pay/doc/0002ce8b3007d89db65aa98f655c00 2.bilibili李东教学 https://www.bilibili.com/video/BV1uz411B7Kb ================点个赞,是拎包哥继续瞎逼逼的动力哦================= 番外坑 outTradeNo要放在exports.main里面,放在外面则会得到相同而不是随机的值。 老张哥或者各路大神如果你看到这个问题,请帮忙解释一下为啥,谢谢! exports.main = async(event,context)=>{ var randomNo = Math.random().toString(36).subStr(2,15) var timeStamp = parseInt(Date.now()/1000) + '' var outTradeNo = 'otn' + timeStamp + randomNo } ================2020/5/27更新=================
2020-05-27 - 知乎热榜 和 b站 的两个列表滑动是怎么做的?
类似 知乎热榜 和 b站 的两个列表滑动 大概是泽你们写的呢,左右滑动的时候,两个列表完全不影响 我用 swiper +swiper-item (+swiper-view) 写的两个列表,我记录下滑动之前的位置,但切换过去的时候,能看到一瞬间的列表移动,而且在左右缓慢滑动的时候,也能看到要切换过去的列表的位置和之前不一样 [图片][图片]
2020-04-08 - 为什么我的button按钮修改不了宽高,求各位大神帮忙?
[图片] [图片] [图片]
2019-11-16 - 关于微信小程序部分类目报备审核说明
自微信小程序平台上线以来,为了保障小程序内容合规,发布时事新闻、具有社交属性或以视频、电台为载体的小程序需在上线前,完成向省/自治区/直辖市属地网信部门申请报备的工作。特别是,为避免小程序违法违规风险,UGC小程序需要对用户发布的内容做好安全审查措施。 对于开发者们持续以来关注的报备审核相关疑问,做以下说明: 报备审核范围 经与主管部门确认,以类目为范围界定需要报备的小程序,具体报备类目如下: [图片] 社交类目的小程序帮助用户与用户之间产生连接,为用户提供即时通讯、互动、交友、UGC内容发布与分享、直播、直播答题等功能;文娱-视频、文娱-FM/电台类目指小程序内有以视频、电台为载体的内容。时政信息类目小程序内发布政治(含法律法规)、经济、军事、外交、农业、历史、社会突发事件等各种类型的文章/图片/视频/音频的新闻报道; 平台在代码审核环节会确认内容和类目的一致性。如果小程序内涉及有关内容,却没有相关类目,会造成审核不通过并要求补充相应类目。 [图片] 报备审核流程 [图片] 1. 微信侧小程序代码审核:确认是否进入报备审核 在小程序涉及报备类目的版本首次提审时,微信侧审核通过后,自动进入报备审核流程。若微信侧审核不通过,不会进入报备审核流程,下次通过微信侧审核时小程序将自动进入报备审核流程。 小程序涉及报备类目的版本更新提审时,若小程序特定类目曾完成过报备审核,新版本未涉及其他报备类目,无需进入报备审核流程。 撤回代码审核将中止流程,再次提交时若涉及敏感类目会重新进入报备审核。 2、进入属地网信部门复核 开发者在小程序MP后台通知中心收到以下通知,代表小程序进入报备审核流程。从该通知发出时间起,7个自然日内将完成报备审核(境外非企业主体小程序除外)。 [图片] 属地网信部门工作人员会根据当前小程序的主体信息及代码提审信息进行安全审核。也可能会通过开发者预留在微信小程序MP后台的手机号主动联系开发者确认或补充材料。建议开发者在进入报备审核流程后,及时关注和接听来访电话,避免遗漏审核信息,影响报备审核流程。 若开发者收到当地网信部门联系时,请注意核实对方身份,先与其确认真实性后(可通过对方来电、微信、QQ、邮箱等方式询问确认),再提供相关材料。 3、审核周期及预上线说明 报备审核需7个自然日审核周期(不包括境外非企业主体小程序),暂无加急渠道。如遇紧急上线等特殊情况,建议开发者自行联系省/自治区/直辖市属地网信部门。因属地不同,无法列举联系方式,建议自行查看属地相关政府网站查找联系方式。 7个自然日审核周期开发者若未收到审核结果,将收到预上线通知,则开发者可进行版本发布。境外非企业主体小程序无预上线流程。 [图片] 在预上线期间,属地网信部门仍然可能对小程序进行报备审核反馈。预上线的小程序若存在违规或未允许内容,未通过报备审核,将以站内信的形式通知开发者。未通过报备审核的开发者需要及时下架小程序服务,整改小程序内容。 报备审核前重要准备 提供UGC自定义发布内容功能的小程序,需要在小程序内接入官方“内容安全检测接口”校验用户输入的文本/图片,拦截政治、色情、违法等敏感词。用户自定义发布的内容包括:昵称/花名、个人资料签名/日志/聊天/评论、头像/表情/相片、直播等各种场景。其格式内容包括但不限于短文本、长内容、图片或视频等。请开发者做好小程序内容安全识别工作,接入微信公众平台内容安全API(imgSecCheck、msgSecCheck、mediaCheckAsync)能力,以及通过其他技术或人工审核手段做好内容审核,校验用户输入的文本/图片,拦截政治、色情、违法等敏感词,保证用户上传的内容安全健康。 为辅助小程序顺利通过报备审核,代码审核环节会针对小程序的安全保障能力进行敏感类关键词检测,未接入官方“内容安全检测接口”的小程序将无法通过代码审核。开发者需提前做好小程序内容安全识别工作,以免小程序无法通过审核延误版本迭代。若小程序发布后出现UGC内容安全违规问题,也将按平台规则进行处置。 为了满足网信部门的监管要求,平台希望开发者能够明晰小程序报备审核流程,合理规划小程序上线周期,主动预留时间,做好审核准备,配合平台完成向省/自治区/直辖市网信部门报备的工作,保障小程序服务的准时上线。
2022-01-14 - 云开发,获取群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 - splice插入变为删除?
- 需求的场景描述(希望解决的问题) [图片] 打算splice在原数组的最前面插入一个新元素。 但执行后detailList变为空了,为什么? - 希望提供的能力
2019-04-01 - 10行代码实现小程序支付功能!丨实战
前面给大家讲过一个借助小程序云开发实现微信支付的,但是那个操作稍微有点繁琐,并且还会经常出现问题,今天就给大家讲一个简单的,并且借助官方支付api实现小程序支付功能。 传送门: 借助小程序云开发实现小程序支付功能 老规矩,先看本节效果图 [图片] 我们实现这个支付功能完全是借助小程序云开发实现的,不用搭建自己的服务器,不用买域名,不用备案域名,不用支持https。只需要一个简单的云函数,就可以轻松的实现微信小程序支付功能。 核心代码就下面这些: [图片] 一、创建一个云开发小程序 关于如何创建云开发小程序,这里我就不再做具体讲解。不知道怎么创建云开发小程序的同学,可以去翻看腾讯云云开发公众号内菜单【技术交流-视频教程】中的教学视频。 创建云开发小程序有几点注意的 1.一定不要忘记在app.js里初始化云开发环境。 [图片] 2.创建完云函数后,一定要记得上传 二、创建支付的云函数 1.创建云函数pay [图片] [图片] 三、引入三方依赖tenpay 我们这里引入三方依赖的目的,是创建我们支付时需要的一些参数。我们安装依赖是使用里npm 而npm必须安装node,关于如何安装node,我这里不做讲解,百度一下,网上一大堆。 1.首先右键pay,然后选择在终端中打开 [图片] 2.我们使用npm来安装这个依赖。 在命令行里执行 npm i tenpay [图片] [图片] [图片] 安装完成后,我们的pay云函数会多出一个package.json 文件 [图片] 到这里我们的tenpay依赖就安装好了。 四、编写云函数pay [图片] 完整代码如下 [代码]//云开发实现支付 const cloud = require('wx-server-sdk') cloud.init() //1,引入支付的三方依赖 const tenpay = require('tenpay'); //2,配置支付信息 const config = { appid: '你的小程序appid', mchid: '你的微信商户号', partnerKey: '微信支付安全密钥', notify_url: '支付回调网址,这里可以先随意填一个网址', spbill_create_ip: '127.0.0.1' //这里填这个就可以 }; exports.main = async(event, context) => { const wxContext = cloud.getWXContext() let { orderid, money } = event; //3,初始化支付 const api = tenpay.init(config); let result = await api.getPayParams({ out_trade_no: orderid, body: '商品简单描述', total_fee: money, //订单金额(分), openid: wxContext.OPENID //付款用户的openid }); return result; } [代码] 一定要注意把appid,mchid,partnerKey换成你自己的。 到这里我们获取小程序支付所需参数的云函数代码就编写完成了。 不要忘记上传这个云函数。 [图片] 出现下图就代表上传成功 [图片] 五、写一个简单的页面,用来提交订单,调用pay云函数。 [图片] 这个页面很简单: 1.自己随便编写一个订单号(这个订单号要大于6位) 2.自己随便填写一个订单价(单位是分) 3.点击按钮,调用pay云函数。获取支付所需参数。 下图是官方支付api所需要的一些必须参数。 [图片] 下图是我们调用pay云函数获取的参数,和上图所需要的是不是一样。 [图片] 六、调用wx.requestPayment实现支付 下图是官方的示例代码: [图片] 这里不在做具体讲解了,把完整代码给大家贴出来 [代码]// pages/pay/pay.js Page({ //提交订单 formSubmit: function(e) { let that = this; let formData = e.detail.value console.log('form发生了submit事件,携带数据为:', formData) wx.cloud.callFunction({ name: "pay", data: { orderid: "" + formData.orderid, money: formData.money }, success(res) { console.log("提交成功", res.result) that.pay(res.result) }, fail(res) { console.log("提交失败", res) } }) }, //实现小程序支付 pay(payData) { //官方标准的支付方法 wx.requestPayment({ timeStamp: payData.timeStamp, nonceStr: payData.nonceStr, package: payData.package, //统一下单接口返回的 prepay_id 格式如:prepay_id=*** signType: 'MD5', paySign: payData.paySign, //签名 success(res) { console.log("支付成功", res) }, fail(res) { console.log("支付失败", res) }, complete(res) { console.log("支付完成", res) } }) } }) [代码] 到这里,云开发实现小程序支付的功能就完整实现了。 实现效果 1.调起支付键盘 [图片] 2.支付完成 [图片] 3.log日志,可以看出不同支付状态的回调 [图片] 上图是支付成功的回调,我们可以在支付成功回调时,改变订单支付状态。 下图是支付失败的回调: [图片] 下图是支付完成的状态: [图片] 到这里我们就轻松的实现了微信小程序的支付功能了,是不是很简单啊。 源码地址: https://github.com/TencentCloudBase/Good-practice-tutorial-recommended 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心! [图片]
2019-08-15 - 微信小程序自定义导航栏组件(完美适配所有手机),可自定义实现任何你想要的功能
背景 在做小程序时,关于默认导航栏,我们遇到了以下的问题: Android、IOS手机对于页面title的展示不一致,安卓title的显示不居中 页面的title只支持纯文本级别的样式控制,不能够做更丰富的title效果 左上角的事件无法监听、定制 路由导航单一,只能够返回上一页,深层级页面的返回不够友好 探索 小程序自定义导航栏已开放许久>>了解一下,相信不少小伙伴已使用过这个功能,同时不少小伙伴也会发现一些坑: 机型多如牛毛:自定义导航栏高度在不同机型始终无法达到视觉上的统一 调皮的胶囊按钮:导航栏元素(文字,图标等)怎么也对不齐那该死的胶囊按钮 各种尺寸的全面屏,奇怪的刘海屏,简直要抓狂 一探究竟 为了搞明白原理,我先去翻了官方文档,>>飞机,点过去是不是很惊喜,很意外,通篇大文尽然只有最下方的一张图片与这个问题有关,并且啥也看不清,汗汗汗… 我特意找了一张图片来 [图片] 分析上图,我得到如下信息: Android跟iOS有差异,表现在顶部到胶囊按钮之间的距离差了6pt 胶囊按钮高度为32pt, iOS和Android一致 动手分析 我们写一个状态栏,通过wx.getSystemInfoSync().statusBarHeight设置高度 Android: [图片] iOS:[图片] 可以看出,iOS胶囊按钮与状态栏之间距离为:4px, Android为8px,是不是所有手机都是这种情况呢? 答案是:苹果手机确实都是4px,安卓大部分都是7和8 也会有其他的情况(可以自己打印getSystemInfo验证)如何快速便捷算出这个高度,请接着往下看 如何计算 导航栏分为状态栏和标题栏,只要能算出每台手机的导航栏高度问题就迎刃而解 导航栏高度 = 胶囊按钮高度 + 状态栏到胶囊按钮间距 * 2 + 状态栏高度 注:由于胶囊按钮是原生组件,为表现一致,其单位在各种手机中都为px,所以我们自定义导航栏的单位都必需是px(切记不能用rpx),才能完美适配。 解决问题 现在我们明白了原理,可以利用胶囊按钮的位置信息和statusBarHeight高度动态计算导航栏的高度,贴一个实现此功能最重要的方法 [代码]let systemInfo = wx.getSystemInfoSync(); let rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null; //胶囊按钮位置信息 wx.getMenuButtonBoundingClientRect(); let navBarHeight = (function() { //导航栏高度 let gap = rect.top - systemInfo.statusBarHeight; //动态计算每台手机状态栏到胶囊按钮间距 return 2 * gap + rect.height; })(); [代码] gap信息就是不同的手机其状态栏到胶囊按钮间距,具体更多代码实现和使用demo请移步下方代码仓库,代码中还会有输入框文字跳动解决办法,安卓手机输入框文字飞出解决办法,左侧按钮边框太粗解决办法等等 胶囊信息报错和获取不到 问题就在于 getMenuButtonBoundingClientRect 这个方法,在某些机子和环境下会报错或者获取不到,对于此种情况完美可以模拟一个胶囊位置出来 [代码]try { rect = Taro.getMenuButtonBoundingClientRect ? Taro.getMenuButtonBoundingClientRect() : null; if (rect === null) { throw 'getMenuButtonBoundingClientRect error'; } //取值为0的情况 if (!rect.width) { throw 'getMenuButtonBoundingClientRect error'; } } catch (error) { let gap = ''; //胶囊按钮上下间距 使导航内容居中 let width = 96; //胶囊的宽度,android大部分96,ios为88 if (systemInfo.platform === 'android') { gap = 8; width = 96; } else if (systemInfo.platform === 'devtools') { if (ios) { gap = 5.5; //开发工具中ios手机 } else { gap = 7.5; //开发工具中android和其他手机 } } else { gap = 4; width = 88; } if (!systemInfo.statusBarHeight) { //开启wifi的情况下修复statusBarHeight值获取不到 systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20; } rect = { //获取不到胶囊信息就自定义重置一个 bottom: systemInfo.statusBarHeight + gap + 32, height: 32, left: systemInfo.windowWidth - width - 10, right: systemInfo.windowWidth - 10, top: systemInfo.statusBarHeight + gap, width: width }; console.log('error', error); console.log('rect', rect); } [代码] 以上代码主要是借鉴了拼多多的默认值写法,android 机子中 gap 值大部分为 8,ios 都为 4,开发工具中 ios 为 5.5,android 为 7.5,这样处理之后自己模拟一个胶囊按钮的位置,这样在获取不到胶囊信息的情况下,可保证绝大多数机子完美显示导航头 吐槽 这么重要的问题,官方尽然没有提供解决方案…竟然提供了一张看不清的图片??? 网上有很多ios设置44,android设置48,还有根据不同的手机型号设置不同高度,通过长时间的开发和尝试,本人发现以上方案并不完美,并且bug很多 代码库 Taro组件gitHub地址详细用法请参考README 原生组件npm构建版本gitHub地址详细用法请参考README 原生组件简易版gitHub地址详细用法请参考README 由于本人精力有限,目前只计划发布维护好这2种组件,其他组件请自行修改代码,有问题请联系 备注 上方2种组件在最下方30多款手机测试情况表现良好 iPhone手机打电话和开热点导致导航栏样式错乱,问题已经解决啦,请去demo里测试,这里特别感谢moments网友提出的问题 本文章并无任何商业性质,如有侵权请联系本人修改或删除 文章少量部分内容是本人查询搜集而来 如有问题可以下方留言讨论,微信zhijunxh 比较 斗鱼: [图片] 虎牙: [图片] 微博: [图片] 酷狗: [图片] 知乎: [图片] [图片] 知乎是这里边做的最好的,但是我个人认为有几个可以优化的小问题 打电话或者开启热点导致样式错落,这也是大部门小程序的问题 导航栏下边距太小,看起来不舒服 搜索框距离2侧按钮组距离不对等 自定义返回和home按钮中的竖线颜色重了,并且感觉太粗 如果您看到了此篇文章,请赶快修改自己的代码,并运用在实践中吧 扫码体验我的小程序: [图片] 创作不易,如果对你有帮助,请移步Taro组件gitHub原生组件gitHub给个星星 star✨✨ 谢谢 测试信息 手机型号 胶囊位置信息 statusBarHeight 测试情况 iPhoneX 80 32 281 369 48 88 44 通过 iPhone8 plus 56 32 320 408 24 88 20 通过 iphone7 56 32 281 368 24 87 20 通过 iPhone6 plus 56 32 320 408 24 88 20 通过 iPhone6 56 32 281 368 24 87 20 通过 HUAWEI SLA-AL00 64 32 254 350 32 96 24 通过 HUAWEI VTR-AL00 64 32 254 350 32 96 24 通过 HUAWEI EVA-AL00 64 32 254 350 32 96 24 通过 HUAWEI EML-AL00 68 32 254 350 36 96 29 通过 HUAWEI VOG-AL00 65 32 254 350 33 96 25 通过 HUAWEI ATU-TL10 64 32 254 350 32 96 24 通过 HUAWEI SMARTISAN OS105 64 32 326 422 32 96 24 通过 XIAOMI MI6 59 28 265 352 31 87 23 通过 XIAOMI MI4LTE 60 32 254 350 28 96 20 通过 XIAOMI MIX3 74 32 287 383 42 96 35 通过 REDMI NOTE3 64 32 254 350 32 96 24 通过 REDMI NOTE4 64 32 254 350 32 96 24 通过 REDMI NOTE3 55 28 255 351 27 96 20 通过 REDMI 5plus 67 32 287 383 35 96 28 通过 MEIZU M571C 65 32 254 350 33 96 25 通过 MEIZU M6 NOTE 62 32 254 350 30 96 22 通过 MEIZU MX4 PRO 62 32 278 374 30 96 22 通过 OPPO A33 65 32 254 350 33 96 26 通过 OPPO R11 58 32 254 350 26 96 18 通过 VIVO Y55 64 32 254 350 32 96 24 通过 HONOR BLN-AL20 64 32 254 350 32 96 24 通过 HONOR NEM-AL10 59 28 265 352 31 87 24 通过 HONOR BND-AL10 64 32 254 350 32 96 24 通过 HONOR duk-al20 64 32 254 350 32 96 24 通过 SAMSUNG SM-G9550 64 32 305 401 32 96 24 通过 360 1801-A01 64 32 254 350 32 96 24 通过
2019-11-17 - 不做交易支付的商品信息单页发布,需要电商平台资质吗
请问下,我只给我个人或者商家做单页的商品信息填写 以及 用户商品预约或者下单功能,但不涉及交易支付。 只是提供该工具提升效率,请问选择工具类目就可以的吗? 不需要电商平台的资质吧?
2019-01-12 - 云服务器调用security.imgSecCheck完成代码分享
云服务器代码: // 云函数入口文件 const cloud = require(‘wx-server-sdk’) cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const {value} = event; try { const res = await cloud.openapi.security.imgSecCheck({ media: { header: {‘Content-Type’: ‘application/octet-stream’}, contentType: ‘image/png’, value:Buffer.from(value) } }) return res; } catch (err) { return err; } } 本地函数: wx.chooseImage({count: 1}).then((res) => { if(!res.tempFilePaths[0]){ return; } console.log(JSON.stringify(res)) if (res.tempFiles[0] && res.tempFiles[0].size > 1024 * 1024) { wx.showToast({ title: ‘图片不能大于1M’, icon: ‘none’ }) return; } wx.getFileSystemManager().readFile({ filePath: res.tempFilePaths[0], success: buffer => { console.log(buffer.data) wx.cloud.callFunction({ name: ‘checkImg’, data: { value: buffer.data } }).then( imgRes => { console.log(JSON.stringify(imgRes)) if(imgRes.result.errorCode == ‘87014’){ wx.showToast({ title:‘图片含有违法违规内容’, icon:‘none’ }) return }else{ //图片正常 } [代码] } ) }, fail: err => { console.log(err) } } ) 我相信做出来的人很多,但是没有分享出来,我今天分享出来就是为了避免更多程序员不要在这种简单的问题上,浪费太多的时间,我就浪费了很多时间,兼职太坑爹了[代码]
2019-07-26 - 哪些API会返回cloudID字段
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html 这个cloudID,目前没在其他地方看到,请问还有哪些API会返回? [图片]
2019-05-27 - 小程序通过wx.getShareInfo返回的cloudID是顶层字段吗?
使用云函数获取opengid的过程中,可以使用以下方法直接获取解密后的opengid吗? [图片]
2019-07-31 - 广告收入问题?
[图片] 请问广告收入怎么计算的,根据曝光量还是点击量? 另外每条Banner或激励视频点击价格是一样的吗?
2019-03-07 - 这种小程序选择什么分类(见帖子描述)
产品设计如下: 1、用户在小程序上传图片制作网络相册 2、用户选择网络相册,并在小程序内支付购买由该网络相册制作的成实体精美相册 3、支付完成后,我们制作好实体相册,然后根据用户填写的收货地址进行邮寄 有两个问题: 1、这种涉及虚拟支付范畴吗? 2、这样的小程序需要选择什么类型呢,需要特殊的材料证件资质吗?
2019-07-24 - 小程序云开发实现微信支付,小程序支付常见问题汇总及解决方案
近期有比较多的同学反映,使用云开发调取微信支付时,老是提示订单不存在。今天就把这几天大家遇到的问题统一汇总到这里。 一,订单不存在的错误 如下图所示的错误。 [图片] 通常看到这个错误时,最好去看下上面看下,有这么一段日志。 [图片] 其实这里已经很明确的给出了错误信息。 [图片] 二,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 - 微信支付什么时候支持云调用?
问题:微信支付实操接起来好复杂,作为新手不明白怎么接通。 需求:微信支付能否支持云调用?
2019-07-23 - 调用msgSecCheck,报错incorrect header check
- 当前 Bug 的表现(可附上截图) [代码]const got = require([代码][代码]'got'[代码][代码]) [代码][代码]// 引入 got 库[代码][代码]const Config = require([代码][代码]'../controllers/defaultConfig'[代码][代码]);[代码] [代码]router.post([代码][代码]'/msgSecCheck'[代码][代码], async(ctx, next) => {[代码][代码] [代码][代码]console.log(ctx)[代码][代码] [代码][代码]// 获取 access_token 值[代码][代码] [代码][代码]let tokenUrl = [代码][代码]'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='[代码] [代码]+ Config.wxConfig.appid + [代码][代码]'&secret='[代码] [代码]+ Config.wxConfig.secret;[代码][代码] [代码][代码]// 文本内容检测接口[代码][代码] [代码][代码]let checkUrl = [代码][代码]'https://api.weixin.qq.com/wxa/msg_sec_check?access_token='[代码][代码];[代码][代码] [代码][代码]let tokenResponse = await got(tokenUrl); [代码][代码]// 通过 got 请求 api[代码][代码] [代码][代码]let token = JSON.parse(tokenResponse.body).access_token; [代码][代码]// JSON.parse 将数据转换成对象获取到具体 access_token 值[代码][代码] [代码][代码]// 文本内容检测接口拼接 access_token 值, JSON.stringIfy 将值转换成 JSON 字符串[代码][代码] [代码][代码]let checkResponse;[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]checkResponse = await got(checkUrl + token, {[代码][代码] [代码][代码]method: [代码][代码]'POST'[代码][代码],[代码][代码] [代码][代码]headers: {[代码][代码] [代码][代码]'Content-Type'[代码][代码]: [代码][代码]'application/json'[代码][代码] [代码][代码]},[代码][代码] [代码][代码]body: JSON.stringify({[代码][代码] [代码][代码]content: ctx.request.body.content[代码][代码] [代码][代码]})[代码][代码] [代码][代码]});[代码][代码] [代码][代码]} [代码][代码]catch[代码][代码](err) {[代码][代码] [代码][代码]console.log(err)[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]ctx.body.data = checkResponse;[代码][代码]})[代码]进入catch,打出 [代码]{ ReadError: incorrect header check[代码][代码] [代码][代码]at EventEmitter.emitter.on (I:\work\whigest\whigestTalkServer\node_modules\got\source\as-promise.js:28:12)[代码][代码] [代码][代码]at <anonymous>[代码][代码] [代码][代码]at process._tickCallback (internal/process/next_tick.js:188:7)[代码][代码] [代码][代码]name: [代码][代码]'ReadError'[代码][代码],[代码][代码] [代码][代码]code: [代码][代码]'Z_DATA_ERROR'[代码][代码],[代码][代码] [代码][代码]host: [代码][代码]'api.weixin.qq.com'[代码][代码],[代码][代码] [代码][代码]hostname: [代码][代码]'api.weixin.qq.com'[代码][代码],[代码][代码] [代码][代码]method: [代码][代码]'POST'[代码][代码],[代码][代码] [代码][代码]path: [代码][代码]'/wxa/msg_sec_check?access_token=23_Xqd0UPPUJBSjbkLosKBvLCm6oZvtvbpuK6NNZGSSvPJ4Y7ljhcXnqr2b1S8u7XYMXZ0novGe2wJftEfqD6v9QEqD6dwZ5-3WzPQkSYUroJp_HKiUPvWQFTjXY_tvZ_ZQXX0jpWSM3rEJc4MIEOJgAHAMWR'[代码][代码],[代码][代码] [代码][代码]socketPath: undefined,[代码][代码] [代码][代码]protocol: [代码][代码]'https:'[代码][代码],[代码][代码] [代码][代码]url: [代码][代码]'https://api.weixin.qq.com/wxa/msg_sec_check?access_token=23_Xqd0UPPUJBSjbkLosKBvLCm6oZvtvbpuK6NNZGSSvPJ4Y7ljhcXnqr2b1S8u7XYMXZ0novGe2wJftEfqD6v9QEqD6dwZ5-3WzPQkSYUroJp_HKiUPvWQFTjXY_tvZ_ZQXX0jpWSM3rEJc4MIEOJgAHAMWR'[代码][代码],[代码][代码] [代码][代码]gotOptions:[代码][代码] [代码][代码]{ path: [代码][代码]'/wxa/msg_sec_check?access_token=23_Xqd0UPPUJBSjbkLosKBvLCm6oZvtvbpuK6NNZGSSvPJ4Y7ljhcXnqr2b1S8u7XYMXZ0novGe2wJftEfqD6v9QEqD6dwZ5-3WzPQkSYUroJp_HKiUPvWQFTjXY_tvZ_ZQXX0jpWSM3rEJc4MIEOJgAHAMWR'[代码][代码],[代码][代码] [代码][代码]protocol: [代码][代码]'https:'[代码][代码],[代码][代码] [代码][代码]slashes: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]auth: [代码][代码]null[代码][代码],[代码][代码] [代码][代码]host: [代码][代码]'api.weixin.qq.com'[代码][代码],[代码][代码] [代码][代码]port: [代码][代码]null[代码][代码],[代码][代码] [代码][代码]hostname: [代码][代码]'api.weixin.qq.com'[代码][代码],[代码][代码] [代码][代码]hash: [代码][代码]null[代码][代码],[代码][代码] [代码][代码]search: [代码][代码]'?access_token=23_Xqd0UPPUJBSjbkLosKBvLCm6oZvtvbpuK6NNZGSSvPJ4Y7ljhcXnqr2b1S8u7XYMXZ0novGe2wJftEfqD6v9QEqD6dwZ5-3WzPQkSYUroJp_HKiUPvWQFTjXY_tvZ_ZQXX0jpWSM3rEJc4MIEOJgAHAMWR'[代码][代码],[代码][代码] [代码][代码]pathname: [代码][代码]'/wxa/msg_sec_check'[代码][代码],[代码][代码] [代码][代码]href: [代码][代码]'https://api.weixin.qq.com/wxa/msg_sec_check?access_token=23_Xqd0UPPUJBSjbkLosKBvLCm6oZvtvbpuK6NNZGSSvPJ4Y7ljhcXnqr2b1S8u7XYMXZ0novGe2wJftEfqD6v9QEqD6dwZ5-3WzPQkSYUroJp_HKiUPvWQFTjXY_tvZ_ZQXX0jpWSM3rEJc4MIEOJgAHAMWR'[代码][代码],[代码][代码] [代码][代码]retry:[代码][代码] [代码][代码]{ retries: [Function],[代码][代码] [代码][代码]methods: [Object],[代码][代码] [代码][代码]statusCodes: [Object],[代码][代码] [代码][代码]errorCodes: [Object] },[代码][代码] [代码][代码]headers:[代码][代码] [代码][代码]{ [代码][代码]'user-agent'[代码][代码]: [代码][代码]'got/9.6.0 (https://github.com/sindresorhus/got)'[代码][代码],[代码][代码] [代码][代码]'content-type'[代码][代码]: [代码][代码]'application/json'[代码][代码],[代码][代码] [代码][代码]'accept-encoding'[代码][代码]: [代码][代码]'gzip, deflate'[代码][代码],[代码][代码] [代码][代码]'content-length'[代码][代码]: 106 },[代码][代码] [代码][代码]hooks:[代码][代码] [代码][代码]{ beforeRequest: [],[代码][代码] [代码][代码]beforeRedirect: [],[代码][代码] [代码][代码]beforeRetry: [],[代码][代码] [代码][代码]afterResponse: [],[代码][代码] [代码][代码]beforeError: [],[代码][代码] [代码][代码]init: [] },[代码][代码] [代码][代码]decompress: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]throwHttpErrors: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]followRedirect: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]stream: [代码][代码]false[代码][代码],[代码][代码] [代码][代码]form: [代码][代码]false[代码][代码],[代码][代码] [代码][代码]json: [代码][代码]false[代码][代码],[代码][代码] [代码][代码]cache: [代码][代码]false[代码][代码],[代码][代码] [代码][代码]useElectronNet: [代码][代码]false[代码][代码],[代码][代码] [代码][代码]method: [代码][代码]'POST'[代码][代码],[代码][代码] [代码][代码]body: [代码][代码]'{"content":"特3456书yuuo莞6543李zxcz蒜7782法fgnv级\\n完2347全dfji试3726测asad感3847知qwez到"}'[代码] [代码]} }[代码]- 预期表现 接口正常 - 复现路径 post请求 - 提供一个最简复现 Demo 代码如上 使用koa2 请问哪里不对呢
2019-07-21 - 开发者工具incorrect header check
[图片] 从昨天开始 就出现incorrect header 问题 确定不是代码的问题,求解答
2019-05-25 - 小程序文本信息检查接口报错
备注:之前可以正常使用,在没改动代码的情况下最近就不能使用了。 小程序文本信息检查接口(msgSecCheck)报错,报错信息如下: Error: errCode: -404011 cloud function execution error | errMsg: cloud.callFunction:fail requestID xxxxxxxxxx, cloud function service error code -504002, error message incorrect header check; at cloud.callFunction api 返回结果为: {"errorCode":1,"errorMessage":"user code exception caught","stackTrace":"incorrect header check"}
2019-06-05 - 获取手机号的api文档里面发现的一个疑问
[图片] 我看到这里最低版本是2.8.0,可是我在开发工具,还有后台管理的最低版本设置里面,没有看到这个版本
2019-07-09 - 云开发获取用户手机号
- 需求的场景描述(希望解决的问题) 云开发中能否获取用户微信绑定的手机号 刚开始学小程序的制作,想用云开发做一个小程序试试,在制作过程中发现我无法获取用户的手机号,百度了一些基本都是根据login获得的code发送到后台来获取手机号,目前没有自己后台的我有些为难啊。 - 希望提供的能力 希望有经验的小伙伴可以指点一二,云开发中有什么方法来获取用户手机号吗?
2019-06-29 - 微信小程序云开发想要获得群opengid应该发送到什么接口
如题,网上教程都是把加密信息发送到后台再返回解密的openGid,但是只是通过纯粹的微信小程序云开发应该怎么搞。求指点,急!!!!
2019-04-16 - 云开发如何获取群ID(opengid)
云开发如何获取群ID?大佬能提供示例代码吗?
2018-09-14 - text组件获取接口返回数据不能换行
- 当前 Bug 的表现(可附上截图) [图片] [图片] - 预期表现 {{aaaaa}}是接口返回的数据,数据是字符串,里面的换行符解析不了,但是本地写的换行可以,我在后面追加了222\ndsadad 发现可以换行,很奇怪 - 复现路径 - 提供一个最简复现 Demo
2019-03-01 - 关于后台接收的数据换行显示的问题
从后台获取一串字符,字符串中含有 '\n',但是前端要却没有换行,还直接把 \n 显示出来了。请问后台接收的一大串文字怎么换行?
2017-04-21