跳转至

Widget(PageWidget)定义编写指南

目标:Agent 直接生成/修改 workspace 首页小组件。知识以本文为准。不写原理;不依赖外链。

iScript:写 type=iscript|pageactionContent、或 authMode=2userScript 时,先读 iscript-usage(含 GraalVM 差异) → widget.md;本文只定类型/授权与落盘。

术语:实体类 PageWidget;产品文档「Widget / 小组件 / 小工具」同义。是软件前台**主页门户**的砖块(视图/摘要/图表/流程待办/脚本 HTML 等),**不是**表单控件,**不是**菜单。

依赖:设计态新建 Widget 必须先有 Widget 分组PageWidgetGroup)。无分组时保存会抛「请选择widget分组!」。分组知识写在本文 §7。

产出物一览

部分 落盘 形态
Widget 分组 widget/{分组名}.widgetgroup/{分组名}.widgetgroup 目录型;JAXB 根 pageWidgetGourp(拼写是 Gourp,不是 Group)
Widget widget/{分组名}.widgetgroup/{Widget名}.widget 单文件;JAXB 根 pageWidgetid 为根属性
# 推荐(设计态现行):分组下挂 Widget
storage/workspace/{软件名}.application/widget/{分组名}.widgetgroup/{分组名}.widgetgroup
storage/workspace/{软件名}.application/widget/{分组名}.widgetgroup/{Widget名}.widget

# 历史/样例常见:无分组,文件直接躺在 widget/ 下(仍可被递归扫到)
storage/workspace/{软件名}.application/widget/{Widget名}.widget

路径相对 storage/workspace。DAO 按后缀 .widget **递归扫描**整棵应用树,两种布局运行时都能载入。

新建最低配置:

  1. 若不存在分组:先建 {name}.widgetgroup/ + 同名元数据(见 §7)
  2. {名}.widgetidnametypepublished=truewidgetGroupId=分组 id、parentId=分组 id、authMode=0widgetType=0orderno
  3. typeactionContent / moduleid / carouselImg / helpPath / countType / showAll 等(见类型表)
  4. icon 写 JSON 字符串;icontype=imgfont

