ru.coon 2.6.23 → 2.7.0
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/CHANGELOG.md +28 -0
- package/package.json +1 -1
- package/src/common/field/ForeignField.js +1 -1
- package/src/common/panel/WindowWrap.js +9 -0
- package/src/report/component/ReportPanel.js +42 -1
- package/src/report/component/reportpanel/NorthPanel.js +3 -0
- package/src/report/component/reportpanel/ReportGrid.scss +4 -0
- package/src/report/component/settings/property/ReportPropertiesPanel.js +0 -1
- package/src/report/component/settings/property/ReportPropertyDictionary.js +11 -0
- package/src/report/plugin/configPanel/AddFilterConditionPluginConfigPanel.js +195 -0
- package/src/report/plugin/configPanel/AddFilterConditionPluginConfigPanel.scss +53 -0
- package/src/report/plugin/grid/AddFilterConditionPlugin.js +178 -0
- package/src/report/plugin/grid/GridContextMenu.js +50 -12
- package/src/uielement/component/formchips/Chip.js +35 -5
- package/src/uielement/component/formchips/FilterConditionToolbar.js +59 -21
- package/src/uielement/component/formchips/FilterConditionToolbar.scss +29 -8
- package/src/uielement/component/formchips/FilterConditionToolbarController.js +235 -72
- package/src/version.js +1 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Для правильного инстанцирования компонента
|
|
3
|
-
* Панель, содержащая поля формы поиска, должна иметь itemId searchFormItemId
|
|
2
|
+
* Для правильного инстанцирования компонента в кастомной панели нужно чтобы панель, содержащая поля формы поиска, имела itemId searchFormItemId.
|
|
4
3
|
*/
|
|
5
4
|
Ext.define('Coon.uielement.component.formchips.FilterConditionToolbarController', {
|
|
6
5
|
extend: 'Ext.app.ViewController',
|
|
@@ -15,86 +14,196 @@ Ext.define('Coon.uielement.component.formchips.FilterConditionToolbarController'
|
|
|
15
14
|
*/
|
|
16
15
|
searchFormItemId: 'searchFormItemId',
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
initToolbar: function() {
|
|
18
|
+
const reportPanel = this.getView().findParentByType('ReportPanel');
|
|
19
|
+
|
|
20
|
+
if (reportPanel) { // В репорте
|
|
21
|
+
this.searchForm = reportPanel.northPanel;
|
|
22
|
+
this.getView().on('rememberconditions', function() {
|
|
23
|
+
this.setLastSearchFields(this.getCompletedFields(this.getFilterPanel()));
|
|
24
|
+
this.onReloadChipsHandler();
|
|
25
|
+
}, this);
|
|
26
|
+
|
|
27
|
+
this.searchForm.on('boxready', function() {
|
|
28
|
+
if (!reportPanel.getEnableChipToolbar()) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.addChangeReaction();
|
|
33
|
+
|
|
34
|
+
const owner = reportPanel.ownerCt;
|
|
35
|
+
if (owner.xtype === 'WindowWrap') {
|
|
36
|
+
owner.addToolBefore(this.getTool());
|
|
37
|
+
}
|
|
38
|
+
}, this);
|
|
39
|
+
|
|
40
|
+
reportPanel.on('filterchange', this.setReportGridFilters, this);
|
|
41
|
+
reportPanel.on('clearFilter', this.onReportFilterClear, this);
|
|
42
|
+
} else { // Реализация в кастомной панели
|
|
43
|
+
const mainPanel = this.getView().up();
|
|
44
|
+
this.searchForm = mainPanel ? mainPanel.query(`#${this.searchFormItemId}`)[0]: undefined;
|
|
45
|
+
this.getView().show();
|
|
46
|
+
if (mainPanel) {
|
|
47
|
+
mainPanel.addTool(this.getTool());
|
|
48
|
+
} else {
|
|
49
|
+
Coon.log.error('Для инициализации чип панели не найден контейнер формы или родительский контейнер');
|
|
50
|
+
}
|
|
51
|
+
// Слушаем событие, чтобы запомнить критерии поиска.
|
|
52
|
+
this.getView().on('rememberconditions', function() {
|
|
53
|
+
this.setLastSearchFields(this.getCompletedFields(this.searchForm));
|
|
54
|
+
this.onReloadChipsHandler();
|
|
55
|
+
}, this);
|
|
56
|
+
|
|
57
|
+
this.addChangeReaction();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.searchForm.on('hide', function() {
|
|
61
|
+
this.toggleButtons(false);
|
|
62
|
+
}, this);
|
|
63
|
+
|
|
64
|
+
this.searchForm.on('show', function() {
|
|
65
|
+
this.toggleButtons(true);
|
|
66
|
+
}, this);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
addChangeReaction: function() {
|
|
70
|
+
let fields = this.searchForm.query('field');
|
|
71
|
+
fields = (fields || []).concat(this.searchForm.query('fieldcontainer'));
|
|
72
|
+
fields.forEach(function(field) {
|
|
73
|
+
field.on('change', function(field) {
|
|
74
|
+
if (!this.usedForLastSearch(field)) {
|
|
75
|
+
this.onReloadChipsHandler();
|
|
76
|
+
}
|
|
77
|
+
}, this);
|
|
78
|
+
}, this);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Запоминаем поля, использованные для поиска последний раз.
|
|
83
|
+
* @param fields
|
|
84
|
+
*/
|
|
85
|
+
setLastSearchFields: function(fields) {
|
|
86
|
+
this.lastSearchFields = fields;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
usedForLastSearch: function(field) {
|
|
90
|
+
const id = field.id;
|
|
91
|
+
const value = field.value;
|
|
92
|
+
const found = (this.lastSearchFields || []).find((f) => f.fieldLink.id === id && f.value === value);
|
|
93
|
+
return !!found;
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Добавляем иконку-кнопку в хедер для коллапсирования панели поиска
|
|
98
|
+
*/
|
|
99
|
+
getTool: function() {
|
|
19
100
|
const me = this;
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
listeners: {
|
|
37
|
-
afterrender: function(tool) {
|
|
38
|
-
tool.setTooltip(tool.tooltipTexts.show);
|
|
39
|
-
},
|
|
101
|
+
const eyeTool = {
|
|
102
|
+
iconCls: 'x-fa fa-eye-slash tool-eye-horus',
|
|
103
|
+
cls: 'FilterConditionToolbar',
|
|
104
|
+
width: 20,
|
|
105
|
+
height: 16,
|
|
106
|
+
margin: '0 11 0 0',
|
|
107
|
+
tooltipTexts: {
|
|
108
|
+
show: 'скрыть панель фильтров',
|
|
109
|
+
hide: 'показать панель фильтров',
|
|
110
|
+
},
|
|
111
|
+
handler: function(event, toolEl, panelHeader, tool) {
|
|
112
|
+
me.toggleFilterPanel.call(me, tool);
|
|
113
|
+
},
|
|
114
|
+
listeners: {
|
|
115
|
+
afterrender: function(tool) {
|
|
116
|
+
tool.setTooltip(tool.tooltipTexts.show);
|
|
40
117
|
},
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Слушаем событие, чтобы запомнить критерии поиска.
|
|
46
|
-
this.getView().on('rememberconditions', this.setLastSearchFields, this);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return eyeTool;
|
|
47
122
|
},
|
|
48
123
|
|
|
49
|
-
|
|
50
|
-
|
|
124
|
+
getFilterPanel() {
|
|
125
|
+
if (!this.filterPanel) {
|
|
126
|
+
const reportPanel = this.getView().findParentByType('ReportPanel');
|
|
127
|
+
this.filterPanel = reportPanel && reportPanel.down('FilterPanel');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return this.filterPanel;
|
|
51
131
|
},
|
|
52
132
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Работает только когда компонент встроен в UI панель.
|
|
135
|
+
* @param tool
|
|
136
|
+
*/
|
|
137
|
+
toggleFilterPanel: function(tool) {
|
|
138
|
+
const newState = !this.searchForm.isHidden();
|
|
139
|
+
if (this.searchForm.isHidden()) {
|
|
58
140
|
tool.setIconCls('x-fa fa-eye-slash');
|
|
59
141
|
tool.setTooltip(tool.tooltipTexts.show);
|
|
60
142
|
} else {
|
|
61
|
-
this.getView().show();
|
|
62
|
-
this.northPanel.hide();
|
|
63
143
|
tool.setIconCls('x-fa fa-eye');
|
|
64
144
|
tool.setTooltip(tool.tooltipTexts.hide);
|
|
65
|
-
this.onReloadChipsHandler();
|
|
66
145
|
}
|
|
146
|
+
this.searchForm.setHidden(newState);
|
|
67
147
|
},
|
|
68
148
|
|
|
69
149
|
/**
|
|
70
|
-
*
|
|
150
|
+
* Заполнение тулбара чипами
|
|
71
151
|
*/
|
|
72
|
-
fillChipsHandler: function(
|
|
152
|
+
fillChipsHandler: function() {
|
|
153
|
+
this.fillRemoteChips();
|
|
154
|
+
this.fillLocalChips();
|
|
155
|
+
this.fillIfNoChips();
|
|
156
|
+
this.addButtons();
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
fillRemoteChips: function() {
|
|
73
160
|
const closableFilterCondition = this.getView().getClosableFilterCondition();
|
|
74
161
|
const fields = this.getAllFields();
|
|
75
|
-
const cont = this.
|
|
162
|
+
const cont = this.lookup('chipsContainer');
|
|
76
163
|
if (cont && fields.length) {
|
|
77
|
-
fields
|
|
78
|
-
|
|
79
|
-
unusable:
|
|
80
|
-
|
|
81
|
-
|
|
164
|
+
fields
|
|
165
|
+
.sort((a, b) => {
|
|
166
|
+
return (a.unusable === b.unusable)? 0 : a.unusable ? -1 : 1;
|
|
167
|
+
})
|
|
168
|
+
.forEach((field) => {
|
|
169
|
+
const cmp = new Coon.uielement.component.formchips.Chip({
|
|
170
|
+
unusable: field.unusable,
|
|
171
|
+
closable: closableFilterCondition,
|
|
172
|
+
data: field,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
cmp.on('needReloadChips', this.onReloadChipsHandler, this);
|
|
176
|
+
cmp.on('delete', function(field) {
|
|
177
|
+
this.getView().fireEvent('chipdelete', field);
|
|
178
|
+
}, this);
|
|
179
|
+
cont.add(cmp);
|
|
180
|
+
this.countRemoteChips++;
|
|
82
181
|
});
|
|
182
|
+
}
|
|
183
|
+
},
|
|
83
184
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
185
|
+
fillLocalChips: function() {
|
|
186
|
+
const cont = this.lookup('chipsContainer');
|
|
187
|
+
const filters = this.filters || [];
|
|
188
|
+
this.countLocalChips = 0;
|
|
189
|
+
filters.forEach(function(chipConfig) {
|
|
190
|
+
const cmp = new Coon.uielement.component.formchips.Chip(chipConfig);
|
|
191
|
+
cmp.on('needReloadChips', this.onReloadChipsHandler, this);
|
|
192
|
+
cont.add(cmp);
|
|
193
|
+
this.countLocalChips++;
|
|
194
|
+
}, this);
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
fillIfNoChips: function() {
|
|
198
|
+
if (!this.countRemoteChips && !this.countLocalChips) {
|
|
91
199
|
const cmp = new Coon.uielement.component.formchips.Chip({
|
|
92
200
|
service: true,
|
|
93
201
|
data: {
|
|
94
202
|
value: 'Нет критериев поиска',
|
|
95
203
|
},
|
|
96
204
|
});
|
|
97
|
-
|
|
205
|
+
|
|
206
|
+
this.lookup('chipsContainer').add(cmp);
|
|
98
207
|
}
|
|
99
208
|
},
|
|
100
209
|
|
|
@@ -105,33 +214,59 @@ Ext.define('Coon.uielement.component.formchips.FilterConditionToolbarController'
|
|
|
105
214
|
* @returns {[]}
|
|
106
215
|
*/
|
|
107
216
|
getAllFields: function() {
|
|
108
|
-
const ret = [
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
217
|
+
const ret = [];
|
|
218
|
+
const formPanel = this.getView().ownerCt;
|
|
219
|
+
const completedFields = this.getCompletedFields(formPanel) || [];
|
|
220
|
+
|
|
221
|
+
this.lastSearchFields.forEach((item) => {
|
|
222
|
+
item.unusable = false;
|
|
223
|
+
const found = completedFields.find((field) => item.fieldLink.id === field.fieldLink.id);
|
|
224
|
+
if (!found) {
|
|
225
|
+
// очистили поле
|
|
226
|
+
item.unusable = true;
|
|
227
|
+
} else {
|
|
228
|
+
if (item.value !== found.value) {
|
|
229
|
+
item.unusable = true;
|
|
118
230
|
}
|
|
119
|
-
}
|
|
120
|
-
|
|
231
|
+
}
|
|
232
|
+
ret.push(item);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Для получения новых заполненных полей
|
|
236
|
+
completedFields.forEach((field) => {
|
|
237
|
+
const found = this.lastSearchFields.find((ff) => ff.fieldLink === field.fieldLink);
|
|
238
|
+
if (!found) {
|
|
239
|
+
field.unusable = true;
|
|
240
|
+
ret.push(field);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
121
244
|
return ret;
|
|
122
245
|
},
|
|
123
246
|
|
|
124
247
|
/**
|
|
125
|
-
*
|
|
248
|
+
* Установить данные для чипов из фильтров
|
|
249
|
+
* @param {Array<Object>} filters
|
|
250
|
+
*/
|
|
251
|
+
setReportGridFilters(filters) {
|
|
252
|
+
this.filters = filters || [];
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
onReportFilterClear() {
|
|
256
|
+
this.setLastSearchFields([]);
|
|
257
|
+
this.onReloadChipsHandler();
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Получение заполненных в текущий момент полей формы.
|
|
126
262
|
* @returns {{fieldLink: *, label: *, value: *}[]}
|
|
127
263
|
*/
|
|
128
|
-
getCompletedFields: function() {
|
|
129
|
-
const formPanel = this.getView().ownerCt;
|
|
264
|
+
getCompletedFields: function(formPanel) {
|
|
130
265
|
let fields = formPanel.query('field');
|
|
131
266
|
fields = (fields || []).concat(formPanel.query('fieldcontainer'));
|
|
132
267
|
const retFields = fields.map((f) => {
|
|
133
268
|
let value;
|
|
134
|
-
if (f.getRawValue) {
|
|
269
|
+
if (Ext.isFunction(f.getRawValue)) {
|
|
135
270
|
value = f.getRawValue();
|
|
136
271
|
} else {
|
|
137
272
|
value = f.getValue();
|
|
@@ -151,8 +286,13 @@ Ext.define('Coon.uielement.component.formchips.FilterConditionToolbarController'
|
|
|
151
286
|
this.fillChipsHandler();
|
|
152
287
|
},
|
|
153
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Удаление всех чипов
|
|
291
|
+
*/
|
|
154
292
|
removeAllChips() {
|
|
155
|
-
this.
|
|
293
|
+
this.countRemoteChips = 0;
|
|
294
|
+
this.countLocalChips = 0;
|
|
295
|
+
this.lookup('chipsContainer').removeAll();
|
|
156
296
|
},
|
|
157
297
|
|
|
158
298
|
/**
|
|
@@ -166,4 +306,27 @@ Ext.define('Coon.uielement.component.formchips.FilterConditionToolbarController'
|
|
|
166
306
|
duration: 500,
|
|
167
307
|
});
|
|
168
308
|
},
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Добавляем дублирующие кнопки
|
|
312
|
+
*/
|
|
313
|
+
addButtons: function() {
|
|
314
|
+
this.toggleButtons(!this.searchForm.isHidden());
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Показать/спрятать кнопки
|
|
319
|
+
* @param {boolean} state
|
|
320
|
+
*/
|
|
321
|
+
toggleButtons: function(state) {
|
|
322
|
+
this.lookup('buttonsContainer').setHidden(state);
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
onClearButton: function() {
|
|
326
|
+
this.getView().fireEvent('clear');
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
onSearchButton: function() {
|
|
330
|
+
this.getView().fireEvent('needreload');
|
|
331
|
+
},
|
|
169
332
|
});
|
package/src/version.js
CHANGED