288 lines
11 KiB
JavaScript
288 lines
11 KiB
JavaScript
import EditableInput from './EditableInput';
|
|
|
|
class LayoutGridModel {
|
|
constructor(items) {
|
|
this.items = items;
|
|
}
|
|
}
|
|
|
|
class LayoutGridView {
|
|
|
|
constructor(model, controller) {
|
|
this.model = model;
|
|
this.controller = controller;
|
|
|
|
}
|
|
|
|
render() {
|
|
this.renderApiLayout();
|
|
}
|
|
|
|
renderApiLayout() {
|
|
const requestContainer = this.targetContent.find('.request_layout');
|
|
requestContainer.empty();
|
|
|
|
const headerHtml = `
|
|
<table class="layout_table" style="border-collapse: collapse; border: 1px gray solid;">
|
|
<colgroup>
|
|
<col style="width: 50px;">
|
|
<col style="width: 80px;">
|
|
<col style="width: 80px;">
|
|
<col style="width: 120px;">
|
|
<col style="width: 150px;">
|
|
<col style="width: 60px;">
|
|
<col style="width: 110px;">
|
|
<col style="width: 80px;">
|
|
<col style="width: 100px;">
|
|
<col style="width: 80px;">
|
|
<col style="width: 120px;">
|
|
<col style="width: 120px;">
|
|
</colgroup>
|
|
<thead>
|
|
<tr>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 80px;">#</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 40px;">ID</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 40px;">Parent</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 120px;">항목명(영문)</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 150px;">항목설명</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 60px;">깊이</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 110px;">아이템 유형</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 80px;">반복 횟수 </th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 100px;">반복 참조 필드</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 80px;">데이터 길이</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 120px;">값</th>
|
|
<th scope="col" style="border: 1px gray solid; text-align: center;width: 120px;">비고 <button type="button" class="btn btn-sm btn-outline-secondary add-layout-item"><i class="fas fa-plus"></i></button></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
`;
|
|
|
|
const footerHtml = `
|
|
</tbody>
|
|
</table>
|
|
`;
|
|
|
|
const fieldsHtml = this.model.items ? this.model.items.map((item, index) => {
|
|
return this.renderApiLayoutItem(item, index);
|
|
}).join('') : '';
|
|
|
|
requestContainer.append(headerHtml + fieldsHtml + footerHtml);
|
|
|
|
requestContainer.find('.editable').each((index, editableElement) => {
|
|
const editableInput = new EditableInput({
|
|
name: editableElement.dataset.name,
|
|
value: editableElement.dataset.value,
|
|
style : {width : '120px'}
|
|
});
|
|
|
|
editableInput.init(editableElement, async (value) => {
|
|
_.set(this.model, editableElement.dataset.name, value);
|
|
await this.controller.additionalCallback(this.model);
|
|
});
|
|
});
|
|
|
|
let totalLength = 0;
|
|
this.model.items.forEach(item => {
|
|
totalLength += parseInt(item.loutItemLength);
|
|
});
|
|
|
|
|
|
this.setupListeners();
|
|
}
|
|
|
|
getFieldValue(element) {
|
|
if ($(element).is('select') || $(element).is('input[type="text"]') || $(element).is('input[type="number"]')) {
|
|
return $(element).val();
|
|
}
|
|
if ($(element).is('input[type="checkbox"]')) {
|
|
return $(element).is(':checked');
|
|
}
|
|
}
|
|
|
|
recalculateSerno() {
|
|
// Reassign loutItemSerno sequentially based on the current order
|
|
this.model.items.forEach((item, index) => {
|
|
item.loutItemSerno = index + 1; // Assuming you want to start numbering from 1
|
|
});
|
|
}
|
|
|
|
async handleUIUpdate(event) {
|
|
if (event) {
|
|
event.preventDefault();
|
|
const element = event.currentTarget;
|
|
const fieldName = $(element).attr('name');
|
|
const newValue = this.getFieldValue(element);
|
|
if (fieldName !== '') {
|
|
_.set(this.model, fieldName, newValue);
|
|
await this.controller.additionalCallback(this.model);
|
|
}
|
|
}
|
|
let totalLength = 0;
|
|
this.model.items.forEach(item => {
|
|
totalLength += parseInt(item.loutItemLength);
|
|
});
|
|
}
|
|
|
|
getLastId (){
|
|
let lastId = 0;
|
|
this.model.items.forEach(item => {
|
|
if (item.id > lastId) {
|
|
lastId = item.id;
|
|
}
|
|
});
|
|
return lastId + 1;
|
|
}
|
|
|
|
setupListeners() {
|
|
const table = this.targetContent.find('.layout_table');
|
|
table.on('change', 'select.field, input.field', this.handleUIUpdate.bind(this));
|
|
|
|
table.on('click', '.add-layout-item', async(event) => {
|
|
let item = {
|
|
'id' : this.getLastId(),
|
|
'parent': 0,
|
|
'level': 1,
|
|
'loutName': '',
|
|
'loutItemName': '',
|
|
'loutItemDesc': '',
|
|
'loutItemDepth': 1,
|
|
'loutItemType': 'Field',
|
|
'loutItemOccCnt': '',
|
|
'loutItemOccRef': '',
|
|
'loutItemDataType': 'String',
|
|
'loutItemLength': 1,
|
|
'loutItemDecimal': 0,
|
|
'loutItemDefault': '',
|
|
'loutItemMaskYn': 'N',
|
|
'loutItemMaskOffset': 0,
|
|
'loutItemMaskLength': 0,
|
|
'parentLoutItemIndex': 0,
|
|
'expanded': true,
|
|
'isLeaf': true,
|
|
'LOUTITEMPATH': '',
|
|
'value': '',
|
|
'loutItemSerno': 0
|
|
};
|
|
this.model.items.push(item);
|
|
this.recalculateSerno();
|
|
this.renderApiLayout();
|
|
await this.controller.additionalCallback(this.model);
|
|
});
|
|
|
|
table.on('click', '.move-up', async (event) => {
|
|
const index = $(event.currentTarget).data('index');
|
|
if (index > 0) {
|
|
const temp = this.model.items[index];
|
|
this.model.items[index] = this.model.items[index - 1];
|
|
this.model.items[index - 1] = temp;
|
|
this.recalculateSerno();
|
|
this.renderApiLayout();
|
|
await this.controller.additionalCallback(this.model);
|
|
}
|
|
});
|
|
|
|
table.on('click', '.move-down', async (event) => {
|
|
const index = $(event.currentTarget).data('index');
|
|
if (index < this.model.items.length - 1) {
|
|
const temp = this.model.items[index];
|
|
this.model.items[index] = this.model.items[index + 1];
|
|
this.model.items[index + 1] = temp;
|
|
this.recalculateSerno();
|
|
this.renderApiLayout();
|
|
await this.controller.additionalCallback(this.model);
|
|
}
|
|
});
|
|
|
|
table.on('click', '.delete', async (event) => {
|
|
const index = $(event.currentTarget).data('index');
|
|
this.model.items.splice(index, 1);
|
|
this.recalculateSerno();
|
|
this.renderApiLayout();
|
|
await this.controller.additionalCallback(this.model);
|
|
});
|
|
}
|
|
|
|
renderApiLayoutItem(item, index) {
|
|
if (item.value === null) {
|
|
item.value = '';
|
|
}
|
|
|
|
return `
|
|
<tr class="layout-grid-row">
|
|
<td class="layout-grid-cell" style="width: 80px; text-align: center;">
|
|
<span style="width: 80px; text-align: center;">${item.loutItemSerno}</span>
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="text" class="form-control" name="items[${index}].id" value="${item.id}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="text" class="form-control" name="items[${index}].parent" value="${item.parent}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="text" class="form-control" name="items[${index}].loutItemName" value="${item.loutItemName}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="text" class="form-control" name="items[${index}].loutItemDesc" value="${item.loutItemDesc}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="number" class="form-control" style="width: 60px;" name="items[${index}].loutItemDepth" min="1" value="${item.loutItemDepth}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<select name="items[${index}].loutItemType" class="form-select">
|
|
<option value="Field" ${item.loutItemType === 'Field' ? 'selected' : ''}>Field</option>
|
|
<option value="Grid" ${item.loutItemType === 'Grid' ? 'selected' : ''}>Grid</option>
|
|
<option value="Group" ${item.loutItemType === 'Group' ? 'selected' : ''}>Group</option>
|
|
<option value="Attr" ${item.loutItemType === 'Attr' ? 'selected' : ''}>Attr</option>
|
|
</select>
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="text" class="form-control" style="width: 80px;" name="items[${index}].loutItemOccCnt" min="0" value="${item.loutItemOccCnt}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
<input type="text" class="form-control" name="items[${index}].loutItemOccRef" value="${item.loutItemOccRef}"></td>
|
|
<td class="layout-grid-cell">
|
|
<input type="number" class="form-control" style="width: 80px;" name="items[${index}].loutItemLength" min="1" value="${item.loutItemLength}">
|
|
</td>
|
|
<td class="layout-grid-cell">
|
|
${ item.loutItemType ==='Field'? `<div class="editable" data-name="items[${index}].value" data-value="${item.value}"></div>`:''}
|
|
</td>
|
|
<td class="layout-grid-cell" style="width: 120px; text-align: center;">
|
|
<div style="width: 120px !important;">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary move-up" data-index="${index}"><i class="fas fa-arrow-up"></i></button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary move-down" data-index="${index}"><i class="fas fa-arrow-down"></i></button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary delete" data-index="${index}"><i class="fas fa-trash-alt"></i></button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
|
|
}
|
|
|
|
class LayoutGridController {
|
|
constructor(model) {
|
|
this.model = model;
|
|
this.view = new LayoutGridView(this.model, this);
|
|
}
|
|
|
|
init(targetContent, callback) {
|
|
this.additionalCallback = callback;
|
|
this.view.targetContent = targetContent;
|
|
this.view.render();
|
|
}
|
|
|
|
}
|
|
|
|
class LayoutGrid {
|
|
constructor(layout) {
|
|
this.model = new LayoutGridModel(layout.layoutItems);
|
|
this.controller = new LayoutGridController(this.model);
|
|
}
|
|
|
|
init(targetContent, callback) {
|
|
this.controller.init(targetContent, callback);
|
|
}
|
|
}
|
|
|
|
export default LayoutGrid;
|