ru.coon 2.8.65 → 3.0.2

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.
Files changed (130) hide show
  1. package/CHANGELOG.md +319 -6
  2. package/package.json +1 -1
  3. package/src/Function.js +1 -1
  4. package/src/VisualLinker.js +610 -0
  5. package/src/VisualLinker.scss +219 -0
  6. package/src/app/Application.js +1 -0
  7. package/src/app/ApplicationSettings.js +49 -0
  8. package/src/app/Config.js +60 -0
  9. package/src/app/Router.js +37 -21
  10. package/src/app/viewPort/CVWrapperPanel.js +53 -0
  11. package/src/app/viewPort/CenterView.js +153 -46
  12. package/src/app/viewPort/CenterView.scss +151 -0
  13. package/src/app/viewPort/ComponentContextManager.js +24 -0
  14. package/src/app/viewPort/Main.js +4 -1
  15. package/src/app/viewPort/Routing.d2 +23 -0
  16. package/src/app/viewPort/TabHistory.js +81 -0
  17. package/src/common/ComponentFactory.js +167 -0
  18. package/src/common/button/DropdownContentButton.js +146 -0
  19. package/src/common/button/DropdownContentButton.scss +92 -0
  20. package/src/common/button/DropdownContentButtonController.js +60 -0
  21. package/src/common/component/editor/EditorFactory.js +3 -1
  22. package/src/common/component/formeditor/UiCustomFilterForm.scss +0 -1
  23. package/src/common/component/settings/modules/settingClosePageConfirmation.js +1 -3
  24. package/src/common/component/settings/modules/settingFavoritePanelShow.js +21 -0
  25. package/src/common/component/settings/modules/settingReportCalculator.js +22 -0
  26. package/src/common/component/settings/modules/settingShowNeedReloadMessage.js +1 -3
  27. package/src/common/component/settings/modules/settingwindowHolder.js +2 -3
  28. package/src/common/panel/WindowWrap.js +19 -11
  29. package/src/common/tree/BaseContextMenu.js +4 -3
  30. package/src/log.js +4 -5
  31. package/src/nav/AppNavCalcButton.js +23 -0
  32. package/src/nav/AppNavCalcButton.scss +6 -0
  33. package/src/nav/AppNavigationBar.js +1 -1
  34. package/src/nav/AppNavigationMenuController.js +2 -2
  35. package/src/nav/AppNavigationMenuMinimized.js +61 -0
  36. package/src/nav/AppNavigationPanel.js +115 -0
  37. package/src/nav/AppNavigationPanel.scss +178 -0
  38. package/src/nav/AppNavigationPanelController.js +386 -0
  39. package/src/nav/FavoriteCfg.js +18 -0
  40. package/src/nav/MenuEntity.js +23 -15
  41. package/src/nav/MenuFavoritesBar.js +100 -0
  42. package/src/nav/MenuFavoritesBar.scss +92 -0
  43. package/src/nav/editor/NavigateElementEditorView.js +2 -2
  44. package/src/nav/editor/workspace/NavWorkspaceListView.js +1 -4
  45. package/src/nav/menu/WorkspaceMenuViewMinimized.js +22 -0
  46. package/src/overrides/panel/TabPanel.js +36 -0
  47. package/src/overrides/panel/TabPanel.scss +65 -0
  48. package/src/report/component/ClearFiltersButton.js +4 -1
  49. package/src/report/component/CopyCellValueMenuItem.js +18 -0
  50. package/src/report/component/ReportFieldMap.js +274 -0
  51. package/src/report/component/ReportPanel.js +73 -44
  52. package/src/report/component/ReportPanel.scss +2 -2
  53. package/src/report/component/ReportTagLookup.js +59 -2
  54. package/src/report/component/calculator/ReportCalculatorController.js +266 -0
  55. package/src/report/component/calculator/ReportCalculatorField.js +47 -0
  56. package/src/report/component/calculator/ReportCalculatorHistoryPlugin.js +128 -0
  57. package/src/report/component/calculator/ReportCalculatorHistoryPlugin.scss +33 -0
  58. package/src/report/component/calculator/ReportCalculatorMenuItem.js +101 -0
  59. package/src/report/component/calculator/ReportCalculatorMenuItem.scss +41 -0
  60. package/src/report/component/calculator/ReportCalculatorPanel.js +363 -0
  61. package/src/report/component/calculator/ReportCalculatorPanel.scss +86 -0
  62. package/src/report/component/reportpanel/CopyReportPanelController.js +1 -1
  63. package/src/report/component/reportpanel/FilterPanel.js +13 -15
  64. package/src/report/component/reportpanel/FilterPanel.scss +5 -1
  65. package/src/report/component/reportpanel/FormFieldFocusPlugin.js +157 -0
  66. package/src/report/component/reportpanel/FormFieldFocusPlugin.scss +14 -0
  67. package/src/report/component/reportpanel/NorthPanel.js +16 -17
  68. package/src/report/component/reportpanel/NorthPanel.scss +1 -1
  69. package/src/report/component/reportpanel/ReportContextMenu.js +219 -0
  70. package/src/report/component/reportpanel/ReportGrid.js +1 -0
  71. package/src/report/component/reportpanel/ReportGrid.scss +4 -0
  72. package/src/report/component/settings/field/ReportFormFieldsGrid.js +2 -2
  73. package/src/report/component/settings/field/ReportFormFieldsGridController.js +2 -2
  74. package/src/report/component/settings/property/ReportPropertiesPanelController.js +1 -1
  75. package/src/report/component/settings/property/ReportPropertyDictionary.js +7 -0
  76. package/src/report/plugin/configPanel/AddFilterConditionPluginConfigPanel.js +34 -11
  77. package/src/report/plugin/configPanel/GridFiltersPluginConfigPanelFiltersGrid.js +3 -2
  78. package/src/report/plugin/configPanel/openCustomPanelButtonPlugin/OpenCustomPanelButtonPluginConfigPanel.js +274 -255
  79. package/src/report/plugin/configPanel/openCustomPanelButtonPlugin/OpenCustomPanelButtonPluginConfigPanel.scss +17 -0
  80. package/src/report/plugin/grid/AddFilterConditionPlugin.js +530 -87
  81. package/src/report/plugin/grid/CalculatorPlugin.js +90 -0
  82. package/src/report/plugin/grid/GridContextMenu.js +12 -10
  83. package/src/report/plugin/grid/GridRowCountPlugin.js +0 -1
  84. package/src/report/plugin/grid/GridToolbarButtonPlugin.js +11 -2
  85. package/src/report/plugin/grid/OpenCustomPanelButtonPlugin.js +126 -89
  86. package/src/report/plugin/grid/ReportCharacteristicBindPlugin.js +6 -4
  87. package/src/report/plugin/grid/ReportColumnStatePlugin.js +15 -3
  88. package/src/report/plugin/grid/SetSingleParameterPlugin.js +1 -1
  89. package/src/report/plugin/grid/ToolbarButtonPlugin.js +1 -1
  90. package/src/report/plugin/grid/addFilterConditionPlugin/AdvancedSearchPanel.js +55 -0
  91. package/src/report/plugin/grid/addFilterConditionPlugin/AdvancedSearchPanelController.js +226 -0
  92. package/src/report/plugin/grid/addFilterConditionPlugin/FilterConfigMixin.js +138 -0
  93. package/src/report/plugin/grid/addFilterConditionPlugin/FilterFieldFactory.js +223 -0
  94. package/src/report/plugin/grid/addFilterConditionPlugin/FilterItem.js +164 -0
  95. package/src/report/plugin/grid/addFilterConditionPlugin/FilterItem.scss +21 -0
  96. package/src/report/plugin/grid/addFilterConditionPlugin/FilterMenu.js +29 -0
  97. package/src/report/plugin/grid/addFilterConditionPlugin/FilterWrapPanel.js +53 -0
  98. package/src/report/plugin/grid/addFilterConditionPlugin/FilterWrapPanelController.js +57 -0
  99. package/src/report/plugin/grid/addFilterConditionPlugin/InfoMenuItem.js +111 -0
  100. package/src/report/plugin/grid/addFilterConditionPlugin/InfoMenuItem.scss +83 -0
  101. package/src/report/plugin/grid/addFilterConditionPlugin/SelectColumnPanel.js +102 -0
  102. package/src/report/selectionModels/MixedRowSelectionModel.js +36 -45
  103. package/src/report/toolbar/dropdown/ToolbarOverflowButton.js +16 -5
  104. package/src/report/toolbar/dropdown/ToolbarOverflowPanel.scss +26 -14
  105. package/src/report/toolbar/layout/ReportToolbarOverflow.js +5 -2
  106. package/src/ringBuffer.js +7 -3
  107. package/src/uielement/command/GetUIElementCommand.js +0 -1
  108. package/src/uielement/component/UiCPWrapper.js +26 -36
  109. package/src/uielement/component/UiCustomController.js +7 -0
  110. package/src/uielement/component/UiCustomPanel.js +46 -2
  111. package/src/uielement/component/settings/UiCustomPanelEditorController.js +35 -29
  112. package/src/uielement/component/settings/plugin/UiCustomPanelPluginGrid.js +146 -33
  113. package/src/uielement/component/settings/plugin/UiCustomPanelPluginGrid.scss +28 -0
  114. package/src/uielement/component/settings/plugin/UiCustomPanelPluginGridController.js +366 -22
  115. package/src/uielement/component/settings/plugin/UiCustomPanelPluginModel.js +1 -0
  116. package/src/uielement/component/settings/plugin/UiCustomPanelPluginPanel.js +10 -4
  117. package/src/uielement/component/settings/plugin/UiCustomPanelPluginPanelController.js +28 -30
  118. package/src/uielement/plugin/UnifiedButtonToolbarPlugin.js +203 -22
  119. package/src/uielement/plugin/configPanel/ExecuteFunctionPluginConfigPanelFormEditor.js +4 -36
  120. package/src/uielement/plugin/configPanel/FireEventPluginConfigPanelFormEditor.js +5 -36
  121. package/src/uielement/plugin/configPanel/MethodChainPluginConfigPanelFormEditor.js +4 -36
  122. package/src/uielement/plugin/configPanel/OpenPanelPluginConfigPanelFormEditor.js +2 -38
  123. package/src/uielement/plugin/configPanel/PrintPdfPluginConfigPanelFormEditor.js +2 -35
  124. package/src/uielement/plugin/configPanel/UiCPPluginFormPanel.js +54 -0
  125. package/src/uielement/plugin/configPanel/UnifiedButtonToolbarPluginConfigPanelFormEditor.js +28 -18
  126. package/src/userSettings.js +1 -0
  127. package/src/util/ContextManager.js +109 -0
  128. package/src/util.js +96 -15
  129. package/src/version.js +1 -1
  130. package/src/app/viewPort/CenterViewController.js +0 -158
