2024年最全小程序云开发实战:推文留言板_微信小程序留言板开发github,2024年最新一线互联网架构师360°全方面性能调优

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

*要点3:如何判断用户权限,让不同用户看到不同内容

原理很简单,但是自己摸索需要花点时间,我在另一篇博客中单独讲了这个问题:

小程序开发笔记:判断用户权限,显示不同内容

*要点4:云函数的初始化使用

云函数的使用其实就是遵循一个固定格式。创建一个云函数后会给你一个框架模板,然后自己手动删除不用的代码,写自己的功能就行了,多看几个云函数找找共同点。调用云函数同样是一个框架,把逻辑写在框架里就行了。

官方文档中对云函数初始化使用讲得很详细了,网上其他博客大多照搬官方文档。其实你不用了解那么原理,框架给你了你会用就行。

要注意的是调用云函数时怎么给云函数传递参数

*要点5:云数据库的增删查改

官方文档中云开发部分云数据库的使用讲得很详细,底下有云函数调用的实例代码,直接复制过来改参数就行。

*要点6:如何调用获取用户信息授权

这是使用微信官方的 API 接口。官方文档里这部分讲的很详细,而且有示例代码。其他什么获取位置啊,获取相册啊都可以参看官方文档。

*要点7:form 表单组件的使用和用户输入数据的提交

后续会写一篇博客单独来说说。官方文档有说,不过显得有点复杂。

*要点8:用户输入字数的统计与实时显示

我参考了这篇博客,写得不错。

*要点9:wx:for 的使用

后续会单独写博客

*要点10:微信小程序权限管理问题:为什么我不能改其他人的数据?

请看我另一篇博客:
小程序云开发笔记 :数据库权限管理问题

*要点11:如何把 wxml 中组件的唯一 id 传递到 js 文件中

后续会单独写博客

*要点12:如何在小程序里新建结构相同内容不同的页面

后续会单独写博客


二、前言

或许部分人不了解,在18年三月以后注册的微信公众号都没有了留言功能。现在新账号想要有留言功能,只有通过迁移合并原来有留言功能的公众号,全套搞下来价格不便宜。还有一个曲线救国的方法就是使用小程序插入到推文底部,用户点击可打开小程序查看留言,虽然体验差一点但是也比没有好。但是看了几个提供小程序留言服务的,也要几十上百块,还要担心数据安全问题。所以我早就想自己做个小程序来实现这个功能,只是一直没太多时间去学去做,现在在家废了半个月后,就决定把这件事完成。


三、背景

我以前并没接触过网页开发、小程序开发,只能看懂一点点基本代码,在这一块基本算个小白。所以我觉得我这次实战总结应该也很适合像我一样的小白作为入门项目

先说一下我开始做这个之前先做了哪些功课吧。

首先我在B站上跟一个up主上手小程序实战。是的我没有去学语法结构等理论的东西,因为不想花那么多时间,对我个人来说边实践边学比较快。可惜的是那个up主只还没更新完,只做了天气查询、视频播放等普通的小程序,还没开始云开发。

然后我继续看了腾讯云大学小程序云开发基础实战课程(也是在B站),并跟着做了基于云开发的待办事项小程序。看这个的感觉就是,不愧是官方出的视频,质量是真的高,而且里面很多很方便操作是普通up主不知道的,强烈推荐。

在这之前我一共做了三个普通一个云开发小程序,学会了基本操作,大概花了5天。写留言板用了整整两天半,途中遇到不少问题,就参考着微信官方文档和网上一些乱七八糟的资料琢磨着解决了。

这是微信官方文档,学会使用和查询非常重要!


这里不得不吐槽一下:

网上关于小程序入门的教程博客不少,但是那也太入门了!再深入一点的我都没怎么找到,很多问题我只能靠官方文档和视频慢慢尝试。所以我接下来将会把踩过的坑都单独挑出来捋一捋,方便像我一样的初学者学习。


四、开始

1、前期配置
(1) 初始化云开发环境

创建云开发项目,然后进入云开发控制台初始化环境。在数据库中添加三个集合:author、message、msgPages

可参看官方文档的云开发部分

(2) 安装 node.js 和引入 Vent UI

*要点1:node.js 及 npm 命令使用

*要点2:Vent UI 组件库的使用

具体操作可看我的另一篇博客:

微信小程序开发笔记:Vant Weapp 的 npm 引入和使用

2、文件结构

我会从每个组件开始,怎么设置布局,怎么实现功能来讲解。每个代码块会在第一行标识当前在哪个文件中。
在这里插入图片描述

