597 lines
17 KiB
JavaScript
597 lines
17 KiB
JavaScript
|
|
//공통
|
|
export const util = {
|
|
getOneToArrayByObjectKey: function (arr, key, value) {
|
|
let result = {};
|
|
arr.forEach(item => {
|
|
if (item[key] === value) {
|
|
result = item;
|
|
}
|
|
});
|
|
return result;
|
|
},
|
|
groupByProperty: (arr, prop, parentProp, group) => {
|
|
const tree = [];
|
|
const mappedArr = {};
|
|
let arrElem;
|
|
let mappedElem;
|
|
// 먼저 배열의 노드를 개체에 매핑하고 -> 해시 테이블을 생성
|
|
for (let i = 0; i < arr.length; i++) {
|
|
arrElem = arr[i];
|
|
if (group && arrElem.mnuGroup !== group) continue;
|
|
mappedArr[arrElem[prop]] = arrElem;
|
|
mappedArr[arrElem[prop]].children = [];
|
|
}
|
|
|
|
for (const mnuCd in mappedArr) {
|
|
if (mappedArr.hasOwnProperty(mnuCd)) {
|
|
mappedElem = mappedArr[mnuCd];
|
|
// 요소가 루트 수준에 없으면 자식의 부모 배열에 추가.
|
|
if (mappedElem[parentProp] && mappedArr[mappedElem[parentProp]]?.children) {
|
|
mappedArr[mappedElem[parentProp]].children.push(mappedElem);
|
|
}
|
|
// 요소가 루트 수준에 있는 경우 첫 번째 수준 요소 배열에 추가
|
|
else {
|
|
tree.push(mappedElem);
|
|
}
|
|
}
|
|
}
|
|
return tree;
|
|
},
|
|
getLowestChildren: function (obj) {
|
|
if (!obj.children || obj.children.length === 0) {
|
|
return obj;
|
|
} else {
|
|
return this.getLowestChildren(obj.children[0]);
|
|
}
|
|
},
|
|
hasClass: (e, clazz) => {
|
|
return e.target.classList.contains(clazz);
|
|
},
|
|
isNull: function (str) {
|
|
if (typeof str === 'undefined' || str === null || str === 'null' || str === '' || str === 'undefined') {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
nvl: function (str, returnVal) {
|
|
if (this.isNull(str)) {
|
|
return returnVal;
|
|
}
|
|
return str;
|
|
},
|
|
isNumeric: function (str) {
|
|
const pattern = /\d/;
|
|
return pattern.test(str);
|
|
},
|
|
isAlpha: function (str) {
|
|
const pattern = /[a-zA-Z]/;
|
|
return pattern.test(str);
|
|
},
|
|
isAlphaNumeric: function (str) {
|
|
const pattern = /^[a-zA-Z0-9]+$/;
|
|
return pattern.test(str);
|
|
},
|
|
isSpecialChar: function (str) {
|
|
const pattern = /[~!@#$%^&*()_+|<>?:{}]/;
|
|
return pattern.test(str);
|
|
},
|
|
isKoreanAplhaNumeric: function (str) {
|
|
const pattern = /^[ㄱ-ㅎ|ㅏ-ㅣ|가-힣|a-zA-Z0-9]+$/;
|
|
return pattern.test(str);
|
|
},
|
|
isValidPassword: function (str) {
|
|
const pattern = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*(),.?":{}|<>])[a-zA-Z0-9!@#$%^&*(),.?":{}|<>]{8,20}$/;
|
|
return pattern.test(str);
|
|
},
|
|
isValidExt: function (fileName, allowedExtensions) {
|
|
const fileExtension = fileName.split('.').pop().toLowerCase();
|
|
return allowedExtensions.includes(fileExtension);
|
|
},
|
|
isValidAllDate: function (dateString) {
|
|
const formats = ['YYYY', 'YYYYMM', 'YYYYMMDD'];
|
|
for (let format of formats) {
|
|
if (moment(dateString, format, true).isValid()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
isEmptyObject: function (param) {
|
|
return Object.keys(param).length === 0 && param.constructor === Object;
|
|
},
|
|
castArrayObjectToList: function (array, key) {
|
|
var newArray = [];
|
|
for (var i = 0; i < array.length; i++) {
|
|
newArray.push(array[i][key]);
|
|
}
|
|
return newArray;
|
|
},
|
|
arraysMatch: function (arr1, arr2) {
|
|
// 길이가 같은지 확인
|
|
if (arr1.length !== arr2.length) return false;
|
|
|
|
// 각 요소가 일치하는지 확인
|
|
return arr1.every((element, index) => element === arr2[index]);
|
|
},
|
|
//상위뎁스 객체들 조회
|
|
findObjectByKeyAndValue: function ({ array, find: { key: findKey, value: findValue }, result: { key: resultKey }, parents = [] }) {
|
|
for (let i = 0; i < array.length; i++) {
|
|
let obj = array[i];
|
|
let siblings = [...array];
|
|
//siblings.splice(i, 1); // 현재 메뉴를 제거하여 형제 메뉴들만 남김
|
|
let currentParents = [...parents, { [resultKey]: obj, siblings: siblings }];
|
|
|
|
if (obj[findKey] === findValue) {
|
|
let formattedResult = currentParents.reduce((acc, cur, idx) => {
|
|
acc[`${resultKey}${idx + 1}`] = { active: cur[resultKey], list: cur.siblings };
|
|
return acc;
|
|
}, {});
|
|
return formattedResult;
|
|
}
|
|
if (obj.children) {
|
|
let result = this.findObjectByKeyAndValue({ array: obj.children, find: { key: findKey, value: findValue }, result: { key: resultKey }, parents: currentParents });
|
|
if (result !== null) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
autoComma(val, returnVal) {
|
|
if (!val && val !== 0) return returnVal ? returnVal : '';
|
|
|
|
// 문자열로 변환하고, 숫자와 소수점만을 허용
|
|
val = String(val).replace(/[^0-9.-]/g, '');
|
|
|
|
// 소수점을 기준으로 숫자를 분리
|
|
let [integerPart, decimalPart] = val.split('.');
|
|
|
|
// 정수 부분에 대해 쉼표 추가
|
|
integerPart = integerPart.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
|
|
|
// 소수점 이하 숫자가 있다면 다시 합치기
|
|
return decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
|
|
},
|
|
removeComma: function (str) {
|
|
str = String(str);
|
|
return str.replace(/[^\d]+/g, '');
|
|
},
|
|
removeSpaceAndTrim: function (str) {
|
|
if (str) {
|
|
str = String(str);
|
|
return str.replace(/\s/g, '');
|
|
} else {
|
|
return str;
|
|
}
|
|
},
|
|
isNotWhiteSpace: function (str) {
|
|
const hasWhitespace = /\s/.test(str);
|
|
return !hasWhitespace;
|
|
},
|
|
//숫자만 남기고 모두 제거해서 반환
|
|
retainOnlyNumbers: function (str) {
|
|
str = String(str);
|
|
return str.replace(/\D+/g, '');
|
|
},
|
|
proxyToObject: function (proxy) {
|
|
return JSON.parse(JSON.stringify(Object.assign({}, proxy)));
|
|
},
|
|
dateFormatter: function ({ input, isHan = false }) {
|
|
const value = this.retainOnlyNumbers(input);
|
|
|
|
if (value.length === 6) {
|
|
const date = moment(value, 'YYYYMM');
|
|
return date.format(isHan ? 'YYYY년 MM월' : 'YYYY-MM');
|
|
} else if (value.length === 8) {
|
|
const date = moment(value, 'YYYYMMDD');
|
|
return date.format(isHan ? 'YYYY년 MM월 DD일' : 'YYYY-MM-DD');
|
|
} else if (value.length === 14) {
|
|
const date = moment(value, 'YYYYMMDDHHmmss');
|
|
return date.format(isHan ? 'YYYY년 MM월 DD일 HH시 mm분 ss초' : 'YYYY-MM-DD HH:MM:SS');
|
|
} else {
|
|
return input;
|
|
}
|
|
},
|
|
generateYearArray: function (num) {
|
|
const currentYear = moment().year();
|
|
if (num <= 1 || isNaN(num)) {
|
|
return [(currentYear - 2).toString(), (currentYear - 1).toString(), currentYear.toString()];
|
|
}
|
|
|
|
const years = [];
|
|
for (let i = num - 1; i >= 0; i--) {
|
|
years.push((currentYear - i).toString());
|
|
}
|
|
|
|
return years;
|
|
},
|
|
getFileId: function () {
|
|
let timeStamp = moment().format('YYYYMMDDHHmmssSSS'); // YYYYMMDDHHmmssSSS 형태로 타임스탬프를 생성
|
|
return 'CFM_' + timeStamp.slice(-12); // slice(-12)는 문자열의 끝에서 12자리를 추출합니다.
|
|
},
|
|
lpad: function (number, pad, length) {
|
|
let str = number.toString();
|
|
while (str.length < length) {
|
|
str = pad + str;
|
|
}
|
|
return str;
|
|
},
|
|
convertKeysToCamelCase: function (obj) {
|
|
if (obj === null || typeof obj !== 'object') {
|
|
return obj;
|
|
}
|
|
return Object.keys(obj).reduce((acc, key) => {
|
|
const camelCaseKey = key.replace(/([-_]\w)/g, match => match.replace(/[-_]/, '').toUpperCase());
|
|
acc[camelCaseKey] = typeof obj[key] === 'object' ? this.convertKeysToCamelCase(obj[key]) : obj[key];
|
|
return acc;
|
|
}, {});
|
|
},
|
|
blobUrlToBlob: async blobUrl => {
|
|
try {
|
|
const response = await fetch(blobUrl);
|
|
return await response.blob();
|
|
} catch (error) {
|
|
console.error(error);
|
|
return new Blob();
|
|
}
|
|
},
|
|
hexToRgba: hex => {
|
|
const r = parseInt(hex.substring(1, 3), 16);
|
|
const g = parseInt(hex.substring(3, 5), 16);
|
|
const b = parseInt(hex.substring(5, 7), 16);
|
|
return `rgba(${r}, ${g}, ${b} , 0.5)`;
|
|
},
|
|
rgbaToHex: rgba => {
|
|
/*rgba값을 가져와서 rgb 로 추출 후 hex 값 반환 */
|
|
if (!rgba) return false;
|
|
const rgbArray = rgba.match(/\d+/g).slice(0, 3).map(Number);
|
|
return `#${((1 << 24) | (rgbArray[0] << 16) | (rgbArray[1] << 8) | rgbArray[2]).toString(16).slice(1).toUpperCase()}`;
|
|
},
|
|
rgbAddOpacity: (rgb, opacity) => {
|
|
const rgbValues = rgb.match(/\d+/g); // rgb 값을 배열로 추출
|
|
const colorWithOpacity = `rgba(${rgbValues[0]}, ${rgbValues[1]}, ${rgbValues[2]}, ${opacity})`; // rgba로 변환
|
|
return colorWithOpacity;
|
|
},
|
|
generateMonthArray: (pSmonth, pEmonth) => {
|
|
let startYear = Math.floor(pSmonth / 100);
|
|
let startMonth = pSmonth % 100;
|
|
let endYear = Math.floor(pEmonth / 100);
|
|
let endMonth = pEmonth % 100;
|
|
|
|
let months = [];
|
|
|
|
for (let year = startYear; year <= endYear; year++) {
|
|
let monthStart = year === startYear ? startMonth : 1;
|
|
let monthEnd = year === endYear ? endMonth : 12;
|
|
|
|
for (let month = monthStart; month <= monthEnd; month++) {
|
|
let monthString = month < 10 ? `0${month}` : `${month}`;
|
|
months.push(`${year}${monthString}`);
|
|
}
|
|
}
|
|
|
|
return months;
|
|
},
|
|
findDuplicatesForArr: (arr, key) => {
|
|
const map = new Map();
|
|
const duplicates = [];
|
|
|
|
arr.forEach(item => {
|
|
const keyValue = item[key];
|
|
if (map.has(keyValue)) {
|
|
if (!duplicates.includes(keyValue)) {
|
|
duplicates.push(keyValue);
|
|
}
|
|
} else {
|
|
map.set(keyValue, true);
|
|
}
|
|
});
|
|
|
|
return duplicates;
|
|
},
|
|
createHtmlFromJson: (html, data) => {
|
|
if (!html || util.isEmptyObject(html)) return null;
|
|
const { classNm, headerKey, rows } = JSON.parse(html);
|
|
if (!rows) return null;
|
|
const tableRows = rows
|
|
.map(row => {
|
|
const value = data?.[row.key] ?? null;
|
|
let displayValue;
|
|
if (typeof value === 'number') {
|
|
displayValue = util.autoComma(value, 0);
|
|
} else if (typeof value === 'string') {
|
|
displayValue = util.nvl(value, '-');
|
|
} else {
|
|
displayValue = '-';
|
|
}
|
|
return `
|
|
<tr>
|
|
<th>${row.th}</th>
|
|
<td>${displayValue}</td>
|
|
</tr>
|
|
`;
|
|
})
|
|
.join('');
|
|
return `
|
|
<div class="${classNm}">
|
|
<table>
|
|
<tr>
|
|
<th colspan="2" style="position: relative; text-align: center; background-color: #DEEAF3;">
|
|
${data?.[headerKey] ?? '-'}
|
|
</th>
|
|
</tr>
|
|
${tableRows}
|
|
</table>
|
|
</div>
|
|
`;
|
|
},
|
|
};
|
|
|
|
/**
|
|
*
|
|
* 다이렉트로 import하여 사용하기 위해 export로 수정중
|
|
*
|
|
* */
|
|
|
|
//객체 배열에서 특정 key의 value인 것을 추출
|
|
export const getArrayByObjectKey = function (arr, key, value) {
|
|
return arr.filter(item => item[key] === value);
|
|
};
|
|
|
|
//소수점 1자리를 기준으로 점을 언더스코어로 변경
|
|
export const numberDotToUnderscore = function (num) {
|
|
if (Number.isInteger(num)) {
|
|
return num.toString();
|
|
} else {
|
|
return num.toFixed(1).replace('.', '_');
|
|
}
|
|
};
|
|
|
|
export const stripHTML = function (htmlString) {
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(htmlString, 'text/html');
|
|
return doc.body.textContent || '';
|
|
};
|
|
|
|
//URL에서 매개변수 추출
|
|
export function getParameterByName(name, url = window.location.search) {
|
|
name = name.replace(/[\[\]]/g, '\\$&');
|
|
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
|
|
results = regex.exec(url);
|
|
if (!results) return null;
|
|
if (!results[2]) return '';
|
|
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
|
}
|
|
|
|
export function dateFormatForDB({ date: isoString, format = 'YYYY-MM-DD HH:MI:SS' }) {
|
|
if (!isoString) return '';
|
|
|
|
let date = new Date(isoString);
|
|
|
|
let year = date.getFullYear();
|
|
let month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
let day = date.getDate().toString().padStart(2, '0');
|
|
|
|
let hours = date.getHours().toString().padStart(2, '0');
|
|
let minutes = date.getMinutes().toString().padStart(2, '0');
|
|
let seconds = date.getSeconds().toString().padStart(2, '0');
|
|
|
|
return (
|
|
format
|
|
//
|
|
.replace('YYYY', year)
|
|
.replace('MM', month)
|
|
.replace('DD', day)
|
|
.replace('HH', hours)
|
|
.replace('MI', minutes)
|
|
.replace('SS', seconds)
|
|
);
|
|
}
|
|
|
|
//blob 다운로드
|
|
//blob 다운로드
|
|
export function downloadFromBlob(res, data) {
|
|
const url = URL.createObjectURL(new Blob([res.data], { type: res.data.type }));
|
|
const link = document.createElement('a');
|
|
|
|
const contentDisposition = res.headers?.['content-disposition']; // 파일 이름
|
|
let fileName = data?.fileName || 'export'; //기본 파일 이름
|
|
|
|
if (contentDisposition) {
|
|
const [fileNameMatch] = contentDisposition.split(';').filter(str => str.includes('filename'));
|
|
if (fileNameMatch) {
|
|
[, fileName] = fileNameMatch.split('=');
|
|
// 파일명이 "UTF-8''" 등으로 인코딩되어 있을 수 있으니 decodeURIComponent 추가
|
|
fileName = decodeURIComponent(fileName);
|
|
}
|
|
}
|
|
|
|
link.href = url;
|
|
// 브라우저 인코딩 관련 이슈 방지를 위해 파일명에 대한 추가 decodeURI 처리
|
|
link.download = decodeURIComponent(fileName).replace(/"/g, '').trim();
|
|
link.style.display = 'none';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
|
|
link.remove();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
export function downloadBlobAsFile(blob, filename = 'export') {
|
|
// 브라우저에 따라 인코딩 처리 햔재는 파이어폭스만
|
|
const url = URL.createObjectURL(blob);
|
|
const encodedFilename = encodeURIComponent(filename);
|
|
const downloadLink = document.createElement('a');
|
|
|
|
downloadLink.href = url;
|
|
downloadLink.download = decodeURIComponent(encodedFilename);
|
|
|
|
document.body.appendChild(downloadLink);
|
|
downloadLink.click();
|
|
|
|
document.body.removeChild(downloadLink);
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
export function viewFile(res, data) {
|
|
// Blob URL 생성
|
|
const url = URL.createObjectURL(new Blob([res.data], { type: res.data.type }));
|
|
|
|
let fileType = null;
|
|
let fileName = data?.fileName || 'export'; // 기본 파일 이름
|
|
|
|
// PDF 및 이미지 타입의 MIME 타입 확인
|
|
if (res.data.type === 'application/pdf') {
|
|
fileType = 'pdf';
|
|
} else if (res.data.type.startsWith('image/')) {
|
|
fileType = 'img';
|
|
} else if (res.data.type === 'application/octet-stream') {
|
|
// Content-Disposition 헤더에서 파일 이름 추출
|
|
const contentDisposition = res.headers?.['content-disposition'];
|
|
if (contentDisposition) {
|
|
const fileNameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
|
if (fileNameMatch && fileNameMatch[1]) {
|
|
fileName = fileNameMatch[1].replace(/['"]/g, ''); // 파일 이름 추출
|
|
fileName = decodeURIComponent(fileName); // URL 인코딩된 파일 이름을 디코딩
|
|
}
|
|
}
|
|
|
|
// 파일 확장자 추출
|
|
const fileExtension = fileName.split('.').pop().toLowerCase();
|
|
|
|
// 확장자를 통해 파일 타입 결정
|
|
if (fileExtension === 'pdf') {
|
|
fileType = 'pdf';
|
|
} else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'].includes(fileExtension)) {
|
|
fileType = 'img';
|
|
} else {
|
|
console.error('지원하지 않는 파일 형식입니다.');
|
|
}
|
|
}
|
|
|
|
// URL과 파일 타입 반환
|
|
return { url, fileType, fileName };
|
|
}
|
|
|
|
export function getToday() {
|
|
return getDate('today');
|
|
}
|
|
|
|
export function getDate(type, diff) {
|
|
const date = new Date();
|
|
|
|
switch (type) {
|
|
case 'year':
|
|
date.setFullYear(date.getFullYear() + diff);
|
|
break;
|
|
case 'month':
|
|
date.setMonth(date.getMonth() + diff);
|
|
break;
|
|
case 'day':
|
|
date.setDate(date.getDate() + diff);
|
|
break;
|
|
case 'today':
|
|
date.setDate(date.getDate());
|
|
break;
|
|
default:
|
|
console.log('잘못된 타입입니다. 올바른 타입: 년, 월, 일');
|
|
return;
|
|
}
|
|
|
|
const year = date.getFullYear();
|
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
const day = date.getDate().toString().padStart(2, '0');
|
|
|
|
return `${year}${month}${day}`;
|
|
}
|
|
|
|
//promise 처리
|
|
export const byPromise = function (actions) {
|
|
return actions.reduce((promise, action) => {
|
|
return promise.then(async () => {
|
|
if (typeof action === 'function') {
|
|
return await action();
|
|
} else {
|
|
return await action;
|
|
}
|
|
});
|
|
}, Promise.resolve());
|
|
};
|
|
|
|
export const getMessage = (msgGroup, msgKey, innerText, fix) => {
|
|
let resultString = GLOBAL.MESSAGE[msgGroup][msgKey];
|
|
const prefix = fix?.prefix || '';
|
|
const suffix = fix?.suffix || '';
|
|
|
|
if (innerText) {
|
|
for (const [key, value] of Object.entries(innerText)) {
|
|
const regex = new RegExp(`\\$\{${key}\}`, 'g');
|
|
resultString = resultString.replace(regex, value);
|
|
}
|
|
}
|
|
|
|
if (resultString) resultString = resultString.replace(/\\n/g, '\n');
|
|
return resultString ? `${prefix}${resultString}${suffix}` : '안내 메시지 로딩 오류 - 관리자에게 문의하세요.';
|
|
};
|
|
|
|
// global.js의 message를 읽어오는 alert
|
|
export const gAlert = (msgGroup, msgKey, innerText, fix) => {
|
|
alert(getMessage(msgGroup, msgKey, innerText, fix));
|
|
};
|
|
|
|
// message를 confirm
|
|
export const gConfirm = (msgGroup, msgKey, innerText, fix) => {
|
|
return window.confirm(getMessage(msgGroup, msgKey, innerText, fix));
|
|
};
|
|
|
|
// message를 함수 리턴
|
|
export const gMessage = getMessage;
|
|
|
|
// message를 save후 응답까지 처리
|
|
export const gSaveCommonConfirm = async ({ message, api = async () => {}, param = {}, success, fail, prefix }) => {
|
|
try {
|
|
if (!gConfirm('CONFIRM', `${message}_QUEST`, null, { prefix })) return;
|
|
|
|
const result = await api(param, { isAlert: false });
|
|
|
|
if (result?.code !== 200) return;
|
|
|
|
const rep = message === 'UPLOAD' ? result.data.data : result.data;
|
|
|
|
if (rep > 0) {
|
|
gAlert('INFO', `${message}_COMPLETE`);
|
|
await success?.();
|
|
} else {
|
|
gAlert('ERROR', `${message}_FAIL`);
|
|
await fail?.();
|
|
}
|
|
} catch (error) {
|
|
console.error('오류 발생:', error); // 시큐어코딩 관련 수정 : 오류 정보를 로그로 출력
|
|
}
|
|
};
|
|
|
|
export const nullToValue = (text, { value = '-', prefix = '', suffix = '' } = {}) => {
|
|
if (!text || text === value) {
|
|
return value;
|
|
}
|
|
return `${prefix}${text}${suffix}`;
|
|
};
|
|
|
|
export const filterArrayOfObjectsByKeys = (arrayOfObjects, keys) => {
|
|
return arrayOfObjects.map(obj => {
|
|
return keys.reduce((filteredObj, key) => {
|
|
if (obj.hasOwnProperty(key)) {
|
|
filteredObj[key] = obj[key];
|
|
}
|
|
return filteredObj;
|
|
}, {});
|
|
});
|
|
};
|
|
|
|
export const isMobile = () => {
|
|
const width = window.innerWidth > 0 ? window.innerWidth : window.screen.width;
|
|
|
|
return width < GLOBAL.MOBILE_SIZE;
|
|
};
|