在多个组件中添加渲染模式支持,优化插件配置和数据加载逻辑,增强用户体验和代码可读性

This commit is contained in:
jxxghp
2025-05-03 10:04:50 +08:00
parent 04e9b68e4a
commit 4a8cf16012
5 changed files with 251 additions and 64 deletions

View File

@@ -3,6 +3,7 @@ import { useDisplay } from 'vuetify'
import type { Plugin } from '@/api/types'
import PageRender from '@/components/render/PageRender.vue'
import api from '@/api'
import { useToast } from 'vue-toast-notification'
// 输入参数
const props = defineProps({
@@ -19,25 +20,80 @@ const display = useDisplay()
// APP
const appMode = inject('pwaMode') && display.mdAndDown.value
// 提示框
const $toast = useToast()
// 是否刷新
const isRefreshed = ref(false)
// 渲染模式: 'vuetify' 或 'vue'
const renderMode = ref('vuetify')
// Vue 模式:组件 URL
const vueComponentUrl = ref<string | null>(null)
// 插件数据页面配置项
let pluginPageItems = ref([])
// 调用API读取数据页面
async function loadPluginPage() {
try {
const result: [] = await api.get(`plugin/page/${props.plugin?.id}`)
if (result) pluginPageItems.value = result
} catch (error) {
console.error(error)
// Vue 模式:动态加载的组件
const dynamicComponent = computed(() => {
if (renderMode.value === 'vue' && vueComponentUrl.value) {
const url = vueComponentUrl.value
return defineAsyncComponent(() =>
import(/* @vite-ignore */ url)
.then(module => {
if (module.default) {
return module.default
} else {
$toast.error(`无法从 ${url} 加载默认导出的 Vue 组件`)
return { render: () => h('div', '组件加载失败: 无默认导出') }
}
})
.catch(err => {
$toast.error(`无法加载插件组件: ${url}`, err)
return { render: () => h('div', '组件加载失败') }
}),
)
}
isRefreshed.value = true
return null
})
// 调用API读取数据页面UI
async function loadPluginUIData() {
isRefreshed.value = false
pluginPageItems.value = []
renderMode.value = 'vuetify'
vueComponentUrl.value = null
try {
const result: { [key: string]: any } = await api.get(`plugin/page/${props.plugin?.id}`)
if (!result || !result.render_mode) {
console.error(`插件 ${props.plugin?.plugin_name} UI数据加载失败无效的响应`)
return
}
renderMode.value = result.render_mode
if (renderMode.value === 'vue') {
vueComponentUrl.value = result.component_url
if (!vueComponentUrl.value) {
console.error(`插件 ${props.plugin?.plugin_name} 配置错误未提供Vue组件URL`)
renderMode.value = 'vuetify'
}
} else {
pluginPageItems.value = result.page || []
}
} catch (error: any) {
console.error(error)
} finally {
isRefreshed.value = true
}
}
// 重新加载数据(可由 PageRender 或 Vue component 触发)
function handleAction() {
loadPluginUIData()
}
onMounted(() => {
loadPluginPage()
loadPluginUIData()
})
</script>
<template>
@@ -46,7 +102,15 @@ onMounted(() => {
<VDialogCloseBtn @click="emit('close')" />
<LoadingBanner v-if="!isRefreshed" class="mt-5" />
<VCardText v-else class="min-h-40">
<PageRender @action="loadPluginPage" v-for="(item, index) in pluginPageItems" :key="index" :config="item" />
<!-- Vuetify 渲染模式 -->
<div v-if="renderMode === 'vuetify'">
<PageRender @action="handleAction" v-for="(item, index) in pluginPageItems" :key="index" :config="item" />
<div v-if="!pluginPageItems || pluginPageItems.length === 0">此插件没有详情页面</div>
</div>
<!-- Vue 渲染模式 -->
<div v-else-if="renderMode === 'vue' && dynamicComponent">
<component :is="dynamicComponent" @action="handleAction" />
</div>
</VCardText>
<VFab
icon="mdi-cog"