mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-06-13 19:49:56 +08:00
feat(system): 添加后端初始化和健康检查功能
- 新增后端初始化对话框组件 - 实现后端健康检查和初始化逻辑 - 在 App 组件中集成后端初始化和健康检查 - 新增系统健康检查 API 和相关服务
This commit is contained in:
@@ -2,10 +2,10 @@ import './App.css'
|
||||
import { HomePage } from './pages/HomePage/Home.tsx'
|
||||
import { useTaskPolling } from '@/hooks/useTaskPolling.ts'
|
||||
import SettingPage from './pages/SettingPage/index.tsx'
|
||||
import { BrowserRouter,HashRouter, Navigate, Routes } from 'react-router-dom'
|
||||
import { BrowserRouter, HashRouter, Navigate, Routes } from 'react-router-dom'
|
||||
import { Route } from 'react-router-dom'
|
||||
import Index from '@/pages/Index.tsx'
|
||||
import NotFoundPage from '@/pages/NotFoundPage' //
|
||||
import NotFoundPage from '@/pages/NotFoundPage'
|
||||
import Model from '@/pages/SettingPage/Model.tsx'
|
||||
import Transcriber from '@/pages/SettingPage/transcriber.tsx'
|
||||
import ProviderForm from '@/components/Form/modelForm/Form.tsx'
|
||||
@@ -15,15 +15,32 @@ import Prompt from '@/pages/SettingPage/Prompt.tsx'
|
||||
import AboutPage from '@/pages/SettingPage/about.tsx'
|
||||
import Downloader from '@/pages/SettingPage/Downloader.tsx'
|
||||
import DownloaderForm from '@/components/Form/DownloaderForm/Form.tsx'
|
||||
import { useEffect } from 'react'
|
||||
import { systemCheck } from '@/services/system.ts'
|
||||
import { useCheckBackend } from '@/hooks/useCheckBackend.ts'
|
||||
import BackendInitDialog from '@/components/BackendInitDialog'
|
||||
|
||||
function App() {
|
||||
useTaskPolling(3000) // 每 3 秒轮询一次
|
||||
const steps = [
|
||||
{ label: '解析链接', key: 'PARSING', icon: <Downloading /> },
|
||||
{ label: '下载音频', key: 'DOWNLOADING' },
|
||||
{ label: '转写文字', key: 'TRANSCRIBING' },
|
||||
{ label: '总结内容', key: 'SUMMARIZING' },
|
||||
{ label: '保存完成', key: 'SUCCESS' },
|
||||
]
|
||||
const { loading, initialized } = useCheckBackend()
|
||||
|
||||
// 在后端初始化完成后执行系统检查
|
||||
useEffect(() => {
|
||||
if (initialized) {
|
||||
systemCheck()
|
||||
}
|
||||
}, [initialized])
|
||||
|
||||
// 如果后端还未初始化,显示初始化对话框
|
||||
if (!initialized) {
|
||||
return (
|
||||
<>
|
||||
<BackendInitDialog open={loading} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// 后端已初始化,渲染主应用
|
||||
return (
|
||||
<>
|
||||
<HashRouter>
|
||||
@@ -34,16 +51,12 @@ function App() {
|
||||
<Route index element={<Navigate to="model" replace />} />
|
||||
<Route path="model" element={<Model />}>
|
||||
<Route path="new" element={<ProviderForm isCreate />} />
|
||||
{/*<Route index element={<Navigate to="openai" replace />} />*/}
|
||||
<Route path=":id" element={<ProviderForm />} />
|
||||
</Route>
|
||||
{/*<Route path="transcriber" element={<Transcriber />}></Route>*/}
|
||||
{/*<Route path="prompt" element={<Prompt />}></Route>*/}
|
||||
<Route path="download" element={<Downloader />}>
|
||||
<Route path=":id" element={<DownloaderForm />} />
|
||||
</Route>
|
||||
<Route path="about" element={<AboutPage />}></Route>
|
||||
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
@@ -54,4 +67,4 @@ function App() {
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App
|
||||
23
BillNote_frontend/src/components/BackendInitDialog.tsx
Normal file
23
BillNote_frontend/src/components/BackendInitDialog.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
|
||||
interface Props {
|
||||
open: boolean
|
||||
}
|
||||
|
||||
function BackendInitDialog({ open }: Props) {
|
||||
return (
|
||||
<Dialog open={open}>
|
||||
<DialogContent className="text-center">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="animate-spin w-5 h-5" />
|
||||
后端正在初始化中…
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<p className="text-muted-foreground mt-2">请稍候,系统正在启动后端服务</p>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
export default BackendInitDialog
|
||||
141
BillNote_frontend/src/components/ui/dialog.tsx
Normal file
141
BillNote_frontend/src/components/ui/dialog.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Dialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
52
BillNote_frontend/src/hooks/useCheckBackend.ts
Normal file
52
BillNote_frontend/src/hooks/useCheckBackend.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const MAX_RETRIES = 3
|
||||
const RETRY_INTERVAL = 10000 // 10秒
|
||||
|
||||
export const useCheckBackend = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let retries = 0
|
||||
|
||||
const check = async () => {
|
||||
try {
|
||||
await request.get('/sys_health')
|
||||
setInitialized(true)
|
||||
setLoading(false)
|
||||
} catch {
|
||||
if (retries === 0) {
|
||||
// 第一次失败时开始显示加载状态
|
||||
setLoading(true)
|
||||
}
|
||||
|
||||
if (retries < MAX_RETRIES) {
|
||||
retries++
|
||||
setTimeout(check, RETRY_INTERVAL)
|
||||
} else {
|
||||
// 达到重试上限,继续轮询直到后端就绪
|
||||
waitUntilBackendReady()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const waitUntilBackendReady = async () => {
|
||||
while (true) {
|
||||
try {
|
||||
await request.get('/sys_health')
|
||||
setInitialized(true)
|
||||
setLoading(false)
|
||||
break
|
||||
} catch {
|
||||
await new Promise(res => setTimeout(res, RETRY_INTERVAL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check()
|
||||
}, [])
|
||||
|
||||
return { loading, initialized }
|
||||
}
|
||||
5
BillNote_frontend/src/services/system.ts
Normal file
5
BillNote_frontend/src/services/system.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export const systemCheck=async()=>{
|
||||
return await request.get('/sys_health')
|
||||
}
|
||||
Reference in New Issue
Block a user