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..7aca5ed3d83fea9958d14e39060d8736b874a175 100644 --- a/packages/command-services/lib/navigation-event.service.ts +++ b/packages/command-services/lib/navigation-event.service.ts @@ -1,10 +1,14 @@ import { RuntimeFrameworkService } from './rtf.service'; import { QuerystringService } from './querystring.service'; -import { TAB_EVENT } from './types'; +import { TAB_EVENT, TabClosingConfirmResult } from './types'; import { CommandContext, Token } from '@farris/devkit-vue'; import { NavigationHistoryService } from './navigation-history.service'; export class NavigationEventService { + + /** + * 命令上下文 + */ public commandContext: CommandContext; /** * 关闭后事件处理器 @@ -13,12 +17,20 @@ export class NavigationEventService { /** * 关闭前处理器 */ - private onClosingListeners: Map Promise>; + 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,17 +39,25 @@ export class NavigationEventService { } return params; } + + /** + * 构造函数 + */ constructor( private runtimeFrameworkService: RuntimeFrameworkService, private querystringService: QuerystringService, private navigationHistoryService: NavigationHistoryService ) { this.onClosedListeners = new Map void>(); - this.onClosingListeners = new Map Promise>(); + this.onClosingListeners = new Map Promise>(); this.onTabSwitchListeners = new Map void>(); this.onTabRefreshListeners = new Map void>(); this.registerEvent(); } + + /** + * 向框架注册事件 + */ public registerEvent() { const options = this.querystrings; // 注册标签页切换事件 @@ -47,6 +67,10 @@ export class NavigationEventService { // 注册标签页关闭前事件 this.runtimeFrameworkService.addEventListener(TAB_EVENT.onTabClosing, (e) => this.handleTabClosingEvent(e), options); } + + /** + * 处理标签页切换事件 + */ private handleTabSwitchEvent(e: any) { if (!e) { return; @@ -65,10 +89,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 +103,9 @@ export class NavigationEventService { } }); } + /** - * 标签页关闭前事件 + * 处理Tab闭前事件 */ private handleTabClosingEvent(event: any) { if (!event) { @@ -101,37 +126,51 @@ 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; + + // 用户已确认(点击了确定或取消) + let userConfirmed = false; + let userConfirmResult: boolean | undefined; const resultPromise = listeners.reduce((promiseChain: Promise, handle) => { - return promiseChain.then(chainResults => { - // 如果用户已经拒绝,则跳过后续的处理 - if (userRejected) { - return chainResults; - } + return promiseChain.then((chainResults: TabClosingConfirmResult[]) => { + + const extraOptions = {userConfirmed, userConfirmResult}; + // 处理当前的 handle,只取第一个结果 - return handle(e).then(result => { - // 如果用户拒绝,则设置标志 - userRejected = !result; + return handle(e, extraOptions).then((result: TabClosingConfirmResult) => { + + // 如果用户点击了确定或取消,则终止 + userConfirmed = result.confirmType === 'User'; + if (result.confirmType === 'User') { + userConfirmed = true; + userConfirmResult = result.confirmResult; + } + // 将结果添加到结果链中 return [...chainResults, result]; }); + }); }, Promise.resolve([])); - return resultPromise.then(chainResults => { + + // 如果所有用户返回true,则关闭标签页 + return resultPromise.then((chainResults: TabClosingConfirmResult[]) => { + // 检查是否所有的结果都是 true - return chainResults.every((result: any) => result); + return chainResults.every((result: TabClosingConfirmResult) => { + return result.confirmResult === true; + }); }); } + /** * 标签页关闭后事件 */ @@ -151,9 +190,9 @@ export class NavigationEventService { } this.fireTabClosedEvent(e); } + /** - * 触发关闭后事件 - * @param e event + * 触发标签页关闭后事件 */ private fireTabClosedEvent(e: any) { if (!this.onClosedListeners || this.onClosedListeners.size < 1) { @@ -165,6 +204,10 @@ export class NavigationEventService { } }); } + + /** + * 触发标签页刷新事件 + */ private fireTabRefreshEvent() { if (!this.onTabRefreshListeners || this.onTabRefreshListeners.size < 1) { return; @@ -176,6 +219,7 @@ export class NavigationEventService { }); } // #endregion + /** * 注册事件监听器 * @param eventType 事件类型 onTabClosed @@ -198,6 +242,7 @@ export class NavigationEventService { } return null; } + /** * 移除事件监听器 * @param eventType 事件类型 @@ -211,6 +256,7 @@ export class NavigationEventService { } return false; } + /** * 清空事件监听器 * @param eventType 事件类型 @@ -222,8 +268,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..9dce5cc79c55a110764c55cd8cf94c48263bd5c4 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,38 @@ 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: any, extraOptions?: any) => { + + // 如果用户已经手工确认了是否关闭菜单,以用户选择为准,不再重复确认 + if (extraOptions && extraOptions.userConfirmed) { + + // 用户已经确定不关闭菜单 + if (extraOptions.userConfirmResult === false) { + return Promise.resolve({confirmResult:false, confirmType: 'User'}); + } else { + + // 用户已经确定关闭菜单 + return this.cancelDataService.cancel(false).then(() => { + return {confirmResult: true, confirmType: 'User'}; + }); + } + } + + // 判断是否已经有关闭前的确认框 const isConfirming = this.viewModel.getModule().getContext().getParam('ON_CLOSING_CONFIRM') || false; if (isConfirming) { - return Promise.resolve(false); + return Promise.resolve({confirmResult:false, confirmType: 'User'}); } + // 检测变更前先结束编辑 this.endEditService.endEdit(); return this.entityChangeService.hasChanges().then((changed: boolean) => { @@ -34,26 +65,52 @@ 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); + return this.cancelDataService.cancel(false).then(() => { + return {confirmResult:true, confirmType: 'User'}; + }); } - return Promise.resolve(false); - } - ); + + // 否则返回false,阻止Tab页关闭 + return Promise.resolve({confirmResult:false, confirmType: 'User'}); + }); } else { - return Promise.resolve(true); + + // 未检测到变更,关闭Tab页 + return Promise.resolve({confirmResult:true, confirmType: 'NoChange'}); } }).catch((error) => { return this.formMessageService.confirm(this.languageService.language.hasChangeCheckFaild).then((result: boolean) => { - return Promise.resolve(result); + if (result) { + return Promise.resolve({confirmResult:true, confirmType: 'User'}); + } else { + return Promise.resolve( {confirmResult:false, confirmType: 'User'}); + } }); }); }); + + if (listenerToken) { + this.listenerTokens.push(listenerToken); + } + } + + /** + * 注册到模块 + */ + public registerToModule() { + const module = this.viewModel.getModule(); + module.registerDisposable(this); } + /** * 获取tabid,如果targetId存在则直接使用targetId * @description 将用户要查看的数据id转换为运行框架需要的tabId @@ -74,4 +131,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..169d176f71fd0c496930a53e014fe19f21b31d06 100644 --- a/packages/command-services/lib/navigation.service.ts +++ b/packages/command-services/lib/navigation.service.ts @@ -251,9 +251,17 @@ export class NavigationService { * @param handler * @returns */ - public addEventListener(eventType: string, handler: (options: any) => any): string | null { + public addEventListener(eventType: string, handler: (options: any, extraOptions?: 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/command-services/lib/types.ts b/packages/command-services/lib/types.ts index 4227e18c14f715e1513693bdf9ddf9af59c89f67..0c1d787b0de96e57e88acef8331f528477183b51 100644 --- a/packages/command-services/lib/types.ts +++ b/packages/command-services/lib/types.ts @@ -7,21 +7,36 @@ export const AppType = { }; export const TAB_EVENT = { + /** * Tab关闭后 */ onTabClosed: 'FuncClosed', + /** * Tab关闭前 */ onTabClosing: 'beforeFuncCloseEvent', + /** * Tab切换 */ onTabSwitched: 'funcSwitchEvent', + + /** + * Tab刷新 + */ onTabRefresh: 'tabRefresh' }; +/** + * 标签页关闭前确认结果 + */ +export interface TabClosingConfirmResult { + confirmResult: boolean; + confirmType: 'User' | 'NoChange' +} + export const TAB_QUERY_STRING = { TabId: 'tabId', AppType: 'appType', diff --git a/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.component.tsx b/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.component.tsx index 995cb1d248afa8c09f41c3b0fc91800a4f8aff68..155261f009e6c217881e3bf9b4bdcc612e9dd838 100644 --- a/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.component.tsx +++ b/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.component.tsx @@ -141,17 +141,6 @@ export default defineComponent({ const { openNewFieldModal } = useOpenNewField(useFormSchema, newEntityCodeList, refreshEntityTree, serializedTreeData); const { openModifyFieldModal } = useOpenModifyField(useFormSchema, designViewModelUtils, refreshEntityTree, serializedTreeData, context); - /** 低代码:刷新实体(同步视图对象) */ - function renderHeader() { - if (designerMode !== DesignerMode.PC_RTC) { - return
-
- -
-
; - } - } - return () => { return
{{ - header: renderHeader, cellTemplate: ({ cell, row }) => { const rowData = row.raw; return <> @@ -183,6 +171,12 @@ export default defineComponent({ openModifyFieldModal(event, row.raw)}>
} + {/* 低代码表单实体节点,支持刷新实体数据 */} + {designerMode !== DesignerMode.PC_RTC && rowData.nodeType === 'entity' && !rowData.parent && +
+ +
+ } ; }, diff --git a/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.scss b/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.scss index d45d08b85334c87968778c4ad267446fe4ebbade..9212163ba4f46b6d3424e5836cf7f7e20ae3c834 100644 --- a/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.scss +++ b/packages/designer/src/components/components/entity-tree-view/components/entity-tree-view.scss @@ -31,4 +31,8 @@ bottom: 0; background: #fcfdff; } + + .fv-grid-row.fv-grid-row-selected .toolbar-panel { + background: #dae9ff; + } } \ No newline at end of file diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx index d6ea25cf0aeba15cb27387aafad80bbce858e242..c5105f9eeb8bc8ae8c7900a54bed0f95d332f36a 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx @@ -20,7 +20,7 @@ export default defineComponent({ const metadataService = new MetadataService(); const formBasicInfo = formSchemaUtils.getFormMetadataBasicInfo(); - const editorParams = { formBasicInfo }; + const editorParams = { formBasicInfo, enableGroup: false }; const viewOptions = [ { id: 'recommend', title: '推荐', type: 'Card', dataSource: 'Recommand', pagination: true }, { id: 'total', title: '全部', type: 'Card', dataSource: 'Total', pagination: true } diff --git a/packages/designer/src/components/components/form-designer/form-designer.component.tsx b/packages/designer/src/components/components/form-designer/form-designer.component.tsx index 0da86153b53d0be7535e64cca58b2db216d8d405..cd493444e614c0e74b69585495c8088e03bfb830 100644 --- a/packages/designer/src/components/components/form-designer/form-designer.component.tsx +++ b/packages/designer/src/components/components/form-designer/form-designer.component.tsx @@ -3,7 +3,6 @@ import { ComponentSchema, DesignerComponentInstance, FDesignerCanvas, FTabs, FTa import { FormDesignerProps, formDesignerProps } from "./form-designer.props"; import { useComponentSchemaService } from '../../composition/component-schema.service'; import MonacoEditor from '../monaco-editor/monaco-editor.component'; -import modulePropertyConfig from '../../types/form-property-config.json'; import FEntityTreeView from '../entity-tree-view/components/entity-tree-view.component'; import { afterPropeControlPropertyChangedService } from "../../composition/control-property-changed.service"; import { UseDesignViewModel, UseFormSchema, UseSchemaService } from "../../types"; @@ -15,6 +14,7 @@ import { ExternalComponentSchema } from "./components/external-component-panel/c import { UseFormCommandService } from "../../../components/types/command"; import { useFormValidation } from "../../composition/use-form-validation"; import { DesignerMode } from "../../types/designer-context"; +import { resolveFormModulePropertyConfig } from "../../../components/types/form-property-config"; export default defineComponent({ name: 'FFormDesigner', @@ -192,7 +192,7 @@ export default defineComponent({ }; }); - propertyConfigSchemaMapForDesigner['Module'] = modulePropertyConfig; + propertyConfigSchemaMapForDesigner['Module'] = resolveFormModulePropertyConfig(designerMode);; const shouldRenderExternalComponentPanel = designerMode !== DesignerMode.PC_RTC; @@ -227,7 +227,7 @@ export default defineComponent({ if (selectionSchema?.type === 'Module') { propertyName.value = 'Module'; propertyPanelInstance?.value?.updateDesignerItem(null, selectionSchema.id); - focusingSchema.value = selectionSchema; + focusingSchema.value = Object.assign({}, useFormSchema.getFormMetadataBasicInfo(), { type: 'Module' }); clearComponentSelectionStyles(); } diff --git a/packages/designer/src/components/composition/form-metadata-rtc.service.tsx b/packages/designer/src/components/composition/form-metadata-rtc.service.tsx index 8d514940771733959cd43a45b71382ecd4e32ab0..47aa19130ef8b3e2d93bfa7f85bf301b6977e284 100644 --- a/packages/designer/src/components/composition/form-metadata-rtc.service.tsx +++ b/packages/designer/src/components/composition/form-metadata-rtc.service.tsx @@ -205,7 +205,7 @@ export function useRtcFormMetadata( dim1: formBasicInfo.dimension1, dim2: formBasicInfo.dimension2, isRtc: '1', - metadataId: formBasicInfo.id, + metadataId: formBasicInfo.rtcId, menuId: menuSelectedData.value.value.id }; diff --git a/packages/designer/src/components/composition/schema-repository/lookup/lookup-schema.service.ts b/packages/designer/src/components/composition/schema-repository/lookup/lookup-schema.service.ts index 66a64d3c5d6799b9add670a1843f9b8acd083906..09a9d4b8a7fb86f833c95b5521edbb82ff805de6 100644 --- a/packages/designer/src/components/composition/schema-repository/lookup/lookup-schema.service.ts +++ b/packages/designer/src/components/composition/schema-repository/lookup/lookup-schema.service.ts @@ -5,8 +5,7 @@ export class LookupSchemaService { private metadataType = '.hlp'; - constructor(private metadataService: MetadataService) { - } + constructor(private metadataService: MetadataService) { } private getLocalMetadata(metadataPath) { return this.metadataService?.getMetadataListInSu(metadataPath, this.metadataType); @@ -31,18 +30,37 @@ export class LookupSchemaService { }); } - getRecommandData = async (searchingText: string, pagination: SchemaRepositoryPagination, editorParams): Promise => { - const {relativePath} = editorParams.formBasicInfo; + private mergeAndDeduplicateById(...items: T[][]): T[] { + const idSet = new Set(); + const mergedItems: T[] = []; + + for (const array of items) { + for (const item of array) { + if (!idSet.has(item.id)) { + idSet.add(item.id); + mergedItems.push(item); + } + } + } + return mergedItems; + } + + getRecommandData = async (searchingText: string, pagination: SchemaRepositoryPagination, editorParams: any): Promise => { + const { relativePath } = editorParams.formBasicInfo; + const shouldDeduplicate = editorParams.enableGroup === false; const recentRequest = await this.getRecentMetadata(relativePath); const recentData = this.metadata2SchemaItem(recentRequest.data, 'recent'); const suMetadataRequest = await this.getLocalMetadata(relativePath); const localData = this.metadata2SchemaItem(suMetadataRequest.data, 'local'); + if (shouldDeduplicate) { + return this.mergeAndDeduplicateById(recentData, localData); + } return recentData.concat(localData); }; - getSchemaData = async (searchingText: string, pagination: SchemaRepositoryPagination, editorParams): Promise => { + getSchemaData = async (searchingText: string, pagination: SchemaRepositoryPagination, editorParams: any): Promise => { const { relativePath } = editorParams.formBasicInfo; const allMetadataRes = await this.metadataService?.getAllMetadataList(relativePath, this.metadataType); let items = allMetadataRes.data; diff --git a/packages/designer/src/components/types/form-property-config.json b/packages/designer/src/components/types/form-property-config.json deleted file mode 100644 index c84eac0932c50bf46034cb24cca1a154646c60b1..0000000000000000000000000000000000000000 --- a/packages/designer/src/components/types/form-property-config.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "title": "module", - "description": "表单元数据属性配置", - "type": "object", - "categories": { - "basic": { - "title": "基本信息", - "properties": { - "id": { - "title": "表单元数据标识", - "type": "string", - "readonly": true - }, - "code": { - "title": "表单元数据编号", - "type": "string", - "readonly": true - }, - "name": { - "title": "表单元数据名称", - "type": "string", - "readonly": true - } - } - } - } -} \ No newline at end of file diff --git a/packages/designer/src/components/types/form-property-config.ts b/packages/designer/src/components/types/form-property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..925108314e80b0472f47501dfd83aed504db9905 --- /dev/null +++ b/packages/designer/src/components/types/form-property-config.ts @@ -0,0 +1,52 @@ +import { DesignerMode } from "./designer-context"; + +export function resolveFormModulePropertyConfig(designerMode: DesignerMode) { + return { + "title": "module", + "description": "表单元数据属性配置", + "type": "object", + "categories": { + "rtcBasic": { + "title": "扩展表单", + "hide": designerMode !== DesignerMode.PC_RTC, + "properties": { + "rtcId": { + "title": "表单元数据标识", + "type": "string", + "readonly": true + }, + "rtcCode": { + "title": "表单元数据编号", + "type": "string", + "readonly": true + }, + "rtcName": { + "title": "表单元数据名称", + "type": "string", + "readonly": true + } + } + }, + "basic": { + "title": designerMode === DesignerMode.PC_RTC ? "基础表单" : "基本信息", + "properties": { + "id": { + "title": "表单元数据标识", + "type": "string", + "readonly": true + }, + "code": { + "title": "表单元数据编号", + "type": "string", + "readonly": true + }, + "name": { + "title": "表单元数据名称", + "type": "string", + "readonly": true + } + } + } + } + } +}; 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 } ]; diff --git a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx index 2de34d57fe0f4b8a9af4a38b76bbe6ad7cdc473f..b9015c69dc3313026dff2376efc1083eaa054a01 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx @@ -909,31 +909,43 @@ export default defineComponent({ function onMouseLeaveYearView($event: any, isSecondCalendar: boolean) { } - function onConfirm() { - // 区间 - if (enablePeriod.value && selectedPeriod.value) { - const isBeginEmptyDate = !isInitializedDate(selectedPeriod.value.from); - const isEndEmptyDate = !isInitializedDate(selectedPeriod.value.to); + const updateRangeDate = () => { + const { from, to } = selectedPeriod.value; - if (isBeginEmptyDate || isEndEmptyDate) { - const now = new Date(); - if (isBeginEmptyDate) { - selectedPeriod.value.from = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }; - } + const isBeginEmptyDate = !isInitializedDate(from); + const isEndEmptyDate = !isInitializedDate(to); - selectedPeriod.value.to = { ...selectedPeriod.value.from }; - } + let begin = {...from}; + let end = {...to}; - if (props.showTime) { - const { hour, minute, second } = getTimeValue(timeValue.value, true); - selectedPeriod.value.from = { ...selectedPeriod.value.from, ...{ hour, minute, second } }; - const { hour: endHour, minute: endMinute, second: endSecond } = getEndTimeValue(timeRangeValue.value); - selectedPeriod.value.to = { ...selectedPeriod.value.to, ...{ hour: endHour, minute: endMinute, second: endSecond } }; + if (isBeginEmptyDate || isEndEmptyDate) { + const now = new Date(); + if (isBeginEmptyDate) { + begin = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }; } - const { from, to } = selectedPeriod.value; - context.emit('datePicked', { startDate: from, endDate: to }); + + end = { ...begin }; + } + + if (props.showTime) { + const { hour, minute, second } = getTimeValue(timeValue.value, true); + begin = { ...begin, ...{ hour, minute, second } }; + + const { hour: endHour, minute: endMinute, second: endSecond } = getEndTimeValue(timeRangeValue.value); + end = { ...end, ...{ hour: endHour, minute: endMinute, second: endSecond } }; + } + + return { begin, end }; + }; + + function onConfirm() { + // 区间 + if (enablePeriod.value && selectedPeriod.value) { + const { begin, end } = updateRangeDate(); + selectedPeriod.value= { from: begin, to: end }; + context.emit('datePicked', { startDate: begin, endDate: end }); } else { if (props.showTime) { const { hour, minute, second } = getTimeValue(timeValue.value, true); @@ -1176,15 +1188,16 @@ export default defineComponent({ const canConfirm = computed(() => { if (enablePeriod.value) { - // if (props.showTime) { - // const { from: begin, to: end } = selectedPeriod.value; - // const canClick = equalOrEarlier(begin, end); - // return { - // "pointer-events": canClick ? "auto" : "none", - // opacity: canClick ? 1 : 0.3, - // }; - // } - return true; + if (props.showTime) { + const { begin, end } = updateRangeDate(); + const canClick = equalOrEarlier(begin, end); + + return { + "pointer-events": canClick ? "auto" : "none", + opacity: canClick ? 1 : 0.3, + }; + } + // return true; } else { const { hour, minute, second } = getTimeValue(timeValue.value, true); const currentDateValue = { ...selectedDate.value, ...{ hour, minute, second } }; diff --git a/packages/ui-vue/components/date-picker/src/composition/use-date.ts b/packages/ui-vue/components/date-picker/src/composition/use-date.ts index bd684a34b67ee0ff2ae8eb0604df33a5f798f9f3..f1e672e5a19de9ebca855f85371c5f8c137fc842 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-date.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-date.ts @@ -205,9 +205,9 @@ export function useDate(): UseDate { const timeArr = timeValue.replace('时', ':').replace('分', ':').replace('秒', '').split(':'); if (timeArr.length >= 2) { return { - hour: timeArr[0], - minute: timeArr[1], - second: timeArr[2] ? timeArr[2] : 0 + hour: parseInt(timeArr[0], 10), + minute: parseInt(timeArr[1]), + second: parseInt(timeArr[2] || '0') || 0 }; } } else { diff --git a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx index d6f928626e2828419da674c9c4adb90d4bd15e34..4e7d2d2e61981161546a745b72e2fef6fe488312 100644 --- a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx +++ b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx @@ -77,6 +77,7 @@ export default defineComponent({ } } else if (editorType === 'lookup' && editor.value['onUpdate:idValue'] && typeof editor.value['onUpdate:idValue'] === 'function') { editorProps['onUpdate:idValue'] = editor.value['onUpdate:idValue']; + editorProps.id = id.value; } else if (editorType === 'collection-property-editor' && editor.value['onSelectionChange'] && typeof editor.value['onSelectionChange'] === 'function') { editorProps['onSelectionChange'] = editor.value['onSelectionChange']; } else if (editorType === 'property-editor') { diff --git a/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts b/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts index 2f79d9031798529e426f8e57cb7f71a8f82dc32e..fb407faff4b89cab467e50809ad7bed72a177158 100644 --- a/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts +++ b/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts @@ -140,15 +140,13 @@ export class ExternalContainerProperty extends BaseControlProperty { communicationIds.map(communicationId => { const communication = communications.find(item => item.id === communicationId); if (communication?.source && communication.source.formId !== propertyData.externalComponent.id) { - communication.source.formId = propertyData.externalComponent.id; - communication.source.formCode = propertyData.externalComponent.code; - communication.source.componentId = ''; - communication.source.event = ''; - + communication.needDelete = true; needNotify = true; } }); + formSchema.module.communications = formSchema.module.communications.filter(item => !item.needDelete); if (needNotify) { + propertyData.onCommunication = ''; const notifyService: any = new FNotifyService(); notifyService.globalConfig = { position: 'top-center' }; notifyService.warning({ message: '切换引入表单后,请重新配置组件通讯。' }); diff --git a/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts b/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts index 8247a36218dd7c59b457078f93b513c4a638ab1b..2b8e0387783a2764fdd78c5dca1a839bf71e12cf 100644 --- a/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts +++ b/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts @@ -76,16 +76,8 @@ export class ExternalLookupPropertyConfig extends BaseControlProperty { private getEventPropConfig(propertyData: any) { const self: any = this; const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, this.events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData, - viewSourceHandle: (commandInfo: any) => { - self.eventsEditorUtils.jumpToMethod(commandInfo); - } - } - }; + const properties = self.createBaseEventProperty(initialData); + return { title: '事件', hideTitle: true, diff --git a/packages/ui-vue/components/modal/src/modal.component.tsx b/packages/ui-vue/components/modal/src/modal.component.tsx index 7a65d0a351fb2c713ae1d7eb50a56701dd3bea46..9c1941f55fd5381cf32066972101d94254b49615 100644 --- a/packages/ui-vue/components/modal/src/modal.component.tsx +++ b/packages/ui-vue/components/modal/src/modal.component.tsx @@ -144,6 +144,9 @@ export default defineComponent({ if (modelValue.value) { hasModal.value = hasOpenModal(); + } else { + maximized.value = false; + allowDrag.value = props.draggable; } }); // 监听是否展示标题变化 diff --git a/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts b/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts index 72a57b7d066d77f675826a294d0ae1fd3fed4564..d4669ba11885c8a4b7e7d96f9f41a849e315c228 100644 --- a/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts +++ b/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts @@ -84,16 +84,8 @@ export class ModalProperty extends BaseControlProperty { const self: any = this; const events = ModalEvents; const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData, - viewSourceHandle: (commandInfo: any) => { - self.eventsEditorUtils.jumpToMethod(commandInfo); - } - } - }; + const properties = self.createBaseEventProperty(initialData); + return { title: '事件', hideTitle: true, @@ -110,8 +102,7 @@ export class ModalProperty extends BaseControlProperty { } events.forEach(event => { newPropertyData[event.label] = propertyData[event.label]; - }) - + }); } }; } diff --git a/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css b/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css index ffa645de94e0d71efd2759e5e7a918b9eac81b8c..a9de211a06bdc4987d8bd508f01974a5b027b1ce 100644 --- a/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css +++ b/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css @@ -1,6 +1,5 @@ .f-property-editor-customize-container { - width: 65%; - float: left; - margin-left: 3%; + width: 100%; + padding-left: 3%; background: #FFFFFF; } \ No newline at end of file