宜搭低代码高级认证:待办列表自定义页面实战指南
本文档详细记录了在钉钉宜搭平台中,通过自定义页面实现待办事项管理功能的完整流程。主要涉及表单设计、自定义页面搭建以及 JavaScript 逻辑编写,涵盖数据的增删改查及状态流转。
1. 基础环境准备
首先需要在钉钉组织中完成相关配置。如果二维码扫描失效,可通过手机钉钉右下角【我的】->【客服与帮助】->【快捷工具】->【加入团队】搜索组织名称加入。
1.1 创建数据表单
我们需要两个核心表单:'进行中待办'和'已完成待办'。
- 进行中待办:包含待办事项(单行文本)、分类(单选)、重要度(评分,默认值 1)、提醒日期(日期)、详情(多行文本)。
- 已完成待办:复制上述表单并修改名称即可。
注意:组件的唯一标识(如
rateField_me2n0yf0)每次创建都会不同,后续代码中需替换为你实际获取到的标识。

2. 自定义页面搭建
新建自定义页面时,选择【工作台模板 -01】作为基础,删除不需要的布局容器,将标题改为'待办列表'。
2.1 页面布局
使用分组组件将页面分为'进行中待办'和'已完成待办'两部分。每个部分内部放置表格组件,并根据表单字段调整列显示。 对于'重要度'等枚举字段,需在表格配置中定义样式映射,例如:
[ { "color": "grey", "text": "1", "value": 1 }, ... ]
设置页面背景色为白色以保持界面整洁。
2.2 关键参数获取
在编写 JS 逻辑前,需要获取应用编码(AppType)和表单 UUID(formUuid)。
- AppType: 浏览器地址栏中查找
appType参数。 - formUuid: 在表单设计器顶部地址栏或属性面板中获取。 这些参数是调用后端 API 的必填项。

