mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-31 21:30:33 +08:00
在多个组件中添加渲染模式支持,优化插件配置和数据加载逻辑,增强用户体验和代码可读性
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user