- 小程序读取excel表格数据,并存储到云数据库
最近一直比较忙,答应大家的小程序解析excel一直没有写出来,今天终于忙里偷闲,有机会把这篇文章写出来给大家了。 老规矩先看效果图 [图片] 效果其实很简单,就是把excel里的数据解析出来,然后存到云数据库里。说起来很简单。但是真的做起来的时候,发现其中要用到的东西还是很多的。不信。。。。 那来看下流程图 流程图 [图片] 通过流程图,我看看到我们这里使用了云函数,云存储,云数据库。 流程图主要实现下面几个步骤 1,使用wx.chooseMessageFile选择要解析的excel表格 2,通过wx.cloud.uploadFile上传excel文件到云存储 3,云存储返回一个fileid 给我们 4,定义一个excel云函数 5,把第3步返回的fileid传递给excel云函数 6,在excel云函数里解析excel,并把数据添加到云数据库。 可以看到最神秘,最重要的就是我们的excel云函数。 所以我们先把前5步实现了,后面重点讲解下我们的excel云函数。 一,选择并上传excel表格文件到云存储 这里我们使用到了云开发,使用云开发必须要先注册一个小程序,并给自己的小程序开通云开发功能。这个知识点我讲过很多遍了,还不知道怎么开通并使用云开发的同学,去翻下我前面的文章,或者看下我录的讲解视频《5小时入门小程序云开发》 1,先定义我们的页面 页面很简单,就是一个按钮如下图,点击按钮时调用chooseExcel方法,选择excel [图片] 对应的wxml代码如下 [图片] 2,编写文件选择和文件上传方法 [图片] 上图的chooseExcel就是我们的excel文件选择方法。 uploadExcel就是我们的文件上传方法,上传成功以后会返回一个fildID。我们把fildID传递给我们的jiexi方法,jiexi方法如下 3 把fildID传递给云函数 [图片] 二,解下来就是定义我们的云函数了。 1,首先我们要新建云函数 [图片] 如果你还不知道如何新建云函数,可以翻看下我之前写的文章,也可以看我录的视频《5小时入门小程序云开发》 如下图所示的excel就是我们创建的云函数 [图片] 2,安装node-xlsx依赖库 [图片] 如上图所示,右键excel,然后点击在终端中打开。 打开终端后, 输入 npm install node-xlsx 安装依赖。可以看到下图安装中的进度条 [图片] 这一步需要你电脑上安装过node.js并配置npm命令。 3,安装node-xlsx依赖库完成 [图片] 三,编写云函数 我把完整的代码贴出来给大家 [代码]const cloud = require('wx-server-sdk') cloud.init() var xlsx = require('node-xlsx'); const db = cloud.database() exports.main = async(event, context) => { let { fileID } = event //1,通过fileID下载云存储里的excel文件 const res = await cloud.downloadFile({ fileID: fileID, }) const buffer = res.fileContent const tasks = [] //用来存储所有的添加数据操作 //2,解析excel文件里的数据 var sheets = xlsx.parse(buffer); //获取到所有sheets sheets.forEach(function(sheet) { console.log(sheet['name']); for (var rowId in sheet['data']) { console.log(rowId); var row = sheet['data'][rowId]; //第几行数据 if (rowId > 0 && row) { //第一行是表格标题,所有我们要从第2行开始读 //3,把解析到的数据存到excelList数据表里 const promise = db.collection('users') .add({ data: { name: row[0], //姓名 age: row[1], //年龄 address: row[2], //地址 wechat: row[3] //wechat } }) tasks.push(promise) } } }); // 等待所有数据添加完成 let result = await Promise.all(tasks).then(res => { return res }).catch(function(err) { return err }) return result } [代码] 上面代码里注释的很清楚了,我这里就不在啰嗦了。 有几点注意的给大家说下 1,要先创建数据表 [图片] 2,有时候如果老是解析失败,可能是有的电脑需要在云函数里也要初始化云开发环境 [图片] 四,解析并上传成功 如我的表格里有下面三条数据 [图片] 点击上传按钮,并选择我们的表格文件 [图片] 上传成功的返回如下,可以看出我们添加了3条数据到数据库 [图片] 添加成功效果图如下 [图片] 到这里我们就完整的实现了小程序上传excel数据到数据库的功能了。 再来带大家看下流程图 [图片] 如果你有遇到问题,可以在底部留言,我看到后会及时解答。后面我会写更多小程序云开发实战的文章出来。也会录制本节的视频出来,敬请关注。
2019-11-12 - 【干货】微信内置浏览器缓存清理
之前做过很多公众号的项目,项目写完后给客户看项目,客户一而再再而三的修改元素向左挪1px,向右挪2px。改好之后让客户看,客户说我特泽发克,你啥都没有修改,你竟然骗我!!! 这其实就是微信内置浏览器的缓存在作祟啦,那么如何清理微信内置浏览器的缓存呢? 你们是否知道 ios版微信 和 android版微信 的内置浏览器的内核是不一样的呢? android版微信内置浏览器(X5内核) 在安卓版微信内打开链接 http://debugx5.qq.com 拉到调试页面的最底端,勾选上所有的缓存项目,点击清除。 [图片] 点击确定之后即可完成清除微信浏览器缓存的操作。 ios版微信内置浏览器(WKWebView) ios版微信内置浏览器内核并不是 X5内核,而是使用的ios的浏览器内核WKWebView,所以安卓手机的那种方案对ios手机用户不生效,因为那个链接压根打不开 只要微信用户退出登录,然后重新登录,ios版微信内置浏览器内核即可清除,不行的话,你们回来打我 有人说了:“IOS中 设置—通用----存储空间 就会看到“正在计算空间”计算完了会清理一点清理即可”,这种办法当然也可以,但是这种办法不光是清理微信内置浏览器的缓存,同时也清理其他的一些数据,比如朋友圈的视频图片和聊天记录等等缓存,而且容易误删某些想留下的数据,对于开发而言,我认为退出重新登录是最好的解决办法。
2020-01-08 - 梦想与实践——Crowwork开发历程
Crowwork开发历程 [代码]本文参加2019年「大赛文章征集」征文活动 [代码] [图片] [代码]最初听说微信小程序大赛,是在去年第一届微信小程序大赛进行的时候。当时,仅仅会一些算法的我,还没有勇气报名参赛,但是看了看官方给出的开发教程,突然感觉微信小程序开发挺好上手,暗自将这个比赛记在心中,想着:明年如果还有这个比赛,一定要报名参赛。 到了今年三月份,微信公众号突然推了一条推送,是开赛通知!我兴奋地打开推送,发现今年的比赛规程有些许变化。今年的比赛不仅有小程序比赛,还开通了小游戏开发比赛。官方也出了一个小游戏开发视频教学。带着求学的干劲和探索的好奇心,我把小游戏开发的教程看了一遍。比较小程序和小游戏的体验以及小程序和小游戏开发过程,一番比对后,我决定要报名小程序开发。 有了我自己对比赛的认识之后,我开始寻找队友。比赛规程中规定:大赛每支队伍至少1个人,最多4个人。以我对自己清晰的认识,我一个人应该坚持不下来将小程序独立开发出来。于是,我想到了高中与我”分道扬镳“的两位同学,一位来自网络工程却“走火入魔”对前端开发情有独钟,写过几个网页,有html,css,js基础,有些项目经验;一位来自软件工程学过后台运行原理,但没有用过node.js也不会云开发。找到这两位后,本着“三个臭皮匠,还缺个诸葛亮”的精神,我们开始寻找UI。但是,直到现在UI还没有出现! 找到队友后,我们开始起草开发什么,我们一致认为,好的idea是比赛的关键。我们的观点是:开发的小程序要结合小程序的使用特点。一方面,要具有非常准确的目标性,不能有过于繁琐的功能。结合这点要求,我们开始体验好的小程序是怎么样的。通过一段时间的体验,我认为最好的小程序是腾讯开发的《乘车码》小程序,功能简单,应用市场大。我们认为,小程序之所以用小程序,而不是开发单独的一个app,最大的一个特点就是使用便捷、功能单一。 另一方面,小程序得实用,需要有较大的市场前景。如果开发一款小程序只是为了比赛,我认为没有较大的意义。开发完后如果能上线运行,给用户体验到小程序的样子,我们开发者心中会有一种自豪感、成就感。本着这个目标,我们开始寻找哪些方面的小程序很需要,但是还没有一款比较好的小程序上线。最后,我们发现,国外市场比较火爆的upwork,市场外包这一领域,小程序涉及的比较少。 [代码] [图片] [代码]有了这一思路的想法,但是,怎么设计才能让这种比较冗杂的信息符合小程序的特点,功能单一、界面简洁呢?这个问题困扰了我们很久。期间,我们也找了许多外包方面的app、网站、小程序来参考,认为没有一款做的比较符合我们心中的想法。突然,我脑中浮过一丝想法:每个人找外包工作基本就是那一类,我们仅推送那一类的工作不就界面简单、功能单一了吗。 找到开发方向后,我们开始了技术开发! [代码] 初识Crowwork [代码]第一次了解众包工作模式的时候很惊喜,我们有一种打开新世界的感觉,三个涉世未深的少年,2 年的学习、了解经验,为Crowwork投入也有三月有余,过程真是挑战巨大,研发中也是饱含艰辛。 我们想做的,很简单,就是想推出一款极具创新感的小程序。正是我们对创新的孜孜追求推动着我们开发的前进,加快了我们开发小程序的步伐。看到这个报告的出现,我真是莫名激动,感觉自己是走在正确的路上,这条路上还有其他优秀的小伙伴。我们所做的任何探索,其实都是大学生对于工作模式的进一步认识,不管是996、5*8也好、2-4也好,这些肯定不是工作模式的最好表现,我们只是希望能在人类工作模式上,留下点创新的探索。 所以在这里,我想分享一些在开发过程中的一些思考和探索,以供参考。文以载道当社会进步发展到一定阶段,一定要有相应的工作模式与之相匹。当青少年不愿意接受拘束的工作环境时,其实我们应该变换一种形式,让全新的工作模式走进传统企业的视野,这是社会发展的必要条件。 众包的任务通常是由个人来承担,但如果涉及到需要多人协作完成的任务,也有可能以依靠开源的个体生产的形式出现。现今众包模式已经对美国的一些产业产生了颠覆性的影响:一个跨国公司耗费几十亿美元也无法解决的研发难题,被一个外行人在两周的时间内圆满完成;过去要数百美元一张的专业水准图片,现在只要一美元就可以买到。有人惊呼我们将迎来众包时代,众包将成为外包的终结者。 现在有一些互联网弄潮儿采用众包模式,让供需双方直接对接,全球化协同作业,第三方资金托管模式,保障双方权益,权威专家测评。以易绚网为例本着解决项目纠纷,一对一专属项目经理全程把控,帮助有模型制作需要的企业提供人才与技术服务。这是众包工作模式好的方面,所以,要让更多领域的企业和人们了解、尝试、加入这个工作模式,来感受众包的优越性。 所有项目开发成员其实都有一个信念:感受到时代的蓬勃发展,展现出众包模式的优越之处。 为实现中国梦的伟大愿景而努力工作。 [代码] [图片] 小程序简介 [图片] [代码]Crowwork小程序是基于国外upwork网站下的一款集人力外包服务及社交、赚钱功能于一体的小程序。 在小程序里,每位用户既可以网罗身边的海量外包信息,又可以发挥自媒体的价值创造可观收益来赚钱,同时新颖有趣的社交功能和与同行大佬之间的专业交流将每位用户紧密联系在一起,一边赚钱,一边畅享生活。 [代码] 总包模式的出现 [代码]虽然众包这一术语主要用于形容网络行为,但是追溯回历史,一些事件我们今天也可以形容为众包。 1714年 --- The longitude Prize: 英国政府为了知道在海上的船只的经度位置,在群众中集思广益,并且宣布给给出最佳方法的人金钱奖励。 1783年 --- 路易十六提供奖励给能够用最经济实惠的用海盐方法制造成碱的人。 1957年 --- Jørn Utzon的设计赢得了悉尼歌剧院众包的设计比赛。 2009年 --- Waze, 以社区为中心的GPS的app,允许用户提交实时路况信息,比如报告交通堵塞或者交通事故,提交信息后所有使用此app的用户将能及时看到路况消息。 [代码] [图片] 中国众包工作模式的特点 [代码]中国企业将众包作为一个新的商业模式,将在企业生产者与消费者之间创建一种新的联系, 中国当前的众包应用的范围较少,局限在广告词,商标等一些范围的设置,仍未将之提升到公司战略高度。 尚未确立为一种新的商业模式,当前在中国众包只作为信息服务的中介机构。 将众包与外包进行对立的看待,认为有众包就不能有外包。其实众包是脱离于外包的一种形式,两个模式应该互相合作互相配合。 [代码] 小程序的应用场景 [代码]众包是一种分布式的问题解决和生产模式。问题以公开招标的方式传播给未知的解决方案提供者群体。用户(这里指众包里的“众”)典型地组成在线社区并提交方案。群“众”还要审查方案,发现最好的。这些最好的方案最后由最先提出问题的一方(众包人,crowdsourcer)所有,并且群“众”中胜出的个人有时会被奖励。有时,这些工作会有不错的报酬,无论是金钱上的、奖励上的或者只是名声和知名度。另外一些情况,胜出者会有智力上的满足感。众包可以通过业余人士或志愿者利用他们的空余时间提供解决方案,或者让专家或小型企业从无人知晓到初具规模。 [代码] 线上场景 [代码]公司提出问题或难题 --> 在互联网上发布和传播问题 --> 公司请求群众给出解决方案 --> 公司提出问题或难题 --> 在互联网上发布和传播问题 --> 公司请求群众给出解决方案 --> 公司获得和拥有胜出的解决方案 --> 公司得利 --> 完成 [代码] [图片] 混合场景 [代码]共享汽车、共享单车、共享民宿、共享充电宝……“共 享经济”已成为时下最为火爆的经济现象之一。“共享物流”模式在物流行业也是大受追捧,尤其是积极抢占“最后一公里”市场的众包物流模式成为行业焦点。 众包物流模式,针对最后一公里市场效率低、成本高的痛点,开发社会闲置运力资源,利用信息化手段来优化配送,提高效率。 [代码] [图片] 众包物流发展现状: [代码]早在7年前,人人快递总裁谢勤就提出了众包物流的概念。2011年,谢勤看到随着电商的迅速发展,快递包裹也越来越多,小区里、大街上、写字楼,随处可见送快递的小哥。谢勤认为传统行业都会向互联网电子商务的方向发展,意味着物流这个行业将会更壮大。 但无论是海上、路上、空中运输,最终的末端配送都需要人力。要如何才能解决这个庞大的人力需求呢?谢勤的想法就是全民参与,于是产生了“众包物流”的概念。 目前,众包物流模式代表性企业有:人人快送、新达达、蜂鸟众包、闪送等,它们的模式各不相同,但都瞄准了最后一公里市场。 人人快送由人人快递更名而来,主要切入同城配送业务,倡导“人人参与”“顺路捎带”的众包形式,通过“自由快递人”的形式整合社会运力资源。 [代码] 技术开发方案 [代码]俗话说只有搁凉的菜,没有搁凉的事,自古以来人们的生活就与工作紧密相连。我们团队认为,随着移动互联网时代的到来,手机外包类小程序行业最具潜力成为未来市场上的一匹多金黑马。外包行业在市场的地位都是永摧不倒。而且如今的外包行业,不仅仅局限于向自由职业者传递一些生产和经营服务的工作,在移动互联网时代的外包行业也带来了一种新的生活方式和工作理念。所以,手机外包类小程序占居消费市场与“劳”相关的入口,拥有巨大的想象空间,发展前景也是光明而又乐观的。 [代码] [图片] 任务概述 目标 [代码]本小程序旨在针对需要发布外包的企业以及需要相关外包信息服务的广大用户(已学生群体以及自由职业群体为主)建设以满足客户基本需求(外包信息发布以及查询,获得最新信息)为基础,提升用户体验(增加互动性和准确性)为目标的新型享型外包信息分享。用户可以通过该小程序查看外包信息,发布外包信息,浏览相关资讯,发表评论,分享经验心得,进行好友分享以及讨论区进行交流讨论。该应用软件管理员可通过以发布信息,管理(修改,删除和增加)外包信息,管理讨论区对该小程序的日常运营进行管理和维护。 [代码] 运行环境 [代码]微信小程序运行在三端:iOS(iPhone/iPad)、Android 和 用于调试的开发者工具。 三端的脚本执行环境以及用于渲染非原生组件的环境是各不相同的: 在 iOS 上,小程序逻辑层的 javascript 代码运行在 JavaScriptCore 中,视图层是由 WKWebView 来渲染的,环境有 iOS8、iOS9、iOS10; 在 Android 上,旧版本,小程序逻辑层的 javascript 代码运行中 X5 JSCore 中,视图层是由X5基于Mobile Chrome 57内核来渲染的;新版本,小程序逻辑层的javascript代码运行在V8中,视图层是由自研XWeb引擎基于Mobile Chrome 67内核来渲染的; 在 开发工具上,小程序逻辑层的javascript代码是运行在NW.js中,视图层是由Chromium 60 Webview来渲染的。 [代码] 数据库介绍 [代码]1) 服务器程序在管理员与用户进行各种操作(创建,收藏,修改,删除,评论外包,删除,创建,评论主题,查看,删除用户)时需要对数据库进行数据提交,即对数据库进行查询与修改:在上面的操作过程中都需要对数据库中的所有表,进行联合查询、修改。 2) 物理数据结构主要用于各模块之间函数的信息传递。 3) 接口传递的信息是用数据结构封装了的数据,以参数传递或返回值的形式在各模块间进行传输。 4) 对于出错信息要返回出错提示给用户,并提供出错信息的处理方法以保证软件运行的可维护性。 [代码] 小程序功能分析 前台功能 [代码]1) 普通用户可以通过该小程序查看外包信息 2) 普通用户可以通过该小程序搜索外包信息(按专业类别,工作类别等) 3) 普通用户可以通过该小程序浏览他人分享的外包信息,经验 4) 普通用户可以通过该小程序注册成为会员(免费注册) 5) 会员可以创建个人外包信息,进行经验和心得的分享 6) 会员可以对其他会员创建的外包信息进行评论,收藏。 7) 会员可以在专门的讨论区发言,与其他会员进行线上交流与讨论 8) 会员可以对个人信息进行管理(修改昵称,修改头像,修改密码) 9) 会员可以对自己创建的外包信息进行管理(修改外包信息,删除外包信息) [代码] 后台功能 [代码]管理员通过后台对外包信息进行管理 (1) 浏览外包信息 (2) 推荐外包信息 (3) 删除外包信息 管理员通过后台对该小程序信息进行管理 (1) 发布一些招聘消息和每日精选的信息 (2) 管理已经发布的信息(有条件的修改和删除) 管理员通过后台对会员进行管理 (1) 查看会员(基本信息,创建的外包信息,发表的言论) (2) 删除会员(严重违规会员) 管理员通过后台对讨论区进行管理 (1) 浏览发言 (2) 管理发言(删除违规言论,推荐优质分享) [代码] 小程序开发性能需求 准时性和及时性 [代码](1) 小程序处理的准确性和及时性是小程序的必要性能。 (2) 在小程序设计和开发过程中,要充分考虑小程序当前和将来可能承受的工作量,使小程序的处理能力和响应时间能够满足用户对信息处理的需求。 (3) 由于小程序的查询功能对于整个小程序的功能和性能完成举足轻重。作为小程序的很多数据来源,而美食的种类又影响用户的决策活动,其准确性很大程度上决定了小程序的成败。在小程序开发过程中,必须采用一定的方法保证小程序的准确性。 [代码] 开放性和可扩充性 [代码](1) 小程序在开发过程中,应该充分考虑以后的可扩充性。用户 查询的需求也会不断的更新和完善。所有这些,都要求小程序提供足够的手段进行功能的调整和扩充。 (2) 要实现可扩充性,应通过小程序的开放性来完成,即小程序应是一个开放小程序,只要符合一定的规范,可以简单的加入或减少小程序的模块。通过软件的修补、替换操作来完成小程序的升级和更新换代。 [代码] 易用性和可维护性 [代码](1) 小程序是直接面对使用人员的,而手机的品牌却是各不相同的。这就要求小程序能够提供良好的用户接口,易用的人机交互界面。 (2) 要实现易用性,就要求小程序应尽量选择用户熟悉的术语和语言界面;并针对用户可能出现的使用问题,提供相应的在线帮助,缩短用户对小程序熟悉的时间。 (3) 小程序应提供方便的方式供小程序维护人员进行数据的备份,日常的安全管理,以及小程序意外崩溃时数据的恢复等操作。 [代码] 赋予工作模式以时代内涵 [代码]最近几年,互联网焕发活力,成为各个行业追逐的热点。如何通过互联网的方式来驱动各行各业的发展?众包模式给出了新颖的答案。 从2009年,以社区为中心的GPS的app,允许用户提交实时路况信息,比如报告交通堵塞或者交通事故,提交信息后所有使用此app的用户将能及时看到路况消息起,众包发展目前已走过 10 年,早在我国互联网行业发展之初,许多新兴企业采取众包模式来为公司解决一些问题,大至为企业谋求转型、小至填写各类调查问卷,几乎所有被顶尖互联网公司都采取有众包模式的工作方式为企业取得利益。 [代码] [图片] [代码]众包模式在全球的发展是一个探索的过程,市场需要更多的时间去积淀和钻研。虽然厂商偏向稳重的策略,但他们对众包模式的应用往往是报以好奇的态度。许多企业开始运用众包模式往往是因为对高端人才的暂时需求,而并不想像传统工作模式一样去招聘高价的员工。因此,众包模式的产生,能够有效地解决那些企业迫在眉睫的问题。 众包模式有广阔的市场空间和无限的潜力,可以相信,在大众创业、万众创新的时代号角的呼唤下,众包模式将谱写新的篇章。[代码]
2019-10-31 - 小程序10行代码实现微信头像挂红旗,国庆节个性化头像
最近朋友圈里经常有看到这样的头像 [图片] 既然这么火,大家要图又这么难,作为程序员的自己当然要自己动手实现一个。 老规矩,先看效果图 [图片] 仔细研究了下,发现实现起来并不难,核心代码只有下面10行。 [代码] wx.canvasToTempFilePath({ x: 0, y: 0, width: num, height: num, destWidth: num, destHeight: num, canvasId: 'shareImg', success: function(res) { that.setData({ prurl: res.tempFilePath }) wx.hideLoading() }, fail: function(res) { wx.hideLoading() } }) [代码] 一,首先要创建一个小程序 至于如何创建小程序,我这里就不在细讲了,我也有写过创建小程序的文章,也有路过相关的学习视频,去翻下我历史文章找找就行。 二,创建好小程序后,我们就开始来布局 布局很简单,只有下面几行代码。 [代码]<!-- 画布大小按需定制 这里我按照背景图的尺寸定的 --> <canvas canvas-id="shareImg"></canvas> <!-- 预览区域 --> <view class='preview'> <image src='{{prurl}}' mode='aspectFit'></image> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="1">生成头像1</button> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="2">生成头像2</button> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="3">生成头像3</button> <button size='mini' open-type="getUserInfo" bindgetuserinfo="shengcheng" data-k="4">生成头像4</button> <button type='primary' bindtap='save'>保存分享图</button> </view> [代码] 实现效果图如下 [图片] 三,使用canvas来画图 其实我们实现微信头像挂红旗,原理很简单,就是把头像放在下面,然后把有红旗的相框盖在头像上面 [图片] 下面就直接把核心代码贴给大家 [代码]let promise1 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: "../../images/xiaoshitou.jpg", success: function(res) { console.log("promise1", res) resolve(res); } }) }); let promise2 = new Promise(function(resolve, reject) { wx.getImageInfo({ src: `../../images/head${index}.png`, success: function(res) { console.log(res) resolve(res); } }) }); Promise.all([ promise1, promise2 ]).then(res => { console.log("Promise.all", res) //主要就是计算好各个图文的位置 let num = 1125; ctx.drawImage('../../'+res[0].path, 0, 0, num, num) ctx.drawImage('../../' + res[1].path, 0, 0, num, num) ctx.stroke() ctx.draw(false, () => { wx.canvasToTempFilePath({ x: 0, y: 0, width: num, height: num, destWidth: num, destHeight: num, canvasId: 'shareImg', success: function(res) { that.setData({ prurl: res.tempFilePath }) wx.hideLoading() }, fail: function(res) { wx.hideLoading() } }) }) }) [代码] 来看下画出来的效果图 [图片] 四,头像加红旗画好以后,我们就要想办法把图片保存到本地了 [图片] 保存图片的代码也很简单。 [代码]save: function() { var that = this wx.saveImageToPhotosAlbum({ filePath: that.data.prurl, success(res) { wx.showModal({ content: '图片已保存到相册,赶紧晒一下吧~', showCancel: false, confirmText: '好哒', confirmColor: '#72B9C3', success: function(res) { if (res.confirm) { console.log('用户点击确定'); } } }) } }) } [代码] 来看下保存后的效果图 [图片] 到这里,我的微信头像就成功的加上了小红旗了。 [图片] 源码我也已经给大家准备好了,有需要的同学在文末留言即可。 [图片] 后面我准备录制一门视频课程出来,来详细教大家实现这个功能,敬请关注。
2019-09-26 - [打怪升级]小程序自定义头部导航栏“完美”解决方案
[图片] 为什么要做这个? 主要是在项目中,智酷君发现的一些问题 一些页面是通过扫码和订阅消息访问后,没有直接可以点击去首页的,需要添加一个home链接 需要添加自定义搜索功能 需要自定义一些功能按钮 [图片] 其实,第一个问题,在最近的微信版本更新中已经优化了,通过 小程序模板消息 过来的,系统会自动加上home按钮,但对于其他的访问方式则没有支持~ 一个不大不小的问题:两边ICON不对齐问题 [图片] 智酷君之前尝试了各种解决方法,发现有一个问题,就是现在手机屏幕太多种多样,有 传统头部、宽/窄刘海屏、水滴屏等等,无法八门,很多解决方案都无法解决特殊头部,系统**“胶囊按钮”** 和 自定义按钮在Android屏幕可能有 几像素不对齐 的问题(强迫症的噩梦)。 下面分享下一个相对比较完善的解决方案: [图片] 小程序代码段DEMO Link: https://developers.weixin.qq.com/s/cuUaCimT72cH ID: cuUaCimT72cH 智酷君做了一个demo代码段,方便大家直接用IDE工具查看源码~ [图片] 页面配置 1、页面JSON配置 [代码]{ "usingComponents": { "NavComponent": "/components/nav/common" //以插件的方式引入 }, "navigationStyle": "custom" //自定义头部需要设置 } [代码] 如果需要自定义头部,需要设置navigationStyle为 “custom” 2、页面代码 [代码]<!-- home 类型的菜单 --> <NavComponent v-title="自定义头部" bind:commonNavAttr="commonNavAttr"></NavComponent> <!-- 搜索菜单 --> <NavComponent is-search="true" bind:commonNavAttr="commonNavAttr"></NavComponent> [代码] 可以在自定义导航标签上添加属性配置来设置功能,具体按照实际需要来 3、目录结构 [代码]│ ├─components │ └─nav │ common.js │ common.json │ common.wxml │ common.wxss │ ├─images │ back.png │ home.png │ └─index index.js index.json index.wxml index.wxss search.js search.json search.wxml search.wxss [代码] 仅供参考 插件对应的JS部分 components/nav/common.js部分 [代码]const app = getApp(); Component({ properties: { vTitle: { type: String, value: "" }, isSearch:{ type: Boolean, value: false } }, data: { haveBack: true, // 是否有返回按钮,true 有 false 没有 若从分享页进入则没有返回按钮 statusBarHeight: 0, // 状态栏高度 navbarHeight: 0, // 顶部导航栏高度 navbarBtn: { // 胶囊位置信息 height: 0, width: 0, top: 0, bottom: 0, right: 0 }, cusnavH: 0, //title高度 }, // 微信7.0.0支持wx.getMenuButtonBoundingClientRect()获得胶囊按钮高度 attached: function () { if (!app.globalData.systeminfo) { app.globalData.systeminfo = wx.getSystemInfoSync(); } if (!app.globalData.headerBtnPosi) app.globalData.headerBtnPosi = wx.getMenuButtonBoundingClientRect(); console.log(app.globalData) let statusBarHeight = app.globalData.systeminfo.statusBarHeight // 状态栏高度 let headerPosi = app.globalData.headerBtnPosi // 胶囊位置信息 console.log(statusBarHeight) console.log(headerPosi) let btnPosi = { // 胶囊实际位置,坐标信息不是左上角原点 height: headerPosi.height, width: headerPosi.width, top: headerPosi.top - statusBarHeight, // 胶囊top - 状态栏高度 bottom: headerPosi.bottom - headerPosi.height - statusBarHeight, // 胶囊bottom - 胶囊height - 状态栏height (胶囊实际bottom 为距离导航栏底部的长度) right: app.globalData.systeminfo.windowWidth - headerPosi.right // 这里不能获取 屏幕宽度,PC端打开小程序会有BUG,要获取窗口高度 - 胶囊right } let haveBack; if (getCurrentPages().length != 1) { // 当只有一个页面时,并且是从分享页进入 haveBack = false; } else { haveBack = true; } var cusnavH = btnPosi.height + btnPosi.top + btnPosi.bottom // 导航高度 console.log( app.globalData.systeminfo.windowWidth, headerPosi.width) this.setData({ haveBack: haveBack, // 获取是否是通过分享进入的小程序 statusBarHeight: statusBarHeight, navbarHeight: headerPosi.bottom + btnPosi.bottom, // 胶囊bottom + 胶囊实际bottom navbarBtn: btnPosi, cusnavH: cusnavH }); //将实际nav高度传给父类页面 this.triggerEvent('commonNavAttr',{ height: headerPosi.bottom + btnPosi.bottom }); }, methods: { _goBack: function () { wx.navigateBack({ delta: 1 }); }, bindKeyInput:function(e){ console.log(e.detail.value); } } }) [代码] 解决不同屏幕头部不对齐问题的终极办法是 wx.getMenuButtonBoundingClientRect() 这个方法从微信7.0.0开始支持,通过这个方法我们可以获取到右边系统胶囊的top、height、right等属性,这样无论是水滴屏、刘海屏、异形屏,都能完美对齐右边系统默认的胶囊bar,完美治愈强迫症~ APP.js 部分 [代码]//app.js App({ /** * 加载页面 * @param {*} options */ onShow: function (options) { }, onLaunch: async function () { let self = this; //设置默认分享 this.globalData.shareData = { title: "智酷方程式" } // this.getSysInfo(); }, globalData: { //默认分享文案 shareData: {}, qrCodeScene: false, //二维码扫码进入传参 systeminfo: false, //系统信息 headerBtnPosi: false, //头部菜单高度 } }); [代码] 将获取的参数存储在一个全局变量globalData中,可以减少反复调用的性能消耗。 插件HTML部分 [代码]<view class="custom_nav" style="height:{{navbarHeight}}px;"> <view class="custom_nav_box" style="height:{{navbarHeight}}px;"> <view class="custom_nav_bar" style="top:{{statusBarHeight}}px; height:{{cusnavH}}px;"> <!-- 搜索部分--> <block wx:if="{{isSearch}}"> <input class="navSearch" style="height:{{navbarBtn.height-2}}px;line-height:{{navbarBtn.height-4}}px; top:{{navbarBtn.top+1}}px; left:{{navbarBtn.right}}px; border-radius:{{navbarBtn.height/2}}px;" maxlength="10" bindinput="bindKeyInput" placeholder="输入文字搜索" /> </block> <!-- HOME 部分--> <block wx:else> <view class="custom_nav_icon {{!haveBack||'borderLine'}}" style="height:{{navbarBtn.height}}px;line-height:{{navbarBtn.height-2}}px; top:{{navbarBtn.top}}px; left:{{navbarBtn.right}}px; border-radius:{{navbarBtn.height/2}}px;"> <view wx:if="{{haveBack}}" class="icon-back" bindtap='_goBack'> <image src='/images/back.png' mode='aspectFill' class='back-pre'></image> </view> <view wx:if="{{haveBack}}" class='navbar-v-line'></view> <view class="icon-home"> <navigator class="home_a" url="/pages/home/index" open-type="switchTab"> <image src='/images/home.png' mode='aspectFill' class='back-home'></image> </navigator> </view> </view> <view class="nav_title" style="height:{{cusnavH}}px; line-height:{{cusnavH}}px;"> {{vTitle}} </view> </block> </view> </view> </view> [代码] 主要是对几种状态的判断和定位的计算。 插件CSS部分 [代码]/* components/nav/test.wxss */ .custom_nav { width: 100%; background: #3a7dd7; position: relative; z-index: 99999; } .custom_nav_box { position: fixed; width: 100%; background: #3a7dd7; z-index: 99999; border-bottom: 1rpx solid rgba(255, 255, 255, 0.3); } .custom_nav_bar { position: relative; z-index: 9; } .custom_nav_box .nav_title { font-size: 28rpx; color: #fff; text-align: center; position: absolute; max-width: 360rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; top: 0; left: 0; right: 0; bottom: 0; margin: auto; z-index: 1; } .custom_nav_box .custom_nav_icon { position:absolute; z-index: 2; display: inline-block; border-radius: 50%; vertical-align: top; font-size:0; box-sizing: border-box; } .custom_nav_box .custom_nav_icon.borderLine { border: 1rpx solid rgba(255, 255, 255, 0.3); background: rgba(0, 0, 0, 0.1); } .navbar-v-line { width: 1px; margin-top: 14rpx; height: 32rpx; background-color: rgba(255, 255, 255, 0.3); display: inline-block; vertical-align: top; } .icon-back { display: inline-block; width: 74rpx; padding-left: 20rpx; vertical-align: top; /* margin-top: 12rpx; vertical-align: top; */ height: 100%; } .icon-home { /* margin-top: 8rpx; vertical-align: top; */ display: inline-block; width: 80rpx; text-align: center; vertical-align: top; height: 100%; } .icon-home .home_a { height: 100%; display: inline-block; vertical-align: top; width: 35rpx; } .custom_nav_box .back-pre, .custom_nav_box .back-home { width: 35rpx; height: 35rpx; vertical-align: middle; } .navSearch { width: 200px; background: #fff; font-size: 14px; position: absolute; padding: 0 20rpx; z-index: 9; } [代码] 总结: 通过微信API: getMenuButtonBoundingClientRect(),结果各类手机屏幕的适配问题 将算好的参数存储在全局变量中,一次计算全局使用,爽YY~ 往期回顾: [填坑手册]小程序PC版来了,如何做PC端的兼容?! [填坑手册]小程序Canvas生成海报(一) [拆弹时刻]小程序Canvas生成海报(二)
2021-09-13 - 小程序顶部自定义导航组件实现原理及坑分享
为什么使用自定义导航 对比默认导航栏,我们会更需要: 统一Android、IOS手机对于页面title的展示样式及位置 更丰富的导航栏定制效果,如添加home图标等 左上角返回事件的监听处理 统一实现双击返回顶部功能 自定义导航组件实现思路 自定义导航组件实现的核心是需要计算导航栏的真实高度 这里以官方文档->扩展能力中的Navigation组件为例分析实现思路。当使用"navigationStyle": "custom"时,默认导航被移除,页面的开始位置变成了屏幕顶部,这时我们需要实现的导航栏是在状态栏下面。 导航栏的真实高度=状态栏高度+导航栏内容。 [图片] 使用wx.getSystemInfo获取到statusBarHeight便是导航栏的高度,但是导航栏内容高度呢? 有人可能觉得导航栏内容高度顾名思义就是导航栏内容高度啊,内容撑起还用管嘛!要,必须要! 因为右上角胶囊按钮是原生加载的,我们的导航栏内容需要正好贴在胶囊的下方且垂直居中。 导航栏内容高度=(胶囊按钮的顶部距离 - 状态高度)*2 + 胶囊高度 [图片] 如何计算胶囊的数据呢?幸运的是我们有 wx.getMenuButtonBoundingClientRect() 获取胶囊按钮的布局位置信息,那么动态计算导航栏的内容高度就很方便啦。 好了,以上就是动态计算的核心思路,我们再来看官方Navigation组件高度是怎么实现的 [代码]page{--height:44px;--right:190rpx;} .weui-navigation-bar .android{--height:48px;--right:222rpx} .weui-navigation-bar__inner{ position:fixed;top:0;left:0;z-index:5001;display:flex;align-items:center; height:var(--height);padding-right:var(--right);width:calc(100% - var(--right)) } [代码] 导航栏内容的高度是通过- -height这个css变量提前声明好的,安卓机型会重新覆盖为新的css变量值,目前没发现有适配问题。 官方就是官方啊,具体尺寸都知道,那就不用一番计算周折啦,直接拿来主义即可。 导航的布局位置已经搞定啦,剩下就是写具体的内容,不同业务实现需求不同这里就不一一赘述了。 完善官方顶部导航组件 本着拿来主义,直接使用官方Navigation组件,但在实际业务开发中还是遇到不少需要自定义的需求,就比如: loadding样式没实现 标题内容超出没有出现省略号 和原生顶部的样式不兼容,导致单个页面引入时跳转有明显差异出现 没有双击返回顶部功能开关功能 引入页面需要获取导航栏的高度,来控制其他元素距离顶部的位置, 不能根据页面栈数据动态显示隐藏back按钮, 针对以上需求,我们对官方的组件进行二次完善开发,满足常规的自定义需求绰绰有余,直接引入开箱即用。 源码使用示例 https://github.com/YuniorZen/minicode-debug/tree/master/minicode02 [图片] 使用说明 [代码]/*自定义头部导航组件,基于官方组件Navigation开发。*/ <navigation-bar title="会员中心" bindgetBarInfo="getBarInfo"></navigation-bar> [代码] 组件属性列表 属性 类型 描述 bindgetBarInfo function 组件实例载入页面时触发此事件,首参为event对象,event.detail携带当前导航栏信息,如导航栏高度 event.detail.topBarHeight bindback function 点击back按钮触发此事件响应函数 backImage string back按钮的图标地址 homeImage string home按钮的图标地址 ext-class string 添加在组件内部结构的class,可用于修改组件内部的样式 title string 导航标题,如果不提供为空 background string 导航背景色,默认#ffffff color string 导航字体颜色 dbclickBackTop boolean 是否开启双击返回顶部功能,默认true border boolean 是否显示顶部导航下边框分割线,默认false loading boolean 是否显示标题左侧的loading,默认false show boolean 显示隐藏导航,隐藏的时候navigation的高度占位还在,默认true left boolean 左侧区域是否使用slot内容,默认false center boolean 中间区域是否使用slot内容,默认false Slot name 描述 left 左侧slot,在back按钮位置显示,当left属性为true的时候有效 center 标题slot,在标题位置显示,当center属性为true的时候有效 自定义顶部导航目前存在的坑 弹窗的背景蒙层无法覆盖原生胶囊按钮 页面下拉刷新的圆点会被自定义导航遮盖 如果要自定义顶部导航,以上问题避免不了,只能忍着接受。 目前还没遇到完美的解决方案,针对下拉刷新圆点被遮挡的问题微信官方还在需求开发中,如果你有好的想法欢迎留言反馈,一起学习交流。
2019-10-31 - 做完一个小程序,总结出一个入门开发课程
根据自己从零开始做「字节加工厂」这个小程序的经验,我计划写一个《微信小程序开发实战》的课程,当前已经完成了**「入门篇」**,介绍微信小程序开发,从 0 到 1 这个过程。 虽然说是入门篇,但是涉及的内容还是挺多的,也并不基础。有些内容涉及到 Web 前端的知识点,还有些内容涉及到 Node 知识点,例如「使用云函数开发」这篇,示例代码中还涉及到了 Node 中 https API 的使用。 技术相关的内容,基础大抵都是类似的。看似形形色色的不同形态,不同语言,不同架构,其实只是应用层面的使用方法不同而已。 最重要的还是基本功,越是基础的东西,越难习得,当然它的价值也最高。 这仅仅指的是技术层面,技术层面虽然很难,但还不是最难的,最难的是思路,也就是「产品能力」。 很多时候,我们学会了很多技术,却不知道用来做什么。 这个课程,我也没有办法去解决这个问题。 能够解决的是,帮助你将官方文档窜起来,从实际问题出发,去解决问题。文档终归是文档,它只是罗列出使用说明,我希望通过这个课程,带你学会实际解决问题的能力。 按照我的学习路径,《入门篇》我总结了 12 篇文章,如下目录: 了解小程序的页面逻辑 从写一个完整的页面开始 使用 Map API,完成一个页面交互 使用 Storage API,实现数据持久化保存 使用 Canvas API,做一个分享卡片 页面传参的几种方式 学会使用第三方 NPM 扩展包 使用 request API,调用第三方接口数据 使用云函数开发,绕过设置合法域名信息 学会云函数的本地测试以及云端测试 聊一聊小程序的服务端开发 学会使用云开发数据库能力 根据我的经验,假如你真的学会了上述文章中提到的知识点,微信小程序开发肯定是入门了,如果再深入一点,可能往「全栈开发」也踏入了半只脚。 当然,这个入门篇并非适合所有「新人」,它是根据我的学习路径而成,我本身是具备 Web 前端 以及 Node 开发经验的。 所以,如果你正好也有类似的开发经验,那么,这个入门篇的内容,对于你而言,可能要容易得多了,至少也能帮你节省一些时间,少走一些弯路。 写完《入门篇》之后,又花了点时间,整理成了 PDF 电子书,欢迎加入我的免费知识星球「字节加工厂」,获取电子书。 [图片] 写完「入门篇」,后面计划开始整理「效率篇」,文章将会同步更新在 GitHub 上,仓库地址:https://github.com/pengloo53/miniprogram-articles 欢迎 star
2019-10-08 - 借助小程序·云开发制作校园导览小程序丨实战
背景 当你刚上大学的时候,要想不迷失校园,除了依靠不怎么可靠的路边标识外,总会收到那么一张卡通绘制的校园地图: [图片] 这种静态图片可以让我们快速地了解到所需的地理位置信息,但使用和思考过后,会发现以下问题: 地理位置信息粒度高,而同一个地点通常具有多个服务功能和别名。 地理位置信息变更导致地图信息过时。一旦服务网点迁移或更名,需要重绘地图,带来一定的延迟和信息滞后。 入口较深。存储在手机上的地图并不是那么好找,尤其是随着时间的推移。 无法准确的定位当前所处位置,需要寻找参照物,这是静态地图致命的缺点。 缺乏更为详细的地点介绍,只能在有限的画面里堆积内容。 为此,我设计了一款校园导览应用,用当下流行的微信小程序结合云开发能力,低成本高效能地解决了以上问题。此外,我还根据对市面上的同类应用进行设计上的研究,在界面和交互设计上做功夫。下面我会进行简短的介绍。 南苑导览 [图片] 南苑导览是一款由学生独立开发的以地图为载体,提供中山大学南方学院(南苑)具体地点的位置信息、导航、校园历史及文化介绍的小程序。旨在解决校园导航标识不到位、地图形式低效单一、信息设计不够好等问题,为来南苑新人和游客提供更加完美的出行体验。 <div align=center> 仅需修改地图配置文件,即可适配任意场景(校园、景区)的小程序个性化地图定制。 技术栈:原生小程序 + TypeScript + gulp + vantUI + 云开发能力 2019 微信小程序高校大赛 · 华南赛区二等奖 </div> 南苑导览 · 开发 config 配置 [代码]├─ src ├─── config ├───── index.ts // 入口 ├───── cloud.ts // 云开发相关配置 ├───── info.ts // 应用介绍信息 ├───── markerStyle.ts // 地图marker样式 ├───── panorama.ts // 第三方全景地图配置(个人类型无webview权限,默认关闭) └───── secret.ts // 腾讯地图key等敏感信息(可选) [代码] 使用云数据库 [代码]// markers表 数据格式 { _id: "5ce8fe1c29c7a8581bc1e989", // id,云数据库录入upsert更新用 type: "生活服务", // 场景名称 icon: "shfw", // marker默认图标,为场景名称拼音缩写 scale: 15.0, // 场景在地图上的缩放值,可选。已废弃,用includePoints代替 position: 0, // 指定在各个场景中的排列顺序 data: [ // 该场景下的地点markers { name: "孙中山铜像", // 地点名称 short_name: "铜像", // 名称缩写 desc: "中山铜像...", // 描述信息 logo: "tx", // 地点logo,缩写拼音, 如作各院系logo展示 icon: "tx@2", // 自定义marker图标,“@”后数字为图标相较于默认图标的缩放值 images: 3, // 图片数量,作云存储拼接路径用(cloud://cloudRoot/1教/n.jpg) panorama: 0, // 全景场景id latitude: "23.635875", // 经度 longitude: "113.678965", // 纬度 contact: { phone: "020-123456", address: "出门左转" } // 联系方式 } ] } [代码] 使用 excel 进行数据维护,通过 python pandas 进行数据清洗,使用 jsonlines 库输出符合云数据库的 JSON Lines 格式文件,以 upsert 形式导入数据库。 数据更新流程如下: [图片] 加载并清洗数据 使用 request 或云数据库进行异步数据请求时。由于 app.js 中的 onLaunch 和首页 index 的 onLoad 的执行顺序不是固定的,所以如果首页有基于 app.js 请求的数据时要注意生命周期的问题。 [代码]// index async loadMarkers() { let markers; if (app.globalData.config.debug) { // 本地 markers = mockMarkers; } else { // 云 await wx.cloud .callFunction({ name: "loadMarkers" }) .then((res: any) => { markers = res.result.data; }); } app.globalData.markers = markers; } clearMarkers(markers: any[]) { let num = 0; // 每个marker都要有一个id for (const i of markers) { for (const j of i.data) { j.id = num; num += 1; j.iconPath = `/assets/images/markers/${j.icon ? j.icon : i.icon}.png`; ... // 自定义气泡样式 j.callout = Object.assign( { content: j.short_name ? j.short_name : j.name }, app.globalData.config.markerStyle.calloutStyle ); } } return markers; } [代码] 巧用 MapContextundefined你不需要去手动地为每个场景设置 scale,用 includePoints 即可让地图视野自动覆盖到当前所有 POI。undefined你也不需要去手动地去获取权限设置用户位置,用 moveToLocation 即可轻松定位。 [代码]// index onReady() { this.setData!({ mapContext: wx.createMapContext("map") }); } includePoints(padding: number) { this.data.mapContext.includePoints({ padding: [padding, padding, padding, padding], points: this.data.markers }); } locate() { this.data.mapContext.moveToLocation(); } [代码] 使用云存储管理图片undefined添加新图片时,直接修改 images 字段即可,文件夹目录为地点名称。 [代码]<!-- 地点详情页 轮播图 --> <swiper indicator-dots="{{imgUrls.length > 1}}" autoplay="{{true}}" interval="3000" duration="1000" > <block wx:for="{{imgUrls}}" wx:key="{{index}}"> <swiper-item> <image src="{{item}}" class="slide" data-id="{{index}}" bindtap="previewImage" /> </swiper-item> </block> </swiper> [代码] [代码]for (let i = 0; i < marker.images; i++) { imgUrls.push( this.data.cloudRoot + "images/" + (marker.short_name || marker.name) + "/" + i + ".jpg" ); } [代码] 南苑导览 · 设计 如果你在微信上搜索「导览」二字,看到的小程序大多都是一个模板,页面层级深,界面拥挤,列表式的信息展示并不符合我们日常使用地图 APP 的经验。为此,我做出了多项改良: [图片] 更好的视野 - 自定义导航栏与侧边栏undefined因为只有特定的页面需要使用自定义导航栏,所以只需要设置页面级的 config: [代码] "navigationStyle": "custom" [代码] 接下来获取胶囊按钮位置信息: [代码]bounding: wx.getMenuButtonBoundingClientRect(); [代码] [图片] 动态地设置样式: [代码]<!-- SIDE MENU --> <view class="sidebar" hidden="{{toggleRoutes}}" style="top:{{bounding.height + bounding.top + 10}}px" > ... </view> [代码] FAB 与侧边栏设计 把最主要的定位、搜索和路线推荐功能在视觉上成为整体,通过点击 FAB 弹出菜单选项。侧边栏的地点场景菜单设计为下拉滚动,注意使用半遮设计来提醒用户滚动。同时,为了让界面更加精简,侧边菜单会在点击 FAB(Float Action Button)和母按钮时 toggle 显示与隐藏。 [图片] 用点击代替滚动 - scroll-into-viewundefined在路线面板和搜索页中,使用到了 scroll-view 组件,利用其 scroll-into-view 特性,实现点击代替滚动的操作,同时也能起到提醒后置选项的作用。 [图片] [代码]windowWidth: wx.getSystemInfoSync().screenWidth; [代码] [代码]<scroll-view class="route" scroll-x scroll-into-view="{{focusPointId}}"> <view class="points" style="width:{{routes[routeIndex].count * 140 < windowWidth ? windowWidth : routes[routeIndex].count * 140}}rpx;" > ... </view> </scroll-view> [代码] 更好的视角 - 全景功能undefined结合 web-view 和全景服务平台,可以为一款地图导览应用增色不少。 [图片] 总结 云开发让小程序开发者无需搭建服务器,使用平台提供的 API 即可快速地进行业务开发、上线和迭代,免费的基础版完全可以满足中小应用的需求。「南苑导览」借助腾讯云开发能力,上线以来,帮助到了许许多多的新生和来客,实现了产品价值。最后,期望官方早日开放自定义地图底图能力,让开发者能够个性化地图,探索出更多的应用场景! 源码地址 https://github.com/TencentCloudBase/Good-practice-tutorial-recommended 如果你想要了解更多关于云开发CloudBase相关的技术故事/技术实战经验,请扫码关注【腾讯云云开发】公众号~ [图片]
2019-10-11 - [填坑手册]小程序PC版来了,如何做PC端的兼容?!
[图片] 微信宣布小程序将可以在PC端微信打开后,智库君就接到要求,需要兼容PC端小程序,一开始以为官方已经做了完美适配,不需要改什么,但当本人下载内测版开始测试的时候,才发现或许坑还挺多的~~~ 下面分享下本人“搬砖填坑”的全过程: (以下都是PC端小程序特有的问题,手机端正常) 先说下使用流程 [图片] 微信开发者工具菜单栏点击 设置->通用设置,在自动预览部分勾选“启动 PC 端自动预览”。 使用自动预览功能,点击 预览->自动预览->编译并预览,成功的话将在微信 PC 版上自动拉起小程序。 [图片] PC版打开后就横屏问题 [图片] [代码]{ "pages": [], "resizable":false, //在这里设置false,使得小程序默认手机尺寸 "pageOrientation":"portrait", //这里默认设置即可 ... } [代码] PC版微信默认打开小程序是ipad版,这样就会出现各种形变,布局错乱,这个可以在app.json进行配置,静止自动旋转,默认手机竖屏样子打开。 页面找不到问题 [图片] 这个问题本人也找了很久,一直很纳闷IDE工具和手机打开看都没什么问题,用PC打开小程序就出现页面找不到的情况,大致报错是: [代码]page[pages/XXX/XXX] not found.May be caused by :1. Forgot to add page route in app.json.2. Invoking Page() in async task. [代码] 一般这种情况以往是 app.json没配,或者页面里面缺少page(),但这次诡异的地方是只有“PC版小程序”报这个错!后来分析问题发现是:目前PC版小程序不能直接支持ES6,必须转换成ES5,同时由于一些语法转化不够完善,特别是ES7中的await 和 async 导致转化二次报错,这里就需要打开 “增强编译” 配置。 [图片] 打开有CSS报错 [图片] 因为目前PC版小程序估计内核的机制问题,还只支持低版本的选择器,如果你直接写小程序的标签,它无法识别,比如 [代码].popCont navigator{ //navigator 标签是小程序里的,PC端无法支持 width: 560rpx; height: 300rpx; } .popCont image{ //image 标签是小程序里的,PC端无法支持 width: 560rpx; height: 300rpx; } [代码] 但这些写法,其实在手机小程序和IDE工具里是完全正常的,PC版需要做兼容,改成class选择器。 布局结构混乱 如果遇到这种情况,会检查一下是否使用屏幕尺寸(rpx)来计算布局,PC 上屏幕尺寸比窗口尺寸大,应该使用窗口尺寸来计算。 小程序如何判断是 PC 平台? 通过 getSystemInfo 官方接口(platform 是 windows) 通过 UA(PC UA 包含 MiniProgramEnv/Windows) 微信官方PC版小程序内测地址: https://dldir1.qq.com/weixin/Windows/WeChat2.7.0_beta.exe 最新官方IDE调试工具 https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html 往期回顾: [打怪升级]小程序评论回复和发帖功能实战(二) [打怪升级]小程序评论回复和发贴组件实战(一) [填坑手册]小程序Canvas生成海报(一) [拆弹时刻]小程序Canvas生成海报(二) [填坑手册]小程序目录结构和component组件使用心得
2021-09-13 - 计算器 源码
一、界面 1、主要界面:基本计算 [图片] 2、扩展界面:单位计算 [图片] 3、其它界面 房贷计算、正则表达式等,具体可扫码参考 [图片] 二、代码 1、index.js //加载第三方组件,处理四则运算,代替禁用的eval函数 var rpn = require("…/utils/rpn.js"); Page ( { data: { idb: “back”, idc: “clear”, idt: “toggle”, id9: “9”, id8: “8”, id7: “7”, id6: “6”, id5: “5”, id4: “4”, id3: “3”, id2: “2”, id1: “1”, id0: “0”, idadd: “+”, idj: “-”, idx: “×”, iddiv: “÷”, idd: “.”, ide: “=”, screenData: “0”, operaSymbo: { “+”: “+”, “-”: “-”, “×”: “*”, “÷”: “/”}, lastIsOperaSymbo: false, lastIsPoint: false, iconType: ‘waiting_circle’, iconColor: ‘white’, arr: [], logs: [] }, [代码]onLoad: function (options) { // 页面初始化 options为页面跳转所带来的参数 }, onReady: function () { // 页面渲染完成 }, onShow: function () { // 页面显示 }, onHide: function () { // 页面隐藏 }, onUnload: function () { // 页面关闭 }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { }, //点击函数 clickBtn: function (event) { var id = event.target.id; if (id == this.data.idb) { //退格← var data = this.data.screenData.toString(); if (data == "0") { return; } data = data.substring(0, data.length - 1); if (data == "" || data == "-") { data = 0; } this.setData({ "screenData": data }); this.data.arr.pop(); } else if (id == this.data.idc) { //清屏C this.setData({ "screenData": "0" }); this.data.arr.length = 0; this.data.lastIsPoint = false; } else if (id == this.data.idt) { //正负号+/- var data = this.data.screenData; if (data == "0") { return; } var firstWord = data.charAt(0); if (firstWord == "-") { data = data.substr(1); this.data.arr.shift(); } else { data = "-" + data; this.data.arr.unshift("-"); } this.setData({ "screenData": data }); } else if (id == this.data.ide) { //等于= var data = this.data.screenData; if (data == "0") { return; } var lastWord = data.charAt(data.length); if (isNaN(lastWord)) { return; } console.log("parseFloat(data)" + parseFloat(data)); console.log("data" + data) if (parseFloat(data) == data) { return; } var log = data; var data = rpn.calCommonExp(data); // var num = ""; // var lastOperator = ""; // var arr = this.data.arr; // var optarr = []; // //遍历屏幕数组,获取操作数和操作符 // for (var i in arr) // { // if (isNaN(arr[i]) == false || arr[i] == this.data.idd || arr[i] == this.data.idt) // { // num += arr[i]; // } // else // { // lastOperator = arr[i]; // optarr.push(num); // optarr.push(arr[i]); // num = ""; // } // } // optarr.push(Number(num));//最后一个操作数 // var result = Number(optarr[0]) * 1.0; // console.log(result); // //循环操作数据,遇到符号就操作符号和下一个数 // for (var i = 1; i < optarr.length; i++) // { // if (isNaN(optarr[i])) // { // if (optarr[i] == this.data.idadd) // { // result += Number(optarr[i + 1]); // } // else if (optarr[i] == this.data.idj) // { // result -= Number(optarr[i + 1]); // } // else if (optarr[i] == this.data.idx) // { // result *= Number(optarr[i + 1]); // } // else if (optarr[i] == this.data.iddiv) // { // result /= Number(optarr[i + 1]); // } // } // } //存储历史记录 // this.data.logs.push(data + '=' + result); wx.setStorageSync("calclogs", this.data.logs); this.data.arr.length = 0; //this.data.arr.push(result); //this.setData({ "screenData": result + "" }); this.setData({ "screenData": data}); } else { if (this.data.operaSymbo[id]) { //如果是符号+-*/ this.data.lastIsPoint = false; //如果上一个输入字符是符号,或者屏幕是0,则再输入符号时不处理。 if (this.data.lastIsOperaSymbo || this.data.screenData == "0") { return; } } //对于不是符号的其它字符 var sd = this.data.screenData; var data; if(id == this.data.idd) { if (this.data.lastIsPoint) return; else { data = sd + id; this.data.lastIsPoint = true; } } else { if (sd === '0') { data = id; } else { data = sd + id; } } this.setData({ "screenData": data });//更新屏幕数据 this.data.arr.push(id); //更新本次输入的字符是否是运算符 if (this.data.operaSymbo[id]) { this.setData({ "lastIsOperaSymbo": true }); } else { this.setData({ "lastIsOperaSymbo": false }); } } }, [代码] } ) 2、index.wxml <view class=“content”> <view class=“layout-top”> <view class=“screen”> {{screenData}} </view> </view> <view class=“layout-bottom”> <view class=“btnGroup”> <view class=“item orange” bindtap=“clickBtn” id="{{idc}}">С</view> <view class=“item orange” bindtap=“clickBtn” id="{{idb}}">←</view> <view class=“item orange” bindtap=“clickBtn” id="{{idt}}">+/-</view> <view class=“item orange” bindtap=“clickBtn” id="{{idadd}}">+</view> </view> <view class=“btnGroup”> <view class=“item blue” bindtap=“clickBtn” id="{{id9}}">9</view> <view class=“item blue” bindtap=“clickBtn” id="{{id8}}">8</view> <view class=“item blue” bindtap=“clickBtn” id="{{id7}}">7</view> <view class=“item orange” bindtap=“clickBtn” id="{{idj}}">-</view> </view> <view class=“btnGroup”> <view class=“item blue” bindtap=“clickBtn” id="{{id6}}">6</view> <view class=“item blue” bindtap=“clickBtn” id="{{id5}}">5</view> <view class=“item blue” bindtap=“clickBtn” id="{{id4}}">4</view> <view class=“item orange” bindtap=“clickBtn” id="{{idx}}">×</view> </view> <view class=“btnGroup”> <view class=“item blue” bindtap=“clickBtn” id="{{id3}}">3</view> <view class=“item blue” bindtap=“clickBtn” id="{{id2}}">2</view> <view class=“item blue” bindtap=“clickBtn” id="{{id1}}">1</view> <view class=“item orange” bindtap=“clickBtn” id="{{iddiv}}">÷</view> </view> <view class=“btnGroup”> <view class=“item blue zero” bindtap=“clickBtn” id="{{id0}}">0</view> <view class=“item blue” bindtap=“clickBtn” id="{{idd}}">.</view> <view class=“item orange” bindtap=“clickBtn” id="{{ide}}">=</view> </view> </view> </view> 3、index.wcss /* //height: 90%;*/ .content { display: flex; flex-direction: column; align-items: center; background-color: #ccc; font-family: “Microsoft YaHei”; overflow-x: hidden; } .layout-top{ width: 100%; margin-bottom: 10rpx; } .layout-bottom{ width: 100%; } .screen { text-align: right; width: 100%; line-height: 200rpx; padding: 0 10rpx; font-weight: bold; font-size: 60px; box-sizing: border-box; border-top: 1px solid #fff; } .btnGroup { display: flex; flex-direction: row; flex: 1; width: 100%; height: 5rem; background-color: #fff; } .item { width:25%; display: flex; align-items: center; flex-direction: column; justify-content: center; margin-top: 1px; margin-right: 1px; } .item:active { background-color: #ff0000; } .zero{ width: 50%; } .orange { color: #fef4e9; background: #f78d1d; font-weight: bold; } .blue { color:#d9eef7; background-color: #0095cd; } .iconBtn{ display: flex; } .icon{ display: flex; align-items: center; width:100%; justify-content: center; } 三、完整工程下载链接 [](https://download.csdn.net/download/kevin_lp/11931364
2019-10-28 - 小程序识别身份证,银行卡,营业执照,驾照
最近老是有同学问我小程序ocr识别的问题,就趁机研究了下,实现了小程序识别身份证,银行卡,驾照,营业执照,图片文字的功能。今天来给大家讲讲详细的实现流程。 先画一张流程图出来 [图片] 第一次看到这个流程图,可能有点萌,什么云开发,云函数。。。。 不要着急,我们接下来会一步步带大家实现。 先看下我们的页面和效果图。 [图片] 功能其实很简单,就是我们点对应的按钮后,去拍照或者去相册选择对应的图片。然后把图片上传到云存储,会有一个对应的图片url,然后把这个图片url传递到云函数,然后云函数里使用小程序的开发ocr能力,来识别图片,返回对应的信息回来。如下图所示,我们识别银行卡(身份证什么的就不演示了,涉及到石头哥个人隐私) [图片] 接下来就是代码的实现了。 一,首先要创建一个云开发的小程序项目 这里我前面文章有讲解过,就不再细说了,不会的同学去翻看下我之前的文章。或者看下我录制的 讲解视频 这里有一点需要注意的给大家说下 [图片] 二,创建一个简单的小程序页面 1,index.wxml如下 [图片] 2,index.js完整代码如下 [代码]Page({ //身份证 shenfenzheng() { this.photo("shenfenzheng") }, //银行卡 yinhangka() { this.photo("yinhangka") }, //行驶证 xingshizheng() { this.photo("xingshizheng") }, //拍照或者从相册选择要识别的照片 photo(type) { let that = this wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success(res) { // tempFilePath可以作为img标签的src属性显示图片 let imgUrl = res.tempFilePaths[0]; that.uploadImg(type, imgUrl) } }) }, // 上传图片到云存储 uploadImg(type, imgUrl) { let that = this wx.cloud.uploadFile({ cloudPath: 'ocr/' + type + '.png', filePath: imgUrl, // 文件路径 success: res => { console.log("上传成功", res.fileID) that.getImgUrl(type, res.fileID) }, fail: err => { console.log("上传失败", err) } }) }, //获取云存储里的图片url getImgUrl(type, imgUrl) { let that = this wx.cloud.getTempFileURL({ fileList: [imgUrl], success: res => { let imgUrl = res.fileList[0].tempFileURL console.log("获取图片url成功", imgUrl) that.shibie(type, imgUrl) }, fail: err => { console.log("获取图片url失败", err) } }) }, //调用云函数,实现OCR识别 shibie(type, imgUrl) { wx.cloud.callFunction({ name: "ocr", data: { type: type, imgUrl: imgUrl }, success(res) { console.log("识别成功", res) }, fail(res) { console.log("识别失败", res) } }) } }) [代码] 上面代码注释讲解的很清楚了,再结合我们的流程图,相信你可以看明白。 [图片] 三,重头戏来了,识别的核心代码是下面这个云函数 [图片] 云函数的完整代码也给大家贴出来 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async(event, context) => { let { type, imgUrl } = event switch (type) { case 'shenfenzheng': { // 识别身份证 return shenfenzheng(imgUrl) } case 'yinhangka': { // 识别银行卡 return yinhangka(imgUrl) } case 'xingshizheng': { // 识别行驶证 return xingshizheng(imgUrl) } default: { return } } } //识别身份证 async function shenfenzheng(imgUrl) { try { const result = await cloud.openapi.ocr.idcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } //识别银行卡 async function yinhangka(imgUrl) { try { const result = await cloud.openapi.ocr.bankcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } //识别行驶证 async function xingshizheng(imgUrl) { try { const result = await cloud.openapi.ocr.vehicleLicense({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } } [代码] 其实没什么特别的,就是用一个switch方法,根据用户传入的不同的type值,来实现不同的识别效果。 如用传入的type是‘ yinhangka’,我们就调用银行卡识别 [代码]try { const result = await cloud.openapi.ocr.bankcard({ type: 'photo', imgUrl: imgUrl }) return result } catch (err) { console.log(err) return err } [代码] 进而把识别的结果返回给小程序端,如下图 [图片] 到这里我们就完整的实现了,小程序识别身份证,银行卡,行驶证的功能。至于别的更多的ocr识别,可以去看小程序官方文档,结合着我的这篇文章,相信你也可以轻松实现更多的图片识别。 [图片] 源码其实在上面都已经贴给大家了,如果你觉得不完整,想要完整的源码可以在文章底部留言或者私信我。
2019-10-30 - 微信小程序转其他端小程序实战
背景 公司一直是在用原生来开发微信小程序,完成上线后产品又提出需要百度、头条小程序。技术这边初步考虑有两个方案: 1、直接微信小程序代码转成百度、头条小程序代码; 2、为了方便以后的功能升级,先把微信小程序代码转成uni-app、taro这种支持多端的第三方框架代码后,再编译到百度、头条 直接转换 由于众所周知的原因,不管是百度还是头条,他们的原生开发的组件、api等都和微信小程序及其类似,这也就使得直接转换变得十分方便。 百度后发现微信到头条有专门的工具,安装和使用都很方便 安装 [代码]npm i wx2tt -g [代码] 使用 [代码]wx2tt <微信小程序目录> <头条小程序目录> [代码] 不使用转换工具,手工转换的话也还算清晰,主要是以下4步: 1、将 wxml 文件中的 wx: 替换成 tt 2、将 js 文件中的 wx. 替换成 tt. 3、将 wxml 文件后缀改为 ttml 4、将 wxss 文件后缀改为 ttss 转换后导入头条开发工具可以顺利启动。 百度小程序就更简单了,百度开发者工具自带搬家功能,可以直接将微信小程序代码转成百度小程序代码并顺利启动。 当然这些转换工具仅仅是简单的代码层面的转换,实际要使用还是要进行一些调整。虽然百度、头条和微信的规范很类似,还是有一些差别,需要在转换之后详细的测试和调整。目前我们实际测试下来有这些不同和需要调整的地方: 1、列表渲染头条、百度不支持直接使用数字,类似[代码]wx:for="{{4}}"[代码]这种在微信小程序里是可用的,头条、百度里不可用 2、头条小程序不支持canvas的[代码]arcTo[代码]方法,虽然文档里是有这个方法的,但是实际使用时会报错:ctx.arcTo is not a function 可能还存在其他问题,需要更详细的测试。另外、涉及到登录、支付等功能也需要后端做相对应的开发。 第三方框架 为了方便以后的功能升级,我们也考虑了使用uni-app、taro这种支持多端的第三方框架。由于已经有了微信端的原生代码,所以使用思路上是像先把微信的原生代码转换成第三方框架的代码后,再编译到百度、头条。 不管是uni-app还是taro都提供了转换方法和工具 uni-app:https://ask.dcloud.net.cn/article/35786 taro:https://taro-docs.jd.com/taro/docs/taroize.html 实际转换也是成功完成了,但是转换之后的代码并不能直接使用,要在百度、头条开发者工具里使用的话,还需要再编译一次。编译也很成功,但是编译好之后放到开发者工具之后就报错了,由于编译之后的代码在可读性上变差了很多,基本上没办法定位错误并调整修改。不仅是百度、头条,编译成微信小程序也会报错。 进过实际的操作和对比后,最终决定放弃第三方框架。 结论 经过这次实战,我们这边对于小程序的多端开发,最终的结论就是:在不涉及app开发的前提下,优先微信原生开发后,再使用转换工具转换成其他端代码,不建议使用第三方框架。 不建议使用第三方框架的最主要原因就是第三方开发出来的代码并不能直接使用,需要再编译一次,编译之后的代码可读性变差,基本没有办法调试和修改错误。 后续 没有后续了,公司砍掉了整个项目,我被裁员了
2019-11-20 - 阅读 9小时搞定微信小程序开发 源码总结(小书架)。
目录与页面模块 读代码首先应该认真阅读文档的[代码]README.md[代码]。 看下小书架页面模块、目录结构清晰,虽然模块不多,实际业务开发中首先应该构思拆解业务模块,确定目录结构。小程序包大小超过 2M 需要分包,所以从一开始确定目录结构的时候就要考虑进去,不然线上跑起来再去分包会有点小麻烦。 目录结构 [代码]├── config │ └── config.js ├── images ├── pages │ ├── books │ │ ├── books.js │ │ ├── books.json │ │ ├── books.wxml │ │ └── books.wxss │ ├── comment │ │ ├── comment.js │ │ ├── comment.js │ │ ├── comment.js │ │ └── comment.wxss │ ├── detail │ │ ├── detail.js │ │ ├── detail.js │ │ ├── detail.js │ │ └── detail.wxss │ ├── my │ │ ├── my.js │ │ ├── my.js │ │ ├── my.js │ │ └── my.wxss │ └── myBooks │ ├── myBooks.js │ ├── myBooks.js │ ├── myBooks.js │ └── myBooks.wxss ├── utils │ └── util.js ├── app.js ├── app.json ├── app.wxss └── project.config.json [代码] 各页面模块 页面 描述 books 首页/书籍列表页 comment 评论页面 detail 书籍详情页 my 个人中心页 myBooks 已购书籍页 接口封装 小书架没有对 wx.request 封装。考虑到是入门教程,没做处理也是正常。接口请求路径封装在 config.js [代码]// 服务器域名 const baseUrl = 'http://127.0.0.1:[your port]/'; // 获取书籍信息接口地址(可选择全部或单个书籍) const getBooksUrl = baseUrl + 'api/book/getBooks'; //... module.exports = { getBooksUrl: getBooksUrl //... }; [代码] 也可以根据个人习惯进行封装,这里一定要写注释以及考虑后期维护 [代码]let returnCancel = (memberId, refundId) => http.post(`api/return/goods/cancel`, { memberId: memberId, refundId: refundId}) export default { returnCancel }; [代码] 页面 book book.js 代码干净、整洁。注释很详细,虽然这是入门教程,但我们开发的时候也要养成这样的好习惯。 data [代码]data: { bookList: [], // 书籍列表数组 indicatorDots: false, // 是否显示轮播指示点 autoplay: false, // 是否自动播放轮播 sideMargin: '100rpx', // 幻灯片前后边距 showLoading: true // 是否显示loading态 //... }, [代码] onLoad getBookList 方法获取所有书籍列表,不要把所有的 wx.request 都写在load里面,简单封装下可维护性大大提高 [代码]/** * 获取所有书籍列表 */ getBookList: function() { let that = this; wx.request({ url: api.getBooksUrl, data: { is_all: 1 }, success: function(res) { let data = res.data; // console.log(data); if (data.result === 0) { setTimeout(function() { that.setData({ bookList: data.data, showLoading: false }); }, 800); } }, error: function(err) { console.log(err); } }); }, onLoad: function(options) { let that = this; that.getBookList(); }, [代码] loading处理 [代码]<block wx:if="{{showLoading}}"> <view class="donut-container"> <view class="donut"></view> </view> </block> // 默认 true 。getBookList 方法成功回调里设为 false。没有错误处,接口请求失败的话应该也做下处理的 [代码] comment comment.js 封装了检查用户输入的方法,实际业务中如果输入较多的话,可以提炼到 unit.js。封装了 wx.showToast [代码]// 检查输入是否为空,起名称注意语义话 checkEmpty: function(input) { return input === ''; }, /** * 检查用户是否输入了非法字符 */ checkIllegal: function(input) { let patern = /[`#^<>:"?{}\/;'[\]]/im; let _result = patern.test(input); return _result; }, /** * 检查用户输入 */ checkUserInput: function() { /* * 检测用户输入 * 1. 是否包含非法字符 * 2. 是否为空 * 3. 是否超出长度限制 */ let that = this; let comment = that.data.comment; let showToastFlag = false; let toastWording = ''; if (that.checkEmpty(comment)) { showToastFlag = true; toastWording = '输入不能为空'; } else if (that.checkIllegal(comment)) { showToastFlag = true; toastWording = '含有非法字符'; } else if (comment.length > 140) { showToastFlag = true; toastWording = '长度超出限制'; } if (showToastFlag) { that.showInfo(toastWording); return false; } else { return true; } }, [代码] 封装toast [代码]showInfo: function(info, icon = 'none', callback = () => {}) { wx.showToast({ title: info, icon: icon, duration: 1500, mask: true, success: callback }); }, [代码] detail 简单的返回刷新处理以及下载进度条 [代码]// 从上级页面返回时 重新拉去评论列表 backRefreshPage: function() { let that = this; that.setData({ commentLoading: true }); that.getPageData(); }, /** * 生命周期函数--监听页面显示 */ onShow: function() { if (wx.getStorageSync('isFromBack')) { wx.removeStorageSync('isFromBack') this.backRefreshPage(); } } [代码] 进度条(这个还是很少见的需求,很可爱) [代码]<!-- 下载进度条 --> <view class="loading-container" wx:if="{{downloading}}"> <progress percent="{{downloadPercent}}" stroke-width="6" activeColor="#1aad19" backgroundColor="#cdcdcd" show-info /> </view> [代码] my 主要是检查登陆。myBooks没有什么亮眼的操作,就不上场了。 [代码]data: { userInfo: {}, // 用户信息 hasLogin: wx.getStorageSync('loginFlag') ? true : false // 是否登录,根据后台返回的skey判断 }, [代码] app.js 主要负责检查处理登陆信息 [代码]App({ // 小程序启动生命周期 onLaunch: function () { let that = this; // 检查登录状态 that.checkLoginStatus(); }, // 检查本地 storage 中是否有登录态标识 checkLoginStatus: function () { let that = this; let loginFlag = wx.getStorageSync('loginFlag'); if (loginFlag) { // 检查 session_key 是否过期 wx.checkSession({ // session_key 有效(为过期) success: function () { // 直接从Storage中获取用户信息 let userStorageInfo = wx.getStorageSync('userInfo'); if (userStorageInfo) { that.globalData.userInfo = JSON.parse(userStorageInfo); } else { that.showInfo('缓存信息缺失'); console.error('登录成功后将用户信息存在Storage的userStorageInfo字段中,该字段丢失'); } }, // session_key 过期 fail: function () { // session_key过期 that.doLogin(); } }); } else { // 无登录态 that.doLogin(); } }, // 登录动作 doLogin: function (callback = () => {}) { let that = this; wx.login({ success: function (loginRes) { if (loginRes.code) { /* * @desc: 获取用户信息 期望数据如下 * * @param: userInfo [Object] * @param: rawData [String] * @param: signature [String] * @param: encryptedData [String] * @param: iv [String] **/ wx.getUserInfo({ withCredentials: true, // 非必填, 默认为true success: function (infoRes) { console.log(infoRes,'>>>') // 请求服务端的登录接口 wx.request({ url: api.loginUrl, data: { code: loginRes.code, // 临时登录凭证 rawData: infoRes.rawData, // 用户非敏感信息 signature: infoRes.signature, // 签名 encryptedData: infoRes.encryptedData, // 用户敏感信息 iv: infoRes.iv // 解密算法的向量 }, success: function (res) { console.log('login success'); res = res.data; if (res.result == 0) { that.globalData.userInfo = res.userInfo; wx.setStorageSync('userInfo', JSON.stringify(res.userInfo)); wx.setStorageSync('loginFlag', res.skey); callback(); } else { that.showInfo(res.errmsg); } }, fail: function (error) { // 调用服务端登录接口失败 that.showInfo('调用接口失败'); console.log(error); } }); }, fail: function (error) { // 获取 userInfo 失败,去检查是否未开启权限 wx.hideLoading(); that.checkUserInfoPermission(); } }); } else { // 获取 code 失败 that.showInfo('登录失败'); console.log('调用wx.login获取code失败'); } }, fail: function (error) { // 调用 wx.login 接口失败 that.showInfo('接口调用失败'); console.log(error); } }); }, // 检查用户信息授权设置 checkUserInfoPermission: function (callback = () => { }) { wx.getSetting({ success: function (res) { if (!res.authSetting['scope.userInfo']) { wx.openSetting({ success: function (authSetting) { console.log(authSetting) } }); } }, fail: function (error) { console.log(error); } }); }, // 获取用户登录标示 供全局调用 getLoginFlag: function () { return wx.getStorageSync('loginFlag'); }, // app全局数据 globalData: { userInfo: null } }); [代码] 中规中矩的小程序,入门还是可以的,代码简洁干净。新手的话撸一遍还是可以的。这样不知道算不算侵权,侵删。
2019-11-15 - 个人开发了二个月的小程序终于完成,想得瑟一下行吗?
个人从小白开始开发的,小学英语小程序,用时两个月完成。个人感觉还不错,想得瑟一下,但是怕有发广告之嫌,就发个样子吧。界面都是自己设计的,图标很可爱是吧哈哈哈~自己做的小程序,越看越可爱,越瞅越顺眼。 [图片]
2019-11-20 - 微信小程序WXS 有什么用?
微信WXS介绍 WXS(WeiXin Script)是微信创造的一套脚本语言,它的官方说法是:“WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致”。 那微信为何要脱离 JavaScript ,单独创造一套语言呢?这要从微信小程序的底层逻辑(运行环境)讲起。 小程序的运行环境分为逻辑层和视图层,分别由2个线程管理,其中: WXML 模板和 WXSS 样式工作在视图层,界面使用 WebView 进行渲染 JavaScript代码工作在逻辑层,运行在JsCore或v8里 小程序在视图层与逻辑层两个线程间提供了数据传输和事件系统。这样的分离设计,带来了显而易见的好处: 逻辑和视图分离,即使业务逻辑计算非常繁忙,也不会阻塞渲染和用户在视图层上的交互 但同时也带来了明显的坏处: 视图层(webview)中不能运行JS,而逻辑层JS又无法直接修改页面DOM,数据更新及事件系统只能靠线程间通讯,但跨线程通信的成本极高,特别是需要频繁通信的场景 从本质来讲,wxs是一种被限制过的、运行在视图层webview里的js。它并不是真的发明了一种新语言。 WXS特征及适用场景 WXS具备如下特征: WXS是可以在视图层(webview)中运行的JS WXS无法直接修改业务数据,仅能设置当前组件的class和style,或者对数据进行格式化。 WXS是被限制过的JavaScript,可以进行一些简单的逻辑运算 故可以得出WXS的适用场景,主要包括: 数据格式处理,比如文本、日期格式化,或者国际化。 类比vue 微信小程序的wxs语法 可以当做vue的计算属性和vue filter 使用。因为wxs中的函数可以写在{{ }}中 。 例如: 可用在 <view>{{ foo() }}</view> 知识背景 其实今天总结wxs是有真实背景的,在周末我在开发在线答题小程序的过程中,需要在一个radio中存放一个字符串,但是这个字符串需要通过JSON.stringfy转化成字符串,但是微信小程序本身的wxml绑定{{}}并不支持,所以才有了本文。 就是说在wxml中如果需要对小程序的this.data数据进行额外的操作,就可以用wxs,这个在vue里面就是计算属性。 帖子来源 https://developers.weixin.qq.com/community/develop/doc/000a2ab8840b9011be89e3d3d56c00
2019-12-02 - weui内置扩展库使用步骤
更新最新的 nightly 版开发者工具 在app.json里新增 “useExtendedLib”: { “weui”: true } 在使用的页面json文件应用组件,比如在index.json里 { “navigationStyle”:“custom”, “usingComponents”: { “mp-navigation-bar”:“weui-miniprogram/navigation-bar/navigation-bar” } } wxml文件使用组件,比如在index.wxml里 <mp-navigation-bar title=“首页”></mp-navigation-bar> 验证有无生效。 示例代码片段:https://developers.weixin.qq.com/s/zB6mFrmc7elr 备注: 文档里说目前暂不支持在分包中引用,但据代码测试,分包也能使用。 各位觉得有用的话,给个赞呗。
2020-10-28 - 成语答题小程序
接前文 1)成语答题小程序云开发记录系列一? https://developers.weixin.qq.com/community/develop/article/doc/000a64f2dbcff012591b0361356813 输入图片说明 最近从答题小程序切入成语答题这个细分领域,发现一些有意思的事情,今天跟大家谈谈 本文背景 说起答题小程序,可能更多的是面向行业,面向某部分人群,但是作为成语答题,这种天然带娱乐性质的,老少皆宜,不区分人群,任何人都可能会感兴趣来两把, 所以当你你把一个成语答题小程序分享给其他微信好友的时候,不会给人打来烦恼,反而有可能因为你的分享,而喜欢上这个答题活动 今天主要谈谈在开发和运营过程中的一些实际运营操作 本文内容 我大概从以下几个方面来讲 1)关于兑换 2)关于盈利 关于兑换,就要提到金币、红包、商品 这块我参考了很多小程序,也跟很多成语答题的运营同学有过沟通,主要的兑换粗略是这样 金币主要是用来答题的,用户进来默认会给一些金币,比如默认是5000金币; 金币获取的场景 1)用户授权,会默认5000金币; 2)用户邀请好友,邀请一个好友,会增加一定金币;(此处是否合规有待考虑) 3)观看激励式视频广告可获取一定金币(此处是否合规有待考虑) 金币消费的场景 1)答题消费,用户每作答一次,减少100金币,不管有没有正确 金币不能兑换红包,红包是另外单独的维度,在答题过程中,每次答题挑战失败,都会有一定金额的红包,这个红包金额的发放策略非常讲究任性,可以参考拼多多的砍价模式 我刚开始玩的时候,以为我做对的越多,红包给的也越多,后面跟几个运营同学聊天才发现,完全不是这么一回事,我贴个图 通过上图我们不难看出 这个红包奖励金额跟用户当前余额有关系,余额越少奖励红包越大,当用户接近兑换商品的临界值时,基本是一分,二分的递增 具体的红包获取界面截图如下所示 关于兑换 当红包累计到一定金额,可以通过兑换商品来消费掉这些红包 关于广告 对于成语答题赚这种小程序,开通流量主,通过广告变现是唯一一种盈利方式,具体几个广告位我来介绍下 首页可以安排底部banner和一个插屏广告,具体截图如下所示 [图片] 广告位置2-答题页广告位,此处建议安排视频广告,因为在这个答题的页面,往往用户持续的时间比较长,这个地方非常适合视频广告这种长时间展示的。 [图片] 3)广告位置3,商品兑换页面,此处安排一般的banner广告和插屏广告 [图片] 4)广告位置4,免费得金币模块,此处安排普通视频广告和激励式视频广告 [图片] 广告安排 最近总结了下几点广告设置的小经验 1、用户停留时间长的页面,安排视频广告 2、用户刚需页面,可安排插屏广告,由于插屏广告是可以带来操作障碍的,非刚需页面尽量不要安排这种广告类型。 [图片] 广告收益 近期运营广告收益截图如下 1 本文总结 本文通过自身最近几天的体验,总结了成语答题赚小程序的一些运营策略,希望通过这种分享,认识更多的朋友 经过一番针对广告类型的踩坑操作(此处省略一万字…😢),最终得到正解(此处感谢小伙伴的雪中送炭🙏) 本文备注 该成语答题小程序由其他几个小程序输血,包括抽奖小程序、激活码小程序以及矩阵内的其他几个小程序,都有对应的入口引导到成语答题小程
2020-11-02 - 小程序流量主、广告位类型和广告收益分析
小程序流量主、广告位类型和广告收益分析 ## 本文介绍 最近在小程序的几个微信群,经常有朋友问到以下几个问题 1、小程序怎么盈利 2、小程序流量主是什么以及怎么开通 3、小程序广告有哪些类型,哪种广告类型相对收益最大 4、 ## 小程序如何盈利 目前对个人小程序开发者而言,只有通过开通流量主,并且按照官方规范要求添加广告位,才能获取收益,当然打赏除外。 ## 什么是流量主如何开通 流量主是微信对外提供的一个服务,通过开通流量主,就可以在小程序合适的位置引入广告位,进而实现收益 登录公众号后台( https://mp.weixin.qq.com/ )在左侧菜单中,找到 推广-流量主,点击进去会看到如下截图 [图片] 小程序流量主: 1.开通条件:小程序累计独立访客(UV)1000以上,且无违规记录,即可开通流量主功能。 温馨提示:如满足条件仍无法开通,可能是数据同步问题,建议等待1-2个工作日后再试。 2.申请方法:进入微信公众平台小程序后台,点击左侧面板“流量主”,满足开通门槛的小程序开发者点击“开通”,提交财务资料,待审核通过后成功开通流量主功能,即可创建相应的广告位。 3.广告接入指引: 广告接入可查看: 微信小程序广告接入指引 在开通流量主的过程中,会绑定个人银行卡,以方便进行后续的广告收益结算,目前结算每月两次,具体官方公告可以查阅 [流量主结算周期及开票规则调整说明][2019-12-03发布] https://mp.weixin.qq.com/promotion/readtemplate?t=notice/detail_page&time=1575340587¬ice_id=634169 ## 广告类型有哪些 Banner激励式视频插屏视频广告前贴视频 以下为各广告类型,截图示例, [banner广告] [图片] [插屏广告] [图片] 视频广告 [图片] 由于插屏广告会影响用户体验,所以不建议放太多场景使用。 具体不同类型广告体验,可以扫码 [图片] 首页模块-->>插屏广告使用说明-->>视频广告关于我们-->>banner广告 ## 哪种广告类型收益相对最大 [图片] 在10月30号,将banner广告同一替换为激励式视频广告和视频广告,收益很明显从30元上升到90元、150元 可以看到视频广告相对于banner广告,对于收益增加是有用的。 下图是某小程序12月4号一天的收益数据 [图片] 12月4号一天,不同广告类型,收益分析 总收益 194.74+23.27+147.82=365.83 具体分拆来看 广告类型点击量总收益单个点击收益(元)banner1956194.740.099插屏广告6223.270.375激励式视频广告152147.820.972 通过上图我们对比分析,不难得出以下结论:激励式视频广告单个点击的收益最大、 当然我们不能通过单一维度来了解哪种收益最好,还要综合考虑,比如哪种广告对用户影响最小,毕竟不管哪种方式,广告的接入肯定会带来交互体验上的障碍, 我们必须在交互体验和广告收益这两者之间做好权衡。 ## 系统公告 激励式广告于7月31日支持30秒视频素材,广告流量将逐步放开,MP后台-广告位管理模块可支持选择6-15秒视频或6-30秒视频素材的功能,请流量主根据产品进行调整。程序视频广告已于9月4日正式全量上线,开通后即按广告曝光获得分成收入,进一步提升流量变现收益。小程序视频前贴广告组件已于8月30日正式全量上线,开通后即按广告曝光获得分成收入,进一步提升流量变现收益。## 官方文档 小程序广告组件流量主操作指引https://wximg.qq.com/wxp/pdftool/get.html?id=BJSyDkLqz&pa=14&name=miniprogramAds_supplier_manual应用规范https://wxa.wxs.qq.com/mpweb/delivery/legacy/pdftool/get.html?id=rynYA8o3f&pa=10&name=miniprogramAds_supplier_guidance小程序流量主应用规范https://wximg.qq.com/wxp/pdftool/get.html?id=rynYA8o3f&pa=10&name=miniprogramAds_supplier_guidance处罚标准https://wxa.wxs.qq.com/mpweb/delivery/legacy/pdftool/get.html?id=BkTGkbs2G&pa=1&name=miniprogramAds_supplier_regulation小程序视频广告流量主指引https://wximg.qq.com/wxp/pdftool/get.html?post_id=1317小程序视频前贴广告流量主指引https://wximg.qq.com/wxp/pdftool/get.html?post_id=1318## 总结三点 从纯收益的角度来讲,在各种广告类型中,视频广告(包含激励式视频广告、视频广告、视频前贴广告)要比banner广告要好,而且好很多从用户体验来讲,插屏广告是首次打开带插屏广告的页面强制弹出的,但是广告过后,在页面是不占空间的,这是区分与其他广告的地方,banner广告、激励式视频广告、视频广告、视频前贴广告都是在页面中占固定的空间的,这一点要小程序运营同学权衡。Banner广告是按点击,激励式视频、视频广告、插屏广告都是按照曝光来收取广告费用的,这一点非常重要,难怪我每次手工点击我的视频广告没有见流量的增加[哭脸.jpg]。[感谢 @ 仙森 补充于2019年12月9号] 虽然对个人开发者而言,我们开发小程序的目的是为了收益(当然也有为了情怀而开发),在了解如何收益的情况下,我们还是应该尽量把精力放在小程序本身的开发上面。 感谢 在此特别感谢,小程序运营讨论群的两位小伙伴,微信号中间两位已打码 1、@迭戈 (yang_##chun) 2、@风猫 (cs##26)
2020-12-25 - 在线答题小程序代码解读
在线答题小程序开发的诱因先来说说我为什么开发在线答题小程序? 九月份的时候,在小程序技术交流的微信群里,有一位朋友扔了一张下面的截图,问大家意见。 [图片] 这个小程序就是在线答题类小程序,这对个人来说,完全可以,没有UGC。 其实,像我们在四五线小城市,每个月三四千的工资已经很好了,而且是被动收入,睡后收入,睡觉的睡。 我当时看到这个信息,很心动,我想每个人都会心动的,当天晚上就在开发者工具上初始化好了项目。 跟很多人一样接下来一段时间,一直没有下文,直到最近,我身边有个在证券公司的朋友,一起吃饭的时候,说起下个周末就要考试了,而且是第二次,不知道能不能通过,很是郁闷,因为在证券公司如果不能通过证券从业资格考试,原则上是不允许转正的。 晚上下班后,我便开始设计数据库结构,题目信息表,科目表(为了扩展其他资格考试),题目类型表(维护单选、多选、判断),答题历史记录表,四张核心表足够了。 接下来我们进入小程序开发正题 小程序采用框架 未采用第三方框架,使用小程序原生框架,未引入任何UI组件库 接口采用PHP YII2框架 小程序实现的功能 ## 目前小程序已经实现的功能有: 选择科目在线答题,答题可以选择单题模式还是列表模式答题结束实时展示分数答题过程,对过题操作进行提示查看分数结束可以查看正确答案答题历史纪录查询,可以查阅当时做题情况也就是说作为一个在线答题系统,基本功能都已闭环。 开发小程序过程中遇到的问题 第一个问题:radio取值问题 在单选选择题的时候,用到以下两个表单组件 [图片] radio-group https://developers.weixin.qq.com/miniprogram/dev/component/radio-group.html radio https://developers.weixin.qq.com/miniprogram/dev/component/radio.html 默认的radio组件事件 wxml文件 {{item.value}} js文件 Page({ data: { items: [ { name: 'USA', value: '美国' }, { name: 'CHN', value: '中国', checked: 'true' }, { name: 'BRA', value: '巴西' }, { name: 'JPN', value: '日本' }, { name: 'ENG', value: '英国' }, { name: 'FRA', value: '法国' }, ] }, radioChange: function (e) { console.log('radio发生change事件,携带value值为:', e.detail.value) } }) 没错,用的就是官方示例代码,我们看到在选择的时候,默认e.detail.value,只能取一个字符串,当时遇到的第一个问题就在这里,如果把这整个选项的信息提取出来,能简单的用{{JSON.stringfy(item)}}吗,当然不可以,因为原生小程序本身不支持。 当时在社区查到解决方案具体可以参考 [单选框radio除了可以传value可以传其他的值吗?] https://developers.weixin.qq.com/community/develop/article/doc/0006ce9771c528ed7b89a6f485bc13 方案就是引入wxs,之前看官方文档,每次到这里,因为不知道这是干什么的,以及解决什么问题,现在明白了,想了解更多关于wxs的内容,也请移步下面两篇文档 [微信小程序wxs有什么用?] https://developers.weixin.qq.com/community/develop/article/doc/0008888a01c69872b689448a051013 [小程序里面精度计算问题] https://developers.weixin.qq.com/community/develop/article/doc/0000ae30ea4da802b989540175b013 第二个问题:每次10道题目是如何选择的 在答题的时候,每次会展示10个题目,这10个题目是从当前科目题库中,随机抽取10个,在题库足够大的情况下,基本可以保证每次进来答题的10个题目跟前面的答题都是不一样的。 小程序截图 [图片] [图片] [图片] [图片] [图片] [图片] [图片] [图片] 代码路径 前端小程序代码,请移步下面 https://gitee.com/xiaofeiyang3369/myexamapp 后端接口用的PHP,代码链接如下,由于我几个小程序都用这个PHP服务,项目代码要远比该小程序的PHP代码要多一些。 https://gitee.com/xiaofeiyang3369/phpapp 如果大家细心,数据库也是可以在线登录的,如果遇到问题,可以微信我。 适用人群 该开源代码适用于小程序初学者,以及大学做在线答题小程序的毕业设计时可以参考。 扫码体验 微信小程序搜索 从业资格题库或者直接扫码 [图片]
2020-06-08 - 用wordpress打造的第一个小程序:图书共享借阅
wx-booker 微信小程序-书巢小程序(图书共享预定 ) 更新至V2.0.0 体验效果 在微信中搜索小程序【建始同城共享书】或扫码体验 [图片] [图片] [图片] 初衷 通过微信小程序向当地爱书者提供一个图书共享预定平台,前期期望投入费用越少越好。同时还要有一定的变现能力,例如图书借阅费、本地商家广告投放。 V2.0.0功能新增(20200205) 完全自定义文章类型和自定义字段 增加输入ISBN一键获取豆瓣书籍信息 增加看广告显示隐藏内容短代码,可插入到正文任意位置 增加办公文档查看 增加切换风格 增加关于页面,内容在后台配置输出 增加悬浮广告位,可后台配置显示在首页/列表页/详情页 增加可关闭借阅按钮 增加若干全局设置 V1.0.0功能分解 图书分类管理 图书信息管理,包括介绍和图书相关ISBN、关联分类等 预借功能与费用管理 图书在馆状态 全局搜索图书 图书条形码扫码查询 用户管理与用户等级 广告管理 本地商家服务投放 图书订阅帮助信息 用户浏览与喜欢统计 UI交互清爽简单 程序选型 域名:阿里云购买 制作小程序域名还是必须的,这个去阿里云买一个.com域名就好。年费50元+。 服务器:阿里云ECS 这个是做互联网没办法少的,选用ECS主要因为我服务器有其他建站需求,建立多个网站使用。年费:500元+。 后台:Wordpress 强大而免费,通过插件的使用可以打造出功能完善的CMS/商城系统。年费:0元。 RESTAPI:微慕小程序插件 这个是用于将wordpress和小程序通信的插件程序,我是用的是微慕小程序插件1.6.0。年费:0元。 小程序:微信小程序个人版 前期因为无法预测变现能力,所以考虑成本,我输出的是个人版小程序。未来有更多本地商业服务的时候,移植到企业版。年费:0元。 前端:微慕开源版+自己摸索完成 小程序的前端是我通过微慕开源版3.6的代码二次开发完成,我现在Github已开源,有需要的朋友可以Clone。费用:0元。 使用 环境准备 服务器选型购买、域名购买、备案、配置环境网上很多教程这里不再赘述,注意:微信小程序要求域名需备案、https://开头。 程序安装 Wordpress安装 前往官方下载或者https://github.com/WordPress/WordPress最新版本,按网上教程一步步安装。 Wordpress主题安装 我使用raz主题,主要是我比较懒,这个主题不光是因为我网站商城需要简洁的效果,自带的文章自定义字段也方便我定义。 [图片] Wordpress插件安装 这里用到两个插件:微慕的小程序插件https://github.com/iamxjb/rest-api-to-miniprogram+全局搜索插件Search Everything,在wordpress后台安装新插件搜索进行安装。前者的RestAPI用于与微信小程序通信,后者用于小程序支持字段搜索。 [图片] [图片] [图片] 微慕开源版下载 微慕算很良心的小程序开源商,功能多,文档详细,社区活跃,值得使用。需要高级功能的可以去微慕商城购买专业版,提供在线技术支持。微慕开源版本在Github的传送门:https://github.com/iamxjb/winxin-app-watch-life.net。 微信小程序账号申请 前往微信官方注册小程序账号,后台获取你的appid和秘匙,填写相应的小程序信息。选择服务类型时要注意,个人类型的小程序在服务类型选择上要慎重,看官方文档,最好选择工具类型。 微信小程序开发环境安装 前往微信官方下载最新版本的微信开发者工具,安装后启用微慕开源版的代码,按微慕官方的帮助文档修改代码中相应的设置。 [图片] 至此,程序安装完成,这一步预览小程序的效果是微慕小程序的模板。 微信小程序-书巢小程序模板安装调试 Wordpress插件替换 Clone 微信小程序-图书共享预定到本地,其中code是小程序前端代码。plug是微慕的小程序插件的修改版,该插件务必配合使用raz主题才能用。 [图片] 小程序前端代码替换 将code里的代码复制替换微慕的前端代码即可。 [图片] 建立分类 代码中的书籍、帮助FAQ、本地活动调用的是Wordpress的三个父级目录。在Wordpress的文章分类中首先创建三个父级目录,将目录ID分别替换代码的中cateparentID/categories。三个id分别在page/index/index.js,page/faq/faq.js,page/act/act.js [图片] [图片] 写文章 自定义字段和分类记得填写和选择。注意书目、本地活动对应的字段对应不同的用途。 [图片] [图片] 配置本地活动轮播 进入wordpress后台微慕的小程序插件,输入要在小程序中轮播显示的本地活动文章ID。用于轮播显示在详情页底部和我的信息页入口。 [图片] 天气和地址配置 使用高德SDK,前往并注册账号获取key,key和city需要配置成你的信息。需要配置的文件pages/mine/mine.js,pages/amap/amap.js。SDK在Github的传送门:https://github.com/amap-demo/wx-regeo-poiaround-weather。 [图片] 微信小程序2.0.0-书巢小程序模板安装调试 将文件包插件目录下的两个插件文件安装或覆盖在wordpress的插件目录中(/wp-content/plugins/); 进入wordpress后台管理,开启pods和微慕小程序插件; 在pods插件中启用Migrate: Packages组件; 在Migrate: Packages组件中导入文件包data目录下的pods文件; 导入后,左侧菜单栏会出现【书目】【帮助】【活动】三个自定义文章以及二级菜单分类和设置等; 接下来导入书目模拟数据:进入菜单工具-可用工具中启用【导入】, 然后将文件包中的书目模拟数据导入; 将小程序代码直接在小程序开发工具中导入。 在wordpress后台配置微慕小程序的相关key 在小程序代码里的project.config.json和config.js进行配置相关key 在wordpress的书目设置和全局设置中进行相关文字设置,包括首页标题等 完成后,进行测试即可。 任何问题进QQ群反馈:928993356 20200331补充: 不知为何mac笔记本搜索不到群。先通过加我个人QQ号和个人微信号拉进群。 QQ号:2307944871 (小鱼哥) 微信号:gamch2 (小鱼哥) 感谢 感谢https://www.hellobeebee.com/ 提供空间。升级为书巢小程序官方网站。 感谢https://www.appbeebee.com/ 提供主题。V2.0.0无需安装此主题。 感谢https://shops.minapper.com/ 提供小程序代码 如果你觉得这个模板不错,请打赏馆长。 [图片] 相关资源 github https://github.com/shiheme/wx-booker gitee镜像 https://gitee.com/week7day/wx-booker
2020-03-31