OWL ITS + 탐지시스템(인터넷 진흥원)
wyu
2021-11-22 6ee7dd9c3d8cc211ee5a01bbe481cabbb9c890eb
이슈 상세 연관,하위 테이블 표시 설정 - [프론트]
4개 파일 추가됨
14개 파일 변경됨
839 ■■■■ 파일 변경됨
src/main/webapp/assets/styles/main.css 13 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/i18n/ko/global.json 3 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issue.js 2 ●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueAdd.controller.js 18 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueAddDownTableConfig.controller.js 187 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueAddRelationTableConfig.controller.js 194 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueList.controller.js 19 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueModify.controller.js 95 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/project/projectAdd.controller.js 4 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/project/projectModify.controller.js 6 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/components/issueTableConfig/issueTableConfig.service.js 14 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/main.js 2 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueDetail.html 69 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueDownTableConfig.html 95 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueListNormal.html 10 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueRelationTableConfig.html 95 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/project/projectAdd.html 7 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/project/projectModify.html 6 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/assets/styles/main.css
@@ -934,6 +934,9 @@
    margin-left: 91.6666666667%;
}
.offset-12 {
    margin-left: 100%;
}
@media (min-width: 576px) {
    .col-sm {
        -ms-flex-preferred-size: 0;
@@ -3339,6 +3342,12 @@
    color: #fff;
    background-color: #343a40;
    border-color: #343a40;
}
.btn-darkgrey {
    color: #fff;
    background-color: #727072;
    border-color: #727072;
}
.btn-dark:hover {
@@ -30223,4 +30232,8 @@
    color:#0066ff;
}
.mgr-1 {
    margin-right: 1rem;
}
/* 간트차트 end */
src/main/webapp/i18n/ko/global.json
@@ -788,7 +788,8 @@
        "detectingData": "탐지일시",
        "import": "가져오기",
        "diffuserURL": "유포자URL",
        "country": "국가"
        "country": "국가",
        "tableView": "테이블 표시설정"
    },
    "tasks": {
        "agileBoardTitle": "칸반 보드"
src/main/webapp/scripts/app/issue/issue.js
@@ -37,7 +37,7 @@
                                'chartLoader', 'jsTable', 'tableColumnGenerator', 'modalFormAutoScroll', 'summerNote', 'summerNote-ko-KR', 'fullScroll', 'workflowService', 'priorityService', 'issueSearchService', 'issueTableConfigService', 'inputRegex',
                                'severityService', 'issueTypeService', 'issueTypeCustomFieldService', 'issueService', 'issueStatusService', 'issueUserService','issueDepartmentService','issueModifyUserController', 'issueModifyDepartmentController', 'customFieldService', 'issueSearchFieldKeyViewElement',
                                'issueSearchCustomFieldViewElement', 'tableUserImage', 'fullScroll', 'issueCommentService', 'detectIssueEditor', 'formSubmit', 'issueModifyStatusController', 'jsShortCut',
                                'issueAddTableConfigController', 'domAppend', 'issueDetailImagePreview', 'issueSendMailController', 'htmlDiff', 'issueVersionViewController', 'issueVersionService',
                                'issueAddTableConfigController','issueAddRelationTableConfigController','issueAddDownTableConfigController','domAppend', 'issueDetailImagePreview', 'issueSendMailController', 'htmlDiff', 'issueVersionViewController', 'issueVersionService',
                                'jsHtmlDiff', 'issueReservationController', 'issueReservationService', 'issueVersionService', 'issueStatusAutoFocus', 'issueRelationService'
                            ], function () {
                                deferred.resolve();
src/main/webapp/scripts/app/issue/issueAdd.controller.js
@@ -33,6 +33,7 @@
                    getIssueTypeCustomFields : getIssueTypeCustomFields,    //  이슈 유형에 연결된 사용자 정의 필드 목록 가져오기
                    removeUploadFile : removeUploadFile,    //  업로드하려는 특정 파일을 삭제
                    removeManager : removeManager,  //  담당자 삭제
                    removeDepartment : removeDepartment,  //  담당부서 삭제
                    setIssueTypeTemplate : setIssueTypeTemplate,    //  이슈 유형 템플릿 적용하기
                    startExecute : startExecute //  컨트롤 로딩시 처음으로 시작되는 함수
                };
