From 3917cb0dc9664fa61eeefb4431d5fbe343e1f41c Mon Sep 17 00:00:00 2001 From: bietiaop <1527109126@qq.com> Date: Sun, 26 Jan 2025 21:48:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20webui=E6=A3=80=E6=9F=A5=E6=9B=B4?= =?UTF-8?q?=E6=96=B0&=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97=E5=AD=97?= =?UTF-8?q?=E4=BD=93=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- napcat.webui/package.json | 5 +- .../src/components/log_com/realtime.tsx | 2 - napcat.webui/src/components/modal.tsx | 19 +- napcat.webui/src/components/system_info.tsx | 173 +++++++++++++++--- .../src/components/tailwind_markdown.tsx | 49 +++++ napcat.webui/src/components/xterm.tsx | 30 ++- napcat.webui/src/contexts/dialog.tsx | 38 +--- napcat.webui/src/pages/dashboard/about.tsx | 9 +- napcat.webui/src/utils/version.ts | 36 ++++ 9 files changed, 287 insertions(+), 74 deletions(-) create mode 100644 napcat.webui/src/components/tailwind_markdown.tsx create mode 100644 napcat.webui/src/utils/version.ts diff --git a/napcat.webui/package.json b/napcat.webui/package.json index 1649d9dc..7017a4c4 100644 --- a/napcat.webui/package.json +++ b/napcat.webui/package.json @@ -10,8 +10,6 @@ "preview": "vite preview" }, "dependencies": { - "@monaco-editor/loader": "^1.4.0", - "@monaco-editor/react": "4.7.0-rc.0", "@heroui/avatar": "2.2.7", "@heroui/breadcrumbs": "2.2.7", "@heroui/button": "2.2.10", @@ -38,6 +36,8 @@ "@heroui/tabs": "2.2.8", "@heroui/theme": "2.4.6", "@heroui/tooltip": "2.2.8", + "@monaco-editor/loader": "^1.4.0", + "@monaco-editor/react": "4.7.0-rc.0", "@react-aria/visually-hidden": "3.8.18", "@reduxjs/toolkit": "^2.5.0", "@uidotdev/usehooks": "^2.4.1", @@ -62,6 +62,7 @@ "react-hook-form": "^7.54.2", "react-hot-toast": "^2.4.1", "react-icons": "^5.4.0", + "react-markdown": "^9.0.3", "react-redux": "^9.2.0", "react-responsive": "^10.0.0", "react-router-dom": "7.1.0", diff --git a/napcat.webui/src/components/log_com/realtime.tsx b/napcat.webui/src/components/log_com/realtime.tsx index 7c14a827..ceab7c47 100644 --- a/napcat.webui/src/components/log_com/realtime.tsx +++ b/napcat.webui/src/components/log_com/realtime.tsx @@ -47,7 +47,6 @@ const RealTimeLogs = () => { } return logLevel.has(log.level) }) - .slice(-100) .map((log) => colorizeLogLevelWithTag(log.message, log.level)) .join('\r\n') Xterm.current?.clear() @@ -65,7 +64,6 @@ const RealTimeLogs = () => { useEffect(() => { const subscribeLogs = () => { try { - console.log('subscribeLogs') const source = WebUIManager.getRealTimeLogs((data) => { setDataArr((prev) => { const newData = [...prev, ...data] diff --git a/napcat.webui/src/components/modal.tsx b/napcat.webui/src/components/modal.tsx index 38a871e8..25bdfa6b 100644 --- a/napcat.webui/src/components/modal.tsx +++ b/napcat.webui/src/components/modal.tsx @@ -13,12 +13,15 @@ export interface ModalProps { content: React.ReactNode title?: React.ReactNode size?: React.ComponentProps['size'] + scrollBehavior?: React.ComponentProps['scrollBehavior'] onClose?: () => void onConfirm?: () => void onCancel?: () => void backdrop?: 'opaque' | 'blur' | 'transparent' showCancel?: boolean dismissible?: boolean + confirmText?: string + cancelText?: string } const Modal: React.FC = React.memo((props) => { @@ -26,12 +29,14 @@ const Modal: React.FC = React.memo((props) => { backdrop = 'blur', title, content, - size = 'md', showCancel = true, dismissible, + confirmText = '确定', + cancelText = '取消', onClose, onConfirm, - onCancel + onCancel, + ...rest } = props const { onClose: onNativeClose } = useDisclosure() @@ -44,11 +49,11 @@ const Modal: React.FC = React.memo((props) => { onClose?.() onNativeClose() }} - size={size} classNames={{ backdrop: 'z-[99999999]', wrapper: 'z-[999999999]' }} + {...rest} > {(nativeClose) => ( @@ -56,7 +61,7 @@ const Modal: React.FC = React.memo((props) => { {title && ( {title} )} - {content} + {content} {showCancel && ( )} diff --git a/napcat.webui/src/components/system_info.tsx b/napcat.webui/src/components/system_info.tsx index 2223bd51..1d101054 100644 --- a/napcat.webui/src/components/system_info.tsx +++ b/napcat.webui/src/components/system_info.tsx @@ -1,45 +1,188 @@ +import { Button } from '@heroui/button' import { Card, CardBody, CardHeader } from '@heroui/card' +import { Chip } from '@heroui/chip' import { Spinner } from '@heroui/spinner' +import { Tooltip } from '@heroui/tooltip' import { useRequest } from 'ahooks' -import { FaCircleInfo } from 'react-icons/fa6' -import { FaQq } from 'react-icons/fa6' +import { FaCircleInfo, FaInfo, FaQq } from 'react-icons/fa6' import { IoLogoChrome, IoLogoOctocat } from 'react-icons/io' import { RiMacFill } from 'react-icons/ri' +import useDialog from '@/hooks/use-dialog' + +import { request } from '@/utils/request' +import { compareVersion } from '@/utils/version' + import WebUIManager from '@/controllers/webui_manager' +import { GithubRelease } from '@/types/github' import packageJson from '../../package.json' +import TailwindMarkdown from './tailwind_markdown' export interface SystemInfoItemProps { title: string icon?: React.ReactNode value?: React.ReactNode + endContent?: React.ReactNode } const SystemInfoItem: React.FC = ({ title, value = '--', - icon + icon, + endContent }) => { return (
{icon}
{title}
{value}
+
{endContent}
) } +export interface NewVersionTipProps { + currentVersion?: string +} + +const NewVersionTip = (props: NewVersionTipProps) => { + const { currentVersion } = props + const dialog = useDialog() + const { data: releaseData, error } = useRequest(() => + request.get( + 'https://api.github.com/repos/NapNeko/NapCatQQ/releases' + ) + ) + + if (error) { + return ( + + + + ) + } + + const latestVersion = releaseData?.data?.[0]?.tag_name + + if (!latestVersion || !currentVersion) { + return null + } + + if (compareVersion(latestVersion, currentVersion) <= 0) { + return null + } + + const middleVersions: GithubRelease[] = [] + + for (let i = 0; i < releaseData.data.length; i++) { + const versionInfo = releaseData.data[i] + if (compareVersion(versionInfo.tag_name, currentVersion) > 0) { + middleVersions.push(versionInfo) + } else { + break + } + } + + return ( + + + + ) +} + +const NapCatVersion = () => { + const { + data: packageData, + loading: packageLoading, + error: packageError + } = useRequest(WebUIManager.getPackageInfo) + + const currentVersion = packageData?.version + + return ( + } + value={ + packageError ? ( + `错误:${packageError.message}` + ) : packageLoading ? ( + + ) : ( + currentVersion + ) + } + endContent={} + /> + ) +} + export interface SystemInfoProps { archInfo?: string } const SystemInfo: React.FC = (props) => { const { archInfo } = props - const { - data: packageData, - loading: packageLoading, - error: packageError - } = useRequest(WebUIManager.getPackageInfo) const { data: qqVersionData, loading: qqVersionLoading, @@ -53,19 +196,7 @@ const SystemInfo: React.FC = (props) => {
- } - value={ - packageError ? ( - `错误:${packageError.message}` - ) : packageLoading ? ( - - ) : ( - packageData?.version - ) - } - /> + } diff --git a/napcat.webui/src/components/tailwind_markdown.tsx b/napcat.webui/src/components/tailwind_markdown.tsx new file mode 100644 index 00000000..c02af8b5 --- /dev/null +++ b/napcat.webui/src/components/tailwind_markdown.tsx @@ -0,0 +1,49 @@ +import Markdown from 'react-markdown' +import remarkGfm from 'remark-gfm' + +const TailwindMarkdown: React.FC<{ content: string }> = ({ content }) => { + return ( + ( +

+ ), + h2: ({ node, ...props }) => ( +

+ ), + h3: ({ node, ...props }) => ( +

+ ), + p: ({ node, ...props }) =>

, + a: ({ node, ...props }) => ( + + ), + ul: ({ node, ...props }) => ( +

) } diff --git a/napcat.webui/src/utils/version.ts b/napcat.webui/src/utils/version.ts new file mode 100644 index 00000000..9763f82c --- /dev/null +++ b/napcat.webui/src/utils/version.ts @@ -0,0 +1,36 @@ +/** + * 版本号转为数字 + * @param version 版本号 + * @returns 版本号数字 + */ +export const versionToNumber = (version: string): number => { + const finalVersionString = version.replace(/^v/, '') + + const versionArray = finalVersionString.split('.') + const versionNumber = + parseInt(versionArray[2]) + + parseInt(versionArray[1]) * 100 + + parseInt(versionArray[0]) * 10000 + + return versionNumber +} + +/** + * 比较版本号 + * @param version1 版本号1 + * @param version2 版本号2 + * @returns 比较结果 + * 0: 相等 + * 1: version1 > version2 + * -1: version1 < version2 + */ +export const compareVersion = (version1: string, version2: string): number => { + const versionNumber1 = versionToNumber(version1) + const versionNumber2 = versionToNumber(version2) + + if (versionNumber1 === versionNumber2) { + return 0 + } + + return versionNumber1 > versionNumber2 ? 1 : -1 +}