跳到主要内容

自定义页面表格实现数据管理页功能

1. 使用场景

在用户方收集了一批用户信息表,填写表单后,希望可以直观的对数据进行管理,以往我们可以使用创建数据管理页来实现,然而数据管理页会稍微有一些局限性,不能直接对数据进行修改新增等操作,那么这时,我们可以使用自定义页面中的表格组件来实现,将数据进行展示后,可以对其操作列进行编辑,从而实现增删改查的效果。

2. 实现功能

若实现更复杂的业务场景,可按需配置(高级版)功能。

本示例功能清单:

  1. 配置数据展示到表格中
    1. 图片上传自定义展示
    2. 表格分页
    3. 表格排序
  1. 搜索(高级版)
  2. 创建活动(高级版)
  3. 刷新
  4. 查看详情
  5. 修改活动(高级版)
  6. 删除活动(高级版)
  7. 批量删除活动(高级版)
  8. 图片下载(高级版)

2.1. 配置【活动信息表】表单

2.2. 获取数据展示到表格中

2.2.1. 添加远程数据源

参考文档:根据条件搜索表单实例详情列表

接口配置如图:

`/${window.pageConfig.appType}/v1/form/searchFormDatas.json`

2.2.2. 添加变量

2.2.2.1. 每页实例数

2.2.2.2. 当前页码

2.2.2.3. 查询条件(高级版)

2.2.2.4. 排序条件

2.2.2.5. 表格加载状态

2.2.2.6. 表格数据

2.2.2.7. 当前选中的表格数据实例id(高级版)

用于一些批量操作。

2.2.3. 将下述 JS 代码拷贝至页面 JS 中并在 didMount 中调用

2.2.3.1. 高级版

export function didMount() {
this.getData(); // 获取数据
}

// 获取数据 - 高级版
export function getData() {
const { pageSize, currentPage, searchFieldData = {}, dynamicOrderData = {} } = this.state;
this.dataSourceMap.getData.load({
formUuid: 'FORM-KW766OD1AGUFQQHJ80EG66IGJ6S13GIUKYROL2',
pageSize,
currentPage,
searchFieldJson: JSON.stringify(searchFieldData),
dynamicOrder: JSON.stringify(dynamicOrderData),
}).then((res) => {
const { totalCount, data = [] } = res;
const result = data.map((item) => {
const { formInstId, formUuid, formData = {} } = item;
return {
formInstId,
formUuid,
...formData,
};
});
this.setState({
tableData: {
currentPage,
data: result,
totalCount,
},
tableIsLoading: false,
});
}).catch(({ message }) => {
this.utils.toast({
title: message,
type: 'error',
});
this.setState({
tableIsLoading: false,
});
});
}
2.2.3.2. 基础版

export function didMount() {
this.getData(); // 获取数据
}

// 获取数据 - 基础版
export function getData() {
const { pageSize, currentPage, dynamicOrderData = {} } = this.state;
this.dataSourceMap.getData.load({
formUuid: 'FORM-KW766OD1AGUFQQHJ80EG66IGJ6S13GIUKYROL2',
pageSize,
currentPage,
dynamicOrder: JSON.stringify(dynamicOrderData),
}).then((res) => {
const { totalCount, data = [] } = res;
const result = data.map((item) => {
const { formInstId, formUuid, formData = {} } = item;
return {
formInstId,
formUuid,
...formData,
};
});
this.setState({
tableData: {
currentPage,
data: result,
totalCount,
},
tableIsLoading: false,
});
}).catch(({ message }) => {
this.utils.toast({
title: message,
type: 'error',
});
this.setState({
tableIsLoading: false,
});
});
}

2.2.4. 表格配置

2.2.4.1. 数据列

数据字段直接填入【活动信息表】中的组件标识即可。

活动图片自定义渲染:

