layout03Core.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <template>
  2. <div class="core--component--wrap core--tp">
  3. <!--현황 정보 영역 -->
  4. <div>
  5. <div class="inner--header--wrap">
  6. <h2 class="inner--component--title none--after">
  7. CORE
  8. </h2>
  9. <span class="inner--component--date">{{ current }}</span>
  10. </div>
  11. <div class="inner--content">
  12. <div class="oper--stat">
  13. <div class="card--title">
  14. <h3>운영 현황</h3>
  15. <p>Total : <span>{{ objCount.total }}</span></p>
  16. </div>
  17. <div class="card--cont">
  18. <div class="card">
  19. <div class="card--count">
  20. NE 수 : <span>{{ objCount.customer0 }}</span>
  21. </div>
  22. <div class="card--txt">
  23. 대내 <span>{{ objCount.customer0 }}</span>
  24. </div>
  25. </div>
  26. <div class="card">
  27. <div class="card--count">
  28. NE 수 : <span>{{ objCount.customer1 }}</span>
  29. </div>
  30. <div class="card--txt">
  31. 대외 <span>{{ objCount.customer1 }}</span>
  32. </div>
  33. </div>
  34. <div class="card">
  35. <div class="card--count">
  36. NE 수 : <span>{{ objCount.customer2 }}</span>
  37. </div>
  38. <div class="card--txt">
  39. 공공 <span>{{ objCount.customer2 }}</span>
  40. </div>
  41. </div>
  42. </div>
  43. </div>
  44. <!-- <div class="oper--stat">
  45. <div class="card--title">
  46. <h3>장비 상태</h3>
  47. <p>Total : <span>0</span></p>
  48. </div>
  49. <div class="card--alarm">
  50. <div class="card">
  51. <div class="ico" />
  52. <div class="alarm--txt">
  53. <p>알람 수신</p>
  54. <span>0</span>
  55. </div>
  56. </div>
  57. <div class="card no--alarm">
  58. <div class="ico" />
  59. <div class="alarm--txt">
  60. <p>알람 미수신</p>
  61. <span>0</span>
  62. </div>
  63. </div>
  64. </div>
  65. </div> -->
  66. <div class="link--stat discon">
  67. <div class="card--title">
  68. <h3>연동 상태</h3>
  69. </div>
  70. <div class="card--cont">
  71. <div class="ico" />
  72. <p>Disconnected</p>
  73. <span>{{ objCount.disconnected }}</span>
  74. </div>
  75. </div>
  76. </div>
  77. </div>
  78. <!-- 위젯 영역 -->
  79. <WidgetM
  80. v-if="isLoaded && widgetSize === 'M'"
  81. :items="coreItems"
  82. :total-cnt="objCount.total"
  83. />
  84. <WidgetS
  85. v-if="isLoaded && widgetSize === 'S'"
  86. :items="coreItems"
  87. :total-cnt="objCount.total"
  88. />
  89. </div>
  90. </template>
  91. <script setup>
  92. /***********************
  93. * import
  94. ************************/
  95. import { useI18n } from "vue-i18n"
  96. import apiUrl from '@/composables/useApi';
  97. import useAxios from '@/composables/useAxios';
  98. import useUtil from '@/composables/useUtil';
  99. import dayjs from "#build/dayjs.imports.mjs";
  100. import WidgetM from "@/components/home/dashboard/layout03/core/layout03CoreWidgetM.vue"
  101. import WidgetS from "@/components/home/dashboard/layout03/core/layout03CoreWidgetS.vue"
  102. import testJson from "../../test.json"
  103. /***********************
  104. * plugins inject
  105. ************************/
  106. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp()
  107. // props
  108. const props = defineProps({
  109. config: {
  110. type: Object,
  111. default: () => {}
  112. },
  113. intervalTime: {
  114. type: Number,
  115. default: 5000
  116. }
  117. })
  118. // 참조가능 데이터 설정
  119. defineExpose({})
  120. // 발신 이벤트 선언
  121. const emit = defineEmits([""]);
  122. const i18n = useI18n();
  123. /***********************
  124. * data & created
  125. ************************/
  126. // TenantName
  127. const tenantName = computed(() => useAuthStore().getTenantName);
  128. // 설정된 위젯 사이즈 : M or S
  129. const widgetSize = computed(() => props.config?.widgetSize || 'M');
  130. // Swiper 표시 slide 갯수
  131. const slideItemSize = computed(() => widgetSize.value == 'M' ? 4 : 8)
  132. // 현재 일시
  133. const current = ref(dayjs().format('YYYY-MM-DD HH:mm:ss'));
  134. // 카운트 정보
  135. const objCount = ref({
  136. total: 0,
  137. customer0: 0,
  138. customer1: 0,
  139. customer2: 0,
  140. disconnected: 0,
  141. })
  142. // 코어 리스트
  143. const coreItems = ref([]);
  144. // 데이터 조회 여부
  145. const isLoaded = ref(false);
  146. /***********************
  147. * Methods
  148. ************************/
  149. /**
  150. * 리셋
  151. */
  152. const fnReset = () => {
  153. objCount.value.total = 0;
  154. objCount.value.customer0 = 0;
  155. objCount.value.customer1 = 0;
  156. objCount.value.customer2 = 0;
  157. objCount.value.disconnected = 0;
  158. current.value = dayjs().format('YYYY-MM-DD HH:mm:ss');
  159. }
  160. /**
  161. * 코어 리스트 조회
  162. */
  163. function fnGetCoreList() {
  164. const params = {
  165. tenantName: tenantName.value,
  166. }
  167. isLoaded.value = false;
  168. useAxios().post(apiUrl.getCoreInfoList, params).then((res) => {
  169. const {resCode, resMsg, data} = res.data;
  170. if(resCode == 200) {
  171. // 테스트를 위한 테스트 코드 (삭제 필수)
  172. // fnParseData([...(data?.items || []), ...testJson.core.data.items])
  173. fnParseData(data.items || [])
  174. $log.debug("[dashboard][fnGetCoreList][success]")
  175. } else {
  176. $log.debug("[dashboard][fnGetCoreList][error]", `[${resCode}] ${resMsg}`);
  177. }
  178. }).catch((error)=>{
  179. // 테스트를 위한 테스트 코드 (삭제 필수)
  180. // fnParseData([...testJson.core.data.items])
  181. $log.debug("[dashboard][fnGetCoreList][error]", error)
  182. useErrorHandler().fnSetCommErrorHandle(error, fnGetCoreList)
  183. }).finally(()=>{
  184. $log.debug("[dashboard][fnGetCoreList][finished]")
  185. isLoaded.value = true;
  186. })
  187. }
  188. /**
  189. * 데이터 가공
  190. */
  191. function fnParseData(items = []) {
  192. coreItems.value = [];
  193. if(!items || items.length < 1) {
  194. objCount.value.total = 0;
  195. objCount.value.customer0 = 0;
  196. objCount.value.customer1 = 0;
  197. objCount.value.customer2 = 0;
  198. objCount.value.disconnected = 0;
  199. return;
  200. }
  201. let temp = [];
  202. // 정렬 적용
  203. temp = items.sort((a, b) => {
  204. // 위험도순
  205. return a.criCnt < b.criCnt ? 1 : a.criCnt > b.criCnt ? -1 : (a.majCnt < b.majCnt ? 1 : a.majCnt > b.majCnt ? -1 : (a.minCnt < b.minCnt ? 1 : a.minCnt > b.minCnt ? -1 : 0));
  206. })
  207. // 데이터 가공
  208. temp = temp.map((item) => {
  209. //카운트 셋팅
  210. const { customerType } = item;
  211. objCount.value.total = objCount.value.total+1;
  212. if(customerType == 0) objCount.value.customer0 = objCount.value.customer0 + 1;
  213. else if(customerType == 1) objCount.value.customer1 = objCount.value.customer1 + 1;
  214. else if(customerType == 2) objCount.value.customer2 = objCount.value.customer2 + 1;
  215. // KPI 정보 셋팅 - TODO: CPU, MEM 외의 값이 추가되면 여기에 추가
  216. const { cpu, mem } = item;
  217. const kpiItems = [];
  218. if(cpu) {
  219. const objCpu = typeof cpu === 'string' ? JSON.parse(cpu) : cpu;
  220. if(objCpu && objCpu[`AVG_CPU_L(%)`]){
  221. kpiItems.push({label: 'CPU', val: objCpu[`AVG_CPU_L(%)`] || 0})
  222. item.cpu = objCpu;
  223. }
  224. }
  225. if(mem) {
  226. const objMem = typeof mem === 'string' ? JSON.parse(mem) : mem ;
  227. if(objMem && objMem['AVG_MEM_L(%)']){
  228. kpiItems.push({label: 'MEMORY', val: objMem['AVG_MEM_L(%)'] || 0})
  229. item.mem = objMem;
  230. }
  231. }
  232. // disconected 셋팅
  233. const { emsStatus, mcmStatus, psmStatus } = item;
  234. const connect = {
  235. isConnected: false,
  236. reason: [],
  237. }
  238. if([emsStatus, mcmStatus, psmStatus].every((status) => status == 0)) {
  239. connect.isConnected = true;
  240. } else {
  241. // disconnected 카운트 셋팅
  242. objCount.value.disconnected = objCount.value.disconnected + 1;
  243. connect.isConnected = false;
  244. if(emsStatus != 0 ) connect.reason.push(`emsSatats: ${emsStatus}`);
  245. if(mcmStatus != 0 ) connect.reason.push(`mcmStatus: ${mcmStatus}`);
  246. if(psmStatus != 0 ) connect.reason.push(`psmStatus: ${psmStatus}`);
  247. }
  248. // 심각도 level 셋팅
  249. const { criCnt ,majCnt, minCnt } = item;
  250. let level = '';
  251. if(criCnt > 0) level = 'CRITICAL';
  252. else if(majCnt > 0) level = 'MAJOR';
  253. else if(minCnt > 0) level = 'MINOR';
  254. return {
  255. ...item,
  256. kpiItems, // kpi 정보
  257. connect, // 연결 상태
  258. level, // 심각도 레벨
  259. }
  260. });
  261. coreItems.value = splitIntoChunk(temp, slideItemSize.value)
  262. }
  263. /** Array Spliter */
  264. function splitIntoChunk(arr, chunk) {
  265. // 빈 배열 생성
  266. const result = [];
  267. for (let index=0; index < arr.length; index += chunk) {
  268. // slice() 메서드를 사용하여 특정 길이만큼 배열을 분리함
  269. const tempArray = arr.slice(index, index + chunk);
  270. // 빈 배열에 특정 길이만큼 분리된 배열을 추가
  271. result.push(tempArray);
  272. }
  273. return result;
  274. }
  275. onMounted(() => fnGetCoreList())
  276. // 위젯사이즈 변경 watch
  277. watch(() => widgetSize.value, () => {
  278. fnReset();
  279. fnGetCoreList();
  280. }, {deep: true})
  281. </script>