diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index 092dcf6d33..034e742848 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -1281,6 +1281,7 @@ async def process_plugin(plugin): for plugin, logo_url, pages in results: _t = { "name": plugin.name, + "marketplace_name": (plugin.name or "").replace("_", "-"), "repo": "" if plugin.repo is None else str(plugin.repo), "author": plugin.author, "desc": plugin.desc, @@ -1332,6 +1333,7 @@ async def get_plugin_detail(self): .ok( { "name": plugin.name, + "marketplace_name": (plugin.name or "").replace("_", "-"), "repo": "" if plugin.repo is None else str(plugin.repo), "author": plugin.author, "desc": plugin.desc, @@ -1384,6 +1386,7 @@ async def get_plugin_page_components(self, plugin) -> list[dict]: "i18n_key": page["i18n_key"], "description": "Plugin Page entry", "plugin_name": plugin.name, + "plugin_marketplace_name": (plugin.name or "").replace("_", "-"), } for page in pages ] diff --git a/dashboard/src/views/extension/useExtensionPage.js b/dashboard/src/views/extension/useExtensionPage.js index 8db1a9c33c..05b3c11461 100644 --- a/dashboard/src/views/extension/useExtensionPage.js +++ b/dashboard/src/views/extension/useExtensionPage.js @@ -484,7 +484,7 @@ export const useExtensionPage = () => { const failRes = await axios.get("/api/plugin/source/get-failed-plugins"); failedPluginsDict.value = failRes.data.data || {}; - checkUpdate(); + // checkUpdate() is called after pluginMarketData is loaded in onMounted } catch (err) { toast(err, "error"); } finally { @@ -642,14 +642,20 @@ export const useExtensionPage = () => { if (plugin.repo) { onlinePluginsMap.set(normalizeInstallUrl(plugin.repo).toLowerCase(), plugin); } - onlinePluginsNameMap.set(plugin.name, plugin); + const normalizedName = normalizeStr(plugin.name); + onlinePluginsNameMap.set(normalizedName, plugin); }); const data = Array.isArray(extension_data?.data) ? extension_data.data : []; + data.forEach((extension) => { const repoKey = extension.repo ? normalizeInstallUrl(extension.repo).toLowerCase() : undefined; const onlinePlugin = repoKey ? onlinePluginsMap.get(repoKey) : null; - const onlinePluginByName = onlinePluginsNameMap.get(extension.name); + + // 使用 marketplace_name 进行市场匹配(后端已统一为减号格式) + const normalizedExtensionName = normalizeStr(extension.marketplace_name); + const onlinePluginByName = onlinePluginsNameMap.get(normalizedExtensionName); + const matchedPlugin = onlinePlugin || onlinePluginByName; if (matchedPlugin) { @@ -1233,17 +1239,22 @@ export const useExtensionPage = () => { const checkAlreadyInstalled = () => { const data = Array.isArray(extension_data?.data) ? extension_data.data : []; + // repo 匹配:两边统一使用 normalizeInstallUrl + const installedRepos = new Set( + data + .filter((ext) => ext.repo) + .map((ext) => normalizeInstallUrl(ext.repo).toLowerCase()), + ); + // 使用 marketplace_name 进行市场匹配(后端已统一为减号格式) + const installedNames = new Set(data.map((ext) => normalizeStr(ext.marketplace_name))); + // 创建映射用于查询已安装插件的详细信息 const installedByRepo = new Map( data .filter((ext) => ext.repo) .map((ext) => [normalizeInstallUrl(ext.repo).toLowerCase(), ext]), ); - const installedRepos = new Set(installedByRepo.keys()); - const installedNames = new Set( - data.map((ext) => normalizeStr(ext.name).replace(/_/g, "-")), - ); //统一格式,以防下面的匹配不生效 - const installedByName = new Map(data.map((ext) => [ext.name, ext])); - + const installedByName = new Map(data.map((ext) => [ext.marketplace_name, ext])); + for (let i = 0; i < pluginMarketData.value.length; i++) { const plugin = pluginMarketData.value[i]; const matchedInstalled = @@ -1265,8 +1276,8 @@ export const useExtensionPage = () => { } plugin.installed = - (plugin.repo && installedRepos.has(normalizeInstallUrl(plugin.repo).toLowerCase())) || - installedNames.has(normalizeStr(plugin.name).replace(/_/g, "-")); //统一格式,防止匹配失败 + installedRepos.has(normalizeInstallUrl(plugin.repo).toLowerCase()) || + installedNames.has(normalizeStr(plugin.name)); } let installed = []; @@ -1477,6 +1488,7 @@ export const useExtensionPage = () => { selectedMarketInstallPlugin.value = null; await getExtensions(); checkAlreadyInstalled(); + checkUpdate(); viewReadme({ name: resData.data.name, diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index 11870b3ddc..ef6edc899d 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -889,6 +889,7 @@ async def test_plugin_detail_includes_scanned_page_component( "i18n_key": f"pages.{PLUGIN_PAGE_DEMO_PAGE_NAME}", "description": "Plugin Page entry", "plugin_name": PLUGIN_PAGE_DEMO_NAME, + "plugin_marketplace_name": PLUGIN_PAGE_DEMO_NAME.replace("_", "-"), } ]