// 活动图片表格列自定义渲染
export function renderImageCell(value, index, rowData) {
const imgList = value ? JSON.parse(value) : [];
const imgUrls = imgList.map((item) => { return item.url; });
return <div class='imgContainer' style={{
display: 'flex',
width: '170px',
paddingBottom: '12px',
overflowX: 'auto',
userSelect: 'none'
}}>{imgList.map((item, index) => {
return (
<img
src={item.previewUrl}
onClick={() => {
this.utils.previewImage({
urls: imgUrls,
current: item.url,
});
}}
style={{
display: 'block',
width: '48px',
height: '48px',
borderRadius: '6px',
cursor: 'pointer',
marginRight: (index + 1) !== imgList.length ? '12px' : '0px'
}}
/>
)
})}</div>;
}
2.2.4.2. 数据源

2.2.4.3. 数据主键

formInstId

2.2.4.4. 加载状态

2.2.4.5. 分页设置

// 分页
export function onFetchData(params) {
const { pageSize } = this.state;
if (params.from === 'search' || params.pageSize !== pageSize) {
params.currentPage = 1;
}
const orderTypeText = {
'desc': '-', // 降序
'asc': '+', // 升序
};
let dynamicOrderData = {};
dynamicOrderData[params.orderColumn] = orderTypeText[params.orderType];
this.setState({
currentPage: params.currentPage,
pageSize: params.pageSize,
dynamicOrderData,
tableIsLoading: true,
});
this.getData();
}
2.2.4.6. 行选择器(高级版)

// 表格数据选择变动回调
export function onTableDataSelectChange(selectedRowKeys, records) {
this.setState({
selectedTableDataRowKeys: selectedRowKeys,
});
}

2.3. 页面 JS 中 添加工具函数(高级版)

无需做任何修改。

// 判断是否是钉钉容器
export function isDingTalkEnv(win) {
win = win || window;
return win.navigator && /dingtalk/i.test(win.navigator.userAgent) //true false
}

// 获取成员字段的 value
export function formatEmployeeFieldValue(userData) {
if (Array.isArray(userData)) {
return userData.length ? userData.map((item) => { return item.value; }) : '';
} else {
return userData ? [userData.value] : '';
}
}

// fieldList:Array,需要校验组件的唯一标识集合
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;
}

2.4. 搜索配置(高级版)

若需要使用更多操作符的高级搜索,可参考下述案例对搜索活动功能进行升级。

FaaS 连接器 - 钉钉开放平台 - 通过高级查询条件获取表单实例数据(包括子表单组件数据)

2.4.1. 添加查询组件

2.4.2. 绑定搜索事件

根据实际情况修改字段映射。

// 搜索
export function onSearch(values) {
this.setState({
currentPage: 1,
searchFieldData: {
textField_l9m5jvma: values.textField_lorywk26, // 活动名称
radioField_l9m5jvmc: values.selectField_los2eeiz, // 活动类型
radioField_l9m5jvmg: values.selectField_l9xx3dnz, // 所属组织
radioField_l9m5jvmn: values.selectField_lorywk27, // 活动进度
dateField_l9m5jvmi: values.cascadeDateField_l9njsj95 ? [values.cascadeDateField_l9njsj95.start, new Date(values.cascadeDateField_l9njsj95.end).setHours(23, 59, 59, 999)] : '', // 开始时间
employeeField_l9m5jvmp: this.formatEmployeeFieldValue(values.employeeField_l9njsj96), // 发起人
},
tableIsLoading: true,
});
this.getData(); // 获取数据
}

2.4.3. 绑定重置事件

// 重置
export function onReset(values) {
this.setState({
currentPage: 1,
searchFieldData: {},
tableIsLoading: true,
});
this.getData(); // 获取数据
}

2.5. 创建活动(高级版)

若需要批量创建活动,可参考下述案例对创建活动功能进行升级。

FaaS 连接器 - 钉钉开放平台 - 批量新增表单实例

2.5.1. 添加远程数据源

参考文档: 新增表单实例

