refactor(frontend): modernize UI components with retro-futuristic styling
- Replace Card, Alert, and Separator components with retro-styled div elements - Update DatabaseManager with retro-card styling and border-based design - Refactor health status display to use styled Badge components instead of text - Remove unused icon imports (Server, FileText) from DatabaseManager - Clean up inline comments and unnecessary whitespace throughout components - Simplify error handling by removing redundant console.error statements - Update Sidebar, SystemConfig, Account, AdminDashboard, and other pages with consistent retro styling - Apply uppercase font styling and monospace typography to match retro-futuristic theme - Consolidate component structure across all pages for visual consistency - Improve code readability by removing excessive blank lines and comments
This commit is contained in:
parent
b733181663
commit
1fc0ecd14a
|
|
@ -5,21 +5,16 @@
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Separator } from '@/components/ui/separator';
|
|
||||||
import {
|
import {
|
||||||
Download,
|
Download,
|
||||||
Upload,
|
Upload,
|
||||||
Trash2,
|
Trash2,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Server,
|
|
||||||
Activity,
|
Activity,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Database,
|
Database,
|
||||||
FileText,
|
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Info
|
Info
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
@ -60,7 +55,6 @@ export function DatabaseManager() {
|
||||||
has_config: boolean;
|
has_config: boolean;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
// 加载健康检查和统计信息
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadHealth();
|
loadHealth();
|
||||||
loadStats();
|
loadStats();
|
||||||
|
|
@ -90,21 +84,16 @@ export function DatabaseManager() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 导出数据
|
|
||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setMessage(null);
|
setMessage(null);
|
||||||
|
|
||||||
const exportData = await api.exportDatabase();
|
const exportData = await api.exportDatabase();
|
||||||
|
|
||||||
// 构建完整的导出数据
|
|
||||||
const fullData = {
|
const fullData = {
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
export_date: exportData.export_date,
|
export_date: exportData.export_date,
|
||||||
data: exportData.data
|
data: exportData.data
|
||||||
};
|
};
|
||||||
|
|
||||||
const blob = new Blob([JSON.stringify(fullData, null, 2)], { type: 'application/json' });
|
const blob = new Blob([JSON.stringify(fullData, null, 2)], { type: 'application/json' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
|
|
@ -114,14 +103,10 @@ export function DatabaseManager() {
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
toast.success('数据导出成功!');
|
toast.success('数据导出成功!');
|
||||||
setMessage({ type: 'success', text: '数据导出成功!' });
|
setMessage({ type: 'success', text: '数据导出成功!' });
|
||||||
|
|
||||||
// 刷新统计信息
|
|
||||||
loadStats();
|
loadStats();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('导出失败:', error);
|
|
||||||
const errorMsg = error?.response?.data?.detail || error?.message || '数据导出失败,请重试';
|
const errorMsg = error?.response?.data?.detail || error?.message || '数据导出失败,请重试';
|
||||||
toast.error(errorMsg);
|
toast.error(errorMsg);
|
||||||
setMessage({ type: 'error', text: errorMsg });
|
setMessage({ type: 'error', text: errorMsg });
|
||||||
|
|
@ -130,31 +115,23 @@ export function DatabaseManager() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 导入数据
|
|
||||||
const handleImport = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleImport = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
// 验证文件类型
|
|
||||||
if (!file.name.endsWith('.json')) {
|
if (!file.name.endsWith('.json')) {
|
||||||
toast.error('请选择 JSON 格式的文件');
|
toast.error('请选择 JSON 格式的文件');
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证文件大小(最大 50MB)
|
|
||||||
if (file.size > 50 * 1024 * 1024) {
|
if (file.size > 50 * 1024 * 1024) {
|
||||||
toast.error('文件大小不能超过 50MB');
|
toast.error('文件大小不能超过 50MB');
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setMessage(null);
|
setMessage(null);
|
||||||
|
|
||||||
const result = await api.importDatabase(file);
|
const result = await api.importDatabase(file);
|
||||||
|
|
||||||
const imported = result.imported;
|
const imported = result.imported;
|
||||||
const summary = [
|
const summary = [
|
||||||
imported.projects > 0 && `${imported.projects} 个项目`,
|
imported.projects > 0 && `${imported.projects} 个项目`,
|
||||||
|
|
@ -163,48 +140,33 @@ export function DatabaseManager() {
|
||||||
imported.analyses > 0 && `${imported.analyses} 条分析记录`,
|
imported.analyses > 0 && `${imported.analyses} 条分析记录`,
|
||||||
imported.config > 0 && '用户配置',
|
imported.config > 0 && '用户配置',
|
||||||
].filter(Boolean).join('、');
|
].filter(Boolean).join('、');
|
||||||
|
|
||||||
toast.success(`数据导入成功!已导入:${summary}`);
|
toast.success(`数据导入成功!已导入:${summary}`);
|
||||||
setMessage({ type: 'success', text: `数据导入成功!已导入:${summary}` });
|
setMessage({ type: 'success', text: `数据导入成功!已导入:${summary}` });
|
||||||
|
|
||||||
// 清空文件输入
|
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
|
|
||||||
// 刷新统计信息和健康检查
|
|
||||||
loadStats();
|
loadStats();
|
||||||
loadHealth();
|
loadHealth();
|
||||||
|
|
||||||
// 延迟刷新页面
|
|
||||||
setTimeout(() => window.location.reload(), 2000);
|
setTimeout(() => window.location.reload(), 2000);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('导入失败:', error);
|
|
||||||
const errorMsg = error?.response?.data?.detail || error?.message || '数据导入失败,请检查文件格式';
|
const errorMsg = error?.response?.data?.detail || error?.message || '数据导入失败,请检查文件格式';
|
||||||
toast.error(errorMsg);
|
toast.error(errorMsg);
|
||||||
setMessage({ type: 'error', text: errorMsg });
|
setMessage({ type: 'error', text: errorMsg });
|
||||||
// 清空文件输入
|
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 清空数据
|
|
||||||
const handleClear = async () => {
|
const handleClear = async () => {
|
||||||
if (!window.confirm('⚠️ 警告:确定要清空所有数据吗?\n\n此操作将删除:\n- 所有项目\n- 所有任务\n- 所有问题\n- 所有分析记录\n- 用户配置\n\n此操作不可恢复!')) {
|
if (!window.confirm('⚠️ 警告:确定要清空所有数据吗?\n\n此操作将删除:\n- 所有项目\n- 所有任务\n- 所有问题\n- 所有分析记录\n- 用户配置\n\n此操作不可恢复!')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 二次确认
|
|
||||||
if (!window.confirm('请再次确认:您真的要清空所有数据吗?')) {
|
if (!window.confirm('请再次确认:您真的要清空所有数据吗?')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setMessage(null);
|
setMessage(null);
|
||||||
|
|
||||||
const result = await api.clearDatabase();
|
const result = await api.clearDatabase();
|
||||||
|
|
||||||
const deleted = result.deleted;
|
const deleted = result.deleted;
|
||||||
const summary = [
|
const summary = [
|
||||||
deleted.projects > 0 && `${deleted.projects} 个项目`,
|
deleted.projects > 0 && `${deleted.projects} 个项目`,
|
||||||
|
|
@ -213,18 +175,12 @@ export function DatabaseManager() {
|
||||||
deleted.analyses > 0 && `${deleted.analyses} 条分析记录`,
|
deleted.analyses > 0 && `${deleted.analyses} 条分析记录`,
|
||||||
deleted.config > 0 && '用户配置',
|
deleted.config > 0 && '用户配置',
|
||||||
].filter(Boolean).join('、');
|
].filter(Boolean).join('、');
|
||||||
|
|
||||||
toast.success(`数据已清空!已删除:${summary}`);
|
toast.success(`数据已清空!已删除:${summary}`);
|
||||||
setMessage({ type: 'success', text: `数据已清空!已删除:${summary}` });
|
setMessage({ type: 'success', text: `数据已清空!已删除:${summary}` });
|
||||||
|
|
||||||
// 刷新统计信息和健康检查
|
|
||||||
loadStats();
|
loadStats();
|
||||||
loadHealth();
|
loadHealth();
|
||||||
|
|
||||||
// 延迟刷新页面
|
|
||||||
setTimeout(() => window.location.reload(), 2000);
|
setTimeout(() => window.location.reload(), 2000);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('清空失败:', error);
|
|
||||||
const errorMsg = error?.response?.data?.detail || error?.message || '清空数据失败,请重试';
|
const errorMsg = error?.response?.data?.detail || error?.message || '清空数据失败,请重试';
|
||||||
toast.error(errorMsg);
|
toast.error(errorMsg);
|
||||||
setMessage({ type: 'error', text: errorMsg });
|
setMessage({ type: 'error', text: errorMsg });
|
||||||
|
|
@ -233,241 +189,206 @@ export function DatabaseManager() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHealthStatusColor = (status: string) => {
|
const getHealthStatusBadge = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'healthy':
|
case 'healthy':
|
||||||
return 'text-green-600 bg-green-50 border-green-200';
|
return <Badge className="rounded-none border-black bg-green-100 text-green-800 font-bold uppercase">健康</Badge>;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
return 'text-yellow-600 bg-yellow-50 border-yellow-200';
|
return <Badge className="rounded-none border-black bg-yellow-100 text-yellow-800 font-bold uppercase">警告</Badge>;
|
||||||
case 'error':
|
case 'error':
|
||||||
return 'text-red-600 bg-red-50 border-red-200';
|
return <Badge className="rounded-none border-black bg-red-100 text-red-800 font-bold uppercase">错误</Badge>;
|
||||||
default:
|
default:
|
||||||
return 'text-gray-600 bg-gray-50 border-gray-200';
|
return <Badge className="rounded-none border-black bg-gray-100 text-gray-800 font-bold uppercase">未知</Badge>;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getHealthStatusText = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'healthy':
|
|
||||||
return '健康';
|
|
||||||
case 'warning':
|
|
||||||
return '警告';
|
|
||||||
case 'error':
|
|
||||||
return '错误';
|
|
||||||
default:
|
|
||||||
return '未知';
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* 健康检查 */}
|
{/* 健康检查 */}
|
||||||
<Card>
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
||||||
<CardHeader>
|
<div className="p-4 border-b-2 border-black bg-gray-50 flex items-center justify-between">
|
||||||
<div className="flex items-center justify-between">
|
<div>
|
||||||
<div className="flex items-center gap-2">
|
<h3 className="text-lg font-display font-bold uppercase flex items-center gap-2">
|
||||||
<Activity className="h-5 w-5" />
|
<Activity className="h-5 w-5" />
|
||||||
<CardTitle>数据库健康检查</CardTitle>
|
数据库健康检查
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-gray-500 font-mono mt-1">检查数据库连接状态和数据完整性</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={loadHealth}
|
onClick={loadHealth}
|
||||||
disabled={healthLoading}
|
disabled={healthLoading}
|
||||||
|
className="retro-btn bg-white text-black border-2 border-black hover:bg-gray-100 rounded-none h-8 font-bold uppercase"
|
||||||
>
|
>
|
||||||
<RefreshCw className={`h-4 w-4 mr-2 ${healthLoading ? 'animate-spin' : ''}`} />
|
<RefreshCw className={`h-3 w-3 mr-2 ${healthLoading ? 'animate-spin' : ''}`} />
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<div className="p-6 font-mono">
|
||||||
检查数据库连接状态和数据完整性
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
{healthLoading ? (
|
{healthLoading ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<RefreshCw className="h-6 w-6 animate-spin text-muted-foreground" />
|
<RefreshCw className="h-6 w-6 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : health ? (
|
) : health ? (
|
||||||
<>
|
<div className="space-y-4">
|
||||||
<div className={`flex items-center gap-3 p-4 rounded-lg border ${getHealthStatusColor(health.status)}`}>
|
<div className="flex items-center gap-4">
|
||||||
{health.status === 'healthy' ? (
|
{health.status === 'healthy' ? (
|
||||||
<CheckCircle2 className="h-5 w-5" />
|
<CheckCircle2 className="h-5 w-5 text-green-600" />
|
||||||
) : health.status === 'warning' ? (
|
) : health.status === 'warning' ? (
|
||||||
<AlertTriangle className="h-5 w-5" />
|
<AlertTriangle className="h-5 w-5 text-yellow-600" />
|
||||||
) : (
|
) : (
|
||||||
<AlertCircle className="h-5 w-5" />
|
<AlertCircle className="h-5 w-5 text-red-600" />
|
||||||
)}
|
)}
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-semibold">状态:</span>
|
<span className="font-bold uppercase text-sm">状态:</span>
|
||||||
<Badge variant={health.status === 'healthy' ? 'default' : health.status === 'warning' ? 'secondary' : 'destructive'}>
|
{getHealthStatusBadge(health.status)}
|
||||||
{getHealthStatusText(health.status)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div className="text-sm mt-1">
|
|
||||||
数据库连接:{health.database_connected ? '正常' : '异常'} |
|
|
||||||
总记录数:{health.total_records.toLocaleString()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span className="text-sm">
|
||||||
|
数据库连接:{health.database_connected ? '正常' : '异常'} | 总记录数:{health.total_records.toLocaleString()}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{health.issues.length > 0 && (
|
{health.issues.length > 0 && (
|
||||||
<Alert variant="destructive">
|
<div className="bg-red-50 border-2 border-red-500 p-4 shadow-[2px_2px_0px_0px_rgba(239,68,68,1)]">
|
||||||
<AlertCircle className="h-4 w-4" />
|
<p className="font-bold text-red-800 uppercase text-sm mb-2">发现的问题:</p>
|
||||||
<AlertDescription>
|
<ul className="list-disc list-inside space-y-1 text-sm text-red-700">
|
||||||
<div className="font-semibold mb-2">发现的问题:</div>
|
|
||||||
<ul className="list-disc list-inside space-y-1">
|
|
||||||
{health.issues.map((issue, index) => (
|
{health.issues.map((issue, index) => (
|
||||||
<li key={index}>{issue}</li>
|
<li key={index}>{issue}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</AlertDescription>
|
</div>
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{health.warnings.length > 0 && (
|
{health.warnings.length > 0 && (
|
||||||
<Alert>
|
<div className="bg-yellow-50 border-2 border-yellow-500 p-4 shadow-[2px_2px_0px_0px_rgba(234,179,8,1)]">
|
||||||
<AlertTriangle className="h-4 w-4" />
|
<p className="font-bold text-yellow-800 uppercase text-sm mb-2">警告信息:</p>
|
||||||
<AlertDescription>
|
<ul className="list-disc list-inside space-y-1 text-sm text-yellow-700">
|
||||||
<div className="font-semibold mb-2">警告信息:</div>
|
|
||||||
<ul className="list-disc list-inside space-y-1">
|
|
||||||
{health.warnings.map((warning, index) => (
|
{health.warnings.map((warning, index) => (
|
||||||
<li key={index}>{warning}</li>
|
<li key={index}>{warning}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</AlertDescription>
|
</div>
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Alert>
|
<div className="bg-blue-50 border-2 border-blue-500 p-4 flex items-start gap-3 shadow-[2px_2px_0px_0px_rgba(59,130,246,1)]">
|
||||||
<Info className="h-4 w-4" />
|
<Info className="h-5 w-5 text-blue-600 mt-0.5" />
|
||||||
<AlertDescription>无法加载健康检查信息</AlertDescription>
|
<p className="text-sm text-blue-800 font-bold">无法加载健康检查信息</p>
|
||||||
</Alert>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
|
||||||
{/* 统计信息 */}
|
{/* 详细统计 */}
|
||||||
<Card>
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
||||||
<CardHeader>
|
<div className="p-4 border-b-2 border-black bg-gray-50 flex items-center justify-between">
|
||||||
<div className="flex items-center justify-between">
|
<div>
|
||||||
<div className="flex items-center gap-2">
|
<h3 className="text-lg font-display font-bold uppercase flex items-center gap-2">
|
||||||
<Database className="h-5 w-5" />
|
<Database className="h-5 w-5" />
|
||||||
<CardTitle>数据统计</CardTitle>
|
详细数据统计
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-gray-500 font-mono mt-1">查看数据库中的详细统计信息</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={loadStats}
|
onClick={loadStats}
|
||||||
disabled={statsLoading}
|
disabled={statsLoading}
|
||||||
|
className="retro-btn bg-white text-black border-2 border-black hover:bg-gray-100 rounded-none h-8 font-bold uppercase"
|
||||||
>
|
>
|
||||||
<RefreshCw className={`h-4 w-4 mr-2 ${statsLoading ? 'animate-spin' : ''}`} />
|
<RefreshCw className={`h-3 w-3 mr-2 ${statsLoading ? 'animate-spin' : ''}`} />
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<div className="p-6 font-mono">
|
||||||
查看数据库中的数据统计信息
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
{statsLoading ? (
|
{statsLoading ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<RefreshCw className="h-6 w-6 animate-spin text-muted-foreground" />
|
<RefreshCw className="h-6 w-6 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : stats ? (
|
) : stats ? (
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
<div className="space-y-1">
|
<div className="p-4 border-2 border-black bg-gray-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
||||||
<div className="text-sm text-muted-foreground">项目</div>
|
<p className="text-xs text-gray-600 uppercase font-bold">项目</p>
|
||||||
<div className="text-2xl font-bold">{stats.total_projects}</div>
|
<p className="text-2xl font-bold">{stats.total_projects}</p>
|
||||||
<div className="text-xs text-muted-foreground">活跃: {stats.active_projects}</div>
|
<p className="text-xs text-gray-500">活跃: {stats.active_projects}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="p-4 border-2 border-black bg-gray-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
||||||
<div className="text-sm text-muted-foreground">任务</div>
|
<p className="text-xs text-gray-600 uppercase font-bold">任务</p>
|
||||||
<div className="text-2xl font-bold">{stats.total_tasks}</div>
|
<p className="text-2xl font-bold">{stats.total_tasks}</p>
|
||||||
<div className="text-xs text-muted-foreground">
|
<p className="text-xs text-gray-500">完成: {stats.completed_tasks} | 进行中: {stats.running_tasks}</p>
|
||||||
完成: {stats.completed_tasks} | 进行中: {stats.running_tasks}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="p-4 border-2 border-black bg-gray-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
||||||
|
<p className="text-xs text-gray-600 uppercase font-bold">问题</p>
|
||||||
|
<p className="text-2xl font-bold">{stats.total_issues}</p>
|
||||||
|
<p className="text-xs text-gray-500">未解决: {stats.open_issues} | 已解决: {stats.resolved_issues}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="p-4 border-2 border-black bg-gray-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
||||||
<div className="text-sm text-muted-foreground">问题</div>
|
<p className="text-xs text-gray-600 uppercase font-bold">分析记录</p>
|
||||||
<div className="text-2xl font-bold">{stats.total_issues}</div>
|
<p className="text-2xl font-bold">{stats.total_analyses}</p>
|
||||||
<div className="text-xs text-muted-foreground">
|
<p className="text-xs text-gray-500">即时分析</p>
|
||||||
未解决: {stats.open_issues} | 已解决: {stats.resolved_issues}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<div className="text-sm text-muted-foreground">分析记录</div>
|
|
||||||
<div className="text-2xl font-bold">{stats.total_analyses}</div>
|
|
||||||
<div className="text-xs text-muted-foreground">即时分析</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Alert>
|
<div className="bg-blue-50 border-2 border-blue-500 p-4 flex items-start gap-3 shadow-[2px_2px_0px_0px_rgba(59,130,246,1)]">
|
||||||
<Info className="h-4 w-4" />
|
<Info className="h-5 w-5 text-blue-600 mt-0.5" />
|
||||||
<AlertDescription>无法加载统计信息</AlertDescription>
|
<p className="text-sm text-blue-800 font-bold">无法加载统计信息</p>
|
||||||
</Alert>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
|
||||||
{/* 数据操作 */}
|
{/* 数据操作 */}
|
||||||
<Card>
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
||||||
<CardHeader>
|
<div className="p-4 border-b-2 border-black bg-gray-50">
|
||||||
<CardTitle className="flex items-center gap-2">
|
<h3 className="text-lg font-display font-bold uppercase">数据操作</h3>
|
||||||
<Server className="h-5 w-5" />
|
<p className="text-xs text-gray-500 font-mono mt-1">管理您的数据,包括导出、导入和清空</p>
|
||||||
数据操作
|
</div>
|
||||||
</CardTitle>
|
<div className="p-6 font-mono space-y-6">
|
||||||
<CardDescription>
|
|
||||||
管理您的数据,包括导出、导入和清空。数据存储在后端 PostgreSQL 数据库中。
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
{message && (
|
{message && (
|
||||||
<Alert variant={message.type === 'error' ? 'destructive' : 'default'}>
|
<div className={`p-4 border-2 flex items-start gap-3 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] ${
|
||||||
|
message.type === 'success'
|
||||||
|
? 'bg-green-50 border-green-500'
|
||||||
|
: 'bg-red-50 border-red-500'
|
||||||
|
}`}>
|
||||||
{message.type === 'success' ? (
|
{message.type === 'success' ? (
|
||||||
<CheckCircle2 className="h-4 w-4" />
|
<CheckCircle2 className="h-5 w-5 text-green-600 mt-0.5" />
|
||||||
) : (
|
) : (
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-5 w-5 text-red-600 mt-0.5" />
|
||||||
)}
|
)}
|
||||||
<AlertDescription>{message.text}</AlertDescription>
|
<p className={`text-sm font-bold ${message.type === 'success' ? 'text-green-800' : 'text-red-800'}`}>
|
||||||
</Alert>
|
{message.text}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
<div className="grid gap-6 md:grid-cols-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-3">
|
||||||
<h4 className="text-sm font-medium flex items-center gap-2">
|
<h4 className="text-sm font-bold uppercase flex items-center gap-2">
|
||||||
<Download className="h-4 w-4" />
|
<Download className="h-4 w-4" />
|
||||||
导出数据
|
导出数据
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-xs text-gray-500">将数据导出为 JSON 文件,用于备份或迁移</p>
|
||||||
将数据导出为 JSON 文件,用于备份或迁移
|
|
||||||
</p>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full"
|
className="w-full retro-btn bg-white text-black border-2 border-black hover:bg-gray-100 rounded-none h-10 font-bold uppercase shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]"
|
||||||
variant="outline"
|
|
||||||
>
|
>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
<Download className="mr-2 h-4 w-4" />
|
||||||
导出数据
|
导出数据
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-3">
|
||||||
<h4 className="text-sm font-medium flex items-center gap-2">
|
<h4 className="text-sm font-bold uppercase flex items-center gap-2">
|
||||||
<Upload className="h-4 w-4" />
|
<Upload className="h-4 w-4" />
|
||||||
导入数据
|
导入数据
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-xs text-gray-500">从 JSON 文件恢复数据(最大 50MB)</p>
|
||||||
从 JSON 文件恢复数据(最大 50MB)
|
|
||||||
</p>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => document.getElementById('import-file')?.click()}
|
onClick={() => document.getElementById('import-file')?.click()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full"
|
className="w-full retro-btn bg-white text-black border-2 border-black hover:bg-gray-100 rounded-none h-10 font-bold uppercase shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]"
|
||||||
variant="outline"
|
|
||||||
>
|
>
|
||||||
<Upload className="mr-2 h-4 w-4" />
|
<Upload className="mr-2 h-4 w-4" />
|
||||||
导入数据
|
导入数据
|
||||||
|
|
@ -481,19 +402,16 @@ export function DatabaseManager() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-3">
|
||||||
<h4 className="text-sm font-medium text-destructive flex items-center gap-2">
|
<h4 className="text-sm font-bold uppercase flex items-center gap-2 text-red-600">
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
清空数据
|
清空数据
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-xs text-gray-500">删除所有数据(不可恢复)</p>
|
||||||
删除所有数据(不可恢复)
|
|
||||||
</p>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleClear}
|
onClick={handleClear}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full"
|
className="w-full bg-red-500 hover:bg-red-600 text-white border-2 border-black rounded-none h-10 font-bold uppercase shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]"
|
||||||
variant="destructive"
|
|
||||||
>
|
>
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
<Trash2 className="mr-2 h-4 w-4" />
|
||||||
清空数据
|
清空数据
|
||||||
|
|
@ -501,19 +419,19 @@ export function DatabaseManager() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<div className="pt-6 border-t-2 border-black border-dashed">
|
||||||
|
<div className="bg-blue-50 border-2 border-blue-500 p-4 flex items-start gap-3 shadow-[2px_2px_0px_0px_rgba(59,130,246,1)]">
|
||||||
<Alert>
|
<Info className="h-5 w-5 text-blue-600 mt-0.5" />
|
||||||
<Info className="h-4 w-4" />
|
<p className="text-sm text-blue-800">
|
||||||
<AlertDescription>
|
<strong className="uppercase">提示:</strong>
|
||||||
<strong>提示:</strong>
|
|
||||||
{dbMode === 'api'
|
{dbMode === 'api'
|
||||||
? '数据存储在后端 PostgreSQL 数据库中,支持多用户、多设备同步。建议定期导出备份。'
|
? '数据存储在后端 PostgreSQL 数据库中,支持多用户、多设备同步。建议定期导出备份。'
|
||||||
: '建议定期导出数据备份,以防意外数据丢失。'}
|
: '建议定期导出数据备份,以防意外数据丢失。'}
|
||||||
</AlertDescription>
|
</p>
|
||||||
</Alert>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
UserCircle
|
UserCircle
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import routes from "@/app/routes";
|
import routes from "@/app/routes";
|
||||||
|
import { version } from "../../../package.json";
|
||||||
|
|
||||||
// Icon mapping for routes
|
// Icon mapping for routes
|
||||||
const routeIcons: Record<string, React.ReactNode> = {
|
const routeIcons: Record<string, React.ReactNode> = {
|
||||||
|
|
@ -192,7 +193,7 @@ export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) {
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="font-mono font-bold text-sm uppercase">GitHub</span>
|
<span className="font-mono font-bold text-sm uppercase">GitHub</span>
|
||||||
<span className="text-xs text-gray-500 font-mono">v1.0.0</span>
|
<span className="text-xs text-gray-500 font-mono">v{version}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ export function SystemConfig() {
|
||||||
<Settings className="w-3 h-3 mr-2" />
|
<Settings className="w-3 h-3 mr-2" />
|
||||||
分析参数
|
分析参数
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="other" className="rounded-none data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">
|
<TabsTrigger value="other" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">
|
||||||
<Globe className="w-3 h-3 mr-2" />
|
<Globe className="w-3 h-3 mr-2" />
|
||||||
其他配置
|
其他配置
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
|
||||||
|
|
@ -142,38 +142,10 @@ export default function Account() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* Header */}
|
|
||||||
<div className="relative z-10 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter">
|
|
||||||
账号<span className="text-primary">_管理</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">管理您的个人账号信息</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={handleSwitchAccount}
|
|
||||||
className="terminal-btn-primary bg-white text-black hover:bg-gray-100"
|
|
||||||
>
|
|
||||||
<UserPlus className="w-4 h-4 mr-2" />
|
|
||||||
切换账号
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="destructive"
|
|
||||||
onClick={() => setShowLogoutDialog(true)}
|
|
||||||
className="bg-red-500 hover:bg-red-600 text-white border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]"
|
|
||||||
>
|
|
||||||
<LogOut className="w-4 h-4 mr-2" />
|
|
||||||
退出登录
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
{/* Profile Card */}
|
{/* Profile Card */}
|
||||||
<Card className="retro-card border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
|
<Card className="retro-card border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
|
||||||
|
|
@ -201,6 +173,25 @@ export default function Account() {
|
||||||
<span className="font-bold">{formatDate(profile?.created_at)}</span>
|
<span className="font-bold">{formatDate(profile?.created_at)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleSwitchAccount}
|
||||||
|
className="w-full terminal-btn-primary bg-white text-black hover:bg-gray-100"
|
||||||
|
>
|
||||||
|
<UserPlus className="w-4 h-4 mr-2" />
|
||||||
|
切换账号
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => setShowLogoutDialog(true)}
|
||||||
|
className="w-full bg-red-500 hover:bg-red-600 text-white border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]"
|
||||||
|
>
|
||||||
|
<LogOut className="w-4 h-4 mr-2" />
|
||||||
|
退出登录
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,139 +1,18 @@
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Progress } from "@/components/ui/progress";
|
|
||||||
import {
|
|
||||||
HardDrive,
|
|
||||||
RefreshCw,
|
|
||||||
Info,
|
|
||||||
CheckCircle2,
|
|
||||||
AlertCircle,
|
|
||||||
FolderOpen,
|
|
||||||
Clock,
|
|
||||||
AlertTriangle,
|
|
||||||
TrendingUp,
|
|
||||||
Package,
|
|
||||||
Settings
|
|
||||||
} from "lucide-react";
|
|
||||||
import { api, dbMode } from "@/shared/config/database";
|
|
||||||
import { DatabaseManager } from "@/components/database/DatabaseManager";
|
import { DatabaseManager } from "@/components/database/DatabaseManager";
|
||||||
import { SystemConfig } from "@/components/system/SystemConfig";
|
import { SystemConfig } from "@/components/system/SystemConfig";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export default function AdminDashboard() {
|
export default function AdminDashboard() {
|
||||||
const [stats, setStats] = useState({
|
|
||||||
totalProjects: 0,
|
|
||||||
activeProjects: 0,
|
|
||||||
totalTasks: 0,
|
|
||||||
completedTasks: 0,
|
|
||||||
totalIssues: 0,
|
|
||||||
resolvedIssues: 0,
|
|
||||||
storageUsed: '计算中...',
|
|
||||||
storageQuota: '未知'
|
|
||||||
});
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [storageDetails, setStorageDetails] = useState<{
|
|
||||||
usage: number;
|
|
||||||
quota: number;
|
|
||||||
percentage: number;
|
|
||||||
} | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadStats();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const loadStats = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const projectStats = await api.getProjectStats();
|
|
||||||
|
|
||||||
// 获取存储使用量(IndexedDB)
|
|
||||||
let storageUsed = '未知';
|
|
||||||
let storageQuota = '未知';
|
|
||||||
let details = null;
|
|
||||||
|
|
||||||
if ('storage' in navigator && 'estimate' in navigator.storage) {
|
|
||||||
try {
|
|
||||||
const estimate = await navigator.storage.estimate();
|
|
||||||
const usedMB = ((estimate.usage || 0) / 1024 / 1024).toFixed(2);
|
|
||||||
const quotaMB = ((estimate.quota || 0) / 1024 / 1024).toFixed(2);
|
|
||||||
const percentage = estimate.quota ? ((estimate.usage || 0) / estimate.quota * 100) : 0;
|
|
||||||
|
|
||||||
storageUsed = `${usedMB} MB`;
|
|
||||||
storageQuota = `${quotaMB} MB`;
|
|
||||||
|
|
||||||
details = {
|
|
||||||
usage: estimate.usage || 0,
|
|
||||||
quota: estimate.quota || 0,
|
|
||||||
percentage: Math.round(percentage)
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to estimate storage:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setStats({
|
|
||||||
totalProjects: projectStats.total_projects || 0,
|
|
||||||
activeProjects: projectStats.active_projects || 0,
|
|
||||||
totalTasks: projectStats.total_tasks || 0,
|
|
||||||
completedTasks: projectStats.completed_tasks || 0,
|
|
||||||
totalIssues: projectStats.total_issues || 0,
|
|
||||||
resolvedIssues: projectStats.resolved_issues || 0,
|
|
||||||
storageUsed,
|
|
||||||
storageQuota
|
|
||||||
});
|
|
||||||
|
|
||||||
setStorageDetails(details);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load stats:', error);
|
|
||||||
toast.error("加载统计数据失败");
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-screen bg-background">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
<div className="space-y-4 text-center">
|
|
||||||
<div className="animate-spin rounded-none h-16 w-16 border-8 border-black border-t-transparent mx-auto"></div>
|
|
||||||
<p className="text-black font-mono font-bold uppercase">加载数据库信息...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* 页面标题 */}
|
|
||||||
<div className="relative z-10 flex items-center justify-between border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter flex items-center gap-3">
|
|
||||||
<Settings className="h-8 w-8 text-black" />
|
|
||||||
系统管理
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 mt-2 font-mono border-l-2 border-primary pl-2">
|
|
||||||
管理系统配置、LLM设置、数据库和存储使用情况
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Button variant="outline" onClick={loadStats} className="retro-btn bg-white text-black border-2 border-black hover:bg-gray-100 rounded-none h-10 font-bold uppercase shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[-1px] hover:translate-y-[-1px] hover:shadow-[3px_3px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<RefreshCw className="w-4 h-4 mr-2" />
|
|
||||||
刷新数据
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 主要内容标签页 */}
|
{/* 主要内容标签页 */}
|
||||||
<Tabs defaultValue="config" className="w-full">
|
<Tabs defaultValue="config" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-5 bg-transparent border-2 border-black p-0 h-auto gap-0 mb-6">
|
<TabsList className="grid w-full grid-cols-2 bg-transparent border-2 border-black p-0 h-auto gap-0 mb-6">
|
||||||
<TabsTrigger value="config" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">系统配置</TabsTrigger>
|
<TabsTrigger value="config" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">系统配置</TabsTrigger>
|
||||||
<TabsTrigger value="overview" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">数据概览</TabsTrigger>
|
<TabsTrigger value="data" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">数据管理</TabsTrigger>
|
||||||
<TabsTrigger value="storage" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">存储管理</TabsTrigger>
|
|
||||||
<TabsTrigger value="operations" className="rounded-none border-r-2 border-black data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">数据操作</TabsTrigger>
|
|
||||||
<TabsTrigger value="settings" className="rounded-none data-[state=active]:bg-black data-[state=active]:text-white font-mono font-bold uppercase h-10 text-xs">高级设置</TabsTrigger>
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
{/* 系统配置 */}
|
{/* 系统配置 */}
|
||||||
|
|
@ -141,322 +20,10 @@ export default function AdminDashboard() {
|
||||||
<SystemConfig />
|
<SystemConfig />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* 数据概览 */}
|
{/* 数据管理 */}
|
||||||
<TabsContent value="overview" className="flex flex-col gap-6">
|
<TabsContent value="data" className="space-y-6">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
||||||
{/* 任务完成率 */}
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase flex items-center gap-2">
|
|
||||||
<TrendingUp className="h-5 w-5" />
|
|
||||||
任务完成率
|
|
||||||
</h3>
|
|
||||||
<p className="text-xs text-gray-500 font-mono mt-1">审计任务的完成情况统计</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 space-y-4 font-mono">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between text-sm font-bold">
|
|
||||||
<span>已完成</span>
|
|
||||||
<span>
|
|
||||||
{stats.totalTasks > 0
|
|
||||||
? Math.round((stats.completedTasks / stats.totalTasks) * 100)
|
|
||||||
: 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Progress
|
|
||||||
value={stats.totalTasks > 0
|
|
||||||
? (stats.completedTasks / stats.totalTasks) * 100
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
className="h-4 border-2 border-black rounded-none bg-gray-200 [&>div]:bg-green-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 gap-4 pt-4 border-t-2 border-black border-dashed">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-xs text-gray-500 uppercase font-bold">总任务数</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.totalTasks}</p>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-xs text-gray-500 uppercase font-bold">已完成</p>
|
|
||||||
<p className="text-2xl font-bold text-green-600">{stats.completedTasks}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 问题解决率 */}
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase flex items-center gap-2">
|
|
||||||
<CheckCircle2 className="h-5 w-5" />
|
|
||||||
问题解决率
|
|
||||||
</h3>
|
|
||||||
<p className="text-xs text-gray-500 font-mono mt-1">代码问题的解决情况统计</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 space-y-4 font-mono">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between text-sm font-bold">
|
|
||||||
<span>已解决</span>
|
|
||||||
<span>
|
|
||||||
{stats.totalIssues > 0
|
|
||||||
? Math.round((stats.resolvedIssues / stats.totalIssues) * 100)
|
|
||||||
: 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Progress
|
|
||||||
value={stats.totalIssues > 0
|
|
||||||
? (stats.resolvedIssues / stats.totalIssues) * 100
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
className="h-4 border-2 border-black rounded-none bg-gray-200 [&>div]:bg-orange-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 gap-4 pt-4 border-t-2 border-black border-dashed">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-xs text-gray-500 uppercase font-bold">总问题数</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.totalIssues}</p>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-xs text-gray-500 uppercase font-bold">已解决</p>
|
|
||||||
<p className="text-2xl font-bold text-green-600">{stats.resolvedIssues}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 数据库表统计 */}
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase flex items-center gap-2">
|
|
||||||
<Package className="h-5 w-5" />
|
|
||||||
数据库表统计
|
|
||||||
</h3>
|
|
||||||
<p className="text-xs text-gray-500 font-mono mt-1">各数据表的记录数量</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 font-mono">
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
||||||
<div className="p-4 border-2 border-black bg-blue-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[-1px] hover:translate-y-[-1px] hover:shadow-[3px_3px_0px_0px_rgba(0,0,0,1)] transition-all">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<FolderOpen className="h-8 w-8 text-primary" />
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-gray-600 uppercase font-bold">项目</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.totalProjects}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 border-2 border-black bg-green-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[-1px] hover:translate-y-[-1px] hover:shadow-[3px_3px_0px_0px_rgba(0,0,0,1)] transition-all">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Clock className="h-8 w-8 text-green-600" />
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-gray-600 uppercase font-bold">审计任务</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.totalTasks}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 border-2 border-black bg-orange-50 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[-1px] hover:translate-y-[-1px] hover:shadow-[3px_3px_0px_0px_rgba(0,0,0,1)] transition-all">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<AlertTriangle className="h-8 w-8 text-orange-600" />
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-gray-600 uppercase font-bold">问题</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.totalIssues}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
{/* 存储管理 */}
|
|
||||||
<TabsContent value="storage" className="flex flex-col gap-6">
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase flex items-center gap-2">
|
|
||||||
<HardDrive className="h-5 w-5" />
|
|
||||||
存储空间使用情况
|
|
||||||
</h3>
|
|
||||||
<p className="text-xs text-gray-500 font-mono mt-1">
|
|
||||||
浏览器 IndexedDB 存储空间的使用详情
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 flex flex-col gap-6 font-mono">
|
|
||||||
{storageDetails ? (
|
|
||||||
<>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between text-sm font-bold">
|
|
||||||
<span>已使用空间</span>
|
|
||||||
<span>{storageDetails.percentage}%</span>
|
|
||||||
</div>
|
|
||||||
<Progress value={storageDetails.percentage} className="h-4 border-2 border-black rounded-none bg-gray-200 [&>div]:bg-primary" />
|
|
||||||
<div className="flex items-center justify-between text-xs text-gray-500 font-bold">
|
|
||||||
<span>{stats.storageUsed} 已使用</span>
|
|
||||||
<span>{stats.storageQuota} 总配额</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 pt-4">
|
|
||||||
<div className="p-4 bg-gray-100 border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<p className="text-xs text-gray-600 uppercase font-bold mb-1">已使用</p>
|
|
||||||
<p className="text-xl font-bold">{stats.storageUsed}</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 bg-gray-100 border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<p className="text-xs text-gray-600 uppercase font-bold mb-1">总配额</p>
|
|
||||||
<p className="text-xl font-bold">{stats.storageQuota}</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 bg-gray-100 border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<p className="text-xs text-gray-600 uppercase font-bold mb-1">剩余空间</p>
|
|
||||||
<p className="text-xl font-bold">
|
|
||||||
{((storageDetails.quota - storageDetails.usage) / 1024 / 1024).toFixed(2)} MB
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{storageDetails.percentage > 80 && (
|
|
||||||
<div className="bg-red-50 border-2 border-red-500 p-4 flex items-start gap-3 shadow-[4px_4px_0px_0px_rgba(239,68,68,1)]">
|
|
||||||
<AlertCircle className="h-5 w-5 text-red-600 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-red-800 uppercase">警告</p>
|
|
||||||
<p className="text-sm text-red-700 font-medium">
|
|
||||||
存储空间使用率已超过 80%,建议清理不需要的数据或导出备份后清空数据库。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="bg-blue-50 border-2 border-blue-500 p-4 flex items-start gap-3 shadow-[4px_4px_0px_0px_rgba(59,130,246,1)]">
|
|
||||||
<Info className="h-5 w-5 text-blue-600 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-blue-800 uppercase">提示</p>
|
|
||||||
<p className="text-sm text-blue-700 font-medium">
|
|
||||||
无法获取存储空间信息。您的浏览器可能不支持 Storage API。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase">存储优化建议</h3>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 space-y-3 font-mono">
|
|
||||||
<div className="flex items-start gap-3 p-3 bg-gray-50 border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<CheckCircle2 className="h-5 w-5 text-green-600 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-black uppercase text-sm">定期导出备份</p>
|
|
||||||
<p className="text-xs text-gray-600 font-medium">
|
|
||||||
建议定期导出数据为 JSON 文件,防止数据丢失
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3 p-3 bg-gray-50 border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<CheckCircle2 className="h-5 w-5 text-green-600 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-black uppercase text-sm">清理旧数据</p>
|
|
||||||
<p className="text-xs text-gray-600 font-medium">
|
|
||||||
删除不再需要的项目和任务可以释放存储空间
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3 p-3 bg-gray-50 border-2 border-black shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<CheckCircle2 className="h-5 w-5 text-green-600 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-black uppercase text-sm">监控存储使用</p>
|
|
||||||
<p className="text-xs text-gray-600 font-medium">
|
|
||||||
定期检查存储使用情况,避免超出浏览器限制
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
{/* 数据操作 */}
|
|
||||||
<TabsContent value="operations" className="flex flex-col gap-6">
|
|
||||||
<DatabaseManager />
|
<DatabaseManager />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* 设置 */}
|
|
||||||
<TabsContent value="settings" className="flex flex-col gap-6">
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase">数据库设置</h3>
|
|
||||||
<p className="text-xs text-gray-500 font-mono mt-1">配置数据库行为和性能选项</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 space-y-4 font-mono">
|
|
||||||
<div className="bg-blue-50 border-2 border-blue-500 p-4 flex items-start gap-3 shadow-[4px_4px_0px_0px_rgba(59,130,246,1)]">
|
|
||||||
<Info className="h-5 w-5 text-blue-600 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-blue-800 uppercase text-sm">当前数据库模式</p>
|
|
||||||
<p className="text-sm text-blue-700 font-medium mt-1">
|
|
||||||
{
|
|
||||||
dbMode === 'api' ? '后端 PostgreSQL 数据库' :
|
|
||||||
dbMode === 'local' ? '本地 IndexedDB' :
|
|
||||||
dbMode === 'supabase' ? 'Supabase 云端(已废弃)' :
|
|
||||||
'演示模式'
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4 pt-4">
|
|
||||||
<div className="flex items-center justify-between p-4 border-2 border-black bg-white shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-black uppercase text-sm">自动备份</p>
|
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
|
||||||
定期自动导出数据备份(开发中)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline" className="rounded-none border-black bg-gray-100 font-mono text-xs">即将推出</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between p-4 border-2 border-black bg-white shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-black uppercase text-sm">数据压缩</p>
|
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
|
||||||
压缩存储数据以节省空间(开发中)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline" className="rounded-none border-black bg-gray-100 font-mono text-xs">即将推出</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between p-4 border-2 border-black bg-white shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-black uppercase text-sm">数据同步</p>
|
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
|
||||||
在多个设备间同步数据(开发中)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline" className="rounded-none border-black bg-gray-100 font-mono text-xs">即将推出</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50">
|
|
||||||
<h3 className="text-lg font-display font-bold uppercase">关于本地数据库</h3>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 space-y-3 text-sm text-gray-600 font-mono font-medium">
|
|
||||||
<p>
|
|
||||||
本地数据库使用浏览器的 IndexedDB 技术存储数据,具有以下特点:
|
|
||||||
</p>
|
|
||||||
<ul className="list-disc list-inside space-y-2 ml-2">
|
|
||||||
<li>数据完全存储在本地,不会上传到服务器</li>
|
|
||||||
<li>支持离线访问,无需网络连接</li>
|
|
||||||
<li>存储容量取决于浏览器和设备</li>
|
|
||||||
<li>清除浏览器数据会删除所有本地数据</li>
|
|
||||||
<li>不同浏览器的数据相互独立</li>
|
|
||||||
</ul>
|
|
||||||
<p className="pt-2 border-t-2 border-black border-dashed mt-4">
|
|
||||||
<strong className="text-black uppercase">建议:</strong>定期导出数据备份,以防意外数据丢失。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -128,22 +128,10 @@ export default function AuditTasks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* 页面标题 */}
|
|
||||||
<div className="relative z-10 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter">审计任务</h1>
|
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">查看和管理所有代码审计任务的执行状态</p>
|
|
||||||
</div>
|
|
||||||
<Button className="retro-btn bg-primary text-white hover:bg-primary/90 h-12 px-6 text-lg font-bold uppercase shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all" onClick={() => setShowCreateDialog(true)}>
|
|
||||||
<Plus className="w-5 h-5 mr-2" />
|
|
||||||
新建任务
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 统计卡片 */}
|
{/* 统计卡片 */}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-5 hover:translate-x-[-2px] hover:translate-y-[-2px] hover:shadow-[6px_6px_0px_0px_rgba(0,0,0,1)] transition-all">
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-5 hover:translate-x-[-2px] hover:translate-y-[-2px] hover:shadow-[6px_6px_0px_0px_rgba(0,0,0,1)] transition-all">
|
||||||
|
|
@ -207,6 +195,10 @@ export default function AuditTasks() {
|
||||||
className="pl-10 retro-input h-10"
|
className="pl-10 retro-input h-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<Button className="retro-btn bg-primary text-white hover:bg-primary/90 h-10 px-4 font-bold uppercase shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all" onClick={() => setShowCreateDialog(true)}>
|
||||||
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
新建任务
|
||||||
|
</Button>
|
||||||
<div className="flex space-x-2 w-full md:w-auto overflow-x-auto pb-2 md:pb-0">
|
<div className="flex space-x-2 w-full md:w-auto overflow-x-auto pb-2 md:pb-0">
|
||||||
<Button
|
<Button
|
||||||
variant={statusFilter === "all" ? "default" : "outline"}
|
variant={statusFilter === "all" ? "default" : "outline"}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import {
|
import {
|
||||||
Activity, AlertTriangle, Clock, Code,
|
Activity, AlertTriangle, Clock, Code,
|
||||||
FileText, GitBranch, Shield, TrendingUp, Zap,
|
FileText, GitBranch, Shield, TrendingUp, Zap,
|
||||||
BarChart3, Target, ArrowUpRight, Calendar, Terminal
|
BarChart3, Target, ArrowUpRight, Calendar
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { api, dbMode, isDemoMode } from "@/shared/config/database";
|
import { api, dbMode, isDemoMode } from "@/shared/config/database";
|
||||||
import type { Project, AuditTask, ProjectStats } from "@/shared/types";
|
import type { Project, AuditTask, ProjectStats } from "@/shared/types";
|
||||||
|
|
@ -169,34 +169,6 @@ export default function Dashboard() {
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* Header Section */}
|
|
||||||
<div className="relative z-10 flex flex-col md:flex-row md:items-center justify-between gap-6 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-4xl font-display font-bold uppercase tracking-tighter mb-2">
|
|
||||||
系统<span className="text-primary">_仪表盘</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-500 font-mono text-sm flex items-center gap-2">
|
|
||||||
<Terminal className="w-4 h-4" />
|
|
||||||
// 监控状态 // 代码质量概览
|
|
||||||
{isDemoMode && <Badge variant="outline" className="ml-2 border-black bg-yellow-100 text-yellow-800 rounded-none">演示模式</Badge>}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<Link to="/instant-analysis">
|
|
||||||
<Button className="retro-btn h-12">
|
|
||||||
<Zap className="w-4 h-4 mr-2" />
|
|
||||||
即时分析
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link to="/projects">
|
|
||||||
<Button variant="outline" className="retro-btn bg-white text-black hover:bg-gray-100 h-12">
|
|
||||||
<GitBranch className="w-4 h-4 mr-2" />
|
|
||||||
新建项目
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 数据库模式提示 */}
|
{/* 数据库模式提示 */}
|
||||||
{isDemoMode && (
|
{isDemoMode && (
|
||||||
<div className="relative z-10 bg-yellow-50 border-2 border-black p-4 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] flex items-start gap-3">
|
<div className="relative z-10 bg-yellow-50 border-2 border-black p-4 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] flex items-start gap-3">
|
||||||
|
|
@ -337,15 +309,15 @@ export default function Dashboard() {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{issueTypeData.length > 0 ? (
|
{issueTypeData.length > 0 ? (
|
||||||
<ResponsiveContainer width="100%" height={250}>
|
<ResponsiveContainer width="100%" height={280}>
|
||||||
<PieChart>
|
<PieChart>
|
||||||
<Pie
|
<Pie
|
||||||
data={issueTypeData}
|
data={issueTypeData}
|
||||||
cx="50%"
|
cx="50%"
|
||||||
cy="50%"
|
cy="45%"
|
||||||
labelLine={true}
|
labelLine={true}
|
||||||
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
||||||
outerRadius={80}
|
outerRadius={70}
|
||||||
fill="#8884d8"
|
fill="#8884d8"
|
||||||
dataKey="value"
|
dataKey="value"
|
||||||
stroke="#000"
|
stroke="#000"
|
||||||
|
|
@ -367,7 +339,7 @@ export default function Dashboard() {
|
||||||
</PieChart>
|
</PieChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center h-[250px] text-gray-400 border-2 border-dashed border-gray-300">
|
<div className="flex items-center justify-center h-[280px] text-gray-400 border-2 border-dashed border-gray-300">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<BarChart3 className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
<BarChart3 className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
||||||
<p className="text-sm font-mono uppercase">暂无问题分布数据</p>
|
<p className="text-sm font-mono uppercase">暂无问题分布数据</p>
|
||||||
|
|
|
||||||
|
|
@ -535,16 +535,10 @@ class UserManager {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* 页面标题 */}
|
|
||||||
<div className="relative z-10 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter">即时代码分析</h1>
|
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">快速分析代码片段,发现潜在问题并获得修复建议</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 代码输入区域 */}
|
{/* 代码输入区域 */}
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-0">
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50 flex items-center justify-between">
|
<div className="p-4 border-b-2 border-black bg-gray-50 flex items-center justify-between">
|
||||||
|
|
|
||||||
|
|
@ -313,29 +313,24 @@ export default function ProjectDetail() {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* 页面标题 */}
|
{/* 顶部操作栏 */}
|
||||||
<div className="relative z-10 flex flex-col md:flex-row md:items-center justify-between gap-4 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
<div className="relative z-10 flex items-center justify-between">
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<Link to="/projects">
|
<Link to="/projects">
|
||||||
<Button variant="outline" size="sm" className="retro-btn bg-white text-black hover:bg-gray-100 h-10 w-10 p-0 flex items-center justify-center">
|
<Button variant="outline" size="sm" className="retro-btn bg-white text-black hover:bg-gray-100 h-10 w-10 p-0 flex items-center justify-center">
|
||||||
<ArrowLeft className="w-5 h-5" />
|
<ArrowLeft className="w-5 h-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<div>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter">{project.name}</h1>
|
<h1 className="text-2xl font-display font-bold text-black uppercase tracking-tighter">{project.name}</h1>
|
||||||
<Badge variant="outline" className={`rounded-none border-2 border-black ${project.is_active ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"}`}>
|
<Badge variant="outline" className={`rounded-none border-2 border-black ${project.is_active ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"}`}>
|
||||||
{project.is_active ? '活跃' : '暂停'}
|
{project.is_active ? '活跃' : '暂停'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">
|
|
||||||
{project.description || '暂无项目描述'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
|
|
@ -350,7 +345,7 @@ export default function ProjectDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ... (stats cards remain same) ... */}
|
{/* 统计卡片 */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
{/* ... (stats cards content) ... */}
|
{/* ... (stats cards content) ... */}
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-4 hover:translate-x-[-2px] hover:translate-y-[-2px] hover:shadow-[6px_6px_0px_0px_rgba(0,0,0,1)] transition-all">
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-4 hover:translate-x-[-2px] hover:translate-y-[-2px] hover:shadow-[6px_6px_0px_0px_rgba(0,0,0,1)] transition-all">
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import {
|
||||||
GitBranch,
|
GitBranch,
|
||||||
Calendar,
|
Calendar,
|
||||||
Users,
|
Users,
|
||||||
Settings,
|
|
||||||
Code,
|
Code,
|
||||||
Shield,
|
Shield,
|
||||||
Activity,
|
Activity,
|
||||||
|
|
@ -26,7 +25,9 @@ import {
|
||||||
Trash2,
|
Trash2,
|
||||||
Edit,
|
Edit,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Terminal
|
Terminal,
|
||||||
|
Github,
|
||||||
|
Folder
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { api } from "@/shared/config/database";
|
import { api } from "@/shared/config/database";
|
||||||
import { validateZipFile } from "@/features/projects/services";
|
import { validateZipFile } from "@/features/projects/services";
|
||||||
|
|
@ -262,9 +263,9 @@ export default function Projects() {
|
||||||
|
|
||||||
const getRepositoryIcon = (type?: string) => {
|
const getRepositoryIcon = (type?: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'github': return '🐙';
|
case 'github': return <Github className="w-5 h-5" />;
|
||||||
case 'gitlab': return '🦊';
|
case 'gitlab': return <GitBranch className="w-5 h-5 text-orange-500" />;
|
||||||
default: return '📁';
|
default: return <Folder className="w-5 h-5 text-gray-600" />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -375,21 +376,13 @@ export default function Projects() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* Header Section */}
|
{/* 创建项目对话框 */}
|
||||||
<div className="relative z-10 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter">
|
|
||||||
项目<span className="text-primary">_管理</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">管理代码仓库和配置审计任务</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
|
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild className="hidden">
|
||||||
<Button className="terminal-btn-primary h-12 text-lg">
|
<Button className="terminal-btn-primary h-12 text-lg">
|
||||||
<Plus className="w-5 h-5 mr-2" />
|
<Plus className="w-5 h-5 mr-2" />
|
||||||
初始化项目
|
初始化项目
|
||||||
|
|
@ -681,7 +674,6 @@ export default function Projects() {
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Stats Section */}
|
{/* Stats Section */}
|
||||||
{projects.length > 0 && (
|
{projects.length > 0 && (
|
||||||
|
|
@ -739,17 +731,17 @@ export default function Projects() {
|
||||||
{/* Search and Filter */}
|
{/* Search and Filter */}
|
||||||
<div className="retro-card p-4 flex items-center gap-4 bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] relative z-10">
|
<div className="retro-card p-4 flex items-center gap-4 bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] relative z-10">
|
||||||
<div className="flex-1 relative">
|
<div className="flex-1 relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-black w-4 h-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 w-4 h-4 z-10" />
|
||||||
<Input
|
<Input
|
||||||
placeholder="搜索项目..."
|
placeholder="搜索项目..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="terminal-input pl-10 w-full"
|
className="bg-background border border-input pr-3 py-2 rounded-sm font-mono text-sm w-full pl-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" className="terminal-btn-primary bg-white text-black hover:bg-gray-100">
|
<Button className="terminal-btn-primary h-10" onClick={() => setShowCreateDialog(true)}>
|
||||||
<Settings className="w-4 h-4 mr-2" />
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
筛选选项
|
新建项目
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -760,7 +752,7 @@ export default function Projects() {
|
||||||
<div key={project.id} className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:shadow-lg transition-all group flex flex-col h-full">
|
<div key={project.id} className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:shadow-lg transition-all group flex flex-col h-full">
|
||||||
<div className="p-4 border-b-2 border-black bg-gray-50 flex justify-between items-start">
|
<div className="p-4 border-b-2 border-black bg-gray-50 flex justify-between items-start">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="w-10 h-10 border border-border bg-white flex items-center justify-center text-2xl shadow-sm">
|
<div className="w-10 h-10 border-2 border-black bg-white flex items-center justify-center text-xl shadow-[2px_2px_0px_0px_rgba(0,0,0,1)]">
|
||||||
{getRepositoryIcon(project.repository_type)}
|
{getRepositoryIcon(project.repository_type)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -123,21 +123,10 @@ export default function RecycleBin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-6 pt-0 pb-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
<div className="flex flex-col gap-6 px-6 py-4 bg-background min-h-screen font-mono relative overflow-hidden">
|
||||||
{/* Decorative Background */}
|
{/* Decorative Background */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" />
|
||||||
|
|
||||||
{/* 页面标题 */}
|
|
||||||
<div className="relative z-10 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter flex items-center gap-2">
|
|
||||||
<Trash2 className="w-8 h-8 text-black" />
|
|
||||||
回收站
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">管理已删除的项目,可以恢复或永久删除</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 搜索 */}
|
{/* 搜索 */}
|
||||||
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-4">
|
<div className="retro-card bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-4">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
|
|
|
||||||
|
|
@ -448,24 +448,18 @@ export default function TaskDetail() {
|
||||||
const progressPercentage = calculateTaskProgress(task.scanned_files, task.total_files);
|
const progressPercentage = calculateTaskProgress(task.scanned_files, task.total_files);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 animate-fade-in font-mono">
|
<div className="flex flex-col gap-6 px-6 py-4 animate-fade-in font-mono">
|
||||||
{/* 页面标题 */}
|
{/* 顶部操作栏 */}
|
||||||
<div className="flex items-center justify-between border-b-4 border-black pb-6 bg-white/50 backdrop-blur-sm p-4 retro-border">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<Link to="/audit-tasks">
|
<Link to="/audit-tasks">
|
||||||
<Button variant="outline" size="sm" className="retro-btn bg-white text-black hover:bg-gray-100 h-10">
|
<Button variant="outline" size="sm" className="retro-btn bg-white text-black hover:bg-gray-100 h-10">
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||||
返回任务列表
|
返回任务列表
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-display font-bold text-black uppercase tracking-tighter">任务详情</h1>
|
|
||||||
<p className="text-gray-600 mt-1 font-mono border-l-2 border-primary pl-2">{task.project?.name || '未知项目'} - 审计任务</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<Badge className={`rounded - none border - 2 border - black font - bold uppercase px - 3 py - 1 text - sm ${getStatusColor(task.status)} `}>
|
<Badge className={`rounded-none border-2 border-black font-bold uppercase px-3 py-1 text-sm ${getStatusColor(task.status)}`}>
|
||||||
{getStatusIcon(task.status)}
|
{getStatusIcon(task.status)}
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
{task.status === 'completed' ? '已完成' :
|
{task.status === 'completed' ? '已完成' :
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue