diff --git a/packages/grid/src/components/field-menu/field-menu.component.ts b/packages/grid/src/components/field-menu/field-menu.component.ts index fd49f9de..1b7bc2ba 100644 --- a/packages/grid/src/components/field-menu/field-menu.component.ts +++ b/packages/grid/src/components/field-menu/field-menu.component.ts @@ -1,6 +1,6 @@ -import { Component, ChangeDetectionStrategy, Input, ElementRef, signal } from '@angular/core'; -import { AITableFieldMenu } from '../../types/field'; -import { AITableField, AITable } from '../../core'; +import { Component, ChangeDetectionStrategy, Input, ElementRef, signal, Signal } from '@angular/core'; +import { AITableFieldMenuItem } from '../../types/field'; +import { AITable, AITableField } from '../../core'; import { ThyDropdownMenuItemDirective, ThyDropdownMenuItemNameDirective, @@ -9,6 +9,7 @@ import { } from 'ngx-tethys/dropdown'; import { ThyIcon } from 'ngx-tethys/icon'; import { ThyDivider } from 'ngx-tethys/divider'; +import { getRecordOrField } from '../../utils'; @Component({ selector: 'field-menu', @@ -25,16 +26,16 @@ import { ThyDivider } from 'ngx-tethys/divider'; ] }) export class FieldMenu { - @Input({ required: true }) field!: AITableField; + @Input({ required: true }) fieldId!: string; @Input({ required: true }) aiTable!: AITable; - @Input({ required: true }) fieldMenus!: AITableFieldMenu[]; + @Input({ required: true }) fieldMenus!: AITableFieldMenuItem[]; @Input() origin!: HTMLElement | ElementRef; - execute(menu: AITableFieldMenu) { - const field = signal({ ...this.field }); + execute(menu: AITableFieldMenuItem) { + const field = getRecordOrField(this.aiTable.fields, this.fieldId) as Signal; menu.exec && menu.exec(this.aiTable, field, this.origin); } } diff --git a/packages/grid/src/components/field-property-editor/field-property-editor.component.html b/packages/grid/src/components/field-property-editor/field-property-editor.component.html index aa8370fb..23316f8f 100644 --- a/packages/grid/src/components/field-property-editor/field-property-editor.component.html +++ b/packages/grid/src/components/field-property-editor/field-property-editor.component.html @@ -3,7 +3,7 @@ item.id === this.aiField().id); + Actions.setField(this.aiTable, this.aiField(), [path]); } else { Actions.addField(this.aiTable, this.aiField(), [this.aiTable.fields().length]); } diff --git a/packages/grid/src/constants/field.ts b/packages/grid/src/constants/field.ts index 44d9cec6..50d4489c 100644 --- a/packages/grid/src/constants/field.ts +++ b/packages/grid/src/constants/field.ts @@ -1,7 +1,7 @@ import { AITable, AITableField } from '../core'; -import { AITableFieldMenu } from '../types/field'; +import { AITableFieldMenuItem } from '../types/field'; import { AI_TABLE_GRID_FIELD_SERVICE_MAP } from '../services/field.service'; -import { ElementRef, WritableSignal } from '@angular/core'; +import { ElementRef, signal, Signal, WritableSignal } from '@angular/core'; export const DividerMenuItem = { id: 'divider' @@ -11,10 +11,11 @@ export const EditFieldPropertyItem = { id: 'editFieldProperty', name: '编辑列', icon: 'edit', - exec: (aiTable: AITable, field: WritableSignal, origin?: HTMLElement | ElementRef) => { + exec: (aiTable: AITable, field: Signal, origin?: HTMLElement | ElementRef) => { const fieldService = AI_TABLE_GRID_FIELD_SERVICE_MAP.get(aiTable); - origin && fieldService?.editFieldProperty(origin, aiTable, field, true); + const copyField: WritableSignal = signal(JSON.parse(JSON.stringify(field()))); + origin && fieldService?.editFieldProperty(origin, aiTable, copyField, true); } }; -export const DefaultFieldMenus: AITableFieldMenu[] = [EditFieldPropertyItem]; +export const DefaultFieldMenus: AITableFieldMenuItem[] = [EditFieldPropertyItem]; diff --git a/packages/grid/src/core/action/field.ts b/packages/grid/src/core/action/field.ts index 83e9b66d..377c842c 100644 --- a/packages/grid/src/core/action/field.ts +++ b/packages/grid/src/core/action/field.ts @@ -1,4 +1,5 @@ -import { ActionName, AddFieldAction, AIFieldPath, AITable, AITableField } from '../types'; +import { ActionName, AddFieldAction, AIFieldPath, AITable, AITableField, SetFieldAction } from '../types'; +import { AITableQueries } from '../utils'; export function addField(aiTable: AITable, field: AITableField, path: AIFieldPath) { const operation: AddFieldAction = { @@ -9,6 +10,32 @@ export function addField(aiTable: AITable, field: AITableField, path: AIFieldPat aiTable.apply(operation); } +export function setField(aiTable: AITable, value: Partial, path: AIFieldPath) { + const field = AITableQueries.getField(aiTable, path); + if (field) { + const oldField: Partial = {}; + const newField: Partial = {}; + for (const k in value) { + if (field[k] !== value[k]) { + if (field.hasOwnProperty(k)) { + oldField[k] = field[k]; + } + newField[k] = value[k]; + } + } + + const operation: SetFieldAction = { + type: ActionName.SetField, + field: oldField, + newField, + path + }; + + aiTable.apply(operation); + } +} + export const FieldActions = { - addField + addField, + setField }; diff --git a/packages/grid/src/core/action/general.ts b/packages/grid/src/core/action/general.ts index d6c9e790..eb49cff1 100644 --- a/packages/grid/src/core/action/general.ts +++ b/packages/grid/src/core/action/general.ts @@ -27,6 +27,18 @@ const apply = (aiTable: AITable, records: AITableRecords, fields: AITableFields, ...newRecord }; }); + break; + } + + case ActionName.SetField: { + const [fieldIndex] = options.path; + if (fieldIndex > -1) { + fields.splice(fieldIndex, 1, { + ...fields[fieldIndex], + ...options.newField + }); + } + break; } } return { diff --git a/packages/grid/src/core/action/record.ts b/packages/grid/src/core/action/record.ts index 3d79a7cd..b8f5d41c 100644 --- a/packages/grid/src/core/action/record.ts +++ b/packages/grid/src/core/action/record.ts @@ -2,11 +2,11 @@ import { ActionName, AddRecordAction, AIRecordPath, UpdateFieldValueAction, AITa import { AITableQueries } from '../utils'; export function updateFieldValue(aiTable: AITable, value: any, path: AIFieldValuePath) { - const node = AITableQueries.getFieldValue(aiTable, path); - if (node !== value) { + const field = AITableQueries.getFieldValue(aiTable, path); + if (field !== value) { const operation: UpdateFieldValueAction = { type: ActionName.UpdateFieldValue, - fieldValue: node, + fieldValue: field, newFieldValue: value, path }; diff --git a/packages/grid/src/core/types/action.ts b/packages/grid/src/core/types/action.ts index 0444151a..a5f6819e 100644 --- a/packages/grid/src/core/types/action.ts +++ b/packages/grid/src/core/types/action.ts @@ -11,7 +11,8 @@ export type Path = AIRecordPath | AIFieldPath | AIFieldValuePath; export enum ActionName { UpdateFieldValue = 'update_field_value', AddRecord = 'add_record', - AddField = 'add_field' + AddField = 'add_field', + SetField = 'set_field' } export enum ExecuteType { @@ -39,4 +40,15 @@ export type AddFieldAction = { field: AITableField; }; -export type AITableAction = UpdateFieldValueAction | AddRecordAction | AddFieldAction; +export type SetFieldAction = { + type: ActionName.SetField; + path: AIFieldPath; + field: Partial; + newField: Partial; +}; + +export type AITableAction = + | UpdateFieldValueAction + | AddRecordAction + | AddFieldAction + | SetFieldAction; diff --git a/packages/grid/src/core/utils/queries.ts b/packages/grid/src/core/utils/queries.ts index 2eeeb016..379a1407 100644 --- a/packages/grid/src/core/utils/queries.ts +++ b/packages/grid/src/core/utils/queries.ts @@ -18,13 +18,16 @@ export const AITableQueries = { }, getFieldValue(aiTable: AITable, path: [number, number]): any { if (!aiTable) { - throw new Error(`aiTable does not exist [${path}]`); + throw new Error(`aiTable does not exist`); } if (!aiTable.records()) { - throw new Error(`aiTable has no records [${path}]`); + throw new Error(`aiTable has no records`); } if (!aiTable.fields()) { - throw new Error(`aiTable has no fields [${path}]`); + throw new Error(`aiTable has no fields`); + } + if (!path) { + throw new Error(`path does not exist as path [${path}]`); } const field = aiTable.fields()[path[1]]; @@ -32,5 +35,15 @@ export const AITableQueries = { throw new Error(`can not find field at path [${path}]`); } return aiTable.records()[path[0]].values[field.id]; + }, + + getField(aiTable: AITable, path: AIFieldPath): AITableField { + if (!aiTable) { + throw new Error(`aiTable does not exist`); + } + if (!path) { + throw new Error(`path does not exist as path [${path}]`); + } + return aiTable.fields()[path[0]]; } }; diff --git a/packages/grid/src/grid.component.html b/packages/grid/src/grid.component.html index 264201b9..ad80a2fd 100644 --- a/packages/grid/src/grid.component.html +++ b/packages/grid/src/grid.component.html @@ -10,7 +10,7 @@ [ngClass]="{ highlight: aiTable.selection().selectedFields.has(field.id) }" [ngStyle]="{ width: field.width + 'px' }" > - + {{ field.name }} @@ -23,7 +23,7 @@ href="javascript:;" > - + diff --git a/packages/grid/src/grid.component.ts b/packages/grid/src/grid.component.ts index c438828b..a5cdc4b3 100644 --- a/packages/grid/src/grid.component.ts +++ b/packages/grid/src/grid.component.ts @@ -5,7 +5,7 @@ import { ThyTag } from 'ngx-tethys/tag'; import { ThyPopoverModule } from 'ngx-tethys/popover'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { buildGridData } from './utils'; -import { AIFieldConfig, AITableFieldMenu, AITableRowHeight } from './types'; +import { AIFieldConfig, AITableFieldMenuItem, AITableRowHeight } from './types'; import { Actions, createAITable, @@ -94,7 +94,7 @@ export class AITableGrid implements OnInit { aiTableInitialized = output(); - fieldMenus!: AITableFieldMenu[]; + fieldMenus!: AITableFieldMenuItem[]; gridData = computed(() => { return buildGridData(this.aiRecords(), this.aiFields(), this.aiTable.selection()); diff --git a/packages/grid/src/types/field.ts b/packages/grid/src/types/field.ts index c6881034..a35ca0c2 100644 --- a/packages/grid/src/types/field.ts +++ b/packages/grid/src/types/field.ts @@ -1,11 +1,11 @@ -import { ElementRef, Signal, WritableSignal } from '@angular/core'; +import { ElementRef, Signal } from '@angular/core'; import { AITable, AITableField } from '../core'; -export interface AITableFieldMenu { +export interface AITableFieldMenuItem { id: string; name?: string; icon?: string; - exec?: (aiTable: AITable, field: WritableSignal, origin?: HTMLElement | ElementRef) => void; + exec?: (aiTable: AITable, field: Signal, origin?: HTMLElement | ElementRef) => void; hidden?: (aiTable: AITable, field: Signal) => boolean; disabled?: (aiTable: AITable, field: Signal) => boolean; } diff --git a/packages/grid/src/types/grid.ts b/packages/grid/src/types/grid.ts index 09f38c13..71a5f682 100644 --- a/packages/grid/src/types/grid.ts +++ b/packages/grid/src/types/grid.ts @@ -1,5 +1,5 @@ import { AITableField, AITableFieldType, AITableRecord } from '../core'; -import { AITableFieldMenu } from './field'; +import { AITableFieldMenuItem } from './field'; export enum AITableRowHeight { Short = 1, @@ -26,5 +26,5 @@ export interface AITableSelection { export interface AIFieldConfig { fieldRenderers?: Partial>; fieldPropertyEditor?: any; - fieldMenus?: AITableFieldMenu[]; + fieldMenus?: AITableFieldMenuItem[]; }