備忘ですが、

誰かの役に立てばいい

【kintone】で宛名ラベルを作ってみる

やりたいこと→kintoneに作った住所録を使って、ワンクリックで宛名ラベル印刷画面を生成する。

世の中にはプリントクリエイターってプラグインがあるじゃろがいって言われそう。

(2018/12/24 一覧表示の方法をVueからHandsontableへ変更)

ライブラリとか

  • handsontable.full.min.js、handsontable.full.min.css (ver 6.2.0)
  • underscore-min.js

(CybozuCDNより)

  • カスタマイズビューを使用(ページネーションを表示するはオフ)

HTML

<div id="my-customized-view" ></div>
 <!-- 子ウィンドウHTML -->
<textarea id="TB" style="display: none;">
  <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>印刷画面</title>
      <style type="text/css">
        @page { margin: 0 }
        body { margin: 0 }
        .sheet {
        margin: 0;
        overflow: hidden;
        position: relative;
        box-sizing: border-box;
        page-break-after: always;
        }
        /** Paper sizes **/
        body.A4 .sheet { width: 210mm; height: 296mm }
        /** Padding area **/
        .sheet.padding-10mm { padding: 10mm }
        .sheet.padding-15mm { padding: 15mm }
        .sheet.padding-20mm { padding: 20mm }
        .sheet.padding-25mm { padding: 25mm }
        /** For screen preview **/
        @media screen {
          body { background: #e0e0e0 }
          .sheet {
          background: white;
          box-shadow: 0 .5mm 2mm rgba(0,0,0,.3);
          margin: 5mm;
          }
        }
        /** Fix for Chrome issue #273306 **/
        @media print {
          body.A4 { width: 210mm }
        }
        /** 印刷画面 **/
        table.print {
        position: absolute;
        top: 50%;
        left: 50%;
        width: 172.8mm;
        height: 254.6mm;
        margin: -127.3mm 0 0 -86.4mm;
        border-collapse: collapse;
        font-family: "MS 明朝";
        }
        table.print td {
        border: 1px solid;  /** 使用時は消す **/
        border-color: black;  /** 使用時は消す **/
        border-collapse: collapse;
        position: relative;
        width: 86.4mm;
        height: 42.3mm;
        }
        table.print p{
        position: relative;
        width: 75.0mm;
        margin: 0;
        left: 5mm;
        }
        table.print p.zipcode { font-size: 11pt; }
        table.print p.address1 { font-size: 11pt; }
        table.print p.address2 { font-size: 11pt; }
        table.print p.client { font-size: 11pt; }
        table.print p.namae { font-size: 13pt; }
        table.print br { line-height: 6pt; }
      </style>
    </head>

    <body class="A4">
      <section class="sheet" id="originSheet">
        <table class="print">
          <tbody>
            <tr>
              <td>
                <div class="data">
                  <!-- 開発時確認用 -->
                  <p class="zipcode">123-4567</p>
                  <p class="address1">○○県○○○市○○○1-1-1</p>
                  <p class="address2">△△△△△△ビル</p>
                  <br>
                  <p class="client">株式会社サンプルホールディングス</p>
                  <p class="namae">代表取締役 サンプル 太郎 様</p>
                </div>
              </td>
              <td></td>
            </tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
          </tbody>
        </table>
      </section>
    </body>
  </html>
</textarea>

JavaScript

(function() {
 "use strict";
  kintone.events.on("app.record.index.show", function(event) {
   if (event.viewId !== 一覧IDを入れる) return event;
        
   const fields = ['役職','氏名','会社名','郵便番号','住所1','住所2'];

   function fetch(opt_offset, opt_records) {
    var offset = opt_offset || 0;
    var records = opt_records || [];
    var appId = kintone.app.getId();
    var query = kintone.app.getQueryCondition();
    var query2 = kintone.app.getQuery();
    var sort = query2.split(/by|limit/);
    var params = {
     app: appId,
     query: query + ' order by' + sort[1] + ' limit 500 offset ' + offset
    };
    return kintone.api('/k/v1/records', 'GET', params).then(function(resp) {
     records = records.concat(resp.records);
     if (resp.records.length === 500) {
      return fetch(offset + 500, records);
     }
     return records;
    });
   }
        
   var TB = document.getElementById("TB").value;
   function show(datalist){
    var obj = window.open();
    obj.document.open();
    obj.document.write(TB);
    obj.document.close();
    // 12枚を超える場合はページを増やす
    var sheets = Math.ceil(datalist.length / 12);
    _.times(sheets - 1, function () {
     var clone = obj.document.getElementById('originSheet').cloneNode(true);
     obj.document.body.appendChild(clone);
    });
    // underscore.js のテンプレート機能でテーブルのセルにデータを流し込む
    var compiled = _.template(
     '<div class="data"><p class="zipcode"><%= data.zipcode %></p>' +
     '<p class="address1"><%= data.address1 %></p>' +
     '<p class="address2"><%= data.address2 %></p>' + '<br>' +
     '<p class="client"><%= data.client %></p>' +
     '<p class="namae"><%= data.namae %> 様</p></div>');
    var cells = obj.document.getElementsByTagName('td');
    _.each(datalist, function (value, key) {
     cells[key].innerHTML = compiled({"data": value});
    });
    obj;
   }

   fetch().then(function(myrecords) {
    var records = myrecords;
    function allprint(records) {
     var datalist = [];
     for (let i = 0; i < records.length; i++) {
      let listrow = {
       "namae": records[i][fields[0]].value +' '+ records[i][fields[1]].value,
       "client": records[i][fields[2]].value,
       "zipcode": records[i][fields[3]].value,
       "address1": records[i][fields[4]].value,
       "address2": records[i][fields[5]].value
      };
      datalist.push(listrow);
     }
     show(datalist);
    }

    var button = document.createElement("button");
    button.id = 'my_index_button';
    button.textContent = "表示レコードを1ラベルずつ作成";
    button.addEventListener("click", function() {
     allprint(records);
    });

    var label = document.createElement('label');
    label.appendChild(button);
    label.appendChild(document.createTextNode(" 表示件数:"+ records.length + " 件"));
    if (document.getElementById('my_index_button') !== null) {
     kintone.app.getHeaderMenuSpaceElement().textContent = null;
    }
    kintone.app.getHeaderMenuSpaceElement().appendChild(label);

    //Handsontableで表と行ボタン作成
    var container = document.getElementById('my-customized-view');
    var columns = [];
    for(let i = 0;i < fields.length; i++){
     columns[i] = {data: fields[i] + '.value', title: fields[i], editor: false};
    }
    for(let i =0; i<records.length; i++) {
     let record = records[i];
     record["リンク用HTML"] = '<a href="/k/' + kintone.app.getId() + '/show#record=' + record.$id.value + '">' +
     record.$id.value +'</a>';
    }
    columns.unshift({data: "リンク用HTML", renderer: 'html', title:'レコード番号', editor: false});
            
    function showbutton(instance, td , row, col, prop, value, cellProp){
     let button = document.createElement("button");
     button.textContent = "作成";
     button.addEventListener("click", function() {
      var datalist = [];
      var record = cellProp.instance.getSourceDataAtRow(row);
      for(let i =0;i < 12; i++){
       let listrow = {
        "namae": record[fields[0]].value +' '+ record[fields[1]].value,
        "client": record[fields[2]].value,
        "zipcode": record[fields[3]].value,
        "address1": record[fields[4]].value,
        "address2": record[fields[5]].value
       };
       datalist.push(listrow);
      }
      show(datalist);
     });
     td.innerHTML = '';
     td.appendChild(button);
    }
    columns.unshift({data: "ボタン", renderer: showbutton, title: 'ラベル', editor: false});
            
    var hot = new Handsontable(container, {
     data: records,
     minSpareRows: 0,
     contextMenu: false,
     fillHandle: false,
     columns: columns
    });
   });
  });
})();

bibouroq.hatenablog.com
bibouroq.hatenablog.com

出来上がり

個人情報は架空のダミーデータ。

絞り込みレコードを全てラベル化
f:id:bibouroq:20181217042450p:plain
1レコードのみラベル化
f:id:bibouroq:20181217042521p:plain