/** * 表单视图模型(1.7.2)。 * * 单行记录的表单编辑:字段定义(可编辑/只读/必填),值的脏检测与回滚, * 提交时产出 CellChange 交给 TableEditor 生成 UPDATE。 */ import { CellChange } from './TableEditor'; export interface FieldSpec { column: string; label: string; editable: boolean; required: boolean; } export class FormViewModel { private readonly fields: FieldSpec[]; private original: Map = new Map(); private current: Map = new Map(); constructor(fields: FieldSpec[]) { this.fields = fields.slice(); } fields_(): FieldSpec[] { return this.fields.slice(); } /** 载入一行数据(重置脏状态)。 */ load(values: Map): void { this.original = new Map(); this.current = new Map(); for (let i = 0; i < this.fields.length; i++) { const column: string = this.fields[i].column; const value: string | undefined = values.get(column); this.original.set(column, value === undefined ? '' : value); this.current.set(column, value === undefined ? '' : value); } } get(column: string): string { const value: string | undefined = this.current.get(column); return value === undefined ? '' : value; } /** 修改值;只读列或未知列忽略。 */ set(column: string, value: string): boolean { const field: FieldSpec | undefined = this.fieldOf(column); if (field === undefined || !field.editable) { return false; } this.current.set(column, value); return true; } isEditable(column: string): boolean { const field: FieldSpec | undefined = this.fieldOf(column); return field !== undefined && field.editable; } /** 脏字段(值发生过变化)。 */ dirtyFields(): string[] { const out: string[] = []; for (let i = 0; i < this.fields.length; i++) { const column: string = this.fields[i].column; if (this.get(column) !== this.originalOf(column)) { out.push(column); } } return out; } isDirty(): boolean { return this.dirtyFields().length > 0; } /** 回滚到载入时的值。 */ revert(): void { this.current = new Map(); this.original.forEach((value: string, column: string) => { this.current.set(column, value); }); } /** 必填校验;返回错误列与消息。 */ validate(): string[] { const errors: string[] = []; for (let i = 0; i < this.fields.length; i++) { const field: FieldSpec = this.fields[i]; if (field.required && this.get(field.column).trim().length === 0) { errors.push(field.column + ' is required'); } } return errors; } /** 产出变更(供 TableEditor 提交);必填不通过时返回空数组。 */ changes(): CellChange[] { if (this.validate().length > 0) { return []; } const out: CellChange[] = []; const dirty: string[] = this.dirtyFields(); for (let i = 0; i < dirty.length; i++) { out.push({ rowIndex: 0, column: dirty[i], oldValue: this.originalOf(dirty[i]), newValue: this.get(dirty[i]) }); } return out; } private originalOf(column: string): string { const value: string | undefined = this.original.get(column); return value === undefined ? '' : value; } private fieldOf(column: string): FieldSpec | undefined { for (let i = 0; i < this.fields.length; i++) { if (this.fields[i].column === column) { return this.fields[i]; } } return undefined; } }