@ldquocc/dynamic-form 0.0.4 → 0.0.6
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/fesm2022/ldquocc-dynamic-form.mjs +1029 -2842
- package/fesm2022/ldquocc-dynamic-form.mjs.map +1 -1
- package/lib/shared/components/component.d.ts +13 -14
- package/lib/shared/components/dynamic-field/dynamic-field.component.d.ts +5 -5
- package/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.d.ts +1 -0
- package/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.d.ts +7 -6
- package/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.d.ts +1 -0
- package/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.d.ts +1 -0
- package/lib/shared/consts/common/index.d.ts +0 -1
- package/lib/shared/models/fields/upload/index.d.ts +4 -0
- package/lib/shared/services/dynamic-form.service.d.ts +3 -3
- package/lib/shared/utils/validators/custom-validators.d.ts +1 -2
- package/package.json +1 -1
- package/public-api.d.ts +3 -4
- package/lib/shared/components/fields/radio/custom/reminder-type-radio/reminder-type-radio.component.d.ts +0 -24
- package/lib/shared/consts/common/sample-test.const.d.ts +0 -68
- package/lib/shared/models/fields/radio/custom/reminder-type-radio/reminder-option.model.d.ts +0 -26
- package/lib/shared/models/fields/radio/custom/reminder-type-radio/reminder-type-radio-field.model.d.ts +0 -14
- package/lib/shared/models/fields/radio/custom/reminder-type-radio/reminder-type.model.d.ts +0 -1
- package/lib/shared/models/fields/radio/custom/reminder-type-radio/reminder-value.model.d.ts +0 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i1 from '@angular/forms';
|
|
2
|
-
import { FormsModule, ReactiveFormsModule,
|
|
2
|
+
import { FormsModule, ReactiveFormsModule, Validators, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
3
3
|
import { of, delay, Subject, takeUntil, finalize, Observable, debounceTime, distinctUntilChanged } from 'rxjs';
|
|
4
4
|
import * as i1$2 from '@angular/common';
|
|
5
5
|
import { CommonModule, CurrencyPipe } from '@angular/common';
|
|
@@ -22,24 +22,23 @@ import { NzButtonModule } from 'ng-zorro-antd/button';
|
|
|
22
22
|
import * as i5 from 'ng-zorro-antd/core/transition-patch';
|
|
23
23
|
import * as i6 from 'ng-zorro-antd/core/wave';
|
|
24
24
|
import { NgxCurrencyDirective } from 'ngx-currency';
|
|
25
|
-
import * as i3$1 from 'ng-zorro-antd/
|
|
26
|
-
import { NzRadioModule } from 'ng-zorro-antd/radio';
|
|
27
|
-
import * as i4$1 from 'ng-zorro-antd/select';
|
|
25
|
+
import * as i3$1 from 'ng-zorro-antd/select';
|
|
28
26
|
import { NzSelectModule } from 'ng-zorro-antd/select';
|
|
29
|
-
import { ToastrService } from 'ngx-toastr';
|
|
30
27
|
import * as i3$2 from 'ng-zorro-antd/spin';
|
|
31
28
|
import { NzSpinModule } from 'ng-zorro-antd/spin';
|
|
32
29
|
import * as i2$1 from 'ng-zorro-antd/tree-select';
|
|
33
30
|
import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select';
|
|
34
31
|
import * as i2$2 from 'ng-zorro-antd/upload';
|
|
35
32
|
import { NzUploadModule } from 'ng-zorro-antd/upload';
|
|
33
|
+
import { NzNotificationService } from 'ng-zorro-antd/notification';
|
|
34
|
+
import { ToastrService } from 'ngx-toastr';
|
|
36
35
|
import { TranslateService } from '@ngx-translate/core';
|
|
37
36
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
|
38
37
|
import * as i7 from 'ng-zorro-antd/image';
|
|
39
38
|
import { NzImageModule } from 'ng-zorro-antd/image';
|
|
40
39
|
import * as i2$3 from 'ng-zorro-antd/grid';
|
|
41
40
|
import { NzGridModule } from 'ng-zorro-antd/grid';
|
|
42
|
-
import * as i4$
|
|
41
|
+
import * as i4$1 from 'ng-zorro-antd/collapse';
|
|
43
42
|
import { NzCollapseModule } from 'ng-zorro-antd/collapse';
|
|
44
43
|
|
|
45
44
|
// Core Angular modules (cần thiết ở hầu hết các component).
|
|
@@ -171,6 +170,220 @@ const DEFAULT_ACCEPTED_IMAGE_MIME_TYPES = [
|
|
|
171
170
|
IMAGE_TYPES.svg,
|
|
172
171
|
];
|
|
173
172
|
|
|
173
|
+
const COMBINE_DEPEND_TYPES = ['skip', 'null', 'valid'];
|
|
174
|
+
|
|
175
|
+
// Format file size, đầu vào là Byte, và loại muốn format sang.
|
|
176
|
+
const formatFileSize = (size, type) => {
|
|
177
|
+
let returnVal = 0;
|
|
178
|
+
switch (type) {
|
|
179
|
+
case 'KB':
|
|
180
|
+
returnVal = size / 1024;
|
|
181
|
+
break;
|
|
182
|
+
case 'MB':
|
|
183
|
+
returnVal = size / (1024 * 1024);
|
|
184
|
+
break;
|
|
185
|
+
case 'GB':
|
|
186
|
+
returnVal = size / (1024 * 1024 * 1024);
|
|
187
|
+
break;
|
|
188
|
+
case 'TB':
|
|
189
|
+
returnVal = size / (1024 * 1024 * 1024 * 1024);
|
|
190
|
+
break;
|
|
191
|
+
default:
|
|
192
|
+
returnVal = size;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
return returnVal;
|
|
196
|
+
};
|
|
197
|
+
// Lấy extension. dựa vào các cấu hình mimeTypes sẵn.
|
|
198
|
+
const getExtensions = (acptMimeTypes) => {
|
|
199
|
+
return acptMimeTypes.map(mime => `.${MIME_TYPES_TO_EXTENSION[mime] || mime}`);
|
|
200
|
+
};
|
|
201
|
+
// Tạo một file giả từ thông tin name, type và size.
|
|
202
|
+
const createFakeFile = (name, type, size) => {
|
|
203
|
+
const buffer = new Uint8Array(size); // tạo buffer theo size, không crash nếu file có dung lượng lớn.
|
|
204
|
+
const blob = new Blob([buffer], { type });
|
|
205
|
+
return new File([blob], name, { type, lastModified: Date.now() });
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
var FileSizeTypeEnum;
|
|
209
|
+
(function (FileSizeTypeEnum) {
|
|
210
|
+
FileSizeTypeEnum["MB"] = "MB";
|
|
211
|
+
FileSizeTypeEnum["KB"] = "KB";
|
|
212
|
+
FileSizeTypeEnum["GB"] = "GB";
|
|
213
|
+
FileSizeTypeEnum["TB"] = "TB";
|
|
214
|
+
})(FileSizeTypeEnum || (FileSizeTypeEnum = {}));
|
|
215
|
+
|
|
216
|
+
// Kiểm tra tính hợp lệ của JSON string.
|
|
217
|
+
const isValidJSON = (str) => {
|
|
218
|
+
try {
|
|
219
|
+
JSON.parse(str);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
};
|
|
226
|
+
// Tạo một chuỗi uuid.
|
|
227
|
+
const uuidGenerator = () => {
|
|
228
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
229
|
+
var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
230
|
+
return v.toString(16);
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
const lowerCaseFirstChar = (str) => {
|
|
234
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
235
|
+
};
|
|
236
|
+
const upperCaseFirstChar = (str) => {
|
|
237
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
238
|
+
};
|
|
239
|
+
const deepCopyArray = (array) => {
|
|
240
|
+
return array.map(item => {
|
|
241
|
+
if (Array.isArray(item)) {
|
|
242
|
+
return deepCopyArray(item); // Deep copy nếu phần tử là mảng lồng nhau
|
|
243
|
+
}
|
|
244
|
+
else if (item && typeof item === 'object') {
|
|
245
|
+
return deepCopyObject(item); // Deep copy nếu phần tử là đối tượng
|
|
246
|
+
}
|
|
247
|
+
return item; // Trả về giá trị gốc nếu không phải mảng hoặc đối tượng
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
const deepCopyObject = (obj) => {
|
|
251
|
+
return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
|
|
252
|
+
if (Array.isArray(value)) {
|
|
253
|
+
return [key, deepCopyArray(value)]; // Deep copy nếu giá trị là mảng
|
|
254
|
+
}
|
|
255
|
+
else if (value && typeof value === 'object') {
|
|
256
|
+
return [key, deepCopyObject(value)]; // Deep copy nếu giá trị là đối tượng
|
|
257
|
+
}
|
|
258
|
+
return [key, value]; // Trả về giá trị gốc
|
|
259
|
+
}));
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Không cho phép nhập khoảng trắng ở đầu chuỗi, nếu không nhập gì vẫn tính đúng.
|
|
263
|
+
const noLeadingSpaceValidator = () => {
|
|
264
|
+
return (control) => {
|
|
265
|
+
const value = (control.value ?? "").toString();
|
|
266
|
+
if (!value)
|
|
267
|
+
return null; // Không required thì để trống là hợp lệ
|
|
268
|
+
return value[0] === " " ? { leadingSpace: true } : null;
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
// Không cho phép nhập toàn khoảng trắng.
|
|
272
|
+
const noWhitespaceValidator = () => {
|
|
273
|
+
return (control) => {
|
|
274
|
+
const isWhitespace = (control.value || "").toString().trim().length === 0;
|
|
275
|
+
return isWhitespace ? { whitespace: true } : null;
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
// Bắt đầu với một kí tự được chỉ định.
|
|
279
|
+
const startsWithValidator = (prefix) => {
|
|
280
|
+
return (control) => {
|
|
281
|
+
const value = control.value ?? "";
|
|
282
|
+
return value.startsWith(prefix)
|
|
283
|
+
? null
|
|
284
|
+
: { startsWith: { requiredPrefix: prefix } };
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
// So sánh giá trị field A không được lớn hơn field B.
|
|
288
|
+
const minMaxFieldValidator = (minKey, maxKey) => {
|
|
289
|
+
// group: AbstractControl sẽ đại diện cho FormGroup tức là cha bao hàm các field.
|
|
290
|
+
return (group) => {
|
|
291
|
+
const min = group.get(minKey)?.value;
|
|
292
|
+
const max = group.get(maxKey)?.value;
|
|
293
|
+
if (min != null && max != null && min > max) {
|
|
294
|
+
return { minMaxInvalid: true };
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
// Match giữa hai field (ví dụ: password/confirmPassword,...).
|
|
300
|
+
const matchFieldsValidator = (fieldToMatch) => {
|
|
301
|
+
return (control) => {
|
|
302
|
+
const parent = control.parent;
|
|
303
|
+
if (!parent)
|
|
304
|
+
return null;
|
|
305
|
+
const target = parent.get(fieldToMatch);
|
|
306
|
+
if (!target?.value)
|
|
307
|
+
return null;
|
|
308
|
+
// Nếu đang validate chính nó (ví dụ: confirmPassword)
|
|
309
|
+
const isMismatch = control.value !== target.value;
|
|
310
|
+
// Nếu trước đó password (target) đang bị lỗi matchField nhưng giờ đã khớp
|
|
311
|
+
if (!isMismatch && target.hasError("matchField")) {
|
|
312
|
+
const errors = { ...target.errors };
|
|
313
|
+
delete errors["matchField"];
|
|
314
|
+
// Nếu không còn lỗi nào khác, set null
|
|
315
|
+
target.setErrors(Object.keys(errors).length ? errors : null);
|
|
316
|
+
}
|
|
317
|
+
return isMismatch ? { matchField: true } : null;
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
// Async: Check if username is taken (simulate API).
|
|
321
|
+
const asyncUsernameValidator = (takenUsernames) => {
|
|
322
|
+
return (control) => {
|
|
323
|
+
const value = control.value ?? "";
|
|
324
|
+
const isTaken = takenUsernames.includes(value);
|
|
325
|
+
return of(isTaken ? { usernameTaken: true } : null).pipe(delay(500));
|
|
326
|
+
};
|
|
327
|
+
};
|
|
328
|
+
// Không có ký tự đặc biệt.
|
|
329
|
+
const noSpecialCharValidator = () => {
|
|
330
|
+
return (control) => {
|
|
331
|
+
const valid = /^[a-zA-Z0-9]*$/.test(control.value);
|
|
332
|
+
return valid ? null : { noSpecialChar: true };
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
// Username không được nằm trong blacklist.
|
|
336
|
+
const usernameBlacklistValidator = (blacklist) => {
|
|
337
|
+
return (control) => {
|
|
338
|
+
return blacklist.includes(control.value) ? { blacklisted: true } : null;
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
// Có thuộc mimetypes từ mảng mimetypes được chỉ định hay không?
|
|
342
|
+
const acceptedMimeTypesValidator = (acceptedMimeTypes) => {
|
|
343
|
+
return (control) => {
|
|
344
|
+
const value = control?.value;
|
|
345
|
+
if (value) {
|
|
346
|
+
let fileType = "";
|
|
347
|
+
if (value instanceof File) {
|
|
348
|
+
fileType = value.type;
|
|
349
|
+
}
|
|
350
|
+
if (isValidJSON(value)) {
|
|
351
|
+
const { contentType } = JSON.parse(value)[0] || {};
|
|
352
|
+
if (contentType)
|
|
353
|
+
fileType = contentType;
|
|
354
|
+
}
|
|
355
|
+
if (fileType) {
|
|
356
|
+
if (!acceptedMimeTypes.includes(fileType)) {
|
|
357
|
+
return {
|
|
358
|
+
acceptedMimeTypes: {
|
|
359
|
+
actualValue: fileType,
|
|
360
|
+
requiredValue: `Nằm trong ${acceptedMimeTypes}`,
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return null;
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
const maxFileSizeInMBValidator = (maxSizeInMB) => {
|
|
370
|
+
return (control) => {
|
|
371
|
+
const file = control?.value;
|
|
372
|
+
if (file) {
|
|
373
|
+
const fileSizeInMB = formatFileSize(file.size, FileSizeTypeEnum.MB);
|
|
374
|
+
if (fileSizeInMB > maxSizeInMB) {
|
|
375
|
+
return {
|
|
376
|
+
maxSizeInMB: {
|
|
377
|
+
actualValue: fileSizeInMB,
|
|
378
|
+
requiredValue: `Nhỏ hơn ${maxSizeInMB}`,
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
|
|
174
387
|
// Enum cho các validator chung được angular reactive form hỗ trợ.
|
|
175
388
|
var CommonValidaterEnum;
|
|
176
389
|
(function (CommonValidaterEnum) {
|
|
@@ -190,46 +403,13 @@ var CustomValidaterEnum;
|
|
|
190
403
|
CustomValidaterEnum["LeadingSpace"] = "leadingSpace";
|
|
191
404
|
})(CustomValidaterEnum || (CustomValidaterEnum = {}));
|
|
192
405
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
ControlTypeEnum["Radio"] = "radio";
|
|
201
|
-
ControlTypeEnum["Date"] = "date";
|
|
202
|
-
ControlTypeEnum["Upload"] = "upload";
|
|
203
|
-
ControlTypeEnum["Switch"] = "switch";
|
|
204
|
-
ControlTypeEnum["Autocomplete"] = "autocomplete";
|
|
205
|
-
})(ControlTypeEnum || (ControlTypeEnum = {}));
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Interface base chung cho Các dạng Date.
|
|
209
|
-
*/
|
|
210
|
-
class BaseDateField extends BaseField {
|
|
211
|
-
/**
|
|
212
|
-
* Props validation chung cho các dạng Date.
|
|
213
|
-
*/
|
|
214
|
-
constructor(init) {
|
|
215
|
-
super(init);
|
|
216
|
-
/**
|
|
217
|
-
* Gán giá trị cho các props cần giá trị mặc định ở Date.
|
|
218
|
-
*/
|
|
219
|
-
this.controlType = ControlTypeEnum.Date;
|
|
220
|
-
this.allowPastDates = true;
|
|
221
|
-
this.allowFutureDates = true;
|
|
222
|
-
this.format = 'dd-MM-yyyy';
|
|
223
|
-
// this.value = undefined;
|
|
224
|
-
Object.assign(this, init);
|
|
225
|
-
}
|
|
226
|
-
// Tạo Form Control cho Input Field.
|
|
227
|
-
toFormControl() {
|
|
228
|
-
const commonValidators = this.getCommonValidators();
|
|
229
|
-
const customValidators = this.getCustomValidators();
|
|
230
|
-
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
406
|
+
var FormModeEnum;
|
|
407
|
+
(function (FormModeEnum) {
|
|
408
|
+
FormModeEnum["Add"] = "add";
|
|
409
|
+
FormModeEnum["Edit"] = "edit";
|
|
410
|
+
FormModeEnum["View"] = "view";
|
|
411
|
+
FormModeEnum["Design"] = "design";
|
|
412
|
+
})(FormModeEnum || (FormModeEnum = {}));
|
|
233
413
|
|
|
234
414
|
var SelectOutputValueTypeEnum;
|
|
235
415
|
(function (SelectOutputValueTypeEnum) {
|
|
@@ -255,14 +435,6 @@ var UploadOutputValueTypeEnum;
|
|
|
255
435
|
UploadOutputValueTypeEnum["Object"] = "object";
|
|
256
436
|
})(UploadOutputValueTypeEnum || (UploadOutputValueTypeEnum = {}));
|
|
257
437
|
|
|
258
|
-
var FileSizeTypeEnum;
|
|
259
|
-
(function (FileSizeTypeEnum) {
|
|
260
|
-
FileSizeTypeEnum["MB"] = "MB";
|
|
261
|
-
FileSizeTypeEnum["KB"] = "KB";
|
|
262
|
-
FileSizeTypeEnum["GB"] = "GB";
|
|
263
|
-
FileSizeTypeEnum["TB"] = "TB";
|
|
264
|
-
})(FileSizeTypeEnum || (FileSizeTypeEnum = {}));
|
|
265
|
-
|
|
266
438
|
var CombineDependTypeEnum;
|
|
267
439
|
(function (CombineDependTypeEnum) {
|
|
268
440
|
CombineDependTypeEnum["Valid"] = "valid";
|
|
@@ -281,283 +453,77 @@ var ViewModeEnum;
|
|
|
281
453
|
ViewModeEnum["text"] = "text";
|
|
282
454
|
})(ViewModeEnum || (ViewModeEnum = {}));
|
|
283
455
|
|
|
284
|
-
// Dạng field cụ thể.
|
|
285
|
-
var FieldTypeEnum;
|
|
286
|
-
(function (FieldTypeEnum) {
|
|
287
|
-
FieldTypeEnum["InputText"] = "input-text";
|
|
288
|
-
FieldTypeEnum["InputPassword"] = "input-password";
|
|
289
|
-
FieldTypeEnum["Textarea"] = "textarea";
|
|
290
|
-
FieldTypeEnum["DatePicker"] = "date-picker";
|
|
291
|
-
FieldTypeEnum["DateRange"] = "date-range";
|
|
292
|
-
FieldTypeEnum["SingleSelect"] = "single-select";
|
|
293
|
-
FieldTypeEnum["TreeSelect"] = "tree-select";
|
|
294
|
-
FieldTypeEnum["MultiSelect"] = "multi-select";
|
|
295
|
-
FieldTypeEnum["SingleFileUpload"] = "single-file-upload";
|
|
296
|
-
FieldTypeEnum["MultiFileUpload"] = "multi-file-upload";
|
|
297
|
-
FieldTypeEnum["MultiImageUpload"] = "multi-image-upload";
|
|
298
|
-
FieldTypeEnum["AvatarUpload"] = "avatar-upload";
|
|
299
|
-
FieldTypeEnum["Checkbox"] = "checkbox";
|
|
300
|
-
FieldTypeEnum["Radio"] = "radio";
|
|
301
|
-
FieldTypeEnum["ReminderTypeRadio"] = "reminder-type-radio";
|
|
302
|
-
FieldTypeEnum["Switch"] = "switch";
|
|
303
|
-
FieldTypeEnum["Autocomplete"] = "autocomplete";
|
|
304
|
-
})(FieldTypeEnum || (FieldTypeEnum = {}));
|
|
305
|
-
|
|
306
|
-
var InputFieldTypeEnum;
|
|
307
|
-
(function (InputFieldTypeEnum) {
|
|
308
|
-
InputFieldTypeEnum["Text"] = "text";
|
|
309
|
-
InputFieldTypeEnum["Email"] = "email";
|
|
310
|
-
InputFieldTypeEnum["Password"] = "password";
|
|
311
|
-
InputFieldTypeEnum["Number"] = "number";
|
|
312
|
-
})(InputFieldTypeEnum || (InputFieldTypeEnum = {}));
|
|
313
|
-
|
|
314
|
-
var DateFieldTypeEnum;
|
|
315
|
-
(function (DateFieldTypeEnum) {
|
|
316
|
-
DateFieldTypeEnum["Picker"] = "picker";
|
|
317
|
-
DateFieldTypeEnum["Range"] = "range";
|
|
318
|
-
})(DateFieldTypeEnum || (DateFieldTypeEnum = {}));
|
|
319
|
-
|
|
320
|
-
var SelectFieldTypeEnum;
|
|
321
|
-
(function (SelectFieldTypeEnum) {
|
|
322
|
-
SelectFieldTypeEnum["Single"] = "single";
|
|
323
|
-
SelectFieldTypeEnum["Multi"] = "multi";
|
|
324
|
-
})(SelectFieldTypeEnum || (SelectFieldTypeEnum = {}));
|
|
325
|
-
|
|
326
|
-
var UploadFieldTypeEnum;
|
|
327
|
-
(function (UploadFieldTypeEnum) {
|
|
328
|
-
UploadFieldTypeEnum["SingleFile"] = "single-file";
|
|
329
|
-
UploadFieldTypeEnum["MultiFile"] = "multi-file";
|
|
330
|
-
UploadFieldTypeEnum["Avatar"] = "avatar";
|
|
331
|
-
})(UploadFieldTypeEnum || (UploadFieldTypeEnum = {}));
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* FIELD_TYPES là readonly ['input', 'select', 'checkbox',...]
|
|
335
|
-
*/
|
|
336
|
-
const FIELD_TYPES = [
|
|
337
|
-
'input-text',
|
|
338
|
-
'input-password',
|
|
339
|
-
'textarea',
|
|
340
|
-
'date-picker',
|
|
341
|
-
'date-range',
|
|
342
|
-
'single-select',
|
|
343
|
-
'tree-select',
|
|
344
|
-
'multi-select',
|
|
345
|
-
'single-file-upload',
|
|
346
|
-
'multi-file-upload',
|
|
347
|
-
'multi-image-upload',
|
|
348
|
-
'avatar-upload',
|
|
349
|
-
'checkbox',
|
|
350
|
-
'radio',
|
|
351
|
-
'reminder-type-radio',
|
|
352
|
-
'switch',
|
|
353
|
-
'autocomplete',
|
|
354
|
-
];
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* FIELD_TYPES là readonly ['input', 'select', 'checkbox',...]
|
|
358
|
-
*/
|
|
359
|
-
const INPUT_FIELD_TYPES = ['text', 'email', 'password', 'number'];
|
|
360
|
-
|
|
361
456
|
/**
|
|
362
|
-
*
|
|
457
|
+
* Abstract class chung cho Field.
|
|
363
458
|
*/
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const dd = String(date.getDate()).padStart(2, '0');
|
|
388
|
-
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
389
|
-
const yyyy = date.getFullYear();
|
|
390
|
-
return `${yyyy}${separator}${mm}${separator}${dd}`;
|
|
391
|
-
};
|
|
392
|
-
// Chuyển đổi string ở các dạng: YYYY-MM-DD, DD-MM-YYYY, MM-DD-YYYY đến Date, nếu sẵn là Date thì mặc định không đổi.
|
|
393
|
-
const normalizeToDate = (input) => {
|
|
394
|
-
if (!input)
|
|
395
|
-
return null;
|
|
396
|
-
if (input instanceof Date && !isNaN(input.getTime()))
|
|
397
|
-
return input;
|
|
398
|
-
if (typeof input !== 'string')
|
|
399
|
-
return null;
|
|
400
|
-
const inputTrimed = input.trim();
|
|
401
|
-
let match;
|
|
402
|
-
let year, month, day;
|
|
403
|
-
let hour = 0, minute = 0, second = 0;
|
|
404
|
-
if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.YYYY_MM_DD))) {
|
|
405
|
-
year = +match[1];
|
|
406
|
-
month = +match[3] - 1;
|
|
407
|
-
day = +match[4];
|
|
408
|
-
}
|
|
409
|
-
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.DD_MM_YYYY))) {
|
|
410
|
-
day = +match[1];
|
|
411
|
-
month = +match[3] - 1;
|
|
412
|
-
year = +match[4];
|
|
413
|
-
}
|
|
414
|
-
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY))) {
|
|
415
|
-
month = +match[1] - 1;
|
|
416
|
-
day = +match[3];
|
|
417
|
-
year = +match[4];
|
|
418
|
-
}
|
|
419
|
-
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY_HMS))) {
|
|
420
|
-
month = +match[1] - 1;
|
|
421
|
-
day = +match[3];
|
|
422
|
-
year = +match[4];
|
|
423
|
-
hour = +match[5];
|
|
424
|
-
minute = +match[6];
|
|
425
|
-
second = +match[7];
|
|
459
|
+
class BaseField {
|
|
460
|
+
constructor(init) {
|
|
461
|
+
// Thứ tự hiển thị của field
|
|
462
|
+
this.seqNum = 0;
|
|
463
|
+
if (!init?.key || !init?.label) {
|
|
464
|
+
throw new Error('key, label, and type are required');
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Gán giá trị cho các props cần giá trị mặc định ở BaseField.
|
|
468
|
+
*/
|
|
469
|
+
this.id = uuidGenerator();
|
|
470
|
+
this.required = false;
|
|
471
|
+
this.grid = { xs: 24 };
|
|
472
|
+
this.labelGrid = { xs: 24 };
|
|
473
|
+
this.controlGrid = { xs: 24 };
|
|
474
|
+
this.labelAlign = 'left';
|
|
475
|
+
this.isHidden = false;
|
|
476
|
+
this.isDisabled = false;
|
|
477
|
+
this.isParent = false;
|
|
478
|
+
this.allowSetValue = [FormModeEnum.Add, FormModeEnum.Edit, FormModeEnum.View];
|
|
479
|
+
this.viewMode = ViewModeEnum.form;
|
|
480
|
+
this.enableFieldChange = false;
|
|
481
|
+
Object.assign(this, init);
|
|
426
482
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
483
|
+
// Tạo các validate chung cho các props ở abstract. this khi gọi trong class con sẽ đại diện cho đối tượng tạo từ class đó.
|
|
484
|
+
getCommonValidators() {
|
|
485
|
+
const validators = [];
|
|
486
|
+
if (this.required)
|
|
487
|
+
validators.push(Validators.required);
|
|
488
|
+
return validators;
|
|
430
489
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
date.getSeconds() === second) {
|
|
438
|
-
return date;
|
|
490
|
+
// Tạo các validate custom chung cho các props ở abstract. this khi gọi trong class con sẽ đại diện cho đối tượng tạo từ class đó.
|
|
491
|
+
getCustomValidators() {
|
|
492
|
+
const validators = [];
|
|
493
|
+
if (this.matchField)
|
|
494
|
+
validators.push(matchFieldsValidator(this.matchField));
|
|
495
|
+
return validators;
|
|
439
496
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
const normalizeToDate1 = (input) => {
|
|
444
|
-
if (input) {
|
|
445
|
-
if (input instanceof Date && !isNaN(input.getTime()))
|
|
446
|
-
return input;
|
|
447
|
-
if (typeof input !== 'string')
|
|
448
|
-
return null;
|
|
449
|
-
const inputTrimed = String(input).trim();
|
|
450
|
-
let match;
|
|
451
|
-
let year, month, day;
|
|
452
|
-
if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.YYYY_MM_DD))) {
|
|
453
|
-
year = +match[1];
|
|
454
|
-
month = +match[3] - 1;
|
|
455
|
-
day = +match[4];
|
|
456
|
-
}
|
|
457
|
-
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.DD_MM_YYYY))) {
|
|
458
|
-
day = +match[1];
|
|
459
|
-
month = +match[3] - 1;
|
|
460
|
-
year = +match[4];
|
|
461
|
-
}
|
|
462
|
-
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY))) {
|
|
463
|
-
month = +match[1] - 1;
|
|
464
|
-
day = +match[3];
|
|
465
|
-
year = +match[4];
|
|
466
|
-
}
|
|
467
|
-
else {
|
|
468
|
-
console.warn('Sai định dạng chuỗi date');
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
471
|
-
const date = new Date(year, month, day); // Validate: kiểm tra lại giá trị date (28/29/30/31 hợp lệ)
|
|
472
|
-
if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {
|
|
473
|
-
return date;
|
|
497
|
+
getErrorMessage(errorKey, errorValue, allFields) {
|
|
498
|
+
if (errorKey === CommonValidaterEnum.Required) {
|
|
499
|
+
return `${this.label || 'Trường này'} là bắt buộc`;
|
|
474
500
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
501
|
+
if (errorKey === CustomValidaterEnum.MatchField) {
|
|
502
|
+
const matchKey = this.matchField;
|
|
503
|
+
const targetLabel = allFields.find(f => f.key === matchKey)?.label || matchKey;
|
|
504
|
+
return `${this.label} không khớp với ${targetLabel}`;
|
|
478
505
|
}
|
|
506
|
+
return '';
|
|
479
507
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
return years;
|
|
490
|
-
};
|
|
491
|
-
// Tạo danh sách năm chạy từ năm chỉ định đến năm hiện tại.
|
|
492
|
-
const generateYearsAsc = (from) => {
|
|
493
|
-
const currentYear = new Date().getFullYear();
|
|
494
|
-
const years = [];
|
|
495
|
-
for (let year = from; year <= currentYear; year++) {
|
|
496
|
-
years.push(year);
|
|
497
|
-
}
|
|
498
|
-
return years;
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
const DEFAULT_DATE_PICKER_VALUE = '';
|
|
502
|
-
/**
|
|
503
|
-
* Interface của Date type là Picker.
|
|
504
|
-
*/
|
|
505
|
-
class DatePickerField extends BaseDateField {
|
|
506
|
-
constructor(init) {
|
|
507
|
-
super(init);
|
|
508
|
-
/**
|
|
509
|
-
* Gán giá trị cho các props cần giá trị mặc định ở Date picker.
|
|
510
|
-
*/
|
|
511
|
-
this.type = FieldTypeEnum.DatePicker;
|
|
512
|
-
this.value = DEFAULT_DATE_PICKER_VALUE;
|
|
513
|
-
this.initialValue = DEFAULT_DATE_PICKER_VALUE;
|
|
514
|
-
this.outputAs = DatePickerOutputValueTypeEnum.String;
|
|
515
|
-
Object.assign(this, init);
|
|
516
|
-
}
|
|
517
|
-
getDefaultValue() {
|
|
518
|
-
return DEFAULT_DATE_PICKER_VALUE;
|
|
519
|
-
}
|
|
520
|
-
toFormValue(modelValue) {
|
|
521
|
-
const value = modelValue;
|
|
522
|
-
if (!value)
|
|
523
|
-
return null;
|
|
524
|
-
return normalizeToDate(value);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Dữ liệu sau khi đã tiến hành xử lý gom nhóm từ fields và groups.
|
|
511
|
+
class GroupedFields {
|
|
512
|
+
constructor(init) {
|
|
513
|
+
this.noGroup = [];
|
|
514
|
+
this.grouped = [];
|
|
515
|
+
Object.assign(this, init);
|
|
525
516
|
}
|
|
526
517
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Interface của Date type là Range.
|
|
531
|
-
*/
|
|
532
|
-
class DateRangeField extends BaseDateField {
|
|
518
|
+
// Đại diện cho 1 group.
|
|
519
|
+
class FieldGroup {
|
|
533
520
|
constructor(init) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
this.type = FieldTypeEnum.DateRange;
|
|
539
|
-
this.value = DEFAULT_DATE_RANGE_VALUE;
|
|
540
|
-
this.initialValue = DEFAULT_DATE_RANGE_VALUE;
|
|
541
|
-
this.outputAs = DateRangeOutputValueTypeEnum.StringArray;
|
|
521
|
+
this.isActive = false;
|
|
522
|
+
this.showArrow = true;
|
|
523
|
+
this.disabled = false;
|
|
524
|
+
this.required = false;
|
|
542
525
|
Object.assign(this, init);
|
|
543
526
|
}
|
|
544
|
-
getDefaultValue() {
|
|
545
|
-
return DEFAULT_DATE_RANGE_VALUE;
|
|
546
|
-
}
|
|
547
|
-
toFormValue(modelValue) {
|
|
548
|
-
let value = modelValue;
|
|
549
|
-
if (typeof value === 'string') {
|
|
550
|
-
value = value.split(',').map(dateStr => dateStr.trim());
|
|
551
|
-
}
|
|
552
|
-
let dateRange = [];
|
|
553
|
-
if (value?.length === 2) {
|
|
554
|
-
const startDate = normalizeToDate(value[0]);
|
|
555
|
-
const endDate = normalizeToDate(value[1]);
|
|
556
|
-
if (startDate && endDate)
|
|
557
|
-
dateRange = [startDate, endDate];
|
|
558
|
-
}
|
|
559
|
-
return dateRange;
|
|
560
|
-
}
|
|
561
527
|
}
|
|
562
528
|
|
|
563
529
|
// Các patterns regex thường dùng trong ứng dụng
|
|
@@ -680,6 +646,51 @@ const resolvePattern = (pattern) => {
|
|
|
680
646
|
}
|
|
681
647
|
};
|
|
682
648
|
|
|
649
|
+
const getDefaultPatternErrorMessage = (label, pattern) => {
|
|
650
|
+
let finalErrorMessage = '';
|
|
651
|
+
const resolvePatternResult = resolvePattern(pattern);
|
|
652
|
+
// if (resolvePatternResult?.type === 'unknown') {
|
|
653
|
+
// return finalErrorMessage; // Không phải pattern hợp lệ
|
|
654
|
+
// }
|
|
655
|
+
if (resolvePatternResult.type === 'regexObject') {
|
|
656
|
+
finalErrorMessage = `${label || 'Trường này'} không hợp lệ!`;
|
|
657
|
+
}
|
|
658
|
+
if (resolvePatternResult.type === 'regexKey') {
|
|
659
|
+
const patternMess = PATTERN_ERROR_MESSAGES?.[pattern] ?? '';
|
|
660
|
+
finalErrorMessage = `${label || 'Trường này'} không hợp lệ! ${patternMess.replace(/%s/g, () => label)}`;
|
|
661
|
+
}
|
|
662
|
+
return finalErrorMessage;
|
|
663
|
+
};
|
|
664
|
+
const getDefaultMinLengthErrorMessage = (label, errorValue) => {
|
|
665
|
+
return `${label || 'Trường này'} cần tối thiểu ${errorValue.requiredLength} ký tự`;
|
|
666
|
+
};
|
|
667
|
+
const getDefaultMaxLengthErrorMessage = (label, errorValue) => {
|
|
668
|
+
return `${label || 'Trường này'} cần đât thiểu ${errorValue.requiredLength} ký tự`;
|
|
669
|
+
};
|
|
670
|
+
const getDefaultAcptMimeTypesErrorMessage = (typeString) => {
|
|
671
|
+
return `Tập tin tải lên không được hỗ trợ. Vui lòng tải các tập tin có định dạng ${typeString}`;
|
|
672
|
+
};
|
|
673
|
+
const getDefaultMaxSizeInMBErrorMessage = (maxSizeInMB) => {
|
|
674
|
+
return `Kích thước tập tin tải lên không được phép vượt quá ${maxSizeInMB} MB`;
|
|
675
|
+
};
|
|
676
|
+
const getLeadingSpaceErrorMessage = (label) => {
|
|
677
|
+
return `${label || 'Trường này'} không được bắt đầu bằng khoảng trắng`;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// Dạng control.
|
|
681
|
+
var ControlTypeEnum;
|
|
682
|
+
(function (ControlTypeEnum) {
|
|
683
|
+
ControlTypeEnum["Input"] = "input";
|
|
684
|
+
ControlTypeEnum["Textarea"] = "textarea";
|
|
685
|
+
ControlTypeEnum["Select"] = "select";
|
|
686
|
+
ControlTypeEnum["Checkbox"] = "checkbox";
|
|
687
|
+
ControlTypeEnum["Radio"] = "radio";
|
|
688
|
+
ControlTypeEnum["Date"] = "date";
|
|
689
|
+
ControlTypeEnum["Upload"] = "upload";
|
|
690
|
+
ControlTypeEnum["Switch"] = "switch";
|
|
691
|
+
ControlTypeEnum["Autocomplete"] = "autocomplete";
|
|
692
|
+
})(ControlTypeEnum || (ControlTypeEnum = {}));
|
|
693
|
+
|
|
683
694
|
const DEFAULT_INPUT_VALUE = '';
|
|
684
695
|
/**
|
|
685
696
|
* Interface base chung cho Các dạng Input Field.
|
|
@@ -745,6 +756,55 @@ class InputBaseField extends BaseField {
|
|
|
745
756
|
}
|
|
746
757
|
}
|
|
747
758
|
|
|
759
|
+
// Dạng field cụ thể.
|
|
760
|
+
var FieldTypeEnum;
|
|
761
|
+
(function (FieldTypeEnum) {
|
|
762
|
+
FieldTypeEnum["InputText"] = "input-text";
|
|
763
|
+
FieldTypeEnum["InputPassword"] = "input-password";
|
|
764
|
+
FieldTypeEnum["Textarea"] = "textarea";
|
|
765
|
+
FieldTypeEnum["DatePicker"] = "date-picker";
|
|
766
|
+
FieldTypeEnum["DateRange"] = "date-range";
|
|
767
|
+
FieldTypeEnum["SingleSelect"] = "single-select";
|
|
768
|
+
FieldTypeEnum["TreeSelect"] = "tree-select";
|
|
769
|
+
FieldTypeEnum["MultiSelect"] = "multi-select";
|
|
770
|
+
FieldTypeEnum["SingleFileUpload"] = "single-file-upload";
|
|
771
|
+
FieldTypeEnum["MultiFileUpload"] = "multi-file-upload";
|
|
772
|
+
FieldTypeEnum["MultiImageUpload"] = "multi-image-upload";
|
|
773
|
+
FieldTypeEnum["AvatarUpload"] = "avatar-upload";
|
|
774
|
+
FieldTypeEnum["Checkbox"] = "checkbox";
|
|
775
|
+
FieldTypeEnum["Radio"] = "radio";
|
|
776
|
+
FieldTypeEnum["ReminderTypeRadio"] = "reminder-type-radio";
|
|
777
|
+
FieldTypeEnum["Switch"] = "switch";
|
|
778
|
+
FieldTypeEnum["Autocomplete"] = "autocomplete";
|
|
779
|
+
})(FieldTypeEnum || (FieldTypeEnum = {}));
|
|
780
|
+
|
|
781
|
+
var InputFieldTypeEnum;
|
|
782
|
+
(function (InputFieldTypeEnum) {
|
|
783
|
+
InputFieldTypeEnum["Text"] = "text";
|
|
784
|
+
InputFieldTypeEnum["Email"] = "email";
|
|
785
|
+
InputFieldTypeEnum["Password"] = "password";
|
|
786
|
+
InputFieldTypeEnum["Number"] = "number";
|
|
787
|
+
})(InputFieldTypeEnum || (InputFieldTypeEnum = {}));
|
|
788
|
+
|
|
789
|
+
var DateFieldTypeEnum;
|
|
790
|
+
(function (DateFieldTypeEnum) {
|
|
791
|
+
DateFieldTypeEnum["Picker"] = "picker";
|
|
792
|
+
DateFieldTypeEnum["Range"] = "range";
|
|
793
|
+
})(DateFieldTypeEnum || (DateFieldTypeEnum = {}));
|
|
794
|
+
|
|
795
|
+
var SelectFieldTypeEnum;
|
|
796
|
+
(function (SelectFieldTypeEnum) {
|
|
797
|
+
SelectFieldTypeEnum["Single"] = "single";
|
|
798
|
+
SelectFieldTypeEnum["Multi"] = "multi";
|
|
799
|
+
})(SelectFieldTypeEnum || (SelectFieldTypeEnum = {}));
|
|
800
|
+
|
|
801
|
+
var UploadFieldTypeEnum;
|
|
802
|
+
(function (UploadFieldTypeEnum) {
|
|
803
|
+
UploadFieldTypeEnum["SingleFile"] = "single-file";
|
|
804
|
+
UploadFieldTypeEnum["MultiFile"] = "multi-file";
|
|
805
|
+
UploadFieldTypeEnum["Avatar"] = "avatar";
|
|
806
|
+
})(UploadFieldTypeEnum || (UploadFieldTypeEnum = {}));
|
|
807
|
+
|
|
748
808
|
/**
|
|
749
809
|
* Interface của Input Field type là Text.
|
|
750
810
|
*/
|
|
@@ -795,122 +855,105 @@ class InputPasswordField extends InputBaseField {
|
|
|
795
855
|
}
|
|
796
856
|
}
|
|
797
857
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
858
|
+
const buildForm = (fields, mode, model) => {
|
|
859
|
+
const groupObj = {};
|
|
860
|
+
// console.log("model", model);
|
|
861
|
+
for (const field of fields) {
|
|
862
|
+
// Loại bỏ nếu quyết định ẩn field đi.
|
|
863
|
+
if (field.isHidden)
|
|
864
|
+
continue;
|
|
865
|
+
// Nếu là dạng InputPasswordField nhưng ở mode edit và view thì không được dùng.
|
|
866
|
+
if (field instanceof InputPasswordField &&
|
|
867
|
+
(mode === FormModeEnum.Edit || mode === FormModeEnum.View))
|
|
868
|
+
continue;
|
|
869
|
+
const fieldKey = field?.key;
|
|
870
|
+
// field.value = field.allowSetValue ? (model?.[fieldKey] || field.value) : null;
|
|
871
|
+
if (field.allowSetValue) {
|
|
872
|
+
field.value = field.getDefaultValue();
|
|
873
|
+
field.modelValue = null;
|
|
874
|
+
if (model?.[fieldKey]) {
|
|
875
|
+
field.value = field.toFormValue(model[fieldKey]);
|
|
876
|
+
field.modelValue = model[fieldKey];
|
|
877
|
+
}
|
|
878
|
+
field.initialValue = field.value;
|
|
879
|
+
}
|
|
880
|
+
// console.log("e", field.value );
|
|
881
|
+
// field.value = field.allowSetValue ? field.toFormValue(model?.[fieldKey] | field.getDefaultValue()) : field.getDefaultValue();
|
|
882
|
+
const control = field.toFormControl();
|
|
883
|
+
control.reset(field.value);
|
|
884
|
+
if (field.isDisabled) {
|
|
885
|
+
//console.log(field.key, 'dfdfdfdf');
|
|
886
|
+
control?.disable();
|
|
887
|
+
}
|
|
888
|
+
groupObj[fieldKey] = control;
|
|
829
889
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
890
|
+
return new FormGroup(groupObj);
|
|
891
|
+
};
|
|
892
|
+
// Tạo placeholder mặc định cho các dạng Input, Textarea.
|
|
893
|
+
const createPlaceholder = (field) => {
|
|
894
|
+
const placeholder = field?.placeholder;
|
|
895
|
+
if (placeholder)
|
|
896
|
+
return placeholder;
|
|
897
|
+
const label = field?.label;
|
|
898
|
+
if (label)
|
|
899
|
+
return `Nhập ${label?.toLowerCase()}`;
|
|
900
|
+
return `Nhập ${field.key?.toLowerCase()}`;
|
|
901
|
+
};
|
|
902
|
+
// Tạo placeholder mặc định cho các dạng Select.
|
|
903
|
+
const createSelectPlaceholder = (field) => {
|
|
904
|
+
const placeholder = field?.placeholder;
|
|
905
|
+
if (placeholder)
|
|
906
|
+
return placeholder;
|
|
907
|
+
const label = field?.label;
|
|
908
|
+
if (label) {
|
|
909
|
+
const isSingleSelect = field?.type === FieldTypeEnum.SingleSelect;
|
|
910
|
+
return `Chọn ${isSingleSelect ? '' : 'các'} ${label?.toLowerCase()}`;
|
|
834
911
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
const commonValidators = this.getCommonSelectValidators();
|
|
838
|
-
const customValidators = this.getCustomSelectValidators();
|
|
839
|
-
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
840
|
-
}
|
|
841
|
-
getErrorMessage(errorKey, errorValue, allFields) {
|
|
842
|
-
return super.getErrorMessage(errorKey, errorValue, allFields);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
const DEFAULT_MULTI_SELECT_VALUE = [];
|
|
847
|
-
/**
|
|
848
|
-
* Interface của Select Field type là dạng Multi.
|
|
849
|
-
*/
|
|
850
|
-
class MultiSelectField extends SelectBaseField {
|
|
851
|
-
constructor(init) {
|
|
852
|
-
super(init);
|
|
853
|
-
/**
|
|
854
|
-
* Gán giá trị cho các props cần giá trị mặc định ở Multi Select.
|
|
855
|
-
*/
|
|
856
|
-
this.type = FieldTypeEnum.MultiSelect;
|
|
857
|
-
this.value = DEFAULT_MULTI_SELECT_VALUE;
|
|
858
|
-
this.initialValue = DEFAULT_MULTI_SELECT_VALUE;
|
|
859
|
-
this.maxTagCount = 53;
|
|
860
|
-
this.maxMultipleCount = Infinity;
|
|
861
|
-
Object.assign(this, init);
|
|
862
|
-
}
|
|
863
|
-
getDefaultValue() {
|
|
864
|
-
return DEFAULT_MULTI_SELECT_VALUE;
|
|
865
|
-
}
|
|
866
|
-
toFormValue(modelValue) {
|
|
867
|
-
const value = modelValue;
|
|
868
|
-
const optionsValue = value.map((val) => {
|
|
869
|
-
if (typeof val === 'object') {
|
|
870
|
-
let transformVal = val?.[this.bindValue] ?? '';
|
|
871
|
-
return transformVal;
|
|
872
|
-
}
|
|
873
|
-
return val;
|
|
874
|
-
});
|
|
875
|
-
return optionsValue;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
912
|
+
return `Nhập ${field.key?.toLowerCase()}`;
|
|
913
|
+
};
|
|
878
914
|
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
915
|
+
const isInputTextField = (field) => {
|
|
916
|
+
return field.controlType === ControlTypeEnum.Input && field.type === FieldTypeEnum.InputText;
|
|
917
|
+
};
|
|
918
|
+
const isInputPasswordField = (field) => {
|
|
919
|
+
return field.controlType === ControlTypeEnum.Input && field.type === FieldTypeEnum.InputPassword;
|
|
920
|
+
};
|
|
921
|
+
const isSingleSelectField = (field) => {
|
|
922
|
+
return field.controlType === ControlTypeEnum.Select && field.type === FieldTypeEnum.SingleSelect;
|
|
923
|
+
};
|
|
924
|
+
const isMultiSelectField = (field) => {
|
|
925
|
+
return field.controlType === ControlTypeEnum.Select && field.type === FieldTypeEnum.MultiSelect;
|
|
926
|
+
};
|
|
927
|
+
const isDatePickerField = (field) => {
|
|
928
|
+
return field.controlType === ControlTypeEnum.Date && field.type === FieldTypeEnum.DatePicker;
|
|
929
|
+
};
|
|
930
|
+
const isDateRangeField = (field) => {
|
|
931
|
+
return field.controlType === ControlTypeEnum.Date && field.type === FieldTypeEnum.DateRange;
|
|
932
|
+
};
|
|
933
|
+
const isSingleFileUploadField = (field) => {
|
|
934
|
+
return (field.controlType === ControlTypeEnum.Upload && field.type === FieldTypeEnum.SingleFileUpload);
|
|
935
|
+
};
|
|
936
|
+
const isMultiFileUploadField = (field) => {
|
|
937
|
+
return (field.controlType === ControlTypeEnum.Upload && field.type === FieldTypeEnum.MultiFileUpload);
|
|
938
|
+
};
|
|
939
|
+
const isAvatarUploadField = (field) => {
|
|
940
|
+
return field.controlType === ControlTypeEnum.Upload && field.type === FieldTypeEnum.AvatarUpload;
|
|
941
|
+
};
|
|
942
|
+
// Tạo FieldGroup render UI từ thông tin fields và groups truyền vào cho dynamic-form.
|
|
943
|
+
const buildGroupedFields = (fields, groups = []) => {
|
|
944
|
+
const noGroup = fields.filter(field => !field?.groupCode || field?.groupCode.toLowerCase() === 'none');
|
|
945
|
+
const sortedGroups = [...groups].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
946
|
+
const grouped = sortedGroups
|
|
947
|
+
.map(group => ({
|
|
948
|
+
group,
|
|
949
|
+
fields: fields.filter(field => field.groupCode?.toLowerCase() === group.code.toLowerCase()),
|
|
950
|
+
}))
|
|
951
|
+
.filter(groupedFields => groupedFields.fields.length > 0);
|
|
952
|
+
return new GroupedFields({
|
|
953
|
+
noGroup,
|
|
954
|
+
grouped,
|
|
955
|
+
});
|
|
956
|
+
};
|
|
914
957
|
|
|
915
958
|
const DEFAULT_TEXTAREA_VALUE = '';
|
|
916
959
|
/**
|
|
@@ -977,2190 +1020,506 @@ class TextareaField extends BaseField {
|
|
|
977
1020
|
}
|
|
978
1021
|
|
|
979
1022
|
/**
|
|
980
|
-
*
|
|
1023
|
+
* FIELD_TYPES là readonly ['single', 'multi', 'tree',...]
|
|
981
1024
|
*/
|
|
982
|
-
|
|
1025
|
+
const SELECT_FIELD_TYPES = ['single', 'multi', 'tree'];
|
|
1026
|
+
const DEFAULT_BIND_LABEL = 'name';
|
|
1027
|
+
const DEFAULT_BIND_VALUE = 'code';
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Interface base chung cho Các dạng Input Field.
|
|
1031
|
+
*/
|
|
1032
|
+
class SelectBaseField extends BaseField {
|
|
983
1033
|
/**
|
|
984
|
-
* Props validation chung cho các dạng
|
|
1034
|
+
* Props validation chung cho các dạng Select.
|
|
985
1035
|
*/
|
|
1036
|
+
// ................. Chưa có .................
|
|
986
1037
|
constructor(init) {
|
|
987
1038
|
super(init);
|
|
988
1039
|
/**
|
|
989
|
-
* Gán giá trị cho các props cần giá trị mặc định ở
|
|
1040
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Select.
|
|
990
1041
|
*/
|
|
991
|
-
this.controlType = ControlTypeEnum.
|
|
992
|
-
this.
|
|
993
|
-
this.
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
this.
|
|
998
|
-
this.
|
|
999
|
-
this.
|
|
1000
|
-
this.
|
|
1042
|
+
this.controlType = ControlTypeEnum.Select;
|
|
1043
|
+
this.bindLabel = DEFAULT_BIND_LABEL;
|
|
1044
|
+
this.bindValue = DEFAULT_BIND_VALUE;
|
|
1045
|
+
this.matchSelectWidth = true;
|
|
1046
|
+
this.outputAs = SelectOutputValueTypeEnum.Object;
|
|
1047
|
+
this.dependsOn = [];
|
|
1048
|
+
this.dependType = DependTypeEnum.All;
|
|
1049
|
+
this.suffixes = [];
|
|
1050
|
+
this.optionsToPrepend = [];
|
|
1051
|
+
this.optionsToAppend = [];
|
|
1052
|
+
this.options = [];
|
|
1053
|
+
this.exceptOptions = [];
|
|
1001
1054
|
Object.assign(this, init);
|
|
1002
1055
|
}
|
|
1003
|
-
// Mảng ValidatorFn các validation chung được hỗ trợ sẵn bởi angular cho
|
|
1004
|
-
|
|
1056
|
+
// Mảng ValidatorFn các validation chung được hỗ trợ sẵn bởi Reactive form của angular cho Select.
|
|
1057
|
+
getCommonSelectValidators() {
|
|
1058
|
+
const validators = this.getCommonValidators();
|
|
1059
|
+
return validators;
|
|
1060
|
+
}
|
|
1061
|
+
// Mảng ValidatorFn các custom validation có thể dùng cho Select.
|
|
1062
|
+
getCustomSelectValidators() {
|
|
1005
1063
|
const validators = this.getCustomValidators();
|
|
1006
|
-
// Kiểm tra xem file tải lên có type nằm trong mảng acceptedMimeTypes đã chỉ định hay không.
|
|
1007
|
-
if (this.acceptedMimeTypes?.length > 0)
|
|
1008
|
-
validators.push(acceptedMimeTypesValidator(this.acceptedMimeTypes));
|
|
1009
|
-
// Kiểm tra kích thước tối đa mà file tải lên được cho phép.
|
|
1010
|
-
validators.push(maxFileSizeInMBValidator(this.maxSizeInMB));
|
|
1011
1064
|
return validators;
|
|
1012
1065
|
}
|
|
1013
1066
|
// Tạo Form Control cho Input Field.
|
|
1014
1067
|
toFormControl() {
|
|
1015
|
-
const commonValidators = this.
|
|
1016
|
-
const customValidators = this.
|
|
1068
|
+
const commonValidators = this.getCommonSelectValidators();
|
|
1069
|
+
const customValidators = this.getCustomSelectValidators();
|
|
1017
1070
|
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
1018
1071
|
}
|
|
1019
1072
|
getErrorMessage(errorKey, errorValue, allFields) {
|
|
1020
|
-
// Lấy error message mặc định của acceptedMimeTypes.
|
|
1021
|
-
if (errorKey === CustomValidaterEnum.AcceptedMimiTypes) {
|
|
1022
|
-
const typeString = getExtensions(this.acceptedMimeTypes).join(', ');
|
|
1023
|
-
return getDefaultAcptMimeTypesErrorMessage(typeString);
|
|
1024
|
-
}
|
|
1025
|
-
// Lấy error message mặc định của maxSizeInMB.
|
|
1026
|
-
if (errorKey === CustomValidaterEnum.MaxSizeInMB) {
|
|
1027
|
-
return getDefaultMaxSizeInMBErrorMessage(this.maxSizeInMB);
|
|
1028
|
-
}
|
|
1029
1073
|
return super.getErrorMessage(errorKey, errorValue, allFields);
|
|
1030
1074
|
}
|
|
1031
1075
|
}
|
|
1032
1076
|
|
|
1033
|
-
const
|
|
1077
|
+
const DEFAULT_SINGLE_SELECT_VALUE = null;
|
|
1034
1078
|
/**
|
|
1035
|
-
* Interface của
|
|
1079
|
+
* Interface của Select Field type là dạng Single.
|
|
1036
1080
|
*/
|
|
1037
|
-
class
|
|
1081
|
+
class SingleSelectField extends SelectBaseField {
|
|
1038
1082
|
constructor(init) {
|
|
1039
1083
|
super(init);
|
|
1040
1084
|
/**
|
|
1041
|
-
* Gán giá trị cho các props cần giá trị mặc định ở
|
|
1085
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Single Select.
|
|
1042
1086
|
*/
|
|
1043
|
-
this.type = FieldTypeEnum.
|
|
1044
|
-
this.value =
|
|
1045
|
-
this.
|
|
1046
|
-
this.initialValue = DEFAULT_AVATAR_UPLOAD_VALUE;
|
|
1087
|
+
this.type = FieldTypeEnum.SingleSelect;
|
|
1088
|
+
this.value = DEFAULT_SINGLE_SELECT_VALUE;
|
|
1089
|
+
this.initialValue = DEFAULT_SINGLE_SELECT_VALUE;
|
|
1047
1090
|
Object.assign(this, init);
|
|
1048
1091
|
}
|
|
1049
1092
|
getDefaultValue() {
|
|
1050
|
-
return
|
|
1093
|
+
return DEFAULT_SINGLE_SELECT_VALUE;
|
|
1051
1094
|
}
|
|
1052
1095
|
toFormValue(modelValue) {
|
|
1053
|
-
|
|
1096
|
+
if (!modelValue)
|
|
1097
|
+
return null;
|
|
1098
|
+
const value = modelValue;
|
|
1099
|
+
if (typeof value === 'object') {
|
|
1100
|
+
const valueFromObj = value?.[this.bindValue];
|
|
1101
|
+
const typeOfValueFromObj = typeof valueFromObj;
|
|
1102
|
+
if (typeOfValueFromObj !== 'object')
|
|
1103
|
+
return String(valueFromObj);
|
|
1104
|
+
else {
|
|
1105
|
+
console.warn('Từ bindValue nhưng đối tượng vẫn là dạng object');
|
|
1106
|
+
return '';
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return String(value);
|
|
1054
1110
|
}
|
|
1055
1111
|
}
|
|
1056
1112
|
|
|
1057
|
-
const
|
|
1113
|
+
const DEFAULT_MULTI_SELECT_VALUE = [];
|
|
1058
1114
|
/**
|
|
1059
|
-
* Interface của
|
|
1115
|
+
* Interface của Select Field type là dạng Multi.
|
|
1060
1116
|
*/
|
|
1061
|
-
class
|
|
1117
|
+
class MultiSelectField extends SelectBaseField {
|
|
1062
1118
|
constructor(init) {
|
|
1063
1119
|
super(init);
|
|
1064
1120
|
/**
|
|
1065
|
-
* Gán giá trị cho các props cần giá trị mặc định ở Multi
|
|
1121
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Multi Select.
|
|
1066
1122
|
*/
|
|
1067
|
-
this.type = FieldTypeEnum.
|
|
1068
|
-
this.value =
|
|
1069
|
-
this.initialValue =
|
|
1070
|
-
this.
|
|
1123
|
+
this.type = FieldTypeEnum.MultiSelect;
|
|
1124
|
+
this.value = DEFAULT_MULTI_SELECT_VALUE;
|
|
1125
|
+
this.initialValue = DEFAULT_MULTI_SELECT_VALUE;
|
|
1126
|
+
this.maxTagCount = 53;
|
|
1127
|
+
this.maxMultipleCount = Infinity;
|
|
1071
1128
|
Object.assign(this, init);
|
|
1072
1129
|
}
|
|
1073
1130
|
getDefaultValue() {
|
|
1074
|
-
return
|
|
1131
|
+
return DEFAULT_MULTI_SELECT_VALUE;
|
|
1075
1132
|
}
|
|
1076
1133
|
toFormValue(modelValue) {
|
|
1077
|
-
|
|
1134
|
+
const value = modelValue;
|
|
1135
|
+
const optionsValue = value.map((val) => {
|
|
1136
|
+
if (typeof val === 'object') {
|
|
1137
|
+
let transformVal = val?.[this.bindValue] ?? '';
|
|
1138
|
+
return transformVal;
|
|
1139
|
+
}
|
|
1140
|
+
return val;
|
|
1141
|
+
});
|
|
1142
|
+
return optionsValue;
|
|
1078
1143
|
}
|
|
1079
1144
|
}
|
|
1080
1145
|
|
|
1081
|
-
const DEFAULT_MULTI_IMAGE_UPLOAD_VALUE = '';
|
|
1082
1146
|
/**
|
|
1083
|
-
* Interface
|
|
1147
|
+
* Interface base chung cho Các dạng Date.
|
|
1084
1148
|
*/
|
|
1085
|
-
class
|
|
1149
|
+
class BaseDateField extends BaseField {
|
|
1150
|
+
/**
|
|
1151
|
+
* Props validation chung cho các dạng Date.
|
|
1152
|
+
*/
|
|
1086
1153
|
constructor(init) {
|
|
1087
1154
|
super(init);
|
|
1088
1155
|
/**
|
|
1089
|
-
* Gán giá trị cho các props cần giá trị mặc định ở
|
|
1156
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Date.
|
|
1090
1157
|
*/
|
|
1091
|
-
this.
|
|
1092
|
-
this.
|
|
1093
|
-
this.
|
|
1094
|
-
this.
|
|
1095
|
-
this.
|
|
1096
|
-
this.imageCardHeight = '108px';
|
|
1158
|
+
this.controlType = ControlTypeEnum.Date;
|
|
1159
|
+
this.allowPastDates = true;
|
|
1160
|
+
this.allowFutureDates = true;
|
|
1161
|
+
this.format = 'dd-MM-yyyy';
|
|
1162
|
+
// this.value = undefined;
|
|
1097
1163
|
Object.assign(this, init);
|
|
1098
1164
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
return
|
|
1165
|
+
// Tạo Form Control cho Input Field.
|
|
1166
|
+
toFormControl() {
|
|
1167
|
+
const commonValidators = this.getCommonValidators();
|
|
1168
|
+
const customValidators = this.getCustomValidators();
|
|
1169
|
+
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
1104
1170
|
}
|
|
1105
1171
|
}
|
|
1106
1172
|
|
|
1107
|
-
const DEFAULT_SINGLE_FILE_UPLOAD_VALUE = '';
|
|
1108
1173
|
/**
|
|
1109
|
-
*
|
|
1174
|
+
* FIELD_TYPES là readonly ['input', 'select', 'checkbox',...]
|
|
1110
1175
|
*/
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// Sample extra.
|
|
1132
|
-
// InputText
|
|
1133
|
-
const inputTextExtra = {
|
|
1134
|
-
form: {
|
|
1135
|
-
formTypes: ['add'],
|
|
1136
|
-
configs: {
|
|
1137
|
-
add: {
|
|
1138
|
-
key: 'name',
|
|
1139
|
-
label: 'Tên nhiệm vụ đề xuất',
|
|
1140
|
-
type: 'input-text',
|
|
1141
|
-
controlType: 'input',
|
|
1142
|
-
placeholder: 'Nhập tên nhiệm vụ đề xuất nào',
|
|
1143
|
-
required: false,
|
|
1144
|
-
grid: { xs: 12 },
|
|
1145
|
-
labelGrid: { xs: 8 },
|
|
1146
|
-
controlGrid: { xs: 16 },
|
|
1147
|
-
isHidden: false,
|
|
1148
|
-
isParent: false,
|
|
1149
|
-
allowSetValue: ['add'],
|
|
1150
|
-
},
|
|
1151
|
-
},
|
|
1152
|
-
},
|
|
1153
|
-
};
|
|
1154
|
-
// Input Password
|
|
1155
|
-
// SingleSelect Province
|
|
1156
|
-
// SingleSelect District
|
|
1157
|
-
// DatePicker
|
|
1158
|
-
// DateRange
|
|
1159
|
-
// SingleFileUpload
|
|
1160
|
-
// MultiFileUpload
|
|
1161
|
-
// Sample test ObjFields lấy từ API.
|
|
1162
|
-
const sampleObjFields = [
|
|
1163
|
-
// Input Text
|
|
1164
|
-
{
|
|
1165
|
-
code: 'Name',
|
|
1166
|
-
name: 'Tên nhiệm vụ',
|
|
1167
|
-
nameEn: 'Name',
|
|
1168
|
-
dataType: 'string',
|
|
1169
|
-
allowShare: 1,
|
|
1170
|
-
objClassCode: 'SciTaskProposal',
|
|
1171
|
-
componentUI: 'InputText',
|
|
1172
|
-
codeOnDb: 'tenNhiemVu',
|
|
1173
|
-
seqNum: 3,
|
|
1174
|
-
required: 3,
|
|
1175
|
-
allowStatistic: 0,
|
|
1176
|
-
preventEdit: 0,
|
|
1177
|
-
validateRegex: 'ALPHA_DASH_VN',
|
|
1178
|
-
extra: JSON.stringify(inputTextExtra),
|
|
1179
|
-
id: '7902fd41-f7c7-47e3-86f4-29b59a97dae3',
|
|
1180
|
-
},
|
|
1181
|
-
// Input Password
|
|
1182
|
-
// SingleSelect Province
|
|
1183
|
-
// SingleSelect District
|
|
1184
|
-
// DatePicker
|
|
1185
|
-
// DateRange
|
|
1186
|
-
// SingleFileUpload
|
|
1187
|
-
// MultiFileUpload
|
|
1188
|
-
// Field rác test k có cấu hình 1
|
|
1189
|
-
// Field rác test k có cấu hình 2
|
|
1190
|
-
// {
|
|
1191
|
-
// code: 'Code',
|
|
1192
|
-
// name: 'Mã đề xuất',
|
|
1193
|
-
// nameEn: 'Code',
|
|
1194
|
-
// dataType: 'string',
|
|
1195
|
-
// allowShare: 1,
|
|
1196
|
-
// objClassCode: 'SciTaskProposal',
|
|
1197
|
-
// componentUI: 'InputText',
|
|
1198
|
-
// codeOnDb: 'maDeXuat',
|
|
1199
|
-
// seqNum: 1,
|
|
1200
|
-
// required: 2,
|
|
1201
|
-
// allowStatistic: 0,
|
|
1202
|
-
// preventEdit: 0,
|
|
1203
|
-
// viewType: 'Basic',
|
|
1204
|
-
// id: '39d81def-6a34-4594-ab59-978233bea797',
|
|
1205
|
-
// },
|
|
1206
|
-
// {
|
|
1207
|
-
// code: 'Id',
|
|
1208
|
-
// name: 'Id',
|
|
1209
|
-
// nameEn: 'Id',
|
|
1210
|
-
// dataType: 'Guid',
|
|
1211
|
-
// allowShare: 1,
|
|
1212
|
-
// objClassCode: 'SciTaskProposal',
|
|
1213
|
-
// componentUI: 'TextView',
|
|
1214
|
-
// codeOnDb: 'id',
|
|
1215
|
-
// seqNum: 1,
|
|
1216
|
-
// required: 2,
|
|
1217
|
-
// allowStatistic: 0,
|
|
1218
|
-
// preventEdit: 1,
|
|
1219
|
-
// validateRegex: 'UUID',
|
|
1220
|
-
// viewType: 'Detail',
|
|
1221
|
-
// id: '0e460f53-76b0-4718-be0a-7e87c4a252a1',
|
|
1222
|
-
// },
|
|
1223
|
-
// {
|
|
1224
|
-
// code: 'ProposalName',
|
|
1225
|
-
// name: 'Tên đề xuất',
|
|
1226
|
-
// nameEn: 'ProposalName',
|
|
1227
|
-
// dataType: 'string',
|
|
1228
|
-
// allowShare: 1,
|
|
1229
|
-
// objClassCode: 'SciTaskProposal',
|
|
1230
|
-
// componentUI: 'InputText',
|
|
1231
|
-
// codeOnDb: 'tenDeXuat',
|
|
1232
|
-
// seqNum: 2,
|
|
1233
|
-
// required: 1,
|
|
1234
|
-
// allowStatistic: 0,
|
|
1235
|
-
// preventEdit: 0,
|
|
1236
|
-
// viewType: 'Detail',
|
|
1237
|
-
// id: '425739aa-0ab5-4524-8eec-1ccfaaf53ccc',
|
|
1238
|
-
// },
|
|
1239
|
-
// {
|
|
1240
|
-
// code: 'Name',
|
|
1241
|
-
// name: 'Tên nhiệm vụ',
|
|
1242
|
-
// nameEn: 'Name',
|
|
1243
|
-
// dataType: 'string',
|
|
1244
|
-
// allowShare: 1,
|
|
1245
|
-
// objClassCode: 'SciTaskProposal',
|
|
1246
|
-
// componentUI: 'InputText',
|
|
1247
|
-
// codeOnDb: 'tenNhiemVu',
|
|
1248
|
-
// seqNum: 3,
|
|
1249
|
-
// required: 3,
|
|
1250
|
-
// allowStatistic: 0,
|
|
1251
|
-
// preventEdit: 0,
|
|
1252
|
-
// validateRegex: 'ALPHA_DASH_VN',
|
|
1253
|
-
// extra:
|
|
1254
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":1,"viewType":"taskInfo"},"editting":{"seqNumber":1,"viewType":"taskInfo"},"viewing":{"seqNumber":1,"section":"left","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}}}}',
|
|
1255
|
-
// viewType: 'Basic',
|
|
1256
|
-
// id: '7902fd41-f7c7-47e3-86f4-29b59a97dae3',
|
|
1257
|
-
// },
|
|
1258
|
-
// {
|
|
1259
|
-
// code: 'Level',
|
|
1260
|
-
// name: 'Cấp nhiệm vụ đề xuất',
|
|
1261
|
-
// nameEn: 'Level',
|
|
1262
|
-
// dataType: 'ObjType',
|
|
1263
|
-
// allowShare: 1,
|
|
1264
|
-
// objClassCode: 'SciTaskProposal',
|
|
1265
|
-
// apiURL: '{MainApiURL}/CommonCats/ObjType/byTypeGroupCode/{ObjTypeGroupCode}',
|
|
1266
|
-
// componentUI: 'Combobox(SingleChoice)',
|
|
1267
|
-
// codeOnDb: 'cap',
|
|
1268
|
-
// seqNum: 4,
|
|
1269
|
-
// required: 3,
|
|
1270
|
-
// allowStatistic: 0,
|
|
1271
|
-
// preventEdit: 0,
|
|
1272
|
-
// extra:
|
|
1273
|
-
// '{"formField":{"displayMode":["add","edit"],"adding":{"seqNumber":2,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"editting":{"seqNumber":2,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"viewing":{"seqNumber":2,"section":"left","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}}}}',
|
|
1274
|
-
// viewType: 'Detail',
|
|
1275
|
-
// objTypeGroupCode: 'RECOMMEND_TASK',
|
|
1276
|
-
// id: '03230f20-2c88-4c05-a990-8290d435ce31',
|
|
1277
|
-
// },
|
|
1278
|
-
// {
|
|
1279
|
-
// code: 'Program',
|
|
1280
|
-
// name: 'Chương trình mà nhiệm vụ trực thuộc',
|
|
1281
|
-
// nameEn: 'Program',
|
|
1282
|
-
// dataType: 'string',
|
|
1283
|
-
// allowShare: 1,
|
|
1284
|
-
// objClassCode: 'SciTaskProposal',
|
|
1285
|
-
// componentUI: 'InputText',
|
|
1286
|
-
// codeOnDb: 'chuongTrinh',
|
|
1287
|
-
// seqNum: 5,
|
|
1288
|
-
// required: 1,
|
|
1289
|
-
// allowStatistic: 0,
|
|
1290
|
-
// preventEdit: 0,
|
|
1291
|
-
// validateRegex: 'ALPHA_DASH_VN',
|
|
1292
|
-
// extra:
|
|
1293
|
-
// '{"formField":{"displayMode":["add","edit"],"adding":{"seqNumber":2,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"editting":{"seqNumber":2,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"viewing":{"seqNumber":2,"section":"left","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}}}}',
|
|
1294
|
-
// viewType: 'Detail',
|
|
1295
|
-
// id: 'c03a9d28-ca43-44ca-a59c-fc54494f3c73',
|
|
1296
|
-
// },
|
|
1297
|
-
// {
|
|
1298
|
-
// code: 'ResearchField',
|
|
1299
|
-
// name: 'Lĩnh vực',
|
|
1300
|
-
// nameEn: 'ResearchField',
|
|
1301
|
-
// dataType: 'ObjType',
|
|
1302
|
-
// allowShare: 1,
|
|
1303
|
-
// objClassCode: 'SciTaskProposal',
|
|
1304
|
-
// apiURL: '{MainApiURL}/CommonCats/ObjType/byTypeGroupCode/{ObjTypeGroupCode}',
|
|
1305
|
-
// componentUI: 'Combobox(SingleChoice)',
|
|
1306
|
-
// codeOnDb: 'linhVucNghienCuu',
|
|
1307
|
-
// seqNum: 6,
|
|
1308
|
-
// required: 3,
|
|
1309
|
-
// allowStatistic: 0,
|
|
1310
|
-
// preventEdit: 0,
|
|
1311
|
-
// extra:
|
|
1312
|
-
// '{"formField":{"displayMode":["add","edit","view","filter"],"adding":{"seqNumber":2,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"editting":{"seqNumber":2,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"viewing":{"seqNumber":2,"section":"left","isShowBgInput":true,"inputBgColor":"#2E7D32","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}},"filtering":{"codeField":"researchFieldCode","seqNumber":3,"required":false,"dataTypeCombobox":"single"}}}',
|
|
1313
|
-
// viewType: 'Detail',
|
|
1314
|
-
// objTypeGroupCode: 'RESEARCH_FIELD',
|
|
1315
|
-
// id: 'b5248535-fb73-486e-a980-24e8ada1993b',
|
|
1316
|
-
// },
|
|
1317
|
-
// {
|
|
1318
|
-
// code: 'Type',
|
|
1319
|
-
// name: 'Loại hình nhiệm vụ',
|
|
1320
|
-
// nameEn: 'Type',
|
|
1321
|
-
// dataType: 'ObjType',
|
|
1322
|
-
// allowShare: 1,
|
|
1323
|
-
// objClassCode: 'SciTaskProposal',
|
|
1324
|
-
// apiURL: '{MainApiURL}/CommonCats/ObjType/byTypeGroupCode/{ObjTypeGroupCode}',
|
|
1325
|
-
// componentUI: 'Combobox(SingleChoice)',
|
|
1326
|
-
// codeOnDb: 'loaiHinhNhiemVu',
|
|
1327
|
-
// seqNum: 7,
|
|
1328
|
-
// required: 3,
|
|
1329
|
-
// allowStatistic: 0,
|
|
1330
|
-
// preventEdit: 0,
|
|
1331
|
-
// extra:
|
|
1332
|
-
// '{"formField":{"displayMode":["add","edit","view","filter"],"adding":{"seqNumber":3,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"editting":{"seqNumber":3,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"viewing":{"seqNumber":3,"section":"left","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}},"filtering":{"codeField":"typeCode","seqNumber":1,"required":false,"dataTypeCombobox": "single"}}}',
|
|
1333
|
-
// viewType: 'Detail',
|
|
1334
|
-
// objTypeGroupCode: 'TYPE_TASK',
|
|
1335
|
-
// id: 'f6793a1e-f451-4050-b751-aa3a5f86d862',
|
|
1336
|
-
// },
|
|
1337
|
-
// {
|
|
1338
|
-
// code: 'PlanningProposalYear',
|
|
1339
|
-
// name: 'Năm kế hoạch đề xuất',
|
|
1340
|
-
// nameEn: 'PlanningProposalYear',
|
|
1341
|
-
// dataType: 'int',
|
|
1342
|
-
// allowShare: 1,
|
|
1343
|
-
// objClassCode: 'SciTaskProposal',
|
|
1344
|
-
// componentUI: 'InputText',
|
|
1345
|
-
// codeOnDb: 'namKeHoachDeXuat',
|
|
1346
|
-
// seqNum: 8,
|
|
1347
|
-
// required: 1,
|
|
1348
|
-
// allowStatistic: 0,
|
|
1349
|
-
// preventEdit: 0,
|
|
1350
|
-
// viewType: 'Detail',
|
|
1351
|
-
// id: '90f48f9f-5d3d-43d0-bbac-1dd072a4ffd1',
|
|
1352
|
-
// },
|
|
1353
|
-
// {
|
|
1354
|
-
// code: 'ResearchObjective',
|
|
1355
|
-
// name: 'Mục tiêu của nhiệm vụ ',
|
|
1356
|
-
// nameEn: 'ResearchObjective',
|
|
1357
|
-
// dataType: 'string',
|
|
1358
|
-
// allowShare: 1,
|
|
1359
|
-
// objClassCode: 'SciTaskProposal',
|
|
1360
|
-
// componentUI: 'TextArea',
|
|
1361
|
-
// codeOnDb: 'mucTieu',
|
|
1362
|
-
// seqNum: 9,
|
|
1363
|
-
// required: 3,
|
|
1364
|
-
// allowStatistic: 0,
|
|
1365
|
-
// preventEdit: 0,
|
|
1366
|
-
// extra:
|
|
1367
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":3,"viewType":"taskInfo"},"editting":{"seqNumber":3,"viewType":"taskInfo"},"viewing":{"seqNumber":1,"section":"bottom"}}}',
|
|
1368
|
-
// viewType: 'Detail',
|
|
1369
|
-
// id: 'ecc54ec4-ee0e-4b97-9c5b-2e06d25d6bf0',
|
|
1370
|
-
// },
|
|
1371
|
-
// {
|
|
1372
|
-
// code: 'ExpectedContent',
|
|
1373
|
-
// name: 'Dự kiến các nội dung cần thực hiện',
|
|
1374
|
-
// nameEn: 'ExpectedContent',
|
|
1375
|
-
// dataType: 'string',
|
|
1376
|
-
// allowShare: 1,
|
|
1377
|
-
// objClassCode: 'SciTaskProposal',
|
|
1378
|
-
// componentUI: 'InputText',
|
|
1379
|
-
// codeOnDb: 'duKienNoiDung',
|
|
1380
|
-
// seqNum: 10,
|
|
1381
|
-
// required: 1,
|
|
1382
|
-
// allowStatistic: 0,
|
|
1383
|
-
// preventEdit: 0,
|
|
1384
|
-
// viewType: 'Detail',
|
|
1385
|
-
// id: '180c8300-11da-41f4-8685-97d2e8885200',
|
|
1386
|
-
// },
|
|
1387
|
-
// {
|
|
1388
|
-
// code: 'ExpectedResultsAndIndicators',
|
|
1389
|
-
// name: 'Dự kiến kết quả chính và các chỉ tiêu cần đạt',
|
|
1390
|
-
// nameEn: 'ExpectedResultsAndIndicators',
|
|
1391
|
-
// dataType: 'string',
|
|
1392
|
-
// allowShare: 1,
|
|
1393
|
-
// objClassCode: 'SciTaskProposal',
|
|
1394
|
-
// componentUI: 'TextArea',
|
|
1395
|
-
// codeOnDb: 'duKienKetQuaVaChiTieu',
|
|
1396
|
-
// seqNum: 11,
|
|
1397
|
-
// required: 3,
|
|
1398
|
-
// allowStatistic: 0,
|
|
1399
|
-
// preventEdit: 0,
|
|
1400
|
-
// extra:
|
|
1401
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":5,"viewType":"taskInfo"},"editting":{"seqNumber":5,"viewType":"taskInfo"},"viewing":{"seqNumber":2,"section":"bottom"}}}',
|
|
1402
|
-
// viewType: 'Detail',
|
|
1403
|
-
// id: '617af2b0-d4d8-4e41-b0cb-388c4fbd917a',
|
|
1404
|
-
// },
|
|
1405
|
-
// {
|
|
1406
|
-
// code: 'ExpectedApplicationPlan',
|
|
1407
|
-
// name: 'Dự kiến phương án ứng dụng hoặc sử dụng kết quả tạo ra',
|
|
1408
|
-
// nameEn: 'ExpectedApplicationPlan',
|
|
1409
|
-
// dataType: 'string',
|
|
1410
|
-
// allowShare: 1,
|
|
1411
|
-
// objClassCode: 'SciTaskProposal',
|
|
1412
|
-
// componentUI: 'TextArea',
|
|
1413
|
-
// codeOnDb: 'duKienPhuongAnUngDung',
|
|
1414
|
-
// seqNum: 12,
|
|
1415
|
-
// required: 3,
|
|
1416
|
-
// allowStatistic: 0,
|
|
1417
|
-
// preventEdit: 0,
|
|
1418
|
-
// extra:
|
|
1419
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":6,"viewType":"taskInfo"},"editting":{"seqNumber":6,"viewType":"taskInfo"},"viewing":{"seqNumber":3,"section":"bottom"}}}',
|
|
1420
|
-
// viewType: 'Detail',
|
|
1421
|
-
// id: 'c89bb6c9-3dcf-4380-94f5-630baadb9b31',
|
|
1422
|
-
// },
|
|
1423
|
-
// {
|
|
1424
|
-
// code: 'EstimatedBudget',
|
|
1425
|
-
// name: 'Dự kiến kinh phí (đồng)',
|
|
1426
|
-
// nameEn: 'EstimatedBudget',
|
|
1427
|
-
// dataType: 'long',
|
|
1428
|
-
// allowShare: 1,
|
|
1429
|
-
// objClassCode: 'SciTaskProposal',
|
|
1430
|
-
// componentUI: 'InputText',
|
|
1431
|
-
// codeOnDb: 'duKienKinhPhi',
|
|
1432
|
-
// seqNum: 13,
|
|
1433
|
-
// required: 3,
|
|
1434
|
-
// allowStatistic: 0,
|
|
1435
|
-
// preventEdit: 0,
|
|
1436
|
-
// validateRegex: 'INTEGER',
|
|
1437
|
-
// extra:
|
|
1438
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":7,"span":{"sm":24,"md":"12"},"viewType":"taskInfo","isCurrency":true},"editting":{"seqNumber":7,"span":{"sm":24,"md":"12"},"viewType":"taskInfo","isCurrency":true},"viewing":{"seqNumber":4,"section":"left","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}},"isCurrency":true}}}',
|
|
1439
|
-
// viewType: 'Detail',
|
|
1440
|
-
// id: 'a8072db6-0bd1-4352-8e30-326446989eef',
|
|
1441
|
-
// },
|
|
1442
|
-
// {
|
|
1443
|
-
// code: 'EstimatedDuration',
|
|
1444
|
-
// name: 'Dự kiến thời gian thực hiện (tháng)',
|
|
1445
|
-
// nameEn: 'EstimatedDuration',
|
|
1446
|
-
// dataType: 'int',
|
|
1447
|
-
// allowShare: 1,
|
|
1448
|
-
// objClassCode: 'SciTaskProposal',
|
|
1449
|
-
// componentUI: 'InputText',
|
|
1450
|
-
// codeOnDb: 'duKienThoiGianThucHien',
|
|
1451
|
-
// seqNum: 14,
|
|
1452
|
-
// required: 3,
|
|
1453
|
-
// allowStatistic: 0,
|
|
1454
|
-
// preventEdit: 0,
|
|
1455
|
-
// validateRegex: 'INTEGER',
|
|
1456
|
-
// extra:
|
|
1457
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":8,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"editting":{"seqNumber":8,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"viewing":{"seqNumber":5,"section":"left","executionTime":"month","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}}}}',
|
|
1458
|
-
// viewType: 'Detail',
|
|
1459
|
-
// id: 'f69bdf14-c555-4cf5-b793-1eba85a25701',
|
|
1460
|
-
// },
|
|
1461
|
-
// {
|
|
1462
|
-
// code: 'ProposalAttachment',
|
|
1463
|
-
// name: 'Văn bản đề xuất đính kèm (pdf)',
|
|
1464
|
-
// nameEn: 'ProposalAttachment',
|
|
1465
|
-
// dataType: 'string',
|
|
1466
|
-
// allowShare: 1,
|
|
1467
|
-
// objClassCode: 'SciTaskProposal',
|
|
1468
|
-
// componentUI: 'File',
|
|
1469
|
-
// codeOnDb: 'vanBanDeXuat',
|
|
1470
|
-
// seqNum: 15,
|
|
1471
|
-
// required: 3,
|
|
1472
|
-
// allowStatistic: 0,
|
|
1473
|
-
// preventEdit: 0,
|
|
1474
|
-
// extra:
|
|
1475
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"title":"Văn bản đề xuất đính kèm (hỗ trợ tập tin dạng .pdf)","seqNumber":9,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"editting":{"title":"Văn bản đề xuất đính kèm (hỗ trợ tập tin dạng .pdf)","seqNumber":9,"span":{"sm":24,"md":"12"},"viewType":"taskInfo"},"viewing":{"seqNumber":7,"section":"left","viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}}}}',
|
|
1476
|
-
// viewType: 'Detail',
|
|
1477
|
-
// id: '75596544-c7b1-40dd-ad24-deee21e8b8d1',
|
|
1478
|
-
// },
|
|
1479
|
-
// {
|
|
1480
|
-
// code: 'ProposalOrganizationName',
|
|
1481
|
-
// name: 'Tên đơn vị/cá nhân đề xuất',
|
|
1482
|
-
// nameEn: 'ProposalOrganizationName',
|
|
1483
|
-
// dataType: 'string',
|
|
1484
|
-
// allowShare: 1,
|
|
1485
|
-
// objClassCode: 'SciTaskProposal',
|
|
1486
|
-
// componentUI: 'InputText',
|
|
1487
|
-
// codeOnDb: 'tenToChucDeXuat',
|
|
1488
|
-
// seqNum: 16,
|
|
1489
|
-
// required: 3,
|
|
1490
|
-
// allowStatistic: 0,
|
|
1491
|
-
// preventEdit: 0,
|
|
1492
|
-
// validateRegex: 'ALPHA_DASH_VN',
|
|
1493
|
-
// extra:
|
|
1494
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":2,"viewType":"taskOrgzIndivInfo"},"editting":{"seqNumber":2,"viewType":"taskOrgzIndivInfo"},"viewing":{"seqNumber":1,"section":"right"}}}',
|
|
1495
|
-
// viewType: 'Detail',
|
|
1496
|
-
// id: '9ba7ea0a-e1d3-4006-9a10-bba7b54f17e4',
|
|
1497
|
-
// },
|
|
1498
|
-
// {
|
|
1499
|
-
// code: 'ProposerName',
|
|
1500
|
-
// name: 'Tên cá nhân đề xuất',
|
|
1501
|
-
// nameEn: 'ProposerName',
|
|
1502
|
-
// dataType: 'string',
|
|
1503
|
-
// allowShare: 1,
|
|
1504
|
-
// objClassCode: 'SciTaskProposal',
|
|
1505
|
-
// componentUI: 'InputText',
|
|
1506
|
-
// codeOnDb: 'tenCaNhanDeXuat',
|
|
1507
|
-
// seqNum: 17,
|
|
1508
|
-
// required: 3,
|
|
1509
|
-
// allowStatistic: 0,
|
|
1510
|
-
// preventEdit: 0,
|
|
1511
|
-
// extra: '',
|
|
1512
|
-
// viewType: 'Detail',
|
|
1513
|
-
// id: 'cb2e752c-ed93-46f1-8cd8-75aeb399952b',
|
|
1514
|
-
// },
|
|
1515
|
-
// {
|
|
1516
|
-
// code: 'PhoneNumber',
|
|
1517
|
-
// name: 'Số điện thoại',
|
|
1518
|
-
// nameEn: 'PhoneNumber',
|
|
1519
|
-
// dataType: 'string',
|
|
1520
|
-
// allowShare: 1,
|
|
1521
|
-
// objClassCode: 'SciTaskProposal',
|
|
1522
|
-
// componentUI: 'InputText',
|
|
1523
|
-
// codeOnDb: 'soDienThoai',
|
|
1524
|
-
// seqNum: 18,
|
|
1525
|
-
// required: 1,
|
|
1526
|
-
// allowStatistic: 0,
|
|
1527
|
-
// preventEdit: 0,
|
|
1528
|
-
// validateRegex: 'PHONE_NUMBER',
|
|
1529
|
-
// extra:
|
|
1530
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":4,"viewType":"taskOrgzIndivInfo","span":{"sm":24,"md":12}},"editting":{"seqNumber":4,"viewType":"taskOrgzIndivInfo","span":{"sm":24,"md":12}},"viewing":{"seqNumber":3,"section":"right"}}}',
|
|
1531
|
-
// viewType: 'Detail',
|
|
1532
|
-
// id: '06137a76-2793-42db-a0d1-2039e087b2ce',
|
|
1533
|
-
// },
|
|
1534
|
-
// {
|
|
1535
|
-
// code: 'Email',
|
|
1536
|
-
// name: 'Thư điện tử',
|
|
1537
|
-
// nameEn: 'Email',
|
|
1538
|
-
// dataType: 'string',
|
|
1539
|
-
// allowShare: 1,
|
|
1540
|
-
// objClassCode: 'SciTaskProposal',
|
|
1541
|
-
// componentUI: 'InputText',
|
|
1542
|
-
// codeOnDb: 'thuDienTu',
|
|
1543
|
-
// seqNum: 19,
|
|
1544
|
-
// required: 3,
|
|
1545
|
-
// allowStatistic: 0,
|
|
1546
|
-
// preventEdit: 0,
|
|
1547
|
-
// validateRegex: 'EMAIL',
|
|
1548
|
-
// extra:
|
|
1549
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":3,"viewType":"taskOrgzIndivInfo","span":{"sm":24,"md":12}},"editting":{"seqNumber":3,"viewType":"taskOrgzIndivInfo","span":{"sm":24,"md":12}},"viewing":{"seqNumber":2,"section":"right"}}}',
|
|
1550
|
-
// viewType: 'Detail',
|
|
1551
|
-
// id: 'acef6046-1f41-48f2-b53a-c0ecebb42939',
|
|
1552
|
-
// },
|
|
1553
|
-
// {
|
|
1554
|
-
// code: 'Address',
|
|
1555
|
-
// name: 'Địa chỉ liên hệ',
|
|
1556
|
-
// nameEn: 'Address',
|
|
1557
|
-
// dataType: 'string',
|
|
1558
|
-
// allowShare: 1,
|
|
1559
|
-
// objClassCode: 'SciTaskProposal',
|
|
1560
|
-
// componentUI: 'TextArea',
|
|
1561
|
-
// codeOnDb: 'diaChi',
|
|
1562
|
-
// seqNum: 20,
|
|
1563
|
-
// required: 1,
|
|
1564
|
-
// allowStatistic: 0,
|
|
1565
|
-
// preventEdit: 0,
|
|
1566
|
-
// extra:
|
|
1567
|
-
// '{"formField":{"displayMode":["add","edit","view"],"adding":{"seqNumber":4,"viewType":"taskOrgzIndivInfo"},"editting":{"seqNumber":4,"viewType":"taskOrgzIndivInfo"},"viewing":{"type":"InputText","seqNumber":3,"section":"right"}}}',
|
|
1568
|
-
// viewType: 'Detail',
|
|
1569
|
-
// id: 'faef0e51-f288-424e-9e72-50ed155313a5',
|
|
1570
|
-
// },
|
|
1571
|
-
// {
|
|
1572
|
-
// code: 'ProposalSource',
|
|
1573
|
-
// name: 'Nguồn đề xuất',
|
|
1574
|
-
// nameEn: 'ProposalSource',
|
|
1575
|
-
// dataType: 'ObjType',
|
|
1576
|
-
// allowShare: 1,
|
|
1577
|
-
// objClassCode: 'SciTaskProposal',
|
|
1578
|
-
// apiURL: '{MainApiURL}/CommonCats/ObjType/byTypeGroupCode/{ObjTypeGroupCode}',
|
|
1579
|
-
// componentUI: 'Radiobox',
|
|
1580
|
-
// codeOnDb: 'nguonDeXuat',
|
|
1581
|
-
// seqNum: 21,
|
|
1582
|
-
// required: 3,
|
|
1583
|
-
// allowStatistic: 0,
|
|
1584
|
-
// preventEdit: 0,
|
|
1585
|
-
// extra:
|
|
1586
|
-
// '{"formField":{"displayMode":["add","edit","filter"],"adding":{"seqNumber":1,"viewType":"taskOrgzIndivInfo"},"editting":{"seqNumber":1,"viewType":"taskOrgzIndivInfo"},"filtering":{"codeField":"proposalSourceCode","seqNumber":2,"type":"Dropdown","required":false,"dataTypeCombobox": "single"}}}',
|
|
1587
|
-
// viewType: 'Detail',
|
|
1588
|
-
// objTypeGroupCode: 'PROPOSAL_SOURCE',
|
|
1589
|
-
// id: '6dea381c-368f-4423-83fb-957df68b176d',
|
|
1590
|
-
// },
|
|
1591
|
-
// {
|
|
1592
|
-
// code: 'ProposalStatus',
|
|
1593
|
-
// name: 'Trạng thái',
|
|
1594
|
-
// nameEn: 'ProposalStatus',
|
|
1595
|
-
// dataType: 'ObjType',
|
|
1596
|
-
// allowShare: 1,
|
|
1597
|
-
// objClassCode: 'SciTaskProposal',
|
|
1598
|
-
// apiURL: '{MainApiURL}/CommonCats/ObjType/byTypeGroupCode/{ObjTypeGroupCode}',
|
|
1599
|
-
// componentUI: 'Combobox(SingleChoice)',
|
|
1600
|
-
// codeOnDb: 'trangThaiDeXuat',
|
|
1601
|
-
// seqNum: 22,
|
|
1602
|
-
// required: 2,
|
|
1603
|
-
// allowStatistic: 0,
|
|
1604
|
-
// preventEdit: 0,
|
|
1605
|
-
// extra:
|
|
1606
|
-
// '{"formField":{"displayMode":["view","filter"],"viewing":{"seqNumber":6,"section":"left","isShowBgInput":true,"viewConfig":{"labelSpan":{"sm":24,"md":10},"formControlSpan":{"sm":24,"md":14}}},"filtering":{"codeField":"proposalStatusCode","seqNumber":4,"dataTypeCombobox":"single"}}}',
|
|
1607
|
-
// viewType: 'Detail',
|
|
1608
|
-
// objTypeGroupCode: 'PROPOSAL_STATUS',
|
|
1609
|
-
// id: 'b77dcf37-880b-48f9-acfb-09b983df4a0d',
|
|
1610
|
-
// },
|
|
1611
|
-
// {
|
|
1612
|
-
// code: 'ApplicationStartDate',
|
|
1613
|
-
// name: 'Ngày bắt đầu mở ứng tuyển cho đề xuất ',
|
|
1614
|
-
// nameEn: 'ApplicationStartDate',
|
|
1615
|
-
// dataType: 'Date',
|
|
1616
|
-
// allowShare: 1,
|
|
1617
|
-
// objClassCode: 'SciTaskProposal',
|
|
1618
|
-
// componentUI: 'Calendar(Date)',
|
|
1619
|
-
// codeOnDb: 'ngayBatDauUngTuyen',
|
|
1620
|
-
// seqNum: 23,
|
|
1621
|
-
// required: 3,
|
|
1622
|
-
// allowStatistic: 0,
|
|
1623
|
-
// preventEdit: 0,
|
|
1624
|
-
// extra:
|
|
1625
|
-
// '{"formField":{"displayMode":["edit","view"],"editting":{"title":"Thời gian bắt đầu","allowFutureDates":true,"seqNumber":1,"span":{"sm":24,"md":12},"section":"approval"},"viewing":{"title":"Thời gian bắt đầu","seqNumber":1,"section":"application"}}}',
|
|
1626
|
-
// viewType: 'Detail',
|
|
1627
|
-
// id: '790d2aee-4fb2-456f-8fd0-8cd5e0296a57',
|
|
1628
|
-
// },
|
|
1629
|
-
// {
|
|
1630
|
-
// code: 'ApplicationEndDate',
|
|
1631
|
-
// name: 'Ngày kết thúc ứng tuyển cho đề xuất ',
|
|
1632
|
-
// nameEn: 'ApplicationEndDate',
|
|
1633
|
-
// dataType: 'Date',
|
|
1634
|
-
// allowShare: 1,
|
|
1635
|
-
// objClassCode: 'SciTaskProposal',
|
|
1636
|
-
// componentUI: 'Calendar(Date)',
|
|
1637
|
-
// codeOnDb: 'ngayKetThucUngTuyen',
|
|
1638
|
-
// seqNum: 24,
|
|
1639
|
-
// required: 3,
|
|
1640
|
-
// allowStatistic: 0,
|
|
1641
|
-
// preventEdit: 0,
|
|
1642
|
-
// extra:
|
|
1643
|
-
// '{"formField":{"displayMode":["edit","view"],"editting":{"title":"Thời gian kết thúc","allowFutureDates":true,"seqNumber":1,"span":{"sm":24,"md":12},"section":"approval"},"viewing":{"title":"Thời gian kết thúc","seqNumber":2,"section":"application"}}}',
|
|
1644
|
-
// viewType: 'Detail',
|
|
1645
|
-
// id: '95822a49-2883-4ddb-a6ff-a3c8fc84f3bf',
|
|
1646
|
-
// },
|
|
1647
|
-
// {
|
|
1648
|
-
// code: 'ApprovalAttachment',
|
|
1649
|
-
// name: 'Văn bản giao nhiệm vụ - ứng tuyển được duyệt làm ',
|
|
1650
|
-
// nameEn: 'ApprovalAttachment',
|
|
1651
|
-
// dataType: 'string',
|
|
1652
|
-
// allowShare: 1,
|
|
1653
|
-
// objClassCode: 'SciTaskProposal',
|
|
1654
|
-
// componentUI: 'InputText',
|
|
1655
|
-
// codeOnDb: 'vanBanGiaoNhiemVu',
|
|
1656
|
-
// seqNum: 25,
|
|
1657
|
-
// required: 3,
|
|
1658
|
-
// allowStatistic: 0,
|
|
1659
|
-
// preventEdit: 0,
|
|
1660
|
-
// viewType: 'Detail',
|
|
1661
|
-
// id: 'fa00b65d-ebfb-49f9-8da2-09cd44138b79',
|
|
1662
|
-
// },
|
|
1663
|
-
// {
|
|
1664
|
-
// code: 'ApprovalDuration',
|
|
1665
|
-
// name: 'thời gian thức hiện duyệt',
|
|
1666
|
-
// nameEn: 'ApprovalDuration',
|
|
1667
|
-
// dataType: 'int',
|
|
1668
|
-
// allowShare: 1,
|
|
1669
|
-
// objClassCode: 'SciTaskProposal',
|
|
1670
|
-
// codeOnDb: 'thoiGianThucHienDuyet',
|
|
1671
|
-
// seqNum: 26,
|
|
1672
|
-
// required: 0,
|
|
1673
|
-
// allowStatistic: 0,
|
|
1674
|
-
// preventEdit: 0,
|
|
1675
|
-
// viewType: 'Detail',
|
|
1676
|
-
// id: '54477413-ec20-43d8-8f9e-6e73205b86ad',
|
|
1677
|
-
// },
|
|
1678
|
-
// {
|
|
1679
|
-
// code: 'ApprovalTotalBudget',
|
|
1680
|
-
// name: 'Tổng kinh phí cần thiết được duyệt với văn bản giao nhiệm vụ ',
|
|
1681
|
-
// nameEn: 'ApprovalTotalBudget',
|
|
1682
|
-
// dataType: 'long',
|
|
1683
|
-
// allowShare: 1,
|
|
1684
|
-
// objClassCode: 'SciTaskProposal',
|
|
1685
|
-
// codeOnDb: 'tongKinhPhiCanThietDuyet',
|
|
1686
|
-
// seqNum: 27,
|
|
1687
|
-
// required: 0,
|
|
1688
|
-
// allowStatistic: 0,
|
|
1689
|
-
// preventEdit: 0,
|
|
1690
|
-
// viewType: 'Detail',
|
|
1691
|
-
// id: 'f9a6c1a6-780e-4e9c-87ac-41a7dfbf37df',
|
|
1692
|
-
// },
|
|
1693
|
-
// {
|
|
1694
|
-
// code: 'ApprovalGovermentBudget',
|
|
1695
|
-
// name: 'Kinh phí từ Ngân sách nhà nước được duyệt với văn bản giao nhiệm vụ ',
|
|
1696
|
-
// nameEn: 'ApprovalGovermentBudget',
|
|
1697
|
-
// dataType: 'long',
|
|
1698
|
-
// allowShare: 1,
|
|
1699
|
-
// objClassCode: 'SciTaskProposal',
|
|
1700
|
-
// codeOnDb: 'kinhPhiTuNSNNDuyet',
|
|
1701
|
-
// seqNum: 28,
|
|
1702
|
-
// required: 0,
|
|
1703
|
-
// allowStatistic: 0,
|
|
1704
|
-
// preventEdit: 0,
|
|
1705
|
-
// viewType: 'Detail',
|
|
1706
|
-
// id: '37690b42-c4f7-40bf-ae4a-c42f10735409',
|
|
1707
|
-
// },
|
|
1708
|
-
// {
|
|
1709
|
-
// code: 'ApprovalNonGovermentBudget',
|
|
1710
|
-
// name: 'Kinh phí từ các nguồn ngoài ngân sách nhà nước được duyệt với văn bản giao nhiệm vụ ',
|
|
1711
|
-
// nameEn: 'ApprovalNonGovermentBudget',
|
|
1712
|
-
// dataType: 'long',
|
|
1713
|
-
// allowShare: 1,
|
|
1714
|
-
// objClassCode: 'SciTaskProposal',
|
|
1715
|
-
// codeOnDb: 'kinhPhiTuNgoaiNSNNDuyet',
|
|
1716
|
-
// seqNum: 29,
|
|
1717
|
-
// required: 0,
|
|
1718
|
-
// allowStatistic: 0,
|
|
1719
|
-
// preventEdit: 0,
|
|
1720
|
-
// viewType: 'Detail',
|
|
1721
|
-
// id: 'f9cf4aa3-6593-4b2d-88ca-1513069ce57f',
|
|
1722
|
-
// },
|
|
1723
|
-
// {
|
|
1724
|
-
// code: 'ApprovalContractStateBudget',
|
|
1725
|
-
// name: 'Kinh phí Ngân sách nhà nước khoán được duyệt với văn bản giao nhiệm vụ ',
|
|
1726
|
-
// nameEn: 'ApprovalContractStateBudget',
|
|
1727
|
-
// dataType: 'long',
|
|
1728
|
-
// allowShare: 1,
|
|
1729
|
-
// objClassCode: 'SciTaskProposal',
|
|
1730
|
-
// codeOnDb: 'kinhPhiNSNNKhoanDuyet',
|
|
1731
|
-
// seqNum: 30,
|
|
1732
|
-
// required: 0,
|
|
1733
|
-
// allowStatistic: 0,
|
|
1734
|
-
// preventEdit: 0,
|
|
1735
|
-
// viewType: 'Detail',
|
|
1736
|
-
// id: '9c0aeb4b-b6db-460e-8c01-7bc728535e7a',
|
|
1737
|
-
// },
|
|
1738
|
-
// {
|
|
1739
|
-
// code: 'ApprovalNonContractStateBudget',
|
|
1740
|
-
// name: 'Kinh phí Ngân sách nhà nước không khoán được duyệt với văn bản giao nhiệm vụ ',
|
|
1741
|
-
// nameEn: 'ApprovalNonContractStateBudget',
|
|
1742
|
-
// dataType: 'long',
|
|
1743
|
-
// allowShare: 1,
|
|
1744
|
-
// objClassCode: 'SciTaskProposal',
|
|
1745
|
-
// codeOnDb: 'kinhPhiNSNNKhongKhoanDuyet',
|
|
1746
|
-
// seqNum: 31,
|
|
1747
|
-
// required: 0,
|
|
1748
|
-
// allowStatistic: 0,
|
|
1749
|
-
// preventEdit: 0,
|
|
1750
|
-
// viewType: 'Detail',
|
|
1751
|
-
// id: 'c5cf6569-306a-4ede-ace6-5b9212003a0c',
|
|
1752
|
-
// },
|
|
1753
|
-
// {
|
|
1754
|
-
// code: 'ApprovalImplementationMethod',
|
|
1755
|
-
// name: 'Phương thức thực hiện',
|
|
1756
|
-
// nameEn: 'ApprovalImplementationMethod',
|
|
1757
|
-
// dataType: 'ObjType',
|
|
1758
|
-
// allowShare: 1,
|
|
1759
|
-
// objClassCode: 'SciTaskProposal',
|
|
1760
|
-
// codeOnDb: 'phuongThucThucHienDuyet',
|
|
1761
|
-
// seqNum: 32,
|
|
1762
|
-
// required: 1,
|
|
1763
|
-
// allowStatistic: 0,
|
|
1764
|
-
// preventEdit: 0,
|
|
1765
|
-
// viewType: 'Detail',
|
|
1766
|
-
// id: 'cf73d6e1-581d-4228-b016-6557466ed61a',
|
|
1767
|
-
// },
|
|
1768
|
-
// {
|
|
1769
|
-
// code: 'UpdatedById',
|
|
1770
|
-
// name: 'Mã người cập nhật trang thái hiện tại của nhiệm vụ',
|
|
1771
|
-
// nameEn: 'UpdatedById',
|
|
1772
|
-
// dataType: 'Guid',
|
|
1773
|
-
// allowShare: 1,
|
|
1774
|
-
// objClassCode: 'SciTaskProposal',
|
|
1775
|
-
// apiURL: '1',
|
|
1776
|
-
// codeOnDb: 'maNguoiCapNhat',
|
|
1777
|
-
// seqNum: 33,
|
|
1778
|
-
// required: 2,
|
|
1779
|
-
// allowStatistic: 0,
|
|
1780
|
-
// preventEdit: 0,
|
|
1781
|
-
// viewType: 'Detail',
|
|
1782
|
-
// id: '8c8de675-f6fa-4dd7-b706-39ea7408a12d',
|
|
1783
|
-
// },
|
|
1784
|
-
// {
|
|
1785
|
-
// code: 'UpdatedBy',
|
|
1786
|
-
// name: 'Người cập nhật trang thái hiện tại của nhiệm vụ',
|
|
1787
|
-
// nameEn: 'UpdatedBy',
|
|
1788
|
-
// dataType: 'string',
|
|
1789
|
-
// allowShare: 1,
|
|
1790
|
-
// objClassCode: 'SciTaskProposal',
|
|
1791
|
-
// apiURL: '1',
|
|
1792
|
-
// codeOnDb: 'nguoiCapNhat',
|
|
1793
|
-
// seqNum: 34,
|
|
1794
|
-
// required: 2,
|
|
1795
|
-
// allowStatistic: 0,
|
|
1796
|
-
// preventEdit: 0,
|
|
1797
|
-
// viewType: 'Detail',
|
|
1798
|
-
// id: 'f09197be-09c3-43e2-b2a0-b535be2643f0',
|
|
1799
|
-
// },
|
|
1800
|
-
// {
|
|
1801
|
-
// code: 'CreatedAt',
|
|
1802
|
-
// name: 'Thời gian tạo',
|
|
1803
|
-
// nameEn: 'CreatedAt',
|
|
1804
|
-
// dataType: 'DateTime',
|
|
1805
|
-
// allowShare: 1,
|
|
1806
|
-
// objClassCode: 'SciTaskProposal',
|
|
1807
|
-
// componentUI: 'Calendar(Date)',
|
|
1808
|
-
// codeOnDb: 'createdAt',
|
|
1809
|
-
// seqNum: 35,
|
|
1810
|
-
// required: 2,
|
|
1811
|
-
// allowStatistic: 0,
|
|
1812
|
-
// preventEdit: 0,
|
|
1813
|
-
// extra:
|
|
1814
|
-
// '{"formField":{"displayMode":["filter"],"filtering":{"seqNumber":5,"type":"DateRange"}}}',
|
|
1815
|
-
// viewType: 'Detail',
|
|
1816
|
-
// id: '8b425192-574b-4df3-8b9a-c28bcd47bdc7',
|
|
1817
|
-
// },
|
|
1818
|
-
// {
|
|
1819
|
-
// code: 'UpdatedAt',
|
|
1820
|
-
// name: 'Thời gian cập nhật',
|
|
1821
|
-
// nameEn: 'UpdatedAt',
|
|
1822
|
-
// dataType: 'DateTime',
|
|
1823
|
-
// allowShare: 1,
|
|
1824
|
-
// objClassCode: 'SciTaskProposal',
|
|
1825
|
-
// componentUI: 'TextView',
|
|
1826
|
-
// codeOnDb: 'updatedAt',
|
|
1827
|
-
// seqNum: 36,
|
|
1828
|
-
// required: 0,
|
|
1829
|
-
// allowStatistic: 0,
|
|
1830
|
-
// preventEdit: 0,
|
|
1831
|
-
// viewType: 'Detail',
|
|
1832
|
-
// id: '004a60a1-0b66-4d0b-acb0-eb76d57f49f1',
|
|
1833
|
-
// },
|
|
1834
|
-
// {
|
|
1835
|
-
// code: 'ApprovalNumber',
|
|
1836
|
-
// name: 'Số quyết định giao nhiệm vụ',
|
|
1837
|
-
// nameEn: 'ApprovalNumber',
|
|
1838
|
-
// dataType: 'string',
|
|
1839
|
-
// allowShare: 1,
|
|
1840
|
-
// objClassCode: 'SciTaskProposal',
|
|
1841
|
-
// componentUI: 'InputText',
|
|
1842
|
-
// codeOnDb: 'soQuyetDinhGiaoNhiemVu',
|
|
1843
|
-
// seqNum: 37,
|
|
1844
|
-
// required: 1,
|
|
1845
|
-
// allowStatistic: 0,
|
|
1846
|
-
// preventEdit: 0,
|
|
1847
|
-
// viewType: 'Detail',
|
|
1848
|
-
// id: '4000def5-2820-4d20-a624-9fc91834487e',
|
|
1849
|
-
// },
|
|
1850
|
-
];
|
|
1851
|
-
// Sample test mảng fields json đầu vào cho tham số @Input() fields.
|
|
1852
|
-
const sampleFields = [
|
|
1853
|
-
// new MultiImageUploadField({
|
|
1854
|
-
// key: 'images',
|
|
1855
|
-
// label: 'Hình ảnh kèm theo',
|
|
1856
|
-
// required: true,
|
|
1857
|
-
// maxSizeInMB: 62,
|
|
1858
|
-
// imageCardWidth: '208px'
|
|
1859
|
-
// }),
|
|
1860
|
-
// new SingleSelectField({
|
|
1861
|
-
// key: 'role',
|
|
1862
|
-
// label: 'Vai trò',
|
|
1863
|
-
// bindLabel: 'name',
|
|
1864
|
-
// bindValue: 'code',
|
|
1865
|
-
// required: true,
|
|
1866
|
-
// // outputAs: 'single',
|
|
1867
|
-
// suffixes: [
|
|
1868
|
-
// new Suffix({
|
|
1869
|
-
// key: 'test',
|
|
1870
|
-
// visibleOn: ['add', 'edit', 'view']
|
|
1871
|
-
// }),
|
|
1872
|
-
// new Suffix({
|
|
1873
|
-
// key: 'edit',
|
|
1874
|
-
// }),
|
|
1875
|
-
// ],
|
|
1876
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
1877
|
-
// const { name, code } = option;
|
|
1878
|
-
// if (name && code) return `${name} - ${code}`;
|
|
1879
|
-
// return '';
|
|
1880
|
-
// },
|
|
1881
|
-
// apiConfig: {
|
|
1882
|
-
// baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/system/Role',
|
|
1883
|
-
// },
|
|
1884
|
-
// options: [
|
|
1885
|
-
// { name: 'Quản trị viên', code: 'admin' },
|
|
1886
|
-
// { name: 'Người vận hành', code: 'operator' },
|
|
1887
|
-
// { name: 'Người dân', code: 'anonymous' },
|
|
1888
|
-
// ],
|
|
1889
|
-
// errorMessages: {
|
|
1890
|
-
// [CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
1891
|
-
// },
|
|
1892
|
-
// }),
|
|
1893
|
-
// new SingleSelectField({
|
|
1894
|
-
// key: 'province',
|
|
1895
|
-
// label: 'Tỉnh/Thành phố',
|
|
1896
|
-
// required: true,
|
|
1897
|
-
// outputAs: 'single',
|
|
1898
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
1899
|
-
// const { name, code } = option;
|
|
1900
|
-
// if (name && code) return `${name} - ${code}`;
|
|
1901
|
-
// return '';
|
|
1902
|
-
// },
|
|
1903
|
-
// apiConfig: {
|
|
1904
|
-
// baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/main/CommonCats/Province',
|
|
1905
|
-
// },
|
|
1906
|
-
// isParent: true,
|
|
1907
|
-
// }),
|
|
1908
|
-
// new SingleSelectField({
|
|
1909
|
-
// key: 'district',
|
|
1910
|
-
// label: 'Quận/Huyện',
|
|
1911
|
-
// required: false,
|
|
1912
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
1913
|
-
// const { name, code } = option;
|
|
1914
|
-
// if (name && code) return `${name} - ${code}`;
|
|
1915
|
-
// return '';
|
|
1916
|
-
// },
|
|
1917
|
-
// apiConfig: {
|
|
1918
|
-
// baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/main/CommonCats/District',
|
|
1919
|
-
// },
|
|
1920
|
-
// isParent: true,
|
|
1921
|
-
// dependsOn: [
|
|
1922
|
-
// {
|
|
1923
|
-
// fieldKey: 'province',
|
|
1924
|
-
// paramKey: 'provinceCode',
|
|
1925
|
-
// },
|
|
1926
|
-
// ],
|
|
1927
|
-
// }),
|
|
1928
|
-
// new SingleSelectField({
|
|
1929
|
-
// key: 'ward',
|
|
1930
|
-
// label: 'Phường/Xã',
|
|
1931
|
-
// required: false,
|
|
1932
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
1933
|
-
// const { name, code } = option;
|
|
1934
|
-
// if (name && code) return `${name} - ${code}`;
|
|
1935
|
-
// return '';
|
|
1936
|
-
// },
|
|
1937
|
-
// apiConfig: {
|
|
1938
|
-
// baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/main/CommonCats/Ward',
|
|
1939
|
-
// },
|
|
1940
|
-
// dependsOn: [
|
|
1941
|
-
// {
|
|
1942
|
-
// fieldKey: 'district',
|
|
1943
|
-
// paramKey: 'districtCode',
|
|
1944
|
-
// },
|
|
1945
|
-
// ],
|
|
1946
|
-
// }),
|
|
1947
|
-
// new MultiSelectField({
|
|
1948
|
-
// key: 'multiRole',
|
|
1949
|
-
// label: 'Nhiều vai trò',
|
|
1950
|
-
// bindLabel: 'name',
|
|
1951
|
-
// bindValue: 'code',
|
|
1952
|
-
// outputAs: 'single',
|
|
1953
|
-
// required: true,
|
|
1954
|
-
// isDisabled: false,
|
|
1955
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
1956
|
-
// const { name, code } = option;
|
|
1957
|
-
// if (name && code) return `${name} - ${code}`;
|
|
1958
|
-
// return '';
|
|
1959
|
-
// },
|
|
1960
|
-
// apiConfig: {
|
|
1961
|
-
// baseUrl: `${environment.GDCI_SYSTEM_URL}CommonCats/Position`,
|
|
1962
|
-
// },
|
|
1963
|
-
// errorMessages: {
|
|
1964
|
-
// [CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
1965
|
-
// },
|
|
1966
|
-
// suffixes: [
|
|
1967
|
-
// new Suffix({
|
|
1968
|
-
// key: 'test',
|
|
1969
|
-
// visibleOn: ['add', 'edit'],
|
|
1970
|
-
// }),
|
|
1971
|
-
// new Suffix({
|
|
1972
|
-
// key: 'edit',
|
|
1973
|
-
// }),
|
|
1974
|
-
// ],
|
|
1975
|
-
// }),
|
|
1976
|
-
// new InputTextField({
|
|
1977
|
-
// key: 'username',
|
|
1978
|
-
// label: 'Tên tài khoản',
|
|
1979
|
-
// value: '',
|
|
1980
|
-
// required: true,
|
|
1981
|
-
// minLength: 1,
|
|
1982
|
-
// isDisabled: false,
|
|
1983
|
-
// suffixes: [
|
|
1984
|
-
// new Suffix({
|
|
1985
|
-
// key: 'view',
|
|
1986
|
-
// visibleOn: ['view']
|
|
1987
|
-
// }),
|
|
1988
|
-
// new Suffix({
|
|
1989
|
-
// key: 'edit',
|
|
1990
|
-
// }),
|
|
1991
|
-
// ],
|
|
1992
|
-
// errorMessages: {
|
|
1993
|
-
// // [CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
1994
|
-
// // [CommonValidaterEnum.MinLength]: 'Tối thiểu nè hẹ hẹ',
|
|
1995
|
-
// },
|
|
1996
|
-
// pattern: 'alphaDashVN',
|
|
1997
|
-
// // grid: { xs: 12 },
|
|
1998
|
-
// // labelGrid: { xs: 8 },
|
|
1999
|
-
// // controlGrid: { xs: 16 }
|
|
2000
|
-
// }),
|
|
2001
|
-
// new InputPasswordField({
|
|
2002
|
-
// key: 'password',
|
|
2003
|
-
// label: 'Mật khẩu',
|
|
2004
|
-
// value: '',
|
|
2005
|
-
// required: false,
|
|
2006
|
-
// matchField: 'confirmPassword',
|
|
2007
|
-
// errorMessages: {
|
|
2008
|
-
// // [CustomValidaterEnum.MatchField]: 'Mật khẩu không khớp',
|
|
2009
|
-
// },
|
|
2010
|
-
// // grid: { xs: 12 },
|
|
2011
|
-
// // labelGrid: { xs: 8 },
|
|
2012
|
-
// // controlGrid: { xs: 16 }
|
|
2013
|
-
// }),
|
|
2014
|
-
// new InputPasswordField({
|
|
2015
|
-
// key: 'confirmPassword',
|
|
2016
|
-
// label: 'Xác nhận mật khẩu',
|
|
2017
|
-
// value: '',
|
|
2018
|
-
// required: false,
|
|
2019
|
-
// matchField: 'password',
|
|
2020
|
-
// errorMessages: {
|
|
2021
|
-
// // [CustomValidaterEnum.MatchField]: 'Mật khẩu không khớp',
|
|
2022
|
-
// },
|
|
2023
|
-
// // labelGrid: { xs: 16 },
|
|
2024
|
-
// // controlGrid: { xs: 8 }
|
|
2025
|
-
// }),
|
|
2026
|
-
// new TextareaField({
|
|
2027
|
-
// key: 'note',
|
|
2028
|
-
// label: 'Ghi chú',
|
|
2029
|
-
// value: '',
|
|
2030
|
-
// required: false,
|
|
2031
|
-
// minLength: 3,
|
|
2032
|
-
// maxLength: 128,
|
|
2033
|
-
// errorMessages: {
|
|
2034
|
-
// // [CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
2035
|
-
// // [CommonValidaterEnum.MinLength]: 'Tối thiểu nè hẹ hẹ',
|
|
2036
|
-
// },
|
|
2037
|
-
// // grid: { xs: 12 },
|
|
2038
|
-
// // labelGrid: { xs: 8 },
|
|
2039
|
-
// // controlGrid: { xs: 16 }
|
|
2040
|
-
// }),
|
|
2041
|
-
// new DatePickerField({
|
|
2042
|
-
// key: 'date',
|
|
2043
|
-
// label: 'Ngày',
|
|
2044
|
-
// required: true,
|
|
2045
|
-
// allowPastDates: false,
|
|
2046
|
-
// allowFutureDates: true,
|
|
2047
|
-
// isDisabled: true
|
|
2048
|
-
// }),
|
|
2049
|
-
// new DateRangeField({
|
|
2050
|
-
// key: 'expectedTime',
|
|
2051
|
-
// label: 'Thời gian dự kiến',
|
|
2052
|
-
// required: true,
|
|
2053
|
-
// outputAs: 'date',
|
|
2054
|
-
// allowPastDates: false,
|
|
2055
|
-
// allowFutureDates: true,
|
|
2056
|
-
// isDisabled: true
|
|
2057
|
-
// }),
|
|
2058
|
-
// new SingleFileUploadField({
|
|
2059
|
-
// key: 'attachment',
|
|
2060
|
-
// label: 'Văn bản đính kèm',
|
|
2061
|
-
// // required: true,
|
|
2062
|
-
// acceptedMimeTypes: [APPLICATION_TYPES.pdf, IMAGE_TYPES.tif, IMAGE_TYPES.tiff],
|
|
2063
|
-
// maxSizeInMB: 100000,
|
|
2064
|
-
// required: true,
|
|
2065
|
-
// apiFileConfig: {
|
|
2066
|
-
// uploadUrl: DEFAULT_UPLOAD_TIFF_URL,
|
|
2067
|
-
// deleteUrl: DEFAULT_DELETE_URL,
|
|
2068
|
-
// perMimeTypeConfig: {
|
|
2069
|
-
// 'image/tiff': {
|
|
2070
|
-
// uploadUrl: DEFAULT_UPLOAD_TIFF_URL,
|
|
2071
|
-
// buildFormData: (file, extraParams) => {
|
|
2072
|
-
// const fd = new FormData();
|
|
2073
|
-
// fd.append('File', file);
|
|
2074
|
-
// fd.append('SaveFile', String(true));
|
|
2075
|
-
// fd.append('BucketName', 'documents');
|
|
2076
|
-
// return fd;
|
|
2077
|
-
// },
|
|
2078
|
-
// },
|
|
2079
|
-
// },
|
|
2080
|
-
// },
|
|
2081
|
-
// }),
|
|
2082
|
-
new MultiFileUploadField({
|
|
2083
|
-
key: 'pictures',
|
|
2084
|
-
label: 'Những hình ảnh kèm theo',
|
|
2085
|
-
// required: true,
|
|
2086
|
-
// acceptedMimeTypes: [APPLICATION_TYPES.pdf],
|
|
2087
|
-
maxSizeInMB: 2,
|
|
2088
|
-
required: true,
|
|
2089
|
-
}),
|
|
2090
|
-
// new AvatarUploadField({
|
|
2091
|
-
// key: 'avatar',
|
|
2092
|
-
// label: 'Ảnh đại diện',
|
|
2093
|
-
// acceptedMimeTypes: [IMAGE_TYPES.jpeg, IMAGE_TYPES.jpg, IMAGE_TYPES.png],
|
|
2094
|
-
// required: false,
|
|
2095
|
-
// labelGrid: { xs: 4 },
|
|
2096
|
-
// controlGrid: { xs: 20 },
|
|
2097
|
-
// }),
|
|
2098
|
-
];
|
|
2099
|
-
const sampleFields2 = [
|
|
2100
|
-
new SingleSelectField({
|
|
2101
|
-
key: 'role',
|
|
2102
|
-
label: 'Vai trò',
|
|
2103
|
-
bindLabel: 'name',
|
|
2104
|
-
bindValue: 'code',
|
|
2105
|
-
required: false,
|
|
2106
|
-
outputAs: 'single',
|
|
2107
|
-
transformLabel: (option) => {
|
|
2108
|
-
const { name, code } = option;
|
|
2109
|
-
if (name && code)
|
|
2110
|
-
return `${name} - ${code}`;
|
|
2111
|
-
return '';
|
|
2112
|
-
},
|
|
2113
|
-
apiConfig: {
|
|
2114
|
-
baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/system/Role',
|
|
2115
|
-
},
|
|
2116
|
-
options: [
|
|
2117
|
-
{ name: 'Quản trị viên', code: 'admin' },
|
|
2118
|
-
{ name: 'Người vận hành', code: 'operator' },
|
|
2119
|
-
{ name: 'Người dân', code: 'anonymous' },
|
|
2120
|
-
],
|
|
2121
|
-
errorMessages: {
|
|
2122
|
-
[CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
2123
|
-
},
|
|
2124
|
-
}),
|
|
2125
|
-
new SingleSelectField({
|
|
2126
|
-
key: 'province',
|
|
2127
|
-
label: 'Tỉnh/Thành phố',
|
|
2128
|
-
required: true,
|
|
2129
|
-
transformLabel: (option) => {
|
|
2130
|
-
const { name, code } = option;
|
|
2131
|
-
if (name && code)
|
|
2132
|
-
return `${name} - ${code}`;
|
|
2133
|
-
return '';
|
|
2134
|
-
},
|
|
2135
|
-
apiConfig: {
|
|
2136
|
-
baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/main/CommonCats/Province',
|
|
2137
|
-
},
|
|
2138
|
-
isParent: true,
|
|
2139
|
-
}),
|
|
2140
|
-
new SingleSelectField({
|
|
2141
|
-
key: 'district',
|
|
2142
|
-
label: 'Quận/Huyện',
|
|
2143
|
-
required: false,
|
|
2144
|
-
transformLabel: (option) => {
|
|
2145
|
-
const { name, code } = option;
|
|
2146
|
-
if (name && code)
|
|
2147
|
-
return `${name} - ${code}`;
|
|
2148
|
-
return '';
|
|
2149
|
-
},
|
|
2150
|
-
apiConfig: {
|
|
2151
|
-
baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/main/CommonCats/District',
|
|
2152
|
-
},
|
|
2153
|
-
isParent: true,
|
|
2154
|
-
dependsOn: [
|
|
2155
|
-
{
|
|
2156
|
-
fieldKey: 'province',
|
|
2157
|
-
paramKey: 'provinceCode',
|
|
2158
|
-
},
|
|
2159
|
-
],
|
|
2160
|
-
}),
|
|
2161
|
-
new MultiSelectField({
|
|
2162
|
-
key: 'multiRole',
|
|
2163
|
-
label: 'Nhiều vai trò',
|
|
2164
|
-
bindLabel: 'name',
|
|
2165
|
-
bindValue: 'code',
|
|
2166
|
-
outputAs: 'single',
|
|
2167
|
-
required: true,
|
|
2168
|
-
transformLabel: (option) => {
|
|
2169
|
-
const { name, code } = option;
|
|
2170
|
-
if (name && code)
|
|
2171
|
-
return `${name} - ${code}`;
|
|
2172
|
-
return '';
|
|
2173
|
-
},
|
|
2174
|
-
apiConfig: {
|
|
2175
|
-
baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/system/Role',
|
|
2176
|
-
},
|
|
2177
|
-
errorMessages: {
|
|
2178
|
-
[CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
2179
|
-
},
|
|
2180
|
-
}),
|
|
2181
|
-
new InputTextField({
|
|
2182
|
-
key: 'username',
|
|
2183
|
-
label: 'Tên tài khoản',
|
|
2184
|
-
value: '',
|
|
2185
|
-
required: true,
|
|
2186
|
-
minLength: 1,
|
|
2187
|
-
errorMessages: {
|
|
2188
|
-
// [CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
2189
|
-
// [CommonValidaterEnum.MinLength]: 'Tối thiểu nè hẹ hẹ',
|
|
2190
|
-
},
|
|
2191
|
-
pattern: 'alphaDashVN',
|
|
2192
|
-
// grid: { xs: 12 },
|
|
2193
|
-
// labelGrid: { xs: 8 },
|
|
2194
|
-
// controlGrid: { xs: 16 }
|
|
2195
|
-
}),
|
|
2196
|
-
new TextareaField({
|
|
2197
|
-
key: 'note',
|
|
2198
|
-
label: 'Ghi chú',
|
|
2199
|
-
value: '',
|
|
2200
|
-
required: false,
|
|
2201
|
-
minLength: 3,
|
|
2202
|
-
maxLength: 128,
|
|
2203
|
-
errorMessages: {
|
|
2204
|
-
// [CommonValidaterEnum.Required]: 'Bắt buộc nè hẹ hẹ',
|
|
2205
|
-
// [CommonValidaterEnum.MinLength]: 'Tối thiểu nè hẹ hẹ',
|
|
2206
|
-
},
|
|
2207
|
-
// grid: { xs: 12 },
|
|
2208
|
-
// labelGrid: { xs: 8 },
|
|
2209
|
-
// controlGrid: { xs: 16 }
|
|
2210
|
-
}),
|
|
2211
|
-
new DatePickerField({
|
|
2212
|
-
key: 'date',
|
|
2213
|
-
label: 'Ngày',
|
|
2214
|
-
required: true,
|
|
2215
|
-
allowPastDates: false,
|
|
2216
|
-
allowFutureDates: true,
|
|
2217
|
-
}),
|
|
2218
|
-
new DateRangeField({
|
|
2219
|
-
key: 'expectedTime',
|
|
2220
|
-
label: 'Thời gian dự kiến',
|
|
2221
|
-
required: true,
|
|
2222
|
-
outputAs: 'string',
|
|
2223
|
-
allowPastDates: false,
|
|
2224
|
-
allowFutureDates: true,
|
|
2225
|
-
}),
|
|
2226
|
-
new SingleFileUploadField({
|
|
2227
|
-
key: 'attachment',
|
|
2228
|
-
label: 'Văn bản đính kèm',
|
|
2229
|
-
// required: true,
|
|
2230
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf],
|
|
2231
|
-
maxSizeInMB: 2,
|
|
2232
|
-
required: true,
|
|
2233
|
-
}),
|
|
2234
|
-
new MultiFileUploadField({
|
|
2235
|
-
key: 'documents',
|
|
2236
|
-
label: 'Những hồ sơ đính kèm',
|
|
2237
|
-
// required: true,
|
|
2238
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf],
|
|
2239
|
-
maxSizeInMB: 2,
|
|
2240
|
-
required: false,
|
|
2241
|
-
}),
|
|
2242
|
-
];
|
|
2243
|
-
// Sample test model data 1.
|
|
2244
|
-
const sampleModel = {
|
|
2245
|
-
province: { code: 35, name: 'Hà Name' },
|
|
2246
|
-
username: 'ldquocccccccccc',
|
|
2247
|
-
role: {
|
|
2248
|
-
name: 'Quản trị viên',
|
|
2249
|
-
code: 'ADMIN',
|
|
2250
|
-
},
|
|
2251
|
-
// role: 'ADMIN',
|
|
2252
|
-
multiRole: [
|
|
2253
|
-
{
|
|
2254
|
-
name: 'Quản trị',
|
|
2255
|
-
code: 'ADMIN',
|
|
2256
|
-
},
|
|
2257
|
-
{
|
|
2258
|
-
name: 'Vận hành',
|
|
2259
|
-
code: 'OPERATOR',
|
|
2260
|
-
},
|
|
2261
|
-
],
|
|
2262
|
-
note: 'Hãy nhớ những điều này:\n-Tôi là tôi.\n-Ai là tôi?\n-Sẽ ra sao nếu tôi tiến lên.\n-Hẹ hẹ.',
|
|
2263
|
-
// note: 'hè hè\nhihihi\nfefefef',
|
|
2264
|
-
// date: '08-01-2025',
|
|
2265
|
-
// date: '30-08-2025',
|
|
2266
|
-
// date: '2025-08-28',
|
|
2267
|
-
date: new Date('2025-08-28'),
|
|
2268
|
-
expectedTime: ['2025-08-28', new Date('2025-08-30')],
|
|
2269
|
-
// attachment: [{
|
|
2270
|
-
// id: 'd3f549e0-a036-4b16-aa67-30444d23d795',
|
|
2271
|
-
// name: 'dummy.pdf',
|
|
2272
|
-
// path: '/dummy.pdf',
|
|
2273
|
-
// type: APPLICATION_TYPES.pdf,
|
|
2274
|
-
// size: 1800
|
|
2275
|
-
// }],
|
|
2276
|
-
// attachment:
|
|
2277
|
-
// '[{\"id\":\"1952e9ea-61c3-4b8d-abe2-4df238804458\",\"name\":\"Ho_Cubic_30L_Tong_Hop.pdf\",\"contentType\":\"application/pdf\",\"size\":28407,\"bucket\":\"caosuvn\",\"isDir\":false,\"createdAt\":\"2025-08-08\",\"updatedAt\":\"2025-08-08\",\"path\":\"/Ho_Cubic_30L_Tong_Hop.pdf\",\"isPublic\":true}]',
|
|
2278
|
-
attachment: '[{"id":"8fe5f9b1-18aa-415d-a33e-b4a843b7cab1","name":"image.png","contentType":"image/png","size":77239,"bucket":"public","isDir":false,"createdAt":"2025-09-04","updatedAt":"2025-09-04","path":"/image.png","isPublic":true},{"id":"b8963fb1-e842-42ac-9fc4-1733a3b65dc5","name":"image (1).png","contentType":"image/png","size":79610,"bucket":"public","isDir":false,"createdAt":"2025-09-04","updatedAt":"2025-09-04","path":"/image (1).png","isPublic":true}]',
|
|
2279
|
-
pictures: '[{\"id\":\"1952e9ea-61c3-4b8d-abe2-4df238803338\",\"name\":\"Ho_Cubic_30L_Tong_Hop.pdf\",\"contentType\":\"application/pdf\",\"size\":28407,\"bucket\":\"caosuvn\",\"isDir\":false,\"createdAt\":\"2025-08-08\",\"updatedAt\":\"2025-08-08\",\"path\":\"/Ho_Cubic_30L_Tong_Hop.pdf\",\"isPublic\":true},{\"id\":\"1952e9ea-61c3-4b8d-abe2-4df238804458\",\"name\":\"Ho_Cubic_35L_Tong_Hop.pdf\",\"contentType\":\"application/pdf\",\"size\":28407,\"bucket\":\"caosuvn\",\"isDir\":false,\"createdAt\":\"2025-08-08\",\"updatedAt\":\"2025-08-08\",\"path\":\"/Ho_Cubic_35L_Tong_Hop.pdf\",\"isPublic\":true}]',
|
|
2280
|
-
// pictures: '[{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800}]'
|
|
2281
|
-
};
|
|
2282
|
-
// Sample test model data 2.
|
|
2283
|
-
const sampleModel2 = {
|
|
2284
|
-
images: '[{"id":"56062785-a619-41f7-8b64-ed648e57f934","name":"vrg_file_5141201818_104814_39-11_anh_bai_chinh_ml41ozadgy_ug51tjdtjb.jpg","contentType":"image/jpeg","size":176638,"bucket":"public","isDir":false,"createdAt":"2025-09-08","updatedAt":"2025-09-08","path":"/vrg_file_5141201818_104814_39-11_anh_bai_chinh_ml41ozadgy_ug51tjdtjb.jpg","isPublic":true},{"id":"79c62863-8241-4785-ac52-162a1d5347a6","name":"vrg_file_5409423468_coral-reef-fish-reef-fish-aquarium-wallpaper-preview.jpg","contentType":"image/jpeg","size":128651,"bucket":"public","isDir":false,"createdAt":"2025-09-08","updatedAt":"2025-09-08","path":"/vrg_file_5409423468_coral-reef-fish-reef-fish-aquarium-wallpaper-preview.jpg","isPublic":true},{"id":"973d2861-fe87-481f-9dc7-aeb7991516de","name":"vrg_file_8710772406_nemo_fi.jpg","contentType":"image/jpeg","size":19134,"bucket":"public","isDir":false,"createdAt":"2025-09-08","updatedAt":"2025-09-08","path":"/vrg_file_8710772406_nemo_fi.jpg","isPublic":true}]',
|
|
2285
|
-
avatar: '[{"id":"55283e86-6d16-4f12-9d9b-fce1b928eae5","name":"104814_39-11_anh_bai_chinh_ml41ozadgy_ug51tjdtjb.jpg","contentType":"image/jpeg","size":176638,"bucket":"public","isDir":false,"createdAt":"2025-08-19","updatedAt":"2025-08-19","path":"/104814_39-11_anh_bai_chinh_ml41ozadgy_ug51tjdtjb.jpg","isPublic":true}]',
|
|
2286
|
-
province: { code: 35, name: 'Hà Name' },
|
|
2287
|
-
district: { code: 349, name: 'Duy tiên' },
|
|
2288
|
-
username: 'dddddddddddddddddddddddd',
|
|
2289
|
-
role: {
|
|
2290
|
-
name: 'Vận hành',
|
|
2291
|
-
code: 'OPERATOR',
|
|
2292
|
-
},
|
|
2293
|
-
// role: 'ADMIN',
|
|
2294
|
-
multiRole: [
|
|
2295
|
-
{
|
|
2296
|
-
name: 'Quản trị',
|
|
2297
|
-
code: 'ADMIN',
|
|
2298
|
-
},
|
|
2299
|
-
{
|
|
2300
|
-
name: 'Vận hành',
|
|
2301
|
-
code: 'OPERATOR',
|
|
2302
|
-
},
|
|
2303
|
-
{
|
|
2304
|
-
name: 'Người dân',
|
|
2305
|
-
code: 'ANONYMOUS',
|
|
2306
|
-
},
|
|
2307
|
-
],
|
|
2308
|
-
note: 'CCCCC',
|
|
2309
|
-
// note: 'hè hè\nhihihi\nfefefef',
|
|
2310
|
-
// date: '08-01-2025',
|
|
2311
|
-
// date: '30-08-2025',
|
|
2312
|
-
// date: '2025-08-28',
|
|
2313
|
-
date: new Date('2025-08-30'),
|
|
2314
|
-
expectedTime: ['2025-08-01', new Date('2025-08-02')],
|
|
2315
|
-
// attachment: [{
|
|
2316
|
-
// id: 'd3f549e0-a036-4b16-aa67-30444d23d795',
|
|
2317
|
-
// name: 'dummy.pdf',
|
|
2318
|
-
// path: '/dummy.pdf',
|
|
2319
|
-
// type: APPLICATION_TYPES.pdf,
|
|
2320
|
-
// size: 1800
|
|
2321
|
-
// }],
|
|
2322
|
-
attachment: '[{"id":"8fe5f9b1-18aa-415d-a33e-b4a843b7cab1","name":"image.png","contentType":"image/png","size":77239,"bucket":"public","isDir":false,"createdAt":"2025-09-04","updatedAt":"2025-09-04","path":"/image.png","isPublic":true},{"id":"b8963fb1-e842-42ac-9fc4-1733a3b65dc5","name":"image (1).png","contentType":"image/png","size":79610,"bucket":"public","isDir":false,"createdAt":"2025-09-04","updatedAt":"2025-09-04","path":"/image (1).png","isPublic":true}]',
|
|
2323
|
-
// attachment:
|
|
2324
|
-
// '[{\"id\":\"1952e9ea-61c3-4b8d-abe2-4df238804458\",\"name\":\"dumpleeeee.pdf\",\"contentType\":\"application/pdf\",\"size\":28407,\"bucket\":\"caosuvn\",\"isDir\":false,\"createdAt\":\"2025-08-08\",\"updatedAt\":\"2025-08-08\",\"path\":\"/Ho_Cubic_30L_Tong_Hop.pdf\",\"isPublic\":true}]',
|
|
2325
|
-
pictures: '[{\"id\":\"1952e9ea-61c3-4b8d-abe2-4df238803338\",\"name\":\"Ho_Cubic_45L_Tong_Hop.pdf\",\"contentType\":\"application/pdf\",\"size\":28407,\"bucket\":\"caosuvn\",\"isDir\":false,\"createdAt\":\"2025-08-08\",\"updatedAt\":\"2025-08-08\",\"path\":\"/Ho_Cubic_30L_Tong_Hop.pdf\",\"isPublic\":true},{\"id\":\"1952e9ea-61c3-4b8d-abe2-4df238804458\",\"name\":\"Ho_Cubic_60L_Tong_Hop.pdf\",\"contentType\":\"application/pdf\",\"size\":28407,\"bucket\":\"caosuvn\",\"isDir\":false,\"createdAt\":\"2025-08-08\",\"updatedAt\":\"2025-08-08\",\"path\":\"/Ho_Cubic_35L_Tong_Hop.pdf\",\"isPublic\":true}]',
|
|
2326
|
-
// pictures: '[{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800},{"id":"d3f549e0-a036-4b16-aa67-30444d23d795","name":"dummy.pdf","path":"/dummy.pdf","contentType":"application/pdf","size":1800}]'
|
|
2327
|
-
};
|
|
2328
|
-
//#region Multi Form
|
|
2329
|
-
const sampleMultiFormFieldsA = [
|
|
2330
|
-
// ======== Input Text ========
|
|
2331
|
-
new InputTextField({
|
|
2332
|
-
key: 'usernameA',
|
|
2333
|
-
label: 'Tên tài khoản A',
|
|
2334
|
-
value: '',
|
|
2335
|
-
required: true,
|
|
2336
|
-
minLength: 2,
|
|
2337
|
-
isDisabled: false,
|
|
2338
|
-
errorMessages: {
|
|
2339
|
-
[CommonValidaterEnum.Required]: 'Bắt buộc nha hẹ hẹ',
|
|
2340
|
-
[CommonValidaterEnum.MinLength]: 'Tối thiểu là 2 nha hẹ hẹ',
|
|
2341
|
-
},
|
|
2342
|
-
pattern: 'alphaDashVN',
|
|
2343
|
-
// grid: { xs: 12 },
|
|
2344
|
-
// labelGrid: { xs: 8 },
|
|
2345
|
-
// controlGrid: { xs: 16 },
|
|
2346
|
-
// suffixes: [
|
|
2347
|
-
// new Suffix({
|
|
2348
|
-
// key: 'view',
|
|
2349
|
-
// visibleOn: ['view'],
|
|
2350
|
-
// }),
|
|
2351
|
-
// new Suffix({
|
|
2352
|
-
// key: 'edit',
|
|
2353
|
-
// }),
|
|
2354
|
-
// ],
|
|
2355
|
-
}),
|
|
2356
|
-
// ======== Input Text ========
|
|
2357
|
-
// ======== Input Password ========
|
|
2358
|
-
// new InputPasswordField({
|
|
2359
|
-
// key: 'passwordA',
|
|
2360
|
-
// label: 'Mật khẩu',
|
|
2361
|
-
// // value: '',
|
|
2362
|
-
// required: false,
|
|
2363
|
-
// matchField: 'confirmPasswordA',
|
|
2364
|
-
// errorMessages: {
|
|
2365
|
-
// [CustomValidaterEnum.MatchField]: 'Mật khẩu không khớp',
|
|
2366
|
-
// },
|
|
2367
|
-
// // grid: { xs: 12 },
|
|
2368
|
-
// // labelGrid: { xs: 8 },
|
|
2369
|
-
// // controlGrid: { xs: 16 }
|
|
2370
|
-
// }),
|
|
2371
|
-
// new InputPasswordField({
|
|
2372
|
-
// key: 'confirmPasswordA',
|
|
2373
|
-
// label: 'Xác nhận mật khẩu',
|
|
2374
|
-
// // value: '',
|
|
2375
|
-
// required: false,
|
|
2376
|
-
// matchField: 'passwordA',
|
|
2377
|
-
// errorMessages: {
|
|
2378
|
-
// [CustomValidaterEnum.MatchField]: 'Xác nhận mật khẩu không khớp',
|
|
2379
|
-
// },
|
|
2380
|
-
// // labelGrid: { xs: 16 },
|
|
2381
|
-
// // controlGrid: { xs: 8 }
|
|
2382
|
-
// }),
|
|
2383
|
-
// ======== Input Password ========
|
|
2384
|
-
// ======== Textarea ========
|
|
2385
|
-
// new TextareaField({
|
|
2386
|
-
// key: 'noteA',
|
|
2387
|
-
// label: 'Ghi chú A',
|
|
2388
|
-
// value: '',
|
|
2389
|
-
// required: false,
|
|
2390
|
-
// minLength: 3,
|
|
2391
|
-
// maxLength: 128,
|
|
2392
|
-
// // grid: { xs: 12 },
|
|
2393
|
-
// // labelGrid: { xs: 8 },
|
|
2394
|
-
// // controlGrid: { xs: 16 }
|
|
2395
|
-
// }),
|
|
2396
|
-
// ======== Textarea ========
|
|
2397
|
-
// ======== DATE PICKER ========
|
|
2398
|
-
// new DatePickerField({
|
|
2399
|
-
// key: 'dateA',
|
|
2400
|
-
// label: 'Ngày A',
|
|
2401
|
-
// required: true,
|
|
2402
|
-
// allowPastDates: false,
|
|
2403
|
-
// allowFutureDates: true,
|
|
2404
|
-
// isDisabled: false,
|
|
2405
|
-
// }),
|
|
2406
|
-
// ======== DATE PICKER ========
|
|
2407
|
-
// ======== DATE RANGE ========
|
|
2408
|
-
// new DateRangeField({
|
|
2409
|
-
// key: 'expectedTimeA',
|
|
2410
|
-
// label: 'Thời gian dự kiến A',
|
|
2411
|
-
// required: true,
|
|
2412
|
-
// outputAs: 'date-array',
|
|
2413
|
-
// allowPastDates: false,
|
|
2414
|
-
// allowFutureDates: true,
|
|
2415
|
-
// isDisabled: false,
|
|
2416
|
-
// }),
|
|
2417
|
-
// ======== DATE RANGE ========
|
|
2418
|
-
// ======== SINGLE SELECT ========
|
|
2419
|
-
// new SingleSelectField({
|
|
2420
|
-
// key: 'roleA',
|
|
2421
|
-
// label: 'Vai trò A',
|
|
2422
|
-
// bindLabel: 'name',
|
|
2423
|
-
// bindValue: 'code',
|
|
2424
|
-
// required: true,
|
|
2425
|
-
// // outputAs: 'single',
|
|
2426
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
2427
|
-
// const { name, code } = option;
|
|
2428
|
-
// if (name && code) return `${name} - ${code}`;
|
|
2429
|
-
// return '';
|
|
2430
|
-
// },
|
|
2431
|
-
// apiConfig: {
|
|
2432
|
-
// baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/system/Role',
|
|
2433
|
-
// },
|
|
2434
|
-
// options: [
|
|
2435
|
-
// { name: 'Quản trị viên', code: 'admin' },
|
|
2436
|
-
// { name: 'Người vận hành', code: 'operator' },
|
|
2437
|
-
// { name: 'Người dân', code: 'anonymous' },
|
|
2438
|
-
// ],
|
|
2439
|
-
// }),
|
|
2440
|
-
// ======== SINGLE SELECT ========
|
|
2441
|
-
// ======== MULTI SELECT ========
|
|
2442
|
-
// new MultiSelectField({
|
|
2443
|
-
// key: 'multiRoleA',
|
|
2444
|
-
// label: 'Nhiều vai trò A',
|
|
2445
|
-
// bindLabel: 'name',
|
|
2446
|
-
// bindValue: 'code',
|
|
2447
|
-
// outputAs: 'single',
|
|
2448
|
-
// required: true,
|
|
2449
|
-
// isDisabled: false,
|
|
2450
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
2451
|
-
// const { name, code } = option;
|
|
2452
|
-
// if (name && code) return `${name} - ${code}`;
|
|
2453
|
-
// return '';
|
|
2454
|
-
// },
|
|
2455
|
-
// apiConfig: {
|
|
2456
|
-
// baseUrl: `${environment.GDCI_SYSTEM_URL}CommonCats/Position`,
|
|
2457
|
-
// },
|
|
2458
|
-
// }),
|
|
2459
|
-
// ======== MULTI SELECT ========
|
|
2460
|
-
// ======== SINGLE FILE UPLOAD ========
|
|
2461
|
-
new SingleFileUploadField({
|
|
2462
|
-
key: 'attachmentA',
|
|
2463
|
-
label: 'Văn bản đính kèm A',
|
|
2464
|
-
// required: true,
|
|
2465
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf, IMAGE_TYPES.tif, IMAGE_TYPES.tiff],
|
|
2466
|
-
maxSizeInMB: 100000,
|
|
2467
|
-
required: true,
|
|
2468
|
-
}),
|
|
2469
|
-
// ======== SINGLE FILE UPLOAD ========
|
|
2470
|
-
// ======== MULTI FILE UPLOAD ========
|
|
2471
|
-
new MultiFileUploadField({
|
|
2472
|
-
key: 'picturesA',
|
|
2473
|
-
label: 'Những hình ảnh kèm theo A',
|
|
2474
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf],
|
|
2475
|
-
maxSizeInMB: 2,
|
|
2476
|
-
required: false,
|
|
2477
|
-
}),
|
|
2478
|
-
// ======== MULTI FILE UPLOAD ========
|
|
2479
|
-
// ======== MULTI IMAGE UPLOAD ========
|
|
2480
|
-
new MultiImageUploadField({
|
|
2481
|
-
key: 'imagesA',
|
|
2482
|
-
label: 'Hình ảnh kèm theo A',
|
|
2483
|
-
required: true,
|
|
2484
|
-
maxSizeInMB: 62,
|
|
2485
|
-
imageCardWidth: '208px',
|
|
2486
|
-
}),
|
|
2487
|
-
// ======== MULTI IMAGE UPLOAD ========
|
|
2488
|
-
// ======== AVATAR UPLOAD ========
|
|
2489
|
-
new AvatarUploadField({
|
|
2490
|
-
key: 'avatarA',
|
|
2491
|
-
label: 'Ảnh đại diện A',
|
|
2492
|
-
acceptedMimeTypes: [IMAGE_TYPES.jpeg, IMAGE_TYPES.jpg, IMAGE_TYPES.png],
|
|
2493
|
-
required: false,
|
|
2494
|
-
labelGrid: { xs: 4 },
|
|
2495
|
-
controlGrid: { xs: 20 },
|
|
2496
|
-
}),
|
|
2497
|
-
// ======== AVATAR UPLOAD ========
|
|
2498
|
-
];
|
|
2499
|
-
const sampleMultiFormFieldsB = [
|
|
2500
|
-
// ======== Input Text ========
|
|
2501
|
-
new InputTextField({
|
|
2502
|
-
key: 'usernameB',
|
|
2503
|
-
label: 'Tên tài khoản B',
|
|
2504
|
-
value: '',
|
|
2505
|
-
required: true,
|
|
2506
|
-
minLength: 2,
|
|
2507
|
-
isDisabled: false,
|
|
2508
|
-
errorMessages: {
|
|
2509
|
-
[CommonValidaterEnum.Required]: 'Bắt buộc nha hẹ hẹ',
|
|
2510
|
-
[CommonValidaterEnum.MinLength]: 'Tối thiểu là 2 nha hẹ hẹ',
|
|
2511
|
-
},
|
|
2512
|
-
pattern: 'alphaDashVN',
|
|
2513
|
-
// grid: { xs: 12 },
|
|
2514
|
-
// labelGrid: { xs: 8 },
|
|
2515
|
-
// controlGrid: { xs: 16 },
|
|
2516
|
-
// suffixes: [
|
|
2517
|
-
// new Suffix({
|
|
2518
|
-
// key: 'view',
|
|
2519
|
-
// visibleOn: ['view'],
|
|
2520
|
-
// }),
|
|
2521
|
-
// new Suffix({
|
|
2522
|
-
// key: 'edit',
|
|
2523
|
-
// }),
|
|
2524
|
-
// ],
|
|
2525
|
-
}),
|
|
2526
|
-
// ======== Input Text ========
|
|
2527
|
-
// ======== Input Password ========
|
|
2528
|
-
// new InputPasswordField({
|
|
2529
|
-
// key: 'passwordB',
|
|
2530
|
-
// label: 'Mật khẩu',
|
|
2531
|
-
// // value: '',
|
|
2532
|
-
// required: false,
|
|
2533
|
-
// matchField: 'confirmPasswordB',
|
|
2534
|
-
// errorMessages: {
|
|
2535
|
-
// [CustomValidaterEnum.MatchField]: 'Mật khẩu không khớp',
|
|
2536
|
-
// },
|
|
2537
|
-
// // grid: { xs: 12 },
|
|
2538
|
-
// // labelGrid: { xs: 8 },
|
|
2539
|
-
// // controlGrid: { xs: 16 }
|
|
2540
|
-
// }),
|
|
2541
|
-
// new InputPasswordField({
|
|
2542
|
-
// key: 'confirmPasswordB',
|
|
2543
|
-
// label: 'Xác nhận mật khẩu',
|
|
2544
|
-
// // value: '',
|
|
2545
|
-
// required: false,
|
|
2546
|
-
// matchField: 'passwordB',
|
|
2547
|
-
// errorMessages: {
|
|
2548
|
-
// [CustomValidaterEnum.MatchField]: 'Xác nhận mật khẩu không khớp',
|
|
2549
|
-
// },
|
|
2550
|
-
// // labelGrid: { xs: 16 },
|
|
2551
|
-
// // controlGrid: { xs: 8 }
|
|
2552
|
-
// }),
|
|
2553
|
-
// ======== Input Password ========
|
|
2554
|
-
// ======== Textarea ========
|
|
2555
|
-
// new TextareaField({
|
|
2556
|
-
// key: 'noteB',
|
|
2557
|
-
// label: 'Ghi chú B',
|
|
2558
|
-
// value: '',
|
|
2559
|
-
// required: false,
|
|
2560
|
-
// minLength: 3,
|
|
2561
|
-
// maxLength: 128,
|
|
2562
|
-
// // grid: { xs: 12 },
|
|
2563
|
-
// // labelGrid: { xs: 8 },
|
|
2564
|
-
// // controlGrid: { xs: 16 }
|
|
2565
|
-
// }),
|
|
2566
|
-
// ======== Textarea ========
|
|
2567
|
-
// ======== DATE PICKER ========
|
|
2568
|
-
// new DatePickerField({
|
|
2569
|
-
// key: 'dateB',
|
|
2570
|
-
// label: 'Ngày B',
|
|
2571
|
-
// required: true,
|
|
2572
|
-
// allowPastDates: false,
|
|
2573
|
-
// allowFutureDates: true,
|
|
2574
|
-
// isDisabled: false,
|
|
2575
|
-
// }),
|
|
2576
|
-
// ======== DATE PICKER ========
|
|
2577
|
-
// ======== DATE RANGE ========
|
|
2578
|
-
// new DateRangeField({
|
|
2579
|
-
// key: 'expectedTimeB',
|
|
2580
|
-
// label: 'Thời gian dự kiến B',
|
|
2581
|
-
// required: true,
|
|
2582
|
-
// outputAs: 'date-array',
|
|
2583
|
-
// allowPastDates: false,
|
|
2584
|
-
// allowFutureDates: true,
|
|
2585
|
-
// isDisabled: false,
|
|
2586
|
-
// }),
|
|
2587
|
-
// ======== DATE RANGE ========
|
|
2588
|
-
// ======== SINGLE SELECT ========
|
|
2589
|
-
// new SingleSelectField({
|
|
2590
|
-
// key: 'roleB',
|
|
2591
|
-
// label: 'Vai trò B',
|
|
2592
|
-
// bindLabel: 'name',
|
|
2593
|
-
// bindValue: 'code',
|
|
2594
|
-
// required: true,
|
|
2595
|
-
// // outputAs: 'single',
|
|
2596
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
2597
|
-
// const { name, code } = option;
|
|
2598
|
-
// if (name && code) return `${name} - ${code}`;
|
|
2599
|
-
// return '';
|
|
2600
|
-
// },
|
|
2601
|
-
// apiConfig: {
|
|
2602
|
-
// baseUrl: 'https://quyhoachxaydung.hanam.gov.vn/gdci/system/Role',
|
|
2603
|
-
// },
|
|
2604
|
-
// options: [
|
|
2605
|
-
// { name: 'Quản trị viên', code: 'admin' },
|
|
2606
|
-
// { name: 'Người vận hành', code: 'operator' },
|
|
2607
|
-
// { name: 'Người dân', code: 'anonymous' },
|
|
2608
|
-
// ],
|
|
2609
|
-
// }),
|
|
2610
|
-
// ======== SINGLE SELECT ========
|
|
2611
|
-
// ======== MULTI SELECT ========
|
|
2612
|
-
// new MultiSelectField({
|
|
2613
|
-
// key: 'multiRoleB',
|
|
2614
|
-
// label: 'Nhiều vai trò B',
|
|
2615
|
-
// bindLabel: 'name',
|
|
2616
|
-
// bindValue: 'code',
|
|
2617
|
-
// outputAs: 'single',
|
|
2618
|
-
// required: true,
|
|
2619
|
-
// isDisabled: false,
|
|
2620
|
-
// transformLabel: (option: Record<string, unknown>) => {
|
|
2621
|
-
// const { name, code } = option;
|
|
2622
|
-
// if (name && code) return `${name} - ${code}`;
|
|
2623
|
-
// return '';
|
|
2624
|
-
// },
|
|
2625
|
-
// apiConfig: {
|
|
2626
|
-
// baseUrl: `${environment.GDCI_SYSTEM_URL}CommonCats/Position`,
|
|
2627
|
-
// },
|
|
2628
|
-
// }),
|
|
2629
|
-
// ======== MULTI SELECT ========
|
|
2630
|
-
// ======== SINGLE FILE UPLOAD ========
|
|
2631
|
-
new SingleFileUploadField({
|
|
2632
|
-
key: 'attachmentB',
|
|
2633
|
-
label: 'Văn bản đính kèm B',
|
|
2634
|
-
// required: true,
|
|
2635
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf, IMAGE_TYPES.tif, IMAGE_TYPES.tiff],
|
|
2636
|
-
maxSizeInMB: 100000,
|
|
2637
|
-
required: true,
|
|
2638
|
-
}),
|
|
2639
|
-
// ======== SINGLE FILE UPLOAD ========
|
|
2640
|
-
// ======== MULTI FILE UPLOAD ========
|
|
2641
|
-
new MultiFileUploadField({
|
|
2642
|
-
key: 'picturesB',
|
|
2643
|
-
label: 'Những hình ảnh kèm theo B',
|
|
2644
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf],
|
|
2645
|
-
maxSizeInMB: 2,
|
|
2646
|
-
required: false,
|
|
2647
|
-
}),
|
|
2648
|
-
// ======== MULTI FILE UPLOAD ========
|
|
2649
|
-
// ======== MULTI IMAGE UPLOAD ========
|
|
2650
|
-
new MultiImageUploadField({
|
|
2651
|
-
key: 'imagesB',
|
|
2652
|
-
label: 'Hình ảnh kèm theo B',
|
|
2653
|
-
required: true,
|
|
2654
|
-
maxSizeInMB: 62,
|
|
2655
|
-
imageCardWidth: '208px',
|
|
2656
|
-
}),
|
|
2657
|
-
// ======== MULTI IMAGE UPLOAD ========
|
|
2658
|
-
// ======== AVATAR UPLOAD ========
|
|
2659
|
-
new AvatarUploadField({
|
|
2660
|
-
key: 'avatarB',
|
|
2661
|
-
label: 'Ảnh đại diện B',
|
|
2662
|
-
acceptedMimeTypes: [IMAGE_TYPES.jpeg, IMAGE_TYPES.jpg, IMAGE_TYPES.png],
|
|
2663
|
-
required: false,
|
|
2664
|
-
labelGrid: { xs: 4 },
|
|
2665
|
-
controlGrid: { xs: 20 },
|
|
2666
|
-
}),
|
|
2667
|
-
// ======== AVATAR UPLOAD ========
|
|
2668
|
-
];
|
|
2669
|
-
const sampleMultiFormModelA = {
|
|
2670
|
-
usernameA: 'Lương Văn Lào',
|
|
2671
|
-
};
|
|
2672
|
-
const sampleMultiFormModelB = {
|
|
2673
|
-
usernameB: 'Đào Mai Tôn',
|
|
2674
|
-
};
|
|
2675
|
-
//#endregion Multi Form
|
|
2676
|
-
//#region MultiSelect depend on field has isParent
|
|
2677
|
-
const sampleMultiSelectDependOnParent = [
|
|
2678
|
-
new MultiFileUploadField({
|
|
2679
|
-
key: 'picturesB',
|
|
2680
|
-
label: 'Những hình ảnh kèm theo B',
|
|
2681
|
-
acceptedMimeTypes: [APPLICATION_TYPES.pdf, IMAGE_TYPES.png, IMAGE_TYPES.jpg],
|
|
2682
|
-
maxSizeInMB: 1,
|
|
2683
|
-
required: true,
|
|
2684
|
-
}),
|
|
2685
|
-
new SingleSelectField({
|
|
2686
|
-
key: 'province',
|
|
2687
|
-
label: 'Tỉnh/Thành phố',
|
|
2688
|
-
required: true,
|
|
2689
|
-
outputAs: 'single',
|
|
2690
|
-
transformLabel: (option) => {
|
|
2691
|
-
const { name, code } = option;
|
|
2692
|
-
if (name && code)
|
|
2693
|
-
return `${name} - ${code}`;
|
|
2694
|
-
return '';
|
|
2695
|
-
},
|
|
2696
|
-
apiConfig: {
|
|
2697
|
-
baseUrl: `http://mock-api.com/CommonCats/Province`,
|
|
2698
|
-
},
|
|
2699
|
-
isParent: true,
|
|
2700
|
-
}),
|
|
2701
|
-
new MultiSelectField({
|
|
2702
|
-
key: 'ward',
|
|
2703
|
-
label: 'Phường/Xã',
|
|
2704
|
-
required: false,
|
|
2705
|
-
transformLabel: (option) => {
|
|
2706
|
-
const { name, code } = option;
|
|
2707
|
-
if (name && code)
|
|
2708
|
-
return `${name} - ${code}`;
|
|
2709
|
-
return '';
|
|
2710
|
-
},
|
|
2711
|
-
apiConfig: {
|
|
2712
|
-
baseUrl: `http://mock-api.com/CommonCats/Ward`,
|
|
2713
|
-
},
|
|
2714
|
-
dependsOn: [
|
|
2715
|
-
{
|
|
2716
|
-
fieldKey: 'province',
|
|
2717
|
-
paramKey: 'provinceCode',
|
|
2718
|
-
},
|
|
2719
|
-
],
|
|
2720
|
-
// dependType: DependTypeEnum.All
|
|
2721
|
-
}),
|
|
1176
|
+
const FIELD_TYPES = [
|
|
1177
|
+
'input-text',
|
|
1178
|
+
'input-password',
|
|
1179
|
+
'textarea',
|
|
1180
|
+
'date-picker',
|
|
1181
|
+
'date-range',
|
|
1182
|
+
'single-select',
|
|
1183
|
+
'tree-select',
|
|
1184
|
+
'multi-select',
|
|
1185
|
+
'single-file-upload',
|
|
1186
|
+
'multi-file-upload',
|
|
1187
|
+
'multi-image-upload',
|
|
1188
|
+
'avatar-upload',
|
|
1189
|
+
'checkbox',
|
|
1190
|
+
'radio',
|
|
1191
|
+
'reminder-type-radio',
|
|
1192
|
+
'switch',
|
|
1193
|
+
'autocomplete',
|
|
2722
1194
|
];
|
|
2723
|
-
//#endregion MultiSelect depend on field has isParent
|
|
2724
1195
|
|
|
2725
|
-
|
|
1196
|
+
/**
|
|
1197
|
+
* FIELD_TYPES là readonly ['input', 'select', 'checkbox',...]
|
|
1198
|
+
*/
|
|
1199
|
+
const INPUT_FIELD_TYPES = ['text', 'email', 'password', 'number'];
|
|
2726
1200
|
|
|
2727
|
-
//
|
|
2728
|
-
const
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
returnVal = size / (1024 * 1024);
|
|
2736
|
-
break;
|
|
2737
|
-
case 'GB':
|
|
2738
|
-
returnVal = size / (1024 * 1024 * 1024);
|
|
2739
|
-
break;
|
|
2740
|
-
case 'TB':
|
|
2741
|
-
returnVal = size / (1024 * 1024 * 1024 * 1024);
|
|
2742
|
-
break;
|
|
2743
|
-
default:
|
|
2744
|
-
returnVal = size;
|
|
2745
|
-
break;
|
|
2746
|
-
}
|
|
2747
|
-
return returnVal;
|
|
1201
|
+
// Loại date field.
|
|
1202
|
+
const DATE_FIELD_TYPES = ['picker', 'range'];
|
|
1203
|
+
// Các pattern dùng để check định dạng date.
|
|
1204
|
+
const FORMAT_DATE_PATTERNS = {
|
|
1205
|
+
YYYY_MM_DD: /^(\d{4})([-\/])(0[1-9]|1[0-2])\2(0[1-9]|[12]\d|3[01])$/,
|
|
1206
|
+
DD_MM_YYYY: /^(0[1-9]|[12]\d|3[01])([-\/])(0[1-9]|1[0-2])\2(\d{4})$/,
|
|
1207
|
+
MM_DD_YYYY: /^(0[1-9]|1[0-2])([-\/])(0[1-9]|[12]\d|3[01])\2(\d{4})$/,
|
|
1208
|
+
MM_DD_YYYY_HMS: /^(0[1-9]|1[0-2])([-\/])(0[1-9]|[12]\d|3[01])\2(\d{4})\s([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/,
|
|
2748
1209
|
};
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
1210
|
+
|
|
1211
|
+
// Chuyển đổi đối tượng Date thành string có format DDMMYYYY.
|
|
1212
|
+
const formatDateToDDMMYYYY = (date, separator = '-') => {
|
|
1213
|
+
const dd = String(date.getDate()).padStart(2, '0');
|
|
1214
|
+
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
1215
|
+
const yyyy = date.getFullYear();
|
|
1216
|
+
return `${dd}${separator}${mm}${separator}${yyyy}`;
|
|
2752
1217
|
};
|
|
2753
|
-
//
|
|
2754
|
-
const
|
|
2755
|
-
const
|
|
2756
|
-
const
|
|
2757
|
-
|
|
1218
|
+
// Chuyển đổi đối tượng Date thành string có format DDMMYYYY.
|
|
1219
|
+
const formatDateToYYYYMMDD = (date, separator = '-') => {
|
|
1220
|
+
const dd = String(date.getDate()).padStart(2, '0');
|
|
1221
|
+
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
1222
|
+
const yyyy = date.getFullYear();
|
|
1223
|
+
return `${yyyy}${separator}${mm}${separator}${dd}`;
|
|
2758
1224
|
};
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
1225
|
+
// Chuyển đổi string ở các dạng: YYYY-MM-DD, DD-MM-YYYY, MM-DD-YYYY đến Date, nếu sẵn là Date thì mặc định không đổi.
|
|
1226
|
+
const normalizeToDate = (input) => {
|
|
1227
|
+
if (!input)
|
|
1228
|
+
return null;
|
|
1229
|
+
if (input instanceof Date && !isNaN(input.getTime()))
|
|
1230
|
+
return input;
|
|
1231
|
+
if (typeof input !== 'string')
|
|
1232
|
+
return null;
|
|
1233
|
+
const inputTrimed = input.trim();
|
|
1234
|
+
let match;
|
|
1235
|
+
let year, month, day;
|
|
1236
|
+
let hour = 0, minute = 0, second = 0;
|
|
1237
|
+
if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.YYYY_MM_DD))) {
|
|
1238
|
+
year = +match[1];
|
|
1239
|
+
month = +match[3] - 1;
|
|
1240
|
+
day = +match[4];
|
|
2764
1241
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
1242
|
+
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.DD_MM_YYYY))) {
|
|
1243
|
+
day = +match[1];
|
|
1244
|
+
month = +match[3] - 1;
|
|
1245
|
+
year = +match[4];
|
|
2767
1246
|
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
1247
|
+
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY))) {
|
|
1248
|
+
month = +match[1] - 1;
|
|
1249
|
+
day = +match[3];
|
|
1250
|
+
year = +match[4];
|
|
1251
|
+
}
|
|
1252
|
+
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY_HMS))) {
|
|
1253
|
+
month = +match[1] - 1;
|
|
1254
|
+
day = +match[3];
|
|
1255
|
+
year = +match[4];
|
|
1256
|
+
hour = +match[5];
|
|
1257
|
+
minute = +match[6];
|
|
1258
|
+
second = +match[7];
|
|
1259
|
+
}
|
|
1260
|
+
else {
|
|
1261
|
+
console.warn('Sai định dạng chuỗi date');
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
const date = new Date(year, month, day, hour, minute, second);
|
|
1265
|
+
if (date.getFullYear() === year &&
|
|
1266
|
+
date.getMonth() === month &&
|
|
1267
|
+
date.getDate() === day &&
|
|
1268
|
+
date.getHours() === hour &&
|
|
1269
|
+
date.getMinutes() === minute &&
|
|
1270
|
+
date.getSeconds() === second) {
|
|
1271
|
+
return date;
|
|
1272
|
+
}
|
|
1273
|
+
console.warn('Ngày hoặc giờ không hợp lệ');
|
|
1274
|
+
return null;
|
|
2782
1275
|
};
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
if (
|
|
2786
|
-
return
|
|
1276
|
+
const normalizeToDate1 = (input) => {
|
|
1277
|
+
if (input) {
|
|
1278
|
+
if (input instanceof Date && !isNaN(input.getTime()))
|
|
1279
|
+
return input;
|
|
1280
|
+
if (typeof input !== 'string')
|
|
1281
|
+
return null;
|
|
1282
|
+
const inputTrimed = String(input).trim();
|
|
1283
|
+
let match;
|
|
1284
|
+
let year, month, day;
|
|
1285
|
+
if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.YYYY_MM_DD))) {
|
|
1286
|
+
year = +match[1];
|
|
1287
|
+
month = +match[3] - 1;
|
|
1288
|
+
day = +match[4];
|
|
2787
1289
|
}
|
|
2788
|
-
else if (
|
|
2789
|
-
|
|
1290
|
+
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.DD_MM_YYYY))) {
|
|
1291
|
+
day = +match[1];
|
|
1292
|
+
month = +match[3] - 1;
|
|
1293
|
+
year = +match[4];
|
|
2790
1294
|
}
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
|
|
2796
|
-
if (Array.isArray(value)) {
|
|
2797
|
-
return [key, deepCopyArray(value)]; // Deep copy nếu giá trị là mảng
|
|
1295
|
+
else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY))) {
|
|
1296
|
+
month = +match[1] - 1;
|
|
1297
|
+
day = +match[3];
|
|
1298
|
+
year = +match[4];
|
|
2798
1299
|
}
|
|
2799
|
-
else
|
|
2800
|
-
|
|
1300
|
+
else {
|
|
1301
|
+
console.warn('Sai định dạng chuỗi date');
|
|
1302
|
+
return null;
|
|
2801
1303
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
// Không cho phép nhập khoảng trắng ở đầu chuỗi, nếu không nhập gì vẫn tính đúng.
|
|
2807
|
-
const noLeadingSpaceValidator = () => {
|
|
2808
|
-
return (control) => {
|
|
2809
|
-
const value = (control.value ?? '').toString();
|
|
2810
|
-
if (!value)
|
|
2811
|
-
return null; // Không required thì để trống là hợp lệ
|
|
2812
|
-
return value[0] === ' ' ? { leadingSpace: true } : null;
|
|
2813
|
-
};
|
|
2814
|
-
};
|
|
2815
|
-
// Không cho phép nhập toàn khoảng trắng.
|
|
2816
|
-
const noWhitespaceValidator = () => {
|
|
2817
|
-
return (control) => {
|
|
2818
|
-
const isWhitespace = (control.value || '').toString().trim().length === 0;
|
|
2819
|
-
return isWhitespace ? { whitespace: true } : null;
|
|
2820
|
-
};
|
|
2821
|
-
};
|
|
2822
|
-
// Bắt đầu với một kí tự được chỉ định.
|
|
2823
|
-
const startsWithValidator = (prefix) => {
|
|
2824
|
-
return (control) => {
|
|
2825
|
-
const value = control.value ?? '';
|
|
2826
|
-
return value.startsWith(prefix) ? null : { startsWith: { requiredPrefix: prefix } };
|
|
2827
|
-
};
|
|
2828
|
-
};
|
|
2829
|
-
// So sánh giá trị field A không được lớn hơn field B.
|
|
2830
|
-
const minMaxFieldValidator = (minKey, maxKey) => {
|
|
2831
|
-
// group: AbstractControl sẽ đại diện cho FormGroup tức là cha bao hàm các field.
|
|
2832
|
-
return (group) => {
|
|
2833
|
-
const min = group.get(minKey)?.value;
|
|
2834
|
-
const max = group.get(maxKey)?.value;
|
|
2835
|
-
if (min != null && max != null && min > max) {
|
|
2836
|
-
return { minMaxInvalid: true };
|
|
1304
|
+
const date = new Date(year, month, day); // Validate: kiểm tra lại giá trị date (28/29/30/31 hợp lệ)
|
|
1305
|
+
if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {
|
|
1306
|
+
return date;
|
|
2837
1307
|
}
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
};
|
|
2841
|
-
// Match giữa hai field (ví dụ: password/confirmPassword,...).
|
|
2842
|
-
const matchFieldsValidator = (fieldToMatch) => {
|
|
2843
|
-
return (control) => {
|
|
2844
|
-
const parent = control.parent;
|
|
2845
|
-
if (!parent)
|
|
2846
|
-
return null;
|
|
2847
|
-
const target = parent.get(fieldToMatch);
|
|
2848
|
-
if (!target?.value)
|
|
1308
|
+
else {
|
|
1309
|
+
console.warn('Ngày đưa vào không hợp lệ');
|
|
2849
1310
|
return null;
|
|
2850
|
-
// Nếu đang validate chính nó (ví dụ: confirmPassword)
|
|
2851
|
-
const isMismatch = control.value !== target.value;
|
|
2852
|
-
// Nếu trước đó password (target) đang bị lỗi matchField nhưng giờ đã khớp
|
|
2853
|
-
if (!isMismatch && target.hasError('matchField')) {
|
|
2854
|
-
const errors = { ...target.errors };
|
|
2855
|
-
delete errors['matchField'];
|
|
2856
|
-
// Nếu không còn lỗi nào khác, set null
|
|
2857
|
-
target.setErrors(Object.keys(errors).length ? errors : null);
|
|
2858
1311
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
};
|
|
2862
|
-
// Async: Check if username is taken (simulate API).
|
|
2863
|
-
const asyncUsernameValidator = (takenUsernames) => {
|
|
2864
|
-
return (control) => {
|
|
2865
|
-
const value = control.value ?? '';
|
|
2866
|
-
const isTaken = takenUsernames.includes(value);
|
|
2867
|
-
return of(isTaken ? { usernameTaken: true } : null).pipe(delay(500));
|
|
2868
|
-
};
|
|
2869
|
-
};
|
|
2870
|
-
// Không có ký tự đặc biệt.
|
|
2871
|
-
const noSpecialCharValidator = () => {
|
|
2872
|
-
return (control) => {
|
|
2873
|
-
const valid = /^[a-zA-Z0-9]*$/.test(control.value);
|
|
2874
|
-
return valid ? null : { noSpecialChar: true };
|
|
2875
|
-
};
|
|
2876
|
-
};
|
|
2877
|
-
// Username không được nằm trong blacklist.
|
|
2878
|
-
const usernameBlacklistValidator = (blacklist) => {
|
|
2879
|
-
return (control) => {
|
|
2880
|
-
return blacklist.includes(control.value) ? { blacklisted: true } : null;
|
|
2881
|
-
};
|
|
2882
|
-
};
|
|
2883
|
-
// Có thuộc mimetypes từ mảng mimetypes được chỉ định hay không?
|
|
2884
|
-
const acceptedMimeTypesValidator = (acceptedMimeTypes) => {
|
|
2885
|
-
return (control) => {
|
|
2886
|
-
const value = control?.value;
|
|
2887
|
-
if (value) {
|
|
2888
|
-
let fileType = '';
|
|
2889
|
-
if (value instanceof File) {
|
|
2890
|
-
fileType = value.type;
|
|
2891
|
-
}
|
|
2892
|
-
if (isValidJSON(value)) {
|
|
2893
|
-
const { contentType } = JSON.parse(value)[0] || {};
|
|
2894
|
-
if (contentType)
|
|
2895
|
-
fileType = contentType;
|
|
2896
|
-
}
|
|
2897
|
-
if (fileType) {
|
|
2898
|
-
if (!acceptedMimeTypes.includes(fileType)) {
|
|
2899
|
-
return {
|
|
2900
|
-
acceptedMimeTypes: {
|
|
2901
|
-
actualValue: fileType,
|
|
2902
|
-
requiredValue: `Nằm trong ${acceptedMimeTypes}`,
|
|
2903
|
-
},
|
|
2904
|
-
};
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
}
|
|
2908
|
-
return null;
|
|
2909
|
-
};
|
|
1312
|
+
}
|
|
1313
|
+
return null;
|
|
2910
1314
|
};
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
actualValue: fileSizeInMB,
|
|
2920
|
-
requiredValue: `Nhỏ hơn ${maxSizeInMB}`,
|
|
2921
|
-
},
|
|
2922
|
-
};
|
|
2923
|
-
}
|
|
2924
|
-
}
|
|
2925
|
-
return null;
|
|
2926
|
-
};
|
|
1315
|
+
// Tạo danh sách năm chạy ngược từ hiện tại đến năm chỉ định.
|
|
1316
|
+
const generateYearsDesc = (from) => {
|
|
1317
|
+
const currentYear = new Date().getFullYear();
|
|
1318
|
+
const years = [];
|
|
1319
|
+
for (let year = currentYear; year >= from; year--) {
|
|
1320
|
+
years.push(year);
|
|
1321
|
+
}
|
|
1322
|
+
return years;
|
|
2927
1323
|
};
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
};
|
|
2937
|
-
}
|
|
2938
|
-
if (reminderValue) {
|
|
2939
|
-
const { reminderDate } = reminderValue;
|
|
2940
|
-
if (!reminderDate) {
|
|
2941
|
-
return {
|
|
2942
|
-
reminderCustomRequired: {
|
|
2943
|
-
actualValue: reminderDate,
|
|
2944
|
-
requiredValue: `Giá trị từ select hoặc date`,
|
|
2945
|
-
},
|
|
2946
|
-
};
|
|
2947
|
-
}
|
|
2948
|
-
}
|
|
2949
|
-
return null;
|
|
2950
|
-
};
|
|
1324
|
+
// Tạo danh sách năm chạy từ năm chỉ định đến năm hiện tại.
|
|
1325
|
+
const generateYearsAsc = (from) => {
|
|
1326
|
+
const currentYear = new Date().getFullYear();
|
|
1327
|
+
const years = [];
|
|
1328
|
+
for (let year = from; year <= currentYear; year++) {
|
|
1329
|
+
years.push(year);
|
|
1330
|
+
}
|
|
1331
|
+
return years;
|
|
2951
1332
|
};
|
|
2952
1333
|
|
|
2953
|
-
const
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
1334
|
+
const DEFAULT_DATE_PICKER_VALUE = '';
|
|
1335
|
+
/**
|
|
1336
|
+
* Interface của Date type là Picker.
|
|
1337
|
+
*/
|
|
1338
|
+
class DatePickerField extends BaseDateField {
|
|
1339
|
+
constructor(init) {
|
|
1340
|
+
super(init);
|
|
1341
|
+
/**
|
|
1342
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Date picker.
|
|
1343
|
+
*/
|
|
1344
|
+
this.type = FieldTypeEnum.DatePicker;
|
|
1345
|
+
this.value = DEFAULT_DATE_PICKER_VALUE;
|
|
1346
|
+
this.initialValue = DEFAULT_DATE_PICKER_VALUE;
|
|
1347
|
+
this.outputAs = DatePickerOutputValueTypeEnum.String;
|
|
1348
|
+
Object.assign(this, init);
|
|
1349
|
+
}
|
|
1350
|
+
getDefaultValue() {
|
|
1351
|
+
return DEFAULT_DATE_PICKER_VALUE;
|
|
1352
|
+
}
|
|
1353
|
+
toFormValue(modelValue) {
|
|
1354
|
+
const value = modelValue;
|
|
1355
|
+
if (!value)
|
|
1356
|
+
return null;
|
|
1357
|
+
return normalizeToDate(value);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const DEFAULT_DATE_RANGE_VALUE = [];
|
|
1362
|
+
/**
|
|
1363
|
+
* Interface của Date type là Range.
|
|
1364
|
+
*/
|
|
1365
|
+
class DateRangeField extends BaseDateField {
|
|
1366
|
+
constructor(init) {
|
|
1367
|
+
super(init);
|
|
1368
|
+
/**
|
|
1369
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Date range.
|
|
1370
|
+
*/
|
|
1371
|
+
this.type = FieldTypeEnum.DateRange;
|
|
1372
|
+
this.value = DEFAULT_DATE_RANGE_VALUE;
|
|
1373
|
+
this.initialValue = DEFAULT_DATE_RANGE_VALUE;
|
|
1374
|
+
this.outputAs = DateRangeOutputValueTypeEnum.StringArray;
|
|
1375
|
+
Object.assign(this, init);
|
|
1376
|
+
}
|
|
1377
|
+
getDefaultValue() {
|
|
1378
|
+
return DEFAULT_DATE_RANGE_VALUE;
|
|
2961
1379
|
}
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
1380
|
+
toFormValue(modelValue) {
|
|
1381
|
+
let value = modelValue;
|
|
1382
|
+
if (typeof value === 'string') {
|
|
1383
|
+
value = value.split(',').map(dateStr => dateStr.trim());
|
|
1384
|
+
}
|
|
1385
|
+
let dateRange = [];
|
|
1386
|
+
if (value?.length === 2) {
|
|
1387
|
+
const startDate = normalizeToDate(value[0]);
|
|
1388
|
+
const endDate = normalizeToDate(value[1]);
|
|
1389
|
+
if (startDate && endDate)
|
|
1390
|
+
dateRange = [startDate, endDate];
|
|
1391
|
+
}
|
|
1392
|
+
return dateRange;
|
|
2965
1393
|
}
|
|
2966
|
-
|
|
2967
|
-
};
|
|
2968
|
-
const getDefaultMinLengthErrorMessage = (label, errorValue) => {
|
|
2969
|
-
return `${label || 'Trường này'} cần tối thiểu ${errorValue.requiredLength} ký tự`;
|
|
2970
|
-
};
|
|
2971
|
-
const getDefaultMaxLengthErrorMessage = (label, errorValue) => {
|
|
2972
|
-
return `${label || 'Trường này'} cần đât thiểu ${errorValue.requiredLength} ký tự`;
|
|
2973
|
-
};
|
|
2974
|
-
const getDefaultAcptMimeTypesErrorMessage = (typeString) => {
|
|
2975
|
-
return `Tập tin tải lên không được hỗ trợ. Vui lòng tải các tập tin có định dạng ${typeString}`;
|
|
2976
|
-
};
|
|
2977
|
-
const getDefaultMaxSizeInMBErrorMessage = (maxSizeInMB) => {
|
|
2978
|
-
return `Kích thước tập tin tải lên không được phép vượt quá ${maxSizeInMB} MB`;
|
|
2979
|
-
};
|
|
2980
|
-
const getLeadingSpaceErrorMessage = (label) => {
|
|
2981
|
-
return `${label || 'Trường này'} không được bắt đầu bằng khoảng trắng`;
|
|
2982
|
-
};
|
|
2983
|
-
|
|
2984
|
-
var FormModeEnum;
|
|
2985
|
-
(function (FormModeEnum) {
|
|
2986
|
-
FormModeEnum["Add"] = "add";
|
|
2987
|
-
FormModeEnum["Edit"] = "edit";
|
|
2988
|
-
FormModeEnum["View"] = "view";
|
|
2989
|
-
FormModeEnum["Design"] = "design";
|
|
2990
|
-
})(FormModeEnum || (FormModeEnum = {}));
|
|
1394
|
+
}
|
|
2991
1395
|
|
|
2992
1396
|
/**
|
|
2993
|
-
*
|
|
1397
|
+
* Interface base chung cho Các dạng Date.
|
|
2994
1398
|
*/
|
|
2995
|
-
class BaseField {
|
|
1399
|
+
class BaseUploadField extends BaseField {
|
|
1400
|
+
/**
|
|
1401
|
+
* Props validation chung cho các dạng Date.
|
|
1402
|
+
*/
|
|
2996
1403
|
constructor(init) {
|
|
2997
|
-
|
|
2998
|
-
this.seqNum = 0;
|
|
2999
|
-
if (!init?.key || !init?.label) {
|
|
3000
|
-
throw new Error('key, label, and type are required');
|
|
3001
|
-
}
|
|
1404
|
+
super(init);
|
|
3002
1405
|
/**
|
|
3003
|
-
* Gán giá trị cho các props cần giá trị mặc định ở
|
|
1406
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Date.
|
|
3004
1407
|
*/
|
|
3005
|
-
this.
|
|
3006
|
-
this.
|
|
3007
|
-
this.
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
this.
|
|
3012
|
-
this.
|
|
3013
|
-
this.
|
|
3014
|
-
this.
|
|
3015
|
-
this.viewMode = ViewModeEnum.form;
|
|
3016
|
-
this.enableFieldChange = false;
|
|
1408
|
+
this.controlType = ControlTypeEnum.Upload;
|
|
1409
|
+
this.outputAs = UploadOutputValueTypeEnum.Object;
|
|
1410
|
+
this.apiFileConfig = {
|
|
1411
|
+
uploadUrl: '',
|
|
1412
|
+
deleteUrl: '',
|
|
1413
|
+
};
|
|
1414
|
+
this.acceptedMimeTypes = DEFAULT_ACCEPTED_MIME_TYPES;
|
|
1415
|
+
this.maxSizeInMB = 25;
|
|
1416
|
+
this.showAcceptedExtensionGuide = true;
|
|
1417
|
+
this.uploadTemp = true;
|
|
3017
1418
|
Object.assign(this, init);
|
|
3018
1419
|
}
|
|
3019
|
-
//
|
|
3020
|
-
|
|
3021
|
-
const validators =
|
|
3022
|
-
|
|
3023
|
-
|
|
1420
|
+
// Mảng ValidatorFn các validation chung được hỗ trợ sẵn bởi angular cho Input Password.
|
|
1421
|
+
getCustomBaseUploadValidators() {
|
|
1422
|
+
const validators = this.getCustomValidators();
|
|
1423
|
+
// Kiểm tra xem file tải lên có type nằm trong mảng acceptedMimeTypes đã chỉ định hay không.
|
|
1424
|
+
if (this.acceptedMimeTypes?.length > 0)
|
|
1425
|
+
validators.push(acceptedMimeTypesValidator(this.acceptedMimeTypes));
|
|
1426
|
+
// Kiểm tra kích thước tối đa mà file tải lên được cho phép.
|
|
1427
|
+
validators.push(maxFileSizeInMBValidator(this.maxSizeInMB));
|
|
3024
1428
|
return validators;
|
|
3025
1429
|
}
|
|
3026
|
-
// Tạo
|
|
3027
|
-
|
|
3028
|
-
const
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
return validators;
|
|
1430
|
+
// Tạo Form Control cho Input Field.
|
|
1431
|
+
toFormControl() {
|
|
1432
|
+
const commonValidators = this.getCommonValidators();
|
|
1433
|
+
const customValidators = this.getCustomBaseUploadValidators();
|
|
1434
|
+
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
3032
1435
|
}
|
|
3033
1436
|
getErrorMessage(errorKey, errorValue, allFields) {
|
|
3034
|
-
|
|
3035
|
-
|
|
1437
|
+
// Lấy error message mặc định của acceptedMimeTypes.
|
|
1438
|
+
if (errorKey === CustomValidaterEnum.AcceptedMimiTypes) {
|
|
1439
|
+
const typeString = getExtensions(this.acceptedMimeTypes).join(', ');
|
|
1440
|
+
return getDefaultAcptMimeTypesErrorMessage(typeString);
|
|
3036
1441
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
return `${this.label} không khớp với ${targetLabel}`;
|
|
1442
|
+
// Lấy error message mặc định của maxSizeInMB.
|
|
1443
|
+
if (errorKey === CustomValidaterEnum.MaxSizeInMB) {
|
|
1444
|
+
return getDefaultMaxSizeInMBErrorMessage(this.maxSizeInMB);
|
|
3041
1445
|
}
|
|
3042
|
-
return
|
|
1446
|
+
return super.getErrorMessage(errorKey, errorValue, allFields);
|
|
3043
1447
|
}
|
|
3044
1448
|
}
|
|
3045
1449
|
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
Object.assign(this, init);
|
|
3052
|
-
}
|
|
3053
|
-
}
|
|
3054
|
-
// Đại diện cho 1 group.
|
|
3055
|
-
class FieldGroup {
|
|
1450
|
+
const DEFAULT_SINGLE_FILE_UPLOAD_VALUE = '';
|
|
1451
|
+
/**
|
|
1452
|
+
* Interface của Single file upload. Đầu vào có thể là chuỗi JSON hoặc đối tượng File.
|
|
1453
|
+
*/
|
|
1454
|
+
class SingleFileUploadField extends BaseUploadField {
|
|
3056
1455
|
constructor(init) {
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
1456
|
+
super(init);
|
|
1457
|
+
/**
|
|
1458
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Single file upload.
|
|
1459
|
+
*/
|
|
1460
|
+
this.type = FieldTypeEnum.SingleFileUpload;
|
|
1461
|
+
this.value = DEFAULT_SINGLE_FILE_UPLOAD_VALUE;
|
|
1462
|
+
this.initialValue = DEFAULT_SINGLE_FILE_UPLOAD_VALUE;
|
|
1463
|
+
this.suffixes = [];
|
|
3061
1464
|
Object.assign(this, init);
|
|
3062
1465
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
const buildForm = (fields, mode, model) => {
|
|
3066
|
-
const groupObj = {};
|
|
3067
|
-
// console.log("model", model);
|
|
3068
|
-
for (const field of fields) {
|
|
3069
|
-
// Loại bỏ nếu quyết định ẩn field đi.
|
|
3070
|
-
if (field.isHidden)
|
|
3071
|
-
continue;
|
|
3072
|
-
// Nếu là dạng InputPasswordField nhưng ở mode edit và view thì không được dùng.
|
|
3073
|
-
if (field instanceof InputPasswordField &&
|
|
3074
|
-
(mode === FormModeEnum.Edit || mode === FormModeEnum.View))
|
|
3075
|
-
continue;
|
|
3076
|
-
const fieldKey = field?.key;
|
|
3077
|
-
// field.value = field.allowSetValue ? (model?.[fieldKey] || field.value) : null;
|
|
3078
|
-
if (field.allowSetValue) {
|
|
3079
|
-
field.value = field.getDefaultValue();
|
|
3080
|
-
field.modelValue = null;
|
|
3081
|
-
if (model?.[fieldKey]) {
|
|
3082
|
-
field.value = field.toFormValue(model[fieldKey]);
|
|
3083
|
-
field.modelValue = model[fieldKey];
|
|
3084
|
-
}
|
|
3085
|
-
field.initialValue = field.value;
|
|
3086
|
-
}
|
|
3087
|
-
// console.log("e", field.value );
|
|
3088
|
-
// field.value = field.allowSetValue ? field.toFormValue(model?.[fieldKey] | field.getDefaultValue()) : field.getDefaultValue();
|
|
3089
|
-
const control = field.toFormControl();
|
|
3090
|
-
control.reset(field.value);
|
|
3091
|
-
if (field.isDisabled) {
|
|
3092
|
-
//console.log(field.key, 'dfdfdfdf');
|
|
3093
|
-
control?.disable();
|
|
3094
|
-
}
|
|
3095
|
-
groupObj[fieldKey] = control;
|
|
1466
|
+
getDefaultValue() {
|
|
1467
|
+
return DEFAULT_SINGLE_FILE_UPLOAD_VALUE;
|
|
3096
1468
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
// Tạo placeholder mặc định cho các dạng Input, Textarea.
|
|
3100
|
-
const createPlaceholder = (field) => {
|
|
3101
|
-
const placeholder = field?.placeholder;
|
|
3102
|
-
if (placeholder)
|
|
3103
|
-
return placeholder;
|
|
3104
|
-
const label = field?.label;
|
|
3105
|
-
if (label)
|
|
3106
|
-
return `Nhập ${label?.toLowerCase()}`;
|
|
3107
|
-
return `Nhập ${field.key?.toLowerCase()}`;
|
|
3108
|
-
};
|
|
3109
|
-
// Tạo placeholder mặc định cho các dạng Select.
|
|
3110
|
-
const createSelectPlaceholder = (field) => {
|
|
3111
|
-
const placeholder = field?.placeholder;
|
|
3112
|
-
if (placeholder)
|
|
3113
|
-
return placeholder;
|
|
3114
|
-
const label = field?.label;
|
|
3115
|
-
if (label) {
|
|
3116
|
-
const isSingleSelect = field?.type === FieldTypeEnum.SingleSelect;
|
|
3117
|
-
return `Chọn ${isSingleSelect ? '' : 'các'} ${label?.toLowerCase()}`;
|
|
1469
|
+
toFormValue(modelValue) {
|
|
1470
|
+
return String(modelValue);
|
|
3118
1471
|
}
|
|
3119
|
-
|
|
3120
|
-
};
|
|
1472
|
+
}
|
|
3121
1473
|
|
|
3122
|
-
const
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
.
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
}
|
|
1474
|
+
const DEFAULT_MULTI_FILE_UPLOAD_VALUE = '';
|
|
1475
|
+
/**
|
|
1476
|
+
* Interface của Multi file upload.
|
|
1477
|
+
*/
|
|
1478
|
+
class MultiFileUploadField extends BaseUploadField {
|
|
1479
|
+
constructor(init) {
|
|
1480
|
+
super(init);
|
|
1481
|
+
/**
|
|
1482
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Multi file upload.
|
|
1483
|
+
*/
|
|
1484
|
+
this.type = FieldTypeEnum.MultiFileUpload;
|
|
1485
|
+
this.value = DEFAULT_MULTI_FILE_UPLOAD_VALUE;
|
|
1486
|
+
this.initialValue = DEFAULT_MULTI_FILE_UPLOAD_VALUE;
|
|
1487
|
+
this.maxFileCount = 6;
|
|
1488
|
+
Object.assign(this, init);
|
|
1489
|
+
}
|
|
1490
|
+
getDefaultValue() {
|
|
1491
|
+
return DEFAULT_MULTI_FILE_UPLOAD_VALUE;
|
|
1492
|
+
}
|
|
1493
|
+
toFormValue(modelValue) {
|
|
1494
|
+
return String(modelValue);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
const DEFAULT_MULTI_IMAGE_UPLOAD_VALUE = '';
|
|
1499
|
+
/**
|
|
1500
|
+
* Interface của Multi image upload.
|
|
1501
|
+
*/
|
|
1502
|
+
class MultiImageUploadField extends BaseUploadField {
|
|
1503
|
+
constructor(init) {
|
|
1504
|
+
super(init);
|
|
1505
|
+
/**
|
|
1506
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Multi image upload.
|
|
1507
|
+
*/
|
|
1508
|
+
this.type = FieldTypeEnum.MultiImageUpload;
|
|
1509
|
+
this.value = DEFAULT_MULTI_IMAGE_UPLOAD_VALUE;
|
|
1510
|
+
this.initialValue = DEFAULT_MULTI_IMAGE_UPLOAD_VALUE;
|
|
1511
|
+
this.maxFileCount = 6;
|
|
1512
|
+
this.imageCardWidth = '108px';
|
|
1513
|
+
this.imageCardHeight = '108px';
|
|
1514
|
+
Object.assign(this, init);
|
|
1515
|
+
}
|
|
1516
|
+
getDefaultValue() {
|
|
1517
|
+
return DEFAULT_MULTI_IMAGE_UPLOAD_VALUE;
|
|
1518
|
+
}
|
|
1519
|
+
toFormValue(modelValue) {
|
|
1520
|
+
return String(modelValue);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
3164
1523
|
|
|
3165
1524
|
const DYNAMIC_FORM_CONFIG = new InjectionToken('DYNAMIC_FORM_CONFIG');
|
|
3166
1525
|
|
|
@@ -3264,6 +1623,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
3264
1623
|
args: [{ providedIn: 'root' }]
|
|
3265
1624
|
}] });
|
|
3266
1625
|
|
|
1626
|
+
const DEFAULT_AVATAR_UPLOAD_VALUE = '';
|
|
1627
|
+
/**
|
|
1628
|
+
* Interface của Avatar upload.
|
|
1629
|
+
*/
|
|
1630
|
+
class AvatarUploadField extends BaseUploadField {
|
|
1631
|
+
constructor(init) {
|
|
1632
|
+
super(init);
|
|
1633
|
+
/**
|
|
1634
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Avatar upload.
|
|
1635
|
+
*/
|
|
1636
|
+
this.type = FieldTypeEnum.AvatarUpload;
|
|
1637
|
+
this.value = DEFAULT_AVATAR_UPLOAD_VALUE;
|
|
1638
|
+
this.acceptedMimeTypes = DEFAULT_ACCEPTED_IMAGE_MIME_TYPES;
|
|
1639
|
+
this.initialValue = DEFAULT_AVATAR_UPLOAD_VALUE;
|
|
1640
|
+
Object.assign(this, init);
|
|
1641
|
+
}
|
|
1642
|
+
getDefaultValue() {
|
|
1643
|
+
return DEFAULT_AVATAR_UPLOAD_VALUE;
|
|
1644
|
+
}
|
|
1645
|
+
toFormValue(modelValue) {
|
|
1646
|
+
return String(modelValue);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
|
|
3267
1650
|
const DEFAULT_TREE_SELECT_VALUE = null;
|
|
3268
1651
|
/**
|
|
3269
1652
|
* Interface của Select Field type là dạng Single.
|
|
@@ -3332,7 +1715,7 @@ class DynamicFormService {
|
|
|
3332
1715
|
* Thao tác với control và fields cũng như output đầu ra của data form.
|
|
3333
1716
|
*/
|
|
3334
1717
|
setFieldValue(fieldKey, rawValue) {
|
|
3335
|
-
const field = this.fields.find(f => f.key === fieldKey);
|
|
1718
|
+
const field = this.fields.find((f) => f.key === fieldKey);
|
|
3336
1719
|
if (!field)
|
|
3337
1720
|
return;
|
|
3338
1721
|
const formValue = field.toFormValue(rawValue);
|
|
@@ -3398,9 +1781,10 @@ class DynamicFormService {
|
|
|
3398
1781
|
return fields.sort((a, b) => a.seqNum - b.seqNum);
|
|
3399
1782
|
}
|
|
3400
1783
|
createFieldFromJson(fieldConfig, objField) {
|
|
3401
|
-
fieldConfig.key =
|
|
3402
|
-
|
|
3403
|
-
fieldConfig.
|
|
1784
|
+
fieldConfig.key =
|
|
1785
|
+
fieldConfig?.key || lowerCaseFirstChar(objField.code) || "";
|
|
1786
|
+
fieldConfig.label = fieldConfig?.label || objField?.name || "";
|
|
1787
|
+
fieldConfig.groupCode = fieldConfig.groupCode ?? objField.viewType ?? "";
|
|
3404
1788
|
fieldConfig.seqNum = fieldConfig.seqNum ?? objField.seqNum;
|
|
3405
1789
|
fieldConfig.required = fieldConfig.required ?? objField.required === 3;
|
|
3406
1790
|
const fieldType = fieldConfig.type;
|
|
@@ -3416,14 +1800,18 @@ class DynamicFormService {
|
|
|
3416
1800
|
if (fieldType === FieldTypeEnum.SingleSelect) {
|
|
3417
1801
|
const singleSelect = fieldConfig;
|
|
3418
1802
|
if (!singleSelect.apiConfig) {
|
|
3419
|
-
singleSelect.apiConfig = {
|
|
1803
|
+
singleSelect.apiConfig = {
|
|
1804
|
+
baseUrl: this.formConfigService.handleApiUrlCreation(objField.apiURL || "", objField.objTypeGroupCode),
|
|
1805
|
+
};
|
|
3420
1806
|
}
|
|
3421
1807
|
return new SingleSelectField({ ...singleSelect });
|
|
3422
1808
|
}
|
|
3423
1809
|
if (fieldType === FieldTypeEnum.MultiSelect) {
|
|
3424
1810
|
const multiSelect = fieldConfig;
|
|
3425
1811
|
if (!multiSelect.apiConfig) {
|
|
3426
|
-
multiSelect.apiConfig = {
|
|
1812
|
+
multiSelect.apiConfig = {
|
|
1813
|
+
baseUrl: this.formConfigService.handleApiUrlCreation(objField.apiURL || "", objField.objTypeGroupCode),
|
|
1814
|
+
};
|
|
3427
1815
|
}
|
|
3428
1816
|
return new MultiSelectField({ ...multiSelect });
|
|
3429
1817
|
}
|
|
@@ -3431,7 +1819,7 @@ class DynamicFormService {
|
|
|
3431
1819
|
const treeSelect = fieldConfig;
|
|
3432
1820
|
if (!treeSelect.apiConfig) {
|
|
3433
1821
|
treeSelect.apiConfig = {
|
|
3434
|
-
baseUrl: this.formConfigService.handleApiUrlCreation(objField.apiURL ||
|
|
1822
|
+
baseUrl: this.formConfigService.handleApiUrlCreation(objField.apiURL || "", objField.objTypeGroupCode),
|
|
3435
1823
|
};
|
|
3436
1824
|
}
|
|
3437
1825
|
return new TreeSelectField({ ...treeSelect });
|
|
@@ -3443,26 +1831,32 @@ class DynamicFormService {
|
|
|
3443
1831
|
return new DateRangeField({ ...fieldConfig });
|
|
3444
1832
|
}
|
|
3445
1833
|
if (fieldType === FieldTypeEnum.SingleFileUpload) {
|
|
3446
|
-
return new SingleFileUploadField({
|
|
1834
|
+
return new SingleFileUploadField({
|
|
1835
|
+
...fieldConfig,
|
|
1836
|
+
});
|
|
3447
1837
|
}
|
|
3448
1838
|
if (fieldType === FieldTypeEnum.MultiFileUpload) {
|
|
3449
|
-
return new MultiFileUploadField({
|
|
1839
|
+
return new MultiFileUploadField({
|
|
1840
|
+
...fieldConfig,
|
|
1841
|
+
});
|
|
3450
1842
|
}
|
|
3451
1843
|
if (fieldType === FieldTypeEnum.AvatarUpload) {
|
|
3452
1844
|
return new AvatarUploadField({ ...fieldConfig });
|
|
3453
1845
|
}
|
|
3454
1846
|
if (fieldType === FieldTypeEnum.MultiImageUpload) {
|
|
3455
|
-
return new MultiImageUploadField({
|
|
1847
|
+
return new MultiImageUploadField({
|
|
1848
|
+
...fieldConfig,
|
|
1849
|
+
});
|
|
3456
1850
|
}
|
|
3457
1851
|
return null;
|
|
3458
1852
|
}
|
|
3459
1853
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFormService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3460
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFormService, providedIn:
|
|
1854
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFormService, providedIn: "root" }); }
|
|
3461
1855
|
}
|
|
3462
1856
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFormService, decorators: [{
|
|
3463
1857
|
type: Injectable,
|
|
3464
1858
|
args: [{
|
|
3465
|
-
providedIn:
|
|
1859
|
+
providedIn: "root",
|
|
3466
1860
|
}]
|
|
3467
1861
|
}] });
|
|
3468
1862
|
|
|
@@ -3762,7 +2156,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
3762
2156
|
args: [{ required: false }]
|
|
3763
2157
|
}] } });
|
|
3764
2158
|
|
|
3765
|
-
const ZORRO_MODULES$
|
|
2159
|
+
const ZORRO_MODULES$e = [NzDatePickerModule, NzInputModule];
|
|
3766
2160
|
class DatePickerComponent extends BaseValueAccessor {
|
|
3767
2161
|
constructor() {
|
|
3768
2162
|
super();
|
|
@@ -3856,7 +2250,7 @@ class DatePickerComponent extends BaseValueAccessor {
|
|
|
3856
2250
|
}
|
|
3857
2251
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
3858
2252
|
type: Component,
|
|
3859
|
-
args: [{ selector: 'app-date-picker', standalone: true, imports: [...FORM_MODULES, ...ZORRO_MODULES$
|
|
2253
|
+
args: [{ selector: 'app-date-picker', standalone: true, imports: [...FORM_MODULES, ...ZORRO_MODULES$e], providers: [
|
|
3860
2254
|
{
|
|
3861
2255
|
provide: NG_VALUE_ACCESSOR,
|
|
3862
2256
|
useExisting: forwardRef(() => DatePickerComponent),
|
|
@@ -3865,7 +2259,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
3865
2259
|
], template: "@if (modeSignal() === formModeEnum.View) {\r\n <input nz-input [value]=\"displayValue\" [disabled]=\"true\" class=\"modeView\" />\r\n} @else {\r\n <nz-date-picker\r\n [id]=\"fieldSignal().key\"\r\n [class]=\"'w-100'\"\r\n [nzDisabled]=\"isDisabled\"\r\n [nzPlaceHolder]=\"fieldSignal().placeholder!\"\r\n [nzFormat]=\"fieldSignal().format\"\r\n [nzDisabledDate]=\"disabledDate()\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"handleChangeDate($event)\"\r\n >\r\n </nz-date-picker>\r\n}\r\n", styles: [".w-100{width:100%}.h-100{height:100%}.flex{display:flex}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.items-center{align-items:center}.mb-0{margin-bottom:0!important}.break-work{word-break:break-word}:host ::ng-deep .ant-input[disabled]{background-color:#fff}.modeView{pointer-events:none;color:#575757!important;font-size:12px}\n"] }]
|
|
3866
2260
|
}], ctorParameters: () => [] });
|
|
3867
2261
|
|
|
3868
|
-
const ZORRO_MODULES$
|
|
2262
|
+
const ZORRO_MODULES$d = [NzDatePickerModule, NzInputModule];
|
|
3869
2263
|
class DateRangeComponent extends BaseValueAccessor {
|
|
3870
2264
|
constructor() {
|
|
3871
2265
|
super();
|
|
@@ -3957,7 +2351,7 @@ class DateRangeComponent extends BaseValueAccessor {
|
|
|
3957
2351
|
}
|
|
3958
2352
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DateRangeComponent, decorators: [{
|
|
3959
2353
|
type: Component,
|
|
3960
|
-
args: [{ selector: 'app-date-range', standalone: true, imports: [...FORM_MODULES, ...ZORRO_MODULES$
|
|
2354
|
+
args: [{ selector: 'app-date-range', standalone: true, imports: [...FORM_MODULES, ...ZORRO_MODULES$d], providers: [
|
|
3961
2355
|
{
|
|
3962
2356
|
provide: NG_VALUE_ACCESSOR,
|
|
3963
2357
|
useExisting: forwardRef(() => DateRangeComponent),
|
|
@@ -3966,7 +2360,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
3966
2360
|
], template: "@if (modeSignal() === formModeEnum.View) {\r\n @if (value!.length > 0) {\r\n <nz-range-picker\r\n [class]=\"'w-100'\"\r\n [nzFormat]=\"fieldSignal().format\"\r\n [nzDisabled]=\"true\"\r\n [(ngModel)]=\"value\"\r\n class=\"modeView\"\r\n [nzSuffixIcon]=\"suffixIcon\"\r\n ></nz-range-picker>\r\n <ng-template #suffixIcon>\r\n <!-- \u0110\u1EC3 tr\u1ED1ng, ho\u1EB7c custom icon kh\u00E1c -->\r\n </ng-template>\r\n } @else {\r\n <input nz-input [value]=\"displayValueNoInfo\" [disabled]=\"true\" class=\"modeView\" />\r\n }\r\n} @else {\r\n <nz-range-picker\r\n [id]=\"fieldSignal().key\"\r\n class=\"w-100\"\r\n [nzDisabled]=\"isDisabled\"\r\n [nzFormat]=\"fieldSignal().format\"\r\n [nzDisabledDate]=\"disabledDate()\"\r\n [nzPlaceHolder]=\"fieldSignal().placeholder ?? ['Ng\u00E0y b\u1EAFt \u0111\u1EA7u', 'Ng\u00E0y k\u1EBFt th\u00FAc']\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"handleChangeDateRange($event)\"\r\n ></nz-range-picker>\r\n}\r\n", styles: [".w-100{width:100%}.h-100{height:100%}.flex{display:flex}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.items-center{align-items:center}.mb-0{margin-bottom:0!important}.break-work{word-break:break-word}:host ::ng-deep .modeView.ant-picker.ant-picker-disabled{background-color:inherit!important;cursor:default;pointer-events:none}:host ::ng-deep .modeView .ant-picker-input>input[disabled]{color:#575757!important}.modeView{pointer-events:none;background-color:#fff;font-size:12px;color:#575757!important}\n"] }]
|
|
3967
2361
|
}], ctorParameters: () => [] });
|
|
3968
2362
|
|
|
3969
|
-
const ZORRO_MODULES$
|
|
2363
|
+
const ZORRO_MODULES$c = [NzInputModule, NzIconModule];
|
|
3970
2364
|
class InputPasswordComponent extends BaseValueAccessor {
|
|
3971
2365
|
constructor() {
|
|
3972
2366
|
super();
|
|
@@ -4012,7 +2406,7 @@ class InputPasswordComponent extends BaseValueAccessor {
|
|
|
4012
2406
|
}
|
|
4013
2407
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InputPasswordComponent, decorators: [{
|
|
4014
2408
|
type: Component,
|
|
4015
|
-
args: [{ selector: 'app-input-password', standalone: true, imports: [...ZORRO_MODULES$
|
|
2409
|
+
args: [{ selector: 'app-input-password', standalone: true, imports: [...ZORRO_MODULES$c], providers: [
|
|
4016
2410
|
{
|
|
4017
2411
|
provide: NG_VALUE_ACCESSOR,
|
|
4018
2412
|
useExisting: forwardRef(() => InputPasswordComponent),
|
|
@@ -4039,7 +2433,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
4039
2433
|
}]
|
|
4040
2434
|
}], ctorParameters: () => [] });
|
|
4041
2435
|
|
|
4042
|
-
const ZORRO_MODULES$
|
|
2436
|
+
const ZORRO_MODULES$b = [NzButtonModule, NzIconModule];
|
|
4043
2437
|
class SuffixComponent {
|
|
4044
2438
|
constructor() {
|
|
4045
2439
|
this.formId = '';
|
|
@@ -4057,14 +2451,14 @@ class SuffixComponent {
|
|
|
4057
2451
|
}
|
|
4058
2452
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SuffixComponent, decorators: [{
|
|
4059
2453
|
type: Component,
|
|
4060
|
-
args: [{ selector: 'app-suffix', standalone: true, imports: [...CORE_MODULES, ...ZORRO_MODULES$
|
|
2454
|
+
args: [{ selector: 'app-suffix', standalone: true, imports: [...CORE_MODULES, ...ZORRO_MODULES$b], template: "<button\r\n nz-button\r\n nzType=\"primary\"\r\n nzSize=\"small\"\r\n [title]=\"suffixConfig.description\"\r\n class=\"h-100 suffix-btn font-size-12\"\r\n [ngClass]=\"suffixConfig.class ?? ''\"\r\n (click)=\"handleSuffix()\"\r\n>\r\n @if (suffixConfig.icon) {\r\n <span class=\"icon\">\r\n <!-- N\u1EBFu l\u00E0 icon Zorro -->\r\n @if (suffixConfig.icon.type === 'zorro') {\r\n <span\r\n nz-icon\r\n [nzType]=\"suffixConfig.icon.value\"\r\n nzTheme=\"outline\"\r\n [ngClass]=\"suffixConfig.icon.class ?? ''\"\r\n >\r\n </span>\r\n }\r\n\r\n <!-- N\u1EBFu l\u00E0 Boxicons -->\r\n @if (suffixConfig.icon.type === 'mdi') {\r\n <span [class]=\"'mdi ' + suffixConfig.icon.value\" [ngClass]=\"suffixConfig.icon.class ?? ''\">\r\n </span>\r\n }\r\n </span>\r\n }\r\n\r\n @if (suffixConfig.label) {\r\n <span class=\"label\">\r\n {{ suffixConfig.label }}\r\n </span>\r\n }\r\n\r\n @if (!suffixConfig.icon && !suffixConfig.label) {\r\n <span class=\"label\">\r\n {{ defaultText }}\r\n </span>\r\n }\r\n</button>\r\n", styles: [".w-100{width:100%}.h-100{height:100%}.flex{display:flex}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.items-center{align-items:center}.mb-0{margin-bottom:0!important}.break-work{word-break:break-word}.suffix-btn{margin-left:.5rem}\n"] }]
|
|
4061
2455
|
}], propDecorators: { suffixConfig: [{
|
|
4062
2456
|
type: Input
|
|
4063
2457
|
}], formId: [{
|
|
4064
2458
|
type: Input
|
|
4065
2459
|
}] } });
|
|
4066
2460
|
|
|
4067
|
-
const ZORRO_MODULES$
|
|
2461
|
+
const ZORRO_MODULES$a = [
|
|
4068
2462
|
NzInputModule,
|
|
4069
2463
|
NgxCurrencyDirective,
|
|
4070
2464
|
CurrencyPipe,
|
|
@@ -4136,7 +2530,7 @@ class InputTextComponent extends BaseValueAccessor {
|
|
|
4136
2530
|
}
|
|
4137
2531
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InputTextComponent, decorators: [{
|
|
4138
2532
|
type: Component,
|
|
4139
|
-
args: [{ selector: 'app-input-text', standalone: true, imports: [...ZORRO_MODULES$
|
|
2533
|
+
args: [{ selector: 'app-input-text', standalone: true, imports: [...ZORRO_MODULES$a, ...COMPONENTS$6], providers: [
|
|
4140
2534
|
{
|
|
4141
2535
|
provide: NG_VALUE_ACCESSOR,
|
|
4142
2536
|
useExisting: forwardRef(() => InputTextComponent),
|
|
@@ -4145,244 +2539,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
4145
2539
|
], template: "<div class=\"flex\">\r\n @if (modeSignal() === formModeEnum.View) {\r\n @if (fieldSignal().viewMode === viewModeEnum.form) {\r\n @if (fieldSignal().isCurrency) {\r\n <input\r\n nz-input\r\n [(ngModel)]=\"inputDisplayValue\"\r\n class=\"modeView\"\r\n [currencyMask]=\"{\r\n prefix: '',\r\n suffix: ' VND',\r\n thousands: ',',\r\n decimal: '.',\r\n precision: 2,\r\n align: 'left',\r\n }\"\r\n />\r\n } @else {\r\n <input nz-input [value]=\"inputDisplayValue\" class=\"modeView\" />\r\n }\r\n }\r\n @if (fieldSignal().viewMode === viewModeEnum.text) {\r\n @if (fieldSignal().isCurrency) {\r\n <span class=\"modeView\">\r\n {{ inputDisplayValue | currency: 'VND' : 'symbol' : '1.0-0' : 'vi-VN' }}\r\n </span>\r\n } @else {\r\n <span class=\"modeView\">{{ inputDisplayValue }}</span>\r\n }\r\n }\r\n } @else {\r\n @if (fieldSignal().isCurrency) {\r\n <input\r\n nz-input\r\n class=\"input-control\"\r\n [currencyMask]=\"{\r\n prefix: '',\r\n suffix: ' VND',\r\n thousands: ',',\r\n decimal: '.',\r\n precision: 2,\r\n align: 'left',\r\n }\"\r\n [(ngModel)]=\"value\"\r\n [placeholder]=\"fieldSignal().placeholder!\"\r\n [disabled]=\"isDisabled\"\r\n (ngModelChange)=\"handleChangeFormValue($event)\"\r\n (blur)=\"onTouched()\"\r\n />\r\n } @else {\r\n <input\r\n nz-input\r\n class=\"input-control\"\r\n [value]=\"value\"\r\n [placeholder]=\"fieldSignal().placeholder!\"\r\n [disabled]=\"isDisabled\"\r\n (input)=\"handleChangeInputText($event)\"\r\n (blur)=\"onTouched()\"\r\n />\r\n }\r\n }\r\n\r\n <!-- Suffixes -->\r\n @if (fieldSignal().suffixes.length > 0) {\r\n @for (suffix of fieldSignal().suffixes; track $index) {\r\n @if (suffix.visibleOn?.includes(this.modeSignal())) {\r\n <app-suffix [suffixConfig]=\"suffix\" [formId]=\"formId\"> </app-suffix>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".w-100{width:100%}.h-100{height:100%}.flex{display:flex}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.items-center{align-items:center}.mb-0{margin-bottom:0!important}.break-work{word-break:break-word}.input-text-wrapper{display:flex}.modeView{pointer-events:none;color:#575757;font-size:12px}\n"] }]
|
|
4146
2540
|
}], ctorParameters: () => [] });
|
|
4147
2541
|
|
|
4148
|
-
/**
|
|
4149
|
-
* Interface base chung cho Các dạng Radio.
|
|
4150
|
-
*/
|
|
4151
|
-
class RadioBaseField extends BaseField {
|
|
4152
|
-
/**
|
|
4153
|
-
* Props validation chung cho các dạng Radio.
|
|
4154
|
-
*/
|
|
4155
|
-
// ................. Chưa có .................
|
|
4156
|
-
constructor(init) {
|
|
4157
|
-
super(init);
|
|
4158
|
-
/**
|
|
4159
|
-
* Gán giá trị cho các props cần giá trị mặc định ở Radio.
|
|
4160
|
-
*/
|
|
4161
|
-
this.controlType = ControlTypeEnum.Radio;
|
|
4162
|
-
this.bindLabel = DEFAULT_BIND_LABEL;
|
|
4163
|
-
this.bindValue = DEFAULT_BIND_VALUE;
|
|
4164
|
-
this.dependsOn = [];
|
|
4165
|
-
Object.assign(this, init);
|
|
4166
|
-
}
|
|
4167
|
-
// Mảng ValidatorFn các validation chung được hỗ trợ sẵn bởi Reactive form của angular cho Radio.
|
|
4168
|
-
getCommonSelectValidators() {
|
|
4169
|
-
const validators = this.getCommonValidators();
|
|
4170
|
-
return validators;
|
|
4171
|
-
}
|
|
4172
|
-
// Mảng ValidatorFn các custom validation có thể dùng cho Radio.
|
|
4173
|
-
getCustomSelectValidators() {
|
|
4174
|
-
const validators = this.getCustomValidators();
|
|
4175
|
-
return validators;
|
|
4176
|
-
}
|
|
4177
|
-
// Tạo Form Control cho Input Field.
|
|
4178
|
-
toFormControl() {
|
|
4179
|
-
const commonValidators = this.getCommonSelectValidators();
|
|
4180
|
-
const customValidators = this.getCustomSelectValidators();
|
|
4181
|
-
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
4182
|
-
}
|
|
4183
|
-
getErrorMessage(errorKey, errorValue, allFields) {
|
|
4184
|
-
return super.getErrorMessage(errorKey, errorValue, allFields);
|
|
4185
|
-
}
|
|
4186
|
-
}
|
|
4187
|
-
|
|
4188
|
-
const DEFAULT_REMINDER_TYPE_RADIO_VALUE = null;
|
|
4189
|
-
// const DEFAULT_OPTIONS = [
|
|
4190
|
-
// {
|
|
4191
|
-
// code: 'REMINDERDATE_01',
|
|
4192
|
-
// name: 'Chọn thời điểm',
|
|
4193
|
-
// type: 'select',
|
|
4194
|
-
// },
|
|
4195
|
-
// {
|
|
4196
|
-
// code: 'REMINDERDATE_02',
|
|
4197
|
-
// name: 'Chọn ngày cụ thể',
|
|
4198
|
-
// type: 'date',
|
|
4199
|
-
// },
|
|
4200
|
-
// ] as ReminderOption[];
|
|
4201
|
-
/**
|
|
4202
|
-
* Interface của Select Field type là dạng Reminder Type Radio.
|
|
4203
|
-
*/
|
|
4204
|
-
class ReminderTypeRadioField extends RadioBaseField {
|
|
4205
|
-
constructor(init) {
|
|
4206
|
-
super(init);
|
|
4207
|
-
/**
|
|
4208
|
-
* Gán giá trị cho các props cần giá trị mặc định ở Reminder Type Radio.
|
|
4209
|
-
*/
|
|
4210
|
-
this.type = FieldTypeEnum.ReminderTypeRadio;
|
|
4211
|
-
this.value = DEFAULT_REMINDER_TYPE_RADIO_VALUE;
|
|
4212
|
-
this.initialValue = DEFAULT_REMINDER_TYPE_RADIO_VALUE;
|
|
4213
|
-
// this.options = DEFAULT_OPTIONS;
|
|
4214
|
-
Object.assign(this, init);
|
|
4215
|
-
}
|
|
4216
|
-
getDefaultValue() {
|
|
4217
|
-
return DEFAULT_REMINDER_TYPE_RADIO_VALUE;
|
|
4218
|
-
}
|
|
4219
|
-
toFormValue(modelValue) {
|
|
4220
|
-
console.log('modelValue', modelValue);
|
|
4221
|
-
return modelValue;
|
|
4222
|
-
}
|
|
4223
|
-
getCustomReminderTypeValidators() {
|
|
4224
|
-
const validators = [];
|
|
4225
|
-
if (this.required)
|
|
4226
|
-
validators.push(reminderCustomRequiredValidator());
|
|
4227
|
-
return validators;
|
|
4228
|
-
}
|
|
4229
|
-
// Tạo Form Control cho Input Field.
|
|
4230
|
-
toFormControl() {
|
|
4231
|
-
const customValidators = this.getCustomReminderTypeValidators();
|
|
4232
|
-
return new FormControl(this.value ?? '', [...customValidators]);
|
|
4233
|
-
}
|
|
4234
|
-
}
|
|
4235
|
-
|
|
4236
|
-
class ReminderValue {
|
|
4237
|
-
constructor(init) {
|
|
4238
|
-
this.reminderDate = '';
|
|
4239
|
-
Object.assign(this, init);
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
|
|
4243
|
-
const ZORRO_MODULES$a = [NzRadioModule, NzSelectModule, NzDatePickerModule];
|
|
4244
|
-
class ReminderTypeRadioComponent extends BaseValueAccessor {
|
|
4245
|
-
get selectDetail() {
|
|
4246
|
-
return this.selectedOption.type === 'select'
|
|
4247
|
-
? this.selectedOption.detail
|
|
4248
|
-
: undefined;
|
|
4249
|
-
}
|
|
4250
|
-
get dateDetail() {
|
|
4251
|
-
return this.selectedOption.type === 'date'
|
|
4252
|
-
? this.selectedOption.detail
|
|
4253
|
-
: undefined;
|
|
4254
|
-
}
|
|
4255
|
-
constructor() {
|
|
4256
|
-
super();
|
|
4257
|
-
// Field config từ mảng fields.
|
|
4258
|
-
this.fieldSignal = computed(() => {
|
|
4259
|
-
const base = this.fieldInput();
|
|
4260
|
-
if (!(base instanceof ReminderTypeRadioField) ||
|
|
4261
|
-
(base.type !== FieldTypeEnum.ReminderTypeRadio && base.controlType !== ControlTypeEnum.Radio)) {
|
|
4262
|
-
throw new Error('Expected ReminderTypeRadioField');
|
|
4263
|
-
}
|
|
4264
|
-
return base;
|
|
4265
|
-
});
|
|
4266
|
-
this.options = [];
|
|
4267
|
-
this.selectedOptionCode = '';
|
|
4268
|
-
this.deadline = null;
|
|
4269
|
-
this.disabledAfterLimit = (current) => {
|
|
4270
|
-
if (!this.deadline)
|
|
4271
|
-
return false;
|
|
4272
|
-
// clone và normalize limitDate
|
|
4273
|
-
const dayBefore = new Date(this.deadline);
|
|
4274
|
-
dayBefore.setHours(0, 0, 0, 0);
|
|
4275
|
-
dayBefore.setDate(dayBefore.getDate());
|
|
4276
|
-
// normalize current
|
|
4277
|
-
const cur = new Date(current);
|
|
4278
|
-
cur.setHours(0, 0, 0, 0);
|
|
4279
|
-
return cur >= dayBefore;
|
|
4280
|
-
};
|
|
4281
|
-
this.toast = inject(ToastrService);
|
|
4282
|
-
// 1. Effect chung Add + Edit
|
|
4283
|
-
effect(() => {
|
|
4284
|
-
if (this.isAddOrEdit()) {
|
|
4285
|
-
const field = untracked(() => this.fieldSignal());
|
|
4286
|
-
const { options } = field;
|
|
4287
|
-
// Reminder option.
|
|
4288
|
-
this.options = options || [];
|
|
4289
|
-
// Mặc định chọn index 0 của radio reminder.
|
|
4290
|
-
if (this.options.length > 0) {
|
|
4291
|
-
this.selectedOption = this.options[0];
|
|
4292
|
-
this.selectedOptionCode = this.options[0].code;
|
|
4293
|
-
this.value = new ReminderValue({
|
|
4294
|
-
reminderDateType: {
|
|
4295
|
-
code: this.selectedOption.code,
|
|
4296
|
-
},
|
|
4297
|
-
reminderDate: '',
|
|
4298
|
-
});
|
|
4299
|
-
}
|
|
4300
|
-
}
|
|
4301
|
-
});
|
|
4302
|
-
effect(() => {
|
|
4303
|
-
const key = this.fieldSignal().dependsOn[0].fieldKey;
|
|
4304
|
-
const dependValuesMap = this.fieldService.getDependValuesMap();
|
|
4305
|
-
const dependSignal = dependValuesMap.get(key); // <- giữ nguyên signal
|
|
4306
|
-
if (dependSignal) {
|
|
4307
|
-
const value = dependSignal(); // lấy giá trị hiện tại
|
|
4308
|
-
if (value)
|
|
4309
|
-
this.deadline = new Date(value);
|
|
4310
|
-
else
|
|
4311
|
-
this.deadline = null;
|
|
4312
|
-
}
|
|
4313
|
-
});
|
|
4314
|
-
}
|
|
4315
|
-
// Chạy khi value form được gán bởi các hàm reset, patchValue, setValue.
|
|
4316
|
-
writeValueToView(value) {
|
|
4317
|
-
// if (this.isView()) {
|
|
4318
|
-
// this.displayValue = this.getDisplayValue(
|
|
4319
|
-
// this.fieldSignal().modelValue as string | Record<string, unknown>,
|
|
4320
|
-
// );
|
|
4321
|
-
// return;
|
|
4322
|
-
// }
|
|
4323
|
-
if (!this.isAddOrEdit())
|
|
4324
|
-
return;
|
|
4325
|
-
const { key } = untracked(() => this.fieldSignal());
|
|
4326
|
-
// Gán data vào đầu ra của form.
|
|
4327
|
-
const newOutputValue = value;
|
|
4328
|
-
this.patchFormData(key, newOutputValue);
|
|
4329
|
-
}
|
|
4330
|
-
// Xử lý phụ thuộc khi change value
|
|
4331
|
-
handleValueChange(value) {
|
|
4332
|
-
const newOutputValue = value && new ReminderValue({ ...value });
|
|
4333
|
-
if (this.selectedOption.type === 'date') {
|
|
4334
|
-
const date = newOutputValue.reminderDate;
|
|
4335
|
-
newOutputValue.reminderDate = value && formatDateToYYYYMMDD(date);
|
|
4336
|
-
}
|
|
4337
|
-
this.patchFormData(this.fieldSignal().key, newOutputValue);
|
|
4338
|
-
}
|
|
4339
|
-
reminderRadioChange(radioValue) {
|
|
4340
|
-
const currOption = this.options.find(o => o.code === radioValue);
|
|
4341
|
-
if (currOption) {
|
|
4342
|
-
this.selectedOption = currOption;
|
|
4343
|
-
this.selectedOptionCode = currOption.code;
|
|
4344
|
-
if (currOption.type === 'date' && !this.deadline)
|
|
4345
|
-
this.toast.warning(`Chọn ${this.fieldSignal().dependsOn[0].fieldLabel} trước khi chọn ngày cụ thể cho việc Nhắc báo cáo`);
|
|
4346
|
-
}
|
|
4347
|
-
this.value = new ReminderValue({
|
|
4348
|
-
reminderDateType: {
|
|
4349
|
-
code: radioValue,
|
|
4350
|
-
},
|
|
4351
|
-
reminderDate: '',
|
|
4352
|
-
});
|
|
4353
|
-
}
|
|
4354
|
-
changeOptionSelect(optionValue) {
|
|
4355
|
-
this.propagateValue(new ReminderValue({
|
|
4356
|
-
...this.value,
|
|
4357
|
-
reminderDate: optionValue,
|
|
4358
|
-
}));
|
|
4359
|
-
}
|
|
4360
|
-
changeDate(date) {
|
|
4361
|
-
this.propagateValue(new ReminderValue({
|
|
4362
|
-
...this.value,
|
|
4363
|
-
reminderDate: date,
|
|
4364
|
-
}));
|
|
4365
|
-
}
|
|
4366
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ReminderTypeRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4367
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ReminderTypeRadioComponent, isStandalone: true, selector: "app-reminder-type-radio", providers: [
|
|
4368
|
-
{
|
|
4369
|
-
provide: NG_VALUE_ACCESSOR,
|
|
4370
|
-
useExisting: forwardRef(() => ReminderTypeRadioComponent),
|
|
4371
|
-
multi: true,
|
|
4372
|
-
},
|
|
4373
|
-
], usesInheritance: true, ngImport: i0, template: "@if (modeSignal() === formModeEnum.View) {\r\n <!-- <input nz-input [value]=\"displayValue\" class=\"modeView\" /> -->\r\n} @else {\r\n <!-- *ngIf=\"!!deadline\" -->\r\n <div class=\"reminder-wrapper\">\r\n <div class=\"reminder\">\r\n <div class=\"radio-group\">\r\n <nz-radio-group\r\n [ngModel]=\"selectedOptionCode\"\r\n (ngModelChange)=\"reminderRadioChange($event)\"\r\n >\r\n <label *ngFor=\"let opt of options\" nz-radio [nzValue]=\"opt.code\">\r\n {{ opt.name }}\r\n </label>\r\n </nz-radio-group>\r\n </div>\r\n\r\n <div class=\"detail-for-radio\" [ngSwitch]=\"selectedOption.type\">\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <nz-select\r\n [nzPlaceHolder]=\"'Ch\u1ECDn th\u1EDDi \u0111i\u1EC3m nh\u1EAFc b\u00E1o c\u00E1o'\"\r\n [nzAllowClear]=\"true\"\r\n [ngModel]=\"value!.reminderDate || ''\"\r\n (ngModelChange)=\"changeOptionSelect($event)\"\r\n >\r\n <nz-option\r\n *ngFor=\"let o of selectDetail?.options\"\r\n [nzValue]=\"o?.code || ''\"\r\n [nzLabel]=\"o?.name || ''\"\r\n ></nz-option>\r\n </nz-select>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n <div>\r\n <nz-date-picker\r\n [ngModel]=\"value!.reminderDate || null\"\r\n (ngModelChange)=\"changeDate($event)\"\r\n [nzAllowClear]=\"true\"\r\n [nzPlaceHolder]=\"'Ch\u1ECDn ng\u00E0y nh\u1EAFc b\u00E1o c\u00E1o c\u1EE5 th\u1EC3'\"\r\n [nzDisabledDate]=\"disabledAfterLimit\"\r\n [nzDisabled]=\"!deadline\"\r\n ></nz-date-picker>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <!-- <div\r\n class=\"\"\r\n [ngClass]=\"{\r\n 'background-disabled': !deadline,\r\n }\"\r\n ></div> -->\r\n </div>\r\n}\r\n", styles: [".reminder-wrapper{position:relative}.reminder-wrapper .background-disabled{position:absolute;inset:0;z-index:99;background-color:#f5f5f5;opacity:.5}.radio-group{margin-bottom:.5rem}.detail-for-radio nz-date-picker{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: i1$2.CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "ngmodule", type: NzRadioModule }, { kind: "component", type: i3$1.NzRadioComponent, selector: "[nz-radio],[nz-radio-button]", inputs: ["nzValue", "nzDisabled", "nzAutoFocus", "nz-radio-button"], exportAs: ["nzRadio"] }, { kind: "component", type: i3$1.NzRadioGroupComponent, selector: "nz-radio-group", inputs: ["nzDisabled", "nzButtonStyle", "nzSize", "nzName"], exportAs: ["nzRadioGroup"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i4$1.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i4$1.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzBorderless", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i2.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzAllowClear", "nzAutoFocus", "nzDisabled", "nzBorderless", "nzInputReadOnly", "nzInline", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }] }); }
|
|
4374
|
-
}
|
|
4375
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ReminderTypeRadioComponent, decorators: [{
|
|
4376
|
-
type: Component,
|
|
4377
|
-
args: [{ selector: 'app-reminder-type-radio', standalone: true, imports: [...CORE_MODULES, ...FORM_MODULES, ...ZORRO_MODULES$a], providers: [
|
|
4378
|
-
{
|
|
4379
|
-
provide: NG_VALUE_ACCESSOR,
|
|
4380
|
-
useExisting: forwardRef(() => ReminderTypeRadioComponent),
|
|
4381
|
-
multi: true,
|
|
4382
|
-
},
|
|
4383
|
-
], template: "@if (modeSignal() === formModeEnum.View) {\r\n <!-- <input nz-input [value]=\"displayValue\" class=\"modeView\" /> -->\r\n} @else {\r\n <!-- *ngIf=\"!!deadline\" -->\r\n <div class=\"reminder-wrapper\">\r\n <div class=\"reminder\">\r\n <div class=\"radio-group\">\r\n <nz-radio-group\r\n [ngModel]=\"selectedOptionCode\"\r\n (ngModelChange)=\"reminderRadioChange($event)\"\r\n >\r\n <label *ngFor=\"let opt of options\" nz-radio [nzValue]=\"opt.code\">\r\n {{ opt.name }}\r\n </label>\r\n </nz-radio-group>\r\n </div>\r\n\r\n <div class=\"detail-for-radio\" [ngSwitch]=\"selectedOption.type\">\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <nz-select\r\n [nzPlaceHolder]=\"'Ch\u1ECDn th\u1EDDi \u0111i\u1EC3m nh\u1EAFc b\u00E1o c\u00E1o'\"\r\n [nzAllowClear]=\"true\"\r\n [ngModel]=\"value!.reminderDate || ''\"\r\n (ngModelChange)=\"changeOptionSelect($event)\"\r\n >\r\n <nz-option\r\n *ngFor=\"let o of selectDetail?.options\"\r\n [nzValue]=\"o?.code || ''\"\r\n [nzLabel]=\"o?.name || ''\"\r\n ></nz-option>\r\n </nz-select>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n <div>\r\n <nz-date-picker\r\n [ngModel]=\"value!.reminderDate || null\"\r\n (ngModelChange)=\"changeDate($event)\"\r\n [nzAllowClear]=\"true\"\r\n [nzPlaceHolder]=\"'Ch\u1ECDn ng\u00E0y nh\u1EAFc b\u00E1o c\u00E1o c\u1EE5 th\u1EC3'\"\r\n [nzDisabledDate]=\"disabledAfterLimit\"\r\n [nzDisabled]=\"!deadline\"\r\n ></nz-date-picker>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <!-- <div\r\n class=\"\"\r\n [ngClass]=\"{\r\n 'background-disabled': !deadline,\r\n }\"\r\n ></div> -->\r\n </div>\r\n}\r\n", styles: [".reminder-wrapper{position:relative}.reminder-wrapper .background-disabled{position:absolute;inset:0;z-index:99;background-color:#f5f5f5;opacity:.5}.radio-group{margin-bottom:.5rem}.detail-for-radio nz-date-picker{width:100%}\n"] }]
|
|
4384
|
-
}], ctorParameters: () => [] });
|
|
4385
|
-
|
|
4386
2542
|
const ZORRO_MODULES$9 = [NzSelectModule, NzInputModule, NzSpinModule];
|
|
4387
2543
|
const COMPONENTS$5 = [SuffixComponent];
|
|
4388
2544
|
class MultiSelectComponent extends BaseValueAccessor {
|
|
@@ -4585,7 +2741,7 @@ class MultiSelectComponent extends BaseValueAccessor {
|
|
|
4585
2741
|
useExisting: forwardRef(() => MultiSelectComponent),
|
|
4586
2742
|
multi: true,
|
|
4587
2743
|
},
|
|
4588
|
-
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex\">\r\n @if (modeSignal() === formModeEnum.View) {\r\n @if (fieldSignal().viewMode === viewModeEnum.form) {\r\n @if (fieldSignal().value!.length > 0) {\r\n <nz-select\r\n nzAllowClear\r\n [nzMode]=\"'multiple'\"\r\n [(ngModel)]=\"displayOptionsValue\"\r\n [nzMaxTagCount]=\"fieldSignal().maxTagCount!\"\r\n [nzMaxMultipleCount]=\"fieldSignal().maxMultipleCount!\"\r\n [nzDisabled]=\"true\"\r\n >\r\n @for (displayOption of displayOptionsValue; track $index) {\r\n <nz-option [nzValue]=\"displayOption\" [nzLabel]=\"displayOption\"> </nz-option>\r\n }\r\n </nz-select>\r\n } @else {\r\n <input nz-input [value]=\"displayNoValueText\" [disabled]=\"true\" />\r\n }\r\n }\r\n\r\n @if (fieldSignal().viewMode === viewModeEnum.text) {\r\n @if (displayOptionsValue && displayOptionsValue.length) {\r\n <ul class=\"view-list\">\r\n @for (label of displayOptionsValue; track $index) {\r\n <li>{{ label }}</li>\r\n }\r\n </ul>\r\n } @else {\r\n <span class=\"modeView\">{{ displayNoValueText }}</span>\r\n }\r\n }\r\n } @else {\r\n <nz-select\r\n [id]=\"fieldSignal().key\"\r\n nzShowSearch\r\n nzAllowClear\r\n [nzDisabled]=\"isDisabled\"\r\n [nzMode]=\"'multiple'\"\r\n [nzPlaceHolder]=\"fieldSignal().placeholder!\"\r\n [nzMaxTagCount]=\"fieldSignal().maxTagCount!\"\r\n [nzMaxMultipleCount]=\"fieldSignal().maxMultipleCount!\"\r\n [nzSuffixIcon]=\"loading() ? loadingIcon : null\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"handleChangeOptions($event)\"\r\n >\r\n @for (option of options; track `${option[this.fieldSignal().bindValue]}-${$index}`) {\r\n <nz-option [nzValue]=\"transformOptionValue(option)\" [nzLabel]=\"getOptionLabel(option)\">\r\n </nz-option>\r\n }\r\n </nz-select>\r\n\r\n <ng-template #loadingIcon>\r\n <nz-spin nzSimple [nzSize]=\"'small'\"></nz-spin>\r\n </ng-template>\r\n }\r\n\r\n <!-- Suffixes -->\r\n @if (fieldSignal().suffixes.length > 0) {\r\n @for (suffix of fieldSignal().suffixes; track $index) {\r\n @if (suffix.visibleOn?.includes(this.modeSignal())) {\r\n <app-suffix [suffixConfig]=\"suffix\" [formId]=\"formId\"> </app-suffix>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".view-list{margin-bottom:0;padding-inline-start:24px!important}:host ::ng-deep .ant-input[disabled]{background-color:#fff;color:#575757!important;font-size:12px}:host ::ng-deep .ant-select-multiple.ant-select-disabled.ant-select:not(.ant-select-customize-input) .ant-select-selector{background:#fff}:host ::ng-deep .ant-select-disabled.ant-select-multiple .ant-select-selection-item{color:#575757;font-size:12px}\n"], dependencies: [{ kind: "ngmodule", type: i1$2.CommonModule }, { kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type:
|
|
2744
|
+
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex\">\r\n @if (modeSignal() === formModeEnum.View) {\r\n @if (fieldSignal().viewMode === viewModeEnum.form) {\r\n @if (fieldSignal().value!.length > 0) {\r\n <nz-select\r\n nzAllowClear\r\n [nzMode]=\"'multiple'\"\r\n [(ngModel)]=\"displayOptionsValue\"\r\n [nzMaxTagCount]=\"fieldSignal().maxTagCount!\"\r\n [nzMaxMultipleCount]=\"fieldSignal().maxMultipleCount!\"\r\n [nzDisabled]=\"true\"\r\n >\r\n @for (displayOption of displayOptionsValue; track $index) {\r\n <nz-option [nzValue]=\"displayOption\" [nzLabel]=\"displayOption\"> </nz-option>\r\n }\r\n </nz-select>\r\n } @else {\r\n <input nz-input [value]=\"displayNoValueText\" [disabled]=\"true\" />\r\n }\r\n }\r\n\r\n @if (fieldSignal().viewMode === viewModeEnum.text) {\r\n @if (displayOptionsValue && displayOptionsValue.length) {\r\n <ul class=\"view-list\">\r\n @for (label of displayOptionsValue; track $index) {\r\n <li>{{ label }}</li>\r\n }\r\n </ul>\r\n } @else {\r\n <span class=\"modeView\">{{ displayNoValueText }}</span>\r\n }\r\n }\r\n } @else {\r\n <nz-select\r\n [id]=\"fieldSignal().key\"\r\n nzShowSearch\r\n nzAllowClear\r\n [nzDisabled]=\"isDisabled\"\r\n [nzMode]=\"'multiple'\"\r\n [nzPlaceHolder]=\"fieldSignal().placeholder!\"\r\n [nzMaxTagCount]=\"fieldSignal().maxTagCount!\"\r\n [nzMaxMultipleCount]=\"fieldSignal().maxMultipleCount!\"\r\n [nzSuffixIcon]=\"loading() ? loadingIcon : null\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"handleChangeOptions($event)\"\r\n >\r\n @for (option of options; track `${option[this.fieldSignal().bindValue]}-${$index}`) {\r\n <nz-option [nzValue]=\"transformOptionValue(option)\" [nzLabel]=\"getOptionLabel(option)\">\r\n </nz-option>\r\n }\r\n </nz-select>\r\n\r\n <ng-template #loadingIcon>\r\n <nz-spin nzSimple [nzSize]=\"'small'\"></nz-spin>\r\n </ng-template>\r\n }\r\n\r\n <!-- Suffixes -->\r\n @if (fieldSignal().suffixes.length > 0) {\r\n @for (suffix of fieldSignal().suffixes; track $index) {\r\n @if (suffix.visibleOn?.includes(this.modeSignal())) {\r\n <app-suffix [suffixConfig]=\"suffix\" [formId]=\"formId\"> </app-suffix>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".view-list{margin-bottom:0;padding-inline-start:24px!important}:host ::ng-deep .ant-input[disabled]{background-color:#fff;color:#575757!important;font-size:12px}:host ::ng-deep .ant-select-multiple.ant-select-disabled.ant-select:not(.ant-select-customize-input) .ant-select-selector{background:#fff}:host ::ng-deep .ant-select-disabled.ant-select-multiple .ant-select-selection-item{color:#575757;font-size:12px}\n"], dependencies: [{ kind: "ngmodule", type: i1$2.CommonModule }, { kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3$1.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3$1.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzBorderless", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzInputModule }, { kind: "directive", type: i1$1.NzInputDirective, selector: "input[nz-input],textarea[nz-input]", inputs: ["nzBorderless", "nzSize", "nzStepperless", "nzStatus", "disabled"], exportAs: ["nzInput"] }, { kind: "ngmodule", type: NzSpinModule }, { kind: "component", type: i3$2.NzSpinComponent, selector: "nz-spin", inputs: ["nzIndicator", "nzSize", "nzTip", "nzDelay", "nzSimple", "nzSpinning"], exportAs: ["nzSpin"] }, { kind: "component", type: SuffixComponent, selector: "app-suffix", inputs: ["suffixConfig", "formId"] }] }); }
|
|
4589
2745
|
}
|
|
4590
2746
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MultiSelectComponent, decorators: [{
|
|
4591
2747
|
type: Component,
|
|
@@ -4823,7 +2979,7 @@ class SingleSelectComponent extends BaseValueAccessor {
|
|
|
4823
2979
|
useExisting: forwardRef(() => SingleSelectComponent),
|
|
4824
2980
|
multi: true,
|
|
4825
2981
|
},
|
|
4826
|
-
], usesInheritance: true, ngImport: i0, template: "<div class=\"d-flex\">\r\n @if (modeSignal() === formModeEnum.View) {\r\n @if (fieldSignal().viewMode === viewModeEnum.form) {\r\n <input [title]=\"displayValue\" nz-input [value]=\"displayValue\" class=\"modeView\" />\r\n }\r\n @if (fieldSignal().viewMode === viewModeEnum.text) {\r\n <span [title]=\"displayValue\" class=\"modeView break-work\">{{ displayValue }}</span>\r\n }\r\n } @else {\r\n <nz-select\r\n [id]=\"fieldSignal().key\"\r\n nzShowSearch\r\n nzAllowClear\r\n [nzDisabled]=\"isDisabled\"\r\n [nzPlaceHolder]=\"fieldSignal().placeholder!\"\r\n [nzSuffixIcon]=\"loadingSignal() ? loadingIcon : null\"\r\n [title]=\"fieldSignal().tooltip || ''\"\r\n [nzDropdownMatchSelectWidth]=\"fieldSignal().matchSelectWidth\"\r\n [ngModel]=\"value\"\r\n class=\"flex-1 select-box\"\r\n (ngModelChange)=\"handleChangeOptions($event)\"\r\n >\r\n @for (option of options; track `${option[this.fieldSignal().bindValue]}-${$index}`) {\r\n <nz-option [nzValue]=\"transformOptionValue(option)\" [nzLabel]=\"getOptionLabel(option)\">\r\n </nz-option>\r\n }\r\n </nz-select>\r\n\r\n <ng-template #loadingIcon>\r\n <nz-spin nzSimple [nzSize]=\"'small'\"></nz-spin>\r\n </ng-template>\r\n }\r\n\r\n <!-- Suffixes -->\r\n @if (fieldSignal().suffixes.length > 0) {\r\n @for (suffix of fieldSignal().suffixes; track $index) {\r\n @if (suffix.visibleOn?.includes(this.modeSignal())) {\r\n <app-suffix [suffixConfig]=\"suffix\" [formId]=\"formId\"> </app-suffix>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".w-100{width:100%}.h-100{height:100%}.flex{display:flex}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.items-center{align-items:center}.mb-0{margin-bottom:0!important}.break-work{word-break:break-word}.modeView{pointer-events:none;color:#575757;font-size:12px}.select-box{min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type:
|
|
2982
|
+
], usesInheritance: true, ngImport: i0, template: "<div class=\"d-flex\">\r\n @if (modeSignal() === formModeEnum.View) {\r\n @if (fieldSignal().viewMode === viewModeEnum.form) {\r\n <input [title]=\"displayValue\" nz-input [value]=\"displayValue\" class=\"modeView\" />\r\n }\r\n @if (fieldSignal().viewMode === viewModeEnum.text) {\r\n <span [title]=\"displayValue\" class=\"modeView break-work\">{{ displayValue }}</span>\r\n }\r\n } @else {\r\n <nz-select\r\n [id]=\"fieldSignal().key\"\r\n nzShowSearch\r\n nzAllowClear\r\n [nzDisabled]=\"isDisabled\"\r\n [nzPlaceHolder]=\"fieldSignal().placeholder!\"\r\n [nzSuffixIcon]=\"loadingSignal() ? loadingIcon : null\"\r\n [title]=\"fieldSignal().tooltip || ''\"\r\n [nzDropdownMatchSelectWidth]=\"fieldSignal().matchSelectWidth\"\r\n [ngModel]=\"value\"\r\n class=\"flex-1 select-box\"\r\n (ngModelChange)=\"handleChangeOptions($event)\"\r\n >\r\n @for (option of options; track `${option[this.fieldSignal().bindValue]}-${$index}`) {\r\n <nz-option [nzValue]=\"transformOptionValue(option)\" [nzLabel]=\"getOptionLabel(option)\">\r\n </nz-option>\r\n }\r\n </nz-select>\r\n\r\n <ng-template #loadingIcon>\r\n <nz-spin nzSimple [nzSize]=\"'small'\"></nz-spin>\r\n </ng-template>\r\n }\r\n\r\n <!-- Suffixes -->\r\n @if (fieldSignal().suffixes.length > 0) {\r\n @for (suffix of fieldSignal().suffixes; track $index) {\r\n @if (suffix.visibleOn?.includes(this.modeSignal())) {\r\n <app-suffix [suffixConfig]=\"suffix\" [formId]=\"formId\"> </app-suffix>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".w-100{width:100%}.h-100{height:100%}.flex{display:flex}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.items-center{align-items:center}.mb-0{margin-bottom:0!important}.break-work{word-break:break-word}.modeView{pointer-events:none;color:#575757;font-size:12px}.select-box{min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3$1.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3$1.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzBorderless", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzSpinModule }, { kind: "component", type: i3$2.NzSpinComponent, selector: "nz-spin", inputs: ["nzIndicator", "nzSize", "nzTip", "nzDelay", "nzSimple", "nzSpinning"], exportAs: ["nzSpin"] }, { kind: "ngmodule", type: NzInputModule }, { kind: "directive", type: i1$1.NzInputDirective, selector: "input[nz-input],textarea[nz-input]", inputs: ["nzBorderless", "nzSize", "nzStepperless", "nzStatus", "disabled"], exportAs: ["nzInput"] }, { kind: "component", type: SuffixComponent, selector: "app-suffix", inputs: ["suffixConfig", "formId"] }] }); }
|
|
4827
2983
|
}
|
|
4828
2984
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SingleSelectComponent, decorators: [{
|
|
4829
2985
|
type: Component,
|
|
@@ -5234,6 +3390,7 @@ class AvatarUploadComponent extends BaseValueAccessor {
|
|
|
5234
3390
|
this.objStorageList = signal([]);
|
|
5235
3391
|
this.fileService = inject(FileService);
|
|
5236
3392
|
this.formConfigService = inject(FormConfigService);
|
|
3393
|
+
this.nzNotification = inject(NzNotificationService);
|
|
5237
3394
|
this.beforeUpload = (file, fileList) => {
|
|
5238
3395
|
// const originFile = file?.originFileObj as unknown as File; // có thể ở version mới nhất đã fix lỗi NzUploadFile nhưng trả về File của tham số file.
|
|
5239
3396
|
// Xử lý gán vào CVA để tiến hành validation theo các props đã truyền vào.
|
|
@@ -5372,8 +3529,9 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5372
3529
|
this.fieldSignal = computed(() => {
|
|
5373
3530
|
const base = this.fieldInput();
|
|
5374
3531
|
if (!(base instanceof MultiFileUploadField) ||
|
|
5375
|
-
(base.type !== FieldTypeEnum.MultiFileUpload &&
|
|
5376
|
-
|
|
3532
|
+
(base.type !== FieldTypeEnum.MultiFileUpload &&
|
|
3533
|
+
base.controlType !== ControlTypeEnum.Upload)) {
|
|
3534
|
+
throw new Error("Expected MultiFileUploadField");
|
|
5377
3535
|
}
|
|
5378
3536
|
return base;
|
|
5379
3537
|
});
|
|
@@ -5382,7 +3540,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5382
3540
|
// Mảng đại diện các images.
|
|
5383
3541
|
this.allFiles = signal([]);
|
|
5384
3542
|
// Text hướng dẫn các loại extension được upload.
|
|
5385
|
-
this.fileExtensionGuide = signal(
|
|
3543
|
+
this.fileExtensionGuide = signal("");
|
|
5386
3544
|
// Đếm số file đã pass beforeUpload nhưng chưa upload xong (đang trong hàng chờ customRequest/uploadFile).
|
|
5387
3545
|
// Dùng để tính toán count chính xác khi user chọn nhiều file cùng lúc.
|
|
5388
3546
|
this.pendingCount = signal(0);
|
|
@@ -5394,6 +3552,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5394
3552
|
this.toast = inject(ToastrService);
|
|
5395
3553
|
this.translate = inject(TranslateService);
|
|
5396
3554
|
this.nzMessage = inject(NzMessageService);
|
|
3555
|
+
this.nzNotification = inject(NzNotificationService);
|
|
5397
3556
|
this.formConfigService = inject(FormConfigService);
|
|
5398
3557
|
/**
|
|
5399
3558
|
* Xử lý trước khi upload — trả về Observable<boolean> để hỗ trợ batch detection.
|
|
@@ -5405,7 +3564,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5405
3564
|
* batch một lần, cho phép kiểm tra tổng số lượng chính xác trước khi upload.
|
|
5406
3565
|
*/
|
|
5407
3566
|
this.beforeUpload = (file) => {
|
|
5408
|
-
return new Observable(subscriber => {
|
|
3567
|
+
return new Observable((subscriber) => {
|
|
5409
3568
|
// Đẩy file vào buffer với hàm resolve tương ứng.
|
|
5410
3569
|
this._batchBuffer.push({
|
|
5411
3570
|
file,
|
|
@@ -5427,8 +3586,8 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5427
3586
|
const originFile = item.file;
|
|
5428
3587
|
// Nếu validation không thỏa, dừng tại đây và xóa optimistic entry khỏi list.
|
|
5429
3588
|
if (this.control?.invalid) {
|
|
5430
|
-
this.pendingCount.update(c => Math.max(0, c - 1));
|
|
5431
|
-
this.allFiles.update(curr => curr.filter(f => f.uid !== uid));
|
|
3589
|
+
this.pendingCount.update((c) => Math.max(0, c - 1));
|
|
3590
|
+
this.allFiles.update((curr) => curr.filter((f) => f.uid !== uid));
|
|
5432
3591
|
return of(null).subscribe(); // fake subscription.
|
|
5433
3592
|
}
|
|
5434
3593
|
// Validation đã thỏa, tiến hành upload file.
|
|
@@ -5439,7 +3598,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5439
3598
|
if (this.isAddOrEdit()) {
|
|
5440
3599
|
const field = untracked(() => this.fieldSignal());
|
|
5441
3600
|
const { acceptedMimeTypes } = field;
|
|
5442
|
-
this.fileExtensionGuide.set(`Hỗ trợ các định dạng ${getExtensions(acceptedMimeTypes).join(
|
|
3601
|
+
this.fileExtensionGuide.set(`Hỗ trợ các định dạng ${getExtensions(acceptedMimeTypes).join(", ")}`);
|
|
5443
3602
|
}
|
|
5444
3603
|
});
|
|
5445
3604
|
// Trigger reset từ bên ngoài form.
|
|
@@ -5471,11 +3630,11 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5471
3630
|
this.objStorageList.set(objStorageList);
|
|
5472
3631
|
// Khởi tạo danh sách MyUploadFile từ data json.
|
|
5473
3632
|
// File từ server luôn ở trạng thái success.
|
|
5474
|
-
const myUploadFiles = objStorageList.map(objStorage => ({
|
|
3633
|
+
const myUploadFiles = objStorageList.map((objStorage) => ({
|
|
5475
3634
|
objStorage,
|
|
5476
3635
|
isTemp: false,
|
|
5477
|
-
previewUrl: this.formConfigService.getViewFileUrl(objStorage.name ??
|
|
5478
|
-
uploadStatus:
|
|
3636
|
+
previewUrl: this.formConfigService.getViewFileUrl(objStorage.name ?? "", false),
|
|
3637
|
+
uploadStatus: "success",
|
|
5479
3638
|
}));
|
|
5480
3639
|
this.allFiles.set(myUploadFiles);
|
|
5481
3640
|
}
|
|
@@ -5498,7 +3657,12 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5498
3657
|
const currentCount = this.allFiles().length + this.pendingCount();
|
|
5499
3658
|
// ── CHECK 1: Tổng batch vượt maxFileCount → reject TOÀN BỘ batch ──
|
|
5500
3659
|
if (currentCount + batch.length > maxFileCount) {
|
|
5501
|
-
this.toast.warning(
|
|
3660
|
+
// this.toast.warning(
|
|
3661
|
+
// this.translate.instant(
|
|
3662
|
+
// `Không thể tải lên: số lượng file chọn (${batch.length}) vượt quá giới hạn cho phép. Tối đa ${maxFileCount} tập tin.`,
|
|
3663
|
+
// ),
|
|
3664
|
+
// );
|
|
3665
|
+
this.nzNotification.warning("Cảnh báo", `Không thể tải lên: số lượng file tải lên vượt quá giới hạn cho phép. Tối đa ${maxFileCount} tập tin.`, { nzDuration: 4500 });
|
|
5502
3666
|
batch.forEach(({ resolve }) => resolve(false));
|
|
5503
3667
|
return;
|
|
5504
3668
|
}
|
|
@@ -5506,7 +3670,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5506
3670
|
for (const { file, resolve } of batch) {
|
|
5507
3671
|
const originFile = file;
|
|
5508
3672
|
const fileSizeInMB = formatFileSize(file.size, FileSizeTypeEnum.MB);
|
|
5509
|
-
const fileType = file.type ??
|
|
3673
|
+
const fileType = file.type ?? "";
|
|
5510
3674
|
const isSizeOk = fileSizeInMB <= maxSizeInMB;
|
|
5511
3675
|
const isMimeOk = acceptedMimeTypes.includes(fileType);
|
|
5512
3676
|
if (!isSizeOk || !isMimeOk) {
|
|
@@ -5516,7 +3680,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5516
3680
|
reasons.push(`định dạng không hợp lệ`);
|
|
5517
3681
|
if (!isSizeOk)
|
|
5518
3682
|
reasons.push(`kích thước vượt ${maxSizeInMB}MB`);
|
|
5519
|
-
this.nzMessage.error(`"${file.name}" không được tải lên: ${reasons.join(
|
|
3683
|
+
this.nzMessage.error(`"${file.name}" không được tải lên: ${reasons.join(", ")}.`);
|
|
5520
3684
|
// Propagate để CVA/validator hiển thị trạng thái lỗi trên control.
|
|
5521
3685
|
this.propagateValue(originFile);
|
|
5522
3686
|
resolve(false);
|
|
@@ -5525,25 +3689,25 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5525
3689
|
// File hợp lệ: thêm optimistic entry vào allFiles với trạng thái loading ngay lập tức.
|
|
5526
3690
|
// Dùng file.uid (do nz-upload tự tạo, duy nhất cho mỗi NzUploadFile) làm key để track và update sau này.
|
|
5527
3691
|
const uid = file.uid;
|
|
5528
|
-
this.allFiles.update(curr => [
|
|
3692
|
+
this.allFiles.update((curr) => [
|
|
5529
3693
|
...curr,
|
|
5530
3694
|
{
|
|
5531
3695
|
uid,
|
|
5532
3696
|
objStorage: { id: uid, name: originFile.name }, // placeholder, sẽ được thay bằng data thật sau khi upload
|
|
5533
3697
|
isTemp: true,
|
|
5534
|
-
previewUrl:
|
|
5535
|
-
uploadStatus:
|
|
3698
|
+
previewUrl: "",
|
|
3699
|
+
uploadStatus: "loading",
|
|
5536
3700
|
},
|
|
5537
3701
|
]);
|
|
5538
3702
|
// Propagate để trigger validator, tăng pendingCount, cho phép upload.
|
|
5539
3703
|
this.propagateValue(originFile);
|
|
5540
|
-
this.pendingCount.update(c => c + 1);
|
|
3704
|
+
this.pendingCount.update((c) => c + 1);
|
|
5541
3705
|
resolve(true);
|
|
5542
3706
|
}
|
|
5543
3707
|
}
|
|
5544
3708
|
uploadFile(file, uid) {
|
|
5545
3709
|
const { apiFileConfig, uploadTemp } = this.fieldSignal();
|
|
5546
|
-
const newFileName = this.formConfigService.generateRandomFileName(file.name,
|
|
3710
|
+
const newFileName = this.formConfigService.generateRandomFileName(file.name, "file");
|
|
5547
3711
|
const renamedFile = new File([file], newFileName, { type: file.type });
|
|
5548
3712
|
const mimeCfg = apiFileConfig.perMimeTypeConfig?.[file.type]; // Lấy mimeTypes file upload.
|
|
5549
3713
|
const uploadUrl = mimeCfg?.uploadUrl ?? apiFileConfig.uploadUrl; // Nếu có config upload riêng cho mimeTypes đang kiểm tra.
|
|
@@ -5552,30 +3716,30 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5552
3716
|
: (() => {
|
|
5553
3717
|
// Mặc định
|
|
5554
3718
|
const fd = new FormData();
|
|
5555
|
-
fd.append(
|
|
3719
|
+
fd.append("File", renamedFile);
|
|
5556
3720
|
// fd.append('isTemp', 'true');
|
|
5557
3721
|
return fd;
|
|
5558
3722
|
})();
|
|
5559
3723
|
if (uploadTemp)
|
|
5560
|
-
formData.append(
|
|
3724
|
+
formData.append("isTemp", "true");
|
|
5561
3725
|
return this.fileService
|
|
5562
3726
|
.uploadFile(renamedFile, formData, { uploadUrl })
|
|
5563
3727
|
.pipe(this.takeUntilDestroy())
|
|
5564
3728
|
.subscribe({
|
|
5565
|
-
next: res => {
|
|
3729
|
+
next: (res) => {
|
|
5566
3730
|
// Giảm pending dù thành công hay thất bại.
|
|
5567
|
-
this.pendingCount.update(c => Math.max(0, c - 1));
|
|
3731
|
+
this.pendingCount.update((c) => Math.max(0, c - 1));
|
|
5568
3732
|
const { code, data, message } = res;
|
|
5569
3733
|
if (code === 0) {
|
|
5570
3734
|
// Cập nhật objStorageList với data thật từ server.
|
|
5571
|
-
this.objStorageList.update(curr => [...curr, data]);
|
|
3735
|
+
this.objStorageList.update((curr) => [...curr, data]);
|
|
5572
3736
|
// Thay thế optimistic entry (uid) bằng data thật, chuyển sang trạng thái success.
|
|
5573
|
-
this.allFiles.update(curr => curr.map(f => f.uid === uid
|
|
3737
|
+
this.allFiles.update((curr) => curr.map((f) => f.uid === uid
|
|
5574
3738
|
? {
|
|
5575
3739
|
...f,
|
|
5576
3740
|
objStorage: data,
|
|
5577
|
-
previewUrl: this.formConfigService.getViewFileUrl(data.name ??
|
|
5578
|
-
uploadStatus:
|
|
3741
|
+
previewUrl: this.formConfigService.getViewFileUrl(data.name ?? "", true),
|
|
3742
|
+
uploadStatus: "success",
|
|
5579
3743
|
}
|
|
5580
3744
|
: f));
|
|
5581
3745
|
this.patchFormData(this.fieldKey(), JSON.stringify(this.objStorageList()));
|
|
@@ -5584,7 +3748,9 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5584
3748
|
}
|
|
5585
3749
|
else {
|
|
5586
3750
|
// Cập nhật optimistic entry sang trạng thái error.
|
|
5587
|
-
this.allFiles.update(curr => curr.map(f => f.uid === uid
|
|
3751
|
+
this.allFiles.update((curr) => curr.map((f) => f.uid === uid
|
|
3752
|
+
? { ...f, uploadStatus: "error" }
|
|
3753
|
+
: f));
|
|
5588
3754
|
console.error(message);
|
|
5589
3755
|
// Đồng bộ control value → nếu tất cả file đều fail, objStorageList rỗng → required kích hoạt.
|
|
5590
3756
|
this._syncControlValue();
|
|
@@ -5592,10 +3758,12 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5592
3758
|
},
|
|
5593
3759
|
error: () => {
|
|
5594
3760
|
// Giảm pending khi upload thất bại.
|
|
5595
|
-
this.pendingCount.update(c => Math.max(0, c - 1));
|
|
3761
|
+
this.pendingCount.update((c) => Math.max(0, c - 1));
|
|
5596
3762
|
// Cập nhật optimistic entry sang trạng thái error.
|
|
5597
|
-
this.allFiles.update(curr => curr.map(f =>
|
|
5598
|
-
|
|
3763
|
+
this.allFiles.update((curr) => curr.map((f) => f.uid === uid
|
|
3764
|
+
? { ...f, uploadStatus: "error" }
|
|
3765
|
+
: f));
|
|
3766
|
+
console.error("Lỗi trong quá trình tải tập đến máy chủ!!");
|
|
5599
3767
|
// Đồng bộ control value → nếu tất cả file đều fail → required kích hoạt.
|
|
5600
3768
|
this._syncControlValue();
|
|
5601
3769
|
},
|
|
@@ -5605,20 +3773,20 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5605
3773
|
const { objStorage, uploadStatus } = file;
|
|
5606
3774
|
const { uploadTemp } = this.fieldSignal();
|
|
5607
3775
|
// Loại bỏ và cập nhật danh sách ObjectStorage.
|
|
5608
|
-
const newObjStorageList = this.objStorageList().filter(item => item.id !== objStorage?.id);
|
|
3776
|
+
const newObjStorageList = this.objStorageList().filter((item) => item.id !== objStorage?.id);
|
|
5609
3777
|
this.objStorageList.set(newObjStorageList);
|
|
5610
3778
|
// Loại bỏ và cập nhật danh sách MyUploadFile.
|
|
5611
|
-
const newAllFiles = this.allFiles().filter(item => item.objStorage?.id !== objStorage?.id);
|
|
3779
|
+
const newAllFiles = this.allFiles().filter((item) => item.objStorage?.id !== objStorage?.id);
|
|
5612
3780
|
this.allFiles.set(newAllFiles);
|
|
5613
3781
|
// File ở trạng thái error: upload chưa thành công, không cần gọi API xoá trên server.
|
|
5614
|
-
if (uploadStatus ===
|
|
3782
|
+
if (uploadStatus === "error") {
|
|
5615
3783
|
this.patchFormData(this.fieldKey(), JSON.stringify(this.objStorageList()));
|
|
5616
3784
|
// Đồng bộ control value → nếu xóa hết file error còn lại → required kích hoạt.
|
|
5617
3785
|
this._syncControlValue();
|
|
5618
3786
|
return;
|
|
5619
3787
|
}
|
|
5620
3788
|
if (uploadTemp) {
|
|
5621
|
-
this.deleteObjStorage(objStorage.id ||
|
|
3789
|
+
this.deleteObjStorage(objStorage.id || "");
|
|
5622
3790
|
return;
|
|
5623
3791
|
}
|
|
5624
3792
|
// Cập nhật đầu ra cho form.
|
|
@@ -5628,13 +3796,15 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5628
3796
|
}
|
|
5629
3797
|
deleteObjStorage(id) {
|
|
5630
3798
|
return this.fileService
|
|
5631
|
-
.deleteObjStorage(id, {
|
|
3799
|
+
.deleteObjStorage(id, {
|
|
3800
|
+
deleteUrl: this.fieldSignal().apiFileConfig.deleteUrl,
|
|
3801
|
+
})
|
|
5632
3802
|
.pipe(this.takeUntilDestroy())
|
|
5633
3803
|
.subscribe({
|
|
5634
|
-
next: res => {
|
|
3804
|
+
next: (res) => {
|
|
5635
3805
|
const { code, message } = res;
|
|
5636
3806
|
if (code === 0) {
|
|
5637
|
-
const newObjStorageList = this.objStorageList().filter(item => item.id !== id);
|
|
3807
|
+
const newObjStorageList = this.objStorageList().filter((item) => item.id !== id);
|
|
5638
3808
|
this.objStorageList.set(newObjStorageList);
|
|
5639
3809
|
this.patchFormData(this.fieldKey(), JSON.stringify(this.objStorageList()));
|
|
5640
3810
|
// Đồng bộ control value sau khi server xác nhận xóa.
|
|
@@ -5645,9 +3815,9 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5645
3815
|
}
|
|
5646
3816
|
},
|
|
5647
3817
|
error: () => {
|
|
5648
|
-
console.error(
|
|
3818
|
+
console.error("Lỗi trong quá trình xóa tập tin khỏi máy chủ!!");
|
|
5649
3819
|
// Fake vì hiện api xóa đang có vấn đề.
|
|
5650
|
-
const newObjStorageList = this.objStorageList().filter(item => item.id !== id);
|
|
3820
|
+
const newObjStorageList = this.objStorageList().filter((item) => item.id !== id);
|
|
5651
3821
|
this.objStorageList.set(newObjStorageList);
|
|
5652
3822
|
this.patchFormData(this.fieldKey(), JSON.stringify(this.objStorageList()));
|
|
5653
3823
|
// Đồng bộ control value (fake delete thành công).
|
|
@@ -5662,7 +3832,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5662
3832
|
* - Gọi fileClick để xem file nếu đã upload thành công.
|
|
5663
3833
|
*/
|
|
5664
3834
|
onFileItemClick(file) {
|
|
5665
|
-
if (file.uploadStatus ===
|
|
3835
|
+
if (file.uploadStatus === "loading" || file.uploadStatus === "error")
|
|
5666
3836
|
return;
|
|
5667
3837
|
this.fileService.onFileClick(file.objStorage);
|
|
5668
3838
|
}
|
|
@@ -5699,7 +3869,7 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5699
3869
|
_syncControlValue() {
|
|
5700
3870
|
const list = this.objStorageList();
|
|
5701
3871
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5702
|
-
this.propagateValue((list.length > 0 ? JSON.stringify(list) :
|
|
3872
|
+
this.propagateValue((list.length > 0 ? JSON.stringify(list) : ""));
|
|
5703
3873
|
}
|
|
5704
3874
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MultiFileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5705
3875
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: MultiFileUploadComponent, isStandalone: true, selector: "app-multi-file-upload", providers: [
|
|
@@ -5708,17 +3878,17 @@ class MultiFileUploadComponent extends BaseValueAccessor {
|
|
|
5708
3878
|
useExisting: forwardRef(() => MultiFileUploadComponent),
|
|
5709
3879
|
multi: true,
|
|
5710
3880
|
},
|
|
5711
|
-
], usesInheritance: true, ngImport: i0, template: "@if (isAddOrEdit()) {\r\n <nz-upload\r\n [nzShowUploadList]=\"false\"\r\n [nzMultiple]=\"true\"\r\n [nzBeforeUpload]=\"beforeUpload\"\r\n [nzCustomRequest]=\"customRequest\"\r\n >\r\n <button nz-button class=\"btn-upload\" nzType=\"primary\" nzSize=\"small\">\r\n <nz-icon nzType=\"upload\" nzTheme=\"outline\" />\r\n <span class=\"font-size-12\">T\u1EA3i l\u00EAn</span>\r\n </button>\r\n\r\n <span class=\"font-size-12\">( T\u1ED1i \u0111a {{ fieldSignal().maxFileCount }} t\u1EADp tin )</span>\r\n\r\n @if (fieldSignal().showAcceptedExtensionGuide) {\r\n <div class=\"file-extension-guide\">{{ fileExtensionGuide() }}</div>\r\n }\r\n </nz-upload>\r\n}\r\n\r\n<!-- Custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n@if (allFiles().length > 0) {\r\n <ng-container *ngTemplateOutlet=\"renderFileTpl\"></ng-container>\r\n}\r\n\r\n<!-- Template custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n<ng-template #renderFileTpl>\r\n <ul class=\"custom-upload-list\">\r\n @for (file of allFiles(); track file.objStorage?.id) {\r\n <li\r\n class=\"upload-item\"\r\n [class.is-loading]=\"file.uploadStatus === 'loading'\"\r\n [class.is-error]=\"file.uploadStatus === 'error'\"\r\n (click)=\"onFileItemClick(file)\"\r\n >\r\n <!-- Icon tr\u1EA1ng th\u00E1i b\u00EAn tr\u00E1i -->\r\n @switch (file.uploadStatus) {\r\n @case (
|
|
3881
|
+
], usesInheritance: true, ngImport: i0, template: "@if (isAddOrEdit()) {\r\n <nz-upload\r\n [nzShowUploadList]=\"false\"\r\n [nzMultiple]=\"true\"\r\n [nzBeforeUpload]=\"beforeUpload\"\r\n [nzCustomRequest]=\"customRequest\"\r\n >\r\n <button nz-button class=\"btn-upload\" nzType=\"primary\" nzSize=\"small\">\r\n <nz-icon nzType=\"upload\" nzTheme=\"outline\" />\r\n <span class=\"font-size-12\">T\u1EA3i l\u00EAn</span>\r\n </button>\r\n\r\n <span class=\"font-size-12\"\r\n >( T\u1ED1i \u0111a {{ fieldSignal().maxFileCount }} t\u1EADp tin )</span\r\n >\r\n\r\n @if (fieldSignal().showAcceptedExtensionGuide) {\r\n <div class=\"file-extension-guide\">{{ fileExtensionGuide() }}</div>\r\n }\r\n </nz-upload>\r\n}\r\n\r\n<!-- Custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n@if (allFiles().length > 0) {\r\n <ng-container *ngTemplateOutlet=\"renderFileTpl\"></ng-container>\r\n}\r\n\r\n<!-- Template custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n<ng-template #renderFileTpl>\r\n <ul class=\"custom-upload-list\">\r\n @for (file of allFiles(); track file.objStorage?.id) {\r\n <li\r\n class=\"upload-item\"\r\n [class.is-loading]=\"file.uploadStatus === 'loading'\"\r\n [class.is-error]=\"file.uploadStatus === 'error'\"\r\n (click)=\"onFileItemClick(file)\"\r\n >\r\n <!-- Icon tr\u1EA1ng th\u00E1i b\u00EAn tr\u00E1i -->\r\n @switch (file.uploadStatus) {\r\n @case (\"loading\") {\r\n <span\r\n nz-icon\r\n nzType=\"loading\"\r\n nzTheme=\"outline\"\r\n class=\"status-icon loading-icon\"\r\n ></span>\r\n }\r\n @case (\"error\") {\r\n <span\r\n nz-icon\r\n nzType=\"close-circle\"\r\n nzTheme=\"fill\"\r\n class=\"status-icon error-icon\"\r\n ></span>\r\n }\r\n @default {\r\n <span\r\n nz-icon\r\n nzType=\"paper-clip\"\r\n nzTheme=\"outline\"\r\n class=\"paper-clip-icon\"\r\n ></span>\r\n }\r\n }\r\n\r\n <span class=\"file-name\" [title]=\"file.objStorage.name ?? ''\">{{\r\n file.objStorage.name\r\n }}</span>\r\n\r\n @if (isAddOrEdit() && file.uploadStatus !== \"loading\") {\r\n <span\r\n nz-icon\r\n nzType=\"delete\"\r\n nzTheme=\"outline\"\r\n title=\"X\u00F3a\"\r\n class=\"delete-icon\"\r\n (click)=\"handleDelete(file); $event.stopPropagation()\"\r\n ></span>\r\n }\r\n\r\n @if (\r\n !isAddOrEdit() &&\r\n file.uploadStatus !== \"error\" &&\r\n file.uploadStatus !== \"loading\"\r\n ) {\r\n <span\r\n nz-icon\r\n nzType=\"eye\"\r\n nzTheme=\"outline\"\r\n class=\"view-icon\"\r\n title=\"Xem file\"\r\n ></span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n</ng-template>\r\n", styles: [".upload{display:flex}nz-upload .btn-upload{margin-right:.75rem}nz-upload .file-extension-guide{font-size:11px;margin-top:8px;font-style:italic;color:#0f766e}.custom-upload-list{margin-top:.625rem;list-style-type:none;padding-inline-start:10px}.custom-upload-list .upload-item{margin-bottom:.25rem;display:flex;align-items:center;max-width:100%;gap:.5rem;cursor:pointer;border-radius:4px;transition:background-color .15s ease}.custom-upload-list .upload-item .paper-clip-icon{margin-right:.5rem;color:#595959;flex-shrink:0}.custom-upload-list .upload-item .status-icon{flex-shrink:0}.custom-upload-list .upload-item .loading-icon{color:#1677ff}.custom-upload-list .upload-item .error-icon{color:#ff4d4f}.custom-upload-list .upload-item .file-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.custom-upload-list .upload-item .file-name:hover{text-decoration:underline;color:#0a4c9f;cursor:pointer}.custom-upload-list .upload-item .delete-icon{color:red;cursor:pointer;flex-shrink:0}.custom-upload-list .upload-item .view-icon{color:#1677ff;flex-shrink:0;opacity:.6;transition:opacity .15s ease}.custom-upload-list .upload-item:hover .view-icon{opacity:1}.custom-upload-list .upload-item.is-loading{cursor:default;opacity:.75}.custom-upload-list .upload-item.is-loading .file-name:hover{text-decoration:none;color:inherit;cursor:default}.custom-upload-list .upload-item.is-error{cursor:default}.custom-upload-list .upload-item.is-error .file-name{color:#ff4d4f}.custom-upload-list .upload-item.is-error .file-name:hover{text-decoration:none;color:#ff4d4f;cursor:default}\n"], dependencies: [{ kind: "ngmodule", type: i1$2.CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NzUploadModule }, { kind: "component", type: i2$2.NzUploadComponent, selector: "nz-upload", inputs: ["nzType", "nzLimit", "nzSize", "nzFileType", "nzAccept", "nzAction", "nzDirectory", "nzOpenFileDialogOnClick", "nzBeforeUpload", "nzCustomRequest", "nzData", "nzFilter", "nzFileList", "nzDisabled", "nzHeaders", "nzListType", "nzMultiple", "nzName", "nzShowUploadList", "nzShowButton", "nzWithCredentials", "nzRemove", "nzPreview", "nzPreviewFile", "nzPreviewIsImage", "nzTransformFile", "nzDownload", "nzIconRender", "nzFileListRender"], outputs: ["nzChange", "nzFileListChange"], exportAs: ["nzUpload"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i3.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i4.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i5.ɵNzTransitionPatchDirective, selector: "[nz-button], nz-button-group, [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i6.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }] }); }
|
|
5712
3882
|
}
|
|
5713
3883
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MultiFileUploadComponent, decorators: [{
|
|
5714
3884
|
type: Component,
|
|
5715
|
-
args: [{ selector:
|
|
3885
|
+
args: [{ selector: "app-multi-file-upload", standalone: true, imports: [...CORE_MODULES, ...ZORRO_MODULES$4], providers: [
|
|
5716
3886
|
{
|
|
5717
3887
|
provide: NG_VALUE_ACCESSOR,
|
|
5718
3888
|
useExisting: forwardRef(() => MultiFileUploadComponent),
|
|
5719
3889
|
multi: true,
|
|
5720
3890
|
},
|
|
5721
|
-
], template: "@if (isAddOrEdit()) {\r\n <nz-upload\r\n [nzShowUploadList]=\"false\"\r\n [nzMultiple]=\"true\"\r\n [nzBeforeUpload]=\"beforeUpload\"\r\n [nzCustomRequest]=\"customRequest\"\r\n >\r\n <button nz-button class=\"btn-upload\" nzType=\"primary\" nzSize=\"small\">\r\n <nz-icon nzType=\"upload\" nzTheme=\"outline\" />\r\n <span class=\"font-size-12\">T\u1EA3i l\u00EAn</span>\r\n </button>\r\n\r\n <span class=\"font-size-12\">( T\u1ED1i \u0111a {{ fieldSignal().maxFileCount }} t\u1EADp tin )</span>\r\n\r\n @if (fieldSignal().showAcceptedExtensionGuide) {\r\n <div class=\"file-extension-guide\">{{ fileExtensionGuide() }}</div>\r\n }\r\n </nz-upload>\r\n}\r\n\r\n<!-- Custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n@if (allFiles().length > 0) {\r\n <ng-container *ngTemplateOutlet=\"renderFileTpl\"></ng-container>\r\n}\r\n\r\n<!-- Template custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n<ng-template #renderFileTpl>\r\n <ul class=\"custom-upload-list\">\r\n @for (file of allFiles(); track file.objStorage?.id) {\r\n <li\r\n class=\"upload-item\"\r\n [class.is-loading]=\"file.uploadStatus === 'loading'\"\r\n [class.is-error]=\"file.uploadStatus === 'error'\"\r\n (click)=\"onFileItemClick(file)\"\r\n >\r\n <!-- Icon tr\u1EA1ng th\u00E1i b\u00EAn tr\u00E1i -->\r\n @switch (file.uploadStatus) {\r\n @case (
|
|
3891
|
+
], template: "@if (isAddOrEdit()) {\r\n <nz-upload\r\n [nzShowUploadList]=\"false\"\r\n [nzMultiple]=\"true\"\r\n [nzBeforeUpload]=\"beforeUpload\"\r\n [nzCustomRequest]=\"customRequest\"\r\n >\r\n <button nz-button class=\"btn-upload\" nzType=\"primary\" nzSize=\"small\">\r\n <nz-icon nzType=\"upload\" nzTheme=\"outline\" />\r\n <span class=\"font-size-12\">T\u1EA3i l\u00EAn</span>\r\n </button>\r\n\r\n <span class=\"font-size-12\"\r\n >( T\u1ED1i \u0111a {{ fieldSignal().maxFileCount }} t\u1EADp tin )</span\r\n >\r\n\r\n @if (fieldSignal().showAcceptedExtensionGuide) {\r\n <div class=\"file-extension-guide\">{{ fileExtensionGuide() }}</div>\r\n }\r\n </nz-upload>\r\n}\r\n\r\n<!-- Custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n@if (allFiles().length > 0) {\r\n <ng-container *ngTemplateOutlet=\"renderFileTpl\"></ng-container>\r\n}\r\n\r\n<!-- Template custom hi\u1EC3n th\u1ECB danh s\u00E1ch t\u1EADp tin -->\r\n<ng-template #renderFileTpl>\r\n <ul class=\"custom-upload-list\">\r\n @for (file of allFiles(); track file.objStorage?.id) {\r\n <li\r\n class=\"upload-item\"\r\n [class.is-loading]=\"file.uploadStatus === 'loading'\"\r\n [class.is-error]=\"file.uploadStatus === 'error'\"\r\n (click)=\"onFileItemClick(file)\"\r\n >\r\n <!-- Icon tr\u1EA1ng th\u00E1i b\u00EAn tr\u00E1i -->\r\n @switch (file.uploadStatus) {\r\n @case (\"loading\") {\r\n <span\r\n nz-icon\r\n nzType=\"loading\"\r\n nzTheme=\"outline\"\r\n class=\"status-icon loading-icon\"\r\n ></span>\r\n }\r\n @case (\"error\") {\r\n <span\r\n nz-icon\r\n nzType=\"close-circle\"\r\n nzTheme=\"fill\"\r\n class=\"status-icon error-icon\"\r\n ></span>\r\n }\r\n @default {\r\n <span\r\n nz-icon\r\n nzType=\"paper-clip\"\r\n nzTheme=\"outline\"\r\n class=\"paper-clip-icon\"\r\n ></span>\r\n }\r\n }\r\n\r\n <span class=\"file-name\" [title]=\"file.objStorage.name ?? ''\">{{\r\n file.objStorage.name\r\n }}</span>\r\n\r\n @if (isAddOrEdit() && file.uploadStatus !== \"loading\") {\r\n <span\r\n nz-icon\r\n nzType=\"delete\"\r\n nzTheme=\"outline\"\r\n title=\"X\u00F3a\"\r\n class=\"delete-icon\"\r\n (click)=\"handleDelete(file); $event.stopPropagation()\"\r\n ></span>\r\n }\r\n\r\n @if (\r\n !isAddOrEdit() &&\r\n file.uploadStatus !== \"error\" &&\r\n file.uploadStatus !== \"loading\"\r\n ) {\r\n <span\r\n nz-icon\r\n nzType=\"eye\"\r\n nzTheme=\"outline\"\r\n class=\"view-icon\"\r\n title=\"Xem file\"\r\n ></span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n</ng-template>\r\n", styles: [".upload{display:flex}nz-upload .btn-upload{margin-right:.75rem}nz-upload .file-extension-guide{font-size:11px;margin-top:8px;font-style:italic;color:#0f766e}.custom-upload-list{margin-top:.625rem;list-style-type:none;padding-inline-start:10px}.custom-upload-list .upload-item{margin-bottom:.25rem;display:flex;align-items:center;max-width:100%;gap:.5rem;cursor:pointer;border-radius:4px;transition:background-color .15s ease}.custom-upload-list .upload-item .paper-clip-icon{margin-right:.5rem;color:#595959;flex-shrink:0}.custom-upload-list .upload-item .status-icon{flex-shrink:0}.custom-upload-list .upload-item .loading-icon{color:#1677ff}.custom-upload-list .upload-item .error-icon{color:#ff4d4f}.custom-upload-list .upload-item .file-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.custom-upload-list .upload-item .file-name:hover{text-decoration:underline;color:#0a4c9f;cursor:pointer}.custom-upload-list .upload-item .delete-icon{color:red;cursor:pointer;flex-shrink:0}.custom-upload-list .upload-item .view-icon{color:#1677ff;flex-shrink:0;opacity:.6;transition:opacity .15s ease}.custom-upload-list .upload-item:hover .view-icon{opacity:1}.custom-upload-list .upload-item.is-loading{cursor:default;opacity:.75}.custom-upload-list .upload-item.is-loading .file-name:hover{text-decoration:none;color:inherit;cursor:default}.custom-upload-list .upload-item.is-error{cursor:default}.custom-upload-list .upload-item.is-error .file-name{color:#ff4d4f}.custom-upload-list .upload-item.is-error .file-name:hover{text-decoration:none;color:#ff4d4f;cursor:default}\n"] }]
|
|
5722
3892
|
}], ctorParameters: () => [] });
|
|
5723
3893
|
|
|
5724
3894
|
const ZORRO_MODULES$3 = [NzUploadModule, NzIconModule, NzButtonModule, NzImageModule];
|
|
@@ -5741,6 +3911,7 @@ class MultiImageUploadComponent extends BaseValueAccessor {
|
|
|
5741
3911
|
this.fileService = inject(FileService);
|
|
5742
3912
|
this.toast = inject(ToastrService);
|
|
5743
3913
|
this.translate = inject(TranslateService);
|
|
3914
|
+
this.nzNotification = inject(NzNotificationService);
|
|
5744
3915
|
this.formConfigService = inject(FormConfigService);
|
|
5745
3916
|
// Xử lý trước khi upload.
|
|
5746
3917
|
this.beforeUpload = (file) => {
|
|
@@ -5751,7 +3922,10 @@ class MultiImageUploadComponent extends BaseValueAccessor {
|
|
|
5751
3922
|
const { maxFileCount, maxSizeInMB, acceptedMimeTypes } = this.fieldSignal();
|
|
5752
3923
|
// Kiểm tra số lượng tối đa hình ảnh có thể tải lên.
|
|
5753
3924
|
if (this.allFiles().length + 1 > maxFileCount) {
|
|
5754
|
-
this.toast.warning(
|
|
3925
|
+
// this.toast.warning(
|
|
3926
|
+
// this.translate.instant(`Chỉ được phép tải lên tối đa ${maxFileCount} hình ảnh`),
|
|
3927
|
+
// );
|
|
3928
|
+
this.nzNotification.warning('Cảnh báo', `Chỉ được phép tải lên tối đa ${maxFileCount} hình ảnh`, { nzDuration: 4500 });
|
|
5755
3929
|
return false;
|
|
5756
3930
|
}
|
|
5757
3931
|
const fileSizeInMB = formatFileSize(file.size, FileSizeTypeEnum.MB);
|
|
@@ -5937,6 +4111,7 @@ class SingleFileUploadComponent extends BaseValueAccessor {
|
|
|
5937
4111
|
this.fileService = inject(FileService);
|
|
5938
4112
|
this.toast = inject(ToastrService);
|
|
5939
4113
|
this.translate = inject(TranslateService);
|
|
4114
|
+
this.nzNotification = inject(NzNotificationService);
|
|
5940
4115
|
this.formConfigService = inject(FormConfigService);
|
|
5941
4116
|
this.beforeUpload = (file, fileList) => {
|
|
5942
4117
|
// const originFile = file?.originFileObj as unknown as File; // có thể ở version mới nhất đã fix lỗi NzUploadFile nhưng trả về File của tham số file.
|
|
@@ -6140,7 +4315,6 @@ const FIELD_COMPONENTS = [
|
|
|
6140
4315
|
MultiFileUploadComponent,
|
|
6141
4316
|
MultiImageUploadComponent,
|
|
6142
4317
|
AvatarUploadComponent,
|
|
6143
|
-
ReminderTypeRadioComponent,
|
|
6144
4318
|
];
|
|
6145
4319
|
|
|
6146
4320
|
class ValidationService {
|
|
@@ -6192,14 +4366,14 @@ class DynamicFieldComponent {
|
|
|
6192
4366
|
* Lấy thông báo lỗi đầu tiên của trường đang thao tác dựa trên control và field.
|
|
6193
4367
|
*/
|
|
6194
4368
|
getErrorMessage() {
|
|
6195
|
-
return this.validationService.getFirstError(this.control(), this.field(), this.fields()) ??
|
|
4369
|
+
return (this.validationService.getFirstError(this.control(), this.field(), this.fields()) ?? "");
|
|
6196
4370
|
}
|
|
6197
4371
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6198
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: DynamicFieldComponent, isStandalone: true, selector: "app-dynamic-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, formId: { classPropertyName: "formId", publicName: "formId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div [formGroup]=\"formGroup()\">\r\n <nz-form-item>\r\n <nz-form-label\r\n [nzXXl]=\"field().labelGrid.xxl ?? null\"\r\n [nzXl]=\"field().labelGrid.xl ?? null\"\r\n [nzLg]=\"field().labelGrid.lg ?? null\"\r\n [nzMd]=\"field().labelGrid.md ?? null\"\r\n [nzSm]=\"field().labelGrid.sm ?? null\"\r\n [nzXs]=\"field().labelGrid.xs ?? null\"\r\n [nzLabelAlign]=\"field().labelAlign\"\r\n >\r\n @if (field().required) {\r\n <span class=\"required\">*</span>\r\n }\r\n <span class=\"form-label\">\r\n {{ field().label }}\r\n @if (field().sublabel) {\r\n <span class=\"sub-label mb-sm\">{{ field().sublabel }}</span>\r\n }\r\n </span>\r\n </nz-form-label>\r\n\r\n <nz-form-control\r\n [nzXXl]=\"field().controlGrid.xxl ?? null\"\r\n [nzXl]=\"field().controlGrid.xl ?? null\"\r\n [nzLg]=\"field().controlGrid.lg ?? null\"\r\n [nzMd]=\"field().controlGrid.md ?? null\"\r\n [nzSm]=\"field().controlGrid.sm ?? null\"\r\n [nzXs]=\"field().controlGrid.xs ?? null\"\r\n [nzErrorTip]=\"getErrorMessage()\"\r\n >\r\n @switch (field().type) {\r\n <!-- Input text -->\r\n @case (fieldTypeEnum.InputText) {\r\n <app-input-text\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-input-text>\r\n }\r\n\r\n <!-- Input password -->\r\n @case (fieldTypeEnum.InputPassword) {\r\n @if (mode() === formModeEnum.Add) {\r\n <app-input-password\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-input-password>\r\n }\r\n }\r\n\r\n <!-- Textarea -->\r\n @case (fieldTypeEnum.Textarea) {\r\n <app-textarea\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-textarea>\r\n }\r\n\r\n <!-- Single select -->\r\n @case (fieldTypeEnum.SingleSelect) {\r\n <app-single-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-single-select>\r\n }\r\n\r\n <!-- Multi select -->\r\n @case (fieldTypeEnum.MultiSelect) {\r\n <app-multi-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-select>\r\n }\r\n\r\n @case (fieldTypeEnum.TreeSelect) {\r\n <app-tree-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-tree-select>\r\n }\r\n\r\n <!-- Date Picker -->\r\n @case (fieldTypeEnum.DatePicker) {\r\n <app-date-picker\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-date-picker>\r\n }\r\n\r\n <!-- Date Range -->\r\n @case (fieldTypeEnum.DateRange) {\r\n <app-date-range\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-date-range>\r\n }\r\n\r\n <!-- Single File Upload -->\r\n @case (fieldTypeEnum.SingleFileUpload) {\r\n <app-single-file-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-single-file-upload>\r\n }\r\n\r\n <!-- Multi File Upload -->\r\n @case (fieldTypeEnum.MultiFileUpload) {\r\n <app-multi-file-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-file-upload>\r\n }\r\n\r\n <!-- Multi File Upload -->\r\n @case (fieldTypeEnum.MultiImageUpload) {\r\n <app-multi-image-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-image-upload>\r\n }\r\n\r\n <!-- Avatar -->\r\n @case (fieldTypeEnum.AvatarUpload) {\r\n <app-avatar-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-avatar-upload>\r\n }\r\n\r\n <!-- Radio -->\r\n @case (fieldTypeEnum.Radio) {\r\n <span>Radio</span>\r\n }\r\n\r\n @case (fieldTypeEnum.ReminderTypeRadio) {\r\n <app-reminder-type-radio\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-reminder-type-radio>\r\n }\r\n\r\n <!-- Checkbox -->\r\n @case (fieldTypeEnum.Checkbox) {\r\n <span>Checkbox</span>\r\n }\r\n\r\n <!-- Radio -->\r\n @case (fieldTypeEnum.Radio) {\r\n <span>Radio</span>\r\n }\r\n\r\n <!-- Switch -->\r\n @case (fieldTypeEnum.Switch) {\r\n <span>Switch</span>\r\n }\r\n\r\n <!-- Autocomplete -->\r\n @case (fieldTypeEnum.Autocomplete) {\r\n <span>Autocomplete</span>\r\n }\r\n }\r\n </nz-form-control>\r\n </nz-form-item>\r\n</div>\r\n", styles: [".required{color:red;font-weight:700;margin-right:4px}.form-label{white-space:normal!important;word-break:break-word;font-weight:500;font-size:12px}.sub-label{white-space:normal!important;word-break:break-word;font-size:11px;font-style:italic}:host ::ng-deep .ant-form-item{align-items:center}:host ::ng-deep .ant-form-item-label>label{display:inline-block!important}\n"], dependencies: [{ kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NzFormModule }, { kind: "directive", type: i2$3.NzColDirective, selector: "[nz-col],nz-col,nz-form-control,nz-form-label", inputs: ["nzFlex", "nzSpan", "nzOrder", "nzOffset", "nzPush", "nzPull", "nzXs", "nzSm", "nzMd", "nzLg", "nzXl", "nzXXl"], exportAs: ["nzCol"] }, { kind: "directive", type: i2$3.NzRowDirective, selector: "[nz-row],nz-row,nz-form-item", inputs: ["nzAlign", "nzJustify", "nzGutter"], exportAs: ["nzRow"] }, { kind: "component", type: i3$3.NzFormItemComponent, selector: "nz-form-item", exportAs: ["nzFormItem"] }, { kind: "component", type: i3$3.NzFormLabelComponent, selector: "nz-form-label", inputs: ["nzFor", "nzRequired", "nzNoColon", "nzTooltipTitle", "nzTooltipIcon", "nzLabelAlign", "nzLabelWrap"], exportAs: ["nzFormLabel"] }, { kind: "component", type: i3$3.NzFormControlComponent, selector: "nz-form-control", inputs: ["nzSuccessTip", "nzWarningTip", "nzErrorTip", "nzValidatingTip", "nzExtra", "nzAutoTips", "nzDisableAutoTips", "nzHasFeedback", "nzValidateStatus"], exportAs: ["nzFormControl"] }, { kind: "ngmodule", type: NzGridModule }, { kind: "component", type: InputTextComponent, selector: "app-input-text" }, { kind: "component", type: InputPasswordComponent, selector: "app-input-password" }, { kind: "component", type: SingleSelectComponent, selector: "app-single-select", inputs: ["isCustomInputText"] }, { kind: "component", type: MultiSelectComponent, selector: "app-multi-select" }, { kind: "component", type: TreeSelectComponent, selector: "app-tree-select", inputs: ["isCustomInputText"] }, { kind: "component", type: TextareaComponent, selector: "app-textarea" }, { kind: "component", type: DatePickerComponent, selector: "app-date-picker" }, { kind: "component", type: DateRangeComponent, selector: "app-date-range" }, { kind: "component", type: SingleFileUploadComponent, selector: "app-single-file-upload" }, { kind: "component", type: MultiFileUploadComponent, selector: "app-multi-file-upload" }, { kind: "component", type: MultiImageUploadComponent, selector: "app-multi-image-upload" }, { kind: "component", type: AvatarUploadComponent, selector: "app-avatar-upload", inputs: ["size"] }, { kind: "component", type: ReminderTypeRadioComponent, selector: "app-reminder-type-radio" }] }); }
|
|
4372
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: DynamicFieldComponent, isStandalone: true, selector: "app-dynamic-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, formId: { classPropertyName: "formId", publicName: "formId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div [formGroup]=\"formGroup()\">\r\n <nz-form-item>\r\n <nz-form-label\r\n [nzXXl]=\"field().labelGrid.xxl ?? null\"\r\n [nzXl]=\"field().labelGrid.xl ?? null\"\r\n [nzLg]=\"field().labelGrid.lg ?? null\"\r\n [nzMd]=\"field().labelGrid.md ?? null\"\r\n [nzSm]=\"field().labelGrid.sm ?? null\"\r\n [nzXs]=\"field().labelGrid.xs ?? null\"\r\n [nzLabelAlign]=\"field().labelAlign\"\r\n >\r\n @if (field().required) {\r\n <span class=\"required\">*</span>\r\n }\r\n <span class=\"form-label\">\r\n {{ field().label }}\r\n @if (field().sublabel) {\r\n <span class=\"sub-label mb-sm\">{{ field().sublabel }}</span>\r\n }\r\n </span>\r\n </nz-form-label>\r\n\r\n <nz-form-control\r\n [nzXXl]=\"field().controlGrid.xxl ?? null\"\r\n [nzXl]=\"field().controlGrid.xl ?? null\"\r\n [nzLg]=\"field().controlGrid.lg ?? null\"\r\n [nzMd]=\"field().controlGrid.md ?? null\"\r\n [nzSm]=\"field().controlGrid.sm ?? null\"\r\n [nzXs]=\"field().controlGrid.xs ?? null\"\r\n [nzErrorTip]=\"getErrorMessage()\"\r\n >\r\n @switch (field().type) {\r\n <!-- Input text -->\r\n @case (fieldTypeEnum.InputText) {\r\n <app-input-text\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-input-text>\r\n }\r\n\r\n <!-- Input password -->\r\n @case (fieldTypeEnum.InputPassword) {\r\n @if (mode() === formModeEnum.Add) {\r\n <app-input-password\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-input-password>\r\n }\r\n }\r\n\r\n <!-- Textarea -->\r\n @case (fieldTypeEnum.Textarea) {\r\n <app-textarea\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-textarea>\r\n }\r\n\r\n <!-- Single select -->\r\n @case (fieldTypeEnum.SingleSelect) {\r\n <app-single-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-single-select>\r\n }\r\n\r\n <!-- Multi select -->\r\n @case (fieldTypeEnum.MultiSelect) {\r\n <app-multi-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-select>\r\n }\r\n\r\n @case (fieldTypeEnum.TreeSelect) {\r\n <app-tree-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-tree-select>\r\n }\r\n\r\n <!-- Date Picker -->\r\n @case (fieldTypeEnum.DatePicker) {\r\n <app-date-picker\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-date-picker>\r\n }\r\n\r\n <!-- Date Range -->\r\n @case (fieldTypeEnum.DateRange) {\r\n <app-date-range\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-date-range>\r\n }\r\n\r\n <!-- Single File Upload -->\r\n @case (fieldTypeEnum.SingleFileUpload) {\r\n <app-single-file-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-single-file-upload>\r\n }\r\n\r\n <!-- Multi File Upload -->\r\n @case (fieldTypeEnum.MultiFileUpload) {\r\n <app-multi-file-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-file-upload>\r\n }\r\n\r\n <!-- Multi File Upload -->\r\n @case (fieldTypeEnum.MultiImageUpload) {\r\n <app-multi-image-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-image-upload>\r\n }\r\n\r\n <!-- Avatar -->\r\n @case (fieldTypeEnum.AvatarUpload) {\r\n <app-avatar-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-avatar-upload>\r\n }\r\n\r\n <!-- Radio -->\r\n @case (fieldTypeEnum.Radio) {\r\n <span>Radio</span>\r\n }\r\n\r\n <!-- Checkbox -->\r\n @case (fieldTypeEnum.Checkbox) {\r\n <span>Checkbox</span>\r\n }\r\n\r\n <!-- Radio -->\r\n @case (fieldTypeEnum.Radio) {\r\n <span>Radio</span>\r\n }\r\n\r\n <!-- Switch -->\r\n @case (fieldTypeEnum.Switch) {\r\n <span>Switch</span>\r\n }\r\n\r\n <!-- Autocomplete -->\r\n @case (fieldTypeEnum.Autocomplete) {\r\n <span>Autocomplete</span>\r\n }\r\n }\r\n </nz-form-control>\r\n </nz-form-item>\r\n</div>\r\n", styles: [".required{color:red;font-weight:700;margin-right:4px}.form-label{white-space:normal!important;word-break:break-word;font-weight:500;font-size:12px}.sub-label{white-space:normal!important;word-break:break-word;font-size:11px;font-style:italic}:host ::ng-deep .ant-form-item{align-items:center}:host ::ng-deep .ant-form-item-label>label{display:inline-block!important}\n"], dependencies: [{ kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NzFormModule }, { kind: "directive", type: i2$3.NzColDirective, selector: "[nz-col],nz-col,nz-form-control,nz-form-label", inputs: ["nzFlex", "nzSpan", "nzOrder", "nzOffset", "nzPush", "nzPull", "nzXs", "nzSm", "nzMd", "nzLg", "nzXl", "nzXXl"], exportAs: ["nzCol"] }, { kind: "directive", type: i2$3.NzRowDirective, selector: "[nz-row],nz-row,nz-form-item", inputs: ["nzAlign", "nzJustify", "nzGutter"], exportAs: ["nzRow"] }, { kind: "component", type: i3$3.NzFormItemComponent, selector: "nz-form-item", exportAs: ["nzFormItem"] }, { kind: "component", type: i3$3.NzFormLabelComponent, selector: "nz-form-label", inputs: ["nzFor", "nzRequired", "nzNoColon", "nzTooltipTitle", "nzTooltipIcon", "nzLabelAlign", "nzLabelWrap"], exportAs: ["nzFormLabel"] }, { kind: "component", type: i3$3.NzFormControlComponent, selector: "nz-form-control", inputs: ["nzSuccessTip", "nzWarningTip", "nzErrorTip", "nzValidatingTip", "nzExtra", "nzAutoTips", "nzDisableAutoTips", "nzHasFeedback", "nzValidateStatus"], exportAs: ["nzFormControl"] }, { kind: "ngmodule", type: NzGridModule }, { kind: "component", type: InputTextComponent, selector: "app-input-text" }, { kind: "component", type: InputPasswordComponent, selector: "app-input-password" }, { kind: "component", type: SingleSelectComponent, selector: "app-single-select", inputs: ["isCustomInputText"] }, { kind: "component", type: MultiSelectComponent, selector: "app-multi-select" }, { kind: "component", type: TreeSelectComponent, selector: "app-tree-select", inputs: ["isCustomInputText"] }, { kind: "component", type: TextareaComponent, selector: "app-textarea" }, { kind: "component", type: DatePickerComponent, selector: "app-date-picker" }, { kind: "component", type: DateRangeComponent, selector: "app-date-range" }, { kind: "component", type: SingleFileUploadComponent, selector: "app-single-file-upload" }, { kind: "component", type: MultiFileUploadComponent, selector: "app-multi-file-upload" }, { kind: "component", type: MultiImageUploadComponent, selector: "app-multi-image-upload" }, { kind: "component", type: AvatarUploadComponent, selector: "app-avatar-upload", inputs: ["size"] }] }); }
|
|
6199
4373
|
}
|
|
6200
4374
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFieldComponent, decorators: [{
|
|
6201
4375
|
type: Component,
|
|
6202
|
-
args: [{ selector:
|
|
4376
|
+
args: [{ selector: "app-dynamic-field", standalone: true, imports: [...FORM_MODULES, ...ZORRO_MODULES$1, ...COMPONENTS$1], template: "<div [formGroup]=\"formGroup()\">\r\n <nz-form-item>\r\n <nz-form-label\r\n [nzXXl]=\"field().labelGrid.xxl ?? null\"\r\n [nzXl]=\"field().labelGrid.xl ?? null\"\r\n [nzLg]=\"field().labelGrid.lg ?? null\"\r\n [nzMd]=\"field().labelGrid.md ?? null\"\r\n [nzSm]=\"field().labelGrid.sm ?? null\"\r\n [nzXs]=\"field().labelGrid.xs ?? null\"\r\n [nzLabelAlign]=\"field().labelAlign\"\r\n >\r\n @if (field().required) {\r\n <span class=\"required\">*</span>\r\n }\r\n <span class=\"form-label\">\r\n {{ field().label }}\r\n @if (field().sublabel) {\r\n <span class=\"sub-label mb-sm\">{{ field().sublabel }}</span>\r\n }\r\n </span>\r\n </nz-form-label>\r\n\r\n <nz-form-control\r\n [nzXXl]=\"field().controlGrid.xxl ?? null\"\r\n [nzXl]=\"field().controlGrid.xl ?? null\"\r\n [nzLg]=\"field().controlGrid.lg ?? null\"\r\n [nzMd]=\"field().controlGrid.md ?? null\"\r\n [nzSm]=\"field().controlGrid.sm ?? null\"\r\n [nzXs]=\"field().controlGrid.xs ?? null\"\r\n [nzErrorTip]=\"getErrorMessage()\"\r\n >\r\n @switch (field().type) {\r\n <!-- Input text -->\r\n @case (fieldTypeEnum.InputText) {\r\n <app-input-text\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-input-text>\r\n }\r\n\r\n <!-- Input password -->\r\n @case (fieldTypeEnum.InputPassword) {\r\n @if (mode() === formModeEnum.Add) {\r\n <app-input-password\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-input-password>\r\n }\r\n }\r\n\r\n <!-- Textarea -->\r\n @case (fieldTypeEnum.Textarea) {\r\n <app-textarea\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-textarea>\r\n }\r\n\r\n <!-- Single select -->\r\n @case (fieldTypeEnum.SingleSelect) {\r\n <app-single-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-single-select>\r\n }\r\n\r\n <!-- Multi select -->\r\n @case (fieldTypeEnum.MultiSelect) {\r\n <app-multi-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-select>\r\n }\r\n\r\n @case (fieldTypeEnum.TreeSelect) {\r\n <app-tree-select\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-tree-select>\r\n }\r\n\r\n <!-- Date Picker -->\r\n @case (fieldTypeEnum.DatePicker) {\r\n <app-date-picker\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-date-picker>\r\n }\r\n\r\n <!-- Date Range -->\r\n @case (fieldTypeEnum.DateRange) {\r\n <app-date-range\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-date-range>\r\n }\r\n\r\n <!-- Single File Upload -->\r\n @case (fieldTypeEnum.SingleFileUpload) {\r\n <app-single-file-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-single-file-upload>\r\n }\r\n\r\n <!-- Multi File Upload -->\r\n @case (fieldTypeEnum.MultiFileUpload) {\r\n <app-multi-file-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-file-upload>\r\n }\r\n\r\n <!-- Multi File Upload -->\r\n @case (fieldTypeEnum.MultiImageUpload) {\r\n <app-multi-image-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-multi-image-upload>\r\n }\r\n\r\n <!-- Avatar -->\r\n @case (fieldTypeEnum.AvatarUpload) {\r\n <app-avatar-upload\r\n [formControlName]=\"field().key\"\r\n [fieldInput]=\"field()\"\r\n [modeSignal]=\"mode()\"\r\n [control]=\"control()\"\r\n [formId]=\"formId()\"\r\n ></app-avatar-upload>\r\n }\r\n\r\n <!-- Radio -->\r\n @case (fieldTypeEnum.Radio) {\r\n <span>Radio</span>\r\n }\r\n\r\n <!-- Checkbox -->\r\n @case (fieldTypeEnum.Checkbox) {\r\n <span>Checkbox</span>\r\n }\r\n\r\n <!-- Radio -->\r\n @case (fieldTypeEnum.Radio) {\r\n <span>Radio</span>\r\n }\r\n\r\n <!-- Switch -->\r\n @case (fieldTypeEnum.Switch) {\r\n <span>Switch</span>\r\n }\r\n\r\n <!-- Autocomplete -->\r\n @case (fieldTypeEnum.Autocomplete) {\r\n <span>Autocomplete</span>\r\n }\r\n }\r\n </nz-form-control>\r\n </nz-form-item>\r\n</div>\r\n", styles: [".required{color:red;font-weight:700;margin-right:4px}.form-label{white-space:normal!important;word-break:break-word;font-weight:500;font-size:12px}.sub-label{white-space:normal!important;word-break:break-word;font-size:11px;font-style:italic}:host ::ng-deep .ant-form-item{align-items:center}:host ::ng-deep .ant-form-item-label>label{display:inline-block!important}\n"] }]
|
|
6203
4377
|
}] });
|
|
6204
4378
|
|
|
6205
4379
|
const ZORRO_MODULES = [NzFormModule, NzGridModule, NzCollapseModule];
|
|
@@ -6385,7 +4559,7 @@ class DynamicFormComponent {
|
|
|
6385
4559
|
return field.key || `field-${index}`;
|
|
6386
4560
|
}
|
|
6387
4561
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFormComponent, deps: [{ token: DynamicFormService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6388
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: DynamicFormComponent, isStandalone: true, selector: "app-dynamic-forms", inputs: { formId: { classPropertyName: "formId", publicName: "formId", isSignal: false, isRequired: false, transformFunction: null }, valueChangesDebounceTime: { classPropertyName: "valueChangesDebounceTime", publicName: "valueChangesDebounceTime", isSignal: false, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, fieldGroups: { classPropertyName: "fieldGroups", publicName: "fieldGroups", isSignal: true, isRequired: false, transformFunction: null }, displayNonGroupedFields: { classPropertyName: "displayNonGroupedFields", publicName: "displayNonGroupedFields", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formValueChanges: "formValueChanges", formSubmit: "formSubmit", formInvalid: "formInvalid" }, ngImport: i0, template: "@if (formGroup) {\r\n <form [formGroup]=\"formGroup\">\r\n @if (oldFields().length > 0) {\r\n <!-- 1. N\u1EBFu kh\u00F4ng c\u00F3 groups th\u00EC render t\u1EEB fields -->\r\n @if (!fieldGroups() || fieldGroups().length === 0) {\r\n <div nz-row [nzGutter]=\"[12, 0]\">\r\n @for (field of oldFields(); track field?.id) {\r\n @if (!isHiddenField(field)) {\r\n <div\r\n nz-col\r\n [nzXXl]=\"field?.grid?.xxl ?? null\"\r\n [nzXl]=\"field?.grid?.xl ?? null\"\r\n [nzLg]=\"field?.grid?.lg ?? null\"\r\n [nzMd]=\"field?.grid?.md ?? null\"\r\n [nzSm]=\"field?.grid?.sm ?? null\"\r\n [nzXs]=\"field?.grid?.xs ?? null\"\r\n [nzFlex]=\"\r\n !field?.grid?.xs &&\r\n !field?.grid?.sm &&\r\n !field?.grid?.md &&\r\n !field?.grid?.lg &&\r\n !field?.grid?.xl &&\r\n !field?.grid?.xxl\r\n ? '1 0 0'\r\n : null\r\n \"\r\n class=\"field\"\r\n >\r\n <app-dynamic-field\r\n [field]=\"field\"\r\n [fields]=\"oldFields()\"\r\n [mode]=\"mode()\"\r\n [control]=\"formGroup.get(field.key)!\"\r\n [formGroup]=\"formGroup\"\r\n [formId]=\"formId\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n <!-- 2. N\u1EBFu c\u00F3 groups th\u00EC render t\u1EEB groupedFields -->\r\n @if (fieldGroups() && fieldGroups().length > 0) {\r\n <!-- C\u00E1c field noGroup -->\r\n @if (groupedFields().noGroup.length > 0 && displayNonGroupedFields()) {\r\n <div nz-row [nzGutter]=\"24\">\r\n @for (field of groupedFields().noGroup; track field?.id) {\r\n @if (!isHiddenField(field)) {\r\n <div\r\n nz-col\r\n [nzXXl]=\"field?.grid?.xxl ?? null\"\r\n [nzXl]=\"field?.grid?.xl ?? null\"\r\n [nzLg]=\"field?.grid?.lg ?? null\"\r\n [nzMd]=\"field?.grid?.md ?? null\"\r\n [nzSm]=\"field?.grid?.sm ?? null\"\r\n [nzXs]=\"field?.grid?.xs ?? null\"\r\n class=\"field\"\r\n >\r\n <app-dynamic-field\r\n [field]=\"field\"\r\n [fields]=\"oldFields()\"\r\n [mode]=\"mode()\"\r\n [control]=\"formGroup.get(field.key)!\"\r\n [formGroup]=\"formGroup\"\r\n [formId]=\"formId\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Group c\u00E1c field -->\r\n @if (groupedFields().grouped.length > 0) {\r\n <nz-collapse [nzBordered]=\"false\">\r\n @for (groupField of groupedFields().grouped; track groupField.group.code) {\r\n <nz-collapse-panel\r\n [nzHeader]=\"groupField.group.template ?? groupField.group.label\"\r\n [nzActive]=\"groupField.group.isActive\"\r\n [nzShowArrow]=\"groupField.group.showArrow\"\r\n [nzDisabled]=\"groupField.group.disabled\"\r\n [style]=\"collapseCustomStyle\"\r\n class=\"mt-sm\"\r\n >\r\n <div nz-row [nzGutter]=\"24\">\r\n @for (field of groupField.fields; track field?.id) {\r\n @if (!isHiddenField(field)) {\r\n <div\r\n nz-col\r\n [nzXXl]=\"field?.grid?.xxl ?? null\"\r\n [nzXl]=\"field?.grid?.xl ?? null\"\r\n [nzLg]=\"field?.grid?.lg ?? null\"\r\n [nzMd]=\"field?.grid?.md ?? null\"\r\n [nzSm]=\"field?.grid?.sm ?? null\"\r\n [nzXs]=\"field?.grid?.xs ?? null\"\r\n class=\"field\"\r\n >\r\n <app-dynamic-field\r\n [field]=\"field\"\r\n [fields]=\"oldFields()\"\r\n [mode]=\"mode()\"\r\n [control]=\"formGroup.get(field.key)!\"\r\n [formGroup]=\"formGroup\"\r\n [formId]=\"formId\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n </nz-collapse-panel>\r\n }\r\n </nz-collapse>\r\n }\r\n }\r\n }\r\n </form>\r\n}\r\n", styles: [":host ::ng-deep .ant-collapse .ant-collapse-item-disabled>.ant-collapse-header,:host ::ng-deep .ant-collapse .ant-collapse-item-disabled>.ant-collapse-header>.arrow{color:#575757;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzFormModule }, { kind: "directive", type: i2$3.NzColDirective, selector: "[nz-col],nz-col,nz-form-control,nz-form-label", inputs: ["nzFlex", "nzSpan", "nzOrder", "nzOffset", "nzPush", "nzPull", "nzXs", "nzSm", "nzMd", "nzLg", "nzXl", "nzXXl"], exportAs: ["nzCol"] }, { kind: "directive", type: i2$3.NzRowDirective, selector: "[nz-row],nz-row,nz-form-item", inputs: ["nzAlign", "nzJustify", "nzGutter"], exportAs: ["nzRow"] }, { kind: "ngmodule", type: NzGridModule }, { kind: "ngmodule", type: NzCollapseModule }, { kind: "component", type: i4$
|
|
4562
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: DynamicFormComponent, isStandalone: true, selector: "app-dynamic-forms", inputs: { formId: { classPropertyName: "formId", publicName: "formId", isSignal: false, isRequired: false, transformFunction: null }, valueChangesDebounceTime: { classPropertyName: "valueChangesDebounceTime", publicName: "valueChangesDebounceTime", isSignal: false, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, fieldGroups: { classPropertyName: "fieldGroups", publicName: "fieldGroups", isSignal: true, isRequired: false, transformFunction: null }, displayNonGroupedFields: { classPropertyName: "displayNonGroupedFields", publicName: "displayNonGroupedFields", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formValueChanges: "formValueChanges", formSubmit: "formSubmit", formInvalid: "formInvalid" }, ngImport: i0, template: "@if (formGroup) {\r\n <form [formGroup]=\"formGroup\">\r\n @if (oldFields().length > 0) {\r\n <!-- 1. N\u1EBFu kh\u00F4ng c\u00F3 groups th\u00EC render t\u1EEB fields -->\r\n @if (!fieldGroups() || fieldGroups().length === 0) {\r\n <div nz-row [nzGutter]=\"[12, 0]\">\r\n @for (field of oldFields(); track field?.id) {\r\n @if (!isHiddenField(field)) {\r\n <div\r\n nz-col\r\n [nzXXl]=\"field?.grid?.xxl ?? null\"\r\n [nzXl]=\"field?.grid?.xl ?? null\"\r\n [nzLg]=\"field?.grid?.lg ?? null\"\r\n [nzMd]=\"field?.grid?.md ?? null\"\r\n [nzSm]=\"field?.grid?.sm ?? null\"\r\n [nzXs]=\"field?.grid?.xs ?? null\"\r\n [nzFlex]=\"\r\n !field?.grid?.xs &&\r\n !field?.grid?.sm &&\r\n !field?.grid?.md &&\r\n !field?.grid?.lg &&\r\n !field?.grid?.xl &&\r\n !field?.grid?.xxl\r\n ? '1 0 0'\r\n : null\r\n \"\r\n class=\"field\"\r\n >\r\n <app-dynamic-field\r\n [field]=\"field\"\r\n [fields]=\"oldFields()\"\r\n [mode]=\"mode()\"\r\n [control]=\"formGroup.get(field.key)!\"\r\n [formGroup]=\"formGroup\"\r\n [formId]=\"formId\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n <!-- 2. N\u1EBFu c\u00F3 groups th\u00EC render t\u1EEB groupedFields -->\r\n @if (fieldGroups() && fieldGroups().length > 0) {\r\n <!-- C\u00E1c field noGroup -->\r\n @if (groupedFields().noGroup.length > 0 && displayNonGroupedFields()) {\r\n <div nz-row [nzGutter]=\"24\">\r\n @for (field of groupedFields().noGroup; track field?.id) {\r\n @if (!isHiddenField(field)) {\r\n <div\r\n nz-col\r\n [nzXXl]=\"field?.grid?.xxl ?? null\"\r\n [nzXl]=\"field?.grid?.xl ?? null\"\r\n [nzLg]=\"field?.grid?.lg ?? null\"\r\n [nzMd]=\"field?.grid?.md ?? null\"\r\n [nzSm]=\"field?.grid?.sm ?? null\"\r\n [nzXs]=\"field?.grid?.xs ?? null\"\r\n class=\"field\"\r\n >\r\n <app-dynamic-field\r\n [field]=\"field\"\r\n [fields]=\"oldFields()\"\r\n [mode]=\"mode()\"\r\n [control]=\"formGroup.get(field.key)!\"\r\n [formGroup]=\"formGroup\"\r\n [formId]=\"formId\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Group c\u00E1c field -->\r\n @if (groupedFields().grouped.length > 0) {\r\n <nz-collapse [nzBordered]=\"false\">\r\n @for (groupField of groupedFields().grouped; track groupField.group.code) {\r\n <nz-collapse-panel\r\n [nzHeader]=\"groupField.group.template ?? groupField.group.label\"\r\n [nzActive]=\"groupField.group.isActive\"\r\n [nzShowArrow]=\"groupField.group.showArrow\"\r\n [nzDisabled]=\"groupField.group.disabled\"\r\n [style]=\"collapseCustomStyle\"\r\n class=\"mt-sm\"\r\n >\r\n <div nz-row [nzGutter]=\"24\">\r\n @for (field of groupField.fields; track field?.id) {\r\n @if (!isHiddenField(field)) {\r\n <div\r\n nz-col\r\n [nzXXl]=\"field?.grid?.xxl ?? null\"\r\n [nzXl]=\"field?.grid?.xl ?? null\"\r\n [nzLg]=\"field?.grid?.lg ?? null\"\r\n [nzMd]=\"field?.grid?.md ?? null\"\r\n [nzSm]=\"field?.grid?.sm ?? null\"\r\n [nzXs]=\"field?.grid?.xs ?? null\"\r\n class=\"field\"\r\n >\r\n <app-dynamic-field\r\n [field]=\"field\"\r\n [fields]=\"oldFields()\"\r\n [mode]=\"mode()\"\r\n [control]=\"formGroup.get(field.key)!\"\r\n [formGroup]=\"formGroup\"\r\n [formId]=\"formId\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n </nz-collapse-panel>\r\n }\r\n </nz-collapse>\r\n }\r\n }\r\n }\r\n </form>\r\n}\r\n", styles: [":host ::ng-deep .ant-collapse .ant-collapse-item-disabled>.ant-collapse-header,:host ::ng-deep .ant-collapse .ant-collapse-item-disabled>.ant-collapse-header>.arrow{color:#575757;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: i1.FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: i1.ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzFormModule }, { kind: "directive", type: i2$3.NzColDirective, selector: "[nz-col],nz-col,nz-form-control,nz-form-label", inputs: ["nzFlex", "nzSpan", "nzOrder", "nzOffset", "nzPush", "nzPull", "nzXs", "nzSm", "nzMd", "nzLg", "nzXl", "nzXXl"], exportAs: ["nzCol"] }, { kind: "directive", type: i2$3.NzRowDirective, selector: "[nz-row],nz-row,nz-form-item", inputs: ["nzAlign", "nzJustify", "nzGutter"], exportAs: ["nzRow"] }, { kind: "ngmodule", type: NzGridModule }, { kind: "ngmodule", type: NzCollapseModule }, { kind: "component", type: i4$1.NzCollapsePanelComponent, selector: "nz-collapse-panel", inputs: ["nzActive", "nzDisabled", "nzShowArrow", "nzExtra", "nzHeader", "nzExpandedIcon"], outputs: ["nzActiveChange"], exportAs: ["nzCollapsePanel"] }, { kind: "component", type: i4$1.NzCollapseComponent, selector: "nz-collapse", inputs: ["nzAccordion", "nzBordered", "nzGhost", "nzExpandIconPosition"], exportAs: ["nzCollapse"] }, { kind: "component", type: DynamicFieldComponent, selector: "app-dynamic-field", inputs: ["field", "fields", "mode", "control", "formGroup", "formId"] }] }); }
|
|
6389
4563
|
}
|
|
6390
4564
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DynamicFormComponent, decorators: [{
|
|
6391
4565
|
type: Component,
|
|
@@ -6412,30 +4586,43 @@ var TableCellTypeEnum;
|
|
|
6412
4586
|
TableCellTypeEnum["EMPTY"] = "empty";
|
|
6413
4587
|
})(TableCellTypeEnum || (TableCellTypeEnum = {}));
|
|
6414
4588
|
|
|
6415
|
-
|
|
6416
|
-
|
|
4589
|
+
/**
|
|
4590
|
+
* Interface base chung cho Các dạng Radio.
|
|
4591
|
+
*/
|
|
4592
|
+
class RadioBaseField extends BaseField {
|
|
4593
|
+
/**
|
|
4594
|
+
* Props validation chung cho các dạng Radio.
|
|
4595
|
+
*/
|
|
4596
|
+
// ................. Chưa có .................
|
|
6417
4597
|
constructor(init) {
|
|
6418
|
-
|
|
4598
|
+
super(init);
|
|
4599
|
+
/**
|
|
4600
|
+
* Gán giá trị cho các props cần giá trị mặc định ở Radio.
|
|
4601
|
+
*/
|
|
4602
|
+
this.controlType = ControlTypeEnum.Radio;
|
|
4603
|
+
this.bindLabel = DEFAULT_BIND_LABEL;
|
|
4604
|
+
this.bindValue = DEFAULT_BIND_VALUE;
|
|
4605
|
+
this.dependsOn = [];
|
|
6419
4606
|
Object.assign(this, init);
|
|
6420
4607
|
}
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
Object.assign(this, init);
|
|
4608
|
+
// Mảng ValidatorFn các validation chung được hỗ trợ sẵn bởi Reactive form của angular cho Radio.
|
|
4609
|
+
getCommonSelectValidators() {
|
|
4610
|
+
const validators = this.getCommonValidators();
|
|
4611
|
+
return validators;
|
|
6426
4612
|
}
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
4613
|
+
// Mảng ValidatorFn các custom validation có thể dùng cho Radio.
|
|
4614
|
+
getCustomSelectValidators() {
|
|
4615
|
+
const validators = this.getCustomValidators();
|
|
4616
|
+
return validators;
|
|
4617
|
+
}
|
|
4618
|
+
// Tạo Form Control cho Input Field.
|
|
4619
|
+
toFormControl() {
|
|
4620
|
+
const commonValidators = this.getCommonSelectValidators();
|
|
4621
|
+
const customValidators = this.getCustomSelectValidators();
|
|
4622
|
+
return new FormControl(this.value ?? '', [...commonValidators, ...customValidators]);
|
|
4623
|
+
}
|
|
4624
|
+
getErrorMessage(errorKey, errorValue, allFields) {
|
|
4625
|
+
return super.getErrorMessage(errorKey, errorValue, allFields);
|
|
6439
4626
|
}
|
|
6440
4627
|
}
|
|
6441
4628
|
|
|
@@ -6443,12 +4630,12 @@ class ReminderOption {
|
|
|
6443
4630
|
* Public API Surface of dynamic-form
|
|
6444
4631
|
*/
|
|
6445
4632
|
// ==========================================
|
|
6446
|
-
// 0. BASE FIELD MODELS (
|
|
4633
|
+
// 0. BASE FIELD MODELS (export truoc de tranh loi circular dependency)
|
|
6447
4634
|
// ==========================================
|
|
6448
4635
|
|
|
6449
4636
|
/**
|
|
6450
4637
|
* Generated bundle index. Do not edit.
|
|
6451
4638
|
*/
|
|
6452
4639
|
|
|
6453
|
-
export { BaseDateField, BaseField, BaseUploadField, CombineDependTypeEnum, CommonValidaterEnum, ControlTypeEnum, CustomValidaterEnum, DYNAMIC_FORM_CONFIG, DateFieldTypeEnum,
|
|
4640
|
+
export { ALL_MIME_TYPES, ALL_MIME_TYPES_VALUE, APPLICATION_TYPES, AUDIO_TYPES, AvatarUploadField, BaseDateField, BaseField, BaseUploadField, CombineDependTypeEnum, CommonValidaterEnum, ControlTypeEnum, CustomValidaterEnum, DEFAULT_ACCEPTED_IMAGE_MIME_TYPES, DEFAULT_ACCEPTED_MIME_TYPES, DYNAMIC_FORM_CONFIG, DateFieldTypeEnum, DatePickerField, DatePickerOutputValueTypeEnum, DateRangeField, DateRangeOutputValueTypeEnum, DependTypeEnum, DynamicFormComponent, DynamicFormService, FieldGroup, FieldTypeEnum, FileService, FileSizeTypeEnum, FormConfigService, FormDataService, FormModeEnum, GroupedFields, IMAGE_TYPES, InputBaseField, InputFieldTypeEnum, InputPasswordField, InputTextField, MIME_TYPES_TO_EXTENSION, MultiFileUploadField, MultiImageUploadField, MultiSelectField, RadioBaseField, SelectBaseField, SelectFieldTypeEnum, SelectOutputValueTypeEnum, SingleFileUploadField, SingleSelectField, SuffixService, TEXT_TYPES, TableCellTypeEnum, TextareaField, TreeSelectField, UploadFieldTypeEnum, UploadOutputValueTypeEnum, VIDEO_TYPES, ViewModeEnum, videoTypesPath };
|
|
6454
4641
|
//# sourceMappingURL=ldquocc-dynamic-form.mjs.map
|