3、全局配置

全局配置中 json 文件需要开启页面下拉刷新,以及添加自定义组件。js 文件使用的云开发默认配置,如果你要改环境在 env 参数后改就行了。

// app.json
{
  "pages": [
    "pages/index/index",
    "pages/msgPages/msgPages"
  ],
  "window": {
    "backgroundColor": "#F6F6F6",
    "backgroundTextStyle": "dark",
    "navigationBarBackgroundColor": "#F6F6F6",
    "navigationBarTitleText": "WhiteApple 留言板",
    "navigationBarTextStyle": "black",
    "enablePullDownRefresh": true	//允许页面下拉刷新
  },
  "usingComponents": {	//调用自定义组件(我这里使用的 Vent UI)
    "van-button": "@vant/weapp/button",
    "van-popup": "@vant/weapp/popup",
    "van-cell": "@vant/weapp/cell",
    "van-cell-group": "@vant/weapp/cell-group",
    "van-skeleton": "@vant/weapp/skeleton",
    "van-tag": "@vant/weapp/tag"
  },
  "sitemapLocation": "sitemap57.json"
}

//app.js
App({
  onLaunch: function () {
    if (!wx.cloud) {
      console.error('请使用 2.2.3 或以上的基础库以使用云能力')
    } else {
      wx.cloud.init({
        // env 如不填则使用默认环境(第一个创建的环境)
        //env: 'liuyan-x1zol',
        traceUser: true,
      })
    }
    this.globalData = {}
  }
})

4、留言板页面

项目刚开始的时候是直接把 index 页面作为留言板的,后来基础功能全部完成后才再做了主页面,用来创建不同留言板。所以我写博客也把留言板页面写在前面了,大家也可以考虑先到文章底部看看主页面的代码。

基本思路:最上方有留言按钮,点击打开输入弹窗,输入内容后点击下方提交按钮提交留言。留言按钮下方是留言内容,每条留言左边是头像,右边上面是昵称和置顶标签,下面是内容。加入了管理员功能后,管理员能看到每条留言下面的置顶、回复、删除按钮,点击可实现相应功能。

每新建一条留言就是在云数据库的集合 message 中新增一条数据,这条数据对应的唯一 id 也就是这条留言对应的 id,后续通过这个唯一 id 找到这条留言,可以对这条留言内容进行增删查改。

这是留言板页面的 js 文件中 Page 外连接数据库和 Page.data 中的参数,接下来的所有 js 代码都是在 Page({ }) 中,为方便不再把 Page({ }) 写出来

//pages/msgPages/msgPages.js
//连接数据库
const db = wx.cloud.database();
const message = db.collection("message");
const author = db.collection("author");

Page({
  data: {
    maxNumber: 140,//可输入最大字数
    number: 0,//已输入字数
    
    show: false,  //是否弹出留言面板
    showReply: false, //是否弹出回复面板
    authority: false, //鉴权
    loading: true,  //是否正在加载
    textValue:"",	//输入框内容
    replyMsgId:"",	//回复留言的id
    qr:"",	//小程序码的路径

    //留言数据
    pageId:"",
    name:"",
    imageSrc:"",

    msgList:[]
  },
    
    //……更多函数
})

(1) 进入页面初次加载

*要点3:如何判断用户权限并让不同权限得用户看到内容不同

*要点4:云函数的初始化使用

*要点5:云数据库的增删查改

用户刚进入页面,触发 onLoad 事件,首先判断用户权限,然后获取当前页面参数(方便后面每条留言都对应一个页面参数,通过参数判断在哪个留言板留的言),然后刷新页面显示留言数据。

//pages/msgPages/msgPages.js
// 监听页面加载
  onLoad: function (options) {
    // console.log(options.id)
    this.authentication();	//判读权限
    this.setData({
      pageId: options.id	//当前页面参数
    })
    this.getData();	//刷新页面数据
  },
      
//判断用户权限
  authentication:function(){   
    wx.cloud.callFunction({	//调用云函数返回openid
      name: 'login',
      complete: res => {
        db.collection('author').get().then(res2 => {
          if (res.result.userInfo.openId === res2.data[0]._openid){
            this.setData({
              authority:true	//如果和数据库中管理员id一致,则管理员权限打开
            })
          }
        })
      }
    })
  },
      
