diff --git a/packages/command-services/lib/dialog.service.ts b/packages/command-services/lib/dialog.service.ts index 8f05a598f4de1c21136285493c56acb352d000bc..149d031b9346616294248e6fc5acbe09504a6008 100644 --- a/packages/command-services/lib/dialog.service.ts +++ b/packages/command-services/lib/dialog.service.ts @@ -173,16 +173,14 @@ export class DialogService { resizeable: true, enableEsc: true, showMaxButton: true, - showCloseButton: true + showCloseButton: true }; // 组件实例上的配置 instanceDialogConfig = instanceDialogConfig || {}; - // 构件中的窗口配置 - const { title } = dialogConfig; - - const newLookupDialogProps = { ...defaultDialogConfig, ...instanceDialogConfig, title }; + // 构造新的配置 + const newLookupDialogProps = { ...defaultDialogConfig, ...instanceDialogConfig, ...dialogConfig }; this.stripUndefinedProps(newLookupDialogProps); return { @@ -198,11 +196,13 @@ export class DialogService { return null; } - let modalConfig = {}; if (typeof config === 'string') { - modalConfig = JSON.parse(config); - } else { - modalConfig = Object.assign({}, config); + return JSON.parse(config); + } + + const modalConfig = Object.assign({}, config); + if (Object.keys(modalConfig).length === 0) { + return null; } return modalConfig; diff --git a/packages/command-services/lib/navigation-event.service.ts b/packages/command-services/lib/navigation-event.service.ts index bb00f23b76e6717242ae0c6c9717c6af0d1e4e1c..08c2bd7cb9022e396f2d8758925dcab18dad07ed 100644 --- a/packages/command-services/lib/navigation-event.service.ts +++ b/packages/command-services/lib/navigation-event.service.ts @@ -5,20 +5,34 @@ import { CommandContext, Token } from '@farris/devkit-vue'; import { NavigationHistoryService } from './navigation-history.service'; export class NavigationEventService { + + /** + * 命令上下文 + */ public commandContext: CommandContext; + /** * 关闭后事件处理器 */ private onClosedListeners: Map void>; + /** * 关闭前处理器 */ private onClosingListeners: Map Promise>; /** - * 框架页签切换事件 + * 切换事件处理器 */ private onTabSwitchListeners: Map void>; + + /** + * 刷新事件处理器 + */ private onTabRefreshListeners: Map void>; + + /** + * 获取查询参数 + */ private get querystrings() { const params = this.querystringService.parse(window.location.hash); // 修正formToken @@ -27,6 +41,10 @@ export class NavigationEventService { } return params; } + + /** + * 构造函数 + */ constructor( private runtimeFrameworkService: RuntimeFrameworkService, private querystringService: QuerystringService, @@ -38,6 +56,10 @@ export class NavigationEventService { this.onTabRefreshListeners = new Map void>(); this.registerEvent(); } + + /** + * 向框架注册事件 + */ public registerEvent() { const options = this.querystrings; // 注册标签页切换事件 @@ -47,6 +69,10 @@ export class NavigationEventService { // 注册标签页关闭前事件 this.runtimeFrameworkService.addEventListener(TAB_EVENT.onTabClosing, (e) => this.handleTabClosingEvent(e), options); } + + /** + * 处理标签页切换事件 + */ private handleTabSwitchEvent(e: any) { if (!e) { return; @@ -65,10 +91,10 @@ export class NavigationEventService { } this.fireTabSwitchEvent(e); } + /** - * 触发tab切换事件 - * @param e e - */ + * 触发标签页切换事件 + */ private fireTabSwitchEvent(e: any) { if (!this.onTabSwitchListeners || this.onTabSwitchListeners.size < 1) { return; @@ -79,8 +105,9 @@ export class NavigationEventService { } }); } + /** - * 标签页关闭前事件 + * 处理Tab闭前事件 */ private handleTabClosingEvent(event: any) { if (!event) { @@ -101,37 +128,47 @@ export class NavigationEventService { }); } } + /** - * 触发关闭前事件 + * 触发标签页关闭前事件 */ private fireTabClosingEvent(e: any): Promise { if (!this.onClosingListeners || this.onClosingListeners.size < 1) { return Promise.resolve(true); } const listeners = Array.from(this.onClosingListeners.values()); - // const result$ = from(listeners); + // 用户拒绝 let userRejected = false; const resultPromise = listeners.reduce((promiseChain: Promise, handle) => { return promiseChain.then(chainResults => { + // 如果用户已经拒绝,则跳过后续的处理 if (userRejected) { return chainResults; } + // 处理当前的 handle,只取第一个结果 return handle(e).then(result => { + // 如果用户拒绝,则设置标志 userRejected = !result; + // 将结果添加到结果链中 return [...chainResults, result]; }); + }); }, Promise.resolve([])); + + // 如果所有用户返回true,则关闭标签页 return resultPromise.then(chainResults => { + // 检查是否所有的结果都是 true return chainResults.every((result: any) => result); }); } + /** * 标签页关闭后事件 */ @@ -151,9 +188,9 @@ export class NavigationEventService { } this.fireTabClosedEvent(e); } + /** - * 触发关闭后事件 - * @param e event + * 触发标签页关闭后事件 */ private fireTabClosedEvent(e: any) { if (!this.onClosedListeners || this.onClosedListeners.size < 1) { @@ -165,6 +202,10 @@ export class NavigationEventService { } }); } + + /** + * 触发标签页刷新事件 + */ private fireTabRefreshEvent() { if (!this.onTabRefreshListeners || this.onTabRefreshListeners.size < 1) { return; @@ -176,6 +217,7 @@ export class NavigationEventService { }); } // #endregion + /** * 注册事件监听器 * @param eventType 事件类型 onTabClosed @@ -198,6 +240,7 @@ export class NavigationEventService { } return null; } + /** * 移除事件监听器 * @param eventType 事件类型 @@ -211,6 +254,7 @@ export class NavigationEventService { } return false; } + /** * 清空事件监听器 * @param eventType 事件类型 @@ -222,8 +266,9 @@ export class NavigationEventService { this.onClosingListeners.clear(); } } + /** - * 刷新数据 + * 刷新标签页数据 */ private refresh() { this.fireTabRefreshEvent(); diff --git a/packages/command-services/lib/navigation-middleware.service.ts b/packages/command-services/lib/navigation-middleware.service.ts index 336e6dd59d8997b37d9c74c7c17a99c22a51c81a..5653a232bf395e2e7404031983e2b8586bd24cf0 100644 --- a/packages/command-services/lib/navigation-middleware.service.ts +++ b/packages/command-services/lib/navigation-middleware.service.ts @@ -1,4 +1,4 @@ -import { Token, ViewModel, ViewModelState } from '@farris/devkit-vue'; +import { Token, ViewModel, ViewModelState, IDisposable } from '@farris/devkit-vue'; import { NavigationService } from './navigation.service'; import { TAB_EVENT } from './types'; import { FormMessageService } from './form-message.service'; @@ -7,7 +7,16 @@ import { CancelDataService } from './data-services'; import { EntityChangeService } from './entity-change.service'; import { EndEditService } from './end-edit.service'; -export class NavigationMiddlewareService { +export class NavigationMiddlewareService implements IDisposable { + + /** + * 事件监听Token + */ + private listenerTokens: string[]; + + /** + * 构造函数 + */ constructor( private navigationService: NavigationService, private formMessageService: FormMessageService, @@ -16,16 +25,23 @@ export class NavigationMiddlewareService { private viewModel: ViewModel, private entityChangeService: EntityChangeService, private endEditService: EndEditService - ) { } + ) { + this.listenerTokens = []; + this.registerToModule(); + } + /** * 菜单关闭前 */ public onClosing() { - this.navigationService.addEventListener(TAB_EVENT.onTabClosing, (options) => { + const listenerToken = this.navigationService.addEventListener(TAB_EVENT.onTabClosing, (options) => { + + // 判断是否已经有关闭前的确认框 const isConfirming = this.viewModel.getModule().getContext().getParam('ON_CLOSING_CONFIRM') || false; if (isConfirming) { return Promise.resolve(false); } + // 检测变更前先结束编辑 this.endEditService.endEdit(); return this.entityChangeService.hasChanges().then((changed: boolean) => { @@ -34,17 +50,24 @@ export class NavigationMiddlewareService { if (options && options.beforeCloseHandle && typeof options.beforeCloseHandle === 'function') { options.beforeCloseHandle({ selectedChange: true }); } + + // 打开确认提示框 this.viewModel.getModule().getContext().setParam('ON_CLOSING_CONFIRM', true); const confirm = this.formMessageService.confirm(this.languageService.language.confirmClosing); return confirm.then((result: boolean) => { this.viewModel.getModule().getContext().setParam('ON_CLOSING_CONFIRM', false); + + // 如果用户选择了确定,执行取消并关闭Tab关闭 if (result) { return this.cancelDataService.cancel(false).then(() => true); } + + // 否则返回false,阻止Tab页关闭 return Promise.resolve(false); - } - ); + }); } else { + + // 未检测到变更,关闭Tab页 return Promise.resolve(true); } }).catch((error) => { @@ -53,7 +76,20 @@ export class NavigationMiddlewareService { }); }); }); + + if (listenerToken) { + this.listenerTokens.push(listenerToken); + } } + + /** + * 注册到模块 + */ + public registerToModule() { + const module = this.viewModel.getModule(); + module.registerDisposable(this); + } + /** * 获取tabid,如果targetId存在则直接使用targetId * @description 将用户要查看的数据id转换为运行框架需要的tabId @@ -74,4 +110,13 @@ export class NavigationMiddlewareService { } return paramId; } + + /** + * 释放监听 + */ + public dispose(): void { + this.listenerTokens.forEach((listenerToken) => { + this.navigationService.removeEventListener(TAB_EVENT.onTabClosing, listenerToken); + }) + } } \ No newline at end of file diff --git a/packages/command-services/lib/navigation.service.ts b/packages/command-services/lib/navigation.service.ts index 4d88e3b2ea704017d2bf52fd97c97f87ac4dc455..6938793ee81054fd3b23797c8dc5c28aaddd4aa3 100644 --- a/packages/command-services/lib/navigation.service.ts +++ b/packages/command-services/lib/navigation.service.ts @@ -254,6 +254,14 @@ export class NavigationService { public addEventListener(eventType: string, handler: (options: any) => any): string | null { return this.navigationEventService.addEventListener(eventType, handler); } + + /** + * 删除注册的事件 + */ + public removeEventListener(eventType: string, key: string) { + this.navigationEventService.removeEventListener(eventType, key); + } + private resolveBizMetadataId(baseMetadataId: string, dim1: string, dim2: string) { if (!dim1) { dim1 = 'public'; diff --git a/packages/devkit/lib/module/module.ts b/packages/devkit/lib/module/module.ts index 104d677164fef5bcbd3eab4e64fe8e3ec7e7bfca..fde67153284b71e78f8c3d9e773e350013fafb5a 100644 --- a/packages/devkit/lib/module/module.ts +++ b/packages/devkit/lib/module/module.ts @@ -1,4 +1,4 @@ -import { EventBus, Injector } from '../common/index'; +import { EventBus, IDisposable, Injector } from '../common/index'; import { Devkit, useDevkit } from '../devkit'; import { Entity, EntityState, EntityStore, UIState, UIStore, @@ -18,7 +18,7 @@ const MODULE_INJECTION_TOKEN = Symbol('Module'); /** * 模块定义 */ -class Module { +class Module implements IDisposable{ /** * 模块ID */ @@ -73,11 +73,17 @@ class Module { * 上下文 */ private context: Context; + /** * 事件总线 */ private eventBus: EventBus; + /** + * 可释放对象 + */ + private disposables: IDisposable[] + /** * 构造函数 */ @@ -93,6 +99,7 @@ class Module { this.context = new Context(); this.id = new Date().getTime().toString(); this.eventBus = new EventBus(); + this.disposables = []; } /** @@ -424,12 +431,36 @@ class Module { public getContext() { return this.context; } + + /** + * 获取模块ID + */ public getId() { return this.id; } + + /** + * 获取事件总线 + */ public getEventBus() { return this.eventBus; } + + /** + * 注册可释放对象 + */ + public registerDisposable(disposable: IDisposable): void { + this.disposables.push(disposable); + } + + /** + * 释放资源 + */ + public dispose() { + this.disposables.forEach((disposable) => { + disposable.dispose(); + }); + } } /** diff --git a/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts b/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts index 041791b7809bcee0f66da571faca42c1df520c47..1da8bcb0687affe2d50711206f6a5fb97f69524e 100644 --- a/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts +++ b/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts @@ -94,25 +94,29 @@ export class DataGridComponentConfigResolver extends ComponentConfigResolver { */ private buildColumnCommandHandler(eventName: string, commandSchema: any, gridComponentSchema: any, viewModel: any) { const eventHandlerName = commandSchema[eventName]; - if (!eventHandlerName) { - return; - } - - if (eventHandlerName.startsWith('communication:')) { + if (!eventHandlerName || eventHandlerName.startsWith('communication:')) { // 事件通讯 return (cell: any, row: any) => { - const module = viewModel.getModule(); - const { executeCommunications } = useCommunication(module); - return executeCommunications(eventName, gridComponentSchema, { cell, rowData: row.raw }, true); + return this.executeCommunications(eventName, gridComponentSchema, { cell, rowData: row.raw }, viewModel); }; - } else { - // 执行命令 + // 执行命令后触发事件通信 return (cell: any, row: any) => { - return viewModel[eventHandlerName]({ cell, rowData: row.raw }); + return viewModel[eventHandlerName]({ cell, rowData: row.raw }).then(() => { + return this.executeCommunications(eventName, gridComponentSchema, { cell, rowData: row.raw }, viewModel); + }); }; } } + + /** + * 执行事件通信 + */ + private executeCommunications(eventName: any, gridComponentSchema: any, event: any, viewModel: any): void { + const module = viewModel.getModule(); + const { executeCommunications } = useCommunication(module); + return executeCommunications(eventName, gridComponentSchema, event, true); + } } diff --git a/packages/renderer/src/event-handler/modal-closed-event-handler.ts b/packages/renderer/src/event-handler/modal-closed-event-handler.ts new file mode 100644 index 0000000000000000000000000000000000000000..8867a15db1403fca1fdbaa4464d5b826dce11e25 --- /dev/null +++ b/packages/renderer/src/event-handler/modal-closed-event-handler.ts @@ -0,0 +1,85 @@ +import { Injector, Module } from "@farris/devkit-vue"; +import { EventEmitter } from "../common"; +import { FormMetadataService } from "../service"; +import { ViewEvent } from "../types"; +import { EventHandler } from "./types"; + +/** + * 弹窗关闭前事件 + */ +export class ModalClosedEventHandler implements EventHandler { + + /** + * 注册事件 + */ + constructor( + private emitter: EventEmitter, + private formMetadataService: FormMetadataService, + private module: Module, + private injector: Injector + ){} + + /** + * 注册事件监听 + */ + bind(): void { + this.emitter.on('closed', (payload: ViewEvent) => this.onClosed(payload)); + } + + /** + * 注销事件监听 + */ + dispose(): void { + this.emitter.on('closed', (payload: ViewEvent) => this.onClosed(payload)); + } + + /** + * 关闭处理 + */ + private onClosed(payload: any){ + // 只处理包含外部表单的弹窗 + const externalContainerSchema = this.getExternalContainerSchema(payload.schema); + if (!externalContainerSchema) { + return; + } + + // 释放模块相关资源 + const modalModuleId = this.getExternalModuleId(externalContainerSchema); + const modalModule = this.module.getDevkit().getModule(modalModuleId); + if (modalModule) { + modalModule.dispose(); + } + } + + /** + * 获取弹窗内部的外部容器 + * @param modalSchema + * @returns + */ + private getExternalContainerSchema(modalSchema: any) { + if (!modalSchema || modalSchema.type !== 'modal' || !Array.isArray(modalSchema.contents)) { + return false; + } + + const { contents } = modalSchema; + const externalContainerSchema = contents.find((childComponent: any) => { + return childComponent.type === 'external-container'; + }); + + return externalContainerSchema; + } + + /** + * 获取外部容器内表单的模块ID + */ + private getExternalModuleId(externalContainerSchema: any): string { + const externalContainerId = externalContainerSchema.id; + const { externalComponent } = externalContainerSchema; + const externalComponentCode = externalComponent.code; + + const externalModuleId = `${externalContainerId}-${externalComponentCode}`; + + return externalModuleId; + } + +} diff --git a/packages/renderer/src/event-handler/providers.ts b/packages/renderer/src/event-handler/providers.ts index cd26960b55019a8c96ee4d2f716a607b815f3890..ebed75a3b50859a71cf5e74461325712903bd158 100644 --- a/packages/renderer/src/event-handler/providers.ts +++ b/packages/renderer/src/event-handler/providers.ts @@ -12,6 +12,7 @@ import { DataGridPageSizeChangeEventHandler } from "./data-grid-page-size-change import { DataGridSelectionChangeEventHandler } from "./data-grid-selection-change-event-handler"; import { QuerySolutionConditionChangeEventHandler } from "./query-solution-condition-change-event-handler"; import { DataGridDoubleClickRowEventHandler } from './data-grid-double-click-row-event-handler'; +import { ModalClosedEventHandler } from './modal-closed-event-handler'; export const eventHanderProviders: StaticProvider[] = [ { provide: EVENT_HANDLERS_TOKEN, useClass: LookupDataMappingEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, @@ -23,5 +24,6 @@ export const eventHanderProviders: StaticProvider[] = [ { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridPageSizeChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridSelectionChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, { provide: EVENT_HANDLERS_TOKEN, useClass: QuerySolutionConditionChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, - { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridDoubleClickRowEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true } + { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridDoubleClickRowEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, + { provide: EVENT_HANDLERS_TOKEN, useClass: ModalClosedEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true } ];