约定:

  • JAXB;根 pageWidgetid根属性
  • 脚本/长文本用 CDATA:actionContentuserScriptqueryStringtabTags
  • 文件名 = name + .widget;同应用内 name 不重名(设计态按应用扫同名校验)
  • name 勿含 / % \(落盘替换为 =47/=37/=92
  • parentId:有分组时 = widgetGroupId;历史无分组时 = 软件 id
  • applicationid:基类字段**无** JAXB @XmlElement,落盘常缺;运行/API 靠加载上下文;API 保存时会 set
  • 布尔 true/false;排序字段 XML 名 orderno(全小写)
  • **勿**放到 module/ 下;永远在应用级 widget/

1. 心智模型

软件 Application
  └─ widget/
       ├─ {分组}.widgetgroup/          ← PageWidgetGroup
       │    ├─ {分组}.widgetgroup      ← 分组元数据
       │    └─ {名}.widget             ← PageWidget(推荐)
       └─ {名}.widget                  ← 历史无分组样例

前台主页
  ├─ GET /api/runtime/homepage/config  → widgets 模板 + 用户 setting
  └─ GET /api/runtime/{appId}/widgets/{widgetId}  → 按 type 拉内容

个人布局:运行库 UserDefined.templateElement(JSON),不在 .widget 文件内
概念 说明
分组 设计器左侧树;widgetGroupId 指分组 id;物理目录容器
发布 published=true 才进入用户可配置/可见集合
授权 authMode:全角色 / 选定角色 / 用户脚本筛选
设备 widgetType:通用 / 仅 PC / 仅手机
快速入口 iconShow=true:前台当图标入口;display 时直接返回 actionContent
系统 Widget 运行时 id 以 system_ 开头可虚构(newSystemWidget);设计盘上常见写死的 system_workflow 文件
用户布局 用户拖拽结果存个人设置,不是改 .widget

2. PageWidget 属性 → .widget XML

属性表

属性 XML 类型 默认 说明
id 根属性 string 须生成 __ + 短 UUID
name 子元素 string 必填;文件名;应用内不重名
parentId 子元素 string 有分组 = 分组 id;无分组 = 软件 id
applicationid 常不落盘 string API 侧 = 软件 id
description / remark CDATA string 少用
type 子元素 string 必填;见类型表
moduleid 子元素 string 选模块后再选视图/报表/图表时填模块 id
actionContent CDATA string 内容指针;语义随 type 变(id / URL 脚本 / 分析类型名等)
userScript CDATA string "" authMode=2 时返回用户 id/UserVO 集合
queryString CDATA string 请求参数;少用
width / height 子元素 string 传统宽高(数字串或空);流式布局优先 wgt*
authMode 子元素 int 0 0 全角色 / 1 选定角色 / 2 选定用户(脚本)
authRolesId 子元素 string authMode=1 时角色 id 串(可逗号等拼接;运行用 indexOf
authRolesName 子元素 string 展示用角色名串
tabTags CDATA string [] JSON 数组字符串;iscript 类型的页签切换配置
published bool false 前台要显示须 true
setup bool false 是否曾被用户自定义布局使用过的标记;生成时可 false
icon 子元素 string JSON 字符串,见下
icontype 子元素 string img / font(与 icon JSON 内一致)
carouselImg 子元素 string carousel:图片 URL 的 JSON 数组字符串;其它可 [] 或空
helpPath 子元素 string help:上传的帮助文件路径
frequency 子元素 int 1 轮播秒数(carousel);其它类型常 0/1
titleColor 子元素 string #000
titleBColor 子元素 string 标题背景色 rgba
titleBarColor 子元素 string 标题栏色
iconShow bool false true=快速入口;勾选「广泛使用」类选项
widgetType 子元素 int 0 设备支持:0 通用 / 1 仅网页 / 2 仅手机
orderno 子元素 int 排序,越小越前
countType 子元素 string none 签到统计:none/month/quarter/year
isRefresh bool false 是否可刷新
wgtWidth 子元素 string 流式宽:1X/2X/3X
wgtHeight 子元素 string 流式高:1X8X
widgetGroupId 子元素 string 设计态必填;= 分组 id
showAll bool false 主要为 system_workflow:「显示全部软件」待办/经办

不落盘/运行期:uri、用户 homepage setting、系统虚构 Widget 实例。

id 生成

统一规则:__ + 短 UUID。示例:__MpEzTToulqZtNEFisw6。须全局/库内不冲突(.widget / .widgetgroup 同)。

icon 格式

字符串形式 JSON(不是嵌套 XML):

{"icon":"/uploads/lib/icon_menu_default.png","icontype":"img", "imgUrl": "/uploads/lib/icon_menu_default.png"}

字体图标:

{"icon":"fa fa-bell-o","icontype":"font", "fontUrl":"fa fa-bell-o", "iconFontColor":"rgba(30, 144, 255, 1)"}

落盘路径规则(代码事实)

PageWidget.getPath()

  1. widgetGroupId 非空 → {分组.getPath()}/{name}.widget
    …/widget/{分组名}.widgetgroup/{name}.widget
  2. 否则 → {parent.getPath()}/{name}.widget
    parent 为软件时 ≈ …/{软件名}.application/{name}.widget /widget 段)

历史包常把无分组文件放在 widget/ 目录——与上述无分组公式不完全一致,但查询靠后缀递归,仍可读。新写一律走有分组路径。

authMode

常量 行为
0 AUTH_MODE_ALL_ROLES 有软件角色即可(设计「授权给所有角色」)
1 AUTH_MODE_SOME_ROLES authRolesId 含当前用户角色 id 之一
2 AUTH_MODE_SOME_USERS userScript,结果是用户 id 集合或 UserVO[],含当前用户

published=false → 首页聚合直接跳过,不论授权。

widgetType(设备)

常量 说明
0 WIDGET_TYPE_GENERAL PC+手机
1 WIDGET_TYPE_WEBPAGE 仅网页;手机设备跳过
2 WIDGET_TYPE_MOBILE 仅手机;PC 跳过

3. typeactionContent(核心)

设计器当前下拉(widgetOptions)常见值如下。Java 常量还有更多历史类型,见文末附录。

type 含义 moduleid actionContent 其它关键字段
summary 摘要(待办/分享等) 摘要配置 id(SummaryCfg)
view 挂接视图 模块 id 视图 id 视图 type 常过滤为列表/树等;iconShow 可开快速入口
page 链接内容 iScript:运算后返回 URL 字符串;或纯 URL 思路 运行会 runner.run 整段脚本
customizeReport 自定义报表 模块 id 报表 id iconShow 可选
chart 统计图 模块 id 图表 id iconShow 可选
system_workflow 流程处理 可空 showAll:是否跨全部软件
iscript 计算脚本值 → HTML iScript:必须返回 String HTML 片段 tabTags JSON 数组;环境变量有 WebUser
carboncopy 抄送数据 可空 前台走流程中心抄送数据
carousel 轮播图 可空 carouselImg JSON 数组;frequency
checkin 签到 可空 countType=none/month/quarter/year
help 帮助 可空 helpPath 上传文件路径

设计器保存时把 UI 字段映射进 actionContent

  • view → actionView;chart → chart;iscript → iscript;page → actionUrl;summary → actionSummary;customizeReport → customizeReport
  • carouselImg / tabTags数组 → JSON.stringify 再写入字符串字段

page vs iscript

page iscript
脚本返回 URL / 可被当作页面地址用的串 HTML 字符串
非法返回 视前端处理 后端抛「脚本运算结果不合法,请返回字符串」;错误信息可当 html

iscript 最小示例:

(function () {
    var html = "<div>hello world!</div>";
    return html;
})()

page 最小示例:

(function(){
    var url = "https://www.baidu.com/";
    return url;
})();

summary 的 actionContent

指向摘要配置 id。运行时按 SummaryCfg.scope 取待办/分享/抄送等列表(scope 常见 0 待办、1 分享、6 抄送;设计器列表会过滤部分 scope)。

view 的 actionContent

视图 id。运行时 view.getViewTypeImpl().getViewDatas(...)。建议同时写对 moduleid(该视图所属模块),否则设计器回填会丢。

视图筛选(设计器用):常见只列 type==1 || type==16 的视图。

iconShow=true(快速入口)

displayWidget **优先**分支:直接 return actionContent,不再按 type 拉取视图数据等。适合把入口画成图标,点击打开 actionContent 指向的资源。


4. .widget XML 骨架

视图 Widget(推荐有分组)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pageWidget id="__WIDGET_UUID">
  <name>待办列表</name>
  <parentId>__GROUP_UUID</parentId>
  <type>view</type>
  <moduleid>__MODULE_UUID</moduleid>
  <actionContent><![CDATA[__VIEW_UUID]]></actionContent>
  <userScript><![CDATA[]]></userScript>
  <width></width>
  <height></height>
  <authMode>0</authMode>
  <authRolesId></authRolesId>
  <authRolesName></authRolesName>
  <tabTags><![CDATA[[]]]></tabTags>
  <published>true</published>
  <setup>false</setup>
  <icon>{"icon":"/uploads/lib/icon_menu_default.png","icontype":"img", "imgUrl": "/uploads/lib/icon_menu_default.png"}</icon>
  <icontype>img</icontype>
  <carouselImg>[]</carouselImg>
  <helpPath></helpPath>
  <frequency>0</frequency>
  <titleColor>#000</titleColor>
  <titleBColor>rgba(255, 255, 255, 0)</titleBColor>
  <titleBarColor>rgba(255, 255, 255, 0)</titleBarColor>
  <iconShow>false</iconShow>
  <widgetType>0</widgetType>
  <orderno>1</orderno>
  <countType>none</countType>
  <isRefresh>true</isRefresh>
  <wgtWidth>2X</wgtWidth>
  <wgtHeight>4X</wgtHeight>
  <widgetGroupId>__GROUP_UUID</widgetGroupId>
  <showAll>false</showAll>
</pageWidget>

路径:/{软件}.application/widget/{分组名}.widgetgroup/待办列表.widget

流程处理

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pageWidget id="__WF_WIDGET">
  <name>流程处理</name>
  <parentId>__GROUP_UUID</parentId>
  <type>system_workflow</type>
  <moduleid></moduleid>
  <actionContent><![CDATA[]]></actionContent>
  <userScript><![CDATA[]]></userScript>
  <authMode>0</authMode>
  <authRolesId></authRolesId>
  <authRolesName></authRolesName>
  <tabTags><![CDATA[[]]]></tabTags>
  <published>true</published>
  <setup>false</setup>
  <icon>{"icon":"/uploads/lib/icon/iconstaff.png","icontype":"img", "imgUrl": "/uploads/lib/icon/iconstaff.png"}</icon>
  <icontype>img</icontype>
  <carouselImg>[]</carouselImg>
  <helpPath></helpPath>
  <frequency>0</frequency>
  <titleColor>#000</titleColor>
  <iconShow>false</iconShow>
  <widgetType>0</widgetType>
  <orderno>1</orderno>
  <countType>none</countType>
  <isRefresh>true</isRefresh>
  <wgtWidth></wgtWidth>
  <wgtHeight></wgtHeight>
  <widgetGroupId>__GROUP_UUID</widgetGroupId>
  <showAll>true</showAll>
</pageWidget>

轮播

  • type=carousel
  • carouselImg=["/uploads/resource/image/a.jpg","/uploads/.../b.jpg"](属性文本,或 CDATA)
  • frequency=3(秒)

iscript

  • type=iscript
  • actionContent = 返回 HTML 的脚本(CDATA)
  • 可在 HTML 内嵌 <o-action …> 做打开文档/视图等(常用脚本集合模式)

5. 设计态 / 运行态 API(定位用)

设计态(前缀以部署为准,常见 /api/designtime/applications):

方法 路径 说明
GET /{appId}/widgets 列表;query:namewidgetGroupIdpageNolinesPerPage
GET /{appId}/widgets/{widgetId} 详情;summary 额外返回 summaryName
POST /{appId}/widgets 新建;强制 parentId=widgetGroupId;空 id 自动序列
PUT /{appId}/widgets/{widgetId} 更新;同上;uri 置 null
DELETE /{appId}/widgets body:id 数组

校验:widgetGroupId 空 → 400「请选择widget分组!」;同应用 name 重复 →「该名称已存在」。

运行态(常见 /api/runtime):

方法 路径 说明
GET /{appId}/widgets/{widgetId} 按 type 取内容;路径参数可能按用户加密
GET /homepage/config 用户可见 widgets 模板 + setting;applicationIdisMobile
POST /homepage/config 保存用户布局 JSON → UserDefined
GET /{appId}/homepage/phoneConfig 读软件 systemWidgetSetting(Banner/menuIcon/system_workflow 等)

首页聚合逻辑摘要:跨用户可见软件 → 收集 published Widget → 按设备与 authMode 过滤 → orderno 升序 → 与用户 templateElement 合成。

iscript 类型在下发 homepage 模板时**故意不**把脚本原文放进 actionContent(防泄露);展示时再 display。

部分类型在 homepage 响应里会对 actionContent/applicationId 做用户级加密(summary/view/chart/customizeReport/system_workflow/carboncopy/carousel 等)。


6. 与角色 / 菜单 / 软件的边界

能力 载体 Widget 是否管
软件可见 域绑定 + activated + 角色
菜单可见 ResourceVO + role.permissions 否(widgetGroupId 字段在菜单上存在但少用)
Widget 可见 published + authMode(+设备)
个人摆放 UserDefined.templateElement 否(运行库)
手机系统区开关 Application.systemWidgetSetting 软件级字符串,不是 .widget

角色 .rolepermissions **不**用来授权单个 Widget(Widget 用自身 authMode/authRolesId)。


7. Widget 分组(PageWidgetGroup)— 本文内自足

实体:PageWidgetGroup。根元素名历史拼写错误,**必须**写作:

  • XML 根:<pageWidgetGourp id="...">(Gourp)
  • @XmlType(name = "PageWidgetGourp")

后缀:ModelSuffix.WIDGET_GROUP_* → 路径/文件后缀均为 widgetgroup;文件组 /widget

属性

属性 XML 说明
id 根属性 须生成
name 子元素 必填;目录名;同应用下不重名
parentId 子元素 = 软件 id
description/remark CDATA 可选;样例常省略

无业务字段。分组本身不作前台渲染单元,只作分类与路径父级。

骨架

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pageWidgetGourp id="__GROUP_UUID">
  <name>默认分组</name>
  <parentId>SOFTWARE_UUID</parentId>
</pageWidgetGourp>

路径:/{软件}.application/widget/默认分组.widgetgroup/默认分组.widgetgroup

设计态 API:/{appId}/widgetgroups CRUD。更新只改 name 等,删除可批。新建时 parentId=applicationId

改分组 name = 改目录名;其下 .widget 随之搬家(靠 path 计算)。**勿随意改名**除非同步磁盘。

删除分组前应先迁走/删除子 Widget,否则遗留文件。


8. 检查清单(生成后自检)

  • 已有 .widgetgroup,且 Widget 的 widgetGroupId=parentId=该分组 id
  • 文件在 widget/{分组}.widgetgroup/{name}.widget
  • 根元素 pageWidget;分组根元素 pageWidgetGourp
  • type 取自上表;actionContent/moduleid/carouselImg/helpPath/countType/showAll 与 type 匹配
  • published=true(若要前台出现)
  • authMode=1authRolesId 非空;=2userScript 返回用户集合
  • icon 是合法 JSON 字符串;icontype 一致
  • carouselcarouselImg 为字符串化数组;frequency>0
  • iscript:返回 HTML 字符串;勿只返回对象
  • orderno 整数;流式用 1X… 格式
  • 同应用无同名 Widget;name 无 /\%

9. 典型生成顺序

  1. 确认软件目录 {app}.application/ 存在
  2. 建分组 widget/{G}.widgetgroup/{G}.widgetgroup
  3. 按业务选 type,解析目标资源 id(视图/摘要/图表/报表)
  4. .widget 到分组目录下
  5. 需要选定角色授权时填 authMode=1 + authRolesId(角色 id 来自 .role
  6. 在前台用有该软件角色的用户打开首页验证;或调 runtime homepage/config

附录 A:Java 常量中另有、设计器主菜单未列出的 type

可用于兼容旧包或特殊场景;生成新 Widget 优先用设计器列表。

常量值 含义
link 快速入口/内部链接(常量 TYPE_LINK)
activity 按钮操作(运行 display 现为 FIXME/null)
report 报表(旧)
runquanReport 润乾报表
customizeReport 已在主列表
system_announcement / system_weather / system_email / system_knowledge / system_empconbook 系统类;多为运行虚构或历史
workflow_analyzer 流程分析;actionContent 为分析器方法名如 analyzerActorTimeConsumingTopX

运行时对 widgetId.startsWith("system_") 可不用磁盘文件,直接 newSystemWidget(widgetId)

附录 B:后缀常量(ModelSuffix)

常量
WIDGET_PATH_SUFFIX / WIDGET_FILE_SUFFIX widget
WIDGET_GROUP_PATH_SUFFIX / WIDGET_GROUP_FILE_SUFFIX widgetgroup
WIDGET_GROUP_FILE_GROUP /widget

附录 C:常见失败

现象 原因
设计器保存报请选择分组 widgetGroupId
首页看不到 published=false / authMode 未命中 / widgetType 与设备不符 / 用户无软件角色或软件未激活未绑定
iscript 出异常字串 未 return String
视图空白 actionContent 不是合法视图 id,或视图无权限/无数据
改名后找不到 文件名与 <name> 不一致,或分组改名未搬文件
分组 XML 解析失败 根标签写成了 pageWidgetGroup(少了历史拼写 Gourp)