layout03CoreWidgetS.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <div>
  3. <div class="inner--header--wrap">
  4. <h2
  5. v-if="totalPage > 0"
  6. class="inner--component--title none--after"
  7. >
  8. 장비별 KPI ({{ page || 0 }}/{{ totalPage }})
  9. </h2>
  10. <h2 v-else>
  11. 장비별 KPI
  12. </h2>
  13. <p class="inner--component--total">
  14. Total : <span>{{ props.totalCnt }}</span>
  15. </p>
  16. </div>
  17. <div class="inner--content df--block pt--1rem swiper--view--2">
  18. <swiper
  19. :autoplay="{
  20. delay: 20000,
  21. disableOnInteraction: false,
  22. }"
  23. :loop="true"
  24. :touch-ratio="0"
  25. :slides-per-view="1"
  26. :modules="[Autoplay]"
  27. @slide-change="onSlideChange"
  28. >
  29. <swiper-slide
  30. v-for="(slide, index) in props.items"
  31. :key="`core-swiper-slide-${index}`"
  32. >
  33. <div class="equip--card--wrap">
  34. <div
  35. v-for="(item, idx) in fnSplitArraySniffling(slide, 0)"
  36. :key="`core-swiper-slide-item-${idx}`"
  37. class="equip--card"
  38. :class="getLevelClass(item.connect, item.level)"
  39. >
  40. <div class="equip--name">
  41. {{ item.neName }}
  42. </div>
  43. <ul class="equip--st">
  44. <!-- <li><i class="circle red" /><p>STATUS</p><span class="active">Active</span></li> -->
  45. <li>
  46. <i
  47. class="circle"
  48. :class="getBodyStatusClass(item.connect)"
  49. />
  50. <p>STATUS</p>
  51. <span class="active">{{ item.connect.isConnected ? 'ACTIVE' : item.connect.reason.join(',') }}</span>
  52. </li>
  53. <li
  54. v-for="(kpi, idx) in item.kpiItems"
  55. :key="`core-widget-${index}-${idx}`"
  56. >
  57. <i
  58. class="circle"
  59. :class="getBodyLevelClass(kpi.val)"
  60. />
  61. <p>{{ kpi.label }}</p>
  62. <span>{{ toRoundFix(kpi.val, 2) }}%</span>
  63. </li>
  64. </ul>
  65. </div>
  66. </div>
  67. <div class="equip--card--wrap">
  68. <div
  69. v-for="(item, idx) in fnSplitArraySniffling(slide, 1)"
  70. :key="`core-swiper-slide-item-${idx}`"
  71. class="equip--card"
  72. :class="getLevelClass(item.connect, item.level)"
  73. >
  74. <div class="equip--name">
  75. {{ item.neName }}
  76. </div>
  77. <ul class="equip--st">
  78. <!-- <li><i class="circle red" /><p>STATUS</p><span class="active">Active</span></li> -->
  79. <li>
  80. <i
  81. class="circle"
  82. :class="getBodyStatusClass(item.connect)"
  83. />
  84. <p>STATUS</p>
  85. <span class="active">{{ item.connect.isConnected ? 'ACTIVE' : item.connect.reason.join(',') }}</span>
  86. </li>
  87. <li
  88. v-for="(kpi, idx) in item.kpiItems"
  89. :key="`core-widget-${index}-${idx}`"
  90. >
  91. <i
  92. class="circle"
  93. :class="getBodyLevelClass(kpi.val)"
  94. />
  95. <p>{{ kpi.label }}</p>
  96. <span>{{ toRoundFix(kpi.val, 2) }}%</span>
  97. </li>
  98. </ul>
  99. </div>
  100. </div>
  101. </swiper-slide>
  102. </swiper>
  103. </div>
  104. </div>
  105. </template>
  106. <script setup>
  107. /***********************
  108. * import
  109. ************************/
  110. import { useI18n } from "vue-i18n"
  111. import apiUrl from '@/composables/useApi';
  112. import useAxios from '@/composables/useAxios';
  113. import useUtil from '@/composables/useUtil';
  114. import { Swiper, SwiperSlide } from 'swiper/vue';
  115. import { Navigation, Pagination, Autoplay } from 'swiper/modules';
  116. import 'swiper/css';
  117. import 'swiper/swiper-bundle.css'
  118. /***********************
  119. * plugins inject
  120. ************************/
  121. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp()
  122. // props
  123. const props = defineProps({
  124. items: {
  125. type: Array,
  126. default: () => []
  127. },
  128. totalCnt: {
  129. type: Number,
  130. default: 0
  131. }
  132. })
  133. // 참조가능 데이터 설정
  134. defineExpose({})
  135. // 발신 이벤트 선언
  136. const emit = defineEmits([""]);
  137. const i18n = useI18n();
  138. /***********************
  139. * data & created
  140. ************************/
  141. const page = ref(1);
  142. const totalPage = computed(() => Math.floor(props.totalCnt / 8) + (props.totalCnt % 8 == 0 ? 0 : 1))
  143. /***********************
  144. * Methods
  145. ************************/
  146. /** Slide onChang Event */
  147. function onSlideChange(swiper) {
  148. if(swiper.realIndex != page.value + 1)
  149. page.value = swiper.realIndex + 1
  150. }
  151. const toRoundFix = (data, digit) => parseFloat(data).toFixed(digit);
  152. // 레벨 클래스 : CRITICAL, MAJOR , MINOR
  153. const getLevelClass = (connect, level) => {
  154. if(!connect.isConnected) return 'discon';
  155. else if(level === 'CRITICAL') return 'critical';
  156. else if(level === 'MAJOR') return 'major';
  157. else if(level === 'MINOR') return 'minor';
  158. else return 'normal';
  159. }
  160. // 세부 데이터 상태값
  161. const getBodyLevelClass = (val) => {
  162. if(val >= 95) return 'critical'; // red
  163. else if(val >= 90) return 'major'; // brown
  164. else if(val >= 85) return 'minor'; // yellow
  165. else return 'normal'; // green
  166. }
  167. // 세부 데이터 STATUS 상태값
  168. const getBodyStatusClass = (connect) => {
  169. if(connect.isConnected) return 'normal';
  170. else return 'critical';
  171. }
  172. // swiper item filter by 홀수, 짝수
  173. const fnSplitArraySniffling = (arr = [], sniffling) => {
  174. return arr.filter((item, idx) => {
  175. // 짝수
  176. if(sniffling == 0) return idx % 2 == 0
  177. else return idx % 2 != 0;
  178. })
  179. }
  180. </script>