OWL ITS + 탐지시스템(인터넷 진흥원)
wyu
2021-12-08 257766aa7e8a88b2b371fc6f8f52751af7d84eda
연관 ,하위 이슈 요청 횟수 수정
1개 파일 추가됨
11개 파일 변경됨
431 ■■■■■ 파일 변경됨
src/main/java/kr/wisestone/owl/domain/IssueTableConfig.java 6 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/IssueTableConfigService.java 4 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java 24 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueTableConfigServiceImpl.java 40 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/vo/IssueTableConfigVo.java 52 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/vo/IssueVo.java 16 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/web/controller/IssueTableConfigController.java 17 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/assets/styles/main.css 3 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js 12 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/issue/issueDetail.controller.js 228 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/scripts/app/project/projectList.controller.js 19 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/views/issue/issueDetail.html 10 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/domain/IssueTableConfig.java
@@ -10,6 +10,12 @@
public class IssueTableConfig extends BaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final int ISSUE_TABLE_TYPE_MAIN = 1;
    public static final int ISSUE_TABLE_TYPE_REL = 2;
    public static final int ISSUE_TABLE_TYPE_DOWN = 3;
    public static int[] IssueTableTypes = {ISSUE_TABLE_TYPE_MAIN, ISSUE_TABLE_TYPE_REL, ISSUE_TABLE_TYPE_DOWN};
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
src/main/java/kr/wisestone/owl/service/IssueTableConfigService.java
@@ -14,10 +14,6 @@
    void detailIssueTableConfig(Map<String, Object> params, Map<String, Object> resJsonData);
    void detailRelationIssueTableConfig(Map<String, Object> params, Map<String, Object> resJsonData);
    void detailDownIssueTableConfig(Map<String, Object> params, Map<String, Object> resJsonData);
    IssueTableConfig findByUserIdAndWorkspaceIdAndIssueTypeIdAndIssueTableType(Long issueId, int issueTableType);
    IssueTableConfig findByIssueTypeIdAndIssueTableType(Long issueTypeId, int issueTableType);
src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -60,6 +60,9 @@
    private ProjectService projectService;
    @Autowired
    private IssueTableConfigService issueTableConfigService;
    @Autowired
    private IssueStatusService issueStatusService;
    @Autowired
@@ -1226,6 +1229,7 @@
                case "02": //  프로젝트, 이슈 유형, 이슈 상태,  우선순위, 중요도, 담당자, 첨부파일, 사용자 정의 필드 정보, 댓글, 기록을 셋팅한다.
                    this.setIssueDetail(issueVo, issue);    //  이슈 상세 정보를 셋팅한다.
                    this.setIssueTableConfigs(issue, issueVo);
                    issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class));
                    break;
            }
@@ -1237,6 +1241,26 @@
        resJsonData.put(Constants.RES_KEY_CONTENTS, issueVo);
    }
    // 테이블 설정 셋팅
    private void setIssueTableConfigs(Issue issue, IssueVo issueVo) {
        Long IssueTypeId = issue.getIssueType().getId();
        for (int tableConfigType : IssueTableConfig.IssueTableTypes) {
            if (tableConfigType != IssueTableConfig.ISSUE_TABLE_TYPE_MAIN) {
                issueVo.addIssueTableConfigVo(createIssueTableConfigVo(IssueTypeId, tableConfigType));
            }
        }
    }
    private IssueTableConfigVo createIssueTableConfigVo(Long issueTypeId, int tableConfigType) {
        IssueTableConfig issueTableConfig = this.issueTableConfigService.findByUserIdAndWorkspaceIdAndIssueTypeIdAndIssueTableType(issueTypeId, tableConfigType);
        if (issueTableConfig != null) {
            return ConvertUtil.copyProperties(issueTableConfig, IssueTableConfigVo.class);
        }
        return new IssueTableConfigVo();
    }
    // 하위 이슈 정보를 셋팅한다
    private void setDownIssues(Issue issue, IssueVo issueVo) {
        List<Issue> downIssues = this.issueRepository.findByParentIssueId(issue.getId());
src/main/java/kr/wisestone/owl/service/impl/IssueTableConfigServiceImpl.java
@@ -85,6 +85,7 @@
        return this.addIssueTableConfig(params, issueTypeId, issueTableType);
    }
    //  해당 업무 공간에서 사용자의 이슈 테이블 설정을 조회한다.
    @Override
    @Transactional(readOnly = true)
