- TBM 관리 탭의 비관리자 클라이언트 필터링 제거 (모바일과 동일하게 전체 표시) - tbm.js의 state.js 프록시 변수 중복 선언 제거 (mapRegions, mapCanvas 등) - 누락된 common/utils.js, common/base-state.js 추가 - 캐시 버스팅을 위한 버전 쿼리 스트링 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
145 lines
3.4 KiB
JavaScript
145 lines
3.4 KiB
JavaScript
/**
|
|
* Common Utilities
|
|
* TBM/작업보고 공통 유틸리티 함수
|
|
*/
|
|
|
|
class CommonUtils {
|
|
/**
|
|
* 서울 시간대(Asia/Seoul, UTC+9) 기준 오늘 날짜를 YYYY-MM-DD 형식으로 반환
|
|
*/
|
|
getTodayKST() {
|
|
const now = new Date();
|
|
const kstOffset = 9 * 60;
|
|
const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
|
|
const kstTime = new Date(utc + (kstOffset * 60000));
|
|
|
|
const year = kstTime.getFullYear();
|
|
const month = String(kstTime.getMonth() + 1).padStart(2, '0');
|
|
const day = String(kstTime.getDate()).padStart(2, '0');
|
|
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
|
|
/**
|
|
* 날짜를 YYYY-MM-DD 형식으로 변환 (문자열 또는 Date 객체)
|
|
*/
|
|
formatDate(date) {
|
|
if (!date) return '';
|
|
|
|
// 이미 YYYY-MM-DD 형식이면 그대로 반환
|
|
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
return date;
|
|
}
|
|
|
|
const dateObj = date instanceof Date ? date : new Date(date);
|
|
const year = dateObj.getFullYear();
|
|
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
|
|
const day = String(dateObj.getDate()).padStart(2, '0');
|
|
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
|
|
/**
|
|
* 요일 반환 (일/월/화/수/목/금/토)
|
|
*/
|
|
getDayOfWeek(date) {
|
|
const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
|
|
const dateObj = date instanceof Date ? date : new Date(date instanceof String || typeof date === 'string' ? date + 'T00:00:00' : date);
|
|
return dayNames[dateObj.getDay()];
|
|
}
|
|
|
|
/**
|
|
* 오늘인지 확인
|
|
*/
|
|
isToday(date) {
|
|
return this.formatDate(date) === this.getTodayKST();
|
|
}
|
|
|
|
/**
|
|
* UUID v4 생성
|
|
*/
|
|
generateUUID() {
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
const r = Math.random() * 16 | 0;
|
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* HTML 이스케이프
|
|
*/
|
|
escapeHtml(text) {
|
|
if (!text) return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
/**
|
|
* 디바운스
|
|
*/
|
|
debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func(...args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 쓰로틀
|
|
*/
|
|
throttle(func, limit) {
|
|
let inThrottle;
|
|
return function(...args) {
|
|
if (!inThrottle) {
|
|
func.apply(this, args);
|
|
inThrottle = true;
|
|
setTimeout(() => inThrottle = false, limit);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 객체 깊은 복사
|
|
*/
|
|
deepClone(obj) {
|
|
return JSON.parse(JSON.stringify(obj));
|
|
}
|
|
|
|
/**
|
|
* 빈 값 확인
|
|
*/
|
|
isEmpty(value) {
|
|
if (value === null || value === undefined) return true;
|
|
if (typeof value === 'string') return value.trim() === '';
|
|
if (Array.isArray(value)) return value.length === 0;
|
|
if (typeof value === 'object') return Object.keys(value).length === 0;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 배열 그룹화
|
|
*/
|
|
groupBy(array, key) {
|
|
return array.reduce((result, item) => {
|
|
const groupKey = typeof key === 'function' ? key(item) : item[key];
|
|
if (!result[groupKey]) {
|
|
result[groupKey] = [];
|
|
}
|
|
result[groupKey].push(item);
|
|
return result;
|
|
}, {});
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.CommonUtils = new CommonUtils();
|
|
|
|
console.log('[Module] common/utils.js 로드 완료');
|