6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめに

kintone Advent Calendar 2022 14日目の記事です。

さて、12月14日といえば、忠臣蔵です!
ということで、忠臣蔵にひっかけたkintoneカスタマイズをしてみました!
(※旧暦の12月14日なので、今の時代だと1月30日になるそうですが…)

デモ

こんなアプリをつくってみました!
吉良邸屋敷図の図面上に設置したボタンから、各義士の配置をセットできるカスタマイズです。
標準の入力フィールドよりも、直感的に入力をすることが可能となります。
demo.gif

要件と実現方法

  1. 隊編成アプリの入力画面に、吉良邸屋敷図を表示する。
    • スペースフィールドを設置し、そこに吉良邸屋敷図の画像を表示させる。
  2. 吉良邸屋敷図上における隊の配置場所にボタンを設置する。
    • cssによるスタイリングを施し、吉良邸屋敷図画像の上にボタンを設置する。
  3. ボタンを押したとき、隊に関する情報を各フィールドにセットする。
    • 設置したボタンにクリックイベントを追加し、ボタン押下でフィールドに値をセットする。
  4. 登録済みのメンバーの配置がわかるようにする。
    • 関連レコードにて対応する。

作り方

吉良邸屋敷図を作成

Wordで吉良邸屋敷図を作成し、画像で保存しました。1
map.png

kintoneアプリ作成

まず、土台となるアプリを作成します。
アプリを作成した後に、kintone REST API をたたいて、取得したフォーム設計情報を記載します。
※今回作成した隊編成アプリIDは203。名前フィールドの値は、アプリID204のマスタ用のアプリからルックアップで取得する構成です。

kintone.api(kintone.api.url('/k/v1/form.json', true), 'GET', { app: 203}, (resp) => { console.log(resp) });
resp
{
    "properties": [
        {
            "type": "SINGLE_LINE_TEXT",
            "label": "名前",
            "noLabel": "false",
            "code": "名前",
            "required": "false",
            "relatedApp": "204"
        },
        {
            "type": "DROP_DOWN",
            "label": "エリア",
            "noLabel": "false",
            "code": "エリア",
            "required": "false",
            "options": [
                "表門",
                "裏門"
            ],
            "defaultValue": null
        },
        {
            "type": "DROP_DOWN",
            "label": "役割",
            "noLabel": "false",
            "code": "役割",
            "required": "false",
            "options": [
                "司令",
                "屋内討入",
                "屋外",
                "場の内"
            ],
            "defaultValue": null
        },
        {
            "type": "SINGLE_LINE_TEXT",
            "label": "隊",
            "noLabel": "false",
            "code": "隊",
            "required": "false",
            "minLength": null,
            "maxLength": null,
            "expression": "",
            "hideExpression": "false",
            "unique": "false",
            "defaultValue": ""
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "司令部",
            "noLabel": "false",
            "code": "表門隊_司令部",
            "relatedApp": "203"
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "屋内討入隊",
            "noLabel": "false",
            "code": "表門隊_屋内討入隊",
            "relatedApp": "203"
        },
        {
            "type": "SINGLE_LINE_TEXT",
            "label": "プロジェクト名",
            "noLabel": "false",
            "code": "プロジェクト名",
            "required": "false",
            "minLength": null,
            "maxLength": null,
            "expression": "",
            "hideExpression": "false",
            "unique": "false",
            "defaultValue": "吉良邸討入"
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "場の内隊",
            "noLabel": "false",
            "code": "表門隊_場の内隊",
            "relatedApp": "203"
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "屋外隊",
            "noLabel": "false",
            "code": "表門隊_屋外隊",
            "relatedApp": "203"
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "司令部",
            "noLabel": "false",
            "code": "裏門隊_司令部",
            "relatedApp": "203"
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "屋内討入隊",
            "noLabel": "false",
            "code": "裏門隊_屋内討入隊",
            "relatedApp": "203"
        },
        {
            "type": "REFERENCE_TABLE",
            "label": "屋外隊",
            "noLabel": "false",
            "code": "裏門隊_屋外隊",
            "relatedApp": "203"
        },
        {
            "type": "NUMBER",
            "label": "#",
            "noLabel": "false",
            "code": "ナンバー",
            "required": "false",
            "minValue": null,
            "maxValue": null,
            "digit": "false",
            "unique": "false",
            "defaultValue": null,
            "displayScale": null,
            "unit": null,
            "unitPosition": "BEFORE"
        },
        {
            "type": "SPACER",
            "elementId": "sp_map"
        }
    ]
}

