@myrtex-org/form 1.1.95 → 1.1.97
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/esm2022/lib/modules/object-form/components/elements/input/date/input-date.component.mjs +5 -3
- package/esm2022/lib/modules/object-form/components/elements/input/table/components/input-table-modal/input-table-modal.component.mjs +9 -21
- package/esm2022/lib/modules/object-form/components/elements/input/table/input-table.component.mjs +23 -123
- package/esm2022/lib/modules/object-form/factories/component-factory/component-factory.directive.mjs +3 -46
- package/fesm2022/myrtex-org-form.mjs +36 -190
- package/fesm2022/myrtex-org-form.mjs.map +1 -1
- package/lib/modules/object-form/components/elements/input/table/input-table.component.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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 (
|
|
1268
|
-
|
|
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) {
|
|
@@ -1832,9 +1789,8 @@ class InputDateComponent extends BaseFieldComponent {
|
|
|
1832
1789
|
_initSubscriptionForValue() {
|
|
1833
1790
|
this._subscriptions$.push(this._store.select(selectValueModel(this.settings))
|
|
1834
1791
|
.subscribe((result) => {
|
|
1835
|
-
if (!result || this._isUpdatingInternal)
|
|
1792
|
+
if (!result || this._isUpdatingInternal)
|
|
1836
1793
|
return;
|
|
1837
|
-
}
|
|
1838
1794
|
// Если компонент уже инициализирован, проверяем, нужно ли обновляться.
|
|
1839
1795
|
// Мы НЕ обновляемся, если данные в Store идентичны тем, что мы уже держим в памяти.
|
|
1840
1796
|
if (this._isInit) {
|
|
@@ -1892,6 +1848,7 @@ class InputDateComponent extends BaseFieldComponent {
|
|
|
1892
1848
|
const start = new Date(startDate);
|
|
1893
1849
|
const end = new Date(endDate);
|
|
1894
1850
|
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
|
|
1851
|
+
console.error('Invalid date format');
|
|
1895
1852
|
return 0;
|
|
1896
1853
|
}
|
|
1897
1854
|
const differenceInMs = Math.abs(end.getTime() - start.getTime());
|
|
@@ -1901,9 +1858,11 @@ class InputDateComponent extends BaseFieldComponent {
|
|
|
1901
1858
|
if (startDate) {
|
|
1902
1859
|
const start = new Date(startDate);
|
|
1903
1860
|
if (isNaN(start.getTime())) {
|
|
1861
|
+
console.error('Invalid start date format');
|
|
1904
1862
|
return startDate;
|
|
1905
1863
|
}
|
|
1906
1864
|
if (!Number.isInteger(days) || days < 0) {
|
|
1865
|
+
console.error('Days must be a non-negative integer');
|
|
1907
1866
|
return startDate;
|
|
1908
1867
|
}
|
|
1909
1868
|
let newDate = new Date(start.getTime() + days * this._millisecondsInDay);
|
|
@@ -2238,26 +2197,13 @@ class InputTableModalComponent extends ModalServiceComponent {
|
|
|
2238
2197
|
this.result = { result: false, rowModel: this.rowModel };
|
|
2239
2198
|
}
|
|
2240
2199
|
componentValueChanged(valueModel) {
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
// Собираем чистый массив значений дат [string | null, string | null]
|
|
2247
|
-
const rangeValues = valueModel.map(m => m.value);
|
|
2248
|
-
// Создаем синтетическую модель, где value — это массив дат
|
|
2249
|
-
const rangePayload = {
|
|
2250
|
-
...valueModel[0],
|
|
2251
|
-
value: rangeValues
|
|
2252
|
-
};
|
|
2253
|
-
this._transformValues(rangePayload);
|
|
2254
|
-
}
|
|
2255
|
-
else {
|
|
2256
|
-
// Если это массив от разных компонентов (стандартная логика dispenser)
|
|
2257
|
-
valueModel.forEach(model => this._transformValues(model));
|
|
2258
|
-
}
|
|
2200
|
+
console.log(valueModel, 'valueModel in componentValueChanged');
|
|
2201
|
+
if (isArray(valueModel)) {
|
|
2202
|
+
valueModel.forEach(model => {
|
|
2203
|
+
this._transformValues(model);
|
|
2204
|
+
});
|
|
2259
2205
|
}
|
|
2260
|
-
else
|
|
2206
|
+
else {
|
|
2261
2207
|
this._transformValues(valueModel);
|
|
2262
2208
|
}
|
|
2263
2209
|
this._recalculateFormulaFields();
|
|
@@ -2284,7 +2230,7 @@ class InputTableModalComponent extends ModalServiceComponent {
|
|
|
2284
2230
|
const cloneRowModel = structuredClone(this.rowModel);
|
|
2285
2231
|
const findValue = cloneRowModel.data.find((c) => c.sysName === value.sysName);
|
|
2286
2232
|
if (findValue) {
|
|
2287
|
-
findValue.value = value.value;
|
|
2233
|
+
findValue.value = value.value;
|
|
2288
2234
|
}
|
|
2289
2235
|
this.rowModel = cloneRowModel;
|
|
2290
2236
|
}
|
|
@@ -2452,37 +2398,10 @@ class InputTableComponent {
|
|
|
2452
2398
|
this._subscriptions$.push(this._store.select(selectValueModel(this.settings))
|
|
2453
2399
|
.subscribe((result) => {
|
|
2454
2400
|
const cloneResult = structuredClone(result);
|
|
2455
|
-
if (cloneResult && !Array.isArray(cloneResult) &&
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
if (row.data && Array.isArray(row.data)) {
|
|
2460
|
-
row.data.forEach((cell) => {
|
|
2461
|
-
// 1. Если пришла строка со слэшем — парсим в массив
|
|
2462
|
-
if (cell.type === 'inputDate' && typeof cell.value === 'string' && cell.value.includes('/')) {
|
|
2463
|
-
cell.value = cell.value.split('/');
|
|
2464
|
-
}
|
|
2465
|
-
// 2. ЗАЩИТА: Если бэк прислал null, но у нас в текущей модели таблицы КУДА-ТО УЖЕ введена дата
|
|
2466
|
-
if (cell.type === 'inputDate' && cell.value === null) {
|
|
2467
|
-
// Ищем эту же ячейку в наших текущих строках на UI
|
|
2468
|
-
const currentRow = this._rows?.find((r) => r.id === row.id);
|
|
2469
|
-
const currentCell = currentRow?.data?.find((c) => c.sysName === cell.sysName);
|
|
2470
|
-
if (currentCell && currentCell.value) {
|
|
2471
|
-
// Не даем занулить! Возвращаем ей текущее валидное значение из UI
|
|
2472
|
-
cell.value = structuredClone(currentCell.value);
|
|
2473
|
-
}
|
|
2474
|
-
}
|
|
2475
|
-
});
|
|
2476
|
-
}
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
// -------------------------------------------------
|
|
2480
|
-
const isDataChanged = JSON.stringify(this.model?.data) !== JSON.stringify(cloneResult.data);
|
|
2481
|
-
if (isDataChanged) {
|
|
2482
|
-
this.model = structuredClone(cloneResult);
|
|
2483
|
-
this._rows = this._extractRows(this.model.data);
|
|
2484
|
-
this._initDataSource(this._rows);
|
|
2485
|
-
}
|
|
2401
|
+
if (cloneResult && !Array.isArray(cloneResult) && this.model.data !== cloneResult.data) {
|
|
2402
|
+
this.model = structuredClone(cloneResult);
|
|
2403
|
+
this._rows = this._extractRows(this.model.data);
|
|
2404
|
+
this._initDataSource(this._rows);
|
|
2486
2405
|
}
|
|
2487
2406
|
}));
|
|
2488
2407
|
}
|
|
@@ -2491,7 +2410,6 @@ class InputTableComponent {
|
|
|
2491
2410
|
this._subscriptions$.forEach((subscription) => subscription.unsubscribe());
|
|
2492
2411
|
}
|
|
2493
2412
|
createRow() {
|
|
2494
|
-
const initialRowModel = getRowModel(this.settings);
|
|
2495
2413
|
this._modalService.open(InputTableModalComponent, {
|
|
2496
2414
|
title: 'Создание строки',
|
|
2497
2415
|
okText: 'Создать',
|
|
@@ -2499,12 +2417,9 @@ class InputTableComponent {
|
|
|
2499
2417
|
rowModel: getRowModel(this.settings),
|
|
2500
2418
|
isCheckRequired: this._isCheckRequired
|
|
2501
2419
|
}).afterClosed().subscribe(resolve => {
|
|
2502
|
-
if (resolve
|
|
2503
|
-
// Накатываем модель из модалки (там внутри ДАТЫ ЕЩЕ В ВИДЕ МАССИВА Array(2))
|
|
2420
|
+
if (resolve.result) {
|
|
2504
2421
|
this._rows = [...structuredClone(this._rows), resolve.rowModel];
|
|
2505
|
-
// Инициализируем Грид МАССИВОМ — DevExtreme сразу отрендерит "дд.мм.гггг - дд.мм.гггг" без мигания!
|
|
2506
2422
|
this._initDataSource(this._rows);
|
|
2507
|
-
// А вот теперь принудительно вызываем сборку модели, которая сама сожмет массивы в строки ДЛЯ СТОРА
|
|
2508
2423
|
this._changeSubject$.next(this._buildTableValueModel());
|
|
2509
2424
|
}
|
|
2510
2425
|
});
|
|
@@ -2521,54 +2436,26 @@ class InputTableComponent {
|
|
|
2521
2436
|
return !this._canEditObject;
|
|
2522
2437
|
}
|
|
2523
2438
|
editRow(event) {
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
if (
|
|
2533
|
-
const
|
|
2534
|
-
|
|
2535
|
-
if (
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
];
|
|
2541
|
-
}
|
|
2542
|
-
else {
|
|
2543
|
-
cell.value = structuredClone(currentValue);
|
|
2439
|
+
const findRow = this._rows.find(item => item.id === event.row.data.id);
|
|
2440
|
+
if (findRow) {
|
|
2441
|
+
this._modalService.open(InputTableModalComponent, {
|
|
2442
|
+
title: 'Редактирование строки',
|
|
2443
|
+
okText: 'Сохранить',
|
|
2444
|
+
settings: this.settings,
|
|
2445
|
+
rowModel: findRow
|
|
2446
|
+
}).afterClosed().subscribe(resolve => {
|
|
2447
|
+
if (resolve.result) {
|
|
2448
|
+
const cloneRows = structuredClone(this._rows);
|
|
2449
|
+
const editableRow = cloneRows.find(row => row.id === resolve.rowModel.id);
|
|
2450
|
+
if (editableRow) {
|
|
2451
|
+
editableRow.data = resolve.rowModel.data;
|
|
2452
|
+
this._rows = cloneRows;
|
|
2453
|
+
this._initDataSource(this._rows);
|
|
2454
|
+
this._changeSubject$.next(this._buildTableValueModel());
|
|
2544
2455
|
}
|
|
2545
2456
|
}
|
|
2546
|
-
return cell;
|
|
2547
2457
|
});
|
|
2548
2458
|
}
|
|
2549
|
-
this._modalService.open(InputTableModalComponent, {
|
|
2550
|
-
title: 'Редактирование строки',
|
|
2551
|
-
okText: 'Сохранить',
|
|
2552
|
-
settings: this.settings,
|
|
2553
|
-
rowModel: rowModel,
|
|
2554
|
-
isCheckRequired: this._isCheckRequired
|
|
2555
|
-
}).afterClosed().subscribe(resolve => {
|
|
2556
|
-
if (resolve && resolve.result && resolve.rowModel) {
|
|
2557
|
-
// При сохранении вытаскиваем из структуры [modelStart, modelEnd] чистый массив дат обратно для грида
|
|
2558
|
-
if (resolve.rowModel.data && Array.isArray(resolve.rowModel.data)) {
|
|
2559
|
-
resolve.rowModel.data = resolve.rowModel.data.map((cell) => {
|
|
2560
|
-
if (cell.type === 'inputDate' && cell.options?.range && Array.isArray(cell.value)) {
|
|
2561
|
-
// Если там прилетел массив моделей (или массив строк от componentValueChanged)
|
|
2562
|
-
cell.value = cell.value.map((item) => item?.sysName ? item.value : item);
|
|
2563
|
-
}
|
|
2564
|
-
return cell;
|
|
2565
|
-
});
|
|
2566
|
-
}
|
|
2567
|
-
this._rows = this._rows.map((r) => r.id === rowId ? resolve.rowModel : r);
|
|
2568
|
-
this._initDataSource(this._rows);
|
|
2569
|
-
this._changeSubject$.next(this._buildTableValueModel());
|
|
2570
|
-
}
|
|
2571
|
-
});
|
|
2572
2459
|
}
|
|
2573
2460
|
getFormat(component) {
|
|
2574
2461
|
switch (component.type) {
|
|
@@ -2616,9 +2503,7 @@ class InputTableComponent {
|
|
|
2616
2503
|
this.dataSource = data.map((item) => {
|
|
2617
2504
|
const newDataItem = {};
|
|
2618
2505
|
for (let i in item.data) {
|
|
2619
|
-
|
|
2620
|
-
// Напрямую берем значение ячейки (будь то строка или массив дат)
|
|
2621
|
-
newDataItem[cell.sysName] = cell.value;
|
|
2506
|
+
newDataItem[item.data[i].sysName] = item.data[i].value;
|
|
2622
2507
|
}
|
|
2623
2508
|
newDataItem.id = item.id;
|
|
2624
2509
|
return newDataItem;
|
|
@@ -2663,31 +2548,6 @@ class InputTableComponent {
|
|
|
2663
2548
|
format: this.getFormat(component),
|
|
2664
2549
|
minWidth: 160
|
|
2665
2550
|
};
|
|
2666
|
-
if (component.type === ComponentType.InputDate && component.options.range) {
|
|
2667
|
-
column.calculateCellValue = (rowData) => {
|
|
2668
|
-
const dateRange = rowData[component.sysName];
|
|
2669
|
-
if (Array.isArray(dateRange) && dateRange.length === 2) {
|
|
2670
|
-
const formatDate = (dateStr) => {
|
|
2671
|
-
if (!dateStr)
|
|
2672
|
-
return '';
|
|
2673
|
-
const d = new Date(dateStr);
|
|
2674
|
-
if (isNaN(d.getTime()))
|
|
2675
|
-
return '';
|
|
2676
|
-
const day = String(d.getDate()).padStart(2, '0');
|
|
2677
|
-
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
2678
|
-
const year = d.getFullYear();
|
|
2679
|
-
return `${day}.${month}.${year}`;
|
|
2680
|
-
};
|
|
2681
|
-
const startFormatted = formatDate(dateRange[0]);
|
|
2682
|
-
const endFormatted = formatDate(dateRange[1]);
|
|
2683
|
-
if (startFormatted && endFormatted) {
|
|
2684
|
-
return `${startFormatted} — ${endFormatted}`;
|
|
2685
|
-
}
|
|
2686
|
-
return startFormatted || endFormatted || '';
|
|
2687
|
-
}
|
|
2688
|
-
return dateRange || '';
|
|
2689
|
-
};
|
|
2690
|
-
}
|
|
2691
2551
|
if (dataType === 'boolean') {
|
|
2692
2552
|
column.calculateCellValue = (rowData) => {
|
|
2693
2553
|
const value = rowData[component.sysName];
|
|
@@ -2756,22 +2616,8 @@ class InputTableComponent {
|
|
|
2756
2616
|
});
|
|
2757
2617
|
}
|
|
2758
2618
|
_buildTableValueModel() {
|
|
2759
|
-
// 1. Пробегаемся по ТЕКУЩИМ локальным строкам таблицы и принудительно жмем массивы дат в строки
|
|
2760
|
-
if (this._rows && Array.isArray(this._rows)) {
|
|
2761
|
-
this._rows.forEach(row => {
|
|
2762
|
-
if (row.data && Array.isArray(row.data)) {
|
|
2763
|
-
row.data.forEach((cell) => {
|
|
2764
|
-
if (cell.type === 'inputDate' && Array.isArray(cell.value)) {
|
|
2765
|
-
cell.value = cell.value.join('/');
|
|
2766
|
-
}
|
|
2767
|
-
});
|
|
2768
|
-
}
|
|
2769
|
-
});
|
|
2770
|
-
}
|
|
2771
|
-
// 2. Делаем стандартную группировку Myrtex
|
|
2772
2619
|
const groupedData = this._buildGroupedItems(this._rows, this.settings.options.groups || [], 0);
|
|
2773
2620
|
const totals = this._calculateTotals(this._rows);
|
|
2774
|
-
// 3. Формируем финальную модель, где внутри data уже гарантированно лежат строки вместо массивов
|
|
2775
2621
|
this.model = {
|
|
2776
2622
|
sysName: this.settings.sysName,
|
|
2777
2623
|
type: this.settings.type,
|