2025-04-04 00:22:14 +09:00
63 changed files with 11296 additions and 107 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2,14 +2,13 @@
"use strict";
const APP_ENV = env["0,1歳日誌出力用"];
kintone.events.on("app.record.index.show", (event) => {
const headerSpace = getHeaderSpace('single-label-line');
addApproveFlowAction();
kintone.events.on("app.record.detail.show", (event) => {
const headerSpace = getHeaderSpace('', true);
const elements = createBtnGroupArea('extract-action-area', '日誌作成', handleButtonClick, {
btnElId: 'extract-btn',
yearElId: 'extract-year',
monthElId: 'extract-month',
classElId: 'extract-classroom',
})
if (!elements) {
@@ -19,9 +18,104 @@
headerSpace.appendChild(elements['extract-action-area']);
});
function handleButtonClick(e, { year, month, className }) {
const fileName = getExcelName(APP_ENV, year + month + '_' + className + '組');
console.log(fileName);
async function handleButtonClick(e) {
loading(true, '帳票出力中...');
showError(false);
const api = new KintoneRestAPIClient();
// 本アプリからデータを読み取る
const record = kintone.app.record.get().record;
if (!record) {
// エラー
loading(false);
return e;
}
const yearStr = record['年'].value;
const monthInput = record['月'].value;
if (!yearStr || !monthInput) {
showError('年/月が設定されていません');
loading(false);
return e;
}
const monthStr = String(monthInput).padStart(2, '0');
const className = record['クラス'].value;
if (!className) {
showError('クラスが設定されていません');
loading(false);
return e;
}
const excelName = APP_ENV.excelName;
await createExcelAndDownload({
api,
excelName,
exportName: getExcelName(excelName, yearStr + monthStr + '_' + className + '組'),
bizLogic: writeExcel(record, className, getJapaneseEraDate(new Date(yearStr, monthInput - 1, 1))),
});
loading(false);
}
function writeExcel(record, className, { era, year, month }) {
return async (api, worksheet) => {
const baseCells = findCellsInfo(worksheet, ['園長', '出欠', '食事量', '睡眠時間', '1', '11', '21']);
fillApproveArea(baseCells, worksheet, record);
const totalPages = baseCells['出欠'].length;
for (let i = 0; i < totalPages; i++) {
updateCell(worksheet, { base: baseCells['出欠'][i], up: 1 }, era + ' ' + year + '年 ' + month + '月');
updateCell(worksheet, { base: baseCells['食事量'][i], up: 1 }, 'クラス ' + className);
updateCell(worksheet, { base: baseCells['睡眠時間'][i], up: 1 }, '園児名 ' + record['園児名'].value);
}
fillMainPage(baseCells, worksheet)
}
function fillMainPage(baseCells, worksheet) {
const records = record['帳票出力用_テーブル'].value.map((e) => e.value);
const inputMergedObject = getMergedInfo(worksheet, baseCells['1'][0]);
const inputMergedRowCnt = inputMergedObject.bottom - inputMergedObject.top + 1;
[[baseCells['1'][0], 1, 10], [baseCells['11'][0], 11, 10], [baseCells['21'][0], 21, 11]].forEach((obj) => {
const baseForDetail = obj[0];
const from = obj[1];
const count = obj[2];
const cols = getLabelColsMapping(worksheet, baseForDetail.row - 1, ['日付', '出欠', '降園', '体温', '食事量', '排便', '睡眠時間', '保護者から', '園での様子_伝達事項', '評価反省']);
let currentRow = baseForDetail.row;
for (let i = 0; i < count; i++) {
const index = from + i - 1;
const record = records[index];
if (!record) {
break;
}
const row = worksheet.getRow(currentRow);
cols.forEach(col => {
const base = { row: currentRow, col: col.index };
if (col.field === '体温') {
for (let j = 0; j < 3; j++) {
const val1 = record["検温時刻" + (j + 1)].value || ' : ';
const val2 = (record["体温" + (j + 1)].value || ' . ').padStart(4, ' ');
updateCell(worksheet, { base, down: j }, val1 + ' , ' + val2 + ' ℃');
}
} else if (col.field === '睡眠時間') {
for (let j = 0; j < 2; j++) {
const val1 = record["睡眠開始時間" + (j + 1)].value || ':  ';
const val2 = record["睡眠終了時間" + (j + 1)].value || '  :';
updateCell(worksheet, { base, down: j }, val1 + ' ' + val2);
}
} else {
updateCell(row, { base }, record[col.field].value);
}
});
currentRow += inputMergedRowCnt;
}
worksheet.getRow(currentRow - 1).addPageBreak();
});
}
}
})();

View File

@@ -0,0 +1,6 @@
(function () {
"use strict";
addApproveFlowAction(true);
})();

View File

@@ -0,0 +1,496 @@
/** @copyright 2018 Planning Village Corporation Co., Ltd. */
(function () {
'use strict';
// [IE11] Polyfill of Object.values
if (!Object.values) {
const ownKeys = (
(typeof Reflect === 'object' && typeof Reflect.ownKeys === 'function') ? Reflect.ownKeys :
// @ts-ignore
(typeof Object.getOwnPropertySymbols === 'function') ? (function (O) {
// @ts-ignore
return Object.getOwnPropertyNames(O).concat(Object.getOwnPropertySymbols(O));
}) :
Object.getOwnPropertyNames);
// @ts-ignore
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
// @ts-ignore
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
// @ts-ignore
const concat = Function.bind.call(Function.call, Array.prototype.concat);
//@ts-ignore
Object.values = function values(O) {
//@ts-ignore
return reduce(ownKeys(O), function (v, k) {
return concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : [])
}, [])
};
}
// [IE11] Polyfill of Number.isNaN
if (!Number.isNaN) {
Number.isNaN = function (value) {
return value !== null && (value != value || +value != value);
};
}
// [IE11] Polyfill of String.prototype.startsWith
if (!String.prototype.startsWith) {
Object.defineProperty(String.prototype, 'startsWith', {
//@ts-ignore
value: function (search, rawPos) {
var pos = rawPos > 0 ? rawPos | 0 : 0;
return this.substring(pos, pos + search.length) === search;
}
});
}
// [IE11] Polyfill of String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function (search, start) {
'use strict';
//@ts-ignore
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) {
start = 0;
}
return this.indexOf(search, start) !== -1;
};
}
// [IE11] Polyfill of Array.prototype.findIndex
if (!Array.prototype.findIndex) {
// @ts-ignore
Array.prototype.findIndex = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
//@ts-ignore
window.pvc = window.pvc || {};
//@ts-ignore
window.pvc.lib = window.pvc.lib || {};
//@ts-ignore
window.pvc.lib.exceljsUtil = (function () {
const exceljs = {
/**
* ワークブックの blob を読み取ります。
* @param {Blob} blob Blob
* @return {Promise<ExcelJS.Workbook>} ワークブック
*/
loadWorkbookBlob: function (blob) {
return new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function (e) {
// @ts-ignore
resolve(e.target.result);
};
reader.onerror = function (e) {
reject(e);
}
reader.onabort = function (e) {
reject(e);
}
reader.readAsArrayBuffer(blob);
}).then(function (arrayBuffer) {
// 非公式のメンバー load を使用
// @ts-ignore
return new ExcelJS.Workbook().xlsx.load(arrayBuffer);
});
},
/**
* ワークブックを Blob に変換します。
* @param {ExcelJS.Workbook} workbook ワークブック
* @return {Promise<Blob>} Blob
*/
saveWorkbookToBlob: function (workbook) {
// writeBuffer の戻り値 ExcelJS.Buffer はブラウザ環境では Uint8Array となる
return workbook.xlsx.writeBuffer().then(function (ua) {
return new Blob([ua], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
});
},
/**
* 行をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー元の開始行番号(1から開始)
* @param {number} [bottomRowNumber] コピー元の終了行番号(1から開始)。省略時は開始行番号と同じ値となります。
* @returns {pvc.lib.exceljsUtil.CopiedRowsInfo} 行のコピー情報
*/
copyRows: function (worksheet, topRowNumber, bottomRowNumber) {
const _bottomRowNumber = (bottomRowNumber == null ? topRowNumber : bottomRowNumber);
if (topRowNumber < 0 || _bottomRowNumber < topRowNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const rows = [];
for (let r = topRowNumber; r <= _bottomRowNumber; r++) {
const row = worksheet.getRow(r);
const cells = [];
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
rows.push({
cells: cells,
height: row.height,
// 非公式のメンバー style を使用
// @ts-ignore
style: row.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.top && merge.bottom <= _bottomRowNumber);
}).map(function (merge) {
return {
top: merge.top - topRowNumber,
right: merge.right,
bottom: merge.bottom - topRowNumber,
left: merge.left,
};
}));
return deepCopyObject({
rows: rows,
merges: merges
});
},
/**
* 指定された開始行を起点に、コピーした行を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
pasteRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRowNumber = topRowNumber + copiedRowsInfo.rows.length - 1;
if (topRowNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の行のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.bottom && merge.top <= bottomRowNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedRowsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
topRowNumber + mergeInfo.top,
mergeInfo.left,
topRowNumber + mergeInfo.bottom,
mergeInfo.right);
});
// セルをペースト
copiedRowsInfo.rows.forEach(function (rowInfo, i) {
const row = worksheet.getRow(topRowNumber + i);
row.height = rowInfo.height;
// 非公式のメンバー style を使用
// @ts-ignore
row.style = rowInfo.style;
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
const cellInfo = rowInfo.cells[c - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始行を起点に、コピーした行を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
insertRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRows = (
topRowNumber <= worksheet.rowCount ?
exceljs.copyRows(worksheet, topRowNumber, worksheet.rowCount) :
null);
exceljs.pasteRows(worksheet, topRowNumber, copiedRowsInfo);
if (bottomRows) {
exceljs.pasteRows(worksheet, topRowNumber + copiedRowsInfo.rows.length, bottomRows);
}
},
/**
* 列をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー元の開始列の列番号(1から開始)または列ラベル。終了列の引数を省略して、代わりに "A:B" の形式でコピー元の列の範囲を指定することも可能。
* @param {number | string} [rightColumn] コピー元の終了列の列番号(1から開始)または列ラベル。省略時は開始列と同じとみなされます(開始列を "A:B" の形式で指定された場合を除く)。
* @returns {pvc.lib.exceljsUtil.CopiedColumnsInfo} 列のコピー情報
*/
copyColumns: function (worksheet, leftColumn, rightColumn) {
if (rightColumn == null) {
rightColumn = leftColumn;
if (typeof leftColumn === 'string') {
const sp = leftColumn.split(':');
if (sp.length === 2) {
leftColumn = sp[0];
rightColumn = sp[1];
}
}
}
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = ((typeof rightColumn === 'number') ? rightColumn : exceljs.columnLetterToColumnNumber(rightColumn));
if (leftColumnNumber < 0 || rightColumnNumber < leftColumnNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const columns = [];
for (let c = leftColumnNumber; c <= rightColumnNumber; c++) {
const column = worksheet.getColumn(c);
const cells = [];
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
columns.push({
cells: cells,
width: column.width,
style: column.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.left && merge.right <= rightColumnNumber);
}).map(function (merge) {
return {
top: merge.top,
right: merge.right - leftColumnNumber,
bottom: merge.bottom,
left: merge.left - leftColumnNumber,
};
}));
return {
columns: columns,
merges: merges
};
},
/**
* 指定された開始列を起点に、コピーした列を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedColumnsInfo 列のコピー情報
*/
pasteColumns: function (worksheet, leftColumn, copiedColumnsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = leftColumnNumber + copiedColumnsInfo.columns.length - 1;
if (leftColumnNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の列のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.right && merge.left <= rightColumnNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedColumnsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
mergeInfo.top,
leftColumnNumber + mergeInfo.left,
mergeInfo.bottom,
leftColumnNumber + mergeInfo.right);
});
// セルをペースト
copiedColumnsInfo.columns.forEach(function (columnInfo, i) {
const column = worksheet.getColumn(leftColumnNumber + i);
column.width = columnInfo.width;
column.style = columnInfo.style;
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(leftColumnNumber + i);
const cellInfo = columnInfo.cells[r - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始列を起点に、コピーした列を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedRowsInfo 列のコピー情報
*/
insertColumns: function (worksheet, leftColumn, copiedRowsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumns = (
leftColumnNumber <= worksheet.columnCount ?
exceljs.copyColumns(worksheet, leftColumnNumber, worksheet.columnCount) :
null);
exceljs.pasteColumns(worksheet, leftColumnNumber, copiedRowsInfo);
if (rightColumns) {
exceljs.pasteColumns(worksheet, leftColumnNumber + copiedRowsInfo.columns.length, rightColumns);
}
},
/**
* セルをクリアします。
* @param {ExcelJS.Cell} cell セル
*/
clearCell: function (cell) {
cell.value = null;
/// @ts-ignore
cell.style = cell._mergeStyle(cell._row.style, cell._column.style, {});
},
/**
* 列ラベルから列番号(1から開始)を取得します。
* @param {string} columnLetter 列ラベル
* @return {number} 列番号(1から開始)
*/
columnLetterToColumnNumber: function (columnLetter) {
const letter = columnLetter.toUpperCase();
const l = letter.length;
const charCodeA = 65;
let result = 0;
for (let i = 0, j = l - 1; i < l; i++, j--) {
result += (letter.charCodeAt(j) - charCodeA + 1) * (i === 0 ? 1 : Math.pow(26, i));
}
return result;
},
/**
* 日付のセルの値を作成します。
* @param {Date} date
* @returns {any}
*/
createDateCellValue: function (date) {
// UTC にしないと正しく Excel に反映されない
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
},
deepCopyObject: deepCopyObject,
};
/**
* ワークシートのマージを取得します。
* @param {ExcelJS.Worksheet} worksheet ワークシート
*/
function getWorksheetMerges(worksheet) {
// 非公式のメンバー _merges を使用
// @ts-ignore
const mergesDic = worksheet._merges;
return Object.keys(mergesDic).map(function (address) {
return mergesDic[address];
});
}
/**
* オブジェクのコピーします。ディープコピーとなります。
* @template T
* @param {T} obj
* @returns {T}
*/
function deepCopyObject(obj) {
return copyObject(obj);
//@ts-ignore
function copyObject(obj) {
return (
obj == null ? null :
Array.isArray(obj) ? copyArray(obj) :
isDate(obj) ? copyDate(obj) :
isDic(obj) ? copyDic(obj) :
obj);
}
//@ts-ignore
function copyArray(array) {
//@ts-ignore
return array.map(function (item) {
return copyObject(item);
});
}
//@ts-ignore
function isDate(obj) {
return (obj != null && Object.prototype.toString.call(obj) === '[object Date]');
}
//@ts-ignore
function copyDate(date) {
return (isNaN(date) ? null : new Date(date.getTime()));
}
//@ts-ignore
function isDic(obj) {
return (obj != null && typeof obj === 'object');
}
//@ts-ignore
function copyDic(dic) {
let result = {};
for (let key in dic) {
//@ts-ignore
result[key] = copyObject(dic[key]);
}
return result;
}
}
return exceljs;
}());
})();