@@ -294,6 +295,11 @@
                    $scope.vm.form.departments.splice(index, 1);
                }
                // 담당부서 삭제
                function removeDepartment(index) {
                    $scope.vm.form.departments.splice(index, 1);
                }
                //  업로드 파일 삭제
                function removeUploadFile(index) {
                    $scope.vm.form.files.splice(index, 1);
@@ -344,6 +350,7 @@
                // 업체정보 결과 값 Event 처리(set)
                $scope.$on("companyFieldEvent", function (event, result) {
                    $scope.vm.companyId = result[0].id;
                    $scope.vm.companyName = result[0].id;
                    $scope.vm.companyManager = result[0].manager;
                    $scope.vm.companyTel = result[0].tel;
                    $scope.vm.companyEmail = result[0].email;
@@ -354,6 +361,7 @@
                // ISP정보 결과 값 Event 처리(set)
                $scope.$on("ispFieldEvent", function (event, result) {
                    $scope.vm.ispId = result[0].id;
                    $scope.vm.ispName = result[0].name;
                    $scope.vm.ispCode = result[0].code;
                    $scope.vm.ispManager = result[0].manager;
                    $scope.vm.ispTel = result[0].tel;
@@ -364,6 +372,7 @@
                // 호스팅정보 결과 값 Event 처리(set)
                $scope.$on("hostingFieldEvent", function (event, result) {
                    $scope.vm.hostingId = result[0].id;
                    $scope.vm.hostingName = result[0].name;
                    $scope.vm.hostingCode = result[0].code;
                    $scope.vm.hostingManager = result[0].manager;
                    $scope.vm.hostingTel = result[0].tel;
@@ -378,6 +387,7 @@
                    var content = {
                        title : $rootScope.preventXss($scope.vm.form.title),    //  제목
                        description : $rootScope.preventXss($scope.vm.form.description),   //  내용
                        projectId : (function () {   //  프로젝트 아이디
                            var projectId = "";
@@ -387,9 +397,11 @@
                            return projectId;
                        })(),
                        issueTypeId : $scope.vm.form.issueTypeId,   //  이슈 유형 아이디
                        priorityId : $scope.vm.form.priorityId,    //  우선순위 아이디
                        severityId : $scope.vm.form.severityId,    //  중요도 아이디
                        companyId : (function () {   //  업체 아이디
                            var companyId = -1;
                            if ($scope.vm.form.issueCompanyFields.length > 0) {
@@ -445,9 +457,6 @@
                            return attachedFileIds;
                        })(),
                        startCompleteDateRange : $scope.vm.form.startCompleteDateRange,
                        detectingDateRange : $scope.vm.form.detectingDateRange,
                        issueCompanyFields : (function () {
                            var issueCompanyFields = [];
@@ -510,6 +519,9 @@
                            return issueHostingFields;
                        })(),
                        startCompleteDateRange : $scope.vm.form.startCompleteDateRange,
                        detectingDateRange : $scope.vm.form.detectingDateRange,
                        issueCustomFields : (function () {    //  이슈에서 사용되는 사용자 정의 필드
                            var issueCustomFields = [];
src/main/webapp/scripts/app/issue/issueAddDownTableConfig.controller.js
New file
@@ -0,0 +1,187 @@
/**
 * Created by wisestone on 2019-02-07.
 */
'use strict';
define([
        'app',
        'angular'
    ],
    function (app, angular) {
        app.controller('issueAddDownTableConfigController', ['$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.addDown($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/issueAddRelationTableConfig.controller.js
New file
@@ -0,0 +1,194 @@
/**
 * Created by wisestone on 2019-02-07.
 */
'use strict';
define([
        'app',
        'angular'
    ],
    function (app, angular) {
        app.controller('issueAddRelationTableConfigController', ['$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"), // 우선순위
                        issueId : "",
                        key : "PRIORITY",
                        width : "width-100-p",
                        position : 1,
                        display : true
                    }, {
                        name : $filter("translate")("common.importance"), // 중요도
                        issueId : "",
                        key : "SEVERITY",
                        width : "width-80-p",
                        position : 2,
                        display : true
                    }, {
                        name : $filter("translate")("issue.issueType"), // 이슈 타입
                        issueId : "",
                        key : "ISSUE_TYPE",
                        width : "width-140-p",
                        position : 3,
                        display : true
                    }, {
                        name : $filter("translate")("common.assignee"), // 담당자
                        issueId : "",
                        key : "ASSIGNEE",
                        width : "width-100-p",
                        position : 4,
                        display : true
                    }, {
                        name : $filter("translate")("common.register"), // 등록자
                        issueId : "",
                        key : "REGISTER",
                        width : "width-100-p",
                        position : 5,
                        display : false
                    }, {
                        name : $filter("translate")("common.period"), // 기간
                        issueId : "",
                        key : "PERIOD",
                        width : "width-140-p",
                        position : 6,
                        display : false
                    }, {
                        name : $filter("translate")("common.lastChangeDate"), // 최근 변경일
                        issueId : "",
                        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.addRelation($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/issueList.controller.js
@@ -52,7 +52,7 @@
                        severityIds : [],   //  중요도 검색
                        priorityIds : [],   //  우선순위 검색
                        issueStatusIds : [],    //  이슈 상태 검색
                        issueTypeIds : []   //  이슈 유형 검색
                        issueTypeIds : [],   //  이슈 유형 검색
                    },
                    searchView : false, //  상세 검색 조건 표시 여부
                    detailView : false, //  상세 모드 변경 값
@@ -64,10 +64,12 @@
                    responseData : {
                        data : []
                    },
                    departmentName : "",
                    projectName : "",   //  프로젝트 검색
                    userName : "",  //  담당자 검색
                    registerName : "",  //  등록자 검색
                    projects : [],  //  프로젝트
                    departments : [],
                    issueStatuses : [], //  이슈 상태
                    issueTypes : [],    //  이슈 유형
                    priorities : [],    //  우선 순위
@@ -103,6 +105,7 @@
                            search : $scope.vm.search,
                            projects : $scope.vm.projects,
                            users : $scope.vm.users,
                            departments : $scope.vm.departments,
                            registers : $scope.vm.registers,
                            parentIssueId : $scope.vm.parentIssueId
                        })
@@ -130,9 +133,10 @@
                                var issueListSearchObject = JSON.parse(result.data.data);
                                //  이슈 번호만 적용한다. - 삭제시 처리방법때문에 다른 속성은 적용 보류
                                $scope.vm.search = issueListSearchObject.search;
                                /*$scope.vm.users = issueListSearchObject.users;
                                $scope.vm.users = issueListSearchObject.users;
                                $scope.vm.departments = issueListSearchObject.departments;
                                $scope.vm.projects = issueListSearchObject.projects;
                                $scope.vm.registers = issueListSearchObject.registers;*/
                                $scope.vm.registers = issueListSearchObject.registers;
                                $scope.fn.getPageList(0, true);
                            } else {
@@ -377,6 +381,15 @@
                            return userIds;
                        })(),
                        departmentIds : (function () {
                            var departmentIds = [];
                            angular.forEach($scope.vm.departments, function (department) {
                                departmentIds.push(department.id);
                            });
                            return departmentIds;
                        })(),
                        registerIds : (function () {
                            var registerIds = [];
src/main/webapp/scripts/app/issue/issueModify.controller.js
@@ -33,7 +33,7 @@
                    setFormByIssueTypeCustomFields : setFormByIssueTypeCustomFields,    //  이슈 유형에 연결된 사용자 정의 필드 정보를 입력 폼에서 사용할 수 있게 가공한다.
                    setUseValueByIssueTypeCustomFields : setUseValueByIssueTypeCustomFields,    //  이슈에서 사용자가 선택한 사용자 정의 필드 값을 입력 폼에 셋팅한다.
                    detail : detail,    //  이슈 정보 조회
                    // removeManager : removeManager,  //  담당자 삭제
                    removeManager : removeManager,  //  담당자 삭제
                    removeDepartment : removeDepartment,  //  담당부서 삭제
                    removeUploadFile : removeUploadFile,    //  특정 파일을 삭제
                    setIssueTypeTemplate : setIssueTypeTemplate,    //  이슈 유형 템플릿 적용하기
@@ -161,9 +161,9 @@
                }
                //  담당자 삭제
                // function removeManager(index) {
                //     $scope.vm.form.users.splice(index, 1);
                // }
                 function removeManager(index) {
                     $scope.vm.form.users.splice(index, 1);
                 }
                // 담당부서 삭제
                function removeDepartment(index) {
@@ -386,6 +386,19 @@
                        id : parameter.id,
                        title : $rootScope.preventXss($scope.vm.form.title),    //  제목
                        description : $rootScope.preventXss($scope.vm.form.description),   //  내용
                        ispName : $scope.vm.ispName,
                        ispCode : $scope.vm.ispCode,
                        ispManager : $scope.vm.ispManager,
                        ispTel : $scope.vm.ispTel,
                        ispEmail : $scope.vm.ispEmail,
                        ispMemo : $scope.vm.ispMemo,
                        hostingName : $scope.vm.hostingName,
                        hostingCode : $scope.vm.hostingCode,
                        hostingManager : $scope.vm.hostingManager,
                        hostingTel : $scope.vm.hostingTel,
                        hostingEmail : $scope.vm.hostingEmail,
                        hostingMemo : $scope.vm.hostingMemo,
                        projectId : (function () {   //  프로젝트 아이디
                            var projectId = "";
@@ -430,6 +443,37 @@
                            return hostingId;
                        }),
                        userIds : (function () {
                            var userIds = [];
                            angular.forEach($scope.vm.form.users, function (user) {
                                userIds.push(user.id);
                            });
                            return userIds;
                        })(),
                        departmentIds : (function () {
                            var departmentIds = [];
                            angular.forEach($scope.vm.form.departments, function (department) {
                                departmentIds.push(department.id);
                            });
                            return departmentIds;
                        })(),
                        attachedFileIds : (function () {
                            var attachedFileIds = [];
                            angular.forEach($scope.vm.form.attachedFiles, function (attachedFile) {
                                if ($scope.vm.form.description.indexOf(attachedFile.path) !== -1) {
                                    attachedFileIds.push(attachedFile.id);
                                }
                            });
                            return attachedFileIds;
                        })(),
                        issueCompanyFields : (function () {
                            var issueCompanyFields = [];
@@ -492,50 +536,7 @@
                            return issueHostingFields;
                        })(),
                        ispName : $scope.vm.ispName,
                        ispCode : $scope.vm.ispCode,
                        ispManager : $scope.vm.ispManager,
                        ispTel : $scope.vm.ispTel,
                        ispEmail : $scope.vm.ispEmail,
                        ispMemo : $scope.vm.ispMemo,
                        hostingName : $scope.vm.hostingName,
                        hostingCode : $scope.vm.hostingCode,
                        hostingManager : $scope.vm.hostingManager,
                        hostingTel : $scope.vm.hostingTel,
                        hostingEmail : $scope.vm.hostingEmail,
                        hostingMemo : $scope.vm.hostingMemo,
                        userIds : (function () {
                            var userIds = [];
                            angular.forEach($scope.vm.form.users, function (user) {
                                userIds.push(user.id);
                            });
                            return userIds;
                        })(),
                        departmentIds : (function () {
                            var departmentIds = [];
                            angular.forEach($scope.vm.form.departments, function (department) {
                                departmentIds.push(department.id);
                            });
                            return departmentIds;
                        })(),
                        removeFiles : $scope.vm.form.removeFiles,
                        attachedFileIds : (function () {
                            var attachedFileIds = [];
                            angular.forEach($scope.vm.form.attachedFiles, function (attachedFile) {
                                if ($scope.vm.form.description.indexOf(attachedFile.path) !== -1) {
                                    attachedFileIds.push(attachedFile.id);
                                }
                            });
                            return attachedFileIds;
                        })(),
                        startCompleteDateRange : $scope.vm.form.startCompleteDateRange,
                        issueCustomFields : (function () {    //  이슈에서 사용되는 사용자 정의 필드
src/main/webapp/scripts/app/project/projectAdd.controller.js
@@ -74,10 +74,6 @@
                        return true;
                    }
                    if (!$rootScope.isDefined($scope.vm.form.startEndDateRange)) {
                        return true;
                    }
                    return false;
                }
src/main/webapp/scripts/app/project/projectModify.controller.js
@@ -100,15 +100,9 @@
                        return true;
                    }
                    if ($scope.vm.form.startEndDateRange == "") {
                        return true;
                    }
                    if ($scope.vm.form.managers.length < 1) {
                        return true;
                    }
                    return false;
                }
src/main/webapp/scripts/components/issueTableConfig/issueTableConfig.service.js
@@ -17,6 +17,20 @@
                    $log.debug("이슈 목록 테이블 설정 등록 : ", response);
                    return response;
                });
            },
            addRelation : function (conditions) {
                return $http.post("issueTableConfig/relationAdd", conditions).then(function (response) {
                    $log.debug("연관 이슈 목록 테이블 설정 등록 : ", response);
                    return response;
                });
            },
            addDown : function (conditions) {
                return $http.post("issueTableConfig/downAdd", conditions).then(function (response) {
                    $log.debug("하위 이슈 목록 테이블 설정 등록 : ", response);
                    return response;
                });
            }
        }
    }
src/main/webapp/scripts/main.js
@@ -184,6 +184,8 @@
        'issueImportExcelController' : 'app/issue/issueImportExcel.controller',    //  이슈 엑셀 대량 import
        'issueModifyStatusController' : 'app/issue/issueModifyStatus.controller',   //  이슈 다중 상태 변경 컨트롤러
        'issueAddTableConfigController' : 'app/issue/issueAddTableConfig.controller',   //  이슈 테이블 설정 컨트롤러
        'issueAddRelationTableConfigController' : 'app/issue/issueAddRelationTableConfig.controller',   //  이슈 테이블 설정 컨트롤러
        'issueAddDownTableConfigController' : 'app/issue/issueAddDownTableConfig.controller',   //  이슈 테이블 설정 컨트롤러
        'issueSendMailController' : 'app/issue/issueSendMail.controller',   //  이슈 이메일 발송 컨트롤러
        'issueVersionViewController' : 'app/issue/issueVersionView.controller', //  이슈 버전 확인 컨트롤러
        'issueReservationController' : 'app/issue/issueReservation.controller', //  이슈 발생 예약 컨트롤러
src/main/webapp/views/issue/issueDetail.html
@@ -376,36 +376,44 @@
                    </div>
                </div>
                    <!-- 사용자 정의 필드 -->
<!--                <div class="row">-->
<!--                    <div class="col-md-4" ng-repeat="issueCustomField in vm.viewer.issueCustomFields">-->
<!--                        <label class="issue-detail-label">{{::issueCustomField.customFieldVo.name}}</label>-->
                <div class="row">
                    <div class="col-md-4" ng-repeat="issueCustomField in vm.viewer.issueCustomFields">
                        <label class="issue-detail-label">{{::issueCustomField.customFieldVo.name}}</label>
<!--                        <div ng-switch on="issueCustomField.customFieldVo.customFieldType">-->
<!--                            &lt;!&ndash; 기본 입력 &ndash;&gt;-->
<!--                            <div ng-switch-when="INPUT" class="form-group">-->
<!--                                &lt;!&ndash;    읽기 모드   &ndash;&gt;-->
<!--                                <span class="issue-detail-word-break">{{::issueCustomField.useValues}}</span>-->
<!--                                <span ng-if="!$root.isDefined(issueCustomField.useValues)">-</span>-->
<!--                            </div>-->
                        <div ng-switch on="issueCustomField.customFieldVo.customFieldType">
                            <!-- 기본 입력 -->
                            <div ng-switch-when="INPUT" class="form-group">
                                <!--    읽기 모드   -->
                                <span class="issue-detail-word-break">{{::issueCustomField.useValues}}</span>
                                <span ng-if="!$root.isDefined(issueCustomField.useValues)">-</span>
                            </div>
<!--                            &lt;!&ndash; 단일 셀렉트 &ndash;&gt;-->
<!--                            <div ng-switch-when="SINGLE_SELECT" class="form-group">-->
<!--                                <span class="issue-detail-word-break">{{::issueCustomField.useValues}}</span>-->
<!--                                <span ng-if="!$root.isDefined(issueCustomField.useValues)">-</span>-->
<!--                            </div>-->
                            <!-- 단일 셀렉트 -->
                            <div ng-switch-when="SINGLE_SELECT" class="form-group">
                                <span class="issue-detail-word-break">{{::issueCustomField.useValues}}</span>
                                <span ng-if="!$root.isDefined(issueCustomField.useValues)">-</span>
                            </div>
<!--                            &lt;!&ndash; 멀티 셀렉트 &ndash;&gt;-->
<!--                            <div ng-switch-when="MULTI_SELECT" class="form-group">-->
<!--                                <span class="issue-detail-word-break"-->
<!--                                      ng-repeat="useValue in issueCustomField.useValues">{{::useValue.value}}, </span>-->
<!--                                <span ng-if="!$root.isDefined(issueCustomField.useValues)">-</span>-->
<!--                            </div>-->
<!--                        </div>-->
<!--                    </div>-->
<!--                </div>-->
                            <!-- 멀티 셀렉트 -->
                            <div ng-switch-when="MULTI_SELECT" class="form-group">
                                <span class="issue-detail-word-break"
                                      ng-repeat="useValue in issueCustomField.useValues">{{::useValue.value}}, </span>
                                <span ng-if="!$root.isDefined(issueCustomField.useValues)">-</span>
                            </div>
                        </div>
                <div class="row mt-30">
                    <div class="col-md-10">
                        <span class="info_font h3" translate="issue.relationIssue">연관 이슈</span>
                    </div>
                    <div class="col-md-1">
                        <button class="btn btn-darkgrey offset-12" ng-click="fn.addRelationIssueTableConfig()" type="button"><span translate="issue.settingTableDisplay">테이블 표시 설정</span></button>
                    </div>
                </div>
                <h6 class="todo-content-subheader mt-20" translate="issue.relationIssue">연관 일감</h6>
                <!--    테이블 -->
                <div class="mt-10 issue-detail-word-break width-100">
                    <js-table data="vm.viewer.issueRelationVos" table-configs="vm.relTableConfigs"
@@ -444,7 +452,15 @@
                    </div>
                </div>
                <h6 class="todo-content-subheader mt-20" translate="issue.downIssue">하위 일감</h6>
                <div class="row mt-30">
                    <div class="col-md-10">
                        <span class="info_font h3" translate="issue.downIssue">하위 이슈</span>
                    </div>
                    <div class="col-md-1">
                        <button class="btn btn-darkgrey offset-12"  ng-click="fn.addDownIssueTableConfig()" type="button"><span translate="issue.settingTableDisplay">테이블 표시 설정</span></button>
                    </div>
                </div>
                <!--    테이블 -->
                <div class="mt-10 issue-detail-word-break width-100">
                    <js-table data="vm.viewer.issueDownVos" table-configs="vm.downTableConfigs"
@@ -472,7 +488,7 @@
                    </div>
                </div>
                <h6 class="todo-content-subheader mt-20" translate="common.content">내용</h6>
                <h6 class="todo-content-subheader mt-30" translate="common.content">내용</h6>
                <div class="box mt-10 issue-detail-word-break width-100" >
                    <div ng-bind-html="$root.$sce.trustAsHtml(vm.viewer.description)"></div>
                    <span class="fc-grey" ng-if="!$root.isDefined(vm.viewer.description)" translate="common.noContent">
@@ -664,6 +680,9 @@
                                         translate="common.noRecord">
                                        기록이 없습니다.
                                    </div>
                                    <div>
                                        {{vm.viewer.issueHistoryVos}}
                                    </div>
                                    <div class="activity-item"
                                         ng-repeat="issueHistory in vm.viewer.issueHistoryVos">
src/main/webapp/views/issue/issueDownTableConfig.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="3">
                        <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/issueListNormal.html
@@ -186,12 +186,12 @@
                                            <div class="col-lg-3">
                                                <div class="form-group">
                                                    <label> <span translate="common.assigneeTeam">담당부서</span></label>
                                                    <js-autocomplete-multi data-input-name="users"
                                                                           selected-model="vm.users"
                                                                           search="vm.userName"
                                                    <js-autocomplete-multi data-input-name="departments"
                                                                           selected-model="vm.departments"
                                                                           search="vm.departmentName"
                                                                           input-disabled="false"
                                                                           source="fn.getUserList(vm.userName, vm.users)"
                                                                           translation-texts="{ count : 'common.userNum', empty : 'common.emptyUser' }"
                                                                           source="fn.getUserDepartmentList(vm.departmentName, vm.departments)"
                                                                           translation-texts="{ count : 'common.userNum', empty : 'common.emptyDepartment' }"
                                                                           extra-settings="{ displayProp : 'byName' , idProp : 'id', widthable : false, width : '', imageable : true, imagePathProp : 'profile', type : 'user', maxlength : 100 }">
                                                    </js-autocomplete-multi>
                                                </div>
src/main/webapp/views/issue/issueRelationTableConfig.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="3">
                        <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/project/projectAdd.html
@@ -68,13 +68,14 @@
                <div class="col-lg-6">
                    <div class="form-group">
                        <label><span translate="common.period">기간</span></label>
                        <input type="text"
                        <input tabindex="-1"
                               type="text"
                               class="form-control input-readonly"
                               placeholder="{{'issue.clickToSelectDate' | translate}}"
                               ng-model="vm.form.startEndDateRange"
                               modal-form-auto-scroll
                               date-format="YYYY-MM-DD"
                               parent-el="'#createdWidget'"
                               range-type="date"
                               onfocus="this.blur()"
                               date-range-picker>
                        <div class="row">
                            <div class="col-xs-12">
src/main/webapp/views/project/projectModify.html
@@ -34,14 +34,14 @@
                <div class="col-lg-6">
                    <div class="form-group">
                        <label for="projectModifyForm2"><span translate="common.period">기간</span></label>
                        <input id="projectModifyForm2"
                        <input tabindex="-1"
                               type="text"
                               class="form-control input-readonly"
                               placeholder="{{'issue.clickToSelectDate' | translate}}"
                               ng-model="vm.form.startEndDateRange"
                               modal-form-auto-scroll
                               date-format="YYYY-MM-DD"
                               parent-el="'#createdWidget'"
                               range-type="date"
                               onfocus="this.blur()"
                               date-range-picker>
                        <div class="row">
                            <div class="col-xs-12">