2025-03-21 19:54:59 +08:00

319 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import type { AxiosInterceptorManager, AxiosResponse } from 'axios';
import { getCurrentInstance, onMounted, ref, watch } from 'vue';
import { useQuery } from '@tanstack/vue-query';
import { jsonp, request } from '@vtj/utils';
import { createProvider } from '@vtj/web';
import { ElLoading, ElMessage } from 'element-plus';
import { isFn } from 'licia-es';
import { LowCodeService } from './service';
interface WujieProps {
interceptors?: {
request: AxiosInterceptorManager<InternalAxiosRequestConfig>;
response: AxiosInterceptorManager<AxiosResponse>;
};
// 必填参数
fileId: string;
projectId: number | string;
// 可选参数
applicationId?: number | string;
name?: string;
[key: string]: any;
}
declare global {
interface Window {
$wujie?: {
bus: {
$emit: (event: string, data?: any) => void;
};
props: WujieProps;
};
}
}
// 定义必要的初始化参数
interface InitParams {
// 必填参数
fileId: string;
projectId: number | string;
// 可选参数
applicationId?: number | string;
[key: string]: any;
}
// 响应式状态
const renderer = ref();
const lowCodeService = new LowCodeService();
const isLoading = ref(false);
const provider = ref(null);
const loadingInstance = ref(null);
const errorMessage = ref('');
const initParams = ref<InitParams | null | WujieProps>(null);
// 判断是否为wujie子应用
const isWujieSubApp = window.$wujie !== undefined;
// 从URL解析查询参数
const getParamsFromUrl = (): Partial<InitParams> => {
const params = new URLSearchParams(window.location.search);
return {
fileId: params.get('fileId') || undefined,
projectId: params.get('projectId') || undefined,
applicationId: params.get('applicationId') || undefined,
};
};
// 按优先级获取初始化参数
const getInitParams = (): InitParams | null => {
if (isWujieSubApp && window.$wujie?.props) {
const props: WujieProps = window.$wujie.props;
console.log('WujieProps', props);
if (props.fileId && props.projectId) {
console.log('使用无界初始化渲染器');
return props as WujieProps;
}
}
// 2. 其次从URL参数获取
const urlParams = getParamsFromUrl();
if (urlParams.fileId && urlParams.projectId) {
console.log('使用URL参数初始化渲染器:', urlParams);
return urlParams as InitParams;
}
// 3. 都不满足返回null
console.error('无法获取初始化参数');
errorMessage.value = '无法获取初始化参数请检查无界配置或URL参数';
return null;
};
// 检查参数是否有效
const isValidParams = (params: InitParams | null): params is InitParams => {
return !!params && !!params.fileId && !!params.projectId;
};
// 初始化请求配置
const initRequestConfig = () => {
if (!isWujieSubApp) return console.log('不是无界渲染模式,不进行拦截器合并');
const props = window.$wujie.props;
const mergeRequestInterceptors = () => {
const requestHandlers = props.interceptors?.request?.handlers || [];
requestHandlers.forEach((handler) => {
if (isFn(handler?.fulfilled)) {
request.useRequest(
handler.fulfilled,
handler.rejected,
handler.options,
);
}
});
};
mergeRequestInterceptors();
};
// 显示加载中
const showLoading = (text = '低代码文件加载中...') => {
if (loadingInstance.value) return;
loadingInstance.value = ElLoading.service({ text });
};
// 隐藏加载中
const hideLoading = () => {
if (loadingInstance.value) {
loadingInstance.value.close();
loadingInstance.value = null;
}
};
// 初始化低代码引擎 - 只初始化一次
const initLowCodeEngine = async () => {
// 如果已经初始化过,直接返回
if (provider.value) return provider.value;
// 检查参数是否有效
if (!isValidParams(initParams.value)) {
const error = new Error('缺少必要参数fileId 和 projectId');
errorMessage.value = error.message;
throw error;
}
// 初始化请求配置
initRequestConfig();
try {
const { provider: lowCodeProvider, onReady } = createProvider({
nodeEnv: import.meta.env.NODE_ENV,
service: lowCodeService,
project: { id: Number(initParams.value.projectId) },
adapter: {
request,
jsonp,
},
});
provider.value = { provider: lowCodeProvider, onReady };
return { provider: lowCodeProvider, onReady };
} catch (error) {
console.error('初始化低代码引擎失败:', error);
errorMessage.value = '初始化低代码引擎失败';
ElMessage.error('初始化低代码引擎失败');
return Promise.reject(error);
}
};
// 获取渲染组件
const getRenderComponent = async () => {
if (!isValidParams(initParams.value)) {
errorMessage.value = '缺少必要参数fileId 和 projectId';
return null;
}
isLoading.value = true;
showLoading();
try {
// 1. 确保低代码引擎已初始化
const { provider: lowCodeProvider, onReady } = await initLowCodeEngine();
// 2. 获取渲染组件
return new Promise<any>((resolve) => {
onReady(async () => {
const instance = getCurrentInstance();
instance?.appContext.app.use(lowCodeProvider);
try {
const renderComponent = await lowCodeProvider.getRenderComponent(
initParams.value!.fileId,
);
console.log('渲染组件获取成功');
resolve(renderComponent);
} catch (error) {
console.error('获取渲染组件失败:', error);
errorMessage.value = '获取渲染组件失败';
ElMessage.error('获取渲染组件失败');
resolve(null);
}
});
});
} catch (error) {
console.error('获取渲染组件过程出错:', error);
return null;
} finally {
isLoading.value = false;
hideLoading();
}
};
// 使用 useQuery 管理渲染组件
const { data: rendererComponent, isError } = useQuery({
queryKey: [
'getRenderer',
initParams.value?.fileId,
initParams.value?.projectId,
],
queryFn: getRenderComponent,
enabled: false, // 初始不自动执行,改为手动控制
retry: 1, // 失败后重试一次
staleTime: 1000 * 60 * 5, // 5分钟内不重新获取
});
// 当组件挂载时,将渲染器组件赋值给 renderer
watch(rendererComponent, (newVal) => {
if (newVal) {
renderer.value = newVal;
console.log('渲染器组件已更新');
}
});
// 向父应用发送状态消息
const notifyParent = (event: string, data?: any) => {
if (isWujieSubApp && window.$wujie?.bus) {
window.$wujie.bus.$emit(event, data);
}
};
// 组件挂载后执行初始化
onMounted(async () => {
// 通知父应用已准备就绪如果是wujie子应用
if (isWujieSubApp) {
notifyParent('ready', 'y-code-renderer is ready');
}
// 获取初始化参数 - 在挂载时执行一次
initParams.value = getInitParams();
const paramsValid = isValidParams(initParams.value);
if (!paramsValid) {
errorMessage.value = '缺少必要参数fileId 和 projectId';
if (isWujieSubApp) {
notifyParent('render-fail', errorMessage.value);
}
return;
}
try {
await initLowCodeEngine();
const component = await getRenderComponent();
if (component) {
renderer.value = component;
if (isWujieSubApp) {
notifyParent('render-success');
}
} else {
if (isWujieSubApp) {
notifyParent('render-fail', 'Failed to get component');
}
}
} catch (error) {
console.error('初始化过程出错:', error);
if (isWujieSubApp) {
notifyParent('render-fail', error);
}
}
});
</script>
<template>
<div class="renderer-container">
<component
:is="renderer"
v-if="renderer"
:ctx-props="initParams"
v-bind="initParams"
/>
<div v-else-if="!isValidParams(initParams)" class="error-message">
{{ errorMessage || '缺少必要参数fileId 和 projectId' }}
</div>
<div v-else-if="!isLoading && isError" class="error-message">
{{ errorMessage || '组件加载失败,请检查参数和网络连接' }}
</div>
<div v-else-if="!isLoading" class="error-message">
{{ errorMessage || '组件加载失败,请检查控制台日志' }}
</div>
</div>
</template>
<style scoped>
.renderer-container {
box-sizing: border-box;
padding: 20px;
height: 100%;
width: 100%;
}
.error-message {
margin-top: 50px;
color: red;
text-align: center;
}
</style>