feat: QR 자산 스캔 점검, 모바일 웹뷰 및 관리자 승인 시스템 구현 (DB 기반 맵 좌표 저장 단일화 포함)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { IMAGE_LOCATIONS } from '../components/Modal/SharedData';
|
||||
import { createIcons, X, Save, Trash2, ChevronLeft, ChevronRight } from 'lucide';
|
||||
import { QRPrinter } from '../core/qr_print';
|
||||
|
||||
export class MapEditor {
|
||||
private container: HTMLElement;
|
||||
@@ -42,7 +43,7 @@ export class MapEditor {
|
||||
|
||||
private async loadAssets() {
|
||||
try {
|
||||
const res = await fetch(`http://${location.hostname}:3000/api/assets/master`);
|
||||
const res = await fetch('/api/assets/master');
|
||||
const masterData = await res.json();
|
||||
const allHw = [
|
||||
...(masterData.pc || []),
|
||||
@@ -95,7 +96,7 @@ export class MapEditor {
|
||||
|
||||
private async loadConfig() {
|
||||
try {
|
||||
const res = await fetch(`http://${location.hostname}:3000/api/maps`);
|
||||
const res = await fetch('/api/maps');
|
||||
this.allMapConfig = await res.json();
|
||||
} catch (err) {
|
||||
console.error('Failed to load config:', err);
|
||||
@@ -185,6 +186,30 @@ export class MapEditor {
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('btn-print-map-qrs')?.addEventListener('click', () => {
|
||||
if (this.boxes.length === 0) {
|
||||
alert('인쇄할 구역이 없습니다.');
|
||||
return;
|
||||
}
|
||||
const cleanKey = getCleanMapKey(this.currentPath);
|
||||
const locName = getLocationName(this.currentPath);
|
||||
|
||||
const items = this.boxes.map((box, index) => {
|
||||
const padIdx = String(index + 1).padStart(3, '0');
|
||||
const locCode = `LOC-${cleanKey}-${padIdx}`;
|
||||
const locDetail = getLocationDetail(this.currentPath, index);
|
||||
return {
|
||||
type: 'location' as const,
|
||||
code: locCode,
|
||||
title: '[ HM LOCATION ]',
|
||||
subtitle: locName,
|
||||
dept: locDetail
|
||||
};
|
||||
});
|
||||
|
||||
QRPrinter.print(items);
|
||||
});
|
||||
|
||||
document.getElementById('btn-save-server')?.addEventListener('click', () => this.saveToServer());
|
||||
}
|
||||
|
||||
@@ -195,7 +220,7 @@ export class MapEditor {
|
||||
this.saveBtn.disabled = true;
|
||||
this.saveBtn.textContent = '저장 중...';
|
||||
|
||||
const res = await fetch(`http://${location.hostname}:3000/api/maps/save`, {
|
||||
const res = await fetch('/api/maps/save', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: this.currentPath, boxes: this.boxes })
|
||||
@@ -248,7 +273,10 @@ export class MapEditor {
|
||||
item.innerHTML = `
|
||||
<div class="box-header">
|
||||
<span class="box-index">#${i+1}</span>
|
||||
<button class="btn-del" onclick="removeBox(${i})">×</button>
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||||
<button class="btn btn-outline btn-sm" onclick="printBoxQR(${i})" style="padding: 2px 6px; font-size: 11px; margin: 0; cursor: pointer;">QR</button>
|
||||
<button class="btn-del" onclick="removeBox(${i})">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-inputs margin-bottom">
|
||||
<select data-index="${i}" data-prop="asset_id">
|
||||
@@ -294,5 +322,46 @@ export class MapEditor {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
(window as any).printBoxQR = (index: number) => {
|
||||
const box = this.boxes[index];
|
||||
if (!box) return;
|
||||
const cleanKey = getCleanMapKey(this.currentPath);
|
||||
const padIdx = String(index + 1).padStart(3, '0');
|
||||
const locCode = `LOC-${cleanKey}-${padIdx}`;
|
||||
const locDetail = getLocationDetail(this.currentPath, index);
|
||||
const locName = getLocationName(this.currentPath);
|
||||
|
||||
QRPrinter.print([{
|
||||
type: 'location',
|
||||
code: locCode,
|
||||
title: '[ HM LOCATION ]',
|
||||
subtitle: locName,
|
||||
dept: locDetail
|
||||
}]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getCleanMapKey(path: string) {
|
||||
let clean = path.replace('img/location_photo/', '').replace('.png', '');
|
||||
clean = clean.replace('서관', 'W').replace('동관', 'E');
|
||||
clean = clean.replace('한맥빌딩/MDF실/MDF_', 'HAN-MDF-');
|
||||
clean = clean.replace('기술개발센터/서버실/서버실_', 'DEV-SVR-');
|
||||
clean = clean.replace(/\//g, '-');
|
||||
return clean;
|
||||
}
|
||||
|
||||
function getLocationName(path: string) {
|
||||
if (path.includes('IDC')) return 'IDC';
|
||||
if (path.includes('한맥빌딩')) return '한맥빌딩';
|
||||
if (path.includes('기술개발센터')) return '기술개발센터';
|
||||
return '기타';
|
||||
}
|
||||
|
||||
function getLocationDetail(path: string, idx: number) {
|
||||
let clean = path.replace('img/location_photo/', '').replace('.png', '');
|
||||
let parts = clean.split('/');
|
||||
let lastPart = parts[parts.length - 1];
|
||||
return `${lastPart} 구역 자리 #${idx + 1}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user