@yibozhang/pro-table 0.0.4 → 0.0.5

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.
@@ -2113,59 +2113,75 @@ AntdFormService.ctorParameters = () => [
2113
2113
  { type: FormBuilder }
2114
2114
  ];
2115
2115
 
2116
- /**
2117
- * 数组型数据收集基础服务
2118
- *
2119
- * 功能:
2120
- * 1. 支持通过 ngModel 绑定对应行的数据源
2121
- * 2. 自动为数组数据添加 isEdit 和 uid 字段
2122
- * 3. 支持每个字段的自定义校验方法,返回 'success' | 'warning' | 'error'
2123
- * 4. 提供公共方法支持删除、新增、编辑等操作
2124
- */
2116
+ /** 数组型数据收集基础服务 */
2125
2117
  class ArrayFormService {
2126
2118
  constructor() {
2127
2119
  this.formStore = {};
2128
2120
  }
2129
- /**
2130
- * 初始化数组数据
2131
- * 为现有数据添加 uid isEdit 字段
2132
- *
2133
- * @param data 原始数据数组
2134
- * @param config 表单配置
2135
- * @returns 初始化后的数据数组
2136
- */
2121
+ /** 深度克隆值 */
2122
+ cloneValue(value) {
2123
+ if (value === null || value === undefined) {
2124
+ return value;
2125
+ }
2126
+ if (Array.isArray(value)) {
2127
+ return value.map((item) => this.cloneValue(item));
2128
+ }
2129
+ if (typeof value === "object") {
2130
+ const cloned = {};
2131
+ for (const key of Object.keys(value)) {
2132
+ cloned[key] = this.cloneValue(value[key]);
2133
+ }
2134
+ return cloned;
2135
+ }
2136
+ return value;
2137
+ }
2138
+ /** 比较两个值是否相等 */
2139
+ isEqual(a, b) {
2140
+ if (a === b)
2141
+ return true;
2142
+ if (a === null || b === null)
2143
+ return a === b;
2144
+ if (a === undefined || b === undefined)
2145
+ return a === b;
2146
+ if (typeof a !== typeof b)
2147
+ return false;
2148
+ if (Array.isArray(a) && Array.isArray(b)) {
2149
+ if (a.length !== b.length)
2150
+ return false;
2151
+ return a.every((item, index) => this.isEqual(item, b[index]));
2152
+ }
2153
+ if (typeof a === "object" && typeof b === "object") {
2154
+ const keysA = Object.keys(a);
2155
+ const keysB = Object.keys(b);
2156
+ if (keysA.length !== keysB.length)
2157
+ return false;
2158
+ return keysA.every((key) => this.isEqual(a[key], b[key]));
2159
+ }
2160
+ return false;
2161
+ }
2162
+ /** 创建行的初始值快照 */
2163
+ createRowSnapshot(row, fields) {
2164
+ const snapshot = {};
2165
+ fields.forEach((field) => {
2166
+ snapshot[field.name] = this.cloneValue(row[field.name]);
2167
+ });
2168
+ return snapshot;
2169
+ }
2170
+ /** 初始化数组数据,添加 uid 和 isEdit 字段 */
2137
2171
  initializeData(data) {
2138
2172
  return data.map((item) => this.enrichRow(item));
2139
2173
  }
2140
- /**
2141
- * 为单行数据添加 uid 和 isEdit 字段
2142
- *
2143
- * @param row 原始行数据
2144
- * @returns 增强后的行数据
2145
- */
2174
+ /** 为单行数据添加 uid 和 isEdit 字段 */
2146
2175
  enrichRow(row) {
2147
- return Object.assign(Object.assign({}, row), { uid: row.uid || v4(), isEdit: row.isEdit !== undefined ? row.isEdit : false });
2176
+ return Object.assign(Object.assign({}, row), { uid: row.uid || v4(), isEdit: row.isEdit !== undefined ? row.isEdit : false, disabled: row.disabled !== undefined ? row.disabled : false });
2148
2177
  }
2149
- /**
2150
- * 新增一行数据
2151
- *
2152
- * @param data 数据数组引用
2153
- * @param defaultValues 默认值
2154
- * @returns 新增的行数据
2155
- */
2178
+ /** 新增一行数据 */
2156
2179
  addRow(data, defaultValues) {
2157
- const newRow = Object.assign(Object.assign({}, defaultValues), { uid: v4(), isEdit: true, isAdd: true });
2180
+ const newRow = Object.assign(Object.assign({}, defaultValues), { uid: v4(), isEdit: true, isAdd: true, disabled: false });
2158
2181
  data.push(newRow);
2159
2182
  return newRow;
2160
2183
  }
2161
- /**
2162
- * 校验单个字段
2163
- *
2164
- * @param row 行数据
2165
- * @param fieldName 字段名
2166
- * @param config 表单配置
2167
- * @returns 校验结果
2168
- */
2184
+ /** 校验单个字段 */
2169
2185
  validateField(row, fieldName, config) {
2170
2186
  const fieldConfig = config.fields.find((f) => f.name === fieldName);
2171
2187
  if (!fieldConfig) {
@@ -2176,13 +2192,7 @@ class ArrayFormService {
2176
2192
  }
2177
2193
  return "";
2178
2194
  }
2179
- /**
2180
- * 校验整行数据
2181
- *
2182
- * @param row 行数据
2183
- * @param config 表单配置
2184
- * @returns 校验结果对象,key为字段名,value为校验结果
2185
- */
2195
+ /** 校验整行数据 */
2186
2196
  validateRow(row, config) {
2187
2197
  const results = {};
2188
2198
  config.fields.forEach((fieldConfig) => {
@@ -2190,13 +2200,7 @@ class ArrayFormService {
2190
2200
  });
2191
2201
  return results;
2192
2202
  }
2193
- /**
2194
- * 校验整个数组
2195
- *
2196
- * @param data 数据数组
2197
- * @param config 表单配置
2198
- * @returns 校验结果对象,key为行的uid,value为该行的校验结果
2199
- */
2203
+ /** 校验整个数组 */
2200
2204
  validateData(data, config) {
2201
2205
  const results = {};
2202
2206
  data.forEach((row) => {
@@ -2204,90 +2208,126 @@ class ArrayFormService {
2204
2208
  });
2205
2209
  return results;
2206
2210
  }
2207
- /**
2208
- * 注册表单到存储
2209
- * 如果表单已存在,则更新其配置
2210
- *
2211
- * @param formName 表单名称
2212
- * @param data 数据数组
2213
- * @param config 表单配置
2214
- */
2211
+ /** 注册表单到存储 */
2215
2212
  registerForm(formName, data, config) {
2213
+ const initializedData = this.initializeData(data);
2214
+ // 记录初始值快照
2215
+ const initialSnapshot = {};
2216
+ initializedData.forEach((row) => {
2217
+ initialSnapshot[row.uid] = this.createRowSnapshot(row, config.fields);
2218
+ });
2216
2219
  this.formStore[formName] = {
2217
- data: this.initializeData(data),
2220
+ data: initializedData,
2218
2221
  config,
2219
2222
  validationResults: {},
2223
+ initialSnapshot,
2224
+ editingSnapshot: {},
2225
+ allTouched: false,
2226
+ rowTouched: {},
2220
2227
  };
2221
2228
  // 如果配置了自动更新,立即更新一次
2222
2229
  this.autoUpdateArrayReference(formName);
2223
2230
  }
2224
- /**
2225
- * 从存储中获取表单
2226
- *
2227
- * @param formName 表单名称
2228
- * @returns 表单存储项或undefined
2229
- */
2231
+ /** 从存储中获取表单 */
2230
2232
  getForm(formName) {
2231
2233
  return this.formStore[formName];
2232
2234
  }
2233
- /**
2234
- * 向表单添加行
2235
- *
2236
- * @param formName 表单名称
2237
- * @param defaultValues 默认值
2238
- * @returns 新增的行数据或undefined
2239
- */
2235
+ /** 从存储中获取表单数据 */
2236
+ getFormData(formName) {
2237
+ const form = this.formStore[formName];
2238
+ if (!form) {
2239
+ return undefined;
2240
+ }
2241
+ return form.data.map((item) => {
2242
+ delete item.isEdit;
2243
+ delete item.isAdd;
2244
+ delete item.uid;
2245
+ return item;
2246
+ });
2247
+ }
2248
+ /** 向表单添加行 */
2240
2249
  addRowToForm(formName, defaultValues) {
2241
2250
  const form = this.formStore[formName];
2242
2251
  if (!form) {
2243
2252
  return undefined;
2244
2253
  }
2245
2254
  const newRow = this.addRow(form.data, defaultValues);
2255
+ // 为新行记录初始值快照
2256
+ form.initialSnapshot[newRow.uid] = this.createRowSnapshot(newRow, form.config.fields);
2246
2257
  // 自动更新外部数组引用
2247
2258
  this.autoUpdateArrayReference(formName);
2248
2259
  return newRow;
2249
2260
  }
2250
- /**
2251
- * 校验表单
2252
- *
2253
- * @param formName 表单名称
2254
- * @returns 校验结果或undefined
2255
- */
2261
+ /** 校验表单,自动标记所有字段为已触碰 */
2256
2262
  validateForm(formName) {
2257
2263
  const form = this.formStore[formName];
2258
2264
  if (!form) {
2259
- return undefined;
2265
+ return true;
2260
2266
  }
2267
+ // 标记所有字段为已触碰
2268
+ form.allTouched = true;
2261
2269
  const results = this.validateData(form.data, form.config);
2262
2270
  form.validationResults = results;
2263
- return results;
2271
+ // 检查是否有任何错误
2272
+ for (const uid of Object.keys(results)) {
2273
+ for (const fieldName of Object.keys(results[uid])) {
2274
+ if (results[uid][fieldName]) {
2275
+ return false;
2276
+ }
2277
+ }
2278
+ }
2279
+ return true;
2264
2280
  }
2265
- /**
2266
- * 开启指定行的编辑模式
2267
- *
2268
- * @param row 行数据
2269
- */
2270
- enableEdit(row) {
2281
+ /** 开启编辑模式,保存快照用于取消恢复 */
2282
+ enableEdit(formName, row) {
2283
+ const form = this.formStore[formName];
2284
+ if (form && row.uid) {
2285
+ // 保存编辑前的快照
2286
+ form.editingSnapshot[row.uid] = this.createRowSnapshot(row, form.config.fields);
2287
+ }
2271
2288
  row.isEdit = true;
2272
2289
  }
2273
- /**
2274
- * 关闭指定行的编辑模式
2275
- *
2276
- * @param row 行数据
2277
- */
2278
- disableEdit(row) {
2290
+ /** 取消编辑,恢复到编辑前的数据 */
2291
+ disableEdit(formName, row) {
2292
+ const form = this.formStore[formName];
2293
+ if (form && row.uid && form.editingSnapshot[row.uid]) {
2294
+ // 恢复编辑前的数据
2295
+ const snapshot = form.editingSnapshot[row.uid];
2296
+ form.config.fields.forEach((field) => {
2297
+ row[field.name] = this.cloneValue(snapshot[field.name]);
2298
+ });
2299
+ // 更新初始快照为恢复后的值,消除 touched 状态
2300
+ form.initialSnapshot[row.uid] = this.cloneValue(snapshot);
2301
+ // 清理编辑快照和 touched 状态
2302
+ delete form.editingSnapshot[row.uid];
2303
+ delete form.rowTouched[row.uid];
2304
+ // 清理该行的校验结果
2305
+ if (form.validationResults) {
2306
+ delete form.validationResults[row.uid];
2307
+ }
2308
+ }
2279
2309
  row.isEdit = false;
2280
2310
  }
2281
- /**
2282
- * 从表单中删除指定行
2283
- *
2284
- * @param formName 表单名称
2285
- * @param row 行的uid
2286
- * @returns 是否删除成功
2287
- */
2311
+ /** 确认编辑,保留修改后的数据 */
2312
+ confirmEdit(formName, row) {
2313
+ const form = this.formStore[formName];
2314
+ if (form && row.uid) {
2315
+ // 清理编辑快照(保留当前数据)
2316
+ if (form.editingSnapshot[row.uid]) {
2317
+ delete form.editingSnapshot[row.uid];
2318
+ }
2319
+ // 更新初始快照为当前值(用于后续 touched 检测)
2320
+ form.initialSnapshot[row.uid] = this.createRowSnapshot(row, form.config.fields);
2321
+ // 清除 touched 状态,基于新的 initialSnapshot 重新判断
2322
+ delete form.rowTouched[row.uid];
2323
+ }
2324
+ row.isEdit = false;
2325
+ row.isAdd = false;
2326
+ }
2327
+ /** 从表单中删除指定行 */
2288
2328
  deleteRowFromForm(formName, row) {
2289
2329
  const form = this.formStore[formName];
2290
- if (!form) {
2330
+ if (!form || !row.uid) {
2291
2331
  return false;
2292
2332
  }
2293
2333
  const index = form.data.findIndex((item) => item.uid === row.uid);
@@ -2295,31 +2335,44 @@ class ArrayFormService {
2295
2335
  return false;
2296
2336
  }
2297
2337
  form.data.splice(index, 1);
2298
- if (form.validationResults && form.validationResults[row.uid]) {
2338
+ // 清理该行的所有快照、touched 状态和校验结果
2339
+ delete form.initialSnapshot[row.uid];
2340
+ delete form.editingSnapshot[row.uid];
2341
+ delete form.rowTouched[row.uid];
2342
+ if (form.validationResults) {
2299
2343
  delete form.validationResults[row.uid];
2300
2344
  }
2301
2345
  // 自动更新外部数组引用
2302
2346
  this.autoUpdateArrayReference(formName);
2303
2347
  return true;
2304
2348
  }
2305
- /**
2306
- * 校验指定行的指定字段并返回校验结果
2307
- * 实时执行校验,用于模板中的实时校验显示
2308
- *
2309
- * @param formName 表单名称
2310
- * @param row 行数据
2311
- * @param fieldName 字段名
2312
- * @returns 校验结果
2313
- */
2349
+ /** 检测字段是否已被触碰 */
2350
+ isFieldTouched(form, row, fieldName) {
2351
+ var _a;
2352
+ // 整个表单已 touched(整个表单提交时)
2353
+ if (form.allTouched) {
2354
+ return true;
2355
+ }
2356
+ // 当前行已 touched(单行保存时)
2357
+ if (form.rowTouched[row.uid]) {
2358
+ return true;
2359
+ }
2360
+ // 检测当前值与初始值是否不同
2361
+ const initialValue = (_a = form.initialSnapshot[row.uid]) === null || _a === void 0 ? void 0 : _a[fieldName];
2362
+ const currentValue = row[fieldName];
2363
+ return !this.isEqual(initialValue, currentValue);
2364
+ }
2365
+ /** 校验指定行的指定字段,未修改时不显示校验状态 */
2314
2366
  validateFieldInRow(formName, row, fieldName) {
2315
2367
  const form = this.formStore[formName];
2316
2368
  if (!form) {
2317
- return "success";
2369
+ return "";
2318
2370
  }
2319
2371
  const fieldConfig = form.config.fields.find((f) => f.name === fieldName);
2320
2372
  if (!fieldConfig) {
2321
- return "success";
2373
+ return "";
2322
2374
  }
2375
+ // 执行校验
2323
2376
  let result = "";
2324
2377
  if (fieldConfig.validator) {
2325
2378
  result = fieldConfig.validator(row[fieldName], row, fieldName);
@@ -2332,20 +2385,35 @@ class ArrayFormService {
2332
2385
  form.validationResults[row.uid] = {};
2333
2386
  }
2334
2387
  form.validationResults[row.uid][fieldName] = result;
2388
+ // 如果字段未被触碰,不显示校验状态
2389
+ if (!this.isFieldTouched(form, row, fieldName)) {
2390
+ return "";
2391
+ }
2335
2392
  return result ? "error" : "success";
2336
2393
  }
2337
- /**
2338
- * 校验指定行的所有字段
2339
- *
2340
- * @param formName 表单名称
2341
- * @param row 行数据
2342
- * @returns 是否所有字段都通过校验
2343
- */
2394
+ /** 获取指定行指定字段的校验错误信息 */
2395
+ getFieldErrorMessage(formName, row, fieldName) {
2396
+ var _a, _b;
2397
+ const form = this.formStore[formName];
2398
+ if (!form || !row.uid) {
2399
+ return "";
2400
+ }
2401
+ // 如果字段未被触碰,不显示错误信息
2402
+ if (!this.isFieldTouched(form, row, fieldName)) {
2403
+ return "";
2404
+ }
2405
+ return ((_b = (_a = form.validationResults) === null || _a === void 0 ? void 0 : _a[row.uid]) === null || _b === void 0 ? void 0 : _b[fieldName]) || "";
2406
+ }
2407
+ /** 校验指定行的所有字段 */
2344
2408
  validateRowAllFields(formName, row) {
2345
2409
  const form = this.formStore[formName];
2346
- if (!form) {
2410
+ if (!form || !row.uid) {
2347
2411
  return true;
2348
2412
  }
2413
+ // 更新初始快照为当前值
2414
+ form.initialSnapshot[row.uid] = this.createRowSnapshot(row, form.config.fields);
2415
+ // 标记当前行为 touched(只影响当前行,不影响其他行)
2416
+ form.rowTouched[row.uid] = true;
2349
2417
  let hasError = false;
2350
2418
  form.config.fields.forEach((fieldConfig) => {
2351
2419
  const result = this.validateFieldInRow(formName, row, fieldConfig.name);
@@ -2355,12 +2423,7 @@ class ArrayFormService {
2355
2423
  });
2356
2424
  return !hasError;
2357
2425
  }
2358
- /**
2359
- * 自动更新外部数组引用(如果配置了)
2360
- * 私有方法,在数据变化时自动调用
2361
- *
2362
- * @param formName 表单名称
2363
- */
2426
+ /** 自动更新外部数组引用 */
2364
2427
  autoUpdateArrayReference(formName) {
2365
2428
  const form = this.formStore[formName];
2366
2429
  if (!form || !form.config.targetObject || !form.config.arrayPropertyName) {
@@ -2370,6 +2433,14 @@ class ArrayFormService {
2370
2433
  // 更新目标对象的数组属性为新数组引用
2371
2434
  form.config.targetObject[form.config.arrayPropertyName] = newArray;
2372
2435
  }
2436
+ /** 是否有正在编辑的行 */
2437
+ hasEditingRow(formName) {
2438
+ const form = this.formStore[formName];
2439
+ if (!form) {
2440
+ return false;
2441
+ }
2442
+ return form.data.some((item) => item.isEdit);
2443
+ }
2373
2444
  }
2374
2445
  ArrayFormService.ɵprov = ɵɵdefineInjectable({ factory: function ArrayFormService_Factory() { return new ArrayFormService(); }, token: ArrayFormService, providedIn: "root" });
2375
2446
  ArrayFormService.decorators = [