🎨 chore(web): apply ESLint and Prettier auto-fixes (baseline)

- Ran: bun run eslint:fix && bun run lint:fix
- Inserted AGPL license header via eslint-plugin-header
- Enforced no-multiple-empty-lines and other lint rules
- Formatted code using Prettier v3 (@so1ve/prettier-config)
- No functional changes; formatting-only baseline across JS/JSX files
This commit is contained in:
t0ng7u
2025-08-30 21:15:10 +08:00
parent 41cf516ec5
commit 0d57b1acd4
274 changed files with 11025 additions and 7659 deletions

View File

@@ -21,7 +21,10 @@ import React from 'react';
import { Card, Tag, Timeline, Empty } from '@douyinfe/semi-ui';
import { Bell } from 'lucide-react';
import { marked } from 'marked';
import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
import {
IllustrationConstruction,
IllustrationConstructionDark,
} from '@douyinfe/semi-illustrations';
import ScrollableContainer from '../common/ui/ScrollableContainer';
const AnnouncementsPanel = ({
@@ -29,36 +32,43 @@ const AnnouncementsPanel = ({
announcementLegendData,
CARD_PROPS,
ILLUSTRATION_SIZE,
t
t,
}) => {
return (
<Card
{...CARD_PROPS}
className="shadow-sm !rounded-2xl lg:col-span-2"
className='shadow-sm !rounded-2xl lg:col-span-2'
title={
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-2 w-full">
<div className="flex items-center gap-2">
<div className='flex flex-col lg:flex-row lg:items-center lg:justify-between gap-2 w-full'>
<div className='flex items-center gap-2'>
<Bell size={16} />
{t('系统公告')}
<Tag color="white" shape="circle">
<Tag color='white' shape='circle'>
{t('显示最新20条')}
</Tag>
</div>
{/* 图例 */}
<div className="flex flex-wrap gap-3 text-xs">
<div className='flex flex-wrap gap-3 text-xs'>
{announcementLegendData.map((legend, index) => (
<div key={index} className="flex items-center gap-1">
<div key={index} className='flex items-center gap-1'>
<div
className="w-2 h-2 rounded-full"
className='w-2 h-2 rounded-full'
style={{
backgroundColor: legend.color === 'grey' ? '#8b9aa7' :
legend.color === 'blue' ? '#3b82f6' :
legend.color === 'green' ? '#10b981' :
legend.color === 'orange' ? '#f59e0b' :
legend.color === 'red' ? '#ef4444' : '#8b9aa7'
backgroundColor:
legend.color === 'grey'
? '#8b9aa7'
: legend.color === 'blue'
? '#3b82f6'
: legend.color === 'green'
? '#10b981'
: legend.color === 'orange'
? '#f59e0b'
: legend.color === 'red'
? '#ef4444'
: '#8b9aa7',
}}
/>
<span className="text-gray-600">{legend.label}</span>
<span className='text-gray-600'>{legend.label}</span>
</div>
))}
</div>
@@ -66,9 +76,9 @@ const AnnouncementsPanel = ({
}
bodyStyle={{ padding: 0 }}
>
<ScrollableContainer maxHeight="24rem">
<ScrollableContainer maxHeight='24rem'>
{announcementData.length > 0 ? (
<Timeline mode="left">
<Timeline mode='left'>
{announcementData.map((item, idx) => {
const htmlExtra = item.extra ? marked.parse(item.extra) : '';
return (
@@ -76,16 +86,20 @@ const AnnouncementsPanel = ({
key={idx}
type={item.type || 'default'}
time={`${item.relative ? item.relative + ' ' : ''}${item.time}`}
extra={item.extra ? (
<div
className="text-xs text-gray-500"
dangerouslySetInnerHTML={{ __html: htmlExtra }}
/>
) : null}
extra={
item.extra ? (
<div
className='text-xs text-gray-500'
dangerouslySetInnerHTML={{ __html: htmlExtra }}
/>
) : null
}
>
<div>
<div
dangerouslySetInnerHTML={{ __html: marked.parse(item.content || '') }}
dangerouslySetInnerHTML={{
__html: marked.parse(item.content || ''),
}}
/>
</div>
</Timeline.Item>
@@ -93,10 +107,12 @@ const AnnouncementsPanel = ({
})}
</Timeline>
) : (
<div className="flex justify-center items-center py-8">
<div className='flex justify-center items-center py-8'>
<Empty
image={<IllustrationConstruction style={ILLUSTRATION_SIZE} />}
darkModeImage={<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />}
darkModeImage={
<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />
}
title={t('暂无系统公告')}
description={t('请联系管理员在系统设置中配置公告信息')}
/>
@@ -107,4 +123,4 @@ const AnnouncementsPanel = ({
);
};
export default AnnouncementsPanel;
export default AnnouncementsPanel;

View File

@@ -20,7 +20,10 @@ For commercial licensing, please contact support@quantumnous.com
import React from 'react';
import { Card, Avatar, Tag, Divider, Empty } from '@douyinfe/semi-ui';
import { Server, Gauge, ExternalLink } from 'lucide-react';
import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
import {
IllustrationConstruction,
IllustrationConstructionDark,
} from '@douyinfe/semi-illustrations';
import ScrollableContainer from '../common/ui/ScrollableContainer';
const ApiInfoPanel = ({
@@ -30,12 +33,12 @@ const ApiInfoPanel = ({
CARD_PROPS,
FLEX_CENTER_GAP2,
ILLUSTRATION_SIZE,
t
t,
}) => {
return (
<Card
{...CARD_PROPS}
className="bg-gray-50 border-0 !rounded-2xl"
className='bg-gray-50 border-0 !rounded-2xl'
title={
<div className={FLEX_CENTER_GAP2}>
<Server size={16} />
@@ -44,66 +47,65 @@ const ApiInfoPanel = ({
}
bodyStyle={{ padding: 0 }}
>
<ScrollableContainer maxHeight="24rem">
<ScrollableContainer maxHeight='24rem'>
{apiInfoData.length > 0 ? (
apiInfoData.map((api) => (
<React.Fragment key={api.id}>
<div className="flex p-2 hover:bg-white rounded-lg transition-colors cursor-pointer">
<div className="flex-shrink-0 mr-3">
<Avatar
size="extra-small"
color={api.color}
>
<div className='flex p-2 hover:bg-white rounded-lg transition-colors cursor-pointer'>
<div className='flex-shrink-0 mr-3'>
<Avatar size='extra-small' color={api.color}>
{api.route.substring(0, 2)}
</Avatar>
</div>
<div className="flex-1">
<div className="flex flex-wrap items-center justify-between mb-1 w-full gap-2">
<span className="text-sm font-medium text-gray-900 !font-bold break-all">
<div className='flex-1'>
<div className='flex flex-wrap items-center justify-between mb-1 w-full gap-2'>
<span className='text-sm font-medium text-gray-900 !font-bold break-all'>
{api.route}
</span>
<div className="flex items-center gap-1 mt-1 lg:mt-0">
<div className='flex items-center gap-1 mt-1 lg:mt-0'>
<Tag
prefixIcon={<Gauge size={12} />}
size="small"
color="white"
size='small'
color='white'
shape='circle'
onClick={() => handleSpeedTest(api.url)}
className="cursor-pointer hover:opacity-80 text-xs"
className='cursor-pointer hover:opacity-80 text-xs'
>
{t('测速')}
</Tag>
<Tag
prefixIcon={<ExternalLink size={12} />}
size="small"
color="white"
size='small'
color='white'
shape='circle'
onClick={() => window.open(api.url, '_blank', 'noopener,noreferrer')}
className="cursor-pointer hover:opacity-80 text-xs"
onClick={() =>
window.open(api.url, '_blank', 'noopener,noreferrer')
}
className='cursor-pointer hover:opacity-80 text-xs'
>
{t('跳转')}
</Tag>
</div>
</div>
<div
className="!text-semi-color-primary break-all cursor-pointer hover:underline mb-1"
className='!text-semi-color-primary break-all cursor-pointer hover:underline mb-1'
onClick={() => handleCopyUrl(api.url)}
>
{api.url}
</div>
<div className="text-gray-500">
{api.description}
</div>
<div className='text-gray-500'>{api.description}</div>
</div>
</div>
<Divider />
</React.Fragment>
))
) : (
<div className="flex justify-center items-center py-8">
<div className='flex justify-center items-center py-8'>
<Empty
image={<IllustrationConstruction style={ILLUSTRATION_SIZE} />}
darkModeImage={<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />}
darkModeImage={
<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />
}
title={t('暂无API信息')}
description={t('请联系管理员在系统设置中配置API信息')}
/>
@@ -114,4 +116,4 @@ const ApiInfoPanel = ({
);
};
export default ApiInfoPanel;
export default ApiInfoPanel;

View File

@@ -23,7 +23,7 @@ import { PieChart } from 'lucide-react';
import {
IconHistogram,
IconPulse,
IconPieChart2Stroked
IconPieChart2Stroked,
} from '@douyinfe/semi-icons';
import { VChart } from '@visactor/react-vchart';
@@ -38,80 +38,80 @@ const ChartsPanel = ({
CHART_CONFIG,
FLEX_CENTER_GAP2,
hasApiInfoPanel,
t
t,
}) => {
return (
<Card
{...CARD_PROPS}
className={`!rounded-2xl ${hasApiInfoPanel ? 'lg:col-span-3' : ''}`}
title={
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between w-full gap-3">
<div className='flex flex-col lg:flex-row lg:items-center lg:justify-between w-full gap-3'>
<div className={FLEX_CENTER_GAP2}>
<PieChart size={16} />
{t('模型数据分析')}
</div>
<Tabs
type="button"
type='button'
activeKey={activeChartTab}
onChange={setActiveChartTab}
>
<TabPane tab={
<span>
<IconHistogram />
{t('消耗分布')}
</span>
} itemKey="1" />
<TabPane tab={
<span>
<IconPulse />
{t('消耗趋势')}
</span>
} itemKey="2" />
<TabPane tab={
<span>
<IconPieChart2Stroked />
{t('调用次数分布')}
</span>
} itemKey="3" />
<TabPane tab={
<span>
<IconHistogram />
{t('调用次数排行')}
</span>
} itemKey="4" />
<TabPane
tab={
<span>
<IconHistogram />
{t('消耗分布')}
</span>
}
itemKey='1'
/>
<TabPane
tab={
<span>
<IconPulse />
{t('消耗趋势')}
</span>
}
itemKey='2'
/>
<TabPane
tab={
<span>
<IconPieChart2Stroked />
{t('调用次数分布')}
</span>
}
itemKey='3'
/>
<TabPane
tab={
<span>
<IconHistogram />
{t('调用次数排行')}
</span>
}
itemKey='4'
/>
</Tabs>
</div>
}
bodyStyle={{ padding: 0 }}
>
<div className="h-96 p-2">
<div className='h-96 p-2'>
{activeChartTab === '1' && (
<VChart
spec={spec_line}
option={CHART_CONFIG}
/>
<VChart spec={spec_line} option={CHART_CONFIG} />
)}
{activeChartTab === '2' && (
<VChart
spec={spec_model_line}
option={CHART_CONFIG}
/>
<VChart spec={spec_model_line} option={CHART_CONFIG} />
)}
{activeChartTab === '3' && (
<VChart
spec={spec_pie}
option={CHART_CONFIG}
/>
<VChart spec={spec_pie} option={CHART_CONFIG} />
)}
{activeChartTab === '4' && (
<VChart
spec={spec_rank_bar}
option={CHART_CONFIG}
/>
<VChart spec={spec_rank_bar} option={CHART_CONFIG} />
)}
</div>
</Card>
);
};
export default ChartsPanel;
export default ChartsPanel;

View File

@@ -27,19 +27,19 @@ const DashboardHeader = ({
showSearchModal,
refresh,
loading,
t
t,
}) => {
const ICON_BUTTON_CLASS = "text-white hover:bg-opacity-80 !rounded-full";
const ICON_BUTTON_CLASS = 'text-white hover:bg-opacity-80 !rounded-full';
return (
<div className="flex items-center justify-between mb-4">
<div className='flex items-center justify-between mb-4'>
<h2
className="text-2xl font-semibold text-gray-800 transition-opacity duration-1000 ease-in-out"
className='text-2xl font-semibold text-gray-800 transition-opacity duration-1000 ease-in-out'
style={{ opacity: greetingVisible ? 1 : 0 }}
>
{getGreeting}
</h2>
<div className="flex gap-3">
<div className='flex gap-3'>
<Button
type='tertiary'
icon={<Search size={16} />}
@@ -58,4 +58,4 @@ const DashboardHeader = ({
);
};
export default DashboardHeader;
export default DashboardHeader;

View File

@@ -22,7 +22,10 @@ import { Card, Collapse, Empty } from '@douyinfe/semi-ui';
import { HelpCircle } from 'lucide-react';
import { IconPlus, IconMinus } from '@douyinfe/semi-icons';
import { marked } from 'marked';
import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
import {
IllustrationConstruction,
IllustrationConstructionDark,
} from '@douyinfe/semi-illustrations';
import ScrollableContainer from '../common/ui/ScrollableContainer';
const FaqPanel = ({
@@ -30,12 +33,12 @@ const FaqPanel = ({
CARD_PROPS,
FLEX_CENTER_GAP2,
ILLUSTRATION_SIZE,
t
t,
}) => {
return (
<Card
{...CARD_PROPS}
className="shadow-sm !rounded-2xl lg:col-span-1"
className='shadow-sm !rounded-2xl lg:col-span-1'
title={
<div className={FLEX_CENTER_GAP2}>
<HelpCircle size={16} />
@@ -44,7 +47,7 @@ const FaqPanel = ({
}
bodyStyle={{ padding: 0 }}
>
<ScrollableContainer maxHeight="24rem">
<ScrollableContainer maxHeight='24rem'>
{faqData.length > 0 ? (
<Collapse
accordion
@@ -58,16 +61,20 @@ const FaqPanel = ({
itemKey={index.toString()}
>
<div
dangerouslySetInnerHTML={{ __html: marked.parse(item.answer || '') }}
dangerouslySetInnerHTML={{
__html: marked.parse(item.answer || ''),
}}
/>
</Collapse.Panel>
))}
</Collapse>
) : (
<div className="flex justify-center items-center py-8">
<div className='flex justify-center items-center py-8'>
<Empty
image={<IllustrationConstruction style={ILLUSTRATION_SIZE} />}
darkModeImage={<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />}
darkModeImage={
<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />
}
title={t('暂无常见问答')}
description={t('请联系管理员在系统设置中配置常见问答')}
/>
@@ -78,4 +85,4 @@ const FaqPanel = ({
);
};
export default FaqPanel;
export default FaqPanel;

View File

@@ -28,13 +28,13 @@ const StatsCards = ({
loading,
getTrendSpec,
CARD_PROPS,
CHART_CONFIG
CHART_CONFIG,
}) => {
const navigate = useNavigate();
const { t } = useTranslation();
return (
<div className="mb-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div className='mb-4'>
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4'>
{groupedStatsData.map((group, idx) => (
<Card
key={idx}
@@ -42,24 +42,24 @@ const StatsCards = ({
className={`${group.color} border-0 !rounded-2xl w-full`}
title={group.title}
>
<div className="space-y-4">
<div className='space-y-4'>
{group.items.map((item, itemIdx) => (
<div
key={itemIdx}
className="flex items-center justify-between cursor-pointer"
className='flex items-center justify-between cursor-pointer'
onClick={item.onClick}
>
<div className="flex items-center">
<div className='flex items-center'>
<Avatar
className="mr-3"
size="small"
className='mr-3'
size='small'
color={item.avatarColor}
>
{item.icon}
</Avatar>
<div>
<div className="text-xs text-gray-500">{item.title}</div>
<div className="text-lg font-semibold">
<div className='text-xs text-gray-500'>{item.title}</div>
<div className='text-lg font-semibold'>
<Skeleton
loading={loading}
active
@@ -67,7 +67,11 @@ const StatsCards = ({
<Skeleton.Paragraph
active
rows={1}
style={{ width: '65px', height: '24px', marginTop: '4px' }}
style={{
width: '65px',
height: '24px',
marginTop: '4px',
}}
/>
}
>
@@ -78,9 +82,9 @@ const StatsCards = ({
</div>
{item.title === t('当前余额') ? (
<Tag
color="white"
color='white'
shape='circle'
size="large"
size='large'
onClick={(e) => {
e.stopPropagation();
navigate('/console/topup');
@@ -88,13 +92,16 @@ const StatsCards = ({
>
{t('充值')}
</Tag>
) : (loading || (item.trendData && item.trendData.length > 0)) && (
<div className="w-24 h-10">
<VChart
spec={getTrendSpec(item.trendData, item.trendColor)}
option={CHART_CONFIG}
/>
</div>
) : (
(loading ||
(item.trendData && item.trendData.length > 0)) && (
<div className='w-24 h-10'>
<VChart
spec={getTrendSpec(item.trendData, item.trendColor)}
option={CHART_CONFIG}
/>
</div>
)
)}
</div>
))}
@@ -106,4 +113,4 @@ const StatsCards = ({
);
};
export default StatsCards;
export default StatsCards;

View File

@@ -18,9 +18,20 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React from 'react';
import { Card, Button, Spin, Tabs, TabPane, Tag, Empty } from '@douyinfe/semi-ui';
import {
Card,
Button,
Spin,
Tabs,
TabPane,
Tag,
Empty,
} from '@douyinfe/semi-ui';
import { Gauge, RefreshCw } from 'lucide-react';
import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
import {
IllustrationConstruction,
IllustrationConstructionDark,
} from '@douyinfe/semi-illustrations';
import ScrollableContainer from '../common/ui/ScrollableContainer';
const UptimePanel = ({
@@ -33,15 +44,15 @@ const UptimePanel = ({
renderMonitorList,
CARD_PROPS,
ILLUSTRATION_SIZE,
t
t,
}) => {
return (
<Card
{...CARD_PROPS}
className="shadow-sm !rounded-2xl lg:col-span-1"
className='shadow-sm !rounded-2xl lg:col-span-1'
title={
<div className="flex items-center justify-between w-full gap-2">
<div className="flex items-center gap-2">
<div className='flex items-center justify-between w-full gap-2'>
<div className='flex items-center gap-2'>
<Gauge size={16} />
{t('服务可用性')}
</div>
@@ -49,39 +60,43 @@ const UptimePanel = ({
icon={<RefreshCw size={14} />}
onClick={loadUptimeData}
loading={uptimeLoading}
size="small"
theme="borderless"
size='small'
theme='borderless'
type='tertiary'
className="text-gray-500 hover:text-blue-500 hover:bg-blue-50 !rounded-full"
className='text-gray-500 hover:text-blue-500 hover:bg-blue-50 !rounded-full'
/>
</div>
}
bodyStyle={{ padding: 0 }}
>
{/* 内容区域 */}
<div className="relative">
<div className='relative'>
<Spin spinning={uptimeLoading}>
{uptimeData.length > 0 ? (
uptimeData.length === 1 ? (
<ScrollableContainer maxHeight="24rem">
<ScrollableContainer maxHeight='24rem'>
{renderMonitorList(uptimeData[0].monitors)}
</ScrollableContainer>
) : (
<Tabs
type="card"
type='card'
collapsible
activeKey={activeUptimeTab}
onChange={setActiveUptimeTab}
size="small"
size='small'
>
{uptimeData.map((group, groupIdx) => (
<TabPane
tab={
<span className="flex items-center gap-2">
<span className='flex items-center gap-2'>
<Gauge size={14} />
{group.categoryName}
<Tag
color={activeUptimeTab === group.categoryName ? 'red' : 'grey'}
color={
activeUptimeTab === group.categoryName
? 'red'
: 'grey'
}
size='small'
shape='circle'
>
@@ -92,7 +107,7 @@ const UptimePanel = ({
itemKey={group.categoryName}
key={groupIdx}
>
<ScrollableContainer maxHeight="21.5rem">
<ScrollableContainer maxHeight='21.5rem'>
{renderMonitorList(group.monitors)}
</ScrollableContainer>
</TabPane>
@@ -100,10 +115,12 @@ const UptimePanel = ({
</Tabs>
)
) : (
<div className="flex justify-center items-center py-8">
<div className='flex justify-center items-center py-8'>
<Empty
image={<IllustrationConstruction style={ILLUSTRATION_SIZE} />}
darkModeImage={<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />}
darkModeImage={
<IllustrationConstructionDark style={ILLUSTRATION_SIZE} />
}
title={t('暂无监控数据')}
description={t('请联系管理员在系统设置中配置Uptime')}
/>
@@ -114,15 +131,15 @@ const UptimePanel = ({
{/* 图例 */}
{uptimeData.length > 0 && (
<div className="p-3 bg-gray-50 rounded-b-2xl">
<div className="flex flex-wrap gap-3 text-xs justify-center">
<div className='p-3 bg-gray-50 rounded-b-2xl'>
<div className='flex flex-wrap gap-3 text-xs justify-center'>
{uptimeLegendData.map((legend, index) => (
<div key={index} className="flex items-center gap-1">
<div key={index} className='flex items-center gap-1'>
<div
className="w-2 h-2 rounded-full"
className='w-2 h-2 rounded-full'
style={{ backgroundColor: legend.color }}
/>
<span className="text-gray-600">{legend.label}</span>
<span className='text-gray-600'>{legend.label}</span>
</div>
))}
</div>
@@ -132,4 +149,4 @@ const UptimePanel = ({
);
};
export default UptimePanel;
export default UptimePanel;

View File

@@ -41,7 +41,7 @@ import {
FLEX_CENTER_GAP2,
ILLUSTRATION_SIZE,
ANNOUNCEMENT_LEGEND_DATA,
UPTIME_STATUS_MAP
UPTIME_STATUS_MAP,
} from '../../constants/dashboard.constants';
import {
getTrendSpec,
@@ -49,7 +49,7 @@ import {
handleSpeedTest,
getUptimeStatusColor,
getUptimeStatusText,
renderMonitorList
renderMonitorList,
} from '../../helpers/dashboard';
const Dashboard = () => {
@@ -70,7 +70,7 @@ const Dashboard = () => {
dashboardData.setPieData,
dashboardData.setLineData,
dashboardData.setModelColors,
dashboardData.t
dashboardData.t,
);
// ========== 统计数据 ==========
@@ -82,12 +82,12 @@ const Dashboard = () => {
dashboardData.trendData,
dashboardData.performanceMetrics,
dashboardData.navigate,
dashboardData.t
dashboardData.t,
);
// ========== 数据处理 ==========
const initChart = async () => {
await dashboardData.loadQuotaData().then(data => {
await dashboardData.loadQuotaData().then((data) => {
if (data && data.length > 0) {
dashboardCharts.updateChartData(data);
}
@@ -108,25 +108,30 @@ const Dashboard = () => {
// ========== 数据准备 ==========
const apiInfoData = statusState?.status?.api_info || [];
const announcementData = (statusState?.status?.announcements || []).map(item => {
const pubDate = item?.publishDate ? new Date(item.publishDate) : null;
const absoluteTime = pubDate && !isNaN(pubDate.getTime())
? `${pubDate.getFullYear()}-${String(pubDate.getMonth() + 1).padStart(2, '0')}-${String(pubDate.getDate()).padStart(2, '0')} ${String(pubDate.getHours()).padStart(2, '0')}:${String(pubDate.getMinutes()).padStart(2, '0')}`
: (item?.publishDate || '');
const relativeTime = getRelativeTime(item.publishDate);
return ({
...item,
time: absoluteTime,
relative: relativeTime
});
});
const announcementData = (statusState?.status?.announcements || []).map(
(item) => {
const pubDate = item?.publishDate ? new Date(item.publishDate) : null;
const absoluteTime =
pubDate && !isNaN(pubDate.getTime())
? `${pubDate.getFullYear()}-${String(pubDate.getMonth() + 1).padStart(2, '0')}-${String(pubDate.getDate()).padStart(2, '0')} ${String(pubDate.getHours()).padStart(2, '0')}:${String(pubDate.getMinutes()).padStart(2, '0')}`
: item?.publishDate || '';
const relativeTime = getRelativeTime(item.publishDate);
return {
...item,
time: absoluteTime,
relative: relativeTime,
};
},
);
const faqData = statusState?.status?.faq || [];
const uptimeLegendData = Object.entries(UPTIME_STATUS_MAP).map(([status, info]) => ({
status: Number(status),
color: info.color,
label: dashboardData.t(info.label)
}));
const uptimeLegendData = Object.entries(UPTIME_STATUS_MAP).map(
([status, info]) => ({
status: Number(status),
color: info.color,
label: dashboardData.t(info.label),
}),
);
// ========== Effects ==========
useEffect(() => {
@@ -134,7 +139,7 @@ const Dashboard = () => {
}, []);
return (
<div className="h-full">
<div className='h-full'>
<DashboardHeader
getGreeting={dashboardData.getGreeting}
greetingVisible={dashboardData.greetingVisible}
@@ -166,8 +171,10 @@ const Dashboard = () => {
/>
{/* API信息和图表面板 */}
<div className="mb-4">
<div className={`grid grid-cols-1 gap-4 ${dashboardData.hasApiInfoPanel ? 'lg:grid-cols-4' : ''}`}>
<div className='mb-4'>
<div
className={`grid grid-cols-1 gap-4 ${dashboardData.hasApiInfoPanel ? 'lg:grid-cols-4' : ''}`}
>
<ChartsPanel
activeChartTab={dashboardData.activeChartTab}
setActiveChartTab={dashboardData.setActiveChartTab}
@@ -198,16 +205,18 @@ const Dashboard = () => {
{/* 系统公告和常见问答卡片 */}
{dashboardData.hasInfoPanels && (
<div className="mb-4">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
<div className='mb-4'>
<div className='grid grid-cols-1 lg:grid-cols-4 gap-4'>
{/* 公告卡片 */}
{dashboardData.announcementsEnabled && (
<AnnouncementsPanel
announcementData={announcementData}
announcementLegendData={ANNOUNCEMENT_LEGEND_DATA.map(item => ({
...item,
label: dashboardData.t(item.label)
}))}
announcementLegendData={ANNOUNCEMENT_LEGEND_DATA.map(
(item) => ({
...item,
label: dashboardData.t(item.label),
}),
)}
CARD_PROPS={CARD_PROPS}
ILLUSTRATION_SIZE={ILLUSTRATION_SIZE}
t={dashboardData.t}
@@ -234,12 +243,19 @@ const Dashboard = () => {
setActiveUptimeTab={dashboardData.setActiveUptimeTab}
loadUptimeData={dashboardData.loadUptimeData}
uptimeLegendData={uptimeLegendData}
renderMonitorList={(monitors) => renderMonitorList(
monitors,
(status) => getUptimeStatusColor(status, UPTIME_STATUS_MAP),
(status) => getUptimeStatusText(status, UPTIME_STATUS_MAP, dashboardData.t),
dashboardData.t
)}
renderMonitorList={(monitors) =>
renderMonitorList(
monitors,
(status) => getUptimeStatusColor(status, UPTIME_STATUS_MAP),
(status) =>
getUptimeStatusText(
status,
UPTIME_STATUS_MAP,
dashboardData.t,
),
dashboardData.t,
)
}
CARD_PROPS={CARD_PROPS}
ILLUSTRATION_SIZE={ILLUSTRATION_SIZE}
t={dashboardData.t}
@@ -252,4 +268,4 @@ const Dashboard = () => {
);
};
export default Dashboard;
export default Dashboard;

View File

@@ -30,12 +30,12 @@ const SearchModal = ({
dataExportDefaultTime,
timeOptions,
handleInputChange,
t
t,
}) => {
const formRef = useRef();
const FORM_FIELD_PROPS = {
className: "w-full mb-2 !rounded-lg",
className: 'w-full mb-2 !rounded-lg',
};
const createFormField = (Component, props) => (
@@ -54,7 +54,7 @@ const SearchModal = ({
size={isMobile ? 'full-width' : 'small'}
centered
>
<Form ref={formRef} layout='vertical' className="w-full">
<Form ref={formRef} layout='vertical' className='w-full'>
{createFormField(Form.DatePicker, {
field: 'start_timestamp',
label: t('起始时间'),
@@ -62,7 +62,7 @@ const SearchModal = ({
value: start_timestamp,
type: 'dateTime',
name: 'start_timestamp',
onChange: (value) => handleInputChange(value, 'start_timestamp')
onChange: (value) => handleInputChange(value, 'start_timestamp'),
})}
{createFormField(Form.DatePicker, {
@@ -72,7 +72,7 @@ const SearchModal = ({
value: end_timestamp,
type: 'dateTime',
name: 'end_timestamp',
onChange: (value) => handleInputChange(value, 'end_timestamp')
onChange: (value) => handleInputChange(value, 'end_timestamp'),
})}
{createFormField(Form.Select, {
@@ -82,20 +82,22 @@ const SearchModal = ({
placeholder: t('时间粒度'),
name: 'data_export_default_time',
optionList: timeOptions,
onChange: (value) => handleInputChange(value, 'data_export_default_time')
onChange: (value) =>
handleInputChange(value, 'data_export_default_time'),
})}
{isAdminUser && createFormField(Form.Input, {
field: 'username',
label: t('用户名称'),
value: username,
placeholder: t('可选值'),
name: 'username',
onChange: (value) => handleInputChange(value, 'username')
})}
{isAdminUser &&
createFormField(Form.Input, {
field: 'username',
label: t('用户名称'),
value: username,
placeholder: t('可选值'),
name: 'username',
onChange: (value) => handleInputChange(value, 'username'),
})}
</Form>
</Modal>
);
};
export default SearchModal;
export default SearchModal;