- 最新版人脸识别小程序 图片识别 生成二维码签到 地图上选点进行位置签到 计算签到距离 课程会议活动打卡日常考勤 上课签到
技术选型 1,前端 小程序原生MINA框架 css JavaScript Wxml 2,管理后台 云开发Cms内容管理系统 web网页 3,数据后台 小程序云开发 云函数 云开发数据库(基于MongoDB) 云存储 4,人脸识别算法 基于百度智能云实现人脸识别 一,用户端效果图预览 老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。 1-1,登录注册页 [图片] 可以看到登录页有注册入口,注册页如下 [图片] 我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序 [图片] 1-2,个人中心页 登录成功以后,我们会进入个人中心页 [图片] 我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进而实现签到效果。 [图片] 我们拍照注册人脸前,会有一个提示,让我们详细核实自己的资料,核实无误后,就可以实现人脸注册。 [图片] 我们人脸注册成功以后,后面做人脸识别签到就可以轻松实现了。如下图,就是我们注册人脸图片到百度服务器。因为我们这里用的是百度的人脸识别,所以要把人脸图片注册到百度的可视化脸库里。 [图片] [图片] 注册好以后,我们就来看看人脸识别的效果图。 1-3,人脸签到效果图 如我们添加了一个需要人脸识别实现签到的活动或者课程或者会议 [图片] 点击后进入拍照界面 [图片] 我们做人脸识别签到,其实就是要拿识别的人脸和数据库里的人脸对比,相识度大于一定的值,就可以判定成功。如下是我们程序人脸识别的部分代码。 [图片] 可以看到如我们识别的结果是98.295%相似度,所以这里就可以认定为人脸识别成功 签到成功后,页面就会变成下面这样。 [图片] 1-4,二维码签到效果图 我们这进行二维码签到之前需要管理员根据不同的活动或者课程,生成签到二维码。 [图片] 点击生成如下 [图片] 然后用户就可以扫码进行签到了,签到成功后如下 [图片] 1-5,位置打卡签到 同样还是先由管理员生成一个位置,其实就是在地图上选择一个位置,然后拿到经纬度,进而生成位置签到的坐标点。 [图片] 我们这里可以在地图上搜索位置。 [图片] 管理员选择好签到位置后如下 [图片] 然后用户就可以查看可以进行位置签到的活动 [图片] 进入签到页面 [图片] 进行位置签到,会判断当前位置距离签到位置的距离。距离过远就没法签到打开 [图片] 位置在范围内就可以签到 [图片] 签到成功以后如下,可以看到签到成功以后,按钮就不能再点击了。 [图片] 1-6,活动相关效果图 活动列表 [图片] 活动详情 [图片] 搜索活动 [图片] 已签到人员 [图片] 签到过的人员 [图片] 用户收藏的活动 [图片] 用户签到过的活动 [图片] 1-7,签到的几种类型 [图片] 前三个基本上都给大家演示过了,我们接下来看每日考勤。我们这里主要以课程和会议签到为例 1-8,课程考勤签到 首先看下有那些课程可以签到 [图片] 同样也可以看到自己已经签到过那些课程。 [图片] 可以进入某个课程,查看课程签到情况 [图片] 可以看出,我们不仅可以进行位置距离的签到,还需要学生输入正确的考勤码。 [图片] 如果学生输入错误,就会有提示 [图片] 并且老师或者负责考勤的管理员可以设置开始或者结束签到 [图片] 结束签到 [图片] 这样就可以很方便的实现日常上课考勤,公司上下班考勤,会议签到考勤。 1-9,签到提醒 我们在签到时,会提示用户签到结束时间 [图片] 如果签到时间已过,就无法进入签到页了 [图片] 二,管理员端效果图 2-1,管理员页 [图片] 可以看到我们的管理员可以审核用户,生成二维码和位置签到,可以开启课程或者日常考勤签到。也可以查看人员签到详情 [图片] 还可以看学生或者员工的考勤状况 [图片] [图片] 2-2,cms网页管理后台 我们可以在管理后台添加活动,添加小程序端管理员 [图片] 也可添加课程和会议 [图片] 也可以查看考勤情况 [图片] 2-3,数据库和数据表 我们这里会用到如下几个数据表 [图片] 2-4,百度管理后台 我们其实使用百度的管理后台,主要就是为了使用百度的人脸识别功能。 [图片] 比如可以查看注册的人脸 [图片] 当然我们使用百度的技术,就要去开通百度开发者账号。所以下一节的准备工作要认真看。 三,准备工作(重要) 3-1,注册百度开发者账号 我们这里使用了百度的图片识别技术,所以在使用之前我们要现在注册百度开发者账号,官方地址:https://ai.baidu.com 注册地址:https://login.bce.baidu.com 至于如何注册,这里不需要我再教了吧,大家自行注册就行了。 我们主要用到的是人脸识别技术 [图片] [图片] 3-2,注册完记得要实名下 现在使用百度图片识别,必须要实名认证下。 [图片] 根据自己的情况选择认证方式 [图片] 学习的话,只需要个人认证即可。 [图片] 3-3,创建人脸识别应用 我们这里主要使用的是百度的人脸对比功能,就是事先把要识别人的照片存到百度数据库,然后打卡时,让对应的人拍好照,和数据库里存的人脸图片进行对比,相似度达到一定数值,比如90%相似度,就可以认为识别成功,进行签到即可。 [图片] 估计百度是在为后期收费做准备,但是目前还有免费资源可以领取。既然可以白piao就先领取免费的。即便后期收费了,咱们学习使用估计也用不了太多,几毛钱的估计就够咱们学习用的了。 人脸识别官方文档:https://cloud.baidu.com/product/face 点击免费领取资源 [图片] 既然免费,当然全部领取了啊。 [图片] 领取完,耐心等待生效即可。如果你在学习的时候,不能在免费领取了,那就花几毛钱付费下也行的,基本上几毛钱就够咱们学习使用了。 [图片] 上面该领的都领完以后,我们就来创建应用吧。 [图片] 默认人脸识别相关的都已经勾选了 [图片] 选个人即可。 [图片] 创建好以后,下面两个东西我们后面会用到,知道在哪里即可。 [图片] 3-4,添加百度域名到小程序 我们这里要调用百度的人脸识别接口,所以需要配置域名到小程序,如果不配置的话,就会报如下错误。 [图片] 所以需要到小程序后台,把这个https://aip.baidubce.com 添加到如下位置。 [图片] [图片] 点击上面的服务设置,然后做如下设置。 [图片] 一般设置到10分钟左右生效。 四,接入人脸识别(重要) 4-1,官方文档 我们在自己的小程序里接入人脸识别,就必须去看百度官方的技术文档。 文档地址:https://ai.baidu.com/ai-doc/FACE/Lk37c1tpf 我们这里主要就看这个人脸对比文档。 [图片] 官方文档里并没有给出小程序里如何调用的代码。 [图片] 所以接下来的学习要跟紧石头哥的脚步,石头哥手把手的带大家实现小程序端的调用。 4-2,人脸注册 我们要想实现人脸识别,就需要一开始先在百度的可视化人脸库里注册人脸,要调用的接口如下。 [图片] [图片] 在调用这个之前,我们需要先去获取对应的acess_token,所以接下来我们要做的第一步就是获取acess_token 4-2-1,获取acess_token 我们后面做的所有操作,基本上都要获取这个。 [图片] 所以我把源码贴出来给到大家,client_id和client_secret记得换成你自己的。 [代码]wx.request({ url: 'https://aip.baidubce.com/oauth/2.0/token', data: { grant_type: 'client_credentials', client_id:, //应用的API Key client_secret: //应用的Secret Key }, header: { 'Content-Type': 'application/json' // 默认值 }, success: res => { this.setData({ token: res.data.access_token //获取到token }) console.log('获取到的token', this.data.token) } }) [代码] 4-2-2,拍人脸照 第二步和第三步要同时进行,所以我把代码放到下面第三步 3-2-3,注册人脸到百度人脸库 我们在拍照以后,获取到图片,并通过 wx.getFileSystemManager().readFile()方法把图片转换为base64,因为百度需要这样格式的数据 [图片] 对应的代码如下: [代码] var that = this; //拍照 const ctx = wx.createCameraContext() ctx.takePhoto({ quality: 'high', success: (res) => { that.setData({ src: res.tempImagePath //获取图片 }) //图片base64编码 wx.getFileSystemManager().readFile({ filePath: that.data.src, //选择图片返回的相对路径 encoding: 'base64', //编码格式 success: res => { //成功的回调 that.setData({ base64: res.data }) //第三步:上传人脸进行注册 wx.request({ url: 'https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add?access_token=' + that.data.token, method: 'POST', data: { image: that.data.base64, image_type: 'BASE64', group_id: 'users', //自己建的用户组id user_id: app.globalData.userInfo.phone, //学号 user_info: app.globalData.userInfo.name //存储学生姓名 }, header: { 'Content-Type': 'application/json' // 默认值 }, success(res) { that.setData({ msg: res.data.error_msg }) console.log("人脸注册返回结果", res) //做成功判断 if (that.data.msg == 'SUCCESS') { //微信js字符串使用单引号 wx.showToast({ title: '注册成功', icon: 'success', duration: 2000 }) // that.registerFace() } } }), //失败尝试 wx.showToast({ title: '请重试', icon: 'loading', duration: 500 }) } }) } //拍照成功结束 }) //调用相机结束 [代码] 这几个参数,要记得换成自己的 [代码] group_id: 'users', //自己建的用户组id user_id: app.globalData.userInfo.phone, //学号或者用户电话能唯一标识用户的 user_info: app.globalData.userInfo.name //存储用户姓名 [代码] 我们注册完以后,可以在百度人脸库里看到这条数据,可以看到我们创建的users表。 [图片] 4-3,查看人脸库 我们进入百度的控制台,找到如下的类目 [图片] 然后点开用户123,就可以看到他注册的人脸照片。 [图片] 五,人脸比对和二维码生成核心代码 这里我摘抄部分代码,具体的详细代码需要大家拿米去换完成的代码 [图片] 我们上面注册好人脸以后,接下来就可以使用人脸打卡功能了。使用之前还是第一步,获取acess_token 5-1,获取acess_token [代码] // acess_token获取 getTokenInfo() { var that = this wx.request({ url: 'https://aip.baidubce.com/oauth/2.0/token', data: { grant_type: 'client_credentials', client_id: app.globalData.client_id, //应用的API Key client_secret: app.globalData.client_secret //Secret Key }, header: { 'Content-Type': 'application/json' // 默认值 }, success(res) { that.setData({ token: res.data.access_token //获取到token }) console.log(that.data.token) } }) }, [代码] 5-2,人脸比对 核心代码如下: [代码] //拍照并编码 takePhoto() { let that=this const ctx = wx.createCameraContext() ctx.takePhoto({ quality: 'high', success: (res) => { //图片base64编码 wx.getFileSystemManager().readFile({ filePath: res.tempImagePath, //选择图片返回的相对路径 encoding: 'base64', //编码格式 success: res => { //成功的回调 that.signInFace(res.data) } }) } }) }, //上传人脸进行 比对 signInFace(base64) { var that = this if (base64 != "") { wx.request({ url: 'https://aip.baidubce.com/rest/2.0/face/v3/search?access_token=' + that.data.token, method: 'POST', data: { image: base64, image_type: 'BASE64', group_id_list: 'users' //自己建的用户组id }, header: { 'Content-Type': 'application/json' // 默认值 }, success(res) { console.log("人脸对比返回结果", res) if (res.data.error_msg == "match user is not found") { wx.showModal({ title: '签到失败', content: '请先注册人脸才可以人脸使用', }) } if (res.data.error_msg == "SUCCESS") { that.setData({ msg: res.data.result.user_list[0].score, }) // console.log(res) if (that.data.msg > 80) { //相似度大于80 console.log('人脸识别成功') } else { wx.showToast({ title: '人脸识别失败', }) } } else { wx.showToast({ title: '人脸识别失败', }) } } }); } if (base64 == "") { wx.showToast({ title: '请重试', icon: 'loading', duration: 500 }) } }, [代码] 我们执行代码以后,会返回一个相识度,我这里规定相识度80%以上即为同一个人。 [图片] 看日志可以知道我们的相识度是98.295%,所以这里就可以认定为签到成功 5-3,小程序里生成二维码的核心功能包 [图片] 六,源码下载与部署 我这里先教大家如何导入源码和运行项目,然后在后面的章节里再慢慢的手把手教大家开发这个程序。我源码会在配套资料里给到fufei用户,年卡用户也可以获取到 6-1,源码的下载 进入网盘后,点击进入源码目录,下载最新版的源码即可。 [图片] 6-2,源码的导入 导入源码的时候一定要把appid换成你自己的。appid需要注册小程序才有的,所以学习这门课之前建议你先去看下我云开发基础课:《零基础人入门小程序云开发》 [图片] 如果之前已经开通过云开发,直接点确定即可 [图片] appid获取的位置如下图,需要你先去注册一个非测试版的小程序才可以获取appid。 [图片] 有些同学导入源码时会多一层,我会在视频里教大家如何避免这样的问题。有的同学导入时,可能会出现如下弹窗。直接点击信任并运行即可。[图片] 6-3,云开发的初始化 初始化云开发之前,必须先开通云开发。现在云开发会免费送一个月的试用。只要在有效期内,就放心使用即可。过期了记得点击下续费。费用也就几瓶可乐。和学到的知识比起来,就微不足道了。 [图片] 如果你在开通云开发的时候遇到问题,可以先去看下我云开发基础课:《零基础人入门小程序云开发》 6-3-1,初始化云开发环境id 点击云开发,进入云开发控制台。如果没有这个图标,说明你上面导入源码时用的不是自己的appid。所以一定要用自己的appid [图片] 获取环境id [图片] 把环境id复制到app.js里,把下面部分替换成你自己的环境id [图片] 6-3-2,云函数选择环境 这里要注意,选择的环境,必须和你app.js里填入的环境id保持一致。 [图片] 有的同学第一次运行的时候选择云开发环境为空。这个时候记得多关闭重启几次开发者工具即可。 [图片] 6-4,云函数的部署 cloud目录下的云函数都要部署一下 [图片] 把cloud下面的几个云函数都部署下 [图片] 部署好以后,文件前会有一个小云朵 [图片] 一定要记得cloud目录下的所有云函数都必须要部署一下。 七,开通Cms可视化网页管理后台 我们上面源码导入成功,并把云开发环境初始化成功以后,接下来就来开通cms可视化网页后台。现在开通cms内容管理之前要先开通云函数里的高级日志。这里按照提示开通即可。 7-1,进入云开发控制台开通内容管理(CMS) 如下图所示,直接点击开通内容管理(CMS)即可 [图片] 点击完开通以后,会有如下弹窗,直接点击确定即可。不要被付费吓着,官方会送我们一个月的免费使用。学习得话基本上够用了,如果用量超了,一瓶可乐钱基本上就够了。 [图片] 上面点完确定后,我们只是开启了按量付费功能,因为cms得使用必须要开通按量付费才可以得。所以还要再点一次开通。如下图 [图片] 点完开通后,会有如下弹窗,直接点击下一步即可。 [图片] 然后我们需要设置登录内容管理后台得账号和密码,然后点击确定即可 [图片] 然后我们就等待内容管理功能得开通了,需要等几分钟。 [图片] 开通成功以后,我们就可以通过下面这个地址进入管理后台了。 [图片] 如果想看cms网页后台的源码的话,上图的开源地址,就可以拿到cms网页后台源码。 7-2,登录Cms可视化管理后台 上面开通好以后,就可以通过后台地址登录管理后台了。如下 [图片] 7-3,创建项目 第一次登录,我们还需要创建一个项目 [图片] 自己输入项目名和项目id即可 [图片] 然后点击进入刚刚创建的项目就可以了,到这里我们的cms可视化网页管理后台就创建好了,下面教大家如何导入数据。 八,导入数据并修改数据库权限 8-1,在cms后台导入内容模型(数据表) [图片] 把我为大家提前准备好的 数据表.json文件导入即可。这里的数据表不是代码,而是数据表的备份,导入成功后就可以删除了。 [图片] 导入完以后,可以看到多了以下几个表。网页管理后台里的内容集合,就是我们的数据表。 [图片] 8-2,自己添加活动和管理员 需要自己在cms网页后台添加活动和管理员。 [图片] 8-3,修改表权限(重要) 我们需要把下面几个表的权限改为所有用户可读,仅创建者可读写。 [图片] 到这里我们整个项目就部署成功了。 如果点赞获取转发大于1000了,我会考虑抽时间把这门课的详细讲解录成视频。
2023-04-13 - 批量下载云存储中的文件
最近开始学习使用小程序和小程序的云开发功能,遇到了文件无法批量下载的问题,以下是个人摸索学习的一些经历,在此分享一下 本次为测试环境下的一次尝试,假设小程序已经开通了云开发功能(不开如何使用云存储- -|||) 使用的是官方的云开发默认模板创建的项目,GO! 先往云存储中上传了一些文件,文件结构如下 |-- folder1 |-- file1.txt |-- file2.txt |-- file3.txt |-- folder2 |-- file4.txt |-- file5.txt |-- folder3 |-- file6.txt [图片] 在'cloudfunctions'下新建一个Node.js云函数,在这里就随意了,实际可根据具体的需要来命名 [图片] 进入该函数所在的目录下启动终端,运行命令 // 1.先为了保险起见 npm install // 2.安装这个包,可参考 https://docs.cloudbase.net/api-reference/manager/node/introduction npm install @cloudbase/manager-node 安装完毕后在入口文件开始撸代码 const CloudBase = require('@cloudbase/manager-node') const { storage } = new CloudBase({ envId: "xxxxxx", // 云开发环境ID,进入云开发的时候可以从右上角进行复制 }); // 获取云存储的所有文件信息 async function listDirectoryFiles(params) { const { path } = params // 接口功能:列出文件夹下的所有文件 // 接口声明:listDirectoryFiles(cloudPath: string): Promise<IListFileInfo[]> return await storage.listDirectoryFiles(path); } // 获取云存储文件列表的临时下载链接 async function getTemporaryUrl(params) { const { fileList } = params let newFileList = fileList.filter(file => file.Size !== '0').map(file => ({ cloudPath: file.Key, maxAge: 86400 // 临时下载链接有效时间,单位:秒 })) // 接口功能:获取文件临时下载链接 // 接口声明:getTemporaryUrl(fileList: (string | TempUrlInfo)[]):Promise<Array.<FileUrlItem>> return await storage.getTemporaryUrl(newFileList); } // 云函数入口函数 exports.main = async (event, context) => { const { functionName, params } = event if (functionName === 'listDirectoryFiles') { return await listDirectoryFiles(params) } else if (functionName === 'getTemporaryUrl') { return await getTemporaryUrl(params) } } 为了调用这些代码,需要上传该云函数(或者在本地测试也可以),然后撸个测试页面 <!--pages/test/test.wxml--> <button type="primary" bindtap="getFileUrlList">获取文件url列表</button> // pages/test/test.js Page({ /** * 页面的初始数据 */ data: { }, getFileUrlList() { // 先调用获取文件列表的云函数 wx.cloud.callFunction({ name: 'cloudManagerFunctions', data: { functionName: 'listDirectoryFiles', params: { path: '/' } } }).then(res => { const { result } = res // 处理该文件列表,调用获取下载链接的云函数 return wx.cloud.callFunction({ name: 'cloudManagerFunctions', data: { functionName: 'getTemporaryUrl', params: { fileList: result } } }) }).then(res => { const { result } = res console.log(result) }) }, //......先忽略生命周期函数 }) 然后点击Button触发事件,可以看到控制台打印了一条消息 [图片] 好家伙,连fileId都给我了,不过有这个url其实已经可以下载了(有效期就是代码中的maxAge),具体的操作就由自己喜欢的方式来咯!在这里就不占用大家时间了 希望这篇文章能起到一点小小的作用
2022-02-10 - 微信小程序 -- 基于 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 - 微信小程序开发技巧总结(二) -- 文件的选取、移动、上传和下载
微信小程序开发技巧总结(二) – 文件的选取、移动、上传和下载 1.不同类型文件的选取 1.1 常用的图片 视频 对于大部分开发者来说,需要上传的文件形式主要为图片,微信为此提供了接口。 [代码]wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success (res) { // tempFilePath可以作为img标签的src属性显示图片 const tempFilePaths = res.tempFilePaths } }) [代码] 其次为视频文件的选取,微信也为此提供了接口。 [代码]wx.chooseVideo({ sourceType: ['album','camera'], maxDuration: 60, //视频长度 单位 s camera: 'back', //选取前置 摄像 还是 后置 摄像 success(res) { console.log(res.tempFilePath) } }) [代码] 也为其提供了视频和图片的二合一接口,这个接口不建议调用,图片和视频的上传建议区分开。 [代码]wx.chooseMedia({ count: 9, mediaType: ['image','video'], sourceType: ['album', 'camera'], maxDuration: 30, camera: 'back', success(res) { console.log(res.tempFilePath)con console.log(res.size) } }) [代码] 这几个接口调用成功的回调函数中,都返回的是文件在文件在本机中的路径。 [代码]res.tempFilePath [代码] 这是一个 数组,存放着选择的所有文件的路径,用于上传。 1.2 其余形式各种文档 那么如果是想要在小程序中上传 doc、pdf、ppt 等类型的文件怎么处理?首先要注意的是微信小程序并没有给用户提供文件管理器接口。 开发者:我想要个文件管理器接口! 官方:不,你不想 聪明的开发者他没有办法,只能另辟蹊径。微信提供了一个选择客户端会话文件的方法。 [代码]wx.chooseMessageFile({ success(res){ console.log(res.tempFilePath) } }) [代码] 与上面两个接口相同,返回的也是文件在本地的存储路径,但是不同的是,这个接口可以选取全部的文件类型。 开发者如果想要上传非图片和视频内容的话,需要两步走。 打开微信文件传输助手,将想要上传的文件发送到文件传输助手 在小程序中调用这个接口,选择文件传输助手,从会话中选择想要上传的文件。 [图片] [图片] 2.文件的上传 2.1 uploadFile方法 所有的文件都是以字节流的形式进行上传,所以上传形式并没有什么本质区别,都是调用相应的接口进行上传。 小程序端写法如下: [代码]wx.uploadFile({ url: '你的服务器函数地址', //仅为示例,非真实的接口地址 filePath: '需要上传的文件路径', //res.tempFilepaths name: 'file', // 文件对应的key ,默认 为 file formData: { 'user': 'test' }, //上传额外携带的参数 success (res){ const data = res.data //do something } }) [代码] 2.2 服务器端如何处理上传的文件 服务端如何接受文件的上传?仅展示Java方式(SpringBoot 框架) [代码]@Controller @ResponseBody public class FileController { //文件上传控制类,是核心配置类,Win <->Linux @RequestMapping(value = "/upload/images") public String uploadimages(HttpServletRequest request,@RequestParam("file") MultipartFile file, @RequestParam("user") String user) throws IOException { //更换服务器,这个值也需要修改 //图片上传写法 //type 是上传图片的类型 if(!file.isEmpty()){ //文件不为空 //String path = "E:"+File.separator+"images"+File.separator+type; // this is windows method String path = "/share"+File.separator+"images"+File.separator+type; // this is Linux method String filename = file.getOriginalFilename(); File filepath = new File(path,filename);//新建文件存储路径 System.out.println(filepath); if(!filepath.getParentFile().mkdirs()){ filepath.getParentFile().mkdirs(); } file.transferTo(new File(path+File.separator+filename)); //想要返回可直接访问的链接还要配置 映射,具体请看下面链接 return "你的IP地址"+"/images/"+type+"/"+filename; }else { return "error"; } } } [代码] 配置访问映射 文件访问映射 2.3 云开发文件上传 微信小程序支持云开发,其文件上传接口有一些差异,但是不需要自己再构建后台。 [代码]wx.cloud.uploadFile({ cloudPath: 'file/' + '你的文件名字(带格式后缀)', // 在云端存储的路径 filePath: '', // 就是选择文件返回的路径 }).then(res => { // get resource ID console.log(res.fileID) }).catch(error => { // handle error }) [代码] 我们可以根据返回的fileID 置换 真实的文件访问地址。 其置换方式参见官方文档: [代码]wx.cloud.getTempFileURL({ fileList: ['cloud://xxx', 'cloud://yyy'], success: res => { // get temp file URL console.log(res.fileList) }, fail: err => { // handle error } }) [代码] TIPS:关于云开发文件上传的建议 如果没有保密需求,为了方便后续开发,存储到数据库中的最好是可以直接访问的文件链接。 置换真实文件地址,不要每次上传一次文件就置换一次,先把返回的fileID 存放在数组中,到该事务所有上传完成后,再使用fileID 数组置换真实文件访问链接数组。 要考虑文件名重复的可能,建议使用时间戳在 wx.cloud.uploadFile 中的 cloudPath 中对存储到云环境中的文件命名进行格式化。 总的来说就是先上传文件,再向数据库中写入记录。 2.4 多文件同时上传的处理方式 uploadFile 每次只能上传一个文件 如何处理这个问题? A.不考虑文件的上传次序问题,可以采用遍历上传的方式,采用精确的时间戳和遍历index对文件名进行格式化。通过定时触发检测函数判断是否全部上传完成。这种方式考虑的是并发能力。 [代码]upSeveralfiles() { wx.showLoading({ title: '上传中~', mask: true }) var that = this; var timecode = sev.vcode(new Date()); // 这是时间戳编码函数 var files = this.data.fileList; // 这是 选择文件中返回的 res.tempFilePath 数组 var len = files.length; var i = 0; for (i = 0; i < len; i++) { var str = files[i].name; wx.cloud.uploadFile({ cloudPath: 'file/' + '(' + sev.getformatTime(new Date()) + ')' + str, filePath: files[i].path, success(res) { console.log(res) that.setData({ cloudlist: that.data.cloudlist.concat([res.fileID]), }) // cloudlist 是存放 文件链接置换id 的数组 , 非云开发存储的就是真实可访问的链接数组 // 如果使用的不是云开发 那么 可以返回真实的 访问地址 }, fail(res) { console.log(res) } }) } // 使用定时器检测文件是否全部上传完成 , 并 判断是否进行下一步 操作 var timer = setInterval(function () { if (that.data.cloudlist.length == len) { // 只有全部上传成功了 长度才会相等 clearInterval(timer); // 继续执行下一步 ,根据 cloudlist 置换真实地址 并存放到数据库 // 如果使用的非云开发,那么就继续执行 存储至数据库的操作 } }, 1000) } [代码] 补充文件编码函数 sev.js 中的根据时间编码部分 , 可以根据实际流量自定义。 [代码]function getformatTime(date) { var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var hour = date.getHours() var minute = date.getMinutes() var second = date.getSeconds() return [year, month, day].map(formatNumber).join('-'); }; [代码] B.考虑文件的上传次序问题,采用回调方式进行文件上传(更推荐使用这种方式) [代码]data: { fileList: [], realList: [],//云端地址链接列表 fileid: 0, }, upSeveralfiles() { var that = this; var files = this.data.fileList; // 这是 选择文件中返回的 res.tempFilePath 数组 var len = files.length; var uid = this.data.fileid; wx.uploadFile({ url: '你的服务器文件接收函数请求地址', name: 'file', filePath: files[uid], header: { "Content-Type": "multipart/form-data" }, success(res) { that.setData({ fileid: uid + 1, realList: that.data.realList.concat([res.data]) }, () => { if (that.data.fileid == len) { // 上传完成 ,执行下一步操作 } else { //上传完一个文件 递归执行 下次上传 that.upSeveralfiles(); } }) }, fail(res) { console.log(res.data) } }) }, [代码] 以上是提供的两种思路 , 无论是不是云开发 , 两种思路都是共通的,适用于多文件上传. 文件的下载 这个地方唯一值得注意的是云开发的一种下载方式 可以通过fileID进行download , 当然只能下载存储在自己云环境中的文件. [代码]wx.cloud.downloadFile({ fileID: '', success: res => { console.log(res.tempFilePath) }, fail: err => { } }) [代码] [代码]wx.downloadFile({ url: '', //仅为示例,并非真实的资源 success (res) { //res.tempFilePath }) [代码] 下载进度监控 , 用于下载进度条绘制等功能实现 [代码]const downloadTask = wx.cloud.downloadFile({ *** }) // wx.downloadFile 同理 downloadTask.onProgressUpdate((res) => { //res.progress 为下载进度 }) [代码] 下载下来的文件支持直接打开,就像是在微信聊天中打开一样,需要下载插件。 其使用方式为: [代码]wx.openDocument({ filePath: res.tempFilePath //为文件路径 非数组 , 可使用回调函数 success 等 }) [代码] 文件的移动 这个一般情况下是用不到的, 也不建议使用移动文件的方法作为功能实现手段 ,必然有更好的替代方式,比如修改数据库路径 和 真实文件路径的映射, 效率更高一些. 这里仅讲云开发移动文件方式。 [代码]const cloud = require('wx-server-sdk') const fs = require('fs') const path = require('path') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async (event, context) => { const fileStream = fs.createReadStream(path.join(__dirname, 'demo.jpg')) //这个可以读取 云存储 中的文件 , 所谓删除 就是复制 然后 删除原位置文件 return await cloud.uploadFile({ cloudPath: 'demo.jpg', fileContent: fileStream, }) } [代码] 具体开发文档 建议在数据库中修改映射路径最好。 需要小程序开发的请联系我QQ : 1025584691
2020-02-17 - 免费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 - 只有小程序,没有域名和网站,如何办理《非经营性互联网信息服务备案核准》?
你好。前段时间,我小程序上传了养生类文章、视频课件,审核时不通过,要求办理《非经营性互联网信息服务备案核准》,我问信管局,他们说:小程序不用办理备案,而且小程序没有域名和网站,也没办法办理备案。请问我这种情况,小程序要想上传了养生类文章、视频课件,应该如何办理备案才能通过审核。谢谢
2019-12-18 - 小程序导出数据到excel表,借助云开发后台实现excel数据的保存
我们在做小程序开发的过程中,可能会有这样的需求,就是把我们云数据库里的数据批量导出到excel表里。如果直接在小程序里写是实现不了的,所以我们要借助小程序的云开发功能了。这里需要用到云函数,云存储和云数据库。可以说通过这一个例子,把我们微信小程序云开发相关的知识都用到了。 老规矩,先看效果图 [图片] 上图就是我们保存用户数据到excel生成的excel文件。 实现思路 1,创建云函数 2,在云函数里读取云数据库里的数据 3,安装node-xlsx类库(node类库) 4,把云数据库里读取到的数据存到excel里 5,把excel存到云存储里并返回对应的云文件地址 6,通过云文件地址下载excel文件 一,创建excel云函数 关于云函数的创建,我这里不多说了。如果你连云函数的创建都不知道,建议你去小程序云开发官方文档去看看。或者看下我录制的云开发入门的视频:https://edu.csdn.net/course/detail/9604 创建云函数时有两点需要注意的,给大家说下 1,一定要把app.js里的环境id换成你自己的 [图片] 2,你的云函数目录要选择你对应的云开发环境(通常这里默认选中的) 不过你这里的云开发环境要和你app.js里的保持一致 [图片] 二,读取云数据库里的数据 我们第一步创建好云函数以后,可以先在云函数里读取我们的云数据库里的数据。 1,先看下我们云数据库里的数据 [图片] 2,编写云函数,读取云数据库里的数据(一定要记得部署云函数) [图片] 3,成功读取到数据 [图片] 把读取user数据表的完整代码给大家贴出来。 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init({ env: "test-vsbkm" }) // 云函数入口函数 exports.main = async(event, context) => { return await cloud.database().collection('users').get(); } [代码] 三,安装生成excel文件的类库 node-xlsx 通过上面第二步可以看到我们已经成功的拿到需要保存到excel的源数据,我们接下来要做的就是把数据保存到excel 1,安装node-xlsx类库 [图片] 这一步需要我们事先安装node,因为我们要用到npm命令,通过命令行 [代码]npm install node-xlsx [代码] [图片] 可以看出我们安装完成以后,多了一个package-lock.json的文件 [图片] 四,编写把数据保存到excel的代码, 下图是我们的核心代码 [图片] 这里的数据是我们查询的users表的数据,然后通过下面代码遍历数组,然后存入excel。这里需要注意我们的id,name,weixin要和users表里的对应。 [代码] for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } [代码] 还有下面这段代码,是把excel保存到云存储用的 [代码] //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) [代码] 下面把完整的excel里的index.js代码贴给大家,记得把云开发环境id换成你自己的。 [代码]const cloud = require('wx-server-sdk') //这里最好也初始化一下你的云开发环境 cloud.init({ env: "test-vsbkm" }) //操作excel用的类库 const xlsx = require('node-xlsx'); // 云函数入口函数 exports.main = async(event, context) => { try { let {userdata} = event //1,定义excel表格名 let dataCVS = 'test.xlsx' //2,定义存储数据的 let alldata = []; let row = ['id', '姓名', '微信号']; //表属性 alldata.push(row); for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } //3,把数据保存到excel里 var buffer = await xlsx.build([{ name: "mySheetName", data: alldata }]); //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) } catch (e) { console.error(e) return e } } [代码] 五,把excel存到云存储里并返回对应的云文件地址 我们上面已经成功的把数据存到excel里,并把excel文件存到云存储里。可以看下效果。 [图片] 我们这个时候,就可以通过上图的下载地址下载excel文件了。 [图片] 我们打开下载的excel [图片] 其实到这里就差不多实现了基本的把数据保存到excel里的功能了,但是我们要下载excel,总不能每次都去云开发后台吧。所以我们接下来要动态的获取这个下载地址。 六,获取云文件地址下载excel文件 [图片] 通过上图我们可以看出,我们获取下载链接需要用到一个fileID,而这个fileID在我们保存excel到云存储时,有返回,如下图。我们把fileID传给我们获取下载链接的方法即可。 [图片] 1,我们获取到了下载链接,接下来就要把下载链接显示到页面 [图片] 2,代码显示到页面以后,我们就要复制这个链接,方便用户粘贴到浏览器或者微信去下载 [图片] 下面把我这个页面的完整代码贴给大家 [代码]Page({ onLoad: function(options) { let that = this; //读取users表数据 wx.cloud.callFunction({ name: "getUsers", success(res) { console.log("读取成功", res.result.data) that.savaExcel(res.result.data) }, fail(res) { console.log("读取失败", res) } }) }, //把数据保存到excel里,并把excel保存到云存储 savaExcel(userdata) { let that = this wx.cloud.callFunction({ name: "excel", data: { userdata: userdata }, success(res) { console.log("保存成功", res) that.getFileUrl(res.result.fileID) }, fail(res) { console.log("保存失败", res) } }) }, //获取云存储文件下载地址,这个地址有效期一天 getFileUrl(fileID) { let that = this; wx.cloud.getTempFileURL({ fileList: [fileID], success: res => { // get temp file URL console.log("文件下载链接", res.fileList[0].tempFileURL) that.setData({ fileUrl: res.fileList[0].tempFileURL }) }, fail: err => { // handle error } }) }, //复制excel文件下载链接 copyFileUrl() { let that=this wx.setClipboardData({ data: that.data.fileUrl, success(res) { wx.getClipboardData({ success(res) { console.log("复制成功",res.data) // data } }) } }) } }) [代码] 给大家说下上面代码的步骤。 1,下通过getUsers云函数去云数据库获取数据 2,把获取到的数据通过excel云函数把数据保存到excel,然后把excel保存的云存储。 3,获取云存储里的文件下载链接 4,复制下载链接,到浏览器里下载excel文件。 到这里我们就完整的实现了把数据保存到excel的功能了。 文章有点长,知识点有点多,但是大家把这个搞会以后,就可以完整的学习小程序云开发的:云函数,云数据库,云存储了。可以说这是一个综合的案例。 有什么不懂的地方,或者有疑问的地方,请在文章底部留言,我看到都会及时解答的。后面我还会出一系列关于云开发的文章,敬请关注。
2019-09-07 - 微信小程序之swiper轮播图片高度自适应
微信小程序中使用swiper组件可以实现图片轮播效果,但是默认swiper高度是固定的150px,如果项目中图片大于固定高度就会被隐藏,所以本篇文章要实现轮播图片的高度自适应。 效果图: [图片] wxml代码: [代码]<[代码][代码]swiper[代码] [代码]class[代码][代码]=[代码][代码]"t-swiper"[代码] [代码]indicator-dots[代码][代码]=[代码][代码]"{{indicatordots}}"[代码] [代码]indicator-active-color[代码][代码]=[代码][代码]"{{color}}"[代码] [代码]autoplay[代码][代码]=[代码][代码]"{{autoplay}}"[代码] [代码]interval[代码][代码]=[代码][代码]"{{interval}}"[代码] [代码]duration[代码][代码]=[代码][代码]"{{duration}}"[代码] [代码]style[代码][代码]=[代码][代码]"height:{{height}}"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]block[代码] [代码]wx:for[代码][代码]=[代码][代码]"{{img}}"[代码] [代码]wx:key[代码][代码]=[代码][代码]""[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]src[代码][代码]=[代码][代码]"{{item}}"[代码] [代码]mode[代码][代码]=[代码][代码]"widthFix"[代码] [代码]bindload[代码][代码]=[代码][代码]'goheight'[代码] [代码]/>[代码][代码] [代码][代码]</[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]block[代码][代码]>[代码][代码]</[代码][代码]swiper[代码][代码]>[代码]wxss代码: [代码].t-swiper image {[代码][代码] [代码][代码]width[代码][代码]: [代码][代码]100%[代码][代码];[代码][代码]}[代码]js代码: [代码]Page({[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]img: [[代码][代码] [代码][代码]'img/1.jpg'[代码][代码],[代码][代码] [代码][代码]'img/2.jpg'[代码][代码],[代码][代码] [代码][代码]'img/3.jpg'[代码][代码] [代码][代码]],[代码][代码] [代码][代码]indicatordots: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]//是否显示面板指示点[代码][代码] [代码][代码]autoplay: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]//是否自动切换[代码][代码] [代码][代码]interval: 5000,[代码][代码] [代码][代码]//自动切换时间间隔[代码][代码] [代码][代码]duration: 500,[代码][代码] [代码][代码]//滑动动画时长[代码][代码] [代码][代码]color: [代码][代码]'#ffffff'[代码][代码],[代码][代码] [代码][代码]//当前选中的指示点颜色[代码][代码] [代码][代码]height: [代码][代码]''[代码][代码] [代码][代码]//swiper高度[代码][代码] [代码][代码]},[代码][代码] [代码][代码]goheight: [代码][代码]function[代码] [代码](e) {[代码][代码] [代码][代码]var[代码] [代码]width = wx.getSystemInfoSync().windowWidth[代码][代码] [代码][代码]//获取可使用窗口宽度[代码][代码] [代码][代码]var[代码] [代码]imgheight = e.detail.height[代码][代码] [代码][代码]//获取图片实际高度[代码][代码] [代码][代码]var[代码] [代码]imgwidth = e.detail.width[代码][代码] [代码][代码]//获取图片实际宽度[代码][代码] [代码][代码]var[代码] [代码]height = width * imgheight / imgwidth + [代码][代码]"px"[代码][代码] [代码][代码]//计算等比swiper高度[代码][代码] [代码][代码]this[代码][代码].setData({[代码][代码] [代码][代码]height: height[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码][代码]})[代码]
2019-07-14 - 小程序将小程序码与图片结合生成海报分享朋友圈
样例参考(瑞幸咖啡小程序) [图片][图片][图片] 需求分析 服务器端会返回不确认的图片资源到前端 前端将返回的每张图片都要贴上小程序码 将贴上小程序码的图片使用 swiper 组件轮播 用户点击保存时,将图片保存至相册。 至于点击保存如何保存至相册(wx.saveImageToPhotosAlbuml 了解一下,注意一下授权问题即可) 碰到的问题 canvas为原生组件, 而原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。 [代码]采用方法: 通过定位,将其移除到不在可视范围,如iphone6 .canvas { position: relative; left: -375px; } [代码] 绘制多张图片时, 一个canvas 标签只能对应画一张图片 (目前我测试的是这样,如有其它方法,欢迎评论交流) [代码]采用方法: 1. html端循环要生成的图片张数,对应循环出多个 canvas 组件,分别设置不同的 canvasId 区分 2. 封装一个绘制海报的函数,返回一个Promise对象,用于后面绘制完所有的图片,统一赋值渲染,避免多次触发数据更新 3. js端 循环执行一次 绘制海豹函数,存于一个数组列表 4. 使用 Promise.all 函数,统一绘制完毕将生产图片路径赋值 html: <canvas v-for="u in m.urlList" :key="u" :canvas-id="'poster'+index" class="canvas" style="width:100%; height:100%;"> </canvas> <swiper :indicator-dots="true" :circular="true" :autoplay="true" indicator-color="rgba(255,255,255,.2)" indicator-active-color="#fff" class="swiper"> <swiper-item v-for="f in m.filePaths" :key="index"> <image :src="f"> </swiper-item> </swiper> js: // 数据 (mpvue 开发) function data(){ return { m : { urlLIst: [ // 图片资源 '/static/poster0.jpg', '/static/poster1.jpg' ], filePaths: [], // 生成图片(贴上小程序码) } } } try { let res = wx.getSystemInfoSync(); // 同步获取系统信息 let w = res.windowWidth; // 手机可用区域宽度 let h = res.windowHeight; // 手机可用区域高度 let codeUrl = '/static/code.jpg'; // 小程序码 let drawList = []; // 用于保存绘制海报图Promise对象 m.urlList.forEach( (u, i)=> { // 传入canvas组件ID,图片路径(测试使用的是本地路径) drawList.push(drawPoster('poster'+i, u, codeUrl)); }) // 统一更新数据 Promise.all(drawList).then((valuse)=>{ m.filePaths = valuse; }); // 封装绘制图片函数 function drawPoster(canvasId, bgUrl, codeUrl){ return new Promise( (resolve, reject) => { // 创建画布实例 let ctx = wx.createCanvasContext(canvasId); // 绘制背景图: 图片路径,x坐标,y坐标,宽,高 ctx.drawImage(bgUrl, 0, 0, w, h); // 绘制小程序码 ctx.drawImage(codeUrl, w-120, h-120, 100, 100); // 绘制 ctx.draw(false, ()=>{ // 该通过函数将canvas绘制导出为图片 wx.canvasToTempFilePath({ x: 0, y: 0, width: w, height: h, canvasId: canvasId, success(res){ resolve(res.tempFilePath); } }); }); } }catch(e){ // 自己封装了一成 wx.$toast(e); } [代码] 最终demo效果图 [图片][图片] 在社区中暂未看到多张海报实现的方案,如果有更好的实现方案,欢迎交流
2019-07-30 - 《非经营性互联网信息服务备案核准》是不是就是 ICP 备案?
[图片] 非个人主体中的开放的服务类目 > 社区/论坛所要求的法务建议的资质文件 《非经营性互联网信息服务备案核准》是不是就是 ICP 备案? [图片] 像这样普通备案就可以呗?
2018-11-06 - # 使用小程序云开发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