智能软件平台 XMagital 统一门户应用接入规范

1. 引言

1.1 目的

本规范定义 XMagital 平台门户集成应用的方案和统一规则,为平台工具、基于平台开发的行业应用提供接入标准,以及接入后的集成管理方法。本文档的内容包括门户的核心能力、接入规范定义、接入规范示例、API参考等,目的是构建一个具备强集成能力和配置能力的统一门户框架,支撑公司传统SCADA项目、MES项目、智能工厂项目等多种应用场景,适应用户端个性化的需求。

1.2 预期读者

本文档的预期读者包括:XMagital 平台软件的管理人员、设计人员、开发人员、测试人员,在 XMagital 平台基础上进行行业应用软件产品开发的设计人员、开发人员、测试人员、工程实施人员和维护人员。

1.3 术语与缩略语

本文使用的专业术语、自定义的词语、容易产生理解偏差的词语以及缩略语。

术语解释
平台一个公共的基础,在此基础上可以开发不同产品。平台是产品线开发的基础,它为衍生一个产品提供可共用和可重用的特性、设计元素(组件、代码功能)以及相关流程和工具。本文中所提及的平台即XMagital平台。 [GB/T37413-2019 数字化车间术语和定义]
应用Application,为应用软件(application software)的简称,指专为特定工程或特定生产场景编写的程序包,或本身也具有一定的通用性,但是通过XMagital应用开发框架编写的、并可以随时从系统摘除的软件包或模块(如签名软件,称量软件,或WMS接口程序等等)。
HSM-PortalXMagital平台的统一门户,以单点登录、权限管理为基础,提供强大的应用集成能力,为用户提供统一的应用访问入口。

2. 概述

2.1 简介

HSM-Portal(统一门户)是XMagital平台生产运行环境的入口,面向的目标群体是最终用户。

HSM-Portal通过松耦合方式集成各类应用,包括基于平台的全新应用、已有应用、第三方应用等。通过对应用的集成管理,提供全局的功能和信息导航。用户以门户主页为入口,可方便、快捷地进入各个应用系统,打开并使用具体的应用功能。

HSM-Portal通过单点登录和权限管理将所有应用和数据集成到一个平台之上,实现应用和资源的统一管理;同时具备主页和工作台的配置能力,为用户提供个性化的界面和服务。统一门户既是一个展示窗口,又是一个可定制的业务处理平台。

2.2 Portal核心能力

HSM-Portal提供一套应用集成框架和一组配置工具,主要解决用户端的应用集成和个性化配置问题。

应用集成框架由内置环境和接入规范构成,为应用接入Portal提供统一方法和规则,并提供接入后的集成管理方案,支持各类应用的集成,具备广泛的适用性。单点登录、权限管理是应用集成框架的基础,在此基础上构建的门户级功能和信息导航是应用集成的价值所在。

配置工具提供个性化定制能力,包括:登录页配置、首页配置、用户工作台配置、主题配置、多语言配置等。支持用户根据实际需要定制门户产品形态,以适应不同行业特征、使用场景、风格偏好;支持业务用户定制个性化工作台,配合门户框架提供的消息通知、任务通知功能,为用户日常工作提供便利。

2.2.1 应用集成

应用和资源配置:

  • 可添加/编辑应用(应用名称、图标);

  • 可配置应用资源(菜单)。

应用的界面集成方式:

  • Portal框架内嵌式(平台应用推荐);

  • 独立浏览器页面式(第三方应用推荐)。

2.2.1.1 单点登录

单点登录(Single Sign-On,SSO)是一种身份验证和访问控制机制,它通过一个共享的身份验证中心统一管理用户登录凭证,允许用户通过一次身份认证访问多个应用系统。单点登录机制能够简化用户的身份验证过程,提高用户体验和安全性。

  • 提供共享认证信息Token接口;

  • 提供Portal与接入应用之间认证信息Token共享的开发规范;

  • 提供OAuth2.0认证接口;

  • 提供Portal与接入应用之间通过OAuth2.0协议接入的开发规范。

2.2.1.2 权限管理

提供基于角色的访问控制(RBAC)机制,对接入应用的对象资源和数据资源进行权限管控。权限管理需要和单点登录配合使用。

  • 操作权限(资源/菜单、按钮);

  • API权限;

  • 数据权限(组织、岗位、用户)。

注:数据权限通常需要与具体应用功能配合完成,权限管理提供基础配置信息,应用功能根据实际需要落实数据使用权限。例如:流程图的图元操作,权限管理提供角色信息和图纸级的权限控制,图元级的权限由流程图自身根据角色进行管控。

2.2.1.3 门户导航

通过对接入应用、应用资源(菜单)的集成管理,形成门户级信息和功能导航。

  • 整合多应用消息、任务并集成管理,形成统一的消息中心、任务中心;

  • 整合多应用资源,并支持重新分组、排序,形成门户功能导航。

2.2.2 个性化配置

2.2.2.1 登录页

支持登录页的配置和自定义。

  • 提供默认登录页;

  • 提供登录组件;

  • 可自主开发登录页,通过配置挂接到门户平台。

2.2.2.2 主页面

主页面是用户登录成功后自动跳转的首页,门户主页可配置。

  • 提供默认主页面;

  • 可自主开发主页面,通过配置挂接到门户平台;

  • 可定制主页的布局。

图片 1

主页面布局配置原型图

主页面布局和可定制区域

  • 支持配置多个布局;

  • 支持布局与用户关联,用户未配置或未选择布局时,系统使用默认布局;

  • 同一时间仅有一个生效布局;

  • 布局可定制区域包括:

  • 标题栏;

  • 菜单导航栏;

  • 主应用功能展示区;

  • 次级应用展示区;

  • 布局与用户关联。

2.2.2.3 个人工作台

个人工作台:主应用功能展示区起如页,可定制:

  • 提供默认主应用功能展示区起始页,并支持用户自定义起始页;

  • 起始页包括快捷功能图标区、编辑区,均可定制;

  • 快捷功能图标区用于放置常用功能连接,用户可定制;

  • 编辑区用于放置常用功能,用户可定制;

    • 常用功能举例:消息待办、任务待办、报警清单等;
    • 放入编辑区的功能支持分栏显示:1列显示、2列显示等;
    • 编辑区支持配置的页面数量上限:10。
  • 个人工作台与用户关联。

消息待办和任务待办的接入参考中间件关于待办和通知的相关规范。

2.2.2.4 主题

配置门户使用的颜色主题。

  • 支持多个颜色主题;

  • 默认提供2至3个主题,支持主题扩展;

  • 同一时间仅有一个生效主题;

  • 主题与用户关联。

2.2.2.5 多语言

提供多语言的切换功能,界面文本跟随语言切换。语言切换时向接入HSM-Portal的应用发送消息,通知其进行相应的语言切换操作。

2.2.3 接入流程

图片 5

3 定义

3.1 运行时原理

图片 1

3.2 子应用接入流程

  1. 服务端开发
    信息模型集成
    中间件集成
    权限集成
       API权限
       数据权限

  2. 前端开发
    集成菜单权限
    集成操作权限
    集成主题
    集成多语言 (可选)
    集成图标API(可选)

  3. 预置数据
    预置多语言数据
    预置菜单数据

  4. 打包 子路径(URL)划分

  5. 发布

