From 52c868828c5048a19f37cb24e1f40467fe2dc62c Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sat, 8 Mar 2025 15:22:56 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=8F=92=E4=BB=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=80=81=E4=BF=9D=E5=AD=98=E9=85=8D=E7=BD=AE=E5=9D=87=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=83=AD=E9=87=8D=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/dashboard/routes/config.py | 4 +- dashboard/src/views/ExtensionPage.vue | 586 ++++++++++++-------------- dashboard/src/views/PlatformPage.vue | 4 +- dashboard/src/views/ProviderPage.vue | 6 +- 4 files changed, 283 insertions(+), 317 deletions(-) diff --git a/astrbot/dashboard/routes/config.py b/astrbot/dashboard/routes/config.py index 5b29fca0d..15fb53402 100644 --- a/astrbot/dashboard/routes/config.py +++ b/astrbot/dashboard/routes/config.py @@ -149,9 +149,10 @@ class ConfigRoute(Route): plugin_name = request.args.get("plugin_name", "unknown") try: await self._save_plugin_configs(post_configs, plugin_name) + await self.core_lifecycle.plugin_manager.reload(plugin_name) return ( Response() - .ok(None, f"保存插件 {plugin_name} 成功~ 机器人正在重载配置。") + .ok(None, f"保存插件 {plugin_name} 成功~ 机器人正在热重载插件。") .__dict__ ) except Exception as e: @@ -315,6 +316,5 @@ class ConfigRoute(Route): try: save_config(post_configs, md.config) - self.core_lifecycle.restart() except Exception as e: raise e diff --git a/dashboard/src/views/ExtensionPage.vue b/dashboard/src/views/ExtensionPage.vue index 3250cf5d0..145b32da9 100644 --- a/dashboard/src/views/ExtensionPage.vue +++ b/dashboard/src/views/ExtensionPage.vue @@ -6,6 +6,232 @@ import ConsoleDisplayer from '@/components/shared/ConsoleDisplayer.vue'; import axios from 'axios'; import { useCommonStore } from '@/stores/common'; +// 将所有状态和方法迁移到 setup 语法中 +import { ref, computed, onMounted, reactive } from 'vue'; + +const commonStore = useCommonStore(); +const extension_data = reactive({ + data: [], + message: "" +}); +const showReserved = ref(false); +const snack_message = ref(""); +const snack_show = ref(false); +const snack_success = ref("success"); +const configDialog = ref(false); +const extension_config = reactive({ + metadata: {}, + config: {} +}); +const pluginMarketData = ref([]); +const loadingDialog = reactive({ + show: false, + title: "加载中...", + statusCode: 0, // 0: loading, 1: success, 2: error, + result: "" +}); +const showPluginInfoDialog = ref(false); +const selectedPlugin = ref({}); +const curr_namespace = ref(""); +const wfr = ref(null); + +const plugin_handler_info_headers = [ + { title: '行为类型', key: 'event_type_h' }, + { title: '描述', key: 'desc', maxWidth: '250px' }, + { title: '具体类型', key: 'type' }, + { title: '触发方式', key: 'cmd' }, +]; + +const filteredExtensions = computed(() => { + if (showReserved.value) { + return extension_data.data; + } + return extension_data.data.filter(ext => !ext.reserved); +}); + +// 方法 +const toggleShowReserved = () => { + showReserved.value = !showReserved.value; +}; + +const toast = (message, success) => { + snack_message.value = message; + snack_show.value = true; + snack_success.value = success; +}; + +const resetLoadingDialog = () => { + loadingDialog.show = false; + loadingDialog.title = "加载中..."; + loadingDialog.statusCode = 0; + loadingDialog.result = ""; +}; + +const onLoadingDialogResult = (statusCode, result, timeToClose = 2000) => { + loadingDialog.statusCode = statusCode; + loadingDialog.result = result; + if (timeToClose === -1) return; + setTimeout(resetLoadingDialog, timeToClose); +}; + +const getExtensions = async () => { + try { + const res = await axios.get('/api/plugin/get'); + Object.assign(extension_data, res.data); + checkUpdate(); + } catch (err) { + toast(err, "error"); + } +}; + +const checkUpdate = () => { + const onlinePluginsMap = new Map(); + const onlinePluginsNameMap = new Map(); + + pluginMarketData.value.forEach(plugin => { + if (plugin.repo) { + onlinePluginsMap.set(plugin.repo.toLowerCase(), plugin); + } + onlinePluginsNameMap.set(plugin.name, plugin); + }); + + extension_data.data.forEach(extension => { + const repoKey = extension.repo?.toLowerCase(); + const onlinePlugin = repoKey ? onlinePluginsMap.get(repoKey) : null; + const onlinePluginByName = onlinePluginsNameMap.get(extension.name); + const matchedPlugin = onlinePlugin || onlinePluginByName; + + if (matchedPlugin) { + extension.online_version = matchedPlugin.version; + extension.has_update = extension.version !== matchedPlugin.version && + matchedPlugin.version !== "未知"; + } else { + extension.has_update = false; + } + }); +}; + +const uninstallExtension = async (extension_name) => { + toast("正在卸载" + extension_name, "primary"); + try { + const res = await axios.post('/api/plugin/uninstall', { name: extension_name }); + if (res.data.status === "error") { + toast(res.data.message, "error"); + return; + } + Object.assign(extension_data, res.data); + toast(res.data.message, "success"); + getExtensions(); + } catch (err) { + toast(err, "error"); + } +}; + +const updateExtension = async (extension_name) => { + loadingDialog.show = true; + try { + const res = await axios.post('/api/plugin/update', { + name: extension_name, + proxy: localStorage.getItem('selectedGitHubProxy') || "" + }); + + if (res.data.status === "error") { + onLoadingDialogResult(2, res.data.message, -1); + return; + } + + Object.assign(extension_data, res.data); + onLoadingDialogResult(1, res.data.message); + } catch (err) { + toast(err, "error"); + } +}; + +const pluginOn = async (extension) => { + try { + const res = await axios.post('/api/plugin/on', { name: extension.name }); + if (res.data.status === "error") { + toast(res.data.message, "error"); + return; + } + toast(res.data.message, "success"); + getExtensions(); + } catch (err) { + toast(err, "error"); + } +}; + +const pluginOff = async (extension) => { + try { + const res = await axios.post('/api/plugin/off', { name: extension.name }); + if (res.data.status === "error") { + toast(res.data.message, "error"); + return; + } + toast(res.data.message, "success"); + getExtensions(); + } catch (err) { + toast(err, "error"); + } +}; + +const openExtensionConfig = async (extension_name) => { + curr_namespace.value = extension_name; + configDialog.value = true; + try { + const res = await axios.get('/api/config/get?plugin_name=' + extension_name); + extension_config.metadata = res.data.data.metadata; + extension_config.config = res.data.data.config; + } catch (err) { + toast(err, "error"); + } +}; + +const updateConfig = async () => { + try { + const res = await axios.post('/api/config/plugin/update?plugin_name=' + curr_namespace.value, extension_config.config); + if (res.data.status === "ok") { + toast(res.data.message, "success"); + } else { + toast(res.data.message, "error"); + } + configDialog.value = false; + } catch (err) { + toast(err, "error"); + } +}; + +const showPluginInfo = (plugin) => { + selectedPlugin.value = plugin; + showPluginInfoDialog.value = true; +}; + +const reloadPlugin = async (plugin_name) => { + try { + const res = await axios.post('/api/plugin/reload', { name: plugin_name }); + if (res.data.status === "error") { + toast(res.data.message, "error"); + return; + } + toast("重载成功", "success"); + getExtensions(); + } catch (err) { + toast(err, "error"); + } +}; + +// 生命周期 +onMounted(async () => { + await getExtensions(); + + try { + const data = await commonStore.getPluginCollections(); + pluginMarketData.value = data; + checkUpdate(); + } catch (err) { + console.error("获取插件市场数据失败:", err); + } +}); \ No newline at end of file diff --git a/dashboard/src/views/PlatformPage.vue b/dashboard/src/views/PlatformPage.vue index f93ab25ea..f2d542dd9 100644 --- a/dashboard/src/views/PlatformPage.vue +++ b/dashboard/src/views/PlatformPage.vue @@ -89,8 +89,8 @@ -
- +
+
diff --git a/dashboard/src/views/ProviderPage.vue b/dashboard/src/views/ProviderPage.vue index 1e8fd97c3..5410d49d5 100644 --- a/dashboard/src/views/ProviderPage.vue +++ b/dashboard/src/views/ProviderPage.vue @@ -73,12 +73,10 @@ -
- +
+
- -