import { ICtrlAbility, IEvent, IParam, IViewAbility, IViewActionResult, IViewModel, IProjectSetting, } from '@/core/interface'; import { IViewController } from '@/core/interface/view/controller'; import { IViewControllerParams } from '@/core/interface/view/controller-params'; import { IViewEvent, ViewActionType, } from '@/core/interface/view/event/i-view-event'; import { IViewStore } from '@/core/interface/view/store'; import { LoadingHelper } from '@/core/modules'; import { deepCopy, LayoutUtil, verifyDynamicLogic } from '@/core/utils'; import { QXEvent } from 'qx-util'; import { ControllerBase } from '../common/controller-base'; import { useRoute } from 'vue-router'; import { useViewActionStoreWithOut } from '@/store'; /** * 视图控制器 * * @export * @class ViewController * @implements {IViewController} */ export class ViewController extends ControllerBase implements IViewController { /** * 视图模型 * * @type {IViewModel} * @memberof ViewController */ protected declare readonly model: IViewModel; /** * 视图抛出事件 * * @protected * @type {Function} * @memberof GridViewController */ protected evt!: QXEvent>; /** * 视图是否默认加载 * * @protected * @type {boolean} * @memberof GridViewController */ protected isLoadDefault = true; /** * 视图打开方式 * * @protected * @type {('ROUTE' | 'EMBED' | 'MODAL')} 路由|嵌入|模态(默认路由) * @memberof ViewController */ protected openType: 'ROUTE' | 'EMBED' | 'MODAL' = 'ROUTE'; /** * Creates an instance of ViewController. * @param {IViewControllerParams} params * @memberof ViewController */ constructor(params: IViewControllerParams) { super(params); this.viewInit(params); } /** * 视图初始化 * * @memberof ViewController */ public viewInit(params: IViewControllerParams): void { this.beforeViewInit(params); this.processViewInit(params); this.afterViewInit(params); } /** * 视图初始化前(准备必要参数) * * @param {(IViewControllerParams)} params * @memberof ViewController */ private beforeViewInit(params: IViewControllerParams) { this.evt = params.evt; this.isLoadDefault = this.model.isLoadDefault === true && params.isLoadDefault === true ? true : false; this.openType = params.openType ? params.openType : 'ROUTE'; } /** * 处理视图初始化 * * @param {(IViewControllerParams)} params * @memberof ViewController */ protected processViewInit( params: IViewControllerParams ) { this.store = { context: {}, viewParams: {}, closeView: this.closeView.bind(this), loadingHelper: new LoadingHelper('VIEW', params.pLoadingHelper), viewMsgs: [], counterData: {}, layoutData: {}, layoutModelDetails: {}, viewCtx: { view: this, ctrl: {}, appGlobal: {}, routeViewGlobal: {}, viewGlobal: {}, viewNavData: {}, parentView: undefined, rootView: undefined }, } as IViewStore as S; } /** * 视图初始化后(对store做处理) * * @param {(IViewControllerParams)} params * @memberof ViewController */ private afterViewInit(params: IViewControllerParams) { this.initStore(); // 无部件抛出事件 if (this.model.ctrls.length === 0) { this.evt.emit('viewInit', this.name, this.getAbility()); } } /** * 视图挂载 * * @param {(IParam | undefined)} [opts] * @memberof ViewController */ public viewMounted(opts: IParam = {}): void { this.initViewMsg(); this.initCounterData(); this.initViewActionState(); if (this.UIEngineContainer) { const inputParam = this.getUIEngineInputParam(); this.UIEngineContainer.executeViewEvent('onViewMounted', inputParam); this.UIEngineContainer.executeTimer(inputParam); } this.evt.emit('viewMounted', this.name, opts || {}); } /** * 初始化视图行为状态 * * @memberof ViewController */ public initViewActionState() { const { fullPath } = useRoute(); const { addAppViews } = useViewActionStoreWithOut(); addAppViews(fullPath, this.model.codeName); } /** * 视图销毁 * * @memberof ViewController */ public viewDestroy(): void { this.destroyCounter(); const projectSetting: IProjectSetting = App.getProjectSetting(); if (!projectSetting.multiTabsSetting.show) { const { removeAppViewsByTag } = useViewActionStoreWithOut(); removeAppViewsByTag(this.model.codeName); } if (this.UIEngineContainer) { const inputParam = this.getUIEngineInputParam(); this.UIEngineContainer.executeViewEvent('onViewDestroyed', inputParam); this.UIEngineContainer.destroy(); } } /** * 关闭视图 * * @memberof ViewController */ public closeView() { // 路由模式打开 if (this.openType === 'ROUTE') { App.closeView(); } else { this.emit('viewClose', this.getData()); } } /** * 刷新视图 * * @memberof ViewController */ public refresh(args?: IParam): Promise { return this.getMainCtrlAbility()?.refresh(args) || Promise.resolve(false); } /** * 刷新父节点视图 * * @memberof ViewController */ public refreshParentView(args?: IParam): Promise { const { parentView } = this.store.viewCtx; if (!parentView) { return Promise.resolve(false) } return parentView.refresh(args); } /** * 刷新root视图 * * @memberof ViewController */ public refreshRootView(args?: IParam): Promise { const { rootView } = this.store.viewCtx; if (!rootView) { return Promise.resolve(false) } return rootView.refresh(args); } /** * 获取主部件能力 * * @memberof ViewController */ protected getMainCtrlAbility(): ICtrlAbility | undefined { throw new Error('Method not implemented.'); } /** * 加载视图 * * @memberof ViewController */ public load(args?: IParam): Promise { throw new Error('Method not implemented.'); } /** * 保存视图 * * @memberof ViewController */ public save(args?: IParam): void { this.emit('viewDataSave', []); } /** * 处理部件初始化 * * @param {string} name * @param {ICtrlAbility} ability * @memberof ViewController */ public handleCtrlInit(name: string, ability: ICtrlAbility): void { this.setSubAbility(name, ability); const controls = []; this.model.ctrls.forEach((ctrl) => { if (ctrl.controlType !== 'TOOLBAR' && ctrl.controlType !== 'SEARCHBAR') { controls.push(ctrl); } }); if (this.subAbilityCenter.size === controls.length) { this.evt.emit('viewInit', this.name, this.getAbility()); } } /** * 处理部件行为 * * @param {string} name * @param {string} action * @param {IParam[]} data * @memberof ViewController */ public handleCtrlAction(name: string, action: string, data: IParam[]): void { // 后续处理 if (this.UIEngineContainer) { const inputParam = this.getUIEngineInputParam(); this.UIEngineContainer.executeCtrlEvent(action, inputParam); } } /** * 处理部件销毁 * * @param {string} name * @param {IParam} data * @memberof ViewController */ public handleCtrlDestroy(name: string, data: IParam) { throw new Error(`处理部件销毁暂未实现`); } /** * 获取能力 * * @return {*} {A} * @memberof ViewController */ public getAbility(): A { return { ...super.getAbility(), closeView: this.closeView.bind(this), refresh: this.refresh.bind(this), load: this.load.bind(this), save: this.save.bind(this), getLayoutModelDetails: this.getLayoutModelDetails.bind(this), handlePanelValueChange: this.handlePanelValueChange.bind(this), }; } /** * 抛出视图行为 * * @protected * @param {(T )} action * @param {IParam[]} data * @memberof ViewController */ protected emit(action: T | ViewActionType, data: IParam[]) { this.evt.emit('viewAction', this.name, action, data); } /** * 初始化视图消息组 * * @return {*} {A} * @memberof ViewController */ public initViewMsg() { if (this.model.viewMsgGroupName) { App.getAppViewMsgHelper() .getViewMsgDetails( this.model.viewMsgGroupName, this.store.context, this.store.viewParams ) .then((res: any) => { if (res?.length > 0) { this.store.viewMsgs = res; } }); } } /** * 初始化视图布局面板 * * @return {*} * @memberof ViewController */ protected async initLayout() { const { rootLayoutDetailNames, layoutItems } = this.model; if (rootLayoutDetailNames && layoutItems) { this.store.loadingHelper.beginLoading(); for (let i = 0; i < rootLayoutDetailNames.length; i++) { const name = rootLayoutDetailNames[i]; const rootItem = layoutItems[name]; if (!rootItem) { return; } await this.initLayoutItem(rootItem); } this.store.loadingHelper.endLoading(); } } /** * 初始化布局项 * * @param {*} layoutModelItem 项模型 * @param {number} [index] 多数据容器索引 * @memberof ViewController */ private async initLayoutItem(layoutModelItem: any, index?: number) { const { name } = layoutModelItem; const { layoutItems } = this.model; const { context, viewParams } = this.store; const layoutModelDetail = LayoutUtil.getLayoutItemInstance( { ...layoutModelItem, viewType: this.model.viewType, panel: this, }, context, viewParams ); if (!(index || index === 0)) { await layoutModelDetail.load(context, viewParams); this.store.layoutModelDetails[name] = layoutModelDetail; this.store.layoutData[name] = layoutModelDetail.getData(); this.panelLogic(''); } else { layoutModelDetail.setIndex(index); await layoutModelDetail.load(context, viewParams); this.store.layoutModelDetails[`${name}_${index}`] = layoutModelDetail; this.store.layoutData[`${name}_${index}`] = layoutModelDetail.getData(); this.panelLogic('', index); } if (layoutModelDetail && layoutModelDetail.details) { if (layoutModelDetail.dataRegionType === 'MULTIDATA') { const multiData = layoutModelDetail.getData(); if (multiData && multiData.length > 0) { for (let i = 0; i < multiData.length; i++) { for (let j = 0; j < layoutModelDetail.details.length; j++) { const key = layoutModelDetail.details[j]; if (layoutItems && layoutItems[key]) { await this.initLayoutItem(layoutItems[key], i); } } } } } else { for (let i = 0; i < layoutModelDetail.details.length; i++) { const key = layoutModelDetail.details[i]; if (layoutItems && layoutItems[key]) { await this.initLayoutItem(layoutItems[key], index); } } } } } /** * 面板逻辑 * * @param {string} name * @param {number} [index] * @memberof ViewController */ private panelLogic(name: string, index?: number): void { const { layoutData } = this.store; const { layoutItems } = this.model; if (layoutItems) { Object.values(layoutItems).forEach((item: IParam) => { item.groupLogics?.forEach((logic: IParam) => { const relatedNames = logic.relatedDetailNames || []; if (Object.is(name, '') || relatedNames.indexOf(name) != -1) { const tempLogic = deepCopy(logic); if (index || index === 0) { tempLogic.childLogics.forEach((childLogic: any) => { childLogic.dEFDName = `${childLogic.dEFDName}_${index}`; }); } const fullName = index || index === 0 ? `${item.name}_${index}` : item.name; const ret = verifyDynamicLogic(layoutData, tempLogic); switch (tempLogic.logicCat) { // 动态空输入,不满足则必填 case 'ITEMBLANK': if (this.store.layoutModelDetails[fullName]) { this.store.layoutModelDetails[fullName].required = !ret; } break; // 动态启用,满足则启用 case 'ITEMENABLE': if (this.store.layoutModelDetails[fullName]) { this.store.layoutModelDetails[fullName].disabled = !ret; } break; // 动态显示,满足则显示 case 'PANELVISIBLE': if (this.store.layoutModelDetails[fullName]) { this.store.layoutModelDetails[fullName].visible = ret; } break; } } }); }); } } /** * 处理面板值改变 * * @private * @param {string} name * @param {*} value * @param {number} [index] * @return {*} * @memberof ViewController */ private handlePanelValueChange(name: string, value: any, index?: number) { const fullName = index || index === 0 ? `${name}_${index}` : name; if (!fullName || !this.store.layoutData.hasOwnProperty(fullName)) { return; } this.store.layoutData[fullName] = value; this.store.layoutModelDetails[fullName].setData(value); this.panelLogic(name, index); this.computeButtonAuthState(); } /** * 处理面板按钮点击 * * @private * @param {string} name * @param {*} event * @param {number} [index] * @memberof ViewController */ private handlePanelButtonAction( name: string, event: MouseEvent, index?: number ) { const { context, viewParams } = this.store; const fullName = index || index === 0 ? `${name}_${index}` : name; const layoutModel = this.store.layoutModelDetails[fullName]; if (layoutModel) { let tempData: IParam[] = []; const data: IParam = layoutModel.getData(); const ability: any = layoutModel.getAbility(); const logic = layoutModel.uiAction; if (data) { if (data instanceof Array) { tempData = [...data]; } else { tempData = [data]; } } App.getViewLogicHelper().executeViewLogic( context, viewParams, tempData, event, ability, logic ); } } /** * 计算面板按钮状态权限 * * @memberof ViewController */ private async computeButtonAuthState() { for (const key in this.store.layoutModelDetails) { const layoutModel = this.store.layoutModelDetails[key]; if (layoutModel.itemType == 'BUTTON') { await layoutModel.computeActionAuthState(); } } } /** * 处理组件行为 * * @param {IEvent} actionParam * @memberof ViewController */ public handleComponentAction( actionParam: IEvent, index?: number ): void { const { name, action, data } = actionParam; switch (action) { case 'panelButtonAction': this.handlePanelButtonAction(name, data, index); break; case 'valueChange': this.handlePanelValueChange(name, data, index); this.handlePanelItemChange(name, data, index); break; case 'panelItemFocus': case 'editorFocus': this.handlePanelItemFocus(name, data, index); break; case 'panelItemBlur': case 'editorBlur': this.handlePanelItemBlur(name, data, index); break; case 'panelItemClick': case 'editorClick': this.handlePanelItemClick(name, data, index); break; default: break; } } /** * @description 处理面板项变更 * @param {string} name * @param {*} value * @param {number} [index] * @memberof ViewController */ public handlePanelItemChange(name: string, value: any, index?: number) { this.handlePanelItemLogic(name, value, 'onChange', index); } /** * @description 处理面板项focus * @param {string} name * @param {*} value * @param {number} [index] * @memberof ViewController */ public handlePanelItemFocus(name: string, value: any, index?: number) { this.handlePanelItemLogic(name, value, 'onEnter', index); } /** * @description 处理面板项blur * @param {string} name * @param {*} value * @param {number} [index] * @memberof ViewController */ public handlePanelItemBlur(name: string, value: any, index?: number) { this.handlePanelItemLogic(name, value, 'onLeave', index); } /** * @description 处理面板项点击 * @param {string} name * @param {*} value * @param {number} [index] * @memberof ViewController */ public handlePanelItemClick(name: string, value: any, index?: number) { this.handlePanelItemLogic(name, value, 'onClick', index); } /** * @description 处理面板项逻辑 * @param {string} name * @param {*} value * @param {string} action * @param {number} [index] * @memberof ViewController */ public handlePanelItemLogic(name: string, value: any, action: string, index?: number) { const fullName = index || index === 0 ? `${name}_${index}` : name; if (!fullName || !this.store.layoutData.hasOwnProperty(fullName)) { return; } if (this.UIEngineContainer) { const inputParam = this.getUIEngineInputParam(); this.UIEngineContainer.executePanelEvent(`${fullName}${action}`, inputParam); } } /** * 获取布局面板模型对象 * * @memberof ViewController */ public getLayoutModelDetails() { return this.store.layoutModelDetails; } }