3.2.1 前端开发流程

图片 2

3.2.1.1 安装通信工具

子应用集成过程中大多数行为都涉及到与框架的通信,所以安装并使用通信工具是一个非常基础并且重要的工作。框架提供了通信工具包:event-bus,已发布在 npm 私服中,子应用可以通过 npm 安装该依赖直接使用。Event-bus 中已经封装了部分通信常用的方法,大大降低了自定义通信带来的额外工作量和不方便维持的额外约定。

3.2.1.2 约定通信事件
3.2.1.2.1 token信息

子应用在初次加载的时候可以通过通信工具向框架请求一些基础的系统信息,包括token、用户基础信息、偏好设置信息等。

Token 信息通过 bus.getData(‘user-info’).then(res => {}) 拿到的信息的 res.data.token 读取值

3.2.1.2.2 用户信息

获取到的用户信息主要包含:ID、用户名、地址、头像、邮箱、昵称、性别、电话。用户信息通过 bus.getData(‘user-info’).then(res => {}) 拿到的信息的 res.data.userInfo 读取值

3.2.1.2.3 主题信息

主题信息通过 bus.getData(‘user-info’).then(res => {}) 拿到的数据中的 res.data.setting.theme 字段值。随后监听设置变化事件 bus.on(‘setting-change’),当返回的值中有 theme 字段时,需要更新主题。

3.2.1.2.4 多语言信息

多语言信息通过 bus.getData(‘user-info’).then(res => {}) 拿到的数据中的 res.data.setting.lang字段值。随后监听设置变化事件 bus.on(‘setting-change’),当返回的值中有 lang 字段时,需要更新语言。

3.2.1.2.5 路由跳转

子应用在初始化的时候可以拿到所有子应用相关的菜单列表(详见 3.2.1.3),在需要通知框架跳转时,通过 bus.post({type: ‘route-change’, { code: ‘xxx’, params: { } } }),其中 code 为菜单的编码, params 参数为选传。

3.2.1.3 集成菜单权限

子应用在首次加载的时候,需要通过  bus.getData(‘user-info’).then(res => { res.data.menus })  获取到当前应用的菜单信息。

另外如果需要全量的权限菜单数据,子应用可以通过 bus.getData(‘all-menu’) 拿到。也可以通过 token 去调用 auth 服务接口拿到用户的权限菜单数据。

子应用拿到该权限列表之后根据权限控制页面的访问。例如动态注册路由的方式生成路由表,以避免直接通过修改链接的方式访问无权限页面。

3.2.1.4 集成操作权限

子应用在首次加载的时候,需要通过  bus.getData(‘user-info’).then(res => { res.data.actionAuth })  获取到当前当前应用所有菜单的操作权限列表。页面根据操作权限列表展示当前登录用户可操作的按钮。

3.2.1.5 打包

子应用如果单独部署,正常打包即可,无需额外配置。

但是如果子应用需要与Portal部署在同源环境下,则需要在打包时配置子应用的根路径,例如:baseUrl来实现二级目录部署。

BaseUrl 必须使用应用编码,如工具建模应用的baseUrl为 app-db。

3.3 新应用接入规范

3.3.1 UI/UE风格

为了保证接入HSM-PORTAL的应用的 UI/UE的一致性,应用需遵循 《智能软件平台HSM-OS V1.0软件UI与UE规范》

子应用以iframe方式内嵌到框架内容展示区,是一个独立的展示区。应用内部弹框只覆盖当前内容区,不影响框架其他部分。

3.3.2 前端通信规范EventBus

子应用与框架之间使用EventBus进行通信。

常用事件

事件描述示例
on监听全局事件 包括emit和post的事件bus.on(‘setting-change’, res => { })
off关闭on监听事件bus.off(‘setting-change’)
post向框架发送事件bus.post(data)
emit发送应用内部事件bus.emit(‘change’, data)
clearAll清除所有监听事件bus.clearAll()

常用方法

方法名描述示例
getData获取框架数据获取用户相关信息: bus.getData(“user-info”).then(res => {})
getDataSelf获取另一个 iframe 的数据。需要配置workerPathHMI子应用的 iframe A中: bus.registryApi(‘active-node’, () => ‘textNode’) HMI子应用的  iframeB中: bus.getDataSelf(“active-node”).then(res => {})

自定义事件

事件类型描述示例
route-change子应用需要跳转路由时通知框架bus.post({   type: ‘route-change’,   data: {     path: ‘xxx’ } })
setting-change用户设置修改,包含主题、语言设置信息等bus.on(‘setting-change’, (res) => {})
user-info获取用户信息,包含token、用户名、主题、语言设置等bus.getData(“user-info”).then(res => {})
logout用户退出登录bus.on(‘logout’, () => {})
hsm-message弹出提示消息,配置与 ElMessage 相同bus.post({     type: “hsm-message”,     data: {       type: “success”,       message: “这是一段成功文字”,     },   });
hsm-message-box弹出模态框,配置与 ElMessageBox 相同,需要注意的是不支持回调函数类型的配置bus.getData(“hsm-message-box”, {       title: “标题”,       message:“hsm-message-box message”,       type: “warning”,       showCancelButton: true,     })     .then((res) => {       if (res?.data) {         const action = res.data.action;         console.log(“点击了:”, action);       }     })

详细使用参考API手册

3.3.3 统一资源

3.3.3.1 公共依赖

为了减少项目的体积,加快页面打开速度,对于Vue3、ElementPlus、Echarts 等常用库通过静态资源站点的方式集中管理,加载公共的静态资源可以减少子应用加载过程中额外的网络请求和内存占用,对于公共的依赖,推荐子应用在 index.html 文件中以 script 标签的方式引入使用,静态资源链接的规范格式为:/pkg/{包名}/{版本号}/{包名}.[{min|esm}.]js

3.3.3.2 图标服务

为了图标风格的统一,子应用如有需要,可以使用框架提供的图标库 iconify,该动态图标库部署在我们的静态资源站点,可以动态加载图标,而且通过强缓存缓存在浏览器中,不用担心请求数量过多和重复请求的问题。

对应的UI组件:svg-icon 发布在私服中,支持内网在线图标库、项目本地svg文件、在线图片等多种图标展示方案。

3.3.3.3 多语言服务

通过请求静态资源站点的语言包来实现动态可配置的多语言,语言包按语言以 json 格式分类存放,请求的格式为:/langs/{lang}.json。其中每个应用的文件夹内包含一个 config.json 文件,主要记录当前文件夹有哪些语言,以及默认语言。

子应用可以获取静态资源站点提供的语言包或自己工程 public 目录下定义的语言包文件。建议语言包的存放结构为:

配置文件url: /langs/config.json 语言包文件url: /langs/{lang}.json

服务端部署结构:

 ├─ APP1
      └─/langs
          ├─ config.json
          ├─ zh-CN.json
          └─ en-US.json
  └─ APP2
      └─/langs
          ├─ config.json
          ├─ zh-CN.json
          └─ en-US.json

推荐用法:在路由拦截中判断是否已经获取了在线的语言包,如果未获取,则通过fetch 的方式获取指定应用的语言包配置文件 config.json,通过config.json 中配置的语言包集合来判断是否有框架设置的语言包,如果有则正常加载,如果没有则加载默认语言包。通过 fetch 来获取语言包 json 文件,使用 vue-i18n 的 merge API,合并 message 属性。

