kintoneでExcelのようにシートを表示する方法

こんにちは! 私の名前はチュンです。
株式会社メディアフュージョンでkintone関連製品の開発を担当しているベトナム出身のエンジニアです。
kintoneに興味のある方、kintoneを使っている方に私の蓄積した経験を共有したいと思います。
kintoneのサービス契約管理のような業務アプリでは、一つのレコードに契約の基本情報から、支払いの履歴、技術サポートの履歴まで、関連する非常に多くの情報が含まれることがよくあります。これらの情報はすべて、通常のフィールドやテーブルとして同じページ上に散在して配置されています。データ量が増えるにつれて、このように情報が広範囲に表示されることで、インターフェースが重くなり、情報が追いにくくなります。
例えば、一つのレコードで以下のような多くの情報を処理する必要がある場合があります。
- 契約情報(契約番号、顧客名、契約期間、担当者)
- 支払いリスト(支払い回数、期日、支払い状況)(テーブル)
- 技術サポート履歴(顧客からのリクエストの受付・処理記録)(テーブル)
kintoneでは、関連するフィールドをグループ化して表示を折りたたむ「グループ」を利用できます。しかし、kintoneの制限として、テーブルをグループ内に配置することはできません。このため、ユーザーはテーブルを折りたたむことができず、必要な情報を見つけるために何度もスクロールする必要が生じます。
これは、顧客への迅速な対応や緊急のデータ確認など、迅速な処理が求められる状況において特に不便です。

