diff --git a/src/components/cards/PluginCard.vue b/src/components/cards/PluginCard.vue index 34ebbf61..2a4a3b82 100644 --- a/src/components/cards/PluginCard.vue +++ b/src/components/cards/PluginCard.vue @@ -14,13 +14,14 @@ import store from '@/store' // 输入参数 const props = defineProps({ plugin: Object as PropType, - count: Number, + count: Number, // 下载次数 + action: Boolean, // 动作标识 width: String, height: String, }) // 定义触发的自定义事件 -const emit = defineEmits(['remove', 'save']) +const emit = defineEmits(['remove', 'save', 'actionDone']) // 背景颜色 const backgroundColor = ref('#28A9E1') @@ -64,6 +65,14 @@ const isImageLoaded = ref(false) // 图片是否加载失败 const imageLoadError = ref(false) +// 监听动作标识,如为true则打开详情 +watch(() => props.action, (newAction, oldAction) => { + if (newAction && !oldAction) { + openPluginDetail() + emit('actionDone') + } +}) + // 图片加载完成 async function imageLoaded() { isImageLoaded.value = true @@ -286,6 +295,14 @@ function openLoggerWindow() { window.open(url, '_blank') } +// 打开插件详情 +function openPluginDetail() { + if (props.plugin?.has_page) + showPluginInfo() + else + showPluginConfig() +} + // 弹出菜单 const dropdownItems = ref([ { @@ -372,12 +389,7 @@ watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => { v-if="isVisible" :width="props.width" :height="props.height" - @click="() => { - if (props.plugin?.has_page) - showPluginInfo() - else - showPluginConfig() - }" + @click="openPluginDetail" >
{ - diff --git a/src/components/form/ReorganizeForm.vue b/src/components/form/ReorganizeForm.vue index f81ef229..6b3334f6 100644 --- a/src/components/form/ReorganizeForm.vue +++ b/src/components/form/ReorganizeForm.vue @@ -304,6 +304,7 @@ async function transfer() { v-model="tmdbSelectorDialog" width="40rem" scrollable + max-height="90vh" > +import { useToast } from 'vue-toast-notification' import api from '@/api' import type { Plugin } from '@/api/types' import NoDataFound from '@/components/NoDataFound.vue' import PluginAppCard from '@/components/cards/PluginAppCard.vue' import PluginCard from '@/components/cards/PluginCard.vue' +import noImage from '@images/logos/plugin.png' // 已安装插件列表 const dataList = ref([]) @@ -23,16 +25,115 @@ const PluginAppDialog = ref(false) // 插件安装统计 const PluginStatistics = ref<{ [key: string]: number }>({}) +// 搜索窗口 +const SearchDialog = ref(false) + +// 搜索关键字 +const keyword = ref('') + +// 每一个插件的图标加载状态 +const pluginIconLoaded = ref<{ [key: string]: boolean }>({}) + +// 每一个插件的动作标识 +const pluginActions = ref<{ [key: string]: boolean }>({}) + +// 提示框 +const $toast = useToast() + +// 进度框 +const progressDialog = ref(false) + +// 进度框文本 +const progressText = ref('正在安装插件...') + // 关闭插件市场窗口 function pluginDialogClose() { PluginAppDialog.value = false } +// 安装插件 +async function installPlugin(item: Plugin) { + try { + // 显示等待提示框 + progressDialog.value = true + progressText.value = `正在安装 ${item?.plugin_name} v${item?.plugin_version} ...` + + const result: { [key: string]: any } = await api.get( + `plugin/install/${item?.id}`, + { + params: { + repo_url: item?.repo_url, + force: item?.has_update, + }, + }, + ) + + // 隐藏等待提示框 + progressDialog.value = false + + if (result.success) { + $toast.success(`插件 ${item?.plugin_name} 安装成功!`) + + // 刷新 + refreshData() + } + else { + $toast.error(`插件 ${item?.plugin_name} 安装失败:${result.message}`) + } + } + catch (error) { + console.error(error) + } +} + +// 打开插件搜索结果 +function openPlugin(item: Plugin) { + // 如果是已安装插件则打开插件详情 + if (item.installed === true) { + // 标记插件动作 + pluginActions.value[item.id || '0'] = true + } + else { + // 如果是未安装插件则安装 + installPlugin(item) + } + closeSearchDialog() +} + +// 关闭插件搜索窗口 +function closeSearchDialog() { + SearchDialog.value = false +} + +// 插件图标加载错误 +function pluginIconError(item: Plugin) { + pluginIconLoaded.value[item.id || '0'] = false +} + +// 插件图标地址 +function pluginIcon(item: Plugin) { + // 如果图片加载错误 + if (pluginIconLoaded.value[item.id || '0'] === false) + return noImage + // 如果是网络图片则使用代理后返回 + if (item?.plugin_icon?.startsWith('http')) + return `${import.meta.env.VITE_API_BASE_URL}system/img/${encodeURIComponent(item?.plugin_icon)}/1` + + return `./plugin_icon/${item?.plugin_icon}` +} + +// 过滤插件 +const filterPlugins = computed(() => { + const all_list = [...dataList.value, ...uninstalledList.value] + return all_list.filter((item: Plugin) => { + return item.plugin_name?.includes(keyword.value) || item.plugin_desc?.includes(keyword.value) + }) +}) + // 新安装了插件 function pluginInstalled() { - fetchInstalledPlugins() pluginDialogClose() - fetchUninstalledPlugins() + refreshData() } // 获取插件列表数据 @@ -129,8 +230,10 @@ onBeforeMount(() => { :key="`${data.id}_v${data.plugin_version}`" :count="PluginStatistics[data.id || '0']" :plugin="data" + :action="pluginActions[data.id || '0']" @remove="refreshData" @save="refreshData" + @action-done="pluginActions[data.id || '0'] = false" />
{ + + + + + + + + + + + + + + + + + + + + {{ progressText }} + + + +