开发应用
创建目录
在应用的开发页面,根据规划创建目录Model、Logic和Page,分别用于存放数据对象,服务编排、脚本等,前端页面。
创建对象
对象是AppCube的核心功能之一,对象相当于传统数据库里的一张表,用于持久化业务数据。AppCube中有内置的标准对象,也有租户开发者创建的自定义对象。
一个由租户开发者创建的自定义对象,在创建之后,就会有各种操作方式:前端页面可以使用页面模型绑定此对象,并使用表格组件,直接展现对象数据;使用脚本、自定义JS代码、服务编排等,对该对象中的信息进行增删改查。
本示例规划的自定义字段如表1所示。
字段标签 |
字段名称 |
字段类型 |
取值 |
字段描述 |
读取权限 |
编辑权限 |
添加到页面布局 |
---|---|---|---|---|---|---|---|
ProjectCode |
ProjectCode |
文本 |
数据长度:255 |
项目编码 |
全选 |
全不选 |
选择 |
ProjectName |
ProjectName |
数据长度:255 |
项目名称 |
||||
ProjectStatusCode |
ProjectStatusCode |
数据长度:255 |
项目状态 |
||||
description |
description |
数据长度:255 |
描述 |
||||
startDate |
startDate |
数据长度:255 |
开始时间 |
||||
endDate |
endDate |
数据长度:255 |
结束时间 |
- 在“demo”应用中,如图4所示,单击规划存放对象目录Model右侧的,选择“对象”。
- 在弹出的“添加对象”页面,如图5所示,选择创建新对象,输入对象的“标签”为“projectinfo”,单击“名称”的输入框后,系统将自动生成名称为“projectinfo”,输入描述信息,具体参数说明请参见表2,单击“添加”。
表2 添加对象参数说明 参数
配置说明
示例
创建新对象/导入已有对象
添加对象的方式。
创建新对象
标签
对象展示的名称,为了区分不同对象的描述信息。
对象创建完后,“标签”可以在对象的“基本信息”中修改。
projectinfo
名称
对象在系统内的唯一标识。
- 对象创建后,系统会自动在“名称”增加租户命名空间前缀,以及增加“__CST”后缀,“__CST”是AppCube中对象的后缀标识。
- 对象创建完后,“名称”不可以修改。
projectinfo
说明:对象创建后,系统自动为对象名称添加前后缀,实际创建的对象名为:ISDP__projectinfo__CST。
描述
对象的描述信息。
项目信息
对象创建完成后,自动进入对象详情页面,如图6所示。
- 创建规划的自定义字段(ProjectCode)。
- 参见3,继续添加规划的自定义字段,添加后的字段如图12所示。
创建脚本
针对业务逻辑比较复杂的场景,AppCube平台提供了脚本(Script)能力,支持用户在线开发TypeScript脚本,完成灵活复杂的业务逻辑。
- 添加规划的系统参数如表3所示,供脚本使用。
表3 规划的系统参数 参数名称
值类型
值
描述
是否加密
是否默认
权限
ISDP__demo_token_url
文本
https://ISDP+的域名/oauth2/oauth/rest_toke
粗斜体请根据实际情况填写。
获取ISDP+ token的URL
否
是
保持默认
ISDP__demo_client_id
sdcpxYGmj65aLxR3Qu34kBa3A6lwIyQK
对应ISDP+租户下订阅API的应用ID
否
ISDP__demo_client_secret
********************************
对应ISDP+租户下订阅API的应用令牌
是
说明:当值类型为“文本”时,该参数才会有用,且加密后无法转为非加密。
ISDP__demo_getProjectlist_url
https://ISDP+的域名/openapi/v1/project/findPagedProjectList
查询项目信息URL
否
- 创建脚本getAccesstoken,用户获取ISDP+ token。
- 在“demo”应用中,如图16所示,单击规划存放脚本目录Logic右侧的,选择“脚本”。
- 在弹出的“新增脚本”页面,如图17所示,选择创建一个新脚本,输入脚本“名称”为“getAccesstoken”,“模板”选择“空脚本”,输入描述信息,具体参数说明请参见表4,单击“添加”。
表4 新增脚本参数说明 参数
配置说明
示例
创建一个新脚本/使用已有脚本
创建脚本的方式。
- 创建一个新脚本:选择后,创建空白脚本。
- 使用已有脚本:选择后,复制一个已有的脚本。
创建一个新脚本
名称
脚本在系统内的唯一标识。
- 脚本创建后,系统会自动在“名称”增加租户命名空间前缀。
- 脚本创建完后,“名称”不可以修改。
getAccesstoken
说明:脚本创建后,系统自动为脚本名称添加前缀,实际创建的脚本名为:ISDP__getISDPProjectList。
模板
脚本模板,用于生成不同类型的脚本。
目前支持以下几种类型。
- 空脚本:表示不用模板。
- 示例服务脚本:对外提供服务的脚本,可通过restful直接调用。
- 示例内部脚本:是内部库代码,只能被其它脚本import使用。
- 示例安装脚本:在安装或升级App/BO包时,在导入实例化配置数据之前或者之后执行的脚本。一般用于预清理数据,删除、更新数据等。
- 示例权限脚本:在开放AppCube的服务编排、脚本或者操作对象数据接口给第三方系统调用时,通过该示例脚本可实现在第三方系统配置这些接口的访问权限。
空脚本
描述
脚本的描述信息。
获取ISDP+ token
脚本创建完成后,自动进入脚本编辑页面,如图18所示。
- 编辑获取ISDP+ token脚本。
代码示例:
import { Error } from 'error'; import * as sys from 'sys'; import * as iconv from 'iconv'; import * as http from 'http'; import { Encoding } from 'buffer'; export class Input { } export class Output { @action.param({ type: "String", required: true }) result: string; } const client_secret = sys.getParameter("ISDP__demo_client_secret"); const client_id = sys.getParameter("ISDP__demo_client_id"); const tokenURL = sys.getParameter("ISDP__demo_token_url"); export class GetAccesstoken { @action.method({ input: "Input", output: "Output", description: "获取ISDP+的Token" }) run(): Output { let output = new Output(); let url = tokenURL; let clientId = client_id; let clientSecret = client_secret; let client = http.newClient(); let resp = client.post(url, { headers: { "Content-Type": "application/json" }, data: { "client_id": "isdp-saas-openapi", "grant_type": "password", "client_secret": "isdp-saas-openapi", "username": clientId, "password": clientSecret } }); let data = iconv.decode(resp.data, Encoding.Utf8); let result = JSON.parse(data); if (!result.access_token) { throw new Error(result.errorCode, result.errorMsg) } output.result = result["access_token"]; return output; } }
- 如图19所示,单击页面上方的,保存脚本。
- 如图20所示,单击页面上方的,在页面底部显示测试窗口,如图21所示。
本示例脚本,无需输入参数,直接单击测试窗口的,执行脚本。
查看输出参数和日志,确认没有问题,符合预期结果。
图22 输出参数
- 如图23所示,单击页面上方的,启用脚本。
- 参见2,创建脚本getProjectListdemo,获取项目信息,写入到创建的对象ISDP__projectinfo__CST中。
代码示例:
import { Error } from 'error'; import * as iconv from 'iconv'; import * as http from 'http'; import { Encoding } from 'buffer'; import * as sys from 'sys'; import { GetAccesstoken } from './ISDP__getAccesstoken'; import * as db from 'db'; const queryProjectURL = sys.getParameter("ISDP__demo_getProjectlist_url"); export class projectListInfos { @action.param({ type: "String", required: true, description: "结束日期" }) endDate: string; @action.param({ type: "String", required: false, description: "起始日期" }) startDate: string; @action.param({ type: "String", required: false, description: "描述" }) description: string; @action.param({ type: "String", required: false, description: "项目状态代码" }) ProjectStatusCode: string; @action.param({ type: "String", required: false, description: "项目名称" }) ProjectName: string; @action.param({ type: "String", required: false, description: "项目代码" }) ProjectCode: string; } export class Input { @action.param({ type: "Number", required: true, min: 0, description: "当前页码:,从0开始" }) start: number; @action.param({ type: "Number", required: true, min: 0, max: 100, description: "每页数量" }) count: number; @action.param({ type: "String", required: false, description: "项目编码" }) projectCode: string; } @action.object({ type: 'param' }) export class Output { @action.param({ type: 'Any' }) data: string[]; } let result = this.result @useObject(['ISDP__projectinfo__CST']) @action.object({ type: 'method' }) export class FindProject { @action.method({ input: "Input", output: "Output", description: "查询项目" }) run(input: Input): Output { let output = new Output(); let responses = new Output(); const tokenResult = new GetAccesstoken().run().result; let result = this.queryProject(input, tokenResult).data; let project = this.projectinfos(result); output.data = project; return output; } private queryProject(input, tokenResult) { let url = queryProjectURL; //调用接口查询项目 let client = http.newClient(); let resp = client.post(url, { headers: { "Content-Type": "application/json", "Authorization": "Bearer " + tokenResult }, data: input }); let data = iconv.decode(resp.data, Encoding.Utf8); let result = JSON.parse(data) if (result.status && result.status != 0) { throw new Error("500", result.message) } return result; } private projectinfos(result) { let projectinfos = db.object('ISDP__projectinfo__CST'); let records = []; for (let i = 0; i < result.length; i++) { let data1 = { "ISDP__ProjectCode__CST": result[i].projectCode, "ISDP__ProjectName__CST": result[i].projectName, "ISDP__ProjectStatusCode__CST": result[i].projectStatusCode, "ISDP__description__CST": result[i].description, "ISDP__startDate__CST": result[i].startDate, "ISDP__endDate__CST": result[i].endDate }; records.push(data1); } let project = projectinfos.batchInsert(records); return project } }
- 配置定时任务,定时执行脚本getProjectListdemo。
- 在“demo”应用中,如图24所示,单击“配置”,选择“定时任务”页签。
- 在“定时任务”页面,单击“新建”。
- 如图25所示,设置相关参数,参数配置说明请参见表5。
表5 参数配置说明 参数
说明
示例
名称
定时任务名称。
getProjectListdemo
类型
选择定时任务执行的类型,支持脚本或服务编排。
脚本
服务编排/脚本
选择定时任务执行的脚本或服务编排。
ISDP__getProjectListdemo
输入参数
执行的脚本或服务编排的入参。
{
"start": 0,
"count": 15
}
任务首次执行时间
首次执行脚本或服务编排的时间。
2022-06-08 00:00:00
执行次数
定时任务执行次数。
- 仅执行一次
- 按设置的频率执行多次
按设置的频率执行多次
执行周期
“执行次数”配置为“按设置的频率执行多次”时,需要设置。
1
执行周期单位
“执行次数”配置为“按设置的频率执行多次”时,您需要设置执行任务时间间隔。
- 月
- 天
- 小时
- 分钟
天
描述
定时任务的描述信息,建议描述其用途。
获取项目信息脚本
- 单击“保存”,在弹出的“警告”提示框中单击“确认无风险,继续保存”,完成定时任务的新建。
预览验证
在页面开发时,单击,可进入开发环境的预览页面,跟实际效果稍有差距。本节将介绍如何在应用的预览环境中,预览应用效果。这里预览效果跟实际在运行环境中基本一致。在应用配置下,还可以更改应用框架的样式和颜色。
- 在应用中(demo),如图29所示,单击应用左侧导航下部的“配置”,单击“导航条”页签。
- 如图30所示,单击“菜单树”右侧的“+”,选择“添加页签”。
在导航条上的页面较多时,您可以添加目录。添加目录可以更方便区分页面类型,方便导航使用。目录添加之后,会在应用导航中直接显示,目录下可以添加页签。
- 如图31所示,设置页签信息,页签参数配置说明请参见表6,单击“保存”。
表6 页签参数说明 参数
配置说明
示例
页签类型
当前要添加的页签的类型:
- 主页页签:用于展示个人任务相关的信息。
- 对象页签:关联一个对象的布局,将展示指定对象的某个布局页面。
- 标准页面页签:用于关联并展示一个标准页面。
- 高级页面页签:用于关联并展示一个高级页面。
- BPM页签:用于关联并展示一个BPM。
- Web页签:用于关联一个URL,将展示URL对应的网页或页面。
- 状态机页签:用于关联并展示一个状态机。
- 报表页签:用于关联并展示一个报表。
标准页面页签
显示区域
导航页签的显示区域,有以下两种:
- 主页菜单:会按照布局样式进行显示,例如显示顶栏区域或者右上角。
- 自定义菜单栏:选择该方式后,在App的运行态Portal中,单击右上角用户名,会在下拉选项中显示导航页签。
主页菜单
打开方式
导航页签的打开方法,有以下两种:
- 当前窗口:选择该导航页签后,在当前窗口打开页面。
- 新窗口:选择该导航页签后,在新窗口打开页面。
当前窗口
标签
显示在应用导航的名称。
项目列表
名称
用于唯一标识页签的名称。
项目列表
图标
导航页签的展示图标。
这里可以不设置,如果有,可以选择一个图标直接上传。
页面
需要添加的页面。
projectInfo
描述
当前页面的描述信息,用于了解当前页面的主要内容。
-
- 如图32所示,单击,在弹出的警告框中单击“确定”,删除Home页签。
- 如图33所示,设置布局为手机菜单。
- 如图34所示,在应用左侧导航下,单击,进入应用预览页面。
- 如图35所示,在预览页面,查看页面展示字段,数据等。
编译发布应用
应用开发完成后,需要将应用进行编译打包发布操作,打包后该应用才能发布使用。
- 在应用中(demo),如图36所示,单击,选择“设置”。
- 设置软件包,参数配置说明请参见表7,单击“保存”。
表7 参数说明 参数
配置说明
示例
包类型
应用包的类型:
资产包
是否全量包
打包时是否打全量包:
- 全量包:表示对整个应用(包括应用中的各个组件)作为一个整体进行设置打包。
- 增量包:对应用中部分组件进行设置打包。单击“添加应用组件”,在“类别”中选择相应的类别,勾选需要打包的组件。
全量包
产权设置 > 加密保护
当选择“资产包”打包时,才会显示该参数。表示是否对打包的组件中敏感内容(例如脚本内容)进行加密。
勾选表示加密。在其他环境安装前,包中敏感数据是经过加密的。
否
产权设置 > 版权信息
当选择“资产包”打包时,才会显示该参数。表示该包的版权信息。
选填项。
-
产权设置 > 描述信息
当选择“资产包”打包时,才会显示该参数。表示该包的描述信息。
选填项,建议描述该App提供的功能。
项目列表
产权设置 > 联系邮件
您可以在此留下当前软件包的问题联系邮箱,若安装过程中出现问题会将联系邮件提示给使用者。
-
产权设置 > 联系链接
您可以在此留下当前软件包的文档链接,若安装过程中出现问题会将联系链接提示给使用者。
-
产权设置 > 资产保护 > 保护模式
当选择“资产包”打包时,才会显示该参数。表示打包数据的保护模式。
- 未受保护
- 只读保护
- 不可见保护
未受保护的资产包在开发环境中安装后,可进行二次编辑;在运行环境安装资产包后,不论保护模式是“未受保护”还是“只读保护”,都不可编辑。
只读保护
部署策略 > 安装前置脚本
当选择“资产包”打包且打全量包时,该配置页才会显示。表示在安装应用包时,在导入实例化配置数据之前执行的脚本。一般用于预清理数据,避免数据冲突的情况。
您可以选择已有脚本,也可以单击“创建”新建脚本。
-
部署策略 > 安装后置脚本
当选择“资产包”打包且打全量包时,该配置页才会显示。表示在安装应用包时,在导入实例化配置数据之后执行的脚本。一般用于删除、更新数据等。
您可以选择已有脚本,也可以单击“创建”新建脚本。
-
部署策略 > 安装时组件的更新策略
当打包的组件中包含系统参数、连接器、Rest操作、数据接入或事件流时,才会显示该参数。例如可设置系统参数随包打包发布后,在升级时遇到新旧数据冲突(唯一索引相同的数据)的数据更新策略。
- 覆盖:当相关组件数据在打包升级到其他环境,发生数据冲突时,会进行覆盖。
- 不覆盖:当相关组件数据在打包升级到其他环境,发生数据冲突时,不会进行覆盖。
默认“不覆盖”。在配置为“不覆盖”的情况下,例如在开发环境修改数据接入的任意配置数据(包括所有图元的配置信息),打包升级到测试或者运行环境,不会覆盖同名的数据接入配置,即在开发环境修改的数据在测试或者运行环境不会生效。
覆盖
预置数据
当选择“资产包”打包时,该配置页才会显示。
您可以在该页面选择您在应用打包时一起发布的数据。支持按照对象名称打包。单击“添加对象”可设置数据导出条件,选择对象后,在应用打包时,会将该对象的满足条件的数据都打包出来。打包后,在资产包中“refdata”文件夹下可查看到导出的数据文件。
使用该方式前,您需要先清理不需要发布的数据,且导出对象的“基本信息”页必须勾选上“允许API批量访问”。
-
- 如图37所示,单击,选择“编译”,编译完成如图38所示。
- 如图39所示,单击。
- 如图40所示,在弹出的页面中选择“我的仓库”。
- 如图41所示,填写版本信息,单击“发布”。
如果勾选“压缩高级页面”,表示会对包中所有高级页面涉及的css和js文件进行合并及压缩,这样可以有效降低运行时服务器压力,但从终端浏览器首次访问该站点页面时,访问时间会稍微增加。
发布成功后,页面显示“程序包已经被成功上传到我的仓库。”