OWL ITS + 탐지시스템(인터넷 진흥원)
jhjang
2021-10-25 db5bbe29f9f724dcafd87b33cc1efd150ed0f489
하위/연관 일감 검색 view추가
4개 파일 추가됨
5개 파일 변경됨
1621 ■■■■■ 파일 변경됨
src/main/webapp/assets/styles/main.css 36 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/i18n/ko/global.json 1 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issue.js 4 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueAddTreeTableConfig.controller.js 186 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueListTree.controller.js 880 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/main.js 2 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueAddTreeTableConfig.html 95 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueList.html 7 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueListTree.html 410 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/assets/styles/main.css
@@ -30174,3 +30174,39 @@
}
/* 간트차트 end */
/* 트리구조 임시 */
.tree-container {
    background-color: #f1f0f0;
    border: 1px solid #c2c2c2;
    padding: 20px;
}
.tree-body {
}
.tree{
    margin-top: 5px;
}
.tree{
    list-style: none; /* 기본 리스트 스타일 제거 */
    padding-left: 0px;
}
.tree ul {
    list-style: none; /* 기본 리스트 스타일 제거 */
}
.tree li {
    padding-top: 5px;
    margin-bottom: 5px;
    list-style-position: inside;
    padding-left: 10px;
}
.tree li:before{
    content: '└ '
}
src/main/webapp/i18n/ko/global.json
@@ -275,6 +275,7 @@
        "relationIssueType6" : "다음 일감을 우선 진행",
        "updateIssueUser" : "일감 담당자 변경",
        "normalList" : "일감 목록",
        "treeList" : "하위/연관 일감 검색",
        "timeLine" : "타임 라인"
    },
    "project": {
src/main/webapp/scripts/app/issue/issue.js
@@ -33,11 +33,11 @@
                        loadController: ["$q", function ($q) {
                            var deferred = $q.defer();
                            require([
                                'issueListTimelineController', 'issueManagerController', 'issueListController', 'issueAddController', 'issueModifyController', 'issueDetailController', 'issueImportExcelController',
                                'issueListTreeController','issueListTimelineController', 'issueManagerController', 'issueListController', 'issueAddController', 'issueModifyController', 'issueDetailController', 'issueImportExcelController',
                                'chartLoader', 'jsTable', 'tableColumnGenerator', 'modalFormAutoScroll', 'summerNote', 'summerNote-ko-KR', 'fullScroll', 'workflowService', 'priorityService', 'issueSearchService', 'issueTableConfigService', 'inputRegex',
                                'severityService', 'issueTypeService', 'issueTypeCustomFieldService', 'issueService', 'issueStatusService', 'issueUserService', 'issueModifyUserController', 'customFieldService', 'issueSearchFieldKeyViewElement',
                                'issueSearchCustomFieldViewElement', 'tableUserImage', 'fullScroll', 'issueCommentService', 'detectIssueEditor', 'formSubmit', 'issueModifyStatusController', 'jsShortCut',
                                'issueAddTableConfigController', 'domAppend', 'issueDetailImagePreview', 'issueSendMailController', 'htmlDiff', 'issueVersionViewController', 'issueVersionService',
                                'issueAddTableConfigController','issueAddTreeTableConfigController', 'domAppend', 'issueDetailImagePreview', 'issueSendMailController', 'htmlDiff', 'issueVersionViewController', 'issueVersionService',
                                'jsHtmlDiff', 'issueReservationController', 'issueReservationService', 'issueVersionService', 'issueStatusAutoFocus', 'issueRelationService'
                            ], function () {
                                deferred.resolve();
src/main/webapp/scripts/app/issue/issueAddTreeTableConfig.controller.js
New file
@@ -0,0 +1,186 @@
/**
 * Created by wisestone on 2019-02-07.
 */
'use strict';
define([
        'app',
        'angular'
    ],
    function (app, angular) {
        app.controller('issueAddTreeTableConfigController', ['$scope', '$rootScope', '$log', '$resourceProvider', '$uibModalInstance', 'SweetAlert', '$timeout', 'IssueTableConfig', 'CustomField', '$q', '$filter',
            function ($scope, $rootScope, $log, $resourceProvider, $uibModalInstance, SweetAlert, $timeout, IssueTableConfig, CustomField, $q, $filter) {
                //  함수 모음
                $scope.fn = {
                    cancel : cancel,    //  팝업 창 닫기
                    formSubmit : formSubmit,    //  폼 전송
                    formCheck : formCheck,  //  폼 체크
                    getCustomFields : getCustomFields,   //  사용자 정의 필드 목록을 가져온다.
                    getIssueTableConfigs : getIssueTableConfigs //  이슈 목록 테이블 설정 정보를 가져온다.
                };
                //  변수 모음
                $scope.vm = {
                    issueTableConfigs : [{
                        name : $filter("translate")("common.priority"), // 우선순위
                        key : "PRIORITY",
                        width : "width-100-p",
                        position : 1,
                        display : true
                    }, {
                        name : $filter("translate")("common.importance"), // 중요도
                        key : "SEVERITY",
                        width : "width-80-p",
                        position : 2,
                        display : true
                    }, {
                        name : $filter("translate")("issue.issueType"), // 이슈 타입
                        key : "ISSUE_TYPE",
                        width : "width-140-p",
                        position : 3,
                        display : true
                    }, {
                        name : $filter("translate")("common.assignee"), // 담당자
                        key : "ASSIGNEE",
                        width : "width-100-p",
                        position : 4,
                        display : true
                    }, {
                        name : $filter("translate")("common.register"), // 등록자
                        key : "REGISTER",
                        width : "width-100-p",
                        position : 5,
                        display : false
                    }, {
                        name : $filter("translate")("common.period"), // 기간
                        key : "PERIOD",
                        width : "width-140-p",
                        position : 6,
                        display : false
                    }, {
                        name : $filter("translate")("common.lastChangeDate"), // 최근 변경일
                        key : "MODIFY_DATE",
                        width : "width-100-p",
                        position : 7,
                        display : false
                    }]
                };
                //  폼 전송
                function formCheck(formInvalid) {
                    if (formInvalid) {
                        return true;
                    }
                    return false;
                }
                //  폼 전송
                function formSubmit() {
                    $rootScope.spinner = true;
                    var content = {
                        issueTableConfigs : JSON.stringify($scope.vm.issueTableConfigs)
                    };
                    IssueTableConfig.add($resourceProvider.getContent(content,
                        $resourceProvider.getPageContent(0, 0))).then(function (result) {
                        if (result.data.message.status === "success") {
                            SweetAlert.success($filter("translate")("issue.completedSavingIssueTable"), $filter("translate")("issue.saveIssueTableSettingsInformation")); // "이슈 테이블 설정 저장 완료", "이슈 테이블 설정 정보가 저장되었습니다."
                            //  변경된 이슈 테이블 정보를 이슈 목록 테이블에 갱신한다.
                            $rootScope.$broadcast("getIssueTableConfigs", {});
                        }
                        else {
                            SweetAlert.error($filter("translate")("issue.failedToSaveIssueTableSetting"), result.data.message.message); // 이슈 테이블 설정 저장 실패
                        }
                        $rootScope.spinner = false;
                    });
                }
                //  팝업 창 닫기
                function cancel() {
                    $rootScope.$broadcast("closeLayer");    //  팝업이 열리고 나서 js-multi, js-single 등에서 body 이벤트가 날아가는 현상 수정
                    $uibModalInstance.dismiss('cancel');
                    $(document).unbind("keydown");  //  단축키 이벤트 제거
                }
                //  사용자 정의 필드 목록을 가져온다.
                function getCustomFields() {
                    var deferred = $q.defer();
                    CustomField.find($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status !== "success") {
                            SweetAlert.error($filter("translate")("issue.failedToUserDefinedFieldListLookup"), result.data.message.message); // 사용자 정의 필드 목록 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  이슈 목록 테이블 설정 정보를 가져온다.
                function getIssueTableConfigs() {
                    var deferred = $q.defer();
                    IssueTableConfig.detail($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status !== "success") {
                            SweetAlert.error($filter("translate")("issue.failedToIssueTableColumnLookup"), result.data.message.message); // 이슈 테이블 컬럼 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  사용자 정의 필드를 조회한 후 표시할 이슈 테이블 컬럼을 준비한다.
                $scope.fn.getCustomFields().then(function (result) {
                    var count = 7;
                    angular.forEach(result, function (customField) {
                        $scope.vm.issueTableConfigs.push({
                            name : customField.name,
                            key : "CUSTOM_FIELD_" + customField.id,
                            width : "width-100-p",
                            display : false,
                            position : count
                        });
                        count++;
                    });
                    //  이슈 목록 테이블 설정 정보를 가져온다.
                    $scope.fn.getIssueTableConfigs().then(function (issueTableConfigs) {
                        if ($rootScope.isDefined(issueTableConfigs)) {
                            $rootScope.spinner = true;
                            var saveTableConfigs = JSON.parse(issueTableConfigs);
                            angular.forEach(saveTableConfigs, function (saveTableConfig) {
                                for (var count in $scope.vm.issueTableConfigs) {
                                    var issueTableConfig = $scope.vm.issueTableConfigs[count];
                                    if (issueTableConfig.key === saveTableConfig.key) {
                                        issueTableConfig.display = saveTableConfig.display;
                                        issueTableConfig.width = saveTableConfig.width;
                                        issueTableConfig.position = saveTableConfig.position;
                                        break;
                                    }
                                }
                            });
                            $rootScope.spinner = false;
                        }
                    });
                });
            }]);
    });
src/main/webapp/scripts/app/issue/issueListTree.controller.js
New file
@@ -0,0 +1,880 @@
/**
 * Created by wisestone on 2017-12-21.
 */
'use strict';
define([
        'app',
        'angular'
    ],
    function (app, angular) {
        app.controller('issueListTreeController', ['$scope', '$rootScope', '$log', '$resourceProvider', '$tableProvider', '$state', '$uibModal', '$q',
            '$controller', '$injector', 'SweetAlert', 'Issue', 'IssueType', 'Priority', 'Severity', 'IssueStatus', 'CustomField', 'IssueSearch', 'IssueTableConfig', '$timeout', '$filter',
            function ($scope, $rootScope, $log, $resourceProvider, $tableProvider, $state, $uibModal, $q, $controller, $injector, SweetAlert, Issue, IssueType, Priority, Severity, IssueStatus, CustomField,
                      IssueSearch, IssueTableConfig, $timeout, $filter) {
                //  함수
                $scope.fn = {
                    initSearch : initSearch,    //  검색 초기화
                    getPageList : getPageList,  //  목록 조회
                    changePageRowCount : changePageRowCount,    //  페이지 변경
                    makeTableConfigs : makeTableConfigs, //  테이블 설정
                    setTableColumn : setTableColumn,    //  테이블의 컬럼을 만들어준다.
                    add : add,   //  이슈 생성
                    modify : modify,    //  이슈 수정
                    modifyMultiIssueStatus : modifyMultiIssueStatus,    //  이슈 다중 상태 변경
                    removes : removes,  //  이슈 삭제
                    addIssueTableConfig : addIssueTableConfig,    //  이슈 목록 테이블 설정
                    listView : listView,    //  목록 화면으로 변경
                    importExcel : importExcel,  //  엑셀 import 기능 팝업 호출
                    getIssueTypes : getIssueTypes,  //  이슈 유형 목록을 가져온다.
                    getPriorities : getPriorities,  //  우선순위 목록을 가져온다.
                    getSeverities : getSeverities,  //  중요도 목록을 가져온다.
                    getIssueStatuses : getIssueStatuses,    //  이슈 상태 목록을 가져온다.
                    getCustomFields : getCustomFields,  //  사용자 정의 필드 목록을 가져온다.
                    makeVmSearchObject : makeVmSearchObject, //  검색 조건을 기억한다.
                    getVmSearchObject : getVmSearchObject,  //  저장한 검색 조건을 가져와서 vm 에 셋팅한다.
                    makeSearchConditions : makeSearchConditions,    //  검색 조건을 만든다.
                    getIssueTableConfigs : getIssueTableConfigs,  //  사용자 이슈 목록 테이블 설정 값을 가져와서 적용한다.
                    startExecute : startExecute //  컨트롤 로딩시 처음으로 시작되는 함수
                };
                //  변수
                $scope.vm = {
                    search : {
                        title : "", //  제목
                        description : "",   //  내용
                        combinationIssueNumber : "",   //  이슈 번호
                        projectType : "BTS_PROJECT",    //  프로젝트 유형
                        registerDateRange : "", //  등록일 기간 검색
                        startDateRange : "",    //  시작일 기간 검색
                        completeDateRange : "", //  완료일 기간 검색
                        severityIds : [],   //  중요도 검색
                        priorityIds : [],   //  우선순위 검색
                        issueStatusIds : [],    //  이슈 상태 검색
                        issueTypeIds : []   //  이슈 유형 검색
                    },
                    searchView : false, //  상세 검색 조건 표시 여부
                    detailView : false, //  상세 모드 변경 값
                    page : {
                        selectedPage : 0,
                        selectedPageRowCount : String(10)
                    },
                    tableConfigs : [],  //  테이블 셋팅 정보
                    responseData : {
                        data : []
                    },
                    projectName : "",   //  프로젝트 검색
                    userName : "",  //  담당자 검색
                    registerName : "",  //  등록자 검색
                    projects : [],  //  프로젝트
                    issueStatuses : [], //  이슈 상태
                    issueTypes : [],    //  이슈 유형
                    priorities : [],    //  우선 순위
                    severities : [],    //  중요도
                    users : [], //  담당자
                    registers : [], //  등록자
                    customFields : [],  //  사용자 정의 필드
                    issueTableConfigs : []  //  이슈 테이블 설정
                };
                //  테이블 이벤트
                $scope.tableEvent = {
                    changeDetailView : changeDetailView    //  상세 화면으로 변경
                };
                angular.extend(this, $controller('autoCompleteController', {$scope : $scope, $injector : $injector}));
                //  이슈 목록 데이터 갱신
                $scope.$on("getIssueList", function () {
                    $scope.fn.getPageList($scope.vm.page.selectedPage - 1);
                });
                //  이슈 테이블 설정 정보 갱신
                $scope.$on("getIssueTableConfigs", function () {
                    $scope.fn.startExecute();
                });
                //  검색 조건을 기억한다. - 적용 보류
                function makeVmSearchObject() {
                    var content = {
                        conditions : JSON.stringify({
                            search : $scope.vm.search,
                            projects : $scope.vm.projects,
                            users : $scope.vm.users,
                            registers : $scope.vm.registers
                        })
                    };
                    IssueSearch.add($resourceProvider.getContent(content,
                        $resourceProvider.getPageContent(0, 0))).then(function (result) {
                        if (result.data.message.status === "success") {
                        }
                        else {
                            SweetAlert.error($filter("translate")("issue.failedToSaveFieldConditions"), result.data.message.message); // 검색 필드 조건 저장 실패
                        }
                    });
                }
                //  서버에 저장한 이슈 검색 조건을 가져온다.
                function getVmSearchObject() {
                    IssueSearch.detail($resourceProvider.getContent({}, $resourceProvider.getPageContent(0, 0))).then(function (result) {
                        if (result.data.message.status === "success") {
                            if ($rootScope.isDefined(result.data.data)) {
                                var issueListSearchObject = JSON.parse(result.data.data);
                                //  이슈 번호만 적용한다. - 삭제시 처리방법때문에 다른 속성은 적용 보류
                                $scope.vm.search = issueListSearchObject.search;
                                /*$scope.vm.users = issueListSearchObject.users;
                                $scope.vm.projects = issueListSearchObject.projects;
                                $scope.vm.registers = issueListSearchObject.registers;*/
                                $scope.fn.getPageList(0, true);
                            } else {
                                $scope.fn.getPageList(0);
                            }
                        }
                        else {
                            SweetAlert.error($filter("translate")("issue.failedToGetSearchFieldCondition"), result.data.message.message); // 검색 필드 조건 가져오기 실패
                        }
                    });
                }
                //  상세 화면으로 변경한다.
                function changeDetailView(id) {
                    $scope.vm.tableConfigs = [];
                    $scope.vm.detailView = true;
                    $scope.fn.makeTableConfigs();
                    //  chrome 에서 마우스 떠난걸 감지 못해서 이벤트 추가.
                    $(".full-height-scroll").trigger("mouseleave");
                    //  테이블을 다시 그릴수 있게 데이터 바인딩을 다시한다.
                    var temp = angular.copy($scope.vm.responseData.data);
                    $scope.vm.responseData.data = angular.copy(temp);
                    //  현재 상세화면으로 보려고하는 이슈 id를 기억한다.
                    $rootScope.currentDetailIssueId = id;
                    //  이슈 상세 화면 요청
                    $rootScope.$broadcast("getIssueDetail", {
                        id : id
                    });
                }
                //  목록 화면으로 변경한다.
                function listView() {
                    $scope.vm.detailView = false;
                    //  이슈 테이블 설정
                    $scope.fn.makeTableConfigs();
                    var temp = angular.copy($scope.vm.responseData.data);
                    $scope.vm.responseData.data = angular.copy(temp);
                    //  마지막으로 보고있던 이슈 id를 초기화한다.
                    $rootScope.currentDetailIssueId = null;
                }
                //  이슈 테이블 설정
                function makeTableConfigs() {
                    $scope.vm.tableConfigs = [];
                    $scope.vm.tableConfigs.push($tableProvider.config()
                        .setDType("checkbox")
                        .setHWidth("width-20-p")
                        .setDAlign("text-center"));
                    //  상세형 일때때
                   if ($scope.vm.detailView) {
                        $scope.vm.tableConfigs.push($tableProvider.config()
                            .setHName("issue.issueTitle")
                            .setDName("title")
                            .setDType("renderer")
                            .setHWidth("width-100 bold")
                            .setDRenderer("ISSUE_DETAIL_FLOATING"));
                    }
                    else {
                       $scope.vm.tableConfigs.push($tableProvider.config()
                           .setHName("issue.issueTitle")
                           .setDName("title")
                           .setDType("renderer")
                           .setHWidth("bold")
                           .setDRenderer("ISSUE_DETAIL_FLOATING"));
                        angular.forEach($scope.vm.issueTableConfigs, function (issueTableConfig) {
                            //  표시 대상인 컬럼만 화면에 그려준다.
                            if (issueTableConfig.display) {
                                //  테이블의 컬럼을 만들어준다.
                                $scope.fn.setTableColumn(issueTableConfig);
                            }
                        });
                    }
                }
                //  테이블의 컬럼을 만들어준다.
                function setTableColumn(issueTableConfig) {
                    //  일반 컬럼
                    switch(issueTableConfig.key) {
                        case "PRIORITY" :   //  우선순위
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("common.priority")
                                .setDName("priorityName")
                                .setDType("renderer")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDRenderer("COMMON_PRIORITY"));
                            break;
                        case "SEVERITY" :   //  중요도
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("common.importance")
                                .setDName("severityName")
                                .setDType("renderer")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDRenderer("COMMON_SEVERITY"));
                            break;
                        case "ISSUE_TYPE" : //  이슈 타입
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("issue.issueType")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDName("issueTypeName"));
                            break;
                        case "ASSIGNEE" :   //  담당자
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("common.assignee")
                                .setDType("renderer")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDRenderer("ISSUE_USER"));
                            break;
                        case "REGISTER" :   //  등록자
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("common.register")
                                .setDType("renderer")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDRenderer("REGISTER"));
                            break;
                        case "PERIOD" : //  기간
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("common.period")
                                .setDType("renderer")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDRenderer("ISSUE_DUE_DATE"));
                            break;
                        case "MODIFY_DATE" : //  최근 변경일
                            $scope.vm.tableConfigs.push($tableProvider.config()
                                .setHName("common.lastChangeDate")
                                .setHWidth("bold " + issueTableConfig.width)
                                .setDAlign("text-center")
                                .setDName("modifyDate"));
                            break;
                    }
                    //  사용자 정의 필드 컬럼
                    if (issueTableConfig.key.indexOf("CUSTOM_FIELD_") !== -1) {
                        //  만약 이슈 테이블 컬럼명이 표시되지 않으면 이쪽이 문제
                        for (var count in $scope.vm.customFields) {
                            var customField = $scope.vm.customFields[count];
                            if (customField.id === Number(issueTableConfig.key.substring(13))) {
                                $scope.vm.tableConfigs.push($tableProvider.config()
                                    .setHName(customField.name)
                                    .setDType("renderer")
                                    .setHWidth("bold " + issueTableConfig.width)
                                    .setDAlign("text-center")
                                    .setColumnHint(customField)
                                    .setDRenderer("ISSUE_CUSTOM_FIELD_VALUE_VIEW"));
                                break;
                            }
                        }
                    }
                }
                //  검색 필드 초기화
                function initSearch() {
                    $state.go($state.current, {}, {reload : true});
                    /*IssueSearch.remove($resourceProvider.getContent({screen : "ISSUE_LIST"},
                        $resourceProvider.getPageContent(0, 0))).then(function (result) {
                        if (result.data.message.status == "success") {
                            $state.go($state.current, {}, {reload : true});
                        }
                        else {
                            SweetAlert.swal("검색 필드 초기화 실패", result.data.message.message, "error");
                        }
                    });*/
                }
                //  이슈 검색 조건을 만든다.
                function makeSearchConditions() {
                    var conditions = {
                        title : $scope.vm.search.title,
                        description : $scope.vm.search.description,
                        projectType : $scope.vm.search.projectType,
                        combinationIssueNumber : $scope.vm.search.combinationIssueNumber.trim(),
                        beginRegisterDate : "",
                        endRegisterDate : "",
                        beginStartDate : "",
                        endStartDate : "",
                        beginCompleteDate : "",
                        endCompleteDate : "",
                        projectIds : (function () {
                            var projectIds = [];
                            angular.forEach($scope.vm.projects, function (project) {
                                projectIds.push(project.id);
                            });
                            return projectIds;
                        })(),
                        issueStatusIds : (function () {
                            var issueStatusIds = [];
                            angular.forEach($scope.vm.search.issueStatusIds, function (issueStatusId) {
                                issueStatusIds.push(issueStatusId.fieldKey);
                            });
                            return issueStatusIds;
                        })(),
                        issueTypeIds : (function () {
                            var issueTypeIds = [];
                            angular.forEach($scope.vm.search.issueTypeIds, function (issueTypeId) {
                                issueTypeIds.push(issueTypeId.fieldKey);
                            });
                            return issueTypeIds;
                        })(),
                        priorityIds : (function () {
                            var priorityIds = [];
                            angular.forEach($scope.vm.search.priorityIds, function (priorityId) {
                                priorityIds.push(priorityId.fieldKey);
                            });
                            return priorityIds;
                        })(),
                        severityIds : (function () {
                            var severityIds = [];
                            angular.forEach($scope.vm.search.severityIds, function (severityId) {
                                severityIds.push(severityId.fieldKey);
                            });
                            return severityIds;
                        })(),
                        userIds : (function () {
                            var userIds = [];
                            angular.forEach($scope.vm.users, function (user) {
                                userIds.push(user.id);
                            });
                            return userIds;
                        })(),
                        registerIds : (function () {
                            var registerIds = [];
                            angular.forEach($scope.vm.registers, function (register) {
                                registerIds.push(register.id);
                            });
                            return registerIds;
                        })(),
                        issueCustomFields : (function () {
                            var issueCustomFields = [];
                            angular.forEach($scope.vm.customFields, function (customField) {
                                var useValues = [];
                                if (angular.isArray(customField.useValues)) {
                                    angular.forEach(customField.useValues, function (useValue) {
                                        useValues.push(useValue.value);
                                    });
                                }
                                else {
                                    useValues.push(customField.useValues);
                                }
                                //  useValues 를 배열로 변환한다.
                                var temp = angular.copy(customField);
                                temp.useValues = useValues;
                                issueCustomFields.push(temp);
                            });
                            return issueCustomFields;
                        })()
                    };
                    //  등록일
                    if ($rootScope.isDefined($scope.vm.search.registerDateRange)) {
                        var registerDateRange = $scope.vm.search.registerDateRange.split("~");
                        conditions.beginRegisterDate = registerDateRange[0].trim();
                        conditions.endRegisterDate = registerDateRange[1].trim();
                    }
                    //  시작일
                    if ($rootScope.isDefined($scope.vm.search.startDateRange)) {
                        var startDateRange = $scope.vm.search.startDateRange.split("~");
                        conditions.beginStartDate = startDateRange[0].trim();
                        conditions.endStartDate = startDateRange[1].trim();
                    }
                    //  종료일
                    if ($rootScope.isDefined($scope.vm.search.completeDateRange)) {
                        var completeDateRange = $scope.vm.search.completeDateRange.split("~");
                        conditions.beginCompleteDate = completeDateRange[0].trim();
                        conditions.endCompleteDate = completeDateRange[1].trim();
                    }
                    return conditions;
                }
                //  이슈 목록을 조회한다.
                function getPageList(selectedPage, detail = false) {
                    if (selectedPage < 0) {
                        selectedPage = 0;
                    }
                    //  현재 페이지 정보
                    var currentPage = 0;
                    //  쿠키에 선택한 페이지 정보가 없으면 기본 페이지 정보 0 을 저장
                    if (angular.isUndefined(selectedPage) || selectedPage === "") {
                        currentPage = $scope.vm.page.selectedPage;
                    }
                    else {
                        currentPage = selectedPage;
                    }
                    //  검색 조건을 저장한다.
                    //$scope.fn.makeVmSearchObject();
                    // 현재 선택된 프로젝트를 검색 기본으로 추가
                    if ($rootScope.workProject != null && $rootScope.workProject.id > -1) {
                        var find = findProjectSearch($rootScope.workProject.id);
                        if (!find) {
                            $scope.vm.projects.push($rootScope.workProject);
                        }
                    }
                    //  이슈 검색 조건을 만든다.
                    var conditions = $scope.fn.makeSearchConditions();
                    Issue.find($resourceProvider.getContent(conditions,
                        $resourceProvider.getPageContent(currentPage, $scope.vm.page.selectedPageRowCount))).then(function (result) {
                        if (result.data.message.status === "success") {
                            $scope.vm.page.selectedPage = currentPage + 1;
                            $scope.vm.responseData = result.data;
                            if (detail) {
                                changeDetailView(result.data.data[0].id);
                            }
                        }
                        else {
                            SweetAlert.error($filter("translate")("issue.failedIssueLookup"), result.data.message.message); // 이슈 조회 실패
                        }
                    });
                }
                // 검색조건에서 해당 프로젝트 찾기
                function findProjectSearch(projectId) {
                    var projects = $scope.vm.projects;
                    var find = false;
                    for (let i = 0; i < projects.length; i++) {
                        if (projects[i].id == projectId) {
                            find = true;
                            break;
                        }
                    }
                    return find;
                }
                //  페이지 변경
                function changePageRowCount() {
                    $scope.fn.getPageList(0);
                }
                //  엑셀 import 팝업 호출
                function importExcel() {
                    $uibModal.open({
                        templateUrl : 'views/issue/issueExcelImport.html',
                        size : "lg",
                        controller : 'issueImportExcelController',
                        backdrop : 'static'
                    });
                }
                //  이슈 생성 팝업 호출
                function add() {
                    $uibModal.open({
                        templateUrl : 'views/issue/issueAdd.html',
                        size : "lg",
                        controller : 'issueAddController',
                        backdrop : 'static'
                    });
                }
                //  이슈 다중 상태 변경
                function modifyMultiIssueStatus() {
                    var issueIds = [];
                    angular.forEach($scope.vm.responseData.data, function (data) {
                        if (data.checked) {
                            issueIds.push(data.id);
                        }
                    });
                    if (issueIds.length < 1) {
                        SweetAlert.warning($filter("translate")("issue.selectionCheck"), $filter("translate")("issue.selectIssueToChangeStatus")); // 선택 대상 확인, 상태 변경할 이슈를 선택하세요.
                        return;
                    }
                    $uibModal.open({
                        templateUrl : 'views/issue/issueModifyStatus.html',
                        size : "md",
                        controller : 'issueModifyStatusController',
                        backdrop : 'static',
                        resolve : {
                            parameter : function () {
                                return {
                                    issueIds : issueIds
                                };
                            }
                        }
                    });
                }
                //  이슈 수정 팝업 호출
                function modify(id) {
                    $uibModal.open({
                        templateUrl : 'views/issue/issueModify.html',
                        size : "lg",
                        controller : 'issueModifyController',
                        backdrop : 'static',
                        resolve : {
                            parameter : function () {
                                return {
                                    id : id
                                };
                            }
                        }
                    });
                }
                //  이슈 삭제
                function removes() {
                    var removeIds = [];
                    var removePermission = true;
                    angular.forEach($scope.vm.responseData.data, function (data) {
                        if (data.checked && data.modifyPermissionCheck) {
                            removeIds.push(data.id);
                        }
                        if (data.checked && !data.modifyPermissionCheck) {
                            removePermission = false;
                        }
                    });
                    if (!removePermission) {
                        SweetAlert.swal({
                            html : true,
                            title : $filter("translate")("common.deleteFailed"), // 삭제 실패
                            text : $filter("translate")("issue.notHaveDeletePermissionExistsAnIssue"), // 삭제 권한이 없는 이슈가 존재합니다.
                            type : "error"
                        });
                        return;
                    }
                    if (removeIds.length < 1) {
                        SweetAlert.swal({
                            title : $filter("translate")("common.checkPurgingTargets"), // 삭제 대상 확인
                            text : $filter("translate")("common.selectDestinationDeletion"), // 삭제 대상을 선택하세요.
                            type : "warning"
                        });
                        return;
                    }
                    //  삭제 알림
                    SweetAlert.swal({
                            title : $filter("translate")("issue.deleteIssue"), // 이슈 삭제
                            text : $filter("translate")("issue.wantToDeleteSelectIssue"), // 선택한 이슈을 삭제하겠습니까? 삭제된 이슈은 복구할 수 없습니다.
                            type : "warning",
                            showCancelButton : true,
                            confirmButtonColor : "#DD6B55",
                            confirmButtonText : $filter("translate")("common.delete"), // 삭제
                            cancelButtonText : $filter("translate")("common.cancel"), // 취소
                            closeOnConfirm : false,
                            closeOnCancel : true
                        },
                        function (isConfirm) {
                            SweetAlert.close();
                            if (isConfirm) {
                                $rootScope.spinner = true;
                                Issue.remove($resourceProvider.getContent(
                                    { removeIds : removeIds },
                                    $resourceProvider.getPageContent(0, 0))).then(function (result) {
                                    if (result.data.message.status === "success") {
                                        $timeout(function () {
                                            SweetAlert.success($filter("translate")("common.deleteSucceeded"), result.data.message.message); // 삭제 성공
                                        }, 100);
                                        $scope.fn.listView();
                                        $scope.fn.getPageList(0);
                                    }
                                    else {
                                        $timeout(function () {
                                            SweetAlert.error($filter("translate")("common.deleteFailed"), result.data.message.message); // 삭제 실패
                                        }, 100);
                                    }
                                    $rootScope.spinner = false;
                                });
                            }
                        });
                }
                //  이슈 목록 테이블 설정
                function addIssueTableConfig() {
                    $uibModal.open({
                        templateUrl : 'views/issue/issueAddTreeTableConfig.html',
                        size : "lg",
                        controller : 'issueAddTreeTableConfigController',
                        backdrop : 'static',
                        resolve : {}
                    });
                }
                //  이슈 상태 목록
                function getIssueStatuses() {
                    var deferred = $q.defer();
                    $scope.vm.issueStatuses = [];
                    IssueStatus.find($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status === "success") {
                            angular.forEach(result.data.data, function (issueType) {
                                $scope.vm.issueStatuses.push({
                                    fieldKey : issueType.id,
                                    fieldValue : issueType.name
                                });
                            });
                        }
                        else {
                            SweetAlert.swal($filter("translate")("common.failedToIssueStatusListLookup"), result.data.message.message, "error"); // 이슈 상태 목록 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  이슈 유형 목록
                function getIssueTypes() {
                    var deferred = $q.defer();
                    $scope.vm.issueTypes = [];
                    IssueType.find($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status === "success") {
                            angular.forEach(result.data.data, function (issueType) {
                                $scope.vm.issueTypes.push({
                                    fieldKey : issueType.id,
                                    fieldValue : issueType.name
                                });
                            });
                        }
                        else {
                            SweetAlert.swal($filter("translate")("issue.failedToIssueTypeListLookup"), result.data.message.message, "error"); // 이슈 유형 목록 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  우선순위 목록
                function getPriorities() {
                    var deferred = $q.defer();
                    $scope.vm.priorities = [];
                    Priority.find($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status === "success") {
                            angular.forEach(result.data.data, function (prioritiy) {
                                $scope.vm.priorities.push({
                                    fieldKey : prioritiy.id,
                                    fieldValue : prioritiy.name
                                });
                            });
                        }
                        else {
                            SweetAlert.swal($filter("translate")("issue.failedToPriorityListLookup"), result.data.message.message, "error"); // 우선순위 목록 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  중요도 목록
                function getSeverities() {
                    var deferred = $q.defer();
                    $scope.vm.severities = [];
                    Severity.find($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status === "success") {
                            angular.forEach(result.data.data, function (severity) {
                                $scope.vm.severities.push({
                                    fieldKey : severity.id,
                                    fieldValue : severity.name
                                });
                            });
                        }
                        else {
                            SweetAlert.swal($filter("translate")("issue.failedToCriticalListLookup"), result.data.message.message, "error"); // 중요도 목록 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  사용자 정의 필드 목록
                function getCustomFields() {
                    var deferred = $q.defer();
                    $scope.vm.customFields = [];
                    CustomField.find($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status === "success") {
                            angular.forEach(result.data.data, function (customField) {
                                switch (customField.customFieldType) {
                                    case "INPUT" :
                                        customField.useValues = "";
                                        break;
                                    case "MULTI_SELECT" :
                                    case "SINGLE_SELECT" :
                                        customField.useValues = [];
                                        break;
                                }
                                $scope.vm.customFields.push(customField);
                            });
                        }
                        else {
                            SweetAlert.swal($filter("translate")("issue.failedToUserDefinedFieldListLookup"), result.data.message.message, "error"); // 사용자 정의 필드 목록 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  사용자 이슈 목록 테이블 설정 값을 가져와서 적용한다.
                function getIssueTableConfigs() {
                    var deferred = $q.defer();
                    IssueTableConfig.detail($resourceProvider.getContent({},
                        $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                        if (result.data.message.status !== "success") {
                            SweetAlert.error($filter("translate")("issue.failedToIssueTableColumnLookup"), result.data.message.message); // 이슈 테이블 컬럼 조회 실패
                        }
                        deferred.resolve(result.data.data);
                    });
                    return deferred.promise;
                }
                //  최초 실행
                function startExecute() {
                    // 파라미터 읽기
                    var params = $rootScope.previousGetParams;
                    if ($rootScope.isDefined(params)) {
                        $rootScope.$broadcast("makeIssueSearch", {  projectKey : params.projectKey, issueNumber : params.issueNumber });
                        $rootScope.previousGetParams = null;
                        return;
                    }
                    var promises = {
                        getIssueTypes : $scope.fn.getIssueTypes(),
                        getPriorities : $scope.fn.getPriorities(),
                        getSeverities : $scope.fn.getSeverities(),
                        getIssueStatuses : $scope.fn.getIssueStatuses(),
                        getCustomFields : $scope.fn.getCustomFields()
                    };
                    $q.all(promises).then(function (results){
                        $log.debug("promises 결과 " , results);
                        //  이슈 테이블 설정 조회 및 적용 후
                        //  이슈 검색 조건 가져오기 후
                        //  이슈 조회
                        //  사용자 이슈 목록 테이블 설정 값을 가져와서 적용한다.
                        $scope.fn.getIssueTableConfigs().then(function (issueTableConfigs) {
                            if ($rootScope.isDefined(issueTableConfigs)) {
                                //  이슈 테이블 설정 정보를 저장 한다.
                                $scope.vm.issueTableConfigs = JSON.parse(issueTableConfigs);
                                $scope.vm.issueTableConfigs.sort(function (a, b) {
                                    return a.position < b.position ? -1 : a.position > b.position ? 1: 0;
                                });
                            }
                            else {
                                //  최초 업무 공간에 들어왔을 경우
                                $scope.vm.issueTableConfigs = [{
                                    name : $filter("translate")("common.priority"), // 우선순위
                                    key : "PRIORITY",
                                    width : "width-80-p",
                                    position : 1,
                                    display : true
                                }, {
                                    name : $filter("translate")("common.importance"), // 중요도
                                    key : "SEVERITY",
                                    width : "width-80-p",
                                    position : 2,
                                    display : true
                                }, {
                                    name : $filter("translate")("issue.issueType"), // 이슈 타입
                                    key : "ISSUE_TYPE",
                                    width : "width-140-p",
                                    position : 3,
                                    display : true
                                }, {
                                    name : $filter("translate")("common.assignee"), // 담당자
                                    key : "ASSIGNEE",
                                    width : "width-140-p",
                                    position : 4,
                                    display : true
                                }];
                            }
                            //  이슈 테이블 설정
                            $scope.fn.makeTableConfigs();
                            //  서버에 저장한 이슈 검색 조건을 가져와서 이슈 목록 검색을 진행한다.
                            $scope.fn.getVmSearchObject();
                        });
                    });
                }
                $scope.fn.startExecute();
            }
        ]);
    }
);
src/main/webapp/scripts/main.js
@@ -179,10 +179,12 @@
        'issueModifyController' : 'app/issue/issueModify.controller',  //  이슈 수정 컨트롤러
        'issueDetailController' : 'app/issue/issueDetail.controller',  //  이슈 상세 컨트롤러
        'issueListController' : 'app/issue/issueList.controller',   //  이슈 목록 컨트롤러
        'issueListTreeController' : 'app/issue/issueListTree.controller',   //  이슈 하위/연관일감 검색 컨트롤러
        'issueManagerController' : 'app/issue/issueManager.controller',   //  이슈 관리 컨트롤러
        'issueImportExcelController' : 'app/issue/issueImportExcel.controller',    //  이슈 엑셀 대량 import
        'issueModifyStatusController' : 'app/issue/issueModifyStatus.controller',   //  이슈 다중 상태 변경 컨트롤러
        'issueAddTableConfigController' : 'app/issue/issueAddTableConfig.controller',   //  이슈 테이블 설정 컨트롤러
        'issueAddTreeTableConfigController' : 'app/issue/issueAddTreeTableConfig.controller',   //  이슈 하위/연관일감 검색 테이블 설정 컨트롤러
        'issueSendMailController' : 'app/issue/issueSendMail.controller',   //  이슈 이메일 발송 컨트롤러
        'issueVersionViewController' : 'app/issue/issueVersionView.controller', //  이슈 버전 확인 컨트롤러
        'issueReservationController' : 'app/issue/issueReservation.controller', //  이슈 발생 예약 컨트롤러
src/main/webapp/views/issue/issueAddTreeTableConfig.html
New file
@@ -0,0 +1,95 @@
<div class="formModal">
    <div class="modal-header faded smaller">
        <div class="modal-title">
            <strong translate="issue.setIssueTableDisplay">이슈 테이블 표시 설정</strong>
        </div>
        <button aria-label="Close" class="close" type="button" ng-click="fn.cancel()">
            <span aria-hidden="true"> &times;</span>
        </button>
    </div>
    <div class="modal-body">
        <form role="form" name="issueTableConfigForm">
            <table class="table table-dash table-lightborder table-layout-fixed">
                <thead>
                <tr>
                    <th class="text-center width-80-p">
                        <span translate="issue.columnName">컬럼명</span>
                    </th>
<!--                    <th class="text-center width-80-p">-->
<!--                        <span translate="issue.area">넓이</span>-->
<!--                    </th>-->
                    <th class="text-center width-80-p">
                        <span translate="issue.displayed">표시 여부</span>
                    </th>
                    <th class="text-center width-40-p">
                        <span translate="issue.sequence">순서</span>
                    </th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td class="text-center">
                        <span class="bold" translate="issue.issueTitle">이슈 제목</span>
                    </td>
                    <td class="text-center" colspan="2">
                        <span class="text-danger bold" translate="issue.cannotChangedIssueTitle">이슈 제목은 변경할 수 없습니다.</span>
                    </td>
                </tr>
                <tr ng-repeat="issueTableConfig in vm.issueTableConfigs">
                    <td class="text-center">
                        <span class="bold">{{issueTableConfig.name}}</span>
                    </td>
<!--                    <td class="text-center">-->
<!--                        <select class="form-control" ng-model="issueTableConfig.width">-->
<!--                            <option>선택하세요.</option>-->
<!--                            <option value="width-20-p">20px</option>-->
<!--                            <option value="width-30-p">30px</option>-->
<!--                            <option value="width-40-p">40px</option>-->
<!--                            <option value="width-50-p">50px</option>-->
<!--                            <option value="width-60-p">60px</option>-->
<!--                            <option value="width-70-p">70px</option>-->
<!--                            <option value="width-80-p">80px</option>-->
<!--                            <option value="width-90-p">90px</option>-->
<!--                            <option value="width-100-p">100px</option>-->
<!--                            <option value="width-110-p">110px</option>-->
<!--                            <option value="width-120-p">120px</option>-->
<!--                            <option value="width-130-p">130px</option>-->
<!--                            <option value="width-140-p">140px</option>-->
<!--                            <option value="width-150-p">150px</option>-->
<!--                            <option value="width-160-p">160px</option>-->
<!--                            <option value="width-170-p">170px</option>-->
<!--                            <option value="width-180-p">180px</option>-->
<!--                            <option value="width-190-p">190px</option>-->
<!--                            <option value="width-200-p">200px</option>-->
<!--                            <option value="width-220-p">220px</option>-->
<!--                            <option value="width-240-p">240px</option>-->
<!--                            <option value="width-260-p">260px</option>-->
<!--                            <option value="width-280-p">280px</option>-->
<!--                            <option value="width-300-p">300px</option>-->
<!--                            <option value="width-20">20%</option>-->
<!--                            <option value="width-30">30%</option>-->
<!--                            <option value="width-40">40%</option>-->
<!--                            <option value="width-50">50%</option>-->
<!--                        </select>-->
<!--                    </td>-->
                    <td class="text-center">
                        <label class='switch'><input type='checkbox' ng-model='issueTableConfig.display'><span class='slider round'></span></label>
                    </td>
                    <td class="text-center">
                        <input type="text" class="form-control" ng-model="issueTableConfig.position" maxlength="2" input-regex="[^0-9]">
                    </td>
                </tr>
                </tbody>
            </table>
        </form>
    </div>
    <div class="modal-footer buttons-on-right">
        <button type="button" class="btn btn-md btn-grey" ng-click="fn.cancel()"><span translate="common.close">닫기</span></button>
        <button type="button" class="btn btn-md btn-primary bold"
                ng-disabled="fn.formCheck(issueTableConfigForm.$invalid)"
                ng-click="fn.formSubmit()"><span translate="common.save">저장</span>
        </button>
    </div>
</div>
src/main/webapp/views/issue/issueList.html
@@ -9,6 +9,9 @@
                <a class="nav-link cursor" ng-class="{ 'active' : vm.tab == 'ISSUE_LIST' }" ng-click="fn.changeTab('ISSUE_LIST')" translate="issue.normalList">일감 목록</a>
            </li>
            <li class="nav-item">
                <a class="nav-link cursor" ng-class="{ 'active' : vm.tab == 'ISSUE_TREE_LIST' }" ng-click="fn.changeTab('ISSUE_TREE_LIST')" translate="issue.treeList">구조 목록</a>
            </li>
            <li class="nav-item">
                <a class="nav-link cursor" ng-class="{ 'active' : vm.tab == 'ISSUE_TIMELINE' }" ng-click="fn.changeTab('ISSUE_TIMELINE')" translate="issue.timeLine">타임 라인</a>
            </li>
        </ul>
@@ -21,6 +24,10 @@
        <div ng-include include-replace src="'views/issue/issueListNormal.html'"></div>
    </div>
    <div ng-show="vm.tab == 'ISSUE_TREE_LIST'">
        <div ng-include include-replace src="'views/issue/issueListTree.html'"></div>
    </div>
    <div ng-show="vm.tab == 'ISSUE_TIMELINE'">
        <div ng-include include-replace src="'views/issue/issueListTimeline.html'"></div>
    </div>
src/main/webapp/views/issue/issueListTree.html
New file
@@ -0,0 +1,410 @@
<div class="container-fluid" ng-controller="issueListTreeController">
    <div class="row">
        <div class="col-sm-12" >
            <div class="element-wrapper" ng-if="!vm.detailView">
                <div class="element-box">
                  <div class="row" >
                    <div class="col-sm-12">
                        <div class="searchdiv">
                            <form name="issueSearchForm" role="form" ng-enter="fn.getPageList(0)">
                                <div class="row">
                                    <div class="col-sm-6">
                                        <div class="input-group">
                                            <input class="form-control"
                                                   type="text"
                                                   tabindex="-1"
                                                   maxlength="300"
                                                   kr-input
                                                   owl-auto-focus
                                                   ng-model="vm.search.title"
                                                   placeholder="{{'issue.pleaseEnterIssueName' | translate}}">
                                            <div class="input-group-prepend ml-10">
                                                <button class="btn btn-navy" ng-click="fn.getPageList(0)"> <span translate="common.search">검색</span></button>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row">
                                    <div class="col-sm-12">
                                        <div class="tabfilter">
                                            <div translate="common.selectedSearchCriteria">선택한 검색 조건</div>
                                            <!--    프로젝트    -->
                                            <issue-search-array-view-element lists="vm.projects"
                                                                             type="'project'"></issue-search-array-view-element>
                                            <!--    이슈 타입   -->
                                            <issue-search-field-key-view-element lists="vm.issueTypes"
                                                                                 keys="vm.search.issueTypeIds"></issue-search-field-key-view-element>
                                            <!--    이슈 상태   -->
                                            <issue-search-field-key-view-element lists="vm.issueStatuses"
                                                                                 keys="vm.search.issueStatusIds"></issue-search-field-key-view-element>
                                            <!--    이슈 번호   -->
                                            <p ng-if="$root.isDefined(vm.search.combinationIssueNumber)">
                                                {{vm.search.combinationIssueNumber}}
                                                <span ng-click="vm.search.combinationIssueNumber = ''">×</span>
                                            </p>
                                            <!--    이슈 내용   -->
                                            <p ng-if="$root.isDefined(vm.search.description)">
                                                {{vm.search.description}}
                                                <span ng-click="vm.search.description = ''">×</span>
                                            </p>
                                            <!--    우선 순위   -->
                                            <issue-search-field-key-view-element lists="vm.priorities"
                                                                                 keys="vm.search.priorityIds"></issue-search-field-key-view-element>
                                            <!--    중요도   -->
                                            <issue-search-field-key-view-element lists="vm.severities"
                                                                                 keys="vm.search.severityIds"></issue-search-field-key-view-element>
                                            <!--    담당자   -->
                                            <issue-search-array-view-element lists="vm.users"
                                                                             type="'user'"></issue-search-array-view-element>
                                            <!--    등록자   -->
                                            <issue-search-array-view-element lists="vm.registers"
                                                                             type="'user'"></issue-search-array-view-element>
                                            <!--    시작일   -->
                                            <p ng-if="$root.isDefined(vm.search.startDateRange)">
                                                {{vm.search.startDateRange}}
                                                <span ng-click="vm.search.startDateRange = ''">×</span>
                                            </p>
                                            <!--    종료일   -->
                                            <p ng-if="$root.isDefined(vm.search.completeDateRange)">
                                                {{vm.search.completeDateRange}}
                                                <span ng-click="vm.search.completeDateRange = ''">×</span>
                                            </p>
                                            <!--    등록일   -->
                                            <p ng-if="$root.isDefined(vm.search.registerDateRange)">
                                                {{vm.search.registerDateRange}}
                                                <span ng-click="vm.search.registerDateRange = ''">×</span>
                                            </p>
                                            <!--    텍스트 입력 필드   -->
                                            <issue-search-custom-field-view-element
                                                    custom-fields="vm.customFields"></issue-search-custom-field-view-element>
                                        </div>
                                    </div>
                                </div>
                                <div class="">
                                    <label class="issue-search-label" ng-class="{ 'icon-reverse' : vm.searchView }"
                                           ng-click="vm.searchView = !vm.searchView">
                                        <span translate="common.detailedSearch">상세검색</span>
                                    </label>
                                    <div ng-if="vm.searchView">
                                        <div class="row">
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.project">프로젝트</span></label>
                                                    <js-autocomplete-multi data-input-name="projects"
                                                                           selected-model="vm.projects"
                                                                           search="vm.projectName"
                                                                           input-disabled="false"
                                                                           translation-texts="{ empty : 'common.emptyProject', selectList : 'common.' }"
                                                                           source="fn.getProjectList(vm.projectName, vm.projects, null, null, ['01', '02', '03'])"
                                                                           extra-settings="{ displayProp : 'name' , idProp : 'id', imageable : false, imagePathProp : '', type : '', maxlength : 100}"></js-autocomplete-multi>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="issue.issueType">이슈 타입</span></label>
                                                    <ng-dropdown-multiselect class="multiSelect cursor"
                                                                             data-input-name="issueStatuses"
                                                                             selected-model="vm.search.issueTypeIds"
                                                                             options="::vm.issueTypes"></ng-dropdown-multiselect>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="issue.issueStatus">이슈 상태</span></label>
                                                    <ng-dropdown-multiselect class="multiSelect cursor"
                                                                             data-input-name="issueStatuses"
                                                                             selected-model="vm.search.issueStatusIds"
                                                                             options="::vm.issueStatuses"></ng-dropdown-multiselect>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="issue.issueNumber">이슈 번호</span></label>
                                                    <input type="text"
                                                           name="name"
                                                           class="form-control input-sm"
                                                           autocomplete="off"
                                                           kr-input
                                                           maxlength="20"
                                                           ng-model="vm.search.combinationIssueNumber">
                                                </div>
                                            </div>
                                        </div>
                                        <div class="row">
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="issue.issueContent">이슈 내용</span></label>
                                                    <input type="text"
                                                           name="description"
                                                           class="form-control input-sm"
                                                           kr-input
                                                           maxlength="20"
                                                           autocomplete="off"
                                                           ng-model="vm.search.description">
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.priority">우선 순위</span></label>
                                                    <ng-dropdown-multiselect class="multiSelect cursor"
                                                                             data-input-name="priorities"
                                                                             selected-model="vm.search.priorityIds"
                                                                             options="::vm.priorities"></ng-dropdown-multiselect>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.importance">중요도</span></label>
                                                    <ng-dropdown-multiselect class="multiSelect cursor"
                                                                             data-input-name="severities"
                                                                             selected-model="vm.search.severityIds"
                                                                             options="::vm.severities"></ng-dropdown-multiselect>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.assignee">담당자</span></label>
                                                    <js-autocomplete-multi data-input-name="users"
                                                                           selected-model="vm.users"
                                                                           search="vm.userName"
                                                                           input-disabled="false"
                                                                           source="fn.getUserList(vm.userName, vm.users)"
                                                                           translation-texts="{ count : 'common.userNum', empty : 'common.emptyUser' }"
                                                                           extra-settings="{ displayProp : 'byName' , idProp : 'id', widthable : false, width : '', imageable : true, imagePathProp : 'profile', type : 'user', maxlength : 100 }">
                                                    </js-autocomplete-multi>
                                                </div>
                                            </div>
                                        </div>
                                        <div class="row">
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.register">등록자</span></label>
                                                    <js-autocomplete-multi data-input-name="registers"
                                                                           selected-model="vm.registers"
                                                                           search="vm.registerName"
                                                                           input-disabled="false"
                                                                           source="fn.getUserList(vm.registerName, vm.registers)"
                                                                           translation-texts="{ count : 'common.userNum', empty : 'common.emptyUser' }"
                                                                           extra-settings="{ displayProp : 'byName' , idProp : 'id', widthable : false, width : '', imageable : true, imagePathProp : 'profile', type : 'user', maxlength : 100 }">
                                                    </js-autocomplete-multi>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.registrationDate">등록일</span></label>
                                                    <input type="text"
                                                           readonly
                                                           class="form-control input-sm input-readonly"
                                                           ng-model="vm.search.registerDateRange"
                                                           modal-form-auto-scroll
                                                           date-format="YY-MM-DD"
                                                           parent-el="'#createdWidget'"
                                                           date-range-picker>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.startDate">시작일</span></label>
                                                    <input type="text"
                                                           readonly
                                                           class="form-control input-sm input-readonly"
                                                           ng-model="vm.search.startDateRange"
                                                           date-format="YY-MM-DD"
                                                           parent-el="'#createdWidget'"
                                                           date-range-picker>
                                                </div>
                                            </div>
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.endDate">종료일</span></label>
                                                    <input type="text"
                                                           readonly
                                                           class="form-control input-sm input-readonly"
                                                           ng-model="vm.search.completeDateRange"
                                                           modal-form-auto-scroll
                                                           date-format="YY-MM-DD"
                                                           parent-el="'#createdWidget'"
                                                           date-range-picker>
                                                </div>
                                            </div>
                                            <div class="col-lg-3" ng-repeat="customField in vm.customFields">
                                                <label>{{::customField.name}}</label>
                                                <div ng-switch on="customField.customFieldType">
                                                    <div ng-switch-when="INPUT">
                                                        <input type="text" class="form-control input-sm"
                                                               ng-model="customField.useValues"
                                                               maxlength="100">
                                                    </div>
                                                    <div ng-switch-default>
                                                        <ng-dropdown-multiselect class="multiSelect cursor"
                                                                                 data-input-name="customField.name"
                                                                                 selected-model="customField.useValues"
                                                                                 extra-settings="{ 'idProp' : 'value', 'externalIdProp' : 'value', 'displayProp' : 'value', 'stringTypeOption' : 'true', 'stopRemoveBodyEvent' : 'true' }"
                                                                                 options="::customField.customFieldValueVos"></ng-dropdown-multiselect>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                        <div class="row">
                                            <div class="col-sm-12">
                                                <div class="form-buttons-w text-center mb-20">
                                                    <button class="btn btn-xlg btn-navy" ng-click="fn.getPageList(0)">
                                                        <i class="os-icon os-icon-ui-37"></i> &nbsp;
                                                        &nbsp; <span translate="common.search">검색</span> &nbsp; &nbsp;
                                                    </button>
                                                    <button class="btn btn-xlg btn-white" ng-click="fn.initSearch()">
                                                        <i class="os-icon os-icon-grid-18"></i> &nbsp;
                                                        &nbsp; <span translate="common.reset">초기화</span> &nbsp; &nbsp;
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
                    <div class="controls-above-table mt-30">
                        <div class="row">
                            <!--    좌측  -->
                            <div class="col-7">
                                <div class="dataTables_length">
                                    <label>
                                        <select name="pageRow"
                                                tabindex="-1"
                                                class="form-control form-control-sm"
                                                ng-change="fn.changePageRowCount()"
                                                ng-model="vm.page.selectedPageRowCount">
                                            <option value="10">10</option>
                                            <option value="50">50</option>
                                            <option value="100">100</option>
                                        </select> 1-1 / 2건</span>
                                    </label>
                                </div>
                            </div>
                            <!--    우측  -->
                            <div class="col-5 " >
                                <form class="form-inline justify-content-sm-end  pull-right" method="post" action="/issue/downloadExcel" name="issueListForm" >
                                    <!--span class="badge-tip" function-tool-tip  data-placement="top" data-toggle="tooltip" data-original-title="엑셀 다운로드, 일괄 변경 등 다양한 기능을 제공합니다.">?</span-->
                                    <input type="hidden" name="conditions">
                                    <div class="btn-group">
                                        <button aria-expanded="false" aria-haspopup="true"
                                                tabindex="-1"
                                                class="btn btn-secondary dropdown-toggle"
                                                data-toggle="dropdown" type="button"><span translate="common.addFunction">추가기능</span>
                                        </button>
                                        <div aria-labelledby="dropdownMenuButton2" class="dropdown-menu left-menu"
                                             x-placement="bottom-start" >
                                            <a class="dropdown-item cursor" ng-click="fn.addIssueTableConfig()"> <span translate="issue.settingTableDisplay">테이블 표시 설정</span></a>
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
<!--
                    <div class="table-responsive">
                        <js-table data="vm.responseData.data" table-configs="vm.tableConfigs"
                                  event="tableEvent" detail-view="vm.detailView"></js-table>
                    </div>
                    -->
                    <!-- 트리구조 임시 -->
                    <div class="tree-container">
                        <div class="tree-body">
                            <h5 class="tree-title"><button class="btn btn-primary">홈페이지 변조 탐지</button></h5>
                            <ul class="tree">
                                <li>
                                    <a href="#" class="btn btn-success">https://land.daum.com/news/expertColumnView.naver?artcl_seq=6580&page= | 2020.10.13 22:10:22</a>
                                    <a href="#" class="btn btn-info">연관 일감1</a>
                                    <a href="#" class="btn btn-info">연관 일감2</a>
                                    <ul>
                                        <li>
                                            <a href="#" class="btn btn-warning">https://dict.daum.com/elkodict/moderngreek/#/main | 2020.10.13 22:10:22</a>
                                        </li>
                                    </ul>
                                </li>
                                <li>
                                    <a href="#" class="btn btn-success">https://land.naver.com/news/region.naver | 2020.10.13 22:10:22</a>
                                    <a href="#" class="btn btn-info">연관 일감1</a>
                                    <ul>
                                        <li>
                                            <a href="#" class="btn btn-warning">https://sports.news.naver.com/news.nhn?oid=236&aid=0000218017 | 2020.10.13 22:10:22</a>
                                        </li>
                                    </ul>
                                </li>
                            </ul>
                        </div>
                        <hr>
                        <div class="tree-body">
                            <h5 class="tree-title"><button class="btn btn-primary">MCF</button></h5>
                            <ul class="tree">
                                <li>
                                    <a href="#" class="btn btn-success">https://land.naver.com/news/region.naver | 2020.10.13 22:10:22</a>
                                    <a href="#" class="btn btn-info">연관 일감1</a>
                                    <ul>
                                        <li>
                                            <a href="#" class="btn btn-warning">https://sports.news.naver.com/news.nhn?oid=236&aid=0000218017 | 2020.10.13 22:10:22</a>
                                        </li>
                                    </ul>
                                </li>
                            </ul>
                        </div>
                    </div>
                <div class="controls-below-table text-center">
                    <ul uib-pagination
                        boundary-links-numbes="true"
                        items-per-page="vm.page.selectedPageRowCount"
                        total-items="vm.responseData.page.totalCount"
                        ng-model="vm.page.selectedPage"
                        max-size="10"
                        ng-click="fn.getPageList(vm.page.selectedPage - 1)"
                        class="pagination pagination-sm"
                        previous-text="&lt;"
                        next-text="&gt;"
                        first-text=""
                        last-text="">
                    </ul>
                </div>
            </div>
            </div>
        </div>
    </div>
    <div class="row support-index" ng-show="vm.detailView">
        <div ng-include include-replace src="'views/issue/issueDetail.html'"></div>
    </div>
</div>