提交 06228319 编写于 作者: zhujiamin's avatar zhujiamin

update: 支持飘窗

上级 5145e9f0
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
}, },
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^6.0.1", "@ant-design/icons-vue": "^6.0.1",
"@popperjs/core": "^2.9.2",
"ant-design-vue": "^3.0.0-alpha.14", "ant-design-vue": "^3.0.0-alpha.14",
"axios": "^0.24.0", "axios": "^0.24.0",
"element-plus": "^1.2.0-beta.6", "element-plus": "^1.2.0-beta.6",
......
.app-popover-wrapper {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
.app-popover {
border-radius: 5px;
overflow: auto;
background: #e8e8e8;
}
}
\ No newline at end of file
import { Subject } from 'rxjs';
import Router from '@/router';
import Antd from 'ant-design-vue';
import AppPopoverComponent from "./app-popover.vue";
import IbizLoading from '@components/render/IbizLoading.vue';
import { IParam, ViewDetail } from '@core';
import './app-popover.scss';
import { createPopper, Instance } from '@popperjs/core/lib/popper-lite.js';
import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow.js';
import flip from '@popperjs/core/lib/modifiers/flip.js';
import { Placement } from '@popperjs/core/lib/enums';
export class AppPopover {
/**
* 实例对象
*
* @private
* @static
* @memberof AppPopover
*/
private static popover = new AppPopover();
/**
* PopperJs实例
*
* @private
* @type {Instance}
* @memberof AppPopover
*/
private popperExample?: Instance;
/**
* 是否显示悬浮窗
*
* @private
* @type {boolean}
* @memberof AppPopover
*/
private showPopper: boolean = false;
/**
* 是否在点击空白区域时自动关闭
*
* @private
* @type {boolean}
* @memberof AppPopover
*/
private isAutoClose: boolean = true;
/**
* 是否在离开该飘窗区域时自动关闭
*
* @private
* @type {boolean}
* @memberof AppPopover
*/
private isMoveOutClose: boolean = false;
/**
* 构造方法
*
* @memberof AppPopover
*/
constructor() {
if (AppPopover.popover) {
return AppPopover.popover;
}
}
/**
* 获取实例对象
*
* @static
* @returns
* @memberof AppPopover
*/
public static getInstance() {
if (!AppPopover.popover) {
AppPopover.popover = new AppPopover();
}
return AppPopover.popover;
}
/**
* 创建 Vue 实例对象
*
* @private
* @param event
* @param {view} ViewDetail 视图对象
* @param {IParam} params 视图参数
* @param {IParam} [options] 模态配置项
* @return {*} {Subject<any>}
* @memberof AppPopover
*/
private createVueExample(event:any, view: ViewDetail, params: IParam, options?: IParam): Subject<any> {
try {
// 阻止事件冒泡
event.stopPropagation();
// 处理关闭模式(点击空白区域关闭、鼠标离开飘窗区域)
if (params.isAutoClose) {
this.isAutoClose = params.isAutoClose;
}
if (params.isMoveOutClose) {
this.isMoveOutClose = params.isMoveOutClose;
}
// 获取悬浮窗相对位置的元素
const element: Element = event.toElement || event.srcElement;
let subject: null | Subject<any> = new Subject<any>();
let props = { element: element, view: view, context: params.context, viewParams: params.viewParams, isFullscreen: params.isFullscreen, subject: subject, options: options };
// 解析文件路径
let dir = view.fileDir?.replace(/@page/, '');
// Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块
const modules = import.meta.glob('../../page/*/*/index.ts');
// 创建一个只有在需要时才会加载的异步组件
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: modules['../../page' + dir + '/index.ts'],
// 加载异步组件时要使用的组件
loadingComponent: IbizLoading,
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 0,
});
if (AsyncComp) {
const component = AppPopoverComponent;
// 创建dom
const container = document.createElement('div');
container.className = 'app-popover-wrapper';
const div = document.createElement('div');
container.appendChild(div);
document.body.appendChild(container);
this.showPopper = true;
const app = createApp(component,
{
mouseout: () => {
if (!this.showPopper || !this.isMoveOutClose) {
return;
}
this.popperDestroy();
document.body.removeChild(container);
app.unmount();
},
...props
}
);
app.component(view.name as string, AsyncComp);
app.use(Router).use(Antd).mount(div);
// 创建Popover实例
this.popperExample = createPopper(element, div, {
placement: view.placement as Placement,
strategy: 'absolute',
modifiers: [preventOverflow, flip]
});
// 点击空白区域时事件处理
container.addEventListener('click', () => {
if (!this.showPopper || !this.isAutoClose) {
return;
}
this.popperDestroy();
document.body.removeChild(container);
app.unmount();
})
}
return subject;
} catch (error) {
console.error(error);
return new Subject<any>();
}
}
/**
* 打开飘窗
*
* @param {*} event
* @param {View} view 视图对象
* @param {IParam} params 视图参数
* @param {IParam} [options] 模态配置项
* @returns {Subject<any>}
* @memberof AppPopover
*/
public openPopover(event:any, view: ViewDetail, params: IParam, options?: IParam): Subject<any> {
try {
const subject = this.createVueExample(event, view, params, options);
return subject;
} catch (error) {
console.log(error);
return new Subject<any>();
}
}
/**
* 销毁popper(带回填数据)
*
* @memberof AppPopover
*/
public popperDestroy(): void {
if (this.popperExample) {
this.popperExample.destroy();
this.showPopper = false;
}
}
}
\ No newline at end of file
<script setup lang="ts">
import { IActionParam, IParam, ViewDetail } from '@core';
import { Subject } from 'rxjs';
import { Ref, ref, h } from 'vue';
interface AppPopoverProps {
/**
* @description 视图
*/
view: ViewDetail;
/**
* @description 视图上下文参数
*/
context?: any;
/**
* @description 视图参数
*/
viewParams?: any;
/**
* @description 数据传递对象
*/
subject?: Subject<any>;
/**
* @description 是否全屏
*/
isFullscreen:boolean;
/**
* @description 模态参数
*/
option?: IParam;
/**
* @description 鼠标移出回调
*/
mouseout: Function;
}
const props = withDefaults(defineProps<AppPopoverProps>(), {
context: {},
viewParams: {},
});
/**
* 临时结果
*/
let tempResult = { ret: '' };
/**
* 视图名称
*/
let viewName: Ref<string> = ref('');
/**
* 抽屉的方向
*/
let placement: Ref<string> = ref('bottom');
/**
* 视图标题
*/
let title: string = '';
/**
* 视图宽度
*/
let width: Ref<number> = ref(0);
/**
* 视图高度
*/
let height: Ref<number> = ref(0);
/**
* 视图样式
*/
let style: any = {};
/**
* 暴露subject
*/
const getSubject = () => {
return props.subject;
};
/**
* Vue生命周期beforeMount
*/
onBeforeMount(() => {
if (props.view) {
viewName.value = props.view.name as string;
title = props.view.title || '';
placement.value = props.view.placement || 'bottom';
if (props.isFullscreen) {
Object.assign(style, { height: 'auto' });
} else {
if (!props.view.width || props.view.width === 0 || Object.is(props.view.width, '0px')) {
width.value = 300;
Object.assign(style, { width: width.value + 'px' });
} else {
width.value = props.view.width;
Object.assign(style, { width: width.value + 'px' });
}
if (props.view.height && !Object.is(props.view.height, '0px')) {
height.value = props.view.height;
Object.assign(style, { height: height.value + 'px' });
} else {
height.value = 300;
Object.assign(style, { height: height.value + 'px' });
}
}
}
});
/**
* 视图关闭
*/
const close = (result: any) => {
if (result && Array.isArray(result) && result.length > 0) {
Object.assign(tempResult, { ret: 'OK' }, { datas: JSON.parse(JSON.stringify(result)) });
}
props.subject?.next(tempResult);
};
/**
* 视图数据变化
*/
const viewDataChange = (result: any) => {
tempResult = { ret: '' };
if (result && Array.isArray(result) && result.length > 0) {
Object.assign(tempResult, { ret: 'OK' }, { datas: JSON.parse(JSON.stringify(result)) });
}
};
/**
* 视图数据激活
*/
const viewDataActivated = (result: any) => {
if (result && Array.isArray(result) && result.length > 0) {
close(result);
}
};
/**
* 显示隐藏切换回调
*/
const onVisibleChange = ($event: any) => {
handleShowState($event);
};
/**
* 处理数据,向外抛值
*/
const handleShowState = ($event: any) => {
if (props.subject && tempResult) {
props.subject.next(tempResult);
}
};
/**
* 处理视图事件
*/
const handelViewEvent = (actionParam: IActionParam) => {
const { tag, action, data } = actionParam;
switch (action) {
case 'viewDataChange':
viewDataChange(data);
break;
case 'close':
close(data);
break;
}
};
/**
* 点击事件
*/
const click = (e:any) => {
e.stopPropagation()
}
/**
* 鼠标移出事件
*/
const mouseout = (e:any) => {
props.mouseout();
}
</script>
<template>
<div :style="style" class="app-popover" @click="click" @mouseleave="mouseout">
<component
:is="viewName"
class="app-popover-component"
:width="width"
:height="height"
:context="context"
:viewParams="viewParams"
:viewDefaultUsage="false"
:noViewCaption="true"
@viewEvent="handelViewEvent($event)"
@viewDataActivated="viewDataActivated($event)"
@close="close($event)"
></component>
</div>
</template>
\ No newline at end of file
export { AppModal } from './app-modal/app-modal'; export { AppModal } from './app-modal/app-modal';
export { AppDrawer } from './app-drawer/app-drawer'; export { AppDrawer } from './app-drawer/app-drawer';
export { AppPopover } from './app-popover/app-popover';
export { OpenViewService } from './open-view/open-view-service'; export { OpenViewService } from './open-view/open-view-service';
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册