@yibozhang/pro-table 16.1.6 → 16.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/page-public/antd-form.mjs +319 -8
- package/esm2022/public-api.mjs +1 -1
- package/fesm2022/yibozhang-pro-table.mjs +318 -7
- package/fesm2022/yibozhang-pro-table.mjs.map +1 -1
- package/lib/page-public/antd-form.d.ts +26 -2
- package/lib/page-public/antd-form.d.ts.map +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +1 -1
- package/public-api.d.ts.map +1 -1
package/esm2022/public-api.mjs
CHANGED
|
@@ -22,4 +22,4 @@ export { ArrayFormService } from "./lib/page-public/array-form";
|
|
|
22
22
|
// Export modal width detector
|
|
23
23
|
export { initModalWidthDetector } from "./lib/utils/modal-width-detector";
|
|
24
24
|
export { initSelectWidthDetector } from "./lib/utils/select-width-detector";
|
|
25
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byby10YWJsZS9zcmMvcHVibGljLWFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsWUFBWSxDQUFDO0FBQzNCLGNBQWMsY0FBYyxDQUFDO0FBQzdCLGNBQWMsNkNBQTZDLENBQUM7QUFDNUQsY0FBYyx5Q0FBeUMsQ0FBQztBQUN4RCxjQUFjLHNDQUFzQyxDQUFDO0FBQ3JELGNBQWMsK0NBQStDLENBQUM7QUFDOUQsY0FBYyw0Q0FBNEMsQ0FBQztBQUMzRCxjQUFjLG1EQUFtRCxDQUFDO0FBQ2xFLGNBQWMsZ0RBQWdELENBQUM7QUFDL0QsY0FBYyx1Q0FBdUMsQ0FBQztBQUN0RCxjQUFjLG9DQUFvQyxDQUFDO0FBRW5ELHFDQUFxQztBQUNyQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFnQjlELHNDQUFzQztBQUN0QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQVdoRSw4QkFBOEI7QUFDOUIsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDMUUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sbUNBQW1DLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxyXG4gKiBQdWJsaWMgQVBJIFN1cmZhY2Ugb2YgcHJvLXRhYmxlXHJcbiAqL1xyXG5cclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL3Byby10YWJsZS5jb21wb25lbnRcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL3Byby10YWJsZS5tb2R1bGVcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL2NvbnN0YW50c1wiO1xyXG5leHBvcnQgKiBmcm9tIFwiLi9saWIvdHlwZVwiO1xyXG5leHBvcnQgKiBmcm9tIFwiLi9saWIvdG9rZW5zXCI7XHJcbmV4cG9ydCAqIGZyb20gXCIuL2xpYi9wbGF0ZS1pbnB1dC9wbGF0ZS1wcmVmaXgtbG9hZC5zZXJ2aWNlXCI7XHJcbmV4cG9ydCAqIGZyb20gXCIuL2xpYi9wbGF0ZS1pbnB1dC9wbGF0ZS1pbnB1dC5jb21wb25lbnRcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL3BsYXRlLWlucHV0L3BsYXRlLWlucHV0Lm1vZHVsZVwiO1xyXG5leHBvcnQgKiBmcm9tIFwiLi9saWIvcGFnZS1jb250YWluZXIvcGFnZS1jb250YWluZXIuY29tcG9uZW50XCI7XHJcbmV4cG9ydCAqIGZyb20gXCIuL2xpYi9wYWdlLWNvbnRhaW5lci9wYWdlLWNvbnRhaW5lci5tb2R1bGVcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL3RhYmxlLXNlYXJjaC1iYXIvdGFibGUtc2VhcmNoLWJhci5jb21wb25lbnRcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL3RhYmxlLXNlYXJjaC1iYXIvdGFibGUtc2VhcmNoLWJhci1tb2R1bGVcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL2RpcmVjdGl2ZXMvdHJpbS1pbnB1dC5kaXJlY3RpdmVcIjtcclxuZXhwb3J0ICogZnJvbSBcIi4vbGliL2RpcmVjdGl2ZXMvdHJpbS1pbnB1dC5tb2R1bGVcIjtcclxuXHJcbi8vIEV4cG9ydCBhbnRkLWZvcm0gc2VydmljZSBhbmQgdHlwZXNcclxuZXhwb3J0IHsgQW50ZEZvcm1TZXJ2aWNlIH0gZnJvbSBcIi4vbGliL3BhZ2UtcHVibGljL2FudGQtZm9ybVwiO1xyXG5leHBvcnQgdHlwZSB7XHJcbiAgRmllbGRFcnJvck1lc3NhZ2UsXHJcbiAgRmllbGRFcnJvck1lc3NhZ2VzLFxyXG4gIEZpZWxkQ29uZmlnLFxyXG4gIEZvcm1Hcm91cENvbmZpZyxcclxuICBGb3JtQXJyYXlDb25maWcsXHJcbiAgRm9ybU1vZGlmeVR5cGUsXHJcbiAgRm9ybUNvbmZpZyxcclxuICBXYXRjaE9wdGlvbnMsXHJcbiAgQWRkRmllbGRzQ29uZmlnT3B0aW9ucyxcclxuICBBZGRGaWVsZHNDb25maWdSZXN1bHQsXHJcbiAgUmVtb3ZlRmllbGRzQ29uZmlnT3B0aW9ucyxcclxuICBSZW1vdmVGaWVsZHNDb25maWdSZXN1bHQsXHJcbn0gZnJvbSBcIi4vbGliL3BhZ2UtcHVibGljL2FudGQtZm9ybVwiO1xyXG5cclxuLy8gRXhwb3J0IGFycmF5LWZvcm0gc2VydmljZSBhbmQgdHlwZXNcclxuZXhwb3J0IHsgQXJyYXlGb3JtU2VydmljZSB9IGZyb20gXCIuL2xpYi9wYWdlLXB1YmxpYy9hcnJheS1mb3JtXCI7XHJcbmV4cG9ydCB0eXBlIHtcclxuICBWYWxpZGF0aW9uUmVzdWx0LFxyXG4gIEZpZWxkVmFsaWRhdG9yLFxyXG4gIEZpZWxkQ29uZmlnIGFzIEFycmF5RmllbGRDb25maWcsXHJcbiAgQXJyYXlGb3JtUm93LFxyXG4gIEFycmF5Rm9ybUNvbmZpZyxcclxuICBBcnJheUZvcm1TdG9yZUl0ZW0sXHJcbiAgQXJyYXlGb3JtU3RvcmUsXHJcbn0gZnJvbSBcIi4vbGliL3BhZ2UtcHVibGljL2FycmF5LWZvcm1cIjtcclxuXHJcbi8vIEV4cG9ydCBtb2RhbCB3aWR0aCBkZXRlY3RvclxyXG5leHBvcnQgeyBpbml0TW9kYWxXaWR0aERldGVjdG9yIH0gZnJvbSBcIi4vbGliL3V0aWxzL21vZGFsLXdpZHRoLWRldGVjdG9yXCI7XHJcbmV4cG9ydCB7IGluaXRTZWxlY3RXaWR0aERldGVjdG9yIH0gZnJvbSBcIi4vbGliL3V0aWxzL3NlbGVjdC13aWR0aC1kZXRlY3RvclwiO1xyXG4iXX0=
|
|
@@ -5,7 +5,7 @@ import { isObservable, lastValueFrom } from 'rxjs';
|
|
|
5
5
|
import * as i1 from '@angular/common';
|
|
6
6
|
import { CommonModule } from '@angular/common';
|
|
7
7
|
import * as i2$2 from '@angular/forms';
|
|
8
|
-
import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
|
8
|
+
import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
|
|
9
9
|
import * as i3 from 'ng-zorro-antd/grid';
|
|
10
10
|
import { NzGridModule } from 'ng-zorro-antd/grid';
|
|
11
11
|
import * as i4$1 from 'ng-zorro-antd/form';
|
|
@@ -2025,6 +2025,9 @@ class AntdFormService {
|
|
|
2025
2025
|
labelAlign = "right";
|
|
2026
2026
|
labelObservers = {};
|
|
2027
2027
|
errorMessageStore = {};
|
|
2028
|
+
arrayEditingRows = {};
|
|
2029
|
+
arrayRowSnapshots = {};
|
|
2030
|
+
arrayItemConfigs = {};
|
|
2028
2031
|
classPrefix = "ant-form-";
|
|
2029
2032
|
defaultErrorMessages = {
|
|
2030
2033
|
required: "该字段为必填项",
|
|
@@ -2045,6 +2048,40 @@ class AntdFormService {
|
|
|
2045
2048
|
"type" in config &&
|
|
2046
2049
|
config.type === "group");
|
|
2047
2050
|
}
|
|
2051
|
+
// 判断是否为 FormArray 配置
|
|
2052
|
+
isFormArrayConfig(config) {
|
|
2053
|
+
return (typeof config === "object" &&
|
|
2054
|
+
config !== null &&
|
|
2055
|
+
"type" in config &&
|
|
2056
|
+
config.type === "array");
|
|
2057
|
+
}
|
|
2058
|
+
// 根据路径获取嵌套的 FormArray
|
|
2059
|
+
getNestedFormArray(formGroup, path) {
|
|
2060
|
+
if (!path || path.trim() === "") {
|
|
2061
|
+
return null;
|
|
2062
|
+
}
|
|
2063
|
+
const pathParts = path.split(".").filter((p) => p.trim() !== "");
|
|
2064
|
+
let current = formGroup;
|
|
2065
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
2066
|
+
if (!current)
|
|
2067
|
+
return null;
|
|
2068
|
+
const part = pathParts[i];
|
|
2069
|
+
if (i === pathParts.length - 1) {
|
|
2070
|
+
const target = current.get
|
|
2071
|
+
? current.get(part)
|
|
2072
|
+
: null;
|
|
2073
|
+
return target instanceof UntypedFormArray ? target : null;
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
current = current instanceof UntypedFormGroup ? current.get(part) : null;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
return null;
|
|
2080
|
+
}
|
|
2081
|
+
// 根据 itemConfig 创建 FormArray 的单行 FormGroup
|
|
2082
|
+
createArrayRowGroup(itemConfig, disabled) {
|
|
2083
|
+
return this.createNestedFormGroup(itemConfig, disabled);
|
|
2084
|
+
}
|
|
2048
2085
|
// 递归创建嵌套 FormGroup
|
|
2049
2086
|
createNestedFormGroup(fields, disabled) {
|
|
2050
2087
|
const groupConfig = {};
|
|
@@ -2053,6 +2090,15 @@ class AntdFormService {
|
|
|
2053
2090
|
// 递归创建嵌套的 FormGroup
|
|
2054
2091
|
groupConfig[key] = this.createNestedFormGroup(fieldConfig.fields, fieldConfig.disabled ?? disabled);
|
|
2055
2092
|
}
|
|
2093
|
+
else if (this.isFormArrayConfig(fieldConfig)) {
|
|
2094
|
+
// 创建 FormArray
|
|
2095
|
+
const rows = [];
|
|
2096
|
+
const rowCount = fieldConfig.initialRows ?? 0;
|
|
2097
|
+
for (let i = 0; i < rowCount; i++) {
|
|
2098
|
+
rows.push(this.createArrayRowGroup(fieldConfig.itemConfig, fieldConfig.disabled ?? disabled));
|
|
2099
|
+
}
|
|
2100
|
+
groupConfig[key] = this.fb.array(rows, fieldConfig.validators?.() ?? []);
|
|
2101
|
+
}
|
|
2056
2102
|
else {
|
|
2057
2103
|
// 创建普通 FormControl
|
|
2058
2104
|
groupConfig[key] = [
|
|
@@ -2080,6 +2126,16 @@ class AntdFormService {
|
|
|
2080
2126
|
this.errorMessageStore[name][key] = fieldConfig.errorMessages;
|
|
2081
2127
|
}
|
|
2082
2128
|
}
|
|
2129
|
+
else if (this.isFormArrayConfig(fieldConfig)) {
|
|
2130
|
+
// 处理 FormArray
|
|
2131
|
+
const rows = [];
|
|
2132
|
+
const rowCount = fieldConfig.initialRows ?? 0;
|
|
2133
|
+
for (let i = 0; i < rowCount; i++) {
|
|
2134
|
+
rows.push(this.createArrayRowGroup(fieldConfig.itemConfig, fieldConfig.disabled));
|
|
2135
|
+
}
|
|
2136
|
+
groupConfig[key] = this.fb.array(rows, fieldConfig.validators?.() ?? []);
|
|
2137
|
+
this._ensureArrayItemConfig(name, key, fieldConfig.itemConfig);
|
|
2138
|
+
}
|
|
2083
2139
|
else {
|
|
2084
2140
|
// 处理普通字段
|
|
2085
2141
|
groupConfig[key] = [
|
|
@@ -2117,18 +2173,30 @@ class AntdFormService {
|
|
|
2117
2173
|
// 销毁对应的表单
|
|
2118
2174
|
destory(names) {
|
|
2119
2175
|
names.forEach((name) => {
|
|
2120
|
-
//
|
|
2176
|
+
// 清理错误消息存储
|
|
2121
2177
|
if (this.errorMessageStore[name]) {
|
|
2122
2178
|
delete this.errorMessageStore[name];
|
|
2123
2179
|
}
|
|
2124
|
-
//
|
|
2180
|
+
// 清理表单注册标记
|
|
2125
2181
|
if (this.formRegisterStore[name]) {
|
|
2126
2182
|
delete this.formRegisterStore[name];
|
|
2127
2183
|
}
|
|
2128
|
-
//
|
|
2184
|
+
// 清理表单组(Angular 会自动处理 FormGroup 的清理)
|
|
2129
2185
|
if (this.formStore[name]) {
|
|
2130
2186
|
delete this.formStore[name];
|
|
2131
2187
|
}
|
|
2188
|
+
// 清理 FormArray 编辑状态
|
|
2189
|
+
if (this.arrayEditingRows[name]) {
|
|
2190
|
+
delete this.arrayEditingRows[name];
|
|
2191
|
+
}
|
|
2192
|
+
// 清理 FormArray 行快照
|
|
2193
|
+
if (this.arrayRowSnapshots[name]) {
|
|
2194
|
+
delete this.arrayRowSnapshots[name];
|
|
2195
|
+
}
|
|
2196
|
+
// 清理 FormArray itemConfig 缓存
|
|
2197
|
+
if (this.arrayItemConfigs[name]) {
|
|
2198
|
+
delete this.arrayItemConfigs[name];
|
|
2199
|
+
}
|
|
2132
2200
|
});
|
|
2133
2201
|
}
|
|
2134
2202
|
// 根据路径获取嵌套的 FormGroup
|
|
@@ -2224,6 +2292,16 @@ class AntdFormService {
|
|
|
2224
2292
|
errorMessagesToAdd[errorMessageKey] = fieldConfig.errorMessages;
|
|
2225
2293
|
}
|
|
2226
2294
|
}
|
|
2295
|
+
else if (this.isFormArrayConfig(fieldConfig)) {
|
|
2296
|
+
// 创建 FormArray
|
|
2297
|
+
const rows = [];
|
|
2298
|
+
const rowCount = fieldConfig.initialRows ?? 0;
|
|
2299
|
+
for (let i = 0; i < rowCount; i++) {
|
|
2300
|
+
rows.push(this.createArrayRowGroup(fieldConfig.itemConfig, fieldConfig.disabled));
|
|
2301
|
+
}
|
|
2302
|
+
control = this.fb.array(rows, fieldConfig.validators?.() ?? []);
|
|
2303
|
+
this._ensureArrayItemConfig(formName, fieldName, fieldConfig.itemConfig);
|
|
2304
|
+
}
|
|
2227
2305
|
else {
|
|
2228
2306
|
// 创建普通 FormControl
|
|
2229
2307
|
const controlOptions = {
|
|
@@ -2347,16 +2425,58 @@ class AntdFormService {
|
|
|
2347
2425
|
result.success = result.failed.length === 0;
|
|
2348
2426
|
return result;
|
|
2349
2427
|
}
|
|
2350
|
-
//
|
|
2428
|
+
// 局部赋值(支持 FormArray 回填:自动重建行结构)
|
|
2351
2429
|
patchFormValues(name, values, options) {
|
|
2352
2430
|
const formGroup = this.formStore[name];
|
|
2353
2431
|
if (!formGroup) {
|
|
2354
2432
|
console.warn(`[AntdFormService] patchFormValues: form "${name}" not found.`);
|
|
2355
2433
|
return;
|
|
2356
2434
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2435
|
+
const arrayValues = {};
|
|
2436
|
+
const plainValues = {};
|
|
2437
|
+
// 区分 FormArray 字段和普通字段
|
|
2438
|
+
Object.entries(values).forEach(([key, val]) => {
|
|
2439
|
+
const ctrl = formGroup.get(key);
|
|
2440
|
+
if (ctrl instanceof UntypedFormArray && Array.isArray(val)) {
|
|
2441
|
+
arrayValues[key] = val;
|
|
2442
|
+
}
|
|
2443
|
+
else {
|
|
2444
|
+
plainValues[key] = val;
|
|
2445
|
+
}
|
|
2446
|
+
});
|
|
2447
|
+
// 处理 FormArray 回填:清空旧行,按 itemConfig 重建
|
|
2448
|
+
Object.entries(arrayValues).forEach(([key, rows]) => {
|
|
2449
|
+
const formArray = formGroup.get(key);
|
|
2450
|
+
const itemConfig = this.arrayItemConfigs[name]?.[key];
|
|
2451
|
+
// 清空现有行
|
|
2452
|
+
while (formArray.length > 0) {
|
|
2453
|
+
formArray.removeAt(0, { emitEvent: false });
|
|
2454
|
+
}
|
|
2455
|
+
// 清理该数组的编辑状态和快照
|
|
2456
|
+
this._ensureArrayState(name, key);
|
|
2457
|
+
this.arrayEditingRows[name][key] = new Set();
|
|
2458
|
+
this.arrayRowSnapshots[name][key] = new Map();
|
|
2459
|
+
// 按 itemConfig 创建行并回填
|
|
2460
|
+
rows.forEach((rowData) => {
|
|
2461
|
+
const newRow = itemConfig
|
|
2462
|
+
? this.createArrayRowGroup(itemConfig)
|
|
2463
|
+
: this.fb.group({});
|
|
2464
|
+
if (itemConfig) {
|
|
2465
|
+
newRow.patchValue(rowData, { emitEvent: false });
|
|
2466
|
+
}
|
|
2467
|
+
formArray.push(newRow, { emitEvent: false });
|
|
2468
|
+
});
|
|
2469
|
+
// 统一触发一次 valueChanges
|
|
2470
|
+
if (options?.emitEvent !== false) {
|
|
2471
|
+
formArray.updateValueAndValidity({ emitEvent: true });
|
|
2472
|
+
}
|
|
2359
2473
|
});
|
|
2474
|
+
// 处理普通字段(含嵌套 FormGroup)
|
|
2475
|
+
if (Object.keys(plainValues).length > 0) {
|
|
2476
|
+
formGroup.patchValue(plainValues, {
|
|
2477
|
+
emitEvent: options?.emitEvent ?? true,
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2360
2480
|
}
|
|
2361
2481
|
// 表单校验(自动过滤内部字段)
|
|
2362
2482
|
validateForm(name, options) {
|
|
@@ -2385,6 +2505,12 @@ class AntdFormService {
|
|
|
2385
2505
|
this.markAllControlsAsDirty(childControl, emitEvent, onlySelf);
|
|
2386
2506
|
});
|
|
2387
2507
|
}
|
|
2508
|
+
// 如果是 FormArray,递归处理所有行
|
|
2509
|
+
if (control instanceof UntypedFormArray) {
|
|
2510
|
+
control.controls.forEach((childControl) => {
|
|
2511
|
+
this.markAllControlsAsDirty(childControl, emitEvent, onlySelf);
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2388
2514
|
}
|
|
2389
2515
|
// ==================== 错误消息相关 ====================
|
|
2390
2516
|
// 获取字段首条错误提示
|
|
@@ -2441,6 +2567,191 @@ class AntdFormService {
|
|
|
2441
2567
|
}
|
|
2442
2568
|
return this.setupValueChangeSubscription(control, handler, options);
|
|
2443
2569
|
}
|
|
2570
|
+
// ==================== FormArray 操作 ====================
|
|
2571
|
+
// 获取指定路径的 FormArray 实例(供模板 ngFor 使用)
|
|
2572
|
+
getFormArray(formName, arrayPath) {
|
|
2573
|
+
const formGroup = this.formStore[formName];
|
|
2574
|
+
if (!formGroup) {
|
|
2575
|
+
console.warn(`[AntdFormService] getFormArray: form "${formName}" not found.`);
|
|
2576
|
+
return null;
|
|
2577
|
+
}
|
|
2578
|
+
const formArray = this.getNestedFormArray(formGroup, arrayPath);
|
|
2579
|
+
if (!formArray) {
|
|
2580
|
+
console.warn(`[AntdFormService] getFormArray: FormArray at path "${arrayPath}" not found in form "${formName}".`);
|
|
2581
|
+
}
|
|
2582
|
+
return formArray;
|
|
2583
|
+
}
|
|
2584
|
+
// 向 FormArray 新增一行(自动进入编辑状态,无快照)
|
|
2585
|
+
addArrayRow(formName, arrayPath) {
|
|
2586
|
+
const formGroup = this.formStore[formName];
|
|
2587
|
+
if (!formGroup) {
|
|
2588
|
+
console.warn(`[AntdFormService] addArrayRow: form "${formName}" not found.`);
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
const formArray = this.getNestedFormArray(formGroup, arrayPath);
|
|
2592
|
+
if (!formArray) {
|
|
2593
|
+
console.warn(`[AntdFormService] addArrayRow: FormArray at path "${arrayPath}" not found in form "${formName}".`);
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
// 取第一行的结构作为模板,若无行则无法推断 itemConfig
|
|
2597
|
+
// 新行默认为空 FormGroup(调用方应通过 itemConfig 传入具体结构)
|
|
2598
|
+
// 实际行创建通过 _addArrayRowWithConfig 完成,此处保留空行兜底
|
|
2599
|
+
const newRow = this.fb.group({});
|
|
2600
|
+
formArray.push(newRow);
|
|
2601
|
+
const newIndex = formArray.length - 1;
|
|
2602
|
+
this._ensureArrayState(formName, arrayPath);
|
|
2603
|
+
// 新增行无历史快照,cancelRowEdit 时直接删除
|
|
2604
|
+
this.arrayEditingRows[formName][arrayPath].add(newIndex);
|
|
2605
|
+
}
|
|
2606
|
+
// 向 FormArray 新增一行(指定 itemConfig)
|
|
2607
|
+
addArrayRowWithConfig(formName, arrayPath, itemConfig) {
|
|
2608
|
+
const formGroup = this.formStore[formName];
|
|
2609
|
+
if (!formGroup) {
|
|
2610
|
+
console.warn(`[AntdFormService] addArrayRowWithConfig: form "${formName}" not found.`);
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
const formArray = this.getNestedFormArray(formGroup, arrayPath);
|
|
2614
|
+
if (!formArray) {
|
|
2615
|
+
console.warn(`[AntdFormService] addArrayRowWithConfig: FormArray at path "${arrayPath}" not found in form "${formName}".`);
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
const newRow = this.createArrayRowGroup(itemConfig);
|
|
2619
|
+
formArray.push(newRow);
|
|
2620
|
+
const newIndex = formArray.length - 1;
|
|
2621
|
+
this._ensureArrayState(formName, arrayPath);
|
|
2622
|
+
// 新增行无历史快照,cancelRowEdit 时直接删除
|
|
2623
|
+
this.arrayEditingRows[formName][arrayPath].add(newIndex);
|
|
2624
|
+
}
|
|
2625
|
+
// 删除 FormArray 中指定行(同时清理编辑状态和快照,并重对齐索引)
|
|
2626
|
+
removeArrayRow(formName, arrayPath, index) {
|
|
2627
|
+
const formGroup = this.formStore[formName];
|
|
2628
|
+
if (!formGroup) {
|
|
2629
|
+
console.warn(`[AntdFormService] removeArrayRow: form "${formName}" not found.`);
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
const formArray = this.getNestedFormArray(formGroup, arrayPath);
|
|
2633
|
+
if (!formArray) {
|
|
2634
|
+
console.warn(`[AntdFormService] removeArrayRow: FormArray at path "${arrayPath}" not found in form "${formName}".`);
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
if (index < 0 || index >= formArray.length) {
|
|
2638
|
+
console.warn(`[AntdFormService] removeArrayRow: index ${index} out of range.`);
|
|
2639
|
+
return;
|
|
2640
|
+
}
|
|
2641
|
+
formArray.removeAt(index);
|
|
2642
|
+
this._ensureArrayState(formName, arrayPath);
|
|
2643
|
+
// 清理被删行的快照
|
|
2644
|
+
const snapshotMap = this.arrayRowSnapshots[formName][arrayPath];
|
|
2645
|
+
snapshotMap.delete(index);
|
|
2646
|
+
// 重对齐:快照中 > index 的键全部 -1
|
|
2647
|
+
const newSnapshotMap = new Map();
|
|
2648
|
+
snapshotMap.forEach((value, key) => {
|
|
2649
|
+
newSnapshotMap.set(key > index ? key - 1 : key, value);
|
|
2650
|
+
});
|
|
2651
|
+
this.arrayRowSnapshots[formName][arrayPath] = newSnapshotMap;
|
|
2652
|
+
// 重对齐:editingRows 中 > index 的索引全部 -1
|
|
2653
|
+
const editingSet = this.arrayEditingRows[formName][arrayPath];
|
|
2654
|
+
const newEditingSet = new Set();
|
|
2655
|
+
editingSet.forEach((i) => {
|
|
2656
|
+
if (i !== index) {
|
|
2657
|
+
newEditingSet.add(i > index ? i - 1 : i);
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
this.arrayEditingRows[formName][arrayPath] = newEditingSet;
|
|
2661
|
+
}
|
|
2662
|
+
// 存储 FormArray 的 itemConfig(用于回填时重建行结构)
|
|
2663
|
+
_ensureArrayItemConfig(formName, arrayPath, itemConfig) {
|
|
2664
|
+
if (!this.arrayItemConfigs[formName]) {
|
|
2665
|
+
this.arrayItemConfigs[formName] = {};
|
|
2666
|
+
}
|
|
2667
|
+
this.arrayItemConfigs[formName][arrayPath] = itemConfig;
|
|
2668
|
+
}
|
|
2669
|
+
// 初始化 arrayEditingRows 和 arrayRowSnapshots 对应路径(若不存在则创建)
|
|
2670
|
+
_ensureArrayState(formName, arrayPath) {
|
|
2671
|
+
if (!this.arrayEditingRows[formName]) {
|
|
2672
|
+
this.arrayEditingRows[formName] = {};
|
|
2673
|
+
}
|
|
2674
|
+
if (!this.arrayEditingRows[formName][arrayPath]) {
|
|
2675
|
+
this.arrayEditingRows[formName][arrayPath] = new Set();
|
|
2676
|
+
}
|
|
2677
|
+
if (!this.arrayRowSnapshots[formName]) {
|
|
2678
|
+
this.arrayRowSnapshots[formName] = {};
|
|
2679
|
+
}
|
|
2680
|
+
if (!this.arrayRowSnapshots[formName][arrayPath]) {
|
|
2681
|
+
this.arrayRowSnapshots[formName][arrayPath] = new Map();
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
// ==================== FormArray 行级编辑 ====================
|
|
2685
|
+
// 开启指定行的编辑模式(保存当前值为快照)
|
|
2686
|
+
startRowEdit(formName, arrayPath, index) {
|
|
2687
|
+
const formArray = this.getFormArray(formName, arrayPath);
|
|
2688
|
+
if (!formArray)
|
|
2689
|
+
return;
|
|
2690
|
+
const rowControl = formArray.at(index);
|
|
2691
|
+
if (!rowControl) {
|
|
2692
|
+
console.warn(`[AntdFormService] startRowEdit: row ${index} not found in "${arrayPath}".`);
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
this._ensureArrayState(formName, arrayPath);
|
|
2696
|
+
// 保存快照(仅当快照不存在时,避免重复编辑覆盖原始值)
|
|
2697
|
+
const snapshotMap = this.arrayRowSnapshots[formName][arrayPath];
|
|
2698
|
+
if (!snapshotMap.has(index)) {
|
|
2699
|
+
snapshotMap.set(index, JSON.parse(JSON.stringify(rowControl.getRawValue())));
|
|
2700
|
+
}
|
|
2701
|
+
this.arrayEditingRows[formName][arrayPath].add(index);
|
|
2702
|
+
}
|
|
2703
|
+
// 确认保存指定行(验证通过则清除快照并退出编辑状态,返回行数据;否则返回 false)
|
|
2704
|
+
confirmRowEdit(formName, arrayPath, index) {
|
|
2705
|
+
const formArray = this.getFormArray(formName, arrayPath);
|
|
2706
|
+
if (!formArray)
|
|
2707
|
+
return false;
|
|
2708
|
+
const rowControl = formArray.at(index);
|
|
2709
|
+
if (!rowControl) {
|
|
2710
|
+
console.warn(`[AntdFormService] confirmRowEdit: row ${index} not found in "${arrayPath}".`);
|
|
2711
|
+
return false;
|
|
2712
|
+
}
|
|
2713
|
+
if (rowControl.invalid) {
|
|
2714
|
+
this.markAllControlsAsDirty(rowControl, true, false);
|
|
2715
|
+
return false;
|
|
2716
|
+
}
|
|
2717
|
+
this._ensureArrayState(formName, arrayPath);
|
|
2718
|
+
this.arrayRowSnapshots[formName][arrayPath].delete(index);
|
|
2719
|
+
this.arrayEditingRows[formName][arrayPath].delete(index);
|
|
2720
|
+
return this.excludeInternalFields(rowControl.getRawValue());
|
|
2721
|
+
}
|
|
2722
|
+
// 取消编辑指定行(从快照恢复值;若无快照则说明是新增行,直接删除该行)
|
|
2723
|
+
cancelRowEdit(formName, arrayPath, index) {
|
|
2724
|
+
const formArray = this.getFormArray(formName, arrayPath);
|
|
2725
|
+
if (!formArray)
|
|
2726
|
+
return;
|
|
2727
|
+
this._ensureArrayState(formName, arrayPath);
|
|
2728
|
+
const snapshotMap = this.arrayRowSnapshots[formName][arrayPath];
|
|
2729
|
+
if (snapshotMap.has(index)) {
|
|
2730
|
+
// 已有行:从快照恢复值
|
|
2731
|
+
const snapshot = snapshotMap.get(index);
|
|
2732
|
+
const rowControl = formArray.at(index);
|
|
2733
|
+
if (rowControl) {
|
|
2734
|
+
rowControl.patchValue(snapshot);
|
|
2735
|
+
rowControl.markAsPristine();
|
|
2736
|
+
rowControl.markAsUntouched();
|
|
2737
|
+
}
|
|
2738
|
+
snapshotMap.delete(index);
|
|
2739
|
+
this.arrayEditingRows[formName][arrayPath].delete(index);
|
|
2740
|
+
}
|
|
2741
|
+
else {
|
|
2742
|
+
// 新增行:直接删除
|
|
2743
|
+
this.removeArrayRow(formName, arrayPath, index);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
// 查询指定行是否处于编辑状态
|
|
2747
|
+
isRowEditing(formName, arrayPath, index) {
|
|
2748
|
+
return (this.arrayEditingRows[formName]?.[arrayPath]?.has(index) ?? false);
|
|
2749
|
+
}
|
|
2750
|
+
// 获取所有正在编辑的行索引列表
|
|
2751
|
+
getEditingRowIndices(formName, arrayPath) {
|
|
2752
|
+
const editingSet = this.arrayEditingRows[formName]?.[arrayPath];
|
|
2753
|
+
return editingSet ? Array.from(editingSet).sort((a, b) => a - b) : [];
|
|
2754
|
+
}
|
|
2444
2755
|
// ==================== 工具方法 ====================
|
|
2445
2756
|
// 获取表单类名
|
|
2446
2757
|
getFormClassName(name) {
|