@myrtex-org/form 1.1.94 → 1.1.96

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.
@@ -17,7 +17,7 @@ import { AutoSaveStore, ToasterType, LabelModule, ModalServiceComponent, MODAL_D
17
17
  import { HttpHeaders, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
18
18
  import * as i2$1 from '@myrtex-org/templates';
19
19
  import { FormHeaderModule, MenuAdminModule } from '@myrtex-org/templates';
20
- import { cloneDeep } from 'lodash-es';
20
+ import { cloneDeep, isArray } from 'lodash-es';
21
21
  import * as i2$2 from '@angular/forms';
22
22
  import { FormsModule } from '@angular/forms';
23
23
  import { provideNgxMask } from 'ngx-mask';
@@ -1264,51 +1264,8 @@ class ComponentFactoryDirective {
1264
1264
  }
1265
1265
  }
1266
1266
  applyValues(values) {
1267
- if (!this.dynamicComponent) {
1268
- return;
1269
- }
1270
- // Сохраняем базовое поведение системы Myrtex
1271
- this.dynamicComponent.instance.values = values;
1272
- // ХАК ДЛЯ РЕЖИМА МОДАЛКИ (MANUAL):
1273
- // Принудительно инициализируем компоненты дат в обход пустого Стора
1274
- const currentMode = this.valueMode || this.dynamicComponent.instance.valueMode;
1275
- if (currentMode === 'manual' && values && this.data) {
1276
- const targetField = values.find((v) => v.sysName === this.data?.sysName);
1277
- const instance = this.dynamicComponent.instance;
1278
- if (targetField && targetField.value !== undefined && targetField.value !== null) {
1279
- const componentOptions = this.data?.options;
1280
- // Специфичный хак для InputDate с range: true
1281
- if (this.type === 'inputDate' && componentOptions?.range && Array.isArray(targetField.value)) {
1282
- // Проверяем, что лежит внутри массива. Если там строки, заворачиваем их в объекты, которые ждет компонент
1283
- const firstElement = targetField.value[0];
1284
- const secondElement = targetField.value[1];
1285
- const startValue = firstElement?.sysName ? firstElement.value : firstElement;
1286
- const endValue = secondElement?.sysName ? secondElement.value : secondElement;
1287
- // Формируем полноценные структуры ComponentValueModel
1288
- instance.modelStart = {
1289
- sysName: targetField.sysName,
1290
- type: targetField.type,
1291
- valueType: targetField.valueType,
1292
- value: startValue || null
1293
- };
1294
- instance.modelEnd = {
1295
- sysName: targetField.sysName,
1296
- type: targetField.type,
1297
- valueType: targetField.valueType,
1298
- value: endValue || null
1299
- };
1300
- if (typeof instance._customInit === 'function') {
1301
- instance._customInit();
1302
- }
1303
- }
1304
- else if (this.type === 'inputDate') {
1305
- // Для одиночной даты
1306
- instance.model = structuredClone(targetField);
1307
- if (typeof instance._customInit === 'function') {
1308
- instance._customInit();
1309
- }
1310
- }
1311
- }
1267
+ if (this.dynamicComponent) {
1268
+ this.dynamicComponent.instance.values = values;
1312
1269
  }
1313
1270
  }
1314
1271
  applyValueMode(valueMode) {
@@ -1774,7 +1731,6 @@ class InputDateComponent extends BaseFieldComponent {
1774
1731
  return this.settings.options.viewType === DateTypeEnum.DateTime;
1775
1732
  }
1776
1733
  updateValue(event) {
1777
- console.log(`[DateComponent -> updateValue] [sysName: ${this.settings.sysName}] Событие от UI элемента:`, event);
1778
1734
  if (this._isInit) {
1779
1735
  this._isUpdatingInternal = true; // Блокируем внешние обновления на время ввода
1780
1736
  if (this.settings.options.range) {
@@ -1796,7 +1752,6 @@ class InputDateComponent extends BaseFieldComponent {
1796
1752
  this.dateModel = event.value;
1797
1753
  this.model.value = event.value;
1798
1754
  }
1799
- console.log(`[DateComponent -> updateValue] Сформирован вызов dispatchModify. Отправляемое значение:`, this._transformOutputValue());
1800
1755
  this.dispatchModify(event);
1801
1756
  // Снимаем блокировку чуть позже, когда Store переварит изменения
1802
1757
  setTimeout(() => this._isUpdatingInternal = false, 600);
@@ -1834,12 +1789,8 @@ class InputDateComponent extends BaseFieldComponent {
1834
1789
  _initSubscriptionForValue() {
1835
1790
  this._subscriptions$.push(this._store.select(selectValueModel(this.settings))
1836
1791
  .subscribe((result) => {
1837
- console.log(`[DateComponent -> Store Subscription] [sysName: ${this.settings.sysName}] Получено из Store:`, structuredClone(result));
1838
- if (!result || this._isUpdatingInternal) {
1839
- if (this._isUpdatingInternal)
1840
- console.log(`[DateComponent] Пропущено обновление из Store, так как _isUpdatingInternal = true`);
1792
+ if (!result || this._isUpdatingInternal)
1841
1793
  return;
1842
- }
1843
1794
  // Если компонент уже инициализирован, проверяем, нужно ли обновляться.
1844
1795
  // Мы НЕ обновляемся, если данные в Store идентичны тем, что мы уже держим в памяти.
1845
1796
  if (this._isInit) {
@@ -1865,7 +1816,6 @@ class InputDateComponent extends BaseFieldComponent {
1865
1816
  }));
1866
1817
  }
1867
1818
  _customInit() {
1868
- console.log(`[DateComponent -> _customInit] [sysName: ${this.settings.sysName}] Настройка локальных моделей. Текущее состояние range:`, this.settings.options.range);
1869
1819
  if (this.settings.options.range) {
1870
1820
  if (!this.modelStart) {
1871
1821
  this.modelStart = defaultValueModel(this.settings);
@@ -1891,7 +1841,6 @@ class InputDateComponent extends BaseFieldComponent {
1891
1841
  else {
1892
1842
  this.dateModel = this.model.value;
1893
1843
  }
1894
- console.log(`[DateComponent -> _customInit] Результат инициализации: dateModel =`, this.dateModel, 'modelCounter =', this.modelCounter);
1895
1844
  this._isInit = true;
1896
1845
  this._detector.markForCheck();
1897
1846
  }
@@ -2245,43 +2194,22 @@ class InputTableModalComponent extends ModalServiceComponent {
2245
2194
  this.settings = data.settings;
2246
2195
  this.rowModel = data.rowModel;
2247
2196
  this.isCheckRequired = data.isCheckRequired;
2248
- console.log('[Modal -> Constructor] Данные, пришедшие в модалку:', {
2249
- title: data.title,
2250
- rowModel: structuredClone(data.rowModel)
2251
- });
2252
2197
  this.result = { result: false, rowModel: this.rowModel };
2253
2198
  }
2254
2199
  componentValueChanged(valueModel) {
2255
- console.log('[Modal -> componentValueChanged] Изменение компонента. Пришло значение:', structuredClone(valueModel));
2256
- if (Array.isArray(valueModel) && valueModel.length > 0) {
2257
- // Проверяем, это элементы диапазона дат (одинаковый sysName) или это разные компоненты
2258
- const firstSysName = valueModel[0].sysName;
2259
- const isRangeDate = valueModel.every(m => m.sysName === firstSysName && m.type === ComponentType.InputDate);
2260
- if (isRangeDate) {
2261
- // Собираем чистый массив значений дат [string | null, string | null]
2262
- const rangeValues = valueModel.map(m => m.value);
2263
- // Создаем синтетическую модель, где value — это массив дат
2264
- const rangePayload = {
2265
- ...valueModel[0],
2266
- value: rangeValues
2267
- };
2268
- this._transformValues(rangePayload);
2269
- }
2270
- else {
2271
- // Если это массив от разных компонентов (стандартная логика dispenser)
2272
- valueModel.forEach(model => this._transformValues(model));
2273
- }
2200
+ if (isArray(valueModel)) {
2201
+ valueModel.forEach(model => {
2202
+ this._transformValues(model);
2203
+ });
2274
2204
  }
2275
- else if (valueModel && !Array.isArray(valueModel)) {
2205
+ else {
2276
2206
  this._transformValues(valueModel);
2277
2207
  }
2278
2208
  this._recalculateFormulaFields();
2279
2209
  this.emptyRow = this.rowModel.data.every(x => this.isEmpty(x.value, x.valueType));
2280
2210
  }
2281
2211
  ok() {
2282
- console.log('[Modal -> OK] Попытка сохранения. Текущая модель перед закрытием:', structuredClone(this.rowModel));
2283
2212
  if (!this._isValid()) {
2284
- console.warn('[Modal -> OK] Форма не валидна!');
2285
2213
  // включаем подсветку полей
2286
2214
  this.store.dispatch(updateCheckRequired({ isCheckRequired: true }));
2287
2215
  return;
@@ -2300,15 +2228,10 @@ class InputTableModalComponent extends ModalServiceComponent {
2300
2228
  _transformValues(value) {
2301
2229
  const cloneRowModel = structuredClone(this.rowModel);
2302
2230
  const findValue = cloneRowModel.data.find((c) => c.sysName === value.sysName);
2303
- console.log(`[Modal -> _transformValues] Пытаемся обновить поле "${value.sysName}". Новое записываемое значение:`, value.value);
2304
2231
  if (findValue) {
2305
- findValue.value = value.value; // Теперь сюда запишется массив ['2026-05-05', '2026-05-07']
2306
- }
2307
- else {
2308
- console.warn(`[Modal -> _transformValues] Поле с sysName "${value.sysName}" не найдено в rowModel.data`);
2232
+ findValue.value = value.value;
2309
2233
  }
2310
2234
  this.rowModel = cloneRowModel;
2311
- console.log('[Modal -> _transformValues] Состояние rowModel.data после обновления:', structuredClone(this.rowModel.data));
2312
2235
  }
2313
2236
  _recalculateFormulaFields() {
2314
2237
  const formulaComponents = this._getFormulaNumberComponents(this.settings.components);
@@ -2474,40 +2397,10 @@ class InputTableComponent {
2474
2397
  this._subscriptions$.push(this._store.select(selectValueModel(this.settings))
2475
2398
  .subscribe((result) => {
2476
2399
  const cloneResult = structuredClone(result);
2477
- if (cloneResult && !Array.isArray(cloneResult) && cloneResult.sysName === this.settings.sysName) {
2478
- // --- ОБНОВЛЕННЫЙ БЛОК ПАРСИНГА И ЗАЩИТЫ ОТ NULL ---
2479
- if (cloneResult.data && Array.isArray(cloneResult.data)) {
2480
- cloneResult.data.forEach((row) => {
2481
- if (row.data && Array.isArray(row.data)) {
2482
- row.data.forEach((cell) => {
2483
- // 1. Если пришла строка со слэшем — парсим в массив
2484
- if (cell.type === 'inputDate' && typeof cell.value === 'string' && cell.value.includes('/')) {
2485
- console.log(`[Table <- Store Parsing] Восстанавливаем range-дату:`, cell.value);
2486
- cell.value = cell.value.split('/');
2487
- }
2488
- // 2. ЗАЩИТА: Если бэк прислал null, но у нас в текущей модели таблицы КУДА-ТО УЖЕ введена дата
2489
- if (cell.type === 'inputDate' && cell.value === null) {
2490
- // Ищем эту же ячейку в наших текущих строках на UI
2491
- const currentRow = this._rows?.find((r) => r.id === row.id);
2492
- const currentCell = currentRow?.data?.find((c) => c.sysName === cell.sysName);
2493
- if (currentCell && currentCell.value) {
2494
- console.warn(`[Table Protection] Бэк прислал null для ${cell.sysName}, но мы удерживаем текущее значение:`, currentCell.value);
2495
- // Не даем занулить! Возвращаем ей текущее валидное значение из UI
2496
- cell.value = structuredClone(currentCell.value);
2497
- }
2498
- }
2499
- });
2500
- }
2501
- });
2502
- }
2503
- // -------------------------------------------------
2504
- const isDataChanged = JSON.stringify(this.model?.data) !== JSON.stringify(cloneResult.data);
2505
- if (isDataChanged) {
2506
- console.log(`[Table -> Store Apply] [${this.settings.sysName}] Синхронизируем строки.`);
2507
- this.model = structuredClone(cloneResult);
2508
- this._rows = this._extractRows(this.model.data);
2509
- this._initDataSource(this._rows);
2510
- }
2400
+ if (cloneResult && !Array.isArray(cloneResult) && this.model.data !== cloneResult.data) {
2401
+ this.model = structuredClone(cloneResult);
2402
+ this._rows = this._extractRows(this.model.data);
2403
+ this._initDataSource(this._rows);
2511
2404
  }
2512
2405
  }));
2513
2406
  }
@@ -2516,7 +2409,6 @@ class InputTableComponent {
2516
2409
  this._subscriptions$.forEach((subscription) => subscription.unsubscribe());
2517
2410
  }
2518
2411
  createRow() {
2519
- const initialRowModel = getRowModel(this.settings);
2520
2412
  this._modalService.open(InputTableModalComponent, {
2521
2413
  title: 'Создание строки',
2522
2414
  okText: 'Создать',
@@ -2524,12 +2416,9 @@ class InputTableComponent {
2524
2416
  rowModel: getRowModel(this.settings),
2525
2417
  isCheckRequired: this._isCheckRequired
2526
2418
  }).afterClosed().subscribe(resolve => {
2527
- if (resolve && resolve.result && resolve.rowModel) {
2528
- // Накатываем модель из модалки (там внутри ДАТЫ ЕЩЕ В ВИДЕ МАССИВА Array(2))
2419
+ if (resolve.result) {
2529
2420
  this._rows = [...structuredClone(this._rows), resolve.rowModel];
2530
- // Инициализируем Грид МАССИВОМ — DevExtreme сразу отрендерит "дд.мм.гггг - дд.мм.гггг" без мигания!
2531
2421
  this._initDataSource(this._rows);
2532
- // А вот теперь принудительно вызываем сборку модели, которая сама сожмет массивы в строки ДЛЯ СТОРА
2533
2422
  this._changeSubject$.next(this._buildTableValueModel());
2534
2423
  }
2535
2424
  });
@@ -2546,55 +2435,26 @@ class InputTableComponent {
2546
2435
  return !this._canEditObject;
2547
2436
  }
2548
2437
  editRow(event) {
2549
- const rowId = event.row?.data?.id || event.data?.id;
2550
- const localRow = this._rows.find((r) => r.id === rowId);
2551
- if (!localRow)
2552
- return;
2553
- const rowModel = structuredClone(localRow);
2554
- const gridData = event.row?.data || event.data;
2555
- if (rowModel.data && Array.isArray(rowModel.data)) {
2556
- rowModel.data = rowModel.data.map((cell) => {
2557
- if (gridData && gridData[cell.sysName] !== undefined) {
2558
- const currentValue = gridData[cell.sysName];
2559
- // Если это range-дата и в гриде лежит массив из 2-х элементов
2560
- if (cell.type === 'inputDate' && cell.options?.range && Array.isArray(currentValue)) {
2561
- // Формируем структуру [ modelStart, modelEnd ], которую ждет InputDateComponent
2562
- cell.value = [
2563
- { sysName: cell.sysName, type: cell.type, valueType: cell.valueType, value: currentValue[0] },
2564
- { sysName: cell.sysName, type: cell.type, valueType: cell.valueType, value: currentValue[1] }
2565
- ];
2566
- }
2567
- else {
2568
- cell.value = structuredClone(currentValue);
2438
+ const findRow = this._rows.find(item => item.id === event.row.data.id);
2439
+ if (findRow) {
2440
+ this._modalService.open(InputTableModalComponent, {
2441
+ title: 'Редактирование строки',
2442
+ okText: 'Сохранить',
2443
+ settings: this.settings,
2444
+ rowModel: findRow
2445
+ }).afterClosed().subscribe(resolve => {
2446
+ if (resolve.result) {
2447
+ const cloneRows = structuredClone(this._rows);
2448
+ const editableRow = cloneRows.find(row => row.id === resolve.rowModel.id);
2449
+ if (editableRow) {
2450
+ editableRow.data = resolve.rowModel.data;
2451
+ this._rows = cloneRows;
2452
+ this._initDataSource(this._rows);
2453
+ this._changeSubject$.next(this._buildTableValueModel());
2569
2454
  }
2570
2455
  }
2571
- return cell;
2572
2456
  });
2573
2457
  }
2574
- console.log('[Table -> Edit Row] Чистая модель для модалки:', structuredClone(rowModel));
2575
- this._modalService.open(InputTableModalComponent, {
2576
- title: 'Редактирование строки',
2577
- okText: 'Сохранить',
2578
- settings: this.settings,
2579
- rowModel: rowModel,
2580
- isCheckRequired: this._isCheckRequired
2581
- }).afterClosed().subscribe(resolve => {
2582
- if (resolve && resolve.result && resolve.rowModel) {
2583
- // При сохранении вытаскиваем из структуры [modelStart, modelEnd] чистый массив дат обратно для грида
2584
- if (resolve.rowModel.data && Array.isArray(resolve.rowModel.data)) {
2585
- resolve.rowModel.data = resolve.rowModel.data.map((cell) => {
2586
- if (cell.type === 'inputDate' && cell.options?.range && Array.isArray(cell.value)) {
2587
- // Если там прилетел массив моделей (или массив строк от componentValueChanged)
2588
- cell.value = cell.value.map((item) => item?.sysName ? item.value : item);
2589
- }
2590
- return cell;
2591
- });
2592
- }
2593
- this._rows = this._rows.map((r) => r.id === rowId ? resolve.rowModel : r);
2594
- this._initDataSource(this._rows);
2595
- this._changeSubject$.next(this._buildTableValueModel());
2596
- }
2597
- });
2598
2458
  }
2599
2459
  getFormat(component) {
2600
2460
  switch (component.type) {
@@ -2639,18 +2499,14 @@ class InputTableComponent {
2639
2499
  this._rows = [];
2640
2500
  }
2641
2501
  _initDataSource(data) {
2642
- console.log('[Table -> _initDataSource] Сборка dataSource из rows:', structuredClone(data));
2643
2502
  this.dataSource = data.map((item) => {
2644
2503
  const newDataItem = {};
2645
2504
  for (let i in item.data) {
2646
- const cell = item.data[i];
2647
- // Напрямую берем значение ячейки (будь то строка или массив дат)
2648
- newDataItem[cell.sysName] = cell.value;
2505
+ newDataItem[item.data[i].sysName] = item.data[i].value;
2649
2506
  }
2650
2507
  newDataItem.id = item.id;
2651
2508
  return newDataItem;
2652
2509
  });
2653
- console.log('[Table -> _initDataSource] Итоговый dataSource, переданный в DataGrid:', structuredClone(this.dataSource));
2654
2510
  this._detector.detectChanges();
2655
2511
  if (this.dataGrid?.instance) {
2656
2512
  this.dataGrid.instance.refresh();
@@ -2691,31 +2547,6 @@ class InputTableComponent {
2691
2547
  format: this.getFormat(component),
2692
2548
  minWidth: 160
2693
2549
  };
2694
- if (component.type === ComponentType.InputDate && component.options.range) {
2695
- column.calculateCellValue = (rowData) => {
2696
- const dateRange = rowData[component.sysName];
2697
- if (Array.isArray(dateRange) && dateRange.length === 2) {
2698
- const formatDate = (dateStr) => {
2699
- if (!dateStr)
2700
- return '';
2701
- const d = new Date(dateStr);
2702
- if (isNaN(d.getTime()))
2703
- return '';
2704
- const day = String(d.getDate()).padStart(2, '0');
2705
- const month = String(d.getMonth() + 1).padStart(2, '0');
2706
- const year = d.getFullYear();
2707
- return `${day}.${month}.${year}`;
2708
- };
2709
- const startFormatted = formatDate(dateRange[0]);
2710
- const endFormatted = formatDate(dateRange[1]);
2711
- if (startFormatted && endFormatted) {
2712
- return `${startFormatted} — ${endFormatted}`;
2713
- }
2714
- return startFormatted || endFormatted || '';
2715
- }
2716
- return dateRange || '';
2717
- };
2718
- }
2719
2550
  if (dataType === 'boolean') {
2720
2551
  column.calculateCellValue = (rowData) => {
2721
2552
  const value = rowData[component.sysName];
@@ -2783,45 +2614,15 @@ class InputTableComponent {
2783
2614
  });
2784
2615
  });
2785
2616
  }
2786
- // private _buildTableValueModel(): TableValueModel {
2787
- // console.log('[Table -> _buildTableValueModel] Старт сборки модели для Стора. Текущие локальные _rows:', structuredClone(this._rows));
2788
- // const groupedData = this._buildGroupedItems(this._rows, this.settings.options.groups || [], 0);
2789
- // const totals = this._calculateTotals(this._rows);
2790
- // this.model = {
2791
- // sysName: this.settings.sysName,
2792
- // type: this.settings.type,
2793
- // data: groupedData,
2794
- // totals
2795
- // };
2796
- // console.log('[Table -> _buildTableValueModel] Сформированная модель для отправки в Стор:', structuredClone(this.model));
2797
- // return structuredClone(this.model);
2798
- // }
2799
2617
  _buildTableValueModel() {
2800
- console.log('[Table -> _buildTableValueModel] Старт сборки модели для Стора.');
2801
- // 1. Пробегаемся по ТЕКУЩИМ локальным строкам таблицы и принудительно жмем массивы дат в строки
2802
- if (this._rows && Array.isArray(this._rows)) {
2803
- this._rows.forEach(row => {
2804
- if (row.data && Array.isArray(row.data)) {
2805
- row.data.forEach((cell) => {
2806
- if (cell.type === 'inputDate' && Array.isArray(cell.value)) {
2807
- console.log(`[_buildTableValueModel] Найдена сырая рендж-дата в ${cell.sysName}, жмем в строку:`, cell.value);
2808
- cell.value = cell.value.join('/');
2809
- }
2810
- });
2811
- }
2812
- });
2813
- }
2814
- // 2. Делаем стандартную группировку Myrtex
2815
2618
  const groupedData = this._buildGroupedItems(this._rows, this.settings.options.groups || [], 0);
2816
2619
  const totals = this._calculateTotals(this._rows);
2817
- // 3. Формируем финальную модель, где внутри data уже гарантированно лежат строки вместо массивов
2818
2620
  this.model = {
2819
2621
  sysName: this.settings.sysName,
2820
2622
  type: this.settings.type,
2821
2623
  data: groupedData,
2822
2624
  totals
2823
2625
  };
2824
- console.log('[Table -> _buildTableValueModel] Итоговая модель со строковыми датами для отправки в Стор:', structuredClone(this.model));
2825
2626
  return structuredClone(this.model);
2826
2627
  }
2827
2628
  _buildGroupedItems(rows, groupFields, level) {