// 页面刷新获取数据
  getData:function(e){
    wx.cloud.callFunction({	//使用云函数
      name: 'getData',
      data: {
        id:this.data.pageId,
        db:'message',
      }
    }).then(res => {
      console.log(res.result.data)
      this.setData({
        msgList: res.result.data,	//将云函数返回的云数据库的内容赋给msgList
        loading: false	//停止页面加载动画
      })
    })
  },

这里调用了login云函数,用来返回用户的 openid 完成身份权限识别。这个云函数其实是建立云开发项目时就有的模板项目:

//cloudfuctions/login/login.js
const cloud = require('wx-server-sdk')
// 初始化 cloud
cloud.init({
  // API 调用都保持和云函数当前所在环境一致
  env: cloud.DYNAMIC\_CURRENT\_ENV
})
exports.main = (event, context) => {
  console.log(event)
  console.log(context)
  const wxContext = cloud.getWXContext()
  return {
    event,
    openid: wxContext.OPENID,
    appid: wxContext.APPID,
    unionid: wxContext.UNIONID,
    env: wxContext.ENV,
  }
}

这里还调用了一个获取数据库里数据的云函数。其实这个是可以本地调用的,但是小程序的限制,本地一次最多取20条数据,而云函数一次最多可取100条,鉴于留言板很可能超过20条留言,就直接用云函数取数据了。

这里使用了对数据库操作的 get 方法和查询数据库的 where 指令。使用 where 是为了只找出对应当前留言板页面 id 的留言,就不会把其他留言板页面的留言也找出来了。

//cloudfuctions/getData/getData.js
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const _ = db.command
exports.main = async (event, context) => {
  try {
    return await db.collection(event.db).where({
      pageId:event.id
    })
    .get()
  } catch (e) {
    console.error(e)
  }
}

(2) 下拉刷新

在全局的 app.json 中设置 "enablePullDownRefresh": true 后就允许页面下拉,但是下拉刷新的功能还是得自己写。也很简单,就是刷新页面,在刷新的时候启动一下刷新动画。

//pages/msgPages/msgPages.js
// 监听下拉
  onPullDownRefresh: function () {
    this.setData({
      loading: true
    });
    this.getData();
  },

(3) 留言按钮

*要点6:如何调用获取用户信息授权

留言按钮绑定了获取用户信息事件,先申请获取用户信息再留言。

<!-- pages/msgPages/msgPages.wxml -->
<!-- 留言按钮 使用了VentUI中的button组件 -->
<view class="leaveBtn">
  <van-button
 type="primary" 
 size="large"
 round
 open-type="getUserInfo"
 bindgetuserinfo="onInfo"
 >留言</van-button>
</view>

这些参数都可以在官方文档找到是干嘛的,就不多说了。

/\* pages/msgPages/msgPages.wxss \*/
.leaveBtn{
  width: 90%;
  height: 100rpx;
  margin:5%;
  margin-bottom: 2%;
}

//pages/msgPages/msgPages.js
//获取用户信息
  onInfo:function(e){
    console.log(e.detail.userInfo)
    if (e.detail.errMsg === "getUserInfo:ok"){
      this.showPopup()	//调用弹出面板函数
      this.setData({	
        imageSrc: e.detail.userInfo.avatarUrl,
        name: e.detail.userInfo.nickName,
      })
    }
  },
      
//弹出面板函数
  showPopup() {
    this.setData({ show: true });
  },
  onClose() {
    this.setData({ show: false });
  },

(4) 留言弹窗

*要点7:form 表单组件的使用和用户输入数据的提交

*要点8:用户输入字数的统计与实时显示

这个 popup 组件弹出是通过 show 属性控制的,点击留言按钮获取身份后调用弹起留言弹窗函数将其变为 true,弹窗就出来了,具体使用方法可以去看 Vent UI 官方文档。

提交留言使用了 form 表单组件,绑定提交事件,和属性设置为 formType="submit" 的提交按钮配合使用,点击后即可将组件内的内容提交。这里要注意的是 VentUI 中的按钮不能作为提交按钮,只能使用普通按钮

输入框使用的 textarea 多行输入组件,同时加入了 span 组件来计算输入字数,这里设置最大输入140字。