コーディング

JavaScript

index.js
(() => {
  
  'use strict';

  // ================================================
  // EVENT : create.show, edit.show
  // ================================================
  kintone.events.on(['app.record.create.show', 'app.record.edit.show'], event => {

    const record = event.record;

    const imgMapUrl = 'https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953166/06cfac37-4c1e-2b8b-4ee9-8c8583a6c14f.png';
    const spMap = kintone.app.record.getSpaceElement('sp_map');

    const divMap = document.createElement('div');
    divMap.className = 'hazime__p-container__content';
    spMap.appendChild(divMap);

    const imgMap = document.createElement('img');
    imgMap.src = imgMapUrl;
    imgMap.className = 'hazime__c-media__image__map';
    divMap.appendChild(imgMap);

    // *********************
    // 表門隊
    // *********************

    // 司令部
    const btnFrontGateHeadquarters = document.createElement('button');
    btnFrontGateHeadquarters.textContent = '司令部';
    btnFrontGateHeadquarters.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__headerquarters';

    btnFrontGateHeadquarters.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '表門';
      rec.record.役割.value = '司令';
      rec.record..value = '表門隊_司令部';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnFrontGateHeadquarters);

    // 屋内討入隊
    const btnFrontGateIndoorAttack = document.createElement('button');
    btnFrontGateIndoorAttack.textContent = '屋内討入隊';
    btnFrontGateIndoorAttack.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__indoor-attack';

    btnFrontGateIndoorAttack.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '表門';
      rec.record.役割.value = '屋内討入';
      rec.record..value = '表門隊_屋内討入隊';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnFrontGateIndoorAttack);

    // 場の内隊
    const btnFrontGateInside = document.createElement('button');
    btnFrontGateInside.textContent = '場の内隊';
    btnFrontGateInside.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__inside';

    btnFrontGateInside.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '表門';
      rec.record.役割.value = '場の内';
      rec.record..value = '表門隊_場の内隊';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnFrontGateInside);

    // 屋外隊
    const btnFrontGateOutdoor = document.createElement('button');
    btnFrontGateOutdoor.textContent = '屋外隊';
    btnFrontGateOutdoor.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__outdoor';

    btnFrontGateOutdoor.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '表門';
      rec.record.役割.value = '屋外';
      rec.record..value = '表門隊_屋外隊';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnFrontGateOutdoor);


    // *********************
    // 裏門隊
    // *********************

    // 司令部
    const btnBackGateHeadquarters = document.createElement('button');
    btnBackGateHeadquarters.textContent = '司令部';
    btnBackGateHeadquarters.className = 'hazime__c-button__back-gate hazime__c-button__back-gate__headerquarters';

    btnBackGateHeadquarters.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '裏門';
      rec.record.役割.value = '司令';
      rec.record..value = '裏門隊_司令部';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnBackGateHeadquarters);

    // 屋内討入隊
    const btnBackGateIndoorAttack = document.createElement('button');
    btnBackGateIndoorAttack.textContent = '屋内討入隊';
    btnBackGateIndoorAttack.className = 'hazime__c-button__back-gate hazime__c-button__back-gate__indoor-attack';

    btnBackGateIndoorAttack.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '裏門';
      rec.record.役割.value = '屋内討入';
      rec.record..value = '裏門隊_屋内討入隊';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnBackGateIndoorAttack);

    // 屋外隊
    const btnBackGateOutdoor = document.createElement('button');
    btnBackGateOutdoor.textContent = '屋外隊';
    btnBackGateOutdoor.className = 'hazime__c-button__back-gate hazime__c-button__back-gate__outdoor';

    btnBackGateOutdoor.addEventListener('click', () => {
      let rec = kintone.app.record.get();
      rec.record.エリア.value = '裏門';
      rec.record.役割.value = '屋外';
      rec.record..value = '裏門隊_屋外隊';
      kintone.app.record.set(rec);
    });

    divMap.appendChild(btnBackGateOutdoor);


    return event;

  });


  // ================================================
  // EVENT : detail.show
  // ================================================

  kintone.events.on(['app.record.detail.show'], event => {

    const record = event.record;

    // 全配員表示用のレコードのときは、以下のフィールドを非表示
    if (record.名前.value === '全配員表示用') {
      kintone.app.record.setFieldShown('名前', false);
      kintone.app.record.setFieldShown('エリア', false);
      kintone.app.record.setFieldShown('役割', false);
      kintone.app.record.setFieldShown('', false);
      kintone.app.record.setFieldShown('ナンバー', false);
    }

    const imgMapUrl = 'https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953166/06cfac37-4c1e-2b8b-4ee9-8c8583a6c14f.png';
    const spMap = kintone.app.record.getSpaceElement('sp_map');

    const divMap = document.createElement('div');
    divMap.className = 'hazime__p-container__content';
    spMap.appendChild(divMap);

    const imgMap = document.createElement('img');
    imgMap.src = imgMapUrl;
    imgMap.className = 'hazime__c-media__image__map';
    divMap.appendChild(imgMap);

    // *********************
    // 表門隊
    // *********************

    // 司令部
    const btnFrontGateHeadquarters = document.createElement('button');
    btnFrontGateHeadquarters.disabled = true;
    btnFrontGateHeadquarters.textContent = '司令部';
    btnFrontGateHeadquarters.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__headerquarters';

    if (record..value === '表門隊_司令部') {
      btnFrontGateHeadquarters.classList.add('hazime__c-button__his-placement');
    }

    // btnFrontGateHeadquarters.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnFrontGateHeadquarters);

    // 屋内討入隊
    const btnFrontGateIndoorAttack = document.createElement('button');
    btnFrontGateIndoorAttack.disabled = true;
    btnFrontGateIndoorAttack.textContent = '屋内討入隊';
    btnFrontGateIndoorAttack.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__indoor-attack';

    if (record..value === '表門隊_屋内討入隊') {
      btnFrontGateIndoorAttack.classList.add('hazime__c-button__his-placement');
    }

    // btnFrontGateIndoorAttack.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnFrontGateIndoorAttack);

    // 場の内隊
    const btnFrontGateInside = document.createElement('button');
    btnFrontGateInside.disabled = true;
    btnFrontGateInside.textContent = '場の内隊';
    btnFrontGateInside.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__inside';

    if (record..value === '表門隊_場の内隊') {
      btnFrontGateInside.classList.add('hazime__c-button__his-placement');
    }

    // btnFrontGateInside.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnFrontGateInside);

    // 屋外隊
    const btnFrontGateOutdoor = document.createElement('button');
    btnFrontGateOutdoor.disabled = true;
    btnFrontGateOutdoor.textContent = '屋外隊';
    btnFrontGateOutdoor.className = 'hazime__c-button__front-gate hazime__c-button__front-gate__outdoor';

    if (record..value === '表門隊_屋外隊') {
      btnFrontGateOutdoor.classList.add('hazime__c-button__his-placement');
    }

    // btnFrontGateOutdoor.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnFrontGateOutdoor);


    // *********************
    // 裏門隊
    // *********************


    // 司令部
    const btnBackGateHeadquarters = document.createElement('button');
    btnBackGateHeadquarters.disabled = true;
    btnBackGateHeadquarters.textContent = '司令部';
    btnBackGateHeadquarters.className = 'hazime__c-button__back-gate hazime__c-button__back-gate__headerquarters';

    if (record..value === '裏門隊_司令部') {
      btnBackGateHeadquarters.classList.add('hazime__c-button__his-placement');
    }

    // btnBackGateHeadquarters.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnBackGateHeadquarters);

    // 屋内討入隊
    const btnBackGateIndoorAttack = document.createElement('button');
    btnBackGateIndoorAttack.disabled = true;
    btnBackGateIndoorAttack.textContent = '屋内討入隊';
    btnBackGateIndoorAttack.className = 'hazime__c-button__back-gate hazime__c-button__back-gate__indoor-attack';

    if (record..value === '裏門隊_屋内討入隊') {
      btnBackGateIndoorAttack.classList.add('hazime__c-button__his-placement');
    }

    // btnBackGateIndoorAttack.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnBackGateIndoorAttack);

    // 屋外隊
    const btnBackGateOutdoor = document.createElement('button');
    btnBackGateOutdoor.disabled = true;
    btnBackGateOutdoor.textContent = '屋外隊';
    btnBackGateOutdoor.className = 'hazime__c-button__back-gate hazime__c-button__back-gate__outdoor';
    btnBackGateOutdoor.disabled === true;

    if (record..value === '裏門隊_屋外隊') {
      btnBackGateOutdoor.classList.add('hazime__c-button__his-placement');
    }

    // btnBackGateOutdoor.addEventListener('click', () => {
    //   let rec = kintone.app.record.get();

    //   kintone.app.record.set(rec);
    // });

    divMap.appendChild(btnBackGateOutdoor);


    return event;

  });

})();

