@myrtex-org/form 1.1.95 → 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.
- 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 +8 -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 +35 -190
- package/fesm2022/myrtex-org-form.mjs.map +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,12 @@ class InputTableModalComponent extends ModalServiceComponent {
|
|
|
2238
2197
|
this.result = { result: false, rowModel: this.rowModel };
|
|
2239
2198
|
}
|
|
2240
2199
|
componentValueChanged(valueModel) {
|
|
2241
|
-
if (
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
if (isRangeDate) {
|
|
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
|
+
if (isArray(valueModel)) {
|
|
2201
|
+
valueModel.forEach(model => {
|
|
2202
|
+
this._transformValues(model);
|
|
2203
|
+
});
|
|
2259
2204
|
}
|
|
2260
|
-
else
|
|
2205
|
+
else {
|
|
2261
2206
|
this._transformValues(valueModel);
|
|
2262
2207
|
}
|
|
2263
2208
|
this._recalculateFormulaFields();
|
|
@@ -2284,7 +2229,7 @@ class InputTableModalComponent extends ModalServiceComponent {
|
|
|
2284
2229
|
const cloneRowModel = structuredClone(this.rowModel);
|
|
2285
2230
|
const findValue = cloneRowModel.data.find((c) => c.sysName === value.sysName);
|
|
2286
2231
|
if (findValue) {
|
|
2287
|
-
findValue.value = value.value;
|
|
2232
|
+
findValue.value = value.value;
|
|
2288
2233
|
}
|
|
2289
2234
|
this.rowModel = cloneRowModel;
|
|
2290
2235
|
}
|
|
@@ -2452,37 +2397,10 @@ class InputTableComponent {
|
|
|
2452
2397
|
this._subscriptions$.push(this._store.select(selectValueModel(this.settings))
|
|
2453
2398
|
.subscribe((result) => {
|
|
2454
2399
|
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
|
-
}
|
|
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);
|
|
2486
2404
|
}
|
|
2487
2405
|
}));
|
|
2488
2406
|
}
|
|
@@ -2491,7 +2409,6 @@ class InputTableComponent {
|
|
|
2491
2409
|
this._subscriptions$.forEach((subscription) => subscription.unsubscribe());
|
|
2492
2410
|
}
|
|
2493
2411
|
createRow() {
|
|
2494
|
-
const initialRowModel = getRowModel(this.settings);
|
|
2495
2412
|
this._modalService.open(InputTableModalComponent, {
|
|
2496
2413
|
title: 'Создание строки',
|
|
2497
2414
|
okText: 'Создать',
|
|
@@ -2499,12 +2416,9 @@ class InputTableComponent {
|
|
|
2499
2416
|
rowModel: getRowModel(this.settings),
|
|
2500
2417
|
isCheckRequired: this._isCheckRequired
|
|
2501
2418
|
}).afterClosed().subscribe(resolve => {
|
|
2502
|
-
if (resolve
|
|
2503
|
-
// Накатываем модель из модалки (там внутри ДАТЫ ЕЩЕ В ВИДЕ МАССИВА Array(2))
|
|
2419
|
+
if (resolve.result) {
|
|
2504
2420
|
this._rows = [...structuredClone(this._rows), resolve.rowModel];
|
|
2505
|
-
// Инициализируем Грид МАССИВОМ — DevExtreme сразу отрендерит "дд.мм.гггг - дд.мм.гггг" без мигания!
|
|
2506
2421
|
this._initDataSource(this._rows);
|
|
2507
|
-
// А вот теперь принудительно вызываем сборку модели, которая сама сожмет массивы в строки ДЛЯ СТОРА
|
|
2508
2422
|
this._changeSubject$.next(this._buildTableValueModel());
|
|
2509
2423
|
}
|
|
2510
2424
|
});
|
|
@@ -2521,54 +2435,26 @@ class InputTableComponent {
|
|
|
2521
2435
|
return !this._canEditObject;
|
|
2522
2436
|
}
|
|
2523
2437
|
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);
|
|
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());
|
|
2544
2454
|
}
|
|
2545
2455
|
}
|
|
2546
|
-
return cell;
|
|
2547
2456
|
});
|
|
2548
2457
|
}
|
|
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
2458
|
}
|
|
2573
2459
|
getFormat(component) {
|
|
2574
2460
|
switch (component.type) {
|
|
@@ -2616,9 +2502,7 @@ class InputTableComponent {
|
|
|
2616
2502
|
this.dataSource = data.map((item) => {
|
|
2617
2503
|
const newDataItem = {};
|
|
2618
2504
|
for (let i in item.data) {
|
|
2619
|
-
|
|
2620
|
-
// Напрямую берем значение ячейки (будь то строка или массив дат)
|
|
2621
|
-
newDataItem[cell.sysName] = cell.value;
|
|
2505
|
+
newDataItem[item.data[i].sysName] = item.data[i].value;
|
|
2622
2506
|
}
|
|
2623
2507
|
newDataItem.id = item.id;
|
|
2624
2508
|
return newDataItem;
|
|
@@ -2663,31 +2547,6 @@ class InputTableComponent {
|
|
|
2663
2547
|
format: this.getFormat(component),
|
|
2664
2548
|
minWidth: 160
|
|
2665
2549
|
};
|
|
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
2550
|
if (dataType === 'boolean') {
|
|
2692
2551
|
column.calculateCellValue = (rowData) => {
|
|
2693
2552
|
const value = rowData[component.sysName];
|
|
@@ -2756,22 +2615,8 @@ class InputTableComponent {
|
|
|
2756
2615
|
});
|
|
2757
2616
|
}
|
|
2758
2617
|
_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
2618
|
const groupedData = this._buildGroupedItems(this._rows, this.settings.options.groups || [], 0);
|
|
2773
2619
|
const totals = this._calculateTotals(this._rows);
|
|
2774
|
-
// 3. Формируем финальную модель, где внутри data уже гарантированно лежат строки вместо массивов
|
|
2775
2620
|
this.model = {
|
|
2776
2621
|
sysName: this.settings.sysName,
|
|
2777
2622
|
type: this.settings.type,
|