# BaseCloud **Repository Path**: goolinlee/BaseCloud ## Basic Information - **Project Name**: BaseCloud - **Description**: BaseCloud是一套基于uniapp、uniCloud、uni-id的全栈开发框架,不依赖任何第三方框架,极度精简轻巧。 在开发前端界面时,除了适配移动端外,它对PC端也做了良好的适配; 在开发云函数时,它可以为您提供拦截器配置、路由管理、分页、列表、单数据快速查询等功能。除此之外,对于一些业务开发中的常用函数也已做好封装,拿来即用。 在BaseCloud的初始化项目模板中,为您实现了贯穿前后端的业务模块:管理员登录、用户管理、菜单管理、角色与权限管理、操作日志、系统参数配置等项目通用的基础后台管理功能,这一切全都基于云函数开发。 - **Primary Language**: NodeJS - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 30 - **Created**: 2020-09-07 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 项目简介 BaseCloud是一套基于uniapp、uniCloud、uni-id的全栈开发框架,不依赖任何第三方框架,极度精简轻巧。 在开发前端界面时,除了适配移动端外,它对PC端也做了良好的适配; 在开发云函数时,它可以为您提供拦截器配置、路由管理、分页、列表、单数据快速查询等功能。除此之外,对于一些业务开发中的常用函数也已做好封装,拿来即用。 在BaseCloud的初始化项目模板中,为您实现了贯穿前后端的业务模块:管理员登录、用户管理、菜单管理、角色与权限管理、操作日志、系统参数配置等项目通用的基础后台管理功能,这一切全都基于云函数开发。 ## 项目价值 基于BaseCloud的快速开发UI样式库,可以快速拼装前端界面,高还原度实现设计图效果,兼顾高效与灵活。 基于BaseCloud的云函数公用模块,你可以轻松实现单云函数、多云函数的路由管理、请求拦截管理与权限控制、常用业务函数快速开发。 基于BaseCloud的客户端缓存管理机制,你可以大幅度减少应用的云函数重复调用请求,未来云函数开始计费后,至少节省应用50%的流量费用。 基于BaseCloud的管理后台项目模板,你可以快速初始化一套自带用户、菜单、角色、权限、操作日志、系统参数管理的管理后台项目,在此基础上开始你的项目开发。 当然,这一切都只是刚刚开始,未来我们会基于BaseCloud推出更多贯穿前后端的业务模板,只要您的项目是基于BaseCloud框架,所有的业务模板拿来即用,5分钟快速集成到项目内,无需重复开发前端和后端。 对于开发者而言,基于BaseCloud的全栈快速开发框架,你可以封装自己的贯穿前后端的业务模块,发布到付费业务模块插件市场。 对于企业而言,基于BaseCloud的全栈快速开发框架,无需再费心招募不同工种不同技术栈的工程师,您只需要找到熟悉BaseCloud的工程师,让他们各自独立负责一个业务模块。 未来基于BaseCloud的项目,将会被拆分成几十个甚至上百个独立的模块,每个模块由一个工程师从前端到后端全链路负责,而他只需要懂一门开发语言:javascript,熟悉一个框架:BaseCloud。 项目交付后,您更无须担心后期维护与迭代更新,基于BaseCloud统一的开发规范,您可以很轻松找到随时能够接管您的项目的工程师。 [BaseCloud使用说明文档 << ](http://base-cloud.joiny.cn/docs/#/?isClient=base-cloud) ## 使用过程中如有问题,请加BaseCloud用户交流QQ群: 如果你想入手云开发,本框架是绝佳的学习素材和项目快速搭建方案,学习过程中有问题,快来群里提问,专业客服妹子秒回复。 群号:649807152 [点击链接,直接加入qq群 << ](https://qm.qq.com/cgi-bin/qm/qr?k=upb9fG80Wpsls_At8ZI01QTqDu_0KyUL&jump_from=webapi)  #业务模块快速集成 - 持续开发中 1. [用户端-手机号码登录业务模块 << ](https://ext.dcloud.net.cn/plugin?id=295) 2. [用户端-APP版本检测更新业务模块(含管理端功能) << ](https://ext.dcloud.net.cn/plugin?id=2510) ## BaseCloud项目构成 1. `common>base-cloud.scss` 基础样式库,适配移动端和PC端,22kb。 2. `common>js>base-cloud-client.js` 客户端SDK,14.2kb。 3. `cloudfunctions>common>base-cloud` 云函数公共模块,13.9kb。 4. `components` PC端常用业务组件目录 ## 项目预览 1. 用户端APP版本更新业务模块:    2. 用户端手机号登录业务模块:  3. 管理后台业务模块:    [管理后台演示项目地址:https://base-cloud.joiny.cn <<](https://base-cloud.joiny.cn) 账号:admin 密码:123123123 ## 快速开始 1. 请先下载BaseCloud管理后台项目模板,并导入到Hbuilder中 2. 右键点击cloudfunctions目录,选择一个服务空间,支持阿里云、腾讯云。 3. 找到cloudfunctions目录下的db_init.json数据库初始化文件,右键选择“初始化数据库”。 4. 右键点击cloudfunctions目录,选择上传所有云函数以及公共模块。 5. 点击运行到浏览器,运行成功后,在浏览器中进入登录页,初始账号:admin ,初始密码:123123123 6. !!!!特别注意:如果您初次从插件市场导入项目,由于npm install创建的软链接失效,修改公共模块中的代码将不会同步更新到云函数中, 例如:修改`common > base-cloud > config.js` 中的拦截器配置信息,`amdin`函数中的`node_modules > base-cloud > config.js` 可能不会同步更改, 如果遇到这种情况请删除`admin > node_modules `和`admin > package-lock.json`,然后重新安装`admin`函数的公共模块: >在要引入公用模块的云函数目录,执行npm init -y生成package.json文件。 执行npm install ../common/base-cloud引入base-cloud模块。 ## 使用过程中如有问题或建议,请移步gitee提交issue。 [提交问题、建议](https://gitee.com/phoooob/BaseCloud/issues) ## 项目结构介绍 请务必对照仔细浏览项目目录介绍,您阅读本项目的文档将会事半功倍。 #### 服务端项目目录 ```html ├── cloudfunctions───────────# 云函数目录 │ └── admin──────────────────# 管理后台业务函数 │ └── controller──────────────────# 管理后台业务函数根目录 │ └── menu.js────────────────────────# 菜单管理业务函数 │ └── operateLog.js──────────────────# 操作日志业务函数与接口 │ └── paramConfig.js─────────────────# 系统参数配置业务函数 │ └── role.js────────────────────────# 角色管理业务函数 │ └── user.js────────────────────────# 用户管理业务函数 │ └── node_modules──────────────────# admin函数依赖公共模块 │ └── index.js──────────────────────# admin函数入口文件 │ └── api────────────────────# 用户端业务函数 │ └── clearlogs──────────────# 过期操作日志清理定时任务函数 │ └── common─────────────────# 公共模块 │ └── base-cloud──────────────────# base-cloud公共模块 │ └── intercepters──────────────────# 拦截器函数目录 │ └── authInter.js──────────────────# 用户权限拦截拦截函数 │ └── base-cloud-config.js──────────────────# 公共模块配置文件,注册全局拦截器(重要!) │ └── index.js──────────────────────# BaseCloud公共模块源码,开发阶段无需关心 │ └── db_init.json───────────# 数据库初始化文件,包含数据表和初始化数据 ``` #### 客户端项目目录 ```html ├── cloudfunctions────────# 云函数目录... ├── common────────────────# 静态资源文件目录 │ └── js──────────────────# js文件目录 │ └── base-cloud-client.js─────────────────# BaseCloud客户端SDK │ └── clipBoard.js─────────────────────────# 支持web端复制API │ └── md5.js───────────────────────────────# MD5加密函数,用于密码加密传输,客户端数据缓存等场景 │ └── base.scss────────────────────# BaseCloud样式类库入口文件 │ └── base-font.scss───────────────# BaseCloud图标样式文件 │ └── base-mobile.scss─────────────# BaseCloud移动端样式文件 │ └── base-pc.scss─────────────────# BaseCloud适配PC端样式文件 ├── pages────────────────# 页面 ├── static───────────────# 图片静态资源文件目录 ├── uni.scss─────────────# scss变量配置文件 ``` #### 管理后台业务模块云函数目录结构 ```html ├── cloudfunctions─────────────────# 云函数目录 │ └── admin──────────────────# 管理后台业务函数 │ └── controller──────────────────# 管理后台业务函数根目录 │ └── menu.js────────────────────────# 菜单管理业务函数 │ └── listByType()─────────────────────# 根据菜单类型查询菜单列表接口 │ └── globalData()─────────────────────# 查询登录用户信息、权限菜单列表接口 │ └── info()───────────────────────────# 查询菜单信息接口 │ └── save()───────────────────────────# 保存、更新菜单信息接口 │ └── delete()─────────────────────────# 删除菜单信息接口 │ └── list()───────────────────────────# 菜单列表查询接口 │ └── operateLog.js──────────────────# 操作日志业务函数与接口 │ └── paramConfig.js─────────────────# 系统参数配置业务函数 │ └── info()───────────────────────────# 查询参数配置项信息接口 │ └── save()───────────────────────────# 保存、更新参数配置项信息接口 │ └── delete()─────────────────────────# 删除参数配置项接口 │ └── list()───────────────────────────# 参数配置项列表查询接口 │ └── role.js────────────────────────# 角色管理业务函数 │ └── info()───────────────────────────# 查询角色信息接口 │ └── save()───────────────────────────# 保存、更新角色信息接口 │ └── delete()─────────────────────────# 删除角色接口 │ └── list()───────────────────────────# 角色列表查询接口 │ └── options()────────────────────────# 角色选项列表查询接口(供用户角色选择时使用) │ └── user.js────────────────────────# 用户管理业务函数 │ └── login()──────────────────────────# 登录接口 │ └── checkToken()─────────────────────# token验证接口 │ └── logout()─────────────────────────# 退出登录接口 │ └── changeStatus()───────────────────# 切换用户禁用状态接口 │ └── info()───────────────────────────# 用户信息查询接口 │ └── save()───────────────────────────# 保存、更新用户信息接口 │ └── myInfo()─────────────────────────# 当前用户信息接口 │ └── modify()─────────────────────────# 修改当前用户信息(含密码)接口 │ └── list()───────────────────────────# 用户列表查询接口 │ └── delete()─────────────────────────# 删除用户接口 │ └── node_modules──────────────────# admin函数依赖公共模块 │ └── index.js──────────────────────# admin函数入口文件 ``` ===================================================================== ## 服务端公共模块使用说明文档 #### 【使用公共模块来接管云函数,定义多个访问路径】 1. 根据[公共模块引入说明](https://uniapp.dcloud.io/uniCloud/cf-common)来引入base-cloud公共模块; 2. 在云函数的入口文件`index.js`中引入公共模块,并接管云函数。 ```js 'use strict'; const BaseCloud = require("base-cloud"); exports.main = async ( event , ctx ) => { var fnName = "admin" ; //当前云函数的名称 var controlerDir = `${__dirname}/controller` ; //存放业务函数根目录的绝对路径 return await new BaseCloud({ event, ctx , fnName }).invoke(controlerDir); }; ``` 3. 在云函数中指定的业务函数根目录(此处是controller,你也可以指定其他的目录),创建js文件。 并通过`module.exports`来导出业务处理的函数,可以导出一个或多个。 4. 客户端访问云函数的路径规则为:云函数名称/根目录下的js函数文件名称/js函数文件导出的函数名称; 如果js函数文件直接导出的是一个函数,则路径规则为:云函数名称/根目录下的js函数文件名称。 如下示例为在admin云函数中的`controller>operateLog.js`文件中导出一个函数: ```js 'use strict'; const db = uniCloud.database(); const dbCmd = db.command ; const $ = db.command.aggregate ; const OperateLog = db.collection("t_operate_log"); module.exports = async function(res){ var {pageNumber , pageSize} = this.params ; var page = await this.paginate({ pageNumber , pageSize , collection : OperateLog , eq : ["actionName","userName"], like : ["name"], orderBy : "createTime desc" }); var list = page.list ; list.forEach(item=>{ item.createTime = this.DateKit.toStr( item.createTime ,'seconds'); }); return {page}; }; ``` 此时客户端访问路径为: `admin/operateLog` ,客户端调用示例如下: ```js this.bcc.call({ url : "admin/operateLog" , data : {pageNumber: 1 , pageSize : 20}, success : e=>{} }); ``` 如下示例为在admin云函数中的`controller>role.js`文件中导出多个函数: ```js 'use strict'; const db = uniCloud.database(); const dbCmd = db.command ; const $ = db.command.aggregate ; const Role = db.collection("t_role"); module.exports = { info : async function(e){ var id = this.params.id ; var typeList = TYPE_LIST ; if (!id) { return { typeList }; } var data = this.findFirst( await Role.doc(id).get() ); return { data , typeList }; }, save : async function(e){ var data = this.getModel(); if (data.menuIds) { data.menuIds = data.menuIds.split(','); } if (!data._id) { data.createTime = this.DateKit.now(); await Role.add(data); return this.ok(); } data.updateTime = this.DateKit.now() ; await this.updateById(Role , data); return this.ok(); } }; ``` 此时,通过`admin/role/info` 和 `admin/role/save` 两个路径可以分别访问 `controller>role.js>info()` 和`controller>role.js>save()`,客户端调用示例如下: ```js this.bcc.call({ url : "admin/role/info" , data : {_id : 1}, success : e=>{} }); ``` -------------------------------------------------- #### 【使用公共模块来配置全局的拦截器,以及拦截器的清理】 1. 在`cloudfunctions > common > base-cloud` 目录下,找到 `config.js` 文件, 2. 如下代码所示,在inters中配置了两个拦截器,你可以直接在此处定义拦截器函数(如loginInter),也可以通过文件引入的方式来定义拦截器(如authInter),具体的使用说明,请看注释: ```js //通过文件来引入拦截器函数 const authInter = require("./intercepters/authInter") ; module.exports = { isDebug : true , //会输出一些日志到控制台,方便调试 inters:{ //配置全局拦截器 loginInter: { //直接在此处定义拦截器函数 isGlobal : true , //是否全局拦截,拦截所有的云函数,不配置默认是全局拦截 handle : [] , //拦截的路径,此处留空表示拦截全部的路径 clear : [ //配置要清除拦截器的路径,注意:如果配置了handle则此处的配置无效。 "admin/user/login", //支持字符串、也支持正则表达式(详见示例项目中的authInter的配置规则) "admin/user/checkToken", ] , invoke:async function(attrs){//拦截器函数,入参为上一个拦截器通过setAttr方法传递的所有的键值对 const {event , ctx , uniID } = this ; var res = await uniID.checkToken(event.uniIdToken); if(res.code){ return { state : 'needLogin', msg : "请登录" }; } //将user传入下一个拦截器,在拦截器函数的入参中可以获取到,也可以通过this.getAttr("user")来取到该值。 this.setAttr({user : res.userInfo}); //当前拦截器放行,不调用这个,拦截器不会放行,此次请求到此终止 this.next(); } }, authInter , } } ``` 也就是说,你可以把你所有要配置的拦截器放到 `config.js > inters`中去,每个拦截器注册时, 通过`isGlobal`属性配置是否对所有云函数都拦截,不配置该属性默认为`true`; 在 `handle` 属性中定义要拦截的路径,路径支持正则表达式; 在`clear`属性中,定义要清理拦截器的路径; 使用`invoke`属性来定义拦截器的函数。 特别注意:如果在`handle`中定义了拦截的路径,则`clear`中的配置会被忽略。 在拦截器函数中,接收的参数为一个json,为所有拦截器通过`this.setAttr(key , value)`方法存入的键值对。 在拦截器中,可以使用`this.setAttr(key , value)`方法,将当前拦截器中的变量传递到下一个拦截器或者业务函数。 在下一个拦截器或业务函数的入参中可以接收,也可以使用`this.getAttr(key)`方法,来取到指定的值。 如果拦截器拦截成功,不再继续执行,直接返回响应结果即可。如果拦截器放行,则需要主动调用`this.next()`方法,来放行本次拦截。 >公共模块配置的拦截器对所有引入base-cloud公共模块,并由base-cloud接管的云函数都有效,请合理配置拦截与清理拦截的规则。 >修改公共模块后,除了上传公共模块,也需要上传依赖公共模块的云函数哦~ #### 在业务函数中配置拦截器的拦截和清除规则(v1.1.2新增) 在公共模块中配置拦截器,会全局生效。导致我们经常需要修改公共模块中的拦截器路由拦截规则,反复上传公共模块,极度影响开发效率。 当有新的路由拦截规则时,您可以选择在云函数入口文件中直接定义路由拦截规则,覆盖全局的拦截规则,参考代码如下: ```js //云函数 api > index.js /** * 用户端的API接口 */ 'use strict'; const BaseCloud = require("base-cloud"); exports.main = async ( event , ctx ) => { var baseCloud = new BaseCloud({ event, ctx , fnName : "api" }); //配置云函数`api`的路由拦截规则: baseCloud.setInters({ authInter : { //已在公共模块的config.js > inters 中注册的拦截器的函数名称 clear : [/^api\//] //对所有api/开头的路径不执行authrInter拦截器的拦截 } }); return await baseCloud.invoke(`${__dirname}/controller`); }; ``` 此处的代码,仅对`authInter`进行了局部配置,将覆盖`common > base-cloud > config.js` 中 `authInter` 的拦截规则;而`common > base-cloud > config.js` 中 `loginInter` 拦截器的拦截规则由于未进行局部配置,将按全局配置的拦截规则拦截所有的 `api云函数` 下的访问路径 。 #### 【在业务函数中可以使用的变量】 还是以一个云函数中的业务函数为例: ```js 'use strict'; const db = uniCloud.database(); const dbCmd = db.command ; const $ = db.command.aggregate ; const OperateLog = db.collection("t_operate_log"); module.exports = async function(attrs){ //此处的attr是所有拦截器中通过 this.setAttr(key,value)方法存入的键值对 var user = attrs.user ; //在loginInter拦截器中存入的user变量 var ctx = this.ctx ; //上下文,为入口函数的入参context var event = this.event ; //为入口函数的入参event,本次请求云函数携带的event参数 var params = this.params ; //本次请求客户端通过data或url传递所有的参数 var fullPath = this.fullPath ; //本次请求的路径,如: admin/user/info var actionName = this.action ; //本次请求的action,不含云函数名称,如: user/info var fnName = this.fnName ; //本次请求的云函数的名称 var token = this.token ; //本次请求携带的token var uniID = this.uniID ; //依赖的uniID模块,可以直接使用uniID的API }; ``` #### 【在业务函数中可以使用的方法】 ### this.getModel(prefix , keepKeys) ; 1. 第一个参数为`prefix`参数,指定要获取的参数的前缀符,未指定时,默认为`x`。 2. 第二个参数为`keepKeys`,指定一个或多个键名进行接收,多个使用英文逗号分开,如未指定则接收所有带有指定前缀的参数。 举个例子,比如用户修改个人信息的功能,在客户端传参示例: ```js this.bcc.call({ url : "admin/user/modify" , data : { "x.password" : "123123123" , "x.mobile" : "15688585858" , "x.realAuth.contact_name" : "王大成" , "x.username" : "想改个名字能改吗" , "remark" : "个人的喜好" } }) ``` 此时我们需要只接收带`x.`前缀的参数,统一存放到`data`变量中,以便直接更新用户表的数据。 更新时,我们假设只允许用户修改`password`和`mobile`字段,不允许修改`username`字段, 那么使用 `this.getModel()` 方法可以获取到符合我们条件的参数。 ### this.keep( jsonData , keepKeys) ; 保留jsonData中指定的键值对,用法同上。 ```js module.exports = async function(e){ var data = this.getModel("x" , "mobile,password,realAuth"); }; ``` ### this.findFirst(dataInDB); 从数据库返回的结果中获取一条数据,如果没有数据则返回`null` ```js var user = this.findFirst( await User.doc(id).get() ); if(null == user){ return {} ; } var {username , mobile} = user ; //... ``` ### this.find(dataInDB); 从数据库返回的结果中获取列表数据,如果没有数据则返回 `[]` ; ```js var dataInDB = await Role.orderBy("createTime","asc").get() ; var list = this.find( dataInDB ); if(list.length == 0){ //..do something } ``` ### async this.updateById( collection , updateData ) ; 根据主键_id来更新一条数据,`updateData`中包含`_id`字段和要更新的字段 ```js var Role = uniCloud.database().collection("t_role") ; await this.updateById( Role , data); ``` ### async this.paginate({ collection , where = {} , field = {} , pageNumber = 1, pageSize = 10 , orderBy , eq , like , gte , lte , gt , lt , range , rangeNeq , rangeReq , rangeLeq }); 使用数据库普通查询方法(暂不支持聚合查询)来获取分页数据。参数说明见下方的示例代码中的注释: ```js 'use strict'; const db = uniCloud.database(); const dbCmd = db.command ; const $ = db.command.aggregate ; const OperateLog = db.collection("t_operate_log"); module.exports = async function(res){ var {pageNumber , pageSize , createTimeStart , createTimeEnd } = this.params ; this.params.createTimeStart = this.Datekit.parse(createTimeStart); //字符串的日期转时间戳 this.params.createTimeEnd = this.Datekit.parse(createTimeEnd); var page = await this.paginate({ pageNumber , //分页页码,不传入默认为1 pageSize , //每页数据条数,不传入默认为10 collection : OperateLog , //要查询数据的集合对象 field:{ userName : true }, //指定返回字段,具体用法参见官方文档 where:{},//自定义的固定查询条件 //以下每个数组中的字符串均支持单个key和双key定义的方式 eq : ["actionName","userName"], //筛选的相等条件,如果请求参数中该参数不为空则进行相等条件筛选 like : ["name"],//筛选的模糊查询条件,如果请求参数中该参数不为空则进行模糊查询筛选 gte : ["createTimeStart,createTime"], //如果this.params.createTimeStart值不为空,追加{createTime:dbCmd.gte(this.params.createTimeStart)}筛选条件 lte : ["updateTime"],//如果this.params.updateTime值不为空,追加{updateTime:dbCmd.gte(this.params.updateTime)}筛选条件 //范围类动态筛选条件: //比如根据日志创建日期范围来筛选日志的场景:用户端传入两个参数:createTimeStart、createTimeEnd;数据库中创建时间的字段名为createTime //如下定义,则如果createTimeStart不为空,追加{createTime:dbCmd.gte(this.params.createTimeStart)}筛选条件 //如果createTimeEnd不为空,追加{createTime:dbCmd.lte(this.params.createTimeEnd)}筛选条件 //如果createTimeStart、createTimeEnd都不为空,追加{createTime: dbCmd.gte(this.params.createTimeStart).and(dbCmd.lte(this.params.createTimeEnd)) }筛选条件 range:["createTime,createTimeStart,createTimeEnd" , "updateTime" ], //"createTime,createTimeStart,createTimeEnd"可以简写为"createTime"。"updateTime"代表"updateTime,updateTimeStart,updateTimeEnd" rangeNeq:["createTime,createTimeStart,createTimeEnd" , "updateTime" ], //用法同range,筛选条件为: min < x > max,不含左右等号 rangeLeq:["createTime,createTimeStart,createTimeEnd" , "updateTime" ], //用法同range,筛选条件为: min <= x > max,不含右等号 rangeReq:["createTime,createTimeStart,createTimeEnd" , "updateTime" ], //用法同range,筛选条件为: min < x >= max,不含左等号 orderBy : "createTime desc" }); var list = page.list ; list.forEach(item=>{ item.createTime = this.DateKit.toStr( item.createTime ,'seconds'); }); return {page}; }; ``` 以上查询足以应对大部分的筛选条件的业务场景,如果有一些个别的使用场景,可以自行创建where查询条件后,直接调用分页查询方法: ```js //例如这样的跨字段操作: var where = dbCmd.or( { type: { memory: dbCmd.gt(8) } }, { type: { cpu: 3.2 } } ); var page = await this.paginate({ pageNumber , pageSize , collection : Computer , where: where , orderBy : "price desc" }); ``` 分页查询方法返回的数据结构如下: ```js { pageNumber: 1,//页码 lastPage: true,//是否最后一页 totalPage: 1,//总页码 list: [], //当前页数据 totalRow: 0, //总数据条数 pageSize: 10 //每页数据条数 } ``` ### this.getPage({pageNumber , pageSize , totalRow , list , dataInDB }); |参数 |说明 | |-- |-- | |pageNumber | 页码| |pageSize | 每页数据条数| |totalRow | 数据总条数| |list | 当前页数据列表,数组类型| |dataInDB | 当前页数据列表,数据库查询返回的未经处理的结果,JSON类型,未传入list时传dataInDB| 该方法返回标准的分页数据: ```js { pageNumber: 1,//页码 lastPage: true,//是否最后一页 totalPage: 1,//总页码 list: [], //当前页数据 totalRow: 0, //总数据条数 pageSize: 10 //每页数据条数 } ``` ### this.ok(msg); 返回请求成功的响应结果,`msg`不传入时,默认提示信息为: ```js { state : "ok" , msg : "操作成功" } ``` ### this.fail(msg, state) 返回请求成功的响应结果,`msg`不传入时,默认提示信息为: ```js { state : "fail" , msg : "系统异常,请稍后再试" } ``` ### this.isRepeat( dataInDB , _id ); 做保存更新一体的业务接口时,我们经常会判断某个字段是否已在数据库中存在值。 先根据该字段查询数据中的一条数据,然后使用该方法,来快速判断是否有重复的值。 如下为保存更新用户数据接口代码示例: ```js var data = this.getModel(); var {username , password , _id , roleIds , mobile } = data ; var sameNameUser = this.findFirst(await User.where({username}).limit(1).get()); if( this.isRepeat(sameNameUser , _id) ){ return this.fail("用户名已存在"); } ``` ### async this.setMaxOrderNum(data, collection, where ,step = 10); 适用于具有`orderNum`字段的数据表,自动生成最大的`orderNum`的业务场景。 1. `data` 参数为即将要保存、更新的json数据,必填 2. `collection`为要更新的集合对象,必填 3. `where`为限定排序的条件,可选项 4. `step` 为自增的步长,默认为10,方便自动生成后任意插入新的排序。 ```js var Menu = uniCloud.database().collection("t_menu"); var data = this.getModel(); await this.setMaxOrderNum(data , Menu , {parentId : data.parentId } ); console.log(data.orderNum) ; //输出最大的orderNum ``` ### this.getTitleByValue(optionsArray , value); 在服务端我们经常需要定义多个键值对的数组,作为下拉列表的选项,如用户状态,通常我们会定义如下的数组常量: ```js const STATUS = [ {title : "正常" , value : 0 }, {title : "禁用" , value : 1 }, {title : "待审核" , value : 2 }, {title : "审核拒绝" , value : 3 }, ] ; ``` 此时,直接作为数组返回给客户端,客户端使用 `radios` 或 `selects` 组件来供用户选择使用,一般我们会把`value`存入数据库,当向用户展示时,我们需要根据`value` 找到对应的`title`,此时可以使用`this.getTitleByValue()`方法: ```js var status = user.status ; // 0,1,2,3 var statusTitle = this.getTitleByValue(STATUS , status); //正常、禁用、待审核、审核拒绝 ``` ### this.log(...arguments); 方便调试的方法,如果在 `base-cloud > config.js ` 中配置了 `isDebug` 参数为 `true` ,使用该方法时,可以输出日志,否则不输出日志。 ### this.isNull(obj); 判断是否为空 ### this.isObject(obj); 判断是否为json对象 ### this.isEmptyObject(obj); 判断是否值为{}的json对象,不含有任何键值对的json ### this.isFn(fn); 判断是否为函数 ### this.isNumber(number); 判断是否为数字 ### this.isArray(array); 判断是否为数组 ### this.isString(string); 判断是否为字符串 ### this.isDate(date); 判断是否为日期类型 ### this.isReg(reg); 判断是否为正则表达式 ### this.Datekit.now() uniCloud服务器默认是0时区的时间,使用该方法可以获取东八区当前时间的时间戳,符合国内的习惯。如果使用该方法创建的时间戳存入数据库, 那么返回给用户端之前,需要在服务端直接转成日期字符串,在用户端转换字符串会造成时区差的问题。 ### this.DateKit.addMinutes(minutes , date); date为可选参数,时间戳类型,不传入则使用东八区的当前时间时间戳,增加或减少指定分钟数量,返回时间戳。 ### this.DateKit.addHours(hours , date); date为可选参数,时间戳类型,不传入则使用东八区的当前时间时间戳,增加或减少指定小时数量,返回时间戳。 ### this.DateKit.addDays(days , date); date为可选参数,时间戳类型,不传入则使用东八区的当前时间时间戳,增加或减少指定天数,返回时间戳。 ### this.DateKit.addMonths(months , date); date为可选参数,时间戳类型,不传入则使用东八区的当前时间时间戳,增加或减少指定月份,返回时间戳。 ### this.Datekit.toStr( timestamp , fileds ); 传入一个时间戳时间,格式化为字符串,`fileds` 来指定格式化的时间精度,支持:second、minute、hour、day、month、year,不传默认为minute ### this.DateKit.friendlyDate(timestamp); 传入一个时间戳时间,返回距离东八区当前时间的有多少天、时、分、秒。如:3天前、24分钟后 -------------------------------------------------- #### 【服务端响应结果的约定】 一般操作类的接口,服务端会返回如下结果: ```js { state : 'ok' , msg : "操作成功" } ``` 数据查询类的,正常情况下,服务端不再返回state、msg字段,直接返回要查询的数据结果,如: ```js { list : [], data :{} } ``` |state |说明 | |-- |-- | |ok |请求成功 | |fail |请求失败,失败时需要返回msg字段,作为失败的说明信息 | |noAuth |无操作权限 | |needLogin |需要登录 | |... |其他特殊场景下的状态描述 | ===================================================================== ## 客户端SDK使用说明文档 使用客户端sdk前,请确保已按如下方式,在 `main.js` 中注册全局对象。 ```js import Vue from 'vue' import App from './App' import bcc from "./common/js/base-cloud-client.js" //引入客户端sdk文件 Vue.prototype.bcc = bcc ; //注册为全局对象 Vue.config.productionTip = false App.mpType = 'app' const app = new Vue({ ...App }) app.$mount() ``` #### 【调用云函数】 ```js var data = e.detail.value ; this.bcc.call({ debug : true , url : 'admin/user/save' , //请求路径,直接以函数名称开头,开头不要加/,后面跟着路径 data : data , //请求参数 success : res => { //当服务端返回state == 'ok' 或无state字段时,进入success回调 }, fail : res => { //当服务端返回state == 'fail' 时,进入fail回调; //如未定义fail回调,则默认提示服务端返回的msg字段 }, complete : res => { //请求完成后的回调 }, }); ``` `debug:true`,开启调试模式后,将在控制台输出请求的路径和参数(仅开发模式下有效,编译发布后不会输出),提升开发调试效率。 ```js this.bcc.call({ debug : true //默认为true,如果不需要控制台输出日志信息,请配置为false }) ``` 开启开发模式后,发送请求时,将在浏览器控制台输出日志信息,方便我们快速开发调试: ```js base-cloud-client.js:15 ====================== base-cloud-client.js:16 云函数: admin , action : user/login , data :{"username":"admin","password":"2255BE0951400E260832C85C5D191247"} base-cloud-client.js:17 {username: "admin", password: "2255BE0951400E260832C85C5D191247"} base-cloud-client.js:18 ====================== ``` 默认情况下,表单数据一般都会以字符串的形式提交,直接存入数据库会给我们带来麻烦,因为MongoDB查询数据库时,对数据类型是敏感的。基于这点 `base-cloud` 客户端sdk会在发送请求之前,对提交的参数按会可能的类型进行转换, 这点也会导致一些意外的情况,如果不需要自动转换的,可以做如下配置: ```js this.bcc.call({ keepStr : true , //将所有参数都不进行类型转换 keepKeys : "password,idcard,mobile" //指定不进行转换的参数名,多个用英文逗号分开。 }) ``` 以上两个配置项,可以选择使用,针对一些意外情况的发生进行处理。 对应的,如果使用 `this.bcc.submit()`方法,自动提交表单,则需要在 `form` 标签定义 `data-keepStr` 、 `data-keepKeys` 属性: ```html //指定键名不进行类型转换,键名无需包含校验规则,多个用逗号分开
//指定所有的参数都不进行类型转换 ``` ```js methods:{ submit : function(e){ this.bcc.submit(e); } } ``` #### 【通过客户端本地缓存调用云函数】 >本地没有缓存则请求云函数数据并将请求到的数据缓存至本地,否则直接使用本地缓存数据。 ```js var data = e.detail.value ; this.bcc.callInCache({ url : 'admin/user/list' , //请求路径,直接以函数名称开头,开头不要加/,后面跟着路径 data : data , //请求参数 success : res => { //当服务端返回state == 'ok' 或无state字段时,进入success回调,并且将数据缓存至本地 }, fail : res => { //当服务端返回state == 'fail' 时,进入fail回调; //如未定义fail回调,则默认提示服务端返回的msg字段 }, complete : res => { //请求完成后的回调 }, }); ``` #### 【主动清理客户端本地缓存】 >当本地数据已经发生变更时,需要主动清理本地缓存数据,下次去请求最新的数据。 >清理缓存时,将直接清理所有指定请求地址下的缓存(一个请求地址下,因参数不同可能会有多个本地缓存数据)。 应用场景示例:将用户列表数据存入本地缓存,当编辑用户信息、删除用户数据、更改用户状态等三个操作发生时。直接清理本地的用户列表数据缓存: ```js //传入要清理缓存的请求地址的路径 this.bcc.clearCache("admin/user/list"); ``` #### 【表单数据校验】 >表单校验无须配置各种校验规则,直接将校验规则写入name即可。 >表单的name一共分成四个部分,用|符号分割: 1. 要传入服务端的name 2. 表单的标题,如果标题为空表示该表单可以为空。标题内可以含有:请输入、请上传、请选择 这三类提示文字。 3. 校验规则,目前支持:mobile、email、idcard、count(整数)、amount(金额)、字符长度与长度范围等六种常见的表单验证和非空验证。 4. empty :表示可以为空,如果有值则进行校验,无值则放行。 示例代码: ```html /* 要传入服务端的name为:x.name ,角色名称不可为空,校验规则为:字符长度2~20之间。 */