coreDetailModal.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <template>
  2. <!-- CORE 개별 상세 팝업 : S -->
  3. <v-dialog
  4. v-model="isCoreDetailModal"
  5. persistent
  6. width="62.5rem"
  7. >
  8. <div class="v-common-dialog-wrapper custom-dialog alert">
  9. <div class="modal-tit">
  10. <strong>{{ coreInfo?.neName }}</strong>
  11. <button
  12. class="btn-close"
  13. @click="fnClose()"
  14. />
  15. </div>
  16. <div class="v-common-dialog-content pa-0">
  17. <div class="core--list--component">
  18. <h2>시스템 정보 및 KPI</h2>
  19. <!--
  20. contents critical, major, minor, normal 추가하여 사용
  21. -->
  22. <ul class="system--info">
  23. <li>
  24. <span class="titles">NE 유형</span>
  25. <span class="">{{ coreInfo?.neType }}</span>
  26. </li>
  27. <li>
  28. <span class="titles">고객 유형</span>
  29. <span class="">{{ coreInfo?.customerTypeName }}</span>
  30. </li>
  31. </ul>
  32. <ul class="system--info">
  33. <li>
  34. <span class="titles">STATUS</span>
  35. <span
  36. class="contents"
  37. :class="getBodyStatusClass(coreInfo?.connect)"
  38. >
  39. {{ coreInfo?.connect?.isConnected ? 'ACTIVE' : coreInfo?.connect?.reason.join(',') }}
  40. </span>
  41. </li>
  42. <li v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 0">
  43. <span class="titles">{{ coreInfo?.kpiItems[0].label }}</span>
  44. <span
  45. class="contents"
  46. :class="getBodyLevelClass(coreInfo?.kpiItems[0].val)"
  47. >{{ toRoundFix(coreInfo?.kpiItems[0].val, 2) }}%</span>
  48. </li>
  49. </ul>
  50. <ul
  51. v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 1"
  52. class="system--info"
  53. >
  54. <li v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 1">
  55. <span class="titles">{{ coreInfo?.kpiItems[1].label }}</span>
  56. <span
  57. class="contents"
  58. :class="getBodyLevelClass(coreInfo?.kpiItems[1].val)"
  59. >{{ toRoundFix(coreInfo?.kpiItems[1].val, 2) }}%</span>
  60. </li>
  61. <li v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 2">
  62. <span class="titles">{{ coreInfo?.kpiItems[2].label }}</span>
  63. <span
  64. class="contents"
  65. :class="getBodyLevelClass(coreInfo?.kpiItems[2].val)"
  66. >{{ toRoundFix(coreInfo?.kpiItems[2].val, 2) }}%</span>
  67. </li>
  68. </ul>
  69. <ul
  70. v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 3"
  71. class="system--info"
  72. >
  73. <li v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 3">
  74. <span class="titles">{{ coreInfo?.kpiItems[3].label }}</span>
  75. <span
  76. class="contents"
  77. :class="getBodyLevelClass(coreInfo?.kpiItems[3].val)"
  78. >{{ toRoundFix(coreInfo?.kpiItems[3].val, 2) }}%</span>
  79. </li>
  80. <li v-if="coreInfo?.kpiItems && coreInfo?.kpiItems.length > 4">
  81. <span class="titles">{{ coreInfo?.kpiItems[4].label }}</span>
  82. <span
  83. class="contents"
  84. :class="getBodyLevelClass(coreInfo?.kpiItems[4].val)"
  85. >{{ toRoundFix(coreInfo?.kpiItems[4].val, 2) }}%</span>
  86. </li>
  87. </ul>
  88. </div>
  89. <div class="core--list--component--grid">
  90. <div class="title">
  91. <h2>장애 이력 ({{ objCount.total }})</h2>
  92. <div class="status">
  93. <ul>
  94. <li class="critical">
  95. <span>CRITICAL</span>:<span>{{ objCount.critical }}</span>
  96. </li>
  97. <li class="minor">
  98. <span>MAJOR</span>:<span>{{ objCount.major }}</span>
  99. </li>
  100. <li class="major">
  101. <span>MINOR</span>:<span>{{ objCount.minor }}</span>
  102. </li>
  103. </ul>
  104. </div>
  105. </div>
  106. <div class="tbl-wrapper">
  107. <div class="tbl-wrap">
  108. <!-- ag grid -->
  109. <ag-grid-vue
  110. style="width:100%; height:calc(10 * 2.76rem);"
  111. class="ag-theme-quartz"
  112. :grid-options="gridOptions"
  113. :row-data="eventItems"
  114. :suppress-pagination-panel="true"
  115. @grid-ready="fnOnGridReady"
  116. />
  117. </div>
  118. </div>
  119. </div>
  120. </div>
  121. <div
  122. class="btn-wrap nw--btn--wrap"
  123. style="padding-top:1.88rem"
  124. >
  125. <div />
  126. <div class="inner--btn--wrap">
  127. <v-btn
  128. class="custom-btn btn-gray mini"
  129. @click="fnClose()"
  130. >
  131. <i class="ico" />
  132. 닫기
  133. </v-btn>
  134. </div>
  135. </div>
  136. </div>
  137. </v-dialog>
  138. <!-- CORE 개별 상세 팝업 : E -->
  139. </template>
  140. <script setup>
  141. import { useI18n } from "vue-i18n"
  142. import useAxios from '@/composables/useAxios';
  143. import useUtil from '@/composables/useUtil';
  144. import useErrorHandler from '@/composables/useErrorHandler';
  145. import useEnumCodeKr from '@/composables/useEnumCodeKr';
  146. import apiUrl from '@/composables/useApi';
  147. import dayjs from "#build/dayjs.imports.mjs";
  148. import "ag-grid-community/styles/ag-grid.css";
  149. import "ag-grid-community/styles/ag-theme-quartz.css";
  150. import { AgGridVue } from "ag-grid-vue3";
  151. import testJson from '@/components/home/dashboard/test.json'
  152. /***********************
  153. * plugins inject
  154. ************************/
  155. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp()
  156. // props
  157. const props = defineProps({
  158. isCoreDetailModal: {
  159. type: Boolean,
  160. default: false
  161. },
  162. })
  163. // 참조가능 데이터 설정
  164. defineExpose({
  165. fnInit,
  166. })
  167. // 발신 이벤트 선언
  168. const emit = defineEmits(["closeModal"]);
  169. const i18n = useI18n();
  170. /***********************
  171. * data & created
  172. ************************/
  173. // 모달 open 여부 from props
  174. const isCoreDetailModal = computed(() => props.isCoreDetailModal);
  175. // TenantName
  176. const tenantName = computed(() => useAuthStore().getTenantName);
  177. // 심각도 리스트
  178. const severityList = computed(() => useLangStore().getLang === 'kr' ? useEnumCodeKr.severity : useEnumCodeEn.severity)
  179. // 알람그룹 리스트
  180. const alarmGroupList = computed(() => useLangStore().getLang === 'kr' ? useEnumCodeKr.alarmGroup : useEnumCodeEn.alarmGroup)
  181. // 선택된 Core 정보
  182. const coreInfo = ref({});
  183. // 카운트
  184. const objCount = ref({
  185. total: 0,
  186. critical: 0,
  187. major: 0,
  188. minor: 0,
  189. })
  190. // 장애 이력 list
  191. const eventItems = ref([]);
  192. const remToPx = () => parseFloat(getComputedStyle(document.documentElement).fontSize);
  193. const rowHeightRem = 2.5; // 원하는 rem 값
  194. const rowHeightPx = rowHeightRem * remToPx();
  195. // gird API
  196. const gridApi = shallowRef();
  197. // gridOption
  198. const gridOptions = {
  199. columnDefs: [
  200. { headerName: 'No', valueGetter: "node.rowIndex + 1", maxWidth: 75},
  201. { headerName: 'Code',field: 'eventCode', maxWidth: 150},
  202. { headerName: '심각도', field: 'severity', maxWidth: 150, cellRenderer: fnSeverityCellRenderer},
  203. { headerName: '알람 그룹', field: 'alarmGroup', cellRenderer: fnAlarmGroupCellRenderer},
  204. { headerName: '위치', field: 'location', },
  205. { headerName: '알림 원인', field: 'probcause', },
  206. { headerName: '알람 시간', field: 'alarmTimeStr', cellRenderer: fnAlarmTimeCellRenderer},
  207. ],
  208. rowData: eventItems.value, // 테이블 데이터
  209. suppressMovableColumns: true,
  210. autoSizeStrategy: {
  211. type: "fitGridWidth", // width맞춤
  212. },
  213. headerHeight : rowHeightPx,
  214. rowHeight: rowHeightPx,
  215. suppressPaginationPanel: true, // 하단 default 페이징 컨트롤 숨김
  216. suppressRowClickSelection: true, // 행 클릭 체크박스 무시
  217. localeText: {
  218. noRowsToShow: i18n.t('common.noData')
  219. },
  220. defaultColDef: {
  221. lockVisible: true, // 열을 그리드 밖으로 꺼내지 않음
  222. },
  223. }
  224. /***********************
  225. * Methods
  226. ************************/
  227. /** 모달 시작 */
  228. function fnInit(item) {
  229. coreInfo.value = item;
  230. console.log(coreInfo.value)
  231. }
  232. /** 모달 닫기 */
  233. function fnClose(isReload = false) {
  234. emit('closeModal', {isReload: isReload})
  235. fnReset();
  236. }
  237. /** 초기화 */
  238. function fnReset() {
  239. // coreInfo.value = {};
  240. objCount.value.total = 0;
  241. objCount.value.critical = 0;
  242. objCount.value.major = 0;
  243. objCount.value.minor = 0;
  244. // eventItems.value=[];
  245. }
  246. /** 장애이력 조회 */
  247. function fnGetEventList() {
  248. const params = {
  249. tenantName: coreInfo.value?.tenantName || tenantName.value,
  250. neName: coreInfo.value?.neName || '',
  251. }
  252. useAxios().post(apiUrl.getCoreEventList, params).then((res) => {
  253. const {resCode, resMsg, data} = res.data;
  254. if(resCode == 200) {
  255. // fnParseData(testJson.coreEvent);
  256. fnParseData(data || {})
  257. $log.debug("[dashboard][fnGetEventList][success]")
  258. } else {
  259. $log.debug("[dashboard][fnGetEventList][error]", `[${resCode}] ${resMsg}`);
  260. }
  261. }).catch((error)=>{
  262. $log.debug("[dashboard][fnGetEventList][error]", error)
  263. useErrorHandler().fnSetCommErrorHandle(error, fnGetEventList)
  264. }).finally(()=>{
  265. $log.debug("[dashboard][fnGetEventList][finished]")
  266. })
  267. }
  268. /** 데이터 가공 */
  269. function fnParseData(data = {}) {
  270. // 카운트 셋팅
  271. objCount.value.critical = data?.criCnt || 0;
  272. objCount.value.major = data?.majCnt|| 0;
  273. objCount.value.minor = data?.minCnt || 0;
  274. objCount.value.total = data?.criCnt + data?.majCnt + data?.minCnt
  275. if(data.items) {
  276. eventItems.value = data?.items?.map((item) => {
  277. return {
  278. ...item,
  279. }
  280. })
  281. } else {
  282. eventItems.value = [];
  283. }
  284. }
  285. const toRoundFix = (data, digit) => parseFloat(data).toFixed(digit);
  286. // 세부 데이터 상태값
  287. const getBodyLevelClass = (val) => {
  288. if(val >= 95) return 'critical'; // red
  289. else if(val >= 90) return 'major'; // blue
  290. else if(val >= 85) return 'minor'; // gray
  291. else return 'normal'; // green
  292. }
  293. // 세부 데이터 STATUS 상태값
  294. const getBodyStatusClass = (connect) => {
  295. if(connect?.isConnected) return 'normal';
  296. else return 'critical';
  297. }
  298. /** Grid 관련 */
  299. // Grid 데이터 바인딩
  300. function fnOnGridReady(params){
  301. gridApi.value = params.api
  302. fnGetEventList();
  303. }
  304. // 심각도
  305. function fnSeverityCellRenderer(params) {
  306. const { data } = params;
  307. const { severity } = data;
  308. const severityLabel = severityList.value.find((s) => s.value == severity);
  309. const severityObj = {
  310. label: severityLabel.title.toUpperCase(),
  311. class: severityLabel.title.toLowerCase(),
  312. }
  313. return `<span class="status--ele ${severityObj.class}">${severityObj.label}</span>`
  314. }
  315. // 알람그룹
  316. function fnAlarmGroupCellRenderer(params) {
  317. const { data } = params;
  318. const { alarmGroup } = data;
  319. return alarmGroupList.value.find((a) => a.value == alarmGroup)?.title || '';
  320. }
  321. // 알람시간
  322. function fnAlarmTimeCellRenderer(params) {
  323. const { data } = params;
  324. const { alarmTime } = data;
  325. return dayjs(alarmTime).format('YYYY-MM-DD HH:mm:ss');
  326. }
  327. </script>