请选择 进入手机版 | 继续访问电脑版

【干货案例】前端利用Yonbuilder 快速开发CMS系统

[复制链接]
~陆建伟~ 发表于 2022-1-14 11:32:59
对于前端的开发人员,我们开发应用都依赖于后端同学开发的接口,鉴于此,我们利用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
前端设计说明
推荐使用ucf-web 管理项目,前端UI框架使用Tinper,当然你可以使用其他的框架。
项目目录
页面设计
  • 首页
  • 列表面
  • 详情页


主要功能设计
  • 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系统

1条回复

YonBuilder官方 管理员 276Y币
干货,分享给大家共同学习
您需要登录后才可以回帖 登录

本版积分规则