From 08294e0c7f5ae7c6b81b839753205d6c51c7dd06 Mon Sep 17 00:00:00 2001 From: abearxiong Date: Thu, 5 Mar 2026 23:07:45 +0800 Subject: [PATCH] feat: update @kevisual/api to version 0.0.62 and add ProxyStatusDialog component for monitoring proxy status --- package.json | 2 +- pnpm-lock.yaml | 10 +- .../studio/components/ProxyStatusDialog.tsx | 135 ++++++++++++++++++ src/pages/studio/index.tsx | 15 +- src/pages/studio/store.ts | 19 ++- src/pages/view/list.tsx | 6 +- 6 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 src/pages/studio/components/ProxyStatusDialog.tsx diff --git a/package.json b/package.json index 58edf60..ea08c5f 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "zustand": "^5.0.11" }, "devDependencies": { - "@kevisual/api": "^0.0.60", + "@kevisual/api": "^0.0.62", "@kevisual/context": "^0.0.8", "@kevisual/js-filter": "^0.0.5", "@kevisual/kv-login": "^0.1.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a47b36..26a064f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,8 +88,8 @@ importers: version: 5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: '@kevisual/api': - specifier: ^0.0.60 - version: 0.0.60(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + specifier: ^0.0.62 + version: 0.0.62(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) '@kevisual/context': specifier: ^0.0.8 version: 0.0.8 @@ -528,8 +528,8 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@kevisual/api@0.0.60': - resolution: {integrity: sha512-NTFDx1ns/iGli2fUJLJZRWu8nf5VkXV+sOQUqGGAJvrvGATvXSuITu6mD4P/aDQakx4hzQUPr9wDTZoNk7+RqQ==} + '@kevisual/api@0.0.62': + resolution: {integrity: sha512-GB8Ho2absXoXoZP2GKyuoRqRqjdwtV0JR512DXBaKJR2sIPn1KvuglbBiX+zPjDBBskv/ApvZKOoSwj1OmkrKQ==} '@kevisual/context@0.0.8': resolution: {integrity: sha512-DTJpyHI34NE76B7g6f+QlIqiCCyqI2qkBMQE736dzeRDGxOjnbe2iQY9W+Rt2PE6kmymM3qyOmSfNovyWyWrkA==} @@ -2902,7 +2902,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@kevisual/api@0.0.60(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))': + '@kevisual/api@0.0.62(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))': dependencies: '@kevisual/context': 0.0.8 '@kevisual/js-filter': 0.0.5 diff --git a/src/pages/studio/components/ProxyStatusDialog.tsx b/src/pages/studio/components/ProxyStatusDialog.tsx new file mode 100644 index 0000000..45c05e6 --- /dev/null +++ b/src/pages/studio/components/ProxyStatusDialog.tsx @@ -0,0 +1,135 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { useStudioStore } from '../store'; +import { useShallow } from 'zustand/shallow'; +import { Activity, RefreshCw, CheckCircle, XCircle } from 'lucide-react'; +import { useState, useEffect, useRef } from 'react'; +import { cloneDeep } from 'es-toolkit'; +import { RouterViewItem } from '@kevisual/api/proxy'; + +export const ProxyStatusDialog = () => { + const { showProxyStatus, setShowProxyStatus, getQueryProxyStatus } = useStudioStore( + useShallow((state) => ({ + showProxyStatus: state.showProxyStatus, + setShowProxyStatus: state.setShowProxyStatus, + getQueryProxyStatus: state.getQueryProxyStatus, + })) + ); + + const [statusList, setStatusList] = useState([]); + const [loading, setLoading] = useState(false); + const intervalRef = useRef | null>(null); + + const loadStatus = async () => { + setLoading(true); + try { + const status = await getQueryProxyStatus(); + console.log('Loaded proxy status:', status); + setStatusList(cloneDeep(status)); + } catch (error) { + console.error('Failed to load proxy status:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + if (showProxyStatus) { + loadStatus(); + // 每5秒刷新一次 + intervalRef.current = setInterval(loadStatus, 5000); + } else { + // 关闭弹窗时清除定时器 + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + } + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }; + }, [showProxyStatus]); + + const getStatusStyle = (status?: string) => { + switch (status) { + case 'active': + return { + icon: , + className: 'bg-green-100 text-green-700 border-green-200', + }; + case 'error': + return { + icon: , + className: 'bg-red-100 text-red-700 border-red-200', + }; + default: + return { + icon: null, + className: 'bg-gray-100 text-gray-700 border-gray-200', + }; + } + }; + + return ( + + + + + + Proxy Router 状态 + + + + +
+ {loading ? ( +
加载中...
+ ) : statusList.length === 0 ? ( +
暂无数据
+ ) : ( +
+ {statusList.map((item) => { + const statusStyle = getStatusStyle(item.routerStatus); + return ( +
+
+ {item.title} + {item.id} +
+ {item.type === 'api' && item.api?.url && ( +
+ URL: + {item.api?.url} +
+ )} + {item.routerStatus && ( +
+ + {statusStyle.icon} + {item.routerStatus} + +
+ )} +
+ ); + })} +
+ )} +
+
+
+ ); +}; diff --git a/src/pages/studio/index.tsx b/src/pages/studio/index.tsx index 7d092d5..c7c2144 100644 --- a/src/pages/studio/index.tsx +++ b/src/pages/studio/index.tsx @@ -1,6 +1,6 @@ import { filterRouteInfo, useStudioStore } from './store.ts'; import { use, useEffect, useState } from 'react'; -import { MonitorPlay, Play, PanelLeft, PanelLeftClose, PanelRight, PanelRightClose, PanelTop, PanelTopClose, Filter, FilterX, Search, X, MoreHorizontal, Info, Code, RotateCcw, Book, FolderClosed } from 'lucide-react'; +import { MonitorPlay, Play, PanelLeft, PanelLeftClose, PanelRight, PanelRightClose, PanelTop, PanelTopClose, Filter, FilterX, Search, X, MoreHorizontal, Info, Code, RotateCcw, Book, FolderClosed, Activity } from 'lucide-react'; import { Panel, Group } from 'react-resizable-panels' import { ViewList } from '../view/list.tsx'; import { useShallow } from 'zustand/shallow'; @@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button.tsx'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu.tsx'; import { ExportDialog } from './components/ExportDialog'; import { RouterGroupDialog } from './components/RouterGroupDialog'; +import { ProxyStatusDialog } from './components/ProxyStatusDialog'; import { useQueryViewStore } from '../query-view/store/index.ts'; import { toast } from 'sonner'; import { DetailsDialog } from '../query-view/components/DetailsDialog.tsx'; @@ -117,6 +118,7 @@ export const App = () => { setShowExportDialog: state.setShowExportDialog, setExportRoutes: state.setExportRoutes, setShowRouterGroup: state.setShowRouterGroup, + setShowProxyStatus: state.setShowProxyStatus, }))); const queryViewStore = useQueryViewStore(useShallow((state) => ({ setShowDetailsDialog: state.setShowDetailsDialog, @@ -191,6 +193,7 @@ export const App = () => {
+ {loading &&
加载中...
} {store.showFilter && (
@@ -367,6 +370,16 @@ export const App = () => { })}
+ @@ -228,7 +228,7 @@ export const ViewList = () => {
-
+
{filteredViews.length === 0 ? (
{searchTerm ? '未找到匹配的视图' : '暂无视图'}