- 云函数如何接收mediaCheckAsync中的异步检测结果推送?
云函数 checkContent(审核图片) 中的 index.js const cloud = require('wx-server-sdk'); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async (event, context) => { const { value } = event; const { OPENID } = cloud.getWXContext() try { let imageR = false; //检查 图片内容是否违规 if (value) { console.log(value) imageR = await cloud.openapi.security.mediaCheckAsync({ openid: OPENID, scene: 3, version: 2, media_type: 2, media_url: value }) } return { imageR //图片检查返回值 }; } catch (err) { // 错误处理 // err.errCode !== 0 return err } } 设置消息推送: [图片] 云函数 getCheckResult: // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境 // 云函数入口函数 exports.main = async (event, context) => { return { event, } } 1、我的理解:用一个新的云函数去接收消息推送。我的这种理解对吗? 2、消息推送的事件类型怎么填?我现在的情况是只能选择(空)。事件类型为空有影响吗?
2023-02-25 - 调用mediaCheckAsync并处理异步检测结果推送
仅给出我的处理方式。如果你有更好的处理方式,欢迎在评论区讨论。 本人使用云开发,图片上传到云存储。 整体思路 具体场景(便于讨论):用户发布评论[代码]comment[代码],图片上传到字段[代码]image_upload[代码]。 准备:在数据库里创建一个[代码]traceId[代码]集合; 首先,用户上传图片,触发图片审核,并将[代码]traceId[代码]作为云存储路径; 异步检测结果推送到来时,如果[代码]'suggest' != 'pass'[代码]: 将[代码]traceId[代码]和固定前缀拼出一个云存储的[代码]fileID[代码]; 在集合[代码]traceId[代码],利用[代码]fileID[代码]作为一个字段创建一个对象 利用[代码]fileID[代码]在相关集合(如[代码]comment[代码])的相关字段(如[代码]image_upload[代码])里找:如果找到则直接删除云存储中的[代码]fileID[代码],找不到则不处理; 用户点击提交,首先先创建相关对象(如一个[代码]comment[代码]对象,其中包括字段[代码]image_upload[代码]),创建成功后查询集合[代码]traceId[代码]的所有对象(违规图片集合),将现有图片列表和违规图片集合进行比较,删除违规图片,并对相关字段(如:[代码]image_upload[代码]移除违规图片的[代码]fileID[代码])进行更新。 具体步骤和代码 1、创建云函数:[代码]checkContent[代码](右键 [代码]cloudfunctions[代码]→[代码]新建 Node.js 云函数[代码] 进行创建),上传并部署:云端安装依赖 [代码]//index.js const cloud = require('wx-server-sdk'); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async (event, context) => { const { value, scene } = event; const { OPENID } = cloud.getWXContext() try { let imageR = false; //检查 图片内容是否违规 if (value) { imageR = await cloud.openapi.security.mediaCheckAsync({ openid: OPENID, scene: scene, version: 2, media_type: 2, media_url: value }) } return { imageR, //图片检查返回值 }; } catch (err) { return err } } [代码] 2、在某个[代码]page[代码]中的[代码]js[代码]文件中: [代码]wx.chooseMedia({ count: 9, sizeType: ['original', 'compressed'], mediaType: ['image'], sourceType: ['album', 'camera'], camera: 'back', success: res => { checkAndUploadManyImages(res.tempFiles, this) }, fail: err => { console.log(err) }, }) [代码] 其中,[代码]checkAndUploadManyImages[代码] 函数(放在[代码]Page({})[代码]之外)实现: [代码]/** * 触发图片审核 * @param {待审核图片列表} tempFiles * @param {page = this} page */ function checkAndUploadManyImages(tempFiles, page) { wx.showLoading({ title: '上传中', mask: true }) for (var i = 0; i < tempFiles.length; i++) { const { tempFilePath } = tempFiles[i] /** * 1、触发审核,获取traceId */ wx.cloud.callFunction({ name: 'checkContent', data: { value: wx.cloud.CDN({ type: 'filePath', filePath: tempFilePath }), scene: 3 //场景枚举值(1 资料;2 评论;3 论坛;4 社交日志) }, success: json => { console.log(json) const { traceId } = json.result.imageR /** * 2、将traceId作为图片的云存储路径 */ wx.cloud.uploadFile({ cloudPath: traceId, // 上传至云端的路径 filePath: tempFilePath, // 小程序临时文件路径 success: res => { const { fileID } = res //data里:fileID: []。用于存放图片云存储路径。 page.data.fileID.push(fileID) page.setData({ fileID: page.data.fileID, }) wx.hideLoading() wx.showToast({ title: '上传成功 正在审核', icon: 'none' }) }, fail: err => { console.error('uploadFile err', err) wx.hideLoading() wx.showToast({ icon: 'error', title: '上传失败', }) } }) }, fail: err => { console.log('checkContent err', err) } }) } } [代码] 至此,只是触发了图片审核。接下来配置mediaCheckAsync的异步检测结果推送。 1、创建云函数 [代码]getMediaCheckResult[代码],上传并部署:云端安装依赖 [代码]//index.js const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database(); const traceId = db.collection('traceId') //在此之前,请数据库创建traceId集合 const head = 'cloud://xxx-xxx/' //这里请观察图片云存储路径(可使用consloe.log进行输出查看),将共同的前缀替换这里的字符串 function deleteImage(fileId) { cloud.deleteFile({ fileList: [fileId], success: res => { return res }, fail: err => { return err } }) } // 云函数入口函数 exports.main = async (event, context) => { const { result, trace_id, CreateTime } = event const { suggest } = result const fileId = head + trace_id; if (suggest != 'pass') { traceId.add({ data: { fileId, CreateTime } }).then(() => { //下面根据需要替换成自己的集合 db.collection('comment').where({ image_upload: fileId }).get() .then(() => { deleteImage(fileId); }) .catch((err) => { return err }) }) } else { return { suggest } } } [代码] 2、点击“云开发”-“设置”-“其他设置”-“添加消息推送”-进行配置(下图)-“确定” [图片] 3、回到[代码]page[代码]的[代码]js[代码]文件: [代码]//用户点击“提交”触发的事件 sendContent: function() { ... comment.add({ data: { ... image_upload: that.data.fileID, }, }).then((res) => { /** * 二、图片审核:处理异步检测结果推送 */ /** * 1、拿到全部的traceId集合(违规图片集合) */ const { _id } = res traceId.orderBy('CreateTime', 'desc').get() .then((res) => { /** * 2、删除上传图片列表中违规图片 */ deleteInvalidImages(that.data.fileID, res.data).then((res) => { /** * 3、更新图片列表 */ comment.doc(_id).update({ data: { image_upload: res } }).then(() => { wx.hideLoading() }) }) }) }) } [代码] 其中,[代码]deleteInvalidImages[代码]函数实现: [代码]/** * 删除已上传图片列表中的违规图片,并移除traceId对象 * @param {已上传图片列表} fileIds * @param {违规图片集合} cloudFileIds */ async function deleteInvalidImages(fileIds, cloudFileIds) { return new Promise(async function (resolve, reject) { try { const promises = []; let fileIdsWithoutCommon = []; promises.push(new Promise((resolve, reject) => { // 找到数组 fileIds 和数组 cloudFileIds 共有的元素 const common = fileIds.filter((elementA) => cloudFileIds.some((elementB) => elementA === elementB.fileId) ); // 删除数组 fileIds 中的共有元素 fileIdsWithoutCommon = fileIds.filter((elementA) => !cloudFileIds.some((elementB) => elementA === elementB.fileId) ); /** * 删除云存储中的违规图片 */ wx.cloud.deleteFile({ fileList: common, success: res => { console.log(res) resolve(); }, fail: err => { console.log(err) reject(err); } }) /** * 移除traceId对象 */ traceId.where({ fileId: _.in(common) }).remove({ success: res => { console.log(res) resolve(); }, fail: err => { console.log(err) reject(err); } }) })); await Promise.all(promises); resolve(fileIdsWithoutCommon); } catch (error) { reject(error); } }) } [代码]
2023-02-26 - 小程序开启skyline导致富文本真机编辑出现/n,而模拟器没问题
##富文本内容 <section data-role="outer" class="article135" label="edit by 135editor"><section label="Powered by 135editor.com" style="font-family:思源黑体;"><section style="background:#fdf7e1" class=""><section style=";-o-transform:translateZ(10px);transform: translateZ(10px);-webkit-transform: translateZ(10px);-moz-transform: translateZ(10px);-o-transform: translateZ(10px);"><section style="margin:10px auto;text-align:center;margin-top:-40px;-o-transform:rotate(0deg);transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-o-transform: rotate(0deg);" class=""><section style="display:inline-block" class=""><p style="font-size:45px;letter-spacing:2px;color:#ff6d1c;text-shadow:0.044em 0.044em 0.022em #fbeb9a"><br></p><p style="font-size:45px;letter-spacing:2px;color:#ff6d1c;text-shadow:0.044em 0.044em 0.022em #fbeb9a"><strong>绘画培训班</strong></p><section style="font-size:30px;letter-spacing:1.5px;color:#ff6d1c;margin:0px 0px 10px 0px;text-shadow:0.067em 0.067em 0.033em #fbeb9a"><strong>正在火热招人中</strong></section><section style="font-size:16px;letter-spacing:1.5px;padding:4px 1em;color:#fff;background:#fea513;border-radius:6px;box-sizing:border-box" class=""><strong>高效提升专业 培养绘画兴趣</strong></section><section style="font-size:16px;letter-spacing:1.5px;padding:4px 1em;color:#ff6d1c;margin-top:15px;box-sizing:border-box;text-shadow:0.063em 0.063em 0.063em #fbeb9a" class=""><strong>2024年11月30日正式开课</strong></section></section></section></section><section style="padding:0px 15px;box-sizing:border-box" class=""><section><section style="margin:0px auto;text-align:center" class=""><section style="display:inline-block"><section style="display:flex;justify-content:center;align-items:center"><section class="assistant" style="width:40px;margin-right:-25px;box-sizing:border-box;transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-o-transform: rotate(0deg);"><br></section><section style="font-size:16px;letter-spacing:1.5px;padding:6px 1em 6px 2em;background:#ffffff;color:#f78e05;box-sizing:border-box" class=""><strong>青山艺术启蒙班</strong></section></section></section></section></section><section><section style="margin:10px auto;text-align:center"><section style="display:flex;justify-content:flex-start;align-items:center" class=""><section class="assistant" style="width:45px;box-sizing:border-box;"><br></section><section class="assistant" style="flex:1 1 0%;height:4px;background:#ffffff;margin-top:5px;margin-left:10px;overflow:hidden"></section></section><section style="background:#ffffff;padding:1em;margin-top:-5px;box-sizing:border-box" hm_fix="300:471" class=""><section style="text-align:justify;line-height:1.75em;letter-spacing:1.5px;font-size:14px;color:#1ecaf3"><section style="text-align:justify;line-height:1.75em;letter-spacing:1.5px;font-size:14px;color:#3f3f3f" class=""><p style="vertical-align:inherit">以绘画和有针对性的绘画游戏方式,设计通过视觉、听觉、触觉等多感官的协调及多样化材料的运用,使孩子从感知到运用点、线、面进行造型,感受线、形、色的神奇变化及美感</p></section></section></section></section></section><section class=""><p style="vertical-align:inherit"><br></p></section><section style="height: 0px; overflow: hidden;"></section><section><section style="margin: 10px auto; text-align: center; height: 0px; overflow: hidden;" class=""></section></section><section style="height: 0px; overflow: hidden;"></section><section><section style="margin: 10px auto; text-align: center; height: 0px; overflow: hidden;" class=""></section></section><section><section style="margin:10px auto"><section style="border:10px solid #ffffff;padding:0px 20px 20px;box-sizing:border-box;background:#fff" class=""><section style="margin-top:20px" hm_fix="338:465" class=""><section style="text-align:center" class=""><section style="display:inline-block"><section style="font-size:16px;color:#fff;letter-spacing:1.5px;text-align:justify;box-sizing:border-box;background:#fea513;padding:6px 1em" class=""><strong>美术班开课指南</strong></section></section></section><section class="box-edit" style="margin-top:1.5em"><section style="display:flex;justify-content:flex-start;align-content:flex-start" class=""><section style="width:120px;flex-shrink:0;box-sizing:border-box;"><section style="display:inline-block"><section style="font-size:14px;letter-spacing:1.5px;padding:0px 2px;color:#333333;box-sizing:border-box">入学时间:</section><section style="max-width:100%;width:100%;height:5px;background:#fea513;margin-top:-5px;overflow:hidden;box-sizing:border-box;"></section></section></section><section style="font-size:14px;letter-spacing:1.5px;line-height:1.75em;text-align:justify;color:#464646" class=""><p style="vertical-align:inherit">2024年11月30日</p></section></section></section><section class="box-edit" style="margin-top:1em"><section style="display:flex;justify-content:flex-start;align-content:flex-start" class=""><section style="width:120px;flex-shrink:0;box-sizing:border-box;"><section style="display:inline-block"><section style="font-size:14px;letter-spacing:1.5px;padding:0px 2px;color:#333333;box-sizing:border-box">入学地址:</section><section style="max-width:100%;width:100%;height:5px;background:#fea513;margin-top:-5px;overflow:hidden;box-sizing:border-box;"></section></section></section><section style="font-size:14px;letter-spacing:1.5px;line-height:1.75em;text-align:justify;color:#464646" class=""><p style="vertical-align:inherit">青山路12单元12楼-520</p></section></section></section><section class="box-edit" style="margin-top:1em"><section style="display:flex;justify-content:flex-start;align-content:flex-start" class=""><section style="width:120px;flex-shrink:0;box-sizing:border-box;"><section style="display:inline-block"><section style="font-size:14px;letter-spacing:1.5px;padding:0px 2px;color:#333333;box-sizing:border-box">入学提示:</section><section style="max-width:100%;width:100%;height:5px;background:#fea513;margin-top:-5px;overflow:hidden;box-sizing:border-box;"></section></section></section><section style="font-size:14px;letter-spacing:1.5px;line-height:1.75em;text-align:justify;color:#464646" class=""><p style="vertical-align:inherit">注意安全,带好报名手续。</p></section></section></section></section></section></section></section><section><section style="margin:10px auto;text-align:center" class=""><p style="font-size:16px;letter-spacing:1.5px;padding:4px 1em;color:#3f3f3f;box-sizing:border-box"><br></p><p style="font-size:16px;letter-spacing:1.5px;padding:4px 1em;color:#3f3f3f;box-sizing:border-box"><strong>—END—</strong></p></section></section><section class=""><p style="vertical-align:inherit"><br></p></section><section><section style="font-size:14px;letter-spacing:1.5px;padding:4px 1em;color:#d98b5c;box-sizing:border-box;text-align:center" class=""><span style="color:#3f3f3f;font-family:微软雅黑, "Microsoft YaHei";">作者:青山小书童</span></section><section style="font-size:14px;letter-spacing:1.5px;padding:4px 1em;color:#d98b5c;box-sizing:border-box;text-align:center" class=""><span style="color:#3f3f3f;font-family:微软雅黑, "Microsoft YaHei";">文案 | 网络(侵删)</span></section></section><section class=""><section><section style="margin:10px auto;display:flex;flex-direction:column"><section style="margin-bottom:-12px"><section style="width:40px;height:20px;background-color:#d5260c;overflow:hidden;box-sizing:border-box;"></section></section><section style="background-color:#ffe26e;margin-left:7px;z-index:9"><section style="background-color:#ffffff;border:1px solid #ffe26e;padding:20px 10px;box-sizing:border-box;-o-transform:translate(-3px,-4px);transform: translate(-3px,-4px);-webkit-transform: translate(-3px,-4px);-moz-transform: translate(-3px,-4px);-o-transform: translate(-3px,-4px);"><section style="display:flex;justify-content:space-between;align-items:center"><section style="width:40%;box-sizing:border-box;max-width:40% !important;"><section style="width:110px;box-sizing:border-box;"><img style="width:258px;display:block;vertical-align:inherit;box-sizing:border-box;" title="undefined" src="https://oos-zsm.oss-cn-zhangjiakou.aliyuncs.com//mbookid/mphtml/202404/17/mphtml_ur17oLXG5g49fT6xfzJ6.jpg" data-width="100%" draggable="false" data-ratio="1" data-w="258"></section></section><section style="width:60%;box-sizing:border-box;max-width:60% !important;"><section style="display:flex;justify-content:center;align-items:center;margin-bottom:25px"><section><section style="display:flex;flex-direction:column"><section style="display:flex;justify-content:center;margin-bottom:-42px;height:40px"><section class="box-edit" style="background-color:#ffe26e;border-radius:100%;margin:0 -6px;box-sizing:border-box"><section style="width:2.5em;height:2.5em;background-color:#d5260c;border-radius:100%;overflow:hidden;box-sizing:border-box;transform: translate(-3px, -3px);-webkit-transform: translate(-3px, -3px);-moz-transform: translate(-3px, -3px);-o-transform: translate(-3px, -3px);"></section></section><section class="box-edit" style="background-color:#ffe26e;border-radius:100%;margin:0 -6px;box-sizing:border-box"><section style="width:2.5em;height:2.5em;background-color:#d5260c;border-radius:100%;overflow:hidden;box-sizing:border-box;transform: translate(-3px, -3px);-webkit-transform: translate(-3px, -3px);-moz-transform: translate(-3px, -3px);-o-transform: translate(-3px, -3px);"></section></section><section class="box-edit" style="background-color:#ffe26e;border-radius:100%;margin:0 -6px;box-sizing:border-box"><section style="width:2.5em;height:2.5em;background-color:#d5260c;border-radius:100%;overflow:hidden;box-sizing:border-box;transform: translate(-3px, -3px);-webkit-transform: translate(-3px, -3px);-moz-transform: translate(-3px, -3px);-o-transform: translate(-3px, -3px);"></section></section><section class="box-edit" style="background-color:#ffe26e;border-radius:100%;margin:0 -6px;box-sizing:border-box"><section style="width:2.5em;height:2.5em;background-color:#d5260c;border-radius:100%;overflow:hidden;box-sizing:border-box;transform: translate(-3px, -3px);-webkit-transform: translate(-3px, -3px);-moz-transform: translate(-3px, -3px);-o-transform: translate(-3px, -3px);"></section></section></section><section style="height:40px;z-index:9;display:flex;justify-content:center;align-items:center"><section style="font-size:16px;color:#ffffff;text-align:center"><strong>青山小书童</strong></section></section></section></section></section><section style="font-size:12px;color:#333333;text-align:center;line-height:1.75em">微 信 号:mbookid</section><section style="font-size:12px;color:#333333;text-align:center;line-height:1.75em" class="">新浪微博:@青山小书童</section></section></section></section></section></section></section><p style="vertical-align:inherit"><br></p></section></section></section><section class=""><p><br></p></section></section></section> 开启skyline的情况下模拟器调试不出现\n [图片] 开启skyline真机调试或者发布正式使用均 真机出现\n [图片] 如果不使用skyline模式则模拟器真机均不会出现\n问题 @微信开放社区 #微信开放社区 #skyline #skyline问题
06-04 - rich-text组件与editor组件的配合使用,用rich-text组件渲染显示editor组件内容
很多开发者在使用rich-text组件与editor组件会遇到这样的问题:希望通过rich-text组件按editor组件编辑时呈现的样式渲染出来。但直接用rich-text组件渲染的话,有些样式明显不一样。尤其是有序列表、无序列表、check-list等。 那是因为官方rich-text组件与editor组件是相互独立的。也就是说直接用rich-text组件渲染editor组件获取的html,可能跟想要的结果有差距 这篇文章主要就是告你开发者,怎么消除这些差距。 只需要做到两点就能渲染出想要的结果 1、节点结构 要想得到想要的结果,节点结构必须是下面这样的结构 [代码]<view class="ql-container"> <view class="ql-editor"> <rich-text nodes="{{html}}"></rich-text> </view> </view> [代码] 2、给 .ql-container 做样式调整 [代码].ql-container { display: block; position: relative; box-sizing: border-box; -webkit-user-select: text; user-select: text; outline: none; overflow: hidden; width: 100%; height: auto; min-height: 50px !important; } [代码]
2021-08-14 - editor 组件获取焦点推起时候页面不会上推 怎么解决?
editor 组件获取焦点推起时候页面不会上推(是部分机型不会例如华为) 并且editor 组件也没有adjust-position设置 没有办法自己去设置上推界面
2023-12-25 - 字节跳动小程序如何使用云开发cloudbase
前言 字节跳动小程序上线已经半年多了,字节系的很多产品如抖音、西瓜视频都自带巨大的流量,很适合普通人去掘金,而目前字节跳动小程序就是一个机会,但对于小团队或个人开发者来说字节跳动小程序没有云开发,相对来说成本和难度会高一些,很多和我一样用惯了云开发的小伙伴,也想在字节跳动云开发没出来之前先用腾讯云云开发cloudbase来写抖音小程序。今天搞了一天给搞明白了,直接上教程。 第一步:安装npm 先检查一下有没有这两个文件 [图片] 如果没有的话先安装 npm init npm install然后在安装云开发SDK npm i @cloudbase/js-sdk -Snpm i @maoyan/cloudbase-adapter-tt_mp -S需要注意的是字节跳动开发工具里的构建npm在详情——工程配置——自动构建npm [图片] 第二步:配置云开发cloudbase 1、由于字节跳动小程序没有提供getAccountInfoSync()接口,无法通过接口获取appId 所以需要将appId设置到字节跳动小程序app对象上。 onLaunch(options) { this.appId = appId //字节跳动小程序appid } 2、腾讯云cloudbase安全配置(给你的字节跳动小程序授权) [图片][图片] 输入你的字节跳动小程序appid就可以了。 授权成功后需要获取凭证和记录版本,获取后保存好,接下来要用到 [图片][图片] 3、匿名登陆 为了增加安全性,建议开启匿名登陆。启动匿名登录后,用户将不需要登录即可访问应用。如果有更严格的安全要求,可以自行开启其它身份验证方式。 [图片] 第三步:编写代码 index.js import tcb from '@cloudbase/js-sdk'; import { adapter } from '@maoyan/cloudbase-adapter-tt_mp'; Page({ data: { }, onLoad: function () { console.log('Welcome to Mini Code') }, getclouddata: function () { this.appId = "小程序appid"; //字节跳动小程序appid tcb.useAdapters(adapter); const app = tcb.init({ env: "云开发环境id", appSign: '小程序appid', // 需要设置成字节跳动小程序的appid appSecret: { appAccessKeyId: 1,//版本,一般都是1,安全配置中添加的应用白名单显示但版本 appAccessKey: '凭证',//在第二步安全配置中获得的凭证 } }); // 匿名登陆 const auth = app.auth() const loginState = auth.anonymousAuthProvider().signIn() const db = app.database(); db.collection('book').get().then(res => { console.log(res) }) }, }) 参考:https://developers.weixin.qq.com/community/develop/article/doc/000ac062acc5c047697cd3bdf51813 https://github.com/MaoYanTech/cloudbase-adapter-tt_mp
2021-07-27 - 新建Ts小程序模版构建npm错误 ,没有找到可以构建的 NPM 包,请确认需要参与构建的 npm 都在 `minipro
没有找到可以构建的 NPM 包,请确认需要参与构建的 npm 都在 `miniprogramRoot` 目录内,或配置 project.config.json 的 packNpmManually 和 packNpmRelationList 进行构建 录内,或配置 proje[图片]ct.config.json 的 packNpmManually 和 packNpmRelationList 进行构建 1、首先确实是先初始化过了 ``` npm init ``` 2、如果已经初始化以后,项目根目录找到project.config.json文件,在setting关键字,里面增加 "packNpmManually": true, "packNpmRelationList": [ { "packageJsonPath": "./package.json", "miniprogramNpmDistDir": "miniprogram/" } ] 3、然后在npm安装你想要的包,就可以正常构建了 npm i tdesign-miniprogram -S --production
2022-10-29 - 微信云函数如何解决@cloudbase/manager-node依赖问题?
本地调试能正常运行, 云端就不行, 删除后重新部署也没用, 目测可能是安装依赖的问题. 这两个都按了, 没效果. [图片] 求各路大佬帮帮忙, 谢谢 目的: 小程序读取云储存目录下的所有图片并展示 附云函数代码和调用的代码 //这是云函数代码 const cloud = require('wx-server-sdk') const CloudBase = require('@cloudbase/manager-node') const { storage } = new CloudBase() cloud.init({ // 初始化 env: '环境ID'//只有一个环境ID, 是正确的 }) exports.main = async (event, context) => { const list = await storage.listDirectoryFiles('SmartCampus/data/home/carousel/') console.log("获取目录下文件列表", list); return { data: { fileList: list }, } } //在home.js里面调用云函数 wx.cloud.callFunction({ name: 'getHomeCarousel', data: {} }).then(res => { console.log("test", res.result.data.fileList); var newArr = res.result.data.fileList.slice(1);//去掉第一个 that.setData({ arrPic: newArr }) }) //app.js里面也初始化了 wx.cloud.init({ traceUser: true, nev: '略' });
2022-07-12 - 微信小程序获取云存储中的文件列表
安装依赖 npm i @cloudbase/manager-node 云函数 const cloud = require('wx-server-sdk') const CloudBase = require('@cloudbase/manager-node') /* 初始化 */ cloud.init() const { storage } = new CloudBase() exports.main = async (event, context) => { /* listDirectoryFiles(cloudPath: string): Promise列出文件夹下所有文件的名称 downloadDirectory(options): Promise下载文件夹 listCollections(options: object): object来获取所有集合的名称,然后使用export(collectionName: string, file: object, options: object): object接口来导出所有记录到指定的json或csv文件里。 */ const res = await storage.listDirectoryFiles('images/') console.log(res) return { data: {}, } } 返回的文件列表: [图片]
2021-03-10 - 微信小程序云开发怎么删除云存储中包含多个文件的文件夹?
云开发存储中的文件夹下包含很多文件,如果deletefile一个一个删除会增加很多次访问次数,其次每一次读取fileid都很浪费时间,有没有办法可以一次性删除云存储中的文件夹? 看到有人说使用deleteDirctory方式删除,但是这里需要填写文件夹的云存储的绝对路径,这个绝对路径是什么?通过文件的路径开头是cloud://xxxx(环境ID的信息)/XXXX,但是依旧删除不了,是文件夹的路径表示方法有误?还是deleteDirctory在使用时有注意事项?谢谢您的回答
2021-04-24 - 一张表解决云存储的七大痛点
就是这张表: Collection: material { _id, _openid, createTime, cat,//分类。比如衣服、帽子 tag,//标签。比如产品号等 fileID,//cloud云存储路径 url,//Cloud.getTempFileURL获取的http路径,云存储权限设置为公有读, type,//img, video, file size, name,//上传前文件名 ext,//文件后缀 } 说明: 1、用一张表保存所有云存储文件的信息; 2、文件上传后,将相关信息保存在集合中。 3、任何地方引用图片src,都是使用表中的url,而不是使用fileID, 解决了以下痛点: 痛点一、云存储里有哪些文件,有哪些垃圾文件? 痛点二、云存储某文件夹下有哪些文件?怎么删除云存储文件夹?不熟悉cloud base node sdk或者manage sdk的同学,一定搞不定这个痛点; 痛点三、图片太大,我想用腾讯云图像处理进行压缩裁剪?fileID不支持,只能用url; 痛点四、跨云环境访问图片,不支持fileID,只能用url; 痛点五、在前端引用url,但是删除图片做不到。即通过url,不知道fileID是什么,删除不了云存储文件; 痛点六、前端可以统一管理图片,素材库,而不是在某流程中上传文件后,完全不管理它; 痛点七、可对所有文件图片,分类、贴标签,按openid检索,按type检索,各种姿势检索。 可能还有其他好处,不多介绍。 总之,无论如何,你应该需要这样一张表。
2022-07-12 - 珊瑚图片内容安全检测接口接入详解(小程序端)
珊瑚图片内容安全检测是腾讯推出的检测接口,现如今,小程序审核更加严格,内容安全检测是必需品。官方网址:https://fuwu.weixin.qq.com/detail/000a246b6fca70b76a896e6a25ec15 不多说,上干货。 小程序端接入(实例为用户上传本地图片中调入): local(){ wx.chooseImage({ count: 1, // 图片数目。默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success(res) { const src=res.tempFilePaths[0];//图片本地临时路径 const base64=wx.getFileSystemManager().readFileSync(res.tempFilePaths[0], "base64");//由于此功能只支持对线上图片检测,所以我们将临时本地图片转为base64编码形式 wx.showLoading({//加载框 title: '加载中...', }) wx.serviceMarket.invokeService({ service: '',//当你购买了珊瑚产品后,可以在订单-数据中查看appid号, api: 'imgSecCheck', data: { "Action": "ImageModeration", "Scenes":["PORN", "POLITICS", "TERRORISM"],//检测的三个方面(色情、政治、暴力) "ImageUrl": "", "ImageBase64": base64, "Config": "", "Extra": "" }, }).then(res => { console.log(JSON.stringify(res)) if(res.data.Response.Suggestion!=="PASS"){//这里是整体判断,PASS=通过,REVIEW:疑似,BLOCK:违规 wx.showModal({ title:"图片未通过安全检查!" }); } else{ //此处填写通过检测后想要执行的功能 } }) } }) }, --------------------------------------------------------------------------------------------- 注意点: 1.Service,需填购买产品的,而不是自己app的id号,具体查看方法:订单-数据-appid号; 2.线上图片提供网址,线下图片需转为base64编码; 3.API返回值详情见网址https://cloud.tencent.com/document/api/865/35473#3.-.E8.BE.93.E5.87.BA.E5.8F.82.E6.95.B0 第一次发文,大家有什么建议、问题积极交流哈哈
2021-02-05 - 云开发批量上传图片,上传完图片再上传数据库 [即抄即用,拎包入住]
大家好,又是我拎包哥,今天我们来实现在云开发中批量上传图片。 经过Stephen哥的指正,我改用了Promise.all的方法来达到目的。 Promise.all的作用就是等待所包含的promise函数结束后再执行下一步逻辑,非常方便好用!const db = wx.cloud.database() const test = db.collection('test') Page({ onLoad() { this.imgList = [] wx.chooseImage({ success: (res) => { this.TFP = res.tempFilePaths } }) }, btn() { let promiseMethod = new Array(this.TFP.length) for (let i = 0; i < this.TFP.length; i++) { promiseMethod[i] = wx.cloud.uploadFile({ cloudPath: 'img' + i + '.png', filePath: this.TFP[i] }).then(res => { this.imgList.push(res.fileID) }) } Promise.all([...promiseMethod]).then(() => { test.add({ data: { imgList: this.imgList } }) }) } }) --------------------------------------我是分割线-------------------------------------- async await 要点: ctrl c + ctrl v这里用了await阻塞在wx.cloud.uploadFile前面,避免还没上传完图片就往数据库插入数组。减少了then里的代码,美观逼格高。嘻嘻嘻。await wx.cloud.uploadFile不能放在wx.chooseImage里,如果可以的话,请告诉我怎么做,谢谢!欢迎交流,指出错误,我立刻修改么么哒。 标准版 const db = wx.cloud.database() const test = db.collection('test') Page({ onLoad() { this.imgList = [] wx.chooseImage({ success: (res) => { this.TFP = res.tempFilePaths } }) }, async btn() { this.imgList = [] console.log(this.TFP) for (let i = 0; i < this.TFP.length; i++) { await wx.cloud.uploadFile({ cloudPath: 'img' + i + '.png', filePath: this.TFP[i] }).then(res => { this.imgList.push(res.fileID) }) } test.add({ data: { imgList: this.imgList } }) } }) 新手最爱一锅炖版(不推荐) 为什么不推荐呢,因为选择图片并不意味着要上传图片,用户还没进行最终的确定操作(不过可以用来了解async await)。 onLoad() { this.imgList = [] wx.chooseImage({ success: async res => { this.TFP = res.tempFilePaths for (let i = 0; i < this.TFP.length; i++) { await wx.cloud.uploadFile({ cloudPath: 'img' + i + '.png', filePath: this.TFP[i] }).then(res => { this.imgList.push(res.fileID) }) } test.add({ data: { imgList: this.imgList } }) } }) } [图片] ==========================end==========================
2020-05-17 - 不用云函数,云开发如何获取openid?
极简代码如下: app.js: App({ getOpenid: async function () { let col = 'test'//任意一个未修改过权限的集合 let res = await db.collection(col).get() if (res.data.length) return res.data[0]._openid await db.collection(col).add({ data: {} }) return (await db.collection(col).get()).data[0]._openid }, }) page.js: const app = getApp() Page({ onLoad: async function () { this.openid = await app.getOpenid() console.log('openid:', this.openid) } }) 这应该是史上最简的获取openid代码了。 并且,解决了获取openid异步问题。
2022-12-05 - 富文本editor怎么实现首行缩进?
可以通过 this.editCtx.format('textIndent', '2em') 的方式实现
2019-09-16 - 🎆我们开源啦 | 基于Skyline开发的组件库🚀
我们开源啦,希望可以给大家的开发之旅带来一些灵感。我后溪的小程序也都会基于这个组件库开发,并且会保持组件库的更新与维护。 我是第一次进行开源,肯定会有错漏,欢迎大家指正,我会以最快的时间响应修改。 Skyline UI 组件库 前言 Skyline 是微信小程序推出的一个类原生的渲染引擎,其使用更精简高效的渲染管线,性能比 WebView 更优异,并且带来诸多增强特性,如 Worklet 动画、手势系统、自定义路由、共享元素等。 使用这个组件库的前提是:通过微信小程序原生+skyline框架开发,所以目前我们不保证兼容webview框架(也就是电脑端与低版本的微信),但后续会进行系统性的兼容。 使用 Skyline UI前,请确保你已经学习过微信官方的 微信小程序开发文档 和 Skyline 渲染引擎文档 。 背景 随着Skyline 渲染引擎 1.1.0 版本发布,我们所运营的小程序也平稳的渡过了阵痛期,团队使用Skyline也越来得心应手,所以接下来,团队的开发重心全面偏向Skyline渲染框架,考虑有大量的UI交互重复,我们决定基于Skyline开发了这个UI组件库。 但团队力量有限,这个新生的组件可能有很多的不尽如人意,所以希望能以开源的方式吸引更多开发者使用Skyline框架,如果这个框架不适合你,也可以借鉴其思路。 Gitee Gitee仓库 在线预览 以下是目前两个使用该框架的小程序 SkylineUI组件库 [图片] NONZERO COFFEE [图片] 开始使用 UI库结构 Skyline UI组件库 依赖于以下四部分,具体使用参考以下的具体说明 utils工具库: 其中包含了UI库自定义的一个工具类SkyUtils,它包含了组件中所含的各种函数,非常重要。 各组件元素:sky-*(组件名) skywxss样式库:其中包含深浅色色彩、文字字体、布局等样式wxss 在小程序中引入 UI库 一、直接下载引入 点击下载组件包 将src下所有文件复制到您项目根目录下的components文件夹中,没有的话请自行新建。 二、npm引入 1.在小程序项目中,可以通过 npm 的方式引入 SkylineUI组件库 。如果你还没有在小程序中使用过 npm ,那先在小程序目录中执行命令: [代码]npm init -y [代码] 2.安装组件库 [代码]npm install jieyue-ui-com [代码] 3.npm 命令执行完后,需要在开发者工具的项目中点菜单栏中的 工具 - 构建 npm 两种引入方式的不同可能导致后续使用时,引用组件的路径不同,请注意区别 1.直接引入components文件夹内,引用地址通常是 ‘./components/‘ 2.npm引入,组件引用地址通常是’./miniprogram_npm/jieyue-ui-com/’ 如何使用 1.在app.js文件中初始化工具类,并且添加两个全局变量 [代码]// app.js App({ onLaunch() { ;(async ()=>{ // 全局注册工具类SkyUtils // 这里默认npm引用,地址为'./components/utils/skyUtils',如果是直接引用组件,地址可能是'./components/utils/skyUtils',后面不再说明 const SkyUtils = await import('./components/utils/skyUtils'); wx.SkyUtils = SkyUtils.default; // 初始化设备与系统数据 wx.SkyUtils.skyInit() // 小程序自动更新方法 wx.SkyUtils.versionUpdate() })() }, globalData: { sky_system:{}, sky_menu:{} }, }) [代码] 2.在app.wxss文件中引入样式文件 [代码]//wxss * _dark.wxss 是适配深色模式的色彩变量 @import '/miniprogram_npm/jieyue-ui-com/skywxss/skycolor.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skycolor_dark.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skyfontline.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skyfont.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skyother.wxss'; [代码] 3.page.json中引用组件 [代码]//page.json { "usingComponents": { "sky-text":"/miniprogram_npm/jieyue-ui-com/sky-text/sky-text" } } [代码] 4.页面中使用 [代码] // wxml <sky-text content="文本内容" max-lines="2" fade></sky-text> [代码] 5.其他组件具体使用请参考组件包中的redeme.md 适配深色模式 如果您在开发时,全部使用我们预设好的颜色变量,那么可以自动适配深色模式。 [代码].page{ background-color: var(--bg-l0); } [代码] [代码] <view style="background-color: var(--bg-l0)"></view> <view style="background-color: {{color}}"></view> [代码] [代码] Page({ data: { color: "var(--bg-l0)" } }) [代码]
01-09 - 小程序渲染引擎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 - 小程序app.onLaunch与page.onLoad异步问题的最佳实践
场景: 在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。 直接上终极解决方案,公司内部已接入两年很稳定: 1.可完美解决异步问题 2.不污染原生生命周期,与onLoad等钩子共存 3.使用方便 4.可灵活定制异步钩子 5.采用监听模式实现,接入无需修改以前相关逻辑 6.支持各种小程序和vue架构 。。。 //为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档 //app.js //globalData提出来声明 let globalData = { // 是否已拿到token token: '', // 用户信息 userInfo: { userId: '', head: '' } } //注册自定义钩子 import CustomHook from 'spa-custom-hooks'; CustomHook.install({ 'Login':{ name:'Login', watchKey: 'token', onUpdate(token){ //有token则触发此钩子 return !!token; } }, 'User':{ name:'User', watchKey: 'userInfo', onUpdate(user){ //获取到userinfo里的userId则触发此钩子 return !!user.userId; } } }, globalData) // 正常走初始化逻辑 App({ globalData, onLaunch() { //发起异步登录拿token login((token)=>{ this.globalData.token = token //使用token拿用户信息 getUser((user)=>{ this.globalData.user = user }) }) } }) //关键点来了 //Page.js,业务页面使用 Page({ onLoadLogin() { //拿到token啦,可以使用token发起请求了 const token = getApp().globalData.token }, onLoadUser() { //拿到用户信息啦 const userInfo = getApp().globalData.userInfo }, onReadyUser() { //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦 const userInfo = getApp().globalData.userInfo // 获取canvas上下文 const ctx = getCanvasContext2d() ctx.drawImage(userInfo.head,0,0,100,100) }, onShowUser() { //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑 const userInfo = getApp().globalData.userInfo switch(userInfo.sex){ case 0: // 走女生逻辑 break case 1: // 走男生逻辑 break } } }) 具体文档和Demo见↓ Github:https://github.com/1977474741/spa-custom-hooks 祝大家用的愉快,记得star哦
2023-04-23 - 关于app.js 中onLaunch执行异步函数执行完再执行页面onLoad的问题?
比如在onLaunch中执行异步登录后,首页或者其他页面需要等到这个登录成功或者失败后才能才能在页面中做具体展示,在首页中这个问题可以解决,在app.js中执行完异步操作后,可以使用this.callback,然后在首页中使用this.callback = res => {}做下一步操作,但是除了首页,其他所有页面作为加载的第一个页面调用this.callback = res => {}这个方法都会报错 _this2.callback is not a function at app.js? [sm]:77。这个问题还挺困扰的,因为除了首页,其他很多页面也会因为分享而会成为小程序加载的首页。 先在这里贴出app.js和index.js的相关代码,希望可以获得帮助。 app.js App({ onLaunch() { wx.login({ success:res=>{ this.callback(res) } }) } }) index.js Page({ data: {}, onLoad(query) { app.callback = res => { //todo do something } } })
2023-08-29 - #小程序云开发挑战赛#--吃否CHIFOU--HUIBUR科技
[图片][图片][图片][图片][图片][图片][图片][图片][图片][图片][图片][图片] 作品视频链接:http://m.v.qq.com/play.html?cid=&vid=d3153yg89mz&vuid24=wO8BbfYFStT4QO6%2BuANDhw%3D%3D&url_from=share&second_share=0&share_from=copy
2020-09-20 - #小程序云开发挑战赛#-大学生记账本-阳光队
系统提供支出、收入、转账、余额、借贷五大记账模块,内含多种情景账本,涉及食品、交通、购物、宿舍、娱乐、学习等各种针对学生的场景,以满足不同时期的记账需要。用户可以实时查看自己的账户余额和所有账单记录。本小程序已经上线,欢迎扫码体验。 [图片] 开源代码链接 GitHub:https://github.com/ChangYanwei/accountBook Gitee:https://gitee.com/changyanwei/accountBook 以下是详细介绍。 [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片][图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片]
2022-04-10 - #小程序云开发挑战赛#-民大新生助手-梦的开始
应用场景 当大一新生录取时,刚到学校或者还没到学校时,对学校的概况是一无所知。每当开学季,群里面会出现新生问各种问题,如学校宿舍怎样、食堂如何、环境如何、校园卡在哪里领等问题。虽说群里会有热心的学长学姐回复,但是回复的内容也不是很完全,所以这款民大新生助手的主要功能就是展示宿舍、食堂、校园风景、视频、美食、商品转卖、各院群号,各社团群号,各省群号等信息,为大二大三的也提供了一定的服务,如上传社团、工作室、二手物品、电子商品、新生解答等服务。 目标用户西北民族大学每一届新生 实现思路 微信小程序的框架包含两部分View视图层、App Service逻辑层,View层用来渲染页面结构,AppService层用来逻辑处理、数据请求、接口调用,它们在两个线程里运行。视图层使用WebView渲染,逻辑层使用JSCore运行。视图层和逻辑层通过系统层的JSBridage进行通信,逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。 功能结构图 [图片] 架构图 [图片][图片] 效果截图 首页 [图片][图片][图片][图片][图片] 民大页 [图片][图片][图片][图片] 校园页 [图片][图片][图片] 推荐页 [图片][图片][图片][图片][图片][图片] [图片] 注:管理员认证功能是审核发布的话题,通过则显示,不通过,则删除。 功能代码展示 订阅消息 [图片] [图片] [图片] 点赞云函数 [图片] 检测内容合法性 [图片] [图片] 作品二维码 [图片] 团队简介 个人独自完成。
2020-09-15 - 小程序内完美实现吸顶粘附功能(媲美 美团小程序的吸顶效果)如丝滑一般
利用css属性完美解决吸顶粘附效果(ios&android都适用),分享一下心得: tips:不需要调用onPageScroll 方法 去监听滚动距离!!! 1、给吸顶元素添加css属性 position: sticky;2、给元素设置距离顶部吸附的距离 top: 0px; (这个top值你可以根据实际情况动态设置)3、然后完事了啦啦啦啦啦啦啦啦~by the way:附上简单的代码片段------>https://developers.weixin.qq.com/s/pR8QKjmE7igq
2020-04-02 - 小程序已完成备案,注销时会不会对主体下其他小程序有影响?
小程序已完成备案,注销时会不会对主体下其他小程序有影响?
2023-09-20 - # 使用小程序云开发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 - eCPM单价下降,同领域账号当家三四十甚至七八十,而我只有几块?
如题,刚开始发文eCPM还维持在三十多的水平,从去年开始一直在下降,现在只剩下三四块,明明点击率还上升了,单价却下降了?[图片]
2023-01-31 - 视频广告的拉取量统计有问题,并且cpm为何下降得如此严重?
背景:从首页进入“全科排班表”页面,因为“全科排班表”页面的数据原因,本身需要2-3秒的加载。所以在从首页进入“全科排班表”页面时,我们插了一个中间页,中间页上插入了视频广告,并且设置了展示5秒钟的逻辑。 先看两张“全科排班表”页面分析数据: [图片] [图片] 大家可以看到,8月6日当天,“全科排班表”页面的访问次数、人数分别是:10.2w、3.7w。然后从8月1日到8月6日这6天,访问次数、人数分别是:63.6w、22.3w,平均下来每天恰好是:10.6w,3.71w,也就是说该页面每天访问的次数、人数相差无几。 然后看下视频广告的数据: [图片] 自从接入广告后,“全科排班表”中间页视频广告的cpm就一直在下降,查阅了相关文档后,我们大约得出可能有如下原因: 1、推送广告价格策略原因。2、点击率问题。3、频繁曝光原因。 原因1我们无法控制,原因2还没做优化(但也没想通,为何6号点击率下降了这么多),那么是否和原因3也有一定的关系,所以在8月6日我们做了一个广告曝光策略,就是用户10分钟内访问“全科排班”页面,只会显示1次视频广告。所以这也是为何8月6日,广告的拉取量、曝光量如此低的原因。但是,按照8月6日的页面分析,访问次数、人数分别为:10.2w、3.7w。那么按道理,广告拉取量最低也应当有3.7w的拉取量,而不是现在统计的2.5w。并且由于还做了10分钟只出现一次广告的策略,曝光率应当比以前的数据要高,现在反而降低了。 所以针对问题:1、视频广告的拉取量和曝光量统计存在问题。2、cpm下降如此严重的原因。 请官方给一个说法 AppID(小程序ID):wxfc6abb68b3a64785 再补充一下:我们小程序95%以上都是年龄25-39之间的家庭主妇,满满的购买力。推的广告却大部分都是游戏、汽车、股市之类的广告,也是无奈了。
2020-08-07 - 代码加固,提示加固无法发起,加固配置内没有合法的js文件,请问是什么原因?
如题,正常编译、调试、运行都是OK的。加固配置文件内容如下: { "desc": "关于本文件的更多信息,请参考文档 代码加固开发者文档: https://developers.weixin.qq.com/miniprogram/dev/devtools/code_obfuscation.html", "switch": true, "configs": [ { "path": "app.js", "sub_switch": true } ] }
2023-05-10 - Skyline|原生级卡片转场,小程序轻松实现
在上一篇文章《在小程序中实现原生相册》中,我们学习了自定义路由搭配共享元素实现的原生相册效果,共享元素可以让用户在体验小程序时视觉关联性更强。 除了相册实现之外,常见的卡片转场也非常适合。 [图片] ⬆️ 演示效果:默认动画 vs 卡片转场动画 👇 下面我们来看看卡片转场中通过 共享元素 + 自定义路由 来实现无痕跳转。 [图片] 这里的转场稍微有点复杂,涉及到以下 3 个点 旧卡片:图片放大、内容渐隐新页面:按比例放大、页面渐显手势搭配1、旧卡片:图片放大、内容渐隐 在本示例中,列表页采用的是 scroll-view 瀑布流布局的实现。 [图片] 这里我们的共享元素是卡片,即 grid-view 中的内容 card,卡片包括 图片、内容描述。 [图片] 默认情况下,共享元素是整个节点进行飞跃的,由于前后页面的图片元素一致但文本内容不一致, 导致在第一帧或者最后一帧会有跳动的效果。 为了让转场动画更加自然,我们需要在飞跃的过程中渐隐旧卡片的内容描述。 [图片] 在这里,我们需要先用 this.applyAnimatedStyle 来给对应的节点绑定 worklet 驱动动画。 .card_wrap 节点:整个卡片按比例放大.card_desc 节点:内容描述渐隐[图片] 关于动画执行的时机,我们可以通过配置项修改。 immediate:设置是否立即执行驱动动画flush:shareValue 更新时,applyAnimatedStyle 的 updater 函数刷新时机在本例中,需要保证共享元素的图片与目标页面图片位置重叠,所以 flush 设置 sync 在当前时间片刷新。 [图片] 绑定完驱动动画之后,我们需要给共享元素绑定帧回调事件,根据当前动画进度改变共享变量的值来驱动共享动画 [图片] 2、新页面:按比例放大、页面渐显 新页面在路由中的动画,需要在自定义路由中进行配置。关于自定义路由的更多介绍,可参考《小程序页面转场动画》 在路由动画过程中,我们将上一步的共享元素帧回调拿到 begin、end 的值,然后结合动画进度 t 计算得出新页面的位置、缩放比例。 还有根据动画进度,设置页面渐显,与前面的卡片渐隐承接。 [图片] 3、手势搭配 学习过我们前面的文章的同学都知道,自定义路由经常需要结合页面手势,来实现手势返回,关于手势的基础知识可参考《小程序页面转场动画》 [图片] 这里我们希望手势缩小整个当前页面,所以这里手势返回时只在当前页面做手势动画即可。 在页面详情页的最外层,嵌套一个手势组件 pan-gesture-handler,当手势拖动时根据手势的位置改变整个页面(通过 #fake-host 控制)的位置和大小来达到拖动的效果。 [图片] 同样绑定页面驱动动画,通过 applyAnimatedStyle 给 #fake-host 绑定驱动动画,当共享变量 transX、transY 等变化时则自动改变 transform 来驱动 #fake-host 缩小。 [图片] 接着绑定手势事件,根据手势拖动时拿到位置信息改变共享变量 transX、transY 的值。 [图片] 最后我们需要设置背景颜色透明,来达到类似把卡片拖回列表的视觉效果,更好的减少页面切换感~ [图片] 一个自定义路由的页面会有 3 层可以设置到背景色,要做到透明的效果需要将 3 个背景色都设置为透明。更多自定义路由背景色的详情参考官方文档。 [图片] 想要试试卡片转场的无恒效果~扫描 ⬇️ 下方小程序码即可体验。 如果你也想在小程序中实现卡片转场动画,mark 下这个 源码 直接接到到你的小程序吧~ [图片]
2023-08-03 - Skyline|小程序吸顶、网格、瀑布流布局都拿下~
在之前的文章中,我们知道了新 scroll-view 可以让小程序的长列表做到丝滑滚动~ 也提到了新 scroll-view 提供了很多新能力 sticky、网格布局、瀑布流布局等,这一篇,我们就来看看这些新能力是怎么使用的~ 新 scroll-view 在原来列表模式(type="list")的基础上,新增了自定义模式(type="custom") 在自定义模式下,新增了以下新组件供开发者调用 list-view:列表布局容器sticky-section / sticky-header:吸顶布局容器grid-view:网格布局容器,可实现网格布局、瀑布流布局等sticky布局sticky 布局即在应用中常见的吸顶布局,与 CSS 中的 position: sticky 实现的效果一致,当组件在屏幕范围内时,会按照正常的布局排列,当组件滚出屏幕范围时,始终会固定在屏幕顶部。 常见的使用场景有:通讯录、账单列表、菜单列表等等。 与 position: sticky 不同的是,position: sticky 很难实现列表滚动需要的交错吸顶效果,而 sticky 组件则可以帮忙开发者轻松实现交错吸顶的效果。 sticky 的使用非常简单: 将 scroll-view 切换到 custom 模式采用 sticky-section 作为 scroll-view 的子元素sticky-header 放置吸顶内容list-view 放置列表内容 {{item.name}} ... 我们来看下采用 sticky 布局做出来的通讯录效果~ [视频] sticky 布局也可以通过给 sticky-section 配置 push-pinned-header 来声明吸顶元素重叠时是否继续上推 像下图输入框和标签列表这种类型,标签列表吸顶时还是希望保留输入框吸顶。 [视频] 网格布局网格布局即将列表切割成格子,每一行的高度固定,常见的视频列表、照片列表等通常都采用网格布局。 在此之前,实现网格布局需要开发者自行实现网格切割,再嵌入到 scroll-view 中。 新 scroll-view 直接提供了 grid-view 组件供开发者使用~ 将 scroll-view 切换到 custom 模式采用 grid-view 类型为 aligned 做为直接子节点grid-view 中直接编写列表 ... 下面是使用网格布局实现的图片列表效果~ [视频] 瀑布流布局瀑布流布局与网格布局类似,不同的是瀑布流布局中每个格子的高度都可以是不一致的,所以在小程序中实现瀑布流布局就比较复杂了。 开发者需要通过计算格子高度,然后再进行瀑布流拼接,当滚动内容过多时还需要处理节点过多导致内存不足等问题。 grid-view 组件直接支持了瀑布流模式供开发者直接使用,grid-view 组件会根据子元素高度自动布局: 将 scroll-view 切换到 custom 模式采用 grid-view 类型为 masonry 做为直接子节点grid-view 中直接编写列表 ... 下面是使用瀑布流布局实现的图片列表效果~ [视频] 想要立即体验?现在通过微信开发者工具导入 代码片段,即可体验新版 scroll-view 组件能力~
2023-08-03 - 向官方道歉。接口security.imgSecCheck有问题?我已经解决。并附上最全代码,保证能用
首先,发布的帖子,关于“security.imgSecCheck 这个图片内容审核API 有问题”的帖子,我已经解决。 之前说接口不稳定,错怪了官方! 向官方道歉, 向官方道歉, 向官方道歉!!! 以下分享经验【3个重点】: 1.(重点)控制图片尺寸:图片尺寸不超过 750px x 1334px 2.(重点)检测的图片,一定要压缩后再上传。(图片大小限制:1M) 自己检测过。500k-1MB还是大了。建议在50kb 左右。 3.(重点)通过获取文件信息,图片以ArrayBuffer格式上传云函数进行检测。 wx.getFileSystemManager().readFileSync(图片临时文件) //文件二进制内容 ArrayBuffer 以上条件缺一不可 根据返回结果,执行你需要的代码 复制以下代码。直接可以使用。 ------------------------------------------------------------------------------------------------------------------------ 【xxx.html代码】 <canvas canvas-id='imageBox' class="imageBox" style="border:#000 5px solid; width:{{imageBoxMake_Width}}px; left:{{screenWidth * 1.2}}px;"></canvas> 【xxx.wxss代码】 /* 设定页面决对定位 */ page{ position: relative; } /* 设定画布相对定位 */ .imageBox{ position: absolute; z-index: 0; } 【xxx.js代码】 //图片内容安全-画布 imageBoxMake_Width: '', imageBoxMake_height: '', /**生 命周期函数--监听页面加载 */ onLoad: function(options) { console.log('进入introduction页面') this.setData({ //画布布局。让画布在屏幕之外 screenWidth: app.globalData.screenWidth, //画布距离屏幕左侧宽度 = 屏幕宽度 }) }, //【选择图片】方法 chooseImage() { var that = this //使手机发生较短时间的振动 wx.vibrateShort() wx.showActionSheet({ itemList: ['拍照', '相册',], success(res) { console.log(res.tapIndex) //拍照vounDemo if (res.tapIndex == 0) { wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], //['original', 'compressed'] sourceType: ['camera'], success(res) { // 图片临时地址 const imgFileURL = res.tempFilePaths[0] console.log('打印取到的图片') console.log(res) //canvas绘制并压缩图片,然后图片内容安全检测 that.imageBoxMake(imgFileURL) } }) } //手机相册 vounShow else if (res.tapIndex == 1) { wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album'], success(res) { // 图片临时地址 const imgFileURL = res.tempFilePaths[0] console.log('打印取到的图片') console.log(res) //canvas绘制并压缩图片,然后图片内容安全检测 that.imageBoxMake(imgFileURL) } }) } }, fail(res) { console.log(res.errMsg) } }) }, //绘制【内容安全图片图片】canvas imageBoxMake: function (imgFileURL) { wx.showLoading({ title: '正在压缩图片', //正在内容安全检测 }) console.log('开始imageBoxMake方法') var that = this var saveSize = 100 var canvasId = 'imageBox' //画布ID //创建画布 const ctx = wx.createCanvasContext(canvasId) ctx.save() //获得图片信息 wx.getImageInfo({ src: imgFileURL, //获得画芯图片 success(res) { //图片比例 var perWHcanvas = res.height / res.width that.setData({ //画布尺寸,处理为(100px)像素宽度。 imageBoxMake_Width: saveSize, imageBoxMake_height: saveSize * perWHcanvas, //高度等比 }) //绘制图 ctx.drawImage(res.path, 0, 0, res.width, res.height, 0, 0, saveSize, saveSize * perWHcanvas) ctx.restore() //绘制保存 ctx.draw(true) console.log('绘制【内容安全图片图片】canvas完成') setTimeout(function () { that.imageBoxIMG(saveSize, perWHcanvas, canvasId, imgFileURL) //压缩图片尺寸,并上传 }, 2000) } }) }, //压缩图片尺寸,并上传 imageBoxIMG: function (saveSize, perWHcanvas, canvasId, imgFileURL) { console.log('开始 imageBoxIMG 方法') var that = this wx.canvasToTempFilePath({ x: 0, y: 0, width: saveSize, height: saveSize * perWHcanvas, destWidth: saveSize, destHeight: saveSize * perWHcanvas, canvasId: canvasId, //这是canvasId fileType: 'jpg', //目标文件的类型 quality: 0.8, //图片的质量 success(res) { console.log('保存的图片临时路径' + res.tempFilePath) var canvasImage = res.tempFilePath wx.hideLoading() //隐藏 loading 提示框 const imageArrayBuffer = wx.getFileSystemManager().readFileSync(canvasImage) //文件二进制内容 ArrayBuffer that.imgSecCheck(imgFileURL, imageArrayBuffer) } }) }, //图片智能鉴黄 imgSecCheck: function (imgFileURL, imageArrayBuffer) { console.log('开始imgSecCheck方法') wx.showLoading({ title: '图片安全检测中', //正在内容安全检测 }) //初始化云开发及设置其环境 wx.cloud.init({ env: app.globalData.env, //注意:我这里是调用app.js里设置好的云环境。你们可以改为自己的云环境 traceUser: true }) wx.cloud.callFunction({ // 要调用的云函数名称 name: 'imgSecCheck', // 传递给云函数的event参数 data: { imageArrayBuffer: imageArrayBuffer } }).then(res => { console.log('打印云函数imgSecCheck返回结果为') console.log(res) wx.hideLoading()//隐藏 loading 提示框 if (res.result.errMsg == "openapi.security.imgSecCheck:ok") { //内容正常。这里可以执行你需要的【方法】代码 } else{ //内容检测结果为不安全 this.showModal() } }).catch(err => { //错误 console.log('打印err结果为 错误') console.log(err) wx.hideLoading() //隐藏 loading 提示框 //内容检测结果为不安全 this.showModal() }) }, //内容检测结果为不安提示 showModal: function () { wx.showModal({ title: '图片内容违规', content: '通过腾讯图片智能鉴黄检测到你发布的内容,可能包含涉黄、涉暴、涉政等有害信息。为营造安全绿色的平台,我们坚决拒绝上传危害内容、言论。若你多次上传危害内容,系统将自动封号哦,并同步到网络安全部', confirmText: '知道了', confirmColor: '#000000', showCancel: false, success(res) { if (res.confirm) { console.log('用户点击知道了') } else if (res.cancel) { console.log('用户点击取消') } } }) }, 【云函数代码】 // 云函数入口文件 imgSecCheckPro const cloud = require('wx-server-sdk') // 云函数入口函数 exports.main = async (event, context) => { //初始化云函数 cloud.init({ env: event.env }) try { return await cloud.openapi.security.imgSecCheck({ media: { contentType: 'image/png', value: Buffer.from(event.imageArrayBuffer) } }) } catch (err) { // 错误处理 // err.errCode !== 0 } } ------------------------------------------------------------------------------------------ 复制以上代码。直接可以使用。 希望对各位开发者有帮助。加油!
2020-05-01 - wx.cloud.upLoadFIle 上传excel到云存储,IOS端报错,错误码400?
[图片][图片] 安卓端测试没问题,求解决,要疯掉了!!!
2022-05-25 - 「笔记」字节跳动小程序如何接入腾讯云CloudBase?
前言 最近在把微信小程序迁移至字节跳动小程序,由于服务端使用了腾讯云 CloudBase,网上搜索了一遍,文章千篇一律,都是复制腾讯云官方1年以前的适配器文档,在经过和腾讯云官方技术人员沟通后终于成功解决问题。 安装 npm i @cloudbase/js-sdk -S npm i @maoyan/cloudbase-adapter-tt_mp -S 使用 由于字节跳动小程序没有提供getAccountInfoSync()接口,无法通过接口获取appId 所以需要将appId设置到字节跳动小程序app对象上。 [代码]App({ onLaunch(options) { this.appId = appId } }) [代码] 腾讯云 CloudBase 安全配置 由于字节跳动小程序使用云开发不享受微信生态下的免鉴权,要在终端应用(如APP、小程序等)中使用云开发的身份验证服务,需要将授权的应用加入白名单,并在SDK使用时传入分配的凭证信息。 腾讯云 CloudBase 登陆授权 为了增加安全性,建议开启匿名登陆。启动匿名登录后,用户将不需要登录即可访问应用。如果有更严格的安全要求,可以自行开启其它身份验证方式。 完整代码 [代码]import tcb from '@cloudbase/js-sdk'; import { adapter } from '@maoyan/cloudbase-adapter-tt_mp'; let app; App({ onLaunch(options) { // appId必须设置 this.appId = "字节跳动小程序的appid"; tcb.useAdapters(adapter); // 腾讯云共享环境初始化 app = tcb.init({ env: '云环境id', appSign: '应用标识', // 需要设置成字节跳动小程序的appid appSecret: { appAccessKeyId: '版本', appAccessKey: '凭证' } }) // 匿名登陆 const auth = app.auth() const loginState = auth.anonymousAuthProvider().signIn() let data = await app.callFunction({ name: "云函数名", data: "参数" }); console.log(data) } }) [代码]
2022-03-03 - 全平台(Vue、React、微信小程序)任意角度旋转 图片裁剪组件
SimpleCrop全网唯一支持裁剪图片任意角度旋转、交互体验媲美原生客户端的全平台图片裁剪组件。 Github 地址:https://github.com/newbieYoung/Simple-Crop 特性及优势和目前流行的图片裁剪组件相比,其优势在于以下几点: 裁剪图片支持任意角度旋转;支持 Script 标签、微信小程序、React、Vue;支持移动和 PC 设备;支持边界判断、当裁剪框里出现空白时,图片自动吸附至完全填满裁剪框;移动端缩放以双指中心为基准点;交互体验媲美原生客户端。示例微信小程序示例[图片] 移动端示例[图片] 左侧是 IOS 系统相册中原生的图片裁剪功能,右侧为 SimpleCrop 移动端示例。 可以扫描二维码体验: [图片] 或者访问以下链接: https://newbieyoung.github.io/Simple-Crop/examples/test-2.html PC 示例[图片] 链接如下: https://newbieyoung.github.io/Simple-Crop/examples/test-1.html 安装npm install simple-crop 用法Script 用法微信小程序用法React 用法Vue 用法开源许可协议MIT License. 原理及实现[代码]全平台(Vue、React、微信小程序)任意角度旋转 图片裁剪组件[代码] https://newbieweb.lione.me/2019/05/16/simple-crop/
2020-03-04 - 微信小程序 -- 基于 movable-view 实现拖拽排序
微信小程序 – 基于 movable-view 实现拖拽排序 项目基于[代码]colorui[代码]样式组件 ColorUI组件库 (color-ui.com) 1.实现效果 [图片] 2. 设计思路 movable-view 绑定块移动事件的 块[代码]ID[代码] ,块移动的坐标 移动结束后触发[代码]moveEnd[代码]事件,根据[代码]Y[代码]坐标对对象数组进行排序 根据排序结果重置块位置 3.实现代码 代码已经进行了最简化处理 图中效果实现需引入[代码]colorui[代码]的[代码]main.wxss[代码]样式部分。 wxml [代码]<movable-area class="padding text-center bg-grey" style="width:100%;height:500px;" > <movable-view class="radius shadow bg-white" style="width:80%;height:80px;z-index:{{index==moveId?2:1}}" wx:for="{{tabList}}" wx:key="index" x="{{item.x}}" y="{{item.y}}" direction="all" bindchange="moveStatus" bindtouchend='moveEnd' data-moveid="{{index}}"> {{item.name}}</movable-view> </movable-area> [代码] js [代码]var compare = function (obj1, obj2) { var val1 = obj1.y; var val2 = obj2.y; if (val1 < val2) { return -1; } else if (val1 >= val2) { return 1; } else { return 0; } } Page({ /** * 页面的初始数据 */ data: { branchid:'', appdocid:'', tabList:[ { name:'十步杀一人' }, { name:'千里不留行' }, { name:'事了拂衣去' }, { name:'深藏身与名' } ], //移动的是哪个元素块 moveId:null, //最终停止的位置 endX:0, endY:0 }, initMove(){ let tabList = this.data.tabList; var tarr = [] tabList.forEach(function(ele,index){ let obj = ele obj.id = index obj.x = 30 obj.y = 100*index +20 tarr.push(obj) }) console.log(tarr) this.setData({ tabList:tarr }) }, moveEnd(e){ console.log(e) var that = this; that.setData({ ["tabList["+that.data.moveId+"].x"]:that.data.endX, ["tabList["+that.data.moveId+"].y"]:that.data.endY },()=>{ let tabList = this.data.tabList; tabList = tabList.sort(compare); that.setData({ tabList },()=>{ setTimeout(function(){ that.initMove(); },500) }) }) //计算位置 }, moveStatus(e){ // console.log(e) //移动的块ID var moveid = e.currentTarget.dataset.moveid; //最终坐标 let x = e.detail.x let y = e.detail.y this.setData({ moveId:moveid, endX:x, endY:y }) }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { this.initMove(); } }) [代码] 4.参考文档 movable-view | 微信开放文档 (qq.com)
2021-06-17 - 小程序纯前端将数据导出为excel表格
网上有许多文章是讲述小程序将数据导出为excel表格的,但大多需要经过请求服务端,再加上云存储。那一套逻辑之前做后端的时候就玩过了。很多时候,我们浏览页面时数据已经从服务端获取到本地了,直接将之导出即可,再走服务端,实为多此一举。为了减轻服务端压力,于是便有了这篇文章。本文章介绍如何在小程序使用纯前端技术将以获取到的数据导出为excel表格。文末有代码片段 xlsx插件文档 sheetjs插件文档 [代码]const XLSX = require('../utils/excel.js') Page({ data: { }, onLoad() { }, exportData() { // 数据源 const data = [{ code: 1, name: 'A', }, { code: 2, name: 'B', }, { code: 3, name: 'C', }, { code: 4, name: 'D', }] // 构建一个表的数据 let sheet = [] let title = ['序号', '姓名'] sheet.push(title) data.forEach(item => { let rowcontent = [] rowcontent.push(item.code) rowcontent.push(item.name) sheet.push(rowcontent) }) // XLSX插件使用 var ws = XLSX.utils.aoa_to_sheet(sheet); var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "用户表"); var fileData = XLSX.write(wb, { bookType: "xlsx", type: 'base64' }); let filePath = `${wx.env.USER_DATA_PATH}/用户表.xlsx` // 写文件 const fs = wx.getFileSystemManager() fs.writeFile({ filePath: filePath, data: fileData, encoding: 'base64', bookSST: true, success(res) { console.log(res) const sysInfo = wx.getSystemInfoSync() // 导出 if (sysInfo.platform.toLowerCase().indexOf('windows') >= 0) { // 电脑PC端导出 wx.saveFileToDisk({ filePath: filePath, success(res) { console.log(res) }, fail(res) { console.error(res) util.tips("导出失败") } }) } else { // 手机端导出 // 打开文档 wx.openDocument({ filePath: filePath, showMenu: true, success: function (res) { console.log('打开文档成功') }, fail: console.error }) } }, fail(res) { console.error(res) if (res.errMsg.indexOf('locked')) { wx.showModal({ title: '提示', content: '文档已打开,请先关闭', }) } } }) } }) [代码] 导出效果如下,如需高级设置请参照xlsx插件文档 sheetjs插件文档 [图片] 代码片段 -完-
2023-08-11 - 表格table示例
目的 实现允许编辑的【课程表】。 效果图[图片][图片] wxml代码<view class="mainPage"> <view class="hearderView"> <van-icon class="arrow" bindtap="goHome" name="notes-o" size="20" /> <text class="tipText" style="font-size: 12px;width: 85%;">滑动查看完整课表,长按可以编辑保存。</text> </view> <scroll-view scroll-x="true" scroll-y="true" style="height: {{validHeight-50}}px;"> <view class="table"> <view class="tr h"> <view class="th h1"></view> <view class="th" wx:for="{{[0,1,2,3,4,5,6]}}" wx:key="weekIndex" wx:for-item="weekItem">{{m1.getWeekday(weekItem)}}</view> </view> <block wx:for="{{kebiao.detail}}" wx:key="index" wx:for-item="item"> <view class="tr"> <view class="td c1"> <text>第</text><text>{{item.no+1}}</text><text>节</text> </view> <view class="td c" wx:for="{{item.ke}}" wx:key="index2" wx:for-item="item2" bindlongpress="longpress" data-no="{{item.no}}" data-day="{{index}}"> <text>{{item2.name}}</text> <text>{{item2.time}}</text> <text>{{item2.classroom}}</text> <text>{{item2.teacher}}</text> </view> </view> </block> </view> </scroll-view> <view class="botView" style="width: 100%;height: {{botHeight}}px;"></view> </view> <van-popup show="{{ show }}" position="bottom" custom-style="height: 230px;" bind:close="onClose"> <view class="popView"> <view class="popHeader"> <van-icon class="arrow" bindtap="onClose" name="close" size="20" color="Red" /> <text class="tipText">{{m1.getWeekday(currentKe.day)}} 第{{currentKe.no+1}}节</text> <van-icon class="arrow" bindtap="confirmEdit" name="passed" size="20" color="#1989fa" /> </view> <view class="kebiaoLine"> <text class="label"><text class="kebiaoNo" space="ensp">{{item.no}} </text>课程名</text> <input bindinput="inputText" id="name" value="{{currentKe.ke.name}}"></input> </view> <view class="kebiaoLine"> <text class="label">时间</text> <input bindinput="inputText" id="time" value="{{currentKe.ke.time}}"></input> </view> <view class="kebiaoLine"> <text class="label">教室</text> <input bindinput="inputText" id="classroom" value="{{currentKe.ke.classroom}}"></input> </view> <view class="kebiaoLine"> <text class="label">老师</text> <input bindinput="inputText" id="teacher" value="{{currentKe.ke.teacher}}"></input> </view> </view> </van-popup> <wxs module="m1"> function getWeekday(index) { switch (index) { case 0: return "周一" case 1: return "周二" case 2: return "周三" case 3: return "周四" case 4: return "周五" case 5: return "周六" case 6: return "周日" default: return "" } } module.exports.getWeekday = getWeekday </wxs> js代码data: { kebiao: { totalWeek: 16, currentWeek: 11, currentKe:{day:-1,no:-1,ke:{name:'',time:'',classroom:'',teacher:''}}, detail: [ { no:0, ke:[{name:"计算机组成与结构",time:"1-16周",classroom:"信工楼E536",teacher:"徐苏"}, {name:"",time:"",classroom:"",teacher:""}, {name:"计算机组成与结构",time:"1-16周",classroom:"信工楼E536",teacher:"徐苏"}, {name:"",time:"",classroom:"",teacher:""}, {name:"嵌入式系统",time:"1-16单周",classroom:"信工楼E536",teacher:"周聪"}, {name:"",time:"",classroom:"",teacher:""}, {name:"党课",time:"1-13单周",classroom:"党校B302",teacher:"张三"}] }, }, }, 使用Vant组件 van-popup、van-icon 体验小程序 主页-->小玩艺-->课表日历 [图片]
2021-11-25 - table表格组件,分享给各位
前言 移动端的页面本应该很少有table表格这样的展示、操作,但总归有这样的需求,然而平时用的vant和iview的小程序组件库都没有table组件,这里将自己编写的table组件展示一下供大家查看。 小程序实现table的问题在于,自定义td的实现,而小程序没办法像react一样使用[代码]jsx[代码],也没办法像vue一样用[代码]作用域插槽[代码]传row行的信息给slot,但是小程序还是留有一样东西可以完成自定义td的功能。 抽象节点 这个特性自小程序基础库版本 1.9.6 开始支持。 有时,自定义组件模板中的一些节点,其对应的自定义组件不是由自定义组件本身确定的,而是自定义组件的调用者确定的。这时可以把这个节点声明为“抽象节点”。 微信官方api地址 通过抽象节点我们可以做到使用自定义组件通过key值分发组件内容到不同的td里。 具体的源码地址可点击下方查看,如果对你有帮助请点个star~~ 源码地址 具体的实现效果可以扫描下方小程序码。 [图片] API prop 参数 说明 类型 默认值 是否必填 columns 表格的配置 Columns[] [] true dataList 数据 any[] [] true getListLoading 请求列表的loading boolean false true showTipImage 无数据时的提示文本图片 boolean false true rowKey 用于指明行的唯一标识符,在勾选中有使用 string id false scrollViewHeight 控制可滚动区域高度。 string 600rpx false tipTitle 无数据时的提示文本主标题 string 提示 false tipSubtitle 无数据时的提示文本副标题 string 暂无数据 false scrollX 是否需要X轴滚动。 boolean false false select 控制是否出现勾选。 boolean false false selectKeys 勾选的初始值 any[] [] false generic:action-td 当列表项内具有操作列,需要在[代码]columns[代码]内添加[代码]type:action[代码]的一项,操作列的内容往往需要自定义,小程序不提供react,vue的[代码]rander函数[代码],所以使用到了抽象节点,该属性指明抽象节点的组件。操作列位置可以不固定,点击事件由[代码]bindclickaction[代码]触发 component undefined false isExpand 控制是否点击展开。 boolean false false expandValueKey 展开信息的key值 string false initExpandValue 当展开信息为空时的默认提示语 string ‘暂无信息’ false expandStyle 展开信息的最外层的样式 string ‘’ false generic:expand-component 如果展开区域的内容需要自定义,[代码]expandValueKey[代码]设置为空字符串,则切换到组件模式,传一个组件进来,展开区域的点击事件由[代码]bindclickexpand[代码]触发 component undefined false dynamicValue 给自定义内容的动态值,用于改变状态 ,建议{value:放的数据} object {} false Events 事件 解释 类型 bindclicklistitem 点击列表行事件 Function(e); e.detail.value = {index:number(当前行序号),item: any(当前行的内容)} bindclickexpand 点击展开内容事件 Function(e); e.detail.value = {type:(这个按钮的含义字段,如‘close’),index:(当前的行),item:(当前行的数据)};(这是我这里定义的结构,具体可以自己定义在expand-component里)} bindclickaction 点击抽象节点事件 Function(e); e.detail.value = {type:(这个按钮的含义字段,如‘close’),index:(当前的行),item:(当前行的数据)};(这是我这里定义的结构,具体可以自己定义在action-td里)} bindcheckkey 勾选事件 返回被勾选项的rowKey数组 Function(e); e.detail.value = any[]//(数组内每一项是rowKey字段定义的数据的toString()结果) bindscrolltolower 滚动触底 Function() bindscrolltoupper 滚动触顶 Function() column 列描述数据对象,是 columns 中的一项,Column 使用相同的 API。 事件 解释 类型 必填 title 字段名中文含义 string true key 字段名 string true width 单元格宽度 string false type 判断字段是否是自定义组件 ‘action’/undefined false render td内内容由函数返回 (value: any, item: any, index: number, data?: 当前页面的this.data) => any,// 设置内容 function false
2022-11-24 - 滥用分享行为
违规内容 小程序提供的服务中,不得存在滥用分享违规行为。如强制用户分享行为;分享立即获得利益的诱导行为;通过明示或暗示的样式来达到诱导分享目的;以及蹭流量诱导分享获得红包封面的行为等。包括但不限于以下类型: ▶1 强制分享后才能继续下一步操作。包括但不限于分享后才可解锁功能或能力,分享后才可查阅、下载图片或视频等。 [图片] ▶2 以分享后无需互动或无需深度互动即可获得利益的方式诱导分享至群或好友。“深度互动“是指被分享者理解被分享内容并主动参与活动或业务流程,从而执行的进入页面、点击等一系列相关操作。“利益”包括但不限于:现金奖励、实物奖品、虚拟奖品(红包、优惠券、代金券、积分、话费、流量、信息等)。 2.1 无互动分享获利:诱导分享至群或好友,完成分享操作立即可获得续命机会/积分/金币等。 [图片] 2.2 无深度互动分享获利:诱导分享至群或好友,被分享者仅需访问小程序,分享者即可获得利益等。 [图片] ▶3 通过利益诱导分享至朋友圈,包括但不限于:现金奖励、实物奖品、虚拟奖品(红包、优惠券、代金券、积分、话费、流量、信息等)。 [图片] ▶4 客服消息推送外链内容或者微信公众帐号文章诱导分享至朋友圈。 [图片] ▶5 存在暗示性诱导分享内容,包括但不限于红点提醒等。 [图片] ▶6 虚拟业务小程序中存在被分享者可获利的诱导增加新用户行为。 [图片] ▶7 小程序中存在未经用户允许或授权,披露他人好友关系或用户隐私给陌生人关系(非微信好友关系)的诱导分享行为。 [图片] ▶8 小程序存在过度营销分享行为:如果同一主体、关联主体或者同一帐号、关联帐号或平台认为有联合行动的多个帐号下的小程序分享量过大且分享转化率过低,将会被认为可能存在过度营销分享行为,相关小程序的跳转至APP的能力、分享能力等将会被自动限制,同时小程序关联的微信开放平台开发者帐号分享小程序能力也将被自动限制。 [图片] ▶9 小程序分享活动未清晰明确活动规则,存在误导用户的行为。包括但不限于以下类型: 9.1 小程序内分享活动未明确告知用户活动完整信息,包括不限于整体规则、活动步骤和互动利益结果。在小程序分享活动流程中以概率事件或者不确定的获利结果诱导用户参与。 [图片] 9.2 小程序内分享活动存在标题党式信息进行夸大误导宣传,无法保证活动信息的真实性和准确性。 [图片] 9.3 小程序内分享活动相关信息未在页面显著标识,或使用小微字体、灰色字体等让用户难以注意、识别等方式影响用户获取信息和阅读的行为。 9.4 小程序内分享活动收益和最终收益要求用户长期参与才可获取,不满足即时性要求。 [图片] ▶10 小程序内组队组团类分享活动的参与总人数未限制在5人内。 [图片] 处理规则 一经发现将根据违规程度对该小程序阶梯封禁分享能力或朋友圈二维码识别能力直至封号处理。
2021-12-13 - 微信小程序如何推广?为此我阅读了同类型百篇文章汇总整理而来
为了写这篇文章,真是煞费苦心,潜心研究了圈内80%文章,再结合自己的经验,写了今天这篇文章,希望对小伙伴有滴点帮助。 首先介绍下自己,开发、运营小程序经验5年+ 搭建过各种各样的流量主小程序,初步统计1000+ 小程序如何推广+运营也是100%的运营者问过的话题。 所以也有必要整理一下所有的运营干货。 要想六脉神剑独步武林,必先检查任督二脉 一、小程序的6“脉”神剑要弄懂小程序如何推广,前提是要了解用户都可以从哪些途径进入小程序,然后在这些入口做引导就可以了。主要就是以下这个6个方面,学懂这本武功秘籍,一招鲜吃遍天。 1、搜一搜 2、扫描二维码、搜名字 3、群聊、单聊、朋友圈 4、其他小程序跳转、公众号跳转 5、URL Link(也就是H5外链、仅限企业小程序) 6、发现栏小程序主入口,「最近使用」列表 二、必先打开“任督二脉”这里说的“任督二脉” 1脉是指自查小程序业务、内容不违规 2脉是指自查流量主广告不违规 因为二者触其一,轻则重新提交代码审核,严重者直接封号处理。 第一点,就是小程序本身是否存在明显的违规,需要从以下方面自查。服务内容或者产品是否在微信允许范围内? ---比如你是做影视类小程序,那就要做好随时被封号的准备。 因为个人主体小程序不支持在线播放视频。 ---比如你是做打卡类小程序,也要做好被封的心里准备。 因为没有实际运营内容。 ---不再一一列举 [图片] [图片] 规则千万条,安全第一条,内容不规范,封号两行泪一旦被封,回到解放前,哭都没地方找。 第二点,就是流量主广告位设置是否合理,躲得过初一,过不了十五。常见的有以下几点: 1是激励视频广告,没有提示要看视频广告,没有免费的次数。 2是单屏内出现多个广告位。 3是开通流量主存在刷粉行为。 4是存在不正当刷量、曝光等行为。 [图片]违法规范,永久关闭 [图片]开通流量主通过刷粉行为 [图片]不正当方式制作虚假或无效曝光量、点击量 如果你有经历过以上的方式,尽早放弃,或者重新注册,如果你非要做,只能在中午做,因为早晚会出事。 如果你坚持看到了这里,那我们才算正式步入今天的主题。三、六脉神剑第一式---搜一搜搜一搜包含2个方向: 1、名称、简介核心关键词 2、内容关键词推送和页面收录 1、第一点比较容易理解,也就是小程序名字带内容关键词。比如你是做去水印,那不论怎么改名字,这三个字不能丢,这叫核心关键词 还有长尾关键词,去水印是关键词,那么视频去水印就是长尾词 数据来源于5118官网 [图片] 假设你去搜索“外卖”,那名字里有外卖两个关键词的肯定会出现。 [图片] 那如果你搜“外卖券”,这就是长尾词或者说是下拉词,搜索结果又不一样。 [图片] 小程序的名字是谁先注册谁先使用,所以在一波热搜词起来的同时,肯定会有人去抢占这些关键词,因为都知道,抢占了先机,也就把握了流量。 其实这里还有一点技巧,就算你想要的名字被人注册了,你可以在后面或者前面加数字或者字母也是可以的。但是不能搞品牌词。比如外卖券,外卖券儿,外卖券max,外卖券1688,等等 2、小程序页面收录、服务收录这是一个类似seo的玩法,简单理解就是让微信收录我们的小程序相关页面,在用户搜到相关关键词的时候,展示我们的小程序。 [图片]一般情况下,我们的小程序都是默认开启的 只要开启了该功能,小程序每一个页面都能被直接搜索到,比如搜索“计算器”,在内容一栏中就会展示页面中含有“计算器”的小程序,点击之后直达该页面。 [图片] 这个功能看起来似曾相识,其实就跟百度的搜索类似。当我们打开百度网页,搜索框里输入某个关键词,结果页就会展现出某个网站中含有该关键词的页面,点击就直达该页面,这就是页面收录功能的基本形式。 微信小程序页面收录有2种途径1是让微信爬虫自己来爬--无需操作,基本都是默认开启的[图片] 2是主动推送页面--这种需要在服务后端配置[图片] [图片]当你数据库足够多的时候,大数据的搜索下,必有展示机会 这就是玩转自然搜索流量的核心[图片] 服务搜索,也就是企业小程序申请的服务。[图片] [图片]微信搜一搜服务搜索,需要企业小程序且注册满6个月 四、六脉神剑第二式---扫描二维码、搜名字这种方式主要讲站外留贴的方式,微信平台的留在下节介绍 把上一节的搜一搜结合起来介绍。 1、通过论坛等自媒体平台留贴的方式,留下二维码、小程序名字。 2、伪装群二维码。 最常见的方式 1、贴吧、闲鱼、小红书、知乎等自媒体平台,发帖或者顶帖。 [图片]贴吧为例,基本要么留小程序二维码或者群二维码或者小程序名字 这类的好处就是只要帖子不删,你的内容带了关键词,基本就是持续引流。 当然,贴吧现在质量严重下滑,顶帖,私信等手段数不胜数。 [图片]闲鱼 在闲鱼的玩法一般是卖源码或者搭建服务 一般作为买家肯定要看案例的,所以有以下几种方式: 1、直接在帖子明细处,写明小程序的名字,直接搜“XXX”演示 2、自动回复,演示版直接搜“XXX” [图片]今日头条、短视频抖音、快手同理 这类一般是把资料整理好,做成文章或者短视频。 资源类型的一般是直接备注、留言、个人介绍、自动回复,搜索“xxx”小程序直接获取资源 还有一种是纯介绍、推荐模式,把自己小程序做成视频或者文章推荐形式。 例如: 自媒体人必备的几款小程序推荐 这几款小程序没用过你就out了 必须收藏的几款宝藏小程序~~~ 五、六脉神剑第三式---群聊、单聊、朋友圈这种模式虽然高效,但是容易被微信风控,所以用自己大号要谨慎,尽量用小号。 常规操作就不介绍了,讲一点干货或者说是套路。 怎么找群、多加群就不介绍了。 招式1、单刀直入型也就是直接转发小程序内容、或者海报、或者自己群链接。 举例: 这里海量ppt模板免费随便商用 这里领券,0元点外卖 这款爆粉小程序不收费单日被加50人 招式2、曲线救国型别人群始终有被踢和举报的风险,把人拉倒自己的群。 可以通过伪装自己群二维码或者链接 实操: 自己把这个群的二维码保存,替换成自己的群二维码 招式3、抛砖引玉型1、朋友圈小程序链接、或者海报--分享免费资源、福利。 2、群聊发红包,抛砖引玉 3、转发、群发任务找群主领红包、领资源 这节不做过多介绍,相信你的圈里必有微商,广告群也不少,最好的学习方法就是看看同行怎么做的,复制过来就行了。 五、六脉神剑第四式---小程序跳转、公众号跳转主要介绍2种玩法 1、其他小程序导量、买量跳转 2、公众号配合菜单栏、文章跳转 这里涉及到一个付费方式,也就是买量,从比人的小程序里面导量进入你的小程序,这种方式比较常见也是比较高效的,有舍才有得。一般是按照uv访客数量计算,1000访客收多少,500访客收多少,常规报价在0.2元/uv 简单理解也就是一个真实用户0.2元,至于留存和广告收益,短期内不要报太大幻想。 说道这里也将一点题外话,就是流量主一般是怎么开通的,小程序如何通过导量变现 常见的某宝搜索相关服务,价格有高有低,但是几十块钱就可以开通的基本上都是通过机刷的方式开通流量主,也就是大量的僵尸号微信,通过群控的方式直接扫码小程序二维码这种方式是最常见的封号的操作,但是很多小白根不不懂,只为图便宜,所以被封号。 还有一种通过悬赏任务的方式,但是价格比较高,效率也比较低这种操作的主要是刚入门的小程序转售者使用的方法。 导量,通过日活比较高的小程序导量给其他小程序这种是比较高效和稳定的操作。 所以也就滋生了买量、卖量、导量的操作,当你的小程序流量增长上来,你就可以接一些导量的广告了,或者做一些cpa的广告。相比流量主收益,这个收益更直接。 这里有必要讲下小程序的矩阵操作,也就是1+N矩阵变现,你推广+运营肯定是从1起步,当你的1有成效的时候,就可以导量到其他小程序,可以是自己的,也可以是卖量。 [图片]跳转到其他小程序 公众号的方式跳转1、是公众号底部菜单栏,直接跳转到小程序(前提是公众号与小程序关联) 入口在公众号后台,小程序管理 [图片] 2、关注公众号自动回复 一般是回复小程序名字、或者小程序二维码 [图片] 3、文章穿插小程序链接 订阅号每天可以发文8篇,认证号每个月发文4次*8篇 都是介绍和推广小程序的机会 [图片] 4、视频号发视频添加公众号链接 由于视频号不能直接添加小程序链接,可以把某一篇文章做成小程序介绍,添加小程序链接。 用公众号实现一个中转的过程。 六、六脉神剑第五式---URL Link(也就是H5外链、仅限企业小程序)这种方式有点局限,但是也加进来,其实这个也是微信官方的动作。 小程序不局限于在微信里面才能打开,有了这个功能以后 你可以在APP、在网页、短信、里面打开小程序 也是变相的增加了其他渠道进入小程序的方法 方法也很简单,前提是企业小程序,在小程序后台 [图片]mp后台--工具--生成URL [图片]可以指定页面路径参数,留空默认进入首页 这个功能的强大之处在于可以在短信里面打开,商城小程序在短信营销的时候,就可以直接引导用户进入小程序 七、六脉神剑第六式---发现栏小程序主入口,「最近使用」列表其实把这个方法加进来是考虑了良久 出发点开发了一个小程序,经过一段时间的观察,发现访问人数和添加到我的小程序的数据差异比较大,我想可能存在2种可能: 用户不清楚有添加到我的小程序功能本身产品做的不够好,不愿意添加[图片] 结果上线后,发现添加人数相比之前是倍数增长,这也就验证了产品本身其实没有什么大问题,大多数用户是不知道这个功能,或者说用户的行为需要我们去小小的引导一下,就能产生意想不到的收获。 这个的好处就在于,方便用户的后续访问,直接在常用访问栏就可以找到我们的小程序,不用重新搜索,有时候根本不记得名字了。 这个主要用于用户的二次留存和日活。 以上就是耗时一个下午整理的文档,没办法做到一一枚举。
2021-06-21 - 视频号重磅更新,可以直接跳转小程序了。
近期,视频号重大更新,不用自定义交易组件也可以跳转小程序了。视频号的流量将全面导入小程序。 视频号推出了服务菜单功能,只要是认证的企业号,可以添加客服和小程序任意页面。一共可以添加6个。 需要注意的是,小程序页面路径一定要加上后缀【.html】,不然可能打开就是未知页面了。 [图片] [图片]
2022-03-02