From b18ba14701203c90a58ada7cfad17c31a3456849 Mon Sep 17 00:00:00 2001 From: ShineKOT <1917095344@qq.com> Date: Tue, 14 Jan 2025 13:55:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=20=E6=96=B0=E5=A2=9E=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E6=8E=A7=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 + src/panel-component/index.ts | 2 + src/panel-component/panel-app-title/index.ts | 21 ++ .../panel-app-title.controller.ts | 86 ++++++ .../panel-app-title.provider.ts | 28 ++ .../panel-app-title/panel-app-title.scss | 94 +++++++ .../panel-app-title/panel-app-title.state.ts | 75 ++++++ .../panel-app-title/panel-app-title.tsx | 244 ++++++++++++++++++ 8 files changed, 554 insertions(+) create mode 100644 src/panel-component/panel-app-title/index.ts create mode 100644 src/panel-component/panel-app-title/panel-app-title.controller.ts create mode 100644 src/panel-component/panel-app-title/panel-app-title.provider.ts create mode 100644 src/panel-component/panel-app-title/panel-app-title.scss create mode 100644 src/panel-component/panel-app-title/panel-app-title.state.ts create mode 100644 src/panel-component/panel-app-title/panel-app-title.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index c819b318..b7ef0997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ ## [Unreleased] +### Added + +- 新增应用标题控件 + ## [0.0.48] - 2025-01-11 ### Added diff --git a/src/panel-component/index.ts b/src/panel-component/index.ts index 35d8ecaa..6dd90fca 100644 --- a/src/panel-component/index.ts +++ b/src/panel-component/index.ts @@ -30,6 +30,7 @@ import IBizMobUserMessage from './user-message'; import IBizMobAsyncAction from './async-action'; import IBizPanelButtonList from './panel-button-list'; import IBizAuthSso from './auth-sso'; +import IBizPanelAppTitle from './panel-app-title'; export const IBizPanelComponents = { install: (v: App): void => { @@ -62,6 +63,7 @@ export const IBizPanelComponents = { v.use(IBizPanelButtonList); v.use(IBizAuthSso); v.use(IBizAuthWxmpQrcode); + v.use(IBizPanelAppTitle); }, }; diff --git a/src/panel-component/panel-app-title/index.ts b/src/panel-component/panel-app-title/index.ts new file mode 100644 index 00000000..94d94e5f --- /dev/null +++ b/src/panel-component/panel-app-title/index.ts @@ -0,0 +1,21 @@ +import { App } from 'vue'; +import { withInstall } from '@ibiz-template/vue3-util'; +import { registerPanelItemProvider } from '@ibiz-template/runtime'; +import PanelAppTitle from './panel-app-title'; +import { PanelAppTitleProvider } from './panel-app-title.provider'; + +export * from './panel-app-title.controller'; + +export const IBizPanelAppTitle = withInstall(PanelAppTitle, function (v: App) { + v.component(PanelAppTitle.name!, PanelAppTitle); + registerPanelItemProvider( + 'RAWITEM_APP_APPTITLE', + () => new PanelAppTitleProvider(), + ); + registerPanelItemProvider( + 'CTRLPOS_APP_APPTITLE', + () => new PanelAppTitleProvider(), + ); +}); + +export default IBizPanelAppTitle; diff --git a/src/panel-component/panel-app-title/panel-app-title.controller.ts b/src/panel-component/panel-app-title/panel-app-title.controller.ts new file mode 100644 index 00000000..fd8e69d7 --- /dev/null +++ b/src/panel-component/panel-app-title/panel-app-title.controller.ts @@ -0,0 +1,86 @@ +import { + PanelItemController, + ViewLayoutPanelController, +} from '@ibiz-template/runtime'; +import { IPanelRawItem } from '@ibiz/model-core'; +import { PanelAppTitleState } from './panel-app-title.state'; + +/** + * 面板应用标题控制器 + * + * @export + * @class PanelAppTitleController + * @extends {PanelItemController} + */ +export class PanelAppTitleController extends PanelItemController { + declare state: PanelAppTitleState; + + protected createState(): PanelAppTitleState { + return new PanelAppTitleState(this.parent?.state); + } + + /** + * 面板控制器 + * + * @type {ViewLayoutPanelController} + * @memberof PanelAppTitleController + */ + declare panel: ViewLayoutPanelController; + + /** + * @description 自定义补充参数 + * @type {IData} + * @memberof PanelAppTitleController + */ + rawItemParams: IData = {}; + + /** + * 初始化 + * + * @return {*} {Promise} + * @memberof PanelAppTitleController + */ + async onInit(): Promise { + await super.onInit(); + this.handleRawItemParams(); + const viewModel = this.panel.view.model; + const app = ibiz.hub.getApp(viewModel.appId); + this.state.caption = ibiz.env.AppTitle || app.model.caption || ''; + + // 抬头 + if (viewModel.title && !document.title) { + document.title = viewModel.title; + } + + // 应用图标 + if (this.model.sysImage && this.model.sysImage.rawContent) { + this.state.icon = this.model.sysImage.rawContent; + } + + // 是否为 svg 图标 + if (this.state.icon.endsWith('.svg') || this.state.icon2.endsWith('.svg')) { + this.state.isSvg = true; + } + } + + /** + * 处理自定义补充参数 [{key:'name',value:'data'}] => {name:'data'} + * + * @author zk + * @date 2023-09-27 03:09:55 + * @protected + * @memberof NavPosController + */ + protected handleRawItemParams(): void { + const rawItemParams = this.model.rawItem?.rawItemParams; + if (Array.isArray(rawItemParams)) { + rawItemParams.forEach(item => { + const key = item.key; + const value = item.value; + if (key && value) { + this.rawItemParams[key.toLowerCase()] = value; + } + }); + } + } +} diff --git a/src/panel-component/panel-app-title/panel-app-title.provider.ts b/src/panel-component/panel-app-title/panel-app-title.provider.ts new file mode 100644 index 00000000..bb3de704 --- /dev/null +++ b/src/panel-component/panel-app-title/panel-app-title.provider.ts @@ -0,0 +1,28 @@ +import { + IPanelItemProvider, + PanelController, + PanelItemController, +} from '@ibiz-template/runtime'; +import { IPanelItem } from '@ibiz/model-core'; +import { PanelAppTitleController } from './panel-app-title.controller'; + +/** + * 面板应用标题适配器 + * + * @export + * @class PanelAppTitleProvider + * @implements {IPanelItemProvider} + */ +export class PanelAppTitleProvider implements IPanelItemProvider { + component: string = 'IBizPanelAppTitle'; + + async createController( + panelItem: IPanelItem, + panel: PanelController, + parent: PanelItemController | undefined, + ): Promise { + const c = new PanelAppTitleController(panelItem, panel, parent); + await c.init(); + return c; + } +} diff --git a/src/panel-component/panel-app-title/panel-app-title.scss b/src/panel-component/panel-app-title/panel-app-title.scss new file mode 100644 index 00000000..3fc7e34a --- /dev/null +++ b/src/panel-component/panel-app-title/panel-app-title.scss @@ -0,0 +1,94 @@ +$panel-app-title: ( + 'color': getCssVar(color, text, 0), + 'font-size': getCssVar(font-size, header-3), + 'font-weight': getCssVar(font-weight, bold), + 'padding': 0 getCssVar(spacing, base, tight), + 'collapse-padding': 0 getCssVar(spacing, tight), + 'not-index-height': 60px, +); + +@include b(panel-app-title) { + @include set-component-css-var('panel-app-title', $panel-app-title); + + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: getCssVar('panel-app-title', 'padding'); + cursor: pointer; + + @include e(title) { + @include utils-ellipsis; + + font-size: getCssVar('panel-app-title', 'font-size'); + font-weight: bold; + color: getCssVar('panel-app-title', 'color'); + } + + @include e(logo) { + @include flex(row, center, center); + + height: 100%; + padding: 0 getCssVar('spacing', 'tight'); + font-size: calc(getCssVar('font-size', 'header-1') + 8px); + + img { + display: inline-block; + width: auto; + height: 100%; + } + + svg { + fill: getCssVar('panel-app-title', 'color'); + } + @include m('expand') { + width: 66px; + height: 66px; + + &+svg { + width: calc(100% - 66px); + } + } + } + + + @include when(only-img){ + @include e(logo) { + padding: 0; + } + } + + + @include when(collapse){ + max-width: 100%; + padding: getCssVar(panel-app-title, collapse-padding); + @include e(collapse-title){ + width: 100%; + } + + @include e(collpase-icon){ + img{ + width: 100%; + height: 100%; + } + } + + @include e(caption2){ + overflow: hidden; + font-size: getCssVar(font-size, header-4); + font-weight: getCssVar(panel-app-title, font-weight); + text-align: center; + white-space: nowrap; + } + @include e(subCaption2){ + overflow: hidden; + font-size: getCssVar(font-size, header-5); + text-align: center; + white-space: nowrap; + } + } + + @include when('only-title'){ + height: getCssVar('panel-app-title','not-index-height'); + } +} diff --git a/src/panel-component/panel-app-title/panel-app-title.state.ts b/src/panel-component/panel-app-title/panel-app-title.state.ts new file mode 100644 index 00000000..6f8284e3 --- /dev/null +++ b/src/panel-component/panel-app-title/panel-app-title.state.ts @@ -0,0 +1,75 @@ +import { PanelItemState } from '@ibiz-template/runtime'; + +/** + * 面板应用标题状态 + * + * @author lxm + * @date 2023-02-07 06:04:27 + * @export + * @class PanelAppTitleState + * @extends {PanelItemState} + */ +export class PanelAppTitleState extends PanelItemState { + /** + * 应用标题 + * + * @author chitanda + * @date 2023-07-20 17:07:22 + * @type {string} + */ + caption: string = ''; + + /** + * 应用标题(收缩时) + * + * @author chitanda + * @date 2023-07-20 17:07:22 + * @type {string} + */ + caption2: string = ''; + + /** + * 应用子标题 + * + * @author chitanda + * @date 2023-07-20 17:07:22 + * @type {string} + */ + subCaption?: string = ''; + + /** + * 应用子标题(收缩时) + * + * @author chitanda + * @date 2023-07-20 17:07:22 + * @type {string} + */ + subCaption2?: string = ''; + + /** + * 应用 logo 图片地址 + * + * @author chitanda + * @date 2023-07-20 17:07:11 + * @type {string} + */ + icon: string = ''; + + /** + * 应用 logo 图片2地址(收缩时) + * + * @author chitanda + * @date 2023-07-20 17:07:11 + * @type {string} + */ + icon2: string = ''; + + /** + * 是否为 svg 图标 + * + * @author chitanda + * @date 2023-07-20 17:07:26 + * @type {boolean} + */ + isSvg: boolean = false; +} diff --git a/src/panel-component/panel-app-title/panel-app-title.tsx b/src/panel-component/panel-app-title/panel-app-title.tsx new file mode 100644 index 00000000..cb909f38 --- /dev/null +++ b/src/panel-component/panel-app-title/panel-app-title.tsx @@ -0,0 +1,244 @@ +import { CTX } from '@ibiz-template/runtime'; +import { + route2routePath, + routePath2string, + useNamespace, +} from '@ibiz-template/vue3-util'; +import { IPanelField } from '@ibiz/model-core'; +import { computed, defineComponent, inject, PropType } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { PanelAppTitleController } from './panel-app-title.controller'; +import './panel-app-title.scss'; + +export const PanelAppTitle = defineComponent({ + name: 'IBizPanelAppTitle', + props: { + modelData: { + type: Object as PropType, + required: true, + }, + controller: { + type: PanelAppTitleController, + required: true, + }, + }, + setup(props) { + const ns = useNamespace('panel-app-title'); + + const c = props.controller; + + const route = useRoute(); + + const router = useRouter(); + + const ctx = inject('ctx', undefined); + + const menuAlign = computed(() => { + if (ctx?.view) { + return ctx.view.model.mainMenuAlign || 'LEFT'; + } + return 'LEFT'; + }); + + const handleClick = async () => { + // 适配登录页系统标题不提供点击链接能力 + if (c.panel.view.model.viewType === 'APPLOGINVIEW') return; + // 跳转首页 + if (ctx?.view) { + const routePath = route2routePath(route); + routePath.pathNodes = routePath.pathNodes.slice(0, 1); + const url = routePath2string(routePath); + router.push({ path: url }); + setTimeout(() => { + window.location.reload(); + }); + } + + props.controller.onClick(); + }; + + const showImgOnly = computed(() => { + if (menuAlign.value === 'LEFT') { + return true; + } + return false; + }); + + const isCollapse = computed(() => { + return (c.panel.view.state as IData).isCollapse; + }); + + return { + ns, + c, + menuAlign, + showImgOnly, + handleClick, + isCollapse, + }; + }, + render() { + const { icon, isSvg, caption, caption2, subCaption, subCaption2 } = + this.c.state; + let iconVNode = null; + let content = null; + const captionNode = {caption}; + if (this.c.panel.view.model.viewType !== 'APPINDEXVIEW') { + content = captionNode; + } else { + if (this.menuAlign === 'LEFT') { + if (this.isCollapse) { + if (icon) { + let tempIcon = null; + if (isSvg) { + tempIcon = ; + } else { + tempIcon = ( + + + + ); + } + iconVNode = ( +
{tempIcon}
+ ); + } else { + iconVNode = ( +
+
{caption2}
+
{subCaption2}
+
+ ); + } + } else if (this.c.rawItemParams.showexpandicon === 'true' && icon) { + let tempIcon = null; + if (isSvg) { + tempIcon = ( + + ); + } else { + tempIcon = ( + + + + ); + } + iconVNode = ( + + {tempIcon} + + + + + {caption} + + + + + {subCaption} + + + + + + ); + } else { + iconVNode = ( + + + + + + {caption} + + + + + {subCaption} + + + + + + ); + } + } else if (this.menuAlign === 'TOP') { + if (icon) { + if (isSvg) { + iconVNode = ; + } else { + iconVNode = ( + + + + ); + } + } + } + // 左侧只展示图片 + if (this.menuAlign === 'LEFT') { + content = iconVNode; + } else { + content = [iconVNode, captionNode]; + } + } + return ( +
+ {content} +
+ ); + }, +}); +export default PanelAppTitle; -- Gitee