3
src/2歳以上日誌/FileSaver.min.js vendored Normal file
View File

@@ -0,0 +1,3 @@
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)});
//# sourceMappingURL=FileSaver.min.js.map

File diff suppressed because one or more lines are too long

3
src/2歳以上日誌/exceljs.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2433
src/2歳以上日誌/kuc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -2,15 +2,13 @@
"use strict";
const APP_ENV = env["2歳以上日誌出力用"];
kintone.events.on("app.record.index.show", (event) => {
const headerSpace = getHeaderSpace('single-label-line');
addApproveFlowAction();
kintone.events.on("app.record.detail.show", (event) => {
const headerSpace = getHeaderSpace('', true);
const elements = createBtnGroupArea('extract-action-area', '日誌作成', handleButtonClick, {
btnElId: 'extract-btn',
yearElId: 'extract-year',
monthElId: 'extract-month',
dateElId: 'extract-date',
defaultThisMonth: true,
})
if (!elements) {
@@ -20,8 +18,117 @@
headerSpace.appendChild(elements['extract-action-area']);
});
function handleButtonClick(e, { year, month, date }) {
const fileName = getExcelName(APP_ENV, year + month + date);
console.log(fileName);
async function handleButtonClick(e) {
loading(true, '帳票出力中...');
showError(false);
const api = new KintoneRestAPIClient();
// 本アプリからデータを読み取る
const record = kintone.app.record.get().record;
if (!record) {
// エラー
loading(false);
return e;
}
const dateStr = record['登園日'].value;
if (!dateStr) {
showError('登園日が設定されていません');
loading(false);
return e;
}
const date = new Date(dateStr);
const excelName = APP_ENV.excelName;
await createExcelAndDownload({
api,
excelName,
exportName: getExcelName(excelName, record['学年'].value + '_' + getFormatDateString(date).replaceAll('-', '')),
bizLogic: writeExcel(record, getJapaneseEraDate(date)),
});
loading(false);
}
function writeExcel(record, { era, year, month, day }) {
return async (api, worksheet) => {
const baseCells = findCellsInfo(worksheet, ['園長', '行 事', '主な活動', 'クラス名']);
fillPage1(baseCells, worksheet);
fillPage2(baseCells, worksheet);
}
function fillPage1(baseCells, worksheet) {
fillApproveArea(baseCells, worksheet, record);
updateCell(worksheet, { base: { row: 1, col: 1 } }, `${era} ${year}年 ${month}月 ${day}日 ${record['曜日'].value}日 天候(${record['天気']?.value || '     '})`);
updateCell(worksheet, { base: baseCells['行 事'][0], right: 1 }, record['行事'].value);
const headerRow = baseCells['主な活動'][0].row;
const cols = getLabelColsMapping(worksheet, headerRow, ['活動', '子どもの様子', '反省評価']);
const row = headerRow + 1;
const inputMergedData = getMergedInfo(worksheet, { row, col: baseCells['主な活動'][0].col });
const inputMergedRowCnt = inputMergedData.bottom - inputMergedData.top + 1;
for (let i = 0; i < 4; i++) {
cols.forEach(col => {
updateCell(worksheet, { base: { row: row + i * inputMergedRowCnt, col: col.index } }, record[col.field + (i + 1)]?.value);
});
}
worksheet.getRow(baseCells['クラス名'][0].row - 2).addPageBreak();
}
function fillPage2(baseCells, worksheet) {
const baseForDetail = baseCells['クラス名'][0];
const records = record['園児別テーブル'].value.map((e) => e.value);
const pageSize = 10;
const totalPages = Math.ceil(records.length / pageSize);
const inputMergedObject = getMergedInfo(worksheet, { row: baseForDetail.row + 1, col: baseForDetail.col });
const inputMergedRowCnt = inputMergedObject.bottom - inputMergedObject.top + 1;
// make new copy
if (totalPages > 1) {
const lastPage = 1;
const copyPageRowStart = baseForDetail.row - 1;
const copyPageRowEnd = baseForDetail.row + (pageSize * inputMergedRowCnt);
createCopyFromTemplate(worksheet, {
startPage: lastPage + 1,
totalPages,
copyPageRowStart,
copyPageRowEnd,
callback: (newPage, rowCount) => {
['クラス名'].forEach((label) => {
const last = baseCells[label][newPage - 2];
baseCells[label].push({
col: last.col,
row: last.row + rowCount
});
});
}
});
}
const cols = getLabelColsMapping(worksheet, baseForDetail.row, ['クラス名', '名前', '子どもの様子', '反省評価_園児別テーブル']);
for (let i = 0; i < totalPages; i++) {
const childLabelCell = baseCells['クラス名'][i];
let currentRow = childLabelCell.row + 1;
for (let j = 0; j < pageSize; j++) {
const index = i * pageSize + j;
const record = records[index];
if (!record) {
break;
}
const row = worksheet.getRow(currentRow);
cols.forEach(col => {
updateCell(row, { base: { row: currentRow, col: col.index } }, record[col.field].value);
});
currentRow += inputMergedRowCnt;
}
worksheet.getRow(childLabelCell.row + pageSize * inputMergedRowCnt).addPageBreak();
}
}
}
})();

View File

@@ -0,0 +1,6 @@
(function () {
"use strict";
addApproveFlowAction(true);
})();

View File

@@ -0,0 +1,496 @@
/** @copyright 2018 Planning Village Corporation Co., Ltd. */
(function () {
'use strict';
// [IE11] Polyfill of Object.values
if (!Object.values) {
const ownKeys = (
(typeof Reflect === 'object' && typeof Reflect.ownKeys === 'function') ? Reflect.ownKeys :
// @ts-ignore
(typeof Object.getOwnPropertySymbols === 'function') ? (function (O) {
// @ts-ignore
return Object.getOwnPropertyNames(O).concat(Object.getOwnPropertySymbols(O));
}) :
Object.getOwnPropertyNames);
// @ts-ignore
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
// @ts-ignore
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
// @ts-ignore
const concat = Function.bind.call(Function.call, Array.prototype.concat);
//@ts-ignore
Object.values = function values(O) {
//@ts-ignore
return reduce(ownKeys(O), function (v, k) {
return concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : [])
}, [])
};
}
// [IE11] Polyfill of Number.isNaN
if (!Number.isNaN) {
Number.isNaN = function (value) {
return value !== null && (value != value || +value != value);
};
}
// [IE11] Polyfill of String.prototype.startsWith
if (!String.prototype.startsWith) {
Object.defineProperty(String.prototype, 'startsWith', {
//@ts-ignore
value: function (search, rawPos) {
var pos = rawPos > 0 ? rawPos | 0 : 0;
return this.substring(pos, pos + search.length) === search;
}
});
}
// [IE11] Polyfill of String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function (search, start) {
'use strict';
//@ts-ignore
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) {
start = 0;
}
return this.indexOf(search, start) !== -1;
};
}
// [IE11] Polyfill of Array.prototype.findIndex
if (!Array.prototype.findIndex) {
// @ts-ignore
Array.prototype.findIndex = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
//@ts-ignore
window.pvc = window.pvc || {};
//@ts-ignore
window.pvc.lib = window.pvc.lib || {};
//@ts-ignore
window.pvc.lib.exceljsUtil = (function () {
const exceljs = {
/**
* ワークブックの blob を読み取ります。
* @param {Blob} blob Blob
* @return {Promise<ExcelJS.Workbook>} ワークブック
*/
loadWorkbookBlob: function (blob) {
return new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function (e) {
// @ts-ignore
resolve(e.target.result);
};
reader.onerror = function (e) {
reject(e);
}
reader.onabort = function (e) {
reject(e);
}
reader.readAsArrayBuffer(blob);
}).then(function (arrayBuffer) {
// 非公式のメンバー load を使用
// @ts-ignore
return new ExcelJS.Workbook().xlsx.load(arrayBuffer);
});
},
/**
* ワークブックを Blob に変換します。
* @param {ExcelJS.Workbook} workbook ワークブック
* @return {Promise<Blob>} Blob
*/
saveWorkbookToBlob: function (workbook) {
// writeBuffer の戻り値 ExcelJS.Buffer はブラウザ環境では Uint8Array となる
return workbook.xlsx.writeBuffer().then(function (ua) {
return new Blob([ua], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
});
},
/**
* 行をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー元の開始行番号(1から開始)
* @param {number} [bottomRowNumber] コピー元の終了行番号(1から開始)。省略時は開始行番号と同じ値となります。
* @returns {pvc.lib.exceljsUtil.CopiedRowsInfo} 行のコピー情報
*/
copyRows: function (worksheet, topRowNumber, bottomRowNumber) {
const _bottomRowNumber = (bottomRowNumber == null ? topRowNumber : bottomRowNumber);
if (topRowNumber < 0 || _bottomRowNumber < topRowNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const rows = [];
for (let r = topRowNumber; r <= _bottomRowNumber; r++) {
const row = worksheet.getRow(r);
const cells = [];
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
rows.push({
cells: cells,
height: row.height,
// 非公式のメンバー style を使用
// @ts-ignore
style: row.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.top && merge.bottom <= _bottomRowNumber);
}).map(function (merge) {
return {
top: merge.top - topRowNumber,
right: merge.right,
bottom: merge.bottom - topRowNumber,
left: merge.left,
};
}));
return deepCopyObject({
rows: rows,
merges: merges
});
},
/**
* 指定された開始行を起点に、コピーした行を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
pasteRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRowNumber = topRowNumber + copiedRowsInfo.rows.length - 1;
if (topRowNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の行のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.bottom && merge.top <= bottomRowNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedRowsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
topRowNumber + mergeInfo.top,
mergeInfo.left,
topRowNumber + mergeInfo.bottom,
mergeInfo.right);
});
// セルをペースト
copiedRowsInfo.rows.forEach(function (rowInfo, i) {
const row = worksheet.getRow(topRowNumber + i);
row.height = rowInfo.height;
// 非公式のメンバー style を使用
// @ts-ignore
row.style = rowInfo.style;
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
const cellInfo = rowInfo.cells[c - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始行を起点に、コピーした行を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
insertRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRows = (
topRowNumber <= worksheet.rowCount ?
exceljs.copyRows(worksheet, topRowNumber, worksheet.rowCount) :
null);
exceljs.pasteRows(worksheet, topRowNumber, copiedRowsInfo);
if (bottomRows) {
exceljs.pasteRows(worksheet, topRowNumber + copiedRowsInfo.rows.length, bottomRows);
}
},
/**
* 列をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー元の開始列の列番号(1から開始)または列ラベル。終了列の引数を省略して、代わりに "A:B" の形式でコピー元の列の範囲を指定することも可能。
* @param {number | string} [rightColumn] コピー元の終了列の列番号(1から開始)または列ラベル。省略時は開始列と同じとみなされます(開始列を "A:B" の形式で指定された場合を除く)。
* @returns {pvc.lib.exceljsUtil.CopiedColumnsInfo} 列のコピー情報
*/
copyColumns: function (worksheet, leftColumn, rightColumn) {
if (rightColumn == null) {
rightColumn = leftColumn;
if (typeof leftColumn === 'string') {
const sp = leftColumn.split(':');
if (sp.length === 2) {
leftColumn = sp[0];
rightColumn = sp[1];
}
}
}
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = ((typeof rightColumn === 'number') ? rightColumn : exceljs.columnLetterToColumnNumber(rightColumn));
if (leftColumnNumber < 0 || rightColumnNumber < leftColumnNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const columns = [];
for (let c = leftColumnNumber; c <= rightColumnNumber; c++) {
const column = worksheet.getColumn(c);
const cells = [];
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
columns.push({
cells: cells,
width: column.width,
style: column.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.left && merge.right <= rightColumnNumber);
}).map(function (merge) {
return {
top: merge.top,
right: merge.right - leftColumnNumber,
bottom: merge.bottom,
left: merge.left - leftColumnNumber,
};
}));
return {
columns: columns,
merges: merges
};
},
/**
* 指定された開始列を起点に、コピーした列を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedColumnsInfo 列のコピー情報
*/
pasteColumns: function (worksheet, leftColumn, copiedColumnsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = leftColumnNumber + copiedColumnsInfo.columns.length - 1;
if (leftColumnNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の列のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.right && merge.left <= rightColumnNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedColumnsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
mergeInfo.top,
leftColumnNumber + mergeInfo.left,
mergeInfo.bottom,
leftColumnNumber + mergeInfo.right);
});
// セルをペースト
copiedColumnsInfo.columns.forEach(function (columnInfo, i) {
const column = worksheet.getColumn(leftColumnNumber + i);
column.width = columnInfo.width;
column.style = columnInfo.style;
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(leftColumnNumber + i);
const cellInfo = columnInfo.cells[r - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始列を起点に、コピーした列を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedRowsInfo 列のコピー情報
*/
insertColumns: function (worksheet, leftColumn, copiedRowsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumns = (
leftColumnNumber <= worksheet.columnCount ?
exceljs.copyColumns(worksheet, leftColumnNumber, worksheet.columnCount) :
null);
exceljs.pasteColumns(worksheet, leftColumnNumber, copiedRowsInfo);
if (rightColumns) {
exceljs.pasteColumns(worksheet, leftColumnNumber + copiedRowsInfo.columns.length, rightColumns);
}
},
/**
* セルをクリアします。
* @param {ExcelJS.Cell} cell セル
*/
clearCell: function (cell) {
cell.value = null;
/// @ts-ignore
cell.style = cell._mergeStyle(cell._row.style, cell._column.style, {});
},
/**
* 列ラベルから列番号(1から開始)を取得します。
* @param {string} columnLetter 列ラベル
* @return {number} 列番号(1から開始)
*/
columnLetterToColumnNumber: function (columnLetter) {
const letter = columnLetter.toUpperCase();
const l = letter.length;
const charCodeA = 65;
let result = 0;
for (let i = 0, j = l - 1; i < l; i++, j--) {
result += (letter.charCodeAt(j) - charCodeA + 1) * (i === 0 ? 1 : Math.pow(26, i));
}
return result;
},
/**
* 日付のセルの値を作成します。
* @param {Date} date
* @returns {any}
*/
createDateCellValue: function (date) {
// UTC にしないと正しく Excel に反映されない
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
},
deepCopyObject: deepCopyObject,
};
/**
* ワークシートのマージを取得します。
* @param {ExcelJS.Worksheet} worksheet ワークシート
*/
function getWorksheetMerges(worksheet) {
// 非公式のメンバー _merges を使用
// @ts-ignore
const mergesDic = worksheet._merges;
return Object.keys(mergesDic).map(function (address) {
return mergesDic[address];
});
}
/**
* オブジェクのコピーします。ディープコピーとなります。
* @template T
* @param {T} obj
* @returns {T}
*/
function deepCopyObject(obj) {
return copyObject(obj);
//@ts-ignore
function copyObject(obj) {
return (
obj == null ? null :
Array.isArray(obj) ? copyArray(obj) :
isDate(obj) ? copyDate(obj) :
isDic(obj) ? copyDic(obj) :
obj);
}
//@ts-ignore
function copyArray(array) {
//@ts-ignore
return array.map(function (item) {
return copyObject(item);
});
}
//@ts-ignore
function isDate(obj) {
return (obj != null && Object.prototype.toString.call(obj) === '[object Date]');
}
//@ts-ignore
function copyDate(date) {
return (isNaN(date) ? null : new Date(date.getTime()));
}
//@ts-ignore
function isDic(obj) {
return (obj != null && typeof obj === 'object');
}
//@ts-ignore
function copyDic(dic) {
let result = {};
for (let key in dic) {
//@ts-ignore
result[key] = copyObject(dic[key]);
}
return result;
}
}
return exceljs;
}());
})();