接口配置如图:

`/${window.pageConfig.appType}/v1/form/saveFormData.json`

2.5.2. 添加创建活动对话框

配置如图:

2.5.3. 配置顶部操作并绑定下述函数

// 创建活动弹窗
export function onCreateActivityDialog() {
this.$('dialog_lowmwcke').show(() => {
this.$('textField_l9njsj8u').reset();
this.$('selectField_l9njsj8w').reset();
this.$('selectField_l9njsj8v').reset();
this.$('dateField_l9njsj8y').reset();
this.$('dateField_l9njsj8z').reset();
this.$('selectField_los2eej0').reset();
this.$('employeeField_l9njsj90').reset();
this.$('imageField_l9njsj94').reset();
});
}

2.5.4. 创建活动对话框绑定下述函数

// 确认创建活动
export function onCreateActivityOk() {
// 需要校验组件的唯一标识集合
const createFieldList = [
'textField_l9njsj8u',
'selectField_l9njsj8w',
'selectField_l9njsj8v',
'dateField_l9njsj8y',
'dateField_l9njsj8z',
'selectField_los2eej0',
'employeeField_l9njsj90',
];
// 调用表单校验函数
this.fieldsValidate(createFieldList).then((errorList) => {
setTimeout(() => {
if (errorList.length > 0) {
// 表单校验未通过
return;
};
this.$('dialog_lowmwcke').set('confirmState', 'LOADING'); // 开启对话框加载状态
// 新增表单数据
this.dataSourceMap.saveData.load({
formUuid: 'FORM-KW766OD1AGUFQQHJ80EG66IGJ6S13GIUKYROL2',
appType: pageConfig.appType,
formDataJson: JSON.stringify({
textField_l9m5jvma: this.$('textField_l9njsj8u').getValue(), // 活动名称
radioField_l9m5jvmc: this.$('selectField_l9njsj8w').getValue(), // 活动类型
radioField_l9m5jvmg: this.$('selectField_l9njsj8v').getValue(), // 所属组织
dateField_l9m5jvmi: this.$('dateField_l9njsj8y').getValue(), // 开始时间
dateField_l9m5jvmj: this.$('dateField_l9njsj8z').getValue(), // 结束时间
radioField_l9m5jvmn: this.$('selectField_los2eej0').getValue(), // 活动进度
employeeField_l9m5jvmp: this.formatEmployeeFieldValue(this.$('employeeField_l9njsj90').getValue()), // 发起人
imageField_l9nvelqm: (this.$('imageField_l9njsj94').getValue() || []).map((item) => {
return {
downloadUrl: item.downloadURL || item.downloadUrl,
name: item.name,
previewUrl: item.imgURL || item.imgUrl,
url: item.url,
};
}), // 将上传图片的参数修改为符合接口传参的格式,否则数据无法写入
}),
}).then(() => {
this.$('dialog_lowmwcke').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
this.$('dialog_lowmwcke').hide();
this.utils.toast({
title: '创建成功',
type: 'success',
});
setTimeout(() => {
this.setState({
tableIsLoading: true,
});
this.getData(); // 获取数据
}, 1000);
}).catch(({ message }) => {
this.utils.toast({
title: message,
type: 'error',
});
this.$('dialog_lowmwcke').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
});
});
}, 0);
}

2.6. 刷新

2.6.1. 配置顶部操作并绑定下述函数

// 刷新
export function onRefresh() {
this.setState({
tableIsLoading: true,
});
this.getData(); // 获取数据
}

2.7. 查看详情

2.7.1. 配置操作列并绑定下述函数

// 详情
export function onDetailClick(rowData) {
this.utils.router.push(
`/${pageConfig.appType}/formDetail/${rowData.formUuid}`,
{
formInstId: rowData.formInstId,
},
true,
true,
);
}

2.8. 修改活动(高级版)

2.8.1. 添加远程数据源

参考文档: 更新表单中指定组件值

