perf(X-Pack): 插件管理-前端分布式加载静态资源

This commit is contained in:
fit2cloud-chenyw 2024-06-20 16:32:02 +08:00
parent f6a8f2b947
commit 54d254e290
16 changed files with 82 additions and 55 deletions

View File

@ -1 +1,20 @@
ALTER TABLE `core_export_task` ADD COLUMN `msg` LONGTEXT NULL COMMENT '错误信息' AFTER `params`; ALTER TABLE `core_export_task`
ADD COLUMN `msg` LONGTEXT NULL COMMENT '错误信息' AFTER `params`;
DROP TABLE IF EXISTS `xpack_plugin`;
CREATE TABLE `xpack_plugin`
(
`id` bigint NOT NULL COMMENT 'ID',
`name` varchar(255) NOT NULL COMMENT '插件名称',
`icon` longtext NOT NULL COMMENT '图标',
`version` varchar(255) NOT NULL COMMENT '版本',
`install_time` bigint NOT NULL COMMENT '安装时间',
`flag` varchar(255) NOT NULL COMMENT '类型',
`developer` varchar(255) NOT NULL COMMENT '开发者',
`config` longtext NOT NULL COMMENT '插件配置',
`require_version` varchar(255) NOT NULL COMMENT 'DE最低版本',
`module_name` varchar(255) NOT NULL COMMENT '模块名称',
`jar_name` varchar(255) NOT NULL COMMENT 'Jar包名称',
PRIMARY KEY (`id`)
) COMMENT ='插件表';

View File

