feat: 서버 자산 관리 리스트 고도화 및 3개 관리대장 데이터 통합
This commit is contained in:
522
index.html
522
index.html
@@ -46,15 +46,14 @@
|
||||
<h2 id="current-tab-title">하드웨어 / 대시보드</h2>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<!-- 엑셀 컨트롤러 묶음 -->
|
||||
<button id="btn-download-template" class="btn btn-outline" title="초기 데이터 입력을 위한 전체 엑셀 템플릿 다운로드">
|
||||
<button id="btn-download-template" class="btn btn-outline" title="통합 양식 다운로드">
|
||||
<i data-lucide="download"></i> 통합 양식 다운로드
|
||||
</button>
|
||||
<label for="excel-upload" class="btn btn-outline" title="작성된 엑셀 파일 일괄 업로드">
|
||||
<label for="excel-upload" class="btn btn-outline" title="엑셀 파일 업로드">
|
||||
<i data-lucide="upload"></i> 엑셀 업로드
|
||||
</label>
|
||||
<input type="file" id="excel-upload" accept=".xlsx, .xls" style="display: none;" />
|
||||
<button id="btn-export-excel" class="btn btn-primary" title="마스터 데이터 전체를 엑셀로 저장">
|
||||
<button id="btn-export-excel" class="btn btn-primary" title="일괄 엑셀 저장">
|
||||
<i data-lucide="file-spreadsheet"></i> 일괄 엑셀 저장
|
||||
</button>
|
||||
<button id="btn-add-asset" class="btn btn-primary hidden">
|
||||
@@ -64,106 +63,145 @@
|
||||
</header>
|
||||
|
||||
<main class="content-area" id="main-content">
|
||||
<!-- 대시보드 뷰, 또는 데이터 테이블 뷰가 JavaScript로 주입됩니다 -->
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HW Asset Modal -->
|
||||
<div id="hw-asset-modal" class="modal-overlay hidden">
|
||||
<div class="modal-content">
|
||||
<div class="modal-content" style="max-width: 800px; max-height: 90vh; display: flex; flex-direction: column;">
|
||||
<div class="modal-header">
|
||||
<h2 id="hw-modal-title">자산 상세 정보</h2>
|
||||
<button id="btn-close-hw-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" style="overflow-y: auto; flex: 1;">
|
||||
<form id="hw-asset-form" class="grid-form">
|
||||
<input type="hidden" id="hw-asset-id" />
|
||||
<input type="hidden" id="hw-asset-type" /> <!-- 개인PC, 서버 등 저장용 -->
|
||||
<input type="hidden" id="hw-asset-type" />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-법인">법인</label>
|
||||
<input type="text" id="hw-법인" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hw-자산코드">자산번호/코드</label>
|
||||
<input type="text" id="hw-자산코드" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-용도">용도</label>
|
||||
<input type="text" id="hw-용도" />
|
||||
</div>
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-상세">상세</label>
|
||||
<input type="text" id="hw-상세" />
|
||||
</div>
|
||||
|
||||
<div class="form-group non-server">
|
||||
<label for="hw-명칭">명칭</label>
|
||||
<input type="text" id="hw-명칭" />
|
||||
</div>
|
||||
<div class="form-group" id="hw-비품유형-group" style="display:none;">
|
||||
<label for="hw-비품유형">비품유형</label>
|
||||
<select id="hw-비품유형" class="form-control" style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px;">
|
||||
<option value="노트북">노트북</option>
|
||||
<option value="태블릿">태블릿</option>
|
||||
<option value="휴대폰">휴대폰</option>
|
||||
<select id="hw-비품유형" style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px;">
|
||||
<option value="노트북">노트북</option><option value="태블릿">태블릿</option><option value="휴대폰">휴대폰</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-자산코드">자산코드</label>
|
||||
<input type="text" id="hw-자산코드" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-명칭">명칭</label>
|
||||
<input type="text" id="hw-명칭" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-위치">위치</label>
|
||||
<label for="hw-위치">설치위치</label>
|
||||
<input type="text" id="hw-위치" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-관리자">관리자</label>
|
||||
<input type="text" id="hw-관리자" />
|
||||
<label for="hw-담당자_정">담당자(정)</label>
|
||||
<input type="text" id="hw-담당자_정" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hw-담당자_부">담당자(부)</label>
|
||||
<input type="text" id="hw-담당자_부" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-IP주소">IP주소</label>
|
||||
<label for="hw-IP주소">IP 주소 1</label>
|
||||
<input type="text" id="hw-IP주소" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-MACaddress">MAC address</label>
|
||||
<input type="text" id="hw-MACaddress" />
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-IP2">IP 주소 2</label>
|
||||
<input type="text" id="hw-IP2" />
|
||||
</div>
|
||||
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-원격접속">원격 도구</label>
|
||||
<input type="text" id="hw-원격접속" />
|
||||
</div>
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-서버ID">서버 ID</label>
|
||||
<input type="text" id="hw-서버ID" />
|
||||
</div>
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-서버PW">서버 PW</label>
|
||||
<input type="text" id="hw-서버PW" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-모델명">모델명</label>
|
||||
<input type="text" id="hw-모델명" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hw-OS">OS</label>
|
||||
<input type="text" id="hw-OS" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hw-CPU">CPU</label>
|
||||
<input type="text" id="hw-CPU" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hw-RAM">RAM</label>
|
||||
<input type="text" id="hw-RAM" />
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label for="hw-HW사양">H/W 사양</label>
|
||||
<div class="form-group">
|
||||
<label for="hw-SSD1">Storage 1</label>
|
||||
<input type="text" id="hw-SSD1" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hw-SSD2">Storage 2</label>
|
||||
<input type="text" id="hw-SSD2" />
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width non-server">
|
||||
<label for="hw-HW사양">H/W 사양 상세</label>
|
||||
<textarea id="hw-HW사양" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group non-server">
|
||||
<label for="hw-구매일">구매일</label>
|
||||
<input type="text" id="hw-구매일" placeholder="ex) 2024-01-01" />
|
||||
<input type="text" id="hw-구매일" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group non-server">
|
||||
<label for="hw-금액">금액</label>
|
||||
<input type="text" id="hw-금액" placeholder="ex) 1,000,000" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" />
|
||||
<input type="text" id="hw-금액" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hw-납품업체">납품업체</label>
|
||||
<input type="text" id="hw-납품업체" />
|
||||
<div class="form-group server-only">
|
||||
<label for="hw-비고">비고</label>
|
||||
<input type="text" id="hw-비고" />
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label style="font-size:0.875rem;">품의서 (파일)</label>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem;">
|
||||
<input type="file" id="hw-품의서" style="font-size:0.875rem;" />
|
||||
<input type="file" id="hw-품의서" />
|
||||
<span id="hw-품의서명" style="font-size:0.75rem; color:var(--text-light)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="btn-delete-hw-asset" class="btn btn-outline btn-danger">삭제</button>
|
||||
<button id="btn-delete-hw-asset" class="btn btn-danger">삭제</button>
|
||||
<div class="footer-actions">
|
||||
<button id="btn-cancel-hw-modal" class="btn btn-outline">취소</button>
|
||||
<button id="btn-save-hw-asset" class="btn btn-primary">저장</button>
|
||||
<button id="btn-cancel-hw-modal" class="btn btn-outline">닫기</button>
|
||||
<button id="btn-save-hw-asset" class="btn btn-primary">수정</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,104 +209,41 @@
|
||||
|
||||
<!-- PC Asset Modal -->
|
||||
<div id="pc-asset-modal" class="modal-overlay hidden">
|
||||
<div class="modal-content">
|
||||
<div class="modal-content" style="max-width: 600px; max-height: 90vh; display: flex; flex-direction: column;">
|
||||
<div class="modal-header">
|
||||
<h2 id="pc-modal-title">개인PC 상세 정보</h2>
|
||||
<button id="btn-close-pc-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" style="overflow-y: auto; flex: 1;">
|
||||
<form id="pc-asset-form" class="grid-form">
|
||||
<input type="hidden" id="pc-asset-id" />
|
||||
<input type="hidden" id="pc-asset-type" value="개인PC" />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-법인">법인</label>
|
||||
<select id="pc-법인" required style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; font-size: 0.875rem;">
|
||||
<option value="한맥">한맥 (HM)</option>
|
||||
<option value="삼안">삼안 (SM)</option>
|
||||
<option value="바론">바론 (BR)</option>
|
||||
<select id="pc-법인" required style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px;">
|
||||
<option value="한맥">한맥 (HM)</option><option value="삼안">삼안 (SM)</option><option value="바론">바론 (BR)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-자산코드">자산코드</label>
|
||||
<input type="text" id="pc-자산코드" placeholder="ex) HM-PC-2018-001" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-사용자">사용자</label>
|
||||
<input type="text" id="pc-사용자" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-위치">위치</label>
|
||||
<input type="text" id="pc-위치" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-CPU">CPU</label>
|
||||
<input type="text" id="pc-CPU" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-GPU">GPU</label>
|
||||
<input type="text" id="pc-GPU" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-RAM">RAM</label>
|
||||
<input type="text" id="pc-RAM" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-SSD1">SSD1</label>
|
||||
<input type="text" id="pc-SSD1" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-SSD2">SSD2</label>
|
||||
<input type="text" id="pc-SSD2" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-HDD1">HDD1</label>
|
||||
<input type="text" id="pc-HDD1" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-HDD2">HDD2</label>
|
||||
<input type="text" id="pc-HDD2" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-구매일">구매일</label>
|
||||
<input type="text" id="pc-구매일" placeholder="ex) 2024-01-01" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-금액">금액</label>
|
||||
<input type="text" id="pc-금액" placeholder="ex) 1,000,000" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pc-납품업체">납품업체</label>
|
||||
<input type="text" id="pc-납품업체" />
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label style="font-size:0.875rem;">품의서 (파일)</label>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem;">
|
||||
<input type="file" id="pc-품의서" style="font-size:0.875rem;" />
|
||||
<span id="pc-품의서명" style="font-size:0.75rem; color:var(--text-light)"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"><label for="pc-자산코드">자산코드</label><input type="text" id="pc-자산코드" required /></div>
|
||||
<div class="form-group"><label for="pc-사용자">사용자</label><input type="text" id="pc-사용자" required /></div>
|
||||
<div class="form-group"><label for="pc-위치">위치</label><input type="text" id="pc-위치" /></div>
|
||||
<div class="form-group"><label for="pc-CPU">CPU</label><input type="text" id="pc-CPU" /></div>
|
||||
<div class="form-group"><label for="pc-GPU">GPU</label><input type="text" id="pc-GPU" /></div>
|
||||
<div class="form-group"><label for="pc-RAM">RAM</label><input type="text" id="pc-RAM" /></div>
|
||||
<div class="form-group"><label for="pc-SSD1">SSD1</label><input type="text" id="pc-SSD1" /></div>
|
||||
<div class="form-group"><label for="pc-SSD2">SSD2</label><input type="text" id="pc-SSD2" /></div>
|
||||
<div class="form-group"><label for="pc-HDD1">HDD1</label><input type="text" id="pc-HDD1" /></div>
|
||||
<div class="form-group"><label for="pc-HDD2">HDD2</label><input type="text" id="pc-HDD2" /></div>
|
||||
<div class="form-group"><label for="pc-구매일">구매일</label><input type="text" id="pc-구매일" /></div>
|
||||
<div class="form-group"><label for="pc-금액">금액</label><input type="text" id="pc-금액" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" /></div>
|
||||
<div class="form-group"><label for="pc-납품업체">납품업체</label><input type="text" id="pc-납품업체" /></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="btn-delete-pc-asset" class="btn btn-outline btn-danger">삭제</button>
|
||||
<button id="btn-delete-pc-asset" class="btn btn-danger">삭제</button>
|
||||
<div class="footer-actions">
|
||||
<button id="btn-cancel-pc-modal" class="btn btn-outline">취소</button>
|
||||
<button id="btn-save-pc-asset" class="btn btn-primary">저장</button>
|
||||
<button id="btn-cancel-pc-modal" class="btn btn-outline">닫기</button>
|
||||
<button id="btn-save-pc-asset" class="btn btn-primary">수정</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -276,107 +251,33 @@
|
||||
|
||||
<!-- Storage Asset Modal -->
|
||||
<div id="storage-asset-modal" class="modal-overlay hidden">
|
||||
<div class="modal-content">
|
||||
<div class="modal-content" style="max-width: 600px; max-height: 90vh; display: flex; flex-direction: column;">
|
||||
<div class="modal-header">
|
||||
<h2 id="storage-modal-title">스토리지 상세 정보</h2>
|
||||
<button id="btn-close-storage-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" style="overflow-y: auto; flex: 1;">
|
||||
<form id="storage-asset-form" class="grid-form">
|
||||
<input type="hidden" id="storage-asset-id" />
|
||||
<input type="hidden" id="storage-asset-type" value="스토리지" />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-법인">법인</label>
|
||||
<select id="storage-법인" required style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; font-size: 0.875rem;">
|
||||
<option value="한맥">한맥 (HM)</option>
|
||||
<option value="삼안">삼안 (SM)</option>
|
||||
<option value="바론">바론 (BR)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-유형">유형</label>
|
||||
<select id="storage-유형" required style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; font-size: 0.875rem;">
|
||||
<option value="NAS">NAS</option>
|
||||
<option value="DAS">DAS</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-자산코드">자산코드</label>
|
||||
<input type="text" id="storage-자산코드" placeholder="ex) HM-NAS-2024-001" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-명칭">명칭</label>
|
||||
<input type="text" id="storage-명칭" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-위치">위치</label>
|
||||
<input type="text" id="storage-위치" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-모델명">모델명</label>
|
||||
<input type="text" id="storage-모델명" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-용량">용량</label>
|
||||
<input type="text" id="storage-용량" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-담당자_정">담당자(정)</label>
|
||||
<input type="text" id="storage-담당자_정" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-담당자_부">담당자(부)</label>
|
||||
<input type="text" id="storage-담당자_부" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-IP주소">IP주소</label>
|
||||
<input type="text" id="storage-IP주소" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-MAC주소">MAC 주소</label>
|
||||
<input type="text" id="storage-MAC주소" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-구매일">구매일</label>
|
||||
<input type="text" id="storage-구매일" placeholder="ex) 2024-01-01" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-금액">금액</label>
|
||||
<input type="text" id="storage-금액" placeholder="ex) 1,000,000" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storage-납품업체">납품업체</label>
|
||||
<input type="text" id="storage-납품업체" />
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label style="font-size:0.875rem;">품의서 (파일)</label>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem;">
|
||||
<input type="file" id="storage-품의서" style="font-size:0.875rem;" />
|
||||
<span id="storage-품의서명" style="font-size:0.75rem; color:var(--text-light)"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"><label for="storage-법인">법인</label><input type="text" id="storage-법인" required /></div>
|
||||
<div class="form-group"><label for="storage-유형">유형</label><input type="text" id="storage-유형" required /></div>
|
||||
<div class="form-group"><label for="storage-자산코드">자산코드</label><input type="text" id="storage-자산코드" required /></div>
|
||||
<div class="form-group"><label for="storage-명칭">명칭</label><input type="text" id="storage-명칭" required /></div>
|
||||
<div class="form-group"><label for="storage-위치">위치</label><input type="text" id="storage-위치" /></div>
|
||||
<div class="form-group"><label for="storage-모델명">모델명</label><input type="text" id="storage-모델명" /></div>
|
||||
<div class="form-group"><label for="storage-용량">용량</label><input type="text" id="storage-용량" /></div>
|
||||
<div class="form-group"><label for="storage-담당자_정">담당자(정)</label><input type="text" id="storage-담당자_정" /></div>
|
||||
<div class="form-group"><label for="storage-IP주소">IP주소</label><input type="text" id="storage-IP주소" /></div>
|
||||
<div class="form-group"><label for="storage-구매일">구매일</label><input type="text" id="storage-구매일" /></div>
|
||||
<div class="form-group"><label for="storage-금액">금액</label><input type="text" id="storage-금액" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" /></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="btn-delete-storage-asset" class="btn btn-outline btn-danger">삭제</button>
|
||||
<button id="btn-delete-storage-asset" class="btn btn-danger">삭제</button>
|
||||
<div class="footer-actions">
|
||||
<button id="btn-cancel-storage-modal" class="btn btn-outline">취소</button>
|
||||
<button id="btn-save-storage-asset" class="btn btn-primary">저장</button>
|
||||
<button id="btn-cancel-storage-modal" class="btn btn-outline">닫기</button>
|
||||
<button id="btn-save-storage-asset" class="btn btn-primary">수정</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -384,78 +285,28 @@
|
||||
|
||||
<!-- SW Asset Modal -->
|
||||
<div id="sw-asset-modal" class="modal-overlay hidden">
|
||||
<div class="modal-content">
|
||||
<div class="modal-content" style="max-width: 600px; max-height: 90vh; display: flex; flex-direction: column;">
|
||||
<div class="modal-header">
|
||||
<h2 id="sw-modal-title">S/W 상세 정보</h2>
|
||||
<button id="btn-close-sw-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" style="overflow-y: auto; flex: 1;">
|
||||
<form id="sw-asset-form" class="grid-form">
|
||||
<input type="hidden" id="sw-asset-id" />
|
||||
<input type="hidden" id="sw-asset-type" />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-법인">법인</label>
|
||||
<select id="sw-법인" required style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; font-size: 0.875rem;">
|
||||
<option value="한맥">한맥 (HM)</option>
|
||||
<option value="삼안">삼안 (SM)</option>
|
||||
<option value="바론">바론 (BR)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-제품명">제품명</label>
|
||||
<input type="text" id="sw-제품명" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-구매일">구매일</label>
|
||||
<input type="text" id="sw-구매일" placeholder="ex) 2024-01-01" />
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="sw-구독일-group">
|
||||
<label for="sw-구독일">구독일(시작~끝)</label>
|
||||
<input type="text" id="sw-구독일" placeholder="ex) 2024-01-01 ~ 2024-12-31" />
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="sw-유지보수-group" style="display:none;">
|
||||
<label for="sw-유지보수여부">유지보수 여부</label>
|
||||
<label style="display:flex; align-items:center; gap:0.5rem; height: 38px;">
|
||||
<input type="checkbox" id="sw-유지보수여부" /> 대상 여부
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-금액">금액</label>
|
||||
<input type="text" id="sw-금액" placeholder="ex) 1,000,000" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-수량">수량 (보유량)</label>
|
||||
<input type="number" id="sw-수량" min="1" value="1" style="width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; font-size: 0.875rem;" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-계정명">계정명</label>
|
||||
<input type="text" id="sw-계정명" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-납품업체">납품업체</label>
|
||||
<input type="text" id="sw-납품업체" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sw-비고">비고</label>
|
||||
<input type="text" id="sw-비고" />
|
||||
</div>
|
||||
<input type="hidden" id="sw-asset-id" /><input type="hidden" id="sw-asset-type" />
|
||||
<div class="form-group"><label for="sw-법인">법인</label><input type="text" id="sw-법인" required /></div>
|
||||
<div class="form-group"><label for="sw-제품명">제품명</label><input type="text" id="sw-제품명" required /></div>
|
||||
<div class="form-group"><label for="sw-구매일">구매일</label><input type="text" id="sw-구매일" /></div>
|
||||
<div class="form-group" id="sw-구독일-group"><label for="sw-구독일">구독일</label><input type="text" id="sw-구독일" /></div>
|
||||
<div class="form-group"><label for="sw-금액">금액</label><input type="text" id="sw-금액" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')" /></div>
|
||||
<div class="form-group"><label for="sw-수량">수량</label><input type="number" id="sw-수량" min="1" /></div>
|
||||
<div class="form-group"><label for="sw-비고">비고</label><input type="text" id="sw-비고" /></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="btn-delete-sw-asset" class="btn btn-outline btn-danger">삭제</button>
|
||||
<button id="btn-delete-sw-asset" class="btn btn-danger">삭제</button>
|
||||
<div class="footer-actions">
|
||||
<button id="btn-cancel-sw-modal" class="btn btn-outline">취소</button>
|
||||
<button id="btn-save-sw-asset" class="btn btn-primary">저장</button>
|
||||
<button id="btn-cancel-sw-modal" class="btn btn-outline">닫기</button>
|
||||
<button id="btn-save-sw-asset" class="btn btn-primary">수정</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -463,120 +314,25 @@
|
||||
|
||||
<!-- SW User Management Modal -->
|
||||
<div id="sw-user-modal" class="modal-overlay hidden">
|
||||
<div class="modal-content" style="max-width: 600px;">
|
||||
<div class="modal-content" style="max-width: 600px; max-height: 90vh; display: flex; flex-direction: column;">
|
||||
<div class="modal-header">
|
||||
<h2 id="sw-user-modal-title">S/W 할당 사용자 목록</h2>
|
||||
<button id="btn-close-sw-user-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" style="overflow-y: auto; flex: 1;">
|
||||
<input type="hidden" id="sw-user-asset-id" />
|
||||
|
||||
<div style="text-align: right; margin-bottom: 0.5rem;">
|
||||
<button type="button" id="btn-open-add-user" class="btn btn-primary" style="padding: 0.25rem 1rem;"><i data-lucide="plus"></i> 새 사용자 추가</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="max-height: 250px; overflow-y: auto; border: 1px solid #e2e8f0; border-radius: 4px; margin-top:0;">
|
||||
<table style="width:100%; border-collapse: collapse; font-size:0.875rem;">
|
||||
<thead style="position: sticky; top: 0; background: var(--bg-light); z-index: 10;">
|
||||
<tr style="border-bottom: 1px solid var(--border);">
|
||||
<th style="padding:0.5rem; text-align:left;">법인</th>
|
||||
<th style="padding:0.5rem; text-align:left;">부서/팀</th>
|
||||
<th style="padding:0.5rem; text-align:left;">직위</th>
|
||||
<th style="padding:0.5rem; text-align:left;">이름</th>
|
||||
<th style="padding:0.5rem; text-align:center;">사용기간</th>
|
||||
<th style="padding:0.5rem; text-align:center;">첨부파일</th>
|
||||
<th style="padding:0.5rem; text-align:center;">관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="user-list-body">
|
||||
<!-- Users will be injected here -->
|
||||
</tbody>
|
||||
<div class="table-container">
|
||||
<table style="width:100%;">
|
||||
<thead><tr><th>법인</th><th>부서/팀</th><th>직위</th><th>이름</th><th>사용기간</th><th>관리</th></tr></thead>
|
||||
<tbody id="user-list-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="justify-content: flex-end;">
|
||||
<div class="footer-actions">
|
||||
<button id="btn-cancel-sw-user-modal" class="btn btn-outline">닫기</button>
|
||||
<button id="btn-save-sw-user-mapping" class="btn btn-primary">변경사항 저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SW User Form Modal (Add / Edit) -->
|
||||
<div id="sw-user-edit-modal" class="modal-overlay hidden" style="z-index: 1100;">
|
||||
<div class="modal-content" style="max-width: 500px;">
|
||||
<div class="modal-header">
|
||||
<h2 id="sw-user-edit-modal-title">사용자 추가</h2>
|
||||
<button id="btn-close-sw-user-edit-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="edit-user-idx" value="-1" />
|
||||
<div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.5rem;">
|
||||
<div class="form-group">
|
||||
<label for="new-user-법인" style="font-size:0.875rem;">법인</label>
|
||||
<select id="new-user-법인" class="form-control" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
||||
<option value="한맥">한맥</option><option value="삼안">삼안</option><option value="바론">바론</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-user-부서" style="font-size:0.875rem;">부서</label>
|
||||
<input type="text" id="new-user-부서" placeholder="ex: 기술부" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-user-팀" style="font-size:0.875rem;">팀</label>
|
||||
<input type="text" id="new-user-팀" placeholder="ex: 개발1팀" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-user-직위" style="font-size:0.875rem;">직위</label>
|
||||
<input type="text" id="new-user-직위" placeholder="ex: 대리" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" />
|
||||
</div>
|
||||
<div class="form-group" style="grid-column: span 2;">
|
||||
<label for="new-user-이름" style="font-size:0.875rem;">이름 <span style="color:var(--danger)">*</span></label>
|
||||
<input type="text" id="new-user-이름" placeholder="ex: 홍길동" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" required />
|
||||
</div>
|
||||
<div class="form-group" style="grid-column: span 2;">
|
||||
<label for="new-user-사용기간" style="font-size:0.875rem;">사용기간</label>
|
||||
<input type="text" id="new-user-사용기간" placeholder="ex: 2024.01~12" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" />
|
||||
</div>
|
||||
<div class="form-group" style="grid-column: span 2;">
|
||||
<label style="font-size:0.875rem;">신청서 (파일)</label>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem;">
|
||||
<input type="file" id="new-user-신청서" style="font-size:0.875rem;" />
|
||||
<span id="new-user-신청서명" style="font-size:0.75rem; color:var(--text-light)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="justify-content: flex-end;">
|
||||
<div class="footer-actions">
|
||||
<button id="btn-cancel-sw-user-edit" class="btn btn-outline">취소</button>
|
||||
<button id="btn-save-edit-user" class="btn btn-primary">확인</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Detail Modal -->
|
||||
<div id="dashboard-detail-modal" class="modal-overlay hidden" style="z-index: 1200;">
|
||||
<div class="modal-content" style="max-width: 1000px; max-height: 80vh; display: flex; flex-direction: column;">
|
||||
<div class="modal-header">
|
||||
<h2 id="dashboard-detail-modal-title">상세 자산 목록</h2>
|
||||
<button id="btn-close-dashboard-detail" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
<div class="modal-body" style="overflow-y: auto; flex: 1; padding: 0;">
|
||||
<div class="table-container" style="box-shadow: none; border-radius: 0;">
|
||||
<table style="width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th><th>유형</th><th>자산코드</th><th>명칭/모델</th>
|
||||
<th>위치</th><th>담당/사용자</th><th>구매일</th><th>금액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dashboard-detail-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button id="btn-cancel-sw-user-modal" class="btn btn-outline">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,27 @@ import { state } from '../../state';
|
||||
import { HardwareAsset } from '../../excelHandler';
|
||||
import { openModal } from './BaseModal';
|
||||
|
||||
/**
|
||||
* 폼의 모든 입력 필드를 활성화/비활성화 처리
|
||||
*/
|
||||
function setFormReadOnly(form: HTMLFormElement, isReadOnly: boolean) {
|
||||
const inputs = form.querySelectorAll('input, select, textarea');
|
||||
inputs.forEach(input => {
|
||||
if (input.type === 'file') {
|
||||
(input as HTMLElement).style.display = isReadOnly ? 'none' : 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReadOnly) {
|
||||
(input as HTMLElement).setAttribute('readonly', 'true');
|
||||
if (input.tagName === 'SELECT') (input as HTMLSelectElement).disabled = true;
|
||||
} else {
|
||||
(input as HTMLElement).removeAttribute('readonly');
|
||||
if (input.tagName === 'SELECT') (input as HTMLSelectElement).disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 하드웨어(서버, 전산비품 등) 모달 초기화 및 로직 제어
|
||||
*/
|
||||
@@ -10,33 +31,56 @@ export function initHWModal(renderContent: () => void, closeModals: () => void)
|
||||
const btnSaveHw = document.getElementById('btn-save-hw-asset') as HTMLButtonElement;
|
||||
const btnDeleteHw = document.getElementById('btn-delete-hw-asset') as HTMLButtonElement;
|
||||
|
||||
// 저장 버튼 이벤트
|
||||
// 저장/수정 버튼 통합 이벤트
|
||||
btnSaveHw?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// 현재 버튼이 '수정' 상태인 경우
|
||||
if (btnSaveHw.textContent === '수정') {
|
||||
setFormReadOnly(hwForm, false);
|
||||
btnSaveHw.textContent = '저장';
|
||||
return;
|
||||
}
|
||||
|
||||
// 현재 버튼이 '저장' 상태인 경우 (실제 저장 로직)
|
||||
if (!hwForm.checkValidity()) { hwForm.reportValidity(); return; }
|
||||
|
||||
const id = (document.getElementById('hw-asset-id') as HTMLInputElement).value;
|
||||
const type = (document.getElementById('hw-asset-type') as HTMLInputElement).value;
|
||||
const fileInput = document.getElementById('hw-품의서') as HTMLInputElement;
|
||||
const 품의서명 = fileInput.files && fileInput.files.length > 0 ? fileInput.files[0].name : (document.getElementById('hw-품의서명') as HTMLElement).innerText.replace('📎', '');
|
||||
|
||||
const newAsset: HardwareAsset = {
|
||||
id: id || Math.random().toString(36).substring(2, 9),
|
||||
type: (document.getElementById('hw-asset-type') as HTMLInputElement).value,
|
||||
type: type,
|
||||
법인: (document.getElementById('hw-법인') as HTMLInputElement).value,
|
||||
자산코드: (document.getElementById('hw-자산코드') as HTMLInputElement).value,
|
||||
명칭: (document.getElementById('hw-명칭') as HTMLInputElement).value,
|
||||
명칭: (document.getElementById('hw-명칭') as HTMLInputElement).value || '',
|
||||
위치: (document.getElementById('hw-위치') as HTMLInputElement).value,
|
||||
관리자: (document.getElementById('hw-관리자') as HTMLInputElement).value,
|
||||
관리자: (document.getElementById('hw-담당자_정') as HTMLInputElement).value,
|
||||
담당자_정: (document.getElementById('hw-담당자_정') as HTMLInputElement).value,
|
||||
담당자_부: (document.getElementById('hw-담당자_부') as HTMLInputElement).value,
|
||||
IP주소: (document.getElementById('hw-IP주소') as HTMLInputElement).value,
|
||||
IP2: (document.getElementById('hw-IP2') as HTMLInputElement).value,
|
||||
MACaddress: (document.getElementById('hw-MACaddress') as HTMLInputElement).value,
|
||||
OS: (document.getElementById('hw-OS') as HTMLInputElement).value,
|
||||
CPU: (document.getElementById('hw-CPU') as HTMLInputElement).value,
|
||||
RAM: (document.getElementById('hw-RAM') as HTMLInputElement).value,
|
||||
SSD1: (document.getElementById('hw-SSD1') as HTMLInputElement).value,
|
||||
SSD2: (document.getElementById('hw-SSD2') as HTMLInputElement).value,
|
||||
HW사양: (document.getElementById('hw-HW사양') as HTMLTextAreaElement).value,
|
||||
구매일: (document.getElementById('hw-구매일') as HTMLInputElement).value,
|
||||
금액: (document.getElementById('hw-금액') as HTMLInputElement).value,
|
||||
납품업체: (document.getElementById('hw-납품업체') as HTMLInputElement).value,
|
||||
용도: (document.getElementById('hw-용도') as HTMLInputElement).value,
|
||||
상세: (document.getElementById('hw-상세') as HTMLInputElement).value,
|
||||
원격접속: (document.getElementById('hw-원격접속') as HTMLInputElement).value,
|
||||
서버ID: (document.getElementById('hw-서버ID') as HTMLInputElement).value,
|
||||
서버PW: (document.getElementById('hw-서버PW') as HTMLInputElement).value,
|
||||
비고: (document.getElementById('hw-비고') as HTMLInputElement).value,
|
||||
품의서명,
|
||||
비품유형: (document.getElementById('hw-asset-type') as HTMLInputElement).value === '전산비품'
|
||||
? (document.getElementById('hw-비품유형') as HTMLSelectElement).value : undefined
|
||||
비품유형: type === '전산비품' ? (document.getElementById('hw-비품유형') as HTMLSelectElement).value : undefined,
|
||||
storage유형: (document.getElementById('hw-용도') as HTMLInputElement).value
|
||||
};
|
||||
|
||||
if (id) {
|
||||
@@ -64,49 +108,75 @@ export function initHWModal(renderContent: () => void, closeModals: () => void)
|
||||
|
||||
/**
|
||||
* 하드웨어 상세 모달 열기
|
||||
* @param asset 수정 시 자산 데이터, 신규 시 undefined
|
||||
*/
|
||||
export function openHwModal(asset?: HardwareAsset) {
|
||||
const hwModal = document.getElementById('hw-asset-modal') as HTMLDivElement;
|
||||
const hwForm = document.getElementById('hw-asset-form') as HTMLFormElement;
|
||||
const deleteBtn = document.getElementById('btn-delete-hw-asset')!;
|
||||
const btnSaveHw = document.getElementById('btn-save-hw-asset') as HTMLButtonElement;
|
||||
const currentType = asset ? asset.type : state.activeSubTab;
|
||||
|
||||
openModal('hw-asset-modal');
|
||||
hwForm.reset();
|
||||
|
||||
// 타입에 따른 필드 노출 제어
|
||||
const serverFields = document.querySelectorAll('.server-only');
|
||||
const nonServerFields = document.querySelectorAll('.non-server');
|
||||
|
||||
if (currentType === '서버') {
|
||||
serverFields.forEach(el => (el as HTMLElement).style.display = 'flex');
|
||||
nonServerFields.forEach(el => (el as HTMLElement).style.display = 'none');
|
||||
} else {
|
||||
serverFields.forEach(el => (el as HTMLElement).style.display = 'none');
|
||||
nonServerFields.forEach(el => (el as HTMLElement).style.display = 'flex');
|
||||
}
|
||||
|
||||
if (asset) {
|
||||
document.getElementById('hw-modal-title')!.textContent = '자산 상세 정보 수정';
|
||||
document.getElementById('hw-modal-title')!.textContent = `${currentType} 상세 정보 수정`;
|
||||
deleteBtn.style.display = 'block';
|
||||
btnSaveHw.textContent = '수정';
|
||||
setFormReadOnly(hwForm, true); // 수정 시 초기 상태는 읽기 전용
|
||||
|
||||
(document.getElementById('hw-asset-id') as HTMLInputElement).value = asset.id;
|
||||
(document.getElementById('hw-asset-type') as HTMLInputElement).value = asset.type;
|
||||
(document.getElementById('hw-법인') as HTMLInputElement).value = asset.법인;
|
||||
(document.getElementById('hw-자산코드') as HTMLInputElement).value = asset.자산코드;
|
||||
(document.getElementById('hw-명칭') as HTMLInputElement).value = asset.명칭;
|
||||
(document.getElementById('hw-위치') as HTMLInputElement).value = asset.위치;
|
||||
(document.getElementById('hw-관리자') as HTMLInputElement).value = asset.관리자;
|
||||
(document.getElementById('hw-IP주소') as HTMLInputElement).value = asset.IP주소;
|
||||
(document.getElementById('hw-MACaddress') as HTMLInputElement).value = asset.MACaddress;
|
||||
(document.getElementById('hw-OS') as HTMLInputElement).value = asset.OS;
|
||||
(document.getElementById('hw-HW사양') as HTMLTextAreaElement).value = asset.HW사양;
|
||||
(document.getElementById('hw-법인') as HTMLInputElement).value = asset.법인 || '';
|
||||
(document.getElementById('hw-자산코드') as HTMLInputElement).value = asset.자산코드 || '';
|
||||
(document.getElementById('hw-명칭') as HTMLInputElement).value = asset.명칭 || '';
|
||||
(document.getElementById('hw-위치') as HTMLInputElement).value = asset.위치 || '';
|
||||
(document.getElementById('hw-담당자_정') as HTMLInputElement).value = asset.담당자_정 || asset.관리자 || '';
|
||||
(document.getElementById('hw-담당자_부') as HTMLInputElement).value = asset.담당자_부 || '';
|
||||
(document.getElementById('hw-IP주소') as HTMLInputElement).value = asset.IP주소 || '';
|
||||
(document.getElementById('hw-IP2') as HTMLInputElement).value = asset.IP2 || '';
|
||||
(document.getElementById('hw-MACaddress') as HTMLInputElement).value = asset.MACaddress || '';
|
||||
(document.getElementById('hw-OS') as HTMLInputElement).value = asset.OS || '';
|
||||
(document.getElementById('hw-CPU') as HTMLInputElement).value = asset.CPU || '';
|
||||
(document.getElementById('hw-RAM') as HTMLInputElement).value = asset.RAM || '';
|
||||
(document.getElementById('hw-SSD1') as HTMLInputElement).value = asset.SSD1 || '';
|
||||
(document.getElementById('hw-SSD2') as HTMLInputElement).value = asset.SSD2 || '';
|
||||
(document.getElementById('hw-HW사양') as HTMLTextAreaElement).value = asset.HW사양 || '';
|
||||
(document.getElementById('hw-구매일') as HTMLInputElement).value = asset.구매일 || '';
|
||||
(document.getElementById('hw-금액') as HTMLInputElement).value = asset.금액 ? Number(asset.금액.replace(/,/g, '')).toLocaleString() : '';
|
||||
(document.getElementById('hw-금액') as HTMLInputElement).value = asset.금액 || '';
|
||||
(document.getElementById('hw-납품업체') as HTMLInputElement).value = asset.납품업체 || '';
|
||||
(document.getElementById('hw-용도') as HTMLInputElement).value = asset.용도 || asset.storage유형 || '';
|
||||
(document.getElementById('hw-상세') as HTMLInputElement).value = asset.상세 || '';
|
||||
(document.getElementById('hw-원격접속') as HTMLInputElement).value = asset.원격접속 || '';
|
||||
(document.getElementById('hw-서버ID') as HTMLInputElement).value = asset.서버ID || '';
|
||||
(document.getElementById('hw-서버PW') as HTMLInputElement).value = asset.서버PW || '';
|
||||
(document.getElementById('hw-비고') as HTMLInputElement).value = asset.비고 || '';
|
||||
(document.getElementById('hw-품의서명') as HTMLElement).innerText = asset.품의서명 ? `📎${asset.품의서명}` : '';
|
||||
(document.getElementById('hw-비품유형') as HTMLSelectElement).value = asset.비품유형 || '노트북';
|
||||
|
||||
if (currentType === '전산비품') {
|
||||
(document.getElementById('hw-비품유형') as HTMLSelectElement).value = asset.비품유형 || '노트북';
|
||||
}
|
||||
} else {
|
||||
document.getElementById('hw-modal-title')!.textContent = `새 ${state.activeSubTab} 자산 추가`;
|
||||
document.getElementById('hw-modal-title')!.textContent = `새 ${currentType} 자산 추가`;
|
||||
deleteBtn.style.display = 'none';
|
||||
btnSaveHw.textContent = '저장';
|
||||
setFormReadOnly(hwForm, false); // 신규 등록 시 편집 가능 상태
|
||||
|
||||
(document.getElementById('hw-asset-id') as HTMLInputElement).value = '';
|
||||
(document.getElementById('hw-asset-type') as HTMLInputElement).value = state.activeSubTab;
|
||||
(document.getElementById('hw-asset-type') as HTMLInputElement).value = currentType;
|
||||
(document.getElementById('hw-품의서명') as HTMLElement).innerText = '';
|
||||
(document.getElementById('hw-비품유형') as HTMLSelectElement).value = '노트북';
|
||||
}
|
||||
|
||||
// 전산비품일 경우 유형 선택 필드 노출
|
||||
if (state.activeSubTab === '전산비품') {
|
||||
document.getElementById('hw-비품유형-group')!.style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('hw-비품유형-group')!.style.display = 'none';
|
||||
}
|
||||
document.getElementById('hw-비품유형-group')!.style.display = (currentType === '전산비품') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
@@ -59,11 +59,25 @@ export function generateDummyData(): MasterAssetData {
|
||||
법인: rand(corps),
|
||||
자산코드: `HM-SV-${purchaseYear}-${String(i).padStart(3, '0')}`,
|
||||
명칭: `웹/DB 서버 #${i}`,
|
||||
위치: 'IDC / 전산실',
|
||||
관리자: randUser(),
|
||||
용도: rand(['웹 서버', 'DB 서버', '백업 서버', '개발 서버']),
|
||||
storage유형: rand(['물리', 'VM']),
|
||||
위치: rand(['IDC 1센터', 'IDC 2센터', '본사 전산실']),
|
||||
관리자: rand(users),
|
||||
담당자_정: rand(users),
|
||||
담당자_부: rand(users),
|
||||
IP주소: `192.168.10.${i}`,
|
||||
원격접속: `ssh://192.168.10.${i}:22`,
|
||||
MACaddress: '00:11:22:33:44:' + String(i).padStart(2, '0'),
|
||||
OS: rand(['Windows Server 2019', 'Ubuntu 22.04 LTS', 'CentOS 7']),
|
||||
모델명: rand(['Dell PowerEdge R740', 'HP ProLiant DL380', 'Lenovo ThinkSystem']),
|
||||
CPU: rand(['Xeon Silver 4210', 'Xeon Gold 6248', 'EPYC 7702']),
|
||||
RAM: rand(['64GB', '128GB', '256GB']),
|
||||
GPU: rand(['-', 'RTX A4000', 'Tesla V100']),
|
||||
SSD1: rand(['512GB SSD', '1TB NVMe']),
|
||||
SSD2: rand(['-', '1TB SSD', '2TB SSD']),
|
||||
HDD1: rand(['-', '4TB HDD', '8TB HDD']),
|
||||
모니터링: rand(['Zabbix', 'Grafana', 'PRTG']),
|
||||
비고: i % 5 === 0 ? '정기 점검 대상' : '-',
|
||||
HW사양: 'Xeon 16Core, 64GB RAM',
|
||||
구매일: randDate(purchaseYear, purchaseYear),
|
||||
금액: '5,000,000',
|
||||
|
||||
@@ -28,13 +28,15 @@ export interface HardwareAsset {
|
||||
담당자_부?: string;
|
||||
구매일?: string;
|
||||
금액?: string;
|
||||
납품업체?: string;
|
||||
품의서명?: string;
|
||||
납품업체: string;
|
||||
품의서명: string;
|
||||
용도?: string;
|
||||
상세?: string;
|
||||
원격접속?: string;
|
||||
모니터링?: string;
|
||||
비고?: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface SoftwareAsset {
|
||||
id: string;
|
||||
@@ -74,6 +76,7 @@ const SW_TABS = ['구독SW', '영구SW'];
|
||||
|
||||
const HW_HEADERS = ['법인', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매일', '금액', '납품업체', '품의서명'];
|
||||
const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', '구매일', '금액', '납품업체', '품의서명'];
|
||||
const SERVER_HEADERS = ['법인', '자산번호', '유형', '용도', '설치위치', '담당자(정)', '담당자(부)', 'IP 주소', '원격접속', '모델명', 'OS', 'CPU', 'RAM', 'GPU', 'Storage1', 'Storage2', 'Storage3', '모니터링', '비고'];
|
||||
const STORAGE_HEADERS = ['법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매일', '금액', '납품업체', '품의서명'];
|
||||
const SUB_SW_HEADERS = ['ID', '법인', '제품명', '구매일', '구독일', '금액', '수량', '계정명', '납품업체', '비고'];
|
||||
const PERM_SW_HEADERS = ['ID', '법인', '제품명', '구매일', '유지보수여부', '금액', '수량', '계정명', '납품업체', '비고'];
|
||||
@@ -87,19 +90,26 @@ export function downloadTemplate() {
|
||||
|
||||
// HW 탭들 생성
|
||||
HW_TABS.forEach(tab => {
|
||||
let hd = HW_HEADERS;
|
||||
let wscols: any[] = [];
|
||||
|
||||
if (tab === '개인PC') {
|
||||
const ws = XLSX.utils.aoa_to_sheet([PC_HEADERS]);
|
||||
ws['!cols'] = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
XLSX.utils.book_append_sheet(wb, ws, tab);
|
||||
hd = PC_HEADERS;
|
||||
wscols = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
} else if (tab === '서버') {
|
||||
hd = SERVER_HEADERS;
|
||||
wscols = [{wch:15}, {wch:20}, {wch:15}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:30}];
|
||||
} else if (tab === '스토리지') {
|
||||
const ws = XLSX.utils.aoa_to_sheet([STORAGE_HEADERS]);
|
||||
ws['!cols'] = [{wch:15}, {wch:15}, {wch:25}, {wch:25}, {wch:20}, {wch:25}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
XLSX.utils.book_append_sheet(wb, ws, tab);
|
||||
hd = STORAGE_HEADERS;
|
||||
wscols = [{wch:15}, {wch:15}, {wch:25}, {wch:25}, {wch:20}, {wch:25}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
} else {
|
||||
const ws = XLSX.utils.aoa_to_sheet([HW_HEADERS]);
|
||||
ws['!cols'] = [{wch:15}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:40}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
XLSX.utils.book_append_sheet(wb, ws, tab);
|
||||
hd = HW_HEADERS;
|
||||
wscols = [{wch:15}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:40}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
}
|
||||
|
||||
const ws = XLSX.utils.aoa_to_sheet([hd]);
|
||||
ws['!cols'] = wscols;
|
||||
XLSX.utils.book_append_sheet(wb, ws, tab);
|
||||
});
|
||||
|
||||
// SW 탭들 생성
|
||||
@@ -135,6 +145,12 @@ export function exportToExcel(masterData: MasterAssetData) {
|
||||
...targetAssets.map(a => [a.법인, a.자산코드, a.사용자, a.위치, a.CPU, a.GPU, a.RAM, a.SSD1, a.SSD2, a.HDD1, a.HDD2, a.구매일, a.금액, a.납품업체, a.품의서명])
|
||||
];
|
||||
colsConfig = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
|
||||
} else if (tab === '서버') {
|
||||
wsData = [
|
||||
SERVER_HEADERS,
|
||||
...targetAssets.map(a => [a.법인, a.자산코드, a.storage유형 || '물리', a.용도 || '', a.위치, a.담당자_정 || '', a.담당자_부 || '', a.IP주소, a.원격접속 || '', a.모델명 || '', a.OS, a.CPU, a.RAM, a.GPU, a.SSD1 || '', a.SSD2 || '', a.HDD1 || '', a.모니터링 || '', a.비고 || ''])
|
||||
];
|
||||
colsConfig = [{wch:15}, {wch:20}, {wch:15}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:30}];
|
||||
} else if (tab === '스토리지') {
|
||||
wsData = [
|
||||
STORAGE_HEADERS,
|
||||
@@ -235,6 +251,38 @@ export async function parseExcel(file: File): Promise<MasterAssetData> {
|
||||
납품업체: row['납품업체'] || '',
|
||||
품의서명: row['품의서명'] || '',
|
||||
});
|
||||
} else if (sheetName === '서버') {
|
||||
hwAssets.push({
|
||||
id: Math.random().toString(36).substring(2, 9),
|
||||
type: sheetName,
|
||||
법인: row['법인'] || '',
|
||||
자산코드: row['자산번호'] || row['자산코드'] || '',
|
||||
명칭: row['용도'] || row['명칭'] || '',
|
||||
용도: row['용도'] || '',
|
||||
위치: row['설치위치'] || row['위치'] || '',
|
||||
관리자: row['담당자(정)'] || '',
|
||||
담당자_정: row['담당자(정)'] || '',
|
||||
담당자_부: row['담당자(부)'] || '',
|
||||
IP주소: row['IP 주소'] || row['IP주소'] || '',
|
||||
원격접속: row['원격접속'] || '',
|
||||
모델명: row['모델명'] || '',
|
||||
OS: row['OS'] || '',
|
||||
CPU: row['CPU'] || '',
|
||||
RAM: row['RAM'] || '',
|
||||
GPU: row['GPU'] || '',
|
||||
SSD1: row['Storage1'] || row['SSD1'] || '',
|
||||
SSD2: row['Storage2'] || row['SSD2'] || '',
|
||||
HDD1: row['Storage3'] || row['HDD1'] || '',
|
||||
모니터링: row['모니터링'] || '',
|
||||
비고: row['비고'] || '',
|
||||
storage유형: row['유형'] || '물리',
|
||||
MACaddress: '',
|
||||
HW사양: '',
|
||||
구매일: '',
|
||||
금액: '',
|
||||
납품업체: '',
|
||||
품의서명: '',
|
||||
});
|
||||
} else if (sheetName === '스토리지') {
|
||||
hwAssets.push({
|
||||
id: Math.random().toString(36).substring(2, 9),
|
||||
|
||||
1624
src/realServerData.ts
Normal file
1624
src/realServerData.ts
Normal file
File diff suppressed because it is too large
Load Diff
29
src/state.ts
29
src/state.ts
@@ -1,5 +1,6 @@
|
||||
import { MasterAssetData } from './excelHandler';
|
||||
import { generateDummyData } from './dummyDataGenerator';
|
||||
import { realServerData } from './realServerData';
|
||||
|
||||
// --- State Definitions ---
|
||||
export interface AppState {
|
||||
@@ -9,9 +10,35 @@ export interface AppState {
|
||||
activeCharts: any[];
|
||||
}
|
||||
|
||||
const dummy = generateDummyData();
|
||||
// 서버 데이터만 실제 데이터로 교체
|
||||
const mergedHw = [
|
||||
...dummy.hw.filter(a => a.type !== '서버'),
|
||||
...realServerData.map(s => ({
|
||||
...s,
|
||||
type: '서버',
|
||||
관리자: s.담당자_정 || '홍길동',
|
||||
담당자_정: s.담당자_정 || '홍길동',
|
||||
담당자_부: s.담당자_부 || '김철수',
|
||||
MACaddress: s.MACaddress || '',
|
||||
HW사양: s.HW사양 || '',
|
||||
구매일: s.구매일 || '',
|
||||
금액: s.금액 || '',
|
||||
납품업체: s.납품업체 || '',
|
||||
품의서명: s.품의서명 || '',
|
||||
원격접속: s.원격접속 || '',
|
||||
서버ID: s.서버ID || '',
|
||||
서버PW: s.서버PW || '',
|
||||
비고: s.비고 || ''
|
||||
}))
|
||||
];
|
||||
|
||||
// --- Initial State ---
|
||||
export const state: AppState = {
|
||||
masterData: generateDummyData(),
|
||||
masterData: {
|
||||
...dummy,
|
||||
hw: mergedHw
|
||||
},
|
||||
activeCategory: 'hw',
|
||||
activeSubTab: '대시보드',
|
||||
activeCharts: []
|
||||
|
||||
@@ -279,12 +279,15 @@ body {
|
||||
background-color: var(--white);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
overflow: auto; /* 가로/세로 스크롤 허용 */
|
||||
max-height: calc(100vh - 180px); /* 화면 높이에 맞춰 제한 (가로 스크롤바 노출용) */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-collapse: separate; /* sticky border 유지를 위해 separate 설정 */
|
||||
border-spacing: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -299,6 +302,10 @@ th {
|
||||
font-size: 0.875rem;
|
||||
white-space: nowrap;
|
||||
background-color: #FAFAFA;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
box-shadow: inset 0 -1px 0 var(--border-color); /* sticky 시 경계선 유지 */
|
||||
}
|
||||
|
||||
td {
|
||||
|
||||
@@ -59,7 +59,27 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
|
||||
});
|
||||
} else {
|
||||
if (state.activeSubTab === '서버') {
|
||||
table.innerHTML = `<thead><tr><th>No</th><th>법인</th><th>자산번호</th><th>유형</th><th>용도</th><th>설치위치</th><th>담당자(정)</th><th>담당자(부)</th><th>IP 주소</th><th>원격접속</th><th>모델명</th><th>OS</th><th>CPU</th><th>RAM</th><th>GPU</th><th>Storage1</th><th>Storage2</th><th>Storage3</th><th>모니터링</th><th>비고</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>법인</th>
|
||||
<th>자산번호</th>
|
||||
<th>유형</th>
|
||||
<th>용도</th>
|
||||
<th>상세</th>
|
||||
<th>설치위치</th>
|
||||
<th>담당자</th>
|
||||
<th>IP 주소</th>
|
||||
<th>원격접속</th>
|
||||
<th>모델명</th>
|
||||
<th>OS</th>
|
||||
<th>CPU</th>
|
||||
<th>RAM</th>
|
||||
<th>Storage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dynamic-tbody"></tbody>`;
|
||||
} else {
|
||||
table.innerHTML = `<thead><tr><th>No</th><th>법인</th>${state.activeSubTab === '전산비품' ? '<th>유형</th>' : ''}<th>자산코드</th><th>명칭</th><th>위치</th><th>관리자</th><th>구매일</th><th>금액</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
|
||||
}
|
||||
@@ -67,43 +87,74 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
|
||||
container.appendChild(table);
|
||||
mainContent.appendChild(container);
|
||||
const tbody = document.getElementById('dynamic-tbody')!;
|
||||
const colCount = state.activeSubTab === '서버' ? 21 : (state.activeSubTab === '전산비품' ? 11 : 10);
|
||||
const colCount = state.activeSubTab === '서버' ? 15 : (state.activeSubTab === '전산비품' ? 11 : 10);
|
||||
if (list.length === 0) { tbody.innerHTML = `<tr><td colspan="${colCount}">등록된 자산이 없습니다.</td></tr>`; return; }
|
||||
|
||||
list.forEach((asset, idx) => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.style.cursor = 'pointer';
|
||||
|
||||
const formatInline = (v: any) => String(v || '').replace(/\n/g, ' / ').trim();
|
||||
const getBadge = (text: string, bgColor: string) =>
|
||||
`<span style="background:${bgColor}; color:white; font-size:10px; padding:1px 4px; border-radius:3px; font-weight:700; margin-right:4px; display:inline-block; line-height:1.2;">${text}</span>`;
|
||||
|
||||
if (state.activeSubTab === '서버') {
|
||||
const mainManager = asset.담당자_정 || '';
|
||||
const subManager = asset.담당자_부 || '';
|
||||
|
||||
// 담당자 배지화
|
||||
const managerHtml = [
|
||||
mainManager ? `${getBadge('정', '#1E5149')} ${mainManager}` : '',
|
||||
subManager ? `${getBadge('부', '#9CA3AF')} ${subManager}` : ''
|
||||
].filter(v => v !== '').join(' / ');
|
||||
|
||||
// 원격접속 배지화
|
||||
const tools = (asset.원격접속 || '').split('\n');
|
||||
const ids = (asset.서버ID || '').split('\n');
|
||||
const pws = (asset.서버PW || '').split('\n');
|
||||
const maxLen = Math.max(tools.length, ids.length, pws.length);
|
||||
|
||||
let remoteItems = [];
|
||||
for(let i=0; i<maxLen; i++) {
|
||||
let toolName = tools[i] || '접속';
|
||||
let badgeColor = '#3B82F6'; // 기본 파랑 (Remote)
|
||||
if (toolName.toLowerCase().includes('any')) badgeColor = '#EF4444'; // Anydesk 빨강
|
||||
if (toolName.toLowerCase().includes('chrome')) badgeColor = '#F59E0B'; // Chrome 노랑
|
||||
|
||||
let item = `${getBadge(toolName, badgeColor)}`;
|
||||
if (ids[i] || pws[i]) {
|
||||
item += ` (${ids[i] || '-'}/${pws[i] || '-'})`;
|
||||
}
|
||||
remoteItems.push(item);
|
||||
}
|
||||
const remoteHtml = remoteItems.join(' / ');
|
||||
|
||||
// IP 및 Storage (기존 유지)
|
||||
const ipInfo = [asset.IP주소, asset.IP2].filter(v => v && v !== '').join(' / ');
|
||||
const storageInfo = [asset.SSD1, asset.SSD2].filter(v => v && v !== '').join(' / ');
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${idx+1}</td>
|
||||
<td>${asset.법인}</td>
|
||||
<td class="text-nowrap">${asset.자산코드}</td>
|
||||
<td>${asset.storage유형 || '물리'}</td>
|
||||
<td class="text-nowrap">${asset.용도 || asset.명칭 || '-'}</td>
|
||||
<td class="text-nowrap">${asset.위치}</td>
|
||||
<td>${asset.담당자_정 || asset.관리자 || '-'}</td>
|
||||
<td>${asset.담당자_부 || '-'}</td>
|
||||
<td>${asset.IP주소}</td>
|
||||
<td class="text-nowrap">${asset.원격접속 || '-'}</td>
|
||||
<td class="text-nowrap">${asset.모델명 || '-'}</td>
|
||||
<td>${asset.OS || ''}</td>
|
||||
<td>${asset.CPU || ''}</td>
|
||||
<td>${asset.RAM || ''}</td>
|
||||
<td>${asset.GPU || '-'}</td>
|
||||
<td>${asset.SSD1 || '-'}</td>
|
||||
<td>${asset.SSD2 || '-'}</td>
|
||||
<td>${asset.HDD1 || '-'}</td>
|
||||
<td>${asset.모니터링 || '-'}</td>
|
||||
<td>${asset.비고 || '-'}</td>
|
||||
<td><button class="btn-outline btn-edit">수정</button></td>
|
||||
<td class="text-nowrap">${formatInline(asset.법인)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.자산코드)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.storage유형)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.용도)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.상세)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.위치)}</td>
|
||||
<td class="text-nowrap">${managerHtml}</td>
|
||||
<td class="text-nowrap">${formatInline(ipInfo)}</td>
|
||||
<td class="text-nowrap">${remoteHtml}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.모델명)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.OS)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.CPU)}</td>
|
||||
<td class="text-nowrap">${formatInline(asset.RAM)}</td>
|
||||
<td class="text-nowrap">${formatInline(storageInfo)}</td>
|
||||
`;
|
||||
} else {
|
||||
tr.innerHTML = `<td>${idx+1}</td><td>${asset.법인}</td>${state.activeSubTab === '전산비품' ? `<td>${asset.비품유형||'-'}</td>` : ''}<td>${asset.자산코드}</td><td>${asset.명칭}</td><td>${asset.위치}</td><td>${asset.관리자}</td><td>${asset.구매일||''}</td><td>${asset.금액||''}</td><td><button class="btn-outline btn-edit">수정</button></td>`;
|
||||
}
|
||||
|
||||
tr.addEventListener('click', (e) => { if (!(e.target as HTMLElement).closest('button')) openHwModal(asset); });
|
||||
tr.querySelector('.btn-edit')?.addEventListener('click', () => openHwModal(asset));
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user