## 처리시간 지표 - 업무시간 기준(09-18 평일) / 공휴일 제외 24h / 달력 기준 3가지 모드 선택 - 공휴일 DB 관리 (holidays 테이블, 수동 등록·삭제·일괄 추가) - 2026년 공휴일 등록 지원 - 설정 페이지에서 라디오 버튼으로 모드 선택 ## 대시보드 차트 - 월별 평균 처리시간 막대 차트 추가 - 월별 신고 접수 건수 누적 막대 차트 추가 - 월별 → 일별 드릴다운 (막대 클릭 시 해당 월의 일별 차트로 전환) - 일별 막대 클릭 시 처리 완료/신고 접수 상세 내역 모달 - 충전기별 누적 고장 건수 Top 10 수평 막대 차트 추가 ## 신고 목록 - # 컬럼을 DB PK 대신 현재 목록 순서(1, 2, 3…)로 표시 - 엑셀 export 접수번호도 순차번호로 변경 ## 모바일 네비게이션 버그 수정 - 모바일에서 가로 오버플로우 시 nav가 body 넓이로 늘어나 햄버거 버튼이 화면 밖으로 밀리는 문제 수정 - nav를 position:fixed + body padding-top:54px 로 변경 (전체 페이지 적용) - 충전기 관리·신고 목록 페이지 지도 컨테이너에 isolation:isolate 적용 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
82 lines
3.1 KiB
HTML
82 lines
3.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>QR 스캔</title>
|
|
<link rel="stylesheet" href="/css/style.css">
|
|
<style>
|
|
#reader{width:100%;max-width:400px;margin:0 auto;border-radius:10px;overflow:hidden;}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="nav">
|
|
<span class="nav-brand">⚡ EV AS 관리</span>
|
|
<div id="navUser"></div>
|
|
</nav>
|
|
<div class="mech-tab-bar">
|
|
<a href="/pages/mechanic/dashboard.html">📋<span>AS 목록</span></a>
|
|
<a href="/pages/mechanic/scan.html" class="active">📷<span>QR 스캔</span></a>
|
|
<a href="/pages/mechanic/history.html">🗂<span>처리 이력</span></a>
|
|
</div>
|
|
<div class="layout">
|
|
<div class="sidebar">
|
|
<div class="sidebar-section">메뉴</div>
|
|
<a href="/pages/mechanic/dashboard.html">📋 AS 목록</a>
|
|
<a href="/pages/mechanic/scan.html" class="active">📷 QR 스캔</a>
|
|
<a href="/pages/mechanic/history.html">🗂 처리 이력</a>
|
|
</div>
|
|
<div class="main">
|
|
<div style="max-width:480px;margin:0 auto;">
|
|
<h2 style="font-size:18px;font-weight:700;color:var(--navy);margin-bottom:16px">📷 QR 스캔</h2>
|
|
<div class="alert alert-info" style="margin-bottom:16px;">충전기의 QR 코드를 카메라로 인식해 주세요.</div>
|
|
<div id="reader"></div>
|
|
<div id="result" class="alert alert-success" style="display:none;margin-top:14px;"></div>
|
|
<div style="margin-top:16px;">
|
|
<div class="form-group">
|
|
<label>충전기 ID 직접 입력</label>
|
|
<div style="display:flex;gap:8px;">
|
|
<input type="text" id="manualId" placeholder="예: CG-003">
|
|
<button class="btn btn-primary" onclick="goManual()">이동</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script src="https://unpkg.com/html5-qrcode/minified/html5-qrcode.min.js"></script>
|
|
<script src="/js/api.js"></script><script src="/js/auth.js"></script>
|
|
<script>
|
|
Auth.require(['mechanic','admin']);
|
|
Auth.renderNav(document.getElementById('navUser'));
|
|
|
|
const scanner = new Html5Qrcode("reader");
|
|
scanner.start({ facingMode: "environment" },
|
|
{ fps: 10, qrbox: { width: 250, height: 250 } },
|
|
qrText => {
|
|
scanner.stop();
|
|
document.getElementById('result').style.display = 'block';
|
|
document.getElementById('result').textContent = '인식됨: ' + qrText + ' — 이동 중...';
|
|
// URL에서 charger_id 추출
|
|
try {
|
|
const url = new URL(qrText);
|
|
const parts = url.pathname.split('/');
|
|
const chargerId = parts[parts.length - 1];
|
|
setTimeout(() => location.href = `/pages/mechanic/repair.html?charger_id=${chargerId}`, 800);
|
|
} catch {
|
|
setTimeout(() => location.href = `/pages/mechanic/repair.html?charger_id=${qrText}`, 800);
|
|
}
|
|
},
|
|
() => {}
|
|
).catch(() => {
|
|
document.getElementById('reader').innerHTML = '<div class="alert alert-warn">카메라 접근이 거부되었습니다. 직접 입력을 이용해 주세요.</div>';
|
|
});
|
|
|
|
function goManual() {
|
|
const id = document.getElementById('manualId').value.trim();
|
|
if (!id) return;
|
|
location.href = `/pages/mechanic/repair.html?charger_id=${id}`;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|