この記事では、kintoneのUIをJavaScriptでカスタマイズし、情報を複数のシートに分割して、ユーザーがExcelの複数シートのようにタブ切り替えを簡単に行える方法をご紹介します。
準備アプリ
「契約・支払・サポート管理」という名前のアプリを作成し、以下のデータ項目を含めます。
| フィールド名 | フィールドコード | データ形式 | 説明 |
| 契約番号 | 契約番号 | 文字列(1行) | 契約を識別するための番号 |
| 顧客名 | 顧客名 | 文字列(1行) | 契約先の顧客名 |
| 契約期間(開始) | 契約期間_開始 | 日付 | 契約の開始日 |
| 契約期間(終了) | 契約期間_終了 | 日付 | 契約の終了日 |
| サービス種別 | サービス種別 | ドロップダウンリスト | 提供するサービスの種類 (例:保守サポート、ソフトウェア開発、導入コンサル) |
| 契約担当者 | 契約担当者 | 文字列(1行) | 契約を担当する社員 |
| ? 支払い一覧(テーブル) | 支払い一覧 | テーブル | 各支払いの詳細リスト |
| └ 支払期 | 支払期 | 数値 | 支払いの回数や月次の番号 |
| └ 金額(円) | 金額 | 数値(通貨) | 各回の支払金額 |
| └ 支払期日 | 支払期日 | 日付 | 支払いの期限日 |
| └ ステータス | 支払ステータス | ドロップダウンリスト | 各回の支払いの状態(例:未入金、入金済み、支払遅延) |
| ? サポート対応履歴(テーブル) | サポート対応履歴 | テーブル | 顧客からのサポート依頼の履歴 |
| └ 対応日 | 対応日 | 日付 | サポート対応を行った日付 |
| └ 問い合わせ内容 | 問い合わせ内容 | 文字列(1行) | 顧客からの問い合わせ内容 |
| └ 担当者 | 対応担当者 | 文字列(1行) | 問い合わせに対応した社員 |
| └ 対応結果 | 対応結果 | ドロップダウンリスト | 対応の結果(例:完了、保留中、再対応) |
JavaScriptプログラム
※「JSEdit for kintone」プラグインを使用して、ソースコードを編集します。JSEdit for kintone の画面から jQuery と jQuery UI を追加・インストールすることで、HTML 要素のコーディングがより簡単になります。
プログラムの処理手順
- データ項目の fieldCode と対応するHTML要素の fieldId のマッピング一覧を作成します。 kintoneのすべての画面でHTML要素を取得する方法については、私の以前の記事を参考してください。
参考; kintoneの新規・編集レコード画面のフィールドをカスタマイズする裏技 - 「契約情報」「支払い一覧」「サポート対応履歴」の3つのシートに表示項目をグループ分けしています。
- 「契約情報」 シートには、契約番号、顧客名、契約期間(開始・終了)、サービス種別、契約担当者など、契約に関する基本情報をまとめています。
- 「支払い一覧」 シートには、支払期、金額、支払期日、ステータスなどの情報を含むテーブル「支払い一覧」を表示します。
- 「サポート対応履歴」 シートには、対応日、問い合わせ内容、担当者、対応結果などを記録するテーブル「サポート対応履歴」を表示します。
- 各シートを切り替えるためのタブ(ボタン)を画面上に作成します。
- ボタンを押すと、他の項目は隠して、対応するシートの項目だけを表示するようにします。
- 初期表示では「契約情報」のシートだけを表示して、他の情報は非表示にします。
(($) => {
// =============================
// 初期設定 - フィールドコードとIDのマッピング
// =============================
const FORM_DATA = cybozu.data.page['FORM_DATA'];
const ELEMENT_FIELD_ID = {};
// テーブルフィールドのマッピング
for (const fieldId of Object.keys(FORM_DATA.schema.table.fieldList)) {
const field = FORM_DATA.schema.table.fieldList[fieldId];
ELEMENT_FIELD_ID[field.var] = fieldId;
}
// テーブルのマッピング
for (const subTableId of Object.keys(FORM_DATA.schema.subTable)) {
const subTable = FORM_DATA.schema.subTable[subTableId];
ELEMENT_FIELD_ID[subTable.var] = subTableId;
}
// =============================
// シート定義
// =============================
const SHEETS = {
契約情報: [
"契約番号",
"顧客名",
"契約期間_開始",
"契約期間_終了",
"サービス種別",
"契約担当者"
],
支払い一覧: [
"支払い一覧"
],
サポート対応履歴: [
"サポート対応履歴"
]
};
// =============================
// レコードの作成・編集・詳細画面にタブ切り替え機能を適用します。
// =============================
kintone.events.on([
'app.record.create.show',
'app.record.edit.show',
'app.record.detail.show'
], (event) => {
// 各コンテナ作成
const container = {
契約情報: $('<div></div>'),
支払い一覧: $('<div></div>'),
サポート対応履歴: $('<div></div>')
};
// タブボタン共通スタイル
const buttonStyle = `
padding: 8px 16px;
margin-right: 4px;
cursor: pointer;
border: 1px solid #ccc;
border-bottom: none;
background-color: #f4f4f4;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
`;
// 各シートのボタン作成
const buttons = {};
for (const sheetName of Object.keys(SHEETS)) {
buttons[sheetName] = $('<button>', {
text: sheetName,
style: buttonStyle
});
}
// ボタンとコンテナの追加
const buttonContainer = $('<div style="margin-left: 20px"></div>');
Object.values(buttons).forEach(btn => buttonContainer.append(btn));
$('#record-gaia').prepend(buttonContainer);
Object.values(container).forEach(c => $('#record-gaia').append(c));
// =============================
// 表示制御関数群
// =============================
// 全フィールド非表示 & ボタンフォント初期化
function hideAllFields() {
Object.values(buttons).forEach(btn => btn.css("font-weight", ""));
const allFields = Object.values(SHEETS).flat();
for (const field of allFields) {
const fieldId = ELEMENT_FIELD_ID[field];
$(`.field-${fieldId}, .subtable-row-${fieldId}`).hide();
}
}
// 対象シートのフィールド表示 & ボタン強調
function showSheet(sheetName) {
buttons[sheetName].css("font-weight", "bold");
for (const field of SHEETS[sheetName]) {
const fieldId = ELEMENT_FIELD_ID[field];
$(`.field-${fieldId}, .subtable-row-${fieldId}`).show();
}
}
// ボタンクリックイベント登録
for (const sheetName of Object.keys(SHEETS)) {
buttons[sheetName].on('click', () => {
hideAllFields();
showSheet(sheetName);
});
}
// 初期表示
hideAllFields();
showSheet("契約情報");
return event;
});
})(jQuery);
結果
記事を読んでいただきありがとうございます。
次の記事をお楽しみにお待ちください !

