我竟然用一个小时就做了个小程序!云开发+CMS实践探索

1,132 阅读6分钟

前言

在19年疫情比较严重的时候,在家里的我想要为前线的医护人员打打气,于是就开始做一些跟战疫相关的海报,并且将这些海报分享到了一些社交平台,比如某红书和某博。在这些文章有了一定热度之后就开始有人问我要图片,最初的时候人比较少我还可以一个一个私发,后来问的人越来越多,我也没办法一个一个的去回复,所以我就想办法用自己的技术解决这一个问题。

需求分析

首先我认为这个小程序需要非常简单,只有一个页面,以一个列表的形式展示海报和一些设计素材,然后有一个下载按钮,点击下载就直接保存到手机相册。列表还得有上滑加载下拉刷新功能,图片需要使用懒加载不然有的海报体积比较大。

由于我之前有用过云开发做一个校园小程序,因此我对云开发也比较熟悉,而且使用云开发CMS还可以直接生成后台,我就不用做图片上传和管理的功能了,直接通过生成的CMS去控制就OK了

下面放一下成品效果,大家也可以直接去搜索“by的设计站”这个小程序,已经上线了

小程序实现效果

image.png

后台实现效果

image.png

实现过程

小程序

小程序的目录非常简单,我只引入了一个colorUI

目录结构

image.png

wxml代码

页面中主要是滚动窗口代码,需要实现一个下拉刷新和上滑加载用到了bindrefresherrefreshbindscrolltolower这两个事件

<scroll-view 
    style="height:100vh" 
    class="bg-white" scroll-y="{{true}}" 
    refresher-enabled='{{true}}'
    refresher-triggered='{{list.flag}}' 
    bindrefresherrefresh='refresh' 
    bindscrolltolower='loadMore'>
    <view class="margin padding bg-white main-shadow" wx:for={{list.data}}">
      <view class="flex flex-direction">
        <image mode="widthFix" 
        class="card-image" src="{{item.image}}" 
        lazy-load="{{true}}">
        </image>
        <button class="cu-btn bg-black block text-bold text-lg margin-top " style="width:100%" 
        bindtap="saveToPhone"
        data-image="{{item}}">
        下载图片</button>
      </view>
    </view>
</scroll-view>

js代码

js中的代码也很简单,都是使用云开发的api对云数据库进行查询的一些操作,主要的代码量是在下载文件功能那里,这个功能我知道网上很多人分享我就直接去找了一个复制来用了,要是我自己写的也不会嵌套这么多回调地狱(偷懒)

// miniprogram/pages/home/home.js
const db = wx.cloud.database()
const _ = db.command
Page({
  data: {
    downloadNum:0,
    list:{
      data:[],//页面数组
      limit:10,//一页多少条
      page:0,//页数
      flag:false,
      nomore:false
    }
  },
  onLoad (options) {
    this.getImage()
    this.getRecord()
  },
  async getImage(){
    // 获取图片
    const res = await db.collection('image')
    .orderBy('_createTime', 'desc')
    .skip(this.data.list.page * this.data.list.limit)
    .limit(this.data.list.limit)
    .get()
    if(res.data.length){
      let temp = this.data.list.data
      temp = temp.concat(res.data)
      this.setData({'list.data':temp,'list.flag':false})
    }
    else{
      this.setData({'nomore':true})
    }
  },
  saveToPhone(e) {
    // 保存到手机
    let imgSrc = e.currentTarget.dataset.image.image;  //要保存的图片url
    wx.showLoading({
      title: '保存中...'
    })
    
    wx.cloud.downloadFile({    //下载文件资源到本地
      fileID: imgSrc,
      success:(res)=> {
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success: (data)=> {
            wx.hideLoading()
            wx.showModal({
              title:"下载成功,创作不易,欢迎点击右上角分享小程序噢!",
              cancelColor: '#b6b6b6',
              showCancel: false,
              confirmColor:"#000000",
            })
          },
          fail:(err)=> {
            if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny" || err.errMsg === "saveImageToPhotosAlbum:fail:auth denied") {
              console.log("当初用户拒绝,再次发起授权")
              wx.showModal({
                title: '提示',
                content: '需要您授权保存相册',
                showCancel: false,
                success: modalSuccess => {
                  wx.openSetting({
                    success(settingdata) {
                      if (settingdata.authSetting['scope.writePhotosAlbum']) {
                        wx.showModal({
                          title: '提示',
                          content: '获取权限成功,再次点击图片即可保存',
                          showCancel: false,
                        })
                      } else {
                        wx.showModal({
                          title: '提示',
                          content: '获取权限失败,将无法保存到相册哦~',
                          showCancel: false,
                        })
                      }
                    },
                    fail(failData) {
                      console.log("failData", failData)
                    },
                    complete(finishData) {
                      console.log("finishData", finishData)
                    }
                  })
                }
              })
            }
          },
          complete(res) {
            db.collection('record').add({
              data:{
                imageId:e.currentTarget.dataset.image._id,
                _createTime:new Date().getTime()
              }
            })
            wx.hideLoading()
          }
        })
      }
    })
  },
  loadMore(){
    // 加载更多
    this.setData({'list.page':this.data.list.page+1})
    this.getImage()
  },
  onShareAppMessage(e) {
    // 分享
    return {
      title: "欢迎光临By的设计站~",
      path: "/pages/home/home"
    }
  },
  onShareTimeline(e) {
    // 分享
    return {
      title: "欢迎光临By的设计站~",
      path: "/pages/home/home"
    }
  },
  async refresh(){
    // 刷新
    this.setData({'list.data':[],
    'list.flag':true,
    "list.page":0,
    "list.nomore":false,
    })
    await this.getImage()
  }

})