对于 element-plus 自带的语言选项,我们在项目打包的时候建议把 element-plus 支持的语言包全部打包进去了,只需要动态设置语言选项即可。

图片 744037166

3.3.3.4 对象存储服务

参考DB组提供的对象存储服务

3.3.4 身份验证

3.3.5 权限控制

3.3.5.1 配置

参见《智能软件平台HSM-OS认证授权与单点登录接入规范》“4.2.2 接入支持”章节

3.3.5.2 开发集成

参见《智能软件平台HSM-OS认证授权与单点登录接入规范》“4.2.2 接入支持”章节

3.3.5.3 前端集成

本章节指导用户使用HSM-Protal前端入口集成新应用。

图片 1

3.3.5.3.1 动态构建路由
  1. 在Protal中配置应用菜单及权限,通过其他方式内置权限的可跳过此步骤;

  2. 登录Protal;

  3. 调用Auth提供的permissionOneUserInfo或@hsmos/api/portal/user中的getUserpermission()方法获取当前用户的权限信息;

  4. 根据权限信息中的菜单权限,构建路由。

3.3.5.3.2 动态绑定操作权限
  1. 在Protal中配置应用菜单及权限,通过其他方式内置权限的可跳过此步骤;

  2. 登录Protal;

  3. 调用Auth提供的permissionOneUserInfo或@hsmos/api/portal/user中的getUserpermission()方法获取当前用户的权限信息;

  4. 根据“权限信息>菜单权限”中的操作组及操作权限,动态绑定操作权限。

3.3.6 登录

默认的登录页面是一个独立部署的服务,该服务仅提供登录页面的展示和配置功能,当使用系统默认的登录页面时,通过反向代理的方式,以当前域访问到登录页面,输入正确的用户名、密码,点击登录,服务端校验通过后颁发token,登陆页面会将token存储在当前域的localStorage 中,并且跳转到配置的默认首页。

默认的首页外观可通过配置来调整,主要配置项有背景色、背景图片、登录框位置、标题、欢迎语等。

如果不使用默认的登录页面,也可以自定义登录页面覆盖默认登录页,自定义登录页面应该遵循系统身份认证规范,具体实现参见3.3.6.5

3.3.6.1 登录流程

图片 2

3.3.6.2 业务逻辑

图片 4

3.3.6.3 渲染流程

图片 6

3.3.6.4 设置项

图片 1

登录页面提供一系列的配置项,来实现布局调整。配置项分为3部分,分别是背景相关、登录框相关和欢迎语相关。

背景配置项中有背景图片和背景颜色的配置,其中背景图片可以指定一个图片URL。背景颜色可以使用调色板选择颜色来设置。

登录框可通过配置来设计登录框:

  1. 可设置登录框的位置,包含居左、居中、居右。

  2. 支持登录框背景颜色设置。

  3. 支持登录框标题内容的定制。

欢迎语的配置包含位置信息(居左、居中、居右)、对齐方式(左对齐、居中对齐、右对齐)、主副标题文本颜色、主副标题文本内容。

3.3.6.5 完全自定义登录页

如果不使用系统提供的默认登录页,子应用可以自定义登录页。

3.3.6.5.1 从0开始

当用户输入用户名和密码时,需要将这些信息发送给Auth服务端以获取用户Token。并且把获取到的 Token 存储在 localStorage 中,使用的键名为 —token。

登录成功后需要判断当前url是否携带了 redirect参数,如果有该参数,则跳转到指定的重定向页面,如果没有,跳转到默认首页,默认首页为当前域的 /home 页面。

登录API参见《智能软件平台HSM-OS认证授权与单点登录接入规范》

3.3.6.5.2 部署和配置

自定义开发的登录页面需要独立部署,部署成功之后,在nginx通过反向代理的方式替换原有的系统默认登录页面,之后访问 /login 即为新的自定义登录页面。

3.3.7 多语言

子应用在首次加载的时候应该通过 event-bus 获取用户当前设置的语言信息。框架提供统一多语言切换入口,框架切换了语言时,会通过 EventBus 的 post 方法向所有已加载的应用发送 setting-change 事件,当子应用监听到 setting-change 事件时,获取其中的 lang 参数来设置语言状态。

事件类型描述子应用监听示例
setting-change个人偏好设置包含语言设置,子应用需要监听该事件bus.on(‘setting-change’, res => {   const lang = res.data.lang })

3.3.8 主题

系统管理员: 管理多个系统主题.

普通用户: 从多个系统主题中使用某一个主题.

可使用在线主题配置.

子应用首次加载时通过 event-bus 工具发送 getData(‘user-info’) 请求可获取到当前框架的主题信息,在返回的 res.data.setting.theme 字段中读取主题信息。

主题的切换入口在框架中,当用户选择了主题切换时,会通过bus.emit() / bus.post() 同时向已打开的浏览器页签及集成的子应用发送设置变更的事件,其中的 theme 为变更后的主题信息。

事件类型描述子应用监听示例
setting-change个人偏好设置包含主题设置,子应用需要监听该事件bus.on(‘setting-change’, res => {   const theme= res.data.theme })
3.3.8.1 开发集成

图片 1

主题信息由一组key-val 组成的对象表示,如何使用这组主题信息由子应用自行决定,其中包含主题色、浅色、深色、成功色、警告色、危险色、信息颜色、文本颜色、边框颜色、填充色、背景色等色值。

3.3.9 前端存储

子应用如果与框架部署在同源环境中,原则上不允许操作 localStorage / sessionStorage / cookie ,不能做清空缓存的动作。如果确需使用,需要以自己的 appName 作为前缀,例如:hmi-activeTab,避免使用时产生键值冲突。框架缓存的键一般以双中划线开头,例如:—token.

如果确认不在同源环境中无此约束。

框架已使用的键:

存储位置键名说明
localStorage—tokenPortal token
—langPortal 语言
—themePortal 主题ID
—lastActiveTimePortal 最后操作时间
—isLockPortal 是否锁屏状态标识

3.3.10 URL规划

3.3.10.1 前端

各应用前端应使用子路径的方式打包,如修改代码的baseUrl,各应用的子路径如下:

名称后端URL备注
Portal/ 默认Portal首页,登录完成后进入
登录/login 统一登录入口
公共服务/- 中划线, 框架提供的公用资源以”/-/“开始
公共服务/公用依赖/-/pkg 举例
公共服务/上传的文件/-/uploads 举例
公共服务/图标/-/iconify举例
SCADA/scada举例,“scada”,遵循打包规范或应用仓库规范,全局唯一
any应用/appcode举例,“appcode”,遵循打包规范或应用仓库规范,全局唯一

如上,已占用的一级路径有 ”/”、“/login” 和 ”/-”, 其他应用不得占用

3.3.10.2 后端

与中间件集成各应用方式一致,后端URL规划如下:

名称后端URL备注
平台自带/api/-/…此项为建议,待商榷, 应遵循中间件集成规范
any应用/api/v1/some遵循中间件集成规范,且唯一

3.3.11 使用帮助

3.3.11.1 语法要求

