- 云开发入门
重磅打造的小程序学习路径课,从微信小程序到微信云开发体系化的学习,带来更加顺畅的学习体验。
2021-11-19 - 云开发聚合阶段如何获取上一阶段查询出来的值?
[图片] 期望查询结果: [图片] 网上大多都是多表联查,也就是都是根据第一次连接的表的字段进行查询的. 而我的需求是第二次查询的时候,是根据第一次查出来的结果中的某个字段,再进行第二次查询. [图片] 已只有如上API,可是问题是我怎么取到这个字段,又怎么进行第二次查询,具体的写法是什么?
2020-12-04 - 借助云开发搭建专属技术博客小程序丨实战
▌博客小程序介绍 主要功能: 包括文章的发布及浏览、评论、点赞、浏览历史、分类、排行榜、分享、生成海报图等。 [图片] 效果展示: [图片] ▌数据库设计 数据库主要就7张表,分别为:用户表,分类表,文章表,文章内容表,评论表,点赞表,历史浏览表。 [图片] ▌评论功能设计 以文章评论功能为例,我们来看看代码以及小程序云开发的整个流程。 1. 实现思路 一开始的实现思路是准备搞两张表,一张评论主表,一张回复评论的子表,后来想着不用这么复杂,其实就用一张表也能实现评论及回复的功能。 2. 代码实现 发表评论有三种情况,第一种是评论文章,为一级评论,第二种是评论别人的评论,为二级评论,第三种是回复别人的评论,为三级评论。 2.1 如何新增一条评论 [图片] 结合上面图片,我们再来看看代码,就很清晰了。 [代码]/** * 发布评论 */ submit() { var comment = this.data.inputData if (comment == '') { wx.showToast({ title: '请填写评论', icon: 'none' }) } else { console.log("我的评论:" + this.data.inputData) var type = this.data.type; if (type == 1) { // 1是评论别人的评论》二级评论 this.replyComment(1) } else if (type == 2) { this.replyComment(2) // 2是回复别人的评论》三级评论 } else if (type == 3) { // 3是评论文章》一级评论 this.addComment(); } } }, /** * 新增评论 */ addComment() { var _this = this; var openid = wx.getStorageSync("openid") wx.showLoading({ title: '正在加载...', }) var create_date = util.formatTime(new Date()); console.log("当前时间为:" + create_date); var timestamp = Date.parse(new Date()); timestamp = timestamp / 1000; console.log("当前时间戳为:" + timestamp); // 调用云函数 wx.cloud.callFunction({ name: 'addComment', data: { //_id: timestamp + _this.data.otherUserInfo._id, id: _this.data.articleDetail._id, _openid: openid, avatarUrl: _this.data.userInfo.avatarUrl, nickName: _this.data.userInfo.nickName, comment: _this.data.inputData, create_date: create_date, flag: 0, article_id: _this.data.articleDetail.article_id, timestamp: timestamp, childComment: [], }, success: res => { // res.data 包含该记录的数据 console.log("新增评论成功---") wx.showToast({ title: '评论提交成功', }) wx.navigateBack({ delta: 1 }) }, fail: err => { console.error('[云函数]调用失败', err) }, complete: res => { wx.hideLoading() } }) }, /** * 回复评论 */ replyComment(commentType) { var _this = this; wx.showLoading({ title: '正在加载...', }) var create_date = util.formatTime(new Date()); console.log("当前时间为:" + create_date); var timestamp = Date.parse(new Date()); timestamp = timestamp / 1000; wx.cloud.callFunction({ name: 'replyComment', data: { id: _this.data.articleDetail._id, _id: _this.data.otherUserInfo._id, avatarUrl: _this.data.userInfo.avatarUrl, nickName: _this.data.userInfo.nickName, openId: _this.data.openid, comment: _this.data.inputData, createDate: create_date, flag: commentType, opposite_avatarUrl: _this.data.otherUserInfo.avatarUrl, opposite_nickName: _this.data.otherUserInfo.nickName, opposite_openId: _this.data.otherUserInfo._openid, timestamp: timestamp, }, success: res => { // res.data 包含该记录的数据 console.log("回复评论成功---") wx.showToast({ title: '回复提交成功', }) wx.navigateBack({ delta: 1 }) }, fail: err => { console.error('[云函数]调用失败', err) }, complete: res => { wx.hideLoading() } }) }, [代码] 下面是新增评论和回复评论的两个云函数,主要用到了async和await这两个函数,让新增和回复函数执行完后我们再更新一下article文章表的评论字段,让其加1,async和await的好处就是可以让函数有序的进行,这里就不赘述。 [代码]// 新增评论云函数 const cloud = require('wx-server-sdk') var env = 'hsf-blog-product-xxxxx'; // 正式环境 // var env = 'xxxxxxxxxxxxx'; // 测试环境 cloud.init({ env: env }) const db = cloud.database() const _ = db.command exports.main = async(event, context) => { try { let res = await db.collection('comment').add({ data: { _openid: event._openid, avatarUrl: event.avatarUrl, nickName: event.nickName, comment: event.comment, create_date: event.create_date, flag: event.flag, article_id: event.article_id, timestamp: event.timestamp, childComment: [], } }).then(res => { return res; }) await db.collection('article').doc(event.id).update({ data: { comment_count: _.inc(1) } }) return res; } catch (e) { console.error(e) } } [代码] [代码]// 回复评论云函数 const cloud = require('wx-server-sdk') var env = 'hsf-blog-product-xxxxx'; // 正式环境 // var env = 'xxxxxxxxxxxxxx'; // 测试环境 cloud.init({ env: env }) const db = cloud.database() const _ = db.command exports.main = async(event, context) => { try { let res = await db.collection('comment').doc(event._id).update({ data: { childComment: _.push({ avatarUrl: event.avatarUrl, nickName: event.nickName, openId: event.openId, comment: event.comment, createDate: event.createDate, flag: event.flag, opposite_avatarUrl: event.opposite_avatarUrl, opposite_nickName: event.opposite_nickName, opposite_openId: event.opposite_openId, timestamp: event.timestamp, }) } }).then(res => { return res; }) await db.collection('article').doc(event.id).update({ data: { comment_count: _.inc(1) } }) return res; } catch (e) { console.error(e) } } [代码] 2.2 如何显示每一条评论 从数据库取出评论的数据,循环遍历每一条父评论,如果有子回复也一并循环。这里每一条评论的唯一标识是用户的openId,那么我们可以用这个做一些事情,如:可以判断如果是自己的评论是不能回复的。 [代码]<view class="comment" wx:if="{{commentList.length>0}}"> <view class="comment-line"> <text class="comment-text">评论交流</text> <view class="bottom-line"></view> </view> <block wx:for='{{commentList}}' wx:key='*this' wx:for-item="itemfather"> <view class='commentList'> <view class="top-info"> <view class='img-name'> <image src="{{itemfather.avatarUrl}}"></image> <label>{{itemfather.nickName}}</label> </view> </view> <view class="father-content"> <text class="text">{{itemfather.comment}}</text> <view class="father-reply-time"> <text class="create-time">{{itemfather.create_date}}</text> <text class="reply" data-item="{{itemfather}}" bindtap='clickFatherConter' wx:if="{{openid != itemfather._openid}}">回复</text> </view> </view> <view class="children-content"> <block wx:for='{{itemfather.childComment}}' wx:key='*this'> <view class='childComment'> <view class="child-img-name"> <view class="avatar-name"> <image src="{{item.avatarUrl}}"></image> <text class='nickName'>{{item.nickName}}</text> </view> </view> <view class="child-comment" wx:if="{{item.flag==2 }}"> <text class='huifu'>回复</text> <text class='opposite-nickName'>{{item.opposite_nickName}}</text> <text class='comment-text'>{{item.comment}}</text> </view> <view class="child-comment" wx:if="{{item.flag==1}}"> <text class='comment-text'>{{item.comment}}</text> </view> <view class="child-reply-time"> <text class="child-create-time">{{item.createDate}}</text> <text class="reply" data-item="{{item}}" data-id="{{itemfather._id}}" bindtap='clickChildrenConter' wx:if="{{openid != item.openId}}">回复</text> </view> </view> </block> </view> </view> </block> </view> [代码] ▌项目运行 1. 下载源码 在github上将代码下载到本地: https://github.com/husanfeng/hsf_blog.git ** 2. 环境准备** (1)下载小程序开发工具; (2)注册appid; (3)使用小程序开发工具导入下载的代码,填入自己注册的AppID。 3. 云开发准备 (1)开通云开发功能。 [图片] (2)创建测试环境和生产环境。 [图片] 4. 修改环境ID (1)修改app.js中的环境ID为自己的环境ID。 [图片] (2)修改所有云函数中的环境ID为自己的环境ID。 [图片] 5. 云函数部署 (1)右键云函数目录,点击在终端中打开,执行npm install。 (2)右键执行上传并部署:所有文件。 6. 构建npm (1)勾选使用npm模块。 [图片] (2)点击顶部功能栏,执行构建npm。 7. 执行编译 ▌发布注意事项 小程序现在审核也是越来越严谨了,为了不让大家在审核道路上走弯路,我把我的一些经验分享给大家。 在微信公众平台上为小程序选择正确恰当的服务类目,例如博客类的小程序就可以选择教育信息服务。 如果你的小程序需要账号密码登录,提交审核时需要提交一个账号和密码,而且这个账号不能是测试账号,不能出现测试数据。 提交审核的版本首页需要有数据展示,例如:博客小程序你需要发布一篇或者多篇文章。 文章内容不能存在敏感内容。 评论功能审核比较严格了,一旦评论中存在敏感词汇,肯定审核不通过,官方建议调用小程序内容安全API,或使用其他技术、人工审核手段,过滤色情、违法等有害信息,保障发布内容的安全。 源码地址 https://github.com/TencentCloudBase/Good-practice-tutorial-recommended 如果你想要了解更多关于云开发CloudBase相关的技术故事/技术实战经验,请扫码关注【腾讯云云开发】公众号~ [图片]
2019-12-26 - 使用lookup查询时被连接集合的数量怎么控制?
使用lookup查询时被连接集合的数量怎么控制?比如这个列子中 books 返回的数量。 const db = cloud.database() db.collection('orders').aggregate() .lookup({ from: 'books', localField: 'book', foreignField: 'title', as: 'bookList', }) .end() .then(res => console.log(res)) .catch(err => console.error(err))
2020-06-25 - view 这样布局怎么写?尤其是view上浮动另一个view ?
如题 [图片]
2020-07-17 - 小程序改变视频资源src切换视频不能播放
- 当前 Bug 的表现(可附上截图) - 预期表现 - 复现路径 - 提供一个最简复现 Demo 现象: 切换新的视频回黑屏 步骤: 1. 第一次进入页面, 点击播发后请求后台接口拿到视频资源,把视频链接赋值给video的src, 在setData的回调函数里调用视频play方法, 视频可以 播放 2. 切换视频, 重新赋值video的src, 在setData的回调函数里调用play方法, 视频播放器会黑屏, 并且点击视频播放起播放按钮没有作用, 3. 接下来调用视频pause方法, 再此调用play方法, 即可以重新播放视频 总结: 目前判断是给视频资源src重新赋值后会出现此现象
2019-03-08 - 如何编写一个云函数
首先在我之前开发云函数,从来没有一次成功过,经常要部署四五次,每次部署成功之后,小程序端调用都是以失败告终,重复,删除云函数列表里面的云函数,删除本地,新建等一系列操作。 一直没有摸索一个,一次就能成功的正确方式 今天趁着东哥在bilibili的小程序云开发直播课程,顺便问了这个问题,课程下来学到以下三个问题: 1、增量更新 3、同步云函数列表 3、正确部署一个云函数的顺序 第三个知识点我最重要的 本文的重点在这里,上面都是文字凑数的,如何新建一个云函数 1、创建云函数 2、手工package.json添加依赖包 3、执行cnpm i [图片] 4、编写云函数代码 5、本地调试 6、创建并部署 所有文件,一定是下面第二个选项 [图片] 1 [图片] 2 [图片] 3 [图片] 4
2020-05-12 - lookup联表查询,如何控制右表查询的数量条件?
[图片] 联表查出这样的结果,我如何控制timelist里的数量,条件
2020-02-29 - 微信小程序视频加轮播图示例
index.html [代码]<view style="padding:0" data-e="{{e}}" bindtouchstart="start" bindtouchend="end"> <swiper wx:if="{{!videoSrc}}" current="{{current}}" class="swiper" circular="{{true}}" indicator-dots="{{true}}" data-e="{{e}}" bindchange="changeCurrent"> <view wx:for="{{info}}" wx:key="this"> <swiper-item> <image src="{{item.img}}" class="banner" mode='aspectFill' /> <image class="play" wx:if="{{item.type == 'video'}}" src='/images/play.png' bindtap="play" data-item="{{item}}"/> </swiper-item> </view> </swiper> <!-- 视频的autoplay和controls属性要加上,不加有的安卓手机在缓冲的时候没有loading效果 --> <video enable-progress-gesture="{{false}}" custom-catch="{{false}}" wx:if="{{videoSrc}}" class="video" autoplay="{{true}}" src="{{videoSrc}}" controls="controls" bindpause="handleStop" ></video> </view> [代码] index.js [代码]//index.js //获取应用实例 const app = getApp() Page({ data: { info: [], videoSrc:'', videoImg:'',//视频封面,缓冲时会出现黑屏,加视频封面会提升用户体验 autoplay:true, touchX:0,//手指按下时x的坐标 touchY:0,//手指按下时y的坐标 interval:null,//计时器 time:0,//按下到松开的时间 current:0//swiper的当前轮播图下标 }, //事件处理函数 play: function(val) { this.setData({ videoSrc: val.currentTarget.dataset.item.video, autoplay: false, videoImg: val.currentTarget.dataset.item.img}) }, //禁止视频的手动控制进度属性,监听手指移动去滑动轮播图(手指滑动轮播图和控制视频进度事件冲突) //手指开始触屏 start:function(e){ //获取触摸的原始点 this.setData({ touchX: e.touches.length>0 ? e.touches[0].pageX : 0, touchY: e.touches.length > 0 ? e.touches[0].pageY : 0 }) let timeNew=this.data.time //开始记录时间 this.data.interval=setInterval(()=>timeNew++,100) this.setData({time:timeNew}) }, //手指结束触屏 end:function(e){ let touchX = e.changedTouches.length > 0 ? e.changedTouches[0].pageX : 0 let touchY = e.changedTouches.length > 0 ? e.changedTouches[0].pageY : 0 let tmX = touchX - this.data.touchX let tmY = touchY - this.data.touchY if(this.data.time < 10){ let absX = Math.abs(tmX) let absY = Math.abs(tmY) if(absX > 2*absY){ console.log('5555') //滑动swiper,视频停止播放 this.setData({ autoplay:true, videoSrc:'', videoImg:'' }) if(tmX < 0){ //左滑 console.log('左滑') this.setData({ current : this.data.current == (this.data.info.length-1) ? 0 : this.data.current+1 }) }else{ //右滑 console.log('右滑') this.setData({ current : this.data.current>0 ? this.data.current-1 : this.data.info.length-1 }) } } } clearInterval(this.data.interval) this.setData({time:0}) }, handleStop:function(){ this.setData({ videoSrc: '', autoplay: true, videoImg:''}) }, changeCurrent:function(e){ //手指滑动轮播图已经在视频播放的时候做了,这里只需要做轮播图自动滚动,但是不停的调用setData可能会出现一些未知的bug,可根据需求场景设置 if(e.detail.source == 'autoplay'){ this.setData({current:e.detail.current}) } }, onShow: function() { //从后台拿回数据,写在onshow里面是因为返回此页面时,根据需求去判断是否拿swiper数据 let info = [{ id: '1', img: 'http://img4.imgtn.bdimg.com/it/u=3882508552,2527877926&fm=26&gp=0.jpg', type: 'video', video: 'https://interface.sina.cn/wap_api/video_location.d.html?cid=37766&table_id=36885&did=icezueu6231605&vt=4&creator_id=1001&vid=30447305701&video_id=304473057&r=video.sina.cn%2Fnews%2F2019-09-16%2Fdetail-iicezueu6231605.d.html&wm=30490005525198963&time=1577412073497&rd=0.018419081320328656' }, { id: '2', img: 'http://img2.imgtn.bdimg.com/it/u=3355791600,110151422&fm=26&gp=0.jpg', type: 'img', video: '' }, { id: '3', img: 'http://img3.imgtn.bdimg.com/it/u=2846566051,411513524&fm=26&gp=0.jpg', type: 'img', video: '' } ] this.setData({info}) }, onHide:function(){ //小程序进入后台禁止轮播,防止swiper出现抽搐的bug this.setData({ autoplay:false, videoSrc:'', }) } }) [代码] index.wxss [代码].swiper{ width:100%; height:450rpx; } .banner{ width:100%; height:100%; } .play{ width:100rpx; height:100rpx; position:absolute; top:175rpx; left:50%; margin-left:-50rpx; } .video{ width:100%; height:450rpx; position:absolute; top:0; } [代码] 效果图 [图片] [图片] github 项目地址:https://github.com/kesixin/VideoDemo
2020-05-09