20260511214503
This commit is contained in:
1 parent
364d29dbaa
commit
e98740192d
9 files changed
+2451
-875
No files matched your search
@@ -0,0 +1,274 @@
|
||||
# 草稿态核心架构重构计划
|
||||
|
||||
## 一、问题分析
|
||||
|
||||
当前草稿态管理存在以下核心问题:
|
||||
|
||||
| 问题 | 影响 |
|
||||
|------|------|
|
||||
| 维护操作过程而非最终状态 | recover/search/pagination/merge 逻辑复杂 |
|
||||
| delete 直接覆盖状态 | recover 后编辑数据丢失 |
|
||||
| 本地数据参与搜索 | total/page 不准确,分页错乱 |
|
||||
| 搜索与展示混在一起 | 搜索结果不稳定 |
|
||||
| 缺少基础搜索条件 | 业务层重复拼接条件 |
|
||||
|
||||
---
|
||||
|
||||
## 二、核心设计调整
|
||||
|
||||
### 2.1 草稿态数据模型
|
||||
|
||||
**新模型结构:**
|
||||
|
||||
```js
|
||||
// 草稿状态映射 - 只维护最终状态
|
||||
draftStateMap = {
|
||||
[rowId]: {
|
||||
state: 'insert' | 'update' | 'delete',
|
||||
data: {}, // 本地修改后的数据
|
||||
backup: {} // delete 时的备份(用于 recover)
|
||||
}
|
||||
};
|
||||
|
||||
// 本地新增数据(尚未保存到数据库)
|
||||
insertRows = [];
|
||||
|
||||
// 选中状态(独立于草稿状态)
|
||||
selectedRows = [];
|
||||
```
|
||||
|
||||
**状态转换规则:**
|
||||
|
||||
```text
|
||||
insert → delete → recover → insert(恢复本地新增态)
|
||||
update → delete → recover → update(恢复编辑态)
|
||||
delete → recover → 恢复 backup,如果没有则恢复远程原始态
|
||||
```
|
||||
|
||||
### 2.2 搜索与展示分离
|
||||
|
||||
| 阶段 | 数据源 | 说明 |
|
||||
|------|--------|------|
|
||||
| 搜索 | 远程接口 | 完全依赖服务器,本地数据不参与 |
|
||||
| 展示 | 远程 + 本地覆盖 | 使用本地状态覆盖远程数据展示 |
|
||||
|
||||
### 2.3 baseSearchCondition 机制
|
||||
|
||||
```js
|
||||
// 基础条件(永久保留)
|
||||
baseSearchCondition: "mx_zdorderid='xxx'"
|
||||
|
||||
// 动态条件(可清空)
|
||||
searchCondition: "name like '%abc%'"
|
||||
|
||||
// 最终条件
|
||||
finalCondition = baseSearchCondition + " AND " + searchCondition
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、实施步骤
|
||||
|
||||
### 3.1 步骤一:重构 DraftPageManager
|
||||
|
||||
**修改文件:** `js/feegrid-manager.js`
|
||||
|
||||
**核心改动:**
|
||||
|
||||
1. **新增状态映射结构**:
|
||||
```js
|
||||
var draftStateMap = {}; // { rowId: { state, data, backup } }
|
||||
var insertRows = []; // 本地新增行
|
||||
```
|
||||
|
||||
2. **简化状态操作方法**:
|
||||
```js
|
||||
function setDraftState(rowId, state, data, backup) {
|
||||
draftStateMap[rowId] = { state, data, backup };
|
||||
}
|
||||
|
||||
function getDraftState(rowId) {
|
||||
return draftStateMap[rowId];
|
||||
}
|
||||
|
||||
function removeDraftState(rowId) {
|
||||
delete draftStateMap[rowId];
|
||||
}
|
||||
```
|
||||
|
||||
3. **实现 delete backup 机制**:
|
||||
```js
|
||||
function addDeleteRow(row) {
|
||||
var rowId = getRowKey(row);
|
||||
var existingState = getDraftState(rowId);
|
||||
|
||||
setDraftState(rowId, 'delete', row, existingState ? existingState.data : row);
|
||||
}
|
||||
|
||||
function recoverRow(rowId) {
|
||||
var state = getDraftState(rowId);
|
||||
if (state && state.state === 'delete') {
|
||||
setDraftState(rowId, state.backup._rowState || '', state.backup, null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 步骤二:重构 loadPage 方法
|
||||
|
||||
**核心改动:**
|
||||
|
||||
1. **搜索阶段**:只调用远程接口,本地数据不参与
|
||||
2. **展示阶段**:使用本地状态覆盖远程数据
|
||||
|
||||
```js
|
||||
function loadPage(pageNumber) {
|
||||
_setLoading();
|
||||
|
||||
var searchCondition = buildFinalCondition();
|
||||
|
||||
$.ajax({
|
||||
url: options.searchUrl,
|
||||
data: {
|
||||
viewname: options.viewName,
|
||||
searchcondition: searchCondition,
|
||||
page: pageNumber,
|
||||
rows: options.pageSize
|
||||
},
|
||||
success: function(result) {
|
||||
// 应用本地状态覆盖
|
||||
var displayRows = applyDraftState(result.rows);
|
||||
|
||||
_renderPage(displayRows, result.total, pageNumber);
|
||||
},
|
||||
complete: _clearLoading
|
||||
});
|
||||
}
|
||||
|
||||
function applyDraftState(remoteRows) {
|
||||
var displayRows = [];
|
||||
|
||||
for (var i = 0; i < remoteRows.length; i++) {
|
||||
var row = remoteRows[i];
|
||||
var rowId = getRowKey(row);
|
||||
var draftState = getDraftState(rowId);
|
||||
|
||||
if (draftState) {
|
||||
switch (draftState.state) {
|
||||
case 'delete':
|
||||
// 删除状态不展示
|
||||
continue;
|
||||
case 'update':
|
||||
// 展示更新后的数据
|
||||
displayRows.push(draftState.data);
|
||||
break;
|
||||
default:
|
||||
displayRows.push(row);
|
||||
}
|
||||
} else {
|
||||
displayRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 本地新增行单独处理(不参与分页)
|
||||
return displayRows;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 步骤三:添加 baseSearchCondition 支持
|
||||
|
||||
**配置新增:**
|
||||
```js
|
||||
var DEFAULT_CONFIG = {
|
||||
baseSearchCondition: null,
|
||||
searchCondition: null,
|
||||
// ... 其他配置
|
||||
};
|
||||
```
|
||||
|
||||
**条件构建:**
|
||||
```js
|
||||
function buildFinalCondition() {
|
||||
var parts = [];
|
||||
|
||||
if (options.baseSearchCondition) {
|
||||
parts.push(options.baseSearchCondition);
|
||||
}
|
||||
|
||||
if (options.searchCondition) {
|
||||
parts.push(options.searchCondition);
|
||||
}
|
||||
|
||||
return parts.join(' AND ');
|
||||
}
|
||||
|
||||
function setSearchCondition(condition) {
|
||||
options.searchCondition = condition;
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
options.searchCondition = null;
|
||||
}
|
||||
|
||||
function reload() {
|
||||
loadPage(1);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 步骤四:更新 JSP 调用
|
||||
|
||||
**修改文件:** `editorder_s_cw.jsp`
|
||||
|
||||
**简化调用:**
|
||||
```js
|
||||
var gridManager = createDataGridManager({
|
||||
gridId: 'feegrid',
|
||||
keyField: 'subid',
|
||||
viewName: subviewtable,
|
||||
baseSearchCondition: "mx_zdorderid='" + strid + "'",
|
||||
onLoadSuccess: calcw_selfee,
|
||||
onAfterEdit: calcw_fee,
|
||||
onSelectionChange: calcw_selfee
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、文件修改清单
|
||||
|
||||
| 文件 | 修改类型 | 说明 |
|
||||
|------|----------|------|
|
||||
| `js/feegrid-manager.js` | 重构 | 核心数据模型与搜索逻辑 |
|
||||
| `editorder_s_cw.jsp` | 更新 | 使用新的 baseSearchCondition 配置 |
|
||||
|
||||
---
|
||||
|
||||
## 五、风险与注意事项
|
||||
|
||||
1. **数据一致性**:确保 draftStateMap 与 insertRows 同步
|
||||
2. **性能考虑**:draftStateMap 使用对象查找,O(1) 复杂度
|
||||
3. **状态清理**:保存成功后及时清理 draftStateMap
|
||||
4. **边界情况**:处理 rowId 为 null/undefined 的情况
|
||||
|
||||
---
|
||||
|
||||
## 六、最终架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ createDataGridManager │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Configuration │
|
||||
│ ├── gridId, keyField, viewName │
|
||||
│ ├── baseSearchCondition (永久) │
|
||||
│ └── searchCondition (动态) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Draft State Management │
|
||||
│ ├── draftStateMap: { id → { state, data, backup } } │
|
||||
│ ├── insertRows: [] │
|
||||
│ └── selectedRows: [] │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Search & Display │
|
||||
│ ├── loadPage() → 远程搜索 → applyDraftState() → 展示 │
|
||||
│ └── buildFinalCondition() = base + dynamic │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -0,0 +1,230 @@
|
||||
# FeeGrid Manager 重构计划
|
||||
|
||||
## 一、问题分析总结
|
||||
|
||||
### 核心问题清单
|
||||
|
||||
| 问题类型 | 严重程度 | 影响范围 | 根因 |
|
||||
|---------|---------|---------|------|
|
||||
| 状态来源不唯一 | **高** | 所有状态操作 | `draftStateMap`、`insertRows`、`selectedRows`、`excludedRows` 并行维护 |
|
||||
| Selection 基于对象引用 | **高** | 分页/刷新后选择状态 | `selectedRows` 存储完整行对象 |
|
||||
| applyDraftState 副作用 | **高** | 展示层数据 | 直接使用 `draftState.data` 引用 |
|
||||
| 请求无版本化 | **高** | 异步分页切换 | 旧请求会污染新状态 |
|
||||
| recover 后展示不同步 | **中** | 删除恢复场景 | 状态恢复但未触发刷新 |
|
||||
| insertRows 与 draftStateMap 不同步 | **中** | 插入删除场景 | `addDeleteRow` 未清理 `insertRows` |
|
||||
|
||||
### 架构层面的根本缺陷
|
||||
|
||||
1. **状态模型未真正单源化** - 多个状态源持有同一行引用,修改时容易不同步
|
||||
2. **状态修改具有副作用** - `applyDraftState` 直接返回修改过的引用,导致状态污染链
|
||||
3. **缺乏请求版本控制** - 无法区分旧请求和新请求,导致状态回滚
|
||||
4. **UI 状态与数据状态耦合** - selection 基于对象引用而非 id
|
||||
|
||||
---
|
||||
|
||||
## 二、重构目标
|
||||
|
||||
### 核心原则
|
||||
|
||||
1. **状态单源化** - 仅保留 `draftStateMap` 作为唯一状态源
|
||||
2. **状态 id 化** - 所有 UI 状态(selection、excluded)基于 rowId 而非对象引用
|
||||
3. **纯函数化** - `applyDraftState` 不产生副作用,返回新对象
|
||||
4. **请求版本化** - 仅处理最后一次请求,防止旧请求污染
|
||||
|
||||
### 预期改进
|
||||
|
||||
- 彻底消除请求竞态导致的状态回滚
|
||||
- 消除 reload/refresh/page switch 后的 selection 丢失
|
||||
- 消除 insert → delete → display 的不一致
|
||||
- 消除 recover 后展示层与数据层不一致
|
||||
|
||||
---
|
||||
|
||||
## 三、修改方案
|
||||
|
||||
### 3.1 状态结构重构
|
||||
|
||||
**原结构:**
|
||||
```javascript
|
||||
var draftStateMap = {}; // {rowId: {state, data, backup}}
|
||||
var insertRows = []; // 插入行数组
|
||||
var selectedRows = []; // 选中行数组
|
||||
var excludedRows = []; // 排除行数组
|
||||
var isAllChecked = false; // 全选状态
|
||||
```
|
||||
|
||||
**新结构:**
|
||||
```javascript
|
||||
var draftStateMap = {}; // {rowId: {state, data, backup, selected, excluded}}
|
||||
var isAllChecked = false; // 全选状态
|
||||
```
|
||||
|
||||
**状态对象结构:**
|
||||
```javascript
|
||||
{
|
||||
state: 'insert' | 'update' | 'delete' | null, // 数据状态
|
||||
data: {...}, // 当前数据
|
||||
backup: {...} | null, // 删除时的备份数据
|
||||
selected: boolean, // 是否选中
|
||||
excluded: boolean // 是否排除(全选时)
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 核心函数修改
|
||||
|
||||
#### 3.2.1 applyDraftState - 纯函数化
|
||||
- 创建行的深拷贝
|
||||
- 不直接使用 draftState.data 引用
|
||||
- 返回全新的 displayRows
|
||||
|
||||
#### 3.2.2 loadPage - 请求版本化
|
||||
- 引入 `requestVersion` 计数器
|
||||
- 每次请求递增版本号
|
||||
- 响应时检查版本号,只处理最新请求
|
||||
|
||||
#### 3.2.3 recoverRow - 触发刷新
|
||||
- 恢复状态后调用 `refreshCurrentPage()`
|
||||
- 确保展示层同步更新
|
||||
|
||||
#### 3.2.4 所有 selection 操作 - id 化
|
||||
- `addSelectedRow(row)` → `selectRow(rowId)`
|
||||
- `removeSelectedRow(row)` → `deselectRow(rowId)`
|
||||
- `getSelectedRows()` → 返回当前页中被选中的行(通过 id 匹配)
|
||||
|
||||
---
|
||||
|
||||
## 四、修改步骤
|
||||
|
||||
### 步骤 1:重构 DraftStateManager 状态结构
|
||||
**文件:** `js/feegrid-manager.js`(第 27-302 行)
|
||||
|
||||
**修改内容:**
|
||||
1. 移除 `insertRows`、`selectedRows`、`excludedRows` 数组
|
||||
2. 修改 `draftStateMap` 结构,增加 `selected` 和 `excluded` 字段
|
||||
3. 修改相关函数:
|
||||
- `addInsertRow` - 不再维护 `insertRows`
|
||||
- `addSelectedRow` - 改为通过 rowId 设置 `selected` 字段
|
||||
- `removeSelectedRow` - 改为通过 rowId 清除 `selected` 字段
|
||||
- `addExcludedRow` - 改为通过 rowId 设置 `excluded` 字段
|
||||
- `removeExcludedRow` - 改为通过 rowId 清除 `excluded` 字段
|
||||
- `getInsertRows` - 从 `draftStateMap` 中筛选
|
||||
- `getSelectedRows` - 从 `draftStateMap` 中筛选
|
||||
- `getExcludedRows` - 从 `draftStateMap` 中筛选
|
||||
- `getValidSelectedRows` - 从 `draftStateMap` 中筛选
|
||||
|
||||
### 步骤 2:applyDraftState 纯函数化
|
||||
**文件:** `js/feegrid-manager.js`(第 161-185 行)
|
||||
|
||||
**修改内容:**
|
||||
- 使用深拷贝创建新对象
|
||||
- 不修改原始输入参数
|
||||
- 返回全新的 displayRows 数组
|
||||
|
||||
### 步骤 3:请求版本化
|
||||
**文件:** `js/feegrid-manager.js`(第 441-490 行)
|
||||
|
||||
**修改内容:**
|
||||
- 在 `createDataGridManager` 中添加 `requestVersion` 变量
|
||||
- 修改 `loadPage` 函数,递增版本号并在响应时检查
|
||||
|
||||
### 步骤 4:recoverRow 触发刷新
|
||||
**文件:** `js/feegrid-manager.js`(第 100-114 行)
|
||||
|
||||
**修改内容:**
|
||||
- 在 `recoverRow` 函数末尾调用 `refreshCurrentPage()`
|
||||
|
||||
### 步骤 5:修复 insertRows 与 draftStateMap 同步问题
|
||||
**文件:** `js/feegrid-manager.js`(第 89-98 行)
|
||||
|
||||
**修改内容:**
|
||||
- 在 `addDeleteRow` 中同步清理 insert 状态(已通过单源化解决)
|
||||
|
||||
### 步骤 6:修改 _syncGridChecks 逻辑
|
||||
**文件:** `js/feegrid-manager.js`(第 377-409 行)
|
||||
|
||||
**修改内容:**
|
||||
- 根据 `draftStateMap` 中的 `selected` 和 `excluded` 字段同步 UI
|
||||
- 不再依赖 `selectedRows` 和 `excludedRows` 数组
|
||||
|
||||
---
|
||||
|
||||
## 五、风险处理
|
||||
|
||||
### 5.1 兼容性风险
|
||||
|
||||
**风险:** 外部调用者可能依赖 `getSelectedRows()` 返回行对象数组
|
||||
|
||||
**处理:**
|
||||
- 保持 API 接口不变,内部实现改为从 `draftStateMap` 中筛选
|
||||
- 返回的行对象从当前 grid 数据中获取(保持引用一致性)
|
||||
|
||||
### 5.2 性能风险
|
||||
|
||||
**风险:** 深拷贝可能影响性能
|
||||
|
||||
**处理:**
|
||||
- 只在必要时(applyDraftState)进行深拷贝
|
||||
- 考虑使用结构化克隆或 JSON 序列化(视浏览器支持)
|
||||
|
||||
### 5.3 测试风险
|
||||
|
||||
**风险:** 重构可能引入新的 bug
|
||||
|
||||
**处理:**
|
||||
- 按步骤逐步修改,每步进行功能验证
|
||||
- 重点测试以下场景:
|
||||
- 快速分页切换
|
||||
- insert → update → delete → recover
|
||||
- 搜索后 recover
|
||||
- reload 后 selection 保持
|
||||
|
||||
---
|
||||
|
||||
## 六、测试用例
|
||||
|
||||
### 场景 1:快速分页切换
|
||||
1. 快速点击第1页→第2页→第3页
|
||||
2. 验证最终显示正确页面数据
|
||||
3. 验证分页控件显示正确页码
|
||||
|
||||
### 场景 2:状态流转
|
||||
1. update → delete → recover
|
||||
2. 验证 recover 后状态正确(应为原始状态或 update 状态)
|
||||
3. 验证页面显示恢复的行
|
||||
|
||||
### 场景 3:insert → delete
|
||||
1. insert 行 → delete 该行
|
||||
2. 验证 insertRows 不包含已删除行
|
||||
3. 验证页面不显示已删除行
|
||||
|
||||
### 场景 4:reload 后 selection 保持
|
||||
1. 选中某行 → reload
|
||||
2. 验证选中状态保持
|
||||
|
||||
### 场景 5:搜索后 recover
|
||||
1. update → delete → search → recover
|
||||
2. 验证 recover 后页面显示恢复的行
|
||||
|
||||
---
|
||||
|
||||
## 七、预期代码变更量
|
||||
|
||||
| 修改类型 | 预估行数 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 删除 | ~50 | 移除 insertRows/selectedRows/excludedRows 相关代码 |
|
||||
| 修改 | ~100 | 修改状态管理函数 |
|
||||
| 新增 | ~50 | 请求版本化、深拷贝等 |
|
||||
| 总计 | ~200 | 核心逻辑重构 |
|
||||
|
||||
---
|
||||
|
||||
## 八、实施顺序
|
||||
|
||||
1. **步骤 1** - 状态结构重构(核心)
|
||||
2. **步骤 2** - applyDraftState 纯函数化
|
||||
3. **步骤 3** - 请求版本化
|
||||
4. **步骤 4** - recoverRow 触发刷新
|
||||
5. **步骤 5** - 已通过步骤 1 解决
|
||||
6. **步骤 6** - 修改 _syncGridChecks 逻辑
|
||||
|
||||
> 注:步骤 1 是基础,必须首先完成。后续步骤可并行或按顺序进行。
|
||||
@@ -0,0 +1,400 @@
|
||||
# Feegrid 模块深度重构计划
|
||||
|
||||
## 1. 现状分析
|
||||
|
||||
### 1.1 现有代码结构
|
||||
|
||||
| 文件 | 职责 | 问题 |
|
||||
| -------------------------- | ---------------------- | --------------- |
|
||||
| `editorder_s_cw.jsp` | 业务页面,包含大量 feegrid 操作代码 | 通用逻辑与业务代码混杂 |
|
||||
| `js/draft-page-manager.js` | 草稿态分页管理核心模块 | 已完成基础封装,但仍需上层整合 |
|
||||
|
||||
### 1.2 需要封装的通用逻辑
|
||||
|
||||
通过分析 `editorder_s_cw.jsp`,以下代码模式重复出现且属于通用行为:
|
||||
|
||||
**Loading/Loaded 状态控制(散落在多个函数中):**
|
||||
|
||||
```js
|
||||
$('#feegrid').datagrid('loading');
|
||||
$('#subgridPager').css({ 'pointer-events': 'none', 'opacity': '0.6' });
|
||||
$('#subgridPager').pagination('loading');
|
||||
// ... 数据加载完成后
|
||||
$('#feegrid').datagrid('loaded');
|
||||
$('#subgridPager').css({ 'pointer-events': '', 'opacity': '' });
|
||||
$('#subgridPager').pagination('loaded');
|
||||
```
|
||||
|
||||
**分页刷新逻辑:**
|
||||
|
||||
```js
|
||||
$('#feegrid').datagrid('loadData', pageData);
|
||||
$('#subgridPager').pagination('refresh', { total, pageNumber, pageSize });
|
||||
```
|
||||
|
||||
**选中状态同步:**
|
||||
|
||||
```js
|
||||
syncFeeGridChecksFromSubState(); // 分页切换后同步勾选状态
|
||||
```
|
||||
|
||||
**行状态样式处理:**
|
||||
|
||||
```js
|
||||
// rowStyler 根据 _rowState 设置背景色
|
||||
// onLoadSuccess 中处理删除行样式
|
||||
```
|
||||
|
||||
### 1.3 当前问题总结
|
||||
|
||||
1. **内聚性不足**:loading、pager、datagrid 状态控制分散在业务代码中
|
||||
2. **耦合度高**:业务页面直接操作 DOM 和 datagrid API
|
||||
3. **复用性差**:相同的状态管理逻辑在多个地方重复
|
||||
4. **初始化暴露**:业务代码需要显式创建 draftManager
|
||||
|
||||
***
|
||||
|
||||
## 2. 重构目标
|
||||
|
||||
### 2.1 架构设计
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 业务页面 (editorder_s_cw.jsp) │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ 配置项 + 业务回调(仅关心业务逻辑) │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│ 配置/回调
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ FeegridManager (feegrid-manager.js) │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ 高阶封装层:datagrid + pager + state 的统一管理 │ │
|
||||
│ │ - 初始化整合 │ │
|
||||
│ │ - loading/loaded 状态自动管理 │ │
|
||||
│ │ - 分页切换与选中状态同步 │ │
|
||||
│ │ - 行状态样式自动处理 │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│ 内部调用
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DraftPageManager (draft-page-manager.js) │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ 低阶封装层:草稿池 + 分页 + 选中状态核心逻辑 │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 模块划分
|
||||
|
||||
| 模块 | 职责 | 文件 |
|
||||
| -------------------- | -------------------------------- | ------------------------------ |
|
||||
| **FeegridManager** | 高阶整合层,管理 datagrid + pager + 状态同步 | `js/feegrid-manager.js` |
|
||||
| **DraftPageManager** | 低阶核心层,草稿态分页核心逻辑 | `js/draft-page-manager.js`(现有) |
|
||||
|
||||
***
|
||||
|
||||
## 3. 重构方案
|
||||
|
||||
### 3.1 创建 FeegridManager 模块
|
||||
|
||||
**文件路径**:`js/feegrid-manager.js`
|
||||
|
||||
**核心职责**:
|
||||
|
||||
* 统一管理 datagrid 和 pager 的初始化
|
||||
|
||||
* 自动处理 loading/loaded 状态
|
||||
|
||||
* 封装分页数据加载逻辑
|
||||
|
||||
* 自动同步选中状态
|
||||
|
||||
* 处理行状态样式
|
||||
|
||||
**API 设计**:
|
||||
|
||||
| 方法 | 说明 | 参数 |
|
||||
| ------------------------ | -------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `create(config)` | 创建 FeegridManager 实例 | `config.gridId`, `config.pagerId`, `config.keyField`, `config.pageSize`, `config.callbacks` |
|
||||
| `loadPage(pageNumber)` | 加载指定页码数据 | `pageNumber`: 页码 |
|
||||
| `refreshCurrentPage()` | 刷新当前页 | 无 |
|
||||
| `refreshDisplay()` | 仅刷新当前页显示 | 无 |
|
||||
| `refreshAfterDelete()` | 删除后刷新 | 无 |
|
||||
| `refreshAfterUndo()` | 撤销删除后刷新 | 无 |
|
||||
| `addInsertRows(rows)` | 添加新行到草稿池 | `rows`: 行数据数组 |
|
||||
| `removeInsertRow(row)` | 从草稿池移除行 | `row`: 行数据 |
|
||||
| `addDeleteRow(row)` | 标记行为删除状态 | `row`: 行数据 |
|
||||
| `removeDeleteRow(row)` | 撤销删除标记 | `row`: 行数据 |
|
||||
| `addUpdateRow(row)` | 添加到更新队列 | `row`: 行数据 |
|
||||
| `getValidSelectedRows()` | 获取有效的选中行 | 无 |
|
||||
| `toggleSelectAll()` | 全选/反选切换 | 无 |
|
||||
| `clearSelection()` | 清除选中状态 | 无 |
|
||||
| `setAllChecked(value)` | 设置全选状态 | `value`: boolean |
|
||||
| `reset()` | 重置所有状态 | 无 |
|
||||
|
||||
**配置项结构**:
|
||||
|
||||
```js
|
||||
{
|
||||
gridId: 'feegrid', // datagrid DOM ID
|
||||
pagerId: 'subgridPager', // pagination DOM ID
|
||||
keyField: 'subid', // 主键字段名
|
||||
pageSize: 50, // 每页条数
|
||||
viewName: '', // 查询视图名
|
||||
callbacks: {
|
||||
// 数据加载前回调
|
||||
beforeLoad: function() {},
|
||||
// 数据加载成功回调
|
||||
onLoadSuccess: function(rows) {},
|
||||
// 行编辑完成回调
|
||||
onAfterEdit: function(rowIndex, row, changes) {},
|
||||
// 选中状态变化回调(用于金额计算等)
|
||||
onSelectionChange: function() {}
|
||||
},
|
||||
// 行样式配置
|
||||
rowStateStyles: {
|
||||
insert: 'background-color: #f6ffed;',
|
||||
update: 'background-color: #fffbe6;',
|
||||
delete: 'background-color: #fff1f0;'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 修改业务页面
|
||||
|
||||
**目标**:业务页面仅保留配置和业务回调,移除所有通用逻辑代码。
|
||||
|
||||
**重构后的业务页面结构**:
|
||||
|
||||
```html
|
||||
<script type="text/javascript">
|
||||
// 1. 定义配置
|
||||
var feegridConfig = {
|
||||
gridId: 'feegrid',
|
||||
pagerId: 'subgridPager',
|
||||
keyField: 'subid',
|
||||
pageSize: 50,
|
||||
viewName: 'v_cwzd_orderfee_s',
|
||||
callbacks: {
|
||||
onLoadSuccess: function(rows) {
|
||||
// 业务逻辑:计算金额
|
||||
calcw_selfee();
|
||||
},
|
||||
onAfterEdit: function(rowIndex, row, changes) {
|
||||
// 业务逻辑:重新计算费用
|
||||
calcw_fee();
|
||||
},
|
||||
onSelectionChange: function() {
|
||||
// 业务逻辑:更新选中金额显示
|
||||
calcw_selfee();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 初始化(业务层无需感知内部实现)
|
||||
var feeGrid = createFeegridManager(feegridConfig);
|
||||
|
||||
// 3. 业务操作中调用
|
||||
function saveData() {
|
||||
// 获取草稿数据用于保存
|
||||
var insertRows = feeGrid.getInsertRows();
|
||||
var updateRows = feeGrid.getUpdateRows();
|
||||
var deleteRows = feeGrid.getDeleteRows();
|
||||
// ... 保存逻辑
|
||||
}
|
||||
|
||||
function removeit() {
|
||||
// 删除选中行
|
||||
feeGrid.deleteSelectedRows();
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 4. 具体实施步骤
|
||||
|
||||
### 4.1 步骤一:创建 FeegridManager 模块
|
||||
|
||||
**文件**:`js/feegrid-manager.js`
|
||||
|
||||
封装内容:
|
||||
|
||||
1. datagrid 初始化配置
|
||||
2. pager 初始化配置
|
||||
3. loading/loaded 状态自动管理
|
||||
4. 分页数据加载封装
|
||||
5. 选中状态同步
|
||||
6. 行状态样式处理
|
||||
|
||||
### 4.2 步骤二:修改 DraftPageManager(可选优化)
|
||||
|
||||
* 保持现有 API 稳定
|
||||
|
||||
* 可能需要增加一些内部方法供 FeegridManager 调用
|
||||
|
||||
### 4.3 步骤三:重构业务页面 `editorder_s_cw.jsp`
|
||||
|
||||
**需要移除的代码模式**:
|
||||
|
||||
1. **Loading 状态控制**:
|
||||
|
||||
* `$('#feegrid').datagrid('loading')`
|
||||
|
||||
* `$('#feegrid').datagrid('loaded')`
|
||||
|
||||
* `$('#subgridPager').pagination('loading')`
|
||||
|
||||
* `$('#subgridPager').pagination('loaded')`
|
||||
|
||||
* `$('#subgridPager').css({...})`
|
||||
|
||||
2. **分页数据加载**:
|
||||
|
||||
* `loaddata_sub()` 函数整体移除
|
||||
|
||||
* `refreshCurrentPage()` 函数移除
|
||||
|
||||
* `refreshCurrentPageDisplay()` 函数移除
|
||||
|
||||
* `refreshCurrentPageAfterDelete()` 函数移除
|
||||
|
||||
* `refreshCurrentPageAfterUndo()` 函数移除
|
||||
|
||||
3. **选中状态同步**:
|
||||
|
||||
* `syncFeeGridChecksFromSubState()` 函数移除
|
||||
|
||||
* `pruneSelectedRowsForCurrentPage()` 函数移除
|
||||
|
||||
4. **初始化代码**:
|
||||
|
||||
* `var draftManager = window.DraftPageManager.createDraftManager(...)` 移除
|
||||
|
||||
* datagrid 初始化配置移到 FeegridManager
|
||||
|
||||
* pager 初始化配置移到 FeegridManager
|
||||
|
||||
### 4.4 步骤四:测试验证
|
||||
|
||||
1. 测试基本功能:加载、分页、选中、删除、撤销
|
||||
2. 测试状态同步:分页切换后选中状态保持
|
||||
3. 测试 loading 状态:加载过程中按钮禁用
|
||||
4. 测试行样式:新增/修改/删除行样式正确
|
||||
|
||||
***
|
||||
|
||||
## 5. 代码规范
|
||||
|
||||
### 5.1 命名规范
|
||||
|
||||
| 类型 | 规则 | 示例 |
|
||||
| ---- | -------- | -------------------------------------- |
|
||||
| 模块名 | 小写 + 连字符 | `feegrid-manager.js` |
|
||||
| 函数名 | 驼峰式 | `createFeegridManager`, `loadPageData` |
|
||||
| 变量名 | 驼峰式 | `gridId`, `rowStateStyles` |
|
||||
| 常量名 | 大写 + 下划线 | `DEFAULT_PAGE_SIZE` |
|
||||
| 内部方法 | 下划线开头 | `_handleLoading`, `_syncCheckStates` |
|
||||
|
||||
### 5.2 注释规范
|
||||
|
||||
* 模块开头:说明模块职责和使用方式
|
||||
|
||||
* 函数/方法:说明功能、参数、返回值
|
||||
|
||||
* 关键逻辑:简要说明设计意图
|
||||
|
||||
* 避免冗余注释:不注释显而易见的代码
|
||||
|
||||
### 5.3 文件结构
|
||||
|
||||
```
|
||||
js/
|
||||
├── feegrid-manager.js # 高阶整合模块(新增)
|
||||
├── draft-page-manager.js # 低阶核心模块(现有)
|
||||
└── draft-page-manager.js # 保持不变
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 6. 风险与应对
|
||||
|
||||
### 6.1 风险识别
|
||||
|
||||
| 风险 | 影响 | 应对措施 |
|
||||
| -------------- | -------------- | -------------------------- |
|
||||
| API 变更导致业务代码出错 | 功能异常 | 保持 DraftPageManager API 兼容 |
|
||||
| 状态同步逻辑复杂 | 选中状态错误 | 单元测试覆盖核心场景 |
|
||||
| loading 状态管理不当 | UI 卡顿或按钮可用状态错误 | 统一状态管理机制 |
|
||||
| 浏览器兼容性 | 部分功能不生效 | 使用 jQuery 标准 API |
|
||||
|
||||
### 6.2 测试要点
|
||||
|
||||
1. **分页加载**:远程数据 + 本地插入数据混合展示
|
||||
2. **选中状态**:跨页选中、全选后取消个别、分页切换后状态保持
|
||||
3. **删除/撤销**:标记删除、撤销删除、物理删除
|
||||
4. **loading 状态**:加载过程中按钮禁用、加载完成后恢复
|
||||
5. **行样式**:新增(绿色)、修改(黄色)、删除(红色)样式正确
|
||||
|
||||
***
|
||||
|
||||
## 7. 预期成果
|
||||
|
||||
### 7.1 业务页面变化
|
||||
|
||||
**重构前**:约 800+ 行通用逻辑代码(loaddata\_sub、refresh 系列函数等)
|
||||
|
||||
**重构后**:仅保留配置项和业务回调(约 50 行)
|
||||
|
||||
### 7.2 代码对比
|
||||
|
||||
**重构前业务代码**:
|
||||
|
||||
```js
|
||||
function loaddata_sub() {
|
||||
$('#feegrid').datagrid('loading');
|
||||
$('#subgridPager').css({ 'pointer-events': 'none', 'opacity': '0.6' });
|
||||
$('#subgridPager').pagination('loading');
|
||||
|
||||
var pagination = draftManager.getPagination();
|
||||
// ... 大量分页逻辑
|
||||
|
||||
$.ajax({
|
||||
// ...
|
||||
complete: function() {
|
||||
$('#feegrid').datagrid('loaded');
|
||||
$('#subgridPager').css({ 'pointer-events': '', 'opacity': '' });
|
||||
$('#subgridPager').pagination('loaded');
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**重构后业务代码**:
|
||||
|
||||
```js
|
||||
// 配置
|
||||
var feeGrid = createFeegridManager({
|
||||
gridId: 'feegrid',
|
||||
|
||||
viewName: 'v_cwzd_orderfee_s',
|
||||
onLoadSuccess: calcw_selfee
|
||||
});
|
||||
|
||||
// 使用
|
||||
feeGrid.loadPage(1); // 加载第一页
|
||||
feeGrid.refreshCurrentPage(); // 刷新当前页
|
||||
```
|
||||
|
||||
### 7.3 复用性提升
|
||||
|
||||
重构后,其他页面如需使用类似功能,只需:
|
||||
|
||||
1. 引入 `feegrid-manager.js`
|
||||
2. 配置 gridId、pagerId、viewName
|
||||
3. 设置业务回调
|
||||
|
||||
无需重复编写分页、选中、loading 等通用逻辑。
|
||||
@@ -0,0 +1,162 @@
|
||||
# Feegrid 模块重构计划
|
||||
|
||||
## 1. 修改内容
|
||||
|
||||
### 1.1 JS 文件修改
|
||||
|
||||
| 修改项 | 原内容 | 新内容 | 说明 |
|
||||
|--------|--------|--------|------|
|
||||
| 函数名 | `createFeegridManager` | `createDataGridManager` | 更通用 |
|
||||
| callbacks 配置 | `callbacks: { onLoadSuccess, onAfterEdit, onSelectionChange }` | 直接展开 `onLoadSuccess, onAfterEdit, onSelectionChange` | 简化配置 |
|
||||
| pagerId 配置 | `pagerId: 'subgridPager'` | 移除 | 自动创建,无需配置 |
|
||||
| 全部选中/反选 | JSP 中有 wrapper 函数 | JS 中自动管理按钮文字 | 进一步解耦 |
|
||||
|
||||
### 1.2 JSP 文件修改
|
||||
|
||||
| 修改项 | 说明 |
|
||||
|--------|------|
|
||||
| 删除 `subgridPager` div | 由 JS 自动创建 |
|
||||
| 简化 `toggleSelectAll` 函数 | 直接调用 JS 方法 |
|
||||
| 变量名可保持或改为更通用的名字 | 可选 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 重构后的 API
|
||||
|
||||
```js
|
||||
// 之前
|
||||
var feeGrid = createFeegridManager({
|
||||
gridId: 'feegrid',
|
||||
pagerId: 'subgridPager',
|
||||
keyField: 'subid',
|
||||
pageSize: 50,
|
||||
viewName: subviewtable,
|
||||
callbacks: {
|
||||
onLoadSuccess: function(rows) { calcw_selfee(); },
|
||||
onAfterEdit: function(rowIndex, row, changes) { calcw_fee(); },
|
||||
onSelectionChange: function() { calcw_selfee(); }
|
||||
}
|
||||
});
|
||||
|
||||
// 之后
|
||||
var gridManager = createDataGridManager({
|
||||
gridId: 'feegrid',
|
||||
keyField: 'subid',
|
||||
pageSize: 50,
|
||||
viewName: subviewtable,
|
||||
searchUrl: 'jsp/searchdatapagenew.jsp',
|
||||
orderField: 'father ASC, mx_xh ASC, b_id ASC',
|
||||
onLoadSuccess: function(rows) { calcw_selfee(); },
|
||||
onAfterEdit: function(rowIndex, row, changes) { calcw_fee(); },
|
||||
onSelectionChange: function() { calcw_selfee(); }
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. JSP 中需要修改的部分
|
||||
|
||||
### 3.1 删除的分页器 HTML(自动创建)
|
||||
```html
|
||||
<!-- 删除这行 -->
|
||||
<div id="subgridPager" class="easyui-pagination" style="margin: 6px 0px;background:#efefef;border:1px solid #ccc;"></div>
|
||||
```
|
||||
|
||||
### 3.2 简化的 `toggleSelectAll` 函数
|
||||
```js
|
||||
// 之前:复杂的判断逻辑
|
||||
function toggleSelectAll() {
|
||||
if (feeGridManager.getDraftManager().isAllChecked()) {
|
||||
feeGridManager.toggleSelectAll();
|
||||
$('#btn_select_all').val('全部选中');
|
||||
} else {
|
||||
feeGridManager.toggleSelectAll();
|
||||
$('#btn_select_all').val('全部反选');
|
||||
}
|
||||
}
|
||||
|
||||
// 之后:直接调用
|
||||
function toggleSelectAll() {
|
||||
gridManager.toggleSelectAll();
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 更新的初始化代码
|
||||
```js
|
||||
// 之前
|
||||
feeGridManager = createFeegridManager({
|
||||
gridId: 'feegrid',
|
||||
pagerId: 'subgridPager',
|
||||
keyField: 'subid',
|
||||
pageSize: 50,
|
||||
viewName: subviewtable,
|
||||
callbacks: {
|
||||
onLoadSuccess: function(rows) { calcw_selfee(); },
|
||||
onAfterEdit: function(rowIndex, row, changes) { calcw_fee(); },
|
||||
onSelectionChange: function() { calcw_selfee(); }
|
||||
}
|
||||
});
|
||||
|
||||
// 之后
|
||||
gridManager = createDataGridManager({
|
||||
gridId: 'feegrid',
|
||||
keyField: 'subid',
|
||||
pageSize: 50,
|
||||
viewName: subviewtable,
|
||||
orderField: 'father ASC, mx_xh ASC, b_id ASC',
|
||||
onLoadSuccess: function(rows) { calcw_selfee(); },
|
||||
onAfterEdit: function(rowIndex, row, changes) { calcw_fee(); },
|
||||
onSelectionChange: function() { calcw_selfee(); }
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 检查 JSP 中未使用的代码
|
||||
|
||||
| 函数/变量 | 状态 | 说明 |
|
||||
|-----------|------|------|
|
||||
| `subdata_zdfees` | 未使用 | 可以删除 |
|
||||
| `feegridjson` | 未使用 | 可以删除 |
|
||||
| `loaddata_subdata_zdfees` | 未使用 | 可以删除 |
|
||||
| `loaddata_detail` | 部分使用 | 在 `newSKData`/`newIVData` 的 onClose 中调用 |
|
||||
| `removeit_all` | 注释掉 | 已废弃 |
|
||||
| `getSelections` | 未使用 | 可以删除 |
|
||||
|
||||
---
|
||||
|
||||
## 5. searchCondition 功能
|
||||
|
||||
代码中已有 `searchCondition` 配置项和 `setSearchCondition` 方法,JSP 中通过 `setMasterId` 设置主表ID后,会自动生成 `mx_zdorderid='xxx'` 的查询条件。
|
||||
|
||||
如需自定义查询条件,可使用:
|
||||
```js
|
||||
gridManager.setSearchCondition("mx_dw_id='xxx'");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 实施步骤
|
||||
|
||||
1. 修改 `feegrid-manager.js`:
|
||||
- 重命名函数为 `createDataGridManager`
|
||||
- 移除 `callbacks` 包装,直接展开回调配置
|
||||
- 移除 `pagerId` 配置
|
||||
- 完善 `toggleSelectAll` 自动管理按钮文字
|
||||
- 更新默认值配置
|
||||
|
||||
2. 修改 `editorder_s_cw.jsp`:
|
||||
- 删除 `subgridPager` div
|
||||
- 简化 `toggleSelectAll` 函数
|
||||
- 更新初始化代码
|
||||
- 删除未使用的变量和函数
|
||||
- 变量名改为更通用的 `gridManager`
|
||||
|
||||
---
|
||||
|
||||
## 7. 预期结果
|
||||
|
||||
- **API 简化**:从 `createFeegridManager({ callbacks: {...} })` 变为 `createDataGridManager({ onLoadSuccess, onAfterEdit, onSelectionChange })`
|
||||
- **配置减少**:移除 `pagerId`,无需在 HTML 中定义分页器
|
||||
- **代码清理**:删除未使用的函数和变量
|
||||
- **更好的复用性**:通用名称可在其他页面直接使用
|
||||
@@ -0,0 +1,155 @@
|
||||
# Feegrid 模块合并重构计划
|
||||
|
||||
## 1. 现状分析
|
||||
|
||||
### 1.1 当前文件结构
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `js/draft-page-manager.js` | 439行 | 草稿态分页核心模块(底层) |
|
||||
| `js/feegrid-manager.js` | 772行 | Feegrid 高阶管理模块(依赖上层) |
|
||||
|
||||
**问题**:两个文件分离,DraftPageManager 被 FeegridManager 依赖,但业务页面需要同时引入两个文件,增加了复杂性。
|
||||
|
||||
### 1.2 用户需求
|
||||
|
||||
- 将公共代码统一到一个 JS 文件中
|
||||
- 不拆分多个 JS 文件
|
||||
- 代码放在一起方便维护
|
||||
- 保持单一文件的简单结构
|
||||
|
||||
---
|
||||
|
||||
## 2. 重构目标
|
||||
|
||||
### 2.1 合并方案
|
||||
|
||||
将 `draft-page-manager.js` 和 `feegrid-manager.js` 合并为一个文件 `js/feegrid-manager.js`。
|
||||
|
||||
合并后文件结构:
|
||||
```
|
||||
js/feegrid-manager.js
|
||||
├── DraftPageManager (内部模块,对外不可见)
|
||||
│ ├── 状态管理
|
||||
│ ├── 草稿池操作
|
||||
│ ├── 选中状态操作
|
||||
│ ├── 分页操作
|
||||
│ └── 工具方法
|
||||
│
|
||||
└── createFeegridManager (导出函数)
|
||||
├── 初始化方法
|
||||
├── 数据加载方法
|
||||
├── 草稿池操作方法
|
||||
├── 选中状态方法
|
||||
└── 配置方法
|
||||
```
|
||||
|
||||
### 2.2 优势
|
||||
|
||||
1. **单一文件**:只需引入一个 JS 文件
|
||||
2. **内聚性**:DraftPageManager 作为内部实现细节,不暴露给业务代码
|
||||
3. **简化引用**:业务页面只需 `createFeegridManager`
|
||||
4. **命名空间干净**:不再污染 window.DraftPageManager
|
||||
|
||||
---
|
||||
|
||||
## 3. 重构步骤
|
||||
|
||||
### 3.1 步骤一:合并文件
|
||||
|
||||
将 `draft-page-manager.js` 的内容作为 `js/feegrid-manager.js` 的内部模块,移除 `window.DraftPageManager` 挂载。
|
||||
|
||||
### 3.2 步骤二:修改 DraftPageManager 引用
|
||||
|
||||
将 `feegrid-manager.js` 中所有 `window.DraftPageManager.createDraftManager` 调用改为内部引用。
|
||||
|
||||
### 3.3 步骤三:清理导出
|
||||
|
||||
移除 `window.createFeegridManager` 的 UMD 封装,改为直接 `window.createFeegridManager = createFeegridManager`。
|
||||
|
||||
### 3.4 步骤四:更新 JSP 引用
|
||||
|
||||
将 JSP 页面中的引用从两个文件改为单一文件:
|
||||
```html
|
||||
<!-- 之前 -->
|
||||
<script src="js/draft-page-manager.js"></script>
|
||||
<script src="js/feegrid-manager.js"></script>
|
||||
|
||||
<!-- 之后 -->
|
||||
<script src="js/feegrid-manager.js"></script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 文件结构设计
|
||||
|
||||
### 4.1 合并后的文件
|
||||
|
||||
文件路径:`js/feegrid-manager.js`
|
||||
|
||||
```js
|
||||
/**
|
||||
* Feegrid 公共管理模块
|
||||
*
|
||||
* 包含:
|
||||
* 1. DraftPageManager - 草稿态分页核心(内部模块)
|
||||
* 2. createFeegridManager - Feegrid 高阶管理器(导出)
|
||||
*/
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
// ==================== DraftPageManager(内部模块)====================
|
||||
|
||||
function createDraftManager(config) {
|
||||
// ... draft-page-manager.js 的全部逻辑
|
||||
}
|
||||
|
||||
// ==================== createFeegridManager(导出)====================
|
||||
|
||||
function createFeegridManager(config) {
|
||||
// ... feegrid-manager.js 的全部逻辑
|
||||
// 内部调用 createDraftManager 而非 window.DraftPageManager
|
||||
}
|
||||
|
||||
// 导出到 window
|
||||
window.createFeegridManager = createFeegridManager;
|
||||
|
||||
})(window);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 预期成果
|
||||
|
||||
### 5.1 文件变化
|
||||
|
||||
| 操作 | 文件 |
|
||||
|------|------|
|
||||
| 删除 | `js/draft-page-manager.js` |
|
||||
| 合并+替换 | `js/feegrid-manager.js` |
|
||||
| 更新引用 | `editorder_s_cw.jsp` |
|
||||
|
||||
### 5.2 引用简化
|
||||
|
||||
**之前(JSP 中)**:
|
||||
```html
|
||||
<script src="js/draft-page-manager.js"></script>
|
||||
<script src="js/feegrid-manager.js"></script>
|
||||
```
|
||||
|
||||
**之后(JSP 中)**:
|
||||
```html
|
||||
<script src="js/feegrid-manager.js"></script>
|
||||
```
|
||||
|
||||
### 5.3 业务代码不变
|
||||
|
||||
`createFeegridManager` 的 API 保持不变,业务代码无需修改。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与注意事项
|
||||
|
||||
1. **保持 API 兼容**:合并后 `createFeegridManager` 的接口不变
|
||||
2. **删除源文件**:确认合并成功后删除 `draft-page-manager.js`
|
||||
3. **测试验证**:合并后需要测试分页、选中、删除、撤销等功能
|
||||
@@ -0,0 +1,225 @@
|
||||
# 搜索场景下本地数据处理方案
|
||||
|
||||
## 场景分析
|
||||
|
||||
### 场景一:新增数据后的搜索
|
||||
|
||||
**问题**:用户新增了10条数据(仅在本地,未保存到数据库),执行 LIKE 搜索时,这些本地数据无法被搜索到。
|
||||
|
||||
**期望**:搜索结果应包含本地新增的行,且支持 LIKE 模糊匹配。
|
||||
|
||||
### 场景二:更新数据后的搜索
|
||||
|
||||
**问题**:用户更新了10条数据(本地已修改),执行搜索时,远程返回的是旧数据,本地修改的数据可能不符合搜索条件。
|
||||
|
||||
**期望**:搜索结果应使用本地更新后的数据进行匹配,而不是远程的旧数据。
|
||||
|
||||
***
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方案核心思路
|
||||
|
||||
1. **本地数据也参与搜索过滤**:在搜索时,对本地的 `insertRows` 和 `updateRows` 也应用搜索条件过滤
|
||||
2. **合并展示**:将远程搜索结果与本地过滤后的结果合并展示
|
||||
3. **保持状态**:保留本地数据的 `_rowState` 标记
|
||||
|
||||
### 修改内容
|
||||
|
||||
#### 1. JS 文件修改 (`js/feegrid-manager.js`)
|
||||
|
||||
**新增方法:**
|
||||
|
||||
* `_filterLocalRows(condition)` - 根据搜索条件过滤本地数据
|
||||
|
||||
* 修改 `loadPage()` - 在加载远程数据后,合并本地过滤后的数据
|
||||
|
||||
**搜索条件解析**:
|
||||
|
||||
* 支持 `LIKE '%xxx%'` 模糊匹配
|
||||
|
||||
* 支持 `=`, `!=`, `<`, `>`, `<=`, `>=` 比较
|
||||
|
||||
* 支持 `AND`, `OR` 逻辑(简化版)
|
||||
|
||||
#### 2. 修改 `loadPage()` 方法
|
||||
|
||||
```js
|
||||
function loadPage(pageNumber) {
|
||||
_setLoading();
|
||||
|
||||
var pagination = state.draftManager.getPagination();
|
||||
var pageSize = pagination.pageSize;
|
||||
var remoteTotal = pagination.remoteTotal;
|
||||
var insertRows = state.draftManager.getInsertRows();
|
||||
var updateRows = state.draftManager.getUpdateRows();
|
||||
|
||||
// 获取搜索条件
|
||||
var searchCondition = options.searchCondition ||
|
||||
options.keyField.replace('subid', 'mx_') + "='" + state.masterId + "'";
|
||||
|
||||
// 过滤本地数据
|
||||
var filteredInserts = _filterLocalRows(insertRows, searchCondition);
|
||||
var filteredUpdates = _filterLocalRows(updateRows, searchCondition);
|
||||
|
||||
// ... 原有远程加载逻辑 ...
|
||||
|
||||
// 合并结果时,使用本地过滤后的数据
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 新增条件解析工具函数
|
||||
|
||||
```js
|
||||
function _filterLocalRows(rows, condition) {
|
||||
if (!condition || condition.trim() === '') {
|
||||
return rows;
|
||||
}
|
||||
|
||||
var filtered = [];
|
||||
var conditions = _parseCondition(condition);
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
if (_matchesConditions(row, conditions)) {
|
||||
filtered.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
function _parseCondition(condition) {
|
||||
var result = [];
|
||||
|
||||
var parts = condition.split(/\s+(AND|OR)\s+/i);
|
||||
for (var i = 0; i < parts.length; i += 2) {
|
||||
var expr = parts[i].trim();
|
||||
var operator = parts[i + 1] ? parts[i + 1].toUpperCase() : 'AND';
|
||||
|
||||
var match;
|
||||
|
||||
// LIKE 匹配
|
||||
if ((match = expr.match(/^\s*(\w+)\s+LIKE\s+['"](.*)['"]\s*$/i))) {
|
||||
result.push({
|
||||
field: match[1],
|
||||
operator: 'LIKE',
|
||||
value: match[2],
|
||||
logic: operator
|
||||
});
|
||||
}
|
||||
// 等于匹配
|
||||
else if ((match = expr.match(/^\s*(\w+)\s*=\s*['"](.*)['"]\s*$/))) {
|
||||
result.push({
|
||||
field: match[1],
|
||||
operator: '=',
|
||||
value: match[2],
|
||||
logic: operator
|
||||
});
|
||||
}
|
||||
// 不等于匹配
|
||||
else if ((match = expr.match(/^\s*(\w+)\s*(!=|<>)=\s*['"](.*)['"]\s*$/))) {
|
||||
result.push({
|
||||
field: match[1],
|
||||
operator: '!=',
|
||||
value: match[2],
|
||||
logic: operator
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function _matchesConditions(row, conditions) {
|
||||
if (conditions.length === 0) return true;
|
||||
|
||||
var result = true;
|
||||
|
||||
for (var i = 0; i < conditions.length; i++) {
|
||||
var cond = conditions[i];
|
||||
var fieldValue = row[cond.field];
|
||||
|
||||
if (fieldValue === undefined || fieldValue === null) {
|
||||
fieldValue = '';
|
||||
}
|
||||
|
||||
var fieldStr = String(fieldValue).toLowerCase();
|
||||
var valueStr = String(cond.value).toLowerCase();
|
||||
var matches = false;
|
||||
|
||||
switch (cond.operator) {
|
||||
case 'LIKE':
|
||||
var pattern = valueStr
|
||||
.replace(/%/g, '.*')
|
||||
.replace(/_/g, '.');
|
||||
matches = new RegExp('^' + pattern + '$').test(fieldStr);
|
||||
break;
|
||||
|
||||
case '=':
|
||||
matches = fieldStr === valueStr;
|
||||
break;
|
||||
|
||||
case '!=':
|
||||
matches = fieldStr !== valueStr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cond.logic === 'AND') {
|
||||
result = result && matches;
|
||||
} else {
|
||||
result = result || matches;
|
||||
}
|
||||
|
||||
if (!result && cond.logic === 'AND') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 实施步骤
|
||||
|
||||
1. **修改** **`js/feegrid-manager.js`**:
|
||||
|
||||
* 新增 `_filterLocalRows()` 函数
|
||||
|
||||
* 新增 `_parseCondition()` 函数
|
||||
|
||||
* 新增 `_matchesConditions()` 函数
|
||||
|
||||
* 修改 `loadPage()` 方法,在加载时过滤本地数据
|
||||
|
||||
2. **修改** **`reload()`** **方法**:
|
||||
|
||||
* 确保 `reload()` 时也应用本地数据过滤
|
||||
|
||||
3. **测试验证**:
|
||||
|
||||
* 测试新增数据后的 LIKE 搜索
|
||||
|
||||
* 测试更新数据后的搜索匹配
|
||||
|
||||
***
|
||||
|
||||
## 预期效果
|
||||
|
||||
| 场景 | 操作 | 预期结果 |
|
||||
| --- | ------------------------- | -------------------------------- |
|
||||
| 场景一 | 新增10条数据 → 搜索 LIKE '%xxx%' | 本地新增且匹配的行会显示在搜索结果中 |
|
||||
| 场景二 | 更新10条数据 → 搜索更新后的字段 | 使用本地更新后的数据进行匹配 |
|
||||
| 场景三 | 删除数据后搜索 | 已标记删除的行仍然出现在搜索结果中,但是status显示删除即可 |
|
||||
|
||||
***
|
||||
|
||||
## 风险与注意事项
|
||||
|
||||
1. **条件解析复杂度**:只支持简单的 LIKE、=、!= 操作符,复杂条件可能解析失败
|
||||
2. **性能考虑**:本地数据量大时,过滤可能有性能问题(建议限制本地数据量)
|
||||
3. **日期格式**:日期字段的比较需要注意格式统一
|
||||
4. **多表关联**:不支持跨表字段的搜索条件
|
||||
|
||||
+68
-436
@@ -17,7 +17,7 @@
|
||||
<script type="text/javascript" src="js/datagrid-drag/datagrid-dnd.js"></script>
|
||||
<script type="text/javascript" src="js/datagrid-drag/datagrid-head.js"></script>
|
||||
<script type="text/javascript" src="jsys/chinesetopy.js"></script>
|
||||
<script type="text/javascript" src="js/draft-page-manager.js"></script>
|
||||
<script type="text/javascript" src="js/feegrid-manager.js"></script>
|
||||
<style>
|
||||
.g3-label-width {
|
||||
width: 80px;
|
||||
@@ -230,8 +230,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="subgridPager" class="easyui-pagination"
|
||||
style="margin: 6px 0px;background:#efefef;border:1px solid #ccc;"></div>
|
||||
|
||||
|
||||
<div class="g3-form-row" style="height: 30px; width: 99%; display: flex; align-items: center; gap: 20px;">
|
||||
<div class="g3-form-col" id="ed_div_total" style="flex: 1; min-width: 200px;">
|
||||
@@ -280,36 +279,14 @@
|
||||
var subdata_zdfees = new Array();
|
||||
var mainData = {};
|
||||
|
||||
/**
|
||||
* 子表草稿态 + 分页管理模块
|
||||
* 使用独立模块 draft-page-manager.js 管理
|
||||
*
|
||||
* 职责:
|
||||
* - 草稿池:insertRows / updateRows / deleteRows(保存提交用)
|
||||
* - 行展示态:行对象上的 _rowState(insert | update | delete | '')
|
||||
* - 分页:remoteTotal(服务端)+ insertRows 混排
|
||||
* - UI 勾选:selectedRows 唯一真相源(跨页)
|
||||
*/
|
||||
var draftManager = window.DraftPageManager.createDraftManager({
|
||||
keyField: 'subid',
|
||||
pageSize: 50
|
||||
});
|
||||
|
||||
/**
|
||||
* 修剪当前页选中状态(配合 clearChecked 使用)
|
||||
*/
|
||||
function pruneSelectedRowsForCurrentPage() {
|
||||
var rows = $('#feegrid').datagrid('getRows');
|
||||
draftManager.removeSelectionForRows(rows);
|
||||
}
|
||||
|
||||
let subSummary = {
|
||||
// 分币种
|
||||
moneyTypes: [],
|
||||
// 折合总金额
|
||||
totalAmount: 0
|
||||
};
|
||||
|
||||
// DataGrid 管理器实例
|
||||
var gridManager;
|
||||
|
||||
$(function () {
|
||||
SetEnterFocus();
|
||||
|
||||
@@ -389,8 +366,7 @@
|
||||
// configColumn('feegrid',subviewtable,'local');
|
||||
if (strid != "") {
|
||||
loaddata(strid);
|
||||
// loaddata_detail('feegrid', subviewtable, strid, '');
|
||||
loaddata_sub()
|
||||
gridManager.loadPage(1);
|
||||
calcw_fee();
|
||||
} else {
|
||||
console.log("走到了这里");
|
||||
@@ -494,148 +470,34 @@
|
||||
field: 'bcheck'
|
||||
});
|
||||
|
||||
$('#feegrid').datagrid({
|
||||
idField: 'subid',
|
||||
fit: true,
|
||||
remoteSort: false,
|
||||
multiSort: true,
|
||||
//multiSort :true,
|
||||
singleSelect: true,
|
||||
//ctrlSelect:true,
|
||||
selectOnCheck: false,
|
||||
checkOnSelect: false,
|
||||
rownumbers: true,
|
||||
// onDblClickRow:function(rowIndex, rowData){openDatayw(rowData);},
|
||||
//onClickRow : onClickRow_con,
|
||||
// onBeginEdit:onBeginEdit_con,
|
||||
toolbar: '#tb_con',
|
||||
columns: [columnfield],
|
||||
// pagination : false,
|
||||
// 创建 DataGridManager(自动初始化 datagrid 和分页器)
|
||||
gridManager = createDataGridManager({
|
||||
gridId: 'feegrid',
|
||||
keyField: 'subid',
|
||||
pageSize: 50,
|
||||
autoRowHeight: false,
|
||||
//pageList:[50,100,200,500],
|
||||
LoadSelkeyID: undefined,
|
||||
rowStyler: function (index, row) {
|
||||
if (row._rowState === 'insert') {
|
||||
return 'background-color: #f6ffed;';
|
||||
} else if (row._rowState === 'update') {
|
||||
return 'background-color: #fffbe6;';
|
||||
} else if (row._rowState === 'delete') {
|
||||
return 'background-color: #fff1f0;';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
onLoadSuccess: function (data) {
|
||||
$.messager.progress('close');
|
||||
if ($('#feegrid').datagrid('options').LoadSelkeyID != undefined) {
|
||||
var rows = $('#feegrid').datagrid("getRows");
|
||||
if (rows.length > 0) {
|
||||
$('#b_dw_id').val(rows[0].mx_dw_id);
|
||||
$('#v_dwname').val(rows[0].v_mx_dw_name);
|
||||
if (rows[0].b_mycompany_id != undefined && rows[0].b_mycompany_id != '') {
|
||||
$('#b_mycompany_id').val(rows[0].b_mycompany_id);
|
||||
}
|
||||
if (rows[0].v_mycompanyname != undefined && rows[0].v_mycompanyname != '') {
|
||||
$('#v_mycompanyname').val(rows[0].v_mycompanyname);
|
||||
}
|
||||
}
|
||||
$('#feegrid').datagrid('options').LoadSelkeyID = undefined;
|
||||
}
|
||||
$('.datagrid-body tr').each(function () {
|
||||
var rowIndex = $(this).attr('datagrid-row-index');
|
||||
if (rowIndex !== undefined) {
|
||||
var row = $('#feegrid').datagrid('getRows')[parseInt(rowIndex)];
|
||||
if (row && row._rowState === 'delete') {
|
||||
$(this).addClass('datagrid-row-delete');
|
||||
} else {
|
||||
$(this).removeClass('datagrid-row-delete');
|
||||
}
|
||||
}
|
||||
});
|
||||
syncFeeGridChecksFromSubState();
|
||||
viewName: subviewtable,
|
||||
orderField: 'father ASC, mx_xh ASC, b_id ASC',
|
||||
baseSearchCondition: "mx_zdorderid='" + strid + "'",
|
||||
selectAllBtn: '#btn_select_all',
|
||||
selectAllText: '全部选中',
|
||||
unselectAllText: '全部反选',
|
||||
userid: '',
|
||||
onLoadSuccess: function (rows) {
|
||||
calcw_selfee();
|
||||
},
|
||||
onAfterEdit: function (rowIndex, row, changes) {
|
||||
if (Object.keys(changes).length === 0) {
|
||||
return;
|
||||
}
|
||||
if (draftManager.isInsertRow(row) || draftManager.isInInsertRows(row)) {
|
||||
if (!row._rowState) {
|
||||
row._rowState = 'insert';
|
||||
}
|
||||
} else if (!draftManager.isDeleteRow(row)) {
|
||||
row._rowState = 'update';
|
||||
draftManager.addToUpdateRows(row);
|
||||
}
|
||||
calcw_fee();
|
||||
},
|
||||
onCheckAll: function () {
|
||||
draftManager.setAllChecked(true);
|
||||
draftManager.clearExcludedRows();
|
||||
calcw_selfee()
|
||||
},
|
||||
onUncheckAll: function () {
|
||||
draftManager.setAllChecked(false);
|
||||
draftManager.clearExcludedRows();
|
||||
draftManager.clearSelectedRows();
|
||||
calcw_selfee()
|
||||
},
|
||||
onCheck: function (index, row) {
|
||||
if (draftManager.isAllChecked()) {
|
||||
draftManager.removeExcludedRow(row);
|
||||
} else {
|
||||
draftManager.addSelectedRow(row);
|
||||
}
|
||||
calcw_selfee()
|
||||
},
|
||||
onUncheck: function (index, row) {
|
||||
if (draftManager.isAllChecked()) {
|
||||
draftManager.addExcludedRow(row);
|
||||
} else {
|
||||
draftManager.removeSelectedRow(row);
|
||||
}
|
||||
calcw_selfee()
|
||||
}
|
||||
}).datagrid("columnMoving");
|
||||
|
||||
$('#feegrid').datagrid('getPanel').find('.datagrid-body').on('scroll.datagrid', function (e) {
|
||||
if (editIndex_con != undefined) {
|
||||
$('#feegrid').datagrid('endEdit', editIndex_con);
|
||||
editIndex_con = undefined;
|
||||
}
|
||||
})
|
||||
|
||||
$('#subgridPager').pagination({
|
||||
total: draftManager.getPagination().total,
|
||||
pageSize: draftManager.getPagination().pageSize,
|
||||
pageNumber: draftManager.getPagination().pageNumber,
|
||||
|
||||
pageList: [50, 100, 200],
|
||||
|
||||
onSelectPage: function (pageNumber, pageSize) {
|
||||
draftManager.setPageNumber(pageNumber);
|
||||
draftManager.setPageSize(pageSize);
|
||||
|
||||
loaddata_sub();
|
||||
},
|
||||
onChangePageSize: function (pageSize) {
|
||||
draftManager.setPageNumber(1);
|
||||
draftManager.setPageSize(pageSize);
|
||||
|
||||
if (!draftManager.isAllChecked()) {
|
||||
$('#feegrid').datagrid('clearChecked');
|
||||
draftManager.clearSelectedRows();
|
||||
}
|
||||
|
||||
calcw_selfee()
|
||||
|
||||
loaddata_sub();
|
||||
onSelectionChange: function () {
|
||||
calcw_selfee();
|
||||
}
|
||||
});
|
||||
|
||||
// configColumn('feegrid',subviewtable,'local');
|
||||
gridManager.setColumns(columnfield);
|
||||
}
|
||||
|
||||
// configColumn('feegrid',subviewtable,'local');
|
||||
|
||||
function openData(id, hstype) {
|
||||
var e = window.event || e;
|
||||
if (e.stopPropagation) {
|
||||
@@ -649,227 +511,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 子表:分页请求、与 insertRows 合并、行态恢复、刷新 ==========
|
||||
|
||||
// 拉取当前页:纯 insert 尾段走本地切片;否则 Ajax 后 merge 尾段 insert
|
||||
function loaddata_sub() {
|
||||
$('#feegrid').datagrid('loading');
|
||||
$('#subgridPager').css({
|
||||
'pointer-events': 'none',
|
||||
'opacity': '0.6'
|
||||
});
|
||||
$('#subgridPager').pagination('loading');
|
||||
|
||||
var pagination = draftManager.getPagination();
|
||||
var pageNumber = pagination.pageNumber;
|
||||
var pageSize = pagination.pageSize;
|
||||
var remoteTotal = pagination.remoteTotal;
|
||||
var insertRows = draftManager.getInsertRows();
|
||||
|
||||
if (!strid) {
|
||||
var pageData = [];
|
||||
if (insertRows.length > 0) {
|
||||
var startInInsert = (pageNumber - 1) * pageSize;
|
||||
var endInInsert = startInInsert + pageSize;
|
||||
pageData = insertRows.slice(startInInsert, endInInsert);
|
||||
}
|
||||
|
||||
$('#feegrid').datagrid('loadData', pageData);
|
||||
|
||||
$('#subgridPager').pagination('refresh', {
|
||||
total: insertRows.length,
|
||||
pageNumber: pageNumber,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
$('#feegrid').datagrid('loaded');
|
||||
$('#subgridPager').css({
|
||||
'pointer-events': '',
|
||||
'opacity': ''
|
||||
});
|
||||
$('#subgridPager').pagination('loaded');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteTotal > 0 && draftManager.isInsertOnlyPage(pageNumber)) {
|
||||
var startInInsert = (pageNumber - 1) * pageSize - remoteTotal;
|
||||
var endInInsert = startInInsert + pageSize;
|
||||
var pageData = insertRows.slice(startInInsert, endInInsert);
|
||||
|
||||
$('#feegrid').datagrid('loadData', pageData);
|
||||
|
||||
$('#subgridPager').pagination('refresh', {
|
||||
total: draftManager.getActualTotal(),
|
||||
pageNumber: pageNumber,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
$('#feegrid').datagrid('loaded');
|
||||
$('#subgridPager').css({
|
||||
'pointer-events': '',
|
||||
'opacity': ''
|
||||
});
|
||||
$('#subgridPager').pagination('loaded');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var searchcondition = "mx_zdorderid='" + strid + "'";
|
||||
var searchparam = {
|
||||
viewname: subviewtable,
|
||||
searchcondition: searchcondition,
|
||||
userid: '',
|
||||
orderfield: 'father ASC, mx_xh ASC, b_id ASC',
|
||||
keyfield: 'subid',
|
||||
page: pagination.pageNumber,
|
||||
rows: pagination.pageSize
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: 'jsp/searchdatapagenew.jsp',
|
||||
cache: false,
|
||||
data: searchparam,
|
||||
dataType: 'json',
|
||||
success: function (result) {
|
||||
draftManager.setRemoteTotal(result.total);
|
||||
draftManager.setCurrentRemoteRows(result.rows);
|
||||
|
||||
draftManager.restoreRowStates(result.rows);
|
||||
|
||||
var pageData = draftManager.buildPageData(result.rows, pageNumber);
|
||||
|
||||
$('#feegrid').datagrid('loadData', pageData);
|
||||
|
||||
var newPagination = draftManager.getPagination();
|
||||
$('#subgridPager').pagination('refresh', {
|
||||
total: newPagination.total,
|
||||
pageNumber: newPagination.pageNumber,
|
||||
pageSize: newPagination.pageSize
|
||||
});
|
||||
},
|
||||
error: function () {
|
||||
console.log('分页加载失败');
|
||||
},
|
||||
complete: function () {
|
||||
$('#feegrid').datagrid('loaded');
|
||||
$('#subgridPager').css({
|
||||
'pointer-events': '',
|
||||
'opacity': ''
|
||||
});
|
||||
$('#subgridPager').pagination('loaded');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// buildCurrentPageData 和 restoreRowStates 已移至 draft-page-manager.js 模块中
|
||||
|
||||
// 新增费用等后:若页码越界则纠页,否则重载
|
||||
function refreshCurrentPage() {
|
||||
var currentPage = draftManager.getPagination().pageNumber;
|
||||
var totalPages = draftManager.getTotalPages();
|
||||
|
||||
if (currentPage > totalPages) {
|
||||
draftManager.setPageNumber(totalPages);
|
||||
$('#subgridPager').pagination('select', totalPages);
|
||||
} else {
|
||||
loaddata_sub();
|
||||
}
|
||||
}
|
||||
|
||||
// 仅重绘当前内存行(保存行软删后),会 clearChecked,并同步修剪 selectedRows
|
||||
function refreshCurrentPageDisplay() {
|
||||
var currentRows = $('#feegrid').datagrid('getRows');
|
||||
$('#feegrid').datagrid('loadData', currentRows);
|
||||
$('#feegrid').datagrid('clearChecked');
|
||||
pruneSelectedRowsForCurrentPage();
|
||||
calcw_selfee();
|
||||
}
|
||||
|
||||
// 撤销删除后:重绘并恢复勾选与业务侧选中金额
|
||||
function refreshCurrentPageAfterUndo() {
|
||||
var currentRows = $('#feegrid').datagrid('getRows');
|
||||
$('#feegrid').datagrid('loadData', currentRows);
|
||||
syncFeeGridChecksFromSubState();
|
||||
calcw_selfee();
|
||||
}
|
||||
|
||||
// 物理删除 insert 行等后:按展示条数纠正页码并重载
|
||||
function refreshCurrentPageAfterDelete() {
|
||||
var pagination = draftManager.getPagination();
|
||||
var currentPage = pagination.pageNumber;
|
||||
var pageSize = pagination.pageSize;
|
||||
var displayTotal = draftManager.getDisplayTotal();
|
||||
var totalPages = Math.ceil(displayTotal / pageSize);
|
||||
|
||||
if (currentPage > totalPages && totalPages > 0) {
|
||||
draftManager.setPageNumber(totalPages);
|
||||
$('#subgridPager').pagination('select', totalPages);
|
||||
} else if (displayTotal === 0) {
|
||||
$('#feegrid').datagrid('loadData', []);
|
||||
$('#subgridPager').pagination('refresh', {
|
||||
total: 0,
|
||||
pageNumber: 1,
|
||||
pageSize: pageSize
|
||||
});
|
||||
} else {
|
||||
loaddata_sub();
|
||||
}
|
||||
|
||||
$('#feegrid').datagrid('clearChecked');
|
||||
pruneSelectedRowsForCurrentPage();
|
||||
calcw_selfee();
|
||||
}
|
||||
|
||||
// 根据 selectedRows 主键集合,打勾/取消当前可见行(跨页真相源)
|
||||
function syncFeeGridChecksFromSubState() {
|
||||
var rows = $('#feegrid').datagrid('getRows');
|
||||
var selKeys = {};
|
||||
var selectedRows = draftManager.getSelectedRows();
|
||||
for (var s = 0; s < selectedRows.length; s++) {
|
||||
selKeys[draftManager.getRowKey(selectedRows[s])] = true;
|
||||
}
|
||||
var excludedKeys = {};
|
||||
var excludedRows = draftManager.getExcludedRows();
|
||||
for (var e = 0; e < excludedRows.length; e++) {
|
||||
excludedKeys[draftManager.getRowKey(excludedRows[e])] = true;
|
||||
}
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var key = draftManager.getRowKey(row);
|
||||
if (draftManager.isAllChecked()) {
|
||||
if (excludedKeys[key]) {
|
||||
$('#feegrid').datagrid('uncheckRow', i);
|
||||
} else {
|
||||
$('#feegrid').datagrid('checkRow', i);
|
||||
}
|
||||
} else {
|
||||
if (selKeys[key]) {
|
||||
$('#feegrid').datagrid('checkRow', i);
|
||||
} else {
|
||||
$('#feegrid').datagrid('uncheckRow', i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelectAll() {
|
||||
if (draftManager.isAllChecked()) {
|
||||
draftManager.setAllChecked(false);
|
||||
draftManager.clearExcludedRows();
|
||||
draftManager.clearSelectedRows();
|
||||
$('#feegrid').datagrid('clearChecked');
|
||||
$('#btn_select_all').val('全部选中');
|
||||
} else {
|
||||
draftManager.setAllChecked(true);
|
||||
draftManager.clearExcludedRows();
|
||||
var rows = $('#feegrid').datagrid('getRows');
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
$('#feegrid').datagrid('checkRow', i);
|
||||
}
|
||||
$('#btn_select_all').val('全部反选');
|
||||
}
|
||||
gridManager.toggleSelectAll();
|
||||
}
|
||||
|
||||
//////////-------feegrid-----------
|
||||
@@ -920,8 +563,8 @@
|
||||
|
||||
// 草稿:已保存行标记删除 → 仍计入 DB 汇总,需扣回;本地新增 insertRows → 尚未进 DB,需加上
|
||||
var dIdx;
|
||||
var deleteRows = draftManager.getDeleteRows();
|
||||
var insertRows = draftManager.getInsertRows();
|
||||
var deleteRows = gridManager.getDeleteRows();
|
||||
var insertRows = gridManager.getInsertRows();
|
||||
for (dIdx = 0; dIdx < deleteRows.length; dIdx++) {
|
||||
applyFeeRowToMoneyTypeGroups(groupMap, deleteRows[dIdx], -1);
|
||||
}
|
||||
@@ -981,7 +624,7 @@
|
||||
}
|
||||
|
||||
function calcDeleteFee() {
|
||||
var deleteRows = draftManager.getDeleteRows();
|
||||
var deleteRows = gridManager.getDeleteRows();
|
||||
var deleteGroupMap = {};
|
||||
|
||||
for (var i = 0; i < deleteRows.length; i++) {
|
||||
@@ -1025,7 +668,7 @@
|
||||
|
||||
// 计算选中行的金额(仅业务有效行)
|
||||
function calcw_selfee() {
|
||||
var rows = draftManager.getValidSelectedRows();
|
||||
var rows = gridManager.getValidSelectedRows();
|
||||
|
||||
var irow = rows.length;
|
||||
|
||||
@@ -1254,9 +897,8 @@
|
||||
loaddata(strid);
|
||||
endEditing_con();
|
||||
|
||||
draftManager.reset();
|
||||
|
||||
loaddata_sub();
|
||||
gridManager.reset();
|
||||
gridManager.loadPage(1);
|
||||
|
||||
calcw_fee();
|
||||
var formobj = app.serializeObject2($('#zdskform'));
|
||||
@@ -1374,7 +1016,7 @@
|
||||
|
||||
var ywidMap = {};
|
||||
|
||||
var deleteRows = draftManager.getDeleteRows();
|
||||
var deleteRows = gridManager.getDeleteRows();
|
||||
if (deleteRows) {
|
||||
for (let d of deleteRows) {
|
||||
if (!d || !d.subid) {
|
||||
@@ -1387,7 +1029,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
var insertRows = draftManager.getInsertRows();
|
||||
var insertRows = gridManager.getInsertRows();
|
||||
if (insertRows) {
|
||||
for (let r of insertRows) {
|
||||
if (!r || !r.subid) {
|
||||
@@ -1647,7 +1289,7 @@
|
||||
console.log('主表变化');
|
||||
return true;
|
||||
} else {
|
||||
if (draftManager.getInsertRows().length > 0 || draftManager.getUpdateRows().length > 0 || draftManager.getDeleteRows().length > 0) {
|
||||
if (gridManager.getInsertRows().length > 0 || gridManager.getUpdateRows().length > 0 || gridManager.getDeleteRows().length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1711,19 +1353,19 @@
|
||||
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
var r = result[i];
|
||||
var rk = draftManager.getRowKey(r);
|
||||
var dup = draftManager.getInsertRows().some(function (x) {
|
||||
return draftManager.getRowKey(x) === rk;
|
||||
var rk = gridManager.getDraftManager().getRowKey(r);
|
||||
var dup = gridManager.getInsertRows().some(function (x) {
|
||||
return gridManager.getDraftManager().getRowKey(x) === rk;
|
||||
});
|
||||
if (!dup) {
|
||||
if (!r._rowState) {
|
||||
r._rowState = 'insert';
|
||||
}
|
||||
draftManager.addInsertRow(r);
|
||||
gridManager.addInsertRows(r);
|
||||
}
|
||||
}
|
||||
|
||||
refreshCurrentPage();
|
||||
gridManager.refreshCurrentPage();
|
||||
calcw_fee();
|
||||
}
|
||||
},
|
||||
@@ -1763,30 +1405,30 @@
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var r = rows[i];
|
||||
var rk = draftManager.getRowKey(r);
|
||||
var dup = draftManager.getInsertRows().some(function (x) {
|
||||
return draftManager.getRowKey(x) === rk;
|
||||
var rk = gridManager.getDraftManager().getRowKey(r);
|
||||
var dup = gridManager.getInsertRows().some(function (x) {
|
||||
return gridManager.getDraftManager().getRowKey(x) === rk;
|
||||
});
|
||||
if (!dup) {
|
||||
if (!r._rowState) {
|
||||
r._rowState = 'insert';
|
||||
}
|
||||
draftManager.addInsertRow(r);
|
||||
gridManager.addInsertRows(r);
|
||||
}
|
||||
}
|
||||
|
||||
refreshCurrentPage();
|
||||
gridManager.refreshCurrentPage();
|
||||
calcw_fee();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// 子表草稿:标记删除或移除 insert(数据源 getActualSelectedRows)
|
||||
// 子表草稿:标记删除或移除 insert(数据源 getValidSelectedRows)
|
||||
function removeit() {
|
||||
$.messager.confirm("操作提示", '您确定要删除勾选的费用吗?', function (r) {
|
||||
if (r) {
|
||||
endEditing_con();
|
||||
var selectedRows = draftManager.getValidSelectedRows();
|
||||
var selectedRows = gridManager.getValidSelectedRows();
|
||||
|
||||
if (selectedRows.length === 0) {
|
||||
$.messager.alert('提示', '请选择要删除的行(已标记删除的行请使用「撤销删除」)', 'warning');
|
||||
@@ -1806,33 +1448,30 @@
|
||||
|
||||
var hasInsertDelete = false;
|
||||
|
||||
var dm = gridManager.getDraftManager();
|
||||
for (var i = selectedRows.length - 1; i >= 0; i--) {
|
||||
var row = selectedRows[i];
|
||||
|
||||
if (draftManager.isInsertRow(row) || draftManager.isInInsertRows(row)) {
|
||||
draftManager.removeInsertRow(row);
|
||||
draftManager.removeFromUpdateRows(row);
|
||||
if (dm.isInsertRow(row) || dm.isInInsertRows(row)) {
|
||||
gridManager.removeInsertRow(row);
|
||||
hasInsertDelete = true;
|
||||
} else {
|
||||
var delKey = draftManager.getRowKey(row);
|
||||
var wasUpdate = draftManager.getUpdateRows().some(function (rr) {
|
||||
return draftManager.getRowKey(rr) === delKey;
|
||||
var delKey = dm.getRowKey(row);
|
||||
var wasUpdate = gridManager.getUpdateRows().some(function (rr) {
|
||||
return dm.getRowKey(rr) === delKey;
|
||||
});
|
||||
row._undoRowState = wasUpdate ? 'update' : '';
|
||||
row._rowState = 'delete';
|
||||
draftManager.addToDeleteRows(row);
|
||||
draftManager.removeFromUpdateRows(row);
|
||||
gridManager.addDeleteRow(row);
|
||||
}
|
||||
|
||||
draftManager.removeSelectedRow(row);
|
||||
}
|
||||
|
||||
calcw_fee();
|
||||
|
||||
if (hasInsertDelete) {
|
||||
refreshCurrentPageAfterDelete();
|
||||
gridManager.refreshAfterDelete();
|
||||
} else {
|
||||
refreshCurrentPageDisplay();
|
||||
gridManager.refreshDisplay();
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1844,13 +1483,14 @@
|
||||
var pageRows = $('#feegrid').datagrid('getRows');
|
||||
var targets = [];
|
||||
var selKeys = {};
|
||||
var selectedRows = draftManager.getSelectedRows();
|
||||
var dm = gridManager.getDraftManager();
|
||||
var selectedRows = gridManager.getSelectedRows();
|
||||
for (var sk = 0; sk < selectedRows.length; sk++) {
|
||||
selKeys[draftManager.getRowKey(selectedRows[sk])] = true;
|
||||
selKeys[dm.getRowKey(selectedRows[sk])] = true;
|
||||
}
|
||||
var hasSelectionOnPage = false;
|
||||
for (var hp = 0; hp < pageRows.length; hp++) {
|
||||
if (selKeys[draftManager.getRowKey(pageRows[hp])]) {
|
||||
if (selKeys[dm.getRowKey(pageRows[hp])]) {
|
||||
hasSelectionOnPage = true;
|
||||
break;
|
||||
}
|
||||
@@ -1862,14 +1502,14 @@
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var delSel = draftManager.getDeletedSelectedRows();
|
||||
var delSel = dm.getDeletedSelectedRows();
|
||||
var delKeys = {};
|
||||
for (var dv = 0; dv < delSel.length; dv++) {
|
||||
delKeys[draftManager.getRowKey(delSel[dv])] = true;
|
||||
delKeys[dm.getRowKey(delSel[dv])] = true;
|
||||
}
|
||||
for (var v = 0; v < pageRows.length; v++) {
|
||||
var pr = pageRows[v];
|
||||
if (pr._rowState === 'delete' && delKeys[draftManager.getRowKey(pr)]) {
|
||||
if (pr._rowState === 'delete' && delKeys[dm.getRowKey(pr)]) {
|
||||
targets.push(pr);
|
||||
}
|
||||
}
|
||||
@@ -1883,18 +1523,10 @@
|
||||
if (row._rowState !== 'delete') {
|
||||
continue;
|
||||
}
|
||||
var prev = row._undoRowState;
|
||||
draftManager.removeFromDeleteRows(row);
|
||||
if (prev === 'update') {
|
||||
row._rowState = 'update';
|
||||
draftManager.addToUpdateRows(row);
|
||||
} else {
|
||||
row._rowState = '';
|
||||
}
|
||||
delete row._undoRowState;
|
||||
gridManager.removeDeleteRow(row);
|
||||
}
|
||||
calcw_fee();
|
||||
refreshCurrentPageAfterUndo();
|
||||
gridManager.refreshAfterUndo();
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
@@ -1930,7 +1562,7 @@
|
||||
var zdorderid = $('#b_id').val();
|
||||
var url;
|
||||
var i = 0;
|
||||
var rows = draftManager.getValidSelectedRows();
|
||||
var rows = gridManager.getValidSelectedRows();
|
||||
if (rows.length <= 0) {
|
||||
var moneyid = $('#b_money_id').val();
|
||||
url = "jcwzd/editskd.jsp?zdorderid=" + zdorderid + "&moneyid=" + moneyid;
|
||||
@@ -2005,7 +1637,7 @@
|
||||
}
|
||||
var i = 0;
|
||||
var url;
|
||||
var rows = draftManager.getValidSelectedRows();
|
||||
var rows = gridManager.getValidSelectedRows();
|
||||
/* if (rows.length<=0){
|
||||
return false;
|
||||
};
|
||||
@@ -2340,7 +1972,7 @@
|
||||
});
|
||||
}
|
||||
function locked_ys() {
|
||||
var rows = draftManager.getValidSelectedRows();
|
||||
var rows = gridManager.getValidSelectedRows();
|
||||
var locked_idarr = new Array();
|
||||
var updaterows = new Array();
|
||||
for (var i in rows) {
|
||||
@@ -2359,7 +1991,7 @@
|
||||
}
|
||||
}
|
||||
function locked_cw() {
|
||||
var rows = draftManager.getValidSelectedRows();
|
||||
var rows = gridManager.getValidSelectedRows();
|
||||
var locked_idarr = new Array();
|
||||
var updaterows = new Array();
|
||||
for (var i in rows) {
|
||||
@@ -2378,7 +2010,7 @@
|
||||
}
|
||||
}
|
||||
function unlocked_cw() {
|
||||
var rows = draftManager.getValidSelectedRows();
|
||||
var rows = gridManager.getValidSelectedRows();
|
||||
var locked_idarr = new Array();
|
||||
var updaterows = new Array();
|
||||
for (var i in rows) {
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
/**
|
||||
* 草稿态分页管理模块
|
||||
*
|
||||
* 职责:
|
||||
* 1. 草稿池管理:insertRows / updateRows / deleteRows
|
||||
* 2. 分页管理:远程数据与本地数据混排
|
||||
* 3. 选中状态管理:跨页勾选状态同步
|
||||
* 4. 行状态恢复:_rowState 字段维护
|
||||
*
|
||||
* 使用方式:
|
||||
* var draftManager = window.DraftPageManager.createDraftManager({
|
||||
* keyField: 'subid',
|
||||
* pageSize: 50
|
||||
* });
|
||||
*/
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 创建草稿态管理器实例
|
||||
* @param {Object} config 配置参数
|
||||
* @param {string} config.keyField 主键字段名,默认 'id'
|
||||
* @param {number} config.pageSize 每页条数,默认 50
|
||||
* @returns {Object} 管理器实例
|
||||
*/
|
||||
function createDraftManager(config) {
|
||||
var keyField = config && config.keyField || 'id';
|
||||
var defaultPageSize = config && config.pageSize || 50;
|
||||
|
||||
var state = {
|
||||
pagination: {
|
||||
pageSize: defaultPageSize,
|
||||
pageNumber: 1,
|
||||
total: 0,
|
||||
remoteTotal: 0
|
||||
},
|
||||
currentRemoteRows: [],
|
||||
insertRows: [],
|
||||
updateRows: [],
|
||||
deleteRows: [],
|
||||
selectedRows: [],
|
||||
isAllChecked: false,
|
||||
excludedRows: []
|
||||
};
|
||||
|
||||
// ==================== 状态查询方法 ====================
|
||||
|
||||
function getPagination() {
|
||||
return {
|
||||
pageNumber: state.pagination.pageNumber,
|
||||
pageSize: state.pagination.pageSize,
|
||||
total: state.pagination.total,
|
||||
remoteTotal: state.pagination.remoteTotal
|
||||
};
|
||||
}
|
||||
|
||||
function getInsertRows() {
|
||||
return state.insertRows;
|
||||
}
|
||||
|
||||
function getUpdateRows() {
|
||||
return state.updateRows;
|
||||
}
|
||||
|
||||
function getDeleteRows() {
|
||||
return state.deleteRows;
|
||||
}
|
||||
|
||||
function getSelectedRows() {
|
||||
return state.selectedRows;
|
||||
}
|
||||
|
||||
function getValidSelectedRows() {
|
||||
return state.selectedRows.filter(function(row) {
|
||||
return row._rowState !== 'delete';
|
||||
});
|
||||
}
|
||||
|
||||
function getDeletedSelectedRows() {
|
||||
return state.selectedRows.filter(function(row) {
|
||||
return row._rowState === 'delete';
|
||||
});
|
||||
}
|
||||
|
||||
function isAllChecked() {
|
||||
return state.isAllChecked;
|
||||
}
|
||||
|
||||
function getExcludedRows() {
|
||||
return state.excludedRows;
|
||||
}
|
||||
|
||||
// ==================== 草稿池操作方法 ====================
|
||||
|
||||
function addInsertRow(row) {
|
||||
if (!row._rowState) {
|
||||
row._rowState = 'insert';
|
||||
}
|
||||
var key = getRowKey(row);
|
||||
var exists = state.insertRows.some(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
if (!exists) {
|
||||
state.insertRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
function removeInsertRow(row) {
|
||||
var key = getRowKey(row);
|
||||
state.insertRows = state.insertRows.filter(function(r) {
|
||||
return getRowKey(r) !== key;
|
||||
});
|
||||
}
|
||||
|
||||
function addToUpdateRows(row) {
|
||||
var key = getRowKey(row);
|
||||
var exists = state.updateRows.some(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
if (!exists) {
|
||||
state.updateRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromUpdateRows(row) {
|
||||
var key = getRowKey(row);
|
||||
state.updateRows = state.updateRows.filter(function(r) {
|
||||
return getRowKey(r) !== key;
|
||||
});
|
||||
}
|
||||
|
||||
function addToDeleteRows(row) {
|
||||
var key = getRowKey(row);
|
||||
var exists = state.deleteRows.some(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
if (!exists) {
|
||||
state.deleteRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromDeleteRows(row) {
|
||||
var key = getRowKey(row);
|
||||
state.deleteRows = state.deleteRows.filter(function(r) {
|
||||
return getRowKey(r) !== key;
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 选中状态操作方法 ====================
|
||||
|
||||
function addSelectedRow(row) {
|
||||
var key = getRowKey(row);
|
||||
for (var i = 0; i < state.selectedRows.length; i++) {
|
||||
if (getRowKey(state.selectedRows[i]) === key) {
|
||||
state.selectedRows[i] = row;
|
||||
return;
|
||||
}
|
||||
}
|
||||
state.selectedRows.push(row);
|
||||
}
|
||||
|
||||
function removeSelectedRow(row) {
|
||||
var key = getRowKey(row);
|
||||
state.selectedRows = state.selectedRows.filter(function(item) {
|
||||
return getRowKey(item) !== key;
|
||||
});
|
||||
}
|
||||
|
||||
function clearSelectedRows() {
|
||||
state.selectedRows = [];
|
||||
}
|
||||
|
||||
function setAllChecked(value) {
|
||||
state.isAllChecked = value;
|
||||
if (value) {
|
||||
clearSelectedRows();
|
||||
}
|
||||
}
|
||||
|
||||
function addExcludedRow(row) {
|
||||
if (isRowExcluded(row)) {
|
||||
return;
|
||||
}
|
||||
state.excludedRows.push(row);
|
||||
}
|
||||
|
||||
function removeExcludedRow(row) {
|
||||
var key = getRowKey(row);
|
||||
state.excludedRows = state.excludedRows.filter(function(r) {
|
||||
return getRowKey(r) !== key;
|
||||
});
|
||||
}
|
||||
|
||||
function clearExcludedRows() {
|
||||
state.excludedRows = [];
|
||||
}
|
||||
|
||||
function isRowExcluded(row) {
|
||||
var key = getRowKey(row);
|
||||
return state.excludedRows.some(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 分页操作方法 ====================
|
||||
|
||||
function setPageNumber(pageNumber) {
|
||||
state.pagination.pageNumber = pageNumber;
|
||||
}
|
||||
|
||||
function setPageSize(pageSize) {
|
||||
state.pagination.pageSize = pageSize;
|
||||
}
|
||||
|
||||
function setRemoteTotal(remoteTotal) {
|
||||
state.pagination.remoteTotal = remoteTotal;
|
||||
state.pagination.total = getActualTotal();
|
||||
}
|
||||
|
||||
function setCurrentRemoteRows(rows) {
|
||||
state.currentRemoteRows = rows;
|
||||
}
|
||||
|
||||
function buildPageData(remoteRows, pageNumber) {
|
||||
var pageSize = state.pagination.pageSize;
|
||||
var remoteTotal = state.pagination.remoteTotal;
|
||||
var insertRows = state.insertRows;
|
||||
var pageStart = (pageNumber - 1) * pageSize;
|
||||
var pageEnd = pageStart + pageSize;
|
||||
|
||||
remoteRows.forEach(function(row) {
|
||||
if (!row._rowState) {
|
||||
row._rowState = '';
|
||||
}
|
||||
});
|
||||
|
||||
insertRows.forEach(function(row) {
|
||||
if (!row._rowState) {
|
||||
row._rowState = 'insert';
|
||||
}
|
||||
});
|
||||
|
||||
var result = [];
|
||||
result = result.concat(remoteRows);
|
||||
|
||||
var insertStartIdx = Math.max(0, pageStart - remoteTotal);
|
||||
var insertEndIdx = pageEnd - remoteTotal;
|
||||
|
||||
if (insertStartIdx < insertRows.length) {
|
||||
var insertSlice = insertRows.slice(insertStartIdx, insertEndIdx);
|
||||
result = result.concat(insertSlice);
|
||||
}
|
||||
|
||||
if (result.length > pageSize) {
|
||||
result = result.slice(0, pageSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function restoreRowStates(rows) {
|
||||
rows.forEach(function(row) {
|
||||
var key = getRowKey(row);
|
||||
|
||||
var deleteMatch = state.deleteRows.find(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
if (deleteMatch) {
|
||||
row._rowState = 'delete';
|
||||
if (deleteMatch._undoRowState !== undefined) {
|
||||
row._undoRowState = deleteMatch._undoRowState;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var insertMatch = state.insertRows.find(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
if (insertMatch) {
|
||||
row._rowState = insertMatch._rowState || 'insert';
|
||||
return;
|
||||
}
|
||||
|
||||
var updateMatch = state.updateRows.find(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
if (updateMatch) {
|
||||
row._rowState = 'update';
|
||||
return;
|
||||
}
|
||||
|
||||
row._rowState = '';
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
function getRowKey(row) {
|
||||
return row[keyField];
|
||||
}
|
||||
|
||||
function isInsertRow(row) {
|
||||
return row._rowState === 'insert';
|
||||
}
|
||||
|
||||
function isDeleteRow(row) {
|
||||
return row._rowState === 'delete';
|
||||
}
|
||||
|
||||
function isUpdateRow(row) {
|
||||
return row._rowState === 'update';
|
||||
}
|
||||
|
||||
function isInInsertRows(row) {
|
||||
var key = getRowKey(row);
|
||||
return state.insertRows.some(function(r) {
|
||||
return getRowKey(r) === key;
|
||||
});
|
||||
}
|
||||
|
||||
function isSavedRow(row) {
|
||||
return !isInsertRow(row) && !isInInsertRows(row);
|
||||
}
|
||||
|
||||
function getActualTotal() {
|
||||
return state.pagination.remoteTotal + state.insertRows.length;
|
||||
}
|
||||
|
||||
function getDisplayTotal() {
|
||||
var insertDisplayCount = state.insertRows.filter(function(row) {
|
||||
return row._rowState !== 'delete';
|
||||
}).length;
|
||||
return state.pagination.remoteTotal + insertDisplayCount;
|
||||
}
|
||||
|
||||
function getTotalPages() {
|
||||
return Math.ceil(getActualTotal() / state.pagination.pageSize);
|
||||
}
|
||||
|
||||
function hasInsertRowsInPage(pageNumber) {
|
||||
var range = getPageRange(pageNumber);
|
||||
var remoteTotal = state.pagination.remoteTotal;
|
||||
return range.start < remoteTotal + state.insertRows.length &&
|
||||
range.end > remoteTotal;
|
||||
}
|
||||
|
||||
function isInsertOnlyPage(pageNumber) {
|
||||
var range = getPageRange(pageNumber);
|
||||
return range.start >= state.pagination.remoteTotal;
|
||||
}
|
||||
|
||||
function getPageRange(pageNumber) {
|
||||
var pageSize = state.pagination.pageSize;
|
||||
var startIdx = (pageNumber - 1) * pageSize;
|
||||
var endIdx = startIdx + pageSize;
|
||||
return { start: startIdx, end: endIdx };
|
||||
}
|
||||
|
||||
function removeSelectionForRows(gridRows) {
|
||||
var drop = {};
|
||||
for (var i = 0; i < gridRows.length; i++) {
|
||||
drop[getRowKey(gridRows[i])] = true;
|
||||
}
|
||||
state.selectedRows = state.selectedRows.filter(function(item) {
|
||||
return !drop[getRowKey(item)];
|
||||
});
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.pagination = {
|
||||
pageSize: defaultPageSize,
|
||||
pageNumber: 1,
|
||||
total: 0,
|
||||
remoteTotal: 0
|
||||
};
|
||||
state.currentRemoteRows = [];
|
||||
state.insertRows = [];
|
||||
state.updateRows = [];
|
||||
state.deleteRows = [];
|
||||
state.selectedRows = [];
|
||||
state.isAllChecked = false;
|
||||
state.excludedRows = [];
|
||||
}
|
||||
|
||||
return {
|
||||
getPagination: getPagination,
|
||||
getInsertRows: getInsertRows,
|
||||
getUpdateRows: getUpdateRows,
|
||||
getDeleteRows: getDeleteRows,
|
||||
getSelectedRows: getSelectedRows,
|
||||
getValidSelectedRows: getValidSelectedRows,
|
||||
getDeletedSelectedRows: getDeletedSelectedRows,
|
||||
isAllChecked: isAllChecked,
|
||||
getExcludedRows: getExcludedRows,
|
||||
|
||||
addInsertRow: addInsertRow,
|
||||
removeInsertRow: removeInsertRow,
|
||||
addToUpdateRows: addToUpdateRows,
|
||||
removeFromUpdateRows: removeFromUpdateRows,
|
||||
addToDeleteRows: addToDeleteRows,
|
||||
removeFromDeleteRows: removeFromDeleteRows,
|
||||
|
||||
addSelectedRow: addSelectedRow,
|
||||
removeSelectedRow: removeSelectedRow,
|
||||
clearSelectedRows: clearSelectedRows,
|
||||
setAllChecked: setAllChecked,
|
||||
addExcludedRow: addExcludedRow,
|
||||
removeExcludedRow: removeExcludedRow,
|
||||
clearExcludedRows: clearExcludedRows,
|
||||
isRowExcluded: isRowExcluded,
|
||||
|
||||
setPageNumber: setPageNumber,
|
||||
setPageSize: setPageSize,
|
||||
setRemoteTotal: setRemoteTotal,
|
||||
setCurrentRemoteRows: setCurrentRemoteRows,
|
||||
buildPageData: buildPageData,
|
||||
restoreRowStates: restoreRowStates,
|
||||
|
||||
getRowKey: getRowKey,
|
||||
isInsertRow: isInsertRow,
|
||||
isDeleteRow: isDeleteRow,
|
||||
isUpdateRow: isUpdateRow,
|
||||
isInInsertRows: isInInsertRows,
|
||||
isSavedRow: isSavedRow,
|
||||
getActualTotal: getActualTotal,
|
||||
getDisplayTotal: getDisplayTotal,
|
||||
getTotalPages: getTotalPages,
|
||||
hasInsertRowsInPage: hasInsertRowsInPage,
|
||||
isInsertOnlyPage: isInsertOnlyPage,
|
||||
removeSelectionForRows: removeSelectionForRows,
|
||||
reset: reset
|
||||
};
|
||||
}
|
||||
|
||||
window.DraftPageManager = {
|
||||
createDraftManager: createDraftManager
|
||||
};
|
||||
|
||||
})(window);
|
||||
@@ -0,0 +1,937 @@
|
||||
/**
|
||||
* 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 = $(
|
||||
'<div id="' +
|
||||
pagerId +
|
||||
'" class="pagination" style="margin: 6px 0px;background:#efefef;border:1px solid #ccc;"></div>',
|
||||
);
|
||||
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);
|
||||
Reference in new issue
Block a user