サンプルアプリの人事労務パックを例に、一覧画面で動的な値を表示する方法を示す。
従業員名簿アプリに、一覧名が 年齢と勤続年数
のカスタマイズビューを追加して
「ページネーションを表示する」のチェックを外す。また、以下のHTMLを記述。
<div id="age-tenure-custom-view">
<div class="loading">データを読み込み中...</div>
</div>
<style>
#age-tenure-custom-view {
padding: 20px;
background: #fff;
}
.loading, .error {
text-align: center;
padding: 40px;
font-size: 16px;
}
.loading {
color: #7f8c8d;
}
#employee-table th {
background: #f8f9fa;
font-weight: bold;
text-align: center;
}
</style>
JavaScript / CSSファイル は下図のように配置。
ライブラリは以下のサイトで入手できる。
sample.js は以下のコードを使う。
sample.js
(() => {
'use strict';
const client = new KintoneRestAPIClient();
// 一覧画面が表示されるときに実行される処理
kintone.events.on('app.record.index.show', async (event) => {
// 一覧名が「年齢と勤続年数」でない場合は何もしない
if (event.viewName !== '年齢と勤続年数') return event;
// 集計ボタンの右に「yyyy年MM月dd日現在」の形式で今日の日付を表示
const headerSpace = kintone.app.getHeaderMenuSpaceElement();
if (headerSpace) {
const now = luxon.DateTime.now();
const formatted = now.toFormat('yyyy年MM月dd日');
const infoText = `${formatted}現在`;
let infoEl = headerSpace.querySelector('.current-date-info');
if (!infoEl) {
infoEl = document.createElement('div');
infoEl.className = 'current-date-info';
infoEl.style.cssText = 'font-weight: bold; font-size: 26px;';
headerSpace.appendChild(infoEl);
}
infoEl.textContent = infoText;
}
// カスタマイズビューのコンテナ要素を取得
const container = document.getElementById('age-tenure-custom-view');
// ローディング表示を設定
container.innerHTML = '<div class="loading">データを読み込み中...</div>';
// 在籍中の従業員のレコードを全て取得
const records = await client.record.getAllRecordsWithCursor({
app: kintone.app.getId(),
fields: ['$id', '従業員番号', '氏名', '入社日', '生年月日'],
query: '在籍状況 in ("在籍中")'
});
// 取得したレコードをDataTables用に変換
const tableData = records.map(record => {
// 年齢の計算
let age;
// 生年月日が入力されてない場合はハイフンを年齢に表示
if (!record.生年月日?.value) {
age = '-';
// 生年月日が入力されている場合は 今日 - 生年月日 の差を年齢に表示
} else {
const birth = luxon.DateTime.fromISO(record.生年月日.value);
const today = luxon.DateTime.now();
const ageNum = Math.floor(today.diff(birth, 'years').years);
age = `${ageNum}歳`;
}
// 勤続年数の計算
let tenureHTML;
// 入社日が入力されてない場合はハイフンを勤続年数に表示
if (!record.入社日?.value) {
tenureHTML = '-';
// 入社日が入力されている場合は 今日 - 入社日 の差を勤続年数に表示
} else {
const hire = luxon.DateTime.fromISO(record.入社日.value);
const today = luxon.DateTime.now();
const diff = today.diff(hire, ['years', 'months']).toObject();
const years = Math.floor(diff.years);
const months = Math.floor(diff.months % 12);
// 勤続年数を「○年○ヶ月」の形式に整形
let yearHTML = '';
if (years > 0) {
yearHTML = (years < 10 ? ' '.repeat(1) : '') + years + '年';
}
const monthPad = months < 10 ? ' '.repeat(1) : '';
const monthHTML = monthPad + months + 'ヶ月';
tenureHTML = yearHTML + monthHTML;
}
// 値がない場合はハイフンを表示し、従業員番号に詳細画面へのリンクを追加
return {
employeeNumberLink: `<a href="/k/${kintone.app.getId()}/show#record=${record.$id.value}" target="_blank">${record.従業員番号?.value || '-'}</a>`,
name: record.氏名?.value || '-',
birthDate: record.生年月日?.value || '-',
hireDate: record.入社日?.value || '-',
age: age,
tenureHTML: tenureHTML
};
});
// カスタマイズビュー用のstyle要素が存在しない場合は作成
if (!document.getElementById('custom-datatable-css')) {
const style = document.createElement('style');
style.id = 'custom-datatable-css';
style.textContent = `
#employee-table {
margin: 0 auto;
border-collapse: collapse;
}
#employee-table thead th {
text-align: center !important;
white-space: nowrap;
}
#employee-table td {
text-align: center !important;
padding: 8px;
font-family: monospace;
}
#employee-table td .cell {
display: inline-block;
white-space: nowrap;
font-family: monospace;
}
#employee-table td.name-cell {
text-align: center !important;
}
#employee-table td.name-cell .cell {
text-align: left !important;
}
#employee-table td.name-cell .cell.dash-value {
text-align: center !important;
}
#employee-table td.tenure-cell {
text-align: center !important;
}
#employee-table td.tenure-cell .cell {
text-align: right !important;
}
#employee-table td.tenure-cell .cell.dash-value {
text-align: center !important;
}
#employee-table thead th.sorting_asc::after { content: ' ↑'; color: #007bff; }
#employee-table thead th.sorting_desc::after { content: ' ↓'; color: #007bff; }
#employee-table thead th.sorting::after { content: ' ↕'; color: #ccc; }
`;
document.head.appendChild(style);
}
// カスタマイズビューのHTMLを作成
const tableHtml = `
<table id="employee-table" class="display" style="width:100%">
<thead>
<tr>
<th>従業員番号</th>
<th>氏名</th>
<th>入社日</th>
<th>勤続年数</th>
<th>生年月日</th>
<th>年齢</th>
</tr>
</thead>
<tbody>
${tableData.map(row => `
<tr>
<td><div class="cell">${row.employeeNumberLink}</div></td>
<td class="name-cell"><div class="cell ${row.name === '-' ? 'dash-value' : ''}">${row.name}</div></td>
<td><div class="cell">${row.hireDate || '-'}</div></td>
<td class="tenure-cell"><div class="cell ${row.tenureHTML === '-' ? 'dash-value' : ''}">${row.tenureHTML}</div></td>
<td><div class="cell">${row.birthDate || '-'}</div></td>
<td><div class="cell">${row.age}</div></td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = tableHtml;
// 既にDataTablesのインスタンスが存在する場合は破棄
if ($.fn.DataTable.isDataTable('#employee-table')) {
$('#employee-table').DataTable().destroy();
}
// DataTablesの設定
$('#employee-table').DataTable({
// 日本語化ファイルのURL
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.7/i18n/ja.json'
},
pageLength: 25, // 1ページあたりの表示件数
order: [[0, 'asc']], // 従業員番号の昇順でソート
columnDefs: [
{ targets: [3], type: 'num' } // 勤続年数列を数値型として扱う
],
responsive: true // レスポンシブウェブデザインを有効にする
});
// 最も幅が広いセルに合わせて各列の最小幅を設定
const table = document.querySelector('#employee-table');
if (!table) return;
const rows = Array.from(table.rows);
if (rows.length === 0) return;
const columnCount = rows[0].cells.length;
const maxWidths = new Array(columnCount).fill(0);
rows.forEach(row => {
Array.from(row.cells).forEach((cell, i) => {
const div = cell.querySelector('.cell');
if (!div) return;
const width = div.getBoundingClientRect().width;
maxWidths[i] = Math.max(maxWidths[i], width);
});
});
rows.forEach(row => {
Array.from(row.cells).forEach((cell, i) => {
const div = cell.querySelector('.cell');
if (!div) return;
div.style.minWidth = `${maxWidths[i]}px`;
});
});
return event;
});
})();