diff --git a/CHANGELOG.md b/CHANGELOG.md index e8048f88ab844a3630b553a532c659874f86b499..ccffa8564912bf5c420433ac2b2404b0551db05e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - 气泡工具栏样式调整 - markdown编辑器图片样式调整 - 通知消息样式调整,防止ios点击两次跳转 +- 表单分页、面板分页添加activeTab +- 调整全屏样式为class控制 +- 添加ios搭载平台 ### Fixed diff --git a/src/common/fullscreen-header/fullscreen-header.scss b/src/common/fullscreen-header/fullscreen-header.scss index aee3fdae11c076e104144c5e4a5108f95d0d53a4..dc42aa6167f5e96fdd1fb81146d0d71879e83bf3 100644 --- a/src/common/fullscreen-header/fullscreen-header.scss +++ b/src/common/fullscreen-header/fullscreen-header.scss @@ -1,17 +1,36 @@ +$fullscreen-header: ( + height: rem(44px), + font-size: getCssVar(font-size, header, 5), + font-weight: getCssVar(font-weight, bold), +); + @include b('fullscreen-header'){ - height: getCssVar(spacing,extra,loose); + @include set-component-css-var('fullscreen-header', $fullscreen-header); + height: getCssVar(fullscreen-header, height); display: flex; justify-content: center; align-items: center; overflow: hidden; position: relative; + font-size: getCssVar(fullscreen-header, font-size); + font-weight: getCssVar(fullscreen-header, font-weight); @include e('close'){ position: absolute; right: getCssVar(spacing,extra,tight); - font-size: getCssVar(font-size,header-3); + font-size: getCssVar(font-size,header-1); height: 100%; display: flex; align-items: center; } - +} + + +@include b(full-screen) { + position: fixed; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + // 移动端安全距离 + padding: var(--safe-area-inset-top) var(--safe-area-inset-right) var(--safe-area-inset-bottom) var(--safe-area-inset-left); } \ No newline at end of file diff --git a/src/control/form/form-detail/form-tab-panel/form-tab-panel.tsx b/src/control/form/form-detail/form-tab-panel/form-tab-panel.tsx index 89b80479c803c7e188dad1b053a87d4446a5a497..460177e2433ae22e8b31f4e84608454bf21acbf0 100644 --- a/src/control/form/form-detail/form-tab-panel/form-tab-panel.tsx +++ b/src/control/form/form-detail/form-tab-panel/form-tab-panel.tsx @@ -2,7 +2,10 @@ import { defineComponent, PropType, VNode } from 'vue'; import { useController, useNamespace } from '@ibiz-template/vue3-util'; import './form-tab-panel.scss'; import { IDEFormTabPanel } from '@ibiz/model-core'; -import { FormTabPanelController } from '@ibiz-template/runtime'; +import { + FormTabPageController, + FormTabPanelController, +} from '@ibiz-template/runtime'; export const FormTabPanel = defineComponent({ name: 'IBizFormTabPanel', @@ -20,8 +23,27 @@ export const FormTabPanel = defineComponent({ const ns = useNamespace('form-tab-panel'); useController(props.controller); + const onTabClick = (args: { + name: string; + title: string; + event: MouseEvent; + disabled: boolean; + }) => { + const { name, event } = args; + props.controller.onTabChange(name); + + // 触发对应FormTabPage的点击事件 + const pageC = props.controller.form.details[ + name + ] as FormTabPageController; + if (pageC) { + pageC.onClick(event); + } + }; + return { ns, + onTabClick, }; }, render() { @@ -33,7 +55,8 @@ export const FormTabPanel = defineComponent({ this.ns.m(this.modelData.codeName), ...this.controller.containerClass, ]} - model-value={this.modelData.deformTabPages?.[0].id} + model-value={this.controller.state.activeTab} + onClickTab={this.onTabClick} > {defaultSlots.map(slot => { const props = slot.props as IData; diff --git a/src/ibiz-vue3.ts b/src/ibiz-vue3.ts index 59b1b1f35cd3d9c8c0cedd024037e8d18e818aee..e0434e5d06c00c47209a8449bd219aae62482a2c 100644 --- a/src/ibiz-vue3.ts +++ b/src/ibiz-vue3.ts @@ -36,6 +36,7 @@ import IBizPanelComponents from './panel-component'; import { VueBrowserPlatformProvider, DingTalkPlatformProvider, + IosPlatformProvider, } from './platform'; import { IBizPortalView } from './view/portal-view'; import { IBizViewEngine } from './view-engine'; @@ -48,6 +49,7 @@ export default { // vue 浏览器搭载平台 const browserPlatformProvider = new VueBrowserPlatformProvider(); const dingTalkPlatformProvider = new DingTalkPlatformProvider(); + const iosPlatformProvider = new IosPlatformProvider(); registerPlatformProvider( PlatformType.BROWSER, () => browserPlatformProvider, @@ -56,6 +58,7 @@ export default { PlatformType.DINGTALK, () => dingTalkPlatformProvider, ); + registerPlatformProvider(PlatformType.IOS, () => iosPlatformProvider); v.use(IBizCommonComponents); v.use(IBizViewEngine); diff --git a/src/mob-app/App.scss b/src/mob-app/App.scss index 4b8d52cb9c3390196c351bf8bd01e051dde57646..b22e8273aecca6246e71f157d7d21c87c51bd9f7 100644 --- a/src/mob-app/App.scss +++ b/src/mob-app/App.scss @@ -1,7 +1,12 @@ #app { + --safe-area-inset-top: env(safe-area-inset-top); + --safe-area-inset-right: env(safe-area-inset-right); + --safe-area-inset-bottom: env(safe-area-inset-bottom); + --safe-area-inset-left: env(safe-area-inset-left); + width: 100vw; height: 100vh; background: getCssVar(color, bg, 0); // 移动端安全距离 - padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); + padding: var(--safe-area-inset-top) var(--safe-area-inset-right) var(--safe-area-inset-bottom) var(--safe-area-inset-left); } \ No newline at end of file diff --git a/src/mob-app/App.tsx b/src/mob-app/App.tsx index 4da0d8a0a747baab423e0490c0ac9278d661bc14..bde985e13056773ba8eda519f6b46a4ca28a912c 100644 --- a/src/mob-app/App.tsx +++ b/src/mob-app/App.tsx @@ -12,6 +12,7 @@ export default defineComponent({ transitionName.value = type === 'push' ? 'forward' : 'back'; }; + ibiz.platform.init(); // 适配devtool const listenDevtool = async (e: KeyboardEvent): Promise => { if ((e.ctrlKey || e.metaKey) && e.code === 'F12') { @@ -33,6 +34,7 @@ export default defineComponent({ onUnmounted(() => { off('onBeforeStackChange', onViewStackChange); + ibiz.platform.onDestroyed(); }); const viewModals = new Map(); diff --git a/src/panel-component/panel-tab-panel/panel-tab-panel.controller.ts b/src/panel-component/panel-tab-panel/panel-tab-panel.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0b489d1594ee6bf9efd634f2d7a99bdd5788018 --- /dev/null +++ b/src/panel-component/panel-tab-panel/panel-tab-panel.controller.ts @@ -0,0 +1,42 @@ +import { PanelItemController } from '@ibiz-template/runtime'; +import { IPanelTabPanel } from '@ibiz/model-core'; +import { PanelTabPanelState } from './panel-tab-panel.state'; + +export class PanelTabPanelController extends PanelItemController { + declare state: PanelTabPanelState; + + /** + * 新建状态 + * + * @author tony001 + * @date 2024-05-12 14:05:16 + * @protected + * @return {*} {PanelTabPanelState} + */ + protected createState(): PanelTabPanelState { + return new PanelTabPanelState(this.parent?.state); + } + + /** + * 初始化 + * + * @author tony001 + * @date 2024-05-12 14:05:51 + * @return {*} {Promise} + */ + async onInit(): Promise { + await super.onInit(); + this.state.activeTab = this.model.panelTabPages?.[0].id || ''; + } + + /** + * 分页点击切换处理 + * + * @author tony001 + * @date 2024-05-12 14:05:11 + * @param {string} tabId + */ + onTabChange(tabId: string): void { + this.state.activeTab = tabId; + } +} diff --git a/src/panel-component/panel-tab-panel/panel-tab-panel.provider.ts b/src/panel-component/panel-tab-panel/panel-tab-panel.provider.ts index 0936663d6b7f2f82d20a06178081340d1f491063..c193bb5c1d984620a56921ffa3631deabd3699ad 100644 --- a/src/panel-component/panel-tab-panel/panel-tab-panel.provider.ts +++ b/src/panel-component/panel-tab-panel/panel-tab-panel.provider.ts @@ -4,6 +4,7 @@ import { PanelItemController, } from '@ibiz-template/runtime'; import { IPanelItem } from '@ibiz/model-core'; +import { PanelTabPanelController } from './panel-tab-panel.controller'; /** * 面板分页面板适配器 @@ -19,8 +20,8 @@ export class PanelTabPanelProvider implements IPanelItemProvider { panelItem: IPanelItem, panel: PanelController, parent: PanelItemController | undefined, - ): Promise { - const c = new PanelItemController(panelItem, panel, parent); + ): Promise { + const c = new PanelTabPanelController(panelItem, panel, parent); await c.init(); return c; } diff --git a/src/panel-component/panel-tab-panel/panel-tab-panel.state.ts b/src/panel-component/panel-tab-panel/panel-tab-panel.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..f96a6d732882fbe4e0d50eea2693b41e53e50e8a --- /dev/null +++ b/src/panel-component/panel-tab-panel/panel-tab-panel.state.ts @@ -0,0 +1,21 @@ +import { PanelItemState } from '@ibiz-template/runtime'; + +/** + * 分页面板状态 + * + * @author tony001 + * @date 2024-05-12 14:05:01 + * @export + * @class PanelTabPanelState + * @extends {PanelItemState} + */ +export class PanelTabPanelState extends PanelItemState { + /** + * 当前激活分页 + * + * @author tony001 + * @date 2024-05-12 14:05:36 + * @type {string} + */ + activeTab: string = ''; +} diff --git a/src/panel-component/panel-tab-panel/panel-tab-panel.tsx b/src/panel-component/panel-tab-panel/panel-tab-panel.tsx index b0feed487f6462e3e89c864304e915132ef96fa3..46d389e46f61f573ff6789d90e2aa0cd8469ccd7 100644 --- a/src/panel-component/panel-tab-panel/panel-tab-panel.tsx +++ b/src/panel-component/panel-tab-panel/panel-tab-panel.tsx @@ -1,7 +1,7 @@ import { useNamespace } from '@ibiz-template/vue3-util'; import { IPanelTabPanel } from '@ibiz/model-core'; import { computed, defineComponent, PropType, VNode } from 'vue'; -import { PanelItemController } from '@ibiz-template/runtime'; +import { PanelTabPanelController } from './panel-tab-panel.controller'; import './panel-tab-panel.scss'; export const PanelTabPanel = defineComponent({ @@ -12,7 +12,7 @@ export const PanelTabPanel = defineComponent({ required: true, }, controller: { - type: PanelItemController, + type: PanelTabPanelController, required: true, }, }, @@ -27,9 +27,20 @@ export const PanelTabPanel = defineComponent({ return result; }); + const onTabClick = (args: { + name: string; + title: string; + event: MouseEvent; + disabled: boolean; + }) => { + const { name } = args; + props.controller.onTabChange(name); + }; + return { ns, classArr, + onTabClick, }; }, render() { @@ -47,7 +58,8 @@ export const PanelTabPanel = defineComponent({ ...this.controller.containerClass, ]} lazy-render - model-value={this.modelData.panelTabPages?.[0].id} + model-value={this.controller.state.activeTab} + onClickTab={this.onTabClick} > {defaultSlots!.map(slot => { const props = slot.props as IData; diff --git a/src/platform/index.ts b/src/platform/index.ts index cea95bb884e09eded8ec20c3010797fbef875667..ab3701ae38ef7c35cbe3c87732a46a2fda61c440 100644 --- a/src/platform/index.ts +++ b/src/platform/index.ts @@ -1,2 +1,3 @@ export { VueBrowserPlatformProvider } from './vue-browser-platform-provider'; export { DingTalkPlatformProvider } from './ding-talk-platform-provider'; +export { IosPlatformProvider } from './ios-platform-provider'; diff --git a/src/platform/ios-platform-provider.ts b/src/platform/ios-platform-provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..34ce8152b0205e0dd488c6f1ad3cb942d2987394 --- /dev/null +++ b/src/platform/ios-platform-provider.ts @@ -0,0 +1,45 @@ +import { listenJSEvent, NOOP } from '@ibiz-template/core'; +import { PlatformProviderBase } from '@ibiz-template/runtime'; + +/** + * @description ios搭载平台 + * @export + * @class IosPlatformProvider + * @extends {PlatformProviderBase} + */ +export class IosPlatformProvider extends PlatformProviderBase { + protected clean = NOOP; + + /** + * @description初始化 + * @return {*} {Promise} + * @memberof IosPlatformProvider + */ + async init(): Promise { + this.clean = listenJSEvent(window, 'load', () => { + const windowHeight = window.innerHeight; + const documentHeight = document.documentElement.scrollHeight; + if (documentHeight > windowHeight) { + // 存在安全距离 + const safeAreaBottom = documentHeight - windowHeight; + const root: HTMLElement | null = document.querySelector('#app'); + if (root) { + root.style.setProperty( + '--safe-area-inset-bottom', + `${safeAreaBottom}px`, + ); + } + } + }); + } + + /** + * @description 应用销毁 + * @memberof IosPlatformProvider + */ + async destroyed(): Promise { + if (this.clean !== NOOP) { + this.clean(); + } + } +} diff --git a/src/util/fullscreen/fullscreen-util.ts b/src/util/fullscreen/fullscreen-util.ts index ebee99568546b5c02e716eafbcbb63675171bd40..bb7ab715b04f037956bba71419973858ee21cb84 100644 --- a/src/util/fullscreen/fullscreen-util.ts +++ b/src/util/fullscreen/fullscreen-util.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { createApp } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; import { IBizFullscreenHeader } from '../../common/fullscreen-header/fullscreen-header'; /** @@ -17,6 +18,13 @@ export class FullscreenUtil { */ constructor() {} + /** + * @description 全屏dom + * @type {(HTMLElement | null)} + * @memberof FullscreenUtil + */ + fullscreenElement: HTMLElement | null = null; + /** *是否全屏状态 * @@ -24,7 +32,7 @@ export class FullscreenUtil { * @memberof FullscreenUtil */ get isFullScreen() { - return !!document.fullscreenElement; + return !!this.fullscreenElement; } /** @@ -32,7 +40,7 @@ export class FullscreenUtil { * @author fzh * @date 2024-07-15 19:39:40 */ - public FullscreenClass: string = 'full-screen-class'; + public FullscreenClass: string = 'full-screen'; /** * 指定元素全屏 @@ -43,23 +51,21 @@ export class FullscreenUtil { if (data?.class) { this.FullscreenClass = data.class; } - if (!document.fullscreenElement && div) { + if (!this.isFullScreen && div) { + const ns = useNamespace(this.FullscreenClass); if (this.FullscreenClass) { - div.classList.add(this.FullscreenClass); + div.classList.add(ns.b()); } - // 通过传递参数决定是否绘制标题和关闭按钮 - if (data?.showClose || data?.srftitle) { - const content = document.createElement('div'); - content.id = 'fullscreen'; - const app = createApp(IBizFullscreenHeader, { - title: data?.srftitle, - }); - app.mount(content); - // 直接操作传进来的元素 - div.insertBefore(content, div.children[0]); - } - div.requestFullscreen(); + const content = document.createElement('div'); + content.classList.add(ns.e('header')); + const app = createApp(IBizFullscreenHeader, { + title: data?.srftitle, + }); + app.mount(content); + // 直接操作传进来的元素 + div.insertBefore(content, div.children[0]); + this.fullscreenElement = div; } } @@ -70,11 +76,15 @@ export class FullscreenUtil { */ public closeElementFullscreen(): void { // 退出前移除全屏元素的全屏样式 - document.fullscreenElement?.classList.remove(this.FullscreenClass); - const close = document.fullscreenElement?.querySelector('#fullscreen'); - if (close) { - close.remove(); + if (!this.fullscreenElement) { + return; + } + const ns = useNamespace(this.FullscreenClass); + this.fullscreenElement.classList.remove(ns.b()); + const header = this.fullscreenElement.querySelector(`.${ns.e('header')}`); + if (header) { + header.remove(); } - document.exitFullscreen(); + this.fullscreenElement = null; } }