Index: arms/html/analysisCost/content-container.html =================================================================== diff -u -r209d8274c8b59241cd4ebd4d454d6aa009e167d9 -rd1755e1bbbb768e0e873253e3ea448da99a79104 --- arms/html/analysisCost/content-container.html (.../content-container.html) (revision 209d8274c8b59241cd4ebd4d454d6aa009e167d9) +++ arms/html/analysisCost/content-container.html (.../content-container.html) (revision d1755e1bbbb768e0e873253e3ea448da99a79104) @@ -621,9 +621,19 @@ style="margin-top: 5px"> 각 버전에 투입된 인력의 연봉 정보를 입력합니다. -
+ +
+
+ +
+
+
@@ -641,9 +651,6 @@ 비용 분석 계산 - @@ -657,8 +664,8 @@ - - 요구사항 분석 + + 제품에 대한 투자 비용 대비 성과
@@ -709,42 +716,24 @@
- 요구사항의 연결된 담당자와 입력된 인력별 연봉 정보를 바탕으로 분석된 데이터입니다. - + 제품의 비용을 확인할 수 있습니다.
- -
+
-
+

- - 요구사항 비용 분석 + + 투자 비용 대비 성과

-
+
-
- 활성화(진행중 혹은 완료)된 요구사항의 연결된 담당자 정보를 바탕으로 현재 추정 금액 및 난이도와 - 우선순위 통계를 확인할 수 있습니다. -
- 요구사항 금액은 슬라이드 바 및 스크롤로 확대하여 확인할 수 있습니다. + style="width: 100%; height: 2px">
+
+ 완료 된 요구사항을 기준으로 하여, 제품에 대한 투자 비용 대비 성과를 확인할 수 있습니다. +
-
+ + + + + +
+
+
+ +
-
-
+
+
+
+

+ + + 제품 버전 비용 산정 + +

+ +
+
+
+
+ 제품의 버전 별 비용을 확인할 수 있습니다. +
+ +
+
+
-
+
+
+
+

+ + + 요구사항 분석 + +

+
+
+
+
+
+ 요구사항의 연결된 담당자와 입력된 인력별 연봉 정보를 바탕으로 분석된 데이터입니다. +
+
+

- - 요구사항별 투입비용 현황 + + 요구사항 비용 분석

@@ -920,10 +1038,15 @@
-
요구사항별 투입 비용 현황을 확인할 수 있습니다.
-
-
-
+
+ 활성화(진행중 혹은 완료)된 요구사항의 연결된 담당자 정보를 바탕으로 현재 추정 금액 및 난이도와 + 우선순위 통계를 확인할 수 있습니다. +
+ 요구사항 금액은 슬라이드 바 및 스크롤로 확대하여 확인할 수 있습니다. +
+
@@ -992,18 +1115,7 @@ class="font13" style="margin-top: 5px"> 계산된 요구사항을 기반으로 분석된 데이터입니다. - -
@@ -1012,8 +1124,8 @@ - - 버전 별 총 인력 비용 + + 인력별 성과 분석
@@ -1059,61 +1171,6 @@
-
-
-
버전별 소모된 총 인력 비용을 확인할 수 있습니다.
-
-
-
-
-
-
-
-
-
-
-
-

- - - 연봉기준 성과 분포 - -

- -
@@ -1123,438 +1180,20 @@
- 전체 인력 연봉 대비 성과 분포를 확인할 수 있습니다. - [완료 요구사항 기준] + 인력별 연봉 대비 성과를 한눈에 확인할 수 있습니다. - [완료 요구사항 기준]
-
- -
-
-
-
-
-
-

- - - 인력별 성과 분석 - -

