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
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);
|
|
}
|
|
|