Index: arms/js/analysisCost.js =================================================================== diff -u -r26c90280d2d4dc2c0824c8c35cd56252bbdb3935 -r4f36fe7fce95a76d0685e986bbad57e6bc2e2030 --- arms/js/analysisCost.js (.../analysisCost.js) (revision 26c90280d2d4dc2c0824c8c35cd56252bbdb3935) +++ arms/js/analysisCost.js (.../analysisCost.js) (revision 4f36fe7fce95a76d0685e986bbad57e6bc2e2030) @@ -146,6 +146,7 @@ // --- select2 ( 제품(서비스) 검색 및 선택 ) 이벤트 --- // $("#selected_pdService").on("select2:select", function (e) { selectedPdServiceId = $("#selected_pdService").val(); + 차트초기화(); //refreshDetailChart(); 변수값_초기화(); // 제품( 서비스 ) 선택했으니까 자동으로 버전을 선택할 수 있게 유도 // 디폴트는 base version 을 선택하게 하고 ( select all ) @@ -201,12 +202,13 @@ ////////////////////////////////////////////////////////// //console.log(data.response); var pdServiceVersionIds = []; - versionListData = []; + versionListData = {}; for (var k in data.response) { var obj = data.response[k]; pdServiceVersionIds.push(obj.c_id); - versionListData.push(obj); + console.log(obj); + versionListData[obj.c_id] = obj; // versionListData.push({ c_id: obj.c_id, versionTitle: obj.c_title }); var newOption = new Option(obj.c_title, obj.c_id, true, false); $(".multiple-select").append(newOption); @@ -216,6 +218,7 @@ 수치_초기화(); selectedVersionId = pdServiceVersionIds.join(","); + console.log(selectedVersionId); // 요구사항 및 연결이슈 통계 getReqAndLinkedIssueData(selectedPdServiceId, selectedVersionId); @@ -255,7 +258,7 @@ statusCode: { 200: function (apiResponse) { console.log(" [ analysisCost :: 버전별_요구사항별_인력정보가져오기 ] :: response data -> "); - console.log(apiResponse.response.전체담당자목록); + console.log(apiResponse.response); 버전별요구사항별 = apiResponse.response.버전; 인력맵 = apiResponse.response.전체담당자목록; @@ -275,6 +278,10 @@ }); } +function formatDate(date) { + return new Date(date).toISOString().split('T')[0]; +} + // 버전 비용 및 인력 비용 입력 function costInput(인력맵, pdServiceVersionLinks) { @@ -285,18 +292,19 @@ $('#version-cost').DataTable().clear().destroy(); } - let selectedVersions = pdServiceVersionLinks.split(','); // 문자열을 배열로 변환 + let selectedVersions = pdServiceVersionLinks.split(','); - let versionTableData = versionListData.reduce((acc, item) => { - if (selectedVersions.includes(String(item.c_id))) { // item.c_id가 선택된 버전에 포함되면 - acc.push({ // 배열에 새로운 객체를 추가 - version: item.c_title, - period: item.c_pds_version_start_date + " ~ " + item.c_pds_version_end_date, - cost: 0 - }); - } - return acc; - }, []); + let versionTableData = selectedVersions.map(versionId => { + let item = versionListData[versionId]; + let startDate = item.c_pds_version_start_date === "start" ? formatDate(new Date()) : formatDate(item.c_pds_version_start_date); + let endDate = item.c_pds_version_end_date === "end" ? formatDate(new Date()) : formatDate(item.c_pds_version_end_date); + return { // 객체를 바로 반환 + version: item.c_title, + period: startDate + " ~ " + endDate, + cost: 0, + c_id: item.c_id + }; + }); $('#version-cost').DataTable({ data: versionTableData, @@ -308,7 +316,7 @@ title: "비용 (입력)", className: "dt-center", render: function(data, type, row) { - return ' 만원'; + return ' 만원'; } } ], @@ -321,55 +329,20 @@ }); // 연봉 정보 - let mockManpowerData2 = Object.keys(인력맵).map((key) => { + let manpowerData = Object.keys(인력맵).map((key) => { let data = {}; data.이름 = key; data.연봉 = 인력맵[key].연봉; data.성과 = 인력맵[key].성과; return data; }); - console.log(mockManpowerData2); - - var mockManpowerData = [ - { - "name": "홍길동", - "salary": "" - }, - { - "name": "이순신", - "salary": "" - }, - { - "name": "이순신", - "salary": "" - }, - { - "name": "유관순", - "salary": "" - }, - { - "name": "안중근", - "salary": "" - }, - { - "name": "세종대왕", - "salary": "" - } - /*['홍길동', '1.0', ''], - ['홍길동', '1.1', ''], - ['이순신', 'BaseVersion', ''], - ['이순신', '1.0', ''], - ['이순신', '24.01', ''], - ['유관순', '24.01', '']*/ - ]; - if ($.fn.dataTable.isDataTable('#manpower-annual-income')) { $('#manpower-annual-income').DataTable().clear().destroy(); } $('#manpower-annual-income').DataTable({ - data: mockManpowerData2, + data: manpowerData, columns: [ { name: "이름", @@ -406,37 +379,29 @@ function 비용분석계산() { $("#cost-analysis-calculation").click(function() { - /*$.ajax({ - url: - "/auth-user/api/arms/reqStatus/T_ARMS_REQSTATUS_" + - selectedPdServiceId + - "/getReqStatusListByFilter.do?c_req_pdservice_versionset_link=" + - selectedVersionId, - type: "GET", - contentType: "application/json;charset=UTF-8", - dataType: "json", - progress: true, - statusCode: { - 200: function (data) { - console.log(" [ analysisCost :: 비용분석계산 ] :: data => " ); - console.log(data); - } + let selectedVersions = selectedVersionId.split(','); // 문자열을 배열로 변환 + + let selectVersionData = []; + for (let i = 0; i < selectedVersions.length; i++) { + let item = versionListData[selectedVersions[i]]; + let inputVersionValue = $(`input[name="version-cost"][data-owner=${selectedVersions[i]}]`).val(); + let number = Number(inputVersionValue.replace(/,/g, '')); + + if (isNaN(Number(number))) { + alert("버전 비용 입력란에 숫자를 입력하여주세요."); + return; } - });*/ - // 버전 비용 계산 샘플 - versionListData = versionListData.map(item => { - item.versionCost = 10000000; + item.versionCost = number * 10000; item.consumptionCost = 9000000; - return item; - }); + selectVersionData.push(item); + } + $("#compare_costs").height("620px"); // 버전별 투자 대비 소모 비용 차트 - compareCostsChart(selectedPdServiceId, selectedVersionId); + compareCostsChart(selectVersionData); - - $("#circularPacking").height("620px"); // Circular Packing with D3 차트 var versionTag = $(".multiple-select").val(); @@ -457,17 +422,12 @@ reqCostAnalysisChart(버전별요구사항별); - let inputVersionValues = $('input[name="version-cost"]').map(function() { - return $(this).val(); - }).get(); - console.log(inputVersionValues); let inputSalaryValues = $('input[name="person-salary"]').map(function() { let data = {}; let owner = $(this).data('owner'); - console.log(owner); data.사용자 = owner; data.연봉 = $(this).val(); return data; @@ -497,21 +457,27 @@ } function clearChart(elementId) { - return echarts.init(document.getElementById(elementId)).clear(); + var element = document.getElementById(elementId); + if (element) { + var chartInstance = echarts.getInstanceByDom(element); + if (chartInstance) { + chartInstance.clear(); + } + } } ///////////////////////////////////////////////////////// // 투입 비용 현황 차트 ///////////////////////////////////////////////////////// -function compareCostsChart(selectedPdServiceId, selectedVersionId){ +function compareCostsChart(selectVersionData){ let chartDom = document.getElementById("compare_costs"); let myChart = echarts.init(chartDom); let option; - let titles = versionListData.map(item => item.c_title); + let titles = selectVersionData.map(item => item.c_title); - let versionCosts = versionListData.map(item => item.versionCost); + let versionCosts = selectVersionData.map(item => item.versionCost); - let consumptionCosts = versionListData.map(item => item.consumptionCost); + let consumptionCosts = selectVersionData.map(item => item.consumptionCost); option = { tooltip: { @@ -775,262 +741,208 @@ function reqCostAnalysisChart(버전별요구사항별) { - console.log(" [ analysisCost :: reqCostAnalysisChart :: 버전별요구사항별 data -> "); - console.log(버전별요구사항별); - console.log(selectedVersionId); + $.ajax({ + url: "/auth-user/api/arms/analysis/cost/T_ARMS_REQADD_" + selectedPdServiceId + + "/req-difficulty-priority-list?c_req_pdservice_versionset_link=" + selectedVersionId, + type: "GET", + dataType: "json", + progress: true, + statusCode: { + 200: function (data) { + console.log(" [ analysisCost :: reqCostAnalysisChart :: data -> "); + console.log(data); + let requirementJson = data.requirement; + let difficultyJson = data.difficulty; + let priorityJson = data. priority; - let requirementPriceList = { - 요구사항1: 10000000, - 요구사항2: 20000000, - 요구사항3: 30000000, - 요구사항4: 40000000, - 요구사항5: 50000000, - 요구사항6: 60000000, - 요구사항7: 30000000, - 요구사항8: 30000000, - 요구사항9: 30000000, - 요구사항11: 10000000, - 요구사항12: 20000000, - 요구사항13: 30000000, - 요구사항14: 40000000, - 요구사항15: 50000000, - 요구사항16: 60000000, - 요구사항17: 30000000, - 요구사항18: 30000000, - 요구사항19: 30000000, - 요구사항11: 10000000, - 요구사항12: 20000000, - 요구사항13: 30000000, - 요구사항14: 40000000, - 요구사항15: 50000000, - 요구사항16: 60000000, - 요구사항17: 30000000, - 요구사항18: 30000000, - 요구사항19: 30000000, - 요구사항20: 10000000, - 요구사항21: 20000000, - 요구사항22: 30000000, - 요구사항23: 40000000, - 요구사항24: 50000000, - 요구사항25: 60000000, - 요구사항26: 30000000, - 요구사항27: 30000000, - 요구사항28: 30000000, - 요구사항29: 10000000, - 요구사항30: 20000000, - 요구사항31: 30000000, - 요구사항32: 40000000, - 요구사항33: 50000000, - 요구사항34: 60000000, - 요구사항35: 30000000, - 요구사항36: 30000000, - 요구사항37: 30000000, - 요구사항38: 10000000, - 요구사항39: 20000000, - 요구사항40: 30000000 - }; + let requirementList = {}; + requirementJson.map(item => { + requirementList[item.c_title] = 1000000; + }); - let reqTotalPrice = 0; - for (let key in requirementPriceList) { - reqTotalPrice += requirementPriceList[key]; - } + let reqTotalPrice = 0; + for (let key in requirementList) { + reqTotalPrice += requirementList[key]; + } - let size = Object.keys(requirementPriceList).length; - let x = 1; + let requirementKeys = Object.keys(requirementList); + let requirementData = requirementKeys.map(key => requirementList[key]); + let requirementTotalData = requirementKeys.map(key => reqTotalPrice - requirementList[key]); - if (size > 0) { - x = (15 / size) * 100; - } + let difficultyData = Object.keys(difficultyJson).map(key => ({ + name: key.replace('.js', ''), + value: difficultyJson[key] + })); - console.log(" [ analysisCost :: reqCostAnalysisChart :: requirement total price -> " + reqTotalPrice); + let priorityData = Object.keys(priorityJson).map(key => ({ + name: key.replace('.js', ''), + value: priorityJson[key] + })); - let difficultyJson = { - '매우 어려움': 100, - '어려움': 200, - '보통': 300, - '쉬움': 200, - '매움 쉬움': 100 - }; + let size = requirementKeys.length; + let x = 1; - let priorityJson = { - '1순위': 10, - '2순위': 20, - '3순위': 30, - '4순위': 20, - '5순위': 10 - }; + if (size > 0) { + x = (15 / size) * 100; + } - var dom = document.getElementById('req-cost-analysis-chart'); - var myChart = echarts.init(dom, null, { - renderer: 'canvas', - useDirtyRect: false - }); - var app = {}; + var dom = document.getElementById('req-cost-analysis-chart'); + var myChart = echarts.init(dom, null, { + renderer: 'canvas', + useDirtyRect: false + }); + var app = {}; - var option; + var option; - const waterMarkText = ''; - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - canvas.width = canvas.height = 300; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.globalAlpha = 0.08; - ctx.font = '20px Microsoft Yahei'; - ctx.translate(50, 50); - ctx.rotate(-Math.PI / 4); - ctx.fillText(waterMarkText, 0, 0); - option = { - backgroundColor: { - type: 'pattern', - image: canvas, - repeat: 'repeat' - }, - tooltip: {}, - title: [ - { - // text: '요구사항', - subtext: '전체 ' + reqTotalPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원', - left: '25%', - textAlign: 'center', - textStyle: { - color: '#ffffff' // 제목의 색상을 하얀색으로 변경 - }, - subtextStyle: { - color: '#ffffff' // 부제목의 색상을 하얀색으로 변경 - } - }, - { - text: '난이도 및 우선순위 통계', - subtext: '', - left: '75%', - bottom: '0%', - textAlign: 'center', - textStyle: { - color: '#ffffff' // 제목의 색상을 하얀색으로 변경 - }, - subtextStyle: { - color: '#ffffff' // 부제목의 색상을 하얀색으로 변경 - } - }, - ], - grid: [ - { - top: 50, - left: '5%', // 차트의 왼쪽 여백을 늘려 슬라이더와 겹치지 않게 함 - right: '0%', // 차트의 오른쪽 여백 (필요에 따라 조정) - width: '50%', - bottom: '5%', - containLabel: true - }, - /* { - top: '55%', - width: '100%', - bottom: 0, - left: 10, + option = { + tooltip: { + confine: true + }, + title: [ + { + // text: '요구사항', + subtext: '전체 ' + reqTotalPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +'원', + left: '25%', + textAlign: 'center', + textStyle: { + color: '#ffffff' // 제목의 색상을 하얀색으로 변경 + }, + subtextStyle: { + color: '#ffffff' // 부제목의 색상을 하얀색으로 변경 + } + }, + { + text: '', + subtext: '난이도 및 우선순위 분포', + left: '80%', + bottom: '0%', + textAlign: 'center', + textStyle: { + color: '#ffffff' // 제목의 색상을 하얀색으로 변경 + }, + subtextStyle: { + color: '#ffffff' // 부제목의 색상을 하얀색으로 변경 + } + }, + ], + grid: [ + { + top: 50, + left: '5%', // 차트의 왼쪽 여백을 늘려 슬라이더와 겹치지 않게 함 + right: '0%', // 차트의 오른쪽 여백 (필요에 따라 조정) + width: '50%', + bottom: '5%', containLabel: true - }*/ - ], - xAxis: [ - { - type: 'value', - max: reqTotalPrice, - splitLine: { - show: false - }, - axisLabel: { - rotate: 45, - color: '#FFFFFFFF', - }, - }, - ], - yAxis: [ - { - type: 'category', - data: Object.keys(requirementPriceList), - splitLine: { - show: false - }, - axisLabel: { - color: '#FFFFFFFF', - rotate: 45, - }, - }, - ], - series: [ - { - type: 'bar', - stack: 'chart', - z: 3, - label: { - position: 'right', - show: true - }, - data: Object.keys(requirementPriceList).map(function (key) { - return requirementPriceList[key]; - }) - }, - { - type: 'bar', - stack: 'chart', - silent: true, - itemStyle: { - color: '#FFFFFF' - }, - data: Object.keys(requirementPriceList).map(function (key) { - return reqTotalPrice - requirementPriceList[key]; - }) - }, - { - type: 'pie', - radius: [0, '30%'], - center: ['75%', '25%'], - data: Object.keys(difficultyJson).map(function (key) { - return { - name: key.replace('.js', ''), - value: difficultyJson[key] - }; - }), - }, - { - type: 'pie', - radius: [0, '30%'], - center: ['75%', '65%'], - data: Object.keys(priorityJson).map(function (key) { - return { - name: key.replace('.js', ''), - value: priorityJson[key] - }; - }) - } - ], - dataZoom: [ - { - type: 'inside', - yAxisIndex: [0], // y축에만 dataZoom 기능 적용 - start: 0, - end: x - }, - { - show: true, - type: 'slider', - left: '0%', - backgroundColor: 'rgba(0,0,0,0)', // 슬라이더의 배경색 - dataBackgroundColor: 'rgba(255,255,255,1)', // 데이터 배경색 - yAxisIndex: [0], - start: 0, - end: x - } - ], + }, + /* { + top: '55%', + width: '100%', + bottom: 0, + left: 10, + containLabel: true + }*/ + ], + xAxis: [ + { + type: 'value', + max: reqTotalPrice, + splitLine: { + show: false + }, + axisLabel: { + rotate: 45, + color: '#FFFFFFFF', + }, + }, + ], + yAxis: [ + { + type: 'category', + data: requirementKeys, + splitLine: { + show: false + }, + axisLabel: { + color: '#FFFFFFFF', + rotate: 45, + formatter: function(value) { + // 최대 10자까지 표시 + if (value.length > 6) { + return value.substring(0, 6) + '...'; + } else { + return value; + } + }, + }, + }, + ], + series: [ + { + type: 'bar', + stack: 'chart', + z: 3, + label: { + position: 'right', + show: true + }, + data: requirementData + }, + { + type: 'bar', + stack: 'chart', + silent: true, + itemStyle: { + color: '#FFFFFF' + }, + data: requirementTotalData + }, + { + type: 'pie', + radius: [0, '25%'], + center: ['80%', '25%'], + data: difficultyData, + }, + { + type: 'pie', + radius: [0, '25%'], + center: ['80%', '65%'], + data: priorityData + } + ], + dataZoom: [ + { + type: 'inside', + yAxisIndex: [0], // y축에만 dataZoom 기능 적용 + start: 0, + end: x + }, + { + show: true, + type: 'slider', + left: '0%', + backgroundColor: 'rgba(0,0,0,0)', // 슬라이더의 배경색 + dataBackgroundColor: 'rgba(255,255,255,1)', // 데이터 배경색 + yAxisIndex: [0], + start: 0, + end: x + } + ], - }; + }; - if (option && typeof option === 'object') { - myChart.setOption(option); - } + if (option && typeof option === 'object') { + myChart.setOption(option); + } - window.addEventListener('resize', myChart.resize); + window.addEventListener('resize', myChart.resize); + } + }, + error: function (e) { + jError("버전 조회 중 에러가 발생했습니다."); + } + }); } function 인력별_연봉대비_성과차트_기본세팅(인력맵) {