290 lines
15 KiB
PHP
290 lines
15 KiB
PHP
<?php
|
||
$CURRENT_SECTION = 'item';
|
||
include '../inc/header.php';
|
||
?>
|
||
<div class="container" style="max-width:1280px; margin-top:24px;">
|
||
<form id="bendForm">
|
||
<!-- 기본정보 -->
|
||
<details open class="mb-3">
|
||
<summary class="fw-semibold">기본정보 입력</summary>
|
||
<div class="row g-2 mt-2">
|
||
<div class="col-md-2">
|
||
<label class="form-label">분류</label>
|
||
<select class="form-select" id="bfCategory">
|
||
<option>스크린</option><option>철재</option><option>기타</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-5">
|
||
<label class="form-label">품목명</label>
|
||
<input class="form-control" id="bfName" placeholder="품목명">
|
||
</div>
|
||
<div class="col-md-2">
|
||
<label class="form-label">단위</label>
|
||
<select class="form-select" id="bfUnit"><option>EA</option><option>mm</option></select>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">검색태그</label>
|
||
<input class="form-control" id="bfTags" placeholder="쉼표로 구분">
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<!-- 고정 규격 -->
|
||
<details open class="mb-3">
|
||
<summary class="fw-semibold">고정 규격정보 입력</summary>
|
||
<div class="row g-2 mt-2">
|
||
<div class="col-md-3">
|
||
<label class="form-label">폭</label>
|
||
<input class="form-control" id="fxWidth" type="number" min="0" placeholder="폭(mm)">
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">규격(가로×세로)</label>
|
||
<div class="input-group">
|
||
<input class="form-control" id="fxW" type="number" min="0" placeholder="가로">
|
||
<span class="input-group-text">×</span>
|
||
<input class="form-control" id="fxH" type="number" min="0" placeholder="세로">
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">재질</label>
|
||
<select class="form-select" id="fxMaterial">
|
||
<option>선택</option><option>EGI</option><option>SUS</option><option>AL</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">레이일폭</label>
|
||
<input class="form-control" id="fxRail" type="number" min="0" placeholder="mm">
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<!-- 기타정보 (+) -->
|
||
<details class="mb-3">
|
||
<summary class="fw-semibold d-flex align-items-center">기타정보 입력
|
||
<button type="button" class="btn btn-sm btn-outline-primary ms-2" id="btnAddMeta">+</button>
|
||
</summary>
|
||
<div id="metaRows" class="mt-2"></div>
|
||
</details>
|
||
|
||
<!-- 절곡 이미지/스텝 -->
|
||
<details open class="mb-4">
|
||
<summary class="fw-semibold">절곡 이미지 & 스텝</summary>
|
||
|
||
<div class="row g-3 mt-2">
|
||
<!-- 스텝 테이블 -->
|
||
<div class="col-md-7">
|
||
<div class="table-responsive">
|
||
<table class="table table-bordered align-middle">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th style="width:60px;">번호</th>
|
||
<th>입력 길이</th>
|
||
<th>연신율(%)</th>
|
||
<th>연신 후</th>
|
||
<th>합계</th>
|
||
<th style="width:90px;">A각</th>
|
||
<th style="width:110px;">옵션</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="stepBody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<button type="button" class="btn btn-outline-secondary btn-sm" id="btnAddCol">마지막 열 추가</button>
|
||
<button type="button" class="btn btn-outline-secondary btn-sm" id="btnDelCol">마지막 열 삭제</button>
|
||
<button type="button" class="btn btn-outline-danger btn-sm" id="btnClear">모든칸 비우기</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 캔버스 -->
|
||
<div class="col-md-5">
|
||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||
<strong>도면 캔버스</strong>
|
||
<div class="d-flex gap-2">
|
||
<input type="file" id="bgImage" accept="image/*" class="form-control form-control-sm">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" id="btnDrawLine">선</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" id="btnDrawRect">사각</button>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" id="btnClearCanvas">지우기</button>
|
||
</div>
|
||
</div>
|
||
<div class="border rounded p-1">
|
||
<canvas id="cv" width="420" height="300" style="display:block; background:#fff;"></canvas>
|
||
</div>
|
||
<small class="text-muted">이미지를 올리면 배경으로 표시됩니다. 간단한 선/사각 도구 제공.</small>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<div class="d-flex justify-content-end gap-2">
|
||
<button class="btn btn-outline-secondary" type="button" id="btnPreview">미리보기</button>
|
||
<button class="btn btn-primary" type="submit">저장</button>
|
||
</div>
|
||
|
||
<textarea id="preview" class="form-control mt-3" rows="8" style="display:none;"></textarea>
|
||
</form>
|
||
</div>
|
||
|
||
<style>
|
||
.meta-row .btn{ --bs-btn-padding-y:.15rem; --bs-btn-padding-x:.4rem; --bs-btn-font-size:.75rem; }
|
||
td input[type=number]{ width: 90px; }
|
||
</style>
|
||
|
||
<script>
|
||
(function($){
|
||
// ===== 기타정보 행 =====
|
||
function metaRowTpl(key='', val='', unit=''){
|
||
return `<div class="row g-2 align-items-center meta-row mb-2">
|
||
<div class="col-4"><input class="form-control form-control-sm meta-key" placeholder="규격"></div>
|
||
<div class="col-5"><input class="form-control form-control-sm meta-val" placeholder="값"></div>
|
||
<div class="col-2"><input class="form-control form-control-sm meta-unit" placeholder="단위"></div>
|
||
<div class="col-1 text-end"><button type="button" class="btn btn-outline-danger btn-sm btnMetaDel">–</button></div>
|
||
</div>`;
|
||
}
|
||
$('#btnAddMeta').on('click', ()=> $('#metaRows').append(metaRowTpl()));
|
||
$('#metaRows').on('click','.btnMetaDel', function(){ $(this).closest('.meta-row').remove(); });
|
||
|
||
// ===== 절곡 스텝 테이블 =====
|
||
let COLS = 2; // 기본 2열
|
||
function drawTable(){
|
||
const rows=[];
|
||
const headers=['번호','입력','연신율','연신후','합계','A각','옵션']; // 설명용
|
||
for(let r=0;r<7;r++){
|
||
const cells=[];
|
||
if (r===0){ // 번호
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-control form-control-sm step-no" type="number" min="1" value="${c+1}">`);
|
||
}else if(r===1){ // 입력
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-control form-control-sm step-in" type="number" min="0" value="0">`);
|
||
}else if(r===2){ // 연신율
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-control form-control-sm step-strain" type="number" step="0.01" value="0">`);
|
||
}else if(r===3){ // 연신율계산 후 (= 입력 × (1 + 연신율/100))
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-control form-control-sm step-after" type="number" readonly value="0">`);
|
||
}else if(r===4){ // 합계 (누적)
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-control form-control-sm step-sum" type="number" readonly value="0">`);
|
||
}else if(r===5){ // 음영
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-check-input step-dark" type="checkbox">`);
|
||
}else if(r===6){ // A각 표시
|
||
for(let c=0;c<COLS;c++) cells.push(`<input class="form-check-input step-a" type="checkbox">`);
|
||
}
|
||
rows.push(`<tr>${r===0?'<th>번호</th>': r===1?'<th>입력</th>': r===2?'<th>연신율(%)</th>': r===3?'<th>연신 후</th>': r===4?'<th>합계</th>': r===5?'<th>음영</th>':'<th>A각 표시</th>'}
|
||
${cells.map(td=>`<td>${td}</td>`).join('')}
|
||
</tr>`);
|
||
}
|
||
$('#stepBody').html(rows.join(''));
|
||
recalc();
|
||
}
|
||
function recalc(){
|
||
let running = 0;
|
||
$('#stepBody tr').each(function(idx,tr){
|
||
if (idx<3){ /* 입력·연신율 줄 계산은 아래에서 */ }
|
||
if (idx===3 || idx===4){ // 연신후 / 합계 재계산
|
||
const row = $(tr);
|
||
row.find('td').each(function(col){
|
||
const $in = $('#stepBody .step-in').eq(col);
|
||
const $st = $('#stepBody .step-strain').eq(col);
|
||
const after = (+$in.val()||0) * (1 + (+$st.val()||0)/100);
|
||
if (idx===3){
|
||
row.find('.step-after').eq(col).val(after.toFixed(2));
|
||
}
|
||
});
|
||
}
|
||
});
|
||
// 합계(누적)
|
||
const afters = $('#stepBody .step-after');
|
||
const sums = $('#stepBody .step-sum');
|
||
let acc=0;
|
||
afters.each(function(i){
|
||
acc += +($(this).val()||0);
|
||
sums.eq(i).val(acc.toFixed(2));
|
||
});
|
||
}
|
||
$('#stepBody').on('input', '.step-in, .step-strain', recalc);
|
||
|
||
$('#btnAddCol').on('click', ()=>{ COLS++; drawTable(); });
|
||
$('#btnDelCol').on('click', ()=>{ COLS=Math.max(1,COLS-1); drawTable(); });
|
||
$('#btnClear').on('click', ()=>{
|
||
$('#stepBody input[type=number]').val(0);
|
||
$('#stepBody input[type=checkbox]').prop('checked',false);
|
||
recalc();
|
||
});
|
||
|
||
// ===== 캔버스 (아주 간단한 드로잉) =====
|
||
const cv = document.getElementById('cv');
|
||
const ctx = cv.getContext('2d');
|
||
let bgImg = null;
|
||
function clearCanvas(){
|
||
ctx.clearRect(0,0,cv.width,cv.height);
|
||
if (bgImg){ ctx.drawImage(bgImg, 0,0, cv.width, cv.height); }
|
||
}
|
||
$('#bgImage').on('change', function(){
|
||
const f=this.files[0]; if(!f) return;
|
||
const img=new Image();
|
||
img.onload=function(){ bgImg=img; clearCanvas(); };
|
||
img.src=URL.createObjectURL(f);
|
||
});
|
||
|
||
let mode='line', drawing=false, sx=0, sy=0;
|
||
$('#btnDrawLine').on('click', ()=> mode='line');
|
||
$('#btnDrawRect').on('click', ()=> mode='rect');
|
||
$('#btnClearCanvas').on('click', ()=>{ bgImg=null; clearCanvas(); });
|
||
|
||
cv.addEventListener('mousedown', e=>{ drawing=true; const r=cv.getBoundingClientRect(); sx=e.clientX-r.left; sy=e.clientY-r.top; });
|
||
cv.addEventListener('mouseup', e=>{
|
||
if(!drawing) return; drawing=false;
|
||
const r=cv.getBoundingClientRect(); const x=e.clientX-r.left, y=e.clientY-r.top;
|
||
ctx.strokeStyle='#333'; ctx.lineWidth=2; clearCanvas();
|
||
if(mode==='line'){ ctx.beginPath(); ctx.moveTo(sx,sy); ctx.lineTo(x,y); ctx.stroke(); }
|
||
else{ const w=x-sx, h=y-sy; ctx.strokeRect(sx,sy,w,h); }
|
||
});
|
||
|
||
// ===== 미리보기/저장 =====
|
||
function collect(){
|
||
// 메타
|
||
const metas=[]; $('#metaRows .meta-row').each(function(){
|
||
metas.push({
|
||
key: $(this).find('.meta-key').val().trim(),
|
||
value: $(this).find('.meta-val').val().trim(),
|
||
unit: $(this).find('.meta-unit').val().trim(),
|
||
});
|
||
});
|
||
// 스텝
|
||
const no = $('#stepBody .step-no').map((i,e)=>+e.value||0).get();
|
||
const inp = $('#stepBody .step-in').map((i,e)=>+e.value||0).get();
|
||
const str = $('#stepBody .step-strain').map((i,e)=>+e.value||0).get();
|
||
const aft = $('#stepBody .step-after').map((i,e)=>+e.value||0).get();
|
||
const sum = $('#stepBody .step-sum').map((i,e)=>+e.value||0).get();
|
||
const dark = $('#stepBody .step-dark').map((i,e)=>e.checked).get();
|
||
const a = $('#stepBody .step-a').map((i,e)=>e.checked).get();
|
||
const steps = no.map((_,i)=>({ no:no[i], length:inp[i], strain:str[i], after:aft[i], acc:sum[i], shade:dark[i], a_mark:a[i] }));
|
||
|
||
// 캔버스 스냅샷
|
||
const snapshot = cv.toDataURL('image/png');
|
||
|
||
return {
|
||
base:{ category:$('#bfCategory').val(), name:$('#bfName').val(), unit:$('#bfUnit').val(), tags:$('#bfTags').val() },
|
||
fixed:{ width:+$('#fxWidth').val()||0, w:+$('#fxW').val()||0, h:+$('#fxH').val()||0, material:$('#fxMaterial').val(), rail:+$('#fxRail').val()||0 },
|
||
metas, steps, cols: COLS, snapshot
|
||
};
|
||
}
|
||
|
||
$('#btnPreview').on('click', ()=>{
|
||
const txt = JSON.stringify(collect(), null, 2);
|
||
$('#preview').val(txt).show().focus();
|
||
});
|
||
|
||
$('#bendForm').on('submit', function(e){
|
||
e.preventDefault();
|
||
const payload = collect();
|
||
console.log('[BENDING SAVE]', payload);
|
||
alert('절곡 저장(모의). 콘솔을 확인하세요.');
|
||
// 실제는 $.post('/tenant/api/product/save_bending.php', payload)
|
||
});
|
||
|
||
// 초기
|
||
$('#metaRows').append(metaRowTpl());
|
||
drawTable();
|
||
clearCanvas();
|
||
})(jQuery);
|
||
</script>
|
||
|
||
<?php include '../inc/footer.php'; ?>
|