@ -14,7 +14,7 @@ export default {
], ],
build: { build: {
rollupOptions: { rollupOptions: {
external: id => /de-xpack/.test(id) || /extensions-view-3dpie/.test(id), external: id => /de-xpack/.test(id) || /extensions/.test(id),
output: { output: {
// 用于命名代码拆分时创建的共享块的输出命名 // 用于命名代码拆分时创建的共享块的输出命名
chunkFileNames: `assets/chunk/[name]-${pkg.version}-${pkg.name}.js`, chunkFileNames: `assets/chunk/[name]-${pkg.version}-${pkg.name}.js`,

View File

@ -1490,25 +1490,6 @@ defineExpose({
@linkJumpSetOpen="linkJumpSetOpen(item)" @linkJumpSetOpen="linkJumpSetOpen(item)"
@linkageSetOpen="linkageSetOpen(item)" @linkageSetOpen="linkageSetOpen(item)"
> >
<!--如果是图表 则动态获取预存的chart-view数据-->
<!-- <PluginComponent
v-if="item['isPlugin']"
:jsname="item['pluginFlag'] || 'L2NvbXBvbmVudC9pbmRleA=='"
class="component"
:id="'component' + item.id"
:active="item.id === curComponentId"
:dv-type="dvInfo.type"
:scale="curBaseScale"
:style="getComponentStyle(item.style)"
:prop-value="item.propValue"
:is-edit="true"
:view="canvasViewInfo[item.id]"
:element="item"
:request="item.request"
@input="handleInput"
:dv-info="dvInfo"
:canvas-active="canvasActive"
/> -->
<component <component
:is="findComponent(item.component)" :is="findComponent(item.component)"
v-if="item.component === 'UserView' || item['isPlugin']" v-if="item.component === 'UserView' || item['isPlugin']"

View File

@ -2,7 +2,7 @@
import noLic from './nolic.vue' import noLic from './nolic.vue'
import { ref, useAttrs, onMounted } from 'vue' import { ref, useAttrs, onMounted } from 'vue'
import { execute, randomKey, formatArray } from './convert' import { execute, randomKey, formatArray } from './convert'
import { loadPluginApi, loadDistributed, xpackModelApi } from '@/api/plugin' import { loadPluginApi, xpackModelApi } from '@/api/plugin'
import { useCache } from '@/hooks/web/useCache' import { useCache } from '@/hooks/web/useCache'
import { i18n } from '@/plugins/vue-i18n' import { i18n } from '@/plugins/vue-i18n'
import * as Vue from 'vue' import * as Vue from 'vue'
@ -10,7 +10,7 @@ import axios from 'axios'
import * as Pinia from 'pinia' import * as Pinia from 'pinia'
import * as vueRouter from 'vue-router' import * as vueRouter from 'vue-router'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import request from '@/config/axios'
const { wsCache } = useCache() const { wsCache } = useCache()
const plugin = ref() const plugin = ref()
@ -33,15 +33,12 @@ const generateRamStr = (len: number) => {
} }
const importProxy = (bytesArray: any[]) => { const importProxy = (bytesArray: any[]) => {
/* const promise = import(
`../../../../../../../${formatArray(bytesArray[7])}/${formatArray(bytesArray[8])}/${formatArray(
bytesArray[9]
)}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}.vue`
) */
const promise = import( const promise = import(
`../../../../../../../extensions-view-3dpie/${formatArray(bytesArray[8])}/${formatArray( `../../../../../../../extensions/${formatArray(bytesArray[8])}/${formatArray(
bytesArray[9] bytesArray[9]
)}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}.vue` )}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}/${formatArray(
bytesArray[12]
)}.vue`
) )
promise promise
.then((res: any) => { .then((res: any) => {
@ -53,16 +50,23 @@ const importProxy = (bytesArray: any[]) => {
}) })
} }
const getModuleName = () => {
const jsPath = window.atob(attrs.jsname.toString())
return jsPath.split('/')[0]
}
const loadComponent = () => { const loadComponent = () => {
const moduleName = getModuleName()
loading.value = true loading.value = true
const byteArray = wsCache.get(`de-plugin-proxy-plugin`) const byteArray = wsCache.get(`de-plugin-proxy-${moduleName}`)
if (byteArray) { if (byteArray) {
importProxy(JSON.parse(byteArray)) importProxy(JSON.parse(byteArray))
loading.value = false loading.value = false
return return
} }
const key = generateRamStr(randomKey()) const key = generateRamStr(randomKey())
loadPluginApi(key) const moduleNameKey = window.btoa(moduleName)
const saltKey = `${key},${moduleNameKey}`
loadPluginApi(saltKey)
.then(response => { .then(response => {
let code = response.data let code = response.data
const byteArray = execute(code, key) const byteArray = execute(code, key)
@ -82,7 +86,8 @@ const storeCacheProxy = byteArray => {
byteArray.forEach(item => { byteArray.forEach(item => {
result.push([...item]) result.push([...item])
}) })
wsCache.set(`de-plugin-proxy-plugin`, JSON.stringify(result)) const moduleName = getModuleName()
wsCache.set(`de-plugin-proxy-${moduleName}`, JSON.stringify(result))
} }
const pluginProxy = ref(null) const pluginProxy = ref(null)
const invokeMethod = param => { const invokeMethod = param => {
@ -104,8 +109,9 @@ onMounted(async () => {
distributed = wsCache.get(key) distributed = wsCache.get(key)
} }
if (distributed) { if (distributed) {
if (window['DEXPack']) { const moduleName = getModuleName()
const xpack = await window['DEXPack'].mapping[attrs.jsname] if (window[moduleName]) {
const xpack = await window[moduleName].mapping[attrs.jsname]
plugin.value = xpack.default plugin.value = xpack.default
} else { } else {
window['Vue'] = Vue window['Vue'] = Vue
@ -114,9 +120,10 @@ onMounted(async () => {
window['vueRouter'] = vueRouter window['vueRouter'] = vueRouter
window['MittAll'] = useEmitt().emitter.all window['MittAll'] = useEmitt().emitter.all
window['I18n'] = i18n window['I18n'] = i18n
loadDistributed().then(async res => { const url = `/xpackComponent/pluginStaticInfo/${moduleName}`
new Function(res.data)() request.get({ url }).then(async res => {
const xpack = await window['DEXPack'].mapping[attrs.jsname] new Function(res.data || res)()
const xpack = await window[moduleName].mapping[attrs.jsname]
plugin.value = xpack.default plugin.value = xpack.default
}) })
} }

View File

@ -173,6 +173,8 @@ service.interceptors.response.use(
return response return response
} else if (response.config.url.includes('DEXPack.umd.js')) { } else if (response.config.url.includes('DEXPack.umd.js')) {
return response return response
} else if (response.config.url.startsWith('/xpackComponent/pluginStaticInfo/extensions-')) {
return response
} else { } else {
if ( if (
!response?.config?.url.startsWith('/xpackComponent/content') && !response?.config?.url.startsWith('/xpackComponent/content') &&

View File

@ -48,8 +48,8 @@ const anchorPosition = anchor => {
scrollTo(element.offsetTop) scrollTo(element.offsetTop)
} }
const newComponent = (innerType, isPlugin) => { const newComponent = (innerType, staticMap) => {
eventBus.emit('handleNew', { componentName: 'UserView', innerType: innerType, isPlugin }) eventBus.emit('handleNew', { componentName: 'UserView', innerType: innerType, staticMap })
} }
const handleDragStart = e => { const handleDragStart = e => {
@ -66,14 +66,15 @@ const groupActiveChange = category => {
} }
const loadPluginCategory = data => { const loadPluginCategory = data => {
data.forEach(item => { data.forEach(item => {
const { category, title, render, chartValue, chartTitle, icon } = item const { category, title, render, chartValue, chartTitle, icon, staticMap } = item
const node = { const node = {
render, render,
category, category,
icon, icon,
value: chartValue, value: chartValue,
title: chartTitle, title: chartTitle,
isPlugin: true isPlugin: true,
staticMap
} }
const stack = [...state.chartGroupList] const stack = [...state.chartGroupList]
let findParent = false let findParent = false
@ -128,7 +129,7 @@ const loadPluginCategory = data => {
:key="chartInfo.title" :key="chartInfo.title"
> >
<div <div
v-on:click="newComponent(chartInfo.value, chartInfo['isPlugin'])" v-on:click="newComponent(chartInfo.value, chartInfo['staticMap'])"
class="item-top" class="item-top"
draggable="true" draggable="true"
:data-id="'UserView&' + chartInfo.value" :data-id="'UserView&' + chartInfo.value"

View File

@ -518,8 +518,9 @@ export function findNewComponentFromList(
componentName, componentName,
innerType, innerType,
curOriginThemes, curOriginThemes,
isPlugin?: boolean staticMap?: object
) { ) {
const isPlugin = !!staticMap
let newComponent let newComponent
list.forEach(comp => { list.forEach(comp => {
if (comp.component === componentName) { if (comp.component === componentName) {
@ -540,6 +541,9 @@ export function findNewComponentFromList(
newComponent.label = viewConfig?.title newComponent.label = viewConfig?.title
newComponent.render = viewConfig?.render newComponent.render = viewConfig?.render
newComponent.isPlugin = !!isPlugin newComponent.isPlugin = !!isPlugin
if (isPlugin) {
newComponent.staticMap = staticMap
}
} }
return newComponent return newComponent
} }

View File

@ -1,4 +1,4 @@
declare interface ChartPlugin { declare interface ChartPlugin {
isPlugin: boolean isPlugin: boolean
pluginResourceId?: string staticMap?: object
} }

View File

@ -391,7 +391,7 @@ export const dvMainStore = defineStore('dataVisualization', {
render: component.render, render: component.render,
plugin: { plugin: {
isPlugin: component.isPlugin, isPlugin: component.isPlugin,
pluginResourceId: component.pluginResourceId staticMap: component.staticMap
} }
} as unknown as ChartObj } as unknown as ChartObj
// 处理配置项默认值不同图表的同一配置项默认值不同 // 处理配置项默认值不同图表的同一配置项默认值不同

View File

@ -73,10 +73,9 @@ const editStyle = computed(() => {
// //
const handleNewFromCanvasMain = newComponentInfo => { const handleNewFromCanvasMain = newComponentInfo => {
const { componentName, innerType, isPlugin } = newComponentInfo const { componentName, innerType, staticMap } = newComponentInfo
if (componentName) { if (componentName) {
const component = findNewComponentFromList(componentName, innerType, curOriginThemes, isPlugin) const component = findNewComponentFromList(componentName, innerType, curOriginThemes, staticMap)
component.isPlugin = !!isPlugin
syncShapeItemStyle(component, baseWidth.value, baseHeight.value) syncShapeItemStyle(component, baseWidth.value, baseHeight.value)
component.id = guid() component.id = guid()
component.y = 200 component.y = 200

View File

@ -1605,7 +1605,7 @@ const deleteChartFieldItem = id => {
</div> </div>
<plugin-component <plugin-component
v-else-if="view.plugin?.isPlugin" v-else-if="view.plugin?.isPlugin"
jsname="L2NvbXBvbmVudC9lZGl0b3IvaW5kZXg=" :jsname="view.plugin.staticMap['editor']"
:view="view" :view="view"
:dimension="state.dimension" :dimension="state.dimension"
:quota="state.quota" :quota="state.quota"

View File

@ -762,7 +762,7 @@ const showActionIcons = computed(() => {
<div v-if="chartAreaShow" style="flex: 1; overflow: hidden"> <div v-if="chartAreaShow" style="flex: 1; overflow: hidden">
<plugin-component <plugin-component
v-if="view.plugin?.isPlugin" v-if="view.plugin?.isPlugin"
jsname="L2NvbXBvbmVudC9pbmRleA==" :jsname="view.plugin.staticMap['index']"
:scale="scale" :scale="scale"
:dynamic-area-id="dynamicAreaId" :dynamic-area-id="dynamicAreaId"
:view="view" :view="view"

View File

@ -20,4 +20,7 @@ public interface XpackComponentApi {
@GetMapping("/viewPlugins") @GetMapping("/viewPlugins")
List<XpackPluginsViewVO> viewPlugins(); List<XpackPluginsViewVO> viewPlugins();
@GetMapping("/pluginStaticInfo/{moduleName}")
void pluginStaticInfo(@PathVariable("moduleName") String moduleName);
} }

View File

@ -63,6 +63,7 @@ public class WhitelistUtils {
|| StringUtils.startsWithAny(requestURI, "/appearance/image/") || StringUtils.startsWithAny(requestURI, "/appearance/image/")
|| StringUtils.startsWithAny(requestURI, "/share/proxyInfo") || StringUtils.startsWithAny(requestURI, "/share/proxyInfo")
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/content") || StringUtils.startsWithAny(requestURI, "/xpackComponent/content")
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/pluginStaticInfo")
|| StringUtils.startsWithAny(requestURI, "/geo/") || StringUtils.startsWithAny(requestURI, "/geo/")
|| StringUtils.startsWithAny(requestURI, "/websocket") || StringUtils.startsWithAny(requestURI, "/websocket")
|| StringUtils.startsWithAny(requestURI, "/map/") || StringUtils.startsWithAny(requestURI, "/map/")

View File

@ -1,7 +1,10 @@
package io.dataease.extensions.view.factory; package io.dataease.extensions.view.factory;
import io.dataease.exception.DEException;
import io.dataease.extensions.view.template.PluginsChartTemplate; import io.dataease.extensions.view.template.PluginsChartTemplate;
import io.dataease.extensions.view.vo.XpackPluginsViewVO; import io.dataease.extensions.view.vo.XpackPluginsViewVO;
import io.dataease.license.utils.LogUtil;
import io.dataease.plugins.factory.DataEasePluginFactory;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -11,6 +14,7 @@ public class PluginsChartFactory {
private static final Map<String, PluginsChartTemplate> templateMap = new ConcurrentHashMap<>(); private static final Map<String, PluginsChartTemplate> templateMap = new ConcurrentHashMap<>();
public static PluginsChartTemplate getInstance(String render, String type) { public static PluginsChartTemplate getInstance(String render, String type) {
String key = render + "_" + type; String key = render + "_" + type;
return templateMap.get(key); return templateMap.get(key);
@ -20,13 +24,16 @@ public class PluginsChartFactory {
String key = render + "_" + type; String key = render + "_" + type;
if (templateMap.containsKey(key)) return; if (templateMap.containsKey(key)) return;
templateMap.put(key, template); templateMap.put(key, template);
try {
String moduleName = template.getPluginInfo().getModuleName();
DataEasePluginFactory.loadTemplate(moduleName, template);
} catch (Exception e) {
LogUtil.error(e.getMessage(), new Throwable(e));
DEException.throwException(e);
}
} }
public static List<XpackPluginsViewVO> getViewConfigList() { public static List<XpackPluginsViewVO> getViewConfigList() {
return templateMap.values().stream().map(PluginsChartTemplate::getConfig).toList(); return templateMap.values().stream().map(PluginsChartTemplate::getConfig).toList();
} }
public static List<String> getAllPlugins() {
return null;
}
} }

View File

@ -4,6 +4,7 @@ import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map;
@Data @Data
public class XpackPluginsViewVO implements Serializable { public class XpackPluginsViewVO implements Serializable {
@ -26,4 +27,6 @@ public class XpackPluginsViewVO implements Serializable {
private String render; private String render;
private Map<String, String> staticMap;
} }