接口配置如图:

`/${window.pageConfig.appType}/v1/form/updateFormData.json`

2.8.2. 添加修改活动对话框

配置如图:

2.8.3. 配置操作列并绑定下述函数

// 修改活动弹窗
export function onEditActivityDialog(rowData) {
this.$('dialog_lowmwckf').show(() => {
this.$('textField_los2eeja').setValue(rowData.formInstId);
this.$('textField_los2eej1').setValue(rowData.textField_l9m5jvma);
this.$('selectField_los2eej2').setValue(rowData.radioField_l9m5jvmc_id);
this.$('selectField_los2eej3').setValue(rowData.radioField_l9m5jvmg_id);
this.$('dateField_los2eej4').setValue(rowData.dateField_l9m5jvmi);
this.$('dateField_los2eej5').setValue(rowData.dateField_l9m5jvmj);
this.$('selectField_los2eej6').setValue(rowData.radioField_l9m5jvmn_id);
this.$('employeeField_los2eej7').setValue((rowData.employeeField_l9m5jvmp || []).map((item, index) => {
return {
label: item,
value: rowData.employeeField_l9m5jvmp_id[index],
};
}));
this.$('imageField_los2eej8').setValue(rowData.imageField_l9nvelqm ? JSON.parse(rowData.imageField_l9nvelqm) : '');
});
}

2.8.4. 修改活动对话框绑定下述函数

// 确认修改活动
export function onEditActivityOk() {
// 需要校验组件的唯一标识集合
const editFieldList = [
'textField_los2eej1',
'selectField_los2eej2',
'selectField_los2eej3',
'dateField_los2eej4',
'dateField_los2eej5',
'selectField_los2eej6',
'employeeField_los2eej7',
];
// 调用表单校验函数
this.fieldsValidate(editFieldList).then((errorList) => {
setTimeout(() => {
if (errorList.length > 0) {
// 表单校验未通过
return;
};
this.$('dialog_lowmwckf').set('confirmState', 'LOADING'); // 开启对话框加载状态
// 修改表单数据
this.dataSourceMap.editData.load({
formInstId: this.$('textField_los2eeja').getValue(), // 实例 id
updateFormDataJson: JSON.stringify({
textField_l9m5jvma: this.$('textField_los2eej1').getValue(), // 活动名称
radioField_l9m5jvmc: this.$('selectField_los2eej2').getValue(), // 活动类型
radioField_l9m5jvmg: this.$('selectField_los2eej3').getValue(), // 所属组织
dateField_l9m5jvmi: this.$('dateField_los2eej4').getValue(), // 开始时间
dateField_l9m5jvmj: this.$('dateField_los2eej5').getValue(), // 结束时间
radioField_l9m5jvmn: this.$('selectField_los2eej6').getValue(), // 活动进度
employeeField_l9m5jvmp: this.formatEmployeeFieldValue(this.$('employeeField_los2eej7').getValue()), // 发起人
imageField_l9nvelqm: (this.$('imageField_los2eej8').getValue() || []).map((item) => {
return {
downloadUrl: item.downloadURL || item.downloadUrl,
name: item.name,
previewUrl: item.imgURL || item.imgUrl,
url: item.url,
};
}), // 将上传图片的参数修改为符合接口传参的格式,否则数据无法修改
}),
}).then(() => {
this.$('dialog_lowmwckf').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
this.$('dialog_lowmwckf').hide();
this.utils.toast({
title: '修改成功',
type: 'success',
});
setTimeout(() => {
this.setState({
tableIsLoading: true,
});
this.getData(); // 获取数据
}, 1000);
}).catch(({ message }) => {
this.utils.toast({
title: message,
type: 'error',
});
this.$('dialog_lowmwckf').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
});
});
}, 0);
}

2.9. 删除活动(高级版)

2.9.1. 添加远程数据源

参考文档: 删除表单实例

接口配置如图:

