서버실의 랙배치 작업을 드래그앤 드롭을 적용하여 작업하는 코드포함하여 배포합니다.
마이앱스로 개발된 폼에서 일반적인 자바스크립트를 이용하여 확장하는 방법을 배워봅니다.


경험해보기



주요기능.  
  1. 랙구성및 서버 배치작업
  2. 랙의 드래그앤 드롭으로 랙에 서버를  이동시킬수 있습니다.
  3. 서버정보를 드블클릭하면 서버정보가 팝업이 됩니다.
image.png


2. 랙을 신규로 등록하고 관리합니다.
image.png


3.  서버정보를 등록합니다.
image.png


구현하기



image.png

스크립트 구현.

// 랙정보와 서버정보를 비동기로 가져오기 때문에 동시점을 파악하기 어렵다.
// 렉정보를 가져오는 Promise를 준비하고 서버정보는 랙정보가  가져오면 표시하는 구성으로 진행한다

// 이벤트: proxyMakeDataView 
// 그리드의 데이터가 준비되었다는 
//  form.dataRack.addEventListener("proxyMakeDataView",  e=> {}) 
//   
//   그리드의 특정키값으로 행을 이동시키는 함수
//    form.dataServer.activeKey(keyValue);
//    
//    폼이 팝업으로 설정된 경우에 팝업을 보이게 하는 함수
//    form.dataServer.showPopup();
//
//   그리드의 해당 요소로 이동시키고 정보를 업데이트 함
//   let tr = form.dataServer.activeKey(keyValue);
//   tr.__ref.서버랙번호 = rackNo;
//   tr.__ref.선반번호 = slotNo;  

let dragItem ;  // 드래그되는 객체
let overItem ; //  드래그over되는 base객체


// 렉데이터가 준비되면 
const RackPromise =  new Promise((resolv, reject)=>{

  form.dataRack.addEventListener("proxyMakeDataView",  e=> {
    // 렉정보를 가져오면 렉 요소를 표시한다.
    e.detail.rows.forEach(row=>{
      form.serverRoom.querySelector(`.rack-room`).appendChild(createRackDiv(row));
    })
    // 비동기 완료를 호출한다
    resolv();
  });
})

// 서버데이터를 표시하기 위해서 비동기로 구현한다
form.dataServer.addEventListener("proxyMakeDataView",  e=> {
  RackPromise.then(()=>{
  
    form.serverRoom.querySelectorAll(`.rack-item`).forEach(el=>el.remove());
    form.serverRoom.querySelectorAll(`.rack-base`).forEach(el=>el.setAttribute("state", "empty"));

    e.detail.rows.forEach(row=>{
      let rack = form.serverRoom.querySelector(`[rack_id="${row.서버랙번호}"]`);
      if(!rack){
        rack = form.serverRoom.querySelector(`.wait`);
      }
      rack.querySelector(`.rack-container`).appendChild(createServerDiv(row));
    });
    
    showPosition();
  })
});


// 마우스다운에서 서버이면 드래그를 동작시킨다
form.addEventListener("mousedown", e=>{
  if(e.target.matches(".rack-item")){
    e.target.setAttribute('draggable', 'true');
    dragItem = e.target;
  }
})

// 클릭하면 서버의 상세정보를 팝업시킨다.
form.addEventListener("dblclick", e=>{
  if(e.target.matches(".rack-item")){
    let keyValue = e.target.getAttribute("keyValue");
    form.dataServer.activeKey(keyValue);
    form.dataServer.showPopup();
  }
})

// 드래그 할때 랙에 서버가 들어갈 공간이 있는지 파악함 만일 공간(base)가 여유가 되면 표시함
form.addEventListener('dragover', e => {
  dragoverClear();
  // 서버랙으로 이동하는 경우는 서버랙의 크기(height)를 고려한 공간을 검사함
  if(dragItem && e.target.matches(".rack-base") && isBaseEmptySize(e.target, parseInt(dragItem.getAttribute("u-size"))||1 )){
    overItem =  e.target;
  }
  else if(dragItem && e.target.matches(".wait .rack-container") ){
    overItem =  e.target;
  }

  if(overItem){
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";
    e.target.classList.add("hover");
  }
})

// 서버를 드래그해서 드롭이 되는 경우
// 랙으로 이동하는 경우
// 대기위치로 이동하는 경우
form.addEventListener('drop', e => {
  if(!dragItem) return;
  //  랙으로 이동하는 경우
  let keyValue = dragItem.getAttribute("keyValue");
  let rackNo = null;
  let slotNo = null;

  if(e.target.matches(".rack-base")){  
    rackNo = e.target.closest(".rack").getAttribute("rack_id");
    slotNo = e.target.getAttribute("sno");

    // 이동전 위치를 지움
    baseStateEmpty(dragItem);

    // 드롭된 선반(base)의 번호를 기록함
    dragItem.setAttribute("sno", slotNo);

    // 드롭된 서버객체를 컨테이너에 엘러먼트 요소로 추가함 
    e.target.parentElement.appendChild(dragItem);

    // 추가된 서버엘러먼트의 선반위치로 이동시키는 스타일작업을 진행
    serverDrow(dragItem);
  }
  // 대기위치로 이동한다면
  else if(e.target.matches(".wait .rack-container") ){
    // 이동전 위치를 지움
    baseStateEmpty(dragItem);
    // 대기선반에 추가함
    e.target.appendChild(dragItem);
  }

  // 그리드의 해당 요소로 이동시키고 정보를 업데이트 함
  let tr = form.dataServer.activeKey(keyValue);
  tr.__ref.서버랙번호 = rackNo;
  tr.__ref.선반번호 = slotNo;  
  dragItem = null;
  
})