View File

@@ -5,8 +5,8 @@ const env = {
appId: 19,
excelName: "出欠集計表",
view: {
'0,1歳日誌データ連携用途': 13352636,
'2歳以上日誌データ連携用途': 13352638,
linkFor0to1: 13352636,
linkForOthers: 13352638,
}
},
"0,1歳日誌出力用": {
@@ -24,6 +24,15 @@ const env = {
"園児台帳": {
appId: 16,
},
"保育・教育日数マスタ": {
appId: 41,
},
"Excelテンプレート": {
appId: 46
},
"担任マスタ": {
appId: 52
}
};
const warekiStartYear = {

View File

@@ -21,24 +21,24 @@
.btn-group-area .month,
.btn-group-area .date,
.btn-group-area .classroom {
.btn-group-area .term {
--kuc-combobox-toggle-width: calc(2em + 16px);
--kuc-combobox-toggle-height: 48px;
}
.btn-group-area .classroom {
--kuc-combobox-toggle-width: calc(3em + 16px);
.btn-group-area .term {
--kuc-combobox-toggle-width: calc(4em + 16px);
}
.btn-group-area .month input[class^='kuc-combobox'],
.btn-group-area .date input[class^='kuc-combobox'],
.btn-group-area .classroom input[class^='kuc-combobox'] {
.btn-group-area .term input[class^='kuc-combobox'] {
text-align: center;
padding-right: 8px;
}
.btn-group-area .month input + div[class$="icon"],
.btn-group-area .date input + div[class$="icon"],
.btn-group-area .classroom input + div[class$="icon"] {
.btn-group-area .term input + div[class$="icon"] {
display: none;
}
@@ -46,6 +46,14 @@
margin: 0 8px;
}
.customized-record-header-space {
padding-top: 16px;
padding-left: 8px;
}
.customized-record-header-space > .btn-group-area {
padding: 0;
margin: 0;
}
.customized-header-space-wrapper.single-label-line {
margin-top: 33px;
}

95
src/main.mobile.css Normal file
View File

@@ -0,0 +1,95 @@
.btn-group-area {
display: flex;
/* margin-left: 2.5em; */
align-items: flex-end;
}
.btn-group-area .label {
margin: 0 4px;
}
.btn-group-area .input {
padding-left: 8px;
}
.btn-group-area .year {
--kuc-text-input-width: calc(4em + 16px);
--kuc-text-input-height: 48px;
}
.btn-group-area .year input {
text-align: center;
}
.btn-group-area .month,
.btn-group-area .date,
.btn-group-area .term {
--kuc-combobox-toggle-width: calc(2em + 16px);
--kuc-combobox-toggle-height: 48px;
}
.btn-group-area .term {
--kuc-combobox-toggle-width: calc(3em + 16px);
}
.btn-group-area .month input[class^='kuc-combobox'],
.btn-group-area .date input[class^='kuc-combobox'],
.btn-group-area .term input[class^='kuc-combobox'] {
text-align: center;
padding-right: 8px;
}
.btn-group-area .month input + div[class$="icon"],
.btn-group-area .date input + div[class$="icon"],
.btn-group-area .term input + div[class$="icon"] {
display: none;
}
.action-btn {
margin: 0 8px;
}
.customized-record-header-space {
padding-top: 16px;
padding-left: 8px;
}
.customized-record-header-space > .btn-group-area {
padding: 0;
margin: 0;
}
.kintone-app-headermenu-space {
height: auto;
display: inline-flex;
}
#user-js-header-clocking-btn-area {
margin: 4px 0;
text-align: center;
}
#user-js-header-clocking-btn-area > .action-btn {
--kuc-button-width: 6em
}
#user-js-header-clocking-btn-area > .action-btn:first-child {
margin-right: 1em;
}
#user-js-clock-in-btn-area > .action-btn,
#user-js-clock-out-btn-area > .action-btn {
--kuc-mobile-button-height: 35px;
margin: .5em 12px;
}
.kuc--has-spinner {
position: relative;
}
div[class^='kuc-spinner'][class$='__spinner'] {
margin-top: 89px;
height: calc(100% - 89px);
position: absolute;
--kuc-spinner-text-color: #3498db;
--kuc-spinner-loader-color: #3498db;
}
div[class^='kuc-spinner'][class$='mask'] {
top: 89px;
position: absolute;
background-color: white;
}

206
src/utils-mobile.js Normal file
View File

@@ -0,0 +1,206 @@
const Kuc = Kucs['1.19.0'];
let notificationEl;
function getHeaderSpace(className, isDetailPage) {
const headerSpace = kintone.mobile.app.getHeaderSpaceElement();
if (!headerSpace) {
throw new Error('このページではヘッダー要素が利用できません。');
};
const _className = isDetailPage ? 'customized-record-header-space' : 'customized-header-space';
headerSpace.className += (' ' + _className + ' ' + className);
headerSpace.parentElement.className += (' ' + _className + '-wrapper ' + className);
return headerSpace;
}
const classItems = [
{ label: "にじ", value: "にじ" },
{ label: "ほし", value: "ほし" },
{ label: "つき", value: "つき" },
{ label: "ゆり", value: "ゆり" },
]
const termItems = [
{ label: "0歳児", value: "0歳児" },
{ label: "1歳児", value: "1歳児" },
{ label: "2歳児", value: "2歳児" },
{ label: "3歳児", value: "3歳児" },
{ label: "4歳児", value: "4歳児" },
{ label: "5歳児", value: "5歳児" },
]
function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, yearElId = false, monthElId = false, dateElId = false, termElId = false, defaultThisMonth = false, }) {
const result = {};
if (document.getElementById(groupId)) {
return;
}
const btnGroupAreaEl = document.createElement('div');
btnGroupAreaEl.id = groupId;
btnGroupAreaEl.className = 'btn-group-area'
result[groupId] = btnGroupAreaEl;
if (yearElId) {
const yearEl = new Kuc.MobileText({
value: "" + new Date().getFullYear(),
id: yearElId,
label: '年',
className: 'year input'
});
result[yearElId] = yearEl;
btnGroupAreaEl.appendChild(yearEl);
}
if (monthElId) {
const monthEl = new Kuc.MobileDropdown({
value: defaultThisMonth ? monthItems[new Date().getMonth()].value : undefined,
id: monthElId,
className: 'month input',
label: '月',
items: monthItems,
});
result[monthElId] = monthEl;
btnGroupAreaEl.appendChild(monthEl);
}
if (dateElId) {
const dateEl = new Kuc.MobileDropdown({
value: dateItems[new Date().getDate() - 1].value,
id: dateElId,
items: dateItems,
label: '日',
className: "date input",
});
result[dateElId] = dateEl;
btnGroupAreaEl.appendChild(dateEl);
}
if (termElId) {
const termEl = new Kuc.MobileDropdown({
id: termElId,
items: termItems,
className: "term input",
label: '学年',
});
result[termElId] = termEl;
btnGroupAreaEl.appendChild(termEl);
}
const btnEl = new Kuc.MobileButton({
text: btnLabel,
type: "submit",
className: "action-btn",
id: btnElId,
});
result[btnElId] = btnEl;
btnEl.addEventListener('click', (e) => {
showError(false);
const checkResult = checkInputData(result, { btnLabel, yearElId, monthElId, dateElId, termElId });
if (checkResult) {
btnOnClick(e, checkResult);
}
});
btnGroupAreaEl.appendChild(btnEl);
return result;
}
function checkInputData(map, { btnLabel, yearElId, monthElId, dateElId, termElId }) {
const year = yearElId && map[yearElId].value;
const month = monthElId && map[monthElId].value;
const date = dateElId && (map[dateElId].value === 'end' ? getLastDate(year, month).getDate() : map[dateElId].value);
const term = termElId && map[termElId].value;
const errorMsgs = [];
if (yearElId) {
const yearRegex = /^\d+$/;
if (!yearRegex.test(year)) {
errorMsgs.push(' · 年は整数で入力してください。');
}
}
if (monthElId && !month) {
errorMsgs.push(' · 月を選択してください。');
}
if (dateElId && !date) {
errorMsgs.push(' · 日を選択してください。');
}
if (termElId && !term) {
errorMsgs.push(' · 学年を選択してください。');
}
if (errorMsgs.length > 0) {
showError(true, btnLabel + 'エラー\n' + errorMsgs.join('\n'))
return;
}
return {
year,
month,
date,
term,
}
}
function createBtn(id, label, onClick) {
const btnEl = new Kuc.MobileButton({
text: label,
type: "submit",
className: "action-btn",
id,
});
btnEl.addEventListener('click', onClick);
return btnEl;
}
function hideSpaceField(ids) {
ids.forEach(id => {
const area = kintone.mobile.app.record.getSpaceElement(id);
area.parentElement.style.minWidth = '0';
area.parentElement.style.display = 'none';
});
}
const statusFieldMap = {
'指導教諭確認中': '担任',
'主幹確認中': '指導',
'園長確認中': '主幹',
'完了': '園長'
}
function addApproveFlowAction() {
return kintone.events.on("mobile.app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
}
function showError(show, text) {
if (show) {
buildNotification('danger', text);
notificationEl.open();
console.error(text);
} else {
notificationEl && notificationEl.close();
}
}
function buildNotification(type, text, duration = -1) {
const param = {
type,
text,
duration
}
if (!notificationEl) {
notificationEl = new Kuc.MobileNotification(param);
} else {
Object.assign(notificationEl, param);
}
}

View File

@@ -1,5 +1,7 @@
const Kuc = Kucs['1.19.0'];
const WEEK = ['日', '月', '火', '水', '木', '金', '土']
const monthItems = Array.from({ length: 12 }, (_, i) => {
const month = "" + (i + 1);
return { label: month, value: month.padStart(2, '0') };
@@ -18,8 +20,18 @@ const classItems = [
{ label: "ゆり", value: "ゆり" },
]
let errorEl;
const termItems = [
{ label: "0歳児", value: "0歳児" },
{ label: "1歳児", value: "1歳児" },
{ label: "2歳児", value: "2歳児" },
{ label: "3歳児", value: "3歳児" },
{ label: "4歳児", value: "4歳児" },
{ label: "5歳児", value: "5歳児" },
]
let notificationEl;
let loadingEl;
let dialogEl;
function getHeaderSpace(className, isDetailPage) {
const headerSpace = (isDetailPage ? kintone.app.record : kintone.app).getHeaderMenuSpaceElement();
@@ -35,7 +47,7 @@ function getHeaderSpace(className, isDetailPage) {
return headerSpace;
}
function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, yearElId = false, monthElId = false, dateElId = false, classElId = false, defaultThisMonth = false, }) {
function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, yearElId = false, monthElId = false, dateElId = false, termElId = false, defaultThisMonth = false, }) {
const result = {};
if (document.getElementById(groupId)) {
@@ -56,11 +68,6 @@ function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, ye
});
result[yearElId] = yearEl;
btnGroupAreaEl.appendChild(yearEl);
// const label = document.createElement('div');
// label.classList = 'label'
// label.textContent = '年';
// btnGroupAreaEl.appendChild(label);
}
if (monthElId) {
@@ -73,11 +80,6 @@ function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, ye
});
result[monthElId] = monthEl;
btnGroupAreaEl.appendChild(monthEl);
// const label = document.createElement('div');
// label.classList = 'label'
// label.textContent = '月';
// btnGroupAreaEl.appendChild(label);
}
if (dateElId) {
@@ -90,27 +92,17 @@ function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, ye
});
result[dateElId] = dateEl;
btnGroupAreaEl.appendChild(dateEl);
// const label = document.createElement('div');
// label.classList = 'label'
// label.textContent = '日';
// btnGroupAreaEl.appendChild(label);
}
if (classElId) {
// const label = document.createElement('div');
// label.classList = 'label'
// label.textContent = 'クラス';
// btnGroupAreaEl.appendChild(label);
const classEl = new Kuc.Combobox({
id: classElId,
items: classItems,
className: "classroom input",
label: 'クラス',
if (termElId) {
const termEl = new Kuc.Combobox({
id: termElId,
items: termItems,
className: "term input",
label: '学年',
});
result[classElId] = classEl;
btnGroupAreaEl.appendChild(classEl);
result[termElId] = termEl;
btnGroupAreaEl.appendChild(termEl);
}
const btnEl = new Kuc.Button({
@@ -122,7 +114,7 @@ function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, ye
result[btnElId] = btnEl;
btnEl.addEventListener('click', (e) => {
showError(false);
const checkResult = checkInputData(result, { btnLabel, yearElId, monthElId, dateElId, classElId });
const checkResult = checkInputData(result, { btnLabel, yearElId, monthElId, dateElId, termElId });
if (checkResult) {
btnOnClick(e, checkResult);
}
@@ -132,11 +124,11 @@ function createBtnGroupArea(groupId, btnLabel, btnOnClick, { btnElId = false, ye
return result;
}
function checkInputData(map, { btnLabel, yearElId, monthElId, dateElId, classElId }) {
function checkInputData(map, { btnLabel, yearElId, monthElId, dateElId, termElId }) {
const year = yearElId && map[yearElId].value;
const month = monthElId && map[monthElId].value;
const date = dateElId && map[dateElId].value;
const className = classElId && map[classElId].value;
const date = dateElId && (map[dateElId].value === 'end' ? getLastDate(year, month).getDate() : map[dateElId].value);
const term = termElId && map[termElId].value;
const errorMsgs = [];
@@ -152,8 +144,8 @@ function checkInputData(map, { btnLabel, yearElId, monthElId, dateElId, classElI
if (dateElId && !date) {
errorMsgs.push(' · 日を選択してください。');
}
if (classElId && !className) {
errorMsgs.push(' · クラスを選択してください。');
if (termElId && !term) {
errorMsgs.push(' · 学年を選択してください。');
}
if (errorMsgs.length > 0) {
showError(true, btnLabel + 'エラー\n' + errorMsgs.join('\n'))
@@ -164,10 +156,15 @@ function checkInputData(map, { btnLabel, yearElId, monthElId, dateElId, classElI
year,
month,
date,
className,
term,
}
}
function getLastDate(year, month) {
// month は 1-12 の数値で入力する
return new Date(year, month, 0);
}
function createBtn(id, label, onClick) {
const btnEl = new Kuc.Button({
text: label,
@@ -187,7 +184,7 @@ function hideSpaceField(ids) {
});
}
function getExcelName({ excelName }, nameSuffix = '', suffix = '.xlsx') {
function getExcelName(excelName, nameSuffix = '', suffix = '.xlsx') {
return excelName + (nameSuffix ? (nameSuffix.startsWith('_') ? nameSuffix : ('_' + nameSuffix)) : '') + suffix;
}
@@ -225,12 +222,18 @@ function convertToWesternYear(year, era) {
return warekiStartYear[era] + year - 1;
}
function getFormatDateString(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
function getFormatDateString(dateObjOrYear, month, date) {
let year = dateObjOrYear;
if (typeof dateObjOrYear === "object" && !month && !date) {
year = dateObjOrYear.getFullYear();
month = dateObjOrYear.getMonth() + 1;
date = dateObjOrYear.getDate();
}
const formatY = year;
const formatM = String(month).padStart(2, '0');
const formatD = String(date).padStart(2, '0');
return `${year}-${month}-${day}`;
return `${formatY}-${formatM}-${formatD}`;
}
function loading(show, text) {
@@ -249,17 +252,257 @@ function loading(show, text) {
}
function showError(show, text) {
if (!errorEl) {
errorEl = new Kuc.Notification({
type: 'danger',
text
});
} else {
errorEl.close();
errorEl.text = text;
}
if (show) {
errorEl.open();
buildNotification('danger', text);
notificationEl.open();
console.error(text);
} else {
notificationEl && notificationEl.close();
}
}
function showSuccess(show, text, time = 3000) {
if (show) {
buildNotification('success', text, time);
notificationEl.open();
} else {
notificationEl && notificationEl.close();
}
}
function buildNotification(type, text, duration = -1) {
const param = {
type,
text,
duration
}
if (!notificationEl) {
notificationEl = new Kuc.Notification(param);
} else {
Object.assign(notificationEl, param);
}
}
/**
options: {
icon: '' | 'info' | 'success' | 'error' | 'warning' | 'question',
title: string,
content: string|HTMLElement,
header: string|HTMLElement,
footer: string|HTMLElement,
dataHolder: {},
// ↓ if not footer ↓
ok: string = 'OK',
cancel: boolean|string = 'キャンセル',
onOk: (dataHolder, e) => void,
onCancel: (dataHolder, e) => void,
onClose: (dataHolder, e) => void,
}
*/
function showDialog(options) {
if (!dialogEl) {
createDialogEl(options);
} else {
Object.assign(dialogEl, options);
}
dialogEl.open();
}
function createDialogEl({ ok, cancel, onOk, onCancel, onClose, ...options }) {
if (!options.footer) {
const divEl = document.createElement('div');
divEl.style.textAlign = 'right';
if (cancel !== false) {
const cancelButton = new Kuc.Button({
text: cancel || 'Cancel',
type: 'normal'
});
cancelButton.style.paddingRight = '8px';
cancelButton.addEventListener('click', (event) => {
onCancel && onCancel(dialogEl.dataHolder, event);
dialogEl.close();
});
divEl.appendChild(cancelButton);
}
const okButton = new Kuc.Button({
text: ok || 'OK',
type: 'submit'
});
okButton.addEventListener('click', (event) => {
onOk && onOk(dialogEl.dataHolder, event);
dialogEl.close();
});
divEl.appendChild(okButton);
options.footer = divEl;
}
dialogEl = new Kuc.Dialog(options);
if (onClose) {
dialogEl.addEventListener('close', (event) => {
onClose(dialogEl.dataHolder, event);
});
}
}
async function createExcelAndDownload({ bizLogic, api, excelName, exportName }) {
const arrayBuffer = await getTemplateBuffer(api, excelName);
if (!arrayBuffer) {
// エラー
return;
}
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(arrayBuffer);
const worksheet = workbook.getWorksheet();
try {
await bizLogic(api, worksheet);
} catch (e) {
showError(true, '帳票出力エラー\n - ' + e);
return;
}
const blob = await pvc.lib.exceljsUtil.saveWorkbookToBlob(workbook);
saveAs(blob, exportName);
}
async function getTemplateBuffer(api, excelName) {
try {
const templateObj = await api.record.getAllRecordsWithId({
app: env["Excelテンプレート"].appId,
condition: `テンプレート名 = "${excelName}"`
});
const fileKey = templateObj[0]["テンプレートファイル"].value[0].fileKey;
return await api.file.downloadFile({
fileKey
});
} catch (e) {
showError(true, '本アプリのExcelテンプレート読み取りエラー\n - ' + e);
}
}
function findCellsInfo(worksheet, ids) {
const result = ids.reduce((acc, key) => {
acc[key] = [];
return acc;
}, {});
let lastColVal;
worksheet.eachRow((row, rowNumber) => {
row.eachCell((cell, colNumber) => {
const value = cell.value;
if (value !== lastColVal && result[value]) {
result[value].push({
row: rowNumber,
col: colNumber,
});
}
lastColVal = value;
});
});
return result;
}
function getLabelColsMapping(worksheet, row, fields) {
const arr = worksheet.getRow(row).values;
const changedIndices = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i - 1]) {
changedIndices.push({
index: i,
field: fields[changedIndices.length]
});
}
}
return changedIndices;
}
function createCopyFromTemplate(worksheet, { startPage,
totalPages,
copyPageRowStart,
copyPageRowEnd,
callback }) {
const rowCount = copyPageRowEnd - copyPageRowStart + 1;
let newPageRow = copyPageRowEnd + 1;
for (let p = startPage; p <= totalPages; p++) {
const copyRow = pvc.lib.exceljsUtil.copyRows(worksheet, copyPageRowStart, copyPageRowEnd);
pvc.lib.exceljsUtil.insertRows(worksheet, newPageRow, copyRow);
newPageRow += rowCount;
callback(p, rowCount);
}
}
function updateCell(worksheetOrRow, { base, left = 0, right = 0, up = 0, down = 0 }, data) {
let cellObj;
const row = base.row + (down - up);
const col = base.col + (right - left);
if (!worksheetOrRow._worksheet) {
const worksheet = worksheetOrRow;
cellObj = worksheet.getCell(row, col);
} else {
const rowObj = worksheetOrRow;
cellObj = rowObj.getCell(col)
}
cellObj.value = data;
if (!cellObj.alignment) {
cellObj.alignment = {};
}
cellObj.alignment.wrapText = true;
}
function getMergedInfo(worksheet, { row, col }) {
const key = columnToLetter(col) + row;
return worksheet._merges[key];
}
function columnToLetter(columnIndex) {
let letter = '';
while (columnIndex > 0) {
let remainder = (columnIndex - 1) % 26;
letter = String.fromCharCode(65 + remainder) + letter;
columnIndex = Math.floor((columnIndex - 1) / 26);
}
return letter;
}
function fillApproveArea(baseCells, worksheet, record) {
const signLabels = ['園長', '主幹', '指導', '担任'];
const signRow = baseCells['園長'][0].row + 1;
for (let i = 0; i < signLabels.length; i++) {
worksheet.getCell(signRow, baseCells['園長'][0].col + i).value = (record[signLabels[i]]?.value || '');
}
}
function groupingBySex(list) {
return list.reduce(([male, female], record) => {
if (record['性別'].value === '男') {
return [male.concat(record), female];
} else if (record['性別'].value === '女') {
return [male, female.concat(record)];
}
return [male, female];
}, [[], []]);
}
const statusFieldMap = {
'指導教諭確認中': '担任',
'主幹確認中': '指導',
'園長確認中': '主幹',
'完了': '園長'
}
function addApproveFlowAction() {
return kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
}

View File

@@ -0,0 +1,5 @@
const statusFieldMap = {
'主幹確認中': '指導',
'園長確認中': '主幹',
'完了': '園長'
}

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("mobile.app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,9 @@
const statusFieldMap = {
'主幹確認中4週目': '指導',
'園長確認中4週目': '主幹',
'完了4週目': '園長',
'担任作成中5週目': '園長',
'主幹確認中5週目': '指導',
'園長確認中5週目': '主幹',
'完了5週目': '園長',
}

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("mobile.app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

3
src/個別配慮/FileSaver.min.js vendored Normal file
View File

@@ -0,0 +1,3 @@
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)});
//# sourceMappingURL=FileSaver.min.js.map

File diff suppressed because one or more lines are too long

3
src/個別配慮/exceljs.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2433
src/個別配慮/kuc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,8 @@
"use strict";
const APP_ENV = env["個別配慮"];
addApproveFlowAction();
kintone.events.on("app.record.index.show", (event) => {
const headerSpace = getHeaderSpace('single-label-line');
@@ -18,8 +20,105 @@
headerSpace.appendChild(elements['extract-action-area']);
});
function handleButtonClick(e, { year, month }) {
const fileName = getExcelName(APP_ENV, year + month);
console.log(fileName);
async function handleButtonClick(e, { year, month }) {
loading(true, '帳票出力中...');
showError(false);
const api = new KintoneRestAPIClient();
// 本アプリからデータを読み取る
const currentAppRecords = await getRecords(api, year, month);
if (!currentAppRecords) {
// エラー
loading(false);
return e;
}
const excelName = APP_ENV.excelName;
await createExcelAndDownload({
api,
excelName,
exportName: getExcelName(excelName, year + month),
bizLogic: writeExcel(currentAppRecords, year, month),
});
loading(false);
}
})();
async function getRecords(api, year, month) {
const firstDate = getFormatDateString(year, month, 1)
const lastDate = getFormatDateString(getLastDate(year, month));
try {
return await api.record.getAllRecordsWithId({
app: env["個別配慮"].appId,
condition: `日付 >= "${firstDate}" and 日付 <= "${lastDate}"`
});
} catch (e) {
showError(true, '本アプリのデータ読み取りエラー\n - ' + e);
}
}
function writeExcel(records, year, month) {
return async (api, worksheet) => {
if (!records || !records.length) {
return;
}
const baseCells = findCellsInfo(worksheet, ['月', '児童名']);
const pageSize = 10;
const totalPages = Math.ceil(records.length / pageSize);
// make new copy
if (totalPages > 2) {
const lastPage = 2;
const copyPageRowStart = baseCells['月'][lastPage - 1].row - 1;
const copyPageRowEnd = baseCells['児童名'][lastPage - 1].row + pageSize;
createCopyFromTemplate(worksheet, {
startPage: lastPage + 1,
totalPages,
copyPageRowStart,
copyPageRowEnd,
callback: (newPage, rowCount) => {
['月', '児童名'].forEach((label) => {
const last = baseCells[label][newPage - 2];
baseCells[label].push({
col: last.col,
row: last.row + rowCount
});
})
}
})
}
const cols = getLabelColsMapping(worksheet, baseCells['児童名'][0].row, ['児童名', '子どもの姿', '保育者援助_配慮_環境構成']);
const sortedRecords = records.sort((a, b) => Number(a['レコード番号'].value) - Number(b['レコード番号'].value));
for (let i = 0; i < totalPages; i++) {
const monthLabelCell = baseCells['月'][i];
updateCell(worksheet, { base: monthLabelCell, left: 1 }, Number(month));
const childLabelCell = baseCells['児童名'][i];
let currentRow = childLabelCell.row + 1;
for (let j = 0; j < pageSize; j++) {
const index = i * pageSize + j;
const record = sortedRecords[index];
if (!record) {
break;
}
const row = worksheet.getRow(currentRow);
cols.forEach(col => {
updateCell(row, { base: { row: currentRow, col: col.index } }, record[col.field].value);
});
currentRow++;
}
worksheet.getRow(childLabelCell.row + pageSize).addPageBreak();
}
}
}
})();

View File

@@ -0,0 +1,6 @@
(function () {
"use strict";
addApproveFlowAction(true);
})();

View File

@@ -0,0 +1,496 @@
/** @copyright 2018 Planning Village Corporation Co., Ltd. */
(function () {
'use strict';
// [IE11] Polyfill of Object.values
if (!Object.values) {
const ownKeys = (
(typeof Reflect === 'object' && typeof Reflect.ownKeys === 'function') ? Reflect.ownKeys :
// @ts-ignore
(typeof Object.getOwnPropertySymbols === 'function') ? (function (O) {
// @ts-ignore
return Object.getOwnPropertyNames(O).concat(Object.getOwnPropertySymbols(O));
}) :
Object.getOwnPropertyNames);
// @ts-ignore
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
// @ts-ignore
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
// @ts-ignore
const concat = Function.bind.call(Function.call, Array.prototype.concat);
//@ts-ignore
Object.values = function values(O) {
//@ts-ignore
return reduce(ownKeys(O), function (v, k) {
return concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : [])
}, [])
};
}
// [IE11] Polyfill of Number.isNaN
if (!Number.isNaN) {
Number.isNaN = function (value) {
return value !== null && (value != value || +value != value);
};
}
// [IE11] Polyfill of String.prototype.startsWith
if (!String.prototype.startsWith) {
Object.defineProperty(String.prototype, 'startsWith', {
//@ts-ignore
value: function (search, rawPos) {
var pos = rawPos > 0 ? rawPos | 0 : 0;
return this.substring(pos, pos + search.length) === search;
}
});
}
// [IE11] Polyfill of String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function (search, start) {
'use strict';
//@ts-ignore
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) {
start = 0;
}
return this.indexOf(search, start) !== -1;
};
}
// [IE11] Polyfill of Array.prototype.findIndex
if (!Array.prototype.findIndex) {
// @ts-ignore
Array.prototype.findIndex = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
//@ts-ignore
window.pvc = window.pvc || {};
//@ts-ignore
window.pvc.lib = window.pvc.lib || {};
//@ts-ignore
window.pvc.lib.exceljsUtil = (function () {
const exceljs = {
/**
* ワークブックの blob を読み取ります。
* @param {Blob} blob Blob
* @return {Promise<ExcelJS.Workbook>} ワークブック
*/
loadWorkbookBlob: function (blob) {
return new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function (e) {
// @ts-ignore
resolve(e.target.result);
};
reader.onerror = function (e) {
reject(e);
}
reader.onabort = function (e) {
reject(e);
}
reader.readAsArrayBuffer(blob);
}).then(function (arrayBuffer) {
// 非公式のメンバー load を使用
// @ts-ignore
return new ExcelJS.Workbook().xlsx.load(arrayBuffer);
});
},
/**
* ワークブックを Blob に変換します。
* @param {ExcelJS.Workbook} workbook ワークブック
* @return {Promise<Blob>} Blob
*/
saveWorkbookToBlob: function (workbook) {
// writeBuffer の戻り値 ExcelJS.Buffer はブラウザ環境では Uint8Array となる
return workbook.xlsx.writeBuffer().then(function (ua) {
return new Blob([ua], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
});
},
/**
* 行をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー元の開始行番号(1から開始)
* @param {number} [bottomRowNumber] コピー元の終了行番号(1から開始)。省略時は開始行番号と同じ値となります。
* @returns {pvc.lib.exceljsUtil.CopiedRowsInfo} 行のコピー情報
*/
copyRows: function (worksheet, topRowNumber, bottomRowNumber) {
const _bottomRowNumber = (bottomRowNumber == null ? topRowNumber : bottomRowNumber);
if (topRowNumber < 0 || _bottomRowNumber < topRowNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const rows = [];
for (let r = topRowNumber; r <= _bottomRowNumber; r++) {
const row = worksheet.getRow(r);
const cells = [];
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
rows.push({
cells: cells,
height: row.height,
// 非公式のメンバー style を使用
// @ts-ignore
style: row.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.top && merge.bottom <= _bottomRowNumber);
}).map(function (merge) {
return {
top: merge.top - topRowNumber,
right: merge.right,
bottom: merge.bottom - topRowNumber,
left: merge.left,
};
}));
return deepCopyObject({
rows: rows,
merges: merges
});
},
/**
* 指定された開始行を起点に、コピーした行を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
pasteRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRowNumber = topRowNumber + copiedRowsInfo.rows.length - 1;
if (topRowNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の行のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.bottom && merge.top <= bottomRowNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedRowsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
topRowNumber + mergeInfo.top,
mergeInfo.left,
topRowNumber + mergeInfo.bottom,
mergeInfo.right);
});
// セルをペースト
copiedRowsInfo.rows.forEach(function (rowInfo, i) {
const row = worksheet.getRow(topRowNumber + i);
row.height = rowInfo.height;
// 非公式のメンバー style を使用
// @ts-ignore
row.style = rowInfo.style;
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
const cellInfo = rowInfo.cells[c - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始行を起点に、コピーした行を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
insertRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRows = (
topRowNumber <= worksheet.rowCount ?
exceljs.copyRows(worksheet, topRowNumber, worksheet.rowCount) :
null);
exceljs.pasteRows(worksheet, topRowNumber, copiedRowsInfo);
if (bottomRows) {
exceljs.pasteRows(worksheet, topRowNumber + copiedRowsInfo.rows.length, bottomRows);
}
},
/**
* 列をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー元の開始列の列番号(1から開始)または列ラベル。終了列の引数を省略して、代わりに "A:B" の形式でコピー元の列の範囲を指定することも可能。
* @param {number | string} [rightColumn] コピー元の終了列の列番号(1から開始)または列ラベル。省略時は開始列と同じとみなされます(開始列を "A:B" の形式で指定された場合を除く)。
* @returns {pvc.lib.exceljsUtil.CopiedColumnsInfo} 列のコピー情報
*/
copyColumns: function (worksheet, leftColumn, rightColumn) {
if (rightColumn == null) {
rightColumn = leftColumn;
if (typeof leftColumn === 'string') {
const sp = leftColumn.split(':');
if (sp.length === 2) {
leftColumn = sp[0];
rightColumn = sp[1];
}
}
}
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = ((typeof rightColumn === 'number') ? rightColumn : exceljs.columnLetterToColumnNumber(rightColumn));
if (leftColumnNumber < 0 || rightColumnNumber < leftColumnNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const columns = [];
for (let c = leftColumnNumber; c <= rightColumnNumber; c++) {
const column = worksheet.getColumn(c);
const cells = [];
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
columns.push({
cells: cells,
width: column.width,
style: column.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.left && merge.right <= rightColumnNumber);
}).map(function (merge) {
return {
top: merge.top,
right: merge.right - leftColumnNumber,
bottom: merge.bottom,
left: merge.left - leftColumnNumber,
};
}));
return {
columns: columns,
merges: merges
};
},
/**
* 指定された開始列を起点に、コピーした列を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedColumnsInfo 列のコピー情報
*/
pasteColumns: function (worksheet, leftColumn, copiedColumnsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = leftColumnNumber + copiedColumnsInfo.columns.length - 1;
if (leftColumnNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の列のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.right && merge.left <= rightColumnNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedColumnsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
mergeInfo.top,
leftColumnNumber + mergeInfo.left,
mergeInfo.bottom,
leftColumnNumber + mergeInfo.right);
});
// セルをペースト
copiedColumnsInfo.columns.forEach(function (columnInfo, i) {
const column = worksheet.getColumn(leftColumnNumber + i);
column.width = columnInfo.width;
column.style = columnInfo.style;
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(leftColumnNumber + i);
const cellInfo = columnInfo.cells[r - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始列を起点に、コピーした列を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedRowsInfo 列のコピー情報
*/
insertColumns: function (worksheet, leftColumn, copiedRowsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumns = (
leftColumnNumber <= worksheet.columnCount ?
exceljs.copyColumns(worksheet, leftColumnNumber, worksheet.columnCount) :
null);
exceljs.pasteColumns(worksheet, leftColumnNumber, copiedRowsInfo);
if (rightColumns) {
exceljs.pasteColumns(worksheet, leftColumnNumber + copiedRowsInfo.columns.length, rightColumns);
}
},
/**
* セルをクリアします。
* @param {ExcelJS.Cell} cell セル
*/
clearCell: function (cell) {
cell.value = null;
/// @ts-ignore
cell.style = cell._mergeStyle(cell._row.style, cell._column.style, {});
},
/**
* 列ラベルから列番号(1から開始)を取得します。
* @param {string} columnLetter 列ラベル
* @return {number} 列番号(1から開始)
*/
columnLetterToColumnNumber: function (columnLetter) {
const letter = columnLetter.toUpperCase();
const l = letter.length;
const charCodeA = 65;
let result = 0;
for (let i = 0, j = l - 1; i < l; i++, j--) {
result += (letter.charCodeAt(j) - charCodeA + 1) * (i === 0 ? 1 : Math.pow(26, i));
}
return result;
},
/**
* 日付のセルの値を作成します。
* @param {Date} date
* @returns {any}
*/
createDateCellValue: function (date) {
// UTC にしないと正しく Excel に反映されない
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
},
deepCopyObject: deepCopyObject,
};
/**
* ワークシートのマージを取得します。
* @param {ExcelJS.Worksheet} worksheet ワークシート
*/
function getWorksheetMerges(worksheet) {
// 非公式のメンバー _merges を使用
// @ts-ignore
const mergesDic = worksheet._merges;
return Object.keys(mergesDic).map(function (address) {
return mergesDic[address];
});
}
/**
* オブジェクのコピーします。ディープコピーとなります。
* @template T
* @param {T} obj
* @returns {T}
*/
function deepCopyObject(obj) {
return copyObject(obj);
//@ts-ignore
function copyObject(obj) {
return (
obj == null ? null :
Array.isArray(obj) ? copyArray(obj) :
isDate(obj) ? copyDate(obj) :
isDic(obj) ? copyDic(obj) :
obj);
}
//@ts-ignore
function copyArray(array) {
//@ts-ignore
return array.map(function (item) {
return copyObject(item);
});
}
//@ts-ignore
function isDate(obj) {
return (obj != null && Object.prototype.toString.call(obj) === '[object Date]');
}
//@ts-ignore
function copyDate(date) {
return (isNaN(date) ? null : new Date(date.getTime()));
}
//@ts-ignore
function isDic(obj) {
return (obj != null && typeof obj === 'object');
}
//@ts-ignore
function copyDic(dic) {
let result = {};
for (let key in dic) {
//@ts-ignore
result[key] = copyObject(dic[key]);
}
return result;
}
}
return exceljs;
}());
})();

View File

@@ -16,23 +16,51 @@ class BatchCreateHandler {
const api = new KintoneRestAPIClient();
// 園児台帳アプリからデータを読み取る
const result = await this.getMasterRecords(api);
if (!result) {
const masterRecords = await this.getMasterRecords(api);
if (!masterRecords) {
// エラー
loading(false);
return;
}
const today = new Date();
const todayString = getFormatDateString(today);
const records = this.generateRecords(today, todayString, result);
const records = this.generateRecords(today, todayString, masterRecords);
// console.log(records);
const createResult = await this.createData(api, records, todayString);
location.reload();
const result = await this.createData(api, records, todayString);
// showSuccess(true, "出欠簿一括作成完了");
if (result) {
this.showSuccessDialog(result);
}
loading(false);
}
generateRecords = (today, todayString, result) => {
return result.reduce((acc, masterRecord) => {
showSuccessDialog(result) {
const contentEl = document.createElement('div');
contentEl.style.fontSize = '16px';
if (result.updateResult && result.insertResult) {
contentEl.innerHTML = `${result.insertResult.records.length}件のデータを更新し、${result.insertResult.records.length}件のデータを新規作成しました。`
} else if (result.updateResult) {
contentEl.innerHTML = `${result.updateResult.records.length}件のデータを更新しました。`;
} else if (result.insertResult) {
contentEl.innerHTML = `${result.insertResult.records.length}件のデータを生成しました。`;
} else {
contentEl.innerHTML = `データの更新はありません。既に最新の状態です。`
}
showDialog({
title: '出欠簿一括作成完了',
content: contentEl,
ok: '更新して確認',
cancel: false,
onClose: () => { location.reload() }
});
}
generateRecords = (today, todayString, masterRecords) => {
return masterRecords.reduce((acc, masterRecord) => {
if (this.needCreateData(masterRecord, today)) {
acc.push(this.createRecord(masterRecord, todayString));
}
@@ -61,6 +89,8 @@ class BatchCreateHandler {
return {
'登園日': { 'value': todayString },
'園児ユニークキー': { 'value': record['ユニークキー'].value },
'担任': { 'value': record['担当者名'].value.map((x)=>x.name).join('、') },
'保護者ログインID': { 'value': record['保護者ログインID'].value },
}
}
@@ -68,11 +98,10 @@ class BatchCreateHandler {
try {
return await api.record.getAllRecordsWithId({
app: env["園児台帳"].appId,
fields: ['ユニークキー', "和暦_退園年月日", "年_退園年月日", "月_退園年月日", "日_退園年月日"],
fields: ['ユニークキー', '担当者名', "保護者ログインID", "和暦_退園年月日", "年_退園年月日", "月_退園年月日", "日_退園年月日"],
});
} catch (e) {
showError(true, '園児台帳アプリのデータ読み取りエラー\n - ' + e);
loading(false);
}
}
@@ -85,16 +114,40 @@ class BatchCreateHandler {
return;
}
const filteredRecords = records.filter((each) => !alreadyGeneratedKeys[each["園児ユニークキー"].value])
if (!filteredRecords.length) {
return;
const param = {
app: env["園児別出欠簿入力"].appId,
}
const addResult = await api.record.addAllRecords({
app: env["園児別出欠簿入力"].appId,
records: filteredRecords
const insert = [];
const update = [];
records.forEach(record => {
const id = alreadyGeneratedKeys[record["園児ユニークキー"].value]
if (id) {
update.push({
id,
record
});
} else {
insert.push(record);
}
});
return addResult;
let insertResult;
if (insert.length) {
param.records = insert;
insertResult = await api.record.addAllRecords(param);
}
let updateResult;
if (update.length) {
param.records = update;
// 本アプリの当日データでテーブルを上書きし、対応する園児データがない行を【削除】します
updateResult = await api.record.updateAllRecords(param);
}
return {
insertResult,
updateResult
}
} catch (e) {
showError(true, '本アプリのデータ作成失敗\n - ' + e);
}
@@ -109,7 +162,7 @@ class BatchCreateHandler {
condition: `登園日 = "${todayString}" and 園児ユニークキー in (${needCreateUniqueKeys})`
})
return alreadyGeneratedResult.reduce((map, each) => {
map[each["園児ユニークキー"].value] = true;
map[each["園児ユニークキー"].value] = each["$id"].value;
return map;
}, {});
} catch (e) {

View File

@@ -1,10 +1,11 @@
class ExtractHandler {
constructor(headerSpace) {
const elements = createBtnGroupArea('extract-action-area', '出欠集計表出力', this.handleExtractData, {
btnElId: 'extract-btn',
yearElId: 'extract-year',
monthElId: 'extract-month',
classElId: 'extract-classroom'
termElId: 'extract-term'
})
if (!elements) {
return;
@@ -12,9 +13,341 @@ class ExtractHandler {
headerSpace.appendChild(elements['extract-action-area']);
}
handleExtractData(e, { year, month, className }) {
const fileName = getExcelName(env["園児別出欠簿入力"], year + month + '_' + className + '組');
console.log(fileName);
handleExtractData = async (e, { year, month, term }) => {
loading(true, '帳票出力中...');
showError(false);
const api = new KintoneRestAPIClient();
// 本アプリからデータを読み取る
const records = await this.getRecords(api, year, month, term);
if (!records) {
// エラー
loading(false);
return e;
}
const recordMap = this.buildRecordMap(records);
const childMaster = await this.getChildMstRecords(api, year, month, term, Object.keys(recordMap.byId));
if (!childMaster) {
// エラー
loading(false);
return e;
}
const dayMaster = await this.getDayMstRecords(api, year, month);
if (!dayMaster) {
// エラー
loading(false);
return e;
}
const termTeacher = await this.getTermTeacherMstRecords(api, term);
if (termTeacher === undefined) {
// エラー
loading(false);
return e;
}
const excelName = env["園児別出欠簿入力"].excelName;
await createExcelAndDownload({
api,
excelName,
exportName: getExcelName(excelName, year + month + '_' + term),
bizLogic: this.writeExcel({ records, recordMap, childMaster, dayMaster, termTeacher }, term, getJapaneseEraDate(new Date(year, month - 1, 1))),
});
loading(false);
}
getRecords = async (api, year, month, term) => {
const firstDate = getFormatDateString(year, month, 1)
const lastDate = getFormatDateString(getLastDate(year, month));
try {
return await api.record.getAllRecordsWithId({
app: env["園児別出欠簿入力"].appId,
condition: `登園日 >= "${firstDate}" and 登園日 <= "${lastDate}" and 学年 in ("${term}")`
});
} catch (e) {
showError(true, '本アプリのデータ読み取りエラー\n - ' + e);
}
}
getChildMstRecords = async (api, year, month, term, uniqueKeys) => {
const date = getJapaneseEraDate(new Date(year, month - 1, 1))
const prevMonth = getJapaneseEraDate(getLastDate(year, month - 1));
const result = {};
try {
result['入園'] = await api.record.getAllRecordsWithId({
app: env["園児台帳"].appId,
fields: ['性別'],
condition: `和暦_入園年月日 in ("${date.era}") and 年_入園年月日 = "${date.year}" and 月_入園年月日 = "${date.month}" and 学年 in ("${term}")`
});
result['退園'] = await api.record.getAllRecordsWithId({
app: env["園児台帳"].appId,
fields: ['性別'],
condition: `和暦_退園年月日 in ("${prevMonth.era}") and 年_退園年月日 = "${prevMonth.year}" and 月_退園年月日 = "${prevMonth.month}" and 学年 in ("${term}")`
});
if (uniqueKeys?.length) {
result['在籍'] = await api.record.getAllRecordsWithId({
app: env["園児台帳"].appId,
fields: ['ユニークキー', '性別'],
condition: `ユニークキー in ("${uniqueKeys.join('", "')}")`
});
}
return result;
} catch (e) {
showError(true, '園児台帳のデータ読み取りエラー\n - ' + e);
}
}
getDayMstRecords = async (api, yearStr, monthStr) => {
let year = Number(yearStr);
const month = Number(monthStr);
// ※年度=4月翌年3月までの区切りとする2025年3月→2024年度
if (month < 4) {
year--;
}
try {
const data = await api.record.getAllRecordsWithId({
app: env["保育・教育日数マスタ"].appId,
fields: ['教育日数' + month, '保育日数' + month],
condition: `年度 = "${year}"`
});
return data && data[0];
} catch (e) {
showError(true, '保育・教育日数マスタのデータ読み取りエラー\n - ' + e);
}
}
getTermTeacherMstRecords = async (api, term) => {
try {
const data = await api.record.getAllRecordsWithId({
app: env["担任マスタ"].appId,
fields: ['担任'],
condition: `学年 in ("${term}")`
});
return data && data[0] && data[0]['担任']?.value.map((x)=>x.name).join('、') || '';
} catch (e) {
showError(true, '担任マスタのデータ読み取りエラー\n - ' + e);
}
}
writeExcel = ({ records, recordMap, childMaster, dayMaster, termTeacher }, term, { era, year, westernYear, month }) => {
const teachDays = Number(dayMaster['教育日数' + month].value);
const careDays = Number(dayMaster['保育日数' + month].value);
return async (api, worksheet) => {
const baseCells = findCellsInfo(worksheet, ['13', '16', '19', '25', '(担 任)', '1', '番号']);
// header
updateCell(worksheet, { base: baseCells['13'][0], up: 1 }, era);
updateCell(worksheet, { base: baseCells['16'][0], up: 1 }, year + '年');
updateCell(worksheet, { base: baseCells['19'][0], up: 1 }, month + '月');
updateCell(worksheet, { base: baseCells['25'][0], up: 1 }, term);
const weekRow = worksheet.getRow(baseCells['1'][0].row + 1);
const startCol = baseCells['1'][0].col;
const weekStart = new Date(westernYear, month - 1, 1).getDay();
const lastDate = getLastDate(westernYear, month).getDate();
for (let i = 0; i < lastDate; i++) {
updateCell(weekRow, { base: { col: startCol + i } }, WEEK[(weekStart + i) % 7]);
}
if (!records.length) {
return;
}
updateCell(worksheet, { base: baseCells['(担 任)'][0], down: 1 }, termTeacher);
fillMainPage(baseCells, worksheet, recordMap);
fillFooter(worksheet, recordMap);
}
function fillMainPage(baseCells, worksheet, recordMap) {
baseCells['番号'] = baseCells['番号'].filter((_, index) => index % 2 !== 0);
const sortedRecords = Object.values(recordMap.byId).sort((a, b) => a['idKey'].localeCompare(b['idKey']));
const lastPage = 2;
const baseForTemplate = baseCells['番号'][lastPage - 1]; // 番号 merged 2 rows
const pageSize = 15;
const totalPages = Math.ceil(sortedRecords.length / pageSize);
// make new copy
if (totalPages > 2) {
const copyPageRowStart = baseForTemplate.row - 2;
const copyPageRowEnd = baseForTemplate.row + (pageSize);
createCopyFromTemplate(worksheet, {
startPage: lastPage + 1,
totalPages,
copyPageRowStart,
copyPageRowEnd,
callback: (newPage, rowCount) => {
['番号'].forEach((label) => {
const last = baseCells[label][newPage - 2];
baseCells[label].push({
col: last.col,
row: last.row + rowCount
});
});
}
});
}
for (let i = 0; i < totalPages; i++) {
const childLabelCell = baseCells['番号'][i];
let currentRow = childLabelCell.row + 1;
for (let j = 0; j < pageSize; j++) {
const index = i * pageSize + j;
const recordWrapper = sortedRecords[index];
if (!recordWrapper) {
break;
}
const row = worksheet.getRow(currentRow);
const base = { row: currentRow, col: 1 };
// 番号
updateCell(row, { base }, recordWrapper['id']);
// 児 童 氏 名
updateCell(row, { base, right: 1 }, recordWrapper['name']);
const reasons = [];
const sum = {
'出席': 0,
'出席停止': 0,
'病欠': 0,
'自欠': 0,
}
// 日
recordWrapper.list.forEach((record, i) => {
const res = record["出欠"].value;
sum[res]++;
if (res === '出席') {
return;
}
if (res === '出席停止' && record["出席停止理由"].value) {
reasons.push(record["出席停止理由"].value);
}
updateCell(row, { base, right: 2 + i }, res === '出席停止' ? '×' : '');
})
// 出 席
updateCell(row, { base, right: 33 }, sum['出席']);
updateCell(row, { base, right: 34 }, sum['出席停止']);
// 欠 席
updateCell(row, { base, right: 35 }, sum['病欠']);
updateCell(row, { base, right: 36 }, sum['自欠']);
// 教育日数
// updateCell(row, { base, right: 37 }, sum['出席'] + sum['出席停止'] - sum['病欠'] - sum['自欠']);
updateCell(row, { base, right: 37 }, teachDays - sum['病欠'] - sum['自欠']);
// 備考
updateCell(row, { base, right: 38 }, reasons.join("\n"));
currentRow += 1;
}
worksheet.getRow(childLabelCell.row + pageSize + 1).addPageBreak();
}
}
function fillFooter(worksheet, recordMap) {
const baseCells = findCellsInfo(worksheet, ['合 計', '保 育 日 数', '男', '女', '欠 席 総 数', '出 席 率', '']);
// 合 計
const totalAreaRow = worksheet.getRow(baseCells['合 計'][0].row);
const totalAreaRow2 = worksheet.getRow(baseCells['合 計'][1].row);
const totalAreaRow3 = worksheet.getRow(baseCells['合 計'][2].row);
const base = { col: 3 };
const dateList = recordMap.sum.list;
for (let i = 0; i < dateList.length; i++) {
if (dateList[i]) {
updateCell(totalAreaRow, { base, right: i }, dateList[i]['出席']);
updateCell(totalAreaRow2, { base, right: i }, dateList[i]['出停']);
updateCell(totalAreaRow3, { base, right: i }, dateList[i]['欠席']);
}
}
// 保 育 日 数
updateCell(worksheet, { base: baseCells['保 育 日 数'][0], down: 1 }, careDays);
// 入 園 数
let list = childMaster['入園'];
let [male, female] = groupingBySex(list);
updateCell(worksheet, { base: baseCells['男'][0], right: 1 }, male.length);
updateCell(worksheet, { base: baseCells['女'][0], right: 1 }, female.length);
// 退 園 数
list = childMaster['退園'];
[male, female] = groupingBySex(list);
updateCell(worksheet, { base: baseCells['男'][1], right: 1 }, male.length);
updateCell(worksheet, { base: baseCells['女'][1], right: 1 }, female.length);
// 在 籍 数
list = childMaster['在籍'];
[male, female] = groupingBySex(list);
updateCell(worksheet, { base: baseCells['男'][2], right: 1 }, male.length);
updateCell(worksheet, { base: baseCells['女'][2], right: 1 }, female.length);
const total = recordMap.sum['欠席'] + recordMap.sum['出席'];
if (total) {
// 出 席 総 数
updateCell(worksheet, { base: baseCells['欠 席 総 数'][0], left: 1 }, recordMap.sum['出席']);
// 欠 席 総 数
updateCell(worksheet, { base: baseCells['出 席 率'][0], left: 1 }, recordMap.sum['欠席']);
// 出 席 率
updateCell(worksheet, { base: baseCells[''][0], right: 1 }, Math.round(recordMap.sum['出席'] / total * 1000) / 10 + '');
}
}
}
buildRecordMap = (records) => {
const recordMap = {
sum: {
'出席': 0,
'欠席': 0,
list: []
},
byId: {}
}
records.forEach((record) => {
const dateIndex = Number(record['登園日'].value.split('-')[2]) - 1;
const idKey = record['園児ユニークキー'].value;
let attendance = recordMap.byId[idKey];
if (!attendance) {
attendance = {
idKey,
id: record['出席番号'].value,
name: record['園児名'].value,
list: [],
};
recordMap.byId[idKey] = attendance
}
attendance.list[dateIndex] = record;
const status = record["出欠"].value;
if (status === '出席' || status === '出席停止') {
recordMap.sum['出席']++;
} else if (status === '病欠' || status === '自欠') {
recordMap.sum['欠席']++;
}
let dateSum = recordMap.sum.list[dateIndex];
if (!dateSum) {
dateSum = {
'出席': 0,
'出停': 0,
'欠席': 0,
};
recordMap.sum.list[dateIndex] = dateSum
}
if (status === '出席') {
dateSum['出席']++;
} else if (status === '出席停止') {
dateSum['出停']++;
} else if (status === '病欠' || status === '自欠') {
dateSum['欠席']++;
}
});
return recordMap;
}
static getInstance(headerSpace) {

View File

@@ -12,7 +12,197 @@ class Link1Handler {
headerSpace.appendChild(elements['link-1-action-area']);
}
handleLink(e, { year, month }) {
handleLink = async (e, { year, month }) => {
loading(true, '日誌データ連携中...');
showError(false);
const api = new KintoneRestAPIClient();
// 本アプリからデータを読み取る
const currentAppRecords = await this.getRecords(api, year, month);
if (!currentAppRecords) {
// エラー
loading(false);
return;
}
// console.log(currentAppRecords);
const recordsWrapper = await this.generateRecords(api, year, month, currentAppRecords);
// console.log(recordsWrapper);
const result = await this.insertOrUpdateData(api, recordsWrapper);
if (result) {
// showSuccess(true, "日誌データ連携作成完了");
this.showSuccessDialog(result, year, month);
}
loading(false);
}
showSuccessDialog(result, year, month) {
const dateString = year + '年' + month + '月';
const contentEl = document.createElement('div');
contentEl.style.fontSize = '16px';
if (result.updateResult && result.insertResult) {
contentEl.innerHTML = `${dateString}${result.updateResult.records.length}人の園児の日誌を更新し、${result.insertResult.records.length}人の園児の日誌を新規作成しました。`
} else if (result.updateResult) {
contentEl.innerHTML = `${dateString}${result.updateResult.records.length}人の園児の日誌が更新されました。`;
} else if (result.insertResult) {
contentEl.innerHTML = `${dateString}${result.insertResult.records.length}人の園児の日誌が生成されました`;
} else {
contentEl.innerHTML = `${dateString}の園児の日誌に生成するデータはありませんでした。`
}
showDialog({
title: '日誌データ連携作成完了',
content: contentEl,
ok: 'アプリへ行く',
cancel: '閉じる',
onOk: () => { window.open(`${location.origin}/k/${env["0,1歳日誌出力用"].appId}/`) },
});
}
insertOrUpdateData = async (api, recordsWrapper) => {
try {
const param = {
app: env["0,1歳日誌出力用"].appId,
}
let insertResult;
if (recordsWrapper['insert'].length) {
param.records = recordsWrapper['insert'];
insertResult = await api.record.addAllRecords(param);
}
let updateResult;
if (recordsWrapper['update'].length) {
param.records = recordsWrapper['update'];
// 本アプリの当日データでテーブルを上書きし、対応する園児データがない行を【削除】します
updateResult = await api.record.updateAllRecords(param);
}
return {
recordsWrapper,
insertResult,
updateResult
};
} catch (e) {
showError(true, '日誌データ連携作成失敗\n - ' + e);
}
};
generateRecords = async (api, year, month, records) => {
const generatedRecordIdMap = await this.getGeneratedRecordIdMap(api, year, month);
if (!generatedRecordIdMap) {
// エラー
loading(false);
return;
}
const kidsMap = {};
records.forEach((record) => {
const uniqueKey = record['園児ユニークキー'].value;
let map = kidsMap[uniqueKey];
if (!map) {
map = {
existId: generatedRecordIdMap[uniqueKey],
list: [],
'年': { 'value': Number(year) },
'月': { 'value': Number(month) },
'クラス': { 'value': record['クラス'].value },
'出席番号': { 'value': record['出席番号'].value },
'園児名': { 'value': record['園児名'].value },
'園児ユニークキー': { 'value': uniqueKey },
'帳票出力用_テーブル': { 'value': undefined },
};
kidsMap[uniqueKey] = map
}
record['date'] = Number(record['登園日'].value.split('-')[2]);
map.list.push(record);
});
// console.log(generatedRecordIdMap);
// console.log(kidsMap);
const result = {
'update': [],
'insert': []
}
Object.values(kidsMap).forEach((recordWrapper) => {
const subtable = Array.from({ length: 31 }, (_e, i) => ({
value: {
'日付': { 'value': i + 1 },
}
}));
recordWrapper.list.forEach((record) => {
subtable[record['date'] - 1] = {
value: {
'日付': { 'value': record['date'] },
'出欠': { 'value': record['出欠'].value },
'降園': { 'value': record['帰園時刻'].value },
'検温時刻1': { 'value': record['検温時刻1'].value },
'体温1': { 'value': record['体温1'].value },
'検温時刻2': { 'value': record['検温時刻2'].value },
'体温2': { 'value': record['体温2'].value },
'検温時刻3': { 'value': record['検温時刻3'].value },
'体温3': { 'value': record['体温3'].value },
'食事量': { 'value': record['食事量_結合'].value },
'排便': { 'value': record['排便'].value },
'睡眠開始時間1': { 'value': record['睡眠開始時間1'].value },
'睡眠終了時間1': { 'value': record['睡眠終了時間1'].value },
'睡眠開始時間2': { 'value': record['睡眠開始時間2'].value },
'睡眠終了時間2': { 'value': record['睡眠終了時間2'].value },
'保護者から': { 'value': record['保護者から'].value },
'園での様子_伝達事項': { 'value': record['園での様子_伝達事項'].value },
'評価反省': { 'value': record['評価反省'].value },
}
}
})
recordWrapper['帳票出力用_テーブル'].value = subtable;
const id = recordWrapper['existId'];
delete recordWrapper['list'];
delete recordWrapper['existId'];
if (id) {
result['update'].push({
id,
record: recordWrapper
});
} else {
result['insert'].push(recordWrapper);
}
});
return result;
}
getRecords = async (api, year, month) => {
const firstDate = getFormatDateString(year, month, 1)
const lastDate = getFormatDateString(getLastDate(year, month));
try {
return await api.record.getAllRecordsWithId({
app: env["園児別出欠簿入力"].appId,
condition: `学年 in ("0歳児", "1歳児") and 登園日 >= "${firstDate}" and 登園日 <= "${lastDate}"`
});
} catch (e) {
showError(true, '本アプリのデータ読み取りエラー\n - ' + e);
loading(false);
}
}
getGeneratedRecordIdMap = async (api, year, month) => {
try {
const result = await api.record.getAllRecordsWithId({
app: env["0,1歳日誌出力用"].appId,
fields: ["$id", "園児ユニークキー"],
condition: `年 = ${year} and 月 = ${month}`
});
const map = {}
result.forEach((record) => {
const uniqueKey = record['園児ユニークキー'].value;
map[uniqueKey] = record['$id'].value;
});
return map;
} catch (e) {
showError(true, '日誌データ読み取りエラー\n - ' + e);
loading(false);
}
}
static getInstance(headerSpace) {

View File

@@ -15,9 +15,189 @@ class Link2Handler {
}
handleLink(e, { year, month, date }) {
handleLink = async (e, { year, month, date }) => {
loading(true, '日誌データ連携中...');
showError(false);
const api = new KintoneRestAPIClient();
const dateString = getFormatDateString(year, month, date)
// 本アプリからデータを読み取る
const currentAppRecords = await this.getRecords(api, dateString);
if (!currentAppRecords) {
// エラー
loading(false);
return;
}
const groupedRecords = this.groupingAndSort(currentAppRecords);
const records = [];
for (const item of groupedRecords) {
records.push(this.generateRecord(dateString, item));
}
const result = await this.insertOrUpdateData(api, records, dateString);
if (result) {
// showSuccess(true, "日誌データ連携作成完了");
this.showSuccessDialog(result, year, month, date);
}
loading(false);
}
showSuccessDialog(result, year, month, date) {
const dateString = year + '年' + month + '月' + date + '日';
const contentEl = document.createElement('div');
contentEl.style.fontSize = '16px';
if (!result.size) {
contentEl.innerHTML = `${dateString}の園児別テーブルに追加するデータはありませんでした。`;
} else if (!result.insert) {
contentEl.innerHTML = `${dateString}の園児別テーブルに${result.size}件のデータを再作成しました。`;
} else if (!result.update) {
contentEl.innerHTML = `${dateString}の日誌は生成しましたが、園児別テーブルに${result.size}件のデータを追加しました。`;
} else {
contentEl.innerHTML = `${dateString}${result.update}人の園児の日誌を更新し、${result.insert}人の園児の日誌を新規作成しました。`
}
showDialog({
title: '日誌データ連携作成完了',
content: contentEl,
ok: 'アプリへ行く',
cancel: '閉じる',
dataHolder: result,
onOk: () => { window.open(`${location.origin}/k/${env["2歳以上日誌出力用"].appId}/`) },
});
}
getRecords = async (api, dateString) => {
try {
return await api.record.getAllRecordsWithId({
app: env["園児別出欠簿入力"].appId,
fields: ['クラス', '学年', "園児名", "園での様子_伝達事項", "評価反省"],
condition: `学年 not in ("", "0歳児", "1歳児") and 登園日 = "${dateString}"`
});
} catch (e) {
showError(true, '本アプリのデータ読み取りエラー\n - ' + e);
loading(false);
}
}
groupingAndSort = (data) => {
const groupedByGrade = {};
// 1. group by 学年
data.forEach((record) => {
const grade = record['学年'].value;
if (!groupedByGrade[grade]) {
groupedByGrade[grade] = [];
}
groupedByGrade[grade].push(record);
});
// 2. sort by クラス
const classOrder = {};
classItems.forEach((item, index) => {
classOrder[item.value] = index;
});
for (const grade in groupedByGrade) {
groupedByGrade[grade].sort((a, b) => {
return classOrder[a["クラス"].value] - classOrder[b["クラス"].value];
});
}
// 3. return a list
return termItems.reduce((acc, term) => {
const grade = term.value;
// Skip 0歳児 and 1歳児
if (grade === "0歳児" || grade === "1歳児") {
return acc;
}
acc.push({
grade: grade,
items: groupedByGrade[grade] || []
});
return acc;
}, []);
}
generateRecord = (todayString, groupedRecords) => {
return {
'登園日': { 'value': todayString },
'学年': { 'value': groupedRecords.grade },
'園児別テーブル': {
'value': groupedRecords.items.map((record) => {
return {
'value': {
'クラス名': { 'value': record['クラス'].value },
'名前': { 'value': record['園児名'].value },
'子どもの様子': { 'value': record['園での様子_伝達事項'].value },
'反省評価_園児別テーブル': { 'value': record['評価反省'].value },
}
}
})
}
};
}
insertOrUpdateData = async (api, records, dateString) => {
const results = [];
const generatedRecords = await api.record.getAllRecordsWithId({
app: env["2歳以上日誌出力用"].appId,
fields: ["$id", "学年"],
condition: `登園日 = "${dateString}"`
})
const existIdMap = generatedRecords.reduce((map, item) => {
const grade = item['学年'].value;
const id = item['$id'].value;
map[grade] = id;
return map;
}, {});
for (const record of records) {
try {
const generatedRecordId = existIdMap[record["学年"].value];
const param = {
app: env["2歳以上日誌出力用"].appId,
record
}
const result = {
id: generatedRecordId,
type: generatedRecordId ? 'update' : 'insert',
size: record["園児別テーブル"].value.length,
};
let awaitResult;
if (result.type === 'update') {
param.id = generatedRecordId;
// 本アプリの当日データでテーブルを上書きし、対応する園児データがない行を【削除】します
awaitResult = await api.record.updateRecord(param);
} else {
awaitResult = await api.record.addRecord(param);
}
Object.assign(result, awaitResult);
results.push(result);
} catch (e) {
showError(true, '日誌データ連携作成失敗\n - ' + e);
}
}
return results.reduce((result, item) => {
// Sum all sizes
result.size += item.size;
// Sum based on type
if (item.type === 'insert') {
result.insert += item.size;
} else if (item.type === 'update') {
result.update += item.size;
}
return result;
}, {
size: 0, // Total sum of all sizes
insert: 0, // Sum of insert sizes
update: 0 // Sum of update sizes
});;
};
static getInstance(headerSpace) {
if (!Link2Handler.instance) {
Link2Handler.instance = new Link2Handler(headerSpace);

View File

@@ -0,0 +1,57 @@
(function () {
"use strict";
// ------------------- 詳細画面表示時の処理 -------------------
kintone.events.on('mobile.app.record.detail.show', function (event) {
const area = kintone.mobile.app.record.getSpaceElement('header-clocking-btn-area');
const clockIn = createBtn('clock-in', '登園', dateToFieldInDetail('登園時刻'));
const clockOut = createBtn('clock-out', '帰園', dateToFieldInDetail('帰園時刻'));
area.appendChild(clockIn);
area.appendChild(clockOut);
hideSpaceField(['clock-in-btn-area', 'clock-out-btn-area']);
return event;
});
function dateToFieldInDetail(fieldCode) {
return async function (e) {
await new KintoneRestAPIClient().record.updateRecord({
app: kintone.mobile.app.getId(),
id: kintone.mobile.app.record.getId(),
record: {
[fieldCode]: {
value: getCurrentTime()
}
}
});
location.reload();
}
}
// ------------------- 編集画面表示時の処理 -------------------
kintone.events.on(['mobile.app.record.create.show', 'mobile.app.record.edit.show'], function (event) {
const clockIn = createBtn('clock-in', '登園', dateToFieldInEdit('登園時刻'));
kintone.mobile.app.record.getSpaceElement('clock-in-btn-area').appendChild(clockIn);
const clockOut = createBtn('clock-out', '帰園', dateToFieldInEdit('帰園時刻'));
kintone.mobile.app.record.getSpaceElement('clock-out-btn-area').appendChild(clockOut);
return event;
});
function dateToFieldInEdit(fieldCode) {
return function (e) {
var record = kintone.mobile.app.record.get();
record['record'][fieldCode]['value'] = getCurrentTime();
kintone.mobile.app.record.set(record);
}
}
function getCurrentTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
return `${hours}:${minutes}`;
}
})();

View File

@@ -0,0 +1,3 @@
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)});
//# sourceMappingURL=FileSaver.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,496 @@
/** @copyright 2018 Planning Village Corporation Co., Ltd. */
(function () {
'use strict';
// [IE11] Polyfill of Object.values
if (!Object.values) {
const ownKeys = (
(typeof Reflect === 'object' && typeof Reflect.ownKeys === 'function') ? Reflect.ownKeys :
// @ts-ignore
(typeof Object.getOwnPropertySymbols === 'function') ? (function (O) {
// @ts-ignore
return Object.getOwnPropertyNames(O).concat(Object.getOwnPropertySymbols(O));
}) :
Object.getOwnPropertyNames);
// @ts-ignore
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
// @ts-ignore
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
// @ts-ignore
const concat = Function.bind.call(Function.call, Array.prototype.concat);
//@ts-ignore
Object.values = function values(O) {
//@ts-ignore
return reduce(ownKeys(O), function (v, k) {
return concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : [])
}, [])
};
}
// [IE11] Polyfill of Number.isNaN
if (!Number.isNaN) {
Number.isNaN = function (value) {
return value !== null && (value != value || +value != value);
};
}
// [IE11] Polyfill of String.prototype.startsWith
if (!String.prototype.startsWith) {
Object.defineProperty(String.prototype, 'startsWith', {
//@ts-ignore
value: function (search, rawPos) {
var pos = rawPos > 0 ? rawPos | 0 : 0;
return this.substring(pos, pos + search.length) === search;
}
});
}
// [IE11] Polyfill of String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function (search, start) {
'use strict';
//@ts-ignore
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) {
start = 0;
}
return this.indexOf(search, start) !== -1;
};
}
// [IE11] Polyfill of Array.prototype.findIndex
if (!Array.prototype.findIndex) {
// @ts-ignore
Array.prototype.findIndex = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
//@ts-ignore
window.pvc = window.pvc || {};
//@ts-ignore
window.pvc.lib = window.pvc.lib || {};
//@ts-ignore
window.pvc.lib.exceljsUtil = (function () {
const exceljs = {
/**
* ワークブックの blob を読み取ります。
* @param {Blob} blob Blob
* @return {Promise<ExcelJS.Workbook>} ワークブック
*/
loadWorkbookBlob: function (blob) {
return new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function (e) {
// @ts-ignore
resolve(e.target.result);
};
reader.onerror = function (e) {
reject(e);
}
reader.onabort = function (e) {
reject(e);
}
reader.readAsArrayBuffer(blob);
}).then(function (arrayBuffer) {
// 非公式のメンバー load を使用
// @ts-ignore
return new ExcelJS.Workbook().xlsx.load(arrayBuffer);
});
},
/**
* ワークブックを Blob に変換します。
* @param {ExcelJS.Workbook} workbook ワークブック
* @return {Promise<Blob>} Blob
*/
saveWorkbookToBlob: function (workbook) {
// writeBuffer の戻り値 ExcelJS.Buffer はブラウザ環境では Uint8Array となる
return workbook.xlsx.writeBuffer().then(function (ua) {
return new Blob([ua], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
});
},
/**
* 行をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー元の開始行番号(1から開始)
* @param {number} [bottomRowNumber] コピー元の終了行番号(1から開始)。省略時は開始行番号と同じ値となります。
* @returns {pvc.lib.exceljsUtil.CopiedRowsInfo} 行のコピー情報
*/
copyRows: function (worksheet, topRowNumber, bottomRowNumber) {
const _bottomRowNumber = (bottomRowNumber == null ? topRowNumber : bottomRowNumber);
if (topRowNumber < 0 || _bottomRowNumber < topRowNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const rows = [];
for (let r = topRowNumber; r <= _bottomRowNumber; r++) {
const row = worksheet.getRow(r);
const cells = [];
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
rows.push({
cells: cells,
height: row.height,
// 非公式のメンバー style を使用
// @ts-ignore
style: row.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.top && merge.bottom <= _bottomRowNumber);
}).map(function (merge) {
return {
top: merge.top - topRowNumber,
right: merge.right,
bottom: merge.bottom - topRowNumber,
left: merge.left,
};
}));
return deepCopyObject({
rows: rows,
merges: merges
});
},
/**
* 指定された開始行を起点に、コピーした行を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
pasteRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRowNumber = topRowNumber + copiedRowsInfo.rows.length - 1;
if (topRowNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の行のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (topRowNumber <= merge.bottom && merge.top <= bottomRowNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedRowsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
topRowNumber + mergeInfo.top,
mergeInfo.left,
topRowNumber + mergeInfo.bottom,
mergeInfo.right);
});
// セルをペースト
copiedRowsInfo.rows.forEach(function (rowInfo, i) {
const row = worksheet.getRow(topRowNumber + i);
row.height = rowInfo.height;
// 非公式のメンバー style を使用
// @ts-ignore
row.style = rowInfo.style;
for (let c = 1; c <= worksheet.columnCount; c++) {
const cell = row.getCell(c);
const cellInfo = rowInfo.cells[c - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始行を起点に、コピーした行を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
*/
insertRows: function (worksheet, topRowNumber, copiedRowsInfo) {
const bottomRows = (
topRowNumber <= worksheet.rowCount ?
exceljs.copyRows(worksheet, topRowNumber, worksheet.rowCount) :
null);
exceljs.pasteRows(worksheet, topRowNumber, copiedRowsInfo);
if (bottomRows) {
exceljs.pasteRows(worksheet, topRowNumber + copiedRowsInfo.rows.length, bottomRows);
}
},
/**
* 列をコピーします。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー元の開始列の列番号(1から開始)または列ラベル。終了列の引数を省略して、代わりに "A:B" の形式でコピー元の列の範囲を指定することも可能。
* @param {number | string} [rightColumn] コピー元の終了列の列番号(1から開始)または列ラベル。省略時は開始列と同じとみなされます(開始列を "A:B" の形式で指定された場合を除く)。
* @returns {pvc.lib.exceljsUtil.CopiedColumnsInfo} 列のコピー情報
*/
copyColumns: function (worksheet, leftColumn, rightColumn) {
if (rightColumn == null) {
rightColumn = leftColumn;
if (typeof leftColumn === 'string') {
const sp = leftColumn.split(':');
if (sp.length === 2) {
leftColumn = sp[0];
rightColumn = sp[1];
}
}
}
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = ((typeof rightColumn === 'number') ? rightColumn : exceljs.columnLetterToColumnNumber(rightColumn));
if (leftColumnNumber < 0 || rightColumnNumber < leftColumnNumber) {
throw new Error('コピー元の領域とが不正です。');
}
// コピー元の領域の情報を収集
const columns = [];
for (let c = leftColumnNumber; c <= rightColumnNumber; c++) {
const column = worksheet.getColumn(c);
const cells = [];
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(c);
cells.push({
value: cell.value,
style: cell.style,
});
}
columns.push({
cells: cells,
width: column.width,
style: column.style,
});
}
// コピー元の領域の内部に収まるマージを収集
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.left && merge.right <= rightColumnNumber);
}).map(function (merge) {
return {
top: merge.top,
right: merge.right - leftColumnNumber,
bottom: merge.bottom,
left: merge.left - leftColumnNumber,
};
}));
return {
columns: columns,
merges: merges
};
},
/**
* 指定された開始列を起点に、コピーした列を貼り付けます。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* コピー先の領域を一部でも含むマージは全て解除されます。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedColumnsInfo 列のコピー情報
*/
pasteColumns: function (worksheet, leftColumn, copiedColumnsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumnNumber = leftColumnNumber + copiedColumnsInfo.columns.length - 1;
if (leftColumnNumber < 0) {
throw new Error('コピー先の領域とが不正です。');
}
// コピー先の列のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
getWorksheetMerges(worksheet).filter(function (merge) {
return (leftColumnNumber <= merge.right && merge.left <= rightColumnNumber);
}).forEach(function (merge) {
worksheet.unMergeCells(merge.range);
});
// マージをペースト
copiedColumnsInfo.merges.forEach(function (mergeInfo) {
worksheet.mergeCells(
mergeInfo.top,
leftColumnNumber + mergeInfo.left,
mergeInfo.bottom,
leftColumnNumber + mergeInfo.right);
});
// セルをペースト
copiedColumnsInfo.columns.forEach(function (columnInfo, i) {
const column = worksheet.getColumn(leftColumnNumber + i);
column.width = columnInfo.width;
column.style = columnInfo.style;
for (let r = 1; r <= worksheet.rowCount; r++) {
const cell = worksheet.getRow(r).getCell(leftColumnNumber + i);
const cellInfo = columnInfo.cells[r - 1];
if (cellInfo) {
cell.value = cellInfo.value;
cell.style = cellInfo.style;
} else {
exceljs.clearCell(cell);
}
}
});
},
/**
* 指定された開始列を起点に、コピーした列を挿入します。
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
* @param {ExcelJS.Worksheet} worksheet ワークシート
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedRowsInfo 列のコピー情報
*/
insertColumns: function (worksheet, leftColumn, copiedRowsInfo) {
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
const rightColumns = (
leftColumnNumber <= worksheet.columnCount ?
exceljs.copyColumns(worksheet, leftColumnNumber, worksheet.columnCount) :
null);
exceljs.pasteColumns(worksheet, leftColumnNumber, copiedRowsInfo);
if (rightColumns) {
exceljs.pasteColumns(worksheet, leftColumnNumber + copiedRowsInfo.columns.length, rightColumns);
}
},
/**
* セルをクリアします。
* @param {ExcelJS.Cell} cell セル
*/
clearCell: function (cell) {
cell.value = null;
/// @ts-ignore
cell.style = cell._mergeStyle(cell._row.style, cell._column.style, {});
},
/**
* 列ラベルから列番号(1から開始)を取得します。
* @param {string} columnLetter 列ラベル
* @return {number} 列番号(1から開始)
*/
columnLetterToColumnNumber: function (columnLetter) {
const letter = columnLetter.toUpperCase();
const l = letter.length;
const charCodeA = 65;
let result = 0;
for (let i = 0, j = l - 1; i < l; i++, j--) {
result += (letter.charCodeAt(j) - charCodeA + 1) * (i === 0 ? 1 : Math.pow(26, i));
}
return result;
},
/**
* 日付のセルの値を作成します。
* @param {Date} date
* @returns {any}
*/
createDateCellValue: function (date) {
// UTC にしないと正しく Excel に反映されない
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
},
deepCopyObject: deepCopyObject,
};
/**
* ワークシートのマージを取得します。
* @param {ExcelJS.Worksheet} worksheet ワークシート
*/
function getWorksheetMerges(worksheet) {
// 非公式のメンバー _merges を使用
// @ts-ignore
const mergesDic = worksheet._merges;
return Object.keys(mergesDic).map(function (address) {
return mergesDic[address];
});
}
/**
* オブジェクのコピーします。ディープコピーとなります。
* @template T
* @param {T} obj
* @returns {T}
*/
function deepCopyObject(obj) {
return copyObject(obj);
//@ts-ignore
function copyObject(obj) {
return (
obj == null ? null :
Array.isArray(obj) ? copyArray(obj) :
isDate(obj) ? copyDate(obj) :
isDic(obj) ? copyDic(obj) :
obj);
}
//@ts-ignore
function copyArray(array) {
//@ts-ignore
return array.map(function (item) {
return copyObject(item);
});
}
//@ts-ignore
function isDate(obj) {
return (obj != null && Object.prototype.toString.call(obj) === '[object Date]');
}
//@ts-ignore
function copyDate(date) {
return (isNaN(date) ? null : new Date(date.getTime()));
}
//@ts-ignore
function isDic(obj) {
return (obj != null && typeof obj === 'object');
}
//@ts-ignore
function copyDic(dic) {
let result = {};
for (let key in dic) {
//@ts-ignore
result[key] = copyObject(dic[key]);
}
return result;
}
}
return exceljs;
}());
})();

View File

@@ -2,15 +2,17 @@
"use strict";
const APP_ENV = env["園児別出欠簿入力"];
addApproveFlowAction();
kintone.events.on("app.record.index.show", (event) => {
const headerSpace = getHeaderSpace('single-label-line');
if (event.viewId === APP_ENV.view["0,1歳日誌データ連携用途"]) {
if (event.viewId === APP_ENV.view.linkFor0to1) {
Link1Handler.getInstance(headerSpace);
return event;
}
if (event.viewId === APP_ENV.view["2歳以上日誌データ連携用途"]) {
if (event.viewId === APP_ENV.view.linkForOthers) {
Link2Handler.getInstance(headerSpace);
return event;
}

View File

@@ -0,0 +1,21 @@
(function () {
"use strict";
addApproveFlowAction(true);
kintone.events.on("mobile.app.record.index.show", (event) => {
const headerSpace = getHeaderSpace('single-label-line');
// if (event.viewId === APP_ENV.view.linkFor0to1) {
// Link1Handler.getInstance(headerSpace);
// return event;
// }
// if (event.viewId === APP_ENV.view.linkForOthers) {
// Link2Handler.getInstance(headerSpace);
// return event;
// }
// BatchCreateHandler.getInstance(headerSpace);
});
})();

View File

@@ -29,7 +29,7 @@
}
// ------------------- 編集画面保存時の処理 -------------------
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], function (event) {
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit', 'app.record.index.edit.submit'], function (event) {
event.record[FIELD_CODE]['value'] = getUniqueKey(event.record);
return event;
});

View File

@@ -0,0 +1,41 @@
(function () {
"use strict";
const FIELD_CODE = "ユニークキー";
// ------------------- 詳細/印刷/編集画面表示時の処理 -------------------
kintone.events.on(['mobile.app.record.detail.show'], function (event) {
hideField(FIELD_CODE);
return event;
});
// ------------------- 編集画面表示時の処理 -------------------
kintone.events.on(['mobile.app.record.edit.show', 'mobile.app.record.create.show'], function (event) {
const targetFieldEl = kintone.mobile.app.record.getSpaceElement("before-unique-key").nextSibling;
targetFieldEl.querySelector('.control-constraints-gaia').style.display = 'none';
event.record[FIELD_CODE]['value'] = "<自動計算:出席番号+学年+クラス+名前>";
disableField(event.record, FIELD_CODE);
return event;
});
function hideField(fieldCode) {
kintone.mobile.app.record.setFieldShown(fieldCode, false);
}
function disableField(record, fieldCode) {
record[fieldCode]['disabled'] = true;
}
// ------------------- 編集画面保存時の処理 -------------------
kintone.events.on(['mobile.app.record.create.submit', 'mobile.app.record.edit.submit'], function (event) {
event.record[FIELD_CODE]['value'] = getUniqueKey(event.record);
return event;
});
function getUniqueKey(record) {
return (record['出席番号']['value'] + '_' + record['学年']['value'] + '_' + record['クラス']['value'] + '_' + record['名前']['value']).substring(0, 64);
}
})();

View File

@@ -0,0 +1,6 @@
const statusFieldMap = {
'指導教諭確認中': '担任',
'主幹確認中': '指導',
'園長確認中': '主幹',
'完了': '園長'
}

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("mobile.app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,13 @@
const statusFieldMap = {
'主幹確認中1学期': '指導1',
'園長確認中1学期': '主幹1',
'完了1学期': '園長1',
'担任作成中2学期': '園長1',
'主幹確認中2学期': '指導2',
'園長確認中2学期': '主幹2',
'完了2学期': '園長2',
'担任作成中3学期': '園長2',
'主幹確認中3学期': '指導3',
'園長確認中3学期': '主幹3',
'完了3学期': '園長3',
}

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,12 @@
(function () {
"use strict";
kintone.events.on("mobile.app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();