@@ -109,29 +110,11 @@
        return  null;
    }
    // detail 중복 코드 제거
    private void detailIssueTableConfig(Map<String, Object> resJsonData, Long issueTypeId, int issueTableType) {
        //  해당 업무 공간에서 사용자의 이슈 검색 조건을 조회한다.
        IssueTableConfig issueTableConfig = this.findByUserIdAndWorkspaceIdAndIssueTypeIdAndIssueTableType(issueTypeId, issueTableType);
        if (issueTableConfig != null && issueTableConfig.getIssueTableType() == 2) {
            resJsonData.put(Constants.RES_KEY_CONTENTS, issueTableConfig.getIssueTableConfigs());
        } else if (issueTableConfig != null && issueTableConfig.getIssueTableType() == 3) {
            resJsonData.put(Constants.RES_KEY_CONTENTS, issueTableConfig.getIssueTableConfigs());
        }
        else {
            resJsonData.put(Constants.RES_KEY_CONTENTS, "");
        }
    }
    //  저장된 이슈 테이블 설정을 조회한다.
    @Override
    @Transactional(readOnly = true)
    public void detailIssueTableConfig(Map<String, Object> params, Map<String, Object> resJsonData) {
        issueTypeId = MapUtil.getLong(params, "issueTypeId");
        Long issueTypeId = MapUtil.getLong(params,"issueTypeId");
        int issueTableType = MapUtil.getInteger(params,"issueTableType");
        issueTableType = 1;
        //  해당 업무 공간에서 사용자의 이슈 검색 조건을 조회한다.
        IssueTableConfig issueTableConfig = this.findByUserIdAndWorkspaceIdAndIssueTypeIdAndIssueTableType(issueTypeId, issueTableType);
        if (issueTableConfig != null) {
@@ -141,21 +124,4 @@
            resJsonData.put(Constants.RES_KEY_CONTENTS, "");
        }
    }
    //  저장된 연관 이슈 테이블 설정 조회
    @Override
    public void detailRelationIssueTableConfig(Map<String, Object> params, Map<String, Object> resJsonData) {
        issueTableType = 2;
        issueTypeId = MapUtil.getLong(params, "issueTypeId");
        this.detailIssueTableConfig(resJsonData, issueTypeId, issueTableType);
    }
    //  저장된 하위 이슈 테이블 설정 조회
    @Override
    public void detailDownIssueTableConfig(Map<String, Object> params, Map<String, Object> resJsonData) {
        issueTableType = 3;
        issueTypeId = MapUtil.getLong(params, "issueTypeId");
        this.detailIssueTableConfig(resJsonData, issueTypeId, issueTableType);
    }
}
src/main/java/kr/wisestone/owl/vo/IssueTableConfigVo.java
New file
@@ -0,0 +1,52 @@
package kr.wisestone.owl.vo;
import com.google.common.collect.Lists;
import kr.wisestone.owl.domain.User;
import kr.wisestone.owl.domain.Workspace;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.util.ArrayList;
import java.util.List;
public class IssueTableConfigVo extends BaseVo{
    private Long id;
    private String issueTableConfigs;
    private int issueTableType;
    private Long issueTypeId;
    public IssueTableConfigVo() {}
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getIssueTableConfigs() {
        return issueTableConfigs;
    }
    public void setIssueTableConfigs(String issueTableConfigs) {
        this.issueTableConfigs = issueTableConfigs;
    }
    public int getIssueTableType() {
        return issueTableType;
    }
    public void setIssueTableType(int issueTableType) {
        this.issueTableType = issueTableType;
    }
    public Long getIssueTypeId() {
        return issueTypeId;
    }
    public void setIssueTypeId(Long issueTypeId) {
        this.issueTypeId = issueTypeId;
    }
}
src/main/java/kr/wisestone/owl/vo/IssueVo.java
@@ -1,6 +1,7 @@
package kr.wisestone.owl.vo;
import com.google.common.collect.Lists;
import kr.wisestone.owl.domain.IssueTableConfig;
import java.util.ArrayList;
import java.util.List;
@@ -48,6 +49,7 @@
    //private List<IssueDownVo> issueDownVos = Lists.newArrayList();
    private List<IssueVo> issueDownVos = Lists.newArrayList();
    private List<IssueVo> issueRelationVos = Lists.newArrayList();
    private List<IssueTableConfigVo> issueTableConfigVos = Lists.newArrayList();
    private Long attachedFileCount;
    private Long issueCommentCount;
    private String modifyByName;    //  변경자 정보 - 이슈 변경 정보 상세 확인에서 사용
