diff --git a/.gitignore b/.gitignore index 0c76a4b37117ac56270bb5a2d9e4d742d1ebc4f5..548aec194f204bd072bf7b7a4e7d2bfcc2fa0cd5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ dist-ssr # Editor directories and files .idea +.history .DS_Store *.suo *.ntvs* diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250818154710.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250818154710.ts new file mode 100644 index 0000000000000000000000000000000000000000..6aee0053af4cc4e3711ca36a48a30b6e45ccdb11 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250818154710.ts @@ -0,0 +1,394 @@ +import { DesignerHostService } from '../../../designer-canvas/src/composition/types'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '../../../../components/dynamic-resolver'; +import { ComponentBuildInfo } from '../../../component/src/composition/inner-component-build-info'; +import { ComponentSchema } from '../../../../components/designer-canvas'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { cloneDeep } from 'lodash-es'; +import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094721.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094721.ts new file mode 100644 index 0000000000000000000000000000000000000000..c8b423c257c79c787c0906610252d6b05511fe79 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094721.ts @@ -0,0 +1,394 @@ +import { DesignerHostService } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '../../../../components/dynamic-resolver'; +import { ComponentBuildInfo } from '../../../component/src/composition/inner-component-build-info'; +import { ComponentSchema } from '../../../../components/designer-canvas'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { cloneDeep } from 'lodash-es'; +import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094822.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094822.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4453c8ef327b8451d9522ec6ba815fa7ea5f4ca --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094822.ts @@ -0,0 +1,394 @@ +import { DesignerHostService, ComponentSchema } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { ComponentSchema } from '../../../../components/designer-canvas'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { cloneDeep } from 'lodash-es'; +import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094835.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094835.ts new file mode 100644 index 0000000000000000000000000000000000000000..c0bd54cead04824c1ce3a64803695a215f741cd3 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094835.ts @@ -0,0 +1,394 @@ +import { DesignerHostService, ComponentSchema } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; + +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { cloneDeep } from 'lodash-es'; +import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094843.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094843.ts new file mode 100644 index 0000000000000000000000000000000000000000..c698b0b10c5676de600941389409abb6d30f4348 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094843.ts @@ -0,0 +1,393 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094917.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094917.ts new file mode 100644 index 0000000000000000000000000000000000000000..4da91881a689f9901e7d8147048987695d4806d9 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094917.ts @@ -0,0 +1,393 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094925.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094925.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d92d23a36feffbb46ecc27c4f935a3017abfee1 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094925.ts @@ -0,0 +1,392 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094926.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094926.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d92d23a36feffbb46ecc27c4f935a3017abfee1 --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094926.ts @@ -0,0 +1,392 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; +import { useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094937.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094937.ts new file mode 100644 index 0000000000000000000000000000000000000000..946f72dae5edcc9acb73e4c97f88bedaf85047fd --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094937.ts @@ -0,0 +1,391 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName, useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094938.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094938.ts new file mode 100644 index 0000000000000000000000000000000000000000..946f72dae5edcc9acb73e4c97f88bedaf85047fd --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094938.ts @@ -0,0 +1,391 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName, useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094939.ts b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094939.ts new file mode 100644 index 0000000000000000000000000000000000000000..946f72dae5edcc9acb73e4c97f88bedaf85047fd --- /dev/null +++ b/.history/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service_20250819094939.ts @@ -0,0 +1,391 @@ +import { cloneDeep } from 'lodash-es'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName, useGuid } from '@farris/ui-vue/components/common'; + +const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; + +/** + * 创建表格组件服务类 + */ +export class DataGridComponentCreatorService { + + private formSchemaUtils: any; + private controlCreatorUtils: any; + private designViewModelUtils: any; + private useFormCommand: any; + private formStateMachineUtils: any; + + constructor( + private resolver: DynamicResolver, + private designerHostService: DesignerHostService + ) { + this.formSchemaUtils = this.designerHostService.formSchemaUtils; + this.controlCreatorUtils = this.designerHostService.controlCreatorUtils; + this.designViewModelUtils = this.designerHostService.designViewModelUtils; + this.useFormCommand = this.designerHostService.useFormCommand; + this.formStateMachineUtils = this.designerHostService.formStateMachineUtils; + } + + public createComponent(buildInfo: ComponentBuildInfo) { + const componentRefNode = this.createComponentRefNode(buildInfo); + + const componentNode = this.createComponentNode(buildInfo); + + const viewModelNode = this.createViewModeNode(buildInfo); + + const formSchema = this.formSchemaUtils.getFormSchema(); + formSchema.module.viewmodels.push(viewModelNode); + formSchema.module.components.push(componentNode); + + this.designViewModelUtils.assembleDesignViewModel(); + + return this.wrapContainerSectionForComponent(componentRefNode, buildInfo, viewModelNode); + } + /** + * 追加父容器 + * @param componentRefNode + * @param buildInfo + * @returns + */ + private wrapContainerSectionForComponent(componentRefNode: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const parentContainerType = buildInfo?.parentComponentInstance?.schema?.type; + + // 双列表模板、左树右列表模板,拖拽子表时,不生成父标题区域 + const templateId = this.formSchemaUtils.getFormSchema()?.module?.templateId; + if (parentContainerType === DgControl['splitter-pane'].type && ['double-list-template', 'tree-list-template'].includes(templateId)) { + return componentRefNode; + } + + // 1、将表格拖入无标题的目标区域,需要给表格追加Section容器。并给Section添加新增删除按钮 + const parentContainerWithoutTitle = [DgControl['content-container'].type, DgControl['response-layout-item'].type, DgControl['splitter-pane'].type]; + if (parentContainerType && parentContainerWithoutTitle.includes(parentContainerType)) { + const containerSection = this.resolver.getSchemaByType( + 'section', + { + parentComponentInstance: buildInfo.parentComponentInstance, + mainTitle: buildInfo.componentName + }, + this.designerHostService) as ComponentSchema; + if (containerSection && containerSection.contents && containerSection.contents.length) { + const section = containerSection.contents[0]; + section.contents = [componentRefNode]; + + this.appendAddDeleteBtnToParentContainer(section, buildInfo, viewModelNode); + + return containerSection; + } + } + // 2、将表格拖入有标题的目标区域,不需要追加父容器。只需要给父容器添加新增删除按钮 + this.appendAddDeleteBtnToParentContainer(buildInfo?.parentComponentInstance?.schema, buildInfo, viewModelNode); + + if (parentContainerType === DgControl['tab-page'].type) { + // 为解决标签页画布无法更新的问题,手动触发update方法 + if (buildInfo.parentComponentInstance?.parent && buildInfo.parentComponentInstance?.parent['updateToolbarItems']) { + buildInfo.parentComponentInstance?.parent['updateToolbarItems'](); + } + + } + return componentRefNode; + } + + /** + * 为父容器追加新增、删除按钮 + */ + private appendAddDeleteBtnToParentContainer(resolvedContainerSchema: any, buildInfo: ComponentBuildInfo, viewModelNode: any) { + const resolvedContainerType = resolvedContainerSchema.type; + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + + // 限制子表 + if (buildInfo.bindTo === '/') { + return; + } + // 限制列表或卡片 + if (buildInfo.componentType !== 'data-grid' && buildInfo.componentType !== 'form') { + return; + } + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + + const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; + if (!resolvedContainerSchema.toolbar) { + resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; + } + if (!resolvedContainerSchema.toolbar.id) { + resolvedContainerSchema.toolbar.id = `${resolvedContainerSchema.id}_toolbar`; + } + if (!resolvedContainerSchema.toolbar.buttons) { + resolvedContainerSchema.toolbar.buttons = []; + } + resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); + + this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } + } + /** + * 向视图模型添加新增删除命令 + */ + private appendAddAndDeleteCommands(viewModelNode: any) { + const commandPrefix = viewModelNode.id.replace(/-/g, '').replace(/_/g, '').replace('component', '').replace('viewmodel', ''); + const addCommandId = useGuid().guid(); + const deleteCommandId = useGuid().guid(); + const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); + + + // 3、记录构件命令 + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + const cardCmd = webCmds.find(webCmd => webCmd.id === cardControllerId); + cardCmd.refedHandlers.push( + { + host: addCommandId, + handler: 'AddItem' + }, + { + host: deleteCommandId, + handler: 'RemoveItem' + } + ); + } + /** + * 获取新增删除子表命令可用的控制器id。 + */ + private resolveCommandController(): string { + const webCmds = this.formSchemaUtils.getFormSchema().module.webcmds; + // 卡片控制器 / 树卡控制器 / 高级列卡控制器 + const optionalControllerId = [ + '8172a979-2c80-4637-ace7-b13074d3f393', + '8fe977a1-2b32-4f0f-a6b3-2657c4d03574', + '45be24f9-c1f7-44f7-b447-fe2ada458a61' + ]; + const availableController = webCmds.find(cmd => optionalControllerId.includes(cmd.id)); + if (availableController) { + return availableController.id; + } + + // 若当前表单没有可用的控制器,默认新增一个高级列卡控制器。这里需要触发load控制器,不然加载不到控制器内的命令 + const listDicControllerId = '45be24f9-c1f7-44f7-b447-fe2ada458a61'; + webCmds.push({ + id: listDicControllerId, + path: '/projects/packages/Inspur.GS.Gsp.Web.WebCmp/webcmd', + name: 'AdvancedListCardController.webcmd', + refedHandlers: [] + }); + + return listDicControllerId; + + + } + createComponentRefNode(buildInfo: ComponentBuildInfo): any { + const componentRefNode = this.resolver.getSchemaByType('component-ref') as ComponentSchema; + Object.assign(componentRefNode, { + id: `${buildInfo.componentId}-component-ref`, + component: `${buildInfo.componentId}-component`, + }); + return componentRefNode; + } + + createComponentNode(buildInfo: ComponentBuildInfo): any { + const componentNode = this.resolver.getSchemaByType('component') as ComponentSchema; + const contents = this.createDateGridComponentContents(buildInfo); + Object.assign(componentNode, { + id: `${buildInfo.componentId}-component`, + viewModel: `${buildInfo.componentId}-component-viewmodel`, + componentType: buildInfo.componentType, + appearance: { + class: this.getDataGridComponentClass() + }, + contents + }); + return componentNode; + } + /** + * 添加viewModel节点 + */ + createViewModeNode(buildInfo: ComponentBuildInfo): any { + const viewModelNode = { + id: `${buildInfo.componentId}-component-viewmodel`, + code: `${buildInfo.componentId}-component-viewmodel`, + name: buildInfo.componentName, + bindTo: buildInfo.bindTo, + parent: ROOT_VIEW_MODEL_ID, + fields: this.assembleViewModelFields(buildInfo), + commands: [], + states: [], + enableValidation: true + }; + return viewModelNode; + } + /** + * 获取表格组件层级的class样式 + */ + private getDataGridComponentClass(): string { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + + // 双列表标签页、双列表、树列表模板,要求列表填充表单高度 + if (['double-list-in-tab-template', 'double-list-template', 'tree-list-template'].includes(templateId)) { + return 'f-struct-wrapper f-utils-fill-flex-column'; + } + return 'f-struct-is-subgrid'; + + } + /** + * 创建表格组件内层级结构 + */ + private createDateGridComponentContents(buildInfo: ComponentBuildInfo) { + const { templateId } = this.formSchemaUtils.getFormSchema().module; + let container; + // 根据模板不同,创建不同的容器类型和样式 + if (templateId === 'double-list-in-tab-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main px-0 pt-0' + }, + fill: true, + showHeader: false + }); + container = section; + } else if (templateId === 'double-list-template' || templateId === 'tree-list-template') { + // 1、创建setion + const section = this.resolver.getSchemaByType('section') as ComponentSchema; + Object.assign(section, { + id: buildInfo.componentId + '-section', + appearance: { + class: 'f-section-grid f-section-in-main' + }, + fill: true, + showHeader: false + }); + container = section; + } else { + // 1、创建contentContainer + const contentContainer = this.resolver.getSchemaByType('content-container') as ComponentSchema; + Object.assign(contentContainer, { + id: buildInfo.componentId + '-container', + appearance: { + class: 'f-grid-is-sub f-utils-flex-column' + } + }); + container = contentContainer; + } + + // 2、创建DataGrid(暂时不开启子表分页) + const dataGrid = this.resolver.getSchemaByType('data-grid') as ComponentSchema; + const columns: any[] = []; + const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); + const fieldEditable = buildInfo.editable && stateMachineRenderState.length > 0 && stateMachineRenderState.findIndex(d => d.id === 'editable') > -1; + Object.assign(dataGrid, { + id: buildInfo.componentId + '-dataGrid', + appearance: { + class: 'f-component-grid' + }, + columns, + fieldEditable, + dataSource: buildInfo.dataSource || '', + editable: fieldEditable ? 'viewModel.stateMachine[\'editable\']' : false, + pagination: { + enable: buildInfo.editable ? false : true + } + }); + container.contents = [dataGrid]; + const { selectedFields } = buildInfo; + // 3、创建字段 + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + const dgVMField = cloneDeep(field); + const grieFieldMetadata = this.controlCreatorUtils.setGridFieldProperty('data-grid-column', dgVMField, '', fieldEditable); + if (grieFieldMetadata) { + columns.push(grieFieldMetadata); + } + }); + return [container]; + } + + + /** + * 组装viewModel fields 节点 + */ + private assembleViewModelFields(buildInfo: ComponentBuildInfo) { + + const vmFields: any[] = []; + const { selectedFields } = buildInfo; + selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } + let updateOn = 'blur'; + const type = field.type.name; + if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { + updateOn = 'change'; + } + + vmFields.push({ + type: "Form", + id: field.id, + fieldName: field.bindingField, + groupId: null, + groupName: null, + updateOn, + fieldSchema: {} + }); + }); + return vmFields; + } + +} diff --git a/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts b/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts index 6aee0053af4cc4e3711ca36a48a30b6e45ccdb11..946f72dae5edcc9acb73e4c97f88bedaf85047fd 100644 --- a/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts +++ b/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts @@ -1,11 +1,8 @@ -import { DesignerHostService } from '../../../designer-canvas/src/composition/types'; -import { DynamicResolver, getSchemaByTypeForDesigner } from '../../../../components/dynamic-resolver'; -import { ComponentBuildInfo } from '../../../component/src/composition/inner-component-build-info'; -import { ComponentSchema } from '../../../../components/designer-canvas'; -import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; import { cloneDeep } from 'lodash-es'; -import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; -import { useGuid } from '@farris/ui-vue/components/common'; +import { DesignerHostService, ComponentSchema, DgControl } from '@farris/ui-vue/components/designer-canvas'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { ComponentBuildInfo } from '@farris/ui-vue/components/component'; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName, useGuid } from '@farris/ui-vue/components/common'; const ROOT_VIEW_MODEL_ID = 'root-viewmodel'; diff --git a/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts b/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts index d2e5b362661bf4470ce1d4c557942fbe2db3fde4..a92929d9c1301518671ef05dcdb0e2852f22f5e0 100644 --- a/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts +++ b/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts @@ -306,6 +306,13 @@ export class DataGriColumnProperty extends BaseControlProperty { ] }, refreshPanelAfterChanged: true + }, + enableGroup: { + description: '启用分组', + title: '分组', + visible: !!this.dataGridProps?.group?.enable, + type: 'boolean', + refreshPanelAfterChanged: true } }, setPropertyRelates(changeObject, prop, paramters: any) { @@ -316,7 +323,7 @@ export class DataGriColumnProperty extends BaseControlProperty { case 'width': { prop.actualWidth = changeObject.propertyValue; break; - } + }; case 'summaryType': { // 更新合计行字段集合 const { groupFields } = gridData.summary; @@ -327,13 +334,21 @@ export class DataGriColumnProperty extends BaseControlProperty { groupFields.splice(groupFields.indexOf(prop.field), 1); } break; - } + }; case 'columnTemplate': { // 提示以列模板为主 if (changeObject.propertyValue) { self.notifyService.warning({ position: 'top-center', message: '注意:已自定义列模板,【列格式化】【悬浮提示】【数据水平对齐方式】【数据垂直对齐方式】等列属性会失效' }); } break; + }; + case 'enableGroup': { + if(changeObject.propertyValue) { + gridData.group.groupFields = [prop.field]; + } else { + // groupFields.splice(groupFields.indexOf(prop.field), 1); + gridData.group.groupFields = []; + } } } } diff --git a/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts b/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts index f94721830c540b235a5b93acdf1d510f9ac7e9f7..9e28065757e9a8f06255685f6f98d2459cb9078b 100644 --- a/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts +++ b/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts @@ -23,6 +23,8 @@ export class DataGridProperty extends BaseControlProperty { this.propertyConfig.categories['rowOption'] = this.getRowOptionProperties(propertyData); // 合计行 this.propertyConfig.categories['summary'] = this.getSummaryProperties(propertyData); + // 分组配置 + this.propertyConfig.categories['group'] = this.getGroupProperties(propertyData); // 事件 this.getEventPropConfig(propertyData); @@ -177,6 +179,36 @@ export class DataGridProperty extends BaseControlProperty { }; } + private getGroupProperties(propertyData: any) { + return { + title: '分组', + description: '', + properties: { + enable: { + title: '启用', + type: 'boolean', + description: '启用分组', + $converter: '/converter/group.converter', + refreshPanelAfterChanged: true + }, + showSummary: { + title: '显示分组合计行', + type: 'boolean', + visible: !!propertyData.group?.enable, + $converter: '/converter/group.converter', + description: '显示分组合计行' + } + + // groupFields: { + // title: '启用', + // type: 'boolean', + // description: '启用分组', + // refreshPanelAfterChanged: true + // } + } + }; + } + private getSummaryProperties(propertyData: any) { return { title: '合计行', diff --git a/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json b/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json index f230ee64b7491f3c4ab1452147a69c42eaf023b4..63354f7f94379220d84763019430b062e01cf89a 100644 --- a/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json +++ b/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json @@ -177,6 +177,11 @@ "description": "是否允许拖拽", "type": "boolean", "default": false + }, + "enableGroup": { + "description": "", + "type": "boolean", + "default": false } }, "required": [ diff --git a/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json b/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json index 43a56e38a81f53d8a3a8f07de68727652042f6a9..27a9047d9653846902d97334634a059117ce4051 100644 --- a/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json +++ b/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json @@ -479,13 +479,18 @@ } } }, - "row": { + "rowOption": { "type": "object", "properties": { "customRowStyle": { "description": "", "type": "object" }, + "customCellStyle": { + "description": "", + "type": "string", + "default": "" + }, "disable": { "description": "", "type": "obejct" @@ -808,18 +813,6 @@ "description": "", "type": "string", "default": "" - }, - "rowOption": { - "customRowStyle": { - "description": "", - "type": "string", - "default": "" - }, - "customCellStyle": { - "description": "", - "type": "string", - "default": "" - } } }, "required": [ diff --git a/packages/ui-vue/components/data-view/composition/data/use-data-view.ts b/packages/ui-vue/components/data-view/composition/data/use-data-view.ts index a1f6bd0a79fefd92524f5dad44a80b7360d70b1e..1a16b02b9256ad4fb474a8b47082f82d1449a821 100644 --- a/packages/ui-vue/components/data-view/composition/data/use-data-view.ts +++ b/packages/ui-vue/components/data-view/composition/data/use-data-view.ts @@ -296,10 +296,23 @@ export function useDataView( return applyFilterAndSorter([], Array.from(sorterMap.values()), true); } - function collapse(collapseField: string, collapseValue: any) { + function collapse(collapseField: string, collapseValue: any, groupParents: any[]) { const groupedRowId = `group_of_${collapseField}_${collapseValue}`; collapseMap.set(groupedRowId, true); - const collapseFieldFilter = (dataItem: any) => dataItem[collapseField] !== collapseValue; + // 构造分组收折隐藏条件 + // groupParents.forEach((parent: any) => { + // const parentGroupField = parent.__fv_data_grid_group_field__ || ''; + // const parentGroupValue = parent.__fv_data_grid_group_value__; + // const parentGroupFilter = (dataItem: any) => dataItem[parentGroupField] === parentGroupValue; + // groupFilterMap.set(`collapse_${parentGroupField}_${parentGroupValue}`, { + // field: parentGroupField, + // fieldName: parentGroupField, + // fieldType: 'string', + // filterValue: parentGroupValue, + // filter: parentGroupFilter + // }); + // }); + const collapseFieldFilter = (dataItem: any) => dataItem[collapseField] === collapseValue; groupFilterMap.set(`collapse_${collapseField}_${collapseValue}`, { field: collapseField, fieldName: collapseField, @@ -318,9 +331,14 @@ export function useDataView( expandToLayer(dataView.value, layer); } - function expand(expandField: string, expandValue: any) { + function expand(expandField: string, expandValue: any, groupParents: any[]) { const groupedRowId = `group_of_${expandField}_${expandValue}`; collapseMap.set(groupedRowId, false); + // groupParents.forEach((parent: any) => { + // const parentGroupField = parent.__fv_data_grid_group_field__ || ''; + // const parentGroupValue = parent.__fv_data_grid_group_value__; + // groupFilterMap.delete(`collapse_${parentGroupField}_${parentGroupValue}`); + // }); groupFilterMap.delete(`collapse_${expandField}_${expandValue}`); return applyFilterAndSorter(Array.from(groupFilterMap.values()), Array.from(sorterMap.values()), true); } diff --git a/packages/ui-vue/components/data-view/composition/data/use-group-data.ts b/packages/ui-vue/components/data-view/composition/data/use-group-data.ts index 4496935d630cf7326e6142beb3cff183f24bd47b..974b2db03f167906a83fa947749681eff9c87eab 100644 --- a/packages/ui-vue/components/data-view/composition/data/use-group-data.ts +++ b/packages/ui-vue/components/data-view/composition/data/use-group-data.ts @@ -18,12 +18,20 @@ import { useGuid } from '@farris/ui-vue/components/common'; import { CollpasableItem, DataViewOptions, UseGroupData, UseIdentify } from '../types'; interface GroupingItem { + // 分组字段 field: string; + // 分组标题 title: string; + // 分组值 value: any; + // 该分组下的行数据集合 details: any[]; + // 分组层级 + layer: number; + // 子级分组 nestGroup?: Map; + // 分组合计 groupSummaries: Map; } @@ -35,17 +43,21 @@ export function useGroupData( const { idField } = identifyComposition; const summaryOptions = ref(props.summary); - + // 表格分组配置 const groupOptions = ref(props.group); + // 表格分组字段集合,集合的大小代表了分组层级 const groupFields = ref(props.group?.groupFields || []); + // 启用表格分组 const shouldGroupingData = computed(() => props.group?.enable); + // 启用分组面板 const showGroupPanel = computed(() => props.group?.showGroupPanel); + // 合计行字段 const groupSummaryFields = computed(() => { const options = summaryOptions.value; return options?.groupFields || []; }); - + // 启用分组合计行 const shouldShowSummary = computed(() => { const options = groupOptions.value; return options && options.showSummary; @@ -56,6 +68,15 @@ export function useGroupData( let flatGroupedData: any[] = []; let groupedPath = ''; + /** + * 处理并构造原始分组数据 + * @param groupObject 分组对象 + * @param rawData 行数据 + * @param groupLayer 分组层级 + * @param groupFields 分组字段 + * @param columnMaps 列映射集合 + */ + function processGroupTo( groupObject: Map, rawData: any[], groupLayer: number, groupFields: string[], columnMaps: Map @@ -77,6 +98,7 @@ export function useGroupData( }; groupResult.set(groupingValue, groupingItem); } + // 构造分组合计对象 if (groupSummaryFields.value) { groupSummaryFields.value.forEach((summaryField: string) => { if (groupingItem) { @@ -85,9 +107,19 @@ export function useGroupData( } }); } + // 限制分组合计的精度,默认2位小数 + if (groupSummaryFields.value) { + groupSummaryFields.value.forEach((summaryField: string) => { + if (groupingItem) { + const summaryFieldValue = groupingItem.groupSummaries.get(summaryField) || 0; + groupingItem.groupSummaries.set(summaryField, Number(summaryFieldValue.toFixed(2))); + } + }); + } groupingItem.details.push(rawDataItem); return groupResult; }, groupObject); + // 递归构造子级分组 if (groupLayer < groupFields.length - 1) { groupObject.forEach((groupingItem: GroupingItem, groupingValue: any) => { groupingItem.nestGroup = new Map(); @@ -98,13 +130,26 @@ export function useGroupData( } } - function getGroupValue(groupingItem, total) { + /** + * 获取分组标题 + * @param groupingItem 分组原始数据 + * @param total 分组总数 + * @returns 分组标题 + */ + function getGroupValue(groupingItem: GroupingItem, total: number) { if (props.group && props.group.formatter) { return props.group.formatter(groupingItem.value, { ...groupingItem, total }); } return `${groupingItem.title}:${groupingItem.value} (${total})`; } + /** + * flat分组对象,用于渲染分组数据 + * @param targetGroupingObject 已构造的分组对象 + * @param parentVirtualDataItem 父级分组数据项 + * @returns 分组数据 + */ + function toFlattenGroupedObject(targetGroupingObject: Map, parentVirtualDataItem?: any) { const groupingData: any[] = []; targetGroupingObject.forEach((groupingItem: GroupingItem) => { @@ -115,8 +160,13 @@ export function useGroupData( __fv_data_grid_group_field__: groupingItem.field, __fv_data_grid_group_layer__: groupingItem.layer, __fv_data_grid_group_row__: true, - __fv_data_grid_group_value__: groupingItem.value + __fv_data_grid_group_value__: groupingItem.value, + groupParents: [] } as Record; + // 构造父级分组集合 + if (parentVirtualDataItem) { + virtualDataItem.groupParents = [...parentVirtualDataItem.groupParents, parentVirtualDataItem]; + } let total = 0; groupingData.push(virtualDataItem); if (groupingItem.nestGroup) { diff --git a/packages/ui-vue/components/data-view/composition/types.ts b/packages/ui-vue/components/data-view/composition/types.ts index 6dcc900d16440923412b197b9169682fc0433900..19fae70dd60b65b43e3a092868a138efbd1832ff 100644 --- a/packages/ui-vue/components/data-view/composition/types.ts +++ b/packages/ui-vue/components/data-view/composition/types.ts @@ -546,7 +546,8 @@ export interface UseDataView { changePageSizeTo: (newPageSize: number) => void; - collapse: (collapseField: string, collapseValue: any) => any[]; + collapse: (collapseField: string, collapseValue: any, groupParents: VisualData[]) => any[]; + collapseTo: (layer: number) => void; @@ -554,7 +555,8 @@ export interface UseDataView { editDataItem: (editIndex: string | number, newName: string) => void; - expand: (expandField: string, expandValue: any) => any[]; + expand: (expandField: string, expandValue: any, groupParents: any[]) => any[]; + expandTo: (layer: number) => void; diff --git a/packages/ui-vue/components/data-view/composition/visualization/use-visual-data.ts b/packages/ui-vue/components/data-view/composition/visualization/use-visual-data.ts index 7819771cdf37462e2c56af911763365be156c1d5..d8971d01119bcc1826f622b20d5e98043dafaaf9 100644 --- a/packages/ui-vue/components/data-view/composition/visualization/use-visual-data.ts +++ b/packages/ui-vue/components/data-view/composition/visualization/use-visual-data.ts @@ -87,8 +87,8 @@ export function useVisualData( function toggleGroupRow(status: 'collapse' | 'expand', groupRow: VisualData, visibleDatas: VisualData[]): VisualData[] { const groupField = groupRow.groupField || ''; - const { groupValue } = groupRow; - dataViewComposition[status](groupField, groupValue); + const { groupValue, raw } = groupRow; + dataViewComposition[status](groupField, groupValue, raw.groupParents || []); const { dataView } = dataViewComposition; // todo: 当客户端分页时,此处start和end需要更改,后续处理 const start = visibleDatas[0].index; diff --git a/packages/ui-vue/components/data-view/designer/use-visual-data.ts b/packages/ui-vue/components/data-view/designer/use-visual-data.ts index 9b5e06401a053d37d366cc0f98606ab015e5f2ca..cddb9782d0b8b3a3eb04a5782c1f7d9e49cac863 100644 --- a/packages/ui-vue/components/data-view/designer/use-visual-data.ts +++ b/packages/ui-vue/components/data-view/designer/use-visual-data.ts @@ -87,8 +87,9 @@ export function useDesignerVisualData( function toggleGroupRow(status: 'collapse' | 'expand', groupRow: VisualData, visibleDatas: VisualData[]): VisualData[] { const groupField = groupRow.groupField || ''; - const { groupValue } = groupRow; - dataViewComposition[status](groupField, groupValue); + const { groupValue, raw } = groupRow; + dataViewComposition[status](groupField, groupValue, raw.groupParents || []); + const { dataView } = dataViewComposition; const start = visibleDatas[0].index; const end = Math.min(start + visibleCapacity.value + preloadCount + 1, dataView.value.length - 1); diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/group.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/group.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..5caf2b8aa73a07fc78cd1f988eebabdc5a912847 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/group.converter.ts @@ -0,0 +1,46 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertTo: (schema: ComponentSchema, propertyKey: string, propertyValue: any, schemaService: SchemaService) => { + // eslint-disable-next-line no-self-assign + if (schema.group) { + schema.group[propertyKey] = propertyValue; + } else { + schema.group = { + [propertyKey]: propertyValue + }; + } + if (propertyKey === 'enable' && propertyValue) { + // 启用合计行 + if (!schema.group) { + schema.group = { + enable: propertyValue, + groupFields: [], + showSummary: false + }; + } else { + if(!schema.group.groupFields) { + schema.group.groupFields = []; + } + } + } + }, + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + if (schema.group) { + if (propertyKey === 'enable') { + return schema.group.enable; + } + if (propertyKey === 'showSummary') { + return schema.group.showSummary; + } + } + // if (schema.type === 'data-grid-column') { + // if (schema.enableGroup === undefined) { + // return false; + // } + // return schema.enableGroup; + // } + // return ''; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts index 35b09bb6951bb980a8fbcecc42f5060024df3aab..382f82154ef4e5237f02e42f7ac0e41815739530 100644 --- a/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts +++ b/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts @@ -13,6 +13,7 @@ import changeFormatterConverter from './converter/change-formatter.converter'; import columnCommandConverter from './converter/column-command.converter'; import columnOptionConverter from './converter/column-option.converter'; import SummaryConverter from './converter/summary.converter'; +import GroupConverter from './converter/group.converter'; import fieldSelectorConverter from "./converter/field-selector.converter"; import paginationConverter from "./converter/pagination.converter"; import rowNumberConverter from "./converter/row-number.converter"; @@ -34,6 +35,7 @@ const propertyConverterMap = new Map([ ['/converter/column-command.converter', columnCommandConverter], ['/converter/column-option.converter', columnOptionConverter], ['/converter/summary.converter', SummaryConverter], + ['/converter/group.converter', GroupConverter], ['/converter/form-group-label.converter', formGroupLabelConverter], ['/converter/field-selector.converter', fieldSelectorConverter], ['/converter/pagination.converter', paginationConverter], diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts index 92c81d4486ce50c05e1e3f3dbf19efbc61ea94db..4aa1bda425a8e9e6c6eccdb70df6fe55404e0608 100644 --- a/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts @@ -12,6 +12,7 @@ import changeFormatterConverter from '../../converter/change-formatter.converter import columnCommandConverter from '../../converter/column-command.converter'; import columnOptionConverter from '../../converter/column-option.converter'; import SummaryConverter from '../../converter/summary.converter'; +import GroupConverter from '../../converter/group.converter'; import fieldSelectorConverter from "../../converter/field-selector.converter"; import paginationConverter from "../../converter/pagination.converter"; import rowNumberConverter from "../../converter/row-number.converter"; @@ -35,6 +36,7 @@ export function usePropertyConfigResolver(propertyConfigSchemaMap: Record
- +
@@ -72,7 +78,12 @@ for (let index = 0; index < 15; index++) {
- +
diff --git a/packages/ui-vue/demos/combo-list/search.vue b/packages/ui-vue/demos/combo-list/search.vue index d04e2954a37d4f140d398a783e289eaa1919e867..eb172e2babf801d4ea4cbcc76753c1a605ef810b 100644 --- a/packages/ui-vue/demos/combo-list/search.vue +++ b/packages/ui-vue/demos/combo-list/search.vue @@ -27,6 +27,7 @@ for (let index = 0; index < 15; index++) {
diff --git a/packages/ui-vue/demos/data-grid/group_local_data.vue b/packages/ui-vue/demos/data-grid/group_local_data.vue index d5a4ad7a3c9e8effb46658e2e6a67adf8fa44201..dce38fee58267e069fe795c2727593dea1539d4c 100644 --- a/packages/ui-vue/demos/data-grid/group_local_data.vue +++ b/packages/ui-vue/demos/data-grid/group_local_data.vue @@ -92,13 +92,15 @@ const editOption = { } const groupOption = { enable: true, - groupFields: ['name', 'dateField1'] + groupFields: ['name'], + showSummary: true, + summaryPosition: 'separate' // showSummary: true } -// const summaryOption = { -// enable: false, -// groupFields: ['numericField1'] -// }; +const summaryOption = { + enable: false, + groupFields: ['numericField1'] +}; const paginationRef = computed(() => { return { total: 100, @@ -158,6 +160,7 @@ onMounted(() => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3e286b93a9dd2fdbf958c5bd6e83017e4d20d93..b6cd5a7868f19dffc402446fec2e21fd5372f1eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1091,7 +1091,7 @@ importers: version: 2.3.0(@types/node@20.5.1)(rollup@4.45.1)(vite@4.5.5(@types/node@20.5.1)(sass@1.80.3)(terser@5.36.0)) vite-plugin-md: specifier: ^0.20.0 - version: 0.20.6(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) + version: 0.20.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) vite-svg-loader: specifier: ^4.0.0 version: 4.0.0 @@ -1269,7 +1269,7 @@ importers: version: 2.3.0(@types/node@20.5.1)(rollup@4.24.0)(vite@4.5.5(@types/node@20.5.1)(sass@1.80.3)(terser@5.36.0)) vite-plugin-md: specifier: ^0.20.0 - version: 0.20.6(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) + version: 0.20.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) vite-svg-loader: specifier: ^4.0.0 version: 4.0.0 @@ -1499,7 +1499,7 @@ importers: version: 2.3.0(@types/node@20.5.1)(rollup@4.45.1)(vite@4.5.5(@types/node@20.5.1)(sass@1.80.3)(terser@5.36.0)) vite-plugin-md: specifier: ^0.20.0 - version: 0.20.6(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) + version: 0.20.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) vite-svg-loader: specifier: ^4.0.0 version: 4.0.0 @@ -1720,7 +1720,7 @@ importers: version: 2.3.0(@types/node@20.5.1)(rollup@4.45.1)(vite@4.5.5(@types/node@20.5.1)(sass@1.80.3)(terser@5.36.0)) vite-plugin-md: specifier: ^0.20.0 - version: 0.20.6(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) + version: 0.20.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) vite-svg-loader: specifier: ^4.0.0 version: 4.0.0 @@ -2083,7 +2083,7 @@ importers: version: 2.3.0(@types/node@20.5.1)(rollup@4.45.1)(vite@4.5.5(@types/node@20.5.1)(sass@1.80.3)(terser@5.36.0)) vite-plugin-md: specifier: ^0.20.0 - version: 0.20.6(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) + version: 0.20.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) vite-svg-loader: specifier: ^4.0.0 version: 4.0.0 @@ -20122,12 +20122,12 @@ snapshots: - terser - vite - '@yankeeinlondon/builder-api@1.4.1(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0)': + '@yankeeinlondon/builder-api@1.4.1(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0)': dependencies: '@types/markdown-it': 12.2.3 '@yankeeinlondon/happy-wrapper': 2.10.1(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) fp-ts: 2.16.9 - inferred-types: 0.37.6 + inferred-types: 0.37.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) markdown-it: 13.0.2 vite-plugin-md: 0.22.5(@vitejs/plugin-vue@4.6.2(vite@4.5.5(@types/node@18.19.57)(sass@1.80.3)(terser@5.36.0))(vue@3.5.12(typescript@4.9.5)))(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0)(vite@4.5.5(@types/node@18.19.57)(sass@1.80.3)(terser@5.36.0)) transitivePeerDependencies: @@ -20135,6 +20135,7 @@ snapshots: - '@vitest/browser' - '@vitest/ui' - encoding + - happy-dom - jsdom - less - lightningcss @@ -21135,7 +21136,7 @@ snapshots: dependencies: bumpp: 8.2.1 callsites: 4.2.0 - inferred-types: 0.37.6 + inferred-types: 0.37.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) vitest: 0.25.8(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) transitivePeerDependencies: - '@edge-runtime/vm' @@ -29304,9 +29305,9 @@ snapshots: - terser - vite - vite-plugin-md@0.20.6(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0): + vite-plugin-md@0.20.6(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0): dependencies: - '@yankeeinlondon/builder-api': 1.4.1(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) + '@yankeeinlondon/builder-api': 1.4.1(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) '@yankeeinlondon/happy-wrapper': 2.10.1(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) gray-matter: 4.0.3 markdown-it: 13.0.2 @@ -29316,6 +29317,7 @@ snapshots: - '@vitest/browser' - '@vitest/ui' - encoding + - happy-dom - jsdom - less - lightningcss @@ -29351,7 +29353,11 @@ snapshots: vite-plugin-md@0.21.5(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0)(vite@5.4.9(@types/node@20.5.1)(sass-embedded@1.80.3)(sass@1.80.3)(terser@5.36.0)): dependencies: +<<<<<<< HEAD '@yankeeinlondon/builder-api': 1.4.1(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) +======= + '@yankeeinlondon/builder-api': 1.4.1(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) +>>>>>>> complete_feature '@yankeeinlondon/gray-matter': 6.2.1(happy-dom@8.9.0)(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) '@yankeeinlondon/happy-wrapper': 2.10.1(jsdom@20.0.3)(sass@1.80.3)(terser@5.36.0) markdown-it: 13.0.2