关于 ui-generation
面向 ServiceNow 开发者和公民开发者的 UI 生成技能,可将自然语言需求转化为客户端脚本、Service Portal 组件和 UI Builder 页面等组件。流程还包括查询现有实现以参考,并验证生成结果。
主要功能
- 01从自然语言需求识别组件与行为
- 02生成表单客户端脚本
- 03创建 Service Portal 小组件
- 04构建 UI Builder 页面与事件映射
- 05查询现有组件并验证生成结果
适用场景
- 01根据业务描述生成表单字段行为
- 02快速制作 Service Portal 小组件原型
- 03构建带数据资源的工作区页面
- 04为 UI 交互生成事件处理逻辑
MCP 相关
该技能通过 MCP 工具查询现有 UI 组件、创建和更新脚本及页面配置,并发现表结构以支持数据绑定。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill ui-generation安装后在 Claude Code 中输入/ui-generation确认已加载。
详细介绍
使用自然语言生成 UI
概述
此技能介绍如何根据自然语言描述生成 ServiceNow UI 组件:
- 为表单和列表创建客户端脚本(onChange、onLoad、onSubmit、onCellEdit)
- 为 UI Builder 和 Service Portal 小组件构建数据绑定配置
- 为用户交互生成事件处理逻辑
- 使用 Jelly/HTML 模板和处理脚本创建 UI Pages
- 使用 AngularJS 控制器和 HTML 模板构建 Service Portal 小组件
- 使用数据资源和事件映射生成 UI Builder 体验页面
- 为可重用的表单装饰和格式化程序生成 UI 宏
适用场景: 当开发者或公民开发者需要根据自然语言描述的业务需求创建 UI 组件,或需要快速制作表单行为、门户小组件或工作区页面的原型时。
先决条件
- 角色:
admin、personalize_form或sp_admin(用于 Service Portal 小组件) - 插件:
com.glide.ui.ui_page(UI Pages)、com.glide.service-portal(Service Portal)、com.sn_ui_builder(UI Builder) - 访问权限: 对
sys_script_client、sys_ui_page、sp_widget、sys_ux_page_registry表具有写入权限 - 知识要求: JavaScript 基础知识、GlideForm API、AngularJS 基础知识(用于 Service Portal)、UI Builder 概念
- 相关技能: 用于客户端脚本模式的
development/client-scripts,以及用于目录表单行为的catalog/ui-policies
流程
第 1 步:解析自然语言描述
分析用户请求以确定:
- 组件类型: 客户端脚本、UI 页面、Service Portal 小组件、UI Builder 页面、UI 宏
- 目标表单/表: 组件适用的表或表单
- 行为: 应执行的操作(显示/隐藏字段、验证数据、获取记录、渲染 UI)
- 触发条件: 启动行为的事件(字段更改、表单加载、按钮点击、页面加载)
- 数据依赖项: 组件需要读取或写入的数据
第 2 步:查询现有组件以获取模式
MCP 方式:
Tool: SN-Query-Table
Parameters:
table_name: sys_script_client
query: table=incident^active=true^typeLIKEonChange
fields: sys_id,name,table,type,field_name,script,active,ui_type
limit: 10
REST 方式:
GET /api/now/table/sys_script_client
?sysparm_query=table=incident^active=true^typeLIKEonChange
&sysparm_fields=sys_id,name,table,type,field_name,script,active,ui_type
&sysparm_limit=10
第 3 步:生成客户端脚本
将自然语言转换为 GlideForm API 调用。常见模式:
onChange——显示/隐藏字段:
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Show VPN fields when category is Network"
table: incident
type: onChange
field_name: category
ui_type: 0
active: true
script: |
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
var isNetwork = (newValue == 'network');
g_form.setVisible('u_vpn_type', isNetwork);
g_form.setVisible('u_network_segment', isNetwork);
if (isNetwork) {
g_form.setMandatory('u_vpn_type', true);
} else {
g_form.setMandatory('u_vpn_type', false);
}
}
REST 方式:
POST /api/now/table/sys_script_client
Body: {
"name": "Show VPN fields when category is Network",
"table": "incident",
"type": "onChange",
"field_name": "category",
"ui_type": "0",
"active": "true",
"script": "function onChange(control, oldValue, newValue, isLoading) { ... }"
}
第 4 步:生成用于表单初始化的 onLoad 脚本
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Initialize priority banner on form load"
table: incident
type: onLoad
ui_type: 0
active: true
script: |
function onLoad() {
var priority = g_form.getValue('priority');
if (priority == '1') {
g_form.showFieldMsg('priority', 'Critical incident - follow P1 procedures', 'error');
g_form.setReadOnly('category', true);
g_form.setReadOnly('subcategory', true);
}
}
第 5 步:生成 onSubmit 验证脚本
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Validate resolution notes before close"
table: incident
type: onSubmit
ui_type: 0
active: true
script: |
function onSubmit() {
var state = g_form.getValue('state');
if (state == '6') {
var resolution = g_form.getValue('close_notes');
if (!resolution || resolution.trim().length < 20) {
g_form.showFieldMsg('close_notes', 'Resolution notes must be at least 20 characters', 'error');
return false;
}
}
return true;
}
第 6 步:生成 Service Portal 小组件
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sp_widget
data:
name: "Team Incident Dashboard"
id: "team-incident-dashboard"
template: |
<div class="panel panel-default">
<div class="panel-heading"><h3>{{c.data.title}}</h3></div>
<div class="panel-body">
<div ng-repeat="inc in c.data.incidents track by inc.sys_id">
<div class="alert alert-{{inc.priorityClass}}">
<strong>{{inc.number}}</strong> - {{inc.short_description}}
<span class="pull-right badge">{{inc.state}}</span>
</div>
</div>
<p ng-if="c.data.incidents.length === 0">No open incidents.</p>
</div>
</div>
client_script: |
api.controller = function($scope) {
var c = this;
};
server_script: |
(function() {
data.title = "My Team's Open Incidents";
var gr = new GlideRecord('incident');
gr.addQuery('assignment_group', gs.getUser().getMyGroups());
gr.addActiveQuery();
gr.orderByDesc('priority');
gr.setLimit(20);
gr.query();
data.incidents = [];
while (gr.next()) {
data.incidents.push({
sys_id: gr.getUniqueValue(),
number: gr.getValue('number'),
short_description: gr.getValue('short_description'),
state: gr.getDisplayValue('state'),
priority: gr.getValue('priority'),
priorityClass: {'1':'danger','2':'warning','3':'info','4':'success'}[gr.getValue('priority')] || 'default'
});
}
})();
REST 方式:
POST /api/now/table/sp_widget
Body: {
"name": "Team Incident Dashboard",
"id": "team-incident-dashboard",
"template": "<div class='panel panel-default'>...</div>",
"client_script": "api.controller = function($scope) { var c = this; };",
"server_script": "(function() { ... })();"
}
第 7 步:生成 UI Builder 体验页面
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_ux_page_registry
data:
title: "Executive Incident Overview"
path: /executive-incident-overview
description: "Executive dashboard showing incident metrics and trends"
category: workspace
active: true
为页面配置数据资源:
Tool: SN-Create-Record
Parameters:
table_name: sys_ux_data_broker_transform
data:
name: "Incident Metrics Data Resource"
page: "<page_sys_id>"
table: incident
query: "active=true^priority<=2"
fields: "number,short_description,priority,state,assigned_to,sys_created_on"
max_rows: 50
第 8 步:生成事件处理程序
配置 UI Builder 的事件到操作映射:
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_ux_event
data:
name: "Row Click Navigate to Record"
page: "<page_sys_id>"
component: "<component_sys_id>"
event_name: "click"
action_type: "navigate"
action_config: {
"route": "/record/incident/${sys_id}",
"target": "_self"
}
第 9 步:生成用于表单装饰的 UI 宏
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_ui_macro
data:
name: "sla_countdown_banner"
description: "Displays a countdown timer for SLA breach on incident form"
xml: |
<?xml version="1.0" encoding="utf-8"?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide">
<g:evaluate var="jvar_sla_info" expression="
var sla = new GlideRecord('task_sla');
sla.addQuery('task', current.sys_id);
sla.addQuery('has_breached', false);
sla.addActiveQuery();
sla.orderBy('planned_end_time');
sla.setLimit(1);
sla.query();
sla.next() ? sla.getDisplayValue('planned_end_time') : 'N/A';
"/>
<div style="background:#fff3cd;padding:10px;border-radius:4px;margin-bottom:10px;">
<strong>SLA Target:</strong> ${jvar_sla_info}
</div>
</j:jelly>
第 10 步:验证和测试生成的组件
MCP 方式:
Tool: SN-Query-Table
Parameters:
table_name: sys_script_client
query: sys_id=<created_sys_id>
fields: sys_id,name,table,type,active,sys_updated_on
limit: 1
通过检查 sys_update_xml 表中的最新更新记录,确认没有脚本语法错误:
Tool: SN-Query-Table
Parameters:
table_name: sys_update_xml
query: name=sys_script_client_<sys_id>^ORDERBYDESCsys_created_on
fields: sys_id,name,action,sys_created_on
limit: 1
工具用法
| 工具 | 用途 | 使用场景 |
|---|---|---|
| SN-Query-Table | 查找现有 UI 组件以获取模式 | 模式发现、验证 |
| SN-Create-Record | 创建客户端脚本、小组件、UI 页面 | 构建新的 UI 组件 |
| SN-Update-Record | 修改脚本、模板、配置 | 迭代生成的组件 |
| SN-Get-Table-Schema | 查找用于数据绑定的字段名称 | 了解表单字段和数据模型 |
最佳实践
- 始终正确设置
ui_type—— 桌面端使用0,移动端/服务门户使用1,两者都使用2 - 使用
g_formAPI 方法,而不是直接操作 DOM,来编写客户端脚本 - 尽量减少客户端脚本中的服务器调用 —— 使用 GlideAjax 异步查询服务器
- 恰当限定客户端脚本的作用域 —— 使用 IIFE 避免污染全局作用域
- 在多种浏览器上进行测试 —— 避免使用特定于浏览器的 JavaScript 功能
- 在所有生成的 onChange 处理程序中加入空值检查,以处理字段值为空的情况
- 在 onChange 脚本中使用
isLoading防护,避免表单加载时执行 - 在服务器调用期间将组件设为只读,避免重复提交
- 遵循无障碍标准 —— 在自定义组件中加入 ARIA 标签和键盘导航
- 版本控制 —— 创建 UI 组件前,始终将其关联到一个更新集
故障排除
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 客户端脚本未触发 | type 或 field_name 值错误 | 确认 type 与事件匹配,且 field_name 与列名匹配 |
| g_form 未定义错误 | 脚本在表单上下文之外运行 | 确保脚本关联到正确的表和 ui_type |
| 组件数据未加载 | 服务器脚本中的 GlideRecord 查询错误 | 检查服务器脚本中的表权限和查询语法 |
| UI 页面空白 | XML 中的 Jelly 语法错误 | 验证 XML 结构,并检查是否存在未转义字符 |
| 事件处理程序无响应 | 组件 ID 不匹配 | 确认事件配置中的组件 sys_id |
| 表单字段未隐藏 | 字段名与标签不匹配 | 使用列名(例如 u_vpn_type),而不是标签 |
示例
示例 1:从自然语言生成客户端脚本
输入:“当有人在事件表单中选择‘硬件’作为类别时,将型号和序列号字段设为必填,并显示一条提示消息,要求填写这些字段。”
**生成内容:**一个针对 incident.category 的 onChange 客户端脚本;当 newValue == 'hardware' 时,调用 g_form.setMandatory() 和 g_form.showFieldMsg() 处理 u_model 和 u_serial_number。
示例 2:从自然语言生成 Service Portal 组件
输入:“创建一个组件,显示已登录用户尚未关闭的请求,并用状态栏展示完成百分比。”
**生成内容:**一个 Service Portal 组件,其中服务器脚本查询当前用户的 sc_req_item,客户端通过 AngularJS 和 Bootstrap 进度条进行渲染,并提供点击导航功能。
示例 3:从自然语言生成 UI Builder 页面
输入:“构建一个工作区页面,显示按优先级分类的事件图表,并在下方列出我团队尚未关闭的事件。”
**生成内容:**一个 UI Builder 页面注册,包含两个数据资源(用于图表的聚合数据和用于表格的列表数据)、一个绑定到优先级聚合数据的条形图组件,以及一个支持点击行导航的数据表组件。
相关技能
development/client-scripts- 客户端脚本开发模式详解development/ui-actions- UI 操作按钮和链接catalog/ui-policies- 目录专用 UI 策略development/scripted-rest-apis- 用于组件数据的后端 APIgenai/flow-generation- 用于 UI 触发流程的 Flow Designer 自动化