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]; }, [[], []]); }