with one click
yida-custom-page
// 宜搭自定义页面 JSX 开发规范。React 16 类组件模式,宜搭 JS API 调用,状态管理与编码约束。不适用于:原生表单页面开发(无需 JSX),或发布页面(编写完成后需使用 yida-publish-page 发布)。
// 宜搭自定义页面 JSX 开发规范。React 16 类组件模式,宜搭 JS API 调用,状态管理与编码约束。不适用于:原生表单页面开发(无需 JSX),或发布页面(编写完成后需使用 yida-publish-page 发布)。
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | yida-custom-page |
| description | 宜搭自定义页面 JSX 开发规范。React 16 类组件模式,宜搭 JS API 调用,状态管理与编码约束。不适用于:原生表单页面开发(无需 JSX),或发布页面(编写完成后需使用 yida-publish-page 发布)。 |
违反会导致页面崩溃或运行时报错:
project/pages/src/<页面名>.oyd.jsx。宜搭原生写法仍使用 export function renderJsx();有限现代 authoring 可用 export default function Page() + useState + useEffect(..., []),由 OpenYida 发布前降级this 的方法必须用 export function 定义,不得用箭头函数或函数表达式renderJsx 顶部先写 var self = this,事件使用 onClick={(e) => { self.handleClick(e) }},严禁 onClick={this.handleClick} 或 .bind(this).map((item) => ...),禁止 .map(function(item) {...}),否则回调内 this 丢失;.oyd.jsx 构建会尝试自动修复,但生成时仍应直接写正确形式<input> 用 defaultValue + onChange 写入 _customState,禁止 value 受控模式this.utils.loadScript 加载 CDN 脚本openyida get-schema <appType> <formUuid> 获取真实 fieldId,文件顶部定义 FIELDS 常量映射字段别名,禁止猜测或手写this.utils.toast({ title: message, type: 'error' }) 提示用户<div style={{ display: 'none' }}>{this.state && this.state.timestamp}</div>;.oyd.jsx 构建会自动补齐,但生成原生写法时仍必须显式写出影响代码质量和用户体验:
searchFormDatas 等的 pageSize 最大 100didUnmount 中清理所有 setInterval/setTimeout,防止内存泄漏style 属性,渐变用 background 不用 backgroundColorforceUpdate() 后 DOM 不会立即更新,需 setTimeout 延迟访问新 DOM 元素this.utils.isMobile() 判断设备类型,适配 PC 和移动端_isComposing 标记配合 compositionstart/compositionend 事件,避免输入过程中触发提交workbench/{formUuid}?iframe=true,禁止用 formDetaildisplay: none 保留 DOMloading 置回 false,不要只渲染“正在加载...”挡住整页每条规则的代码示例、反模式和常见错误见 编码指南(编写代码前强制必读)。 表单类 JSX 控件、筛选栏、表格、成员/附件等组件写法见 组件指南;未验证的平台组件能力不得编造。
正向触发:
this.utils.yida.* 读写表单数据不适用(应使用其他技能):
| 场景 | 应使用技能 |
|---|---|
| 原生表单页面开发 | yida-create-form-page |
| 发布已编写的页面 | yida-publish-page |
| 批量表格录入 | yida-table-form |
| PPT 幻灯片 | yida-ppt-slider |
| 异常场景 | 处理方式 |
|---|---|
在原生 renderJsx 页面里使用了 React Hooks | 改为类组件模式;或改用 .oyd.jsx 的 export default function Page() authoring 模式,并确认只使用受支持的 useState / useEffect(..., []) |
| 字段 ID 不确定 | 执行 openyida get-schema 获取真实 fieldId |
forceUpdate is not a function | 检查 this 绑定,确认方法用 export function 定义 |
| API 调用无响应 | 确认 .catch() 错误处理,检查登录态 |
| 发布后页面空白 | 检查 renderJsx 是否正确导出,查看浏览器控制台 |
以创建「员工信息查询页」为例,完整流程如下:
# Step 1:获取表单 Schema,确认字段 ID
openyida get-schema APP_XXX FORM-EMPLOYEE > .cache/employee-schema.json 2>&1
# Step 2:创建自定义页面
openyida create-page APP_XXX "员工信息查询"
# 输出:formUuid = FORM-QUERY001
# Step 3:生成/编写页面代码
# 优先使用 spec/blocks 生成高质量页面:openyida generate-page product-homepage --spec page.json --output project/pages/src/employee-query.oyd.jsx
# 需要完整交互样板时:openyida generate-page todo-mvc --output project/pages/src/todo-mvc.oyd.jsx --compile
# 需要手动参考模板时:openyida sample yida-custom-page custom-page-template
# 在 project/pages/src/employee-query.oyd.jsx 中编写
# Step 4:本地规范检查 + 编译校验(不发布)
openyida check-page project/pages/src/employee-query.oyd.jsx
openyida compile project/pages/src/employee-query.oyd.jsx
# Step 5:发布页面
openyida publish project/pages/src/employee-query.oyd.jsx APP_XXX FORM-QUERY001
预期输出:
{
"success": true,
"pageUrl": "https://www.aliwork.com/APP_XXX/custom/FORM-QUERY001"
}
关键说明:
FIELDS 常量映射这些 ID
fieldId(如 textField_k8j2n3m4)即是代码中 FIELDS 常量应映射的值openyida generate-page product-homepage --spec page.json --output pages/src/home.oyd.jsx --compile 生成高质量页面骨架;需要验证事件、状态、循环渲染、编辑和 localStorage 时使用 openyida generate-page todo-mvc --output pages/src/todo-mvc.oyd.jsx --compilegenerate-page 会写入 .openyida-page.json 结构化 manifest,后续改版优先改 spec/blocks 再重新生成 JSXfetch(DataV GeoJSON) -> echarts.registerMap('china', geoJson);不要加载旧版内置中国地图脚本。check-page 会拦截该类旧写法。label.formatter 返回 rich text 模板在宜搭自定义页面环境不稳定;优先使用普通 formatter 字符串,或在数据处理阶段预先拼好标签文本。check-page 支持行级禁用:// openyida-lint-disable-line <rule> 或 // openyida-lint-disable-next-line <rule>。只在确认该行不会触发宜搭运行时问题时使用。openyida sample yida-custom-page custom-page-template 获取编写页面代码前必须完整阅读 编码指南,包含文件结构模板、状态管理模式、生命周期钩子、全局变量及全部 19 条编码注意事项。 涉及输入控件、日期、选择、成员/部门、附件、表格或筛选栏时,同时阅读 组件指南。
| 变量 | 类型 | 说明 |
|---|---|---|
window.g_config._csrf_token | String | CSRF Token,调用需认证的接口(如 AI 接口、Schema 保存)时必须携带 |
window.loginUser.userId | String | 当前登录用户的工号 |
window.loginUser.userName | String | 当前登录用户的姓名 |
this.state.urlParams | Object | 页面 URL 中的查询参数 |
自定义方法必须用 export function 定义:凡是需要在方法内部使用 this(包括 this.utils.yida.*、this.setCustomState 等)的自定义方法,必须且只能使用 export function 方法名() {} 的形式定义,调用时使用 this.方法名()。禁止使用 const fn = () => {}、const fn = function() {} 等形式定义需要访问 this 的方法,这些形式无法被宜搭运行时正确绑定 this:
// ✅ 正确:export function + this.方法名() 调用
export function didMount() {
this.loadStatistics();
}
export function loadStatistics() {
this.utils.yida.searchFormDatas({ formUuid: 'FORM-XXX', pageSize: 10 });
}
// ❌ 错误①:缺少 export,无法被宜搭运行时识别,this 丢失
export function didMount() {
loadStatistics(); // 直接调用,this 丢失
}
function loadStatistics() {
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
}
// ❌ 错误②:箭头函数/函数表达式形式,缺少 export,无法被宜搭运行时绑定 this,禁止使用
const loadStatistics = () => {
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
};
const loadStatistics = function() {
this.utils.yida.searchFormDatas(...); // 报错:this is undefined
};
【严格禁止】事件绑定必须使用箭头函数包裹:在 renderJsx 中绑定任何事件处理器(onClick、onChange、onSubmit 等)时,先在函数顶部定义 var self = this,再使用箭头函数 (e) => { self.方法名(e) }。严禁直接写 this.方法名 或 .bind(this) 作为事件处理器,否则容易在宜搭运行时丢失上下文:
export function handleSubmit(e) {
this.setCustomState({ submitted: true });
this.utils.toast({ title: '提交成功', type: 'success' });
}
// ✅ 正确:renderJsx 顶部固定 self,箭头函数包裹
export function renderJsx() {
var self = this;
return <button onClick={(e) => { self.handleSubmit(e); }}>提交</button>;
}
// ❌ 错误①:直接传方法引用,this 丢失,运行时报错,绝对禁止!
export function renderJsx() {
return <button onClick={this.handleSubmit}>提交</button>;
}
// ❌ 错误②:使用 .bind(this) 绑定,虽然能运行但不符合规范,禁止使用!
export function renderJsx() {
return <button onClick={function() { this.handleSubmit(); }.bind(this)}>提交</button>;
}
生成代码时的自检清单:检查
renderJsx中所有onClick、onChange、onSubmit等事件属性,确保每一个都是(e) => { self.xxx(e) }形式,不存在任何onClick={this.xxx}或.bind(this)的写法。
// ❌ 错误③:在 .map(function(){}) 普通函数回调中使用箭头函数事件处理器,this 已在 function 回调里丢失,箭头函数捕获的 this 是 undefined!
export function renderJsx() {
return (
<div>
{quickBtns.map(function(btn, idx) {
return (
<button
key={idx}
onClick={(e) => { this.goToForm(btn.form); }} // ❌ this 是 undefined,运行时报错
>
{btn.label}
</button>
);
})}
</div>
);
}
// ✅ 正确:.map() 回调必须使用箭头函数,确保 this 正确捕获
export function renderJsx() {
var self = this;
return (
<div>
{quickBtns.map((btn, idx) => (
<button
key={idx}
onClick={(e) => { self.goToForm(btn.form); }} // ✅ 箭头函数回调 + self 调用,this 正确
>
{btn.label}
</button>
))}
</div>
);
}
补充自检项:检查
renderJsx中所有.map()、.filter()、.forEach()等数组方法的回调,确保全部使用箭头函数形式(item) => ...,不存在任何.map(function(item) {...})的写法,否则回调内部的this会丢失。
输入法组合输入处理:使用 _isComposing 标记配合 compositionstart / compositionend 事件,正确处理中文输入法的组合输入状态,避免输入过程中触发提交
定时器清理:在 didUnmount 中必须清理所有通过 setInterval / setTimeout 创建的定时器,防止内存泄漏
错误处理:所有 API 调用(this.utils.yida.*、fetch)必须使用 .catch() 处理异常,并通过 this.utils.toast({ title: message, type: 'error' }) 向用户展示错误提示
样式方式:所有样式通过 JavaScript 对象定义(内联样式),在 renderJsx 中通过 style 属性应用,不使用外部 CSS 文件。注意:CSS 渐变(linear-gradient 等)必须使用 background 属性,不能使用 backgroundColor(只接受纯色值),否则浏览器会忽略该值导致背景变白:
// ✅ 正确
style={{ background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)" }}
// ❌ 错误:backgroundColor 不支持渐变
style={{ backgroundColor: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)" }}
异步操作:可以使用 async/await 语法,Babel 编译会自动转换为 ES5 兼容代码
pageSize 上限:调用 searchFormDatas、searchFormDataIds、getProcessInstances、getProcessInstanceIds 等分页接口时,pageSize 最大值为 100,超过会导致接口报错。禁止将 pageSize 设置为超过 100 的值,推荐使用 10~100 之间的合理值。
输入框使用非受控组件:在宜搭环境中,<input> 的 value 属性绑定状态后会触发重渲染导致输入异常。正确做法:使用 defaultValue,在 onChange 中更新 _customState 而不调用 setCustomState:
// ❌ 错误:受控组件,每次输入都触发重渲染导致无法输入
<input value={userAnswer} onChange={function(e) { this.setCustomState({ userAnswer: e.target.value }); }} />
// ✅ 正确:非受控组件,仅静默更新状态,不触发重渲染
<input id="my-input" defaultValue="" onChange={function(e) { _customState.userAnswer = e.target.value; }} />
// 需要清空时通过 DOM 操作
var inputEl = document.getElementById("my-input");
if (inputEl) { inputEl.value = ""; }
DateField 时间戳格式:保存日期字段时,值必须是 时间戳(毫秒),不能是字符串:
// ❌ 错误:字符串格式
dateField_xxx: '2024-01-15'
// ✅ 正确:时间戳格式
dateField_xxx: new Date().getTime()
多端适配:宜搭自定义页面会在 PC 端和移动端同时展示,使用 this.utils.isMobile() 判断设备类型:
const isMobile = this.utils.isMobile();
var styles = {
container: { padding: isMobile ? '12px' : '16px', minHeight: '100vh' },
card: { padding: isMobile ? '12px' : '16px', marginBottom: isMobile ? '8px' : '12px' },
};
清除默认样式:宜搭自定义页面容器有默认 padding 和圆角,需要强制覆盖:
var styles = {
container: { padding: '0 16px', borderRadius: '0 !important', minHeight: '100vh' },
};
性能优化:
onChange 都调用 setCustomState,可直接写入 _customState 静默更新forceUpdaterenderJsx 顶部定义事件处理函数,避免每次渲染都创建新的内联函数⚠️ forceUpdate() 后的 DOM 渲染时序:
forceUpdate() 调用 this.setState() 后,React 会在下一个微任务中重新渲染组件。这意味着 forceUpdate() 之后同步代码中无法立即访问新渲染的 DOM 元素。
典型错误场景:异步数据加载完成后设置 loading=false 并调用 forceUpdate(),然后立即尝试操作新出现的 DOM 元素(如 document.getElementById('chart-container')),此时 DOM 还未更新,返回 null。
// ❌ 错误:forceUpdate 后立即操作新 DOM
_customState.loading = false;
self.forceUpdate();
var container = document.getElementById('my-chart'); // null!DOM 还没更新
// ✅ 正确:延迟一帧等待 React 完成 DOM 更新
_customState.loading = false;
self.forceUpdate();
setTimeout(function () {
var container = document.getElementById('my-chart'); // 此时 DOM 已存在
if (container) { /* 初始化图表等操作 */ }
}, 100);
适用场景:ECharts 图表初始化、Canvas 绑定、第三方库挂载等需要操作 DOM 的场景。详见
yida-chart技能的「图表渲染时序」章节。
调试技巧:
// 打印当前状态到控制台
console.log('当前状态:', _customState);
// 弹窗提示(适合快速验证逻辑)
this.utils.toast({ title: '调试信息', type: 'info' });
iframe 嵌入表单 URL 规范:在自定义页面中通过 iframe 嵌入宜搭表单时,需使用正确的 URL 格式:
| 场景 | URL 格式 |
|---|---|
| 表单提交页 | {base_url}/{appType}/submission/{formUuid} |
| 数据管理页(列表) | {base_url}/{appType}/workbench/{formUuid}?iframe=true |
| 数据管理页(指定视图) | {base_url}/{appType}/workbench/{formUuid}?viewUuid={viewUuid}&iframe=true |
// ❌ 错误:formDetail 是表单详情页,不是数据列表
const wrongUrl = `${baseUrl}/${appType}/formDetail/${formUuid}`;
// ✅ 正确:workbench 是运行态数据管理页
const listUrl = `${baseUrl}/${appType}/workbench/${formUuid}?iframe=true`;
viewUuid可选,从宜搭「数据管理」→「报表视图」页面的 URL 中获取,不传则使用默认视图。
下拉选项控制选项卡(Tabs)表格页显示/隐藏:当页面中存在选项卡组件包含多个表格页,需要根据下拉选择框的值动态控制特定表格页的显示或隐藏时,使用状态驱动的条件渲染实现。
实现要点:
_customState.selectedType 记录下拉选中值,onChange 时调用 setCustomState 触发重渲染_customState.activeTab 记录当前激活的 Tab,切换时直接写入 _customState 并调用 forceUpdate()display: none 而非条件渲染,保留 DOM 避免 iframe 重复加载该场景按上述状态机规则实现即可;当前技能包不提供单独示例文件,避免尝试读取不存在的 examples/tabs-visibility-control.js。
字段 ID 语义化别名约定
宜搭表单字段 ID 通常是随机字符串(如 textField_k8j2n3m4),直接在代码中使用可读性差、维护困难。推荐在文件顶部统一定义字段别名常量,在代码中始终使用别名引用字段 ID。
约定规范:
// ✅ 推荐:在文件顶部统一定义字段别名
// 字段 ID 来自 openyida get-schema 的输出,或 .cache/<项目名>-schema.json
var FIELDS = {
userName: 'textField_k8j2n3m4', // 姓名
department: 'selectField_a3b9c1d2', // 部门
applyDate: 'dateField_x7y2z5w1', // 申请日期
amount: 'numberField_p4q8r3s6', // 金额
status: 'radioField_m1n5o9p3', // 审批状态
remark: 'textareaField_v2w6x1y4', // 备注
};
// ✅ 使用别名引用字段,代码清晰易读
// 注意:必须用 ES5 写法构建对象,禁止使用计算属性名 { [key]: val }
var searchCondition = {};
searchCondition[FIELDS.department] = '研发部';
searchCondition[FIELDS.status] = '待审批';
this.utils.yida.searchFormDatas({
formUuid: 'FORM-XXX',
searchFieldJson: JSON.stringify(searchCondition),
currentPage: 1,
pageSize: 20,
});
// ✅ 构建提交数据时使用别名
var formDataJson = {};
formDataJson[FIELDS.userName] = _customState.inputName;
formDataJson[FIELDS.department] = _customState.selectedDept;
formDataJson[FIELDS.amount] = _customState.inputAmount;
代码编写前,执行以下命令获取示例模板,再用 read_file 完整读取:
openyida sample yida-custom-page custom-page-template # 完整页面模板(didMount/renderJsx/状态管理/API调用)
openyida sample yida-custom-page product-homepage # 产品/项目首页轻量模板(支持 --var KEY=VALUE)
openyida sample yida-custom-page todo-mvc # TodoMVC 完整交互模板(事件/状态/循环/本地存储)
openyida sample yida-custom-page design-tokens # 设计 token 参考(颜色/间距/字体规范)
openyida generate-page product-homepage --spec page.json --output pages/src/home.oyd.jsx --compile # 基于 spec/blocks 生成首页并本地编译
openyida generate-page todo-mvc --output pages/src/todo-mvc.oyd.jsx --compile # 生成官方 TodoMVC 风格交互样板
openyida check-page pages/src/home.oyd.jsx --json # 输出机器可读的规范检查结果;.oyd.jsx 会先兼容构建
this.utils.yida.<方法>(params))| 方法 | 说明 | 必填参数 |
|---|---|---|
saveFormData | 新建实例 | formUuid, appType, formDataJson |
updateFormData | 更新实例 | formInstId, updateFormDataJson |
deleteFormData | 删除实例 | formUuid |
getFormDataById | 查询详情 | formInstId |
searchFormDatas | 搜索列表 | formUuid |
searchFormDataIds | 搜索 ID 列表 | formUuid |
this.utils.yida.<方法>(params))| 方法 | 说明 | 必填参数 |
|---|---|---|
startProcessInstance | 发起流程 | formUuid, processCode, formDataJson |
getProcessInstanceById | 查询流程详情 | processInstanceId |
getProcessInstances | 搜索流程列表 | — |
this.utils.<方法>())| 方法 | 用途 |
|---|---|
toast | 轻提示 |
dialog | 对话框 |
formatter | 日期/金额格式化 |
getLoginUserId / getLoginUserName | 获取当前用户 |
isMobile | 判断移动端 |
openPage | 打开新页面 |
router.push | 路由跳转 |
loadScript | 动态加载脚本 |
上表为常用 API 速查,完整 API 列表见 yida-api.md。使用前必须阅读完整参数文档,禁止猜测参数。
以下接口用于调用大模型 AI 文本生成能力:
| 方法 | 说明 | 调用方式 |
|---|---|---|
txtFromAI | AI 文本生成 | POST /query/intelligent/txtFromAI.json |
主要参数:_csrf_token(CSRF 令牌)、prompt(提示词)、skill(技能类型,如 ToText)、maxTokens(最大返回 token 数)
使用前必须阅读 model-api.md 查询详细的参数,禁止猜测参数。
| 文档 | 覆盖范围 | 何时阅读 |
|---|---|---|
| 本技能文档 | ||
| 编码指南 | 文件结构模板、状态管理、生命周期、19 条编码规范 | 编写任何页面代码前必读 |
| 设计规范 | 色彩/圆角/字体/间距系统、7 类组件样式模板、8 条反模式 | 实现 UI 样式时必读 |
| 素材资源 | 图片/音乐/Icon 素材库、CDN 安全规范 | 需要引入图片、图标、音效时阅读 |
| 全局共享文档 | ||
| 宜搭 API | 表单/流程/工具 API 完整参数文档 | 调用 this.utils.yida.* 前必读 |
| 大模型 API | AI 文本生成接口参数 | 调用 txtFromAI 前必读 |
_customState)仅在当前页面会话内有效,刷新页面后重置,不跨会话持久化