<!-- pages/msgPages/msgPages.wxml -->
<!-- 留言弹窗 使用了VentUI中的popup组件-->
<van-popup
 show="{{ show }}" 
 bind:close="onClose"
 position="bottom"
 custom-style="height: 70%;"
 round
 closeable
 >
  <!-- 提交留言 -->
  <form bindsubmit="onSubmit">
    <view class="writeView">
      <van-cell title="请留言"/>
      <view class="textArea">
        <textarea
 value="{{textValue}}"
 style="height: 10em" 
 maxlength="{{maxNumber}}" 
 placeholder="请输入留言内容" 
 placeholder-style="color:gray;"
 name="msgInput"
 bindinput='inputText'/>
        <span class="wordWrap">{{number}}/{{maxNumber}}</span>
      </view>
    </view>
    <view class='submitBtnView'>
      <button type="primary" formType="submit" plain="true">提交留言</button>
    </view>
  </form>
</van-popup>

/\* pages/msgPages/msgPages.wxss \*/
.textArea{
  background-color: #F6F6F6;
  margin: 20rpx;
  border-radius: 16rpx;
  box-sizing: content-box;
  padding: 20rpx;
  position: relative;
}
.submitBtnView{
  margin: 20rpx;
}
.wordWrap {
  position: absolute;
  right: 12rpx;
  bottom: 12rpx;
}

提交留言后,就把数据存入云数据库中 message 集合(最开始初始化过),然后刷新页面显示留言。

//pages/msgPages/msgPages.js
//提交留言
  onSubmit:function(e){
    // console.log(e.detail.value.msgInput);
    message.add({	//存入数据库
      data: {
        imageSrc: this.data.imageSrc,
        name: this.data.name,
        text: e.detail.value.msgInput,
        pageId:this.data.pageId,
      }
    }).then(res => {
      wx.showToast({	//提示弹窗
        title: "留言成功",
        icon: "success",
        success: res2 => {
          this.setData({
            textValue: ""	//让输入框内容清空
          });
          this.getData();	//调用刷新页面函数(进入页面时讲了)
        }
      })
    })
  },

//监听记录输入字数
  inputText: function (e) {
    let value = e.detail.value;
    let len = value.length;
    this.setData({
      'number': len
    })
  },

(5) 留言内容

*要点9:wx:for 的使用

*要点10:微信小程序权限管理问题:为什么我不能改其他人的数据?

*要点11:如何把 wxml 中组件的唯一 id 传递到 js 文件中

这部分代码看起来有点复杂,其实分为了两个部分,前面一部分是置顶板块,后面一部分是普通留言板块,使用 wx:if='{{item.top}}' 判断是否是置顶留言,两部分代码基本一样。整体上使用 VentUI 中的 skeleton 骨架屏组件框起来,作用是内容加载中时显示骨架屏加载动画,提高用户体验。

一个板块分为三个部分,左边的头像,右边上面的昵称和标签,下面的留言内容和作者回复内容。以及最底下的管理员才可见的管理按钮,使用 wx:if='{{authority}}' 判断权限,只有管理员能看见。使用了 wx:for="{{msgList}}" 循环取出 msgList 所有留言条目,循环添加到留言部分。

<!-- pages/msgPages/msgPages.wxml -->
<!-- 留言内容 使用了VentUI中的skeleton组件-->
<van-skeleton
 title
 avatar
 row="3"
 loading="{{loading}}"
>
  <!-- 置顶板块 -->
  <block wx:for="{{msgList}}" wx:key="\_id" wx:if='{{item.top}}'>
    <view class="msgContent">
      <!-- 头像部分 -->
      <view class="imgView">
        <image src = "{{item.imageSrc}}" class = "headImg"></image>
      </view>
      <!-- 内容部分 -->
      <view class='msgText'>
        <view class = 'nameView'>
          <text class='nameText'>{{item.name}}</text>
          <van-tag mark wx:if='{{item.top}}'>置顶</van-tag>
        </view>
        <text selectable="{{true}}">{{item.text}}</text>
        <view class = 'replyView' wx:if='{{item.reply}}'>
          <view>
            <text style="color:green;font-weight:800">> </text>
            <text style="color:gray">作者回复</text>
          </view>
          <text selectable="{{true}}">{{item.reply}}</text>
        </view>
      </view>
    </view>
    <!-- 按钮部分 -->
    <view class = "manageBtn" wx:if='{{authority}}'>
      <van-button size="mini" plain bindtap="toTop" data-msgid='{{item.\_id}}' data-msgdata='{{item}}'>置顶</van-button>
      <van-button size="mini" plain bindtap="showRe" data-msgId='{{item.\_id}}'>回复</van-button>
      <van-button size="mini" type="danger" plain bindtap='delect' data-msgId='{{item.\_id}}'>删除</van-button>
    </view>
  </block>
    
  <!-- 普通板块 -->
  <block wx:for="{{msgList}}" wx:key="\_id" wx:if='{{!item.top}}'>
    <view class="msgContent">
      <!-- 头像部分 -->
      <view class="imgView">
        <image src = "{{item.imageSrc}}" class = "headImg"></image>
      </view>
      <!-- 内容部分 -->
      <view class='msgText'>
        <view class = 'nameView'>
          <text class='nameText'>{{item.name}}</text>
          <van-tag mark wx:if='{{item.top}}'>置顶</van-tag>
        </view>
        <text selectable="true">{{item.text}}</text>
        <view class = 'replyView' wx:if='{{item.reply}}'>
          <view>
            <text style="color:green;font-weight:800">> </text>
            <text style="color:gray">作者回复</text>
          </view>
          <text selectable="true">{{item.reply}}</text>
        </view>
      </view>
    </view>
    <!-- 按钮部分 -->
    <view class = "manageBtn" wx:if='{{authority}}'>
      <van-button size="mini" plain bindtap="toTop" data-msgid='{{item.\_id}}' data-msgdata='{{item}}'>置顶</van-button>
      <van-button size="mini" plain bindtap="showRe" data-msgId='{{item.\_id}}'>回复</van-button>
      <van-button size="mini" type="danger" plain bindtap='delect' data-msgId='{{item.\_id}}'>删除</van-button>
    </view>
  </block>
