Index: arms/html/analysisScope/content-container.html =================================================================== diff -u -r4f3c6dd2aae92e35a61739cca20ae43657885344 -rdcf8ad1b1d664764037950914227f27e16973c43 --- arms/html/analysisScope/content-container.html (.../content-container.html) (revision 4f3c6dd2aae92e35a61739cca20ae43657885344) +++ arms/html/analysisScope/content-container.html (.../content-container.html) (revision dcf8ad1b1d664764037950914227f27e16973c43) @@ -990,7 +990,7 @@ class="font13" style="font-weight: bold"> - Circle Packing with d3 chart + 버전별 진행중 요구사항 상태 및 관여한 작업자수 현황
@@ -1041,15 +1041,14 @@ class="gradient_middle_border" style="width: 100%; margin-top: 8px">
- 이번 버전에 처리한 요구사항은 어떤것이고, 이번 버전에 처리하지 못한 요구사항은 어떤것인지 (Text 색으로 - 표시) + 선택한 버전에 진행중인(담당자 존재) 요구사항과 해당 요구사항의 진행 상태
- 버블 하나당 연관된 사람 수가 많을 수록 크게. + 요구사항에 관련된 작업자 수에 비례하여 원의 지름이 커집니다.
+ style="min-height: 550px;"> Index: arms/js/analysis/resource/chart/RadialPolarBarChart.js =================================================================== diff -u -rdff1566fe129df54642a59433f1a91c97bca2a09 -rdcf8ad1b1d664764037950914227f27e16973c43 --- arms/js/analysis/resource/chart/RadialPolarBarChart.js (.../RadialPolarBarChart.js) (revision dff1566fe129df54642a59433f1a91c97bca2a09) +++ arms/js/analysis/resource/chart/RadialPolarBarChart.js (.../RadialPolarBarChart.js) (revision dcf8ad1b1d664764037950914227f27e16973c43) @@ -188,8 +188,7 @@ } function drawChartWithFooter(dataArr,total) { - const existingChartFooter = document.querySelector('.chart-footer'); - + const existingChartFooter = document.querySelector('#'+target+' .chart-footer'); if (existingChartFooter) { existingChartFooter.remove(); } @@ -208,7 +207,7 @@ chartDom.appendChild(chartFooter); - const footerItems = document.querySelectorAll('.footer-item'); + const footerItems = document.querySelectorAll('#'+target+' .chart-footer .footer-item'); const itemCount = footerItems.length; const remainder = itemCount % 3; Index: arms/js/analysis/resource/chart/circularPackingChart.js =================================================================== diff -u -r50887001c32e1e8df91464e5ef472917cbe9f73f -rdcf8ad1b1d664764037950914227f27e16973c43 --- arms/js/analysis/resource/chart/circularPackingChart.js (.../circularPackingChart.js) (revision 50887001c32e1e8df91464e5ef472917cbe9f73f) +++ arms/js/analysis/resource/chart/circularPackingChart.js (.../circularPackingChart.js) (revision dcf8ad1b1d664764037950914227f27e16973c43) @@ -169,6 +169,22 @@ encode: { tooltip: 'value', itemName: 'id' + }, + itemStyle: { + color: function(params) { + // 예시: value (사람수) 를 기준으로. + if (params.data.value % 5 === 4) { + return "rgba(255,255,51,0.71)"; + } else if (params.data.value % 5 ===3) { + return "rgba(151,78,163,0.73)"; + } else if(params.data.value % 5 === 2) { + return "rgba(77,175,74,0.65)"; + } else if(params.data.value % 5 === 1) { + return "rgba(255,127,0,0.7)"; + } else { + return "rgba(55,125,184,0.62)"; + } + } } } }; @@ -205,16 +221,46 @@ }); } -function drawCircularPacking(target, psServiceName,rawData) { +function drawCircularPacking(target, psServiceName,rawData, issueStatusList, colorArr) { var chartDom = document.getElementById(target); var myChart = echarts.init(chartDom); var option; + + let reqCount = 0; + let statusCounts = {}; + let statusDataArr = []; + + var defaultColorSet = [ + "rgba(255,255,51,0.71)", + "rgba(151,78,163,0.73)", + "rgba(77,175,74,0.65)", + "rgba(255,127,0,0.7)", + "rgba(55,125,184,0.62)", + "rgba(166,86,40,0.7)", + "rgba(227,26,27,0.66)" + ]; + if(rawData) { run(rawData); } + function run(rawData) { const dataWrap = prepareData(rawData); console.log(dataWrap); + dataWrap.seriesData.forEach(element => { + if (element["depth"] === 2) { + reqCount++; // 총 활성 요구사항 수 + const status = element["status"]; + if (!statusCounts[status]) { + statusCounts[status] = 1; + } else { + statusCounts[status]++; + } + } + }); + console.log(statusCounts); + statusDataArr = Object.entries(statusCounts).map(([key, value]) => ({ name: key, value })); + console.log(statusDataArr); initChart(dataWrap.seriesData, dataWrap.maxDepth); } function prepareData(rawData) { @@ -242,7 +288,7 @@ } } } - convert(rawData, psServiceName, 0); // 첫번째 Node 이름 설정. + convert(rawData, psServiceName, 0); // raw데이터 삽입,첫번째 Node 이름 설정. return { seriesData: seriesData, maxDepth: maxDepth @@ -347,6 +393,7 @@ } }; } + option = { dataset: { source: seriesData @@ -358,9 +405,7 @@ min: 0, max: maxDepth, dimension: 'depth', - inRange: { - //color: ["#182E3D", "#4A8FBD", "#82A5BD", "#5E7788", "#5D8AA8", "#2c5571"]//['#006edd', '#e0ffff'] - } + inRange: {} // 색 일괄 지정을 하지 않아도. 빈값으로 두어야 합니다. } ], hoverLayerThreshold: Infinity, @@ -369,34 +414,68 @@ renderItem: renderItem, progressive: 0, coordinateSystem: 'none', - encode: { - tooltip: 'value', - itemName: 'id' - }, itemStyle: { color: function(params) { - // 여기에서 각 항목에 특정 색상을 지정하는 함수를 정의할 수 있습니다. - // 데이터에 액세스하고 조건 또는 로직에 따라 색상을 할당할 수 있습니다. - // 예시: - if (params.data.value % 5 === 4) { - return "rgba(255,255,51,0.71)"; // 값이 50보다 큰 경우 빨간색으로 지정 - } else if (params.data.value % 5 ===3) { - return "rgba(151,78,163,0.73)"; - } else if(params.data.value % 5 === 2) { - return "rgba(77,175,74,0.65)"; - } else if(params.data.value % 5 === 1) { - return "rgba(255,127,0,0.7)"; + if (params.data.value) { + + return defaultColorSet[issueStatusList.indexOf(params.data.status)]; } else { - return "rgba(55,125,184,0.62)"; // 기타 값은 초록색으로 지정 + return "rgba(55,125,184,0.62)"; } } + }, + tooltip: { + formatter: function(params) { + // params.value에는 원본 값이 들어있을 것입니다. 여기에 단위를 붙여 반환하면 됩니다. + if(params.data.value) { + return `${params.data.id}
+ - 상태 : ${params.data.status}
+ - 작업자 : ${params.data.value} 명`; + } else { + return `${params.data.id}`; + } + } } - } + }, + graphic: [ + { + type: 'group', + left: 20, + top: 20, + children: [ + { + type: 'text', + z: 100, + left: 0, + top: 0, + style: { + text: [ + '{a| Total }', + '{a| ' + reqCount + '}' + ].join('\n'), + rich: { + a: { + fontSize: 13, + fontWeight: 'bold', + lineHeight: 20, + fontFamily: 'Arial', + fill: 'white' + } + } + } + } + ] + } + ] }; - myChart.setOption(option); + + option && myChart.setOption(option, true); + drawChartWithFooter(statusDataArr,reqCount); + myChart.on('click', { seriesIndex: 0 }, function (params) { drillDown(params.data.id); }); + function drillDown(targetNodeId) { displayRoot = stratify(); if (targetNodeId != null) { @@ -420,7 +499,54 @@ }); } - option && myChart.setOption(option, true); + function replaceNaN(value) { + if (isNaN(value)) { + return " - "; + } else { + return value; + } + } + + function drawChartWithFooter(dataArr,total) { + console.log("drawChartWithFooter 호출"); + const existingChartFooter = document.querySelector('#'+target+' .chart-footer'); + + if (existingChartFooter) { + existingChartFooter.remove(); + } + + const chartFooter = document.createElement("div"); + chartFooter.classList.add("chart-footer"); + + dataArr.forEach((data,index) => { + const item = document.createElement("div"); + const portion =replaceNaN(+(data.value*100/ +total).toFixed(0)); + item.classList.add("footer-item"); + item.style.borderColor = defaultColorSet[index]; + item.innerHTML = `
${data.name}
${data.value} (${portion}%)
`; + chartFooter.appendChild(item); + }); + + chartDom.appendChild(chartFooter); + + const footerItems = document.querySelectorAll('#'+target+' .chart-footer .footer-item'); + const itemCount = footerItems.length; + const remainder = itemCount % 4; //나머지 + const quotient = Math.floor(itemCount / 4); // 몫 + + footerItems.forEach((item, index) => { + if (remainder === 1 && Math.floor(index / 4) === quotient) { + item.style.width = '100%'; + } else if (remainder === 2 && Math.floor(index / 4) >= quotient) { + item.style.width = '50%'; + } else if (remainder === 3 && Math.floor(index / 4) >= quotient) { + item.style.width = '33.33%'; + } else { // 나머지 0 + item.style.width = '25%'; + } + }); + } + window.addEventListener('resize', function () { myChart.resize(); }); Index: arms/js/analysisScope.js =================================================================== diff -u -r50887001c32e1e8df91464e5ef472917cbe9f73f -rdcf8ad1b1d664764037950914227f27e16973c43 --- arms/js/analysisScope.js (.../analysisScope.js) (revision 50887001c32e1e8df91464e5ef472917cbe9f73f) +++ arms/js/analysisScope.js (.../analysisScope.js) (revision dcf8ad1b1d664764037950914227f27e16973c43) @@ -1152,26 +1152,9 @@ } }); - let reqStatusList = []; - console.log("getReqStatusAndAssignees :: getReqStatus ==> 시작"); - getReqStatus(pdServiceLink, pdServiceVersionLinks, function(result) { - if (result != null) { - reqStatusList = result; - } else { - console.error("getReqStatus AJAX 요청에서 에러 발생"); - } - }); - console.log("getReqStatusAndAssignees :: getReqStatus ==> "); - console.log(reqStatusList); - - let 버전별_검색결과_목록 = reqStatusList["검색결과"]["group_by_pdServiceVersion"]; - - if(버전별_검색결과_목록 && 버전별_검색결과_목록.length > 0) { - - } - - let dataObject = {}; + let issueStatusSet = new Set(); + let issueStatusList = []; if (result.length > 0) { for (let i = 0; i < result.length; i++) { // 버전이름 가져오기 @@ -1187,17 +1170,20 @@ result[i]["요구사항들"].forEach((element) => { // 작업자수가 0이 아닌 요구 사항만 (담당자 배정된 요구사항만) if (element["작업자수"] !== 0) { - verSubObject[element["요구_사항_번호"]] = {"$count" : element["작업자수"]}; + verSubObject[element["요구_사항_번호"]] = + {"$count" : element["작업자수"], "$status" : element["요구_사항_상태"]}; + issueStatusSet.add(element["요구_사항_상태"]); } }); dataObject[versionName] = verSubObject; } } - - + issueStatusSet.forEach(e=>issueStatusList.push(e)); console.log("getReqStatusAndAssignees :: dataObject ==> "); console.log(dataObject); - drawCircularPacking("circularPacking",pdServiceName,dataObject); + console.log("getReqStatusAndAssignees :: issueStatusSet"); + console.log(issueStatusSet.size); console.log(issueStatusList); + drawCircularPacking("circularPacking",pdServiceName,dataObject, issueStatusList); } } }); @@ -1209,11 +1195,12 @@ url: "/auth-user/api/arms/analysis/scope/getReqStatus/"+pdServiceId, type: "GET", data: { - 서비스아이디: pdServiceId, + "서비스아이디": pdServiceId, pdServiceVersionLinks: pdServiceVersionLinks, - 메인그룹필드: "pdServiceVersion", - 하위그룹필드들: "key,status.status_name.keyword", - 컨텐츠보기여부: true + "메인그룹필드": "pdServiceVersion", + "하위그룹필드들": "key,status.status_name.keyword", + "isReq" : false, + "컨텐츠보기여부": true }, contentType: "application/json;charset=UTF-8", dataType: "json",