| name | yida-custom-page |
| description | 宜搭自定义页面开发技能,包含宜搭表单 JS API 调用(增删改查/流程/工具类共 27 个)、React 16 JSX 组件开发规范、状态管理模式与编码约束。 |
| license | MIT |
| compatibility | ["opencode","claude-code"] |
| metadata | {"audience":"developers","workflow":"yida-development","version":"1.0.0","tags":["yida","low-code","react","custom-page"]} |
宜搭自定义页面开发技能
概述
本技能提供在阿里宜搭低代码平台上开发自定义页面的完整能力,涵盖从编码到部署的全流程:
| 能力 | 说明 |
|---|
| 表单数据操作 | 通过宜搭前端 JS API(this.utils.yida.*)对表单数据进行增删改查 |
| JSX 组件开发 | 编写 React 16 兼容的 JSX 代码,实现个性化定制页面 |
| AI 能力集成 | 调用大模型 AI 接口(/query/intelligent/txtFromAI.json)实现智能文本生成 |
| 自动编译部署 | 通过工具链将源码编译、压缩,并自动合并到宜搭 Schema 中保存 |
何时使用
当以下场景发生时使用此技能:
- 用户需要开发自定义展示页面(非表单)
- 用户需要实现复杂的页面交互逻辑
- 用户需要调用宜搭 JS API 进行数据操作
- 已有自定义页面,需要编写或修改 JSX 代码
使用示例
注意:编译和发布功能由 yida-publish-page 技能提供,此处仅作流程说明。
示例 1:编译源码
场景:将 JSX 源码编译为宜搭可用的格式
依赖:需先安装 yida-publish-page 依赖
命令:
cd .claude/skills/yida-publish-page/scripts && npm install
node babel-transform/transform.js pages/src/my-page.js
示例 2:发布页面
场景:编译并发布自定义页面到宜搭平台
命令:
node .claude/skills/yida-publish-page/scripts/publish.js APP_XXX FORM-XXX pages/src/my-page.js
快速开始
前置条件
- Node.js 16+(用于 Babel 编译和发布)
- Python 3.12+ +
playwright(用于登录态管理)
- 首次使用需安装依赖:
cd .claude/skills/yida-publish/scripts && npm install
pip install playwright && playwright install chromium
编译源码
node scripts/babel-transform/transform.js <源文件路径>
编译流程:
源文件(.js) → @ali/vu-babel-transform (Babel 转换) → UglifyJS (压缩) → <name>.compile.js
部署到宜搭
cd .claude/skills/yida-publish-page/scripts
npm install
node publish.js <appType> <formUuid> <源文件路径>
部署流程:
编译源码(Babel + UglifyJS) → 代码动态构建 Schema JSON(填入 source/compiled)
→ 调用 yida-login 获取登录态(Cookie 持久化) → 调用 saveFormSchema 接口保存
参数说明:
| 参数 | 说明 | 示例 |
|---|
appType | 应用 ID | APP_XXX |
formUuid | 表单 ID | FORM-XXX |
源文件路径 | 源码文件路径 | pages/src/xxx.js |
baseUrl 无需手动传入,脚本会自动调用 login.py 获取登录态并从中读取 base_url。
开发规范
以下规范是编写宜搭自定义页面代码的核心约束,必须严格遵守。
运行环境与约束
宜搭自定义页面的 JSX 组件本质上是 React 类组件中的 render 方法,而非独立的 React 组件。因此存在以下关键约束:
| 约束 | 说明 |
|---|
| React 版本 | 必须兼容 React 16,禁止使用 Hooks(useState、useEffect 等) |
| 单文件 | 所有代码写在一个文件中(如 index.js) |
| 三方包引入 | 禁止使用 import/require 语法,如需使用第三方库,必须通过 this.utils.loadScript 加载 CDN 脚本,参考 yida-api.md 的「工具类 API」章节。 |
| 函数导出格式 | 使用 export function xxx() {} 格式导出函数 |
| 样式 | 所有 css 必须写在 renderJsx 的方法中,通过 style 的方式引入 |
this 上下文 | 所有导出函数中的 this 指向宜搭页面的 React 类实例 |
禁止使用 this.setState 管理业务状态 | this.setState 已被覆盖,仅用于 forceUpdate(通过更新 timestamp) |
| JavaScript 版本 | 使用 ES2015 (ES6) 语法,不能高于 ES2015 版本 |
| 必须定义 renderJsx 函数 | renderJsx 是宜搭自定义页面核心渲染函数,也是入口函数,必须严格定义,不要改为其他名称 |
文件结构
一个完整的宜搭自定义页面源文件必须包含:
_customState 变量
- getCustomState 函数
- setCustomState 函数
- forceUpdate 函数
- didMount 函数
- didUnmount 函数
- renderJsx 函数
以下是一个完整自定义页面示例,包含状态管理、生命周期钩子、渲染函数
const _customState = {
count: 0,
loading: false,
};
export function getCustomState(key) {
if (key) {
return _customState[key];
}
return { ..._customState };
}
export function setCustomState(newState) {
Object.keys(newState).forEach(function(key) {
_customState[key] = newState[key];
});
this.forceUpdate();
}
export function forceUpdate() {
this.setState({ timestamp: new Date().getTime() });
}
export function didMount() {
}
export function didUnmount() {
}
export function handleSubmit(e) {
this.setCustomState({ submitted: true });
this.utils.toast({ title: '提交成功', type: 'success' });
}
export function renderJsx() {
const { timestamp } = this.state;
return (
<div>
{/* 必须保留:用于触发重新渲染 */}
<div style={{ display: "none" }}>{timestamp}</div>
{/* 页面内容写在这里 */}
<div onClick={(e) => {this.handleSubmit(e)}>提交</div>
</div>
);
}
状态管理使用方式
const state = this.getCustomState();
const count = this.getCustomState('count');
this.setCustomState({ count: count + 1, loading: true });
this.forceUpdate();
生命周期钩子
| 钩子函数 | 触发时机 | 典型用途 |
|---|
didMount() | 页面 DOM 加载渲染完毕 | 初始化数据加载、启动定时器、绑定事件 |
didUnmount() | 页面节点从 DOM 移除 | 清理 setInterval / setTimeout、解绑事件 |
全局变量
| 变量 | 类型 | 说明 |
|---|
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 didMount() {
this.loadStatistics();
}
export function loadStatistics() {
this.utils.yida.searchFormDatas({ formUuid: 'FORM-XXX', pageSize: 10 });
}
export function didMount() {
loadStatistics();
}
function loadStatistics() {
this.utils.yida.searchFormDatas(...);
}
const loadStatistics = () => {
this.utils.yida.searchFormDatas(...);
};
const loadStatistics = function() {
this.utils.yida.searchFormDatas(...);
};
-
【严格禁止】事件绑定必须使用箭头函数包裹:在 renderJsx 中绑定任何事件处理器(onClick、onChange、onSubmit 等)时,必须且只能使用箭头函数 (e) => { this.方法名(e) } 的形式,严禁直接写 this.方法名 作为事件处理器,否则 this 会丢失导致运行时报错:
export function handleSubmit(e) {
this.setCustomState({ submitted: true });
this.utils.toast({ title: '提交成功', type: 'success' });
}
export function renderJsx() {
return <button onClick={(e) => { this.handleSubmit(e); }}>提交</button>;
}
export function renderJsx() {
return <button onClick={this.handleSubmit}>提交</button>;
}
export function renderJsx() {
return <button onClick={function() { this.handleSubmit(); }.bind(this)}>提交</button>;
}
生成代码时的自检清单:检查 renderJsx 中所有 onClick、onChange、onSubmit 等事件属性,确保每一个都是 (e) => { this.xxx(e) } 形式,不存在任何 onClick={this.xxx} 的写法。
-
输入法组合输入处理:使用 _isComposing 标记配合 compositionstart / compositionend 事件,正确处理中文输入法的组合输入状态,避免输入过程中触发提交
-
定时器清理:在 didUnmount 中必须清理所有通过 setInterval / setTimeout 创建的定时器,防止内存泄漏
-
错误处理:所有 API 调用(this.utils.yida.*、fetch)必须使用 .catch() 处理异常,并通过 this.utils.toast({ title: message, type: 'error' }) 向用户展示错误提示
-
样式方式:所有样式通过 JavaScript 对象定义(内联样式),在 renderJsx 中通过 style 属性应用,不使用外部 CSS 文件
-
异步操作:可以使用 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; }} />
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 静默更新
- 只在需要触发重渲染时才调用
forceUpdate
- 在
renderJsx 顶部定义事件处理函数,避免每次渲染都创建新的内联函数
-
调试技巧:
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 |
const wrongUrl = `${baseUrl}/${appType}/formDetail/${formUuid}`;
const listUrl = `${baseUrl}/${appType}/workbench/${formUuid}?iframe=true`;
viewUuid 可选,从宜搭「数据管理」→「报表视图」页面的 URL 中获取,不传则使用默认视图。
API 参考
表单数据操作
通过 this.utils.yida.<方法名>(params) 调用,所有接口返回 Promise。
| 方法 | 说明 | 必填参数 |
|---|
saveFormData | 新建表单实例 | formUuid, appType, formDataJson |
updateFormData | 更新表单实例 | formInstId, updateFormDataJson |
deleteFormData | 删除表单实例 | formUuid |
getFormDataById | 根据实例 ID 查询详情 | formInstId |
searchFormDatas | 按条件搜索表单实例详情列表 | formUuid |
searchFormDataIds | 按条件搜索表单实例 ID 列表 | formUuid |
getFormComponentDefinationList | 获取表单定义 | formUuid |
常用示例 — 新建表单数据:
this.utils.yida.saveFormData({
formUuid: 'FORM-XXX',
appType: window.pageConfig.appType,
formDataJson: JSON.stringify({
textField_xxx: '单行文本',
textareaField_xxx: '多行文本',
}),
}).then(function(res) {
console.log('新建成功,实例ID:', res.result);
}).catch(function(err) {
this.utils.toast({ title: err.message, type: 'error' });
}.bind(this));
常用示例 — 搜索表单数据:
this.utils.yida.searchFormDatas({
formUuid: 'FORM-XXX',
searchFieldJson: JSON.stringify({ textField_xxx: '查询值' }),
currentPage: 1,
pageSize: 10,
}).then(function(res) {
console.log('查询结果:', res.data);
}).catch(function(err) {
this.utils.toast({ title: err.message, type: 'error' });
}.bind(this));
流程操作
| 方法 | 说明 | 必填参数 |
|---|
startProcessInstance | 发起流程 | formUuid, processCode, formDataJson |
updateProcessInstance | 更新流程实例 | processInstanceId, updateFormDataJson |
deleteProcessInstance | 删除流程实例 | processInstanceId |
getProcessInstanceById | 根据实例 ID 查询流程详情 | processInstanceId |
getProcessInstances | 按条件搜索流程实例详情列表 | — |
getProcessInstanceIds | 按条件搜索流程实例 ID 列表 | — |
表单设计类 API
以下接口用于表单页面的创建和配置,通过 HTTP 请求调用:
| 方法 | 说明 | 调用方式 |
|---|
saveFormSchemaInfo | 创建空白表单 | POST /dingtalk/web/{appType}/query/formdesign/saveFormSchemaInfo.json |
getFormSchema | 获取表单 Schema | GET /alibaba/web/{appType}/_view/query/formdesign/getFormSchema.json |
saveFormSchema | 保存表单 Schema | POST /dingtalk/web/{appType}/_view/query/formdesign/saveFormSchema.json |
updateFormConfig | 更新表单配置 | POST /dingtalk/web/{appType}/query/formdesign/updateFormConfig.json |
完整参数说明请参考 yida-api.md 的「表单设计类 API」章节。
大模型 AI 接口
以下接口用于调用大模型 AI 文本生成能力:
| 方法 | 说明 | 调用方式 |
|---|
txtFromAI | AI 文本生成 | POST /query/intelligent/txtFromAI.json |
主要参数:_csrf_token(CSRF 令牌)、prompt(提示词)、skill(技能类型,如 ToText)、maxTokens(最大返回 token 数)
完整参数说明和示例请参考 model-api.md。
工具类 API 速查
以下工具函数通过 this.utils.<方法名>() 调用,无需 yida 命名空间:
| 方法 | 用途 | 典型场景 |
|---|
toast | 轻提示 | 操作成功/失败提示、loading 状态 |
dialog | 对话框 | 确认操作、复杂内容展示 |
formatter | 格式化 | 日期、金额、手机号格式化 |
getDateTimeRange | 获取时间范围 | 按日/月/周筛选数据 |
getLoginUserId / getLoginUserName | 获取当前用户 | 记录操作人、数据权限控制 |
getLocale | 获取语言环境 | 多语言适配 |
isMobile | 判断移动端 | 响应式布局适配 |
isSubmissionPage | 判断是否提交页面 | 页面逻辑区分 |
isViewPage | 判断是否查看页面 | 页面逻辑区分 |
openPage | 打开新页面 | 页面跳转、外链打开 |
router.push | 页面路由跳转工具 | 页面路由跳转、避免新开页面 |
previewImage | 图片预览 | 图片查看、多图轮播 |
loadScript | 动态加载脚本 | 引入第三方库(如二维码生成) |
完整参数说明和示例请参考 yida-api.md 的「工具类 API」章节。
工具链
| Skill | 说明 | 用法 |
|---|
| yida-login | 登录态管理(Cookie 持久化 + 扫码登录) | python3 .claude/skills/yida-login/scripts/login.py |
| yida-publish-page | 编译源码 + 构建 Schema + 发布到宜搭 | node .claude/skills/yida-publish-page/scripts/publish.js <appType> <formUuid> <源文件路径> |
| yida-page-config | 页面配置(URL 验证、公开访问/分享配置) | node .claude/skills/yida-page-config/scripts/verify-short-url.js <appType> <formUuid> /o/xxx |
编译 + 发布(一键完成)
cd .claude/skills/yida-publish-page/scripts
npm install
node publish.js <appType> <formUuid> <源文件路径>
处理流程:
- 通过
@ali/vu-babel-transform 将 JSX 转换为 ES5 + UglifyJS 压缩
- 通过代码动态构建完整的 Schema JSON,将
source 和 compiled 填入 actions.module
- 调用
yida-login 获取登录态(Cookie 持久化,首次需扫码登录)
- 通过 HTTP POST 调用
saveFormSchema 接口保存 Schema
仅编译(不发布)
node .claude/skills/yida-publish-page/scripts/babel-transform/transform.js <源文件路径>
输入 JSX 源文件,输出编译压缩后的 <name>.compile.js(与源文件同目录)。