帮助文档推荐使用markdown语法编写

3.3.11.2 存储目录
3.3.11.3 帮助集成

门户应用自行编写各自模块的手册内容,打包发布后在HSM-PORTAL将应用帮助的在线地址挂载到门户帮助菜单下。

3.4 已有应用接入规范

由于老应用没有遵循HSM-PORTAL应用接入规范开发,在不改造现有项目的场景下是不能接入门户框架的。

集成度:外部链接、身份验证、权限控制、语言、主题, 老应用建议跨域部署

3.4.1 接入点

  • 必选
  1. 身份认证和授权

    • 使用单点登录集成到HSM-PORTAL
  2. 页面

    • 外部链接
    • 内嵌使用iframe的方式集成
  • 可选
  1. 权限控制

  2. 语言

  3. 主题

3.4.2 集成度

  • 浅集成:

    • 身份认证和授权 + 页面
  • 深度集成:

  • 身份认证和授权 + 页面 + 权限控制 + 语言 + 主题

详细集成说明参见接入规范定义

3.5 iframe 安全限制

iframe 在部署时需要按照内容安全策略(CSP)和 X-Frame-Options 的配置策略来确定是否可以被框架正确加载。

4 接入规范示例

4.1 UI/UE设计原则

子应用的UI/UE需遵循 《智能软件平台HSM-OS V1.0软件UI与UE规范》。

4.2 Npm库配置说明

由于开发环境是内网环境,并且使用到的部分依赖是发布在npm私服的,所以使用这些依赖均需设置项目的 npm 源,设置方式为在项目根目录下创建 .npmrc 文件,之后在项目工程目录使用 npm 相关命令访问的源都是基于npm私服的。

.npmrc 文件内容:

// .npmrc

registry=http://172.21.44.57:4873/

4.3 前端通信规范EventBus

HSM-Portal框架与子应用之间通过EventBus进行通信:

4.3.1 安装

在终端中执行 pnpm install @hsmos/event-bus

图片 4

4.3.2 引入

在项目 /src/utils 文件夹中创建 bus.ts 文件

import EventBus, { type BusOption } from '@hsmos/event-bus'
const option: BusOption = {
  name: getQueryString(‘appName’)
}
const bus = new EventBus(option)
export default bus

其中 BusOption 中的 name 指的是当前子应用的编码,这个是Portal的应用信息管理中配置的。在接入 Portal 的时候,可以从iframe的src上读取 appName 参数,来初始化 EventBus。

一个iframe地址示例:

<iframe src=“https://172.21.44.120/scada/#/info?appName=app-scada”></iframe>

读取参数的工具函数示例如下:

// 获取查询字符串中的参数

export function getQueryString(key: string) {
  const arg: Record<string, string> = {};
  if (location.href.includes('?')) {
    const qs = location.href.split('?');
    let query = qs[1];
    if (query.includes('#')) {
      // 如果查询字符串后存在hash值,去掉hash值
      query = query.split('#')[0];
    }
    query.split('&').forEach((v: string) => {
      const kv = v.split('=');
      arg[kv[0]] = kv[1];
    });
  }
  return arg[key];
}

4.3.3 应用

子应用与框架的通信有三种情况:

4.3.3.1 子应用按需获取框架提供的信息

子应用通过 bus.getData() 的方式可以获取框架提供的信息,该方法返回一个 Promise,以保证不会阻塞子应用的主线程。

子应用在初始化EventBus之后的任何时机都可以执行该方法,例如框架提供了一个接口:user-info,那么我们通过该方法获取基座的用户信息。

bus.getData('user-info').then((res) => {
  console.log('获取到的用户信息: ', res);
});
4.3.3.2 子应用向框架发送消息

子应用可以通过 bus.post() 的方式向框架发送消息,比较常见的场景是,子应用点击跳转路由时,需要判断所处的环境,如果是在 iframe 环境,则需要向框架发送路由跳转的消息,由框架来完成路由跳转。

function toPath(path: string) {
  if (window !== parent) {
    bus.post({
      type: 'route-change',
      data: {
        path,
      },
    });
  } else {
    router.push(path);
  }
}
4.3.3.3 子应用接收框架发送的消息

子应用需要通过 bus.on() 来监听基座发送的过来的消息。比较常见的场景是,如果基座改变了语言、主题等偏好设置,会通知子应用更新设置。

bus.on('setting-change', (res) => {
  console.log('收到设置变化消息通知: ', res);
});

4.4 统一资源

4.4.1 公共依赖

公共依赖的服务可以通过 /pkg 标识符反向代理的方式代理在当前域中,所以在访问的时候不需要设置域。

例如:

<script src='/pkg/vue/3.2.37/vue.min.js'>
<script src='/pkg/vue-router/4.0.15/vue-router.min.js'>   

4.4.2 图标服务

图标服务作为静态资源存放在静态资源站点。安装使用步骤如下:

  1. 安装 pnpm i @hsmos/svg-icon

  2. 在 main.ts 中引入 svg-icon 组件

// main.ts
import { createApp } from 'vue';
import SvgIcon from '@hsmos/svg-icon';
// 设置 icon 源

const iconOrigin = import.meta.env.DEV ? 'http://172.21.44.57:3000' : '/icons';
const app = createApp(App);
app.use(SvgIcon, { iconOrigin });
app.mount('#app');
  1. 使用

使用时先在图标集合网址 https://icon-sets.iconify.design/ 中查找需要使用的图标,例如:material-symbols:home

图片 1

默认用法:

<svg-icon icon="material-symbols:home" />

svg-icon 组件除了支持在线的动态图标库以外,还支持项目本地的svg图标加载和在线的图片显示,使用示例如下:

要支持本地 svg 图标加载,首先在 vite.config.ts 中配置 vite 插件

// vite.config.ts

import SvgIconPlugin from '@hsmos/svg-icon/plugin';

export default defineConfig({
  plugins: [
    SvgIconPlugin(), // 可传入 svg 集合路径,默认 src/assets/icons/svg
  ],
});

使用,加载本地svg图标,需要设置type为svg:

<!-- 本地 svg 图标,路径 src/assets/icons/svg/test/build.svg -->

<svg-icon type="svg" icon="test-build" /> 

如果想要加载一个在线的图片作为图标,需要type类型设置为online:

<!-- 在线图片 -->

<svg-icon type="online" icon="http://localhost:8080/assets/logo.svg" /> 

4.4.3 多语言服务

首次加载需要获取当前应用当前版本的语言包,在路由拦截中获取。

  1. 安装
    pnpm i @hsmos/locale

  2. 实例化

// /src/utils/i18n.ts

import HsmI18n from '@hsmos/locale'

// 默认读取当前项目下 /langs 中的 config.json 和 语言包 文件,默认语言为 zh-CN ,默认存储在 localStorage 中的国际化 key 为 lang

const hsmI18n = new HsmI18n()export default hsmI18n
  1. 注册
// /src/main.ts
import hsmI18n from '@/utils/i18n';
app.use(hsmI18n.i18n);
  1. 加载语言

在路由拦截中判断 hsmI18n.isLoaded 是否已加载,如果已加载直接 next,如果未加载 await 加载语言包。

// router.ts