`/${window.pageConfig.appType}/v1/form/deleteFormData.json`

2.9.2. 添加删除活动对话框

配置如图:

2.9.3. 配置操作列并绑定下述函数

// 删除活动弹窗
export function onDeleteActivityDialog(rowData) {
this.$('dialog_l9njsj8t').show(() => {
this.$('textField_los2eejb').setValue(rowData.formInstId);
});
}

2.9.4. 删除活动对话框绑定下述函数

// 确认删除活动
export function onDeleteActivityOk() {
this.$('dialog_l9njsj8t').set('confirmState', 'LOADING'); // 开启对话框加载状态
// 删除表单数据
this.dataSourceMap.deleteData.load({
formInstId: this.$('textField_los2eejb').getValue(), // 实例 id
}).then(() => {
this.$('dialog_l9njsj8t').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
this.$('dialog_l9njsj8t').hide();
this.utils.toast({
title: '删除成功',
type: 'success',
});
setTimeout(() => {
this.setState({
tableIsLoading: true,
});
this.getData(); // 获取数据
}, 1000);
}).catch(({ message }) => {
this.utils.toast({
title: message,
type: 'error',
});
this.$('dialog_l9njsj8t').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
});
}

2.10. 批量删除活动(高级版)

2.10.1. 配置 FaaS 连接器

可参考下述文档配置连接器:

FaaS 连接器 - 钉钉开放平台 - 批量删除表单实例 | 钉钉宜搭·帮助中心

2.10.2. 添加变量并配置行选择器

参考上述 2.2.2.7 和 2.2.4.6。

2.10.3. 添加远程数据源

2.10.4. 添加批量删除活动对话框

配置如图:

2.10.5. 配置顶部操作并绑定下述函数

// 批量删除活动弹窗
export function onBatchDeleteActivityDialog(rowData) {
const { selectedTableDataRowKey = [] } = this.state;
if (!selectedTableDataRowKey.length) {
this.utils.toast({
title: '当前未选中任何活动',
type: 'warning',
});
return;
}
this.$('dialog_lpjg5aha').show(() => {
this.$('numberField_lpjg8ky1').setValue(selectedTableDataRowKey.length);
});
}

2.10.6. 批量删除活动对话框绑定下述函数

// 确认批量删除活动
export function onBatchDeleteActivityOk() {
const { selectedTableDataRowKeys = [] } = this.state;
this.$('dialog_lpjg5aha').set('confirmState', 'LOADING'); // 开启对话框加载状态
// 批量删除表单数据
this.dataSourceMap.batchDeleteData.load({
inputs: JSON.stringify({
body: {
formUuid: 'FORM-4C833A663C8F4FC7A265CB075A82ED962L0T', // 活动信息表formUuid
appType: pageConfig.appType, // 应用 appType
formInstanceIdList: selectedTableDataRowKeys, // 需要删除的数据实例id列表
asynchronousExecution: true, // 是否需要异步执行该任务
executeExpression: false, // 是否需要触发表单绑定的校验规则、关联业务规则和第三方服务回调
},
}),
}).then((res) => {
this.$('dialog_lpjg5aha').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
const { success, error } = res;
if (success) {
this.$('dialog_lpjg5aha').hide();
this.utils.toast({
title: '批量删除成功',
type: 'success',
});
setTimeout(() => {
this.setState({
tableIsLoading: true,
selectedTableDataRowKeys: [],
});
this.getData(); // 获取数据
}, 1000);
} else {
this.utils.toast({
title: error,
type: 'success',
});
}
}).catch(({ message }) => {
this.utils.toast({
title: message,
type: 'error',
});
this.$('dialog_lpjg5aha').set('confirmState', 'NORMAL'); // 关闭对话框加载状态
});
}

2.11. 图片下载(高级版)

不支持钉钉工作台或移动端中使用。

2.11.1. 引入图片下载所需的三方 JS 资源