</van-skeleton>

/\* pages/msgPages/msgPages.wxss \*/
.imgView{
  width: 15%
}
.headImg{
  width: 70rpx;
  height: 70rpx;
  margin:20rpx;
}
.msgContent{
  display: flex;
  justify-content: center;  
  width: 100%;
  border-top: 1rpx solid #eee;
  margin-bottom: 10rpx;
}
.msgText{
  display: flex;
  flex-direction: column;
  width: 90%;
  padding: 20rpx;
  word-wrap:break-word;
}
.nameView{
  display: flex;
  flex-direction: row;
}
.nameText{
  color: gray;
  font-weight: 600;
  margin-right: 15rpx;
  margin-bottom: 3rpx;
}
.replyView{
  margin-top: 10rpx;
  display: flex;
  flex-direction: column;
  word-wrap:break-word;
}

这部分首先是从云数据库中获取数据然后将数据显示出来。就是最上面页面加载时就使用了的 getData() 方法。数据赋给了 msgList 以后,循环这个列表把数据显示在留言区域,前面都说过了。

这里给出的 js 代码是实现置顶、删除功能的,回复功能下面会单独讲。主要原理是调用相应的云函数,给云数据库中这条留言的数据中添加或者更改值。为什么要使用云函数实现?因为微信小程序给的权限有限,当开始我这里都是写的本地实现,可是创建者只能更改自己创建的数据,要能更改所有数据只能使用云函数

//pages/msgPages/msgPages.js
// 置顶
  toTop:function(e){
    if (!e.currentTarget.dataset.msgdata.top) {	//判断现在是否为置顶状态
      wx.cloud.callFunction({
        name: 'toTop',
        data: {
          id: e.currentTarget.dataset.msgid,
        }
      }).then(res => {
        wx.showToast({
          title: "置顶成功",
          icon: "success",
          success: res2 => {
            this.getData();	//重新刷新页面
          }
        })
      })
    }else{
      wx.cloud.callFunction({
        name: 'notTop',
        data: {
          id: e.currentTarget.dataset.msgid,
        }
      }).then(res => {
        wx.showToast({
          title: "取消成功",
          icon: "success",
          success: res2 => {
            this.getData();	//重新刷新页面


**深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

![](https://i-blog.csdnimg.cn/blog_migrate/1e210328c1ebf43f08d3b6c5f5c13b48.png)
![img](https://img-blog.csdnimg.cn/img_convert/15dedefedb527788837e2dc9137699c1.png)
![img](https://img-blog.csdnimg.cn/img_convert/64d61964e46d00bc94a5df011478f28b.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**

}
        })
      })
    }else{
      wx.cloud.callFunction({
        name: 'notTop',
        data: {
          id: e.currentTarget.dataset.msgid,
        }
      }).then(res => {
        wx.showToast({
          title: "取消成功",
          icon: "success",
          success: res2 => {
            this.getData();	//重新刷新页面


**深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

![](https://i-blog.csdnimg.cn/blog_migrate/1e210328c1ebf43f08d3b6c5f5c13b48.png)
[外链图片转存中...(img-kw0MNYkx-1715659015180)]
[外链图片转存中...(img-INv23kqB-1715659015180)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值