メインコンテンツまでスキップ

カスタムページテーブルは、データ管理ページ機能を実現します

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. 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. データ列

データフィールドは「活動情報テーブル」のコンポーネントidに直接入力すればよい。

アクティブな画像のカスタムレンダリング:

// 活动图片表格列自定义渲染
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. オンラインで試遊する

この文書は機械翻訳により生成されています。翻訳により生じた齟齬や相違点は拘束力を持たず、コンプライアンスや執行目的において法的効力はありません。
© DingTalk (Singapore) Private Limited