그리드 컨트롤 수정
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package com.madeu.crm.settings.medicalcategory.ctrl;
|
package com.madeu.crm.settings.medicalcategory.ctrl;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -182,4 +184,29 @@ public class MedicalCategoryController extends ManagerDraftAction {
|
|||||||
log.debug("MedicalCategoryController delMedicalCategory END");
|
log.debug("MedicalCategoryController delMedicalCategory END");
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 진료유형 다건 삭제
|
||||||
|
*/
|
||||||
|
@PostMapping("/delMultiMedicalCategory.do")
|
||||||
|
public HashMap<String, Object> delMultiMedicalCategory(@RequestBody Map<String, List<String>> body) {
|
||||||
|
log.debug("MedicalCategoryController delMultiMedicalCategory START");
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> pidList = body.get("pidList");
|
||||||
|
if (pidList == null || pidList.isEmpty()) {
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "삭제할 항목이 선택되지 않았습니다.");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
map = medicalCategoryService.deleteMultiMedicalCategory(pidList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("delMultiMedicalCategory : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController delMultiMedicalCategory END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,4 +212,74 @@ public class MedicalCategoryService {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 카테고리 다건 삭제 (논리 삭제 처리)
|
||||||
|
* 하위 항목이 있는 카테고리는 건너뛰고, 나머지만 삭제 처리
|
||||||
|
*
|
||||||
|
* @param pidList 삭제 대상 pid 목록
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> deleteMultiMedicalCategory(List<String> pidList) throws Exception {
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
int successCount = 0;
|
||||||
|
int skipCount = 0;
|
||||||
|
List<String> skippedNames = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (String pidStr : pidList) {
|
||||||
|
HashMap<String, Object> paramMap = new HashMap<>();
|
||||||
|
paramMap.put("pid", pidStr);
|
||||||
|
|
||||||
|
// 하위 카테고리 존재 여부 확인
|
||||||
|
int childCount = medicalCategoryMapper.getChildCategoryCount(paramMap);
|
||||||
|
if (childCount > 0) {
|
||||||
|
// 하위 항목이 있으면 건너뜀
|
||||||
|
Map<String, Object> info = medicalCategoryMapper.getMedicalCategory(paramMap);
|
||||||
|
if (info != null && info.get("divi_name") != null) {
|
||||||
|
skippedNames.add(String.valueOf(info.get("divi_name")));
|
||||||
|
}
|
||||||
|
skipCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 삭제 처리
|
||||||
|
int result = medicalCategoryMapper.deleteMedicalCategory(paramMap);
|
||||||
|
if (result > 0) {
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successCount > 0) {
|
||||||
|
map.put("msgCode", Constants.OK);
|
||||||
|
map.put("success", true);
|
||||||
|
} else {
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 결과 메시지 구성
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
if (successCount > 0) {
|
||||||
|
msg.append(successCount + "건 삭제되었습니다.");
|
||||||
|
}
|
||||||
|
if (skipCount > 0) {
|
||||||
|
if (msg.length() > 0)
|
||||||
|
msg.append("\n");
|
||||||
|
msg.append(skipCount + "건은 하위 항목이 존재하여 건너뛰었습니다.");
|
||||||
|
if (!skippedNames.isEmpty()) {
|
||||||
|
msg.append("\n(" + String.join(", ", skippedNames) + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (successCount == 0 && skipCount == 0) {
|
||||||
|
msg.append("삭제할 항목이 없습니다.");
|
||||||
|
}
|
||||||
|
map.put("msgDesc", msg.toString());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("deleteMultiMedicalCategory Error: ", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,47 +30,77 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 메뉴 [삭제] 클릭 이벤트
|
// 메뉴 [삭제] 클릭 이벤트 - 멀티셀렉트 지원
|
||||||
$(document).on('click', '#customContextMenu .delete', function () {
|
$(document).on('click', '#customContextMenu .delete', function () {
|
||||||
var menu = $('#customContextMenu');
|
var menu = $('#customContextMenu');
|
||||||
var pid = menu.data('pid');
|
|
||||||
var name = menu.data('name');
|
|
||||||
menu.hide();
|
menu.hide();
|
||||||
|
|
||||||
if (!pid) return;
|
if (!window.currentContextRow) return;
|
||||||
if (!confirm("'" + name + "' 항목을 삭제하시겠습니까?\n하위 항목이 있을 경우 삭제되지 않습니다.")) return;
|
|
||||||
|
|
||||||
|
var contextTable = window.currentContextRow.getTable();
|
||||||
|
var contextData = window.currentContextRow.getData();
|
||||||
|
var selectedRows = contextTable.getSelectedRows();
|
||||||
|
|
||||||
|
// 우클릭한 행이 선택된 행 목록에 포함되어 있는지 확인
|
||||||
|
var isContextRowSelected = selectedRows.some(function (r) {
|
||||||
|
return r.getData().pid === contextData.pid;
|
||||||
|
});
|
||||||
|
|
||||||
|
var targetRows;
|
||||||
|
if (isContextRowSelected && selectedRows.length > 1) {
|
||||||
|
// 멀티 선택 상태에서 선택된 행 중 하나를 우클릭한 경우 → 전체 선택 삭제
|
||||||
|
targetRows = selectedRows;
|
||||||
|
} else {
|
||||||
|
// 단일 행 또는 선택되지 않은 행을 우클릭한 경우 → 해당 행만 삭제
|
||||||
|
targetRows = [window.currentContextRow];
|
||||||
|
}
|
||||||
|
|
||||||
|
var pidList = targetRows.map(function (r) { return r.getData().pid; });
|
||||||
|
var nameList = targetRows.map(function (r) { return r.getData().divi_name; });
|
||||||
|
|
||||||
|
var confirmMsg;
|
||||||
|
if (pidList.length === 1) {
|
||||||
|
confirmMsg = "'" + nameList[0] + "' 항목을 삭제하시겠습니까?\n하위 항목이 있을 경우 삭제되지 않습니다.";
|
||||||
|
} else {
|
||||||
|
confirmMsg = pidList.length + "개 항목을 삭제하시겠습니까?\n(" + nameList.join(', ') + ")\n하위 항목이 있는 경우 해당 항목은 건너뜁니다.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confirm(confirmMsg)) return;
|
||||||
|
|
||||||
|
if (pidList.length === 1) {
|
||||||
|
// 단건 삭제 - 기존 API 사용
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/settings/medicalCategory/delMedicalCategory.do',
|
url: '/settings/medicalCategory/delMedicalCategory.do',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: { pid: pid },
|
data: { pid: pidList[0] },
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
alert(res.msgDesc);
|
alert(res.msgDesc);
|
||||||
if (res.msgCode === '0' && window.currentContextRow) {
|
if (res.msgCode === '0') {
|
||||||
var tableId = window.currentContextRow.getTable().element.id;
|
loadData(true);
|
||||||
var isSelected = window.currentContextRow.isSelected();
|
|
||||||
|
|
||||||
// 1. 그리드에서 행 삭제
|
|
||||||
window.currentContextRow.delete();
|
|
||||||
|
|
||||||
// 2. 만약 선택된 행이었다면, 우측 하위 패널 초기화
|
|
||||||
if (isSelected) {
|
|
||||||
if (tableId === 'gridDepth2') {
|
|
||||||
table3.setData([]);
|
|
||||||
table4.setData([]);
|
|
||||||
$("#btnArea3").empty();
|
|
||||||
$("#btnArea4").empty();
|
|
||||||
} else if (tableId === 'gridDepth3') {
|
|
||||||
table4.setData([]);
|
|
||||||
$("#btnArea4").empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
alert("삭제 중 오류가 발생했습니다.");
|
alert("삭제 중 오류가 발생했습니다.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 멀티 삭제
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/medicalCategory/delMultiMedicalCategory.do',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({ pidList: pidList }),
|
||||||
|
success: function (res) {
|
||||||
|
alert(res.msgDesc);
|
||||||
|
if (res.msgCode === '0') {
|
||||||
|
loadData(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert("삭제 중 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +124,7 @@ $(document).ready(function () {
|
|||||||
index: "pid",
|
index: "pid",
|
||||||
layout: "fitColumns",
|
layout: "fitColumns",
|
||||||
placeholder: "상위 항목을 선택하세요.",
|
placeholder: "상위 항목을 선택하세요.",
|
||||||
selectable: 1,
|
selectable: true,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
columnDefaults: {
|
columnDefaults: {
|
||||||
headerTooltip: true,
|
headerTooltip: true,
|
||||||
@@ -102,18 +132,17 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Depth 2 Grid - 순번, 명칭, 순서, 관리, 하위
|
// Depth 2 Grid - 순번, 명칭, 순서, 하위
|
||||||
table2 = new Tabulator("#gridDepth2", Object.assign({}, commonOpts, {
|
table2 = new Tabulator("#gridDepth2", Object.assign({}, commonOpts, {
|
||||||
columns: [
|
columns: [
|
||||||
{ title: "순번", formatter: "rownum", width: 38, hozAlign: "center", headerSort: false, cssClass: "col-sm" },
|
{ title: "순번", formatter: "rownum", width: 38, hozAlign: "center", headerSort: false, cssClass: "col-sm" },
|
||||||
{ title: "명칭", field: "divi_name", formatter: categoryNameFormatter, tooltip: true },
|
{ title: "명칭", field: "divi_name", formatter: categoryNameFormatter, tooltip: true },
|
||||||
{ title: "순서", field: "divi_sort", width: 38, hozAlign: "center", cssClass: "col-sm" },
|
{ title: "순서", field: "divi_sort", width: 38, hozAlign: "center", cssClass: "col-sm" },
|
||||||
{ title: "관리", width: 50, hozAlign: "center", headerSort: false, formatter: editFormatter, cellClick: function (e, cell) { e.stopPropagation(); editCategory(cell.getRow().getData().pid); } },
|
|
||||||
{ title: "하위", width: 50, hozAlign: "center", headerSort: false, formatter: function (c) { return addDescendantFormatter(c, 2); }, cellClick: function (e, cell) { e.stopPropagation(); var d = cell.getRow().getData(); addChildCategory(d.pid, 3, d.divi_name); } }
|
{ title: "하위", width: 50, hozAlign: "center", headerSort: false, formatter: function (c) { return addDescendantFormatter(c, 2); }, cellClick: function (e, cell) { e.stopPropagation(); var d = cell.getRow().getData(); addChildCategory(d.pid, 3, d.divi_name); } }
|
||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Depth 3 Grid - 순번, 명칭, 거래처, 담당자(연락처), 단가, 순서, 관리, 하위
|
// Depth 3 Grid - 순번, 명칭, 거래처, 담당자(연락처), 단가, 순서, 하위
|
||||||
table3 = new Tabulator("#gridDepth3", Object.assign({}, commonOpts, {
|
table3 = new Tabulator("#gridDepth3", Object.assign({}, commonOpts, {
|
||||||
columns: [
|
columns: [
|
||||||
{ title: "순번", formatter: "rownum", width: 38, hozAlign: "center", headerSort: false, cssClass: "col-sm" },
|
{ title: "순번", formatter: "rownum", width: 38, hozAlign: "center", headerSort: false, cssClass: "col-sm" },
|
||||||
@@ -122,24 +151,65 @@ $(document).ready(function () {
|
|||||||
{ title: "담당자(연락처)", field: "cust_contact", width: 110, hozAlign: "center", tooltip: true },
|
{ title: "담당자(연락처)", field: "cust_contact", width: 110, hozAlign: "center", tooltip: true },
|
||||||
{ title: "단가", field: "kind_cost", width: 80, hozAlign: "right", formatter: costFormatter },
|
{ title: "단가", field: "kind_cost", width: 80, hozAlign: "right", formatter: costFormatter },
|
||||||
{ title: "순서", field: "divi_sort", width: 38, hozAlign: "center", cssClass: "col-sm" },
|
{ title: "순서", field: "divi_sort", width: 38, hozAlign: "center", cssClass: "col-sm" },
|
||||||
{ title: "관리", width: 50, hozAlign: "center", headerSort: false, formatter: editFormatter, cellClick: function (e, cell) { e.stopPropagation(); editCategory(cell.getRow().getData().pid); } },
|
|
||||||
{ title: "하위", width: 50, hozAlign: "center", headerSort: false, formatter: function (c) { return addDescendantFormatter(c, 3); }, cellClick: function (e, cell) { e.stopPropagation(); var d = cell.getRow().getData(); addChildCategory(d.pid, 4, d.divi_name); } }
|
{ title: "하위", width: 50, hozAlign: "center", headerSort: false, formatter: function (c) { return addDescendantFormatter(c, 3); }, cellClick: function (e, cell) { e.stopPropagation(); var d = cell.getRow().getData(); addChildCategory(d.pid, 4, d.divi_name); } }
|
||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Depth 4 Grid - 순번, 명칭, 순서, 관리 (하위 없음)
|
// Depth 4 Grid - 순번, 명칭, 순서 (하위 없음)
|
||||||
table4 = new Tabulator("#gridDepth4", Object.assign({}, commonOpts, {
|
table4 = new Tabulator("#gridDepth4", Object.assign({}, commonOpts, {
|
||||||
columns: [
|
columns: [
|
||||||
{ title: "순번", formatter: "rownum", width: 38, hozAlign: "center", headerSort: false, cssClass: "col-sm" },
|
{ title: "순번", formatter: "rownum", width: 38, hozAlign: "center", headerSort: false, cssClass: "col-sm" },
|
||||||
{ title: "명칭", field: "divi_name", formatter: categoryNameFormatter, tooltip: true },
|
{ title: "명칭", field: "divi_name", formatter: categoryNameFormatter, tooltip: true },
|
||||||
{ title: "순서", field: "divi_sort", width: 38, hozAlign: "center", cssClass: "col-sm" },
|
{ title: "순서", field: "divi_sort", width: 38, hozAlign: "center", cssClass: "col-sm" }
|
||||||
{ title: "관리", width: 50, hozAlign: "center", headerSort: false, formatter: editFormatter, cellClick: function (e, cell) { e.stopPropagation(); editCategory(cell.getRow().getData().pid); } }
|
|
||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
table2.on("rowClick", function (e, row) { clickRow(row, 2); });
|
// Shift+Click 범위 선택을 위한 마지막 클릭 행 추적
|
||||||
table3.on("rowClick", function (e, row) { clickRow(row, 3); });
|
var lastClickedRow = {};
|
||||||
table4.on("rowClick", function (e, row) { clickRow(row, 4); });
|
|
||||||
|
function handleRowClick(e, row, table, depth) {
|
||||||
|
var tableId = table.element.id;
|
||||||
|
|
||||||
|
if (e.shiftKey && lastClickedRow[tableId]) {
|
||||||
|
// Shift+Click: 범위 선택
|
||||||
|
var allRows = table.getRows();
|
||||||
|
var startIdx = allRows.indexOf(lastClickedRow[tableId]);
|
||||||
|
var endIdx = allRows.indexOf(row);
|
||||||
|
if (startIdx === -1) startIdx = 0;
|
||||||
|
|
||||||
|
var from = Math.min(startIdx, endIdx);
|
||||||
|
var to = Math.max(startIdx, endIdx);
|
||||||
|
|
||||||
|
table.deselectRow();
|
||||||
|
for (var i = from; i <= to; i++) {
|
||||||
|
allRows[i].select();
|
||||||
|
}
|
||||||
|
} else if (e.ctrlKey || e.metaKey) {
|
||||||
|
// Ctrl+Click: 토글 선택
|
||||||
|
row.toggleSelect();
|
||||||
|
} else {
|
||||||
|
// 일반 클릭: 단일 선택
|
||||||
|
table.deselectRow();
|
||||||
|
row.select();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastClickedRow[tableId] = row;
|
||||||
|
clickRow(row, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
table2.on("rowClick", function (e, row) { handleRowClick(e, row, table2, 2); });
|
||||||
|
table3.on("rowClick", function (e, row) { handleRowClick(e, row, table3, 3); });
|
||||||
|
table4.on("rowClick", function (e, row) { handleRowClick(e, row, table4, 4); });
|
||||||
|
|
||||||
|
|
||||||
|
function onCellDblClick(e, cell) {
|
||||||
|
if (cell.getField() === "divi_name") {
|
||||||
|
editCategory(cell.getRow().getData().pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table2.on("cellDblClick", onCellDblClick);
|
||||||
|
table3.on("cellDblClick", onCellDblClick);
|
||||||
|
table4.on("cellDblClick", onCellDblClick);
|
||||||
|
|
||||||
table2.on("rowContext", onRowContextMenu);
|
table2.on("rowContext", onRowContextMenu);
|
||||||
table3.on("rowContext", onRowContextMenu);
|
table3.on("rowContext", onRowContextMenu);
|
||||||
@@ -195,8 +265,6 @@ $(document).ready(function () {
|
|||||||
var data = row.getData();
|
var data = row.getData();
|
||||||
var children = data._children || [];
|
var children = data._children || [];
|
||||||
|
|
||||||
row.getTable().deselectRow();
|
|
||||||
row.select();
|
|
||||||
|
|
||||||
if (depth === 2) {
|
if (depth === 2) {
|
||||||
table3.setData(children).then(function () {
|
table3.setData(children).then(function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user