Files
sam-api/public/tenant/production/screen_work.php
hskwon cc206fdbed style: Laravel Pint 코드 포맷팅 적용
- PSR-12 스타일 가이드 준수
- 302개 파일 스타일 이슈 자동 수정
- 코드 로직 변경 없음 (포맷팅만)
2025-11-06 17:45:49 +09:00

282 lines
12 KiB
PHP

<!-- SAM RULES: include=../inc/header.php; base=/tenant; width=1280; js=jQuery+BS5 -->
<?php
$CURRENT_SECTION = 'process';
include '../inc/header.php';
?>
<div class="container" style="max-width:1280px; margin-top:18px;">
<!-- 탭 (좌측) + 검색 (우측) -->
<div class="d-flex align-items-center justify-content-between mb-2">
<ul class="nav nav-pills gap-2">
<li class="nav-item">
<a class="nav-link active" href="/tenant/production/screen_work.php">작업지시현황</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/tenant/production/screen_progress.php">공정진행현황</a>
</li>
</ul>
<!-- 기본 검색폼 (키워드 + 작업자 + 버튼) -->
<form id="searchForm" class="d-flex gap-2">
<div class="search-form d-flex gap-2">
<input type="text" class="form-control" placeholder="현장명/발주처/제품명 등">
<input type="text" class="form-control" placeholder="작업자">
<button type="submit" class="btn btn-primary">검색</button>
</div>
</form>
</div>
<div class="border rounded p-2">
<div class="table-responsive">
<table class="table table-hover align-middle m-0" id="jobTable">
<thead class="table-light">
<tr>
<th style="width:64px;">NO</th>
<th style="width:90px;">작업순위</th>
<th style="width:120px;">수주계약일</th>
<th style="width:140px;">발주처</th>
<th>현장명</th>
<th style="width:110px;">제품명</th>
<th style="width:90px;">수량(틀)</th>
<th style="width:130px;">출고요청일</th>
<th style="width:120px;">작업지시현황</th>
</tr>
</thead>
<tbody id="jobTbody">
<!-- JS로 렌더 -->
</tbody>
</table>
</div>
<!-- 페이지네이션 -->
<div class="d-flex justify-content-center py-2" id="pager"></div>
</div>
</div>
<!-- 작업지시 모달 (본문 Ajax 로드) -->
<div class="modal fade" id="workInstructionModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">작업 지시</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="닫기"></button>
</div>
<div class="modal-body p-0" id="wiBody">
<!-- /tenant/production/work_instruction.php 로드 -->
</div>
</div>
</div>
</div>
<!-- 모바일 카드 뷰 -->
<div id="jobCards" class="mt-2"></div>
<style>
/* 헤더/셀 간격 줄이기 + 헤더 한 줄 유지 */
#jobTable th{
vertical-align: middle; text-align:center;
white-space: nowrap; padding:.55rem .6rem;
}
#jobTable td{ vertical-align: middle; padding:.55rem .6rem; }
/* 정렬: 번호/순위/수량/상태 */
#jobTable td:nth-child(1),
#jobTable td:nth-child(2),
#jobTable td:nth-child(7),
#jobTable td:nth-child(9){ text-align:center; }
/* 현장명: 고정폭 + 말줄임 */
#jobTable td:nth-child(5){
max-width: 280px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 상태 뱃지 (필요 시 유지) */
.badge-state { font-weight:600; }
.badge-wait { background:#fff1a8; color:#111; }
.badge-run { background:#7fb3ff; }
.badge-rcv { background:#d1e7dd; color:#0f5132;}
.badge-done { background:#adb5bd; }
.badge-cancel { background:#ffb3b3; }
/* '대기' 행 하이라이트 (나머지는 hover시에만) */
.row-waiting { background:#fff8cc; }
/* 페이지네이션 - 얇고 최신 느낌 */
.pagination .page-link{ padding:.35rem .6rem; }
.pagination .page-item.active .page-link{
background:#2c4a85; border-color:#2c4a85;
}
/* 모바일: 테이블 숨기고 카드형 노출 */
@media (max-width: 767.98px){
.table-responsive{ display:none; }
#jobCards{ display:block; }
}
@media (min-width: 768px){
#jobCards{ display:none; }
}
/* 카드형 아이템 */
.job-card .title{ font-weight:700; }
.job-card .meta{ font-size:.9rem; color:#555; }
.job-card.waiting{ background:#fff8cc; }
.search-form .btn {
height: 38px; /* input 높이와 동일하게 */
line-height: 1.5; /* 세로 중앙 정렬 */
padding: 0 16px; /* 좌우 여백만 */
white-space: nowrap; /* 줄바꿈 방지 */
}
</style>
<script>
$(function(){
// ------- 샘플 데이터 (작업자 필드 추가) -------
const rows = [
{no:13, pri:0, od:'2025-07-25', buyer:'명보에스티', site:'일산 동국대병원 3층', prod:'KSS01', qty:10, ship:'2025-08-01', state:'대기', worker:'이강민'},
{no:12, pri:0, od:'2025-07-29', buyer:'명보에스티', site:'일산 동국대병원 2층', prod:'KSS01', qty:10, ship:'2025-08-01', state:'대기', worker:'김소연'},
{no:11, pri:1, od:'2025-07-31', buyer:'이에스디', site:'용인 기흥', prod:'KSS02', qty:2, ship:'2025-08-03', state:'작업중', worker:'홍길동'},
{no:10, pri:2, od:'2025-06-24', buyer:'대성산업', site:'횡덕중학교', prod:'KSS02', qty:9, ship:'2025-08-03', state:'작업중', worker:'김철수'},
{no:9, pri:3, od:'2025-07-14', buyer:'이엘도어', site:'파주아르디움 2층', prod:'KSS01', qty:5, ship:'2025-08-03', state:'작업중', worker:'박지훈'},
{no:8, pri:4, od:'2025-07-26', buyer:'주월기업', site:'여의도IFC 1층', prod:'KSS01', qty:5, ship:'2025-08-05', state:'작업중', worker:'이강민'},
{no:7, pri:5, od:'2025-08-01', buyer:'명보에스티', site:'일산 동국대병원 1층', prod:'KSS01', qty:20, ship:'2025-08-05', state:'작업취소', worker:'김소연'},
{no:6, pri:6, od:'2025-08-01', buyer:'주일기업', site:'여의도IFC 2층', prod:'KSS01', qty:5, ship:'2025-08-05', state:'접수', worker:'홍길동'},
{no:5, pri:7, od:'2025-08-01', buyer:'주일기업', site:'여의도IFC 3층', prod:'KSS01', qty:5, ship:'2025-08-05', state:'접수', worker:'김철수'},
{no:4, pri:3, od:'2025-08-01', buyer:'이엘도어', site:'파주아르디움 1층', prod:'KSS01', qty:5, ship:'2025-08-07', state:'완료', worker:'박지훈'},
{no:3, pri:3, od:'2025-08-03', buyer:'한국특수', site:'인천염림물류창고', prod:'KSS01', qty:5, ship:'2025-08-07', state:'완료', worker:'이강민'},
{no:2, pri:2, od:'2025-08-04', buyer:'명보에스티', site:'평택', prod:'KSS02', qty:1, ship:'2025-08-10', state:'완료', worker:'김소연'},
{no:1, pri:1, od:'2025-08-04', buyer:'명보에스티', site:'장수', prod:'KSS02', qty:2, ship:'2025-08-10', state:'완료', worker:'홍길동'},
];
const pageSize = 10;
let curPage = 1;
let filtered = [...rows];
function badge(state){
switch(state){
case '대기': return '<span class="badge badge-state badge-wait">대기</span>';
case '작업중': return '<span class="badge badge-state badge-run">작업중</span>';
case '접수': return '<span class="badge badge-state badge-rcv">접수</span>';
case '완료': return '<span class="badge badge-state badge-done">완료</span>';
case '작업취소': return '<span class="badge badge-state badge-cancel">작업취소</span>';
default: return state;
}
}
// PC 테이블 렌더
function renderTable(pageItems){
const tbody = pageItems.map(r=>`
<tr class="row-open ${r.state==='대기'?'row-waiting':''}" data-no="${r.no}">
<td>${r.no}</td>
<td>${r.pri}</td>
<td>${r.od}</td>
<td>${r.buyer}</td>
<td title="${r.site}">${r.site}</td>
<td>${r.prod}</td>
<td>${r.qty}</td>
<td>${r.ship}</td>
<td>${badge(r.state)}</td>
</tr>
`).join('');
$('#jobTbody').html(tbody);
}
// 모바일 카드 렌더
function renderCards(pageItems){
const cards = pageItems.map(r=>`
<div class="card mb-2 job-card ${r.state==='대기'?'waiting':''}" data-no="${r.no}">
<div class="card-body">
<div class="d-flex justify-content-between">
<div class="title">${r.site}</div>
<div>${badge(r.state)}</div>
</div>
<div class="meta mt-1">
<span>NO ${r.no}</span> · <span>순위 ${r.pri}</span> · <span>${r.prod}</span> · <span>${r.qty}틀</span>
</div>
<div class="meta">발주처 ${r.buyer} / 출고요청일 ${r.ship}</div>
<div class="mt-2 text-end">
<button class="btn btn-sm btn-outline-primary btn-open-wi">작업 지시</button>
</div>
</div>
</div>
`).join('');
$('#jobCards').html(cards);
}
// 페이지네이션 렌더 (부트스트랩)
function renderPager(totalPage){
let html = `
<nav aria-label="pagination">
<ul class="pagination pagination-sm justify-content-center mb-0">
<li class="page-item ${curPage===1?'disabled':''}">
<a class="page-link" href="#" data-page="prev">«</a>
</li>`;
for(let p=1;p<=totalPage;p++){
html += `
<li class="page-item ${p===curPage?'active':''}">
<a class="page-link" href="#" data-page="${p}">${p}</a>
</li>`;
}
html += `
<li class="page-item ${curPage===totalPage?'disabled':''}">
<a class="page-link" href="#" data-page="next">»</a>
</li>
</ul>
</nav>`;
$('#pager').html(html);
}
function render(){
const start = (curPage-1)*pageSize;
const pageItems = filtered.slice(start, start+pageSize);
renderTable(pageItems);
renderCards(pageItems);
renderPager(Math.max(1, Math.ceil(filtered.length / pageSize)));
}
// 검색 (버튼 submit)
$('#searchForm').on('submit', function(e){
e.preventDefault();
const kw = $('#keyword').val().trim().toLowerCase();
const wk = $('#worker').val().trim().toLowerCase();
filtered = rows.filter(r=>{
const inKW = !kw || [r.buyer,r.site,r.prod,r.state,r.od,String(r.no)].some(v=>String(v).toLowerCase().includes(kw));
const inWK = !wk || String(r.worker||'').toLowerCase().includes(wk);
return inKW && inWK;
});
curPage = 1; render();
});
// 페이지네이션 클릭
$(document).on('click', '.pagination .page-link', function(e){
e.preventDefault();
const val = $(this).data('page');
const total = Math.max(1, Math.ceil(filtered.length / pageSize));
if(val==='prev' && curPage>1) curPage--;
else if(val==='next' && curPage<total) curPage++;
else if(typeof val==='number' || /^\d+$/.test(val)) curPage = parseInt(val,10);
render();
});
// 행/카드 클릭 → 작업지시 모달 로드
$(document).on('click', 'tr.row-open, .btn-open-wi', function(){
// 테이블 행이면 data-no에서, 카드 버튼이면 부모 카드에서 가져옴
const $el = $(this).closest('[data-no]');
const no = $el.data('no');
const r = rows.find(x=>x.no==no);
$('#wiBody').html('<div class="p-4 text-center text-muted">로딩 중…</div>');
$('#wiBody').load('/tenant/production/work_instruction.php', {
job_id: 'SCR-'+String(no).padStart(3,'0'),
cust: r.buyer,
order_no: r.od.replaceAll('-','')+'-'+String(no).padStart(2,'0')
}, function(){
new bootstrap.Modal('#workInstructionModal').show();
});
});
render();
});
</script>
<?php include '../inc/footer.php'; ?>