import Vue from 'vue'; import { Subscription } from 'rxjs'; import { AppCtrlEventEngine, AppCustomEngine, AppModelService, AppServiceBase, AppTimerEngine, ControlInterface, CounterService, GetModelService, LogUtil, Util } from '@ibizstudio/runtime'; import { IPSControl, IPSAppCounterRef } from '@ibizstudio/runtime'; import { PluginService } from '../app-service'; import { Emit, Prop } from 'vue-property-decorator'; import { Watch } from '../decorators'; /** * 部件基类 * * @export * @class ControlBase * @extends {Vue} */ export class ControlBase extends Vue implements ControlInterface { /** * 部件静态参数 * * @memberof AppDefaultNotSupportedControl */ @Prop() staticProps: any; /** * 部件动态参数 * * @memberof AppDefaultNotSupportedControl */ @Prop() dynamicProps: any; /** * 监听动态参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof AppDefaultNotSupportedControl */ @Watch('dynamicProps', { immediate: true, }) watchDynamicPropsChange(newVal: any, oldVal: any) { if (newVal && !Util.isFieldsSame(newVal, oldVal)) { this.onDynamicPropsChange(newVal, oldVal); } } /** * 监听静态参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof AppDefaultNotSupportedControl */ @Watch('staticProps', { immediate: true, }) watchStaticPropsChange(newVal: any, oldVal: any) { if (newVal && !Util.isFieldsSame(newVal, oldVal)) { this.onStaticPropsChange(newVal, oldVal); } } /** * 环境文件 * * @type {any} * @protected * @memberof ControlBase */ protected Environment: any = AppServiceBase.getInstance().getAppEnvironment(); /** * 部件UI是否存在权限 * * @type {boolean} * @memberof ControlBase */ enableControlUIAuth: boolean = true; /** * 原生引用控件名称 * * @memberof ControlBase */ realCtrlRefName: string = ''; /** * 视图标识 * * @type {*} * @memberof ControlBase */ viewtag: any; /** * 插件工厂 * * @type {*} * @memberof ControlBase */ PluginFactory: PluginService = PluginService.getInstance(); /** * 显示处理提示 * * @type {boolean} * @memberof ControlBase */ showBusyIndicator: boolean = true; /** * 是否启用动态模式 * * @type {boolean} * @memberof ControlBase */ enableDynamicMode!: boolean; /** * 部件模型实例对象 * * @type {*} * @memberof ControlBase */ declare controlInstance: any; /** * 名称 * * @type {string} * @memberof ControlBase */ declare name: string; /** * 视图通讯对象 * * @type {*} * @memberof ControlBase */ viewState: any; /** * 应用上下文 * * @type {*} * @memberof ControlBase */ context: any = {}; /** * 视图参数 * * @type {*} * @memberof ControlBase */ viewparams: any = {}; /** * 拷贝应用上下文 * * @type {*} * @memberof ControlBase */ copyContext: any = {}; /** * 拷贝视图参数 * * @type {*} * @memberof ControlBase */ copyViewparams: any = {}; /** * 模型数据是否加载完成 * * @memberof ControlBase */ controlIsLoaded: boolean = false; /** * 绘制参数 * * @type {*} * @memberof AppDefaultSearchForm */ renderOptions: any = { controlClassNames: {}, }; /** * 部件服务对象 * * @type {*} * @memberof ControlBase */ service: any; /** * 实体服务对象 * * @type {*} * @memberof ControlBase */ appEntityService: any; /** * 订阅视图状态事件 * * @public * @type {(Subscription | undefined)} * @memberof ControlBase */ viewStateEvent: Subscription | undefined; /** * 计数器服务对象集合 * * @type {Array<*>} * @memberof ControlBase */ counterServiceArray: Array = []; /** * 模型服务 * * @type {AppModelService} * @memberof ControlBase */ modelService!: AppModelService; /** * 部件是否加载完成(判断是否加载中) * * @readonly * @memberof ControlBase */ isControlLoaded: boolean = false; /** * 部件ID * * @memberof ControlBase */ controlId: string = ''; /** * 外部传入数据对象 * * @type {*} * @memberof ControlBase */ navdatas?: any; /** * 视图操作参数集合 * * @type {*} * @memberof ControlBase */ viewCtx?: any; /** * 视图默认使用(路由:true,非路由:false) * * @type {boolean} * @memberof ControlBase */ viewDefaultUsage!: boolean; /** * 界面触发逻辑Map * * @memberof ControlBase */ ctrlTriggerLogicMap: Map = new Map(); /** * 原生界面触发逻辑集合 * * @memberof ControlBase */ realCtrlTriggerLogicArray: Array = []; /** * 原生界面触发逻辑分隔符 * * @memberof ControlBase */ realCtrlSeparator: string = 'ibiz_'; /** * 注册事件逻辑分隔符 * * @memberof ControlBase */ registerEventSeparator: string = 'ibiz__'; /** * 部件事件抛出方法 * * @param {{ controlname: string; action: string; data: any }} { controlname, action, data } * @memberof ControlBase */ @Emit('ctrl-event') ctrlEvent({ controlname, action, data }: { controlname: string; action: string; data: any }): void {} /** * 部件销毁 * * @author chitanda * @date 2022-06-20 12:06:50 */ destroyed() { this.ctrlDestroyed(); } /** * 部件事件 * @param ctrl 部件 * @param action 行为 * @param data 数据 * * @memberof ViewBase */ onCtrlEvent(controlname: string, action: string, data: any) { if (action == 'controlIsMounted') { this.setIsMounted(controlname); } else { this.ctrlEvent({ controlname, action, data }); } } /** * 初始化部件的绘制参数 * * @type {Array<*>} * @memberof ViewBase */ initRenderOptions(opts?: any) { this.renderOptions = {}; const { controlType, codeName } = this.controlInstance; // 部件类名 const controlClassNames: any = { 'control-container': true, [controlType?.toLowerCase()]: true, [Util.srfFilePath2(codeName)]: true, }; Object.assign(controlClassNames, opts); if (this.controlInstance?.getPSSysCss?.()?.cssName) { Object.assign(controlClassNames, { [this.controlInstance.getPSSysCss()?.cssName]: true }); } this.$set(this.renderOptions, 'controlClassNames', controlClassNames); } /** * 计算目标部件所需参数 * * @param {*} controlInstance 要绘制的部件实例 * @param {*} [otherParam] 其他参数 * @returns * @memberof ControlBase */ computeTargetCtrlData(controlInstance: any) { const targetCtrlName: string = `app-control-shell`; const targetCtrlParam: any = { dynamicProps: { viewparams: Util.deepCopy(this.viewparams), context: Util.deepCopy(this.context), viewCtx: this.viewCtx, }, staticProps: { viewState: this.viewState, viewtag: this.viewtag, modelData: controlInstance, viewDefaultUsage: this.viewDefaultUsage, }, }; Object.defineProperty(targetCtrlParam.staticProps, 'modelData', { enumerable: false, writable: true }); const targetCtrlEvent: any = { 'ctrl-event': ({ controlname, action, data }: { controlname: string; action: string; data: any }) => { this.onCtrlEvent(controlname, action, data); }, closeView: ($event: any) => { this.closeView($event); }, }; return { targetCtrlName: targetCtrlName, targetCtrlParam: targetCtrlParam, targetCtrlEvent: targetCtrlEvent }; } /** * 获取部件类型 * * @returns {string} * @memberof ControlBase */ getControlType(): string { return this.controlInstance.controlType; } /** * 获取多项数据 * * @returns {any[]} * @memberof ControlBase */ getDatas(): any[] { return []; } /** * 获取单项数据 * * @returns {*} * @memberof ControlBase */ getData(): any { return null; } /** * 监听动态参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof ControlBase */ onDynamicPropsChange(newVal: any, oldVal: any) { if (newVal?.context && newVal.context !== oldVal?.context) { this.context = newVal.context; this.copyContext = Util.deepCopy(newVal.context); } if (newVal?.viewparams && newVal.viewparams !== oldVal?.viewparams) { this.viewparams = newVal.viewparams; this.copyViewparams = Util.deepCopy(newVal.viewparams); } if (newVal?.navdatas && newVal.navdatas !== oldVal?.navdatas) { this.navdatas = newVal.navdatas; } if (newVal?.viewCtx && newVal.viewCtx !== oldVal?.viewCtx) { this.viewCtx = newVal.viewCtx; } } /** * 监听导航数据参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof ControlBase */ setNavdatas(args: any) { this.navdatas = args; if (Util.isExistData(this.navdatas)) { this.handleCustomCtrlData(); } else { this.context = Util.deepCopy(this.copyContext); this.viewparams = Util.deepCopy(this.copyViewparams); } } /** * 挂载状态集合 * * @type {Map} * @memberof ControlBase */ mountedMap: Map = new Map(); /** * 是否部件已经完成ctrlMounted * * @type {boolean} * @memberof ControlBase */ hasCtrlMounted: boolean = false; /** * 初始化挂载状态集合 * * @memberof ControlBase */ initMountedMap() { let controls = this.controlInstance?.getPSControls?.(); controls?.forEach((item: any) => { if (item.controlType == 'CONTEXTMENU' || item.controlType == 'TOOLBAR') { this.mountedMap.set(item.name, true); } else { this.mountedMap.set(item.name, false); } }); this.mountedMap.set('self', false); } /** * 设置已经绘制完成状态 * * @memberof ControlBase */ setIsMounted(name: string = 'self') { this.mountedMap.set(name, true); if ([...this.mountedMap.values()].indexOf(false) == -1) { // 执行ctrlMounted if (!this.hasCtrlMounted) { this.$nextTick(() => { this.ctrlMounted(); }); } } } /** * 监听静态参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof ControlBase */ onStaticPropsChange(newVal: any, oldVal: any) { this.controlInstance = this.staticProps.modelData; this.viewState = this.staticProps.viewState; this.viewtag = this.staticProps.viewtag; this.controlId = this.staticProps.controlId; this.viewDefaultUsage = this.staticProps.viewDefaultUsage; this.ctrlModelInit().then(() => { this.ctrlInit(); this.controlIsLoaded = true; this.$nextTick(() => { this.setIsMounted(); }); }); } /** * 初始化模型服务 * * @memberof ControlBase */ async initModelService() { this.modelService = await GetModelService(this.context); } /** * 部件模型数据加载 * * @memberof ControlBase */ async ctrlModelLoad() { // 部件子部件数据加载 if (this.controlInstance?.getPSControls?.()) { for (const control of this.controlInstance.getPSControls() as IPSControl[]) { await control.fill(); } } } /** * 处理部件UI请求 * * @memberof ControlBase */ onControlRequset(action: string, context: any, viewparam: any) {} /** * 处理部件UI响应 * * @memberof ControlBase */ onControlResponse(action: string, response: any) {} /** * 部件模型数据初始化实例 * * @memberof ControlBase */ async ctrlModelInit(args?: any) { await this.initModelService(); await this.ctrlModelLoad(); this.initMountedMap(); this.name = this.controlInstance.name ? this.controlInstance.name : this.controlInstance.codeName; this.showBusyIndicator = this.controlInstance.showBusyIndicator; this.initRenderOptions(); await this.initCounterService(this.controlInstance); await this.initControlLogic(this.controlInstance); } /** * 部件初始化 * * @memberof ControlBase */ ctrlInit(args?: any) { if (this.viewState) { this.viewStateEvent = this.viewState.subscribe(({ tag, action, data }: { tag: string; action: string; data: any }) => { this.viewStateAction(tag, action, data); }); } // 处理部件定时器逻辑 this.handleTimerLogic(); } /** * @description 视图状态触发行为 * @param {string} tag 标识 * @param {string} action 行为 * @param {*} data 行为数据 * @memberof ControlBase */ viewStateAction(tag: string, action: string, data: any) { if (!Object.is(tag, this.name)) { return; } if (Object.is('reset', action)) { this.onReset(); } } /** * 部件挂载 * * @memberof ControlBase */ ctrlMounted(args?: any) { this.hasCtrlMounted = true; this.ctrlEvent({ controlname: this.controlInstance.name, action: 'controlIsMounted', data: true, }); if (this.realCtrlRefName && this.realCtrlTriggerLogicArray.length > 0) { let timer: any = setInterval(() => { if (this.$refs && this.$refs[this.realCtrlRefName]) { clearInterval(timer); for (const item of this.realCtrlTriggerLogicArray) { (this.$refs[this.realCtrlRefName] as Vue).$on(item, (...args: any) => { this.handleRealCtrlEvent(item, this.getData(), args); }); } } }, 100); } } /** * 部件销毁 * * @memberof ControlBase */ ctrlDestroyed(args?: any) { if (this.viewStateEvent) { this.viewStateEvent.unsubscribe(); } // 销毁计数器定时器 if (this.counterServiceArray && this.counterServiceArray.length > 0) { this.counterServiceArray.forEach((item: any) => { let counterService = item.service; if (counterService && counterService.destroyCounter && counterService.destroyCounter instanceof Function) { counterService.destroyCounter(); } }); } // 销毁部件定时器逻辑 this.destroyLogicTimer(); } /** * 计数器刷新 * * @memberof ControlBase */ counterRefresh(arg?: any) { if (this.counterServiceArray && this.counterServiceArray.length > 0) { this.counterServiceArray.forEach((item: any) => { let counterService = item.service; if (counterService && counterService.refreshCounterData && counterService.refreshCounterData instanceof Function) { const tempParams = Util.deepCopy(this.viewparams); if (arg && Object.keys(arg).length > 0) { Object.assign(tempParams, arg); } counterService.refreshCounterData(this.context, tempParams); } }); } } /** * 关闭视图 * * @param {any} args * @memberof ControlBase */ closeView(args: any): void { this.$emit('closeView', [args]); } /** * 初始化计数器服务 * * @memberof ControlBase */ async initCounterService(param: any) { if (!param) { return; } const appCounterRef: Array = param.getPSAppCounterRefs?.() || []; if (appCounterRef && appCounterRef.length > 0) { for (const counterRef of appCounterRef) { const counter = counterRef.getPSAppCounter?.(); if (counter) { await counter.fill(true); const counterService = new CounterService(); await counterService.loaded(counter, { context: this.context, viewparams: this.viewparams }); const tempData: any = { id: counterRef.id, path: counter.modelPath, service: counterService }; this.counterServiceArray.push(tempData); } } } } /** * 初始化部件逻辑 * * @memberof ControlBase */ async initControlLogic(opts: any) { if (opts.getPSControlLogics() && opts.getPSControlLogics().length > 0) { this.realCtrlTriggerLogicArray = []; opts.getPSControlLogics().forEach((element: any) => { // 目标逻辑类型类型为实体界面逻辑、系统预置界面逻辑、前端扩展插件、脚本代码 if ( element && element.triggerType && (Object.is(element.logicType, 'DEUILOGIC') || Object.is(element.logicType, 'SYSVIEWLOGIC') || Object.is(element.logicType, 'PFPLUGIN') || Object.is(element.logicType, 'SCRIPT')) ) { switch (element.triggerType) { case 'CUSTOM': this.ctrlTriggerLogicMap.set(element.name.toLowerCase(), new AppCustomEngine(element)); break; case 'CTRLEVENT': if (element.eventNames.startsWith(this.registerEventSeparator)) { this.ctrlTriggerLogicMap.set(element.eventNames.toLowerCase(), new AppCtrlEventEngine(element)); } else { if (element.eventNames.startsWith(this.realCtrlSeparator)) { let eventNames = element.eventNames.slice(this.realCtrlSeparator.length); eventNames = eventNames.replace(/_/g, '-'); eventNames = `${this.realCtrlSeparator}${eventNames}`; this.ctrlTriggerLogicMap.set(eventNames.toLowerCase(), new AppCtrlEventEngine(element)); } else { this.ctrlTriggerLogicMap.set(element.eventNames.toLowerCase(), new AppCtrlEventEngine(element)); } } break; case 'TIMER': this.ctrlTriggerLogicMap.set(element.name.toLowerCase(), new AppTimerEngine(element)); break; default: console.log(`${element.triggerType}类型暂未支持`); break; } } // 初始化原生界面触发逻辑 if (element.eventNames && element.eventNames.startsWith(this.realCtrlSeparator)) { let eventNames = element.eventNames.slice(this.realCtrlSeparator.length); eventNames = eventNames.replace(/_/g, '-'); this.realCtrlTriggerLogicArray.push(eventNames); } // 绑定用户自定义事件 if (element.eventNames && element.eventNames.startsWith(this.registerEventSeparator)) { this.$on(element.eventNames, (...args: any) => { this.handleCtrlCustomEvent(element.eventNames.toLowerCase(), this.getData(), args); }); } }); } } /** * 处理自定义部件导航数据 * * @memberof ControlBase */ handleCustomCtrlData() { const customCtrlNavContexts = this.controlInstance.getPSControlNavContexts(); const customCtrlParams = this.controlInstance.getPSControlNavParams(); if (customCtrlNavContexts && customCtrlNavContexts.length > 0) { customCtrlNavContexts.forEach((item: any) => { let tempContext: any = {}; let curNavContext: any = item; this.handleCustomDataLogic(curNavContext, tempContext, item.key); Object.assign(this.context, tempContext); }); } if (customCtrlParams && customCtrlParams.length > 0) { customCtrlParams.forEach((item: any) => { let tempParam: any = {}; let curNavParam: any = item; this.handleCustomDataLogic(curNavParam, tempParam, item.key); Object.assign(this.viewparams, tempParam); }); } } /** * 处理部件自定义导航参数逻辑 * * @memberof ControlBase */ handleCustomDataLogic(curNavData: any, tempData: any, item: string) { const navDatas: any = Array.isArray(this.navdatas) ? this.navdatas[0] : this.navdatas; // 直接值直接赋值 if (curNavData.rawValue) { if (Object.is(curNavData.value, 'null') || Object.is(curNavData.value, '')) { Object.defineProperty(tempData, item.toLowerCase(), { value: null, writable: true, enumerable: true, configurable: true, }); } else { Object.defineProperty(tempData, item.toLowerCase(), { value: curNavData.value, writable: true, enumerable: true, configurable: true, }); } } else { // 先从导航上下文取数,没有再从导航参数(URL)取数,如果导航上下文和导航参数都没有则为null if (this.context[curNavData.value.toLowerCase()] != null) { Object.defineProperty(tempData, item.toLowerCase(), { value: this.context[curNavData.value.toLowerCase()], writable: true, enumerable: true, configurable: true, }); return; } else if (this.viewparams[curNavData.value.toLowerCase()] != null) { Object.defineProperty(tempData, item.toLowerCase(), { value: this.viewparams[curNavData.value.toLowerCase()], writable: true, enumerable: true, configurable: true, }); return; } else if (navDatas[curNavData.value.toLowerCase()] != null) { Object.defineProperty(tempData, item.toLowerCase(), { value: navDatas[curNavData.value.toLowerCase()], writable: true, enumerable: true, configurable: true, }); return; } else { Object.defineProperty(tempData, item.toLowerCase(), { value: null, writable: true, enumerable: true, configurable: true, }); } } } /** * 重置 * * @memberof ControlBase */ onReset() { LogUtil.warn(`${this.controlInstance.name}重置功能暂未实现`); } /** * 执行部件事件逻辑 * * @memberof ControlBase */ async executeCtrlEventLogic(name: string, args: any) { if (this.ctrlTriggerLogicMap.get(name)) { await this.ctrlTriggerLogicMap.get(name).executeAsyncUILogic({ arg: args, utils: this.viewCtx, app: this.viewCtx.app, view: this.viewCtx.view, ctrl: this }); return args; } } /** * 调用控件逻辑 * * @memberof ControlBase */ invoke(methodName: string, args: any) { if (!methodName) { LogUtil.warn('方法名不能为空'); return; } if (methodName.startsWith(this.realCtrlSeparator)) { // 原生控件方法 if (this.realCtrlRefName && this.$refs && this.$refs[this.realCtrlRefName]) { let realCtrl: any = this.$refs[this.realCtrlRefName]; let realMethodName: string = methodName.slice(this.realCtrlSeparator.length); if (realCtrl[realMethodName] && realCtrl[realMethodName] instanceof Function) { realCtrl[realMethodName](args); } else { LogUtil.warn(`当前控件未找到指定方法${methodName}`); } } else { LogUtil.warn('原生控件未找到'); } } else { // 代理层控件方法 let proxyCtrl: any = this; if (proxyCtrl[methodName] && proxyCtrl[methodName] instanceof Function) { proxyCtrl[methodName](args); } else { LogUtil.warn(`当前控件未找到指定方法${methodName}`); } } } /** * 处理原生控件事件 * * @memberof ControlBase */ handleRealCtrlEvent(name: string, data: any, args: any) { if (this.ctrlTriggerLogicMap.get(`${this.realCtrlSeparator}${name}`)) { this.ctrlTriggerLogicMap.get(`${this.realCtrlSeparator}${name}`).executeAsyncUILogic({ arg: { sender: this.$refs[this.realCtrlRefName], navContext: this.context, navParam: this.viewparams, navData: this.navdatas, data: data, args: args }, utils: this.viewCtx, app: this.viewCtx.app, view: this.viewCtx.view, ctrl: this, }); } } /** * 处理控件自定义事件 * * @memberof ControlBase */ handleCtrlCustomEvent(name: string, data: any, args: any) { if (this.ctrlTriggerLogicMap.get(`${name}`)) { this.ctrlTriggerLogicMap.get(`${name}`).executeAsyncUILogic({ arg: { sender: this, navContext: this.context, navParam: this.viewparams, navData: this.navdatas, data: data, args: args }, utils: this.viewCtx, app: this.viewCtx.app, view: this.viewCtx.view, ctrl: this, }); } } /** * 处理部件定时器逻辑 * * @memberof ControlBase */ handleTimerLogic() { if (this.ctrlTriggerLogicMap && this.ctrlTriggerLogicMap.size > 0) { for (let item of this.ctrlTriggerLogicMap.values()) { if (item && item instanceof AppTimerEngine) { item.executeAsyncUILogic({ arg: { sender: this, navContext: this.context, navParam: this.viewparams, navData: this.navdatas, data: this.getData() }, utils: this.viewCtx, app: this.viewCtx.app, view: this.viewCtx.view, ctrl: this, }); } } } } /** * 销毁部件定时器逻辑 * * @memberof ControlBase */ destroyLogicTimer() { if (this.ctrlTriggerLogicMap && this.ctrlTriggerLogicMap.size > 0) { for (let item of this.ctrlTriggerLogicMap.values()) { if (item && item instanceof AppTimerEngine) { item.destroyTimer(); } } } } }