| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- <template>
- <!-- 지도형 3Depth 정보 팝업 : S -->
- <v-dialog
- v-model="isModal"
- persistent
- width="62.5rem"
- >
- <div class="v-common-dialog-wrapper custom-dialog alert">
- <div class="modal-tit">
- <strong>{{propsObj.areaName}} {{propsObj.areaCode !== 0 ? '현황' : ''}}</strong>
- <button class="btn-close" @click="fnClose"></button>
- </div>
-
- <div class="v-common-dialog-content pa-0">
- <div class="core--list--component">
- <h2 class="fw--500">이벤트 현황</h2>
- <ul class="event--stat">
- <li class="critical"><i class="ico"></i><span>CRITICAL</span><span>{{propsObj.criCnt}}</span></li>
- <li class="major"><i class="ico"></i><span>MAJOR</span><span>{{propsObj.majCnt}}</span></li>
- <li class="minor"><i class="ico"></i><span>MINOR</span><span>{{propsObj.minCnt}}</span></li>
- <li class="disconnected"><i class="ico"></i><span>DISCONNECTED</span><span>0</span></li>
- </ul>
- </div>
- <div class="core--list--component--grid mt--0">
- <div class="title">
- <h2>
- NE 그룹 목록 <span>({{propsObj.neGroupList.length}}건)</span>
- </h2>
- </div>
- <div class="tbl-wrapper">
- <div class="tbl-wrap">
- <!-- ag grid -->
- <ag-grid-vue
- style="width:100%;"
- :style="propsObj.neGroupList.length > 2 ? 'height: calc(23vh)' : 'height: calc(15vh);'"
- class="ag-theme-quartz"
- :gridOptions="gridOptions"
- @grid-ready="fnOnGridReady"
- @rowClicked="fnRowClick"
- >
- </ag-grid-vue>
- </div>
- </div>
- </div>
- <div class="core--list--component">
- <div class="map--area">
- <div class="side--title" style="zIndex:10" v-if="selectedGroup.neGroup">{{selectedGroup.neGroup}}</div>
- <!--맵 영역-->
- <div id="ran_detail_map" style="height: 100%;"></div>
- </div>
- </div>
-
- <div class="core--list--component--grid mt--0">
- <div class="title">
- <h2 v-if="selectedGroup.neList.length">
- [{{selectedGroup.neGroup}}] NE 목록 <span>({{selectedGroup.neList.length}}건)</span>
- </h2>
- <h2 v-else>
- NE 목록 <span>(0건)</span>
- </h2>
- </div>
- <div class="tbl-wrapper">
- <div class="tbl-wrap">
- <!-- ag grid -->
- <ag-grid-vue
- style="width:100%; height:22vh;"
- class="ag-theme-quartz"
- :gridOptions="gridOptions2"
- @grid-ready="fnOnGridReady2"
- >
- </ag-grid-vue>
- </div>
- </div>
- </div>
- </div>
- <div class="btn-wrap nw--btn--wrap" style="padding-top:1.88rem">
- <div></div>
- <div class="inner--btn--wrap">
- <v-btn
- class="custom-btn btn-gray mini"
- @click="fnClose"
- >
- <i class="ico"></i>
- 닫기
- </v-btn>
- </div>
- </div>
- </div>
- </v-dialog>
- <!-- 카드형 2Depth 정보 팝업 : E -->
- </template>
- <script setup>
- /***********************
- * import
- ************************/
- import "ag-grid-community/styles/ag-grid.css";
- import "ag-grid-community/styles/ag-theme-quartz.css";
- import { AgGridVue } from "ag-grid-vue3";
- import { useI18n } from "vue-i18n";
- import useUtil from "@/composables/useUtil";
- /***********************
- * plugins inject
- ************************/
- const { $toast, $log, $dayjs, $eventBus } = useNuxtApp()
- // props
- const props = defineProps({
- propsObj: {
- type: Object,
- default: function () {
- return {
- areaName: '',
- }
- },
- },
- centerPosition: Object
- })
- // 참조가능 데이터 설정
- defineExpose({})
- // 발신 이벤트 선언
- const emit = defineEmits(["closeModal"])
- const i18n = useI18n()
-
- /***********************
- * data & created
- ************************/
- const isModal = ref(true)
- const map = ref(null) // 카카오 맵 객체
- const marker = ref(null) // 카카오맵 마커(핀) 객체
- const remToPx = () => parseFloat(getComputedStyle(document.documentElement).fontSize)
- const rowHeightRem = 2.5 // 원하는 rem 값
- const rowHeightPx = rowHeightRem * remToPx()
- const gridApi = shallowRef()
- const gridApi2 = shallowRef()
- const selectedGroup = ref({
- neGroup: '',
- neList: []
- })
- const setNeGroup = computed(() => {
- let list = []
- props.propsObj.neGroupList.forEach((item, idx) => {
- let obj = item
- obj.no = idx + 1
- obj.cls = getNeEventCls.value(item)
- list.push(obj)
- })
- return list
- })
- // NE 그룹 수 > 이벤트 단계 클래스
- const getNeEventCls = computed(() => {
- return (obj) => {
- let eventCls = 'gray'
- if(!_isEmpty(obj)) {
- if(obj.minCnt > 0) eventCls = 'black'
- if(obj.majCnt > 0) eventCls = 'blue'
- if(obj.criCnt > 0) eventCls = 'red'
- }
- return eventCls
- }
- })
- const tblItems2 = ref([])
- const gridOptions = {
- columnDefs: [
- {
- headerName: 'No',
- field: 'no',
- sortable: false,
- checkboxSelection: false,
- headerCheckboxSelection: false
- },
- {
- headerName: 'NE 그룹명',
- field: 'neGroup',
- sortable: false,
- },
- {
- headerName: '테넌트',
- field: 'tenantName',
- sortable: false,
- },
- {
- headerName: 'NE 수',
- field: 'neCnt',
- sortable: false,
- },
- {
- headerName: '알림',
- field: 'cls',
- sortable: false,
- cellRenderer: actionCellRenderer
- }
- ],
- rowData: setNeGroup.value, // 테이블 데이터
- suppressMovableColumns: true,
- autoSizeStrategy: {
- type: "fitGridWidth", // width맞춤
- },
- headerHeight : rowHeightPx,
- rowHeight: rowHeightPx,
- pagination: true,
- suppressPaginationPanel: true, // 하단 default 페이징 컨트롤 숨김
- suppressRowClickSelection: true, // 행 클릭 체크박스 무시
- localeText: {
- noRowsToShow: i18n.t('common.noData')
- },
- }
- const gridOptions2 = {
- defaultColDef: {
- lockVisible: true,
- },
- columnDefs: [
- {
- headerName: 'No',
- field: 'no',
- width: 50
- },
- {
- headerName: 'NE 이름',
- field: 'neName',
- cellRenderer: actionCellRenderer2,
- },
- {
- headerName: '테넌트',
- field: 'tenantName',
- },
- {
- headerName: '이벤트',
- field: 'eventStatus',
- cellRenderer: actionCellRenderer3,
- width: 250,
- }
- ],
- rowData: selectedGroup.value.neList, // 테이블 데이터
- autoSizeStrategy: {
- type: "fitGridWidth", //fitCellContents
- },
- headerHeight : rowHeightPx,
- rowHeight: rowHeightPx,
- pagination: true,
- suppressPaginationPanel: true, // 하단 default 페이징 컨트롤 숨김
- suppressRowClickSelection: true, // 행 클릭 체크박스 무시
- localeText: {
- noRowsToShow: 'NE 그룹을 선택하세요'
- },
- }
- const markers = ref([])
- onMounted(() => {
- fnInit()
- })
- /***********************
- * Methods
- ************************/
- /**
- * RAN NE 그룹 상세 팝업
- */
- // function fnGetNeGroupDetailInfo(){
- // useAxios().post('/dashboard/geoNeGroupInfo/list.do').then((res) => {
- // $log.debug("[dashboard][fnGetNeGroupDetailInfo][success]")
- // }).catch((error)=>{
- // $log.debug("[dashboard][fnGetNeGroupDetailInfo][error]")
- // useErrorHandler().fnSetCommErrorHandle(error, fnGetNeGroupDetailInfo)
- // }).finally(()=>{
- // $log.debug("[dashboard][fnGetNeGroupDetailInfo][finished]")
- // })
- // }
- /**
- * 초기 실행
- */
- function fnInit(){
- nextTick().then(() => {
- if (window.kakao && window.kakao.maps) {
- loadMap()
- } else {
- loadScript()
- }
- })
- }
- /**
- * kakao 스크립트 로드
- */
- function loadScript() {
- const script = document.createElement('script')
- script.async = true
- script.onload = () => {
- window.kakao.maps.load(loadMap)
- }
- script.src = `//dapi.kakao.com/v2/maps/sdk.js?autoload=false&libraries=services,clusterer,drawing&appkey=${import.meta.env.VITE_APP_KAKAO_APP_KEY}`
- document.head.appendChild(script)
- }
- /**
- * kakao 지도 로드
- */
- async function loadMap() {
- const mapContainer = document.getElementById('ran_detail_map')
- let mapOption = {
- center: new kakao.maps.LatLng(props.centerPosition.lat, props.centerPosition.lng), // 지도의 중심좌표
- level: 12, // 지도의 확대 레벨
- }
- map.value = new kakao.maps.Map(mapContainer, mapOption)
- let zoomControl = new kakao.maps.ZoomControl()
- // map.value.addControl(zoomControl, kakao.maps.ControlPosition.BOTTOMRIGHT)
- // fnSetEventListener() // 지도 이벤트 등록
- }
- /**
- * 상세팝업 닫기
- */
- function fnClose(){
- isModal.value = false
- setTimeout(() => {
- emit('closeModal')
- }, 250);
- }
- function fnGetNeList(){
- //
- }
- /**
- * 마커 생성 데이터 세팅 (그룹 클릭시 ne마커 표시하기)
- */
- function fnDrawMarker(){
- fnClearMarker()
- console.log('%c selectedGroup.value' ,'color:#bada55', selectedGroup.value)
- selectedGroup.value.neList.map((item, index) => {
- let position = new kakao.maps.LatLng(item.neLocLatitude, item.neLocLongitude)
- let markerObj = {
- overlay: null,
- markers: null,
- data: item,
- click: false,
- position: position
- }
- markers.value.push(markerObj)
- markers.value[index].markers = fnCreateMarker(position, index)
- if(index == 0) {
- map.value.panTo(position)
- }
- })
- }
- import { filename } from 'pathe/utils'
- // 이미지 가져오기 (vite문법)
- const glob = import.meta.glob('~/assets/img/ico_*.{png,svg}', { eager: true })
- const getImages = Object.fromEntries(
- Object.entries(glob).map(([key, value]) => [filename(key), value.default])
- )
- function fnGetMarkerImage(index){
- let color = selectedGroup.value.neList[index].color
- console.log('%c color' ,'color:#bada55', color)
- if(color == 'red') return 'ico_red_pin'
- else if(color == 'blue') return 'ico_blue_pin'
- else if(color == 'black') return 'ico_black_pin'
- else return 'ico_gray_pin'
- }
- /**
- * 마커 생성
- */
- function fnCreateMarker(position, index) {
- const markerImageSrc = getImages[fnGetMarkerImage(index)] // assets 폴더의 이미지 경로
- const markerImageSize = new kakao.maps.Size(40, 40); // 마커 이미지 사이즈
- const markerImage = new kakao.maps.MarkerImage(markerImageSrc, markerImageSize);
- let markerOption = {
- map: map.value, // 마커를 표시할 지도
- position: position, // 마커의 위치
- clickable: true,
- image: markerImage,
- zIndex: 1,
- }
- let marker = new kakao.maps.Marker(markerOption)
- // 마커 클릭 이벤트등록
- kakao.maps.event.addListener(marker,'click', fnClickMarker(index))
- return marker
- }
- /**
- * 마커 클릭
- */
- function fnClickMarker(index) {
- const clickData = markers.value[index].data
- const openInfoWindow = function () {
- // 마커 클릭 > 상태변경 > z-index 및 맵 이동
- fnMarkerClickChk(index)
- fnGetInfo(clickData, index)
- }
- return openInfoWindow
- }
- /**
- * 마커 클릭 이벤트
- */
- function fnMarkerClickChk(index) {
- fnSetUnClickMarker()
- markers.value[index].click = true
- markers.value[index].markers.setZIndex(3)
- let moveLatLng = markers.value[index].position
- map.value.panTo(moveLatLng)
- }
- /**
- * 마커 클릭 상태 해제 및 z인덱스 초기화
- */
- function fnSetUnClickMarker() {
- markers.value.forEach((item) => {
- item.click = false
- item.markers.setZIndex(2)
- })
- }
- function fnGetInfo(data, index){
- // 열려 있던 오버레이 닫기
- fnCloseMarkerInfoOverlay()
- // 오버레이 생성
- markers.value[index].overlay = fnCreateCustomOverlay(data, index)
- // 오버레이 z-index 설정
- markers.value[index].overlay.setZIndex(999)
- // 오버레이 열기
- markers.value[index].overlay.setMap(map.value)
- // 오버레이 이벤트 등록
- fnCustomOverlayAddEventListener()
- }
- /**
- * 커스텀 오버레이 생성
- */
- function fnCreateCustomOverlay(data, index) {
- let cpu = JSON.parse(data.cpu)
- let memory = JSON.parse(data.mem)
- let content = `
- <div class="area--info" style="top: -10.7rem; right: -6rem;">
- <div class="area--info--title">
- <p style="overflow:hidden; text-overflow: ellipsis;">${data.neName}</p>
- <button class="btn-close" id="overlayCloseBtn"></button>
- </div>
- <ul>
- <li><i class="ico green"></i><span>STATUS</span><span class="active">ACTIVE</span></li>
- <li><i class="ico red"></i><span>CPU</span><span class="">${cpu.AVG_CPU_L}%</span></li>
- <li><i class="ico green"></i><span>MEMORY</span><span class="">${memory.AVG_MEM_L}%</span></li>
- <li><i class="ico green"></i><span>DISK</span><span class="">${data.disk}</span></li>
- </ul>
- </div>`
- // 커스텀 오버레이 생성
- let customOverlay = new kakao.maps.CustomOverlay({
- position: markers.value[index].position,
- content: content
- })
- return customOverlay
- }
- /**
- * 커스텀 오버레이 이벤트 등록
- */
- function fnCustomOverlayAddEventListener(){
- // 오버레이 닫기 버튼
- const closeBtn = document.querySelector('#overlayCloseBtn')
- closeBtn.addEventListener('click', fnCloseBtnOverlay)
- }
- /**
- * 마커 정보 오버레이 닫기 버튼
- */
- function fnCloseBtnOverlay() {
- fnCloseMarkerInfoOverlay()
- fnSetUnClickMarker()
- }
- /**
- * 모든 마커 정보 오버레이 닫기
- */
- function fnCloseMarkerInfoOverlay() {
- for (var i = 0; i < markers.value.length; i++) {
- if (markers.value[i].overlay) {
- markers.value[i].overlay.setMap(null)
- markers.value[i].overlay = null
- }
- }
- }
- /*********************
- * grid 관련
- *********************/
- // Grid 데이터 바인딩
- function fnOnGridReady(params){
- gridApi.value = params.api
- }
- function fnOnGridReady2(params){
- gridApi2.value = params.api
- }
- function actionCellRenderer(params) {
- let _cls = "";
- if(params.value == 'red'){
- _cls = 'alarm--red';
- } else if(params.value == 'blue'){
- _cls = 'alarm--blue';
- } else if(params.value == 'gray'){
- _cls = 'alarm--gray'
- }
- let pin = `<span class="${_cls}"></span>`;
- return pin;
- }
- function actionCellRenderer2(params) {
- let _cls = "";
- if(params.data.color == 'red'){
- _cls = 'pin--red';
- } else if(params.data.color == 'blue'){
- _cls = 'pin--blue';
- } else if(params.data.color == 'gray'){
- _cls = 'pin--gray';
- } else if(params.data.color == 'black'){
- _cls = 'pin--black';
- }
- let pin = `<span class="${_cls}">${params.data.neName}</span>`;
- return pin;
- }
- function actionCellRenderer3(params) {
- let _cls0 = "";
- let _cls1 = "";
- let _cls2 = "";
- let _cls3 = "";
- if(params.data.eventStatus[0]){_cls0 = 'evt--critical';}
- if(params.data.eventStatus[1]){_cls1 = 'evt--major';}
- if(params.data.eventStatus[2]){_cls2 = 'evt--minor';}
- let pin = "";
-
- if (_cls0) {
- pin += `<span class="${_cls0}">CRITICAL (${params.data.eventStatus[0]}) </span>`;
- }
- if (_cls1) {
- pin += `<span class="${_cls1}">MAJOR (${params.data.eventStatus[1]}) </span>`;
- }
- if (_cls2) {
- pin += `<span class="${_cls2}">MINOR (${params.data.eventStatus[2]}) </span>`;
- }
- if(pin == ""){
- pin += `<span class="evt--none">-</span>`;
- }
- return pin;
- }
- function fnRowClick(params){
- selectedGroup.value.neGroup = params.data.neGroup
- let list = params.data.neList
- let temp = []
- list.forEach((item, idx) => {
- let obj = item
- obj.no = idx+1
- obj.color = getNeEventCls.value(item)
- obj.eventStatus = [item.criCnt, item.majCnt, item.minCnt]
- temp.push(obj)
- })
- selectedGroup.value.neList = _cloneDeep(temp)
- gridApi2.value.setGridOption("rowData", selectedGroup.value.neList)
- fnDrawMarker()
- }
- function fnClearMarker(){
- markers.value.forEach(marker => marker.markers.setMap(null));
- fnCloseMarkerInfoOverlay()
- markers.value = []
- }
- </script>
|