SCSS

今回のスタイリングについては、SassのSCSS記法を使っています。コンパイルしたCSSファイルをkintoneに適用します。(今回は、VS Codeの拡張機能であるLive Sass Compiler でコンパイルしました。)

scss/style.scss
// ==========================================================================
// Object
// ==========================================================================

// -----------------------------------------------------------------
// Component
// -----------------------------------------------------------------

@import "object/component/_button";
@import "object/component/_media";

// -----------------------------------------------------------------
// Project
// -----------------------------------------------------------------

@import "object/project/_container";

scss/object/component/_button
$cButtonColorFrontGate: rgb(241, 184, 231);
$cButtonColorBackGate: rgb(187, 189, 255);
$cButtonColorHover: rgb(161, 254, 145);
$cButtonHisPlacement: rgb(255, 0, 0);
$cButtonRadius: 10px;

.hazime__c-button {

  // 表門隊
  &__front-gate {

    position: absolute;
    border-radius: $cButtonRadius;
    background-color: $cButtonColorFrontGate;

    &:hover {
      background-color: $cButtonColorHover;
    }

    &:disabled {
      background-color: $cButtonColorFrontGate;
      cursor: default;
    }

    // 司令部
    &__headerquarters {
      top: 245px;
      left: 880px;
    }

    // 屋内討入隊
    &__indoor-attack {
      top: 280px;
      left: 800px;
    }

    // 場の内隊
    &__inside {
      top: 255px;
      left: 690px;
    }

    // 屋外隊
    &__outdoor {
      top: 340px;
      left: 680px;
    }

  }

  // 裏門隊
  &__back-gate {

    position: absolute;
    border-radius: $cButtonRadius;
    background-color: $cButtonColorBackGate;

    &:hover {
      background-color: $cButtonColorHover;
    }

    &:disabled {
      background-color: $cButtonColorBackGate;
      cursor: default;
    }

    // 司令部
    &__headerquarters {
      top: 110px;
      left: 150px;
    }

    // 屋内討入隊
    &__indoor-attack {
      top: 245px;
      left: 185px;
    }

    // 屋外隊
    &__outdoor {
      top: 355px;
      left: 350px;
    }

  }

  // 詳細画面で配置されている隊
  &__his-placement {
    background-color: $cButtonHisPlacement;
    font-weight: bold;
    color: rgb(255, 255, 255);

    &:disabled {
      background-color: $cButtonHisPlacement;
    }
  }

}
scss/object/component/_media
.hazime__c-media {

  &__image__map {
    width: 1000px;
  }

}
scss/object/project/_container
.hazime__p-container{

  &__content {
    position: relative;
  }
  
}
  1. 参考文献:吉良邸討入(元禄赤穂事件) | akoinfo.com

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?