- -
-
-
-
- 인력별 연봉 대비 성과를 한눈에 확인할 수 있습니다. - [완료 요구사항 기준] -
-
-
-
- - + + \ No newline at end of file FishEye: Tag d1755e1bbbb768e0e873253e3ea448da99a79104 refers to a dead (removed) revision in file `arms/html/analysisCostV2/content-container.html'. FishEye: No comparison available. Pass `N' to diff? FishEye: Tag d1755e1bbbb768e0e873253e3ea448da99a79104 refers to a dead (removed) revision in file `arms/html/analysisCostV2/content-header.html'. FishEye: No comparison available. Pass `N' to diff? Index: arms/js/analysisCost.js =================================================================== diff -u -r0bf51d6b98547889144b0a2b24643ac6d6da5beb -rd1755e1bbbb768e0e873253e3ea448da99a79104 --- arms/js/analysisCost.js (.../analysisCost.js) (revision 0bf51d6b98547889144b0a2b24643ac6d6da5beb) +++ arms/js/analysisCost.js (.../analysisCost.js) (revision d1755e1bbbb768e0e873253e3ea448da99a79104) @@ -3,9 +3,9 @@ var dataTableRef; var mailAddressList; // 투입 작업자 메일 var req_count, linkedIssue_subtask_count, resource_count, req_in_action, total_days_progress; - +var modifiedRows = {}; var dashboardColor; - +let fileName = "인력별_연봉정보_템플릿.xlsx"; var pdServiceListData; var versionListData; @@ -61,9 +61,6 @@ // 생성한 차트 import "js/analysis/topmenu/basicRadar.js", "js/analysis/topmenu/topMenu.js", - - //CirclePacking with d3 Chart - "js/analysis/cost/circularPackingChart.js" ], [ "../reference/jquery-plugins/dataTables-1.10.16/media/css/jquery.dataTables_lightblue4.css", @@ -79,40 +76,49 @@ "../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/jszip.min.js", "../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/pdfmake.min.js", "../arms/js/analysis/resource/sankey.js" + ], + [ + "https://fonts.googleapis.com/css?family=Material+Icons", + "../reference/jquery-plugins/jspreadsheet-ce-4.13.1/dist/jsuites.js", + "../reference/jquery-plugins/jspreadsheet-ce-4.13.1/dist/jsuites.css", + "../reference/jquery-plugins/jspreadsheet-ce-4.13.1/dist/index.js", + "../reference/jquery-plugins/jspreadsheet-ce-4.13.1/dist/jspreadsheet.css", + // "../reference/jquery-plugins/jspreadsheet-ce-4.13.1/dist/jspreadsheet.datatables.css", + "../reference/jquery-plugins/jspreadsheet-ce-4.13.1/dist/jspreadsheet.theme.css" ] // 추가적인 플러그인 그룹들을 이곳에 추가하면 됩니다. ]; loadPluginGroupsParallelAndSequential(pluginGroups) - .then(function () { - // 사이드 메뉴 색상 설정 + .then(function() { + // 사이드 메뉴 색상 설정 - $(".widget").widgster(); - setSideMenu("sidebar_menu_analysis", "sidebar_menu_analysis_cost"); + $(".widget").widgster(); + setSideMenu("sidebar_menu_analysis", "sidebar_menu_analysis_cost"); - //제품(서비스) 셀렉트 박스 이니시에이터 - makePdServiceSelectBox(); + //제품(서비스) 셀렉트 박스 이니시에이터 + makePdServiceSelectBox(); - //버전 멀티 셀렉트 박스 이니시에이터 - makeVersionMultiSelectBox(); + //버전 멀티 셀렉트 박스 이니시에이터 + makeVersionMultiSelectBox(); - 비용분석계산버튼(); + 비용분석계산버튼(); - dashboardColor = dashboardPalette.dashboardPalette01; + dashboardColor = dashboardPalette.dashboardPalette01; - }) - .catch(function (e) { - console.error("플러그인 로드 중 오류 발생"); - console.error(e); - }); + }) + .catch(function(e) { + console.error("플러그인 로드 중 오류 발생"); + console.error(e); + }); } /////////////////////// //제품 서비스 셀렉트 박스 ////////////////////// function makePdServiceSelectBox() { //제품 서비스 셀렉트 박스 이니시에이터 - $(".chzn-select").each(function () { + $(".chzn-select").each(function() { $(this).select2($(this).data()); }); @@ -124,7 +130,7 @@ dataType: "json", progress: true, statusCode: { - 200: function (data) { + 200: function(data) { ////////////////////////////////////////////////////////// pdServiceListData = []; for (var k in data.response) { @@ -134,19 +140,19 @@ $("#selected_pdService").append(newOption).trigger("change"); } ////////////////////////////////////////////////////////// - console.log("[analysisCost :: makePdServiceSelectBox] :: pdServiceListData => " ); + console.log("[analysisCost :: makePdServiceSelectBox] :: pdServiceListData => "); console.log(pdServiceListData); } } }); - $("#selected_pdService").on("select2:open", function () { + $("#selected_pdService").on("select2:open", function() { //슬림스크롤 makeSlimScroll(".select2-results__options"); }); // --- select2 ( 제품(서비스) 검색 및 선택 ) 이벤트 --- // - $("#selected_pdService").on("select2:select", function (e) { + $("#selected_pdService").on("select2:select", function(e) { selectedPdServiceId = $("#selected_pdService").val(); 차트초기화(); //refreshDetailChart(); 변수값_초기화(); @@ -164,7 +170,7 @@ //버전 선택시 셀렉트 박스 이니시에이터 $(".multiple-select").multipleSelect({ filter: true, - onClose: function () { + onClose: function() { console.log("onOpen event fire!\n"); var checked = $("#checkbox1").is(":checked"); @@ -175,7 +181,7 @@ selectedVersionId = versionTag.join(","); if (versionTag === null || versionTag == "") { - alert("버전이 선택되지 않았습니다."); + jError("버전이 선택되지 않았습니다."); return; } @@ -188,10 +194,114 @@ getReqAndLinkedIssueData(selectedPdServiceId, selectedVersionId); 버전별_요구사항별_인력정보가져오기(selectedPdServiceId, selectedVersionId); + + } }); } +function productCostChart() { + const url = new UrlBuilder() + .setBaseUrl('/auth-user/api/arms/analysis/cost/product-accumulate-cost-by-month') + .addQueryParam('pdServiceLink', selectedPdServiceId) + .addQueryParam('pdServiceVersionLinks', selectedVersionId) + .addQueryParam("isReqType", "ISSUE") + .addQueryParam('메인그룹필드', "parentReqKey") + .addQueryParam('하위그룹필드들', "assignee.assignee_accountId.keyword") + .build(); + + $.ajax({ + url: url, + type: "GET", + contentType: "application/json;charset=UTF-8", + dataType: "json", + progress: true, + statusCode: { + 200: function(apiResponse) { + var response = apiResponse.response; + var monthlyCost = response.monthlyCost; + console.log(" [ analysisCost :: chart1 ] :: response data -> " + JSON.stringify(monthlyCost)); + var productChartDom = document.getElementById('product-accumulate-cost-by-month'); + $(productChartDom).height("500px"); + var mymChart = echarts.init(productChartDom); + var option; + var mapValues = Object.values(monthlyCost); + var mapKeys = Object.keys(monthlyCost); + var mapKeysSize = mapKeys.length; + var maxValue = response.totalAnnualIncome; + var intervalValue = maxValue / 10; + + option = { + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + crossStyle: { + color: '#999' + } + } + }, + toolbox: { + feature: { + dataView: { show: true, readOnly: false }, + magicType: { show: true, type: ['line', 'bar'] }, + restore: { show: true }, + saveAsImage: { show: true } + } + }, + legend: { + data: ['성과 기준선', '월 별 누적 성과'] + }, + xAxis: [ + { + type: 'category', + data: mapKeys, + // name: '2024', + axisPointer: { + type: 'shadow' + } + } + ], + yAxis: [ + { + type: 'value', + // name: '비용', + min: 0, + max: maxValue, + interval: intervalValue, + axisLabel: { + formatter: '₩{value}' + } + } + ], + series: [ + { + name: '성과 기준선', + type: 'line', + data: [ + [0, maxValue / mapKeysSize], + [mapKeysSize - 1, maxValue] + ], + smooth: true, + }, + { + name: '월 별 누적 성과', + type: 'bar', + data: mapValues, + barWidth: '60%' // 막대너비 + } + ] + }; + + if (option && typeof option === 'object') { + mymChart.setOption(option); + } + window.addEventListener('resize', mymChart.resize); + } + } + }); +} + function bind_VersionData_By_PdService() { $(".multiple-select option").remove(); $.ajax({ @@ -200,7 +310,7 @@ dataType: "json", progress: true, statusCode: { - 200: function (data) { + 200: function(data) { ////////////////////////////////////////////////////////// //console.log(data.response); var pdServiceVersionIds = []; @@ -239,16 +349,53 @@ }); } +//////////////////////////////////////////////////////////////////////////////////////// +// 연봉 정보 수정 PUT API 호출 +//////////////////////////////////////////////////////////////////////////////////////// +function updateSalary() { + return $.ajax({ + url: "/auth-user/api/arms/salaries/update.do", + type: "PUT", + data: { + c_annual_income: $("#editview_assignee_salary").val(), + c_key: $("#editview_assignee_key").val() + // plan_resource: $("#editview_assignee_plan_resource").val(), + // assignee_start_date: new Date($("#editview_assignee_start_date").val()), + // assignee_end_date: new Date($("#editview_assignee_end_date").val()), + } + }); +} +//////////////////////////////////////////////////////////////////////////////////////// +// 연봉 정보 업데이트 완료 후 연봉 데이터 GET API 호출 +//////////////////////////////////////////////////////////////////////////////////////// +function fetchUpdatedData() { + const url = new UrlBuilder() + .setBaseUrl('/auth-user/api/arms/analysis/cost/version-req-assignees') + .addQueryParam('pdServiceLink', selectedPdServiceId) + .addQueryParam('pdServiceVersionLinks', selectedVersionId) + .addQueryParam('크기', 1000) + .addQueryParam('하위크기', 1000) + .addQueryParam('컨텐츠보기여부', true) + .build(); + + return $.ajax({ + url: url, + type: "GET", + contentType: "application/json;charset=UTF-8", + dataType: "json", + }); +} + function 버전별_요구사항별_인력정보가져오기(pdServiceLink, pdServiceVersionLinks) { const url = new UrlBuilder() - .setBaseUrl('/auth-user/api/arms/analysis/cost/version-req-assignees') - // .setBaseUrl('/auth-user/api/arms/analysis/cost/all-assignees') - .addQueryParam('pdServiceLink', pdServiceLink) - .addQueryParam('pdServiceVersionLinks', pdServiceVersionLinks) - .addQueryParam('크기', 1000) - .addQueryParam('하위크기', 1000) - .addQueryParam('컨텐츠보기여부', true) - .build(); + .setBaseUrl('/auth-user/api/arms/analysis/cost/version-req-assignees') + // .setBaseUrl('/auth-user/api/arms/analysis/cost/all-assignees') + .addQueryParam('pdServiceLink', pdServiceLink) + .addQueryParam('pdServiceVersionLinks', pdServiceVersionLinks) + .addQueryParam('크기', 1000) + .addQueryParam('하위크기', 1000) + .addQueryParam('컨텐츠보기여부', true) + .build(); $.ajax({ url: url, @@ -257,7 +404,7 @@ dataType: "json", progress: true, statusCode: { - 200: function (apiResponse) { + 200: function(apiResponse) { console.log(" [ analysisCost :: 버전별_요구사항별_인력정보가져오기 ] :: response data -> "); console.log(apiResponse.response); 버전_요구사항_담당자 = apiResponse.response.버전_요구사항_담당자; @@ -378,13 +525,13 @@ limitMultiFileUploads: 1, paramName: 'excelFile', // Callback for successful uploads: - fail: function (e, data) { + fail: function(e, data) { console.log("--------------------------"); console.log(data); jError(data.jqXHR.responseJSON.error.message); }, - done: function (e, data) { + done: function(e, data) { console.log("--------------------------"); console.log(data); if (data.textStatus == "success") { @@ -399,18 +546,6 @@ } }); - - - /*$("#fileupload").bind("fileuploadsubmit", function (e, data) { - // The example input, doesn't have to be part of the upload form: - var input = $("#fileIdlink"); - data.formData = { pdservice_link: input.val() }; - if (!data.formData.pdservice_link) { - data.context.find("button").prop("disabled", false); - input.focus(); - return false; - } - });*/ } // 버전 비용 및 인력 비용 입력 @@ -423,18 +558,6 @@ } function manpowerInput(전체담당자목록) { - - if ($.fn.dataTable.isDataTable('#manpower-annual-income')) { - $('#manpower-annual-income').DataTable().clear().destroy(); - } - - /*let manpowerData = Object.keys(전체담당자목록).map((key) => { - let data = {}; - data.이름 = 전체담당자목록[key].이름; - data.키 = key; - data.연봉 = 전체담당자목록[key].연봉; - return data; - });*/ 인력별_연봉정보 = Object.keys(전체담당자목록).map((key) => { let data = {}; data.이름 = 전체담당자목록[key].이름; @@ -444,138 +567,20 @@ }); console.log(" [ analysisCost :: manpowerInput ] :: 인력별_연봉정보 => " + JSON.stringify(인력별_연봉정보)); - var columnList = [ - { - name: "name", - title: "이름", - data: "이름", - render: function (data, type, row, meta) { - if (isEmpty(data) || data === "unknown") { - return "
N/A
"; - } else { - return "
" + data + "
"; - } - return data; - }, - className: "dt-center", - visible: true - }, - { - name: "key", - title: "고유 키", - data: "키", - render: function (data, type, row, meta) { - if (isEmpty(data) || data === "unknown") { - return "
N/A
"; - } else { - return "
" + data + "
"; - } - return data; - }, - className: "dt-center", - visible: true - }, - { - name: "annualIncome", - title: "연봉 (입력)", - data: "연봉", - render: function(data, type, row) { - var formattedData = parseInt(data).toLocaleString(); - return ' 만원'; - }, - className: "dt-center", - visible: true - } - ]; + jspreadsheetRender(인력별_연봉정보); - var rowsGroupList = []; - var columnDefList = []; - var orderList = []; - var jquerySelector = "#manpower-annual-income"; - var ajaxUrl = null; - var jsonRoot = null; - var buttonList = []; - var selectList = {}; - var isServerSide = false; - var scrollY = false; - var data = 인력별_연봉정보; - var isAjax = false; - - dataTableRef = dataTable_build( - jquerySelector, - ajaxUrl, - jsonRoot, - columnList, - rowsGroupList, - columnDefList, - selectList, - orderList, - buttonList, - isServerSide, - scrollY, - data, - isAjax - ); - // 템플릿 다운로드 excel_download(인력별_연봉정보); } -// 데이터 테이블 구성 이후 꼭 구현해야 할 메소드 : 열 클릭시 이벤트 -function dataTableClick(tempDataTable, selectedData) {} - -// 데이터 테이블 데이터 렌더링 이후 콜백 함수 -function dataTableCallBack(settings, json) { - /*$("#fileIdlink").val(selectedPdServiceId); - - //파일 리스트 초기화 - $("table tbody.files").empty(); - // Load existing files: - var $fileupload = $("#fileupload"); - - $.ajax({ - // Uncomment the following to send cross-domain cookies: - //xhrFields: {withCredentials: true}, - url: "/auth-user/api/arms/fileRepository/getFilesByNode.do", - data: { fileIdLink: selectedPdServiceId }, - dataType: "json", - context: $fileupload[0] - }).done(function (result) { - $(this).fileupload("option", "done").call(this, null, { result: result }); - $(".file-delete-btn").hide(); // 파일 리스트에서 delete 버튼 display none 처리 - });*/ -} - -function dataTableDrawCallback(tableInfo) { - $("#" + tableInfo.sInstance) - .DataTable() - .columns.adjust() - .responsive.recalc(); - - // 연봉 포맷 설정 및 연봉 정보 저장 - $('.annual-income-input').off('input').on('input', function() { - var value = this.value.replace(/,/g, ''); - this.value = value.replace(/\B(?=(\d{3})+(?!\d))/g, ","); - - let owner = $(this).data('owner'); - 전체담당자목록[owner].연봉 = this.value.replace(/,/g, ''); - 전체담당자목록[owner].인력별소모비용 = 0; - - var manpower = 인력별_연봉정보.find(item => item.키 === owner); - if (manpower) { - manpower.연봉 = 전체담당자목록[owner].연봉; - } - }); -} - function excel_download(인력별_연봉정보) { console.log(" [ analysisCost :: excel_download ] :: 인력별_연봉정보 => " + JSON.stringify(인력별_연봉정보)); let fileName = "인력별_연봉정보_템플릿.xlsx"; - $("#excel-annual-income-template-download").click(function () { + $("#excel-annual-income-template-download").click(function() { if (Object.keys(인력별_연봉정보).length === 0) { - alert("다운로드할 인력 정보가 없습니다."); + jError("다운로드할 인력 정보가 없습니다."); } else { $.ajax({ url: "/auth-user/api/arms/salaries/excel-download.do?excelFileName=" + fileName, @@ -586,7 +591,7 @@ responseType: 'blob' // 응답 데이터 타입을 blob으로 설정 }, statusCode: { - 200: function (data) { + 200: function(data) { var url = window.URL.createObjectURL(data); // blob 데이터로 URL 생성 var a = document.createElement('a'); // 다운로드 링크를 위한 태그 생성 a.href = url; // url 설정 @@ -603,10 +608,10 @@ } function 비용분석계산버튼() { - $("#cost-analysis-calculation").click(function() { + $("#cost-analysis-calculation").on('click', function() { - if(!selectedPdServiceId || !selectedVersionId) { - alert("제품(서비스), 버전을 선택해주세요."); + if (!selectedPdServiceId || !selectedVersionId) { + jError("제품(서비스), 버전을 선택해주세요."); return; } @@ -617,36 +622,38 @@ 전체담당자목록[owner].완료성과 = 0; if (isNaN(전체담당자목록[owner].연봉)) { - alert(owner + "의 연봉 정보가 잘못되었습니다. 숫자만 입력해주세요."); + jError(owner + "의 연봉 정보가 잘못되었습니다. 숫자만 입력해주세요."); return; } isEmpty = false; } if (isEmpty) { - alert("요구사항에 할당된 담당자가 없습니다."); + jError("요구사항에 할당된 담당자가 없습니다."); return; } + productCostChart(); + 비용계산데이터_초기화(); console.log(" [ analysisCost :: 비용 분석 계산 ] :: 전체담당자목록 -> "); console.log(전체담당자목록); const url = new UrlBuilder() - .setBaseUrl("/auth-user/api/arms/analysis/cost/req-linked-issue") - .addQueryParam("pdServiceLink", selectedPdServiceId) - .addQueryParam("pdServiceVersionLinks", selectedVersionId) - .build(); + .setBaseUrl("/auth-user/api/arms/analysis/cost/req-linked-issue") + .addQueryParam("pdServiceLink", selectedPdServiceId) + .addQueryParam("pdServiceVersionLinks", selectedVersionId) + .build(); const url2 = new UrlBuilder() - .setBaseUrl("/auth-user/api/arms/analysis/cost/T_ARMS_REQADD_"+ selectedPdServiceId + "/req-difficulty-priority-list") - .addQueryParam("c_req_pdservice_versionset_link", selectedVersionId) - .build(); + .setBaseUrl("/auth-user/api/arms/analysis/cost/T_ARMS_REQADD_" + selectedPdServiceId + "/req-difficulty-priority-list") + .addQueryParam("c_req_pdservice_versionset_link", selectedVersionId) + .build(); const completeKeywordUrl = new UrlBuilder() - .setBaseUrl("/auth-user/api/arms/reqState/complete-keyword") - .build(); + .setBaseUrl("/auth-user/api/arms/reqState/complete-keyword") + .build(); Promise.all([ $.ajax({ url: url, type: "GET", dataType: "json" }), @@ -679,39 +686,35 @@ // 해당 요구사항이 멀티 버전일 수 있으니 버전목록을 가져옴 let 요구사항이포함된버전목록 = JSON.parse(요구사항.c_req_pdservice_versionset_link); - - // 요구사항의 버전목록을 반복문 돌기 + + // 요구사항의 버전목록을 반복문 돌기 요구사항이포함된버전목록.forEach((버전) => { // 해당 버전에 요구사항 키 목록을 가져옴 let 버전_요구사항_키목록 = 요구사항별_키목록[버전]; // 버전의 요구사항 목록 유무 확인 if (버전_요구사항_키목록 == null) { - } - else { + } else { // 있을 시 계산, 버전과 요구사항 c_id로 해당 요구사항이 가진 키목록 데이터 가져오기 let 요구사항_키목록 = 버전_요구사항_키목록[요구사항.c_id]; // 요구사항 키 목록 유무 확인 if (요구사항_키목록 == null) { // console.log("버전 -> " + 버전 + "\n요구사항 -> " +요구사항.c_id); - } - else { + } else { // 있을 시 키목록을 반목문 돌기 요구사항_키목록.forEach((요구사항키) => { // 키 별 담당자목록 조회를 위한 버전_요구사항_담당자 중 버전 유무 확인 let 요구사항_담당자목록 = 버전_요구사항_담당자[버전]; if (요구사항_담당자목록 == null) { - } - else { + } else { // 있으면 버전_요구사항_담당자 중 담당자목록 유무 확인 let 담당자목록 = 요구사항_담당자목록[요구사항키.c_issue_key]; if (담당자목록 == null) { // console.log("요구사항 키 -> " + 요구사항키.c_issue_key + "n\요구사항_담당자목록 -> " +요구사항.c_id); - } - else { + } else { // console.log("요구사항 키 -> " + 요구사항키.c_issue_key + "\n담당자 -> " + JSON.stringify(담당자목록)); // 있으면 담당자목록을 반목문 돌기 @@ -734,49 +737,26 @@ $("#req-cost-analysis-chart").height("500px"); 요구사항비용분석차트(data2); - // 인력별 성과 측정 차트 - $("#manpower-analysis-chart2").height("500px"); - 인력_연봉성과분포차트(전체담당자목록); + $("#manpower-analysis-chart").height("500px"); 인력별_연봉대비_성과차트(전체담당자목록); - // 버전별 투자 대비 소모 비용 차트 - // $("#compare_costs").height("500px"); - // compareCostsChart(); - let pdServiceName; pdServiceListData.forEach(elements => { if (elements["pdServiceId"] === +selectedPdServiceId) { pdServiceName = elements["pdServiceName"]; } }); - clearChart('circularPacking'); - $("#circularPacking").height("620px"); - drawCircularPacking("circularPacking",pdServiceName, 요구사항별_키목록); - $("#version-stack-container").height("500px"); 버전소모비용스택차트(); + + }).catch(function(error) { console.log('Error:', error); jError("비용 분석 계산 중 에러가 발생했습니다."); }); - // 요구사항별 수익현황 차트 - var income_status_chart = document.getElementById('income_status_chart'); - if (!income_status_chart.hasChildNodes()) { - income_status_chart.style.display = 'flex'; - income_status_chart.style.justifyContent = 'center'; - income_status_chart.style.alignItems = 'center'; - income_status_chart.innerHTML = '

좌측 요구사항을 선택해주세요.

'; - }else{ - var chartInstance = echarts.getInstanceByDom(income_status_chart); - chartInstance.dispose(); - reqCostStatusChart(); - - } - $("#income_status_chart").height("620px"); - }); } @@ -797,39 +777,18 @@ } } -////////////////////////////////////////////////////////////// -/////////////// 요구사항의 일수 계산을 위한 함수/////////////////// -////////////////////////////////////////////////////////////// -function 날짜계산(요구사항) { - let startDate, endDate; - if (요구사항.reqStateEntity == null) { - } - else { - if (요구사항.reqStateEntity.c_id === 12) { - startDate = new Date(formatDate(요구사항.c_req_start_date)); - endDate = new Date(formatDate(요구사항.c_req_end_date)); - } - else { - startDate = new Date(formatDate(요구사항.c_req_start_date)); - endDate = new Date(formatDate(new Date())); - } - } - - return {startDate, endDate}; -} - function 최종비용분석계산(key, 요구사항, 버전, 요구사항키, 완료_요구사항_키워드) { const 완료_요구사항_키워드SET = new Set(완료_요구사항_키워드); let startDate = 요구사항.c_req_start_date - ? new Date(formatDate(요구사항.c_req_start_date)) - : null; + ? new Date(formatDate(요구사항.c_req_start_date)) + : null; let endDate = 요구사항.c_req_end_date - ? new Date(formatDate(요구사항.c_req_end_date)) - : new Date(formatDate(new Date())); + ? new Date(formatDate(요구사항.c_req_end_date)) + : new Date(formatDate(new Date())); if (요구사항.reqStateEntity != null) { if (완료_요구사항_키워드SET.has(요구사항.reqStateEntity.c_title)) { @@ -873,22 +832,19 @@ function 차트초기화() { $("#person-select-box").hide(); + clearChart('product-accumulate-cost-by-month'); clearChart('compare_costs'); - clearChart('circularPacking'); - clearChart('income_status_chart'); clearChart('req-cost-analysis-chart'); clearChart('manpower-analysis-chart'); - clearChart('manpower-analysis-chart2'); clearChart('version-stack-container'); + $("#product-accumulate-cost-by-month").height("0px"); $("#compare_costs").height("0px"); - $("#circularPacking").height("0px"); - $("#income_status_chart").height("0px"); $("#req-cost-analysis-chart").height("0px"); $("#manpower-analysis-chart").height("0px"); - $("#manpower-analysis-chart2").height("0px"); $("#version-stack-container").height("0px"); + } function clearChart(elementId) { @@ -910,22 +866,8 @@ console.log(data); let requirementJson = data.requirement; let difficultyJson = data.difficulty; - let priorityJson = data. priority; + let priorityJson = data.priority; - // let requirementList = {}; - // Object.values(requirementJson).forEach(item => { - // requirementList[item.c_title] = item.요구사항금액; - // }); - // - // let reqTotalPrice = 0; - // for (let key in requirementList) { - // reqTotalPrice += requirementList[key]; - // } - // - // let requirementKeys = Object.keys(requirementList); - // let requirementData = requirementKeys.map(key => requirementList[key]); - // let requirementTotalData = requirementKeys.map(key => reqTotalPrice - requirementList[key]); - let requirementList = Object.values(requirementJson).reduce((result, item) => { result[item.c_title] = item.요구사항금액; return result; @@ -973,7 +915,7 @@ title: [ { // text: '요구사항', - subtext: '전체 ' + reqTotalPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원', + subtext: '전체 ' + reqTotalPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + '원', left: '25%', textAlign: 'center', textStyle: { @@ -1001,7 +943,7 @@ { top: 50, left: '5%', - right: '0%', + right: '0%', width: '55%', bottom: '5%', containLabel: true @@ -1103,7 +1045,7 @@ window.addEventListener('resize', myChart.resize); } -function 버전소모비용스택차트(){ +function 버전소모비용스택차트() { const defaultValue = 0; @@ -1136,7 +1078,7 @@ let stackTypeList = Object.keys(전체담당자목록).map(key => { let data; - data = 전체담당자목록[key].이름 + "["+key+"]"; + data = 전체담당자목록[key].이름 + "[" + key + "]"; return data; }); @@ -1164,17 +1106,17 @@ axisPointer: { type: 'shadow' // 'line' or 'shadow'. 기본값은 'shadow' }, - formatter: function (params) { + formatter: function(params) { const tooltip = params.reduce((acc, param) => { const { marker, seriesName, value } = param; if (param.value > 0) { - let data = param.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원'; + let data = param.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + '원'; acc += `${marker}${seriesName}: ${data}
`; } return acc; }, ''); - const totalCount = params.reduce((acc, param) => acc + param.value, 0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원'; + const totalCount = params.reduce((acc, param) => acc + param.value, 0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + '원'; const versionName = params[0].name; const totalTooltip = `[${versionName}] - Total: ${totalCount}
`; @@ -1264,7 +1206,7 @@ { type: 'inside', yAxisIndex: [0], // y축에만 dataZoom 기능 적용 - start: (100-zoomPersent), + start: (100 - zoomPersent), end: 100 }, { @@ -1274,533 +1216,19 @@ backgroundColor: 'rgba(0,0,0,0)', // 슬라이더의 배경색 dataBackgroundColor: 'rgba(255,255,255,1)', // 데이터 배경색 yAxisIndex: [0], - start: (100-zoomPersent), + start: (100 - zoomPersent), end: 100 } ], }; option && myChart.setOption(option); - window.addEventListener('resize', function () { + window.addEventListener('resize', function() { myChart.resize(); }); } -///////////////////////////////////////////////////////// -// 요구사항 상세 차트 -///////////////////////////////////////////////////////// -function 요구사항_담당자_조회(요구사항_정보) { - let 버전 = 요구사항_정보.versionId; - let 요구사항키_목록 = 요구사항_정보.issueKey; - let result = {}; - - if (버전_요구사항_담당자.hasOwnProperty(버전)) { - 요구사항키_목록.forEach(key => { - if (버전_요구사항_담당자[버전].hasOwnProperty(key)) { - result[key] = 버전_요구사항_담당자[버전][key]; - } - }); - } - - let 요구사항_담당자_목록 = Object.keys(result).flatMap(key => Object.keys(result[key])); - - console.log(" [ analysisCost :: 요구사항별_소모비용_차트 :: 선택한 요구사항 참여 인원 정보 -> "); - console.log(요구사항_담당자_목록); - - let 일급_합산 = 0; - - 요구사항_담당자_목록.forEach(key => { - if (전체담당자목록.hasOwnProperty(key)) { - let 연봉 = 전체담당자목록[key].연봉; - let 일급데이터 = Math.round((연봉 / 365)) * 10000; - 일급_합산 += 일급데이터; - } - }); - - return 일급_합산; -} - -function 요구사항_하위이슈_일자별_소모비용(요구사항_시작일, 일급, 요구사항_이슈키별_업데이트_데이터){ - let 일자별_소모비용 = []; - let assigneeList = new Set(); - - for (let key in 요구사항_이슈키별_업데이트_데이터) { - 요구사항_이슈키별_업데이트_데이터[key].forEach(data => { - if (data.assignee !== null) { - assigneeList.add(data.assignee); - let updated = new Date(data.updated).toISOString().split('T')[0]; - 일자별_소모비용.push({ - updated: updated, - 일급: 일급 - }); - } - }); - } - - 요구사항_시작일 = new Date(요구사항_시작일).toISOString().split('T')[0]; - - // Set을 사용하여 중복된 assignee를 제거하고 요구사항 시작일에 대한 데이터가 없으면 추가. - if (!일자별_소모비용.some(data => data.updated === 요구사항_시작일)) { - 일자별_소모비용.push({ - updated: 요구사항_시작일, - 일급: 0 - }); - } - - // 'updated' 필드를 기준으로 오름차순 정렬 - 일자별_소모비용.sort((a, b) => new Date(a.updated) - new Date(b.updated)); - - let resultData = [일자별_소모비용[0]]; - - for (let i = 1; i < 일자별_소모비용.length; i++) { - //let 시작일 = new Date(resultData[0].updated); - let 이전_데이터 = new Date(일자별_소모비용[i - 1].updated); - let 현재_데이터 = new Date(일자별_소모비용[i].updated); - - // 이전 업데이트 일자와의 차이를 일수로 계산 - let 일자_차이 = (현재_데이터.getTime() - 이전_데이터.getTime()) / (1000 * 60 * 60 * 24); - - // 일자 차이와 일급을 곱하여 일급을 다시 계산 - // 즉 업데이트 일까지의 소모비용을 계산 한 것 - 일자별_소모비용[i].일급 = 일자_차이 * 일자별_소모비용[i].일급; - - // 중복된 날짜가 아닐 경우에만 결과 데이터에 추가 - if (resultData[resultData.length - 1].updated !== 일자별_소모비용[i].updated) { - resultData.push(일자별_소모비용[i]); - } - } -// console.log(" [ analysisCost :: 요구사항별_소모비용_차트 :: 선택한 요구사항항 일자벌 소모 비용 -> "); -// console.log(resultData); - - return resultData; -} - -function 요구사항_일자별_소모비용(요구사항_시작일,요구사항_목표_종료일, 일급,요구사항_이슈키별_업데이트_데이터){ - // 요구사항_종료일(DB에 저장된 요구사항 종료 시점) 이 없거나 - // 요구사항_이슈키별_업데이트_데이터에서 레졸루션 데이트(es에서 수집한 데이터)가 없으면 오늘 날짜까지 만듬 있으면 해당중 가장 큰날까지 만듬 - - let 일자별_소모비용 = []; - let 오늘 = new Date(); - console.log(요구사항_시작일); - console.log(요구사항_목표_종료일); - console.log(오늘); - - if(요구사항_목표_종료일 >= 오늘){ - for (let d = 요구사항_시작일; d <= 오늘; d.setDate(d.getDate() + 1)) { - let dateString = d.toISOString().split('T')[0]; - 일자별_소모비용.push({ - updated: dateString, - 일급: 일급 - }); - } - }else{ - for (let d = 요구사항_시작일; d <= 요구사항_목표_종료일; d.setDate(d.getDate() + 1)) { - let dateString = d.toISOString().split('T')[0]; - 일자별_소모비용.push({ - updated: dateString, - 일급: 일급 - }); - } - } - -// console.log(" [ analysisCost :: 요구사항별_소모비용_차트 :: 일자별 소모 비용 -> "); -// console.log(일자별_소모비용); - - return 일자별_소모비용; -} - -function reqCostStatusChart(data){ - var chartDom = document.getElementById('income_status_chart'); - - if(data != null && data.versionId !== undefined){ - - let 요구사항_정보; - 요구사항_정보 = 요구사항전체목록[data.reqId]; - console.log(" [ analysisCost :: 요구사항별_소모비용_차트 :: 선택한 요구사항 정보 -> "); - console.log(요구사항_정보); - // 요구사항이 생성된 일자를 시작일로 설정 - let 요구사항_시작일 = new Date(요구사항_정보.c_req_start_date); // c_req_start_date - let 요구사항_계획일 = 요구사항_정보.c_req_plan_time; - let 요구사항_목표_종료일 ; - let 요구사항_종료일 = new Date(요구사항_정보.c_req_end_date); - - if(요구사항_계획일 == null){ // 요구사항 계획일이 없으면 버전 종료일로 처리 - 요구사항_목표_종료일 = new Date(versionListData[data.versionId].c_pds_version_end_date); - }else{ - let 임시데이터 = new Date(요구사항_시작일.getTime()); - 임시데이터.setDate(임시데이터.getDate() + 요구사항_계획일); - 요구사항_목표_종료일 = 임시데이터; - } - - const url = new UrlBuilder() - .setBaseUrl('/auth-user/api/arms/analysis/cost/req-updated-list') - .addQueryParam('issueList', data.issueKey) - .build(); - - $.ajax({ - url: url, - type: "GET", - contentType: "application/json;charset=UTF-8", - dataType: "json", - progress: true, - statusCode: { - 200: function (apiResponse) { - console.log(" [ analysisCost :: 요구사항별_소모비용_차트 :: data -> "); - console.log(apiResponse.body); - let 요구사항_이슈키별_업데이트_데이터 = apiResponse.body; - - let allIsReqTrue = Object.values(요구사항_이슈키별_업데이트_데이터).flat().every(item => item.isReq === true); - let 일급 = 요구사항_담당자_조회(data); - let 일자별_소모비용_데이터; - - if (allIsReqTrue) {// 모든 사람이 요구사항을 직접 처리 하는 경우 - 일자별_소모비용_데이터 = 요구사항_일자별_소모비용(요구사항_시작일, 요구사항_목표_종료일, 일급,요구사항_이슈키별_업데이트_데이터); - - } else {// 참여 하는 사람 중 하나라도 하위 이슈 생성하여 작업하는 경우 - 일자별_소모비용_데이터 = 요구사항_하위이슈_일자별_소모비용(요구사항_시작일, 일급, 요구사항_이슈키별_업데이트_데이터); - } - drawReqCostStatusChart(chartDom,요구사항_정보,일급,data,요구사항_목표_종료일,일자별_소모비용_데이터); - } - } - }); - }else{ - chartDom.style.display = 'flex'; - chartDom.style.justifyContent = 'center'; - chartDom.style.alignItems = 'center'; - chartDom.innerHTML = '

좌측 요구사항을 선택해주세요.

'; - } -} - -function drawReqCostStatusChart(chartDom,요구사항_정보, 일급,data,요구사항_목표_종료일,일자별_소모비용_데이터){ - - var 예상비용 = 일급 * 요구사항_정보.c_req_plan_time; - 요구사항_목표_종료일 = 요구사항_목표_종료일.toISOString().substring(0, 10); - - let dates = 일자별_소모비용_데이터.map(item => item.updated); - dates.push(요구사항_목표_종료일); - - let costData = 일자별_소모비용_데이터.map(item => item.일급); - - let accumulatedData = 일자별_소모비용_데이터.reduce((acc, item) => { - let accumulatedCost = (acc.length > 0 ? acc[acc.length - 1] : 0) + item.일급; - return [...acc, accumulatedCost]; - }, []); - - accumulatedData.unshift(0); - - let 누적합 = costData.map((num, idx) => num + (accumulatedData[idx] || 0)); - - var myChart = echarts.init(chartDom, null, { - renderer: "canvas", - useDirtyRect: false - }); - var option; - option = { - title: { - text: 요구사항_정보.c_title, - left: 'center', - textStyle: { - fontSize: 12, - color: '#FFFFFF' - } - }, - grid: { - left: '3%', - right: '4%', - bottom: '3%', - containLabel: true - }, - xAxis: { - type: 'category', - data: dates, - axisLabel: { - color: '#FFFFFF' - }, - scale: true - }, - yAxis: { - type: 'value', - axisLabel: { - color: '#FFFFFF' - }, - scale: true, - }, - series: [ - { - name: '누적 소모 비용', - type: 'line', - lineStyle: { - color: 'green', - type: 'dashed', - width: 3 - }, - itemStyle: { - color: 'green' - }, - data: 누적합 - }, - { - type: 'line', - label: { - show: true, - position: 'top' - }, - markLine: { - lineStyle: { - color: '#5470c6', // line color - type: 'dashed', // line style - width: 2 // line width - }, - label: { - position: 'middle', // label이 markLine의 중간에 위치하도록 설정 - formatter: '예상 비용', // label의 텍스트 설정 - fontSize: 15, // label의 폰트 크기 설정 - color: '#FFFFFF', - formatter: function(){ - return '예상 비용: '+예상비용.toLocaleString(); - } - }, - data: [ - { - yAxis: 예상비용, - name: '예상 비용' // line label - } - ] - } - }, - { - type: 'line', - label: { - show: true, - position: 'top' - }, - markLine: { - lineStyle: { - color: '#5470c6', // line color - type: 'dashed', // line style - width: 2 // line width - }, - label: { - position: 'middle', // label이 markLine의 중간에 위치하도록 설정 - formatter: '요구사항 기한', // label의 텍스트 설정 - fontSize: 15, // label의 폰트 크기 설정 - color: '#FFFFFF', - formatter: function(){ - return '요구사항 기한: '+ 요구사항_목표_종료일; - } - }, - data: [ - { - xAxis: 요구사항_목표_종료일 - } - ] - } - }, - { - name: '누적 소모 비용', - type: 'bar', - stack: 'Total', - silent: true, - itemStyle: { - borderColor: 'transparent', - color: 'transparent' - }, - tooltip :{ - show:false - }, - emphasis: { - itemStyle: { - borderColor: 'transparent', - color: 'transparent' - } - }, - data: accumulatedData // 누적값 - }, - { - name: '소모 비용', - type: 'bar', - stack: 'Total', - label: { - show: true, - color: '#FFFFFF', - position: 'top' - }, - itemStyle: { - color: '#eb5454' // 바의 색상을 빨간색으로 변경 - }, - data:costData// 증폭 - } - ], - tooltip: { - trigger: "axis", - position: "top", - borderWidth: 1, - axisPointer: { - type: "shadow" - } - }, - }; - - if (option && typeof option === "object") { - myChart.setOption(option); - } - window.addEventListener("resize", myChart.resize); -} - -///////////////////////////////////////////////////////// -// 인력 연봉 대비 성과 차트 -///////////////////////////////////////////////////////// -function 인력_연봉성과분포차트() { - - const tooltipFormatter = function (params) { - - let data = dataAll.filter(item => item[0] === params.value[0] && item[1] === params.value[1]); - let tooltipContent = '
'; - - if (data.length > 1) { - for (let i = 0; i < data.length; i++) { - tooltipContent += data[i][2] + "
연봉 : " + data[i][0].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원' + ", 성과 : " + data[i][1].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원' + '
'; - } - } - else if (data.length === 1) { - tooltipContent = data[0][2] + "
연봉 : " + data[0][0].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원' + "
성과 : " + data[0][1].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원'; - } - - tooltipContent += "
"; - - return tooltipContent; - }; - - let dataAll = Object.entries(전체담당자목록).map(([key, value]) => { - return [Number(value.연봉) *10000, Number(value.완료성과), value.이름+"["+key+"]"]; - }); - - var dom = document.getElementById('manpower-analysis-chart2'); - var myChart = echarts.init(dom, null, { - renderer: 'canvas', - useDirtyRect: false - }); - var app = {}; - - var option; - - let maxX = Math.max(...dataAll.map(item => item[0])); - let maxX2 = Math.max(...dataAll.map(item => item[1])); - - let max = Math.max(maxX, maxX2); - - const markLineOpt = { - animation: false, - label: { - formatter: '성과 기준선', - align: 'right', - color: 'white' - }, - lineStyle: { - type: 'dashed', - color: '#EE6666', - width: 2 - }, - tooltip: { - formatter: '성과 기준선' - }, - data: [ - [ - { - coord: [0, 0], - symbol: 'none' - }, - { - coord: [max, max], - symbol: 'none' - } - ] - ] - }; - option = { - grid: [ - { left: '15%', top: '5%'} - ], - tooltip: { - confine: true, - /* formatter: function (params) { - return params.value[2] + "
연봉 : " + params.value[0] + "
성과 : " + params.value[1]; - },*/ - formatter: tooltipFormatter - }, - xAxis: [ - { - gridIndex: 0, - min: 0, - max: max, - axisLabel: { - color: 'white', - interval: 1, - rotate: 45, - formatter: function (value) { - return value === 0 ? '' : value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); - }, - }, - splitLine: { - lineStyle: { - color: 'gray', - type: 'dashed' - } - } - } - ], - yAxis: [ - { - gridIndex: 0, - min: 0, - max: max, - axisLabel: { - color: 'white', - interval: 1, - rotate: 45, - }, - splitLine: { - lineStyle: { - color: 'gray', - type: 'dashed' - } - } - } - ], - series: [ - { - name: 'I', - type: 'scatter', - xAxisIndex: 0, - yAxisIndex: 0, - data: dataAll, - markLine: markLineOpt - } - ], - toolbox: { - show: true, - orient: "vertical", - left: "right", - bottom: "50px", - feature: { - mark: { show: true }, - dataView: {show: true, readOnly: true}, - dataZoom: {show: true} - }, - iconStyle: { - borderColor: "white" - } - }, - }; - - if (option && typeof option === 'object') { - myChart.setOption(option); - } - - window.addEventListener('resize', myChart.resize); -} - function 인력별_연봉대비_성과차트(전체담당자목록) { console.log(" [ analysisCost :: 인력별_연봉대비_성과차트 :: data -> "); console.log(전체담당자목록); @@ -1935,8 +1363,7 @@ show: true, position: 'outside', color: "white", - formatter: function(params) - { + formatter: function(params) { return params.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } }, @@ -1956,8 +1383,7 @@ show: true, position: 'outside', color: "white", - formatter: function(params) - { + formatter: function(params) { return params.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } }, @@ -1972,7 +1398,7 @@ left: "right", bottom: "50px", feature: { - dataZoom: {show: true}, + dataZoom: { show: true }, myTool1: { show: false, title: 'Full screen', @@ -1983,7 +1409,7 @@ $("#my_modal2_description").text('인력별 연봉 대비 성과를 한눈에 확인할 수 있습니다.'); let heights = screen.height;// window.innerHeight; console.log(heights); - $("#my_modal2_body").height(heights-450 + "px"); + $("#my_modal2_body").height(heights - 450 + "px"); $("#my_modal2_body").append(`
`); setTimeout(function() { myChart.resize(); @@ -2022,86 +1448,132 @@ window.addEventListener('resize', myChart.resize); } -function 전역인력맵확인() { - return new Promise(resolve => { - let intervalId = setInterval(() => { - console.log(전체담당자목록); - if (전체담당자목록.length > 0 ) { - clearInterval(intervalId); - resolve(전체담당자목록); - } - }, 500); // 100ms마다 globalDeadline 값 확인 - }); -} +function jspreadsheetRender(data) { + var sheet = jspreadsheet(document.getElementById("spreadsheet"), { + // allowComments: true, + contextMenu: function(o, x, y, e, items) { + var items = []; -///////////////////////////////////////////////////////// -// 투입 비용 현황 차트 -///////////////////////////////////////////////////////// -function compareCostsChart(){ + // Save + items.push({ + title: jSuites.translate('Save as'), + shortcut: 'Ctrl + S', + icon: 'save', + onclick: function () { + o.download(); + } + }); - console.log(" [ analysisCost :: compareCostsChart :: data -> "); - console.log(versionListData); - - let selectedVersions = selectedVersionId.split(','); // 문자열을 배열로 변환 - - let selectVersionData = []; - for (let i = 0; i < selectedVersions.length; i++) { - let item = versionListData[selectedVersions[i]]; - selectVersionData.push(item); - } - - let chartDom = document.getElementById("compare_costs"); - let myChart = echarts.init(chartDom); - let option; - - let titles = selectVersionData.map(item => item.c_title); - let consumptionCosts = selectVersionData.map(item => item.버전비용); - - option = { - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } + return items; }, - legend: { - textStyle: { - color: '#FFFFFF' - } + search:true, + pagination:10, + data: data, + columns: [ + { type: "text", title: "이름", readOnly: true }, + { type: "text", title: "키", readOnly: true }, + { type: "text", title: "연봉" } + ], + onbeforechange: function(instance, cell, x, y, value) { + var cellName = jspreadsheet.getColumnNameFromId([x,y]); + console.log('The cell ' + cellName + ' will be changed' + '\n'); }, - grid: { - left: '3%', - right: '10%', - bottom: '3%', - containLabel: true + oninsertcolumn: function(instance) { + console.log('Column added' + '\n'); }, - xAxis: { - type: 'value', - boundaryGap: [0, 0.01], - axisLabel: { - color: '#FFFFFF', - rotate: 45 + onchange: function(instance, cell, x, y, value) { + var cellName = jspreadsheet.getColumnNameFromId([x,y]); + console.log('onchange :: ' + cell + " :; x :: " + x + " :: y :: " + y +" :: cellName ::" + cellName + ' to: ' + value + '\n'); + if (x == 2) { + var key = instance.jexcel.getValueFromCoords(1, y); + modifiedRows[key] = value; } }, - yAxis: { - type: 'category', - data: titles, - axisLabel: { - color: '#FFFFFF' - } + oninsertrow: function(instance, rowNumber) { + console.log('Row added' + rowNumber); }, - series: [ - { - name: '소모 비용', - type: 'bar', - data: consumptionCosts - } - ] - }; + ondeleterow: function(instance, rowNumber) { + console.log('Row deleted' + rowNumber); + }, + ondeletecolumn: function(instance) { + console.log('Column deleted' +'\n'); + }, + onselection: function(instance, x1, y1, x2, y2, origin) { + var cellName1 = jspreadsheet.getColumnNameFromId([x1, y1]); + var cellName2 = jspreadsheet.getColumnNameFromId([x2, y2]); + console.log('The selection from ' + cellName1 + ' to ' + cellName2 + '' + '\n'); + }, + onsort: function(instance, cellNum, order) { + var order = (order) ? 'desc' : 'asc'; + console.log('The column ' + cellNum + ' sorted by ' + order + '\n'); + }, + onresizerow: function(instance, cell, height) { + console.log('The row ' + cell + ' resized to height ' + height + ' px' + '\n'); + }, + onresizecolumn: function(instance, cell, width) { + console.log('The column ' + cell + ' resized to width ' + width + ' px' + '\n'); + }, + onmoverow: function(instance, from, to) { + console.log('The row ' + from + ' was move to the position of ' + to + ' ' + '\n'); + }, + onmovecolumn: function(instance, from, to) { + console.log('The col ' + from + ' was move to the position of ' + to + ' ' + '\n'); + }, + onload: function(instance) { + console.log('New data is loaded' + '\n'); + }, + onblur: function(instance) { + console.log('The table ' + $(instance).prop('id') + ' is blur' + '\n'); + }, + onfocus: function(instance) { + console.log('The table ' + $(instance).prop('id') + ' is focus' + '\n'); + }, + onpaste: function(data) { + console.log('Paste on the table ' + $(instance).prop('id') + '' + '\n'); + }, + }); - if (option && typeof option === "object") { - myChart.setOption(option, true); + var rowCount = data.length; + + for(var rowIndex = 0; rowIndex < rowCount; rowIndex++) { + sheet.setStyle(`C${rowIndex + 1}`, 'color', 'white'); + // sheet.setStyle(`C${rowIndex + 1}`, 'background-color', 'orange'); } +} - window.addEventListener("resize", myChart.resize); -} \ No newline at end of file +$("#cost-excel-batch-update").on('click', function() { + if (Object.keys(modifiedRows).length === 0) { + jError("수정된 연봉 데이터가 없습니다."); + return; + } + var data = $("#spreadsheet").jexcel("getData"); + + var isValid = data.every(function(row) { + return !isNaN(row[2]) && row[2] !== ""; + }); + + if (!isValid) { + jError("연봉 데이터는 숫자만 입력해야하며, 값이 존재해야 합니다."); + } else { + $.ajax({ + url: "/auth-user/api/arms/salaries", + type: "PUT", + data: JSON.stringify(modifiedRows), + contentType: "application/json", + statusCode: { + 200: function(apiResponse) { + var response = apiResponse.response; + 인력별_연봉정보 = response; + var spreadsheetElement = document.getElementById("spreadsheet"); + if (spreadsheetElement.jexcel) { + spreadsheetElement.jexcel.destroy(); + } + modifiedRows = {}; + jspreadsheetRender(response); + jSuccess("연봉 정보가 수정되었습니다."); + } + } + }); + } +}); + FishEye: Tag d1755e1bbbb768e0e873253e3ea448da99a79104 refers to a dead (removed) revision in file `arms/js/analysisCostV2.js'. FishEye: No comparison available. Pass `N' to diff?