From a28ae49960902b53337767062f6d5c72dbee1716 Mon Sep 17 00:00:00 2001 From: Qiao Date: Sun, 1 Feb 2026 10:44:38 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"=E7=A7=BB=E9=99=A4=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E4=B8=AD=E7=9A=84=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E4=B8=BB=E9=A1=B5=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B1=95=E7=A4=BA=E7=BB=84=E4=BB=B6=E4=BB=A5?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=E3=80=82?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E6=8F=92=E4=BB=B6=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E7=9A=84=E6=A0=B7=E5=BC=8F=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E6=9B=B4=E5=A5=BD=E7=9A=84=E7=94=A8=E6=88=B7=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E3=80=82"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1d22f19fa6d9929fa31b59d43f0228fe5060d1f5. --- .../napcat-onebot/network/plugin/types.ts | 1 + .../napcat-webui-backend/src/api/Plugin.ts | 2 + .../src/components/display_card/container.tsx | 2 +- .../display_card/plugin_store_card.tsx | 88 +++++++++---------- .../src/controllers/plugin_manager.ts | 2 + .../src/pages/dashboard/plugin.tsx | 35 +++++++- .../pages/dashboard/plugin_config_modal.tsx | 21 ++++- 7 files changed, 98 insertions(+), 53 deletions(-) diff --git a/packages/napcat-onebot/network/plugin/types.ts b/packages/napcat-onebot/network/plugin/types.ts index 448b276b..5469816d 100644 --- a/packages/napcat-onebot/network/plugin/types.ts +++ b/packages/napcat-onebot/network/plugin/types.ts @@ -13,6 +13,7 @@ export interface PluginPackageJson { main?: string; description?: string; author?: string; + homepage?: string; } // ==================== 插件配置 Schema ==================== diff --git a/packages/napcat-webui-backend/src/api/Plugin.ts b/packages/napcat-webui-backend/src/api/Plugin.ts index 0367ba2b..d1ca0b98 100644 --- a/packages/napcat-webui-backend/src/api/Plugin.ts +++ b/packages/napcat-webui-backend/src/api/Plugin.ts @@ -72,6 +72,7 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: string; description: string; author: string; + homepage?: string; status: string; hasConfig: boolean; hasPages: boolean; @@ -109,6 +110,7 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { version: p.version || '0.0.0', description: p.packageJson?.description || '', author: p.packageJson?.author || '', + homepage: p.packageJson?.homepage, status, hasConfig: !!(p.runtime.module?.plugin_config_schema || p.runtime.module?.plugin_config_ui), hasPages diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index 99d66630..54743776 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -4,7 +4,7 @@ import clsx from 'clsx'; import key from '@/const/key'; export interface ContainerProps { - title: React.ReactNode; + title: string; tag?: React.ReactNode; action: React.ReactNode; enableSwitch: React.ReactNode; diff --git a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx index 53fd3ba4..0ea60016 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/plugin_store_card.tsx @@ -2,8 +2,8 @@ import { Button } from '@heroui/button'; import { Chip } from '@heroui/chip'; import { Tooltip } from '@heroui/tooltip'; import { useState } from 'react'; -import clsx from 'clsx'; import { IoMdDownload, IoMdRefresh, IoMdCheckmarkCircle } from 'react-icons/io'; +import { FaGithub } from 'react-icons/fa'; import DisplayCardContainer from './container'; import { PluginStoreItem } from '@/types/plugin-store'; @@ -59,32 +59,28 @@ const PluginStoreCard: React.FC = ({ return ( - {homepage ? ( - - window.open(homepage, '_blank')} - > - {name} - - - ) : ( - {name} - )} - v{version} - - } + title={name} tag={
+ {homepage && ( + + + + )} {installStatus === 'installed' && ( } + startContent={} > 已安装 @@ -94,11 +90,17 @@ const PluginStoreCard: React.FC = ({ color="warning" size="sm" variant="flat" - className="h-6 text-[10px] bg-warning-50 dark:bg-warning-500/10 text-warning-600" > 可更新 )} + + v{version} +
} enableSwitch={undefined} @@ -117,46 +119,38 @@ const PluginStoreCard: React.FC = ({ } > -
+
{/* 作者和包名 */} -
- - 作者: {author || '未知'} - - / +
+ 作者: {author || '未知'} + · - {id} + {id}
{/* 描述 */} - -
-
- {description || '暂无描述'} -
+
+
+ {description || '暂无描述'}
- +
- {/* 标签栏 - 优化后的极简风格 */} -
+ {/* 标签 */} +
{tags && tags.length > 0 ? ( - tags.map((tag, index) => ( -
( + - {tag} -
+ )) ) : ( - no tags + 暂无标签 )}
diff --git a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts index 1e36a917..d805ad3d 100644 --- a/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts +++ b/packages/napcat-webui-frontend/src/controllers/plugin_manager.ts @@ -16,6 +16,8 @@ export interface PluginItem { description: string; /** 作者 */ author: string; + /** 主页链接 */ + homepage?: string; /** 状态: active-运行中, disabled-已禁用, stopped-已停止 */ status: PluginStatus; /** 是否有配置项 */ diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx index 667ab4b6..3857fecb 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx @@ -1,5 +1,5 @@ import { Button } from '@heroui/button'; -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState, useRef, useMemo } from 'react'; import toast from 'react-hot-toast'; import { IoMdRefresh } from 'react-icons/io'; import { FiUpload } from 'react-icons/fi'; @@ -10,9 +10,11 @@ import PluginDisplayCard from '@/components/display_card/plugin_card'; import PluginManager, { PluginItem } from '@/controllers/plugin_manager'; import useDialog from '@/hooks/use-dialog'; import PluginConfigModal from '@/pages/dashboard/plugin_config_modal'; +import { PluginStoreItem } from '@/types/plugin-store'; export default function PluginPage () { const [plugins, setPlugins] = useState([]); + const [storePlugins, setStorePlugins] = useState([]); const [loading, setLoading] = useState(false); const [pluginManagerNotFound, setPluginManagerNotFound] = useState(false); const dialog = useDialog(); @@ -25,7 +27,11 @@ export default function PluginPage () { setLoading(true); setPluginManagerNotFound(false); try { - const listResult = await PluginManager.getPluginList(); + // 并行加载本地插件列表和商店插件列表 + const [listResult, storeResult] = await Promise.all([ + PluginManager.getPluginList(), + PluginManager.getPluginStoreList().catch(() => ({ plugins: [] })) + ]); if (listResult.pluginManagerNotFound) { setPluginManagerNotFound(true); @@ -33,6 +39,7 @@ export default function PluginPage () { } else { setPlugins(listResult.plugins); } + setStorePlugins(storeResult.plugins || []); } catch (e: any) { toast.error(e.message); } finally { @@ -40,6 +47,25 @@ export default function PluginPage () { } }; + // 创建一个 Map 用于快速查找商店插件的 homepage + const storeHomepageMap = useMemo(() => { + const map = new Map(); + for (const plugin of storePlugins) { + if (plugin.homepage) { + map.set(plugin.id, plugin.homepage); + } + } + return map; + }, [storePlugins]); + + // 合并本地插件和商店数据中的 homepage + const pluginsWithHomepage = useMemo(() => { + return plugins.map(plugin => ({ + ...plugin, + homepage: plugin.homepage || storeHomepageMap.get(plugin.id) + })); + }, [plugins, storeHomepageMap]); + useEffect(() => { loadPlugins(); }, []); @@ -172,6 +198,7 @@ export default function PluginPage () { isOpen={isOpen} onOpenChange={onOpenChange} pluginId={currentPluginId} + homepage={storeHomepageMap.get(currentPluginId)} />
@@ -211,11 +238,11 @@ export default function PluginPage () { 插件管理器未加载,请检查 plugins 目录是否存在

- ) : plugins.length === 0 ? ( + ) : pluginsWithHomepage.length === 0 ? (
暂时没有安装插件
) : (
- {plugins.map(plugin => ( + {pluginsWithHomepage.map(plugin => ( void; /** 插件包名 (id) */ pluginId: string; + /** 插件主页 URL */ + homepage?: string; } /** Schema 更新事件类型 */ @@ -25,7 +29,7 @@ interface SchemaUpdateEvent { afterKey?: string; } -export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: Props) { +export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId, homepage }: Props) { const [loading, setLoading] = useState(false); const [schema, setSchema] = useState([]); const [config, setConfig] = useState>({}); @@ -373,6 +377,21 @@ export default function PluginConfigModal ({ isOpen, onOpenChange, pluginId }: P )} + {homepage && ( + + + + )}