diff --git a/src/control/index.ts b/src/control/index.ts index a1a3f9f14281ed734323431710af7ad065556bda..a59bac632252c6ff6a241063d8346c48b6f9696a 100644 --- a/src/control/index.ts +++ b/src/control/index.ts @@ -7,3 +7,4 @@ export * from './panel'; export * from './caption-bar'; export * from './data-view'; export * from './pickup-view-panel'; +export * from './tab-exp-panel'; diff --git a/src/control/tab-exp-panel/index.ts b/src/control/tab-exp-panel/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..314aedc97ec5f05bab1b0f119cce11eb78f63396 --- /dev/null +++ b/src/control/tab-exp-panel/index.ts @@ -0,0 +1,21 @@ +import { registerControlProvider, ControlType } from '@ibiz-template/runtime'; +import { withInstall } from '@ibiz-template/vue3-util'; +import { App } from 'vue'; +import { TabExpPanelControl } from './tab-exp-panel'; +import { TabExpPanelProvider } from './tab-exp-panel.provider'; + +export * from './tab-exp-panel.provider'; +export * from './tab-exp-panel.controller'; + +export const IBizTabExpPanelControl = withInstall( + TabExpPanelControl, + function (v: App) { + v.component(TabExpPanelControl.name, TabExpPanelControl); + registerControlProvider( + ControlType.TAB_EXP_PANEL, + () => new TabExpPanelProvider(), + ); + }, +); + +export default IBizTabExpPanelControl; diff --git a/src/control/tab-exp-panel/tab-exp-panel.controller.ts b/src/control/tab-exp-panel/tab-exp-panel.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d43c6306523395cd4762f1d9425e25d0b95ff36 --- /dev/null +++ b/src/control/tab-exp-panel/tab-exp-panel.controller.ts @@ -0,0 +1,158 @@ +import { + ControlController, + ITabExpPanelController, + ITabExpPanelState, + ITabExpPanelEvent, + TabExpPanelPagesState, + calcNavParams, + calcDeCodeNameById, +} from '@ibiz-template/runtime'; +import { IDER1N, IDETabViewPanel, ITabExpPanel } from '@ibiz/model-core'; +import { NavPosController } from '../../panel-component'; +import { ViewLayoutPanelController } from '../panel'; + +/** + * 分页导航面板 + * + * @export + * @class TabExpPanelController + * @extends {ControlController} + * @implements {ITabExpPanelController} + */ +export class TabExpPanelController + extends ControlController + implements ITabExpPanelController +{ + /** + * 布局面板控制器 + * + * @type {ViewLayoutPanelController} + * @memberof TabExpPanelController + */ + layoutPanel!: ViewLayoutPanelController; + + /** + * 导航占位控制器 + * + * @readonly + * @type {(NavPosController | undefined)} + * @memberof TabExpPanelController + */ + get navPos(): NavPosController | undefined { + return this.layoutPanel.panelItems.nav_pos as NavPosController; + } + + /** + * 初始化state的属性 + * + * @protected + * @memberof TabExpPanelController + */ + protected initState(): void { + super.initState(); + this.state.tabPages = []; + this.state.activeName = ''; + } + + /** + * 创建完成 + * + * @protected + * @return {*} {Promise} + * @memberof TabExpPanelController + */ + async doCreated(): Promise { + await super.doCreated(); + this.layoutPanel = this.view.getController( + 'layoutpanel', + ) as ViewLayoutPanelController; + this.initTabPages(); + } + + /** + * 初始化分页数据 + * + * @memberof TabExpPanelController + */ + initTabPages() { + const viewPanel = this.model.controls as IDETabViewPanel[]; + const tabPages: TabExpPanelPagesState[] = []; + viewPanel.forEach((panel: IDETabViewPanel) => { + tabPages.push({ + caption: panel.caption!, + tabTag: panel.id!, + }); + }); + this.state.tabPages = tabPages; + if (tabPages.length > 0) { + this.state.activeName = tabPages[0].tabTag; + this.handleTabChange(); + } + } + + /** + * 处理分页改变 + * + * @memberof TabExpPanelController + */ + async handleTabChange() { + const { activeName } = this.state; + const viewPanel = this.model.controls!.find(tab => tab.id === activeName); + if (viewPanel) { + this.setNavPosParams(viewPanel); + } + } + + /** + * 准备参数 + * + * @param {IDETabViewPanel} tabViewPanel + * @memberof TabExpPanelController + */ + prepareParams(tabViewPanel: IDETabViewPanel) { + const { + navDER, + navFilter, + navigateContexts, + navigateParams, + appDataEntityId, + } = tabViewPanel; + const model = { + deName: calcDeCodeNameById(appDataEntityId!), + navFilter, + pickupDEFName: (navDER as IDER1N)!.pickupDEFName, + navContexts: navigateContexts, + navParams: navigateParams, + }; + const originParams = { + context: this.context, + params: this.params, + data: {}, + }; + const { resultContext, resultParams } = calcNavParams(model, originParams); + const context = Object.assign(this.context.clone(), resultContext); + const params = { ...this.params, ...resultParams }; + return { context, params }; + } + + /** + * 设置导航占位参数 + * + * @param {IParams} params + * @memberof TabExpPanelController + */ + async setNavPosParams(tabViewPanel: IDETabViewPanel) { + const viewModel = await ibiz.hub.getAppView( + tabViewPanel.embeddedAppDEViewId!, + ); + const { context, params } = this.prepareParams(tabViewPanel); + if (this.navPos) { + this.navPos.openView({ + key: viewModel.codeName!.toLowerCase(), + context, + params, + viewModel, + }); + } + } +} diff --git a/src/control/tab-exp-panel/tab-exp-panel.provider.ts b/src/control/tab-exp-panel/tab-exp-panel.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..44b9336b8c4557e3e5745c2460a99e452923416a --- /dev/null +++ b/src/control/tab-exp-panel/tab-exp-panel.provider.ts @@ -0,0 +1,12 @@ +import { IControlProvider } from '@ibiz-template/runtime'; + +/** + * 分页导航面板 + * + * @export + * @class TabExpPanelProvider + * @implements {IControlProvider} + */ +export class TabExpPanelProvider implements IControlProvider { + component: string = 'IBizTabExpPanelControl'; +} diff --git a/src/control/tab-exp-panel/tab-exp-panel.scss b/src/control/tab-exp-panel/tab-exp-panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/control/tab-exp-panel/tab-exp-panel.tsx b/src/control/tab-exp-panel/tab-exp-panel.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e737f9b67fb748411414bc93b0e380b90e4fb9ac --- /dev/null +++ b/src/control/tab-exp-panel/tab-exp-panel.tsx @@ -0,0 +1,53 @@ +import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; +import { defineComponent, PropType } from 'vue'; +import { ITabExpPanel } from '@ibiz/model-core'; +import { TabExpPanelController } from './tab-exp-panel.controller'; +import './tab-exp-panel.scss'; + +export const TabExpPanelControl = defineComponent({ + name: 'IBizTabExpPanelControl', + props: { + modelData: { type: Object as PropType, required: true }, + context: { type: Object as PropType, required: true }, + params: { type: Object as PropType, default: () => ({}) }, + }, + setup() { + const c = useControlController( + (...args) => new TabExpPanelController(...args), + ); + const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); + + const handleTabChange = () => { + c.handleTabChange(); + }; + + return { + c, + ns, + handleTabChange, + }; + }, + render() { + const { isCreated, tabPages } = this.c.state; + return ( + + {isCreated && ( + + {tabPages.map(tab => { + return ( + + ); + })} + + )} + + ); + }, +}); diff --git a/src/index.ts b/src/index.ts index 2d2e941d43ed02c11f8ccae71a7ee77d6999d183..6378c97a4605c56ec2170b2ef0015bdf63e9ffed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import { IBizCaptionBarControl, IBizDataViewControl, IBizPickupViewPanelControl, + IBizTabExpPanelControl, } from './control'; import IBizEditor from './editor'; import { IBizView } from './view'; @@ -41,6 +42,7 @@ export default { v.use(IBizSearchFormControl); v.use(IBizEditFormControl); v.use(IBizCaptionBarControl); + v.use(IBizTabExpPanelControl); // 编辑器 v.use(IBizEditor); }, diff --git a/src/panel-component/index.ts b/src/panel-component/index.ts index 7558b3b3a3d018754fb35fb8d0da783f86ea1715..e712148aed31ca8ddec72acda2bb86935f28928f 100644 --- a/src/panel-component/index.ts +++ b/src/panel-component/index.ts @@ -6,6 +6,7 @@ import IBizPanelContainer from './panel-container'; import IBizPanelCtrlPos from './panel-ctrl-pos'; import IBizScrollContainer from './scroll-container'; import IBizPanelButton from './panel-button'; +import IBizNavPos from './nav-pos'; export * from './panel-container'; export * from './panel-ctrl-pos'; @@ -13,6 +14,7 @@ export * from './scroll-container'; export * from './auth-userinfo'; export * from './nav-pos-index'; export * from './panel-button'; +export * from './nav-pos'; export const IBizPanelComponents = { install: (v: App) => { @@ -23,6 +25,7 @@ export const IBizPanelComponents = { v.use(IBizNavPosIndex); v.use(IBizNavTabs); v.use(IBizPanelButton); + v.use(IBizNavPos); }, }; diff --git a/src/panel-component/nav-pos/index.ts b/src/panel-component/nav-pos/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a1ebfc7a266f50e7e74c43bd1c7ea81e7f28db8 --- /dev/null +++ b/src/panel-component/nav-pos/index.ts @@ -0,0 +1,16 @@ +import { registerPanelItemProvider } from '@ibiz-template/runtime'; +import { withInstall } from '@ibiz-template/vue3-util'; +import { App } from 'vue'; +import { NavPos } from './nav-pos'; +import { NavPosProvider } from './nav-pos.provider'; +import { NavPosState } from './nav-pos.state'; +import { NavPosController } from './nav-pos.controller'; + +export { NavPosProvider, NavPosState, NavPosController }; + +export const IBizNavPos = withInstall(NavPos, function (v: App) { + v.component(NavPos.name, NavPos); + registerPanelItemProvider('RAWITEM_NAV_POS', () => new NavPosProvider()); +}); + +export default IBizNavPos; diff --git a/src/panel-component/nav-pos/nav-pos.controller.ts b/src/panel-component/nav-pos/nav-pos.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..15c0db5b53b55ec2a19d5c512810675c651e7e93 --- /dev/null +++ b/src/panel-component/nav-pos/nav-pos.controller.ts @@ -0,0 +1,149 @@ +import { EventBase, IModal, Modal, ViewMode } from '@ibiz-template/runtime'; +import { IPanelRawItem } from '@ibiz/model-core'; +import { PanelItemController } from '../../control'; +import { NavViewMsg } from '../nav-pos-index/nav-pos-index.state'; +import { NavPosState } from './nav-pos.state'; + +/** + * 导航占位控制器 + * + * @export + * @class NavPosController + * @extends {PanelItemController} + */ +export class NavPosController extends PanelItemController { + /** + * 导航占位状态 + * + * @type {NavPosState} + * @memberof NavPosController + */ + declare state: NavPosState; + + /** + * 导航视图的modal + * + * @type {{ [key: string]: IModal }} + * @memberof NavPosController + */ + viewModals: { [key: string]: IModal } = {}; + + /** + * 创建导航占位状态对象 + * + * @protected + * @return {*} {NavPosState} + * @memberof NavPosController + */ + protected createState(): NavPosState { + return new NavPosState(this.parent?.state); + } + + /** + * 当前路由视图的层级 + * + * @readonly + * @type {(number | undefined)} + * @memberof NavPosController + */ + get routeDepth(): number | undefined { + return this.panel.view.modal.routeDepth; + } + + /** + * 路由变化 + * + * @param {{ + * currentKey: string; + * fullPath: string; + * }} { + * currentKey, + * fullPath, + * } + * @return {*} + * @memberof NavPosController + */ + onRouteChange({ + currentKey, + fullPath, + }: { + currentKey: string; + fullPath: string; + }) { + // 比监控视图层级低的路由跳转不做处理。 + this.state.currentKey = currentKey; + if (currentKey === '') { + return; + } + + // *维护缓存 + if (!this.state.cacheKeys.includes(currentKey)) { + // 缓存里没有的,加入缓存 + this.state.cacheKeys.push(currentKey); + + // 维护每个视图的modal + if (this.routeDepth) { + this.viewModals[currentKey] = new Modal({ + mode: this.routeDepth ? ViewMode.ROUTE : ViewMode.EMBED, + routeDepth: this.routeDepth ? this.routeDepth + 1 : undefined, + dismiss: () => { + // 执行对应key的dismiss方法 + this.dismiss(currentKey); + }, + }); + } + } + + // *维护导航视图信息 + if (this.state.navViewMsgs[currentKey]) { + this.state.navViewMsgs[currentKey].fullPath = fullPath; + } else { + this.state.navViewMsgs[currentKey] = { + key: currentKey, + fullPath, + }; + } + } + + /** + * 自身的dismiss相关操作 + * + * @param {string} key + * @memberof NavPosController + */ + dismiss(key: string) { + console.log(key); + } + + /** + * 监听视图创建 + * + * @param {EventBase} event + * @memberof NavPosController + */ + onViewCreated(event: EventBase) { + console.log(event); + } + + /** + * 打开视图 + * + * @param {NavViewMsg} navViewMsg + * @memberof NavPosController + */ + openView(navViewMsg: NavViewMsg) { + // 路由打开 + if (this.routeDepth) { + const tempContext = Object.assign(navViewMsg.context!.clone(), { + toRouteDepth: this.routeDepth + 1, + }); + ibiz.openView.root(navViewMsg.viewModel!, tempContext, navViewMsg.params); + } else if (!this.state.cacheKeys.includes(navViewMsg.key)) { + this.state.cacheKeys.push(navViewMsg.key); + this.state.navViewMsgs[navViewMsg.key] = navViewMsg; + this.state.currentKey = navViewMsg.key; + } else { + this.state.currentKey = navViewMsg.key; + } + } +} diff --git a/src/panel-component/nav-pos/nav-pos.provider.ts b/src/panel-component/nav-pos/nav-pos.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a8e3fe2fb285df27ce514eabf4991884b4988ad --- /dev/null +++ b/src/panel-component/nav-pos/nav-pos.provider.ts @@ -0,0 +1,34 @@ +import { IPanelItemProvider } from '@ibiz-template/runtime'; +import { IPanelItem } from '@ibiz/model-core'; +import { PanelController, PanelItemController } from '../../control'; +import { NavPosController } from './nav-pos.controller'; + +/** + * 导航占位适配器 + * + * @export + * @class NavPosProvider + * @implements {IPanelItemProvider} + */ +export class NavPosProvider implements IPanelItemProvider { + component: string = 'IBizNavPos'; + + /** + * 创建控制器 + * + * @param {IPanelItem} panelItem + * @param {PanelController} panel + * @param {(PanelItemController | undefined)} parent + * @return {*} {Promise} + * @memberof NavPosProvider + */ + async createController( + panelItem: IPanelItem, + panel: PanelController, + parent: PanelItemController | undefined, + ): Promise { + const c = new NavPosController(panelItem, panel, parent); + await c.init(); + return c; + } +} diff --git a/src/panel-component/nav-pos/nav-pos.scss b/src/panel-component/nav-pos/nav-pos.scss new file mode 100644 index 0000000000000000000000000000000000000000..450d9dfb3568fb8412afcd6e3468a21542017e6d --- /dev/null +++ b/src/panel-component/nav-pos/nav-pos.scss @@ -0,0 +1,4 @@ +@include b(nav-pos) { + width: 100%; + height: 100%; +} diff --git a/src/panel-component/nav-pos/nav-pos.state.ts b/src/panel-component/nav-pos/nav-pos.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..08af39656e080e24adb87af98f55cd2b24622eac --- /dev/null +++ b/src/panel-component/nav-pos/nav-pos.state.ts @@ -0,0 +1,18 @@ +import { NavPosIndexState } from '../nav-pos-index'; + +/** + * 导航占位状态 + * + * @export + * @class NavPosState + * @extends {PanelItemState} + */ +export class NavPosState extends NavPosIndexState { + /** + * 是否启用缓存 + * + * @type {boolean} + * @memberof NavPosState + */ + cache: boolean = true; +} diff --git a/src/panel-component/nav-pos/nav-pos.tsx b/src/panel-component/nav-pos/nav-pos.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aee5e0eccd2551828dc065bb9afbbc40e615ef88 --- /dev/null +++ b/src/panel-component/nav-pos/nav-pos.tsx @@ -0,0 +1,83 @@ +import { defineComponent, h, PropType, resolveComponent } from 'vue'; +import { onRouteChange, useNamespace } from '@ibiz-template/vue3-util'; +import { IPanelRawItem } from '@ibiz/model-core'; +import { NavPosController } from './nav-pos.controller'; +import { EventBase } from '@ibiz-template/runtime'; +import './nav-pos.scss'; + +export const NavPos = defineComponent({ + name: 'IBizNavPos', + props: { + modelData: { + type: Object as PropType, + required: true, + }, + controller: { + type: NavPosController, + required: true, + }, + }, + setup(props) { + const c = props.controller; + const ns = useNamespace('nav-pos'); + const onViewCreated = (event: EventBase) => { + c.onViewCreated(event); + }; + + if (c.routeDepth) { + onRouteChange(args => { + c.onRouteChange(args); + }, c.routeDepth + 1); + } + return { ns, c, onViewCreated }; + }, + render() { + const { viewModals, state } = this.c; + const { currentKey, cacheKeys, navViewMsgs, cache } = state; + return ( +
+ {this.c.routeDepth ? ( + + {({ Component }: { Component: string }) => { + return ( + Component && + (cache ? ( + + + + ) : ( + + )) + ); + }} + + ) : ( + currentKey && + (cache ? ( + + {h(resolveComponent('IBizViewShell'), { + context: navViewMsgs[currentKey].context, + params: navViewMsgs[currentKey].params, + key: navViewMsgs[currentKey].key, + modelData: navViewMsgs[currentKey].viewModel, + onCreated: this.onViewCreated, + })} + + ) : ( + h(resolveComponent('IBizViewShell'), { + context: navViewMsgs[currentKey].context, + params: navViewMsgs[currentKey].params, + key: navViewMsgs[currentKey].key, + modelData: navViewMsgs[currentKey].viewModel, + onCreated: this.onViewCreated, + }) + )) + )} +
+ ); + }, +}); diff --git a/src/view-engine/index.ts b/src/view-engine/index.ts index 150c3ff499bfd31aa4cd3d060fd48dcd22098c0c..c0394d4cde8d317c0ec6985f9b3b977f8ee67727 100644 --- a/src/view-engine/index.ts +++ b/src/view-engine/index.ts @@ -9,6 +9,7 @@ import { OptViewEngine } from './opt-view.engine'; import { PickupGridViewEngine } from './pickup-grid-view.engine'; import { PickupViewEngine } from './pickup-view.engine'; import { MPickupViewEngine } from './mpickup-view-engine'; +import { TabExpViewEngine } from './tab-exp-view.engine'; export * from './grid-view.engine'; export * from './index-view.engine'; @@ -57,5 +58,9 @@ export const IBizViewEngine = { 'VIEW_MPickupView', (c: IViewController) => new MPickupViewEngine(c), ); + ibiz.engine.register( + 'VIEW_TabExpView', + (c: IViewController) => new TabExpViewEngine(c), + ); }, }; diff --git a/src/view-engine/tab-exp-view.engine.ts b/src/view-engine/tab-exp-view.engine.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc70af635d373936f4f891f6b4896cdc46d88dc6 --- /dev/null +++ b/src/view-engine/tab-exp-view.engine.ts @@ -0,0 +1,49 @@ +import { + ViewEngineBase, + ViewController, + ITabExpPanelController, + ITabExpViewEvent, + ITabExpViewState, +} from '@ibiz-template/runtime'; +import { IAppDETabExplorerView } from '@ibiz/model-core'; + +export class TabExpViewEngine extends ViewEngineBase { + /** + * 分页面板视图控制器 + * + * @protected + * @type {ViewController< + * IAppDETabExplorerView, + * ITabExpViewState, + * ITabExpViewEvent + * >} + * @memberof TabExpViewEngine + */ + protected declare view: ViewController< + IAppDETabExplorerView, + ITabExpViewState, + ITabExpViewEvent + >; + + /** + * 分页导航面板 + * + * @readonly + * @memberof TabExpViewEngine + */ + get tabExpPanel() { + return this.view.getController('tabexppanel') as ITabExpPanelController; + } + + /** + * 视图created生命周期执行逻辑 + * + * @return {*} {Promise} + * @memberof TabExpViewEngine + */ + async doCreated(): Promise { + await super.doCreated(); + const { childNames } = this.view; + childNames.push('tabexppanel'); + } +}