router.beforeEach(async (to, from, next) => {
  document.title = to.meta.title;
  if (!hsmI18n.isLoaded) {
    await hsmI18n.init();
  }
  next();
});
  1. 切换语言
import hsmI18n from '@/utils/i18n'await hsmI18n.setLang('en-US')

4.5 身份验证

参见《智能软件平台HSM-OS认证授权与单点登录接入规范》“3单点登录”章节

4.6 权限控制

4.6.1 开发集成

参见《智能软件平台HSM-OS认证授权与单点登录接入规范》“4.2 接入说明”章节

4.6.2 前端集成

本章节指导用户使用HSM-Protal前端入口集成新应用。

4.6.2.1 动态构建路由
  1. 在Protal中配置应用菜单及权限

  2. 登录Protal

  3. 调用Auth提供的permissionOneUserInfo或@hsmos/api/portal/user中的getUserpermission()方法获取当前用户的权限信息

Auth提供的permissionOneUserInfo接口参考身份验证及权限控制接口。

调用示例

query permissionOneUserInfo {
  permissionOneUserInfo {
    applicationId
    applicationInfo {
      id
      applicationCode
      applicationName
      i18n
      applicationType
      isOtherApplication
      visitUrl
      imageUrl
      authType
      componentPath
      routeType
      openStyle
      description
      clientId
      clientSecret
      callbackUrl
      accessTokenOverUnit
      accessTokenOverValue
      refreshTokenOverUnit
      refreshTokenOverValue
      createBy
      createdAt
      updatedBy
      updatedAt
    }
    menus {
      menuId
      menuInfo {
        id
        name
        code
        parentId
        applicationId
        menuType
        openStyle
        icon
        url
        componentPath
        openStyle
        i18n
        sort
      }
      operateGroup {
        operateGroupId
        operateItems {
          operateInfo {
            code
            name
          }
        }
      }
    }
    apis
  }
}

@hsmos/api/portal/user中的getUserpermission()方法调用示例

import { getUserpermission } from '@hsmos/api/portal/user ';
getUserpermission().then((res) => {
  // 后续权限处理
});

接口返回参考

图片 1

  1. 根据权限信息中的ApplicationInfo及menus中的数据,构建路由及导航菜单
import { getUserpermission } from '@hsmos/api/portal/user ';
getUserpermission().then((res) => {
  // 根据ApplicationInfo及menus中的数据,构建路由及菜单
});
  1. 将获取到的权限路由列表根据parentId转为树结构
// 根据 parentId \ menus 组织菜单树

