Index
前言
kintone 集成了向员工显示的公告和常用应用以及应用访问链接等。
例如,制作经费报销应用时,在经费信息中附上收据,员工可以很轻松地提交给会计。
按照原本的做法,需要在门户上设置经费报销应用的链接;跳到经费报销应用画面后,再添加记录;然后将收据的图像作为附件上传;最后保存记录等多个步骤。
但是,对于经常需要报销费用的员工来说,这种方式需要更多的操作,而且比想象中要麻烦得多。
因此,这次我们自定义了门户,可以在门户上进行经费报销,通过将收据拖放上传来减少操作上的麻烦,实现业务改善。
创建经费报销应用
参考以下图片和字段的设置,创建经费报销预付款。
字段类型 | 字段名称 | 字段代码 |
---|---|---|
多行文本框 | 概要 | description |
附件 | 收据 | receipt |
数值 | 费用(不含税) | cost |
日期 | 日期 | date |
Kintone Portal Designer的设置
参考此文(用Kintone Portal Designer来设计门户)来安装 Kintone Portal Designer 。
安装后,转到 kintone 门户,然后单击工具栏上的"</>"按钮以启动。
以下画面显示的内容,请参考编辑 HTML、CSS、JavaScript 并保存各个选项卡上的项目。
编辑 HTML
参考以下内容编辑并保存 HTML 。
/* * 使用Kintone Portal Designer从门户上传附件的程序范例 * Copyright (c) 2021 Cybozu * * Licensed under the MIT License * https://opensource.org/licenses/mit-license.php */ <form> <div class="parent"> <div class="header"> <h2>经费报销</h2> </div> <div class="label1"> <label for="description">概要 </label> </div> <div class="input1"> <input type="text" id="description" name="description"> </div> <div class="label2"> <label for="amount">费用(不含税) </label> </div> <div class="input2"> <input type="text" id="amount" name="amount"> </div> <div class="label3"> <label for="date">日期(YYYY-MM-DD) </label> </div> <div class="input3"> <input type="text" id="date" name="date"> </div> <div class="drop_zone" ondrop="dropHandler(event);" ondragover="dragOverHandler(event);"> <div id="file_name" class="tool_tip">将文件拖放到此处</div> </div> <div class="button"> <input type="submit" value="添加" class="bSubmit" onclick="registerExpense(event);"> </div> </div> </form>
编辑 CSS
参考以下内容编辑并保存 CSS 。
/* * 使用Kintone Portal Designer从门户上传附件的程序范例 * Copyright (c) 2021 Cybozu * * Licensed under the MIT License * https://opensource.org/licenses/mit-license.php */ .parent { display: grid; grid-template-columns: repeat(3,300px); grid-template-rows: 50px repeat(3,30px) 50px; border: 3px solid green; border-radius: 15px; margin: 10px 10px 10px 10px; column-gap: 10px; row-gap: 1em; background-color: #ffffff; } label { font-weight: bold; } input { border: 2px solid green; border-radius: 5px; } .button { grid-area: 5 / 1 / 6 / 4; justify-self: center; align-self: center; } .bSubmit { padding: 5px 30px; font-weight: bold; } .drop_zone { border: 2px dotted green; border-radius: 10px; grid-column: 3 / 4; grid-row: 2 / 5; margin: 10px 10px; } .tool_tip{ text-align: center; padding: 30px 0; opacity: 0.5; } .header { grid-area: 1 / 1 / 2 / 4; font-weight: bold; font-size: 1.5em; padding: 0px 50px; } .label1{ grid-column: 1 / 2; grid-row: 2 / 3; text-align: right; } .label2{ grid-column: 1 / 2; grid-row: 3 / 4; text-align: right; } .label3{ grid-column: 1 / 2; grid-row: 4 / 5; text-align: right; } .input1{ grid-column: 2 / 3; grid-row: 2 / 3; } .input2{ grid-column: 2 / 3; grid-row: 3 / 4; } .input3{ grid-column: 2 / 3; grid-row: 4 / 5; }
编辑 JavaScript
参考以下内容编辑并保存 JavaScript 。这次我们参考 MDN Web Docs 的“File drag and drop”。
/* * 使用Kintone Portal Designer从门户上传附件的程序范例 * Copyright (c) 2021 Cybozu * * Licensed under the MIT License * https://opensource.org/licenses/mit-license.php */ let file = null; const dropHandler = (ev) => { console.log('文件已拖放。'); // 避免默认操作中打开文件。 ev.preventDefault(); if (ev.dataTransfer.items) { // 浏览器为Chrome时,使用 DataTransferItemList 接口访问文件。 for (let i = 0; i < ev.dataTransfer.items.length; i++) { // 若拖入的项目不是文件,则跳过。 if (ev.dataTransfer.items[i].kind === 'file') { file = ev.dataTransfer.items[i].getAsFile(); console.log('... file[' + i + '].name = ' + file.name); } } } else { // 对于老式浏览器,使用 DataTransfer 接口访问文件。 for (let i = 0; i < ev.dataTransfer.files.length; i++) { file = ev.dataTransfer.files[i]; console.log('... file[' + i + '].name = ' + ev.dataTransfer.files[i].name); } } document.getElementById('file_name').innerText = file.name; }; const dragOverHandler = (ev) => { console.log('文件已拖入放置区域。'); // 避免默认操作中打开文件。 ev.preventDefault(); }; const APP_ID = {kintone App ID}; const registerExpense = async (ev) => { console.log('进入了registerExpense函数内。'); ev.preventDefault(); const fileKeys = []; const param = { 'app': APP_ID, 'record': { 'description':{ 'value': document.getElementById('description').value }, 'cost':{ 'value': document.getElementById('amount').value }, 'date':{ 'value': document.getElementById('date').value } } }; try { const resp = await kintone.api(kintone.api.url('/k/v1/record.json', true), 'POST', param); // 成功 console.log(resp); console.log(`Record ID:${resp.id}`); const rec_id = resp.id; console.log(`File:${file}`); if (file) { uploadFile(rec_id); } alert(`记录添加成功。记录ID: ${resp.id}`); resetForm(); } catch(error) { // 错误 alert(`记录添加失败。 ${error.message}`) } }; const uploadFile = (rec_id) => { let formData = new FormData(); formData.append('__REQUEST_TOKEN__', kintone.getRequestToken()); formData.append('file', file, file.name); const url = kintone.api.url('/k/v1/file.json', true); let xhr = new XMLHttpRequest(); xhr.open('POST', url); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.onload = () => { if (xhr.status === 200) { // 成功 console.log(JSON.parse(xhr.responseText)); const key = {'fileKey': JSON.parse(xhr.responseText).fileKey}; updateRecord(rec_id, key); } else { // 错误 console.log(JSON.parse(xhr.responseText)); } }; xhr.send(formData); }; const updateRecord = async (rec_id, fileKey) => { const param = { 'app': APP_ID, 'id': rec_id, 'record': { 'receipt': { 'value': [fileKey] } } } const resp = await kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', param); // 成功 console.log(resp); }; const resetForm = () => { document.getElementById('description').value = ''; document.getElementById('amount').value = ''; document.getElementById('date').value = ''; document.getElementById('file_name').innerText = ''; file = null; };
操作检查
在 Design Portal 中保存上述设置,通过左上角的开关启用 Design Portal 。
打开 kintone 的门户页面,更新页面后会显示以下画面。
填入必要项目,把经费报销使用到的收据文件拖入放置区域,点击添加按钮,就会在上述创建的经费报销应用中添加新的记录。
若打开经费报销应用,发现添加了记录,则以上操作成功。
显示其他部件
单击 Kintone Portal Designer 的“Export”按钮后,在显示的子菜单中单击“Export as JavaScript(Desktop)”。然后制作的门户设计的 JavaScript 文件将下载到下载文件夹。
接着返回门户页面,点击齿轮图标,点击“kintone系统管理”,进入设置页面。
点击“自定义”—“通过JavaScript / CSS自定义”菜单,进入自定义设置页面。
点击 JavaScript文件(电脑专用)的“通过上传添加”按钮,上传并保存刚才下载的门户设计的 JavaScript 文件。
返回 Kintone Portal Designer 的页面、关闭 Default Portal 的开关。
再次回到门户页面后,其他部件也会同时显示。
此外,要自定义显示的部件,请单击右上角的“・・・”,选择“门户的设置”。
在"在门户页面显示"中,选择并保存要查看的内容。
代码说明
此函数是当文件拖放到 Class 名为 drop_zone 的 div 元素中时调用的函数。
const dropHandler = (ev) => { console.log('文件已拖放。'); // 避免默认操作中打开文件。 ev.preventDefault(); if (ev.dataTransfer.items) { // 浏览器为Chrome时,使用 DataTransferItemList 接口访问文件。 for (let i = 0; i < ev.dataTransfer.items.length; i++) { // 若拖入的项目不是文件,则跳过。 if (ev.dataTransfer.items[i].kind === 'file') { file = ev.dataTransfer.items[i].getAsFile(); console.log('... file[' + i + '].name = ' + file.name); } } } else { // 对于老式浏览器,使用 DataTransfer 接口访问文件。 for (let i = 0; i < ev.dataTransfer.files.length; i++) { file = ev.dataTransfer.files[i]; console.log('... file[' + i + '].name = ' + ev.dataTransfer.files[i].name); } } document.getElementById('file_name').innerText = file.name; };
这个方法可以防止文件在拖放时打开。
ev.preventDefault();
在“ev.dataTransfer.items”接口里拖拽的所有数据列表,仅在文件类型时获取数据内容。(这里的接口由最新的浏览器 Chrome 等支持。)
if (ev.dataTransfer.items) { // 浏览器为Chrome时,使用 DataTransferItemList 接口访问文件。 for (let i = 0; i < ev.dataTransfer.items.length; i++) { // 若拖入的项目不是文件,则跳过。 if (ev.dataTransfer.items[i].kind === 'file') { file = ev.dataTransfer.items[i].getAsFile(); console.log('... file[' + i + '].name = ' + file.name); } } }
在“ev.dataTransfer.files”接口上获取拖放文件的列表。(该接口由老式浏览器支持。)
// 对于老式浏览器,使用 DataTransfer 接口访问文件。 for (let i = 0; i < ev.dataTransfer.files.length; i++) { file = ev.dataTransfer.files[i]; console.log('... file[' + i + '].name = ' + ev.dataTransfer.files[i].name); }
此代码是文件被拖到 Class 名为 drop_zone 的 div 元素中时所调用的函数。这里也防止文件打开。
const dragOverHandler = (ev) => { console.log('文件已拖入放置区域。'); // 避免默认操作中打开文件。 ev.preventDefault(); };
此函数将Class 名为 drop_zone 的 div 元素内的文件以及输入的内容作为新记录添加到 kintone 中。另外,请在{kintone App ID}中设置 kintone 创建的经费报销应用的ID。
const APP_ID = {kintone App ID}; const registerExpense = async (ev) => { console.log('进入了registerExpense函数内。'); ev.preventDefault(); const fileKeys = []; const param = { 'app': APP_ID, 'record': { 'description':{ 'value': document.getElementById('description').value }, 'cost':{ 'value': document.getElementById('amount').value }, 'date':{ 'value': document.getElementById('date').value } } }; try { const resp = await kintone.api(kintone.api.url('/k/v1/record.json', true), 'POST', param); // 成功 console.log(resp); console.log(`Record ID:${resp.id}`); const rec_id = resp.id; console.log(`File:${file}`); if (file) { uploadFile(rec_id); } alert(`记录添加成功。记录ID: ${resp.id}`); resetForm(); } catch(error) { // 错误 alert(`记录添加失败。 ${error.message}`) } };
在此代码中,获取经费报销的各字段的值,创建新记录。
这里没有保存文件数据。
const param = { 'app': APP_ID, 'record': { 'description':{ 'value': document.getElementById('description').value }, 'cost':{ 'value': document.getElementById('amount').value }, 'date':{ 'value': document.getElementById('date').value } } }; try { const resp = await kintone.api(kintone.api.url('/k/v1/record.json', true), 'POST', param);
成功创建新记录后,调用函数以获取记录 ID 并上传文件。
// 成功 console.log(resp); console.log(`Record ID:${resp.id}`); const rec_id = resp.id; console.log(`File:${file}`); if (file) { uploadFile(rec_id); } alert(`记录添加成功。记录ID: ${resp.id}`);
通过这个函数调用上传文件的 kintone API ,将下载的文件数据上传到 kintone。
获取上传成功时返回的File Key值。
然后,调用更新记录的函数以便将记录 ID 与 File Key 相关联。
const uploadFile = (rec_id) => { let formData = new FormData(); formData.append('__REQUEST_TOKEN__', kintone.getRequestToken()); formData.append('file', file, file.name); const url = kintone.api.url('/k/v1/file.json', true); let xhr = new XMLHttpRequest(); xhr.open('POST', url); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.onload = () => { if (xhr.status === 200) { // 成功 console.log(JSON.parse(xhr.responseText)); const key = {'fileKey': JSON.parse(xhr.responseText).fileKey}; updateRecord(rec_id, key); } else { // 错误 console.log(JSON.parse(xhr.responseText)); } }; xhr.send(formData); };
用于将记录 ID 与 File Key 关联的函数。
const updateRecord = async (rec_id, fileKey) => { const param = { 'app': APP_ID, 'id': rec_id, 'record': { 'receipt': { 'value': [fileKey] } } } const resp = await kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', param); // 成功 console.log(resp); };
参考网站
结束语
通常,经常使用的应用可以通过在 kintone 门户页面上显示并点击链接来使用。
但是,需要跳转到应用页面上才能使用。
通过 Kintone Portal Designer 进行自定义,可以在门户页面中,通过拖放来上传收据图像文件,比如经费报销应用,并将记录添加到相应的应用中,从而改善系统运用。
此Tips在2021年10月版的 kintone 中确认过。