/** * DataGrid 公共管理模块 * * 核心设计原则: * 1. 草稿态维护最终状态,而非操作过程 * 2. delete 是临时挂起状态,保留 backup 用于恢复 * 3. 本地数据不参与条件搜索,搜索完全依赖远程接口 * 4. 搜索与展示分离:搜索=远程,展示=本地状态覆盖 * 5. baseSearchCondition 作为永久根条件 * * 使用方式: * var gridManager = createDataGridManager({ * gridId: 'feegrid', * keyField: 'subid', * viewName: 'v_cwzd_orderfee_s', * baseSearchCondition: "mx_zdorderid='xxx'", * onLoadSuccess: function(rows) { ... }, * onAfterEdit: function(rowIndex, row, changes) { ... }, * onSelectionChange: function() { ... } * }); */ (function (window) { "use strict"; // ==================== DraftStateManager(内部模块)==================== function createDraftStateManager(config) { var keyField = (config && config.keyField) || "id"; var defaultPageSize = (config && config.pageSize) || 50; var draftStateMap = {}; var isAllChecked = false; var pagination = { pageSize: defaultPageSize, pageNumber: 1, remoteTotal: 0, }; function initOrUpdateDraftState(rowId) { if (!draftStateMap[rowId]) { draftStateMap[rowId] = { state: null, data: null, backup: null, selected: false, excluded: false, }; } return draftStateMap[rowId]; } function setDraftState(rowId, state, data, backup) { var draftState = initOrUpdateDraftState(rowId); draftState.state = state; draftState.data = data; draftState.backup = backup; } function getDraftState(rowId) { return draftStateMap[rowId]; } function removeDraftState(rowId) { delete draftStateMap[rowId]; } function hasDraftState(rowId) { return draftStateMap.hasOwnProperty(rowId); } function setRowSelected(rowId, selected) { var draftState = initOrUpdateDraftState(rowId); draftState.selected = selected; } function setRowExcluded(rowId, excluded) { var draftState = initOrUpdateDraftState(rowId); draftState.excluded = excluded; } function addInsertRow(row) { if (!row._rowState) row._rowState = "insert"; var key = getRowKey(row); setDraftState(key, "insert", row, null); } function removeInsertRow(row) { var key = getRowKey(row); removeDraftState(key); } function addUpdateRow(row) { var key = getRowKey(row); var existingState = getDraftState(key); if (existingState && existingState.state === "delete") { return; } row._rowState = "update"; setDraftState(key, "update", row, null); } function addDeleteRow(row) { var key = getRowKey(row); var existingState = getDraftState(key); var backupData = existingState ? existingState.data : row; row._rowState = "delete"; setDraftState(key, "delete", row, backupData); setRowSelected(key, false); } function recoverRow(row) { var key = getRowKey(row); var state = getDraftState(key); if (state && state.state === "delete") { if (state.backup) { var backupState = state.backup._rowState || ""; setDraftState(key, backupState, state.backup, null); row._rowState = backupState; } else { removeDraftState(key); row._rowState = ""; } } } function addSelectedRow(row) { var key = getRowKey(row); setRowSelected(key, true); } function removeSelectedRow(row) { var key = getRowKey(row); setRowSelected(key, false); } function clearSelectedRows() { for (var key in draftStateMap) { draftStateMap[key].selected = false; } } function addExcludedRow(row) { var key = getRowKey(row); setRowExcluded(key, true); } function removeExcludedRow(row) { var key = getRowKey(row); setRowExcluded(key, false); } function clearExcludedRows() { for (var key in draftStateMap) { draftStateMap[key].excluded = false; } } function isRowExcluded(row) { var key = getRowKey(row); var state = getDraftState(key); return state ? state.excluded : false; } function cloneRow(row) { if (!row) return null; return JSON.parse(JSON.stringify(row)); } function applyDraftState(remoteRows) { var displayRows = []; for (var i = 0; i < remoteRows.length; i++) { var row = remoteRows[i]; var key = getRowKey(row); var draftState = getDraftState(key); if (draftState) { switch (draftState.state) { case "delete": continue; case "update": case "insert": displayRows.push(cloneRow(draftState.data)); break; default: displayRows.push(cloneRow(row)); } } else { displayRows.push(cloneRow(row)); } } return displayRows; } function reset() { draftStateMap = {}; isAllChecked = false; pagination = { pageSize: defaultPageSize, pageNumber: 1, remoteTotal: 0, }; } function getRowKey(row) { return row[keyField]; } function getRowIdsByState(stateFilter) { var ids = []; for (var key in draftStateMap) { var state = draftStateMap[key]; if (state.state === stateFilter) { ids.push(key); } } return ids; } return { keyField: keyField, setDraftState: setDraftState, getDraftState: getDraftState, removeDraftState: removeDraftState, hasDraftState: hasDraftState, addInsertRow: addInsertRow, removeInsertRow: removeInsertRow, addUpdateRow: addUpdateRow, addDeleteRow: addDeleteRow, recoverRow: recoverRow, getInsertRows: function () { var inserts = []; for (var key in draftStateMap) { if (draftStateMap[key].state === "insert") { inserts.push(draftStateMap[key].data); } } return inserts; }, getInsertRowIds: function () { return getRowIdsByState("insert"); }, getUpdateRows: function () { var updates = []; for (var key in draftStateMap) { if (draftStateMap[key].state === "update") { updates.push(draftStateMap[key].data); } } return updates; }, getDeleteRows: function () { var deletes = []; for (var key in draftStateMap) { if (draftStateMap[key].state === "delete") { deletes.push(draftStateMap[key].data); } } return deletes; }, addSelectedRow: addSelectedRow, removeSelectedRow: removeSelectedRow, getSelectedRowIds: function () { var ids = []; for (var key in draftStateMap) { if ( draftStateMap[key].selected && (!draftStateMap[key].state || draftStateMap[key].state !== "delete") ) { ids.push(key); } } return ids; }, getSelectedRows: function () { var rows = []; for (var key in draftStateMap) { var state = draftStateMap[key]; if (state.selected && state.data) { rows.push(state.data); } } return rows; }, getValidSelectedRows: function () { var rows = []; for (var key in draftStateMap) { var state = draftStateMap[key]; if ( state.selected && state.data && (!state.state || state.state !== "delete") ) { rows.push(state.data); } } return rows; }, clearSelectedRows: clearSelectedRows, isAllChecked: function () { return isAllChecked; }, setAllChecked: function (value) { isAllChecked = value; if (value) clearSelectedRows(); }, addExcludedRow: addExcludedRow, removeExcludedRow: removeExcludedRow, getExcludedRowIds: function () { var ids = []; for (var key in draftStateMap) { if (draftStateMap[key].excluded) { ids.push(key); } } return ids; }, getExcludedRows: function () { var rows = []; for (var key in draftStateMap) { var state = draftStateMap[key]; if (state.excluded && state.data) { rows.push(state.data); } } return rows; }, clearExcludedRows: clearExcludedRows, isRowExcluded: isRowExcluded, applyDraftState: applyDraftState, pagination: { get: function () { return pagination; }, setPageNumber: function (pageNumber) { pagination.pageNumber = pageNumber; }, setPageSize: function (pageSize) { pagination.pageSize = pageSize; }, setRemoteTotal: function (remoteTotal) { pagination.remoteTotal = remoteTotal; }, getPageSize: function () { return pagination.pageSize; }, getPageNumber: function () { return pagination.pageNumber; }, getRemoteTotal: function () { return pagination.remoteTotal; }, getTotalPages: function () { return Math.ceil(pagination.remoteTotal / pagination.pageSize); }, }, getRowKey: getRowKey, reset: reset, }; } // ==================== createDataGridManager(导出)==================== function createDataGridManager(config) { var $ = window.jQuery; var DEFAULT_CONFIG = { gridId: "feegrid", keyField: "subid", pageSize: 50, viewName: "", searchUrl: "jsp/searchdatapagenew.jsp", orderField: "b_id", baseSearchCondition: null, searchCondition: null, pageList: [50, 100, 200], rowStateStyles: { insert: "background-color: #f6ffed;", update: "background-color: #fffbe6;", delete: "background-color: #fff1f0;", }, selectAllBtn: "#btn_select_all", selectAllText: "全部选中", unselectAllText: "全部反选", userid: "", customDatagridOptions: null, ajaxTimeout: 30000, onLoadError: null, onLoadSuccess: null, onAfterEdit: null, onSelectionChange: null, }; var options = $.extend(true, {}, DEFAULT_CONFIG, config); var state = { draftManager: null, editIndex: undefined, isLoading: false, pagerId: null, requestVersion: 0, }; _initDraftManager(); _initGrid(); function _initDraftManager() { state.draftManager = createDraftStateManager({ keyField: options.keyField, pageSize: options.pageSize, }); } function _getPagerId() { if (!state.pagerId) { state.pagerId = options.gridId + "_pager"; } return state.pagerId; } function _setLoading() { state.isLoading = true; $("#" + options.gridId).datagrid("loading"); $("#" + _getPagerId()) .css({ "pointer-events": "none", opacity: "0.6", }) .pagination("loading"); } function _clearLoading() { state.isLoading = false; $("#" + options.gridId).datagrid("loaded"); $("#" + _getPagerId()) .css({ "pointer-events": "", opacity: "", }) .pagination("loaded"); } function _syncGridChecks() { var rows = $("#" + options.gridId).datagrid("getRows"); var selectedKeys = {}; var selectedRowIds = state.draftManager.getSelectedRowIds(); for (var s = 0; s < selectedRowIds.length; s++) { selectedKeys[selectedRowIds[s]] = true; } var excludedKeys = {}; var excludedRowIds = state.draftManager.getExcludedRowIds(); for (var e = 0; e < excludedRowIds.length; e++) { excludedKeys[excludedRowIds[e]] = true; } for (var i = 0; i < rows.length; i++) { var row = rows[i]; var key = state.draftManager.getRowKey(row); if (state.draftManager.isAllChecked()) { if (excludedKeys[key]) { $("#" + options.gridId).datagrid("uncheckRow", i); } else { $("#" + options.gridId).datagrid("checkRow", i); } } else { if (selectedKeys[key]) { $("#" + options.gridId).datagrid("checkRow", i); } else { $("#" + options.gridId).datagrid("uncheckRow", i); } } } } function _handleRowStyles() { $(".datagrid-body tr").each(function () { var rowIndex = $(this).attr("datagrid-row-index"); if (rowIndex !== undefined) { var row = $("#" + options.gridId).datagrid("getRows")[ parseInt(rowIndex) ]; if (row && row._rowState === "delete") { $(this).addClass("datagrid-row-delete"); } else { $(this).removeClass("datagrid-row-delete"); } } }); } function buildFinalCondition() { var parts = []; if (options.baseSearchCondition) { parts.push(options.baseSearchCondition); } if (options.searchCondition) { parts.push(options.searchCondition); } return parts.join(" AND "); } function loadPage(pageNumber) { _setLoading(); var pageSize = state.draftManager.pagination.getPageSize(); var searchCondition = buildFinalCondition(); var currentRequestVersion = ++state.requestVersion; if (!searchCondition) { var insertRows = state.draftManager.getInsertRows(); var pageData = []; if (insertRows.length > 0) { var startInInsert = (pageNumber - 1) * pageSize; var endInInsert = startInInsert + pageSize; pageData = insertRows.slice(startInInsert, endInInsert); } _renderPage(pageData, insertRows.length, pageNumber, pageSize); _clearLoading(); return; } var searchParam = { viewname: options.viewName, searchcondition: searchCondition, userid: options.userid, orderfield: options.orderField, keyfield: options.keyField, page: pageNumber, rows: pageSize, }; $.ajax({ type: "post", url: options.searchUrl, cache: false, data: searchParam, dataType: "json", timeout: options.ajaxTimeout, success: function (result) { if (currentRequestVersion !== state.requestVersion) { return; } state.draftManager.pagination.setRemoteTotal(result.total); var displayRows = state.draftManager.applyDraftState(result.rows); _renderPage(displayRows, result.total, pageNumber, pageSize); }, error: function (xhr, status, error) { if (currentRequestVersion !== state.requestVersion) { return; } if (typeof options.onLoadError === "function") { options.onLoadError(xhr, status, error); } else { console.log("分页加载失败: " + status); } }, complete: function () { if (currentRequestVersion === state.requestVersion) { _clearLoading(); } }, }); } function _renderPage(pageData, total, pageNumber, pageSize) { $("#" + options.gridId).datagrid("loadData", pageData); $("#" + _getPagerId()).pagination("refresh", { total: total, pageNumber: pageNumber, pageSize: pageSize, }); } function refreshCurrentPage() { var currentPage = state.draftManager.pagination.getPageNumber(); loadPage(currentPage); } function refreshDisplay() { var currentRows = $("#" + options.gridId).datagrid("getRows"); $("#" + options.gridId).datagrid("loadData", currentRows); $("#" + options.gridId).datagrid("clearChecked"); _triggerCallback("onSelectionChange"); } function refreshAfterDelete() { var pageSize = state.draftManager.pagination.getPageSize(); var remoteTotal = state.draftManager.pagination.getRemoteTotal(); var totalPages = Math.ceil(remoteTotal / pageSize); var currentPage = state.draftManager.pagination.getPageNumber(); if (currentPage > totalPages && totalPages > 0) { state.draftManager.pagination.setPageNumber(totalPages); $("#" + _getPagerId()).pagination("select", totalPages); } else if (remoteTotal === 0) { $("#" + options.gridId).datagrid("loadData", []); $("#" + _getPagerId()).pagination("refresh", { total: 0, pageNumber: 1, pageSize: pageSize, }); } else { loadPage(currentPage); } $("#" + options.gridId).datagrid("clearChecked"); _triggerCallback("onSelectionChange"); } function refreshAfterUndo() { var currentPage = state.draftManager.pagination.getPageNumber(); loadPage(currentPage); } function addInsertRows(rows) { if (!$.isArray(rows)) rows = [rows]; for (var i = 0; i < rows.length; i++) { state.draftManager.addInsertRow(rows[i]); } } function removeInsertRow(row) { state.draftManager.removeInsertRow(row); } function addDeleteRow(row) { state.draftManager.addDeleteRow(row); } function removeDeleteRow(row) { state.draftManager.recoverRow(row); refreshAfterUndo(); } function addUpdateRow(row) { state.draftManager.addUpdateRow(row); } function toggleSelectAll() { var btn = $(options.selectAllBtn); if (state.draftManager.isAllChecked()) { state.draftManager.setAllChecked(false); state.draftManager.clearExcludedRows(); state.draftManager.clearSelectedRows(); $("#" + options.gridId).datagrid("clearChecked"); if (btn.length) btn.val(options.selectAllText); } else { state.draftManager.setAllChecked(true); state.draftManager.clearExcludedRows(); var rows = $("#" + options.gridId).datagrid("getRows"); for (var i = 0; i < rows.length; i++) { $("#" + options.gridId).datagrid("checkRow", i); } if (btn.length) btn.val(options.unselectAllText); } _triggerCallback("onSelectionChange"); } function clearSelection() { state.draftManager.setAllChecked(false); state.draftManager.clearExcludedRows(); state.draftManager.clearSelectedRows(); $("#" + options.gridId).datagrid("clearChecked"); _triggerCallback("onSelectionChange"); } function setAllChecked(value) { state.draftManager.setAllChecked(value); if (value) state.draftManager.clearExcludedRows(); _syncGridChecks(); _triggerCallback("onSelectionChange"); } function setSearchCondition(condition) { options.searchCondition = condition; } function clearSearch() { options.searchCondition = null; } function reload(newSearchCondition) { if (newSearchCondition !== undefined) { options.searchCondition = newSearchCondition; } state.draftManager.pagination.setPageNumber(1); loadPage(1); } function resetSearch() { options.searchCondition = null; reload(); } function _triggerCallback(callbackName) { var callback = options[callbackName]; if (typeof callback === "function") { var args = Array.prototype.slice.call(arguments, 1); callback.apply(null, args); } } function handleAfterEdit(rowIndex, row, changes) { if (Object.keys(changes).length === 0) return; var key = state.draftManager.getRowKey(row); var draftState = state.draftManager.getDraftState(key); if (draftState && draftState.state === "delete") { return; } if (draftState && draftState.state === "insert") { row._rowState = "insert"; state.draftManager.setDraftState(key, "insert", row, null); } else if (draftState && draftState.state === "update") { row._rowState = "update"; state.draftManager.setDraftState(key, "update", row, null); } else { row._rowState = "update"; state.draftManager.addUpdateRow(row); } _triggerCallback("onAfterEdit", rowIndex, row, changes); } function handleRowCheck(index, row) { if (state.draftManager.isAllChecked()) { state.draftManager.removeExcludedRow(row); } else { state.draftManager.addSelectedRow(row); } _triggerCallback("onSelectionChange"); } function handleRowUncheck(index, row) { if (state.draftManager.isAllChecked()) { state.draftManager.addExcludedRow(row); } else { state.draftManager.removeSelectedRow(row); } _triggerCallback("onSelectionChange"); } function handleCheckAll() { state.draftManager.setAllChecked(true); state.draftManager.clearExcludedRows(); _triggerCallback("onSelectionChange"); } function handleUncheckAll() { state.draftManager.setAllChecked(false); state.draftManager.clearExcludedRows(); state.draftManager.clearSelectedRows(); _triggerCallback("onSelectionChange"); } function handleLoadSuccess() { $.messager.progress("close"); _syncGridChecks(); _handleRowStyles(); var rows = $("#" + options.gridId).datagrid("getRows"); _triggerCallback("onLoadSuccess", rows); } function reset() { state.editIndex = undefined; state.isLoading = false; if (state.draftManager) state.draftManager.reset(); } function _initGrid() { var baseDatagridOptions = { idField: options.keyField, pageSize: options.pageSize, rowStyler: function (index, row) { var stateStyle = options.rowStateStyles[row._rowState]; return stateStyle || ""; }, onLoadSuccess: handleLoadSuccess, onAfterEdit: handleAfterEdit, onCheckAll: handleCheckAll, onUncheckAll: handleUncheckAll, onCheck: handleRowCheck, onUncheck: handleRowUncheck, }; var datagridOptions = options.customDatagridOptions ? $.extend(true, {}, baseDatagridOptions, options.customDatagridOptions) : baseDatagridOptions; $("#" + options.gridId) .datagrid(datagridOptions) .datagrid("columnMoving"); $("#" + options.gridId) .datagrid("getPanel") .find(".datagrid-body") .on("scroll.datagrid", function () { if (state.editIndex !== undefined) { $("#" + options.gridId).datagrid("endEdit", state.editIndex); state.editIndex = undefined; } }); var pagerId = _getPagerId(); var pagerSelector = "#" + pagerId; if ($(pagerSelector).length === 0) { var gridPanel = $("#" + options.gridId).datagrid("getPanel"); var pagerDiv = $( '', ); gridPanel.after(pagerDiv); } $(pagerSelector).pagination({ total: 0, pageSize: options.pageSize, pageNumber: 1, pageList: options.pageList, onSelectPage: function (pageNumber, pageSize) { state.draftManager.pagination.setPageNumber(pageNumber); state.draftManager.pagination.setPageSize(pageSize); loadPage(pageNumber); }, onChangePageSize: function (pageSize) { state.draftManager.pagination.setPageNumber(1); state.draftManager.pagination.setPageSize(pageSize); if (!state.draftManager.isAllChecked()) { $("#" + options.gridId).datagrid("clearChecked"); state.draftManager.clearSelectedRows(); } _triggerCallback("onSelectionChange"); loadPage(1); }, }); } function setColumns(columns) { $("#" + options.gridId).datagrid({ columns: [columns], }); } function isLoading() { return state.isLoading; } function getPager() { return $("#" + _getPagerId()); } function getGrid() { return $("#" + options.gridId); } return { setColumns: setColumns, loadPage: loadPage, refreshCurrentPage: refreshCurrentPage, refreshDisplay: refreshDisplay, refreshAfterDelete: refreshAfterDelete, refreshAfterUndo: refreshAfterUndo, addInsertRows: addInsertRows, removeInsertRow: removeInsertRow, addDeleteRow: addDeleteRow, removeDeleteRow: removeDeleteRow, addUpdateRow: addUpdateRow, getValidSelectedRows: function () { return state.draftManager.getValidSelectedRows(); }, getSelectedRows: function () { return state.draftManager.getSelectedRows(); }, toggleSelectAll: toggleSelectAll, clearSelection: clearSelection, setAllChecked: setAllChecked, getInsertRows: function () { return state.draftManager.getInsertRows(); }, getUpdateRows: function () { return state.draftManager.getUpdateRows(); }, getDeleteRows: function () { return state.draftManager.getDeleteRows(); }, getDraftManager: function () { return state.draftManager; }, setSearchCondition: setSearchCondition, clearSearch: clearSearch, reload: reload, resetSearch: resetSearch, isLoading: isLoading, getPager: getPager, getGrid: getGrid, reset: reset, }; } window.createDataGridManager = createDataGridManager; })(window);