ru.coon 2.8.66 → 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 (128) hide show
  1. package/CHANGELOG.md +317 -10
  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/formeditor/UiCustomFilterForm.scss +0 -1
  22. package/src/common/component/settings/modules/settingClosePageConfirmation.js +1 -3
  23. package/src/common/component/settings/modules/settingFavoritePanelShow.js +21 -0
  24. package/src/common/component/settings/modules/settingReportCalculator.js +22 -0
  25. package/src/common/component/settings/modules/settingShowNeedReloadMessage.js +1 -3
  26. package/src/common/component/settings/modules/settingwindowHolder.js +2 -3
  27. package/src/common/panel/WindowWrap.js +19 -11
  28. package/src/common/tree/BaseContextMenu.js +4 -3
  29. package/src/log.js +4 -5
  30. package/src/nav/AppNavCalcButton.js +23 -0
  31. package/src/nav/AppNavCalcButton.scss +6 -0
  32. package/src/nav/AppNavigationBar.js +1 -1
  33. package/src/nav/AppNavigationMenuController.js +2 -2
  34. package/src/nav/AppNavigationMenuMinimized.js +61 -0
  35. package/src/nav/AppNavigationPanel.js +115 -0
  36. package/src/nav/AppNavigationPanel.scss +178 -0
  37. package/src/nav/AppNavigationPanelController.js +386 -0
  38. package/src/nav/FavoriteCfg.js +18 -0
  39. package/src/nav/MenuEntity.js +23 -15
  40. package/src/nav/MenuFavoritesBar.js +100 -0
  41. package/src/nav/MenuFavoritesBar.scss +92 -0
  42. package/src/nav/editor/NavigateElementEditorView.js +2 -2
  43. package/src/nav/editor/workspace/NavWorkspaceListView.js +1 -4
  44. package/src/nav/menu/WorkspaceMenuViewMinimized.js +22 -0
  45. package/src/overrides/panel/TabPanel.js +36 -0
  46. package/src/overrides/panel/TabPanel.scss +65 -0
  47. package/src/report/component/ClearFiltersButton.js +4 -1
  48. package/src/report/component/CopyCellValueMenuItem.js +18 -0
  49. package/src/report/component/ReportFieldMap.js +274 -0
  50. package/src/report/component/ReportPanel.js +73 -44
  51. package/src/report/component/ReportPanel.scss +2 -2
  52. package/src/report/component/ReportTagLookup.js +59 -2
  53. package/src/report/component/calculator/ReportCalculatorController.js +266 -0
  54. package/src/report/component/calculator/ReportCalculatorField.js +47 -0
  55. package/src/report/component/calculator/ReportCalculatorHistoryPlugin.js +128 -0
  56. package/src/report/component/calculator/ReportCalculatorHistoryPlugin.scss +33 -0
  57. package/src/report/component/calculator/ReportCalculatorMenuItem.js +101 -0
  58. package/src/report/component/calculator/ReportCalculatorMenuItem.scss +41 -0
  59. package/src/report/component/calculator/ReportCalculatorPanel.js +363 -0
  60. package/src/report/component/calculator/ReportCalculatorPanel.scss +86 -0
  61. package/src/report/component/reportpanel/CopyReportPanelController.js +1 -1
  62. package/src/report/component/reportpanel/FilterPanel.js +13 -15
  63. package/src/report/component/reportpanel/FilterPanel.scss +5 -1
  64. package/src/report/component/reportpanel/FormFieldFocusPlugin.js +157 -0
  65. package/src/report/component/reportpanel/FormFieldFocusPlugin.scss +14 -0
  66. package/src/report/component/reportpanel/NorthPanel.js +16 -17
  67. package/src/report/component/reportpanel/NorthPanel.scss +1 -1
  68. package/src/report/component/reportpanel/ReportContextMenu.js +219 -0
  69. package/src/report/component/reportpanel/ReportGrid.js +1 -0
  70. package/src/report/component/reportpanel/ReportGrid.scss +4 -0
  71. package/src/report/component/settings/field/ReportFormFieldsGrid.js +2 -2
  72. package/src/report/component/settings/field/ReportFormFieldsGridController.js +2 -2
  73. package/src/report/component/settings/property/ReportPropertiesPanelController.js +1 -1
  74. package/src/report/component/settings/property/ReportPropertyDictionary.js +7 -0
  75. package/src/report/plugin/configPanel/AddFilterConditionPluginConfigPanel.js +34 -11
  76. package/src/report/plugin/configPanel/GridFiltersPluginConfigPanelFiltersGrid.js +3 -2
  77. package/src/report/plugin/configPanel/openCustomPanelButtonPlugin/OpenCustomPanelButtonPluginConfigPanel.js +274 -255
  78. package/src/report/plugin/configPanel/openCustomPanelButtonPlugin/OpenCustomPanelButtonPluginConfigPanel.scss +17 -0
  79. package/src/report/plugin/grid/AddFilterConditionPlugin.js +530 -87
  80. package/src/report/plugin/grid/CalculatorPlugin.js +90 -0
  81. package/src/report/plugin/grid/GridContextMenu.js +12 -10
  82. package/src/report/plugin/grid/GridRowCountPlugin.js +0 -1
  83. package/src/report/plugin/grid/GridToolbarButtonPlugin.js +11 -2
  84. package/src/report/plugin/grid/OpenCustomPanelButtonPlugin.js +126 -89
  85. package/src/report/plugin/grid/ReportColumnStatePlugin.js +15 -3
  86. package/src/report/plugin/grid/SetSingleParameterPlugin.js +1 -1
  87. package/src/report/plugin/grid/ToolbarButtonPlugin.js +1 -1
  88. package/src/report/plugin/grid/addFilterConditionPlugin/AdvancedSearchPanel.js +55 -0
  89. package/src/report/plugin/grid/addFilterConditionPlugin/AdvancedSearchPanelController.js +226 -0
  90. package/src/report/plugin/grid/addFilterConditionPlugin/FilterConfigMixin.js +138 -0
  91. package/src/report/plugin/grid/addFilterConditionPlugin/FilterFieldFactory.js +223 -0
  92. package/src/report/plugin/grid/addFilterConditionPlugin/FilterItem.js +164 -0
  93. package/src/report/plugin/grid/addFilterConditionPlugin/FilterItem.scss +21 -0
  94. package/src/report/plugin/grid/addFilterConditionPlugin/FilterMenu.js +29 -0
  95. package/src/report/plugin/grid/addFilterConditionPlugin/FilterWrapPanel.js +53 -0
  96. package/src/report/plugin/grid/addFilterConditionPlugin/FilterWrapPanelController.js +57 -0
  97. package/src/report/plugin/grid/addFilterConditionPlugin/InfoMenuItem.js +111 -0
  98. package/src/report/plugin/grid/addFilterConditionPlugin/InfoMenuItem.scss +83 -0
  99. package/src/report/plugin/grid/addFilterConditionPlugin/SelectColumnPanel.js +102 -0
  100. package/src/report/selectionModels/MixedRowSelectionModel.js +36 -45
  101. package/src/report/toolbar/dropdown/ToolbarOverflowButton.js +16 -5
  102. package/src/report/toolbar/dropdown/ToolbarOverflowPanel.scss +26 -14
  103. package/src/report/toolbar/layout/ReportToolbarOverflow.js +5 -2
  104. package/src/ringBuffer.js +7 -3
  105. package/src/uielement/command/GetUIElementCommand.js +0 -1
  106. package/src/uielement/component/UiCPWrapper.js +26 -36
  107. package/src/uielement/component/UiCustomController.js +7 -0
  108. package/src/uielement/component/UiCustomPanel.js +46 -2
  109. package/src/uielement/component/settings/UiCustomPanelEditorController.js +35 -29
  110. package/src/uielement/component/settings/plugin/UiCustomPanelPluginGrid.js +146 -33
  111. package/src/uielement/component/settings/plugin/UiCustomPanelPluginGrid.scss +28 -0
  112. package/src/uielement/component/settings/plugin/UiCustomPanelPluginGridController.js +366 -22
  113. package/src/uielement/component/settings/plugin/UiCustomPanelPluginModel.js +1 -0
  114. package/src/uielement/component/settings/plugin/UiCustomPanelPluginPanel.js +10 -4
  115. package/src/uielement/component/settings/plugin/UiCustomPanelPluginPanelController.js +28 -30
  116. package/src/uielement/plugin/UnifiedButtonToolbarPlugin.js +203 -22
  117. package/src/uielement/plugin/configPanel/ExecuteFunctionPluginConfigPanelFormEditor.js +4 -36
  118. package/src/uielement/plugin/configPanel/FireEventPluginConfigPanelFormEditor.js +5 -36
  119. package/src/uielement/plugin/configPanel/MethodChainPluginConfigPanelFormEditor.js +4 -36
  120. package/src/uielement/plugin/configPanel/OpenPanelPluginConfigPanelFormEditor.js +2 -38
  121. package/src/uielement/plugin/configPanel/PrintPdfPluginConfigPanelFormEditor.js +2 -35
  122. package/src/uielement/plugin/configPanel/UiCPPluginFormPanel.js +54 -0
  123. package/src/uielement/plugin/configPanel/UnifiedButtonToolbarPluginConfigPanelFormEditor.js +28 -18
  124. package/src/userSettings.js +1 -0
  125. package/src/util/ContextManager.js +109 -0
  126. package/src/util.js +96 -15
  127. package/src/version.js +1 -1
  128. package/src/app/viewPort/CenterViewController.js +0 -158
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Словарь уникальных значений полей репорта
3
+ *
4
+ * Условия сбора данных:
5
+ * Строковые данные:
6
+ * в словарь собираются уникальные значения поля, до тех пор пока не достигнут максимум (maxMapSize),
7
+ * после чего в зависимости от флага clearMapAfterOverflow он может быть очищен.
8
+ * Числовые данные и даты:
9
+ * в словарь собираются минимум и максимум данного поля.
10
+ *
11
+ * Определение истинного типа данных полей, с динамическим типом.
12
+ * Параметры поля с динамическим типом данных:
13
+ * dynamicTypeDetect - поле имеет динамический тип данных
14
+ * autoNormalizeSort - автоматическая нормализованная сортировка
15
+ */
16
+ Ext.define('Coon.report.component.ReportFieldMap', {
17
+ fields: undefined,
18
+ extremumDataTypes: ['DATETIME', 'TIME', 'DATE', 'MONEY', 'DECIMAL', 'INTEGER'],
19
+
20
+ config: {
21
+ maxMapSize: 500,
22
+ clearMapAfterOverflow: false,
23
+ collectOnlyVisible: true,
24
+ },
25
+ maps: undefined,
26
+ dynamicTypeFields: undefined,
27
+
28
+ constructor(config) {
29
+ this.maps = new Map();
30
+ this.dynamicTypeFields = {};
31
+ Ext.apply(this, config);
32
+ if (Ext.isObject(this.rawConfig)) {
33
+ Ext.apply(this, Coon.util.createObjFromTpl(this.config, this.rawConfig));
34
+ }
35
+ this.configureMaps(this.fields);
36
+ },
37
+
38
+ /** Создание конфига для словаря уникальных значений
39
+ * @param fields
40
+ */
41
+ configureMaps(fields) {
42
+ fields.forEach((field) => {
43
+ const fieldProps = Coon.Function.convertAdvancedProperties(field.properties);
44
+ const fieldMapConfig = fieldProps.fieldMapConfig;
45
+ const dynamicTypeDetect = (fieldMapConfig && fieldMapConfig.dynamicTypeDetect) || false;
46
+ const autoNormalizeSort = (fieldMapConfig && fieldMapConfig.autoNormalizeSort) || false;
47
+ dynamicTypeDetect && this.prepareDataForDynamicTypeCheck(field.reportFieldCd, {autoNormalizeSort});
48
+ if (
49
+ (!fieldMapConfig || (fieldMapConfig && fieldMapConfig.enabled)) &&
50
+ (!this.collectOnlyVisible || (this.collectOnlyVisible && field.visibleSwitch))
51
+ ) {
52
+ const isExtremum = this.extremumDataTypes.includes(field.reportFieldTypeLookup);
53
+ const config = {
54
+ maxMapSize: this.maxMapSize,
55
+ data: this.prepareData(isExtremum),
56
+ clearMapAfterOverflow: this.clearMapAfterOverflow,
57
+ isExtremum: isExtremum,
58
+ isFull: false,
59
+ isFalsyValues: false,
60
+ };
61
+ Ext.apply(config, fieldMapConfig);
62
+ this.maps.set(field.reportFieldCd, config);
63
+ }
64
+ });
65
+ },
66
+
67
+ /**
68
+ * Подготовка данных для анализа полей, все данные которого могут иметь динамический тип.
69
+ * @param originName - имя поля
70
+ */
71
+ prepareDataForDynamicTypeCheck(originName, config) {
72
+ this.dynamicTypeFields[originName] = {
73
+ originName: originName,
74
+ convertedFieldName: this.getDynamicTypeFieldName(originName),
75
+ data: [],
76
+ allValuesOneType: true,
77
+ };
78
+ Ext.apply(this.dynamicTypeFields[originName], config);
79
+ },
80
+
81
+ /**
82
+ * @param {boolean} isExtremum
83
+ * @returns {Map|Set}
84
+ */
85
+ prepareData(isExtremum) {
86
+ return isExtremum? new Map().set('min').set('max') : new Set();
87
+ },
88
+
89
+ /**
90
+ * Сброс данных
91
+ */
92
+ clearData() {
93
+ this.maps.forEach((map, fieldName) => {
94
+ map.data = this.prepareData(map.isExtremum);
95
+ map.isFull = false;
96
+ map.isFalsyValues = false;
97
+ map.dynamicTypeDetect && this.clearDynamicTypeField(fieldName);
98
+ });
99
+ },
100
+
101
+ /**
102
+ * Сброс данных дублирующего поля, хранящее информацию об анализе данных оригинального поля,
103
+ * у которого все данные могут иметь динамический тип.
104
+ * @param fieldName - имя оригинального поля, чьи данные будут анализироваться.
105
+ */
106
+ clearDynamicTypeField(fieldName) {
107
+ Object.keys(this.dynamicTypeFields).forEach((key) => {
108
+ this.dynamicTypeFields[key].data = [];
109
+ this.dynamicTypeFields[key].allValuesOneType = true;
110
+ });
111
+ },
112
+
113
+ /**
114
+ * Функция convert для дополнительного поля. Функция заполняет уникальные значения и вычисляет мин и макс.
115
+ */
116
+ getConvert() {
117
+ return function(_, record) {
118
+ this.maps.forEach((map, key) => {
119
+ const recordValue = record.get(key);
120
+
121
+ if (map.dynamicTypeDetect && this.dynamicTypeFields[key].allValuesOneType) {
122
+ this.checkValueDynamicType(recordValue, key, record);
123
+ }
124
+
125
+ if ((map.data.size === map.maxMapSize) || map.isFull) {
126
+ map.isFull = true;
127
+ if (map.clearMapAfterOverflow) {
128
+ map.data.clear();
129
+ }
130
+ } else {
131
+ if (recordValue === null || recordValue === undefined) {
132
+ map.isFalsyValues = true;
133
+ }
134
+ if (map.isExtremum) {
135
+ const min = map.data.get('min');
136
+ const max = map.data.get('max');
137
+
138
+ map.data.set('min', this.getExtremumValue(min, recordValue, Math.min));
139
+ map.data.set('max', this.getExtremumValue(max, recordValue, Math.max));
140
+ } else {
141
+ map.data.add(recordValue);
142
+ }
143
+ }
144
+ });
145
+ }.bind(this);
146
+ },
147
+
148
+ /**
149
+ * Имя дублирующего поля, хранящего информацию об оригинальном поле, данные которого могут иметь динамический тип.
150
+ * @param originName - имя оригинального поля, чьи данные будут анализироваться.
151
+ * @returns {string} - имя дублирующего поля, с результатом анализа данных.
152
+ */
153
+ getDynamicTypeFieldName(originName) {
154
+ return `_${originName}_convert`;
155
+ },
156
+
157
+ /**
158
+ * Возвращает поля, данные которого могут иметь динамический тип.
159
+ * @returns {*}
160
+ */
161
+ getDynamicTypeFields() {
162
+ return this.dynamicTypeFields;
163
+ },
164
+
165
+ /**
166
+ * Возвращает функцию sorterFn, нормализующую сортировку.
167
+ * Если у всех данных поля одинаковый тип, для сравнения импользуются значения после конвертации,
168
+ * в противном случае используются оригинальные значения.
169
+ * @param originalFieldName
170
+ * @returns {any}
171
+ */
172
+ getNormalizeSorterFn(originalFieldName) {
173
+ const convertedFieldName = this.dynamicTypeFields[originalFieldName].convertedFieldName;
174
+ return function(record1, record2) {
175
+ const allValuesOneType = this.dynamicTypeFields[originalFieldName].allValuesOneType;
176
+ const value1 = allValuesOneType ? record1.get(convertedFieldName) : record1.get(originalFieldName);
177
+ const value2 = allValuesOneType ? record2.get(convertedFieldName) : record2.get(originalFieldName);
178
+ return value1 > value2 ? 1 : (value1 === value2) ? 0 : -1;
179
+ }.bind(this);
180
+ },
181
+
182
+ /**
183
+ * Проверка данных поля, все данные которого могут иметь динамический тип.
184
+ * @param recordValue - значение поля
185
+ * @param colName - имя оригинального поля
186
+ * @param record
187
+ */
188
+ checkValueDynamicType(recordValue, colName, record) {
189
+ let convertedValue = recordValue;
190
+ if (typeof convertedValue === 'string') {
191
+ convertedValue = Ext.String.trim(convertedValue).replaceAll(',', '.');
192
+ convertedValue = Ext.Number.parseFloat(convertedValue);
193
+ if (convertedValue === null) {
194
+ this.dynamicTypeFields[colName].allValuesOneType = false;
195
+ } else {
196
+ this.dynamicTypeFields[colName].data.push(convertedValue || undefined);
197
+ }
198
+ }
199
+ const convertFieldName = this.getDynamicTypeFieldName(colName);
200
+ record.set(convertFieldName, (convertedValue || undefined));
201
+ },
202
+
203
+ /**
204
+ * Map всех словарей уникальных значений.
205
+ * Ключами являются идентификаторы полей.
206
+ * Значения - объекты вида:
207
+ * {
208
+ * isExtremum - Если тип поля относится к extremumDataTypes
209
+ * Если тип поля относится к extremumDataTypes (isExtremum=true), то в data содержатся минимальное и максимальное значения данных поля
210
+ * ! Для данных типа Date содержится число (результат функции getTime)
211
+ * data: {
212
+ * min,
213
+ * max
214
+ * },
215
+ * Иначе data содержит Set из уникальных значений данных по колонке
216
+ * data: Set,
217
+ * clearMapAfterOverflow - флаг очистки в случае переполнения
218
+ * isFalsyValues признак присутствия в наборе значений поля null или undefined
219
+ * isFull - признак достижения maxMapSize - максимального количества сохраненных уникальных значений
220
+ * maxMapSize - максимального количества сохраненных уникальных значений
221
+ * }
222
+ * @returns {*} undefined | Map
223
+ */
224
+ getMaps() {
225
+ return this.maps;
226
+ },
227
+
228
+ getColumnData(reportFieldCd) {
229
+ if (!reportFieldCd) {
230
+ return false;
231
+ }
232
+ const fieldData = this.getMaps().get(reportFieldCd);
233
+ if (!fieldData) {
234
+ return false;
235
+ }
236
+ return fieldData;
237
+ },
238
+
239
+ getExtremum(reportFieldCd) {
240
+ return this.getColumnData(reportFieldCd) && this.getColumnData(reportFieldCd).isExtremum && this.getColumnData(reportFieldCd).data;
241
+ },
242
+
243
+ /**
244
+ * Валидация value для Math.min() или Math.max()
245
+ * @param value
246
+ * @returns {boolean}
247
+ */
248
+ validateExtremumValue(value) {
249
+ return (!isNaN(Number(value)) && (value !== null));
250
+ },
251
+
252
+ /**
253
+ * Получение минимального или максимального значения из двух
254
+ * @param a
255
+ * @param b
256
+ * @param mathFunc функция стравнения (Math.min | Math.max)
257
+ * @returns {undefined} одно из чисел или undefined если сравнение невозможно
258
+ */
259
+ getExtremumValue(a, b, mathFunc) {
260
+ let result;
261
+ const validateA = this.validateExtremumValue(a);
262
+ const validateB = this.validateExtremumValue(b);
263
+ if (validateA && validateB) {
264
+ result = mathFunc.call(this, a, b);
265
+ } else if (validateB) {
266
+ result = b;
267
+ } else if (validateA) {
268
+ result = a;
269
+ } else {
270
+ result = undefined;
271
+ }
272
+ return result;
273
+ },
274
+ });
@@ -20,6 +20,7 @@ Ext.define('Coon.report.component.ReportPanel', {
20
20
  'Coon.report.column.HintColumn',
21
21
  'Coon.report.component.reportpanel.ReportGrid',
22
22
  'Coon.report.component.reportpanel.FilterPanel',
23
+ 'Coon.report.component.ReportFieldMap',
23
24
  'Coon.report.model.*',
24
25
  'Coon.report.model.CharacteristicBeanFields',
25
26
  'Coon.report.plugin.form.EnterConfirmFormPlugin',
@@ -56,6 +57,7 @@ Ext.define('Coon.report.component.ReportPanel', {
56
57
  enableAutoSize: true,
57
58
  enableSummaryFeature: false,
58
59
  enableGroupingFeature: false,
60
+ disableFieldMap: false,
59
61
  toggleFilterPanel: false,
60
62
  startCollapsed: false,
61
63
  enableTextSelection: false,
@@ -76,6 +78,10 @@ Ext.define('Coon.report.component.ReportPanel', {
76
78
  locals: {
77
79
  plugins: new Map(),
78
80
  },
81
+ /**
82
+ * @property {Coon.report.component.ReportFieldMap}
83
+ */
84
+ serviceMap: undefined,
79
85
 
80
86
  statics: {
81
87
  getColumnsConfig: function(reportBeanFields, gridConfig) {
@@ -353,7 +359,13 @@ Ext.define('Coon.report.component.ReportPanel', {
353
359
 
354
360
  this.layout = 'card';
355
361
 
356
- this.northPanel = Ext.widget('NorthPanel', {
362
+ this._visibleFieldsMap = new Map();
363
+
364
+ // для совместимости со старым кодом.
365
+ this.dockedItems = [].concat(this.dockedItems || []);
366
+
367
+ this.dockedItems.push({
368
+ xtype: 'NorthPanel',
357
369
  hideFilterPanel: this.hideFilterPanel,
358
370
  hideFilterButtons: this.hideFilterButtons,
359
371
  findButtonConfig: this.findButtonConfig || {},
@@ -365,7 +377,23 @@ Ext.define('Coon.report.component.ReportPanel', {
365
377
  header: false,
366
378
  });
367
379
 
368
- this._visibleFieldsMap = new Map();
380
+ this.items = [
381
+ Ext.apply(this.centerProperties, {
382
+ xtype: 'panel',
383
+ itemId: 'centerPanel',
384
+ region: 'center',
385
+ minHeight: 100,
386
+ layout: 'fit',
387
+ cls: 'buttonsCenterFilterPanel',
388
+ })
389
+ ];
390
+
391
+ this.callParent();
392
+
393
+ this.centerPanel = this.down('panel#centerPanel');
394
+ this.northPanel = this.down('NorthPanel');
395
+ this.filterContainer = this.northPanel.filterContainer;
396
+
369
397
  this.northPanel.on('hide', function() {
370
398
  this.query('[validate][isReportFilterField][hidden=false]').forEach((field) => {
371
399
  this._visibleFieldsMap.set(field, field.hidden);
@@ -380,25 +408,6 @@ Ext.define('Coon.report.component.ReportPanel', {
380
408
  });
381
409
  }, this);
382
410
 
383
- // для совместимости со старым кодом.
384
- this.filterContainer = this.northPanel.filterContainer;
385
- this.centerPanel = Ext.widget('panel', Ext.apply(this.centerProperties, {
386
- region: 'center',
387
- minHeight: 100,
388
- layout: 'fit',
389
- cls: 'buttonsCenterFilterPanel',
390
- }));
391
- this.dockedItems = [].concat(this.dockedItems || []);
392
-
393
-
394
- this.dockedItems.push(this.northPanel);
395
-
396
- this.items = [
397
- this.centerPanel
398
- ];
399
-
400
- this.callParent();
401
-
402
411
  if (this.reportID) {
403
412
  this.reportId = this.reportID;
404
413
  }
@@ -758,7 +767,8 @@ Ext.define('Coon.report.component.ReportPanel', {
758
767
  return acc;
759
768
  }, new Map());
760
769
 
761
- Ext.merge(this, Coon.Function.convertAdvancedProperties(reportFormBean[ns.$properties]));
770
+ const configProperties = Coon.Function.convertAdvancedProperties(reportFormBean[ns.$properties]);
771
+ Ext.merge(this, configProperties, {configProperties});
762
772
  if (this.getToggleFilterPanel() && !this.hideFilterPanel) {
763
773
  this.addTool({
764
774
  iconCls: 'x-fa fa-eye-slash',
@@ -872,18 +882,34 @@ Ext.define('Coon.report.component.ReportPanel', {
872
882
  }
873
883
  }
874
884
  },
885
+
886
+ mixPlugins(gridPlugins, basicPlugins) {
887
+ const plugins = [];
888
+ basicPlugins.forEach((plugin) => {
889
+ const found = gridPlugins.find((item) => plugin.ptype === item.ptype);
890
+ if (!found) {
891
+ plugins.push(plugin);
892
+ }
893
+ });
894
+ plugins.push(...gridPlugins);
895
+ return plugins;
896
+ },
897
+
875
898
  createGrid: function(reportBean) {
876
899
  const ns = Coon.report.model.ReportBeanFields;
877
- const plugins = Ext.Array.merge(
878
- [{ptype: 'GridContextPlugin'},
879
- {ptype: 'GridContextMenu'},
880
- {ptype: 'gridexporter'}],
881
- this.getPluginConfigByType(
882
- 'GRID_PLUGIN',
883
- 'p',
884
- reportBean[ns.$plugins]
885
- )
900
+ const gridPlugins = this.getPluginConfigByType(
901
+ 'GRID_PLUGIN',
902
+ 'p',
903
+ reportBean[ns.$plugins]
886
904
  );
905
+
906
+ const plugins = this.mixPlugins(gridPlugins, [
907
+ {ptype: 'GridContextPlugin'},
908
+ {ptype: 'GridContextMenu'},
909
+ {ptype: 'gridexporter'},
910
+ {ptype: 'AddFilterConditionPlugin'},
911
+ {ptype: 'CalculatorPlugin'}
912
+ ]);
887
913
  plugins.sort(this.raiseUpGroupButton);
888
914
 
889
915
  const gridConfig = {
@@ -914,29 +940,32 @@ Ext.define('Coon.report.component.ReportPanel', {
914
940
 
915
941
  const defaultGridConfig = Coon.report.component.ReportPanel.getColumnsConfig(reportBean[ns.$fields], gridConfig);
916
942
 
943
+ const fieldMapConfig = Coon.Function.convertAdvancedProperties(reportBean[ns.$properties]).fieldMapConfig || {};
944
+ if (!this.disableFieldMap && !fieldMapConfig.disabled) {
945
+ this.serviceMap = new Coon.report.component.ReportFieldMap({
946
+ maxMapSize: 100,
947
+ fields: reportBean.fields,
948
+ rawConfig: fieldMapConfig,
949
+ });
950
+ defaultGridConfig.fields.push({
951
+ convert: this.serviceMap.getConvert(),
952
+ });
953
+ this.northPanel.on('filterhandlerevent', this.serviceMap.clearData, this.serviceMap);
954
+ }
955
+
917
956
  const widget = Ext.widget(
918
957
  this.getIsTree() ? 'ReportTree' : 'ReportGrid',
919
958
  Ext.apply(defaultGridConfig, this.gridProperties, {reportId: reportBean[ns.$reportId]})
920
959
  );
921
960
 
922
961
  if (widget.contextMenu) {
923
- const copyButton = new Ext.menu.Item({
924
- text: 'Копировать',
925
- handler: this.onCopyCellClick.bind(widget.contextMenu),
926
- });
927
- widget.contextMenu.add(copyButton);
928
- widget.contextMenu.on('requestMenuItems', (params) => {
929
- copyButton.setHidden(!params.td);
930
- }, this);
962
+ const copyButton = Ext.widget('CopyCellValueMenuItem');
963
+ widget.contextMenu.addCopyButton(copyButton);
931
964
  }
932
965
  widget.on('buttonIsAdded', this.sortButtons, this);
933
966
  return widget;
934
967
  },
935
- onCopyCellClick() {
936
- if (this.context && this.context.td) {
937
- Coon.util.copyToClipboard(this.context.td.innerText);
938
- }
939
- },
968
+
940
969
  sortButtons: function(params) {
941
970
  if (params.bar.items.length === 1) {
942
971
  return;
@@ -13,7 +13,7 @@
13
13
  background-color: #f6f6f6;
14
14
  }
15
15
 
16
- .x-panel-header-default {
16
+ /*.x-panel-header-default {
17
17
  padding: 6px 16px;
18
18
  background-color: #f6f6f6;
19
19
  font-size: 17px;
@@ -21,7 +21,7 @@
21
21
  color: #3e4752;
22
22
  }
23
23
 
24
- }
24
+ }*/
25
25
 
26
26
  .x-toolbar-default{
27
27
  background-color: #ffffff;
@@ -43,10 +43,12 @@ Ext.define('Coon.report.component.ReportTagLookup', {
43
43
  // Количество страниц
44
44
  count: 0,
45
45
  },
46
+ autoSortDynamicData: false,
46
47
  },
47
48
 
48
49
  initComponent: function() {
49
50
  this.callParent();
51
+ this.autoSortDynamicData && this.prepareForNormalizedSortDynamicData();
50
52
  // clear trigger
51
53
  if (this.resettable) {
52
54
  this.setTriggers(Object.assign({
@@ -70,6 +72,32 @@ Ext.define('Coon.report.component.ReportTagLookup', {
70
72
  }
71
73
  },
72
74
 
75
+ /**
76
+ * Подготовка к автоматической нормализованнной сортировке данных,
77
+ * где displayField имеет динамический тип данных.
78
+ */
79
+ prepareForNormalizedSortDynamicData() {
80
+ this.serviceMap = Ext.create('Coon.report.component.ReportFieldMap', {
81
+ fields: [
82
+ {
83
+ reportFieldCd: this.displayField,
84
+ properties: {
85
+ fieldMapConfig: {
86
+ enabled: true,
87
+ dynamicTypeDetect: true,
88
+ autoNormalizeSort: true,
89
+ },
90
+ },
91
+ }
92
+ ],
93
+ clearMapAfterOverflow: false,
94
+ collectOnlyVisible: false,
95
+ });
96
+ this.getStore().getModel().addFields([{
97
+ convert: this.serviceMap.getConvert(),
98
+ }]);
99
+ },
100
+
73
101
  onClearInput: function() {
74
102
  this.resetPageControl();
75
103
  this.loadLocalPage(1);
@@ -218,18 +246,19 @@ Ext.define('Coon.report.component.ReportTagLookup', {
218
246
  data = this.checkDangerousAmount(data);
219
247
 
220
248
  // исходные данные для возможности фильтрации
221
- this.initialData = data;
249
+ this.initialData = this.autoSortDynamicData ? this.getNormalizedSortedStoreData(data) : data;
222
250
  this.isLoaded = true;
223
251
 
224
252
  // размер данных больше одной страницы
225
253
  if (data.length > this.pageControl.pageSize) {
226
254
  this.isPagingEnabled = true;
227
- this.unpagedData = data;
255
+ this.unpagedData = this.autoSortDynamicData ? this.getNormalizedSortedStoreData(data) : data;
228
256
  this.configurePagination();
229
257
  this.loadPage(1);
230
258
  } else {
231
259
  this.isPagingEnabled = false;
232
260
  this.store.loadData(data);
261
+ this.autoSortDynamicData && this.setStoreNormalizeSort();
233
262
  }
234
263
 
235
264
  this.store.fireEvent('load', this.store, this.store.getRange(), true);
@@ -243,6 +272,34 @@ Ext.define('Coon.report.component.ReportTagLookup', {
243
272
  }
244
273
  },
245
274
 
275
+ /**
276
+ * Возвращает данные Store после их нормализованной сортировки.
277
+ * @param data
278
+ * @returns {*}
279
+ */
280
+ getNormalizedSortedStoreData(data) {
281
+ this.store.loadData(data);
282
+ this.setStoreNormalizeSort();
283
+ return this.store.getData().items.map((item) => item.getData());
284
+ },
285
+
286
+ /**
287
+ * Применяет нормализованную сортировку к Store.
288
+ * Сортировка происходит относительно displayField, чьи данные могут иметь динамический тип.
289
+ */
290
+ setStoreNormalizeSort() {
291
+ const normalizedSortFields = this.serviceMap.getDynamicTypeFields();
292
+ if (!Ext.Object.isEmpty(normalizedSortFields)) {
293
+ const autoSortField = Object.values(normalizedSortFields).find((value) => value.autoNormalizeSort);
294
+ if (!autoSortField) {
295
+ return;
296
+ }
297
+ this.getStore().setSorters({
298
+ sorterFn: this.serviceMap.getNormalizeSorterFn(autoSortField.originName),
299
+ });
300
+ }
301
+ },
302
+
246
303
  setValue(value) {
247
304
  Coon.log.debug('setValue', value);
248
305
  if (value && Array.isArray(value) && value.length) {