@builder6/server 0.6.5 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.express.js +0 -1
- package/dist/app.express.js.map +1 -1
- package/dist/app.module.js +12 -10
- package/dist/app.module.js.map +1 -1
- package/dist/b6.server.js +11 -134
- package/dist/b6.server.js.map +1 -1
- package/dist/oidc/oidc.service.js +1 -1
- package/dist/oidc/oidc.service.js.map +1 -1
- package/package.json +10 -8
- package/dist/app.config.d.ts +0 -2
- package/dist/app.config.js +0 -55
- package/dist/app.config.js.map +0 -1
- package/dist/b6.config.d.ts +0 -0
- package/dist/b6.config.js +0 -4
- package/dist/b6.config.js.map +0 -1
- package/dist/files/files.controller.d.ts +0 -11
- package/dist/files/files.controller.js +0 -148
- package/dist/files/files.controller.js.map +0 -1
- package/dist/files/files.module.d.ts +0 -2
- package/dist/files/files.module.js +0 -26
- package/dist/files/files.module.js.map +0 -1
- package/dist/files/files.moleculer.d.ts +0 -10
- package/dist/files/files.moleculer.js +0 -53
- package/dist/files/files.moleculer.js.map +0 -1
- package/dist/files/files.service.d.ts +0 -30
- package/dist/files/files.service.js +0 -207
- package/dist/files/files.service.js.map +0 -1
- package/dist/files/index.d.ts +0 -3
- package/dist/files/index.js +0 -20
- package/dist/files/index.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -26
- package/dist/index.js.map +0 -1
- package/dist/moleculer.config.d.ts +0 -2
- package/dist/moleculer.config.js +0 -74
- package/dist/moleculer.config.js.map +0 -1
- package/dist/plugin/moleculer.service.d.ts +0 -11
- package/dist/plugin/moleculer.service.js +0 -96
- package/dist/plugin/moleculer.service.js.map +0 -1
- package/dist/plugin/plugin.module.d.ts +0 -14
- package/dist/plugin/plugin.module.js +0 -72
- package/dist/plugin/plugin.module.js.map +0 -1
- package/dist/plugin/plugin.service.d.ts +0 -3
- package/dist/plugin/plugin.service.js +0 -21
- package/dist/plugin/plugin.service.js.map +0 -1
- package/dist/rooms/globals/augmentation.d.ts +0 -25
- package/dist/rooms/globals/augmentation.js +0 -3
- package/dist/rooms/globals/augmentation.js.map +0 -1
- package/dist/rooms/lib/DateToString.d.ts +0 -3
- package/dist/rooms/lib/DateToString.js +0 -3
- package/dist/rooms/lib/DateToString.js.map +0 -1
- package/dist/rooms/lib/Json.d.ts +0 -9
- package/dist/rooms/lib/Json.js +0 -18
- package/dist/rooms/lib/Json.js.map +0 -1
- package/dist/rooms/lib/utils.d.ts +0 -33
- package/dist/rooms/lib/utils.js +0 -122
- package/dist/rooms/lib/utils.js.map +0 -1
- package/dist/rooms/protocol/AuthToken.d.ts +0 -50
- package/dist/rooms/protocol/AuthToken.js +0 -49
- package/dist/rooms/protocol/AuthToken.js.map +0 -1
- package/dist/rooms/protocol/Authentication.d.ts +0 -22
- package/dist/rooms/protocol/Authentication.js +0 -3
- package/dist/rooms/protocol/Authentication.js.map +0 -1
- package/dist/rooms/protocol/BaseActivitiesData.d.ts +0 -4
- package/dist/rooms/protocol/BaseActivitiesData.js +0 -3
- package/dist/rooms/protocol/BaseActivitiesData.js.map +0 -1
- package/dist/rooms/protocol/BaseRoomInfo.d.ts +0 -6
- package/dist/rooms/protocol/BaseRoomInfo.js +0 -3
- package/dist/rooms/protocol/BaseRoomInfo.js.map +0 -1
- package/dist/rooms/protocol/BaseUserMeta.d.ts +0 -10
- package/dist/rooms/protocol/BaseUserMeta.js +0 -3
- package/dist/rooms/protocol/BaseUserMeta.js.map +0 -1
- package/dist/rooms/protocol/ClientMsg.d.ts +0 -41
- package/dist/rooms/protocol/ClientMsg.js +0 -13
- package/dist/rooms/protocol/ClientMsg.js.map +0 -1
- package/dist/rooms/protocol/Comments.d.ts +0 -146
- package/dist/rooms/protocol/Comments.js +0 -3
- package/dist/rooms/protocol/Comments.js.map +0 -1
- package/dist/rooms/protocol/InboxNotifications.d.ts +0 -49
- package/dist/rooms/protocol/InboxNotifications.js +0 -3
- package/dist/rooms/protocol/InboxNotifications.js.map +0 -1
- package/dist/rooms/protocol/Op.d.ts +0 -82
- package/dist/rooms/protocol/Op.js +0 -28
- package/dist/rooms/protocol/Op.js.map +0 -1
- package/dist/rooms/protocol/SerializedCrdt.d.ts +0 -40
- package/dist/rooms/protocol/SerializedCrdt.js +0 -19
- package/dist/rooms/protocol/SerializedCrdt.js.map +0 -1
- package/dist/rooms/protocol/ServerMsg.d.ts +0 -128
- package/dist/rooms/protocol/ServerMsg.js +0 -25
- package/dist/rooms/protocol/ServerMsg.js.map +0 -1
- package/dist/rooms/protocol/VersionHistory.d.ts +0 -9
- package/dist/rooms/protocol/VersionHistory.js +0 -3
- package/dist/rooms/protocol/VersionHistory.js.map +0 -1
- package/dist/rooms/rooms.controller.d.ts +0 -64
- package/dist/rooms/rooms.controller.js +0 -401
- package/dist/rooms/rooms.controller.js.map +0 -1
- package/dist/rooms/rooms.gateway.d.ts +0 -30
- package/dist/rooms/rooms.gateway.js +0 -188
- package/dist/rooms/rooms.gateway.js.map +0 -1
- package/dist/rooms/rooms.guard.d.ts +0 -9
- package/dist/rooms/rooms.guard.js +0 -48
- package/dist/rooms/rooms.guard.js.map +0 -1
- package/dist/rooms/rooms.module.d.ts +0 -2
- package/dist/rooms/rooms.module.js +0 -36
- package/dist/rooms/rooms.module.js.map +0 -1
- package/dist/rooms/rooms.service.d.ts +0 -51
- package/dist/rooms/rooms.service.js +0 -225
- package/dist/rooms/rooms.service.js.map +0 -1
- package/views/ag-grid/README.md +0 -15
- package/views/ag-grid/ag-grid.hbs +0 -1099
- package/views/devextreme/datagrid.hbs +0 -240
|
@@ -1,1099 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>AG Grid Enterprise with Dynamic Loading and Editing</title>
|
|
8
|
-
<!-- 引入 AG Grid 的 CSS -->
|
|
9
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community/styles/ag-grid.css">
|
|
10
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community/styles/ag-theme-alpine.css">
|
|
11
|
-
<style>
|
|
12
|
-
/* 设置表格的高度和宽度 */
|
|
13
|
-
html {
|
|
14
|
-
overflow: hidden;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
html,
|
|
18
|
-
body {
|
|
19
|
-
height: 100%;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
#myGrid {
|
|
23
|
-
height: calc(100% - 100px);
|
|
24
|
-
width: 100%;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.custom-tooltip {
|
|
28
|
-
padding: 5px;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.custom-tooltip p {
|
|
32
|
-
margin: 5px;
|
|
33
|
-
white-space: nowrap;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.custom-tooltip p:first-of-type {
|
|
37
|
-
font-weight: bold;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.verification-errors-row {
|
|
41
|
-
background-color: #cc693344;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.verification-errors-row .ag-cell-edit-wrapper {
|
|
45
|
-
border: solid 3px #cc693344 !important;
|
|
46
|
-
}
|
|
47
|
-
</style>
|
|
48
|
-
</head>
|
|
49
|
-
|
|
50
|
-
<body>
|
|
51
|
-
<h1>AG Grid - Dynamic Loading and Editing</h1>
|
|
52
|
-
<div class="button-container">
|
|
53
|
-
<!-- 创建一个按钮来添加新行 -->
|
|
54
|
-
<button id="addRowBtn">Add New Row</button>
|
|
55
|
-
<button id="deleteButton">Delete Selected Rows</button>
|
|
56
|
-
</div>
|
|
57
|
-
<!-- 创建一个容器来放置 AG Grid -->
|
|
58
|
-
<div id="myGrid" class="ag-theme-alpine"></div>
|
|
59
|
-
|
|
60
|
-
<script src="https://cdn.jsdelivr.net/npm/lodash"></script>
|
|
61
|
-
<!-- 引入 AG Grid 的 JavaScript -->
|
|
62
|
-
<script src="https://cdn.jsdelivr.net/npm/ag-grid-enterprise/dist/ag-grid-enterprise.noStyle.js"></script>
|
|
63
|
-
<!-- 引入 Amis Formula -->
|
|
64
|
-
<script type="module">
|
|
65
|
-
import * as amisFormula from 'https://cdn.skypack.dev/amis-formula';
|
|
66
|
-
window.amisFormula = amisFormula;
|
|
67
|
-
window.evaluate = amisFormula.evaluate;
|
|
68
|
-
|
|
69
|
-
// const expression = '${1 + 2 * 3}';
|
|
70
|
-
// const result = evaluate(expression);
|
|
71
|
-
// console.log('Result:', result); // Output: 7
|
|
72
|
-
</script>
|
|
73
|
-
<script type="module">
|
|
74
|
-
import * as locale from '/ag-grid/locale_zh.js';
|
|
75
|
-
window.agGridLocale = locale;
|
|
76
|
-
</script>
|
|
77
|
-
</body>
|
|
78
|
-
<script>
|
|
79
|
-
|
|
80
|
-
const B6_TABLES_API = '/api/tables/v2';
|
|
81
|
-
const baseId = '{{ baseId }}';
|
|
82
|
-
const tableId = '{{ tableId }}';
|
|
83
|
-
|
|
84
|
-
// 启用 AG Grid 企业版
|
|
85
|
-
agGrid.LicenseManager.setLicenseKey("YOUR_LICENSE_KEY_HERE");
|
|
86
|
-
|
|
87
|
-
let gridApi; // 提前声明 gridApi 以便全局访问
|
|
88
|
-
let gridOptions; // 提前声明 gridOptions 以便全局访问
|
|
89
|
-
let table;
|
|
90
|
-
const mode = "edit";//edit/readonly/design
|
|
91
|
-
const isReadonly = mode === "readonly";
|
|
92
|
-
|
|
93
|
-
const padZero = num => num.toString().padStart(2, '0');
|
|
94
|
-
class DateTimeEditor {
|
|
95
|
-
init(params) {
|
|
96
|
-
this.eInput = document.createElement('input');
|
|
97
|
-
this.eInput.type = 'datetime-local';
|
|
98
|
-
// this.eInput.value = params.value ? this.formatDate(params.value) : '';
|
|
99
|
-
const localDate = new Date(params.value);
|
|
100
|
-
const value = `${localDate.getFullYear()}-${padZero(localDate.getMonth() + 1)}-${padZero(localDate.getDate())} ${padZero(localDate.getHours())}:${padZero(localDate.getMinutes())}`;
|
|
101
|
-
this.eInput.value = value;
|
|
102
|
-
this.eInput.style.width = '100%';
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
getGui() {
|
|
106
|
-
return this.eInput;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
afterGuiAttached() {
|
|
110
|
-
this.eInput.focus();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getValue() {
|
|
114
|
-
const value = this.eInput.value;
|
|
115
|
-
return value ? new Date(value) : null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
formatDate(date) {
|
|
119
|
-
const d = new Date(date);
|
|
120
|
-
return d.toISOString();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
class MultiSelectCellEditor {
|
|
125
|
-
init(params) {
|
|
126
|
-
this.eInput = document.createElement('select');
|
|
127
|
-
this.eInput.setAttribute('multiple', 'true');
|
|
128
|
-
this.eInput.style.width = '200px';
|
|
129
|
-
const options = params.colDef.cellEditorParams.values;
|
|
130
|
-
options.forEach(option => {
|
|
131
|
-
const optionElement = document.createElement('option');
|
|
132
|
-
optionElement.text = option;
|
|
133
|
-
optionElement.value = option;
|
|
134
|
-
this.eInput.appendChild(optionElement);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// 设置初始值
|
|
138
|
-
const selectedValues = Array.isArray(params.value) ? params.value : [];
|
|
139
|
-
for (let option of this.eInput.options) {
|
|
140
|
-
if (selectedValues.includes(option.value)) {
|
|
141
|
-
option.selected = true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
getGui() {
|
|
147
|
-
return this.eInput;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
afterGuiAttached() {
|
|
151
|
-
this.eInput.focus();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
getValue() {
|
|
155
|
-
const selectedOptions = Array.from(this.eInput.selectedOptions);
|
|
156
|
-
return selectedOptions.map(option => option.value);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
destroy() { }
|
|
160
|
-
|
|
161
|
-
isPopup() {
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
class CustomTooltip {
|
|
167
|
-
constructor() {
|
|
168
|
-
this.eGui = null;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
init(params) {
|
|
172
|
-
this.eGui = document.createElement('div');
|
|
173
|
-
const color = params.color || '#cc693344';
|
|
174
|
-
|
|
175
|
-
this.eGui.classList.add('custom-tooltip');
|
|
176
|
-
this.eGui.style.backgroundColor = color;
|
|
177
|
-
this.eGui.innerHTML = `
|
|
178
|
-
<div><b>不符合校验规则</b></div>
|
|
179
|
-
<div>${params.value}</div>
|
|
180
|
-
`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
getGui() {
|
|
184
|
-
return this.eGui;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// 创建一个异步函数来初始化表格
|
|
189
|
-
function initializeGrid() {
|
|
190
|
-
return new Promise(function (resolve, reject) {
|
|
191
|
-
try {
|
|
192
|
-
// 获取列定义
|
|
193
|
-
fetch(B6_TABLES_API + '/meta/bases/' + baseId + '/tables/' + tableId, { credentials: 'include' })
|
|
194
|
-
.then(function (response) {
|
|
195
|
-
return response.json().then(function (data) {
|
|
196
|
-
table = data;
|
|
197
|
-
console.log(table);
|
|
198
|
-
return data;
|
|
199
|
-
});
|
|
200
|
-
})
|
|
201
|
-
.then(function (data) {
|
|
202
|
-
initGridOptionsAndRender(data, resolve, reject);
|
|
203
|
-
})
|
|
204
|
-
.catch(function (error) {
|
|
205
|
-
console.error('Error initializing grid:', error);
|
|
206
|
-
reject(error);
|
|
207
|
-
});
|
|
208
|
-
} catch (error) {
|
|
209
|
-
console.error('Error initializing grid:', error);
|
|
210
|
-
reject(error);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function getDataTypeDefinitions() {
|
|
216
|
-
return {
|
|
217
|
-
date: {
|
|
218
|
-
baseDataType: 'date',
|
|
219
|
-
extendsDataType: 'date',
|
|
220
|
-
valueParser__: function (params) {
|
|
221
|
-
// ag-grid官网明确说valueParser是用来实现保存数据前数据转换的,但是实测这个函数并不一定会被触发
|
|
222
|
-
// 另外valueSetter也能实现类似功能,但是一样实测不会被触发
|
|
223
|
-
// 所以只能手动在调用保存接口前实现相关转换逻辑
|
|
224
|
-
// 见:
|
|
225
|
-
// https://www.ag-grid.com/javascript-data-grid/column-properties/#reference-editing-valueParser
|
|
226
|
-
// https://www.ag-grid.com/javascript-data-grid/cell-data-types/#overriding-the-pre-defined-cell-data-type-definitions
|
|
227
|
-
// 后面测试到规则是输入值为string类型时,即复制粘贴进来的字段值才会走valueParser
|
|
228
|
-
// 因为要考虑到从excel大量copy数据过来时保持原错误字段值提醒用户手动改值, 所以不可以启用valueParser做值转换,只能手动在调用保存接口前实现相关转换逻辑
|
|
229
|
-
console.log("valueParser:", params.newValue);
|
|
230
|
-
},
|
|
231
|
-
valueGetter: function (params) {
|
|
232
|
-
var fieldType = params.colDef.cellEditorParams.fieldConfig.type;
|
|
233
|
-
var fieldName = params.colDef.field;
|
|
234
|
-
var fieldValue = params.data[fieldName];
|
|
235
|
-
if (!fieldValue) return null;
|
|
236
|
-
|
|
237
|
-
var date = new Date(fieldValue);
|
|
238
|
-
return date;
|
|
239
|
-
},
|
|
240
|
-
valueFormatter: function (params) {
|
|
241
|
-
var fieldType = params.colDef.cellEditorParams.fieldConfig.type;
|
|
242
|
-
var date = new Date(params.value);
|
|
243
|
-
|
|
244
|
-
if (!params.value) return "";
|
|
245
|
-
|
|
246
|
-
if (fieldType === "date") {
|
|
247
|
-
return date.getFullYear() + '-' + padZero(date.getMonth() + 1) + '-' + padZero(date.getDate());
|
|
248
|
-
} else if (fieldType === "datetime") {
|
|
249
|
-
// Convert to local time considering timezone
|
|
250
|
-
var localDate = new Date(date.getTime());
|
|
251
|
-
return localDate.getFullYear() + '-' + padZero(localDate.getMonth() + 1) + '-' + padZero(localDate.getDate()) + ' ' + padZero(localDate.getHours()) + ':' + padZero(localDate.getMinutes());
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return "";
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
formula: {
|
|
258
|
-
baseDataType: 'text',
|
|
259
|
-
extendsDataType: 'text',
|
|
260
|
-
fields: {}
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function tooltipValueGetter(params) {
|
|
266
|
-
if (params.data.__verificationErrors && params.data.__verificationErrors.length) {
|
|
267
|
-
return params.data.__verificationErrors[0];
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function getColumnDef(field, dataTypeDefinitions) {
|
|
272
|
-
var cellDataType,
|
|
273
|
-
cellEditorParams,
|
|
274
|
-
cellEditor,
|
|
275
|
-
cellRendererParams,
|
|
276
|
-
cellRenderer,
|
|
277
|
-
valueFormatter,
|
|
278
|
-
valueGetter,
|
|
279
|
-
fieldOptions,
|
|
280
|
-
editable = true,
|
|
281
|
-
filter,
|
|
282
|
-
filterParams;
|
|
283
|
-
|
|
284
|
-
// 根据字段类型设置 dataType
|
|
285
|
-
cellEditorParams = {
|
|
286
|
-
fieldConfig: field
|
|
287
|
-
};
|
|
288
|
-
cellRendererParams = {};
|
|
289
|
-
filterParams = {
|
|
290
|
-
debounceMs: 200,
|
|
291
|
-
maxNumConditions: 1
|
|
292
|
-
};
|
|
293
|
-
switch (field.type) {
|
|
294
|
-
case 'text':
|
|
295
|
-
case 'textarea':
|
|
296
|
-
cellDataType = 'text';
|
|
297
|
-
filter = 'agTextColumnFilter';
|
|
298
|
-
Object.assign(filterParams, {
|
|
299
|
-
filterOptions: ["contains", "notContains", "equals", "startsWith", "endsWith"]
|
|
300
|
-
});
|
|
301
|
-
break;
|
|
302
|
-
case 'number':
|
|
303
|
-
cellDataType = 'number';
|
|
304
|
-
Object.assign(cellEditorParams, {
|
|
305
|
-
precision: field.precision || 0
|
|
306
|
-
});
|
|
307
|
-
filter = 'agNumberColumnFilter';
|
|
308
|
-
Object.assign(filterParams, {
|
|
309
|
-
filterOptions: ["equals", "greaterThan", "greaterThanOrEqual", "lessThan", "lessThanOrEqual"]
|
|
310
|
-
});
|
|
311
|
-
break;
|
|
312
|
-
case 'select':
|
|
313
|
-
cellDataType = 'text';
|
|
314
|
-
fieldOptions = field.options && field.options.split("\n").map(function (n) { return n.trim(); }) || [];
|
|
315
|
-
fieldOptions.unshift(null);
|
|
316
|
-
Object.assign(cellEditorParams, {
|
|
317
|
-
values: fieldOptions
|
|
318
|
-
});
|
|
319
|
-
cellEditor = "agSelectCellEditor";
|
|
320
|
-
filter = 'agSetColumnFilter';
|
|
321
|
-
Object.assign(filterParams, {
|
|
322
|
-
values: fieldOptions
|
|
323
|
-
});
|
|
324
|
-
break;
|
|
325
|
-
case 'select-multiple':
|
|
326
|
-
cellDataType = 'object';
|
|
327
|
-
fieldOptions = field.options && field.options.split("\n").map(function (n) { return n.trim(); }) || [];
|
|
328
|
-
Object.assign(cellEditorParams, {
|
|
329
|
-
values: fieldOptions
|
|
330
|
-
});
|
|
331
|
-
cellEditor = MultiSelectCellEditor;
|
|
332
|
-
filter = 'agSetColumnFilter';
|
|
333
|
-
Object.assign(filterParams, {
|
|
334
|
-
values: fieldOptions
|
|
335
|
-
});
|
|
336
|
-
break;
|
|
337
|
-
case 'date':
|
|
338
|
-
cellDataType = 'date';
|
|
339
|
-
cellEditor = "agDateCellEditor";
|
|
340
|
-
valueFormatter = dataTypeDefinitions.date.valueFormatter;
|
|
341
|
-
// 如果不定义valueGetter,双击单元格进入编辑状态时,值显示为空
|
|
342
|
-
valueGetter = dataTypeDefinitions.date.valueGetter;
|
|
343
|
-
filter = 'agDateColumnFilter';
|
|
344
|
-
Object.assign(filterParams, {
|
|
345
|
-
filterOptions: ["equals", "greaterThan", "greaterThanOrEqual", "lessThan", "lessThanOrEqual"]
|
|
346
|
-
});
|
|
347
|
-
break;
|
|
348
|
-
case 'datetime':
|
|
349
|
-
cellDataType = 'date';
|
|
350
|
-
editable = false;
|
|
351
|
-
// cellEditor = DateTimeEditor;
|
|
352
|
-
// 因为日期时间依赖了DateTimeEditor.init函数中对初始值定义,所以这里没必要再走一次valueGetter
|
|
353
|
-
// valueGetter = dataTypeDefinitions.date.valueGetter;
|
|
354
|
-
/*
|
|
355
|
-
filter = 'agDateColumnFilter';
|
|
356
|
-
Object.assign(filterParams, {
|
|
357
|
-
filterOptions: ["equals", "greaterThan", "greaterThanOrEqual", "lessThan", "lessThanOrEqual"]
|
|
358
|
-
});*/
|
|
359
|
-
break;
|
|
360
|
-
case 'boolean':
|
|
361
|
-
cellDataType = 'boolean';
|
|
362
|
-
Object.assign(cellRendererParams, {
|
|
363
|
-
disabled: true
|
|
364
|
-
});
|
|
365
|
-
cellRenderer = "agCheckboxCellRenderer";
|
|
366
|
-
filter = 'agSetColumnFilter';
|
|
367
|
-
Object.assign(filterParams, {
|
|
368
|
-
values: [true, false],
|
|
369
|
-
suppressSelectAll: true,
|
|
370
|
-
comparator: function (a, b) {
|
|
371
|
-
// 将 true 显示在 false 之前
|
|
372
|
-
if (a === true && b === false) return -1;
|
|
373
|
-
if (a === false && b === true) return 1;
|
|
374
|
-
return 0;
|
|
375
|
-
},
|
|
376
|
-
valueFormatter: function (params) {
|
|
377
|
-
return params.value ? '是' : '否';
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
break;
|
|
381
|
-
case 'formula':
|
|
382
|
-
cellDataType = 'formula';
|
|
383
|
-
editable = false;
|
|
384
|
-
// 记录所有公式字段配置方便取出来用
|
|
385
|
-
dataTypeDefinitions.formula.fields[field.name.toLowerCase()] = field;
|
|
386
|
-
break;
|
|
387
|
-
default:
|
|
388
|
-
cellDataType = 'text'; // 默认类型
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
field: field.name.toLowerCase(),
|
|
393
|
-
headerName: field.label,
|
|
394
|
-
cellDataType: cellDataType,
|
|
395
|
-
cellEditorParams: cellEditorParams,
|
|
396
|
-
cellEditor: cellEditor,
|
|
397
|
-
cellRendererParams: cellRendererParams,
|
|
398
|
-
cellRenderer: cellRenderer,
|
|
399
|
-
editable: isReadonly ? false : editable,
|
|
400
|
-
valueFormatter: valueFormatter,
|
|
401
|
-
valueGetter: valueGetter,
|
|
402
|
-
tooltipValueGetter: tooltipValueGetter,
|
|
403
|
-
filter: filter,
|
|
404
|
-
filterParams: filterParams
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function initGridOptionsAndRender(data, resolve, reject) {
|
|
410
|
-
var dataTypeDefinitions = getDataTypeDefinitions();
|
|
411
|
-
|
|
412
|
-
var columnDefs = data.fields.map(function (field) {
|
|
413
|
-
return getColumnDef(field, dataTypeDefinitions);
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
var pageSize = 100000;
|
|
417
|
-
// 初始化网格配置
|
|
418
|
-
gridOptions = {
|
|
419
|
-
columnDefs: columnDefs,
|
|
420
|
-
dataTypeDefinitions: dataTypeDefinitions,
|
|
421
|
-
rowClassRules: {
|
|
422
|
-
'verification-errors-row': function (params) {
|
|
423
|
-
if (!params.data) {
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
const hasVerificationErrors = params.data.__verificationErrors && params.data.__verificationErrors.length;
|
|
427
|
-
const isFormulaError = params.data.__isFormulaFieldRunError;
|
|
428
|
-
return hasVerificationErrors || isFormulaError;
|
|
429
|
-
}
|
|
430
|
-
},
|
|
431
|
-
rowData: null, // 初始为空,通过 API 动态加载
|
|
432
|
-
rowModelType: 'serverSide',
|
|
433
|
-
pagination: false,
|
|
434
|
-
paginationPageSizeSelector: false,
|
|
435
|
-
paginationPageSize: pageSize,
|
|
436
|
-
cacheBlockSize: pageSize,
|
|
437
|
-
// editType: 'fullRow',
|
|
438
|
-
cellSelection: {
|
|
439
|
-
handle: {
|
|
440
|
-
mode: 'range',
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
// onRowValueChanged: isReadonly ? null : onRowValueChanged,
|
|
444
|
-
onCellValueChanged: isReadonly ? null : onCellValueChanged,
|
|
445
|
-
//datasource: getDataSource(),
|
|
446
|
-
defaultColDef: {
|
|
447
|
-
flex: 1,
|
|
448
|
-
minWidth: 100,
|
|
449
|
-
resizable: true
|
|
450
|
-
},
|
|
451
|
-
getRowId: function (params) { return params.data._id; },
|
|
452
|
-
selectionColumnDef: {
|
|
453
|
-
pinned: 'left'
|
|
454
|
-
},
|
|
455
|
-
rowSelection: isReadonly ? null : {
|
|
456
|
-
mode: "multiRow",
|
|
457
|
-
selectAll: "all",
|
|
458
|
-
checkboxes: true,
|
|
459
|
-
headerCheckbox: true
|
|
460
|
-
},
|
|
461
|
-
serverSideDatasource: getServerSideDatasource(),
|
|
462
|
-
localeText: agGridLocale.AG_GRID_LOCALE_CN
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
console.log("gridOptions:", gridOptions);
|
|
466
|
-
|
|
467
|
-
// 渲染网格
|
|
468
|
-
var gridDiv = document.querySelector('#myGrid');
|
|
469
|
-
gridApi = agGrid.createGrid(gridDiv, gridOptions);
|
|
470
|
-
|
|
471
|
-
// 设置新增行按钮的事件
|
|
472
|
-
document.getElementById('addRowBtn').addEventListener('click', addNewRow);
|
|
473
|
-
|
|
474
|
-
// Add event listener to the delete button
|
|
475
|
-
document.getElementById('deleteButton').addEventListener('click', deleteSelectedRows);
|
|
476
|
-
|
|
477
|
-
resolve();
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// 校验数据表中配置的Verifications并返回错误信息
|
|
481
|
-
function getTableVerificationErrors(data, tableVerifications) {
|
|
482
|
-
let validated = true;
|
|
483
|
-
const verificationErrors = [];
|
|
484
|
-
// verification校验
|
|
485
|
-
const verifications = tableVerifications || [];
|
|
486
|
-
verifications.forEach(function (verification) {
|
|
487
|
-
validated = runAmisFormula(verification.rule, data, function (ex) {
|
|
488
|
-
console.warn("执行校验规则“" + verification.rule + "”公式出错了,请检查校验规则公式配置:", ex);
|
|
489
|
-
alert("执行校验规则“" + verification.rule + "”公式出错了,请检查校验规则公式配置:" + (ex && ex.toString()));
|
|
490
|
-
});
|
|
491
|
-
if (!validated) {
|
|
492
|
-
verificationErrors.push(verification.alert);
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
return verificationErrors;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// 校验number值小数位数,必须小于等于指定位数
|
|
499
|
-
function checkNumberPrecision(num, precision) {
|
|
500
|
-
const numStr = num.toString();
|
|
501
|
-
const parts = numStr.split('.');
|
|
502
|
-
if (precision > 0) {
|
|
503
|
-
if (parts.length === 1) {
|
|
504
|
-
return true;
|
|
505
|
-
}
|
|
506
|
-
return parts[1].length <= precision;
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
return parts.length === 1;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
//校验字符串日期值是否合法,如果合法返回Date类型,否则返回null
|
|
514
|
-
function parseDate(str) {
|
|
515
|
-
// 定义正则表达式,匹配不同的日期格式
|
|
516
|
-
// 格式:YYYY-MM-DD、YYYY/MM/DD 和 YYYY-MM-DDTHH:MM:SS.SSSZ
|
|
517
|
-
// 最后一种TZ格式是服务端返回的格式值,复制其它列字段值时会把这种格式值提交到接口,必须兼容
|
|
518
|
-
var regex = new RegExp('^(\\d{4})([-\\/])(0?[1-9]|1[0-2])\\2(0?[1-9]|[12][0-9]|3[01])(T(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d{3})?Z)?$');
|
|
519
|
-
|
|
520
|
-
// 检查是否匹配正则表达式
|
|
521
|
-
var match = str.match(regex);
|
|
522
|
-
if (!match) {
|
|
523
|
-
return null;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// 提取年份、月份、日期和时间
|
|
527
|
-
var year = match[1];
|
|
528
|
-
var month = match[3].padStart(2, '0'); // 补齐月份前导零
|
|
529
|
-
var day = match[4].padStart(2, '0'); // 补齐日期前导零
|
|
530
|
-
|
|
531
|
-
// 如果有时间部分,提取时间
|
|
532
|
-
var timePart = match[5] ? match[5] : '';
|
|
533
|
-
var standardizedDateStr = year + '-' + month + '-' + day + timePart;
|
|
534
|
-
|
|
535
|
-
// 使用 Date 对象验证日期是否合法
|
|
536
|
-
// 这里 standardizedDateStr 必须经过上面的补充前导零操作,否则new Date执行的结果会差8小时
|
|
537
|
-
var date = new Date(standardizedDateStr);
|
|
538
|
-
var timestamp = date.getTime();
|
|
539
|
-
|
|
540
|
-
// 检查是否是合法日期
|
|
541
|
-
if (typeof timestamp !== 'number' || isNaN(timestamp)) {
|
|
542
|
-
return null;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// 检查生成的日期和输入是否一致(避免 2024-02-30 这种情况)
|
|
546
|
-
if (date.getUTCFullYear() === parseInt(year, 10) &&
|
|
547
|
-
date.getUTCMonth() + 1 === parseInt(month, 10) &&
|
|
548
|
-
date.getUTCDate() === parseInt(day, 10)) {
|
|
549
|
-
return date;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// 判断一个js变量是否一个合法的Date变量
|
|
556
|
-
function isValidDate(date) {
|
|
557
|
-
if (_.isNil(date)) {
|
|
558
|
-
return true;
|
|
559
|
-
}
|
|
560
|
-
return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// 校验单选字段是否在选项范围
|
|
564
|
-
function checkSelectValueValid(value, options) {
|
|
565
|
-
return (options || []).indexOf(value) > -1;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const changeQueue = new Map();
|
|
569
|
-
|
|
570
|
-
function processChangeQueue() {
|
|
571
|
-
changeQueue.forEach((event, rowIndex) => {
|
|
572
|
-
onRowValueChanged(event);
|
|
573
|
-
});
|
|
574
|
-
changeQueue.clear();
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const debouncedProcessChangeQueue = _.debounce(processChangeQueue, 200);
|
|
578
|
-
|
|
579
|
-
function onCellValueChanged(event) {
|
|
580
|
-
const rowIndex = event.node.rowIndex;
|
|
581
|
-
|
|
582
|
-
if (!changeQueue.has(rowIndex)) {
|
|
583
|
-
changeQueue.set(rowIndex, event);
|
|
584
|
-
} else {
|
|
585
|
-
const existingEvent = changeQueue.get(rowIndex);
|
|
586
|
-
// 更新现有的 event 对象中的数据
|
|
587
|
-
existingEvent.data = { ...existingEvent.data, ...event.data };
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
debouncedProcessChangeQueue();
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// 监听行数据改变事件
|
|
594
|
-
async function onRowValueChanged(event) {
|
|
595
|
-
const data = event.data;
|
|
596
|
-
console.log('Saving updated data to server:', data);
|
|
597
|
-
try {
|
|
598
|
-
const allGridColumns = event.api.getAllGridColumns();
|
|
599
|
-
// 字段类型值转换以及字段校验
|
|
600
|
-
const fieldsVerificationErrors = [];
|
|
601
|
-
const colDefs = _.keyBy(_.map(allGridColumns, "colDef"), "field");
|
|
602
|
-
_.each(data, function (n, k) {
|
|
603
|
-
if (_.isNil(n)) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
const colDef = colDefs[k];
|
|
607
|
-
if (colDef) {
|
|
608
|
-
const fieldConfig = colDef.cellEditorParams.fieldConfig;
|
|
609
|
-
if (fieldConfig.type === "date") {
|
|
610
|
-
let isDateString = false;
|
|
611
|
-
if (typeof n === 'string') {
|
|
612
|
-
// 粘贴行数据过来时是字符串
|
|
613
|
-
n = parseDate(n);
|
|
614
|
-
isDateString = true;
|
|
615
|
-
}
|
|
616
|
-
if (!isValidDate(n)) {
|
|
617
|
-
fieldsVerificationErrors.push("字段“" + fieldConfig.label + "”必须是合法的日期格式!");
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
let utcDate = n;
|
|
621
|
-
if (!isDateString) {
|
|
622
|
-
// 设置为选中日期的 UTC 0 点
|
|
623
|
-
// 只有从日期控件输入的值需要做转换,从粘贴行数据过来的字符串格式不用处理时区,因为要求粘贴过来的只兼容 YYYY-MM-DD YYYY/MM/DD 两种格式
|
|
624
|
-
const timezoneOffset = n.getTimezoneOffset();
|
|
625
|
-
utcDate = new Date(n.getTime() - timezoneOffset * 60 * 1000);
|
|
626
|
-
}
|
|
627
|
-
data[k] = utcDate;
|
|
628
|
-
}
|
|
629
|
-
else if (fieldConfig.type === "number") {
|
|
630
|
-
if (typeof n === 'string') {
|
|
631
|
-
n = Number(n);
|
|
632
|
-
}
|
|
633
|
-
if (typeof n !== 'number' || _.isNaN(n)) {
|
|
634
|
-
fieldsVerificationErrors.push("字段“" + fieldConfig.label + "”必须是数字!");
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
let isPrecisionValid = checkNumberPrecision(n, fieldConfig.precision);
|
|
638
|
-
if (!isPrecisionValid) {
|
|
639
|
-
fieldsVerificationErrors.push("字段“" + fieldConfig.label + "”小数位数不能大于" + fieldConfig.precision || 0 + "!");
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
else if (fieldConfig.type === "select") {
|
|
644
|
-
if (typeof n !== 'string') {
|
|
645
|
-
fieldsVerificationErrors.push("字段“" + fieldConfig.label + "”是单选类型,只支持字符串!");
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
let isSelectValueValid = checkSelectValueValid(n, colDef.cellEditorParams.values || []);
|
|
649
|
-
if (!isSelectValueValid) {
|
|
650
|
-
fieldsVerificationErrors.push("字段“" + fieldConfig.label + "”是单选类型,请输入合法的选项值!");
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
// verifications校验
|
|
656
|
-
const rowNode = event.node;
|
|
657
|
-
const tableVerificationErrors = getTableVerificationErrors(data, table.verifications);
|
|
658
|
-
const verificationErrors = _.union(fieldsVerificationErrors, tableVerificationErrors);
|
|
659
|
-
let allValidated = verificationErrors.length === 0;
|
|
660
|
-
if (!allValidated) {
|
|
661
|
-
console.log("The table verifications is not passed for the row data:", table.verifications, data);
|
|
662
|
-
let editingCellsCount = event.api.getEditingCells().length;
|
|
663
|
-
if (editingCellsCount === 0) {
|
|
664
|
-
// 多行校验不通过时只开启第一行编辑状态
|
|
665
|
-
/*
|
|
666
|
-
// 从行编辑改为单元格编辑后,不再需要自动开启编辑状态
|
|
667
|
-
event.api.startEditingCell({
|
|
668
|
-
rowIndex: event.rowIndex,
|
|
669
|
-
colKey: allGridColumns[0].colId
|
|
670
|
-
});*/
|
|
671
|
-
}
|
|
672
|
-
rowNode.setData(Object.assign({}, data, { __verificationErrors: verificationErrors }));
|
|
673
|
-
alert(verificationErrors.join(`\n`));
|
|
674
|
-
console.log(verificationErrors.join(`\n`));
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
// 循环所有公式字段执行公式计算并设置值到data中
|
|
678
|
-
const isFormulaRunSuccess = setRowDataFormulaValues(data);
|
|
679
|
-
rowNode.setData(Object.assign({}, data, { __isFormulaFieldRunError: !isFormulaRunSuccess }));
|
|
680
|
-
if (!isFormulaRunSuccess) {
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
// 保存更新的数据到服务端
|
|
684
|
-
delete data.__verificationErrors;
|
|
685
|
-
delete data.__isFormulaFieldRunError;
|
|
686
|
-
const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId + '/' + data._id, {
|
|
687
|
-
method: 'PUT',
|
|
688
|
-
headers: {
|
|
689
|
-
'Content-Type': 'application/json'
|
|
690
|
-
},
|
|
691
|
-
body: JSON.stringify(data)
|
|
692
|
-
});
|
|
693
|
-
if (!response.ok) {
|
|
694
|
-
throw new Error('Server error! Status: ' + response.status);
|
|
695
|
-
}
|
|
696
|
-
const responseData = await response.json();
|
|
697
|
-
console.log('Data saved successfully:', responseData);
|
|
698
|
-
rowNode.setData(responseData);
|
|
699
|
-
} catch (error) {
|
|
700
|
-
console.error('Error saving data:', error);
|
|
701
|
-
alert('保存数据失败,请刷新浏览器以查看最新数据状态,并稍后重试。');
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function getServerSideDatasource() {
|
|
706
|
-
return {
|
|
707
|
-
getRows: function (params) {
|
|
708
|
-
console.log('Server Side Datasource - Requesting rows from server:', params.request);
|
|
709
|
-
// 模拟Promise来处理异步流程,尽量贴近async/await效果
|
|
710
|
-
return new Promise(function (resolve, reject) {
|
|
711
|
-
try {
|
|
712
|
-
var colDefs = _.keyBy(_.map(params.api.getAllGridColumns(), function (col) { return col.colDef; }), "field");
|
|
713
|
-
var modelFilters = filterModelToOdataFilters(params.request.filterModel, colDefs);
|
|
714
|
-
console.log('Server Side Datasource - Requesting rows by modelFilters:', modelFilters);
|
|
715
|
-
var url = B6_TABLES_API + '/' + baseId + '/' + tableId;
|
|
716
|
-
// 翻页
|
|
717
|
-
var startRow = params.request.startRow;
|
|
718
|
-
var endRow = params.request.endRow;
|
|
719
|
-
var pageSize = params.api.paginationGetPageSize();
|
|
720
|
-
|
|
721
|
-
var separator = url.indexOf('?') !== -1 ? '&' : '?';
|
|
722
|
-
url += separator + 'skip=' + startRow + '&top=' + pageSize;
|
|
723
|
-
|
|
724
|
-
// 过滤
|
|
725
|
-
if (modelFilters.length > 0) {
|
|
726
|
-
separator = url.indexOf('?') !== -1 ? '&' : '?';
|
|
727
|
-
url += separator + 'filters=' + JSON.stringify(modelFilters);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// 排序
|
|
731
|
-
var sortModel = params.request.sortModel;
|
|
732
|
-
var sort = [];
|
|
733
|
-
_.forEach(sortModel, function (sortField) {
|
|
734
|
-
sort.push(sortField.colId + ' ' + sortField.sort);
|
|
735
|
-
});
|
|
736
|
-
console.log('Server Side Datasource - Requesting rows by sortModel:', sortModel);
|
|
737
|
-
if (sort.length > 0) {
|
|
738
|
-
separator = url.indexOf('?') !== -1 ? '&' : '?';
|
|
739
|
-
url += separator + 'sort=' + sort.join(",");
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
fetch(url).then(function (response) {
|
|
743
|
-
if (!response.ok) {
|
|
744
|
-
throw new Error('Server error! Status: ' + response.status);
|
|
745
|
-
}
|
|
746
|
-
return response.json();
|
|
747
|
-
}).then(function (data) {
|
|
748
|
-
console.log('Server Side Datasource - data:', data);
|
|
749
|
-
params.success({
|
|
750
|
-
rowData: data.data,
|
|
751
|
-
rowCount: data.totalCount
|
|
752
|
-
});
|
|
753
|
-
resolve();
|
|
754
|
-
}).catch(function (error) {
|
|
755
|
-
console.error('Error fetching data from server:', error);
|
|
756
|
-
alert('无法从服务器获取数据,请检查网络连接并重试。如果问题持续,请联系技术支持。');
|
|
757
|
-
params.fail();
|
|
758
|
-
reject(error);
|
|
759
|
-
});
|
|
760
|
-
} catch (error) {
|
|
761
|
-
console.error('Error fetching data from server:', error);
|
|
762
|
-
alert('发生了意外错误。请重试或联系技术支持。');
|
|
763
|
-
params.fail();
|
|
764
|
-
reject(error);
|
|
765
|
-
}
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
function scrollToBottomAfterRefresh() {
|
|
772
|
-
const rowCount = gridApi.getDisplayedRowCount();
|
|
773
|
-
if (rowCount > 0) {
|
|
774
|
-
// Scroll to the last row
|
|
775
|
-
gridApi.ensureIndexVisible(rowCount - 1, 'bottom');
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
// Remove the event listener to prevent scrolling on subsequent updates
|
|
779
|
-
gridApi.removeEventListener('storeRefreshed', scrollToBottomAfterRefresh);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// 新增行的功能
|
|
783
|
-
async function addNewRow() {
|
|
784
|
-
if (!gridOptions) {
|
|
785
|
-
console.error('Grid options not available. Ensure grid is initialized properly.');
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// 有排序和过滤条件情况下不允许新建数据,因为新建后不知道是哪一行
|
|
790
|
-
const gridState = gridApi.getState();
|
|
791
|
-
const sortState = gridState.sort;
|
|
792
|
-
const filterState = gridState.filter;
|
|
793
|
-
if (!_.isEmpty(sortState)) {
|
|
794
|
-
alert("请先移除排序");
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
if (!_.isEmpty(filterState)) {
|
|
799
|
-
alert("请先移除过滤条件");
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
const newRow = createNewRowData();
|
|
804
|
-
if (!newRow) {
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// 将新增数据发送到服务器
|
|
809
|
-
try {
|
|
810
|
-
const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId, {
|
|
811
|
-
method: 'POST',
|
|
812
|
-
headers: {
|
|
813
|
-
'Content-Type': 'application/json'
|
|
814
|
-
},
|
|
815
|
-
body: JSON.stringify(newRow)
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
if (!response.ok) {
|
|
819
|
-
throw new Error('Server error! Status: ' + response.status);
|
|
820
|
-
}
|
|
821
|
-
console.log('New row added successfully');
|
|
822
|
-
// 新增数据成功后刷新网格数据
|
|
823
|
-
gridApi.addEventListener('storeRefreshed', scrollToBottomAfterRefresh);
|
|
824
|
-
gridApi.refreshServerSide({ purge: false });//purge设置为true会造成上面scrollToBottomAfterRefresh不生效
|
|
825
|
-
|
|
826
|
-
} catch (error) {
|
|
827
|
-
console.error('Error adding new row:', error);
|
|
828
|
-
alert('新增行时发生错误,请稍后重试。');
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
function getAllRowData() {
|
|
833
|
-
const rowData = [];
|
|
834
|
-
const rowCount = gridApi.getDisplayedRowCount();
|
|
835
|
-
|
|
836
|
-
for (let i = 0; i < rowCount; i++) {
|
|
837
|
-
const rowNode = gridApi.getDisplayedRowAtIndex(i);
|
|
838
|
-
rowData.push(rowNode.data);
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
return rowData;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function getSelectedRowData() {
|
|
845
|
-
// Get the selected nodes and extract their data
|
|
846
|
-
const gridState = gridApi.getState();
|
|
847
|
-
const rowSelectionState = gridState.rowSelection;
|
|
848
|
-
const isSelectAll = rowSelectionState.selectAll;
|
|
849
|
-
const toggledNodes = rowSelectionState.toggledNodes;
|
|
850
|
-
if (isSelectAll) {
|
|
851
|
-
// 用户勾选了表头全选勾选框时,gridApi.getSelectedNodes()取不到数据,这里手动获取列表上的行数据
|
|
852
|
-
// gridApi.getState().rowSelection.toggledNodes 中记录了全选时用户取消了哪些选项的id值集合
|
|
853
|
-
let allRowData = getAllRowData();
|
|
854
|
-
if (_.isEmpty(toggledNodes)) {
|
|
855
|
-
return allRowData;
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
const selectedData = allRowData.filter(dataItem => toggledNodes.indexOf(dataItem._id) < 0);
|
|
859
|
-
return selectedData;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
else {
|
|
863
|
-
const selectedNodes = gridApi.getSelectedNodes();
|
|
864
|
-
const selectedData = selectedNodes.map(node => node.data);
|
|
865
|
-
return selectedData;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
async function deleteSelectedRows() {
|
|
870
|
-
if (!gridOptions) {
|
|
871
|
-
console.error('Grid options not available. Ensure grid is initialized properly.');
|
|
872
|
-
return;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
const selectedData = getSelectedRowData();
|
|
876
|
-
const selectedIds = selectedData.map(data => data._id);
|
|
877
|
-
console.log('Deleting Rows:', selectedIds);
|
|
878
|
-
|
|
879
|
-
// Check if any rows are selected
|
|
880
|
-
if (selectedIds.length === 0) {
|
|
881
|
-
alert('没有选中任何行!');
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
try {
|
|
886
|
-
/* 后续支持批量删除接口可以改为只调用一次删除接口
|
|
887
|
-
// Call the API to delete the selected rows
|
|
888
|
-
const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId, {
|
|
889
|
-
method: 'DELETE',
|
|
890
|
-
headers: {
|
|
891
|
-
'Content-Type': 'application/json'
|
|
892
|
-
},
|
|
893
|
-
body: JSON.stringify({ ids: selectedIds })
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
const data = await response.json();
|
|
897
|
-
|
|
898
|
-
if (data.success) {
|
|
899
|
-
// Remove the selected rows from the grid
|
|
900
|
-
gridApi.applyTransaction({ remove: selectedData });
|
|
901
|
-
} else {
|
|
902
|
-
alert('删除行时发生错误');
|
|
903
|
-
}*/
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
for (const data of selectedData) {
|
|
907
|
-
const id = data._id;
|
|
908
|
-
// Call the API to delete each selected row
|
|
909
|
-
const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId + '/' + id, {
|
|
910
|
-
method: 'DELETE',
|
|
911
|
-
headers: {
|
|
912
|
-
'Content-Type': 'application/json'
|
|
913
|
-
}
|
|
914
|
-
});
|
|
915
|
-
|
|
916
|
-
if (!response.ok) {
|
|
917
|
-
throw new Error('Server error! Status: ' + response.status);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
const result = await response.json();
|
|
921
|
-
|
|
922
|
-
if (result.deleted) {
|
|
923
|
-
// Remove the row from the grid only if the deletion was successful
|
|
924
|
-
gridApi.applyServerSideTransaction({ remove: [data] });
|
|
925
|
-
} else {
|
|
926
|
-
alert("删除 ID 为 " + id + " 的行失败");
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
} catch (error) {
|
|
930
|
-
console.error('Error deleting rows:', error);
|
|
931
|
-
alert('删除行时发生错误,请稍后重试。');
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
function runAmisFormula(formula, data, catchBack, options) {
|
|
936
|
-
// evalMode为true表示公式表达式中不需要写外层${}
|
|
937
|
-
try {
|
|
938
|
-
return evaluate(formula, data, Object.assign({ evalMode: true }, options));
|
|
939
|
-
}
|
|
940
|
-
catch (ex) {
|
|
941
|
-
typeof catchBack === "function" && catchBack(ex);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
function setRowDataFormulaValues(rowData) {
|
|
946
|
-
// 循环所有公式字段执行公式计算并设置值到rowData中
|
|
947
|
-
var dataTypeDefinitions = gridApi.getGridOption("dataTypeDefinitions");
|
|
948
|
-
var formulaFields = dataTypeDefinitions.formula.fields;
|
|
949
|
-
var success = true;
|
|
950
|
-
|
|
951
|
-
for (var formulaFieldName in formulaFields) {
|
|
952
|
-
if (formulaFields.hasOwnProperty(formulaFieldName)) {
|
|
953
|
-
var formulaField = formulaFields[formulaFieldName];
|
|
954
|
-
var formula = formulaField.formula;
|
|
955
|
-
var formulaValue = runAmisFormula(formula, rowData, function (ex) {
|
|
956
|
-
console.warn("公式字段“" + formulaField.label + "”执行公式”" + formula + "“出错了,请检查公式配置:", ex);
|
|
957
|
-
alert("公式字段“" + formulaField.label + "”执行公式”" + formula + "“出错了,请检查公式配置:" + (ex && ex.toString()));
|
|
958
|
-
success = false;
|
|
959
|
-
});
|
|
960
|
-
// 检查结果是否为数值
|
|
961
|
-
if (typeof formulaValue === 'number') {
|
|
962
|
-
// 四舍五入并保留两位小数
|
|
963
|
-
formulaValue = Math.round(formulaValue * 100) / 100;
|
|
964
|
-
}
|
|
965
|
-
console.log("ag-grid amis formula run:", formulaField.label, formula, formulaValue);
|
|
966
|
-
rowData[formulaFieldName] = formulaValue;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
return success;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// 创建一个新行数据,可以初始化为默认值或空值
|
|
974
|
-
function createNewRowData() {
|
|
975
|
-
const newRow = {};
|
|
976
|
-
// 可以给每个字段一个默认值,例如:
|
|
977
|
-
gridOptions.columnDefs.forEach(colDef => {
|
|
978
|
-
const dfValue = getFieldDefaultValue(colDef); // 设置为空或设置默认值
|
|
979
|
-
if (typeof dfValue != "undefined") {
|
|
980
|
-
newRow[colDef.field] = dfValue;
|
|
981
|
-
}
|
|
982
|
-
});
|
|
983
|
-
|
|
984
|
-
// 设置默认值后需要进行依赖字段默认值的公式计算
|
|
985
|
-
const isFormulaRunSuccess = setRowDataFormulaValues(newRow);
|
|
986
|
-
if (!isFormulaRunSuccess) {
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
return newRow;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
function getFieldDefaultValue(colDef) {
|
|
993
|
-
const fieldConfig = colDef.cellEditorParams.fieldConfig;
|
|
994
|
-
const defaultValue = fieldConfig.default_value;
|
|
995
|
-
if (typeof defaultValue !== "undefined") {
|
|
996
|
-
return defaultValue;
|
|
997
|
-
}
|
|
998
|
-
if (fieldConfig.type === "boolean") {
|
|
999
|
-
return false;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
const FilterTypesMap = {
|
|
1004
|
-
'equals': '=',
|
|
1005
|
-
'notEqual': '!=',
|
|
1006
|
-
'contains': 'contains',
|
|
1007
|
-
'notContains': 'notcontains',
|
|
1008
|
-
'startsWith': 'startswith',
|
|
1009
|
-
'endsWith': 'endswith',
|
|
1010
|
-
'lessThan': '<',
|
|
1011
|
-
'lessThanOrEqual': '<=',
|
|
1012
|
-
'greaterThan': '>',
|
|
1013
|
-
'greaterThanOrEqual': '>=',
|
|
1014
|
-
'empty': 'empty' //TODO 不支持
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
/**
|
|
1018
|
-
* 把ag-grid filterModel 转为魔方filters格式
|
|
1019
|
-
* @param filterModel
|
|
1020
|
-
*/
|
|
1021
|
-
function filterModelToOdataFilters(filterModel, colDefs) {
|
|
1022
|
-
const filters = [];
|
|
1023
|
-
_.forEach(filterModel, (value, key) => {
|
|
1024
|
-
const fieldConfig = colDefs[key].cellEditorParams.fieldConfig;
|
|
1025
|
-
if (value.type === 'between') {
|
|
1026
|
-
if (value.filterType === "number") {
|
|
1027
|
-
filters.push([key, "between", [value.numberFrom, value.numberTo]]);
|
|
1028
|
-
} else {
|
|
1029
|
-
if (value.filter) {
|
|
1030
|
-
filters.push([key, value.type, value.filter]);
|
|
1031
|
-
} else {
|
|
1032
|
-
filters.push([key, "between", [value.dateFrom, value.dateTo]]);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
} else {
|
|
1037
|
-
let filterItem;
|
|
1038
|
-
switch (fieldConfig.type) {
|
|
1039
|
-
case 'text':
|
|
1040
|
-
case 'textarea':
|
|
1041
|
-
filterItem = [key, FilterTypesMap[value.type], value.filter];
|
|
1042
|
-
filters.push(filterItem);
|
|
1043
|
-
break;
|
|
1044
|
-
case 'number':
|
|
1045
|
-
filterItem = [key, FilterTypesMap[value.type], value.filter];
|
|
1046
|
-
filters.push(filterItem);
|
|
1047
|
-
break;
|
|
1048
|
-
case 'select':
|
|
1049
|
-
case 'select-multiple':
|
|
1050
|
-
// 因为不需要支持多选,这里先不处理,如果要支持多选使用anyof过滤操作符应该就可以了,比如["category", "anyof", selectedCategories]
|
|
1051
|
-
const filterValues = value.values;
|
|
1052
|
-
if (filterValues.length) {
|
|
1053
|
-
let filterItem = [];
|
|
1054
|
-
for (let i = 0; i < filterValues.length; i++) {
|
|
1055
|
-
filterItem.push([key, "=", filterValues[i]]);
|
|
1056
|
-
if (i < filterValues.length - 1) {
|
|
1057
|
-
filterItem.push("or");
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
filters.push(filterItem);
|
|
1061
|
-
}
|
|
1062
|
-
break;
|
|
1063
|
-
case 'date':
|
|
1064
|
-
case 'datetime':
|
|
1065
|
-
let dateValue = new Date(value.dateFrom);
|
|
1066
|
-
if (fieldConfig.type === "date") {
|
|
1067
|
-
// 设置为日期的 UTC 0 点
|
|
1068
|
-
const timezoneOffset = dateValue.getTimezoneOffset();
|
|
1069
|
-
dateValue = new Date(dateValue.getTime() - timezoneOffset * 60 * 1000);
|
|
1070
|
-
}
|
|
1071
|
-
filterItem = [key, FilterTypesMap[value.type], dateValue];
|
|
1072
|
-
filters.push(filterItem);
|
|
1073
|
-
break;
|
|
1074
|
-
case 'boolean':
|
|
1075
|
-
let filterValue = value.values[0];
|
|
1076
|
-
if (typeof filterValue !== "boolean") {
|
|
1077
|
-
filterValue = filterValue === "true"
|
|
1078
|
-
}
|
|
1079
|
-
filterItem = [key, "=", filterValue];
|
|
1080
|
-
filters.push(filterItem);
|
|
1081
|
-
break;
|
|
1082
|
-
case 'formula':
|
|
1083
|
-
// 不支持公式字段过滤
|
|
1084
|
-
break;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
})
|
|
1088
|
-
return filters;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
// 在文档加载后初始化表格
|
|
1092
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
1093
|
-
initializeGrid();
|
|
1094
|
-
});
|
|
1095
|
-
</script>
|
|
1096
|
-
|
|
1097
|
-
</body>
|
|
1098
|
-
|
|
1099
|
-
</html>
|