// 드래그드롭이 종료되면 dragoverClear동작을 실행해서 흔적을 지움
form.addEventListener("dragend", e => { 
  dragoverClear();
})



// 렉 요소 만들기
function createRackDiv(row){
  const rsize = row.서버탑재수||8;
  const rackDiv = document.createElement("div");
  rackDiv.classList.add("rack");
  rackDiv.setAttribute("rack_id", row.서버랙번호);
  rackDiv.setAttribute("r-size", rsize);
  rackDiv.innerHTML=`
<h4 class="rack-title">${row.서버랙이름}</h4>
<div class="rack-container"></div>
<h4 class="rack-foot">${row.서버랙이름}</h4>
`;

  // 공간객체를 생성한다.
  for(let i=1; i<=rsize; i++){
    const baseDiv = document.createElement("div");
    baseDiv.classList.add("rack-base");
    baseDiv.setAttribute("sno", i);
    baseDiv.setAttribute("state", "empty");

    baseDiv.innerText = `${i}`;
    rackDiv.querySelector(".rack-container").appendChild(baseDiv);
  } 

  return rackDiv;

}

// 서버요소 만들기
function createServerDiv(row){
  const serverDiv = document.createElement("div");
  serverDiv.classList.add("rack-item");
  serverDiv.setAttribute("keyValue", row.서버번호);
  serverDiv.setAttribute("sno", row.선반번호);
  serverDiv.setAttribute("u-size", row.사이즈||1);
  serverDiv.innerHTML=`
${row.서버이름}
`;
  const led = document.createElement("div");
  led.classList.add('led');
  led.innerHTML = `
<div hidden class="circle3d"></div>
<div class="circle3d green"></div>
<div class="circle3d red"></div>
`;
  serverDiv.appendChild(led);

  const pen = document.createElement("div");
  pen.classList.add('pen');
  pen.innerHTML = `
<i hidden class="fa fa-life-bouy"></i>
<i hidden class="fa fa-life-bouy fa-spin"></i>
<i class="fa fa-life-bouy fa-pulse"></i>
`;
  serverDiv.appendChild(pen);


  return serverDiv;

}

// 렉과 서버요소의 높이와 크기를 위치를 계산해서 표시하는 작업
function showPosition(){
  // 렉의 크기만큼 높이를 설정한다
  form.querySelectorAll(".rack[r-size]").forEach(el=>{
    let rsize = parseInt(el.getAttribute("r-size"));
    el.style.height = (rsize * 20 + 50) + 'px';
  });

  // 서버렉의 위치와 높이를 설정한다
  form.querySelectorAll(".rack .rack-item[sno]").forEach(el=>{
    serverDrow(el);
  });
  form.querySelectorAll(".wait .rack-item[sno]").forEach(el=>{

    let uSize = parseInt(el.getAttribute("u-size"))||1;
    let height = uSize * 20;
    el.style.height = height + 'px'; 
    el.style.lineHeight = height + 'px'; 
  });  
}

// dragover표시를 지운다
function dragoverClear(){
  if(overItem){
    overItem.classList.remove("hover");
    overItem = null;
  }  
}

// 서버의 엘러먼트의 위치(top)와 크기(height)를 구하고
// 서버가 위치하는 base영역에 사용마크를 표시한다.
function serverDrow(el){
  let sno = parseInt(el.getAttribute("sno"));
  let top = (sno-1) * 20;
  el.style.top = top + 'px';

  let uSize = parseInt(el.getAttribute("u-size"))||1;
  let height = uSize * 20;
  el.style.height = height + 'px';  
  el.style.lineHeight = height + 'px'; 

  baseStateUseMark(el.parentElement, sno, uSize);
}

// Base영역에 사용 마크표시
function baseStateUseMark(cont , sno, uSize){

  for(let i=0; i < uSize; i++){
    const rb = cont.querySelector(`.rack-base[sno="${ sno + i}"]`);
    if(rb){
      rb.setAttribute("state", "use")
    }
  }
}

// Base영역에 사용 마크 지우기
function baseStateEmpty(item){

  const cont = item.parentElement;
  const sno = parseInt(item.getAttribute("sno"));
  const uSize = parseInt(item.getAttribute("u-size")||1);

  for(let i=0; i < uSize; i++){
    const rb = cont.querySelector(`.rack-base[sno="${ sno + i}"]`);
    if(rb){
      rb.setAttribute("state", "empty")
    }
  }
}

// Base영역에 서버가 들어갈 공간이 되는지 점검
function isBaseEmptySize(base , uSize){
  const sno = parseInt(base.getAttribute("sno")||1);
  const cont = base.parentElement;

  let empCount = 0
  for(let i=0; i < uSize; i++){
    const rb = cont.querySelector(`.rack-base[sno="${ sno + i}"]`);
    if(rb && rb.getAttribute("state") == "empty"){
      empCount++;
    }
  }

  return uSize == empCount;
}