Revert "Refactor UI components for consistent styling"

This reverts commit 7e6035d98b.
This commit is contained in:
手瓜一十雪 2025-12-22 14:04:26 +08:00
parent 7e6035d98b
commit 7f81bf45ee
6 changed files with 137 additions and 65 deletions

View File

@ -188,20 +188,10 @@ export const UpdateNapCatHandler: RequestHandler = async (_req, res) => {
try { try {
// 获取最新release信息 // 获取最新release信息
const latestRelease = await getLatestRelease() as Release; const latestRelease = await getLatestRelease() as Release;
// 验证 release 响应
if (!latestRelease || !latestRelease.tag_name) {
throw new Error('无法获取最新版本信息,请稍后重试');
}
if (!latestRelease.assets || !Array.isArray(latestRelease.assets)) {
throw new Error('无法获取下载资源列表,可能是 GitHub API 请求限制,请稍后重试');
}
const ReleaseName = WebUiDataRuntime.getWorkingEnv() === NapCatCoreWorkingEnv.Framework ? 'NapCat.Framework.zip' : 'NapCat.Shell.zip'; const ReleaseName = WebUiDataRuntime.getWorkingEnv() === NapCatCoreWorkingEnv.Framework ? 'NapCat.Framework.zip' : 'NapCat.Shell.zip';
const shellZipAsset = latestRelease.assets.find(asset => asset.name === ReleaseName); const shellZipAsset = latestRelease.assets.find(asset => asset.name === ReleaseName);
if (!shellZipAsset) { if (!shellZipAsset) {
throw new Error(`未找到${ReleaseName}文件,可用的资源: ${latestRelease.assets.map(a => a.name).join(', ')}`); throw new Error(`未找到${ReleaseName}文件`);
} }
// 创建临时目录 // 创建临时目录
@ -289,13 +279,12 @@ export const UpdateNapCatHandler: RequestHandler = async (_req, res) => {
// 发送成功响应 // 发送成功响应
const message = failedFiles.length > 0 const message = failedFiles.length > 0
? `更新完成,重启应用以应用剩余${failedFiles.length}个文件的更新` ? `更新完成,重启应用以应用剩余${failedFiles.length}个文件的更新`
: '更新完成,请重启 NapCat 以应用更新'; : '更新完成';
sendSuccess(res, { sendSuccess(res, {
status: 'completed', status: 'completed',
message, message,
newVersion: latestRelease.tag_name, newVersion: latestRelease.tag_name,
failedFilesCount: failedFiles.length, failedFilesCount: failedFiles.length
needRestart: true
}); });
} catch (error) { } catch (error) {

View File

@ -1,22 +1,29 @@
import { Card, CardBody } from '@heroui/card'; import { Card, CardBody } from '@heroui/card';
import { Image } from '@heroui/image'; import { Image } from '@heroui/image';
import { useLocalStorage } from '@uidotdev/usehooks';
import clsx from 'clsx'; import clsx from 'clsx';
import { BsTencentQq } from 'react-icons/bs'; import { BsTencentQq } from 'react-icons/bs';
import key from '@/const/key';
import { SelfInfo } from '@/types/user'; import { SelfInfo } from '@/types/user';
import PageLoading from './page_loading'; import PageLoading from './page_loading';
export interface QQInfoCardProps { export interface QQInfoCardProps {
data?: SelfInfo data?: SelfInfo;
error?: Error error?: Error;
loading?: boolean loading?: boolean;
} }
const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => { const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
const [backgroundImage] = useLocalStorage<string>(key.backgroundImage, '');
const hasBackground = !!backgroundImage;
return ( return (
<Card <Card
className='relative bg-primary-100 bg-opacity-60 overflow-hidden flex-shrink-0 shadow-md shadow-primary-300 dark:shadow-primary-50' className={clsx(
'relative backdrop-blur-sm border border-white/40 dark:border-white/10 overflow-hidden flex-shrink-0 shadow-sm',
hasBackground ? 'bg-white/10 dark:bg-black/10' : 'bg-white/60 dark:bg-black/40'
)}
shadow='none' shadow='none'
radius='lg' radius='lg'
> >
@ -31,28 +38,40 @@ const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
</CardBody> </CardBody>
) )
: ( : (
<CardBody className='flex-row items-center gap-2 overflow-hidden relative'> <CardBody className='flex-row items-center gap-4 overflow-hidden relative p-4'>
<div className='absolute right-0 bottom-0 text-5xl text-primary-400'> {!hasBackground && (
<BsTencentQq /> <div className='absolute right-[-10px] bottom-[-10px] text-7xl text-default-400/10 rotate-12 pointer-events-none'>
</div> <BsTencentQq />
</div>
)}
<div className='relative flex-shrink-0 z-10'> <div className='relative flex-shrink-0 z-10'>
<Image <Image
src={ src={
data?.avatarUrl ?? data?.avatarUrl ??
`https://q1.qlogo.cn/g?b=qq&nk=${data?.uin}&s=1` `https://q1.qlogo.cn/g?b=qq&nk=${data?.uin}&s=1`
} }
className='shadow-md rounded-full w-12 aspect-square' className='shadow-sm rounded-full w-14 aspect-square ring-2 ring-white/50 dark:ring-white/10'
/> />
<div <div
className={clsx( className={clsx(
'w-4 h-4 rounded-full absolute right-0.5 bottom-0 border-2 border-primary-100 z-10', 'w-3.5 h-3.5 rounded-full absolute right-0.5 bottom-0.5 border-2 border-white dark:border-zinc-900 z-10',
data?.online ? 'bg-green-500' : 'bg-gray-500' data?.online ? 'bg-success-500' : 'bg-default-400'
)} )}
/> />
</div> </div>
<div className='flex-col justify-center'> <div className='flex-col justify-center z-10'>
<div className='text-lg truncate'>{data?.nick}</div> <div className={clsx(
<div className='text-primary-500 text-sm'>{data?.uin}</div> 'text-xl font-bold truncate mb-0.5',
hasBackground ? 'text-white drop-shadow-sm' : 'text-default-800 dark:text-gray-100'
)}>
{data?.nick || '未知用户'}
</div>
<div className={clsx(
'font-mono text-xs tracking-wider',
hasBackground ? 'text-white/80' : 'text-default-500 opacity-80'
)}>
{data?.uin || 'Unknown'}
</div>
</div> </div>
</CardBody> </CardBody>
)} )}

View File

@ -3,15 +3,16 @@ import { Button } from '@heroui/button';
import { Chip } from '@heroui/chip'; import { Chip } from '@heroui/chip';
import { Spinner } from '@heroui/spinner'; import { Spinner } from '@heroui/spinner';
import { Tooltip } from '@heroui/tooltip'; import { Tooltip } from '@heroui/tooltip';
import { useLocalStorage } from '@uidotdev/usehooks';
import { useRequest } from 'ahooks'; import { useRequest } from 'ahooks';
import clsx from 'clsx';
import { FaCircleInfo, FaInfo, FaQq } from 'react-icons/fa6'; import { FaCircleInfo, FaInfo, FaQq } from 'react-icons/fa6';
import { IoLogoChrome, IoLogoOctocat } from 'react-icons/io'; import { IoLogoChrome, IoLogoOctocat } from 'react-icons/io';
import { RiMacFill } from 'react-icons/ri'; import { RiMacFill } from 'react-icons/ri';
import { useState } from 'react'; import { useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import key from '@/const/key';
import WebUIManager from '@/controllers/webui_manager'; import WebUIManager from '@/controllers/webui_manager';
import useDialog from '@/hooks/use-dialog'; import useDialog from '@/hooks/use-dialog';
@ -21,6 +22,7 @@ export interface SystemInfoItemProps {
icon?: React.ReactNode; icon?: React.ReactNode;
value?: React.ReactNode; value?: React.ReactNode;
endContent?: React.ReactNode; endContent?: React.ReactNode;
hasBackground?: boolean;
} }
const SystemInfoItem: React.FC<SystemInfoItemProps> = ({ const SystemInfoItem: React.FC<SystemInfoItemProps> = ({
@ -28,12 +30,21 @@ const SystemInfoItem: React.FC<SystemInfoItemProps> = ({
value = '--', value = '--',
icon, icon,
endContent, endContent,
hasBackground = false,
}) => { }) => {
return ( return (
<div className='flex text-sm gap-1 p-2 items-center shadow-sm shadow-primary-100 dark:shadow-primary-100 rounded text-primary-400'> <div className={clsx(
{icon} 'flex text-sm gap-2 p-3 items-center rounded-lg border border-white/20 transition-colors',
<div className='w-24'>{title}</div> hasBackground
<div className='text-primary-200'>{value}</div> ? 'bg-white/10 hover:bg-white/20 text-white/90'
: 'bg-white/50 dark:bg-white/5 hover:bg-white/70 dark:hover:bg-white/10 text-default-600 dark:text-gray-300'
)}>
<div className="text-lg opacity-80">{icon}</div>
<div className='w-24 font-medium'>{title}</div>
<div className={clsx(
'text-xs font-mono',
hasBackground ? 'text-white/70' : 'text-default-500'
)}>{value}</div>
<div className='ml-auto'>{endContent}</div> <div className='ml-auto'>{endContent}</div>
</div> </div>
); );
@ -261,7 +272,11 @@ const NewVersionTip = (props: NewVersionTipProps) => {
); );
}; };
const NapCatVersion = () => { interface NapCatVersionProps {
hasBackground?: boolean;
}
const NapCatVersion: React.FC<NapCatVersionProps> = ({ hasBackground = false }) => {
const { const {
data: packageData, data: packageData,
loading: packageLoading, loading: packageLoading,
@ -274,6 +289,7 @@ const NapCatVersion = () => {
<SystemInfoItem <SystemInfoItem
title='NapCat 版本' title='NapCat 版本'
icon={<IoLogoOctocat className='text-xl' />} icon={<IoLogoOctocat className='text-xl' />}
hasBackground={hasBackground}
value={ value={
packageError packageError
? ( ? (
@ -302,18 +318,28 @@ const SystemInfo: React.FC<SystemInfoProps> = (props) => {
loading: qqVersionLoading, loading: qqVersionLoading,
error: qqVersionError, error: qqVersionError,
} = useRequest(WebUIManager.getQQVersion); } = useRequest(WebUIManager.getQQVersion);
const [backgroundImage] = useLocalStorage<string>(key.backgroundImage, '');
const hasBackground = !!backgroundImage;
return ( return (
<Card className='bg-opacity-60 shadow-sm shadow-primary-100 dark:shadow-primary-100 overflow-visible flex-1'> <Card className={clsx(
<CardHeader className='pb-0 items-center gap-1 text-primary-500 font-extrabold'> 'backdrop-blur-sm border border-white/40 dark:border-white/10 shadow-sm overflow-visible flex-1',
<FaCircleInfo className='text-lg' /> hasBackground ? 'bg-white/10 dark:bg-black/10' : 'bg-white/60 dark:bg-black/40'
)}>
<CardHeader className={clsx(
'pb-0 items-center gap-2 font-bold px-4 pt-4',
hasBackground ? 'text-white drop-shadow-sm' : 'text-default-700 dark:text-white'
)}>
<FaCircleInfo className='text-lg opacity-80' />
<span></span> <span></span>
</CardHeader> </CardHeader>
<CardBody className='flex-1'> <CardBody className='flex-1'>
<div className='flex flex-col justify-between h-full'> <div className='flex flex-col gap-2 justify-between h-full'>
<NapCatVersion /> <NapCatVersion hasBackground={hasBackground} />
<SystemInfoItem <SystemInfoItem
title='QQ 版本' title='QQ 版本'
icon={<FaQq className='text-lg' />} icon={<FaQq className='text-lg' />}
hasBackground={hasBackground}
value={ value={
qqVersionError qqVersionError
? ( ? (
@ -332,11 +358,13 @@ const SystemInfo: React.FC<SystemInfoProps> = (props) => {
title='WebUI 版本' title='WebUI 版本'
icon={<IoLogoChrome className='text-xl' />} icon={<IoLogoChrome className='text-xl' />}
value='Next' value='Next'
hasBackground={hasBackground}
/> />
<SystemInfoItem <SystemInfoItem
title='系统版本' title='系统版本'
icon={<RiMacFill className='text-xl' />} icon={<RiMacFill className='text-xl' />}
value={archInfo} value={archInfo}
hasBackground={hasBackground}
/> />
</div> </div>
</CardBody> </CardBody>

View File

@ -1,18 +1,21 @@
import { Card, CardBody } from '@heroui/card'; import { Card, CardBody } from '@heroui/card';
import { Image } from '@heroui/image'; import { Image } from '@heroui/image';
import { useLocalStorage } from '@uidotdev/usehooks';
import clsx from 'clsx'; import clsx from 'clsx';
import { BiSolidMemoryCard } from 'react-icons/bi'; import { BiSolidMemoryCard } from 'react-icons/bi';
import { GiCpu } from 'react-icons/gi'; import { GiCpu } from 'react-icons/gi';
import bkg from '@/assets/images/bg/1AD934174C0107F14BAD8776D29C5F90.png'; import bkg from '@/assets/images/bg/1AD934174C0107F14BAD8776D29C5F90.png';
import key from '@/const/key';
import UsagePie from './usage_pie'; import UsagePie from './usage_pie';
export interface SystemStatusItemProps { export interface SystemStatusItemProps {
title: string title: string;
value?: string | number value?: string | number;
size?: 'md' | 'lg' size?: 'md' | 'lg';
unit?: string unit?: string;
hasBackground?: boolean;
} }
const SystemStatusItem: React.FC<SystemStatusItemProps> = ({ const SystemStatusItem: React.FC<SystemStatusItemProps> = ({
@ -20,25 +23,35 @@ const SystemStatusItem: React.FC<SystemStatusItemProps> = ({
value = '-', value = '-',
size = 'md', size = 'md',
unit, unit,
hasBackground = false,
}) => { }) => {
return ( return (
<div <div
className={clsx( className={clsx(
'shadow-sm shadow-primary-100 p-2 rounded-md text-sm bg-content1 bg-opacity-30', 'p-2 rounded-lg text-sm border border-white/20 transition-colors',
size === 'lg' ? 'col-span-2' : 'col-span-1 flex justify-between' size === 'lg' ? 'col-span-2' : 'col-span-1 flex justify-between',
hasBackground
? 'bg-white/10 hover:bg-white/20'
: 'bg-white/50 dark:bg-white/5 hover:bg-white/70 dark:hover:bg-white/10'
)} )}
> >
<div className='w-24'>{title}</div> <div className={clsx(
<div className='text-default-400'> 'w-24 font-medium',
hasBackground ? 'text-white/90' : 'text-default-600'
)}>{title}</div>
<div className={clsx(
'font-mono text-xs',
hasBackground ? 'text-white/70' : 'text-default-500'
)}>
{value} {value}
{unit} {unit && <span className="ml-0.5 opacity-70">{unit}</span>}
</div> </div>
</div> </div>
); );
}; };
export interface SystemStatusDisplayProps { export interface SystemStatusDisplayProps {
data?: SystemStatus data?: SystemStatus;
} }
const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => { const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
@ -53,9 +66,14 @@ const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
memoryUsage.system = (systemUsage / system) * 100; memoryUsage.system = (systemUsage / system) * 100;
memoryUsage.qq = (qqUsage / system) * 100; memoryUsage.qq = (qqUsage / system) * 100;
} }
const [backgroundImage] = useLocalStorage<string>(key.backgroundImage, '');
const hasBackground = !!backgroundImage;
return ( return (
<Card className='bg-opacity-60 shadow-sm shadow-primary-100 col-span-1 lg:col-span-2 relative overflow-hidden'> <Card className={clsx(
'backdrop-blur-sm border border-white/40 dark:border-white/10 shadow-sm col-span-1 lg:col-span-2 relative overflow-hidden',
hasBackground ? 'bg-white/10 dark:bg-black/10' : 'bg-white/60 dark:bg-black/40'
)}>
<div className='absolute h-full right-0 top-0'> <div className='absolute h-full right-0 top-0'>
<Image <Image
src={bkg} src={bkg}
@ -69,27 +87,35 @@ const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
</div> </div>
<CardBody className='overflow-visible md:flex-row gap-4 items-center justify-stretch z-10'> <CardBody className='overflow-visible md:flex-row gap-4 items-center justify-stretch z-10'>
<div className='flex-1 w-full md:max-w-96'> <div className='flex-1 w-full md:max-w-96'>
<h2 className='text-lg font-semibold flex items-center gap-1 text-primary-400'> <h2 className={clsx(
<GiCpu className='text-xl' /> 'text-lg font-semibold flex items-center gap-2 mb-2',
hasBackground ? 'text-white drop-shadow-sm' : 'text-default-700 dark:text-gray-200'
)}>
<GiCpu className='text-xl opacity-80' />
<span>CPU</span> <span>CPU</span>
</h2> </h2>
<div className='grid grid-cols-2 gap-2'> <div className='grid grid-cols-2 gap-2'>
<SystemStatusItem title='型号' value={data?.cpu.model} size='lg' /> <SystemStatusItem title='型号' value={data?.cpu.model} size='lg' hasBackground={hasBackground} />
<SystemStatusItem title='内核数' value={data?.cpu.core} /> <SystemStatusItem title='内核数' value={data?.cpu.core} hasBackground={hasBackground} />
<SystemStatusItem title='主频' value={data?.cpu.speed} unit='GHz' /> <SystemStatusItem title='主频' value={data?.cpu.speed} unit='GHz' hasBackground={hasBackground} />
<SystemStatusItem <SystemStatusItem
title='使用率' title='使用率'
value={data?.cpu.usage.system} value={data?.cpu.usage.system}
unit='%' unit='%'
hasBackground={hasBackground}
/> />
<SystemStatusItem <SystemStatusItem
title='QQ主线程' title='QQ主线程'
value={data?.cpu.usage.qq} value={data?.cpu.usage.qq}
unit='%' unit='%'
hasBackground={hasBackground}
/> />
</div> </div>
<h2 className='text-lg font-semibold flex items-center gap-1 text-primary-400 mt-2'> <h2 className={clsx(
<BiSolidMemoryCard className='text-xl' /> 'text-lg font-semibold flex items-center gap-2 mb-2 mt-4',
hasBackground ? 'text-white drop-shadow-sm' : 'text-default-700 dark:text-gray-200'
)}>
<BiSolidMemoryCard className='text-xl opacity-80' />
<span></span> <span></span>
</h2> </h2>
<div className='grid grid-cols-2 gap-2'> <div className='grid grid-cols-2 gap-2'>
@ -98,16 +124,19 @@ const SystemStatusDisplay: React.FC<SystemStatusDisplayProps> = ({ data }) => {
value={data?.memory.total} value={data?.memory.total}
size='lg' size='lg'
unit='MB' unit='MB'
hasBackground={hasBackground}
/> />
<SystemStatusItem <SystemStatusItem
title='使用量' title='使用量'
value={data?.memory.usage.system} value={data?.memory.usage.system}
unit='MB' unit='MB'
hasBackground={hasBackground}
/> />
<SystemStatusItem <SystemStatusItem
title='QQ主线程' title='QQ主线程'
value={data?.memory.usage.qq} value={data?.memory.usage.qq}
unit='MB' unit='MB'
hasBackground={hasBackground}
/> />
</div> </div>
</div> </div>

View File

@ -99,10 +99,8 @@ const Layout: React.FC<{ children: React.ReactNode; }> = ({ children }) => {
transition={{ duration: 0.4 }} transition={{ duration: 0.4 }}
className={clsx( className={clsx(
'flex-1 overflow-y-auto', 'flex-1 overflow-y-auto',
'bg-white/60 dark:bg-black/40 backdrop-blur-xl',
'shadow-[0_8px_32px_0_rgba(31,38,135,0.07)]',
'transition-all duration-300 ease-in-out', 'transition-all duration-300 ease-in-out',
openSideBar ? 'm-3 ml-0 rounded-3xl border border-white/40 dark:border-white/10' : 'm-0 rounded-none', openSideBar ? 'ml-0' : 'ml-0',
'pb-10 md:pb-0' 'pb-10 md:pb-0'
)} )}
> >

View File

@ -1,6 +1,9 @@
import { Card, CardBody } from '@heroui/card'; import { Card, CardBody } from '@heroui/card';
import { useLocalStorage } from '@uidotdev/usehooks';
import { useRequest } from 'ahooks'; import { useRequest } from 'ahooks';
import clsx from 'clsx';
import { useCallback, useEffect, useState, useRef } from 'react'; import { useCallback, useEffect, useState, useRef } from 'react';
import key from '@/const/key';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
@ -92,6 +95,9 @@ const SystemStatusCard: React.FC<SystemStatusCardProps> = ({ setArchInfo }) => {
const DashboardIndexPage: React.FC = () => { const DashboardIndexPage: React.FC = () => {
const [archInfo, setArchInfo] = useState<string>(); const [archInfo, setArchInfo] = useState<string>();
// @ts-ignore
const [backgroundImage] = useLocalStorage<string>(key.backgroundImage, '');
const hasBackground = !!backgroundImage;
return ( return (
<> <>
@ -105,7 +111,10 @@ const DashboardIndexPage: React.FC = () => {
<SystemStatusCard setArchInfo={setArchInfo} /> <SystemStatusCard setArchInfo={setArchInfo} />
</div> </div>
<Networks /> <Networks />
<Card className='bg-opacity-60 shadow-sm shadow-primary-100'> <Card className={clsx(
'backdrop-blur-sm border border-white/40 dark:border-white/10 shadow-sm transition-all',
hasBackground ? 'bg-white/10 dark:bg-black/10' : 'bg-white/60 dark:bg-black/40'
)}>
<CardBody> <CardBody>
<Hitokoto /> <Hitokoto />
</CardBody> </CardBody>