对于前端的开发人员,我们开发应用都依赖于后端同学开发的接口,鉴于此,我们利用Yonbuilder低代码的开发能力快速构建我们需要的接口,从而在不依赖于后端开发的人员的情况下,开发我们需要的应用。下面我以Yonbuilder 开发一个内容管理系统(安全中心)为例,详细讲解开发的过程。 Yonbuilder 使用- 所在租户须开通应用构建服务
- 在导航菜单中找到应用构建服务-应用构建
实体构建点击我们刚才建立的应用,进入数据建模实体页签,新增一个实体,CMS 一般包含栏目和栏目下的文章 我们首先设计栏目的实体cms_menu,一般包括栏目名称和栏目编码。
接下来我们建立文章实体,由于我们的文章是放在栏目下的,所以在建立实体的时候,我们在栏目实体下新增子实体 cms_articel,文章内容article_content的类型我们选择大文本。实体建完以后要发布。到些我们的实体就完成了。 后台管理页面设计后台管理页面我们需要设计两个管理页面,一个是栏目的管理页面,一个是文章管理的页面。在页面建模页签下点击右上角的新建页面,我们选择单据左树右表,点击下一步,数据绑定:树我们选择前面我们建立的安全中心菜单、表选择前面我们建立的安全中心文章的实体。 接下来我们分别设计菜单管理和文章管理的页面,首先我们看一下菜单管理 菜单管理菜单管理我们设计成树形目录的结构。先看一下最后的效果,主要有编码、名称、上级编码 
在设计态时如下图, 在设计时注意,点击上级分类,在右侧面板处选择参照配置,参照显示名称。
文章管理
我们先看一下最终的效果,左边是菜单栏目,右边是文章列表,录入的时候点击各上的新增。 
在设计态下, 1、我们要选中左侧的树,在右侧的面板节点标题处单击选择标题和编码。 2、选中右侧的文章列列表表格,把不需要显示的字段进行删除或隐藏。查询区把不需要的过滤字段进行删除。然后保存即可。 
3、文章录入的设计 文章内容要选择富文本类型,这样可以最大化的自定义我们文章的内容,包括从其他网站复制的图片和链接等。 
这里我们重点说明一下前端函数,在安全中心录入文章的页面有一个需求,我们选不同的分类菜单,下面的录入内容要显示相关的录入字段,其他的要隐藏掉。比如:我选择安全补丁,下面只显示补丁类型和补丁下载地址。这个就需要用到前端函数,下面我们逐步进行说明。 
首先我们打开文章录入页面的设计态,选中分类菜单,点击右侧面板的动作,这个时候我们可以看到各种动作,我们点击值改变后的动作,点击设置会弹出前端函数的编辑页面,这个时候我们这可以编辑函数的相关逻辑了。在写逻辑时,我们首先获取分类菜单选中的值,根据值判断是否安全补丁,如果是,我们就把补丁类型和补丁下载地址两个字段进行显示,其他的字段进行隐藏。以达到我们的要求。获取值时用viewModel.get(\'控件的值\') ,控件的值,可以在右侧面板首先选择页面文章管理单卡,正面的组件列表显示的就是各个组件的值。单击就可以填充到页面里。编辑完成后,一定要点击右上角的保存单启用。否则不生效。 

这两个功能设计调试没有问题后,我们下一步就要开发接口开放。 接口设计与调试接口设计主要是三个接口的设计,一个是获取栏目菜单的接口,一个是获取文章列表的接口,一个获取文章详情的接口。 菜单栏目接口首先打开应用构建我们需要设计的应用,切换到集成配置里的api发布,如下图 点击列表左上角的创建api函数,输入api名称,api分类选择通用api分类,点击下一步 我们以获取安全中心菜单栏目为例说明,这里我们需要设置请求路径,打红色*号的,这个根据你的业务需要,设置访问路径,路径设置完后,接下来设置请求参数,图中由于菜单栏目不需要参数,所以这里的请求参数我们没有设置,参数的设置我们在下面的文章列表中说明。我们继续点击下一步 在这一步,我们点击创建函数,进入创建后端函数的界面,这个是开放接口的核心部分 左侧导航是常用的后端函数常用的功能说明和快捷插入代码的功能,双击相应的功能可以对应功能的代码。这里 推荐使用YonQL,类似我们常用的sql语句,如下 let AbstractAPIHandler = require(\'AbstractAPIHandler\');
class MyAPIHandler extends AbstractAPIHandler {
execute(request){
//select 指定表里的一些属性,然后from 这张表
const sql= \"select id as menu_id,menu_code,menu_title,parent as parent_id,level,sort,isEnd,path from GT43208AT29.GT43208AT29.security_menu\"
var res = ObjectStore.queryByYonQL(sql);
return {list:res};
}
}
exports({\"entryPoint\":MyAPIHandler});
字段名称我们可以点击右侧面板里的字段,可以快速插入到代码中, from 后的表名对应右侧面板中uri,也可以单击右侧面板中对应的uri快速插入到代码。
这样我们的菜单栏目的后端函数就完成了。点击页面右上角的保存并启用。然后回到我们创建函数的页面,点击下面的保存。到这一步我们的第一个接口开发完成。接下来对我们的完成的接口进行调试,看是否满足我们的需要。回到我们的api发布的接口列表页面,将鼠标放到该行数据上,右侧会显示功能列表,我们点击 api测试,进入API测试页面
点击发起调试,在右侧就会看到调试的结果,如果函数写的问题,在调试时会给出错误的信息。一般我们要打开后端函数编辑的窗口和调试的窗口,进行函数的调整和测试,开发效率会有明显的提高。
文章列表接口接下来看一下文章列表的接口开发,这个包含参数的输入,列表分页,按关键字查询等复杂功能的开发。我们主要讲解接口参数的配置和后端函数的写法。其他的步骤与菜单栏目的接口一致。 参数主要包含currentpage(当前页)、pagesize(每页数量)、category(栏目类别)、searchkey(搜索关键字)等几个主要参数。参数类型选择对象(object)。 接下来,我们看后端的函数是如何编写的 let AbstractAPIHandler = require(\'AbstractAPIHandler\');
class MyAPIHandler extends AbstractAPIHandler {
execute(request){
//我们可以通过 request对象获取前面我们设计的参数
var currentPage=request.currentpage?request.currentpage:1
var pageSize = request.pagesize?request.pagesize:10
var category = request.category
var searchkey = request.searchkey
var article_patch_category = request.article_patch_category
var article_course_category = request.article_course_category
var article_whitebooke_field = request.article_whitebooke_field
// 根据参数拼接sql的条件
var where = \"\"
if(category) {
where = \' and security_menu_id.menu_code like \"\'+category+\'\"\'
}
if(searchkey){
where = where +\' and article_title like \"\'+searchkey+\'\"\'
}
if(article_patch_category){
where = where +\' and article_patch_category=\"\'+article_patch_category+\'\"\'
}
if(article_course_category){
where = where +\' and article_course_category=\"\'+article_course_category+\'\"\'
}
if(article_whitebooke_field){
where = where +\' and article_whitebooke_field=\"\'+article_whitebooke_field+\'\"\'
}
var sql=`select
id,article_title,
security_menu_id,
article_type,
article_content,
article_whitebooke_field,
article_course_category,
article_patch_category,
article_certificate_url,
article_course_video_url,
artcle_course_photo_url,
article_patch_download_url,
article_attachment,
createTime,
security_menu_id.menu_code,
security_menu_id.menu_title,
security_menu_id.parent,
security_menu_id.level,
security_menu_id.path,
security_menu_id.sort,
security_menu_id.isEnd
// 这个地方left join 的写法注意,和我们正常的写法不一致,摸索了好久
from GT43208AT29.GT43208AT29.security_article T1 left join security_menu_id T2 on T1.security_menu_id=T2.id
where 1=1
// 分页的写法和与MySQL大同小异,区别点是limit, MySQL中的limit第一个参数代表数据下标,
// 第二个参数代表下标后面返回几条数据,我们的limit代表 第一个参数代表底层已经计算好了的这个分页,
// 2就是返回的一个页数,后面第二个参数就是它每页要返回的数量
`+ where +\' limit \'+currentPage+\',\'+pageSize
// 计算查询出来的总记录数
var sqlcount =\'select count(id) cnt from GT43208AT29.GT43208AT29.security_article where 1=1 \'+where+\'\'
var count = ObjectStore.queryByYonQL(sqlcount);
**.**t = count?count[0].cnt:0
// 查询出记录并返回
var list = ObjectStore.queryByYonQL(sql);
return {list,\"count\":cnt};
}
}
exports({\"entryPoint\":MyAPIHandler});函数编写完后,我们进行测试,看是否有问题,调试正常好,我们的接口就开发完了。 文章详情接口的设计这个我就直接贴代码了,设计和上面的基本一致 let AbstractAPIHandler = require(\'AbstractAPIHandler\');
class MyAPIHandler extends AbstractAPIHandler {
execute(request){
const id=request.id
const sql=`select
article_title,
article_content,
security_menu_id,
article_course_category,
article_course_video_url,
artcle_course_photo_url,
article_patch_category,
article_patch_download_url,
article_whitebooke_field,
article_certificate_url,
article_attachment,
createTime,
security_menu_id.menu_code,
security_menu_id.menu_title
// 注意这个地方left join 的写法
from GT43208AT29.GT43208AT29.security_article T1 left join security_menu_id T2 on T1.security_menu_id=T2.id where id=\"`+id +`\" order by createTime desc`
var res = ObjectStore.queryByYonQL(sql);
return {list:res};
}
}
exports({\"entryPoint\":MyAPIHandler});到此我们Yonbuilder 实体设计和接口开发就完成了。 前端接口调用说明我们要想调用接口,首先得获取到调用接口的令牌 access_token,那怎么获取呢? 开放平台给出了调用说明: 调用接口令牌 access_token 是应用调用开放平台业务接口的凭证,有效期为2小时,过期后需要重新获取 。 应用创建成功后,会生成应用的 appKey 和 appSecret 的值,租户管理员可将这个两个值交给开发者,开发者可用 appKey 和 appSecret 获取 accesstoken 进行接口的调用。 这个 appKey 和 appSecret 的值我们可以在集成配置-api授权里看到
access_token 的获取方式为主动调用开放平台的令牌授权接口,该接口说明如下: 请求地址请求地址 | https://**.**open-auth/**.**essToken | 请求方法 | GET | 请求参数字段 | 类型 | 说明 | appKey | string | 应用 appKey | timestamp | number long | unix timestamp, 毫秒时间戳 | signature | string | 校验签名,HmacSHA256,加签方式看下文 | 加签方式其中,签名字段signature计算使用HmacSHA256,具体计算方式如下: URLEncode( Base64( HmacSHA256( parameterMap ) ) ) 其中,parameterMap 按照参数名称排序,参数名称与参数值依次拼接(signature字段除外),形成待计算签名的字符串。 示例 : 若发送请求参数appKey为41832a3d2df94989b500da6a22268747, 时间戳timestamp为1568098531823,则待加密字符串的值为 appKey41832a3d2df94989b500da6a22268747 timestamp1568098531823 之后对 parameterMap 使用 HmacSHA256 计算签名,Hmac 的 key 为自建应用的 appSecret 。计算出的二进制签名先进行 base64,之后进行 urlEncode,即得到 signatrue 字段的值。 获得 access_token 后开发者就可以调用具体的业务接口,获得具体的业务数据。 请求示例https://**.**open-auth/**.**essToken?appKey=xxx×tamp=xxx&signature=xxx 返回参数说明字段 | 类型 | 说明 | code | String | 结果码,正确返回 \"00000\" | message | String | 结果信息,若有错误,该字段会返回具体错误信息 | data.access_token | String | 接口令牌 access_token | data.expire | number int | 有效期,单位秒 | 返回数据{
\"code\": \"00000\",
\"message\": \"成功!\",
\"data\": {
\"access_token\": \"b8743244c5b44b8fb1e52a55be7e2f\",
\"expire\": 7200
}
}代码实例根据以上规则,封装了获取access_token 的方法 /**
* Author: lujwa
* Date: 2021-01-18 13:21:51
* LastEditors: lujwa
* LastEditTime: 2021-09-09 16:01:14
* Description:
*/
import myCookie from \'js-cookie\';
import CryptoJS from \'crypto-js/crypto-js\'
import { request } from \'utils/request\'
/**
* 获取YonBip accesssToken
*/
export **.**essToken() {
// appKey 和 appSecret
const appKey = \"a07e89df84744af08f3521c689****\"
const appSecret = \'*****************\'
const timestamp = Date.now();
const signStr = \'appKey\' + appKey + \'timestamp\' + timestamp
const signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(signStr, appSecret));
const url = \"https://**.**open-auth/**.**essToken\"
// 获取cookie里的token 判断是否过期
const access_token = myCookie.get(\"access_token\")
if (!access_token) {
return new Promise((resolve) => {
request(url, {
method: \"get\",
params: {
appKey: appKey,
timestamp: timestamp,
signature: signature
}
}).then((res) => {
if (res.data.code === \"00000\") {
const access_token = res.data.data.access_token
const expire = res.data.data.expire
var millisecond = new Date().getTime();
const options = {
expires: new Date(millisecond + expire * 1000)
}
// bip access_token 将token 存入cookie
myCookie.set(\'access_token\', access_token, options)
resolve(true)
} else {
resolve(false)
}
})
})
} else {
return true
}
}接口调用示例比如我们调用栏目菜单的接口,直接加上access_toekn的参数即可。 https://**.****.**monProduct/security/getmenu?access_token=80843f3ac18147dfa8703049cd57301 前端设计说明项目目录页面设计
主要功能设计- BIP 接口调用 这部分主要是BIP access_token的生成和BIP接口的调用 。主要代码如下
/**
* Author: [url=mailto:**.****.**[/url]
* Date: 2021-01-18 13:21:51
* LastEditors: [url=mailto:**.****.**[/url]
* LastEditTime: 2021-09-09 16:01:14
* Description: BIP access_token生成
*/
import myCookie from \'js-cookie\';
import CryptoJS from \'crypto-js/crypto-js\'
import { request } from \'utils/request\'
/**
* 获取YonBip accesssToken
*/
export **.**essToken() {
const appKey = \"****\"
const appSecret = \'******
const timestamp = Date.now();
const signStr = \'appKey\' + appKey + \'timestamp\' + timestamp
const signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(signStr, appSecret));
const url = \"/open-auth/**.**essToken\"
const access_token = myCookie.get(\"access_token\")
if (!access_token) {
return new Promise((resolve) => {
request(url, {
method: \"get\",
params: {
appKey: appKey,
timestamp: timestamp,
signature: signature
}
}).then((res) => {
if (res.data.code === \"00000\") {
const access_token = res.data.data.access_token
const expire = res.data.data.expire
var millisecond = new Date().getTime();
const options = {
// secure: false,
// path: \"/\",
// sameSite: \"None\",
expires: new Date(millisecond + expire * 1000)
}
// bip access_token
myCookie.set(\'access_token\', access_token, options)
resolve(true)
} else {
resolve(false)
Message.create({
content: data.message,
color: \"warninglight\",
position: \"top\",
duration: 10
});
}
})
})
} else {
return true
}
}/**
* 获取BIP安全中心文章列表
* @param {*} param
*/
export const getBipSecurityArticleList = (data) => {
const access_token = myCookie.get(\"access_token\")
return request(URL.GET_BIP_SECURITY_ARTICLE_LIST+\'?access_token=\'+access_token, {
method: \"post\",
data
});
}
**
* 获取文章列表
*/
async getBipSecurityArticleList(category, pageSize) {
const res = **.**essToken()
if (res) {
actions.portal.getBipSecurityArticleList({
curreentpage: 1,
pagesize: 5,
category: \"01\"
}).then((res) => {
if (res.data.code === \"200\") {
this.setState({
noticeList: res.data.data.list
})
} else {
Message.create({
content: res.data.message,
color: \"warninglight\",
position: \"top\",
duration: 10
});
}
})
}
}
来源: 前端利用Yonbuilder 快速开发CMS系统 |