feat: [equipment] 설비 목록에 관리자 정/부 열 분리 및 QR 코드 열 추가

- 담당자 단일 열 → 관리자 정, 관리자 부 2열로 분리
- QR 코드 아이콘 열 추가 (클릭 시 모달로 QR 표시)
- QR PNG 다운로드 기능 포함
This commit is contained in:
김보곤
2026-02-28 16:17:27 +09:00
parent 080e6b13e5
commit 947e1d1993
2 changed files with 81 additions and 2 deletions

View File

@@ -79,9 +79,29 @@ class="bg-white rounded-lg shadow-sm">
</div>
</div>
<!-- QR 코드 모달 -->
<div id="qrModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50" onclick="if(event.target===this)closeQrModal()">
<div class="bg-white rounded-xl shadow-xl p-6 mx-4" style="max-width: 320px; width: 100%;">
<div class="text-center mb-4">
<p id="qrEquipCode" class="text-sm text-blue-600 font-mono"></p>
<p id="qrEquipName" class="text-lg font-semibold text-gray-800"></p>
</div>
<div id="qrCanvas" class="flex justify-center mb-4"></div>
<div class="flex gap-2">
<button onclick="downloadQr()" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm transition">
PNG 다운로드
</button>
<button onclick="closeQrModal()" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg text-sm transition">
닫기
</button>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script>
const FILTER_KEY = 'equipment_filter';
const filterForm = document.getElementById('filterForm');
@@ -127,6 +147,52 @@ function saveFilter() {
saveFilter();
});
let currentQrCode = null;
let currentQrFileName = '';
function showQr(equipmentId, equipCode, equipName) {
document.getElementById('qrEquipCode').textContent = equipCode;
document.getElementById('qrEquipName').textContent = equipName;
const container = document.getElementById('qrCanvas');
container.innerHTML = '';
const baseUrl = '{{ rtrim(config("app.url"), "/") }}';
const url = baseUrl + '/m/inspect/' + equipmentId;
currentQrCode = new QRCode(container, {
text: url,
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.M,
});
currentQrFileName = equipCode + '_' + equipName;
const modal = document.getElementById('qrModal');
modal.classList.remove('hidden');
modal.classList.add('flex');
}
function closeQrModal() {
const modal = document.getElementById('qrModal');
modal.classList.add('hidden');
modal.classList.remove('flex');
}
function downloadQr() {
const canvas = document.querySelector('#qrCanvas canvas');
if (!canvas) return;
canvas.toBlob(function(blob) {
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'QR_' + currentQrFileName + '.png';
a.click();
URL.revokeObjectURL(a.href);
});
}
function confirmDelete(id, name) {
showDeleteConfirm(name, () => {
fetch(`/api/admin/equipment/${id}`, {

View File

@@ -9,8 +9,10 @@
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">위치</th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">생산라인</th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">상태</th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">담당자</th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">관리자 </th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">관리자 </th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">구입일</th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">QR</th>
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700">액션</th>
</tr>
</thead>
@@ -40,9 +42,20 @@
<td class="px-3 py-3 whitespace-nowrap text-sm text-center text-gray-600">
{{ $eq->manager?->name ?? '-' }}
</td>
<td class="px-3 py-3 whitespace-nowrap text-sm text-center text-gray-600">
{{ $eq->subManager?->name ?? '-' }}
</td>
<td class="px-3 py-3 whitespace-nowrap text-sm text-center text-gray-600">
{{ $eq->purchase_date?->format('Y-m-d') ?? '-' }}
</td>
<td class="px-3 py-3 whitespace-nowrap text-sm text-center" onclick="event.stopPropagation()">
<button type="button" onclick="showQr({{ $eq->id }}, '{{ addslashes($eq->equipment_code) }}', '{{ addslashes($eq->name) }}')"
class="text-gray-500 hover:text-blue-600 transition" title="QR 코드">
<svg class="w-5 h-5 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h7v7H3V3zm11 0h7v7h-7V3zM3 14h7v7H3v-7zm14 3h.01M17 14h3v3h-3v-3zm0 4h3v3h-3v-3zm-4 0h3v3h-3v-3z"/>
</svg>
</button>
</td>
<td class="px-3 py-3 whitespace-nowrap text-sm text-center" onclick="event.stopPropagation()">
<a href="{{ route('equipment.edit', $eq->id) }}" class="text-blue-600 hover:text-blue-900 mr-2">수정</a>
<button onclick="confirmDelete({{ $eq->id }}, '{{ $eq->name }}')"
@@ -51,7 +64,7 @@ class="text-red-600 hover:text-red-900">삭제</button>
</tr>
@empty
<tr>
<td colspan="9" class="px-6 py-12 text-center text-gray-500">
<td colspan="11" class="px-6 py-12 text-center text-gray-500">
등록된 설비가 없습니다.
</td>
</tr>