export async function didMount() {
if (!this.utils.isMobile() || !this.isDingTalkEnv()) {
// 不支持在钉钉工作台或移动端中下载
try {
await this.utils.loadScript('https://g.alicdn.com/code/lib/jszip/3.9.1/jszip.min.js');
await this.utils.loadScript('https://g.alicdn.com/code/lib/FileSaver.js/2.0.5/FileSaver.min.js');
console.log('DownloadImage Load Success');
} catch (e) {
this.utils.toast({
title: '图片下载加载失败',
type: 'error'
});
};
};
this.getData(); // 获取数据
}

2.11.2. 添加图片下载对话框

配置如下:

2.11.3. 添加变量

2.11.3.1. 当前选中的图片 id

2.11.3.2. 当前选中的图片详情

2.11.4. 配置图片下载对话框中的表格

2.11.4.1. 数据列

2.11.4.2. 数据主键

previewUrl

2.11.4.3. 行选择器

// 图片选择变动回调
export function onImageSelect(selectedRowKeys, records) {
this.setState({
selectedImageRowKeys: selectedRowKeys,
selectedImageDetail: records,
});
}

2.11.5. 配置操作列并绑定下述函数

// 图片下载弹窗
export function onDownloadImageDialog(rowData) {
const { imageField_l9nvelqm } = rowData;
if (this.utils.isMobile() || this.isDingTalkEnv()) {
this.utils.toast({
title: '不支持在钉钉工作台或移动端中下载',
type: 'warning',
});
return;
};
if (!imageField_l9nvelqm) {
this.utils.toast({
title: '暂无图片需要下载',
type: 'warning',
});
return;
}
this.$('dialog_legisiyq').show(() => {
this.$('textField_legtgpx9').setValue(`批量下载文件_${this.utils.formatter('date', Date.now(), 'YYYYMMDDhhmmss')}`);
this.$('tablePc_legpq38q').set('data', JSON.parse(imageField_l9nvelqm));
this.setState({
selectedImageRowKeys: [],
selectedImageDetail: [],
});
});
}

2.11.6. 页面 JS 中添加工具函数

无需做任何修改。

// 单张图片下载
export function downloadFile(downloadUrl) {
const link = document.createElement('a');
link.href = downloadUrl;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}

// 多张图片打包下载
export function downloadFiles(downloadfiles, fileName) {
const zip = new JSZip();
const result = downloadfiles.map(item => {
let promise = getFileBlob(item.downloadUrl).then((res) => {
zip.file(item.name, res, { binary: true });
});
return promise;
});
Promise.all(result).then(() => {
zip.generateAsync({ type: "blob" }).then((res) => {
saveAs(res, `${fileName}.zip`);
})
})
}

// 通过请求获取文件blob格式
function getFileBlob(url) {
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest()
request.open("GET", url, true)
request.responseType = "blob"
request.onload = (res) => {
if (res.target.status == 200) {
resolve(res.target.response)
} else {
reject(res)
}
}
request.send()
})
}

2.11.7. 图片下载对话框绑定下述函数

// 确定下载图片
export function onDownloadImageOk() {
const { selectedImageDetail = [] } = this.state;
if (!selectedImageDetail.length) {
this.utils.toast({
title: '尚未选择任何图片',
type: 'warning'
});
return;
};
if (selectedImageDetail.length > 1) {
// 批量下载
this.$('textField_legtgpx9').validate();
const fileName = this.$('textField_legtgpx9').getValue();
if (!fileName) { return; };
this.downloadFiles(selectedImageDetail, fileName);
this.$('dialog_legisiyq').hide();
} else {
// 单张下载
this.downloadFile(selectedImageDetail[0].downloadUrl);
this.$('dialog_legisiyq').hide();
};
}

3. 实现效果

3.1. 高级版

3.2. 基础版

4. 在线试玩

Copyright © 2024钉钉(中国)信息技术有限公司和/或其关联公司浙ICP备18037475号-4