云数据库

在数据库中,我只建了两张表,一张用于存放图片数据,一张用于存放下载记录,小程序全程是不需要登录的(要登录我也没法上线了),因此也不用什么用户表之类的。

image.png

云函数

先科普一下 云函数 和小程序中的 db api 有什么区别,使用云开发我们是可以直接在前端操作数据库的,但是往往有的时候会遇到一些不适合前端做的事情,比方说大量数据的处理,一些涉及支付或者用户隐私的操作。

这时候我们就可以在云函数中写入我们要做的操作封装成一个函数,然后类似于传统开发模式前端调用后端接口一样,在小程序中使用Cloud.callFunction这个api去调用就会返回我们数据了。云函数就是我们云开发的后端,它的本质也是node.js

在这个项目中我是没有使用到云函数的,因为功能比较简单,只需要在小程序端操作数据表就好了。

后台

后台我是使用的云开发CMS,下面说一下我们要怎么生成这个后台。

首先我们要进入腾讯云的控制台,找到云开发这一项

image.png

然后进入你小程序使用那一个云环境

image.png

在左侧导航栏找到拓展应用

image.png

然后点击内容管理(CMS)这个应用,等待一会就安装好了

image.png

使用方式

在CMS安装好之后,会为我们的后台生成一个域名,我们点击进入就可以看到我们的CMS后台了。

在你第一次进入后台的时候左侧导航栏里的 内容集合 是空的,我们得建立模型来映射我们的数据表,点击左侧导航栏 内容模型 ,右上角 新建模型,然后根据里面的表单提示,填写我们要从哪一张数据表中取哪一些字段,相信自己摸索一下很快就了解了。

我自己建立了两个内容集合,分别对应我的两张数据表,然后如果我需要在图片表中添加新的数据也只需要在后台操作。

image.png

CMS实现原理

CMS的实现原理其实就和我们自己去写后台一样,只是云开发CMS是动态的,可以自己从数据库中选择需要映射的表格,我们常规的后台就是开发的时候将要展示的数据提前写好。

在CMS创建成功之后,我们可以在云开发控制台看到我们的数据表中多了很多张表,这个是用来存放CMS后台的数据,CMS也是通过 云开发的api 来实现表格的 增删查改 还有一些 上传下载 等功能

image.png

总结

CMS对于简单的小程序来说是非常方便的,但是如果遇到功能上覆盖不到的地方还是有些不方便,譬如说你需要对上传的图片进行一些裁剪或者压缩之类的。而且我在CMS刚刚出现 的时候就用了一次,那个时候在生成模型时使用联表查询还有一些数据类型无法兼容的地方,不知道现在有没有优化好。

在蛮久之前云开发CMS已经开源了CMS github仓库,我认为这是一个合适的选择,毕竟后台这种东西每一个项目都千差万别,想要做到非常通用的话又会为项目带来太多额外的代码,在通用性易用性上总要做出一个权衡,开源了大家可以基于原本的功能进行二次封装也是对这个项目价值的一个提现,也欢迎大家去点个star噢!

这里是新人oil,期待你的关注噢!