504 lines
13 KiB
JavaScript
504 lines
13 KiB
JavaScript
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') };
|
|
});
|
|
|
|
const dateItems = Array.from({ length: 31 }, (_, i) => {
|
|
const date = "" + (i + 1);
|
|
return { label: date, value: date.padStart(2, '0') };
|
|
})
|
|
dateItems.push({ label: '末日', value: 'end' });
|
|
|
|
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歳児" },
|
|
]
|
|
|
|
let notificationEl;
|
|
let loadingEl;
|
|
let dialogEl;
|
|
|
|
function getHeaderSpace(className, isDetailPage) {
|
|
const headerSpace = (isDetailPage ? kintone.app.record : kintone.app).getHeaderMenuSpaceElement();
|
|
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;
|
|
}
|
|
|
|
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.Text({
|
|
value: "" + new Date().getFullYear(),
|
|
id: yearElId,
|
|
label: '年',
|
|
className: 'year input'
|
|
});
|
|
result[yearElId] = yearEl;
|
|
btnGroupAreaEl.appendChild(yearEl);
|
|
}
|
|
|
|
if (monthElId) {
|
|
const monthEl = new Kuc.Combobox({
|
|
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.Combobox({
|
|
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.Combobox({
|
|
id: termElId,
|
|
items: termItems,
|
|
className: "term input",
|
|
label: '学年',
|
|
});
|
|
result[termElId] = termEl;
|
|
btnGroupAreaEl.appendChild(termEl);
|
|
}
|
|
|
|
const btnEl = new Kuc.Button({
|
|
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 getLastDate(year, month) {
|
|
// month は 1-12 の数値で入力する
|
|
return new Date(year, month, 0);
|
|
}
|
|
|
|
function createBtn(id, label, onClick) {
|
|
const btnEl = new Kuc.Button({
|
|
text: label,
|
|
type: "submit",
|
|
className: "action-btn",
|
|
id,
|
|
});
|
|
btnEl.addEventListener('click', onClick);
|
|
return btnEl;
|
|
}
|
|
|
|
function hideSpaceField(ids) {
|
|
ids.forEach(id => {
|
|
const area = kintone.app.record.getSpaceElement(id);
|
|
area.parentElement.style.minWidth = '0';
|
|
area.parentElement.style.display = 'none';
|
|
});
|
|
}
|
|
|
|
function getExcelName(excelName, nameSuffix = '', suffix = '.xlsx') {
|
|
return excelName + (nameSuffix ? (nameSuffix.startsWith('_') ? nameSuffix : ('_' + nameSuffix)) : '') + suffix;
|
|
}
|
|
|
|
function getJapaneseEraDate(date = new Date()) {
|
|
const formatter = new Intl.DateTimeFormat('ja-JP-u-ca-japanese', {
|
|
era: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
const japaneseDate = formatter.format(date);
|
|
|
|
const match = japaneseDate.match(/(.+?)(元|\d+)年(\d+)月(\d+)日/);
|
|
|
|
if (!match) {
|
|
throw new Error("日付の解析に失敗しました。");
|
|
}
|
|
|
|
const era = match[1];
|
|
const year = match[2] === "元" ? 1 : parseInt(match[2], 10);
|
|
const month = parseInt(match[3], 10);
|
|
const day = parseInt(match[4], 10);
|
|
|
|
return {
|
|
japaneseDate,
|
|
era,
|
|
year,
|
|
month,
|
|
day,
|
|
westernYear: date.getFullYear()
|
|
};
|
|
}
|
|
|
|
function convertToWesternYear(year, era) {
|
|
return warekiStartYear[era] + year - 1;
|
|
}
|
|
|
|
function getYearRange() {
|
|
const today = new Date();
|
|
let year = today.getFullYear();
|
|
if (today.getMonth() + 1 < 4) {
|
|
year -= 1;
|
|
}
|
|
return {
|
|
'start': getFormatDateString(year, 4, 1),
|
|
'end': getFormatDateString(year + 1, 3, 31)
|
|
}
|
|
}
|
|
|
|
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 `${formatY}-${formatM}-${formatD}`;
|
|
}
|
|
|
|
function loading(show, text) {
|
|
if (!loadingEl) {
|
|
loadingEl = new Kuc.Spinner({
|
|
container: document.querySelector('.container-gaia'),
|
|
text
|
|
});
|
|
} else {
|
|
loadingEl.close();
|
|
loadingEl.text = text;
|
|
}
|
|
if (show) {
|
|
loadingEl.open();
|
|
}
|
|
}
|
|
|
|
function showError(show, text) {
|
|
if (show) {
|
|
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];
|
|
}, [[], []]);
|
|
}
|