You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1441 lines
52 KiB
JavaScript

$.fn.datepicker.dates['kr'] = {
days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"],
daysShort: ["일", "월", "화", "수", "목", "금", "토"],
daysMin: ["일", "월", "화", "수", "목", "금", "토"],
months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
today: "오늘",
clear: "Clear",
format: "yyyy-mm-dd",
titleFormat: "yyyy MM",
weekStart: 0
};
$(document).ready(function () {
$("body").on("focus", ".datepicker", function () {
$(this).inputmask('9999-99-99');
$(this).datepicker({
format: "yyyy-mm-dd",
language: "kr"
});
});
$("body").on("focus", ".yearpicker", function () {
$(this).inputmask('9999');
$(this).datepicker({
format: "yyyy",
viewMode: "years", // 시작 뷰를 '연도'로 설정
minViewMode: "years", // 최소 뷰 단위를 '연도'로 설정
format: "yyyy",
language: "kr",
autoclose: true
});
});
$("body").on("focus", ".timeMask", function () {
$(this).inputmask("(09|19|20|21|22|23):(09|19|29|39|49|59)");
});
$("body").on("focus", ".numericMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // default: false, 정수 부분 그룹화 사용 여부
groupSeparator: ",", // default: "", 그룹 구분자 정의
digits: 0, // default: "*", 소수 크기 정의
allowMinus: false, // default: true, 음수 사용 여부
repeat: 12, //
autoUnmask: true
});
});
// 중요로직: 소수점을 허용하는 숫자 마스크 (지적 등에 사용)
$("body").on("focus", ".decimalMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용
groupSeparator: ",", // 그룹 구분자
digits: 2, // 소수점 2자리까지 허용
allowMinus: false, // 음수 사용 안함
repeat: 15, // 최대 15자리
autoUnmask: true
});
});
// 중요로직: - decimal(4,4) 스펙에 맞춤
$("body").on("focus", ".decimalMask4", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
allowMinus: false, // 음수 사용 안함
autoUnmask: true,
groupSeparator: ",", // 그룹 구분자 없음
repeat: 1, // 정수부 최대 1자리 (0)
digits: 4, // 소수점 최대 4자리
});
});
// 중요로직: PTOUT 지적 전용 마스크 - decimal(22,2) 스펙에 맞춤
$("body").on("focus", ".ptoutMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
groupSeparator: ",", // 그룹 구분자 없음
digits: 2, // 소수점 2자리까지 허용
allowMinus: false, // 음수 사용 안함
repeat: 20, // 정수부 최대 20자리
autoUnmask: true
});
});
// 중요로직: OALP 공시지가 전용 마스크 - decimal(13,0) 스펙에 맞춤
$("body").on("focus", ".oalpMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
groupSeparator: ",", // 그룹 구분자 없음
digits: 0, // 소수점 사용 안함
allowMinus: false, // 음수 사용 안함
repeat: 13, // 정수부 최대 13자리
autoUnmask: true
});
});
// 중요로직: Strct_Idx_Cd 전용 마스크 - 숫자 3자리
$("body").on("focus", ".strctIdxCdMask", function () {
$(this).inputmask({
mask: "999", // 3자리 숫자 패턴 (선행 0 포함)
placeholder: "000", // 입력 안내용 플레이스홀더
definitions: {
"9": {
validator: "[0-9]", // 숫자만 허용
cardinality: 1
}
},
autoUnmask: true, // 마스크 제거 후 실제 값 반환
rightAlign: false // 왼쪽 정렬
});
});
// 중요로직: Strct_Idx 전용 마스크 - decimal(10,2) 스펙에 맞춤
$("body").on("focus", ".strctIdxMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
allowMinus: false, // 음수 사용 안함
autoUnmask: true,
rightAlign: false,
groupSeparator: ",", // 그룹 구분자 없음
repeat: 8, // 정수부 최대 2자리
digits: 2 // 소수점 사용 안함
});
});
// 중요로직: RDVLRT_CN_YR_CNT 잔가율 내용 연도 수 전용 마스크 - decimal(2,0) 스펙에 맞춤
$("body").on("focus", ".rdvlrtCnYrCntMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
allowMinus: false, // 음수 사용 안함
autoUnmask: true,
rightAlign: false,
groupSeparator: ",", // 그룹 구분자 없음
repeat: 2, // 정수부 최대 2자리
digits: 0 // 소수점 사용 안함
});
});
// 중요로직: LAST_YR_RDVLRT 최종 연도 잔가율 전용 마스크 - decimal(4,2) 스펙에 맞춤
$("body").on("focus", ".lastYrRdvlrtMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
allowMinus: false, // 음수 사용 안함
autoUnmask: true,
rightAlign: false,
groupSeparator: ",", // 그룹 구분자 없음
repeat: 2, // 정수부 최대 2자리
digits: 2 // 소수점 최대 2자리
});
});
// 중요로직: DPRT 감가상각률 전용 마스크 - decimal(4,4) 스펙에 맞춤
$("body").on("focus", ".dprtMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
allowMinus: false, // 음수 사용 안함
autoUnmask: true,
rightAlign: false,
groupSeparator: ",", // 그룹 구분자 없음
repeat: 1, // 정수부 최대 1자리 (0)
digits: 4, // 소수점 최대 4자리
max: 0.9999,
});
});
// 중요로직: Usg_Idx_Cd 전용 마스크 - 숫자 5자리
$("body").on("focus", ".usgIdxCdMask", function () {
$(this).inputmask({
mask: "99999", // 3자리 숫자 패턴 (선행 0 포함)
placeholder: "00000", // 입력 안내용 플레이스홀더
definitions: {
"9": {
validator: "[0-9]", // 숫자만 허용
cardinality: 1
}
},
autoUnmask: true, // 마스크 제거 후 실제 값 반환
rightAlign: false // 왼쪽 정렬
});
});
// 중요로직: Usg_Idx 전용 마스크 - decimal(10,2) 스펙에 맞춤
$("body").on("focus", ".usgIdxMask", function () {
$(this).inputmask("numeric", {
autoGroup: true, // 그룹화 사용 안함
allowMinus: false, // 음수 사용 안함
autoUnmask: true,
rightAlign: false,
groupSeparator: ",", // 그룹 구분자 없음
repeat: 8, // 정수부 최대 2자리
digits: 2 // 소수점 사용 안함
});
});
});
/**
* Checks if value is empty. Deep-checks arrays and objects Note: isEmpty([]) ==
* true, isEmpty({}) == true, isEmpty([{0:false},"",0]) == true, isEmpty({0:1}) ==
* false
*
* @param value
* @returns {boolean}
*/
function isEmpty(value) {
if (value === null || value === undefined) {
return true;
}
if (typeof value === 'string' && value.trim() === '') {
return true;
}
if (Array.isArray(value) && value.length === 0) {
return true;
}
if (typeof value === 'object' && Object.keys(value).length === 0) {
return true;
}
return false;
/*
기존소스 주석. 0 true 가 이닌 false 처리
var isEmptyObject = function(a) {
if(typeof a.length === 'undefined') { // it's an Object, not an Array
var hasNonempty = Object.keys(a).some(function nonEmpty(element) {
return !isEmpty(a[element]);
});
return hasNonempty ? false : isEmptyObject(Object.keys(a));
}
return !a.some(function nonEmpty(element) { // check if array is really
// not empty as JS thinks
return !isEmpty(element); // at least one element should be
// non-empty
});
};
return(value == false || typeof value === 'undefined' || value == null || (typeof value === 'object' && isEmptyObject(value)));
*/
}
/**
* null 이나 빈값을 기본값으로 변경
*
* @param str
* 입력값
* @param defaultVal
* 기본값(옵션)
* @returns {String} 체크 결과값
*/
function nvl(str, defaultVal) {
var defaultValue = "";
if(typeof defaultVal != 'undefined') {
defaultValue = defaultVal;
}
if(typeof str == "undefined" || str == null || str == '' || str == "undefined") {
return defaultValue;
}
return str;
}
/**
* 길이체크
*
* @param str
* @returns {Number}
*/
function checkLength(str) {
var stringLength = str.length;
var stringByteLength = 0;
for(var i = 0; i < stringLength; i++) {
if(escape(str.charAt(i)).length >= 4) {
stringByteLength += 3;
} else if(escape(str.charAt(i)) == "%A7") {
stringByteLength += 3;
} else {
if(escape(str.charAt(i)) != "%0D") {
stringByteLength++;
}
}
}
return stringByteLength;
}
/**
* Left 빈자리 만큼 str 을 붙인다.
* @param src : Right에 붙을 원본 데이터
* @param len : str붙힐 데이터 길이
* @param str : 대상 데이터
* @returns : str과 src가 붙은 데이터
* @example : lpad("123123", 10, " ");
*/
function lpad(src, len, str) {
var retStr = "";
var padCnt = Number(len) - String(src).length;
for(var i=0;i<padCnt;i++) {
retStr += String(str);
}
return retStr+src;
}
/**
* Right 빈자리 만큼 str 을 붙인다.
* @param src : Left에 붙을 원본 데이터
* @param len : str붙힐 데이터 길이
* @param str : 대상 데이터
* @returns : str과 src가 붙은 데이터
* @example : rpad("123123", 10, " ");
*/
function rpad(src, len, str) {
var retStr = "";
var padCnt = Number(len) - String(src).length;
for(var i=0;i<padCnt;i++) {
retStr += String(str);
}
return src+retStr;
}
/**
* 절상, 절하, 반올림 처리
* @param strMode - 수식
* @param nCalcVal - 처리할 값(소수점 이하 데이터 포함)
* @param nDigit - 연산 기준 자릿수(오라클의 ROUND함수 자릿수 기준)
* -2:십단위, -1:원단위, 0:소수점 1자리
* 1:소수점 2자리, 2:소수점 3자리, 3:소수점 4자리, 4:소수점 5자리 처리
* @return String nCalcVal
*/
function fnCalcMath(strMode, nCalcVal, nDigit) {
if(strMode == "CEIL") { //절상
if(nDigit < 0) {
nDigit = -(nDigit);
nCalcVal = Math.ceil(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
} else {
nCalcVal = Math.ceil(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
}
} else if(strMode == "FLOOR") { //절하
if(nDigit < 0) {
nDigit = -(nDigit);
nCalcVal = Math.floor(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
} else {
nCalcVal = Math.floor(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
}
} else { //반올림
if(nDigit < 0) {
nDigit = -(nDigit);
nCalcVal = Math.round(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
} else {
nCalcVal = Math.round(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
}
}
return nCalcVal;
}
/***********************************************************************************
* 함 수 명 : isNull
* 기 능 : null 체크 함수
* 인 자 : value
* 리 턴 값 : boolean
**********************************************************************************/
var isNull = function(value) {
if (new String(value).valueOf() == "undefined") return true;
if (value == null) return true;
value = trimAll(value);
value = new String(value);
if (value == null) return true;
if (value.toString().length == 0) return true;
return false;
};
/***********************************************************************************
* 함 수 명 : utils.trimAll
* 기 능 : 문자열 전체의 공백제거 함수
* 인 자 : value
* 리 턴 값 : string
**********************************************************************************/
var trimAll = function(value) {
if (value == null) return "";
if (new String(value).valueOf() == "undefined") return "";
var rtnValue = "";
value = new String(value);
if (value != null) {
for(var i=0; i<value.length; i++) {
if (value.charAt(i) != " ") {
rtnValue = rtnValue + value.charAt(i);
}
}
} else {
return -1;
}
return rtnValue;
};
var downloadAjax = function(data, jqXhr) {
if (!data) {
alert("다운로드 파일이 없습니다.");
return;
}
try {
var blob = new Blob([data], { type: jqXhr.getResponseHeader('content-type') });
var fileName = getFileName(jqXhr.getResponseHeader('content-disposition'));
fileName = decodeURI(fileName);
if (window.navigator.msSaveOrOpenBlob) { // IE 10+
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else { // not IE
var link = document.createElement('a');
var url = window.URL.createObjectURL(blob);
link.href = url;
link.target = '_self';
if (fileName) link.download = fileName;
document.body.append(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
}
} catch (e) {
alert("다운로드 중 에러입니다.");
console.error(e)
}
}
/**
* URL 파라미터 관리 유틸리티
* 검색 조건 유지 및 파라미터 관리를 위한 함수 모음
*/
/**
* URL의 쿼리 파라미터를 객체로 변환
*
* @param {string} search - location.search 문자열 (기본값: 현재 페이지의 location.search)
* @returns {object} 파라미터 객체
*/
var getUrlParams = function(search) {
// 파라미터가 없으면 현재 페이지의 search 사용
var searchStr = search || window.location.search;
// 빈 객체 생성
var params = {};
// 파라미터가 없으면 빈 객체 반환
if (!searchStr) {
return params;
}
// 맨 앞의 '?' 제거
if (searchStr.startsWith('?')) {
searchStr = searchStr.substring(1);
}
// 파라미터 분리 및 객체로 변환
var pairs = searchStr.split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
// 디코딩하여 저장
if (pair[0]) {
params[decodeURIComponent(pair[0])] = pair.length > 1 ? decodeURIComponent(pair[1] || '') : '';
}
}
return params;
};
/**
* URL에 파라미터 추가
*
* @param {string|object} param - 추가할 파라미터 이름 또는 파라미터 객체
* @param {string} value - 파라미터 값 (param이 문자열일 경우)
* @param {string} url - 대상 URL (기본값: 현재 페이지 URL)
* @returns {string} 파라미터가 추가된 URL
*/
var addUrlParam = function(param, value, url) {
// URL이 없으면 현재 페이지 URL 사용
var targetUrl = url || window.location.href;
var params = getUrlParams(targetUrl.split('?')[1] ? '?' + targetUrl.split('?')[1] : '');
var baseUrl = targetUrl.split('?')[0];
// param이 객체인 경우
if (typeof param === 'object') {
for (var key in param) {
if (param.hasOwnProperty(key)) {
params[key] = param[key];
}
}
} else {
// param이 문자열인 경우
params[param] = value;
}
// 객체를 쿼리 문자열로 변환
var queryString = Object.keys(params)
.filter(function(key) {
return params[key] !== null && params[key] !== undefined;
})
.map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
})
.join('&');
// 쿼리 문자열이 있으면 '?'를 붙여서 반환
return baseUrl + (queryString ? '?' + queryString : '');
};
/**
* URL에서 파라미터 제거
*
* @param {string|array} paramName - 제거할 파라미터 이름 또는 이름 배열
* @param {string} url - 대상 URL (기본값: 현재 페이지 URL)
* @returns {string} 파라미터가 제거된 URL
*/
var removeUrlParam = function(paramName, url) {
// URL이 없으면 현재 페이지 URL 사용
var targetUrl = url || window.location.href;
var params = getUrlParams(targetUrl.split('?')[1] ? '?' + targetUrl.split('?')[1] : '');
var baseUrl = targetUrl.split('?')[0];
// paramName이 배열인 경우
if (Array.isArray(paramName)) {
for (var i = 0; i < paramName.length; i++) {
delete params[paramName[i]];
}
} else {
// paramName이 문자열인 경우
delete params[paramName];
}
// 객체를 쿼리 문자열로 변환
var queryString = Object.keys(params)
.filter(function(key) {
return params[key] !== null && params[key] !== undefined;
})
.map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
})
.join('&');
// 쿼리 문자열이 있으면 '?'를 붙여서 반환
return baseUrl + (queryString ? '?' + queryString : '');
};
/**
* URL에서 파라미터 값 가져오기
*
* @param {string} paramName - 가져올 파라미터 이름
* @param {string} defaultValue - 파라미터가 없을 경우 기본값
* @param {string} url - 대상 URL (기본값: 현재 페이지 URL)
* @returns {string} 파라미터 값 또는 기본값
*/
var getUrlParam = function(paramName, defaultValue, url) {
// URL이 없으면 현재 페이지 URL 사용
var targetUrl = url || window.location.href;
var params = getUrlParams(targetUrl.split('?')[1] ? '?' + targetUrl.split('?')[1] : '');
// 파라미터 값이 있으면 반환, 없으면 기본값 반환
return params[paramName] !== undefined ? params[paramName] : (defaultValue || null);
};
/**
* URL에서 파라미터 값 변경
*
* @param {string|object} param - 변경할 파라미터 이름 또는 파라미터 객체
* @param {string} value - 변경할 파라미터 값 (param이 문자열일 경우)
* @param {string} url - 대상 URL (기본값: 현재 페이지 URL)
* @returns {string} 파라미터가 변경된 URL
*/
var updateUrlParam = function(param, value, url) {
// addUrlParam 함수를 사용하여 파라미터 추가 또는 변경
return addUrlParam(param, value, url);
};
/**
* 현재 URL의 파라미터를 유지하면서 새 URL 생성
*
* @param {object} additionalParams - 추가할 파라미터 객체 (선택사항)
* @param {array} excludeParams - 제외할 파라미터 이름 배열 (선택사항)
* @param {string} newUrl - 새 URL 기본 주소
* @returns {string} 파라미터가 포함된 새 URL
*/
var buildUrlWithCurrentParams = function(additionalParams, excludeParams, newUrl) {
// 현재 URL의 파라미터 가져오기
var currentParams = getUrlParams();
// 제외할 파라미터 처리
if (excludeParams && Array.isArray(excludeParams)) {
for (var i = 0; i < excludeParams.length; i++) {
delete currentParams[excludeParams[i]];
}
}
// 새 URL 생성
var resultUrl = newUrl;
// 현재 파라미터 추가
for (var key in currentParams) {
if (currentParams.hasOwnProperty(key)) {
resultUrl = addUrlParam(key, currentParams[key], resultUrl);
}
}
// 추가 파라미터 처리
if (additionalParams && typeof additionalParams === 'object') {
resultUrl = addUrlParam(additionalParams, null, resultUrl);
}
return resultUrl;
};
/**
* 파라미터를 쿼리 문자열로 변환
*
* @param {object} params - 파라미터 객체
* @returns {string} 쿼리 문자열 ('?' 포함)
*/
var paramsToQueryString = function(params) {
if (!params || typeof params !== 'object') {
return '';
}
var queryString = Object.keys(params)
.filter(function(key) {
return params[key] !== null && params[key] !== undefined;
})
.map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
})
.join('&');
return queryString ? '?' + queryString : '';
};
/**
* 파라미터 객체와 ID를 URL에 추가
*
* @param {object} paramCond - 파라미터 객체 (예: ALL_PARAM_COND)
* @param {string} idName - ID 파라미터 이름
* @param {string} idValue - ID 파라미터 값
* @param {string} baseUrl - 기본 URL
* @returns {string} 모든 파라미터가 추가된 URL
*/
var buildUrlWithParamCondAndId = function(paramCond, idName, idValue, baseUrl) {
// 파라미터 객체가 정의되어 있지 않으면 기본 URL에 ID만 추가
if (typeof paramCond === 'undefined' || !paramCond) {
return addUrlParam(idName, idValue, baseUrl);
}
// 1. 기본 URL에 파라미터 객체의 모든 파라미터 추가
var urlWithParams = addUrlParam(paramCond, null, baseUrl);
// 2. ID 파라미터 추가
return addUrlParam(idName, idValue, urlWithParams);
};
/**
* 파라미터 객체와 여러 개의 key-value 쌍을 URL에 추가
*
* @param {object} paramCond - 파라미터 객체 (예: ALL_PARAM_COND)
* @param {object} additionalParams - 추가할 파라미터들의 key-value 객체 (예: {key1: value1, key2: value2})
* @param {string} baseUrl - 기본 URL
* @returns {string} 모든 파라미터가 추가된 URL
*/
var buildUrlWithParamCondAndMultipleKeys = function(paramCond, additionalParams, baseUrl) {
// 파라미터 객체가 정의되어 있지 않으면 기본 URL에 추가 파라미터만 추가
if (typeof paramCond === 'undefined' || !paramCond) {
return addUrlParam(additionalParams, null, baseUrl);
}
// 1. 기본 URL에 파라미터 객체의 모든 파라미터 추가
var urlWithParams = addUrlParam(paramCond, null, baseUrl);
// 2. 추가 파라미터들 추가
if (additionalParams && typeof additionalParams === 'object') {
urlWithParams = addUrlParam(additionalParams, null, urlWithParams);
}
return urlWithParams;
};
/**
* URL에 컨택스트 패스를 적용하는 유틸리티 함수
* 상대 URL을 받아서 컨택스트 패스가 적용된 절대 URL을 반환
*
* @param {string} url - 상대 URL (예: '/user/list.do', '/bbs/user/post/comment/register.ajax')
* @returns {string} 컨택스트 패스가 적용된 URL
*/
var buildContextUrl = function(url) {
// contextPath 변수가 정의되지 않은 경우 기본값 사용
if (typeof contextPath === 'undefined') {
console.warn('contextPath 변수가 정의되지 않았습니다. 기본값("")을 사용합니다.');
return url;
}
// URL이 비어있거나 null인 경우 처리
if (!url || url.trim() === '') {
return url;
}
// 이미 절대 URL인 경우 (http:// 또는 https://로 시작) 그대로 반환
if (url.startsWith('http://') || url.startsWith('https://')) {
return url;
}
// 상대 URL인 경우 컨택스트 패스 적용
// URL이 '/'로 시작하지 않는 경우 '/' 추가
var normalizedUrl = url.startsWith('/') ? url : '/' + url;
// 컨택스트 패스가 빈 문자열이거나 '/'인 경우 처리
if (!contextPath || contextPath === '/') {
return normalizedUrl;
}
// 컨택스트 패스와 URL 결합 (중복된 '/' 제거)
var result = contextPath;
if (!contextPath.endsWith('/') && !normalizedUrl.startsWith('/')) {
result += '/';
} else if (contextPath.endsWith('/') && normalizedUrl.startsWith('/')) {
normalizedUrl = normalizedUrl.substring(1);
}
result += normalizedUrl;
return result;
};
/**
* 공통코드를 조회하여 select box에 option을 추가하는 함수
* 중요로직: 코드 그룹 ID를 기준으로 해당 코드들을 조회하여 select box를 동적으로 생성
* 확장된 파라미터를 통해 정렬 컬럼, 정렬 방향, 추가 검색 조건 등을 설정할 수 있음
*
* @param {string} cdGroupId - 코드 그룹 ID (예: 'RGN_SE_CD', 'DSCL_MTHD_CD' 등)
* @param {string} selectId - select 엘리먼트의 ID
* @param {string} firstOptionText - 첫번째 옵션 텍스트 (예: '전체', '선택하세요' 등)
* @param {string} selectedValue - 선택될 값 (옵션)
* @param {object} options - 추가 옵션 객체 (옵션)
* @param {string} options.sortColumn - 정렬 컬럼 (예: 'SORT_ORDR', 'CD_NM', 'CD_ID' 등)
* @param {boolean} options.sortAscending - 정렬 방향 (true: 오름차순, false: 내림차순)
* @param {string} options.searchCdId - 코드 ID 검색 조건
* @param {string} options.searchCdNm - 코드명 검색 조건
* @param {string} options.searchUseYn - 사용 여부 (기본값: 'Y')
* @param {string} options.searchAttribute1 - 속성1 검색 조건
* @param {string} options.searchAttribute2 - 속성2 검색 조건
* @param {string} options.searchAttribute3 - 속성3 검색 조건
* @param {string} options.searchAttribute4 - 속성4 검색 조건
* @param {string} options.searchAttribute5 - 속성5 검색 조건
* @param {string} options.searchRegDttmStart - 등록일시 시작일
* @param {string} options.searchRegDttmEnd - 등록일시 종료일
* @param {string} options.searchRgtr - 등록자 검색 조건
* @param {string} options.searchMdfcnDttmStart - 수정일시 시작일
* @param {string} options.searchMdfcnDttmEnd - 수정일시 종료일
* @param {string} options.searchMdfr - 수정자 검색 조건
*/
function commonCodeSelectAjax(cdGroupId, selectId, firstOptionText, selectedValue, options) {
if (!cdGroupId || !selectId) {
console.error('commonCodeSelectAjax: cdGroupId와 selectId는 필수 파라미터입니다.');
return;
}
// select 엘리먼트 찾기
var $select = $('#' + selectId);
if ($select.length === 0) {
console.error('commonCodeSelectAjax: ID "' + selectId + '"인 select 엘리먼트를 찾을 수 없습니다.');
return;
}
// 기존 option 초기화 (첫번째 옵션 제외하고 모두 삭제)
$select.find('option:gt(0)').remove();
// 첫번째 옵션이 제공된 경우 설정
if (firstOptionText) {
$select.find('option:first').text(firstOptionText).val('');
}
// options 객체가 없으면 빈 객체로 초기화
options = options || {};
// AJAX 요청 데이터 구성 - 중요로직: 백엔드 CmmnCodeSearchVO와 정확히 매핑
var requestData = {
searchCdGroupId: cdGroupId, // 필수 파라미터
searchUseYn: options.searchUseYn || 'Y' // 기본값: 사용 중인 코드만 조회
};
// 추가 검색 조건들을 requestData에 추가 (값이 있는 경우만)
if (options.searchCdId) requestData.searchCdId = options.searchCdId;
if (options.searchCdNm) requestData.searchCdNm = options.searchCdNm;
if (options.searchAttribute1) requestData.searchAttribute1 = options.searchAttribute1;
if (options.searchAttribute2) requestData.searchAttribute2 = options.searchAttribute2;
if (options.searchAttribute3) requestData.searchAttribute3 = options.searchAttribute3;
if (options.searchAttribute4) requestData.searchAttribute4 = options.searchAttribute4;
if (options.searchAttribute5) requestData.searchAttribute5 = options.searchAttribute5;
if (options.searchRegDttmStart) requestData.searchRegDttmStart = options.searchRegDttmStart;
if (options.searchRegDttmEnd) requestData.searchRegDttmEnd = options.searchRegDttmEnd;
if (options.searchRgtr) requestData.searchRgtr = options.searchRgtr;
if (options.searchMdfcnDttmStart) requestData.searchMdfcnDttmStart = options.searchMdfcnDttmStart;
if (options.searchMdfcnDttmEnd) requestData.searchMdfcnDttmEnd = options.searchMdfcnDttmEnd;
if (options.searchMdfr) requestData.searchMdfr = options.searchMdfr;
// 정렬 관련 파라미터 추가
if (options.sortColumn) {
requestData.sortColumn = options.sortColumn;
// sortAscending이 명시적으로 false인 경우에만 false로 설정 (기본값: true)
requestData.sortAscending = options.sortAscending !== false;
}
// AJAX로 코드 상세 목록 조회
$.ajax({
url: '/common/code/detail/list.ajax',
type: 'GET',
data: requestData,
success: function(response) {
if (response && response.success) {
// 응답 데이터 구조 확인 (그리드용 응답의 경우 response.data.data에 실제 배열이 있을 수 있음)
var codeList = null;
if (response.data && Array.isArray(response.data)) {
codeList = response.data;
} else if (response.data && response.data.data && Array.isArray(response.data.data)) {
codeList = response.data.data;
} else {
console.warn('commonCodeSelectAjax: 응답 데이터 구조를 찾을 수 없습니다. cdGroupId: ' + cdGroupId, response);
return;
}
// 정렬 처리 - 중요로직: 백엔드에서 정렬되지 않은 경우 클라이언트에서 기본 정렬 적용
if (!options.sortColumn) {
// 정렬 컬럼이 지정되지 않은 경우 기본적으로 정렬순서(sortOrdr) 기준으로 정렬
codeList.sort(function(a, b) {
return (a.sortOrdr || 0) - (b.sortOrdr || 0);
});
}
// option 추가
$.each(codeList, function(index, item) {
var option = $('<option></option>');
option.val(item.cdId).text(item.cdNm);
$select.append(option);
});
// 선택될 값이 있으면 설정
if (selectedValue) {
$select.val(selectedValue);
}
} else {
console.warn('commonCodeSelectAjax: 코드 조회 결과가 없거나 실패했습니다. cdGroupId: ' + cdGroupId);
}
},
error: function(xhr, status, error) {
console.error('commonCodeSelectAjax: 코드 조회 중 오류 발생', {
cdGroupId: cdGroupId,
status: status,
error: error
});
}
});
}
/**
* 자식 팝업창들을 모두 닫고 현재 창을 닫는 공통 함수
*
* @param {Array} childPopups - 자식 팝업창 참조 배열
* @returns {Array} 초기화된 빈 배열
*/
function closeChildPopupsAndSelf(childPopups) {
// 자식 팝업창들이 배열이 아닌 경우 빈 배열로 초기화
if (!Array.isArray(childPopups)) {
childPopups = [];
}
// 열려있는 자식 팝업창들을 먼저 닫기
for (var i = 0; i < childPopups.length; i++) {
try {
if (childPopups[i] && !childPopups[i].closed) {
childPopups[i].close();
}
} catch (e) {
// 팝업창 접근 오류 무시 (이미 닫혔거나 접근 권한 없음)
console.warn('팝업창 닫기 중 오류 발생:', e.message);
}
}
// 자식 팝업 배열 초기화
childPopups.length = 0;
// 자신의 창 닫기
window.close();
// 초기화된 배열 반환
return childPopups;
}
/**
* 전역 드롭다운 인스턴스 관리
* 중요한 로직 주석: 활성화된 드롭다운들을 추적하여 다중 오픈 제어
*/
if (typeof window.XitDropdownInstances === 'undefined') {
window.XitDropdownInstances = [];
}
/**
* XIT 공통 드롭다운 컴포넌트
* 중요한 로직 주석: 재사용 가능한 드롭다운 컴포넌트로 여러 페이지에서 사용할 수 있다.
*/
function XitDropdown(options) {
// 기본 옵션 설정
this.options = $.extend({
inputSelector: null, // input 필드 셀렉터 (필수)
hiddenSelector: null, // hidden 필드 셀렉터 (필수)
dataUrl: null, // 데이터 조회 URL (필수)
width: 'auto', // 드롭다운 넓이
maxHeight: '300px', // 드롭다운 최대 높이
displayFields: [], // 표시할 필드 배열
valueField: 'id', // 값 필드명
textField: 'name', // 텍스트 필드명
searchField: 'name', // 검색 대상 필드명
placeholder: '검색어를 입력하세요', // placeholder 텍스트
noResultsText: '검색 결과가 없습니다', // 검색 결과 없음 텍스트
cssClass: 'xit-dropdown', // CSS 클래스명
disabled: false, // 비활성화 여부
allowMultiple: false // 다중 드롭다운 허용 여부 (기본값: false)
}, options);
// 필수 옵션 검증
if (!this.options.inputSelector || !this.options.hiddenSelector || !this.options.dataUrl) {
throw new Error('XitDropdown: inputSelector, hiddenSelector, dataUrl은 필수입니다.');
}
this.$input = $(this.options.inputSelector);
this.$hidden = $(this.options.hiddenSelector);
this.data = [];
this.filteredData = [];
this.selectedIndex = -1;
this.isOpen = false;
this.keyboardNavActive = false; // 키보드 네비게이션 상태 추적
this.dropdownId = 'xit-dropdown-' + Date.now() + Math.random().toString(36).substr(2, 9);
// 전역 드롭다운 인스턴스 배열에 현재 인스턴스 등록
window.XitDropdownInstances.push(this);
this.init();
}
/**
* XitDropdown 프로토타입 메소드들
*/
XitDropdown.prototype = {
/**
* 드롭다운 초기화
* 중요한 로직 주석: DOM 구조를 생성하고 데이터를 로드한 후 이벤트를 바인딩한다.
*/
init: function() {
this.createDropdownElement();
this.loadData();
this.bindEvents();
// 비활성화 상태 설정
if (this.options.disabled) {
this.$input.prop('disabled', true);
}
},
/**
* 드롭다운 DOM 엘리먼트 생성
* 중요한 로직 주석: input 필드 다음에 드롭다운 컨테이너를 추가한다.
*/
createDropdownElement: function() {
var containerClass = this.options.cssClass + '-container';
var dropdownClass = this.options.cssClass;
// 이미 컨테이너가 있다면 제거
this.$input.closest('.' + containerClass).find('.' + dropdownClass).remove();
// 컨테이너가 없다면 생성
if (!this.$input.parent().hasClass(containerClass)) {
this.$input.wrap('<div class="' + containerClass + '" style="position: relative; display: inline-block;"></div>');
}
// 드롭다운 엘리먼트 생성
var $dropdown = $('<div>')
.attr('id', this.dropdownId)
.addClass(dropdownClass)
.css({
'position': 'absolute',
'top': '100%',
'left': '0',
'right': '0',
'background': 'white',
'border': '1px solid #ccc',
'border-top': 'none',
'border-radius': '0 0 4px 4px',
'box-shadow': '0 2px 8px rgba(0, 0, 0, 0.1)',
'z-index': '1000',
'display': 'none',
'max-height': this.options.maxHeight,
'overflow-y': 'auto',
'width': this.options.width
});
this.$input.parent().append($dropdown);
this.$dropdown = $dropdown;
},
/**
* 데이터 로드
* 중요한 로직 주석: AJAX로 서버에서 데이터를 가져온다.
*/
loadData: function() {
var self = this;
$.ajax({
url: this.options.dataUrl,
type: 'POST',
success: function(response) {
if (response && response.success) {
self.data = response.data || [];
self.filteredData = self.data;
} else {
console.error('XitDropdown: 데이터 로드 실패', response);
}
},
error: function() {
console.error('XitDropdown: 데이터 로드 중 오류 발생');
}
});
},
/**
* 이벤트 바인딩
* 중요한 로직 주석: input 필드와 드롭다운의 각종 이벤트를 처리한다.
*/
bindEvents: function() {
var self = this;
// input 클릭 시 드롭다운 열기
this.$input.on('click.xitdropdown', function(e) {
e.stopPropagation();
if (!self.options.disabled) {
self.showDropdown();
}
});
// input 포커스 시 드롭다운 열기
this.$input.on('focus.xitdropdown', function() {
if (!self.options.disabled) {
self.showDropdown();
}
});
// input 키보드 입력 시 실시간 필터링
this.$input.on('input.xitdropdown', function() {
if (!self.options.disabled) {
self.filterData($(this).val());
self.showDropdown();
self.selectedIndex = -1;
}
});
// 키보드 네비게이션
this.$input.on('keydown.xitdropdown', function(e) {
if (!self.isOpen || self.options.disabled) return;
switch (e.keyCode) {
case 38: // 위 화살표
e.preventDefault();
self.navigateDropdown(-1);
break;
case 40: // 아래 화살표
e.preventDefault();
self.navigateDropdown(1);
break;
case 13: // 엔터
e.preventDefault();
self.selectCurrentItem();
break;
case 27: // ESC
self.hideDropdown();
break;
}
});
// 문서 클릭 시 드롭다운 닫기
$(document).on('click.xitdropdown-' + this.dropdownId, function(e) {
if (!$(e.target).closest('.' + self.options.cssClass + '-container').length) {
self.hideDropdown();
}
});
},
/**
* 데이터 필터링
* 중요한 로직 주석: 검색어에 따라 데이터를 필터링한다. searchField가 배열인 경우 여러 필드에서 검색한다.
*/
filterData: function(searchTerm) {
var term = searchTerm.trim().toLowerCase();
var searchFields = this.options.searchField;
var self = this;
if (term === '') {
this.filteredData = this.data;
} else {
this.filteredData = this.data.filter(function(item) {
// searchField가 배열인지 확인
if (Array.isArray(searchFields)) {
// 배열인 경우: 여러 필드 중 하나라도 검색어가 포함되면 true (OR 조건)
return searchFields.some(function(field) {
return item[field] && item[field].toString().toLowerCase().indexOf(term) > -1;
});
} else {
// 단일 필드인 경우: 기존 로직 유지 (하위 호환성)
return item[searchFields] && item[searchFields].toString().toLowerCase().indexOf(term) > -1;
}
});
}
},
/**
* 드롭다운 표시
* 중요한 로직 주석: 필터링된 데이터를 기반으로 드롭다운을 생성하고 표시한다.
*/
showDropdown: function() {
var html = '';
var self = this;
// 다중 드롭다운 허용하지 않는 경우 다른 드롭다운들 모두 닫기
if (!this.options.allowMultiple) {
window.XitDropdownInstances.forEach(function(instance) {
if (instance !== self && instance.isOpen) {
instance.hideDropdown();
}
});
}
// 중요한 로직 주석: textField에 이미 값이 있다면 해당 값으로 먼저 필터링 처리
var currentInputValue = this.$input.val();
if (currentInputValue && currentInputValue.trim() !== '') {
this.filterData(currentInputValue);
} else {
// textField에 값이 없으면 전체 데이터 표시
this.filteredData = this.data;
}
if (this.filteredData.length === 0) {
html = '<div class="' + this.options.cssClass + '-item no-results" style="padding: 10px 12px; color: #666; font-style: italic; text-align: center; cursor: default;">' + this.options.noResultsText + '</div>';
} else {
this.filteredData.forEach(function(item, index) {
html += '<div class="' + self.options.cssClass + '-item" data-index="' + index + '" data-value="' + item[self.options.valueField] + '" style="padding: 10px 12px; cursor: pointer; border-bottom: 1px solid #f5f5f5; transition: background-color 0.2s;">';
html += ' <div class="' + self.options.cssClass + '-main" style="font-size: 14px; font-weight: 600; color: #333; margin-bottom: 4px;">' + item[self.options.textField] + '</div>';
if (self.options.displayFields.length > 0) {
html += ' <div class="' + self.options.cssClass + '-details" style="display: flex; flex-wrap: wrap; gap: 8px; font-size: 12px;">';
self.options.displayFields.forEach(function(field) {
if (item[field.name] !== undefined) {
var value = field.formatter ? field.formatter(item[field.name]) : item[field.name];
html += ' <span class="' + self.options.cssClass + '-value" style="color: #666; background-color: #f8f9fa; padding: 2px 6px; border-radius: 3px; font-size: 11px;">' + field.label + ': ' + value + '</span>';
}
});
html += ' </div>';
}
html += '</div>';
});
}
this.$dropdown.html(html);
this.$dropdown.show();
this.isOpen = true;
// 클릭 이벤트 바인딩
this.$dropdown.find('.' + this.options.cssClass + '-item:not(.no-results)').on('click', function() {
var index = parseInt($(this).attr('data-index'));
self.selectItem(index);
});
// hover 효과 추가 - 키보드 네비게이션과 상호 배타적으로 동작
this.$dropdown.find('.' + this.options.cssClass + '-item:not(.no-results)').hover(
function() {
// 키보드 네비게이션이 활성화된 상태가 아닐 때만 hover 효과 적용
if (!self.keyboardNavActive) {
$(this).css('background-color', '#e3f2fd');
}
},
function() {
// 키보드 네비게이션이 활성화된 상태가 아니고 선택되지 않은 항목일 때만 스타일 초기화
if (!self.keyboardNavActive && !$(this).hasClass('selected')) {
$(this).css('background-color', '');
}
}
);
// 마우스가 드롭다운 영역에 들어올 때 키보드 네비게이션 상태 초기화
this.$dropdown.on('mouseenter', function() {
if (self.keyboardNavActive) {
self.keyboardNavActive = false;
self.selectedIndex = -1;
self.$dropdown.find('.' + self.options.cssClass + '-item').removeClass('selected').css('background-color', '');
}
});
// 중요한 로직 주석: hidden 필드에 값이 있으면 해당 항목을 찾아 자동 하이라이트 및 위치 이동
this.highlightPreselectedItem();
},
/**
* 미리 선택된 항목 하이라이트
* 중요한 로직 주석: hidden 필드에 값이 있으면 해당 항목을 찾아 자동으로 하이라이트하고 스크롤 위치 조정
*/
highlightPreselectedItem: function() {
var self = this;
var hiddenValue = this.$hidden.val();
// hidden 필드에 값이 없으면 처리하지 않음
if (!hiddenValue || hiddenValue.trim() === '') {
return;
}
var $items = this.$dropdown.find('.' + this.options.cssClass + '-item:not(.no-results)');
var targetIndex = -1;
// valueField와 일치하는 항목 찾기
this.filteredData.forEach(function(item, index) {
if (item[self.options.valueField] === hiddenValue) {
targetIndex = index;
}
});
// 일치하는 항목이 있으면 하이라이트 및 스크롤 조정
if (targetIndex >= 0 && targetIndex < $items.length) {
this.selectedIndex = targetIndex;
var $targetItem = $items.eq(targetIndex);
// 기존 하이라이트 제거
$items.removeClass('selected').css('background-color', '');
// 대상 항목 하이라이트
$targetItem.addClass('selected').css('background-color', '#e3f2fd');
// 스크롤 위치 조정
var dropdownHeight = this.$dropdown.height();
var itemHeight = $targetItem.outerHeight();
var scrollTop = this.$dropdown.scrollTop();
var itemTop = $targetItem.position().top + scrollTop;
if (itemTop < scrollTop) {
this.$dropdown.scrollTop(itemTop);
} else if (itemTop + itemHeight > scrollTop + dropdownHeight) {
this.$dropdown.scrollTop(itemTop + itemHeight - dropdownHeight);
}
}
},
/**
* 드롭다운 숨기기
* 중요한 로직 주석: 드롭다운을 닫을 때 키값 존재 여부에 따라 textField 값을 조정한다.
*/
hideDropdown: function() {
// 중요한 로직 주석: 드롭다운이 닫힐 때 키값이 없으면 텍스트 제거, 키값이 있으면 해당 텍스트로 설정
this.validateAndSetFieldValues();
this.$dropdown.hide();
this.isOpen = false;
this.selectedIndex = -1;
this.keyboardNavActive = false; // 키보드 네비게이션 상태 초기화
},
/**
* 필드 값 검증 및 설정
* 중요한 로직 주석: hidden 필드의 키값에 따라 textField 값을 적절히 조정한다.
*/
validateAndSetFieldValues: function() {
var hiddenValue = this.$hidden.val();
if (!hiddenValue || hiddenValue.trim() === '') {
// 키값이 없으면 textField 값 제거
this.$input.val('');
} else {
// 키값이 있으면 해당 키값에 맞는 텍스트 값을 찾아서 설정
var textValue = this.getTextValueByKey(hiddenValue);
if (textValue) {
this.$input.val(textValue);
}
}
},
/**
* 키값으로 텍스트 값 찾기
* 중요한 로직 주석: 전체 데이터에서 키값에 해당하는 텍스트 값을 찾는다.
*/
getTextValueByKey: function(keyValue) {
var self = this;
var textValue = '';
// 전체 데이터에서 키값과 일치하는 항목 찾기
this.data.forEach(function(item) {
if (item[self.options.valueField] === keyValue) {
textValue = item[self.options.textField];
return false; // 찾았으면 반복 종료
}
});
return textValue;
},
/**
* 드롭다운 네비게이션
* 중요한 로직 주석: 화살표 키로 드롭다운 항목들을 네비게이션한다.
*/
navigateDropdown: function(direction) {
var $items = this.$dropdown.find('.' + this.options.cssClass + '-item:not(.no-results)');
if ($items.length === 0) return;
// 키보드 네비게이션 활성화 상태로 설정
this.keyboardNavActive = true;
// 이전 선택 항목 하이라이트 제거
$items.removeClass('selected').css('background-color', '');
// 새로운 인덱스 계산
this.selectedIndex += direction;
if (this.selectedIndex < 0) this.selectedIndex = $items.length - 1;
if (this.selectedIndex >= $items.length) this.selectedIndex = 0;
// 새로운 항목 하이라이트
var $selectedItem = $items.eq(this.selectedIndex);
$selectedItem.addClass('selected').css('background-color', '#e3f2fd');
// 스크롤 조정
var dropdownHeight = this.$dropdown.height();
var itemHeight = $selectedItem.outerHeight();
var scrollTop = this.$dropdown.scrollTop();
var itemTop = $selectedItem.position().top + scrollTop;
if (itemTop < scrollTop) {
this.$dropdown.scrollTop(itemTop);
} else if (itemTop + itemHeight > scrollTop + dropdownHeight) {
this.$dropdown.scrollTop(itemTop + itemHeight - dropdownHeight);
}
},
/**
* 현재 선택된 항목 선택
*/
selectCurrentItem: function() {
if (this.selectedIndex >= 0 && this.selectedIndex < this.filteredData.length) {
this.selectItem(this.selectedIndex);
}
},
/**
* 항목 선택
* 중요한 로직 주석: 선택된 항목의 값을 input과 hidden 필드에 설정한다.
*/
selectItem: function(index) {
if (index >= 0 && index < this.filteredData.length) {
var selectedItem = this.filteredData[index];
this.$input.val(selectedItem[this.options.textField]);
this.$hidden.val(selectedItem[this.options.valueField]);
this.hideDropdown();
// 선택 이벤트 발생
if (this.options.onSelect && typeof this.options.onSelect === 'function') {
this.options.onSelect(selectedItem);
}
}
},
/**
* 드롭다운 비활성화/활성화
*/
setDisabled: function(disabled) {
this.options.disabled = disabled;
this.$input.prop('disabled', disabled);
if (disabled) {
this.hideDropdown();
}
},
/**
* 드롭다운 파괴
*/
destroy: function() {
// 전역 인스턴스 배열에서 현재 인스턴스 제거
var index = window.XitDropdownInstances.indexOf(this);
if (index > -1) {
window.XitDropdownInstances.splice(index, 1);
}
// 이벤트 제거
this.$input.off('.xitdropdown');
$(document).off('.xitdropdown-' + this.dropdownId);
// DOM 제거
this.$dropdown.remove();
// 컨테이너가 빈 경우 원래 상태로 복원
var $container = this.$input.parent();
if ($container.hasClass(this.options.cssClass + '-container') && $container.children().length === 1) {
this.$input.unwrap();
}
}
};
/**
* 팝업 창 열기 공통함수
* 중요로직: 팝업창을 화면 중앙에 위치시켜 열기, 위치 조정 가능
* @param {string} url - 팝업에서 열 URL
* @param {number} width - 팝업 너비 (기본값: 600)
* @param {number} height - 팝업 높이 (기본값: 600)
* @param {string} windowName - 윈도우 이름 (기본값: 'popup')
* @param {number} leftOffset - 중앙 위치에서 좌우 조정값 (기본값: 0, 음수면 왼쪽, 양수면 오른쪽)
* @param {number} topOffset - 중앙 위치에서 상하 조정값 (기본값: 0, 음수면 위쪽, 양수면 아래쪽)
* @returns {Window} 열린 팝업 창 객체
*/
function openPopup(url, width, height, windowName, leftOffset, topOffset) {
// 기본값 설정
width = width || 600;
height = height || 600;
windowName = windowName || 'popup';
leftOffset = leftOffset || 0;
topOffset = topOffset || 0;
// 화면 중앙 위치 계산
var left = Math.max(0, (window.screen.availLeft + (window.screen.availWidth - width) / 2));
var top = Math.max(0, (window.screen.availTop + (window.screen.availHeight - height) / 2));
// 중요로직: 최종 계산된 중앙 위치에 조정값 적용
left = Math.max(0, left + leftOffset);
top = Math.max(0, top + topOffset);
// 팝업 옵션 설정
var popupOptions = 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',resizable=yes,scrollbars=yes';
// 팝업 창 열기
return window.open(url, windowName, popupOptions);
}