@yibozhang/pro-table 16.1.6 → 16.1.8

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.
@@ -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';
@@ -827,7 +827,6 @@ class ProTableComponent {
827
827
  }
828
828
  }
829
829
  async ngOnInit() {
830
- console.log('aaa');
831
830
  this._searchParams = {};
832
831
  this.formateInnerColumns(this.columns);
833
832
  await this.setSearchParams(this.columns);
@@ -2025,6 +2024,9 @@ class AntdFormService {
2025
2024
  labelAlign = "right";
2026
2025
  labelObservers = {};
2027
2026
  errorMessageStore = {};
2027
+ arrayEditingRows = {};
2028
+ arrayRowSnapshots = {};
2029
+ arrayItemConfigs = {};
2028
2030
  classPrefix = "ant-form-";
2029
2031
  defaultErrorMessages = {
2030
2032
  required: "该字段为必填项",
@@ -2036,6 +2038,7 @@ class AntdFormService {
2036
2038
  // ==================== 构造函数 ====================
2037
2039
  constructor(fb) {
2038
2040
  this.fb = fb;
2041
+ console.log('aaaaaaa');
2039
2042
  }
2040
2043
  // ==================== 表单创建和初始化 ====================
2041
2044
  // 判断是否为嵌套 FormGroup 配置
@@ -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
- // 2. 清理错误消息存储
2176
+ // 清理错误消息存储
2121
2177
  if (this.errorMessageStore[name]) {
2122
2178
  delete this.errorMessageStore[name];
2123
2179
  }
2124
- // 3. 清理表单注册标记
2180
+ // 清理表单注册标记
2125
2181
  if (this.formRegisterStore[name]) {
2126
2182
  delete this.formRegisterStore[name];
2127
2183
  }
2128
- // 4. 清理表单组(Angular 会自动处理 FormGroup 的清理)
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
- formGroup.patchValue(values, {
2358
- emitEvent: options?.emitEvent ?? true,
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
  // 获取字段首条错误提示
@@ -2402,8 +2528,9 @@ class AntdFormService {
2402
2528
  if (!control.errors) {
2403
2529
  return "";
2404
2530
  }
2405
- // 从 errorMessageStore 获取错误消息配置
2406
- const errorMessages = this.errorMessageStore[name]?.[controlName];
2531
+ // 从 errorMessageStore 获取错误消息配置,回退查找 arrayItemConfigs
2532
+ const errorMessages = this.errorMessageStore[name]?.[controlName]
2533
+ ?? this._getArrayItemErrorMessages(name, controlName);
2407
2534
  const mergedMessages = {
2408
2535
  ...this.defaultErrorMessages,
2409
2536
  ...(errorMessages ?? {}),
@@ -2441,6 +2568,207 @@ class AntdFormService {
2441
2568
  }
2442
2569
  return this.setupValueChangeSubscription(control, handler, options);
2443
2570
  }
2571
+ // ==================== FormArray 操作 ====================
2572
+ // 获取指定路径的 FormArray 实例(供模板 ngFor 使用)
2573
+ getFormArray(formName, arrayPath) {
2574
+ const formGroup = this.formStore[formName];
2575
+ if (!formGroup) {
2576
+ console.warn(`[AntdFormService] getFormArray: form "${formName}" not found.`);
2577
+ return null;
2578
+ }
2579
+ const formArray = this.getNestedFormArray(formGroup, arrayPath);
2580
+ if (!formArray) {
2581
+ console.warn(`[AntdFormService] getFormArray: FormArray at path "${arrayPath}" not found in form "${formName}".`);
2582
+ }
2583
+ return formArray;
2584
+ }
2585
+ // 向 FormArray 新增一行(自动进入编辑状态,无快照)
2586
+ addArrayRow(formName, arrayPath) {
2587
+ const formGroup = this.formStore[formName];
2588
+ if (!formGroup) {
2589
+ console.warn(`[AntdFormService] addArrayRow: form "${formName}" not found.`);
2590
+ return;
2591
+ }
2592
+ const formArray = this.getNestedFormArray(formGroup, arrayPath);
2593
+ if (!formArray) {
2594
+ console.warn(`[AntdFormService] addArrayRow: FormArray at path "${arrayPath}" not found in form "${formName}".`);
2595
+ return;
2596
+ }
2597
+ // 取第一行的结构作为模板,若无行则无法推断 itemConfig
2598
+ // 新行默认为空 FormGroup(调用方应通过 itemConfig 传入具体结构)
2599
+ // 实际行创建通过 _addArrayRowWithConfig 完成,此处保留空行兜底
2600
+ const newRow = this.fb.group({});
2601
+ formArray.push(newRow);
2602
+ const newIndex = formArray.length - 1;
2603
+ this._ensureArrayState(formName, arrayPath);
2604
+ // 新增行无历史快照,cancelRowEdit 时直接删除
2605
+ this.arrayEditingRows[formName][arrayPath].add(newIndex);
2606
+ }
2607
+ // 向 FormArray 新增一行(指定 itemConfig)
2608
+ addArrayRowWithConfig(formName, arrayPath, itemConfig) {
2609
+ const formGroup = this.formStore[formName];
2610
+ if (!formGroup) {
2611
+ console.warn(`[AntdFormService] addArrayRowWithConfig: form "${formName}" not found.`);
2612
+ return;
2613
+ }
2614
+ const formArray = this.getNestedFormArray(formGroup, arrayPath);
2615
+ if (!formArray) {
2616
+ console.warn(`[AntdFormService] addArrayRowWithConfig: FormArray at path "${arrayPath}" not found in form "${formName}".`);
2617
+ return;
2618
+ }
2619
+ const newRow = this.createArrayRowGroup(itemConfig);
2620
+ formArray.push(newRow);
2621
+ const newIndex = formArray.length - 1;
2622
+ this._ensureArrayState(formName, arrayPath);
2623
+ // 新增行无历史快照,cancelRowEdit 时直接删除
2624
+ this.arrayEditingRows[formName][arrayPath].add(newIndex);
2625
+ }
2626
+ // 删除 FormArray 中指定行(同时清理编辑状态和快照,并重对齐索引)
2627
+ removeArrayRow(formName, arrayPath, index) {
2628
+ const formGroup = this.formStore[formName];
2629
+ if (!formGroup) {
2630
+ console.warn(`[AntdFormService] removeArrayRow: form "${formName}" not found.`);
2631
+ return;
2632
+ }
2633
+ const formArray = this.getNestedFormArray(formGroup, arrayPath);
2634
+ if (!formArray) {
2635
+ console.warn(`[AntdFormService] removeArrayRow: FormArray at path "${arrayPath}" not found in form "${formName}".`);
2636
+ return;
2637
+ }
2638
+ if (index < 0 || index >= formArray.length) {
2639
+ console.warn(`[AntdFormService] removeArrayRow: index ${index} out of range.`);
2640
+ return;
2641
+ }
2642
+ formArray.removeAt(index);
2643
+ this._ensureArrayState(formName, arrayPath);
2644
+ // 清理被删行的快照
2645
+ const snapshotMap = this.arrayRowSnapshots[formName][arrayPath];
2646
+ snapshotMap.delete(index);
2647
+ // 重对齐:快照中 > index 的键全部 -1
2648
+ const newSnapshotMap = new Map();
2649
+ snapshotMap.forEach((value, key) => {
2650
+ newSnapshotMap.set(key > index ? key - 1 : key, value);
2651
+ });
2652
+ this.arrayRowSnapshots[formName][arrayPath] = newSnapshotMap;
2653
+ // 重对齐:editingRows 中 > index 的索引全部 -1
2654
+ const editingSet = this.arrayEditingRows[formName][arrayPath];
2655
+ const newEditingSet = new Set();
2656
+ editingSet.forEach((i) => {
2657
+ if (i !== index) {
2658
+ newEditingSet.add(i > index ? i - 1 : i);
2659
+ }
2660
+ });
2661
+ this.arrayEditingRows[formName][arrayPath] = newEditingSet;
2662
+ }
2663
+ // 存储 FormArray 的 itemConfig(用于回填时重建行结构)
2664
+ _ensureArrayItemConfig(formName, arrayPath, itemConfig) {
2665
+ if (!this.arrayItemConfigs[formName]) {
2666
+ this.arrayItemConfigs[formName] = {};
2667
+ }
2668
+ this.arrayItemConfigs[formName][arrayPath] = itemConfig;
2669
+ }
2670
+ _getArrayItemErrorMessages(formName, controlName) {
2671
+ const parts = controlName.split('.');
2672
+ if (parts.length < 3)
2673
+ return undefined;
2674
+ const maybeIndex = parts[parts.length - 2];
2675
+ if (!/^\d+$/.test(maybeIndex))
2676
+ return undefined;
2677
+ const fieldName = parts[parts.length - 1];
2678
+ const arrayPath = parts.slice(0, -2).join('.');
2679
+ const itemConfig = this.arrayItemConfigs[formName]?.[arrayPath];
2680
+ const fieldConfig = itemConfig?.[fieldName];
2681
+ if (!fieldConfig || this.isFormGroupConfig(fieldConfig) || this.isFormArrayConfig(fieldConfig)) {
2682
+ return undefined;
2683
+ }
2684
+ return fieldConfig.errorMessages;
2685
+ }
2686
+ // 初始化 arrayEditingRows 和 arrayRowSnapshots 对应路径(若不存在则创建)
2687
+ _ensureArrayState(formName, arrayPath) {
2688
+ if (!this.arrayEditingRows[formName]) {
2689
+ this.arrayEditingRows[formName] = {};
2690
+ }
2691
+ if (!this.arrayEditingRows[formName][arrayPath]) {
2692
+ this.arrayEditingRows[formName][arrayPath] = new Set();
2693
+ }
2694
+ if (!this.arrayRowSnapshots[formName]) {
2695
+ this.arrayRowSnapshots[formName] = {};
2696
+ }
2697
+ if (!this.arrayRowSnapshots[formName][arrayPath]) {
2698
+ this.arrayRowSnapshots[formName][arrayPath] = new Map();
2699
+ }
2700
+ }
2701
+ // ==================== FormArray 行级编辑 ====================
2702
+ // 开启指定行的编辑模式(保存当前值为快照)
2703
+ startRowEdit(formName, arrayPath, index) {
2704
+ const formArray = this.getFormArray(formName, arrayPath);
2705
+ if (!formArray)
2706
+ return;
2707
+ const rowControl = formArray.at(index);
2708
+ if (!rowControl) {
2709
+ console.warn(`[AntdFormService] startRowEdit: row ${index} not found in "${arrayPath}".`);
2710
+ return;
2711
+ }
2712
+ this._ensureArrayState(formName, arrayPath);
2713
+ // 保存快照(仅当快照不存在时,避免重复编辑覆盖原始值)
2714
+ const snapshotMap = this.arrayRowSnapshots[formName][arrayPath];
2715
+ if (!snapshotMap.has(index)) {
2716
+ snapshotMap.set(index, JSON.parse(JSON.stringify(rowControl.getRawValue())));
2717
+ }
2718
+ this.arrayEditingRows[formName][arrayPath].add(index);
2719
+ }
2720
+ // 确认保存指定行(验证通过则清除快照并退出编辑状态,返回行数据;否则返回 false)
2721
+ confirmRowEdit(formName, arrayPath, index) {
2722
+ const formArray = this.getFormArray(formName, arrayPath);
2723
+ if (!formArray)
2724
+ return false;
2725
+ const rowControl = formArray.at(index);
2726
+ if (!rowControl) {
2727
+ console.warn(`[AntdFormService] confirmRowEdit: row ${index} not found in "${arrayPath}".`);
2728
+ return false;
2729
+ }
2730
+ if (rowControl.invalid) {
2731
+ this.markAllControlsAsDirty(rowControl, true, false);
2732
+ return false;
2733
+ }
2734
+ this._ensureArrayState(formName, arrayPath);
2735
+ this.arrayRowSnapshots[formName][arrayPath].delete(index);
2736
+ this.arrayEditingRows[formName][arrayPath].delete(index);
2737
+ return this.excludeInternalFields(rowControl.getRawValue());
2738
+ }
2739
+ // 取消编辑指定行(从快照恢复值;若无快照则说明是新增行,直接删除该行)
2740
+ cancelRowEdit(formName, arrayPath, index) {
2741
+ const formArray = this.getFormArray(formName, arrayPath);
2742
+ if (!formArray)
2743
+ return;
2744
+ this._ensureArrayState(formName, arrayPath);
2745
+ const snapshotMap = this.arrayRowSnapshots[formName][arrayPath];
2746
+ if (snapshotMap.has(index)) {
2747
+ // 已有行:从快照恢复值
2748
+ const snapshot = snapshotMap.get(index);
2749
+ const rowControl = formArray.at(index);
2750
+ if (rowControl) {
2751
+ rowControl.patchValue(snapshot);
2752
+ rowControl.markAsPristine();
2753
+ rowControl.markAsUntouched();
2754
+ }
2755
+ snapshotMap.delete(index);
2756
+ this.arrayEditingRows[formName][arrayPath].delete(index);
2757
+ }
2758
+ else {
2759
+ // 新增行:直接删除
2760
+ this.removeArrayRow(formName, arrayPath, index);
2761
+ }
2762
+ }
2763
+ // 查询指定行是否处于编辑状态
2764
+ isRowEditing(formName, arrayPath, index) {
2765
+ return (this.arrayEditingRows[formName]?.[arrayPath]?.has(index) ?? false);
2766
+ }
2767
+ // 获取所有正在编辑的行索引列表
2768
+ getEditingRowIndices(formName, arrayPath) {
2769
+ const editingSet = this.arrayEditingRows[formName]?.[arrayPath];
2770
+ return editingSet ? Array.from(editingSet).sort((a, b) => a - b) : [];
2771
+ }
2444
2772
  // ==================== 工具方法 ====================
2445
2773
  // 获取表单类名
2446
2774
  getFormClassName(name) {