@@ -7,63 +7,213 @@ Ext.define('Coon.report.plugin.grid.AddFilterConditionPlugin', {
7
7
  ],
8
8
  configurePanelWizard: 'AddFilterConditionPluginConfigPanel',
9
9
 
10
- filters: new Map(),
10
+ columnFilterTypes: undefined,
11
+ filters: undefined,
12
+ filterAllVisible: true,
13
+ filterOptions: {},
11
14
 
12
15
  init: function(grid) {
16
+ this.filters = new Map();
13
17
  this.grid = grid;
14
- this.grid.on('clearAllFilters', this.clearAllFilters, this);
15
- grid.on('added', function(grid) {
16
- const contextMenu = grid.contextMenu;
17
- contextMenu.add(new Ext.menu.Item({
18
- iconCls: 'svg-icon svg-icon-filter-alt',
19
- text: 'фильтр по значению',
20
- itemId: 'filterByCellValue',
21
- handler: this.addFilter.bind(this),
22
- }));
23
- contextMenu.add(new Ext.menu.Item({
24
- originalText: 'Отменить фильтр',
25
- hidden: true,
26
- itemId: 'cancelCurrentColumnFilter',
27
- handler: this.clearCurrentColumnFilter.bind(this),
28
- }));
29
- contextMenu.add(new Ext.menu.Item({
30
- text: 'Отменить все',
31
- itemId: 'cancelAllFilters',
32
- handler: this.clearAllFilters.bind(this),
33
- }));
34
- }, this);
35
- grid.on('load', function() {
36
- this.clearAllFilters();
37
- }, this);
18
+ this.columnFilterTypes = this.getColumnFilterTypes();
19
+ grid.on('clearAllFilters', this.clearAllFilters, this);
20
+ grid.on('added', this.addMenuItems, this);
21
+ grid.on('load', this.clearAllFilters, this);
38
22
  grid.contextMenu.on('requestMenuItems', this.onShowMenu, this);
39
23
  },
24
+ addMenuItems(grid) {
25
+ const contextMenu = grid.contextMenu;
26
+ const infoMenuItem = Ext.widget('InfoMenuItem', {
27
+ itemId: 'infoMenuItem',
28
+ originalText: '', // 'Фильтр не задан',
29
+ forbiddenText: 'Фильтр не разрешен',
30
+ isItemHidden: this.isInfoMenuItemHidden.bind(this),
31
+ isItemDisabled: this.isInfoMenuItemDisabled.bind(this),
32
+ });
33
+ infoMenuItem.on('removefilter', function() {
34
+ this.clearCurrentColumnFilter();
35
+ contextMenu.hide();
36
+ }, this);
37
+ contextMenu.addToAdvancedSearch(infoMenuItem);
38
+ contextMenu.addToAdvancedSearch(new Ext.menu.Item({
39
+ text: 'Фильтр по значению',
40
+ itemId: 'filterByCellValue',
41
+ handler: this.filterByCellValue.bind(this),
42
+ isItemHidden: this.isFilterByCellValueHidden.bind(this),
43
+ isItemDisabled: this.isFilterByCellValueDisabled.bind(this),
44
+ }));
45
+ const filterMenu = Ext.widget('FilterMenu', {
46
+ listeners: {
47
+ beforeshow: this.createSearchMenuItem.bind(this),
48
+ resize: this.onResizeSearchSubMenu.bind(this),
49
+ },
50
+ });
51
+
52
+ contextMenu.addToAdvancedSearch({
53
+ text: 'Поиск',
54
+ iconCls: 'svg-icon svg-icon-magnifying-glass',
55
+ itemId: 'searchByValueItem',
56
+ menu: filterMenu,
57
+ isItemHidden: this.isSearchByValueItemHidden.bind(this),
58
+ isItemDisabled: this.isSearchByValueItemDisabled.bind(this),
59
+ });
60
+ // contextMenu.addToAdvancedSearch(new Ext.menu.Item({
61
+ // text: 'Расширенный поиск',
62
+ // itemId: 'advancedSearch',
63
+ // handler: this.advancedSearch.bind(this),
64
+ // isItemHidden: () => false,
65
+ // isItemDisabled: () => false,
66
+ // }));
67
+ contextMenu.addToAdvancedSearch(new Ext.menu.Item({
68
+ text: 'Очистить все фильтры',
69
+ itemId: 'cancelAllFilters',
70
+ handler: this.clearAllFilters.bind(this),
71
+ isItemHidden: this.isCancelAllFiltersHidden.bind(this),
72
+ isItemDisabled: this.isCancelAllFiltersDisabled.bind(this),
73
+ }));
74
+ },
75
+
76
+ getColumnFilterTypes() {
77
+ const columnFilterTypes = {};
78
+ this.grid.getColumns().forEach((column) => {
79
+ if (column.dataIndex && column.filter) {
80
+ columnFilterTypes[column.dataIndex] = column.filter && column.filter.type;
81
+ }
82
+ });
83
+ return columnFilterTypes;
84
+ },
85
+
86
+ canUseCombo() {
87
+ const col = this.column;
88
+ const info = this.grid.reportPanel.serviceMap.maps.get(col.dataIndex);
89
+ // Можно использовать как список
90
+ const canUsedAsListFilter = Ext.isObject(info) && !info.isExtremum && !info.isFull;
91
+ return !!(canUsedAsListFilter && col.filter.type === 'string');
92
+ },
93
+
94
+ onResizeSearchSubMenu(menu) {
95
+ const searchByValueItem = this.findMenuItem('searchByValueItem');
96
+ menu.alignTo(searchByValueItem, menu._lastAlignToPos);
97
+ },
98
+
99
+ getGridColumns() {
100
+ return this.grid.getVisibleColumns();
101
+ },
40
102
 
41
103
  onShowMenu({record, cellIndex}) {
42
- const cancelAllItem = this.findMenuItem('cancelAllFilters');
43
- cancelAllItem && cancelAllItem.setDisabled(this.filters.size === 0);
44
- if (this.filters.size) {
45
- this.findMenuItem('cancelCurrentColumnFilter').hide();
46
- }
47
- this.findMenuItem('filterByCellValue').hide();
48
- record && this.setMenuText(record, cellIndex);
104
+ const columns = this.grid.getColumns();
105
+ const column = columns.length && columns[cellIndex];
106
+ this.column = column;
107
+ this.columnValue = record && record.get(column.dataIndex);
108
+ this.setMenuText();
109
+ },
110
+
111
+ filterByCellValue: function() {
112
+ const key = this.getColumnKey();
113
+ this.addFilter(key, 'eq', this.columnValue);
114
+ this.doFilter();
115
+ this.changeColumnStyle(this.column);
49
116
  },
50
117
 
51
- addFilter: function() {
118
+ addFilter: function(key, operator, value) {
52
119
  if (!this.grid) {
53
120
  return;
54
121
  }
55
- const key = this.getColumnKey();
56
- this.filters.set(key, this.columnValue);
122
+ this.filters.set(key, {
123
+ value,
124
+ operator,
125
+ });
126
+ },
127
+
128
+ getSearchPanel() {
129
+ const panel = Ext.widget('AdvancedSearchPanel', {
130
+ reportGrid: this.grid,
131
+ filterAllVisible: this.filterAllVisible,
132
+ filterOptions: this.filterOptions,
133
+ });
134
+ const win = Ext.widget('WindowWrap', {
135
+ maximizable: false,
136
+ resizable: false,
137
+ autoShow: true,
138
+ closeAction: 'close',
139
+ width: 600,
140
+ minHeight: 300,
141
+ alwaysInCenter: true,
142
+ items: panel,
143
+ });
144
+
145
+ panel.on('addfilters', this.addFilters, this);
146
+ panel.on('addremovefilter', () => {
147
+ win.center();
148
+ });
149
+
150
+ return panel;
151
+ },
152
+
153
+ addFilters(filters) {
154
+ Coon.log.log('addfilters', filters);
155
+ this.filters.clear();
156
+ if (Array.isArray(filters) && filters.length) {
157
+ const reversed = Ext.Array.clone(filters).reverse();
158
+ reversed.forEach(function(filter) {
159
+ this.addFilter(filter.dataIndex, filter.operator, filter.value);
160
+ }, this);
161
+ }
57
162
  this.doFilter();
58
- this.changeColumnStyle(this.column);
163
+ this.changeColumnsStyle();
164
+ },
165
+
166
+ // Расширенный поиск
167
+ advancedSearch() {
168
+ const usedFilterConfig = {};
169
+ this.filters.forEach(function(value, key) {
170
+ usedFilterConfig[key] = {
171
+ isCurrent: this.column && this.column.dataIndex === key,
172
+ dataIndex: key,
173
+ type: this.columnFilterTypes[key],
174
+ operator: value.operator,
175
+ value: value.value,
176
+ };
177
+ }, this);
178
+ const isCurrentColumnFilterable = this.isFilterable();
179
+ const panel = this.getSearchPanel();
180
+
181
+ const newFilterConfig = {};
182
+ if (this.column) {
183
+ const dataIndex = this.column.dataIndex;
184
+ newFilterConfig.value = this.columnValue;
185
+ newFilterConfig.isCurrentColumnFilterable = isCurrentColumnFilterable;
186
+ newFilterConfig.currentColumnDataIndex = dataIndex;
187
+ const filterType = this.columnFilterTypes[dataIndex];
188
+ if (filterType === 'string') {
189
+ newFilterConfig.useCombo = /* this.filters.size === 0 &&*/ this.canUseCombo();
190
+ }
191
+ }
192
+
193
+ panel.getController().doInit(usedFilterConfig, newFilterConfig);
59
194
  },
60
195
 
61
196
  changeColumnStyle(column) {
62
197
  if (this.filters.has(column.dataIndex)) {
198
+ if (!column.tdCls.includes('filtered-cell')) {
199
+ column.tdCls = column.tdCls + ' filtered-cell';
200
+ }
63
201
  column.addCls('filtered-column');
64
202
  } else {
203
+ column.tdCls = Ext.isString(column.tdCls) && column.tdCls.replace('filtered-cell', ' ');
65
204
  column.removeCls('filtered-column');
66
205
  }
206
+ this.grid.getView().refresh();
207
+ },
208
+
209
+ changeColumnsStyle() {
210
+ this.grid.getColumns().forEach(function(column) {
211
+ const dataIndex = column.dataIndex;
212
+ if (!dataIndex) {
213
+ return;
214
+ }
215
+ this.changeColumnStyle(column);
216
+ }, this);
67
217
  },
68
218
 
69
219
  doFilter() {
@@ -72,31 +222,158 @@ Ext.define('Coon.report.plugin.grid.AddFilterConditionPlugin', {
72
222
  this.grid.getStore().clearFilter();
73
223
  this.grid.getStore().resumeEvents();
74
224
  this.grid.getStore().filter(
75
- [...this.filters.entries()].map(function([property, value]) {
76
- if (Ext.isString(value) || Ext.isNumber(value)) {
77
- return {property, operator: '=', value};
78
- } else if (Ext.isDate(value)) {
79
- return new Ext.util.Filter({
80
- filterFn: function(rec) {
81
- return Ext.Date.format(rec.get(property), Coon.format.dateTime) === Ext.Date.format(value, Coon.format.dateTime);
82
- },
83
- }, this);
84
- } else {
85
- return new Ext.util.Filter({
86
- filterFn: function(rec) {
87
- return rec.get(property) === value;
88
- },
89
- }, this);
90
- }
91
- }, this)
225
+ this.createFilterConfiguration()
92
226
  );
93
227
  } else {
94
228
  this.grid.getStore().clearFilter();
95
229
  }
230
+ this.rebuildServiceMap();
231
+ },
232
+
233
+ rebuildServiceMap() {
234
+ const serviceMap = this.grid.reportPanel.serviceMap;
235
+ if (!serviceMap) {
236
+ return;
237
+ }
238
+ const myMask = new Ext.LoadMask({
239
+ target: this.grid,
240
+ msg: 'Перестройка справочников...',
241
+ });
242
+ myMask.show();
243
+ serviceMap.clearData.call(serviceMap);
244
+ this.grid.getStore().each((record) => {
245
+ const fn = serviceMap.getConvert();
246
+ if (Ext.isFunction(fn)) {
247
+ fn.call(serviceMap, undefined, record);
248
+ }
249
+ });
250
+ myMask.hide();
96
251
  },
97
252
 
98
- isColumnFiltered() {
99
- return this.filters.has(this.getColumnKey());
253
+ prepareFilters() {
254
+ const newFilters = [];
255
+ for (const [property, filterCfg] of [...this.filters.entries()]) {
256
+ const operator = filterCfg.operator;
257
+ const value = filterCfg.value;
258
+ if (operator === 'between') {
259
+ const fromValue = value[0];
260
+ const toValue = value[1];
261
+ const newCfg = Ext.clone(filterCfg);
262
+ if (fromValue) {
263
+ newCfg.id = property + '_from';
264
+ newCfg.operator = 'ge';
265
+ newCfg.value = fromValue;
266
+ newFilters.push([property, newCfg]);
267
+ }
268
+ const newCfgTo = Ext.clone(filterCfg);
269
+ if (toValue) {
270
+ newCfg.id = property + '_to';
271
+ newCfgTo.operator = 'le';
272
+ newCfgTo.value = toValue;
273
+ newFilters.push([property, newCfgTo]);
274
+ }
275
+ } else {
276
+ newFilters.push([property, filterCfg]);
277
+ }
278
+ }
279
+ return newFilters;
280
+ },
281
+
282
+ createFilterConfiguration() {
283
+ const store = this.grid.getStore();
284
+ const fields = store.config.fields;
285
+ const getFieldType = function(dataIndex) {
286
+ const found = Array.isArray(fields) && fields.find((field) => field.name === dataIndex);
287
+ return found && found.type;
288
+ };
289
+
290
+ return this.prepareFilters().map(function([property, filterCfg]) {
291
+ const value = filterCfg.value;
292
+ const id = filterCfg.id;
293
+ const operator = filterCfg.operator;
294
+ const column = this.grid.getColumns().find((column) => column.dataIndex===property);
295
+ const filterType = this.columnFilterTypes[property];
296
+ const fieldType = getFieldType(column.dataIndex);
297
+ if (operator === 'notfilled') {
298
+ return new Ext.util.Filter({
299
+ filterFn: (rec) => {
300
+ return Ext.isEmpty(rec.get(property));
301
+ },
302
+ });
303
+ } else if (filterType === 'list' && Array.isArray(value)) {
304
+ return {property, operator, value};
305
+ } else if (operator === 'likefrom') {
306
+ return new Ext.util.Filter({
307
+ filterFn: function(rec) {
308
+ return Ext.isString(rec.get(property)) && rec.get(property).startsWith(value);
309
+ },
310
+ });
311
+ } else if (filterType === 'numeric') {
312
+ return this.createNumberFilter(property, operator, value, fieldType, id);
313
+ } else if (Ext.isDate(value) && filterType === 'date') {
314
+ return this.createDateFilter(property, operator, value, fieldType, id);
315
+ } else if (Ext.isString(value) || Ext.isNumber(value)) {
316
+ return {property, operator: operator || '=', value};
317
+ } else {
318
+ return new Ext.util.Filter({
319
+ filterFn: function(rec) {
320
+ return rec.get(property) === value;
321
+ },
322
+ });
323
+ }
324
+ }, this);
325
+ },
326
+
327
+ /**
328
+ * Создание фильтра с числовым значением
329
+ * @param {String} property - dataIndex поля
330
+ * @param {String} operator оператор сравнения (lt, gt, ne, eq и другие)
331
+ * @param {Number} value значение фильтра
332
+ * @param {String} fieldType - тип поля (float, string)
333
+ * @param {String} id - id фильтра (фильтр по диапазону реализуется двумя фильтрами по одной колонке. id нужен, чтобы фильтры отличались)
334
+ * @returns {Object} конфигурация фильтра
335
+ */
336
+ createNumberFilter(property, operator, value, fieldType, id) {
337
+ if (fieldType==='float') {
338
+ return {id, property, operator, value};
339
+ } else if (fieldType==='string') {
340
+ return {
341
+ id, property, operator, value, fieldType,
342
+ // конвертация сравниваемых значений
343
+ convert: function(val) {
344
+ if (Ext.isString(val)) {
345
+ return parseFloat(val);
346
+ }
347
+ return val;
348
+ },
349
+ };
350
+ }
351
+ },
352
+
353
+ /**
354
+ * Создание фильтра со значением типа Date
355
+ * @param {String} property - dataIndex поля
356
+ * @param {String} operator оператор сравнения (lt, gt, ne, eq и другие)
357
+ * @param {Date} value значение фильтра
358
+ * @param {String} fieldType - тип поля (date, string)
359
+ * @param {String} id - id фильтра (фильтр по диапазону реализуется двумя фильтрами по одной колонке. id нужен, чтобы фильтры отличались)
360
+ * @returns {Object} конфигурация фильтра
361
+ */
362
+ createDateFilter(property, operator, value, fieldType, id) {
363
+ if (fieldType==='date') {
364
+ return {id, property, operator, value};
365
+ } else if (fieldType==='string') {
366
+ return {
367
+ id, property, operator, value, fieldType,
368
+ // конвертация сравниваемых значений
369
+ convert: function(val) {
370
+ if (Ext.isString(val) && val) {
371
+ return Ext.Date.parse(val, Coon.format.date);
372
+ }
373
+ return val;
374
+ },
375
+ };
376
+ }
100
377
  },
101
378
 
102
379
  findMenuItem: function(findBy) {
@@ -109,13 +386,21 @@ Ext.define('Coon.report.plugin.grid.AddFilterConditionPlugin', {
109
386
  },
110
387
 
111
388
  clearAllFilters: function() {
389
+ if (this.filters.size === 0) {
390
+ return;
391
+ }
112
392
  this.filters.clear();
113
393
  this.doFilter();
394
+
395
+ let needRefresh = false;
114
396
  this.grid.getColumns().forEach((column) => {
115
397
  if (column.hasCls('filtered-column')) {
116
398
  column.removeCls('filtered-column');
399
+ column.tdCls = Ext.isString(column.tdCls) && column.tdCls.replace('filtered-cell', ' ');
400
+ needRefresh = true;
117
401
  }
118
402
  });
403
+ needRefresh && this.grid.getView().refresh();
119
404
  },
120
405
 
121
406
  clearCurrentColumnFilter: function() {
@@ -125,10 +410,13 @@ Ext.define('Coon.report.plugin.grid.AddFilterConditionPlugin', {
125
410
  },
126
411
 
127
412
  isFilterable: function() {
128
- if (!this.filterOptions[this.column.dataIndex]) {
413
+ if (!this.column) {
414
+ return false;
415
+ }
416
+ if (!this.filterAllVisible && !this.filterOptions[this.column.dataIndex]) {
417
+ Coon.log.debug(`Фильтрация по колонке ${this.column.dataIndex} запрещена конфигурацией`);
129
418
  return false;
130
419
  }
131
-
132
420
  if (Array.isArray(this.columnValue) || Ext.isObject(this.columnValue)) {
133
421
  Coon.log.debug('Фильтрация по значению из ячейки невозможна. Сложный тип данных');
134
422
  return false;
@@ -140,51 +428,206 @@ Ext.define('Coon.report.plugin.grid.AddFilterConditionPlugin', {
140
428
  return true;
141
429
  },
142
430
 
143
- getValueText() {
144
- if (typeof this.columnValue !== true && this.columnValue) {
145
- return this.columnValue;
431
+ getValueText(value) {
432
+ if (Ext.isDate(value)) {
433
+ return Ext.Date.format(value, this.column.format || Coon.format.dateTime);
434
+ }
435
+ if (Ext.isNumber(value)) {
436
+ return value;
146
437
  }
147
- if (typeof this.columnValue === 'boolean') {
148
- return this.columnValue ? 'истинно' : 'ложно';
438
+ if (value !== true && value) {
439
+ return value;
149
440
  }
150
- if (this.columnValue === null || this.columnValue === undefined || this.columnValue === '') {
441
+ if (typeof value === 'boolean') {
442
+ const booleanLabels = this.column ? {trueText: this.column.trueText, falseText: this.column.falseText}: {};
443
+ return value ? booleanLabels.trueText || 'да' : booleanLabels.falseText || 'нет';
444
+ }
445
+ if (value === null || value === undefined || value === '') {
151
446
  return 'пустое значение';
152
447
  }
153
448
  },
154
449
 
155
- setMenuText(record, cellIndex) {
450
+ setMenuText() {
156
451
  const contextMenu = this.grid.contextMenu;
157
- const columns = this.grid.getColumns();
158
- const column = columns.length && columns[cellIndex];
159
- if (!column) {
452
+ if (!this.column) {
160
453
  return;
161
454
  }
162
- this.column = column;
163
- this.columnValue = record.get(column.dataIndex);
455
+ const infoMenuItem = contextMenu.down('#infoMenuItem');
456
+ infoMenuItem.removeQTip();
457
+ infoMenuItem.setFilterText(infoMenuItem.originalText);
458
+ infoMenuItem.setFilterPrefix('');
164
459
 
165
- const cancelCurrentItem = this.findMenuItem('cancelCurrentColumnFilter');
166
- cancelCurrentItem.hide();
460
+ const currentColumnFilter = this.filters.get(this.getColumnKey());
461
+ const formatFilterValue = (value) => {
462
+ return Ext.isDate(value) ? Ext.Date.format(value, this.column.format || Coon.format.dateTime): value;
463
+ };
167
464
 
168
465
  const foundMenuItem = contextMenu.down('#filterByCellValue');
169
- foundMenuItem.show();
170
- if (this.filters.size && this.filters.get(this.getColumnKey()) === this.columnValue) {
171
- foundMenuItem.hide();
172
- cancelCurrentItem.setText(`${cancelCurrentItem.originalText} по значению: ${this.getValueText()}`);
173
- cancelCurrentItem.show();
174
- }
175
-
176
- if (this.isFilterable()) {
177
- foundMenuItem.setDisabled(false);
178
- if (Ext.isDate(this.columnValue)) {
179
- foundMenuItem.setText(`фильтр по "${Ext.Date.format(this.columnValue, column.format || Coon.format.dateTime)}"`);
180
- } else if (Ext.isEmpty(this.columnValue)) {
181
- foundMenuItem.setText(`фильтр по пустому значению`);
466
+ if (!this.isFilterable()) {
467
+ infoMenuItem.buttonSetHidden(true);
468
+ infoMenuItem.setFilterText(infoMenuItem.forbiddenText);
469
+ this.column.config && infoMenuItem.setColumnName(this.column.config.text);
470
+ return;
471
+ } else {
472
+ infoMenuItem.buttonSetHidden(false);
473
+ if (Ext.isEmpty(this.columnValue)) {
474
+ foundMenuItem.setText(`Фильтр по пустому значению`);
182
475
  } else {
183
- foundMenuItem.setText(`фильтр по "${this.columnValue}"`);
476
+ foundMenuItem.setText(`<div style="max-width: 300px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
477
+ Фильтр по "${this.getValueText(this.columnValue)}"
478
+ </div>`);
184
479
  }
480
+ }
481
+ if (currentColumnFilter) {
482
+ infoMenuItem.buttonSetHidden(false);
483
+ const operators = {
484
+ 'ne': 'не равно',
485
+ 'gt': 'больше',
486
+ 'ge': 'больше или равно',
487
+ 'lt': 'меньше',
488
+ 'le': 'меньше или равно',
489
+ 'notfilled': 'не заполнено',
490
+ 'like': 'Содержит',
491
+ 'in': 'Список',
492
+ };
493
+ let itemText; let itemPrefix;
494
+ switch (currentColumnFilter.operator) {
495
+ case 'between':
496
+ // Фильтр between может быть задан без одной из границ. Тогда информацию о фильтрации не корректно отображать как диапазон.
497
+ const operator = currentColumnFilter.operator !== 'between' ?
498
+ currentColumnFilter.operator:
499
+ Ext.isEmpty(currentColumnFilter.value[0]) ?
500
+ 'le':
501
+ Ext.isEmpty(currentColumnFilter.value[1]) ? 'ge': 'between';
502
+ const valueStart = formatFilterValue(currentColumnFilter.value[0]);
503
+ const valueEnd = formatFilterValue(currentColumnFilter.value[1]);
504
+ if (operator === 'between') {
505
+ itemPrefix = 'по диапазону';
506
+ itemText = ` &laquo;${valueStart} - ${valueEnd}&raquo;`;
507
+ } else {
508
+ const value = Ext.isEmpty(currentColumnFilter.value[0]) ? currentColumnFilter.value[1] :currentColumnFilter.value[0];
509
+ itemPrefix = operators[operator];
510
+ itemText = ` &laquo;${this.getValueText(value)}&raquo;`;
511
+ }
512
+
513
+ break;
514
+ case 'ge':
515
+ case 'gt':
516
+ case 'le':
517
+ case 'lt':
518
+ case 'ne':
519
+ itemPrefix = operators[currentColumnFilter.operator];
520
+ itemText = `&laquo;${this.getValueText(currentColumnFilter.value)}&raquo;`;
521
+ break;
522
+ case 'eq':
523
+ itemPrefix = ' равно ';
524
+ itemText = `&laquo;${this.getValueText(currentColumnFilter.value)}&raquo;`;
525
+ break;
526
+ case 'like':
527
+ itemPrefix = ' по подстроке ';
528
+ itemText = `&laquo;${currentColumnFilter.value}&raquo;`;
529
+ break;
530
+ case 'in':
531
+ itemPrefix = 'по списку';
532
+ itemText = ' ';
533
+ break;
534
+ case 'notfilled':
535
+ itemPrefix = ' не заполнено ';
536
+ itemText = ' ';
537
+ break;
538
+ }
539
+ infoMenuItem.setColumnName(this.column.config.text);
540
+ infoMenuItem.setFilterText(itemText);
541
+ infoMenuItem.setFilterPrefix(itemPrefix);
542
+ infoMenuItem.setQTip(this.column.config.text, this.getColumnGroup(), itemPrefix, itemText);
185
543
  } else {
186
- foundMenuItem.setText(`Нет возможности фильтрации`);
187
- foundMenuItem.setDisabled(true);
544
+ infoMenuItem.buttonSetHidden(true);
545
+ this.column.config && infoMenuItem.setColumnName(this.column.config.text);
188
546
  }
189
547
  },
548
+
549
+ getColumnGroup() {
550
+ let columnGroup = this.column && this.column.columnGroup;
551
+ columnGroup = Ext.isString(columnGroup) ? columnGroup.replaceAll(';', ' / ') + ' / ': '';
552
+ return columnGroup;
553
+ },
554
+
555
+ /**
556
+ * Создать подменю простого фильтра
557
+ * @param menu
558
+ */
559
+ createSearchMenuItem(menu) {
560
+ const currentColumnFilter = this.filters.get(this.getColumnKey());
561
+ const dataIndex = this.column && this.column.dataIndex;
562
+ const filterType = this.columnFilterTypes[dataIndex];
563
+ if (dataIndex) {
564
+ const filter = {
565
+ dataIndex,
566
+ operator: undefined,
567
+ type: filterType,
568
+ value: undefined,
569
+ };
570
+ if (currentColumnFilter) {
571
+ filter.value = currentColumnFilter.value;
572
+ filter.operator = currentColumnFilter.operator;
573
+ } else {
574
+ filter.value = filterType === 'list' ? [this.columnValue]: this.columnValue;
575
+ filter.operator = filterType === 'list' ?
576
+ 'in':
577
+ Ext.isEmpty(this.columnValue) ? 'notfilled': 'eq';
578
+ }
579
+ if (filterType === 'string') {
580
+ filter.useCombo = /* this.filters.size === 0 &&*/ this.canUseCombo();
581
+ }
582
+ const filterWrapPanelCfg = {
583
+ filterType,
584
+ filter,
585
+ reportGrid: this.grid,
586
+ cellValue: this.columnValue,
587
+ dataIndex: this.column.dataIndex,
588
+ };
589
+ if (['string', 'numeric', 'date', 'list', 'combo'].includes(filterType)) {
590
+ filterWrapPanelCfg.width = 600;
591
+ } else {
592
+ filterWrapPanelCfg.minWidth = 200;
593
+ }
594
+ const menuItem = Ext.widget('FilterWrapPanel', filterWrapPanelCfg);
595
+ menuItem.on('dofilter', function(value, condition) {
596
+ Coon.log.log('dofilter', value, condition);
597
+ this.addFilter(dataIndex, condition, value);
598
+ this.doFilter();
599
+ this.changeColumnsStyle();
600
+ this.grid.contextMenu.hide();
601
+ }, this);
602
+ menuItem.on('advancedsearch', this.advancedSearch, this);
603
+ menu.removeAll();
604
+ menu.add(menuItem);
605
+ }
606
+ },
607
+
608
+ isCancelAllFiltersHidden() {
609
+ return false;
610
+ },
611
+ isCancelAllFiltersDisabled() {
612
+ return this.filters.size === 0;
613
+ },
614
+ isInfoMenuItemHidden() {
615
+ return !this.column;
616
+ },
617
+ isInfoMenuItemDisabled() {
618
+ return false;
619
+ },
620
+ isSearchByValueItemHidden() {
621
+ return !this.column;
622
+ },
623
+ isSearchByValueItemDisabled() {
624
+ return !this.isFilterable();
625
+ },
626
+ isFilterByCellValueHidden() {
627
+ return !this.column || !this.isFilterable() || this.filters.has(this.getColumnKey(this.column));
628
+ },
629
+ isFilterByCellValueDisabled() {
630
+ return false;
631
+ },
190
632
  });
633
+