282 lines
12 KiB
PHP
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'; ?>
|