@@ -484,4 +486,18 @@
    public void setWorkflowDepartmentIds(List<Long> workflowDepartmentIds) {
        this.workflowDepartmentIds = workflowDepartmentIds;
    }
    public List<IssueTableConfigVo> getIssueTableConfigVos() {
        return issueTableConfigVos;
    }
    public void setIssueTableConfigVos(List<IssueTableConfigVo> issueTableConfigVos) {
        this.issueTableConfigVos = issueTableConfigVos;
    }
    public void addIssueTableConfigVo(IssueTableConfigVo issueTableConfigVo) {
        if (this.issueTableConfigVos != null) {
            this.issueTableConfigVos.add(issueTableConfigVo);
        }
    }
}
src/main/java/kr/wisestone/owl/web/controller/IssueTableConfigController.java
@@ -1,6 +1,7 @@
package kr.wisestone.owl.web.controller;
import kr.wisestone.owl.constant.Constants;
import kr.wisestone.owl.domain.IssueTableConfig;
import kr.wisestone.owl.service.IssueTableConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@@ -64,7 +65,10 @@
    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
        Map<String, Object> resJsonData = new HashMap<>();
        this.issueTableConfigService.detailIssueTableConfig(params.get(Constants.REQ_KEY_CONTENT), resJsonData);
        Map<String, Object> param = params.get(Constants.REQ_KEY_CONTENT);
        param.put("issueTableType", IssueTableConfig.ISSUE_TABLE_TYPE_MAIN);
        this.issueTableConfigService.detailIssueTableConfig(param, resJsonData);
        return this.setSuccessMessage(resJsonData);
    }
@@ -75,7 +79,11 @@
    @ResponseBody
    Map<String, Object> relationDetail(@RequestBody Map<String, Map<String, Object>> params) {
        Map<String, Object> resJsonData = new HashMap<>();
        this.issueTableConfigService.detailRelationIssueTableConfig(params.get(Constants.REQ_KEY_CONTENT), resJsonData);
        Map<String, Object> param = params.get(Constants.REQ_KEY_CONTENT);
        param.put("issueTableType", IssueTableConfig.ISSUE_TABLE_TYPE_REL);
        this.issueTableConfigService.detailIssueTableConfig(param, resJsonData);
        return this.setSuccessMessage(resJsonData);
    }
@@ -86,7 +94,10 @@
    @ResponseBody
    Map<String, Object> downDetail(@RequestBody Map<String, Map<String, Object>> params) {
        Map<String, Object> resJsonData = new HashMap<>();
        this.issueTableConfigService.detailDownIssueTableConfig(params.get(Constants.REQ_KEY_CONTENT),resJsonData);
        Map<String, Object> param = params.get(Constants.REQ_KEY_CONTENT);
        param.put("issueTableType", IssueTableConfig.ISSUE_TABLE_TYPE_DOWN);
        this.issueTableConfigService.detailIssueTableConfig(param, resJsonData);
        return this.setSuccessMessage(resJsonData);
    }
