- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
417 lines
12 KiB
Markdown
417 lines
12 KiB
Markdown
# common_addrowJS.php 개발자 가이드
|
|
|
|
## 📋 개요
|
|
|
|
`common_addrowJS.php`는 방화셔터 견적 시스템의 핵심 JavaScript 컴포넌트로, 견적 테이블에 새로운 행을 동적으로 추가하고 관리하는 기능을 제공합니다. 이 파일은 PHP와 JavaScript가 혼합된 형태로 구성되어 있으며, 견적 항목의 복잡한 입력 폼을 자동으로 생성합니다.
|
|
|
|
## 🏗️ 파일 구조
|
|
|
|
### 📁 파일 위치
|
|
```
|
|
/estimate/common/common_addrowJS.php
|
|
```
|
|
|
|
### 📊 파일 정보
|
|
- **파일 크기**: 113KB (2536 lines)
|
|
- **주요 언어**: PHP + JavaScript + jQuery
|
|
- **의존성**: jQuery, Bootstrap, models.json
|
|
|
|
## 🔧 핵심 기능
|
|
|
|
### 1. 모델 데이터 로드
|
|
```php
|
|
// models.json에서 스크린/철재 모델 정보 로드
|
|
$jsonFile = $_SERVER['DOCUMENT_ROOT'] . '/models/models.json';
|
|
$models = json_decode(file_get_contents($jsonFile), true);
|
|
|
|
// 스크린과 철재 모델 분리
|
|
$screenModels = array_filter($models, function ($m) {
|
|
return $m['slatitem'] === '스크린';
|
|
});
|
|
$steelModels = array_filter($models, function ($m) {
|
|
return $m['slatitem'] === '철재';
|
|
});
|
|
```
|
|
|
|
### 2. 메인 함수: `addRow()`
|
|
```javascript
|
|
function addRow(tableBody, rowData, typebutton, afterRow = null, autoData = {})
|
|
```
|
|
|
|
#### 매개변수
|
|
- `tableBody`: 대상 테이블의 tbody 요소
|
|
- `rowData`: 행에 설정할 초기 데이터 객체
|
|
- `typebutton`: 버튼 타입 (사용되지 않음)
|
|
- `afterRow`: 삽입할 기준 행 (선택사항)
|
|
- `autoData`: 자동 계산 데이터 (선택사항)
|
|
|
|
## 📊 컬럼 구조 (71개 컬럼)
|
|
|
|
### 🎯 컬럼별 특성
|
|
|
|
#### **기본 컬럼 (1-3)**
|
|
- **col1**: 행 번호 (자동 생성, readonly)
|
|
- **col2**: 기본 입력 필드
|
|
- **col3**: 기본 입력 필드
|
|
|
|
#### **모델 관련 컬럼 (4-7)**
|
|
- **col4**: 스크린 모델 선택 (드롭다운)
|
|
- `screenModelOptions`에서 동적 로드
|
|
- 변경 시 `modelChange_screen()` 함수 호출
|
|
- **col5**: readonly 필드 (자동 계산)
|
|
- **col6**: 셔터박스 타입 선택
|
|
- 벽면형(120*70)
|
|
- 측면형(120*120)
|
|
- 혼합형(120*70)(120*120)
|
|
- **col7**: 마감 타입 선택
|
|
- SUS마감
|
|
- EGI마감
|
|
|
|
#### **제작 사이즈 컬럼 (10-11)**
|
|
- **col10**: 제작 사이즈 (가로)
|
|
- **col11**: 제작 사이즈 (세로)
|
|
- **특별 처리**: col10은 3개의 필드로 분할
|
|
- `col10_SW`: 가로 사이즈
|
|
- `col11_SH`: 세로 사이즈
|
|
- `col10`: 통합 사이즈
|
|
|
|
#### **수량 컬럼 (14)**
|
|
- **col14**: 수량 입력 + 자동산출 버튼
|
|
```javascript
|
|
'<button type="button" class="btn btn-sm btn-outline-primary ms-1 calc-btn viewNoBtn" title="자동산출"><i class="bi bi-calculator-fill"></i></button>'
|
|
```
|
|
|
|
#### **모터 관련 컬럼 (18-19)**
|
|
- **col18**: 모터 브랜드 + 전압 + 유선/무선
|
|
- 브랜드: 경동(견적가포함), 대한, 기타
|
|
- 전압: 220V, 380V
|
|
- 연결: 유선, 무선
|
|
- **col19**: 모터 용량 선택
|
|
- 브라켓 사이즈별 용량 매핑
|
|
- 380*180: 150K, 300K, 400K
|
|
- 530*320: 300K, 400K
|
|
- 600*350: 500K, 600K
|
|
- 690*390: 800K, 1000K
|
|
|
|
#### **셔터박스 컬럼 (36)**
|
|
- **col36**: 복합 입력 필드
|
|
- 기본 옵션: 500*380, 500*350
|
|
- 직접 입력 옵션
|
|
- 전면밑, 레일폭, 박스 방향 설정
|
|
```javascript
|
|
let selectHtml = '<select name="col36[]">...</select>';
|
|
let inputHtml = '<input type="text" name="col36_custom[]">';
|
|
let inputHtmlfrontbottom = '<input type="text" name="col36_frontbottom[]">';
|
|
let inputHtmlrailwidth = '<input type="text" name="col36_railwidth[]">';
|
|
let selectHtmlboxdirection = '<select name="col36_boxdirection[]">...</select>';
|
|
```
|
|
|
|
#### **마구리 윙 컬럼 (45)**
|
|
- **col45**: 기본 입력 필드
|
|
- **col45_wing**: 마구리 윙 길이
|
|
- 기본값: `$('#maguriWing').val()` 또는 빈 값
|
|
|
|
#### **샤프트 관련 컬럼 (59)**
|
|
- **col59**: 복합 입력 필드
|
|
- 인치 선택: 2인치, 3인치
|
|
- 길이 입력
|
|
- 수량 입력
|
|
|
|
#### **Readonly 컬럼들**
|
|
- **col5, 12, 13, 23, 37, 48, 51, 54, 58, 67**: 자동 계산 필드
|
|
|
|
## 🎨 UI/UX 특징
|
|
|
|
### 📱 반응형 디자인
|
|
- Bootstrap 클래스 활용
|
|
- 모바일 최적화
|
|
- 테이블 반응형 처리
|
|
|
|
### 🎯 컬럼 너비 최적화
|
|
```javascript
|
|
let width = [2, 3, 4, 5, 7, 20, 22, 23, 29, 30, 36, 45].includes(i) ? '85px' : '50px';
|
|
width = [6].includes(i) ? '170px' : width;
|
|
width = [18].includes(i) ? '60px' : width;
|
|
width = [23,32,43,46,49,53,62].includes(i) ? '50px' : width;
|
|
```
|
|
|
|
### 🔘 버튼 시스템
|
|
- **추가 버튼**: `+` (새 행 추가)
|
|
- **삭제 버튼**: `-` (행 삭제)
|
|
- **복사 버튼**: 📋 (행 복사)
|
|
- **계산 버튼**: 🧮 (자동 산출)
|
|
|
|
## ⚡ 이벤트 핸들링
|
|
|
|
### 🔄 모델 변경 이벤트
|
|
```javascript
|
|
newRow.find('.col4-select').on('input change', function() {
|
|
var row = $(this).closest('tr');
|
|
modelChange_screen(row);
|
|
});
|
|
```
|
|
|
|
### 🔌 모터 브랜드 변경 이벤트
|
|
```javascript
|
|
newRow.find('.col18_brand').on('change', function() {
|
|
var row = $(this).closest('tr');
|
|
const qty = row.find('.col14-input').val();
|
|
const col18_brandValue = row.find('.col18_brand').val();
|
|
// 모터 브랜드별 처리 로직
|
|
});
|
|
```
|
|
|
|
### 🎛️ 셔터박스 커스텀 입력 이벤트
|
|
```javascript
|
|
newRow.find('.col36-select').on('change', function() {
|
|
var selectedValue = $(this).val();
|
|
var customInput = $(this).closest('td').find('.col36-custom-input');
|
|
|
|
if (selectedValue === 'custom') {
|
|
customInput.show();
|
|
customInput.focus();
|
|
} else {
|
|
customInput.hide();
|
|
customInput.val('');
|
|
}
|
|
});
|
|
```
|
|
|
|
## 🧮 자동 계산 시스템
|
|
|
|
### 📏 철재 스라트 계산 함수들
|
|
|
|
#### **Slat_updateCol59()**: 샤프트 수량 계산
|
|
```javascript
|
|
function Slat_updateCol59(row) {
|
|
var col10Value = parseFloat(row.find('.col10-input').val()) || 0; // 셔터 길이
|
|
var col13Value = parseFloat(row.find('.col13-input').val()) || 0; // 중량
|
|
var col59Input = row.find('.col59-input');
|
|
|
|
// 길이와 중량에 따른 샤프트 수량 결정
|
|
if (col10Value <= 4500) {
|
|
if (col13Value <= 400) {
|
|
col59Input.val(4);
|
|
} else if (col13Value > 400) {
|
|
col59Input.val(5);
|
|
}
|
|
}
|
|
// ... 추가 조건들
|
|
}
|
|
```
|
|
|
|
#### **Slat_updateCol60to71()**: 샤프트 길이별 수량 계산
|
|
```javascript
|
|
function Slat_updateCol60to71(row) {
|
|
var col15Value = parseFloat(row.find('.col15-input').val()) || 0; // 셔터수량
|
|
var col59Value = parseFloat(row.find('.col22-select').val()) || 0; // 브라켓인치
|
|
var col10Value = parseFloat(row.find('.col10-input').val()) || 0; // 제작사이즈 가로
|
|
|
|
// 브라켓 인치별 샤프트 길이 분류
|
|
row.find('.col61-input').val((col59Value === 4 && col10Value <= 3050) ? col15Value : 0);
|
|
row.find('.col62-input').val((col59Value === 4 && col10Value > 3050 && col10Value <= 4550) ? col15Value : 0);
|
|
// ... 추가 조건들
|
|
}
|
|
```
|
|
|
|
#### **Slat_updateCol72()**: 셔터박스 수량 계산
|
|
```javascript
|
|
function Slat_updateCol72(row) {
|
|
var col38Value = parseFloat(row.find('.col38-input').val()) || 0; // 셔터박스 사이즈
|
|
var col72Input = row.find('.col72-input');
|
|
|
|
// 사이즈별 박스 수량 결정
|
|
col72Input.val((col38Value <= 1600) ? 3 :
|
|
(col38Value <= 2800) ? 4 :
|
|
(col38Value <= 4000) ? 5 :
|
|
// ... 추가 조건들
|
|
0);
|
|
}
|
|
```
|
|
|
|
#### **Slat_updateCol73()**: 셔터박스 길이 계산
|
|
```javascript
|
|
function Slat_updateCol73(row) {
|
|
var col38Value = parseFloat(row.find('.col38-input').val()) || 0; // 셔터박스 사이즈
|
|
var col72Value = parseFloat(row.find('.col72-input').val()) || 0;
|
|
|
|
row.find('.col73-input').val(col38Value + 3000 * col72Value);
|
|
}
|
|
```
|
|
|
|
#### **Slat_updateCol74to75()**: 앵글 수량 계산
|
|
```javascript
|
|
function Slat_updateCol74to75(row) {
|
|
var col15Value = parseFloat(row.find('.col15-input').val()) || 0; // 셔터수량
|
|
var col73Value = parseFloat(row.find('.col73-input').val()) || 0;
|
|
|
|
// 길이별 앵글 수량 계산
|
|
var col74Input = row.find('.col74-input');
|
|
col74Input.val((col73Value <= 9000) ? 3 * col15Value:
|
|
(col73Value > 9000 && col73Value <= 12000) ? 4 * col15Value:
|
|
// ... 추가 조건들
|
|
0);
|
|
}
|
|
```
|
|
|
|
#### **Slat_updateCo76()**: 조인트바 수량 계산
|
|
```javascript
|
|
function Slat_updateCo76(row) {
|
|
var col15Value = parseFloat(row.find('.col15-input').val()) || 0; // 셔터수량
|
|
var col10Value = parseFloat(row.find('.col10-input').val()) || 0; // 제작사이즈 (가로)
|
|
var col76Input = row.find('.col76-input');
|
|
|
|
var calculatedValue = 2 + Math.floor((col10Value - 500) / 1000);
|
|
col76Input.val(calculatedValue * col15Value);
|
|
}
|
|
```
|
|
|
|
#### **Slat_updateTotals()**: 전체 합계 계산
|
|
```javascript
|
|
function Slat_updateTotals() {
|
|
var sumColumns = [16, 17, 18, 19, 20, 21, 22, 31, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 49, 50, 51, 54, 55, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84];
|
|
|
|
sumColumns.forEach(function (colIndex) {
|
|
var total = 0;
|
|
|
|
$('#estimateSlatTable tbody tr').each(function () {
|
|
var $cell = $(this).find('td').eq(colIndex - 1);
|
|
var val = '';
|
|
|
|
if ($cell.find('input').length > 0) {
|
|
val = $cell.find('input').val() || '';
|
|
} else if ($cell.find('select').length > 0) {
|
|
val = $cell.find('select').val() || '';
|
|
}
|
|
|
|
var numericValue = parseFloat(val.toString().replace(/,/g, '')) || 0;
|
|
total += numericValue;
|
|
});
|
|
|
|
var formattedTotal = total === 0 ? '' : total.toLocaleString();
|
|
$('#estimateSlattotalCol' + colIndex).text(formattedTotal);
|
|
});
|
|
}
|
|
```
|
|
|
|
## 🔧 개발자 사용법
|
|
|
|
### 📝 기본 사용법
|
|
```javascript
|
|
// 새로운 행 추가
|
|
addRow($('#estimateTable tbody'), {}, 'add');
|
|
|
|
// 특정 데이터로 행 추가
|
|
addRow($('#estimateTable tbody'), {
|
|
col4: '스크린모델명',
|
|
col14: '10',
|
|
col10: '3000'
|
|
}, 'add');
|
|
|
|
// 특정 행 다음에 삽입
|
|
addRow($('#estimateTable tbody'), {}, 'add', $('#someRow'));
|
|
```
|
|
|
|
### 🎯 커스텀 데이터 처리
|
|
```javascript
|
|
// 자동 계산 데이터 포함
|
|
addRow($('#estimateTable tbody'), rowData, 'add', null, {
|
|
calculated: true,
|
|
timestamp: new Date().getTime()
|
|
});
|
|
```
|
|
|
|
### 🔄 이벤트 바인딩
|
|
```javascript
|
|
// 새로 추가된 행에 이벤트 바인딩
|
|
$('#estimateTable').on('change', '.col4-select', function() {
|
|
var row = $(this).closest('tr');
|
|
modelChange_screen(row);
|
|
});
|
|
```
|
|
|
|
## 🚨 주의사항
|
|
|
|
### ⚠️ 필수 의존성
|
|
- jQuery 3.x 이상
|
|
- Bootstrap 5.x
|
|
- models.json 파일 존재 필요
|
|
|
|
### 🔒 보안 고려사항
|
|
- XSS 방지를 위한 입력값 검증 필요
|
|
- SQL 인젝션 방지를 위한 서버 사이드 검증 필요
|
|
|
|
### 📱 성능 최적화
|
|
- 대량의 행 추가 시 성능 저하 가능
|
|
- 이벤트 위임 사용 권장
|
|
- 메모리 누수 방지를 위한 이벤트 정리 필요
|
|
|
|
## 🐛 디버깅 가이드
|
|
|
|
### 🔍 콘솔 로그 확인
|
|
```javascript
|
|
// 조인트바 계산 디버깅
|
|
console.log('조인트바 계산 col10Value : ', col10Value);
|
|
console.log('조인트바 계산 col15Value : ', col15Value);
|
|
console.log('조인트바 계산 col76 : ', col76Input.val());
|
|
```
|
|
|
|
### 🛠️ 일반적인 문제 해결
|
|
|
|
#### 1. 모델 옵션이 로드되지 않는 경우
|
|
```javascript
|
|
// models.json 파일 경로 확인
|
|
console.log('Models loaded:', screenModelOptions, steelModelOptions);
|
|
```
|
|
|
|
#### 2. 이벤트가 작동하지 않는 경우
|
|
```javascript
|
|
// 이벤트 바인딩 확인
|
|
$(document).on('change', '.col4-select', function() {
|
|
console.log('Model changed:', $(this).val());
|
|
});
|
|
```
|
|
|
|
#### 3. 계산이 올바르지 않은 경우
|
|
```javascript
|
|
// 입력값 확인
|
|
console.log('Input values:', {
|
|
col10: $('.col10-input').val(),
|
|
col13: $('.col13-input').val(),
|
|
col15: $('.col15-input').val()
|
|
});
|
|
```
|
|
|
|
## 📚 관련 파일
|
|
|
|
### 🔗 의존성 파일
|
|
- `/models/models.json`: 모델 데이터
|
|
- `jQuery`: DOM 조작
|
|
- `Bootstrap`: UI 프레임워크
|
|
|
|
### 🔗 연관 함수
|
|
- `modelChange_screen()`: 모델 변경 처리
|
|
- `calculateAmount()`: 금액 계산
|
|
- `updateTotals()`: 합계 업데이트
|
|
|
|
## 🎯 향후 개선 방향
|
|
|
|
### 🔄 코드 리팩토링
|
|
- 함수 분리 및 모듈화
|
|
- TypeScript 도입 고려
|
|
- ES6+ 문법 활용
|
|
|
|
### 🎨 UI/UX 개선
|
|
- 드래그 앤 드롭 행 재정렬
|
|
- 키보드 단축키 지원
|
|
- 실시간 검증 메시지
|
|
|
|
### ⚡ 성능 최적화
|
|
- 가상 스크롤링 도입
|
|
- 지연 로딩 구현
|
|
- 메모리 사용량 최적화
|
|
|
|
---
|
|
|
|
**📅 문서 버전**: 1.0
|
|
**👨💻 작성자**: 전산실장 김보곤 |