function getMenuTree(menus: MenuData[], parentId: null | string = null) {
  const tree = [];
  const children = menus.filter((v) => v.parentId === parentId);
  for (const child of children) {
    const descendants = getMenuTree(menus, child.id);
    if (descendants.length) {
      child.children = descendants;
    }
    tree.push(child);
  }
  return tree;
}
  1. 将树结构动态添加到路由表中

    示例如下

    // router.ts
    
    // 填充动态路由
    
    function fillAsyncRouter(
      asyncMenus: MenuData[],
      parentName: string
    ): RouteData[] {
      const routes: RouteData[] = []
      asyncMenus.forEach((menu) => {
        if (menu.openType === APP\_OPEN\_TYPE.ROUTER\_PUSH) {
          const route: RouteData = {
            path: getMenuPath(menu.path),
            name: menu.code,
            redirect:
              menu.children && menu.children.length ? menu.children[0].path : '',
            meta: {
              appCode: menu.appCode,
              icon: menu.icon,
              title: menu.title,
              openType: menu.openType,
              noCache: menu.noCache,
            },
            component: loadView(menu),
          }
    
          if (menu.children && menu.children.length) {
            route.children = fillAsyncRouter(menu.children, menu.code)
          }
          router.addRoute(parentName, route)
          routes.push(route)
        }
      })
    
      return routes
    }
    
    // menu.path 需要加工,1、添加 / 前缀;2、去掉查询参数
    
    function getMenuPath(path: string) {
      // 去掉查询参数
      if (path.includes('?')) {
        const arr = path.split('?')
        if (arr && arr[0]) path = arr[0]
      }
      if (!path.startsWith('/')) {
        path = `/${path}\`
      }
      return path
    }
    
    // 匹配views里面所有的.vue文件
    
    const modules = import.meta.glob('../views/\*\*/\*.vue')
    function loadView (menu: MenuData) {
      let res
      if (menu.children && menu.children.length) {
        res = ParentView
      } else {
        for (const path in modules) {
          const dir = path.split('views')[1].split('.vue')[0]
          if (menu.component) {
            // 有自定义 component 优先加载自定义的路径
            if (dir === \`${menu.component}\`) {
              res = () => modules[path]()
            }
          } else {
            if (dir === \`${menu.path}/index\`) {
              res = () => modules[path]()
            }
          }
        }
      }
      return res
    }

4.6.2.2 动态绑定操作权限
  • 在Protal中配置应用菜单及权限

  • 登录Protal

  • 调用Auth提供的permissionOneUserInfo或@hsmos/api/portal/user中的getUserpermission()方法获取当前用户的权限信息,参考动态构建路由中的第3步

  • 根据“权限信息>菜单权限”中的操作组及操作权限,动态绑定操作权限

import { getUserpermission } from '@hsmos/api/portal/user ';
getUserpermission().then((res) => {
  // 根据OperateGroup及OperateItems生成操作按钮
});

4.7 多语言

子应用需要监听框架设置变更事件,读取lang字段值并进行多语言的切换。

interface SettingData {
  lang: string
}

bus.on<SettingData\>('setting-change', res => {
  if (res.data) {
    if (rea.data.lang) {
      // 处理语言设置逻辑
    }
  }
})

4.8 主题

子应用需要监听框架设置变更事件,读取theme字段值并进行主题的切换。

interface SettingData {   theme: Record<string, object> }

bus.on<SettingData>(‘setting-change’, res => {   if (res.data) {     if (rea.data.theme) {       // 处理主题设置逻辑     }   } })

4.9 前端存储

// 可以使用

localStorage.getItem('hmi-activeTab');
localStorage.setItem('hmi-activeTab', 'xxx');
localStorage.removeItem('hmi-activeTab');
sessionStorage.setItem('hmi-activeTab', 'xxx');
sessionStorage.removeItem('hmi-activeTab');

// 禁止使用

localStorage.clear();
localStorage.setItem('--token', 'xxx'); // 自定义登录页可以使用
localStorage.removeItem('--token');

4.10 打包

对于需要与 Portal 同源部署的应用,需要配置 baseUrl。

使用 Vite 构建的应用,在 vite.config.ts 文件中做如下配置:

// vite.config.ts

export default defineConfig({
  base: '/app-db',
});

注意 base 属性的字符串要以斜杠开头

使用webpack构建的应用,需要在vue.config.js 文件中做如下配置:

// vue.config.js

module.exports = {
  publicPath: '/app-db',
};

4.11 使用帮助

Gitbook 是一款基于Node.js 的开源文档管理工具,具有丰富的开源插件。

Gitbook支持markdown语法,能够输出html,pdf,epub,mobi等多种格式。

4.11.1 Gitbook工具安装

安装命令:

图片 1

安装完检查:

图片 1

4.11.2 文档创建

4.11.2.1 创建帮助文档

mkdir gitbook-demo

进入该目录:

cd gitbook-demo

图片 1

初始化创建:

gitbook init

图片 1

4.11.2.2 编写

图片 1

README.md 为应用手册自己定义的内容

SUMMARY.md 是编写侧栏的目录结构

\- [引言](++README.md++)

\- [用户](++modules/users/README.md++)

\- [\`权限\`](++modules/permission/README.md++)

\- [权限分配](++modules/permission/create.md++)

4.11.3 本地运行预览

图片 1

本地访问http://localhost:4000就可以运行查看帮助手册

图片 1

4.11.4 打包发布

图片 1

生成编译后的文档

图片 1

4.11.5 集成

将应用的帮助首页菜单挂载到HSM-PORTAL帮助菜单

5 API参考

5.1 前端组件参考

5.1.1 EventBus 通信组件

简介:适用于 iframe 的基座与子应用通信工具,支持跨标签页通信。

5.1.1.1 安装
npm install @hsmos/event-bus
5.1.1.2 使用说明
1. 在 utils 文件夹下创建 bus.ts 文件用于初始化 EventBus 对象

import EventBus, { BusOption } from '@hsmos/event-bus';

const option: BusOption = {
  name: 'app-main', // 当前应用的 name
  children: ['app-sub'], // 可选,仅框架需要配置,子应用列表,框架需要配置当前应用包含的子应用
};
const bus = new EventBus(option);
export default bus;

2. 在 main.ts 中引入

import bus from '@/utils/bus'; // 初始化 EventBus
// 注册监听事件,包括监听来自框架/子应用 post 事件和自身的 emit 事件// 可以在任意文件注册
bus.on('msg', (data) => {
  console.log('收到 msg: ', data);
});

3. 使用

// 向框架发消息,使用 post
import bus from '@/utils/bus';
import { TransferData } from 'event-bus-micro-app';

const data: TransferData = {
  type: 'msg',
  data: {
    msg: 'message',
  },
};

// 第二个参数如果有值,为框架向子应用发送消息,如果第二个参数为字符串 global,则向子应用列表中的所有子应用广播消息,支持数组参数,如果是数组可以批量通知子应用;

// 如果第二个参数为空,则是子应用向框架发送消息

bus.post(data);
bus.post(data, 'app-vite3');
bus.post(data, ['app-vite3', 'app-vite4']);
// 向自身发消息 —— EventBus 也支持当前应用中的发布订阅模式,类似于 mitt 或 this.$bus
bus.emit(type, data);

4. 通过 script 标签引入使用

目前还未部署在静态资源站点,所以需要通过下载压缩包,将 lib 中的 main.js 拷贝到本地项目中引入使用

<script src="./main.js"></script>

EventBus 构造函数已经挂载在全局变量中,直接实例化即可

<!DOCTYPE html>
<html lang="en">
  <body\>
    <script src="./main.ts"></script\>
    <script\>

      const bus = new EventBus({
        name: 'app-name'
      })

    </script\>
  </body\>
</html\>

5.1.1.3 更多配置

1. 子应用调用框架或自身提供的 API

API 的设计借鉴了 ajax 的思想,子应用通过 bus.getData(‘api-path’, params) 发起一个数据请求时,框架会根据 api-path 组织需要的数据并返回,这个过程是基于 Promise 的,所以子应用可以在任何时候,任何位置获取框架可提供的数据。

// 框架或子应用注册一个 api ,子应用可以获取 return 的数据

bus.registryApi('user-info', async () => {
  return {
    username: 'Job',
    age: 18,
  };
});

// 子应用调用框架提供的数据

bus.getData('user-info').then((res) => {
  console.log(res); // res 是符合 ApiData 结构的数据
});

// 子应用其他 iframe 调用子应用提供的数据,该方法需要 sharedWorker 支持,所以在初始化  event-bus 的时候需要传入 workerPath 资源路径

bus.getDataSelf('active-node').then((res) => {
  console.log(res); // res 是符合 ApiData 结构的数据
});
  1. 动态更新子应用列表,会覆盖初始化的子应用列表

const list: string[] = [‘app-app1’, ‘app-app2’, ‘app-app3’] bus.setChildrenApp(list)

5.1.1.4 使用注意事项
  1. 初始化的时候 name 不能为空,否则会报错:EventBus: The name cannot be empty during initialization;

  2. 框架向子应用发送消息时,需要确保子应用已渲染;

  3. 保留字符:

    • global : post 的第二个参数如果为 global,则向子应用列表中的所有子应用以及父应用发送消息,所以子应用命名不可为 global ;
    • get-data / set-data : 用于服务框架注册接口,子应用调用接口的通信服务,所以框架和子应用的通信时事件的 type 不要使用这两个保留名称;
    • page-unload : 用于页面刷新或关闭时通知 shared-worker 清除缓存,事件的 type 不要使用该名称;
  4. post / emit 在发送消息的时候,受 postMessage限制,传递的数据不能是DOM节点,vue 的响应式对象等,如果需要传递,可以进行序列化或使用 vue 提供的 toRaw API 进行转换。

5.1.1.5 类型定义
类型定义

// 内置事件类型

enum EventBusType {
  GetData = 'get-data',
  SetData = 'set-data',
}

// 约定的数据传输结构

interface TransferData<T = unknown> extends Record<PropertyKey, unknown> {
  type: string;
  from?: string; // 不需要手动设置,发送 post 事件时会自动携带
  timestamp?: number; // 不需要手动设置,通信时会自动添加,主要用来标识通信数据的唯一性
  data?: T;
}

// 子应用调用框架接口时通信传输结构

interface ApiData extends TransferData {
  type: EventBusType.GetData | EventBusType.SetData;
  api?: string; // 框架提供的 api
  params?: object; // 子应用调用 api 时传的参数
  data?: unknown; // 框架返回值
}

5.1.1.6 接口定义
接口定义

// 监听全局事件,包括 emit 和 post 的事件
on<T = TransferData\>(type: string, cb: EventCB<T\>): void;

// 发送本地事件,应用内部事件

emit<T=unknown>(type: string, data: T): void;
/\*\*
 \* 发送跨应用事件框架 -> 子应用, 子应用 -> 框架
 \* @param target - global: 向所有子应用和框架广播事件
 \* @param target - string - appName: 向指定的子应用发送事件
 \* @param target - array - string[]: 向传入的列表中的子应用发送事件
 \*  \*/ 

post(data: TransferData, target?: 'global' | string | string[]): void;
// 关闭 on 监听的事件
off<T = unknown>(type: string, cb: EventCB<T\>): void;
// 清除所有 on 中监听的事件
clearAll(): void;
// 框架注册 api
registryApi(api: string, cb: ApiCb): void;
// 子应用调用 api
getData<T=unknown>(api: string, params?: object): Promise<T>;
// 子应用其他iframe调用自身注册的 api,使用时需要注意 2个iframe的name 是否一致,是否在实例化 event-bus de 时候传入 workerPath 资源
getDataSelf<T=unknown>(api: string, params?: object): Promise<T>;
// 动态更新子应用列表 isMerge - 是否合并旧数据
setChildrenApp (list: string[], isMerge = false): void

5.1.1.7 事件列表
事件列表

bus.getData('user-info') - 可以获取到框架初始化用户信息,包含 token、主题、语言设置信息等。

bus.post({type: 'logout'}) - 子应用在需要退出当前系统时可以通知框架退出登录。

bus.post({type: 'hsm-message',data: options}) - 子应用需要通过框架弹出提示消息,options 可以传入 element-plus 支持的配置。

bus.getData('hsm-message-box', options) - 可以通知框架弹出一个确认对话框,当用户点击了取消或确认后会响应给子应用。

bus.getData('all-menu') - 可以获取 portal 中当前用户有权限的所有菜单数据,以树结构响应。

bus.post({type: 'open-new-window', data: { url: 'xxx'}}) - 通知 portal 打开一个新窗口。

bus.post({type: 'route-change', data: { code: 'xxx'}}) - 通知 portal 跳转到某个菜单,code 为菜单编码。

bus.post({type: 'close-task-detail'}) - 子应用完成待办任务后通知 portal关闭对应的待办。

bus.post({type: 'hsm-loading'}) - 通知 portal 开启全局 loading。

bus.post({type: 'hsm-cancel-loading'}) - 通知 portal 关闭全局 loading。

bus.on('setting-change', ()=>{}}) - 子应用监听 Portal 主题、语言设置发生变化。

bus.post({type: 'route-change', { code: 'xxx' }}) - 通知 portal 跳转菜单。

bus.on<{lastMenuCode: string, currentMenuCode: string}>('leave-iframe', ()=>{}}) - 子应用监听 Portal 离开一个已打开的 iframe 页签,被离开的 iframe 页签可以接收到消息。注意该事件是菜单级的事件,只有对应的页签会收到事件,例如,一个应用打开3个页签,在离开某一个页签时,只有离开的页签会接受到事件,其他2个页签不会。监听该事件可以在多标签页的场景下,在页签离开时做一些关闭消耗性能的动作。

bus.on<{lastMenuCode: string, currentMenuCode: string}>('enter-iframe', ()=>{}}) - 子应用监听 Portal 切换到一个已打开的 iframe 页签,当前激活的 iframe 页签可以接收到消息。

5.1.2 图标服务和svg-icon组件

图标服务是部署在静态资源站点的静态服务,开发环境使用可以直接设置svg-icon 的源来使用。

svg-icon 组件是封装的方便用来加载图标服务中资源的UI组件,已发布到npm私服,供子应用按需安装使用。

5.1.2.1 安装

pnpm install @hsmos/svg-icon

5.1.2.2 引入
// main.ts
import { createApp } from 'vue';
import SvgIcon from '@hsmos/svg-icon';

// 设置 icon 源
const iconOrigin = '/icons'; // 开发环境需要设置代理
const app = createApp(App);
app.use(SvgIcon, { iconOrigin });
app.mount('#app');
5.1.2.3 使用
使用

<script setup lang="ts">
    import { Icon } from "@iconify/vue";
</script>

<template>

    <!-- 默认用法 在网站中查到的图标 mdi:home -->
    <svg-icon icon="mdi:home" />
    <!-- 设置样式 -->
    <svg-icon icon="bx:baguette" style="font-size: 50px" color="red" rotate="90deg" />
    <!-- type 默认为 iconify,如果是 svg,则加载本地图标 -->

    <!-- 本地 svg 图标,路径 src/assets/icons/svg/bug.svg -->
    <svg-icon type="svg" icon="bug" />

    <!-- 本地 svg 图标,路径 src/assets/icons/svg/test/build.svg -->
    <svg-icon type="svg" icon="test-build" /> 

    <!-- 可以借助 el-icon 设置样式 -->
    <el-icon color="red" title="home" size="30">
        <svg-icon icon="mdi:home" />
    </el-icon>

    <!-- 在 el-button 中使用 -->
    <el-button type="primary">
        <svg-icon icon="mdi:home" />
    </el-button>

    <!-- 加载在线图片 -->
    <svg-icon type="online" icon="http://localhost:8080/assets/logo.svg" />
    <svg-icon type="online" icon="http://localhost:8080/assets/user.png" /> 

</template>

5.1.3 国际化组件

5.1.3.1 使用步骤
5.1.3.1.1 安装

pnpm i @hsmos/locale

5.1.3.1.2 实例化
// /src/utils/i18n.ts

import HsmI18n from '@hsmos/locale';

// 默认读取当前项目下 /langs 中的 config.json 和 语言包 文件,默认语言为 zh-CN ,默认存储在 localStorage 中的国际化 key 为 lang
const hsmI18n = new HsmI18n();
export default hsmI18n;
5.1.3.1.3 注册
// /src/main.tsimport hsmI18n from '@/utils/i18n'
app.use(hsmI18n.i18n);
5.1.3.1.4 加载语言

在路由拦截中判断 hsmI18n.isLoaded 是否已加载,如果已加载直接 next,如果未加载 await 加载语言包。

// router.ts
router.beforeEach(async (to, from, next) => {
  document.title = to.meta.title;
  if (!hsmI18n.isLoaded) {
    await hsmI18n.init();
  }
  next();
});
5.1.3.1.5 切换语言
import hsmI18n from '@/utils/i18n';

await hsmI18n.setLang('en-US');
5.1.3.2 config.json 文件说明
{
  "langs": [
    {
      "name": "中文简体",
      "fileName": "zh-CN"
    },
    {
      "name": "English",
      "fileName": "en-US"
    }
  ]
}

表示当前项目有 2 种语言可选择,分别是 中文简体 和 English,fileName 是语言包在 resourceUrl 下的文件名。

5.1.3.3 语言包文件名命名规范

语言包的文件名应按如下列表中给出的命名来制定:

语言包文件名列表

日本 : ja-JP
秘鲁 : es-PE
巴拿马 : es-PA
波斯尼亚和黑山共和国 : sr-BA
危地马拉 : es-GT
阿拉伯联合酋长国 : ar-AE
挪威 : no-NO
阿尔巴尼亚 : sq-AL
伊拉克 : ar-IQ
也门 : ar-YE
葡萄牙 : pt-PT
塞浦路斯 : el-CY
卡塔尔 : ar-QA
马其顿王国 : mk-MK
瑞士 : de-CH
美国 : en-US
芬兰 : fi-FI
马耳他 : en-MT
斯洛文尼亚 : sl-SI
斯洛伐克 : sk-SK
土耳其 : tr-TR
沙特阿拉伯 : ar-SA
英国 : en-GB
塞尔维亚及黑山 : sr-CS
新西兰 : en-NZ
挪威 : no-NO
立陶宛 : lt-LT
尼加拉瓜 : es-NI
爱尔兰 : ga-IE
比利时 : fr-BE
西班牙 : es-ES
黎巴嫩 : ar-LB
加拿大 : fr-CA
爱沙尼亚 : et-EE
科威特 : ar-KW
塞尔维亚 : sr-RS
美国 : es-US
墨西哥 : es-MX
苏丹 : ar-SD
印度尼西亚 : in-ID
乌拉圭 : es-UY
拉脱维亚 : lv-LV
巴西 : pt-BR
叙利亚 : ar-SY
多米尼加共和国 : es-DO
瑞士 : fr-CH
印度 : hi-IN
委内瑞拉 : es-VE
巴林 : ar-BH
菲律宾 : en-PH
突尼斯 : ar-TN
奥地利 : de-AT
荷兰 : nl-NL
厄瓜多尔 : es-EC
台湾地区 : zh-TW
约旦 : ar-JO
冰岛 : is-IS
哥伦比亚 : es-CO
哥斯达黎加 : es-CR
智利 : es-CL
埃及 : ar-EG
南非 : en-ZA
泰国 : th-TH
希腊 : el-GR
意大利 : it-IT
匈牙利 : hu-HU
爱尔兰 : en-IE
乌克兰 : uk-UA
波兰 : pl-PL
卢森堡 : fr-LU
比利时 : nl-BE
印度 : en-IN
西班牙 : ca-ES
摩洛哥 : ar-MA
玻利维亚 : es-BO
澳大利亚 : en-AU
新加坡 : zh-SG
萨尔瓦多 : es-SV
俄罗斯 : ru-RU
韩国 : ko-KR
阿尔及利亚 : ar-DZ
越南 : vi-VN
黑山 : sr-ME
利比亚 : ar-LY
中国 : zh-CN
台湾:zh-TW
香港 : zh-HK
白俄罗斯 : be-BY
以色列 : iw-IL
保加利亚 : bg-BG
马耳他 : mt-MT
巴拉圭 : es-PY
法国 : fr-FR
捷克共和国 : cs-CZ
瑞士 : it-CH
罗马尼亚 : ro-RO
波多黎哥 : es-PR
加拿大 : en-CA
德国 : de-DE
卢森堡 : de-LU
阿根廷 : es-AR
马来西亚 : ms-MY
克罗地亚 : hr-HR
新加坡 : en-SG
阿曼 : ar-OM
泰国 : th-TH
瑞典 : sv-SE
丹麦 : da-DK
洪都拉斯 : es-HN

5.2 HTTP API参考

5.2.1 身份验证及权限控制接口

参见《智能软件平台HSM-OS认证授权与单点登录接入规范》“4.5 接口说明”章节

5.3 设置数据参考

5.3.1 登录页设置

登录页设置

// 登录页设置

const signinSettings = {
  // 背景
  background: {
    imgage: {
      url: 'http://xxx',
      size: 'cover',
      fuzz: true,
    },
    color: '#000',
  }, // 登录框

  signbox: {
    position: 'right-middle',
    allowRememberUserName: true,
    allowForgetPassword: true,
    title: '',
    backgroud: {
      color: '#999',
      transport: '10',
    },
  }, // 欢迎语

  slogan: {
    visiable: true,
    text: '',
    size: 36,
    color: '#fff',
    position: 'left-middle',
  },
};

5.3.2 主题设置

主题设置

// 主题设置
const themeSettings = [
  {
    id: 'UUID',
    white: '#ffffff',
    black: '#000000',
    primary: '#409eff',
    success: '#67c23a',
    warning: '#e6a23c',
    danger: '#f56c6c',
    error: '#f56c6c',
    info: '#909399', // 文字色

    text: {
      primary: '#303133',
      regular: '#606266',
      secondary: '#909399',
      placeholder: '#a8abb2',
      disabled: '#c0c4cc',
    }, // 边框色

    border: {
      '': '#dcdfe6',
      light: '#e4e7ed',
      lighter: '#ebeef5',
      'extra-light': ' #f2f6fc',
      dark: '#d4d7de',
      darker: '#cdd0d6',
    }, // 填充色

    fill: {
      '': '#f0f2f5',
      light: '#f5f7fa',
      lighter: '#fafafa',
      'extra-light': '#fafcff',
      dark: '#ebedf0',
      darker: '#e6e8eb',
      blank: '#ffffff',
    }, // 背景色

    bg: {
      '': '#ffffff',
      page: '#f2f3f5',
      overlay: '#ffffff',
    },
  },
];

5.3.3 主功能区域设置

主功能区域设置

const home = {
  // 背景
  background: {
    img: {
      url: 'http://xxx',
      size: 'cover',
      fuzz: true,
    },
    color: '#000',
  }, // 间距
  gutter: '5px', // 显示方式,同CSS中的display
  display: 'flex', // flex布局时的属性
  flex: {
    direction: 'row',
    wrap: 'wrap',
    'justify-content': 'flex-start',
    'align-items': 'flex-start',
    'align-content': 'flex-start',
  }, // 每个元素在flex布局中的特性
  items: [
    {
      order: 0,
      'flex-grow': 1,
      'flex-shrink': 1,
      'flex-basis': '50%',
      'align-self': 'auto',
      url: 'http://xxx',
    },
    {
      order: 1,
      'flex-grow': 1,
      'flex-shrink': 1,
      'flex-basis': '50%',
      'align-self': 'auto',
      url: 'http://xxx',
    },
  ],
};

5.3.4 布局设置

布局设置

// 布局数据结构

const layoutSettings = [
  {
    id: 'UUID', //Header区域

    header: {
      visiable: true,
      hideOnFullScreen: false,
      height: 64,
      align: 'top',
      appsIcon: {
        index: 0,
        visiable: true,
      },

      systemLogo: {
        index: 1,
        visiable: true,
        url: 'http://xxx',
      },

      systemName: {
        index: 2,
        visibale: false,
        url: 'http://xxx',
      },

      activeFuncName: {
        index: 3,
        visibale: false,
      },

      space: {
        index: 4,
        visibale: true,
        algin: 'left',
      },

      setting: {
        index: 5,
        visibale: true,
      },

      speak: {
        index: 6,
        visibale: true,
      },

      fullscreen: {
        index: 7,
        visibale: true,
      },

      help: {
        index: 8,
        visibale: true,
      },

      user: {
        index: 9,
        visibale: true,
      },

      extends: [
        {
          i18n: 'i18n',
          icon: 'icon',
          action: 'action',
        },

        {
          i18n: 'i18n',
          icon: 'icon',
          action: 'action',
        },
      ],
    }, // 导航区

    navbar: {
      // 纵向导航
      navbarV: {
        visiable: true,
        align: 'left',
        backgroundColor: '#000',
        color: '#fff',
        width: 265,
        activeColor: '#999',
      }, // 横向导航

      navbarH: {
        visiable: false,
        align: 'top',
        backgroundColor: '#000',
        color: '#fff',
        activeColor: '#999',
        height: 48,
      },
    }, // 内部多标签页

    tabs: {
      visiable: false,
      max: 10,
      allowCloseHome: true,
      actions: {
        allowCloseAll: true,
        allowCloseOthers: true,
        allowCloseRight: true,
      },
    }, // 面包屑导航

    breadcrumb: {
      visiable: false,
    }, // 扩展区域

    extends: [
      {
        align: 'bottom',
        height: 100,
        url: 'http://xxx',
        autoRefresh: true,
        refreshInterval: 10,
      },
      {
        align: 'top',
        height: 100,
        url: 'http://xxx',
        autoRefresh: true,
        refreshInterval: 10,
      },
      {
        align: 'left',
        width: 100,
        url: 'http://xxx',
        autoRefresh: true,
        refreshInterval: 10,
      },
      {
        align: 'right',
        width: 100,
        url: 'http://xxx',
        autoRefresh: true,
        refreshInterval: 10,
      },
    ],
  },
];