src/main/webapp/assets/styles/main.css
@@ -14271,7 +14271,7 @@
}
.menu-position-side.menu-w.color-scheme-dark .logo-w {
    background-color:#f2f4f8;
    background-color:#FFFFFF;
}
.menu-position-side.menu-w.color-scheme-dark .element-search:before {
@@ -29693,6 +29693,7 @@
    overflow: hidden;
    margin-top: 30px;
    font-size: 0.69rem;
    margin-left: 35rem;
}
.width-20-p {
src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js
@@ -75,9 +75,13 @@
                                        //  프로젝트 이름(프로젝트 리스트에서 사용)
                                        case "PROJECT_NAME" :
                                            if ($rootScope.workProject != null && $rootScope.workProject.id == scope.data.id) {
                                                makeTag += "<span class='titlenameSelect cursor table-word-break-all sub-line' ng-click='event.changeLastProject(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                                // makeTag += "<span class='titlenameSelect cursor table-word-break-all sub-line' ng-click='event.changeLastProject(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                                makeTag += "<span class='titlenameSelect cursor table-word-break-all sub-line' ng-click='event.moveIssue(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                                // makeTag += "<span class='titlenameSelect cursor table-word-break-all sub-line' ng-click='event.changeDetailView(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                            } else {
                                                makeTag += "<span class='titlename cursor table-word-break-all sub-line' ng-click='event.changeLastProject(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                                // makeTag += "<span class='titlename cursor table-word-break-all sub-line' ng-click='event.changeLastProject(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                                makeTag += "<span class='titlename cursor table-word-break-all sub-line' ng-click='event.moveIssue(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                                // makeTag += "<span class='titlename cursor table-word-break-all sub-line' ng-click='event.changeDetailView(data.id)'>" + myToken + scope.data.name.replace(/</gi, '&lt;') + "</span>";
                                            }
                                            break;
@@ -425,8 +429,8 @@
                                            makeTag += '<span class="number-tag">' + scope.data.projectKey + ' - ' + scope.data.issueNumber + '</span>';
                                            makeTag += ' <span class="tag"> / </span> ';
                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.issueStatusColor + "\"," + "\"border-color\"" + " : \"" + scope.data.issueStatusColor + "\", \"color\": \"#FFFFFF\" }'>" + scope.data.issueStatusName + "</span>";
                                            makeTag += ' <span class="tag"> / </span> ';
                                            makeTag += '<span class="tag">' + scope.data.projectName + '</span>';
                                            // makeTag += ' <span class="tag"> / </span> ';
                                            // makeTag += '<span class="tag">' + scope.data.projectName + '</span>';
                                            makeTag += '</div>';
                                            makeTag += '<div class="titlename cursor text-left" ng-click="event.changeDetailView(data.id)">' + scope.data.title.replace(/</gi, '&lt;') + '</div>';
                                            makeTag += '<div class="extra-infodiv text-left">';
src/main/webapp/scripts/app/issue/issueDetail.controller.js
@@ -46,8 +46,8 @@
                $scope.fn.addDownIssue = addDownIssue;      // 하위 이슈 추가
                $scope.fn.addRelationIssueTableConfig = addRelationIssueTableConfig;      // 연관 이슈 목록 테이블 설정
                $scope.fn.addDownIssueTableConfig = addDownIssueTableConfig;      // 하위 이슈 목록 테이블 설정
                $scope.fn.getRelTableConfigs = getRelTableConfigs;
                $scope.fn.getDownTableConfigs = getDownTableConfigs;
                $scope.fn.setRelTableConfigs = setRelTableConfigs;
                $scope.fn.setDownTableConfigs = setDownTableConfigs;
                $scope.fn.containsPartner = containsPartner;
                $scope.fn.onActivate = onActivate;
@@ -137,8 +137,6 @@
                $scope.$on("getIssueList", function () {
                    $scope.fn.getIssueDetail();
                });
                // 하위 이슈 삭제
                function removeDownIssue(id) {
@@ -490,125 +488,95 @@
                    });
                }
                // 연관 이슈 상세 조회
                function getRelTableConfigs() {
                    var content = {
                        issueId : $scope.vm.viewer.id,
                        issueTypeId : $rootScope.getCurrentIssueTypeId()
                    };
                // 연관 이슈 테이블 설정
                function setRelTableConfigs(issueTableConfigVo) {
                    var issueTableConfigs = issueTableConfigVo.issueTableConfigs;
                    IssueTableConfig.relationDetail($resourceProvider.getContent(
                        content,
                        $resourceProvider.getPageContent(0, 0))).then(function (result) {
                    //  연관 슈 목록 테이블 설정 값을 가져와서 적용한다.
                    if ($rootScope.isDefined(issueTableConfigs)) {
                        //  이슈 테이블 설정 정보를 저장 한다.
                        if (result.data.message.status === "success") {
                            var issueTableConfigs = result.data.data;
                        $scope.vm.issueRelTableConfigs = [];
                        $scope.vm.issueRelTableConfigs = JSON.parse(issueTableConfigs);
                        $scope.vm.issueRelTableConfigs.sort(function (a, b) {
                            return a.position < b.position ? -1 : a.position > b.position ? 1 : 0;
                        });
                            //  연관 슈 목록 테이블 설정 값을 가져와서 적용한다.
                            if ($rootScope.isDefined(issueTableConfigs)) {
                                //  이슈 테이블 설정 정보를 저장 한다.
                        $scope.vm.relTableConfigs = [];
                        $scope.vm.relTableConfigs.push($tableProvider.config()
                            .setHName("issue.relationIssueType")
                            .setDType("renderer")
                            .setDAlign("text-center")
                            .setHWidth("width-30-p bold")
                            .setHSort(false)
                            .setDRenderer("ISSUE_RELATION_TYPE"))
                        $scope.vm.relTableConfigs.push($tableProvider.config()
                            .setHName("issue.relationIssueTitle")
                            .setDType("renderer")
                            .setDAlign("text-center")
                            .setHWidth("width-60-p bold")
                            .setHSort(false)
                            .setDRenderer("ISSUE_RELATION_MOVE"))
                        angular.forEach($scope.vm.issueRelTableConfigs, function (Rel_issueTableConfig) {
                            //  표시 대상인 컬럼만 화면에 그려준다.
                            if (Rel_issueTableConfig.display) {
                                //  테이블의 컬럼을 만들어준다.
                                $scope.fn.setRelTableColumn(Rel_issueTableConfig);
                                $scope.vm.issueRelTableConfigs = [];
                                $scope.vm.issueRelTableConfigs = JSON.parse(issueTableConfigs);
                                $scope.vm.issueRelTableConfigs.sort(function (a, b) {
                                    return a.position < b.position ? -1 : a.position > b.position ? 1 : 0;
                                });
                                $scope.vm.relTableConfigs = [];
                                $scope.vm.relTableConfigs.push($tableProvider.config()
                                    .setHName("issue.relationIssueType")
                                    .setDType("renderer")
                                    .setDAlign("text-center")
                                    .setHWidth("width-30-p bold")
                                    .setHSort(false)
                                    .setDRenderer("ISSUE_RELATION_TYPE"))
                                $scope.vm.relTableConfigs.push($tableProvider.config()
                                    .setHName("issue.relationIssueTitle")
                                    .setDType("renderer")
                                    .setDAlign("text-center")
                                    .setHWidth("width-60-p bold")
                                    .setHSort(false)
                                    .setDRenderer("ISSUE_RELATION_MOVE"))
                                angular.forEach($scope.vm.issueRelTableConfigs, function (Rel_issueTableConfig) {
                                    //  표시 대상인 컬럼만 화면에 그려준다.
                                    if (Rel_issueTableConfig.display) {
                                        //  테이블의 컬럼을 만들어준다.
                                        $scope.fn.setRelTableColumn(Rel_issueTableConfig);
                                    }
                                });
                                $scope.vm.relTableConfigs.push($tableProvider.config()
                                    .setHName("issue.relationIssueDelete")
                                    .setDType("renderer")
                                    .setHWidth("width-20-p bold")
                                    .setDRenderer("ISSUE_RELATION_DELETE")
                                    .setHSort(false)
                                    .setDAlign("text-center"))
                            } else {
                                makeRelTableConfigs();
                            }
                        });
                        $scope.vm.relTableConfigs.push($tableProvider.config()
                            .setHName("issue.relationIssueDelete")
                            .setDType("renderer")
                            .setHWidth("width-20-p bold")
                            .setDRenderer("ISSUE_RELATION_DELETE")
                            .setHSort(false)
                            .setDAlign("text-center"))
                        } else {
                            SweetAlert.swal($filter("translate")("issue.errorRemovableIssueStatusList"), result.data.message.message, "error"); // "이동 가능한 이슈 상태 목록 조회 오류"
                        }
                    });
                    } else {
                        makeRelTableConfigs();
                    }
                }
                // 하위 이슈 상세 조회
                function getDownTableConfigs() {
                    var content = {
                        issueId : $scope.vm.viewer.id,
                        issueTypeId : $rootScope.getCurrentIssueTypeId()
                    };
                // 하위 이슈 상세 조회 결과 설정
                function setDownTableConfigs(issueTableConfigVo) {
                    var issueTableConfigs = issueTableConfigVo.issueTableConfigs;
                    IssueTableConfig.downDetail($resourceProvider.getContent(
                        content,
                        $resourceProvider.getPageContent(0, 0))).then(function (result) {
                        if (result.data.message.status === "success") {
                            var issueTableConfigs = result.data.data;
                            //  연관 슈 목록 테이블 설정 값을 가져와서 적용한다.
                            if ($rootScope.isDefined(issueTableConfigs)) {
                                //  이슈 테이블 설정 정보를 저장 한다.
                                $scope.vm.issueDownTableConfigs = [];
                                $scope.vm.issueDownTableConfigs = JSON.parse(issueTableConfigs);
                                $scope.vm.issueDownTableConfigs.sort(function (a, b) {
                                    return a.position < b.position ? -1 : a.position > b.position ? 1 : 0;
                                });
                                $scope.vm.downTableConfigs = [];
                                $scope.vm.downTableConfigs.push($tableProvider.config()
                                    .setHName("issue.downIssueTitle")
                                    .setDType("renderer")
                                    .setDAlign("text-center")
                                    .setHWidth("width-60-p bold")
                                    .setHSort(false)
                                    .setDRenderer("ISSUE_DOWN_MOVE"))
                                angular.forEach($scope.vm.issueDownTableConfigs, function (Down_issueTableConfig) {
                                    //  표시 대상인 컬럼만 화면에 그려준다.
                                    if (Down_issueTableConfig.display) {
                                        //  테이블의 컬럼을 만들어준다.
                                        $scope.fn.setDownTableColumn(Down_issueTableConfig);
                                    }
                                });
                                $scope.vm.downTableConfigs.push($tableProvider.config()
                                    .setHName("issue.relationIssueDelete")
                                    .setDType("renderer")
                                    .setHWidth("width-20-p bold")
                                    .setDRenderer("ISSUE_DOWN_DELETE")
                                    .setHSort(false)
                                    .setDAlign("text-center"))
                            } else {
                                makeDownTableConfigs();
                    //  연관 슈 목록 테이블 설정 값을 가져와서 적용한다.
                    if ($rootScope.isDefined(issueTableConfigs)) {
                        //  이슈 테이블 설정 정보를 저장 한다.
                        $scope.vm.issueDownTableConfigs = [];
                        $scope.vm.issueDownTableConfigs = JSON.parse(issueTableConfigs);
                        $scope.vm.issueDownTableConfigs.sort(function (a, b) {
                            return a.position < b.position ? -1 : a.position > b.position ? 1 : 0;
                        });
                        $scope.vm.downTableConfigs = [];
                        $scope.vm.downTableConfigs.push($tableProvider.config()
                            .setHName("issue.downIssueTitle")
                            .setDType("renderer")
                            .setDAlign("text-center")
                            .setHWidth("width-60-p bold")
                            .setHSort(false)
                            .setDRenderer("ISSUE_DOWN_MOVE"))
                        angular.forEach($scope.vm.issueDownTableConfigs, function (Down_issueTableConfig) {
                            //  표시 대상인 컬럼만 화면에 그려준다.
                            if (Down_issueTableConfig.display) {
                                //  테이블의 컬럼을 만들어준다.
                                $scope.fn.setDownTableColumn(Down_issueTableConfig);
                            }
                        });
                        $scope.vm.downTableConfigs.push($tableProvider.config()
                            .setHName("issue.relationIssueDelete")
                            .setDType("renderer")
                            .setHWidth("width-20-p bold")
                            .setDRenderer("ISSUE_DOWN_DELETE")
                            .setHSort(false)
                            .setDAlign("text-center"))
                        }
                        else {
                            SweetAlert.swal($filter("translate")("issue.errorRemovableIssueStatusList"), result.data.message.message, "error"); // "이동 가능한 이슈 상태 목록 조회 오류"
                        }
                    });
                    } else {
                        makeDownTableConfigs();
                    }
                }
                // 연관 이슈 추가
@@ -665,17 +633,22 @@
                        }
                    });
                }
                //  이슈명을 클릭하면 이슈 상세 정보를 조회한다.
                //  $rootScope.$on("getIssueDetail", function (event, args) {
                //      $scope.vm.viewer.id = args["id"];
                //      $scope.fn.getIssueDetail();
                //  });
                // todo 모르겠다.....
                 // 이슈명을 클릭하면 이슈 상세 정보를 조회한다.
                 // $rootScope.$on("getIssueDetail", function (event, args) {
                 //     $scope.vm.viewer.id = args["id"];
                 //     $scope.fn.getIssueDetail();
                 // });
                
                // 이메일 보낸후 상세화면 갱신
                 $rootScope.$on("getIssueDetail", function (event, args) {
                     $scope.fn.getIssueDetail();
                 });
                // todo 이거 뭐지...
                //  $rootScope.$on("getIssueDetail", function (event, args) {
                //      $scope.fn.getIssueDetail();
                //  });
                $scope.$on("getIssueDetail", function (event, args) {
                    $scope.fn.getIssueDetail();
                });
                //  이슈명을 클릭하면 이슈 상세 정보를 조회한다.
                // $scope.$on("getIssueDetail", function (event, args) {
@@ -683,6 +656,7 @@
                //     $scope.fn.getIssueDetail();
                // });
                // todo 이건 또 뭐지
                $scope.$watch(function() {
                    return $rootScope.currentDetailIssueId;
                }, function() {
@@ -691,6 +665,8 @@
                        $scope.fn.getIssueDetail();
                    }
                }, true);
                //  초기화 해야할 할목을 지정하여 다른 이슈를 클릭할 때 초기화해준다.
                function initReload() {
@@ -865,8 +841,8 @@
                    //  초기화 해야할 할목을 지정하여 다른 이슈를 클릭할 때 초기화해준다.
                    $scope.fn.initReload();
                    $scope.fn.getRelTableConfigs();
                    $scope.fn.getDownTableConfigs();
                    // $scope.fn.getRelTableConfigs();
                    // $scope.fn.getDownTableConfigs();
                        Issue.detail($resourceProvider.getContent(
                        {id : $scope.vm.viewer.id, deep : "02"},
@@ -874,6 +850,7 @@
                        if (result.data.message.status === "success") {
                            if (angular.isDefined(result.data.data)) {
                                $scope.vm.viewer = angular.copy(result.data.data);
                                //  이슈 이미지 미리 보기 만들기
                                $scope.fn.makePreviewImages(result.data.data.attachedFileVos);
@@ -894,6 +871,10 @@
                                $scope.vm.form.issues.push(result.data.data);
                                $scope.vm.form.issuesDown = [];
                                $scope.vm.form.issuesDown.push(result.data.data);
                                var issueTableConfigVos = result.data.data.issueTableConfigVos;
                                $scope.fn.setRelTableConfigs(issueTableConfigVos[0]);
                                $scope.fn.setDownTableConfigs(issueTableConfigVos[1]);
                                // 연관 이슈 반복문
                                if (result.data.data.issueRelationVos !== null) {
@@ -917,7 +898,6 @@
                                }
                                $scope.vm.viewer.issueRelationVos = result.data.data.issueRelationVos;
                                $scope.vm.viewer.issueDownVos = result.data.data.issueDownVos;
                                if ($rootScope.workProject.id > -1 && result.data.data.projectVo !== null) {
                                    $rootScope.changeLastProject(result.data.data.projectVo.id);
src/main/webapp/scripts/app/project/projectList.controller.js
@@ -69,7 +69,9 @@
                $scope.tableEvent = {
                    modify : modify,
                    projectCustomFieldConfig : projectCustomFieldConfig,
                    changeLastProject : changeLastProject
                    changeLastProject : changeLastProject,
                    moveIssue : moveIssue,
                    changeDetailView : changeDetailView
                };
                angular.extend(this, $controller('autoCompleteController', {$scope : $scope, $injector : $injector}));
@@ -364,6 +366,21 @@
                    $rootScope.changeLastProject(projectId);
                }
                function moveIssue(projectId) {
                    // $rootScope.currentDetailIssueId = null;
                    // 이슈 번호를 저장한 후 이슈 목록으로 이동한다.
                    // $rootScope.$broadcast("makeIssueSearch", {id :$rootScope.currentDetailIssueId});
                    //$rootScope.$broadcast("makeIssueSearch", {id :$rootScope.issueTypeMenu.id});
                    $rootScope.$broadcast("makeIssueSearch",projectId);
                }
                function changeDetailView() {
                    // $rootScope.$broadcast("getIssueDetail", {id: $rootScope.currentDetailIssueId});
                    //$scope.$broadcast("getIssueDetail", {id: $rootScope.currentDetailIssueId});
                    // $rootScope.$broadcast("getIssueDetail", {id :$rootScope.currentDetailIssueId});
                    //$rootScope.$broadcast("getIssueList", {id :$rootScope.issueTypeMenu.id});
                }
                $scope.fn.makeTableConfigs();
                $scope.fn.getPageList(0);
            }
src/main/webapp/views/issue/issueDetail.html
@@ -493,7 +493,7 @@
                                            type : '', maxlength : 200, autoResize : true, stopRemoveBodyEvent : true }"></js-autocomplete-single>
                        </div>
                        <div class="col-auto vertical-middle" style="display: flex">
                            <button type="button" class="btn btn-primary form-control input-sm"
                            <button type="button" class="btn btn-primary"
                                    ng-click="fn.addRelationIssue()"
                                    translate="issue.addRelationIssue">연관 이슈 추가</button>
                            <button type="button" class="btn btn-sm btn-primary btn-roundRel  offset-1"
@@ -510,8 +510,10 @@
                        <span class="info_detail_font h3" translate="issue.downIssue">하위 이슈</span>
                    </div>
                    <div class="col-sm-2">
                        <button type="button" class="btn btn-darkgrey form-control input-sm offset-5" ng-click="fn.modifyDownIssueStatus()"
                                translate="common.updateDownIssueAllStatus">하위이슈 상태 전체 변경</button>
                        <a><button type="button" class="btn btn-darkgrey offset-7"
                                ng-click="fn.modifyDownIssueStatus()">
                                <span translate="common.updateDownIssueAllStatus">하위이슈 상태 전체 변경</span>
                        </button></a>
                    </div>
                    <div class="col-sm-1">
                        <button class="btn btn-darkgrey offset-10"  ng-click="fn.addDownIssueTableConfig()" type="button"><span translate="issue.settingTableDisplay">테이블 표시 설정</span></button>
@@ -538,7 +540,7 @@
                                            type : '', maxlength : 200, autoResize : true, stopRemoveBodyEvent : true }"></js-autocomplete-single>
                        </div>
                        <div class="col-auto vertical-middle" style="display: flex">
                            <button type="button" class="btn btn-primary form-control input-sm"
                            <button type="button" class="btn btn-primary"
                                    ng-click="fn.addDownIssue()"
                                    translate="issue.addDownIssue">추가</button>
                        </div>