3. 功能逻辑实现
3.1 数据展示与分页
在数据源中添加远程 API getTodoData,请求 /v1/form/searchFormDatas.json。
数据处理回调中,需要将返回的 formData 映射到表格字段,并保留 formInstId 作为主键。
分页和搜索功能通过绑定表格的触发事件实现,监听 onFetchTodoData,根据用户输入更新 searchKey 和 page 状态,并重新加载数据源。
3.2 新增与编辑
点击'新增待办'按钮弹出对话框。
- 打开弹窗: 重置表单字段默认值。
- 提交保存: 校验必填项后,调用
insert接口。 - 编辑回显: 点击行内编辑时,将当前行数据回填至对话框,并将操作标志位
flag设为edit。 - 保存逻辑: 根据
flag判断是调用新增接口还是更新接口 (updateFormData.json)。
3.3 删除与状态流转
- 删除: 弹出确认框,获取选中行的
instid,调用删除接口。 - 移至已完成: 开启表格行选择器,单选模式下,选中某条记录后自动执行删除'进行中'数据并插入'已完成'数据的逻辑。
4. 完整代码参考
以下是自定义页面 JS 面板的核心逻辑汇总,包含了生命周期、数据交互及业务处理函数。请根据实际组件 ID 进行适配。
// 页面加载完成后初始化
export function didMount() {
console.log(`「页面 JS」:当前页面地址 ${location.href}`);
}
// 分页/搜索触发
export function onFetchTodoData(params) {
if (params.from === 'search') { params.currentPage = 1; }
this.setState({ "searchKey": params.searchKey, "page": params.currentPage });
}
// 新增按钮回调
export function onActionBarItemClick() {
this.setState({ flag:'insert' });
this.$('dialog_me9xwn20').show(() => {
// 重置表单字段
this.$('textField_me9xwn22').reset();
this.$('radioField_me9xwn23').reset();
this.$('rateField_me9xwn24').setValue(1);
this.$('dateField_me9xwn25').reset();
this.$('textareaField_me9xwn26').reset();
this.$('textField_mea9fcw1').reset();
});
}
// 确定按钮逻辑(含新增与编辑判断)
export function onOk() {
const createFieldList = [ 'textField_me9xwn22', 'radioField_me9xwn23', 'rateField_me9xwn24', 'dateField_me9xwn25' ];
const data1 = {
"textField_me2n0yey": this.$('textField_me9xwn22').getValue(),
"radioField_me2n0yez": this.$('radioField_me9xwn23').getValue(),
"rateField_me2n0yf0": this.$('rateField_me9xwn24').getValue(),
"dateField_me2n0yf1": this.$('dateField_me9xwn25').getValue(),
"textareaField_me2n0yf2": this.$('textareaField_me9xwn26').getValue()
};
const param = {
formUuid: "FORM-50859B5DAD37446FA143EDCB4CF3A44375VP",
appType: "APP_L0MRPK82DM5SNZ2UIHPM",
formDataJson: JSON.stringify(data1)
};
this.fieldsValidate(createFieldList).then((errorList) => {
setTimeout(() => {
if (errorList.length > 0) return;
if(this.state.flag === 'insert'){
this.dataSourceMap["insert"].load(param).then(res => {
this.utils.toast({ title: '新增成功', type: 'success', size: 'large' });
this.$('dialog_me9xwn20').hide();
setTimeout(() => { this.dataSourceMap['getTodoData'].load(); }, 1000);
});
} else {
const param2 = {
formInstId: this.$('textField_mea9fcw1').getValue(),
updateFormDataJson: JSON.stringify(data1)
};
this.dataSourceMap['updateData'].load(param2).then(res => {
this.utils.toast({ title: "修改成功", type: "success", size: "large" });
this.$('dialog_me9xwn20').hide();
setTimeout(() => { this.dataSourceMap['getTodoData'].load(); }, 1000);
});
}
}, 0);
});
}
// 辅助校验函数
export async function fieldsValidate(fieldList = []) {
const result = [];
for (let i = 0; i < fieldList.length; i++) {
await this.$(fieldList[i]).validate((errors, values) => {
if (!errors) return;
result.push({
fieldId: fieldList[i],
errors: this.utils.isMobile() ? errors.errors[fieldList[i]].errors : errors[fieldList[i]].errors
});
});
}
return result;
}
// 删除逻辑
export function onDeleteClick(rowData) {
this.$('dialog_mea9fcvy').show(() => {
this.$('textField_mea9fcw0').setValue(rowData.instid);
});
}
export function onDeleteTodoData() {
const param = { formInstId: this.$('textField_mea9fcw0').getValue() };
this.dataSourceMap['deleteData'].load(param).then(res => {
this.utils.toast({ title: "删除成功", type: "success", size: "large" });
this.$('dialog_mea9fcvy').hide();
setTimeout(() => {
this.dataSourceMap['getTodoData'].load();
this.dataSourceMap['getDoneData'].load();
}, 1000);
});
}
// 编辑逻辑
export function onEditClick(rowData) {
this.setState({ flag:'edit' });
this.$('dialog_me9xwn20').show(() => {
this.$('textField_me9xwn22').setValue(rowData.textField_me2n0yey);
this.$('radioField_me9xwn23').setValue(rowData.radioField_me2n0yez);
this.$('rateField_me9xwn24').setValue(rowData.rateField_me2n0yf0);
this.$('dateField_me9xwn25').setValue(rowData.dateField_me2n0yf1);
this.$('textareaField_me9xwn26').setValue(rowData.textareaField_me2n0yf2);
this.$('textField_mea9fcw1').setValue(rowData.instid);
});
}
// 行选择移动状态
export function onSelect(selected, rowData, selectedRows) {
const param1 = { formInstId: rowData.instid };
this.dataSourceMap['deleteData'].load(param1).then(res => {
setTimeout(() => { this.dataSourceMap['getTodoData'].load(); }, 1000);
});
const data1 = {
"textField_me2ndu46": rowData.textField_me2n0yey,
"radioField_me2ndu47": rowData.radioField_me2n0yez,
"rateField_me2ndu48": rowData.rateField_me2n0yf0,
"dateField_me2ndu49": rowData.dateField_me2n0yf1,
"textareaField_me2ndu4a": rowData.textareaField_me2n0yf2
};
const param2 = {
formUuid: "FORM-17BD995E61214B80BCEE9BB6EADF8DC8JNTV",
appType: "APP_L0MRPK82DM5SNZ2UIHPM",
formDataJson: JSON.stringify(data1)
};
this.dataSourceMap["insert"].load(param2).then(res => {});
setTimeout(() => { this.dataSourceMap['getDoneData'].load(); }, 1000);
}
// 已完成列表搜索
export function onFetchDoneData(params) {
if (params.from === 'search') { params.currentPage = 1; }
this.setState({ "doneSearchKey": params.searchKey, "donePage": params.currentPage });
}
通过以上步骤,即可完成一个具备基本 CRUD 及状态流转功能的待办列表应用。实际开发中请注意替换具体的 AppType 和 FormUuid,并根据业务需求调整字段映射。


