feat: TBM 중복 배정 방지, 설비 배치도 좌표계 통일, 구역 상세 CSS 수정

- TBM 팀원 추가 시 중복 배정 검증 및 409 에러 처리 (tbmController, tbmModel, tbm-create.js, tbm.js, tbm/api.js)
- tkuser/tkfb 설비 배치도 좌표계를 좌상단 기준으로 통일 (CSS left/top 방식)
- tkuser 설비 배치도에 드래그 이동, 코너 리사이즈, 배치 버튼 추가
- 대분류 지도 영역 수정 버튼 추가 (workplace-layout-map.js, tkuser-layout-map.js)
- tkfb workplace-status 캔버스 maxWidth 800 통일
- zone-detail.css object-fit:contain 제거 → height:auto로 마커 위치 정확도 개선
- imageUploadService 업로드 경로 Docker 볼륨 마운트 경로로 수정
- repair-management 카테고리 필터 nonconformity → facility 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-05 15:38:11 +09:00
parent 7a12869d26
commit e18983ac06
16 changed files with 465 additions and 72 deletions

View File

@@ -447,12 +447,17 @@ function renderRegionList() {
<div>
<span style="font-weight: 600; color: #1e293b;">${region.workplace_name}</span>
<span style="color: #94a3b8; font-size: 12px; margin-left: 8px;">
(${region.x_start}%, ${region.y_start}%) ~ (${region.x_end}%, ${region.y_end}%)
(${parseFloat(region.x_start).toFixed(1)}%, ${parseFloat(region.y_start).toFixed(1)}%) ~ (${parseFloat(region.x_end).toFixed(1)}%, ${parseFloat(region.y_end).toFixed(1)}%)
</span>
</div>
<button onclick="deleteRegion(${region.region_id})" class="btn-small btn-delete" style="padding: 4px 8px; font-size: 12px;">
🗑️ 삭제
</button>
<div style="display: flex; gap: 4px;">
<button onclick="editRegion(${region.workplace_id})" class="btn-small" style="padding: 4px 8px; font-size: 12px; background: #3b82f6; color: white; border: none; border-radius: 4px; cursor: pointer;">
✏️ 수정
</button>
<button onclick="deleteRegion(${region.region_id})" class="btn-small btn-delete" style="padding: 4px 8px; font-size: 12px; border: none; border-radius: 4px; cursor: pointer;">
🗑️ 삭제
</button>
</div>
</div>
`;
});
@@ -461,6 +466,52 @@ function renderRegionList() {
listDiv.innerHTML = listHtml;
}
/**
* 영역 수정 모드 진입
*/
function editRegion(workplaceId) {
// 작업장 드롭다운 선택
const select = document.getElementById('regionWorkplaceSelect');
if (select) {
select.value = workplaceId;
}
// 해당 영역 하이라이트
const region = mapRegions.find(r => r.workplace_id == workplaceId);
if (region && layoutMapImage) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(layoutMapImage, 0, 0, canvas.width, canvas.height);
drawExistingRegions();
// 수정 대상 영역을 주황색으로 강조
const x1 = (region.x_start / 100) * canvas.width;
const y1 = (region.y_start / 100) * canvas.height;
const x2 = (region.x_end / 100) * canvas.width;
const y2 = (region.y_end / 100) * canvas.height;
ctx.strokeStyle = '#f59e0b';
ctx.lineWidth = 3;
ctx.setLineDash([6, 4]);
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
ctx.setLineDash([]);
ctx.fillStyle = 'rgba(245, 158, 11, 0.25)';
ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
// 라벨
ctx.fillStyle = '#f59e0b';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('✏️ ' + (region.workplace_name || ''), x1 + 5, y1 + 20);
}
window.showToast(`"${region?.workplace_name || '작업장'}" 위치를 수정합니다. 지도에서 새 위치를 드래그한 후 저장하세요.`, 'info');
// 캔버스 영역으로 스크롤
if (canvas) {
canvas.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
/**
* 영역 삭제
*/
@@ -492,3 +543,4 @@ window.uploadLayoutImage = uploadLayoutImage;
window.clearCurrentRegion = clearCurrentRegion;
window.saveRegion = saveRegion;
window.deleteRegion = deleteRegion;
window.editRegion = editRegion;