2020年2月3日月曜日

kintoneのデータをスプレッドシートで編集する(Tabulator編)

最近、kintoneでエンジニアの工数管理みたいなことをしている。
工数データをスプレッドシート型のUIでサクサク入力したくて、HandsontablesTabulatorの両方を試してみた。
Handsontablesを使う方法は、cybozu developer network上にHandsontableを使ってkintoneをExcelライクに入力しよう その1という記事が載っていたりするのだけど、Handsontable自体が最近有償化されてしまったのと、いろいろ実装がうまくいかないところがあって、断念。
一方のTabulatorは、そこそこいい感じに動いているので、備忘のためまとめておく。
ちなみに、今回はレコードの追加や複製、削除には対応していない。あくまで既存のレコードの編集のみ実装している(追加や複製はそのうちやるかも)。
要件(やりたいこと)
kintoneの工数管理アプリに、以下のような案件別・担当者別の日々の工数情報が入っているとする。

プロジェクト番号客先名案件名担当者名年月1日工数2日工数31日工数
12345678○×商事株式会社メールシステム構築山田太郎2020/010.520
12345678○×商事株式会社メールシステム構築鈴木花子2020/01030.5
23456789▲△自動車株式会社仮想化基盤構築田中勘太郎2020/012.51.57

これをスプレッドシートで入力・編集したい。下に合計行もあるとうれしい。
準備①:kintoneの"一覧"を準備
kintoneの「アプリの設定」画面にて、一覧"Tabulator"を作成(※好きな名前でよい)。
レコード一覧の表示形式を「カスタマイズ」にして、以下のようなHTMLを設定しておく。
準備②:Javascriptの準備
kintoneの「アプリの設定」画面の「JavaScript / CSSでカスタマイズ」にて、以下をリンクしておく。
PC用のJavaScriptファイル
https://js.cybozu.com/jquery/3.4.0/jquery.min.js
https://unpkg.com/tabulator-tables@4.5.3/dist/js/tabulator.min.js
PC用のCSSファイル
https://unpkg.com/tabulator-tables@4.5.3/dist/css/tabulator.min.css
これで準備OK。
サンプルコード
以下のコードを「PC用のJavaScriptファイル」にアップロードする。
/**
 * kintone_tabulator.js
 * kintone上でスプレッドシートによるデータ表示・更新を実現
 * Tabulatorにて実装
 * 
 * @author dsp74118
 * @version 1.0 2020.02.03
 *
 */
(function() {

  "use strict";

  // レコード更新用データを生成する関数
  var setParams = function(record) {
    var result = {};
    for (var prop in record) {
      // note: アップデートしないフィールドを除外する処理をここに入れるとよい
      result[prop] = record[prop];
    }
  }

  // 一覧ビュー表示用のイベントハンドラ
  kintone.events.on(['app.record.index.show'], function(event) {
    // 「Tabulator」一覧のみ処理
    if (event.viewName !== "Tabulator") return;
    var records = event.records;

    // 列の定義
    var columns = [
      {title: "プロジェクト番号", field: "プロジェクト番号.value"},
      {title: "担当者氏名", field: "担当者氏名.value"},
      {title: "年月", field: "年月.value"},
      {title: "客先名", field: "客先名.value"},
      {title: "案件名", field: "案件名.value"},
    ];
    for (var i = 1; i <= 31; i++) {
      columns.push({
        title: i + "日工数",
        field: "_" + i + "日工数.value",
        align: "right",
        editor: true,
        bottomCalc: "sum",
        validator: [{type: validateManhours, parameters: {}}]
      });
    }

    // Tabulator初期化
    var tab = new Tabulator('#spreadsheet', {
//      layoutを指定指定すると表示が表示が崩れるためコメントアウト
//      layout: "fitData",
      data: records,
      // 左右矢印キーでセル移動
      keybindings:{
          "navLeft" : "37",
          "navRight" : "39",
      },
      columns: columns,

      // 更新処理
      cellEdited:function(cell) {
        console.log(cell);
        var id = cell._cell.row.data['レコード番号']['value'];

        // データを更新用に加工し配列に格納
        var updateRecords = [];
        updateRecords.push({
          id: id,
          record: setParams(cell._cell.row.data),
        });

        // 更新用Requestを作成
        var requests = [];
        requests.push({
          method: "PUT",
          api: "/k/v1/records.json",
          payload: {
            app: kintone.app.getId(),
            records: updateRecords
          }
        });

        // bulkrequestで一括で更新
        // 失敗した場合はロールバックされる
        var self = this;
        kintone.api('/k/v1/bulkRequest', 'POST', {requests: requests},
          function(resp) {
            console.dir(requests);
            console.dir(resp);
            console.log('Data updated successfully.');
            self.redraw();
          },
          function(resp) {
            console.dir(resp);
            console.log('Data update fail.');
          }
        );
      },
    });
  });
})();
event.recordsをそのままTabulatorに食わせられるので、コードがすごくシンプル! これはうれしい。
kintoneのデータ更新にはbulkrequestを使ったけど、複数セル同時編集はできないはずなので、bulkrequestではなく普通のrecords.jsonで良いかも。

注意点がいくつか。
  • layout: "fitData" で列幅を自動調整させると、横スクロール時に表示がめちゃくちゃ乱れるので、やめた。
  • 列の設定で frozen: true にて左側に特定列を固定すると、これまた横スクロール時に表示がめちゃくちゃ乱れるので、やめた。
このあたり、Tabulator側の問題な気がする(kintoneのCSSとの相性かも?)。まだ追及はできていない。
結果
こんな感じ。Excelと同等の使用感を再現することは無理だけど、矢印キーで縦横無尽に移動しながら、工数をExcel風に入力できるようにはなっている。
※このブログ記事用に別途アプリを作ることが難しい状況なので、実際に職場で作っているアプリのスクショにモザイクかけまくったものを掲載することをご容赦ください。
 ちなみにアプリは開発中のもので、データはテストデータです。悪しからず。