import { generateUUIDv4 } from "../../utils/crypto/generate-uuidv4";
import { sha256 } from "../../utils/crypto/sha256";
import { getBindCellIndexWithMultiColumns } from "../../utils/grid/get-bind-cell-index-with-multi-columns";
import {
removeRowsByCondition,
filterRowsByCondition,
} from "../../utils/dataset/remove-rows-by-condition";
import { forEachDataset } from "../../utils/dataset/for-each-dataset";
import { getMenuId } from "../../adapters/ezworks/com-api/get-menu-id";
import { mkArg } from "../../utils/common/mk-arg";
import { YySmr } from "./yy-smr-wrapper";
import { LinkedComboManager, insertModeRow } from "../../utils/combo/linked-combo";
/**
* @typedef hCmbOption
* @property {nexacro.Div | nexacro.Div[] | undefined} div
* 학사콤보를 적용할 Div 객체 혹은 그
* 배열입니다.
* @property {nexacro.Grid | nexacro.Grid[]} [grid]
* 학사콤보를 적용할 Grid 객체
* 혹은 그 배열입니다.
* @property {HCmbOrgMode | HCmbOrgMode[] | HCmbComboMode | HCmbComboMode[]}
* [orgMode]
* 학사콤보의 콤보 모드. 문자열로 설정시 모든 모드가 동일하게 적용됩니다. (예: "a")
* @property {HCmbOrgMode | HCmbOrgMode[] | HCmbComboMode | HCmbComboMode[]}
* [orgGridMode]
* 그리드에서 사용하는 조직 모드. 문자열로 설정시 모든 모드가 동일하게 적용됩니다. (예: "s")
* @property {FilterRowsByCondtionFunc} [orgFilter]
* 조직 콤보 필터를 위한 함수
* @property {HCmbComboMode} [smrMode]
* 학사콤보의 학기 콤보 모드
* @property {HCmbComboMode} [smrGridMode]
* 그리드에서 사용하는 학사콤보의 학기 콤보 모드
* @property {FilterRowsByCondtionFunc} [smrFilter]
* 학기 콤보 필터를 위한 함수
* @property {FilterRowsByCondtionFunc} [smrGridFilter]
* 그리드에서 사용하는 학기 콤보 필터를 위한 함수
* @property {string | string[]} [smrAliasId]
* 학기 콤보의 표시 타입, 전역 설정에서 hCmbSmrAlias 설정을 통해 참조합니다.
* @property {string | string[]} [smrTypeId]
* 학기 콤보의 타입, 전역 설정에서 hCmbSmrType 설정을 통해 참조합니다.
* @property {string | string[]} [smrGridTypeId]
* 학기 콤보의 타입, 전역 설정에서 hCmbSmrType 설정을 통해 참조합니다.
* @property {HCmbTargetComponentName} [targtCompNm]
* 학사콤보를 적용할 컴포넌트를 판별하는 정규식을 재정의
* 합니다.
* @property {boolean} [useMngtDpmj=false]
* **@deprecated** 이 함수는 추가로 사용하지 마세요. orgDetUse으로 대체되었습니다.
* 조직 데이터를 관리학과 기준으로 조회합니다.
* @property {string} [fcltGrscDstnct]
* 학부대학원구분 (공통코드: 1715) (생략 시 메뉴관리의 데이터를 사용)
* @property {string} [orgDetUse]
* 조직세부사용 (공통코드: 2002) (생략 시 메뉴관리의 데이터를 사용)
* @property {boolean} [useDataAuth]
* 데이터권한여부 (생략 시 메뉴관리의 데이터를 사용)
* @property {number} [smrOffset]
* 학기 설정 값을 기준 연도 학기에서 더하거나 뺄 수
*/
/**
* @typedef HCmbComboComponentSet
* @property {nexacro.Combo} cmb_fcltDvcd
* @property {nexacro.Combo} cmb_univCd
* @property {nexacro.Combo} cmb_dpmjCd
* @property {nexacro.Combo} cmb_majrCd
* @property {nexacro.Combo} cmb_detMajrCd
* @access private
*/
/**
* 콤보의 최상위 빈 값에 사용되는 명칭을 구분하는 플래그 입니다.
*
* 'a': 전체
*
* 's': 학과
*
* 'n': 추가 안함
*
* @typedef {'a' | 's' | 'n'} HCmbComboMode
*/
/**
* 그리드의 각 조직별 콤보 mode 값
*
* @typedef HCmbOrgMode
* @property {HCmbComboMode} [fclt] 대학/대학원 모드
* @property {HCmbComboMode} [univ] 대학 모드
* @property {HCmbComboMode} [dpmj] 학과 모드
* @property {HCmbComboMode} [majr] 전공 모드
* @property {HCmbComboMode} [detMajr] 전공 세부 모드
*/
/** 학사콤보 클래스 */
export class HCmb {
static LOG_KEY = "HCmb";
static EXIST_YY = 1;
static EXIST_SMR = 2;
static EXIST_FCLT = 4;
static EXIST_UNIV = 8;
static EXIST_DPMJ = 16;
static EXIST_MAJR = 32;
static EXIST_DET_MAJR = 64;
static EXIST_YY_SMR = HCmb.EXIST_YY | HCmb.EXIST_SMR;
static EXIST_ORG =
HCmb.EXIST_FCLT |
HCmb.EXIST_UNIV |
HCmb.EXIST_DPMJ |
HCmb.EXIST_MAJR |
HCmb.EXIST_DET_MAJR;
constructor(form) {
this.fm = form;
this.UNIQUE_KEY = sha256(generateUUIDv4()).toString();
this.cmbLockKeys = new Set();
this.oldValue = ""; // 그리드 grdOldValue 저장용
const globalConfig = nexacro.EzFoundation?.hCmbTargetComponentName;
// 대상 컴포넌트 판별 정규식
this.targetCompNm = {
yyCompNm: globalConfig?.yyCompNm || /^spn_(?:grdu)?[yY]y$/,
smrCompNm: globalConfig?.smrCompNm || /^cmb_(?:grdu)?[sS]mr$/,
fcltCompNm: globalConfig?.fcltCompNm || /^cmb_fcltDvcd$/,
univCompNm: globalConfig?.univCompNm || /^cmb_univCd$/,
dpmjCompNm: globalConfig?.dpmjCompNm || /^cmb_dpmjCd$/,
majrCompNm: globalConfig?.majrCompNm || /^cmb_majrCd$/,
detMajrCompNm: globalConfig?.detMajrCompNm || /^cmb_detMajrCd$/,
smrColNm: globalConfig?.smrColNm || ["SMR", "GRDU_SMR"],
fcltColNm: globalConfig?.fcltColNm || "FCLT_DVCD",
univColNm: globalConfig?.univColNm || "UNIV_CD",
dpmjColNm: globalConfig?.dpmjColNm || "DPMJ_CD",
majrColNm: globalConfig?.majrColNm || "MAJR_CD",
detMajrColNm: globalConfig?.detMajrColNm || "DET_MAJR_CD",
};
}
get taskDtlCd() {
const openMenuDs = nexacro.getApplication().gds_openMenu;
return openMenuDs.getColumn(openMenuDs.rowposition, "TASK_DTL_CD");
}
/**
* 학사콤보 초기화
*
* @param {hCmbOption} option
* @returns {Promise<never> | Promise<void>}
*/
async init(option) {
if (!option) return Promise.reject(Error("인자가 부족합니다."));
this.parseOption(option);
this.discoverTargetComponent();
await this.searchData();
this.initYySmrValue();
await this.makeInitDs();
await this.initComp();
}
//
/**
* 받은 이름에 고유 키를 붙여서 반환합니다.
*
* @param {string} name 명칭
* @returns {string} 고유 키가 붙은 명칭
* @access private
*/
unique(name) {
return name + "_" + this.UNIQUE_KEY;
}
/**
* Option을 해석합니다.
*
* @param {hCmbOption} option
* @access private
*/
parseOption(option) {
// arrDiv
this.arrDiv = option.div || [];
if (this.arrDiv && !(this.arrDiv instanceof Array)) {
this.arrDiv = [this.arrDiv];
}
this.arrGrid = option.grid || [];
if (this.arrGrid && !(this.arrGrid instanceof Array)) {
this.arrGrid = [this.arrGrid];
}
// 조직 콤보 모드
let orgMode = option.orgMode || [];
if (orgMode && !(orgMode instanceof Array)) {
orgMode = [orgMode];
}
/**
* @type {HCmbOrgMode[]}
*/
this.orgMode = orgMode.map((mode) => {
if (typeof mode === "string") {
const modeObj = {
fclt: mode,
univ: mode,
dpmj: mode,
majr: mode,
detMajr: mode,
};
return modeObj;
} else if (typeof mode === "object") {
return mode;
} else {
return {};
}
});
// 그리드 조직 콤보 모드
let orgGridMode = option.orgGridMode || [];
if (orgGridMode && !(orgGridMode instanceof Array)) {
orgGridMode = [orgGridMode];
}
/**
* @type {HCmbOrgMode[]}
*/
this.orgGridMode = orgGridMode.map((mode) => {
if (typeof mode === "string") {
return {
fclt: mode,
univ: mode,
dpmj: mode,
majr: mode,
detMajr: mode,
};
} else if (typeof mode === "object") {
return mode;
} else {
return {};
}
});
this.orgFilter = option.orgFilter || null;
// 학기 모드
this.smrMode = option.smrMode || [];
if (this.smrMode && !(this.smrMode instanceof Array)) {
this.smrMode = [this.smrMode];
}
// 그리드 학기 모드
this.smrGridMode = option.smrGridMode || [];
if (this.smrGridMode && !(this.smrGridMode instanceof Array)) {
this.smrGridMode = [this.smrGridMode];
}
// 학기 별칭 아이디
this.smrAliasId = option.smrAliasId || [];
if (this.smrAliasId && !(this.smrAliasId instanceof Array)) {
this.smrAliasId = [this.smrAliasId];
}
// 학기 타입 아이디
this.smrTypeId = option.smrTypeId || [];
if (this.smrTypeId && !(this.smrTypeId instanceof Array)) {
this.smrTypeId = [this.smrTypeId];
}
// 그리드 학기 타입 아이디
this.smrGridTypeId = option.smrGridTypeId || [];
if (this.smrGridTypeId && !(this.smrGridTypeId instanceof Array)) {
this.smrGridTypeId = [this.smrGridTypeId];
}
// 학기 필터
this.smrFilter = option.smrFilter || [];
if (this.smrFilter && !(this.smrFilter instanceof Array)) {
this.smrFilter = [this.smrFilter];
}
// 그리드 학기 필터
this.smrGridFilter = option.smrGridFilter || [];
if (this.smrGridFilter && !(this.smrGridFilter instanceof Array)) {
this.smrGridFilter = [this.smrGridFilter];
}
// 관리학과 사용
this.useMngtDpmj = option.useMngtDpmj || false;
// 학부/대학원 구분
this.fcltGrscDstnct = option.fcltGrscDstnct;
// 조직 세부 사용
this.orgDetUse = option.orgDetUse;
// 데이터권한사용
this.useDataAuth = option.useDataAuth;
/**
* 컴포넌트 명칭 재정의
*
* @type {HCmbTargetComponentName}
* @access private
*/
this.targetCompNm = Object.assign(
this.targetCompNm,
option.targtCompNm,
);
// 학기 오프셋
this.smrOffset = option.smrOffset;
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isYyComponent(comp) {
return this.targetCompNm.yyCompNm.test(comp?.name);
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isSmrComponent(comp) {
return this.targetCompNm.smrCompNm.test(comp?.name);
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isFcltComponent(comp) {
return this.targetCompNm.fcltCompNm.test(comp?.name);
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isUnivComponent(comp) {
return this.targetCompNm.univCompNm.test(comp?.name);
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isDpmjComponent(comp) {
return this.targetCompNm.dpmjCompNm.test(comp?.name);
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isMajrComponent(comp) {
return this.targetCompNm.majrCompNm.test(comp?.name);
}
/**
* @param {nexacro.Component} comp 검사 대상 컴포넌트
* @returns {boolean}
* @access private
*/
isDetMajrComponent(comp) {
return this.targetCompNm.detMajrCompNm.test(comp?.name);
}
/**
* 학사 콤보의 대상 컴포넌트를 찾아 멤버 변수에 저장한다.
*
* @access private
*/
discoverTargetComponent() {
// 일반 컴포넌트
const comp = this.arrDiv?.reduce((acc, div) => {
// 컴포넌트를 반복하여 대상 컴포넌트를 찾는다.
return (
acc |
Array.from(div.form.all).reduce((acc, comp) => {
// 대상 컴포넌트 검사
if (this.isYyComponent(comp)) {
acc |= HCmb.EXIST_YY;
} else if (this.isSmrComponent(comp)) {
acc |= HCmb.EXIST_SMR;
} else if (this.isFcltComponent(comp)) {
acc |= HCmb.EXIST_FCLT;
} else if (this.isUnivComponent(comp)) {
acc |= HCmb.EXIST_UNIV;
} else if (this.isDpmjComponent(comp)) {
acc |= HCmb.EXIST_DPMJ;
} else if (this.isMajrComponent(comp)) {
acc |= HCmb.EXIST_MAJR;
} else if (this.isDetMajrComponent(comp)) {
acc |= HCmb.EXIST_DET_MAJR;
}
return acc;
}, 0)
);
}, 0);
// 그리드 컬럼
const grid = this.arrGrid?.reduce((acc, grid) => {
[
[this.targetCompNm.smrColNm, HCmb.EXIST_SMR],
[this.targetCompNm.fcltColNm, HCmb.EXIST_FCLT],
[this.targetCompNm.univColNm, HCmb.EXIST_UNIV],
[this.targetCompNm.dpmjColNm, HCmb.EXIST_DPMJ],
[this.targetCompNm.majrColNm, HCmb.EXIST_MAJR],
[this.targetCompNm.detMajrColNm, HCmb.EXIST_DET_MAJR],
].forEach(([colNm, flag]) => {
const colIndex = getBindCellIndexWithMultiColumns(
"body",
grid,
colNm,
);
if (colIndex !== -1) {
acc |= flag;
}
});
return acc;
}, 0);
this.existComp = comp | grid;
}
/**
* 데이터를 검색합니다.
*
* @returns {Promise}
* @access private
*/
searchData() {
let DATA_AUTH_XCPT_YN = null;
switch (this.useDataAuth) {
case true:
DATA_AUTH_XCPT_YN = "0";
break;
case false:
DATA_AUTH_XCPT_YN = "1";
break;
}
let FCLT_DVCD = this.fcltGrscDstnct ?? null;
let ORG_DET_USE_DVCD = this.orgDetUse ?? null;
const args = mkArg({
MENU_ID: getMenuId(this.fm),
TASK_DTL_CD: this.taskDtlCd,
DATA_AUTH_XCPT_YN,
FCLT_DVCD,
ORG_DET_USE_DVCD,
});
this._ds_yy = new Dataset("_ds_yy");
this._ds_smr = new Dataset("_ds_smr");
this._ds_org = new Dataset("_ds_org");
this.fm.addChild(this.unique("_ds_yy"), this._ds_yy);
this.fm.addChild(this.unique("_ds_smr"), this._ds_smr);
this.fm.addChild(this.unique("_ds_org"), this._ds_org);
const dataList = [
{
sqlId: nexacro.EzFoundation.hCmbYySqlId || "hcmb.s01",
outDs: this.unique("_ds_yy"),
},
{
sqlId: nexacro.EzFoundation.hCmbSmrSqlId || "hcmb.s02",
outDs: this.unique("_ds_smr"),
},
{
sqlId: this.useMngtDpmj
? nexacro.EzFoundation.hCmbMngtDpmjSqlId || "@hcmb.s04"
: nexacro.EzFoundation.hCmbOrgSqlId || "hcmb.03",
outDs: this.unique("_ds_org"),
},
];
return this.fm.tx.search({
action: "basic",
svcId: this.unique("getYy"),
sqlId: dataList.map((v) => v.sqlId).join(" "),
outDs: dataList.map((v) => v.outDs).join(" "),
param: args,
});
}
/**
* 대학, 학과, 전공 데이터셋을 만든다.
*
* @access private
*/
makeInitDs() {
// 세부 전공 없을 시 행 삭제
if ((HCmb.EXIST_DET_MAJR & this.existComp) === 0) {
removeRowsByCondition(
this._ds_org,
(ctx) => {
return (ctx.rowData["DET_MAJR_CD"] ?? "") !== "";
},
{ mutate: true },
);
}
this._ds_fclt = filterRowsByCondition(this._ds_org, this.orgFilter, {
mutate: false,
});
this._ds_univ = filterRowsByCondition(this._ds_org, this.orgFilter, {
mutate: false,
});
this._ds_dpmj = filterRowsByCondition(this._ds_org, this.orgFilter, {
mutate: false,
});
this._ds_majr = filterRowsByCondition(this._ds_org, this.orgFilter, {
mutate: false,
});
this._ds_detMajr = filterRowsByCondition(this._ds_org, this.orgFilter, {
mutate: false,
});
this.fm.addChild(this.unique("_ds_fcltDvcd"), this._ds_fclt);
this.fm.addChild(this.unique("_ds_univ"), this._ds_univ);
this.fm.addChild(this.unique("_ds_dpmj"), this._ds_dpmj);
this.fm.addChild(this.unique("_ds_majr"), this._ds_majr);
this.fm.addChild(this.unique("_ds_detMajr"), this._ds_detMajr);
// 데이터셋 그룹바이
this.groupByOrgDs(this._ds_fclt, "FCLT_DVCD", "ORD");
this.groupByOrgDs(this._ds_univ, "UNIV_CD", "ORD");
this.groupByOrgDs(this._ds_dpmj, "DPMJ_CD", "ORD");
this.groupByOrgDs(this._ds_majr, "MAJR_CD", "ORD");
this.groupByOrgDs(this._ds_detMajr, "DET_MAJR_CD", "ORD");
// ALL_PATH 변경
this.hotfixAllPath(this._ds_dpmj, 2);
}
/**
* ALL_PATH 컬럼이 맞지 않는 것을 임시로 핫 픽스함.
*
* @access private
*/
hotfixAllPath(ds, maxSep) {
const sep = " > ";
forEachDataset(ds, (ctx) => {
const allPath = ctx.rowData["ALL_PATH"];
if (allPath) {
const pathList = allPath.split(sep);
const newPath = pathList.slice(0, maxSep).join(sep);
ctx.rowData["ALL_PATH"] = newPath;
ctx.dataset.setColumn(ctx.row, "ALL_PATH", newPath);
}
});
}
/**
* 데이터셋 그룹바이
*
* @param {nexacro.Dataset} sDs
* @param {string} colNm
* @param {string} sortColNm
* @param {string[]} [childColIdList]
* @access private
*/
groupByOrgDs(sDs, colNm, sortColNm, childColIdList) {
sDs.enableevent = false;
var initVal = null;
sDs.keystring = `S:${colNm}-ORD`;
removeRowsByCondition(
sDs,
(ctx) => {
// 하위 데이터가 undefined가 아닌 경우 행 삭제
if (
childColIdList &&
childColIdList.some(
(colId) => ctx.rowData[colId] !== undefined,
)
) {
return true;
}
if (!ctx.rowData[colNm]) {
return true;
} else if (initVal !== ctx.rowData[colNm]) {
initVal = ctx.rowData[colNm];
return false;
} else {
return true;
}
},
{ mutate: true },
);
sDs.keystring = `S:${sortColNm}`;
sDs.enableevent = true;
sDs.applyChange();
}
/**
* 연도, 학기 초기화
*
* @access private
*/
initYySmrValue() {
// 연도 초기화
const originYy = this._ds_yy.getColumn(0, "YY");
// 학기 초기화
const originSmr = this._ds_yy.getColumn(0, "SMR");
// 연도 학기 초기화
let yySmr = new YySmr(originYy, originSmr);
if (this.smrOffset !== undefined) {
yySmr = yySmr.add(this.smrOffset);
} else {
nexacro.EzFoundation.hCmbSmrOffsetRule.forEach((rule) => {
if (rule.condition({ form: this.fm })) {
yySmr = yySmr.add(rule.offset);
}
});
}
this.yy = yySmr.yy;
this.smr = yySmr.smr;
}
/**
* 연도 컴포넌트 초기화
*
* @param {nexacro.Spin} comp
* @access private
*/
initYySpin(comp) {
comp.value = this.yy;
}
/**
* 학기 별칭을 분석하여 가져옵니다.
*
* @param {nexacro.Component} comp
* 넥사크로 컴포넌트
* @param {string} [id]
* 키 지정
* @param {string} [gridBindColId]
* @returns {HCmbSmrAliasItem[]}
* 별칭 목록
* @access private
*/
selectSmrAlias({ comp, id, gridBindColId }) {
let result = [];
if (!nexacro.EzFoundation?.hCmbSmrAlias) {
return [];
}
if (id) {
// ID 기반 탐색
for (let alias of nexacro.EzFoundation.hCmbSmrAlias) {
if (id === alias.id) {
result = alias.aliases;
break;
}
}
} else {
// 조건 기반 탐색
for (let alias of nexacro.EzFoundation.hCmbSmrAlias) {
const isCombo = comp instanceof nexacro.Combo;
const isGrid = comp instanceof nexacro.Grid;
if (
alias.condition({
form: this.fm,
component: comp,
isCombo,
isGrid,
gridBindColId,
})
) {
result = alias.aliases;
break;
}
}
}
return result;
}
/**
* 학기 타입을 분석하여 가져옵니다.
*
* @param {nexacro.Component} comp
* 넥사크로 컴포넌트
* @param {string} [id]
* 키 지정
* @param {string} [gridBindColId]
* @returns {HCmbSmrTypeConfig}
* 별칭 목록
* @access private
*/
selectSmrType({ comp, id, gridBindColId }) {
let result = {};
if (!nexacro.EzFoundation?.hCmbSmrType) {
return {};
}
if (id) {
// ID 기반 탐색
for (let type of nexacro.EzFoundation.hCmbSmrType) {
if (id === type.id) {
result = type;
break;
}
}
} else {
// 조건 기반 탐색
for (let type of nexacro.EzFoundation.hCmbSmrType) {
const isCombo = comp instanceof nexacro.Combo;
const isGrid = comp instanceof nexacro.Grid;
if (
type.condition({
form: this.fm,
component: comp,
isCombo,
isGrid,
gridBindColId,
})
) {
result = type;
break;
}
}
}
return result;
}
/**
* 학기 컴포넌트 초기화
*
* @param {nexacro.Combo} comp
* @param {number} divIdx
* @access private
*/
initSmrCombo(comp, divIdx) {
const smrFilter = this.smrFilter[divIdx];
const smrMode = this.smrMode[divIdx] || "n";
const smrType = this.selectSmrType({
comp,
id: this.smrTypeId[divIdx],
});
// 학기 타입 기본 필터
this._ds_smr_c = filterRowsByCondition(this._ds_smr, smrType.filter, {
mutate: false,
});
// 학기 추가 필터
filterRowsByCondition(this._ds_smr_c, smrFilter, {
mutate: true,
});
// 컴포넌트 추가
this.fm.addChild(
this.unique("_ds_smr_c") + divIdx + "_" + comp.id,
this._ds_smr_c,
);
// 학기 콤보 모드 추가
insertModeRow(this._ds_smr_c, ["SMR_NM"], smrMode, {
insertNewRow: false,
});
// 별칭 처리
const smrAlias = this.selectSmrAlias({
comp,
id: this.smrAliasId[divIdx],
});
smrAlias.forEach((item) => {
const foundRow = this._ds_smr_c.findRow("SMR_CD", item.smrCd);
if (foundRow === -1) {
return;
}
this._ds_smr_c.setColumn(foundRow, "SMR_NM", item.smrNm);
});
comp.innerdataset = this._ds_smr_c;
comp.codecolumn = "SMR_CD";
comp.datacolumn = "SMR_NM";
// 기준학기
const baseSmr = this.smr;
// 기본 세팅 값 처리
if (this._ds_smr_c.findRow("SMR_CD", baseSmr) !== -1) {
// 기준 학기 데이터를 찾을 경우 설정
comp.value = baseSmr;
} else if (
typeof smrType.failValue === "string" &&
this._ds_smr_c.findRow("SMR_CD", smrType.failValue) !== -1
) {
// 문자열로 지정된 경우
comp.value = smrType.failValue;
} else if (typeof smrType.failValue === "function") {
const val = this._ds_smr_c.findRow("SMR_CD", smrType.failValue());
if (val !== -1) {
comp.value = value;
} else {
comp.index = 0;
}
} else {
comp.index = 0;
}
}
/**
* 그리드의 학기 콤보를 초기화 한다.
*
* @param {nexacro.Grid} grid
* @param {number} gridIdx
* @access private
*/
initGridSmrCombo(grid, gridIdx) {
const smrFilter = this.smrGridFilter[gridIdx] || "";
const smrMode = this.smrGridMode[gridIdx] || "s";
const bindDsNm = this.unique("_ds_smr_g") + gridIdx + "_" + grid.id;
const dsIdx = getBindCellIndexWithMultiColumns(
"body",
grid,
this.targetCompNm.smrColNm,
);
if (dsIdx === -1) {
return;
}
// 타입 필터링
const smrType = this.selectSmrType({
comp: grid,
id: this.smrGridTypeId[gridIdx],
gridBindColId: grid
.getCellProperty("body", dsIdx, "text")
.replace("bind:", ""),
});
// 학기 타입 기본 필터
this._ds_smr_g = filterRowsByCondition(this._ds_smr, smrType.filter, {
mutate: false,
});
// 학기 추가 필터
filterRowsByCondition(this._ds_smr_g, smrFilter, {
mutate: true,
});
this.fm.addChild(bindDsNm, this._ds_smr_g);
// 학기 콤보 모드 추가
insertModeRow(this._ds_smr_g, ["SMR_NM"], smrMode, {
insertNewRow: false,
});
// 별칭 처리
const smrAlias = this.selectSmrAlias({
comp: grid,
id: this.smrAliasId[gridIdx],
gridBindColId: grid
.getCellProperty("body", dsIdx, "text")
.replace("bind:", ""),
});
smrAlias.forEach((item) => {
const foundRow = this._ds_smr_g.findRow("SMR_CD", item.smrCd);
if (foundRow === -1) {
return;
}
this._ds_smr_g.setColumn(foundRow, "SMR_NM", item.smrNm);
});
if (dsIdx !== -1) {
grid.setCellProperty("body", dsIdx, "combodataset", bindDsNm);
grid.setCellProperty("body", dsIdx, "combocodecol", "SMR_CD");
grid.setCellProperty("body", dsIdx, "combodatacol", "SMR_NM");
}
}
/**
* 컴포넌트 초기화
*
* @access private
*/
initComp() {
this.arrDiv.map((div, i) => {
/**
* @type {HCmbComboComponentSet}
*/
const cmbComp = {};
Array.from(div.form.all).map((v) => {
if (this.isYyComponent(v)) {
this.initYySpin(v);
} else if (this.isSmrComponent(v)) {
this.initSmrCombo(v, i);
} else if (this.isUnivComponent(v)) {
cmbComp.cmb_univCd = v;
this.overrideOrgCompProperty(v);
} else if (this.isDpmjComponent(v)) {
cmbComp.cmb_dpmjCd = v;
this.overrideOrgCompProperty(v);
} else if (this.isMajrComponent(v)) {
cmbComp.cmb_majrCd = v;
this.overrideOrgCompProperty(v);
} else if (this.isDetMajrComponent(v)) {
cmbComp.cmb_detMajrCd = v;
this.overrideOrgCompProperty(v);
} else if (this.isFcltComponent(v)) {
cmbComp.cmb_fcltDvcd = v;
}
}, this);
this.makeCmbDpmj(cmbComp, this.orgMode[i], i, {
showAbl:
/div_WFSA_bg/.test(div.cssclass) ||
div.name === "div_search",
});
}, this);
// 그리드
this.arrGrid.map((grid, i) => {
this.initGridSmrCombo(grid, i);
this.makeGrdDpmj(grid, i);
});
}
attachAblText(ds, dataCol, useYnCol) {
forEachDataset(ds, (ctx) => {
if (ctx.rowData[useYnCol] === "0") {
ds.setColumn(ctx.row, dataCol, `${ctx.rowData[dataCol]}(폐지)`);
}
});
}
/**
* @typedef {Object} hCmbMakeCmbOption
* @property {boolean} [showAbl=false] 폐지 표시 여부
* @access private
*/
/**
* 대학/조직/학과 콤보 생성
*
* @param {HCmbComboComponentSet} cmbComp
* @param {HCmbOrgMode} mode
* @param idx
* @param {hCmbMakeCmbOption} [option]
* @returns {void}
* @access private
*/
makeCmbDpmj(cmbComp, mode = {}, idx, { showAbl = false } = {}) {
const {
fclt: fcltMode = "a",
univ: univMode = "a",
dpmj: dpmjMode = "a",
majr: majrMode = "a",
detMajr: detMajrMode = "a",
} = mode;
// 대학원/대학 구분
if (cmbComp.cmb_fcltDvcd) {
const ds_fclt = $ds.clone(this._ds_fclt);
this.fm.addChild(this.unique("_ds_fclt_c_") + idx, ds_fclt);
cmbComp.cmb_fcltDvcd.innerdataset = ds_fclt;
cmbComp.cmb_fcltDvcd.codecolumn = "FCLT_DVCD";
cmbComp.cmb_fcltDvcd.datacolumn = "FCLT_DVNM";
}
// 대학
if (cmbComp.cmb_univCd) {
const ds_univ = $ds.clone(this._ds_univ);
showAbl && this.attachAblText(ds_univ, "UNIV_NM", "UNIV_USE_YN");
this.fm.addChild(this.unique("_ds_univ_c_") + idx, ds_univ);
cmbComp.cmb_univCd.innerdataset = ds_univ;
cmbComp.cmb_univCd.codecolumn = "UNIV_CD";
cmbComp.cmb_univCd.datacolumn = "UNIV_NM";
}
// 학과
if (cmbComp.cmb_dpmjCd) {
const ds_dpmj = $ds.clone(this._ds_dpmj);
showAbl && this.attachAblText(ds_dpmj, "DPMJ_NM", "DPMJ_USE_YN");
this.fm.addChild(this.unique("_ds_dpmj_c_") + idx, ds_dpmj);
cmbComp.cmb_dpmjCd.innerdataset = ds_dpmj;
cmbComp.cmb_dpmjCd.codecolumn = "DPMJ_CD";
cmbComp.cmb_dpmjCd.datacolumn = "DPMJ_NM";
}
// 전공
if (cmbComp.cmb_majrCd) {
const ds_majr = $ds.clone(this._ds_majr);
showAbl && this.attachAblText(ds_majr, "MAJR_NM", "MAJR_USE_YN");
this.fm.addChild(this.unique("_ds_majr_c_") + idx, ds_majr);
cmbComp.cmb_majrCd.innerdataset = ds_majr;
cmbComp.cmb_majrCd.codecolumn = "MAJR_CD";
cmbComp.cmb_majrCd.datacolumn = "MAJR_NM";
}
// 세부 전공
if (cmbComp.cmb_detMajrCd) {
const ds_detMajr = $ds.clone(this._ds_detMajr);
showAbl &&
this.attachAblText(
ds_detMajr,
"DET_MAJR_NM",
"DET_MAJR_USE_YN",
);
this.fm.addChild(this.unique("_ds_detMajr_c_") + idx, ds_detMajr);
cmbComp.cmb_detMajrCd.innerdataset = ds_detMajr;
cmbComp.cmb_detMajrCd.codecolumn = "DET_MAJR_CD";
cmbComp.cmb_detMajrCd.datacolumn = "DET_MAJR_NM";
}
// LinkedComboManager 인스턴스 생성 및 노드 등록
const comboManager = new LinkedComboManager();
const possibleNodes = [
{
id: "fclt",
component: cmbComp.cmb_fcltDvcd,
codeField: "FCLT_DVCD",
textField: "FCLT_DVNM",
parents: [],
mode: fcltMode,
},
{
id: "univ",
component: cmbComp.cmb_univCd,
codeField: "UNIV_CD",
textField: "UNIV_NM",
parents: ["fclt"],
mode: univMode,
},
{
id: "dpmj",
component: cmbComp.cmb_dpmjCd,
codeField: "DPMJ_CD",
textField: "DPMJ_NM",
parents: ["univ", "fclt"],
allPathField: "ALL_PATH",
mode: dpmjMode,
},
{
id: "majr",
component: cmbComp.cmb_majrCd,
codeField: "MAJR_CD",
textField: "MAJR_NM",
parents: ["dpmj", "univ", "fclt"],
allPathField: "ALL_PATH",
mode: majrMode,
},
{
id: "detMajr",
component: cmbComp.cmb_detMajrCd,
codeField: "DET_MAJR_CD",
textField: "DET_MAJR_NM",
parents: ["dpmj", "univ", "fclt"],
allPathField: "ALL_PATH",
mode: detMajrMode,
},
];
const parentCodeFields = {
fclt: null,
univ: "FCLT_DVCD",
dpmj: { univ: "UNIV_CD", fclt: "FCLT_DVCD" },
majr: { dpmj: "DPMJ_CD", univ: "UNIV_CD", fclt: "FCLT_DVCD" },
detMajr: { dpmj: "DPMJ_CD", univ: "UNIV_CD", fclt: "FCLT_DVCD" },
};
const activeNodes = possibleNodes.filter((node) => node.component);
const activeIds = activeNodes.map((node) => node.id);
activeNodes.forEach((node) => {
const parentId =
node.parents.find((p) => activeIds.includes(p)) || null;
comboManager.addNode({
id: node.id,
parentId: parentId,
component: node.component,
codeField: node.codeField,
textField: node.textField,
parentCodeField: parentCodeFields[node.id],
allPathField: node.allPathField,
mode: node.mode,
});
});
comboManager.attachEvents();
// addNode에서 mode row 삽입 후 첫 번째 항목 선택
if (cmbComp.cmb_fcltDvcd) cmbComp.cmb_fcltDvcd.index = 0;
if (cmbComp.cmb_univCd) cmbComp.cmb_univCd.index = 0;
if (cmbComp.cmb_dpmjCd) cmbComp.cmb_dpmjCd.index = 0;
if (cmbComp.cmb_majrCd) cmbComp.cmb_majrCd.index = 0;
if (cmbComp.cmb_detMajrCd) cmbComp.cmb_detMajrCd.index = 0;
if (!this.comboManagers) {
this.comboManagers = [];
}
this.comboManagers.push(comboManager);
}
/**
* 학사 콤보에 조직명과 ALL_PATH의 데이터를 변경합니다.
*
* @param {nexacro.Dataset} ds 변경할 데이터셋
* @param {string} colNm 변경할 컬럼명
* @param {string} changColNm 변경대상의 컬럼명
* @access private
*/
hCmbChangColVal(ds, colNm, changColNm) {
//그리드쪽에 바인딩 컬럼명을 변경하는순간 검색어가 날라가는 현상이 있어 그리드는 건들지 않으면서 안에 데이터만 변경함d
ds.enableevent = false;
for (var i = 0; i < ds.rowcount; i++) {
let cn = ds.getColumn(i, colNm);
let ccn = ds.getColumn(i, changColNm);
ds.setColumn(i, colNm, ccn);
ds.setColumn(i, changColNm, cn);
}
ds.enableevent = true;
}
/**
* 그리드 학과 콤보 생성
*
* @param {nexacro.Grid} grid
* @param {number} idx
* @access private
*/
makeGrdDpmj(grid, idx) {
const comboConfig = [
{
// 대학
ds: this._ds_fclt,
dsNm: "_ds_fclt",
mode: this.orgGridMode[idx]?.fclt || "s",
comboColNm: this.targetCompNm.fcltColNm,
comboColCode: "FCLT_DVCD",
comboColData: "FCLT_DVNM",
},
{
// 대학
ds: this._ds_univ,
dsNm: "_ds_univ",
mode: this.orgGridMode[idx]?.univ || "s",
comboColNm: this.targetCompNm.univColNm,
comboColCode: "UNIV_CD",
comboColData: "UNIV_NM",
},
{
// 학부
ds: this._ds_dpmj,
dsNm: "_ds_dpmj",
mode: this.orgGridMode[idx]?.dpmj || "n",
comboColNm: this.targetCompNm.dpmjColNm,
comboColCode: "DPMJ_CD",
comboColData: "DPMJ_NM",
},
{
// 전공
ds: this._ds_majr,
dsNm: "_ds_majr",
mode: this.orgGridMode[idx]?.majr || "n",
comboColNm: this.targetCompNm.majrColNm,
comboColCode: "MAJR_CD",
comboColData: "MAJR_NM",
},
{
// 세부 전공
ds: this._ds_detMajr,
dsNm: "_ds_detMajr",
mode: this.orgGridMode[idx]?.detMajr || "n",
comboColNm: this.targetCompNm.detMajrColNm,
comboColCode: "DET_MAJR_CD",
comboColData: "DET_MAJR_NM",
},
];
comboConfig.forEach((c) => {
if (!c.ds) {
return;
}
const ds = $ds.clone(c.ds);
this.fm.addChild(this.unique(c.dsNm) + idx, ds);
const dsIdx = grid.getBindCellIndex("body", c.comboColNm);
if (dsIdx !== -1) {
grid.setCellProperty(
"body",
dsIdx,
"combodataset",
this.unique(c.dsNm) + idx,
);
grid.setCellProperty(
"body",
dsIdx,
"combocodecol",
c.comboColCode,
);
grid.setCellProperty(
"body",
dsIdx,
"combodatacol",
c.comboColData,
);
}
});
// LinkedComboManager 인스턴스 생성 및 노드 등록
const comboManager = new LinkedComboManager();
const possibleNodes = [
{
id: "fclt",
colNm: this.targetCompNm.fcltColNm,
codeField: "FCLT_DVCD",
textField: "FCLT_DVNM",
parents: [],
mode: this.orgGridMode[idx]?.fclt || "s",
},
{
id: "univ",
colNm: this.targetCompNm.univColNm,
codeField: "UNIV_CD",
textField: "UNIV_NM",
parents: ["fclt"],
mode: this.orgGridMode[idx]?.univ || "s",
},
{
id: "dpmj",
colNm: this.targetCompNm.dpmjColNm,
codeField: "DPMJ_CD",
textField: "DPMJ_NM",
parents: ["univ", "fclt"],
allPathField: "ALL_PATH",
mode: this.orgGridMode[idx]?.dpmj || "n",
},
{
id: "majr",
colNm: this.targetCompNm.majrColNm,
codeField: "MAJR_CD",
textField: "MAJR_NM",
parents: ["dpmj", "univ", "fclt"],
allPathField: "ALL_PATH",
mode: this.orgGridMode[idx]?.majr || "n",
},
{
id: "detMajr",
colNm: this.targetCompNm.detMajrColNm,
codeField: "DET_MAJR_CD",
textField: "DET_MAJR_NM",
parents: ["dpmj", "univ", "fclt"],
allPathField: "ALL_PATH",
mode: this.orgGridMode[idx]?.detMajr || "n",
},
];
const parentCodeFields = {
fclt: null,
univ: "FCLT_DVCD",
dpmj: { univ: "UNIV_CD", fclt: "FCLT_DVCD" },
majr: { dpmj: "DPMJ_CD", univ: "UNIV_CD", fclt: "FCLT_DVCD" },
detMajr: { dpmj: "DPMJ_CD", univ: "UNIV_CD", fclt: "FCLT_DVCD" },
};
const activeNodes = possibleNodes.filter(
(node) => grid.getBindCellIndex("body", node.colNm) !== -1,
);
const activeIds = activeNodes.map((node) => node.id);
activeNodes.forEach((node) => {
const parentId =
node.parents.find((p) => activeIds.includes(p)) || null;
comboManager.addNode({
id: node.id,
parentId: parentId,
grid: grid,
colNm: node.colNm,
codeField: node.codeField,
textField: node.textField,
parentCodeField: parentCodeFields[node.id],
allPathField: node.allPathField,
mode: node.mode,
onDisplayChange: (n, type) => {
if (!n.allPathField) return;
const ds = n.adapter.getInnerDataset();
if (!ds) return;
if (ds.__isSwapped === undefined) {
ds.__isSwapped = false;
}
const shouldBeSwapped = type === "allPath";
if (ds.__isSwapped !== shouldBeSwapped) {
this.hCmbChangColVal(ds, n.textField, n.allPathField);
ds.__isSwapped = shouldBeSwapped;
}
},
});
});
comboManager.attachGridEvents(grid);
if (!this.comboManagers) {
this.comboManagers = [];
}
this.comboManagers.push(comboManager);
}
// 컴포넌트 프로퍼티 덮어쓰기
overrideOrgCompProperty(comp) {
comp.type = "filterlike";
}
}
/**
* 학사 콤보 객체를 리턴합니다. 상세한 사용법은 학사 콤보({@link HCmb#init})를 참조하세요.
*
* 인수는 학사 콤보 옵션({@link hCmbOption})을 참고하세요
*
* @function getHCmb
* @param {nexacro.Form} form 현재 폼 (this)
* @returns {HCmb}
* @memberof $f
* @example
* // 기본 사용법
* $f.getHCmb(this).init({
* div: [this.div_search],
* grid: [this.grd_main],
* });
*
* // 특정 조직 필터링
* $f.getHCmb(this).init({
* div: this.div_search,
* orgFilter: (ctx) => ctx.rowData["UNIV_CD"] === "03130000",
* });
*
* // 콤보 모드 설정
* $f.getHCmb(this).init({
* div: this.div_search,
* orgMode: {
* // 조직 콤보 모드
* fclt: "a", // 대학/대학원: '전체' 표시
* univ: "s", // 대학: '선택' 표시
* dpmj: "n", // 학과: 빈값 없음
* majr: "n", // 전공: 빈값 없음
* detMajr: "n", // 세부전공: 빈값 없음
* },
* smrMode: "a", // 학기 콤보: '전체' 표시
* });
*
* // 문자열로 모든 모드 일괄 설정
* $f.getHCmb(this).init({
* div: this.div_search,
* orgMode: "a", // 모든 조직 콤보에 '전체' 표시
* smrMode: "a", // 학기 콤보: '전체' 표시
* });
*
* // 그리드 콤보 모드 설정
* $f.getHCmb(this).init({
* grid: this.grd_main,
* orgGridMode: {
* // 그리드 조직 콤보 모드
* fclt: "s", // 대학/대학원: '선택' 표시
* univ: "s", // 대학: '선택' 표시
* dpmj: "n", // 학과: 빈값 없음
* },
* smrGridMode: "s", // 그리드 학기 콤보: '선택' 표시
* });
*
* // 바인딩 컴포넌트 대상 변경
* $f.getHCmb(this).init({
* div: this.div_search,
* targtCompNm: {
* // 컴포넌트 명 재정의
* yyCompNm: /^spn_aplyYy$/,
* smrCompNm: /^cmb_aplySmr$/,
* fcltCompNm: /^cmb_fcltDvcd$/,
* univCompNm: /^cmb_testUnivCd$/,
* dpmjCompNm: /^cmb_hakgwaCd$/,
* majrCompNm: /^cmb_majrCd$/,
* detMajrCompNm: /^cmb_detMajrCd$/,
*
* // 그리드 컬럼 명 재정의
* smrColNm: ["SMR", "GRDU_SMR"],
* fcltGridColNm: "FCLT_DVCD",
* univColNm: "CUSTOM_1",
* dpmjColNm: "CUSTOM_2",
* majrColNm: "CUSTOM_3",
* detMajrColNm: "detMajrColNm",
* },
* });
*
* // 관리학과 기준으로 데이터 조회
* $f.getHCmb(this).init({
* div: this.div_search,
* useMngtDpmj: true, // 관리학과 기준으로 조직 데이터 조회
* });
*
*/
export function getHCmb(form) {
if (!(form instanceof nexacro.Form)) {
return null;
}
return new HCmb(form);
}