/**
|
* Created by wisestone on 2018-05-10.
|
*/
|
'use strict';
|
|
define(['app', 'angular', 'd3'],
|
function (app, angular, d3) {
|
app.directive('jsWorkflow', ["$log", "$rootScope", "IssueStatus", "$resourceProvider", "SweetAlert", '$filter', '$injector', '$controller',
|
function ($log, $rootScope, IssueStatus, $resourceProvider, SweetAlert, $filter, $injector, $controller) {
|
return {
|
scope : {
|
issueStatusList : '=issueStatusList',
|
vm : '=ngModel',
|
isolationWorkflow : '=isolationWorkflow',
|
firstStatusExist : "=firstStatusExist",
|
middleStatusExist : "=middleStatusExist",
|
lastStatusExist : "=lastStatusExist",
|
departments : "=departments"
|
},
|
restrict : 'E',
|
replace : true,
|
templateUrl : 'custom_components/js-workflow/js-workflow.html',
|
controller : function ($scope, $element, $attrs) {
|
// 함수 모음
|
$scope.fn = {
|
initializeLayout : initializeLayout, // 기초 레이아웃을 설정한다.
|
makeNodeAndLink : makeNodeAndLink, // 노드와 링크 정보를 생성한다.
|
forceLayout : forceLayout, // 워크플로우 다이어그램을 그린다.
|
initCircleColor : initCircleColor, // 노드 색상을 초기화한다.
|
initLinkColor : initLinkColor, // 전이선 색상을 초기화한다.
|
makeNode : makeNode, // 노드를 만든다.
|
makeLink : makeLink, // 전이선을 만든다.
|
makeDragEvent : makeDragEvent, // 드래그 이벤트 생성
|
makeGridLine : makeGridLine, // 표시되는 선을 생성한다.
|
executeForce : executeForce, // 포스 레이아웃 실행
|
setLink : setLink, // 링크를 셋팅한다.
|
setNode : setNode, // 노드를 셋팅한다.
|
setNodeTexts : setNodeTexts, // 노드 텍스트를 셋팅한다.
|
targetClick : targetClick, // 타겟을 클릭했을 때 색상을 변경한다.
|
tick : tick, // 다이어그램에서 도형을 움직일 때 실행
|
startDiagram : startDiagram, // 다이어그램을 그리기 시작한다.
|
getIssueStatusList : getIssueStatusList, // 전체 이슈 상태를 가져온다.
|
addStatus : addStatus, // 이슈 상태를 다이어그램에 추가한다.
|
addTransition : addTransition, // 전이선을 다이어그램에 추가한다.
|
getAvailableStartStatus : getAvailableStartStatus, // 시작하는 이슈 상태의 선택 목록을 가져온다.
|
getAvailableNextStatus : getAvailableNextStatus, // 이동할 이슈 상태의 선택 목록을 가져온다.
|
setLastStatusPosition : setLastStatusPosition, // 상태 마지막 위치 정보 저장
|
setLastTransitionPosition : setLastTransitionPosition, // 전이선 마지막 위치 정보 저장
|
updateClientChangePosition : updateClientChangePosition, // 클라이언트에서 변경한 위치 정보를 클라이언트에서 관리하는 데이터에 업데이트한다.
|
checkIsolationWorkflowAndExceptionGenerate : checkIsolationWorkflowAndExceptionGenerate, // 고립된 이슈 상태가 없는 워크플로우인지 확인한다. - 고립되었을 경우에는 저장 금지
|
checkRequireIssueStatusTypeExceptionGenerate : checkRequireIssueStatusTypeExceptionGenerate, // 상태 속성 별로 1개씩 존재하는지 확인한다. - 부족할 경우 저장 금지
|
generatorTransitionId : generatorTransitionId, // 전이선 임시 id 얻기
|
removeWorkflowTransition : removeWorkflowTransition, // 전이선을 삭제한다.
|
removeIssueStatus : removeIssueStatus, // 이슈 상태를 제거한다.
|
defaultAddIssueStatusId : defaultAddIssueStatusId, // 이슈 상태 추가 셀렉트 박스에서 첫번째 옵션 항목을 기본 선택되게 한다.
|
checkReadyType : checkReadyType, // 다이어그램안에 상태 속성 '대기'인 이슈 상태가 존재하는지 확인한다.
|
getOptionColor : getOptionColor, // 이슈 상태의 색상 정보를 가져온다.
|
changeDepartment : changeDepartment,
|
removeDepartment : removeDepartment
|
};
|
|
// 변수 모음
|
$scope.vm.targetIssueStatusList = [];
|
$scope.vm.issueStatuses = []; // 선택 가능한 이슈 상태 목록
|
$scope.vm.addIssueStatusId = null; // 추가하는 이슈 상태 아이디
|
$scope.vm.issueStatusVos = []; // 화면에 있는 이슈 상태 목록
|
$scope.vm.width = 0; // 넓이
|
$scope.vm.height = 0; // 높이
|
$scope.vm.svg = null; // svg 정보
|
$scope.vm.rect = null; // 다이어그램이 그려지는 사각형 정보
|
$scope.vm.nodes = []; // 원 정보 모음
|
$scope.vm.links = []; // 링크 정보 모음
|
$scope.vm.activeTarget = null; // 현재 선택한 대상 정보
|
$scope.vm.drag = null;
|
$scope.vm.force = null;
|
$scope.vm.node = null;
|
$scope.vm.link = null;
|
$scope.vm.nodeTexts = null; // 원에 표시되는 텍스트
|
$scope.vm.sourceStatusId = null; // 출발하는 이슈 상태 아이디
|
$scope.vm.targetStatusId = null; // 연결하는 이슈 상태 아이디
|
$scope.vm.transitionIdGenerator = 100; // 전이선 임시 id 값
|
|
$scope.vm.departments = []; // 부서 목록
|
$scope.vm.departmentName = "" // 선택된 부서 이름
|
|
angular.extend(this, $controller('autoCompleteController', {$scope : $scope, $injector : $injector}));
|
|
$scope.$watch("issueStatusList", function (newValue) {
|
if ($rootScope.isDefined(newValue)) {
|
$scope.fn.startDiagram();
|
setWorkflowDepartments();
|
}
|
})
|
|
// 저장된 담당부서 설정하기
|
function setWorkflowDepartments() {
|
var workflowDepartmentVos = [];
|
|
angular.forEach($scope.issueStatusList, function (issueStatus) {
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
if (issueStatusVo.id === issueStatus.id) {
|
var issueStatusWorkflowDepartments = {
|
workflowDepartmentVos : workflowDepartmentVos,
|
workflowDepartmentName : ""
|
}
|
issueStatusVo = Object.assign(issueStatusVo, issueStatusWorkflowDepartments);
|
}
|
});
|
})
|
}
|
|
// 이슈 상태 추가 셀렉트 박스에서 첫번째 옵션 항목을 기본 선택되게 한다.
|
function defaultAddIssueStatusId() {
|
if ($scope.vm.issueStatuses.length > 0) {
|
$scope.vm.addIssueStatusId = String($scope.vm.issueStatuses[0].id);
|
}
|
else {
|
$scope.vm.addIssueStatusId = null;
|
}
|
}
|
|
// 다이어그램을 그리기 시작한다.
|
function startDiagram() {
|
$("#svgContent").empty();
|
|
$scope.fn.getIssueStatusList();
|
|
$scope.vm.width = $(".workflowbox").width(); // 넓이
|
$scope.vm.height = $(".workflowbox").height(); // 높이
|
|
$scope.vm.activeTarget = null; // 타겟 초기화
|
$scope.fn.initCircleColor(null, true); // 마지막 선택 노드 색상 초기화
|
$scope.fn.forceLayout(); // 다이어그램 에디터 그리기 시작
|
$scope.fn.checkRequireIssueStatusTypeExceptionGenerate(); // 상태 속성 별로 1개씩 존재하는지 확인한다. - 부족할 경우 저장 금지
|
$scope.fn.checkIsolationWorkflowAndExceptionGenerate(); // 고립된 이슈 상태가 없는 워크플로우인지 확인한다. - 고립되었을 경우에는 저장 금지
|
}
|
|
// 전이선 임시 id 얻기
|
function generatorTransitionId() {
|
return "transition" + $scope.vm.transitionIdGenerator++;
|
}
|
|
// 상태 마지막 위치 정보 저장
|
function setLastStatusPosition(issueStatus) {
|
issueStatus.xLocation = $scope.vm.nodes[issueStatus.name].x;
|
issueStatus.yLocation = $scope.vm.nodes[issueStatus.name].y;
|
}
|
|
// 전이선 마지막 위치 정보 저장
|
function setLastTransitionPosition(workflowTransition, index) {
|
workflowTransition.correctX = $scope.vm.links[index].correctX;
|
workflowTransition.correctY = $scope.vm.links[index].correctY;
|
workflowTransition.direct = $scope.vm.links[index].direct;
|
}
|
|
// 클라이언트에서 변경한 위치 정보를 클라이언트에서 관리하는 데이터에 업데이트한다.
|
function updateClientChangePosition() {
|
// 서버에서 내려온 데이터에 전이선 변경 정보를 업데이트한다.
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
angular.forEach(issueStatusVo.workflowTransitionVos, function (workflowTransition) {
|
for (var count in $scope.vm.links) {
|
if ($scope.vm.links[count].id == workflowTransition.id) {
|
$scope.fn.setLastTransitionPosition(workflowTransition, count);
|
break;
|
}
|
}
|
});
|
|
$scope.fn.setLastStatusPosition(issueStatusVo);
|
});
|
}
|
|
// 시작하는 이슈 상태의 선택 목록을 가져온다.
|
function getAvailableStartStatus() {
|
var availableStatus = [];
|
|
angular.forEach($scope.vm.issueStatusVos, function (startStatus) {
|
if ($scope.vm.targetStatusId != startStatus.id) {
|
availableStatus.push(startStatus);
|
}
|
});
|
|
availableStatus.sort(function (a, b) {
|
return a.name > b.name ? 1 : 0
|
});
|
|
return availableStatus;
|
}
|
|
// 이동할 이슈 상태의 선택 목록을 가져온다.
|
function getAvailableNextStatus() {
|
$scope.vm.targetIssueStatusList = [];
|
$scope.vm.targetStatusId = null;
|
|
if (!$rootScope.isDefined($scope.vm.sourceStatusId)) {
|
return;
|
}
|
|
var excludeIds = [];
|
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatus) {
|
// 선택한 노드가 있을 때
|
if ($scope.vm.sourceStatusId == issueStatus.id) {
|
angular.forEach(issueStatus.workflowTransitionVos, function (workflowTransition) {
|
excludeIds.push(workflowTransition.targetStatusId);
|
});
|
}
|
});
|
|
// 현재 선택한 대상도 제외대상에 추가
|
excludeIds.push($scope.vm.sourceStatusId);
|
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatus) {
|
var notAvailable = false;
|
|
for (var count in excludeIds) {
|
if (excludeIds[count] == issueStatus.id) {
|
notAvailable = true;
|
break;
|
}
|
}
|
|
if (!notAvailable) {
|
$scope.vm.targetIssueStatusList.push(issueStatus);
|
}
|
});
|
|
$scope.vm.targetIssueStatusList.sort(function (a, b) {
|
return a.name > b.name ? 1 : 0
|
});
|
}
|
|
// 다이어그램안에 상태 속성 '대기'인 이슈 상태가 존재하는지 확인한다.
|
function checkReadyType(addIssueStatusId) {
|
var addIssueStatus = null;
|
|
for (var count in $scope.vm.issueStatuses) {
|
if ($scope.vm.issueStatuses[count].id == addIssueStatusId) {
|
addIssueStatus = $scope.vm.issueStatuses[count];
|
break;
|
}
|
}
|
|
$log.debug("$scope.vm.issueStatuses 확인 : ", $scope.vm.issueStatuses);
|
$log.debug("$scope.vm.issueStatusVos 확인 : ", $scope.vm.issueStatusVos);
|
$log.debug("addIssueStatusId 확인 : ", addIssueStatusId);
|
$log.debug("addIssueStatus 확인 : ", addIssueStatus);
|
|
// 추가하려는 이슈 상태의 상태 속성 확인
|
if ($rootScope.isDefined(addIssueStatus)) {
|
if (addIssueStatus.issueStatusType === "READY") {
|
var readyCount = 0;
|
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
if (issueStatusVo.issueStatusType === "READY") {
|
readyCount++;
|
}
|
});
|
|
$log.debug("readyCount 확인 : ", readyCount);
|
|
if (readyCount > 0) {
|
return false;
|
}
|
}
|
}
|
|
return true;
|
}
|
|
// 이슈 상태의 색상 정보를 가져온다
|
function getOptionColor(list, id) {
|
var color = "#353535"; // 기본색은 검은색.
|
|
for (var count in list) {
|
if (String(list[count].id) === id) {
|
color = list[count].color;
|
break;
|
}
|
}
|
|
return color;
|
}
|
|
|
// 이슈 상태를 다이어그램에 추가한다.
|
function addStatus() {
|
if ($rootScope.isDefined($scope.vm.addIssueStatusId)) {
|
// 클라이언트 변경 사항 $scope.vm.issueStatusVos 에 저장
|
$scope.fn.updateClientChangePosition();
|
|
// 상태 속성이 대기인 이슈 상태가 이미 존재할 경우에는 상태를 넣을 수 없어야 한다.
|
if (!$scope.fn.checkReadyType($scope.vm.addIssueStatusId)) {
|
SweetAlert.error($filter("translate")("managementWorkflow.failedToPutIssueStatus"), $filter("translate")("managementWorkflow.onlyOneInTheWorkflow")); // "이슈 상태 넣기 실패", "워크플로우에는 상태 속성 '대기'인 이슈는 1개만 존재해야 합니다."
|
return;
|
}
|
|
// 추가할 이슈 상태를 넣는다.
|
for (var count in $scope.vm.issueStatuses) {
|
var issueStatus = $scope.vm.issueStatuses[count];
|
|
if (issueStatus.id == $scope.vm.addIssueStatusId) {
|
issueStatus.workflowTransitionVos = [];
|
issueStatus.xLocation = Math.random() * ($scope.vm.width - 50);
|
issueStatus.yLocation = Math.random() * ($scope.vm.height - 50);
|
|
$scope.vm.issueStatusVos.push(issueStatus);
|
|
// 담당 부서 추가
|
addWorkflowDepartment(issueStatus);
|
break;
|
}
|
}
|
// 이슈 상태를 추가하면 시작점을 초기화해준다. -> 종점 업데이트를 위해
|
$scope.vm.sourceStatusId = null;
|
|
$scope.fn.defaultAddIssueStatusId();
|
$scope.fn.startDiagram();
|
|
|
}
|
}
|
|
// 이슈 상태를 제거한다.
|
function removeIssueStatus(targetIssueStatus) {
|
$scope.fn.initCircleColor(null, true); // 전체 노드 색상 초기화
|
|
var tempWorkflowStatusVos = [];
|
|
// 삭제 대상 상태 제거
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
if (issueStatusVo.id != targetIssueStatus.id) {
|
$scope.fn.setLastStatusPosition(issueStatusVo);
|
tempWorkflowStatusVos.push(issueStatusVo);
|
}
|
});
|
|
$scope.vm.issueStatusVos = angular.copy(tempWorkflowStatusVos);
|
|
// 삭제 대상 상태 정보를 갖고있는 전이 제거
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
var tempWorkflowTransitionVos = [];
|
|
angular.forEach(issueStatusVo.workflowTransitionVos, function (workflowTransition) {
|
if (workflowTransition.sourceStatusId != targetIssueStatus.id && workflowTransition.targetStatusId != targetIssueStatus.id) {
|
// 보정 값 업데이트 하기
|
for (var count in $scope.vm.links) {
|
if ($scope.vm.links[count].id == workflowTransition.id) {
|
$scope.fn.setLastTransitionPosition(workflowTransition, count);
|
break;
|
}
|
}
|
|
tempWorkflowTransitionVos.push(workflowTransition);
|
}
|
});
|
|
issueStatusVo.workflowTransitionVos = tempWorkflowTransitionVos;
|
});
|
|
// 선택 대상 정보 초기화
|
$scope.vm.activeTarget = null;
|
|
// 클라이언트 변경 사항 $scope.vm.issueStatusVos 에 저장
|
$scope.fn.updateClientChangePosition();
|
|
$scope.fn.startDiagram();
|
|
removeWorkflowDepartment(targetIssueStatus);
|
}
|
|
// 전이선을 다이어그램에 추가한다.
|
function addTransition() {
|
if (!$rootScope.isDefined($scope.vm.targetStatusId)) {
|
return;
|
}
|
|
// 클라이언트 변경 사항 $scope.vm.issueStatusVos 에 저장
|
$scope.fn.updateClientChangePosition();
|
|
var insertWorkflowTransition = {
|
id : $scope.fn.generatorTransitionId(),
|
sourceStatusId : "",
|
sourceStatusName : "",
|
targetStatusId : "",
|
targetStatusName : "",
|
correctX : null,
|
correctY : null,
|
direct : false,
|
type : "02",
|
colorClass : "linkBlue",
|
workflowTransition : null
|
};
|
|
// source, target 이름 추출
|
for (var count in $scope.vm.issueStatusVos) {
|
var issueStatus = $scope.vm.issueStatusVos[count];
|
|
if ($scope.vm.sourceStatusId == issueStatus.id) {
|
insertWorkflowTransition.sourceStatusId = issueStatus.id;
|
insertWorkflowTransition.sourceStatusName = issueStatus.name;
|
}
|
|
if ($scope.vm.targetStatusId == issueStatus.id) {
|
insertWorkflowTransition.targetStatusId = issueStatus.id;
|
insertWorkflowTransition.targetStatusName = issueStatus.name;
|
}
|
}
|
|
for (var count in $scope.vm.issueStatusVos) {
|
var issueStatus = $scope.vm.issueStatusVos[count];
|
|
if ($scope.vm.sourceStatusId == issueStatus.id) {
|
issueStatus.workflowTransitionVos.push(insertWorkflowTransition);
|
|
$scope.vm.sourceStatusId = null;
|
$scope.vm.targetStatusId = null;
|
|
$scope.fn.startDiagram();
|
break;
|
}
|
}
|
}
|
|
// 전이선을 삭제한다.
|
function removeWorkflowTransition(targetWorkflowTransition) {
|
$scope.fn.initLinkColor(null, true); // 전이선 색상 전체 초기화
|
|
// 삭제 대상 전이 제거
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatus) {
|
var tempWorkflowTransitionVos = [];
|
|
issueStatus.xLocation = $scope.vm.nodes[issueStatus.name].x;
|
issueStatus.yLocation = $scope.vm.nodes[issueStatus.name].y;
|
|
angular.forEach(issueStatus.workflowTransitionVos, function (workflowTransition) {
|
|
if (workflowTransition.id != targetWorkflowTransition.id) {
|
// 보정 값 업데이트 하기
|
for (var count in $scope.vm.links) {
|
if ($scope.vm.links[count].id == workflowTransition.id) {
|
workflowTransition.correctX = $scope.vm.links[count].correctX;
|
workflowTransition.correctY = $scope.vm.links[count].correctY;
|
workflowTransition.direct = $scope.vm.links[count].direct;
|
break;
|
}
|
}
|
|
tempWorkflowTransitionVos.push(workflowTransition);
|
}
|
});
|
|
issueStatus.workflowTransitionVos = tempWorkflowTransitionVos;
|
});
|
|
// 선택 대상 정보 초기화
|
$scope.vm.activeTarget = null;
|
|
// 클라이언트 변경 사항 $scope.vm.issueStatusVos 에 저장
|
$scope.fn.updateClientChangePosition();
|
|
$scope.fn.startDiagram();
|
}
|
|
// 고립된 이슈 상태가 없는 워크플로우인지 확인한다. - 고립되었을 경우에는 저장 금지
|
function checkIsolationWorkflowAndExceptionGenerate() {
|
var firstCheck = false;
|
var middleCheck = false;
|
var lastCheck = true;
|
var connectCheck = false; // source/target 연결 찾을 경우 값 변경
|
var transitionList = [];
|
|
// 전체 전이선을 추출한다.
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
angular.forEach(issueStatusVo.workflowTransitionVos, function (workflowTransition) {
|
transitionList.push(workflowTransition);
|
});
|
});
|
|
// 시작 상태 정상 확인
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
|
switch (issueStatusVo.issueStatusType) {
|
case "READY" :
|
for (var count in transitionList) {
|
if (transitionList[count].sourceStatusId == issueStatusVo.id) {
|
firstCheck = true;
|
break;
|
}
|
}
|
break;
|
|
case "OPEN" :
|
var normalSourceCheck = false; // 일반 상태일 때 source 연결 여부 확인
|
var normalTargetCheck = false; // 일반 상태일 때 target 연결 여부 확인
|
middleCheck = true;
|
|
for (var count in transitionList) {
|
if (transitionList[count].sourceStatusId == issueStatusVo.id) {
|
normalSourceCheck = true;
|
}
|
|
if (transitionList[count].targetStatusId == issueStatusVo.id) {
|
normalTargetCheck = true;
|
}
|
}
|
|
if (!normalSourceCheck || !normalTargetCheck) {
|
connectCheck = true;
|
}
|
break;
|
|
case "CLOSE" :
|
// '종료'인 상태에 다른 상태가 있는지 확인
|
// 한번도 없는걸 찾는다.
|
|
// 이번 상태를 타겟으로 하는 전이선 정보가 있는지 확인한다.
|
// 있으면 그냥 통과
|
// 없으면 문제 발생
|
var tempLastCheck = false;
|
|
for (var count in transitionList) {
|
if (transitionList[count].targetStatusId == issueStatusVo.id) {
|
tempLastCheck = true;
|
break;
|
}
|
}
|
// 없으면 문제 발생
|
if (!tempLastCheck) {
|
lastCheck = false;
|
}
|
|
break;
|
}
|
});
|
|
// 고립된 상태가 존재하는지 확인한다.
|
if (!firstCheck || !middleCheck || !lastCheck || connectCheck) {
|
$scope.isolationWorkflow = true;
|
}
|
else {
|
$scope.isolationWorkflow = false;
|
}
|
}
|
|
// 상태 속성 별로 1개씩 존재하는지 확인한다. - 부족할 경우 저장 금지
|
function checkRequireIssueStatusTypeExceptionGenerate() {
|
$scope.firstStatusExist = true;
|
$scope.middleStatusExist = true;
|
$scope.lastStatusExist = true;
|
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
switch (issueStatusVo.issueStatusType) {
|
case "READY" :
|
$scope.firstStatusExist = false;
|
break;
|
case "OPEN" :
|
$scope.middleStatusExist = false;
|
break;
|
case "CLOSE" :
|
$scope.lastStatusExist = false;
|
break;
|
}
|
});
|
}
|
|
// 전체 이슈 상태를 가져온다.
|
function getIssueStatusList() {
|
$scope.vm.issueStatuses = [];
|
|
IssueStatus.findAll($resourceProvider.getContent({},
|
$resourceProvider.getPageContent(0, 0))).then(function (result) {
|
|
if (result.data.message.status === "success") {
|
angular.forEach(result.data.data, function (issueStatus) {
|
var exist = false;
|
|
for (var count in $scope.vm.issueStatusVos) {
|
if ($scope.vm.issueStatusVos[count].id == issueStatus.id) {
|
exist = true;
|
break;
|
}
|
}
|
|
if (!exist) {
|
switch (issueStatus.issueStatusType) {
|
case 'READY' :
|
issueStatus.issueStatusTypeKr = '대기'
|
break;
|
case 'OPEN' :
|
issueStatus.issueStatusTypeKr = '진행'
|
break;
|
case 'CLOSE' :
|
issueStatus.issueStatusTypeKr = '종료'
|
break;
|
}
|
$scope.vm.issueStatuses.push(issueStatus);
|
}
|
});
|
|
// 이슈 상태 추가 셀렉트 박스에서 첫번째 옵션 항목을 기본 선택되게 한다.
|
$scope.fn.defaultAddIssueStatusId();
|
}
|
else {
|
SweetAlert.error($filter("translate")("common.failedToIssueStatusListLookup"), result.data.message.message); // "이슈 상태 목록 조회 실패"
|
}
|
});
|
}
|
|
// 노드 색상을 초기화한다.
|
function initCircleColor(target, all) {
|
d3.selectAll("circle").each(function () {
|
var circle = d3.select(this)[0][0]["__data__"];
|
|
if (all) {
|
d3.select(this).style("fill", circle.originalColor);
|
}
|
else {
|
if (circle.id != target.id) {
|
d3.select(this).style("fill", circle.originalColor);
|
}
|
}
|
});
|
}
|
|
// 전이선 색상을 초기화한다.
|
function initLinkColor(target, all) {
|
d3.selectAll(".link").each(function () {
|
var link = d3.select(this)[0][0]["__data__"];
|
|
// 전체 초기화일경우
|
if (all) {
|
d3.select(this).classed("linkGreen", false);
|
d3.select(this).classed(link.colorClass, true);
|
}
|
else {
|
if (link.id != target.id) {
|
d3.select(this).classed("linkGreen", false);
|
d3.select(this).classed(link.colorClass, true);
|
}
|
}
|
|
d3.select(this).attr("marker-end", "url(#licensing)");
|
});
|
}
|
|
// function getWorkflowDepartments() {
|
// $scope.vm.departments = [];
|
//
|
// if ($scope.vm.activeTarget != null) {
|
// return issueStatus.workflowDepartments;
|
// }
|
// return null;
|
// }
|
|
// 담당부서 설정
|
function setWorkflowDepartment(targetIssueStatus) {
|
if (targetIssueStatus != null) {
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
if (issueStatusVo.id === targetIssueStatus.id) {
|
$scope.vm.departmentName = issueStatusVo.workflowDepartmentName;
|
$scope.vm.departments = [];
|
angular.forEach(issueStatusVo.workflowDepartmentVos, function (workflowDepartment) {
|
$scope.vm.departments.push(workflowDepartment.departmentVo);
|
});
|
}
|
});
|
}
|
}
|
|
// 선택 한 부서 제거
|
function removeDepartment(index) {
|
if (index < $scope.vm.departments.length) {
|
$scope.vm.departments.splice(index, 1);
|
changeDepartment();
|
}
|
}
|
|
function changeDepartment() {
|
if ($scope.vm.activeTarget != null) {
|
var targetIssueStatus = $scope.vm.activeTarget.issueStatus;
|
|
// null 체크
|
if (targetIssueStatus != null) {
|
var myIssueStatus = null;
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
if (issueStatusVo.id === targetIssueStatus.id) {
|
myIssueStatus = issueStatusVo;
|
}
|
});
|
|
if (myIssueStatus != null) {
|
if ($scope.vm.departments != null) {
|
var workflowDepartments = [];
|
|
angular.forEach($scope.vm.departments, function (department) {
|
var workflowDepartment = {
|
departmentVo : department
|
}
|
workflowDepartments.push(workflowDepartment);
|
});
|
myIssueStatus.workflowDepartmentVos = workflowDepartments;
|
}
|
}
|
}
|
}
|
}
|
|
// 담당부서 추가
|
function addWorkflowDepartment(targetIssueStatus)
|
{
|
if ($scope.departments != null && $scope.departments.length > 0) {
|
var workflowDepartmentVos = [];
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo) {
|
if (issueStatusVo.id === targetIssueStatus.id) {
|
var issueStatusWorkflowDepartments = {
|
workflowDepartmentVos : workflowDepartmentVos,
|
workflowDepartmentName : ""
|
}
|
issueStatusVo = Object.assign(issueStatusVo, issueStatusWorkflowDepartments);
|
}
|
});
|
}
|
}
|
|
// 담당부서 삭제
|
function removeWorkflowDepartment(targetIssueStatus)
|
{
|
if ($scope.vm.workflowDepartmentVos != null && $scope.vm.workflowDepartmentVos.length > 0) {
|
$scope.vm.workflowDepartmentVos = $scope.vm.workflowDepartmentVos.filter((el) => el.issueStatus.id === targetIssueStatus.id);
|
}
|
}
|
|
// 타겟을 클릭했을 때 색상을 변경한다.
|
// 담당 부서를 불러온다
|
function targetClick(target) {
|
$scope.vm.activeTarget = angular.copy(target[0][0]["__data__"]);
|
|
setWorkflowDepartment($scope.vm.activeTarget.issueStatus);
|
// node 일경우
|
if ($scope.vm.activeTarget.type == "01") {
|
// 노드, 링크 전체 초기화
|
$scope.fn.initCircleColor($scope.vm.activeTarget, false);
|
$scope.fn.initLinkColor(null, true);
|
|
// 대상 노드 색 변경
|
target.style("fill", "#1DDB16");
|
}
|
else if ($scope.vm.activeTarget.type == "02") {
|
// 노드, 링크 전체 초기화
|
$scope.fn.initCircleColor(null, true);
|
$scope.fn.initLinkColor($scope.vm.activeTarget, false);
|
// 대상 링크 색 변경
|
target.classed("linkGreen", true);
|
target.classed($scope.vm.activeTarget.colorClass, false);
|
target.attr("marker-end", "url(#greenLicensing)");
|
}
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
}
|
|
// 다이어그램에서 도형을 움직일 때 실행
|
function tick() {
|
//IE mark bug fix
|
$scope.vm.link.each(function () {
|
this.parentNode.insertBefore(this, this);
|
});
|
|
$scope.vm.link.attr("d", function (d) {
|
//svg 영역 내에 표시
|
if (d.target.x > $scope.vm.width) {
|
d.target.x = ($scope.vm.width - 31);
|
}
|
else if (d.target.x < 30) {
|
d.target.x = 30;
|
}
|
|
if (d.source.x > $scope.vm.width) {
|
d.source.x = ($scope.vm.width - 31);
|
}
|
else if (d.source.x < 30) {
|
d.source.x = 30;
|
}
|
|
if (d.target.y > $scope.vm.height) {
|
d.target.y = ($scope.vm.height - 31);
|
}
|
else if (d.target.y < 30) {
|
d.target.y = 30;
|
}
|
|
if (d.source.y > $scope.vm.height) {
|
d.source.y = ($scope.vm.height - 31);
|
}
|
else if (d.source.y < 30) {
|
d.source.y = 30;
|
}
|
|
if (d.correctX == null) {
|
d.correctX = d.source.x;
|
}
|
|
if (d.correctY == null) {
|
d.correctY = d.source.y;
|
}
|
|
// 직선화 유지 상태 일 경우
|
if (d.direct) {
|
d.correctX = d.source.x;
|
d.correctY = d.source.y;
|
}
|
|
return "M" + d.source.x + "," + d.source.y + " Q" + d.correctX + ", " + d.correctY + " " + d.target.x + ", " + d.target.y;
|
});
|
|
$scope.vm.node.attr("transform", function (d) {
|
if (d.x > $scope.vm.width) {
|
d.x = ($scope.vm.width - 31);
|
}
|
else if (d.x < 30) {
|
d.x = 30;
|
}
|
|
if (d.y > $scope.vm.height) {
|
d.y = ($scope.vm.height - 31);
|
}
|
else if (d.y < 30) {
|
d.y = 30;
|
}
|
|
return "translate(" + d.x + "," + d.y + ")";
|
});
|
|
$scope.vm.nodeTexts.attr("transform", function (d) {
|
return "translate(" + d.x + "," + d.y + ")";
|
});
|
|
// on 이벤트가 과다하게 발생하는 현상을 방지
|
$scope.vm.force.stop();
|
}
|
|
// 노드를 만든다.
|
function makeNode(issueStatus, sXLocation, sYLocation) {
|
var xLocation = 0;
|
var yLocation = 0;
|
var color = "FAED7D";
|
|
// x좌표
|
if (issueStatus.xLocation == 0 || issueStatus.xLocation == null) {
|
xLocation = sXLocation;
|
}
|
else {
|
xLocation = issueStatus.xLocation;
|
}
|
// y좌표
|
if (issueStatus.yLocation == 0 || issueStatus.yLocation == null) {
|
yLocation = sYLocation;
|
}
|
else {
|
yLocation = issueStatus.yLocation;
|
}
|
|
if (issueStatus.color != null) {
|
color = issueStatus.color;
|
}
|
|
return {
|
id : issueStatus.id,
|
name : issueStatus.name,
|
x : xLocation,
|
y : yLocation,
|
color : color,
|
issueStatusType : issueStatus.issueStatusType,
|
originalColor : issueStatus.color,
|
fixed : true,
|
type : "01", // 수정 모드시 구분
|
issueStatus : issueStatus
|
}
|
}
|
|
// 전이선을 만든다.
|
function makeLink(workflowTransition) {
|
return {
|
id : workflowTransition.id,
|
source : workflowTransition.sourceStatusName,
|
target : workflowTransition.targetStatusName,
|
correctX : workflowTransition.correctX,
|
correctY : workflowTransition.correctY,
|
direct : workflowTransition.direct,
|
type : "02",
|
colorClass : "linkBlue",
|
workflowTransition : workflowTransition
|
}
|
}
|
|
|
// 1. 서버에서 데이터가 내려올때 사용된 이슈 상태 전체 목록과 각 이슈 상태의 전이선 정보가 내려와야 한다.
|
|
// 노드와 링크 정보를 생성한다.
|
function makeNodeAndLink() {
|
$scope.vm.nodes = []; // 원 정보 모음
|
$scope.vm.links = []; // 링크 정보 모음
|
|
var startX = 50; // 새로운 도형의 X축 위치
|
var startY = 50; // 새로운 도형의 Y축 위치
|
|
// 노드와 링크 정보를 만든다.
|
angular.forEach($scope.vm.issueStatusVos, function (issueStatusVo, index) {
|
$scope.vm.nodes[issueStatusVo.name] = $scope.fn.makeNode(issueStatusVo, startX, startY);
|
|
angular.forEach(issueStatusVo.workflowTransitionVos, function (workflowTransition) {
|
$scope.vm.links.push($scope.fn.makeLink(workflowTransition));
|
});
|
|
// 기본 좌표가 없을 경우 계속 60씩 증가
|
startX += 60;
|
|
if (index % 2 == 0) {
|
startY += 30;
|
}
|
else {
|
startY += 30;
|
}
|
});
|
|
angular.forEach($scope.vm.links, function (link) {
|
if (link.source == $scope.vm.nodes[link.source].name) {
|
link.source = $scope.vm.nodes[link.source];
|
}
|
|
if (link.target == $scope.vm.nodes[link.target].name) {
|
link.target = $scope.vm.nodes[link.target];
|
}
|
});
|
}
|
|
// 기초 레이아웃을 설정한다.
|
function initializeLayout() {
|
$scope.vm.svg = d3.select("#svgContent")
|
.append("svg")
|
.attr("width", $scope.vm.width)
|
.attr("height", $scope.vm.height);
|
|
$scope.vm.rect = $scope.vm.svg.append("rect")
|
.attr("width", $scope.vm.width)
|
.attr("height", $scope.vm.height)
|
.attr("fill", "white")
|
.on("click", function (d) {
|
// 원 색상 초기화
|
$scope.fn.initCircleColor(null, true);
|
// 링크 색상 초기화
|
$scope.fn.initLinkColor(null, true);
|
// 현재 선택한 대상 정보
|
$scope.vm.activeTarget = null;
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
});
|
}
|
|
// 드래그 이벤트 생성
|
function makeDragEvent() {
|
// 드래그
|
$scope.vm.drag = d3.behavior.drag().on("drag", function (d) {
|
d.direct = false; // 링크 드래그시 직선화 유지 해제
|
d.correctX += d3.event.dx;
|
d.correctY += d3.event.dy;
|
|
$scope.fn.tick();
|
});
|
}
|
|
// 표시되는 선을 생성한다.
|
function makeGridLine() {
|
// 기본 표시되는 선
|
$scope.vm.svg.append("marker")
|
.attr("orient", "auto")
|
.attr("markerHeight", 4)
|
.attr("markerWidth", 4)
|
.attr("refX", 18)
|
.attr("viewBox", "0 -5 10 10")
|
.attr("id", "licensing")
|
.append("path")
|
.attr("d", function () {
|
return "M0,-5 L10,0 L0,5";
|
});
|
|
// 선을 눌러서 활성화되었을때
|
$scope.vm.svg.append("marker")
|
.attr("orient", "auto")
|
.attr("markerHeight", 5)
|
.attr("markerWidth", 5)
|
.attr("refX", 16)
|
.attr("viewBox", "0 -5 10 10")
|
.attr("id", "greenLicensing")
|
.append("path")
|
.attr("d", function () {
|
return "M0,-5 L10,0 L0,5";
|
});
|
}
|
|
// 포스 레이아웃 실행
|
function executeForce() {
|
$scope.vm.force = d3.layout.force()
|
.nodes(d3.values($scope.vm.nodes))
|
.links($scope.vm.links)
|
.size([$scope.vm.width, $scope.vm.height])
|
.on("tick", $scope.fn.tick)
|
.start();
|
}
|
|
// 링크를 셋팅한다.
|
function setLink() {
|
var linkMenu = [{
|
title : $filter("translate")("managementWorkflow.removeMetastaticLine"), // 전이선 제거
|
action : function (element) {
|
$scope.fn.removeWorkflowTransition(d3.select(element)[0][0]);
|
}
|
}];
|
|
$scope.vm.link = $scope.vm.svg.append("svg:g")
|
.selectAll(".link")
|
.data($scope.vm.force.links())
|
.enter()
|
.append("path")
|
.attr("class", function (d) {
|
return "link " + d.colorClass;
|
})
|
.attr("id", function (d, i) {
|
return "link_" + i;
|
})
|
.attr("marker-end", function (d) {
|
return "url(#licensing)";
|
})
|
.attr("cursor", "pointer")
|
.on("contextmenu", d3.contextMenu(linkMenu))
|
.on("click", function () {
|
$scope.fn.targetClick(d3.select(this));
|
});
|
}
|
|
// 노드를 셋팅한다.
|
function setNode() {
|
var nodeMenu = [{
|
title : $filter("translate")("managementWorkflow.removeIssueStatus"), // "이슈 상태 제거"
|
action : function (element) {
|
$scope.fn.removeIssueStatus(d3.select(element)[0][0]);
|
// 이슈 상태가 제거되었을 때 시작점, 종점 초기화를 위해서..
|
$scope.vm.sourceStatusId = null;
|
$scope.fn.getAvailableNextStatus();
|
}
|
}];
|
|
$scope.vm.node = $scope.vm.svg.append("svg:g")
|
.selectAll("circle")
|
.data($scope.vm.force.nodes())
|
.enter()
|
.append("circle")
|
.attr("class", "node")
|
.style("fill", function (d) {
|
return d.color;
|
})
|
.style("cursor", "pointer")
|
.attr("r", 15)
|
.on("contextmenu", d3.contextMenu(nodeMenu))
|
.on("click", function () {
|
$scope.fn.targetClick(d3.select(this));
|
});
|
}
|
|
// 노드 텍스트를 셋팅한다.
|
function setNodeTexts() {
|
$scope.vm.nodeTexts = $scope.vm.svg.append("svg:g")
|
.selectAll("text")
|
.data($scope.vm.force.nodes())
|
.enter()
|
.append("text")
|
.attr("class", "label")
|
.attr("fill", "black")
|
.attr("dy", 30)
|
.style("font-size", "12px")
|
.style("text-anchor", "middle")
|
.text(function (d) {
|
var name = "";
|
if (d.name != null) {
|
if (d.name.length > 10) {
|
name = d.name.substring(0, 10) + "...";
|
}
|
else {
|
name = d.name;
|
}
|
}
|
|
return name;
|
});
|
}
|
|
// 워크플로우 계획 다이어그램을 그린다.
|
function forceLayout() {
|
$scope.fn.initializeLayout($scope.vm.width, $scope.vm.height);
|
$scope.fn.makeNodeAndLink();
|
$scope.fn.makeDragEvent();
|
$scope.fn.makeGridLine();
|
$scope.fn.executeForce($scope.vm.width, $scope.vm.height);
|
$scope.fn.setLink();
|
$scope.fn.setNode();
|
$scope.fn.setNodeTexts();
|
|
$scope.vm.link.call($scope.vm.drag);
|
$scope.vm.node.call($scope.vm.force.drag);
|
|
}
|
|
|
},
|
link : function (scope, element, attrs) {
|
|
}
|
}
|
}])
|
});
|