276 lines
12 KiB
PHP
276 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" href="/tenant/production/screen_work.php">작업지시현황</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link active" 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" id="keyword" class="form-control" placeholder="현장명/발주처/제품명 등">
|
|
<input type="text" id="worker" 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:150px;">작업자</th> <!-- 추가 -->
|
|
<th style="width:140px;">공정진행현황</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; }
|
|
|
|
/* 정렬: 번호/순위/수량/마지막 2개 */
|
|
#jobTable td:nth-child(1),
|
|
#jobTable td:nth-child(2),
|
|
#jobTable td:nth-child(7),
|
|
#jobTable td:nth-last-child(1),
|
|
#jobTable td:nth-last-child(2){ 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-miss{ background:#86b7fe; color:#052c65; } /* 미싱 */
|
|
.badge-check{ background:#d1e7dd; color:#0f5132; } /* 중간검사 */
|
|
.badge-pack{ background:#cfe2ff; color:#052c65; } /* 포장 */
|
|
.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; 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', workers:['이강민','김소연'], stage:'대기'},
|
|
{no:12, pri:0, od:'2025-07-29', buyer:'명보에스티', site:'일산 동국대병원 2층', prod:'KSS01', qty:10, ship:'2025-08-01', workers:['김소연'], stage:'미싱'},
|
|
{no:11, pri:1, od:'2025-07-31', buyer:'이에스디', site:'용인 기흥', prod:'KSS02', qty:2, ship:'2025-08-03', workers:['홍길동'], stage:'중간검사'},
|
|
{no:10, pri:2, od:'2025-06-24', buyer:'대성산업', site:'횡덕중학교', prod:'KSS02', qty:9, ship:'2025-08-03', workers:['김철수'], stage:'포장'},
|
|
{no:9, pri:3, od:'2025-07-14', buyer:'이엘도어', site:'파주아르디움 2층', prod:'KSS01', qty:5, ship:'2025-08-03', workers:['박지훈'], stage:'포장'},
|
|
{no:8, pri:4, od:'2025-07-26', buyer:'주월기업', site:'여의도IFC 1층', prod:'KSS01', qty:5, ship:'2025-08-05', workers:['이강민','유영수'], stage:'중간검사'},
|
|
{no:7, pri:5, od:'2025-08-01', buyer:'명보에스티', site:'일산 동국대병원 1층', prod:'KSS01', qty:20, ship:'2025-08-05', workers:['김소연'], stage:'대기'},
|
|
{no:6, pri:6, od:'2025-08-01', buyer:'주일기업', site:'여의도IFC 2층', prod:'KSS01', qty:5, ship:'2025-08-05', workers:['홍길동'], stage:'중간검사'},
|
|
{no:5, pri:7, od:'2025-08-01', buyer:'주일기업', site:'여의도IFC 3층', prod:'KSS01', qty:5, ship:'2025-08-05', workers:['김철수'], stage:'미싱'},
|
|
{no:4, pri:3, od:'2025-08-01', buyer:'이엘도어', site:'파주아르디움 1층', prod:'KSS01', qty:5, ship:'2025-08-07', workers:['박지훈'], stage:'완료'},
|
|
{no:3, pri:3, od:'2025-08-03', buyer:'한국특수', site:'인천염림물류창고', prod:'KSS01', qty:5, ship:'2025-08-07', workers:['이강민'], stage:'완료'},
|
|
{no:2, pri:2, od:'2025-08-04', buyer:'명보에스티', site:'평택', prod:'KSS02', qty:1, ship:'2025-08-10', workers:['김소연'], stage:'완료'},
|
|
{no:1, pri:1, od:'2025-08-04', buyer:'명보에스티', site:'장수', prod:'KSS02', qty:2, ship:'2025-08-10', workers:['홍길동'], stage:'완료'},
|
|
];
|
|
|
|
const pageSize = 10;
|
|
let curPage = 1;
|
|
let filtered = [...rows];
|
|
|
|
function stageBadge(stage){
|
|
const cls = {
|
|
'대기':'badge-wait',
|
|
'미싱':'badge-miss',
|
|
'중간검사':'badge-check',
|
|
'포장':'badge-pack',
|
|
'완료':'badge-done',
|
|
'취소':'badge-cancel'
|
|
}[stage] || 'bg-secondary';
|
|
return `<span class="badge badge-state ${cls}">${stage}</span>`;
|
|
}
|
|
|
|
// PC 테이블 렌더
|
|
function renderTable(pageItems){
|
|
const tbody = pageItems.map(r=>`
|
|
<tr class="row-open ${r.stage==='대기'?'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>${(r.workers||[]).join(', ')}</td>
|
|
<td>${stageBadge(r.stage)}</td>
|
|
</tr>
|
|
`).join('');
|
|
$('#jobTbody').html(tbody);
|
|
}
|
|
|
|
// 모바일 카드 렌더
|
|
function renderCards(pageItems){
|
|
const cards = pageItems.map(r=>`
|
|
<div class="card mb-2 job-card ${r.stage==='대기'?'waiting':''}" data-no="${r.no}">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between">
|
|
<div class="title">${r.site}</div>
|
|
<div>${stageBadge(r.stage)}</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="meta">작업자 ${(r.workers||[]).join(', ')}</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)));
|
|
}
|
|
|
|
// 검색
|
|
$('#searchForm').on('submit', function(e){
|
|
e.preventDefault();
|
|
const kw = $('#keyword').val().trim().toLowerCase();
|
|
const wk = $('#worker').val().trim().toLowerCase();
|
|
filtered = rows.filter(r=>{
|
|
const s = [r.buyer,r.site,r.prod,r.od,r.stage,String(r.no)].join(' ').toLowerCase();
|
|
const w = (r.workers||[]).join(',').toLowerCase();
|
|
const inKW = !kw || s.includes(kw);
|
|
const inWK = !wk || w.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(/^\d+$/.test(val)) curPage = parseInt(val,10);
|
|
render();
|
|
});
|
|
|
|
// 행/카드 클릭 → 작업지시 모달 로드(동일 흐름 유지)
|
|
$(document).on('click', 'tr.row-open, .btn-open-wi', function(){
|
|
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/process_detail.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'; ?>
|