提交 1ffa96ef 编写于 作者: tony001's avatar tony001

update:更新

上级 1cb59d67
import { SyncSeriesHook } from "qx-util";
import { Environment } from "@/environments/environment";
import router from "@/router";
import { AppBase, IParam, ViewDetail, IApp, IOpenViewService, deepCopy, Http, IAppAuthService, IAppNotificationService, IAppFuncService, IAppActionService } from "@core";
import { AppBase, IParam, ViewDetail, IApp, IOpenViewService, deepCopy, Http, IAppAuthService, IAppNotificationService, IAppFuncService, IAppActionService, IAppCodeListService } from "@core";
import { AppFuncConfig, AppViewConfig, AppEntityConfig } from './config';
import { DataServiceRegister, UIServiceRegister } from "./register";
import { AppActionService, AppAuthService, AppFuncService, AppNotificationService, OpenViewService } from "@/service";
import { AppActionService, AppAuthService, AppCodeListService, AppFuncService, AppNotificationService, OpenViewService } from "@/service";
export class App extends AppBase implements IApp {
......@@ -120,6 +120,16 @@ export class App extends AppBase implements IApp {
return AppActionService.getInstance();
}
/**
* 获取代码表服务
*
* @return {*} {IAppCodeListService}
* @memberof App
*/
public getCodeListService(): IAppCodeListService {
return AppCodeListService.getInstance();
}
/**
* 获取UI服务
*
......
......@@ -126,7 +126,7 @@ const props = withDefaults(defineProps<CheckboxListProps>(), {
mode: 'string',
});
const emit = defineEmits<EditorEmit>();
const { handleEditorNavParams, handleLevelCodeList, loadCodeListData } = new EditorBase();
const { handleEditorNavParams, loadCodeListData } = new EditorBase();
const { navContext, navViewParam } = handleEditorNavParams(props);
let items: Ref<IParam[]> = ref([]);
const selectArray: Ref<boolean> = computed(() => {
......@@ -205,8 +205,8 @@ const formatCodeList = (items: Array<any>) => {
}
};
onBeforeMount(() => {
loadCodeListData(props.codeListTag, props.codeListType, navContext, navViewParam).then((codeListData: IParam[]) => {
items.value = formatCodeList(handleLevelCodeList(codeListData));
loadCodeListData(props.codeListTag, navContext, navViewParam).then((codeListData: IParam[]) => {
items.value = formatCodeList(codeListData);
});
});
</script>
......
......@@ -56,7 +56,7 @@ interface DropdownListProps {
/**
* @description 代码表标识
*/
codeListTag?: string;
codeListTag: string;
/**
* @description 代码表类型
......@@ -79,7 +79,7 @@ const props = withDefaults(defineProps<DropdownListProps>(), {
})
const emit = defineEmits<EditorEmit>()
const { handleEditorNavParams, loadCodeListData, handleLevelCodeList } = new EditorBase();
const { handleEditorNavParams, loadCodeListData } = new EditorBase();
const { navContext, navViewParam } = handleEditorNavParams(props);
let curValue: Ref<any> = ref(
props.multiple ?
......@@ -94,11 +94,10 @@ const onChange = (select: any, option: any) => {
onBeforeMount(() => {
loadCodeListData(
props.codeListTag,
props.codeListType,
navContext,
navViewParam,
).then((codeListData: IParam[]) => {
items.value = handleLevelCodeList(codeListData);
items.value = codeListData;
})
})
......
......@@ -56,7 +56,7 @@ interface ListBoxProps {
* @type {string}
* @memberof ListBoxProps
*/
codeListTag?: string;
codeListTag: string;
/**
* 代码表类型
......@@ -128,7 +128,7 @@ interface ListBoxProps {
* @type {any}
* @memberof ListBoxProps
*/
multiple?: Boolean;
multiple?: boolean;
/**
* 应用实体主信息属性名称
......@@ -179,7 +179,7 @@ const props = withDefaults(defineProps<ListBoxProps>(), {
deKeyField: 'srfkey',
});
const emit = defineEmits<EditorEmit>();
const { handleEditorNavParams, handleLevelCodeList, loadCodeListData } = new EditorBase();
const { handleEditorNavParams, loadCodeListData } = new EditorBase();
const { navContext, navViewParam } = handleEditorNavParams(props);
let items: Ref<IParam[]> = ref([]);
const selectArray: Ref<boolean> = computed(() => {
......@@ -205,13 +205,13 @@ const selectArray: Ref<boolean> = computed(() => {
}
});
const curMajorField: Ref<string> = computed(() => {
return Object.is(props.editorType, 'LISTBOX') ? 'text' : props.deMajorField;
});
// const curMajorField: Ref<string> = computed(() => {
// return Object.is(props.editorType, 'LISTBOX') ? 'text' : props.deMajorField;
// });
const curKeyField: Ref<string> = computed(() => {
return Object.is(props.editorType, 'LISTBOX') ? 'value' : props.deKeyField;
});
// const curKeyField: Ref<string> = computed(() => {
// return Object.is(props.editorType, 'LISTBOX') ? 'value' : props.deKeyField;
// });
const onChange = ($event: any[]) => {
let value: null | string | number = null;
......@@ -291,8 +291,8 @@ const loadData = () => {
};
onBeforeMount(() => {
if (Object.is('LISTBOX', props.editorType)) {
loadCodeListData(props.codeListTag, props.codeListType, navContext, navViewParam).then((codeListData: IParam[]) => {
items.value = formatCodeList(handleLevelCodeList(codeListData));
loadCodeListData(props.codeListTag, navContext, navViewParam).then((codeListData: IParam[]) => {
items.value = formatCodeList(codeListData);
});
} else if (Object.is('LISTBOXPICKUP', props.editorType)) {
loadData();
......
<script setup lang="ts">
import {
IActionParam,
IParam,
EditorBase,
IContext
} from "@core";
import { onBeforeMount, ref, Ref } from "vue";
import { IActionParam, IParam, EditorBase, IContext } from '@core';
import { onBeforeMount, ref, Ref } from 'vue';
interface RadioGroupProps {
/**
* 值
......@@ -96,7 +91,7 @@ interface RadioGroupProps {
}
interface EditorEmit {
(name: "editorEvent", value: IActionParam): void;
(name: 'editorEvent', value: IActionParam): void;
}
const props = withDefaults(defineProps<RadioGroupProps>(), {
......@@ -104,39 +99,28 @@ const props = withDefaults(defineProps<RadioGroupProps>(), {
readonly: false,
});
const emit = defineEmits<EditorEmit>();
const { handleEditorNavParams, loadCodeListData, handleLevelCodeList } = new EditorBase();
const { handleEditorNavParams, loadCodeListData } = new EditorBase();
const { navContext, navViewParam } = handleEditorNavParams(props);
let items: Ref<IParam[]> = ref([]);
const onChange = ($event: any) => {
const value = $event.target.value
emit("editorEvent", {
const value = $event.target.value;
emit('editorEvent', {
tag: props.name,
action: "valueChange",
action: 'valueChange',
data: value,
});
};
onBeforeMount(() => {
loadCodeListData(
props.codeListTag,
props.codeListType,
navContext,
navViewParam
).then((codeListData: IParam[]) => {
items.value = handleLevelCodeList(codeListData);
loadCodeListData(props.codeListTag, navContext, navViewParam).then((codeListData: IParam[]) => {
items.value = codeListData;
});
});
</script>
<template>
<div :class="['app-editor-container', 'app-radio-group', `app-radio-group-${name}`]">
<a-radio-group
@change="onChange"
:value="value"
:options="items"
:disabled="disabled || readonly"
>
</a-radio-group>
<a-radio-group @change="onChange" :value="value" :options="items" :disabled="disabled || readonly"></a-radio-group>
</div>
</template>
......
......@@ -100,8 +100,7 @@ let textFormat: Ref<string> = ref("");
onBeforeMount(() => {
if (props.codeListTag) {
loadCodeListData(
props.codeListTag,
props.codeListType,
props.codeListTag as string,
navContext,
navViewParam
).then((codeListData: IParam[]) => {
......
import { IParam, ViewDetail } from "../common";
import { IAppActionService, IAppAuthService, IAppFuncService, IAppNotificationService, IOpenViewService } from "../service";
import { IAppActionService, IAppAuthService, IAppCodeListService, IAppFuncService, IAppNotificationService, IOpenViewService } from "../service";
/**
......@@ -59,6 +59,14 @@ export interface IApp {
*/
getAppActionService(): IAppActionService;
/**
* 获取代码表服务
*
* @return {*} {IAppCodeListService}
* @memberof IApp
*/
getCodeListService(): IAppCodeListService;
/**
* 获取UI服务
*
......
import { IContext, IParam } from "../common";
/**
* @description 应用代码表服务
* @export
* @interface IAppCodeListService
*/
export interface IAppCodeListService {
/**
* 获取代码项
*
* @param { tag, context, viewParams }: { tag: string, context: IContext | undefined, viewParams: IParam | undefined }
* @return {*} {Promise<any>}
* @memberof IAppCodeListService
*/
getCodeListItems({ tag, context, viewParams }: { tag: string, context: IContext | undefined, viewParams: IParam | undefined }): Promise<any>;
}
\ No newline at end of file
......@@ -5,3 +5,4 @@ export * from './i-open-view-service';
export * from './i-data-service-register';
export * from './i-ui-service-register';
export * from './i-app-auth-service';
export * from './i-app-code-list-service';
\ No newline at end of file
import { IApp, IAppFuncService, IOpenViewService, ViewDetail } from "@core";
import { IAppActionService, IAppAuthService, IAppNotificationService, IParam } from "@core/interface";
import { IAppActionService, IAppAuthService, IAppCodeListService, IAppNotificationService, IParam } from "@core/interface";
/**
* 应用基类
......@@ -89,6 +89,16 @@ export abstract class AppBase implements IApp {
throw new Error("Method not implemented.");
}
/**
* 获取代码表服务
*
* @return {*} {IAppCodeListService}
* @memberof AppBase
*/
public getCodeListService(): IAppCodeListService {
throw new Error("Method not implemented.");
}
/**
* 获取UI服务
*
......
import { toRefs } from 'vue';
import { UIUtil, IParam, UIBase } from '@core';
import { UIUtil, IParam, UIBase, IContext } from '@core';
import axios from 'axios';
/**
* @description 部件基类
......@@ -35,83 +35,20 @@ export class EditorBase {
/**
* @description 加载代码表数据
* @export
* @param {string} codeListTag 代码表标识
* @param {string} codeListType 代码表类型
* @param {IParam} navContext 导航上下文
* @param {IParam} navViewParam 导航视图参数
* @param {boolean} isNumber 是否为数值类型
* @param {string} tag 代码表标识
* @param {IContext} context 导航上下文
* @param {IParam} viewParams 导航视图参数
* @return {Promise<any[]>} {any[]}
*/
public async loadCodeListData(codeListTag?: string, codeListType?: string, navContext?: IParam, navViewParam?: IParam): Promise<IParam[]> {
const data: IParam[] = [
{
value: '243144',
label: 'Jack',
},
{
value: '7dawda',
label: 'Lucy',
},
{
value: 'disaweqeqeqbled',
label: 'Disabled',
disabled: true,
},
{
value: 'yimirwqrqwenghe',
label: 'Yiminghe',
}
];
if (codeListTag) {
// todo 调用代码表服务获取数据
}
return data;
}
/**
* @description 处理层级代码表
* @export
* @param {IParam[]} codeListData 代码表数据
*/
public handleLevelCodeList(codeListData: IParam[]): IParam[] {
let items: IParam[] = [...codeListData]
const hasChildren: boolean = codeListData.some((item:any) =>{
return item.pvalue;
})
if(hasChildren){
let list: IParam[] = [];
items.forEach((codeItem: IParam) =>{
if(!codeItem.pvalue){
let valueField: string = codeItem.value;
this.setChildCodeItems(valueField, items, codeItem);
list.push(codeItem);
}
})
items = list;
public async loadCodeListData(tag: string, context?: IContext, viewParams?: IParam) {
if (!tag) {
console.log("无代码表标识,无法加载数据");
return;
}
const items = await App.getCodeListService().getCodeListItems({ tag, context, viewParams });
return items;
}
/**
* @description 设置子代码表
* @param {string} pValue 父代码项值
* @param {IParam[]} result 代码表
* @param {IParam} codeItem 代码项
*/
public setChildCodeItems(pValue: string, result: IParam[], codeItem: IParam) {
result.forEach((item: IParam) =>{
if(item.pvalue == pValue){
let valueField:string = item.value;
this.setChildCodeItems(valueField, result, item);
if(!codeItem.options){
codeItem.children = [];
}
codeItem.children.push(item);
}
})
}
/**
* @description 打开选择视图
* @export
......@@ -237,7 +174,7 @@ export class EditorBase {
resolve(img);
}
}
let blob = new Blob([response.data],{type: type});
let blob = new Blob([response.data], { type: type });
this.blobToBase64(blob).then((res: any) => {
// 转化后的base64
img = `${res}`;
......@@ -257,10 +194,10 @@ export class EditorBase {
callback(url, loadingImgMap.get(url));
} else {
let _url = url;
if (!Object.is('/', _url.substring(0,1))) {
_url = '/'+_url;
if (!Object.is('/', _url.substring(0, 1))) {
_url = '/' + _url;
}
let result:Promise<any> = axios({method: 'get', url: _url, responseType: 'blob'});
let result: Promise<any> = axios({ method: 'get', url: _url, responseType: 'blob' });
loadingImgMap.set(url, result);
callback(url, result);
}
......
import { IAppCodeListService, IContext, IParam } from "@core";
/**
* 应用代码表服务基类
*
* @export
* @abstract
* @class AppCodeListServiceBase
* @implements {IAppCodeListService}
*/
export abstract class AppCodeListServiceBase implements IAppCodeListService {
/**
* 获取代码表项
*
* @param { tag, context, viewParams }: { tag: string, context: IContext | undefined, viewParams: IParam | undefined }
* @return {*} {Promise<any>}
* @memberof AppCodeListServiceBase
*/
public async getCodeListItems({ tag, context, viewParams }: { tag: string, context: IContext | undefined, viewParams: IParam | undefined }): Promise<any> {
throw new Error("Method not implemented.");
}
}
\ No newline at end of file
export { AppActionServiceBase } from './app-action-service-base';
export { AppCodeListServiceBase } from './app-code-list-service-base';
export { AppAuthServiceBase } from './app-auth-service-base';
export { AppFuncServiceBase } from './app-func-service-base';
export { AppNotificationServiceBase } from './app-notification-service-base';
......
import { AppCodeListConfig } from "@/app/config";
import { AppCodeListServiceBase, IAppCodeListService, IContext, IParam } from "@core";
/**
* 应用代码表服务基类
*
* @export
* @abstract
* @class AppCodeListService
* @extends AppCodeListServiceBase
* @implements {IAppCodeListService}
*/
export class AppCodeListService extends AppCodeListServiceBase implements IAppCodeListService {
/**
* 唯一实例
*
* @private
* @static
* @memberof AppCodeListService
*/
private static readonly instance = new AppCodeListService();
/**
* 获取唯一实例
*
* @static
* @return {*} {AppCodeListService}
* @memberof AppCodeListService
*/
public static getInstance(): AppCodeListService {
return AppCodeListService.instance;
}
/**
* 获取代码表项
*
* @param { tag, context, viewParams }: { tag: string, context: IContext | undefined, viewParams: IParam | undefined }
* @return {*} {Promise<any>}
* @memberof AppCodeListService
*/
public async getCodeListItems({ tag, context, viewParams }: { tag: string, context: IContext | undefined, viewParams: IParam | undefined }): Promise<any> {
const codeList = AppCodeListConfig[tag];
if (!codeList) return;
let items: any;
if (Object.is(codeList.codeListType, 'STATIC')) {
items = await this.getItemsWithStatic(codeList);
} else {
if (codeList.predefinedType) {
items = await this.getItemsWithPredefined(codeList);
} else {
items = await this.getItemsWithDynamic(codeList);
}
}
// 处理成UI相关结构
this.handleItemData(items);
return items;
}
/**
* 获取静态代码表
*
* @param {IParam} codeList
* @return {*}
* @memberof AppCodeListService
*/
public async getItemsWithStatic(codeList: IParam): Promise<IParam[]> {
const { items } = codeList;
if (items && items.length > 0) {
return items;
} else {
return [];
}
}
/**
* 获取动态代码表(非预置)
*
* @param {IParam} codeList
* @return {*}
* @memberof AppCodeListService
*/
public async getItemsWithDynamic(codeList: IParam): Promise<IParam[]> {
return [];
}
/**
* 获取预定义代码表
*
* @param {IParam} codeList
* @return {*}
* @memberof AppCodeListService
*/
public async getItemsWithPredefined(codeList: IParam): Promise<IParam[]> {
return [];
}
/**
* 处理成UI相关结构
* 1.将{value: 'val',text: 'label'}处理成{value: 'val',label: 'label'}
* 2.处理代码表层级问题
*
* @param {IParam[]} items
* @memberof AppCodeListService
*/
public handleItemData(items: IParam[]) {
// 将{value: 'val',text: 'label'}处理成{value: 'val',label: 'label'}
if (items && (items.length > 0)) {
items.forEach((element: IParam) => {
Object.assign(element, { label: element.text });
});
}
// 处理代码表层级问题
const hasChildren: boolean = items.some((item: any) => {
return item.pvalue;
})
if (hasChildren) {
let list: IParam[] = [];
items.forEach((codeItem: IParam) => {
if (!codeItem.pvalue) {
this.setChildCodeItems(codeItem.value, items, codeItem);
list.push(codeItem);
}
})
items = list;
}
}
/**
* 设置子项数据
*
* @param {string} pValue 父代码项值
* @param {IParam[]} items 代码表集合
* @param {IParam} codeItem 代码项
* @memberof AppCodeListService
*/
public setChildCodeItems(pValue: string, items: IParam[], codeItem: IParam) {
items.forEach((item: IParam) => {
if (item.pvalue == pValue) {
this.setChildCodeItems(item.value, items, item);
if (!codeItem.children) {
codeItem.children = [];
}
codeItem.children.push(item);
}
})
}
}
\ No newline at end of file
export { AppCodeListService } from './app-code-list-service';
\ No newline at end of file
export * from './app-open-view-service';
export * from './app-code-list-service';
export { AppActionService } from './app-action-service';
export { AppAuthService } from './app-auth-service';
export { AppFuncService } from './app-func-service';
......
import { Subject } from 'rxjs';
import Router from '@/router';
import Antd from 'ant-design-vue';
import AppDrawerComponent from "./app-drawer.vue";
import AppLoading from '@components/common/app-loading.vue';
import { IParam, ViewDetail } from '@core';
export class AppDrawer {
/**
* 实例对象
*
* @private
* @static
* @memberof AppDrawer
*/
private static drawer = new AppDrawer();
/**
* 构造方法
*
* @memberof AppDrawer
*/
constructor() {
if (AppDrawer.drawer) {
return AppDrawer.drawer;
}
}
/**
* 获取实例对象
*
* @static
* @returns
* @memberof AppDrawer
*/
public static getInstance() {
if (!AppDrawer.drawer) {
AppDrawer.drawer = new AppDrawer();
}
return AppDrawer.drawer;
}
/**
* 创建 Vue 实例对象
*
* @private
* @param {view} ViewDetail 视图对象
* @param {IParam} params 视图参数
* @param {IParam} [options] 模态配置项
* @return {*} {Subject<any>}
* @memberof AppDrawer
*/
private createVueExample(view: ViewDetail, params: IParam, options?: IParam): Subject<any> {
try {
let subject: null | Subject<any> = new Subject<any>();
let props = { view: view, context: params.context, viewParams: params.viewParams, isFullscreen: params.isFullscreen, subject: subject, options: options };
let dir = view.fileDir?.replace(/@views/, '');
//Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块
const modules = import.meta.glob('../../page/*/*/index.ts');
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: modules['../../page' + dir + '/index.ts'],
// 加载异步组件时要使用的组件
loadingComponent: AppLoading,
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 0,
});
if (AsyncComp) {
const component = AppDrawerComponent;
const div = document.createElement('div');
document.body.appendChild(div);
const app = createApp(component,
{
close: () => { document.body.removeChild(div); app.unmount(); },
...props
}
);
app.component(view.name as string, AsyncComp);
app.use(Router).use(Antd).mount(div);
}
return subject;
} catch (error) {
console.error(error);
return new Subject<any>();
}
}
/**
* 打开抽屉
*
* @param {View} view 视图对象
* @param {IParam} params 视图参数
* @param {IParam} [options] 模态配置项
* @returns {Subject<any>}
* @memberof AppDrawer
*/
public openDrawer(view: ViewDetail, params: IParam, options?: IParam): Subject<any> {
try {
const subject = this.createVueExample(view, params, options);
return subject;
} catch (error) {
console.log(error);
return new Subject<any>();
}
}
/**
* 生成uuid
*
* @private
* @returns {string}
* @memberof AppDrawer
*/
private getUUID(): string {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
}
\ No newline at end of file
<script setup lang="ts">
import { IParam, ViewDetail } from '@core';
import { Subject } from 'rxjs';
import { Ref, ref } from 'vue';
interface AppDrawerProps {
/**
* @description 视图
*/
view: ViewDetail;
/**
* @description 视图上下文参数
*/
context?: any;
/**
* @description 视图参数
*/
viewParams?: any;
/**
* @description 数据传递对象
*/
subject?: Subject<any>;
/**
* @description 关闭回调
*/
close: Function;
/**
* @description 是否全屏
*/
isFullscreen:boolean;
/**
* @description 模态参数
*/
option?: IParam;
}
const props = withDefaults(defineProps<AppDrawerProps>(), {
context: {},
viewParams: {},
});
/**
* 是否显示
*/
let isVisible: Ref<boolean> = ref(false);
/**
* 临时结果
*/
let tempResult = { ret: '' };
/**
* 视图名称
*/
let viewName: Ref<string> = ref('');
/**
* 抽屉的方向
*/
let placement: Ref<string> = ref('right');
/**
* 视图标题
*/
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 || 'right';
if (props.isFullscreen) {
Object.assign(style, { height: 'auto' });
} else {
if (!props.view.width || props.view.width === 0 || Object.is(props.view.width, '0px')) {
let viewWidth = 600;
if (window && window.innerWidth > 100) {
if (window.innerWidth > 100) {
viewWidth = window.innerWidth - 100;
} else {
viewWidth = window.innerWidth;
}
}
width.value = viewWidth;
} else {
width.value = props.view.width;
}
if (props.view.height && !Object.is(props.view.height, '0px')) {
Object.assign(style, { height: props.view.height + 'px' });
height.value = props.view.height;
} else {
height.value = 800;
}
}
}
});
/**
* Vue生命周期mounted
*/
onMounted(() => {
isVisible.value = true;
});
/**
* 视图关闭
*/
const close = (result: any) => {
if (result && Array.isArray(result) && result.length > 0) {
Object.assign(tempResult, { ret: 'OK' }, { datas: JSON.parse(JSON.stringify(result)) });
}
isVisible.value = false;
};
/**
* 视图数据变化
*/
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);
props.close(tempResult);
};
/**
* 处理数据,向外抛值
*/
const handleShowState = ($event: any) => {
if (props.subject && tempResult) {
props.subject.next(tempResult);
}
};
</script>
<template>
<a-drawer
ref="curDrawer"
class="app-drawer"
v-model:visible="isVisible"
:placement="placement"
:title="title"
:footer="null"
:maskClosable="true"
:destroyOnClose="true"
:width="width"
:height="height"
:bodyStyle="style"
@close="onVisibleChange($event)"
>
<component
:is="viewName"
class="app-drawer-view-component"
:width="width"
:height="height"
:context="context"
:viewParams="viewParams"
:viewDefaultUsage="false"
:noViewCaption="true"
@viewDataChange="viewDataChange($event)"
@viewDataActivated="viewDataActivated($event)"
@close="close($event)"
:ref="viewName"
></component>
</a-drawer>
</template>
\ No newline at end of file
import { Subject } from 'rxjs';
import Router from '@/router';
import Antd from 'ant-design-vue';
import AppModalComponent from "./app-modal.vue";
import AppLoading from '@components/common/app-loading.vue';
import { IParam, ViewDetail } from '@core';
export class AppModal {
/**
* 实例对象
*
* @private
* @static
* @memberof AppModal
*/
private static modal = new AppModal();
/**
*
*
* @type {*}
* @memberof AppModal
*/
public asyncComp: any;
/**
* Creates an instance of AppModal.
*
* @memberof AppModal
*/
private constructor() {
if (AppModal.modal) {
return AppModal.modal;
}
}
/**
* 获取单例对象
*
* @static
* @returns {AppModal}
* @memberof AppModal
*/
public static getInstance(): AppModal {
if (!AppModal.modal) {
AppModal.modal = new AppModal();
}
return AppModal.modal;
}
/**
* 创建vue对象
*
* @private
* @param {view} ViewDetail 视图对象
* @param {IParam} params 视图参数
* @param {IParam} [options] 模态配置项
* @return {*} {Subject<any>}
* @memberof AppModal
*/
private createVueExample(view: ViewDetail, params: IParam, options?: IParam): Subject<any> {
try {
let subject: null | Subject<any> = new Subject<any>();
let props = { view: view, context: params.context, viewParams: params.viewParams, isFullscreen: params.isFullscreen, subject: subject, options: options };
let dir = view.fileDir?.replace(/@views/, '');
//Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块
const modules = import.meta.glob('../../views/*/*/index.ts');
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: modules['../../views' + dir + '/index.ts'],
// 加载异步组件时要使用的组件
loadingComponent: AppLoading,
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 0,
});
if (AsyncComp) {
const component = AppModalComponent;
const div = document.createElement('div');
document.body.appendChild(div);
const app = createApp(component,
{
close: () => { document.body.removeChild(div); app.unmount(); },
...props
}
);
app.component(view.name as string, AsyncComp);
app.use(Router).use(Antd).mount(div);
}
return subject;
} catch (error) {
console.error(error);
return new Subject<any>();
}
}
/**
* 打开模态视图
*
* @param {View} view 视图对象
* @param {IParam} params 视图参数
* @param {IParam} [options] 模态配置项
* @return {*} {Subject<any>}
* @memberof AppModal
*/
public openModal(view: ViewDetail, params: IParam, options?: IParam): Subject<any> {
try {
const subject = this.createVueExample(view, params, options);
return subject;
} catch (error) {
console.log(error);
return new Subject<any>();
}
}
/**
* 获取节点标识
*
* @private
* @returns {string}
* @memberof AppModal
*/
private getUUID(): string {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
}
\ No newline at end of file
<script setup lang="ts">
import { IActionParam, IParam, ViewDetail } from '@core';
import { Subject } from 'rxjs';
import { Ref, ref } from 'vue';
interface AppModalProps {
/**
* @description 视图
*/
view: ViewDetail;
/**
* @description 视图上下文参数
*/
context?: any;
/**
* @description 视图参数
*/
viewParams?: any;
/**
* @description 数据传递对象
*/
subject?: Subject<any>;
/**
* @description 关闭回调
*/
close: Function;
/**
* @description 是否全屏
*/
isFullscreen: boolean;
/**
* @description 模态参数
*/
option?: IParam;
}
const props = withDefaults(defineProps<AppModalProps>(), {
context: {},
viewParams: {},
});
/**
* 是否显示
*/
let isVisible: Ref<boolean> = ref(false);
/**
* 临时结果
*/
let tempResult = { ret: '' };
/**
* 视图名称
*/
let viewName: Ref<string> = ref('');
/**
* 视图标题
*/
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 || '';
if (props.isFullscreen) {
Object.assign(style, { height: 'auto' });
} else {
if (!props.view.width || props.view.width === 0 || Object.is(props.view.width, '0px')) {
let viewWidth = 600;
if (window && window.innerWidth > 100) {
if (window.innerWidth > 100) {
viewWidth = window.innerWidth - 100;
} else {
viewWidth = window.innerWidth;
}
}
width.value = viewWidth;
} else {
width.value = props.view.width;
}
if (props.view.height && !Object.is(props.view.height, '0px')) {
Object.assign(style, { height: props.view.height + 'px' });
height.value = props.view.height;
} else {
height.value = 800;
}
}
}
});
/**
* Vue生命周期mounted
*/
onMounted(() => {
isVisible.value = true;
});
/**
* 视图关闭
*/
const close = (result: any) => {
if (result && Array.isArray(result) && result.length > 0) {
Object.assign(tempResult, { ret: 'OK' }, { resultData: JSON.parse(JSON.stringify(result)) });
}
props.subject?.next(tempResult);
isVisible.value = false;
};
/**
* 视图数据变化
*/
const viewDataChange = (result: any) => {
tempResult = { ret: '' };
if (result && Array.isArray(result) && result.length > 0) {
Object.assign(tempResult, { ret: 'OK' }, { resultData: JSON.parse(JSON.stringify(result)) });
}
};
/**
* 视图数据激活
*/
const viewDataActivated = (result: any) => {
if (result && Array.isArray(result) && result.length > 0) {
close(result);
}
};
const handelViewEvent = (actionParam: IActionParam) => {
const { tag, action, data } = actionParam;
switch (action) {
case 'viewDataChange':
viewDataChange(data);
break;
case 'close':
close(data);
break;
}
};
/**
* 模态显示隐藏切换回调
*/
const onVisibleChange = ($event: any) => {
handleShowState($event);
props.close(tempResult);
};
/**
* 处理数据,向外抛值
*/
const handleShowState = ($event: any) => {
if (props.subject && tempResult) {
props.subject.next(tempResult);
}
};
</script>
<template>
<a-modal
ref="curModal"
class="app-modal"
v-model:visible="isVisible"
:title="title"
:footer="null"
:maskClosable="true"
:destroyOnClose="true"
:width="width"
:height="height"
:bodyStyle="style"
@cancel="onVisibleChange($event)"
>
<component
:is="viewName"
class="app-modal-view-component"
:context="context"
:viewParams="viewParams"
:viewDefaultUsage="false"
:noViewCaption="true"
@viewEvent="handelViewEvent($event)"
@viewDataActivated="viewDataActivated($event)"
@close="close($event)"
:ref="viewName"
></component>
</a-modal>
</template>
\ No newline at end of file
.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 AppLoading from '@components/common/app-loading.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(/@views/, '');
// Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块
const modules = import.meta.glob('../../page/*/*/index.ts');
// 创建一个只有在需要时才会加载的异步组件
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: modules['../../page' + dir + '/index.ts'],
// 加载异步组件时要使用的组件
loadingComponent: AppLoading,
// 在显示 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 { AppDrawer } from './app-drawer/app-drawer';
export { AppPopover } from './app-popover/app-popover';
export { OpenViewService } from './open-view/open-view-service';
import { IContext, IOpenViewService, IParam, RouteUtil, ViewDetail } from '@core';
import router from '@/router';
import { AppModal } from '@/utils';
import { Subject } from 'rxjs';
interface Params extends IParam {
context: IContext;
viewParams: IParam;
}
/**
* 视图打开服务
* @export
* @class OpenViewService
*/
export class OpenViewService implements IOpenViewService {
/**
* 唯一实例
*
* @private
* @static
* @memberof OpenViewService
*/
private static readonly instance = new OpenViewService();
/**
* 获取唯一实例
*
* @static
* @return {*} {OpenViewService}
* @memberof OpenViewService
*/
public static getInstance(): OpenViewService {
return OpenViewService.instance;
}
/**
* 打开视图
*
* @param view 视图信息
* @param params 相关参数
*/
public openView(view: ViewDetail, params: Params): Subject<any> | undefined {
// 获取详细视图信息
let _view: ViewDetail | undefined = App.getViewInfo(view.codeName);
if (!_view) {
console.error(`应用中不存在${view.codeName}视图`);
return;
}
// view的openMode覆盖配置的
if (view.openMode) {
_view.openMode = view.openMode;
}
// 重定向视图走重定向逻辑,其他根据openMode打开
if (_view.redirectView) {
this.openRedirectView(_view, params);
} else {
return this.openByOpenMode(_view, params);
}
}
/**
* 根据打开方式打开视图
*
* @param view 视图信息
* @param params 相关参数
*/
public openByOpenMode(view: ViewDetail, params: Params): Subject<any> | undefined {
const openMode = params.openMode || view.openMode;
const { viewParams, context } = params;
// 路由打开视图
if (openMode == 'INDEXVIEWTAB' || openMode == 'POPUPAPP') {
// TODO 视图关系参数处理
const routePath = RouteUtil.buildUpRoutePath(view, context, viewParams, router.currentRoute.value);
if (openMode == 'INDEXVIEWTAB') {
router.push(routePath);
} else {
window.open('./#' + routePath, '_blank');
}
return;
} else if (openMode == 'POPUPMODAL') {
return AppModal.getInstance().openModal(view, params);
} else if (openMode?.indexOf('DRAWER') !== -1) {
// TODO PMS上面抽屉DRAWER_TOP
} else if (openMode == 'POPOVER') {
// TODO 打开气泡卡片
} else {
console.error(`未支持${openMode}打开方式`);
}
}
/**
* 重定向视图处理
*
* @param view 视图信息
* @param params 相关参数
*/
public openRedirectView(view: any, params: Params) {
// TODO 重定向视图处理
}
}
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册