932 lines
33 KiB
TypeScript
932 lines
33 KiB
TypeScript
import React, { useState } from "react";
|
|
import {
|
|
FileText,
|
|
Search,
|
|
Filter,
|
|
Download,
|
|
Target,
|
|
Clock,
|
|
CheckCircle,
|
|
AlertTriangle,
|
|
Plus,
|
|
TrendingUp,
|
|
TrendingDown,
|
|
ChevronRight,
|
|
User,
|
|
Eye,
|
|
} from "lucide-react";
|
|
import { useTheme } from "@/contexts/ThemeContext";
|
|
import { PageType } from "@/components/Sidebar";
|
|
|
|
interface TaskDashboardProps {
|
|
onNavigate: (page: PageType) => void;
|
|
}
|
|
|
|
const TaskDashboard: React.FC<TaskDashboardProps> = ({ onNavigate }) => {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === "dark";
|
|
|
|
// 主题颜色变量
|
|
const cardBg = isDark ? "bg-slate-800/30" : "bg-white";
|
|
const cardBorder = isDark ? "border-blue-800/30" : "border-red-100";
|
|
const textPrimary = isDark ? "text-white" : "text-gray-900";
|
|
const textSecondary = isDark ? "text-slate-400" : "text-gray-600";
|
|
const textMuted = isDark ? "text-slate-500" : "text-gray-400";
|
|
const inputBg = isDark ? "bg-slate-900/50" : "bg-white";
|
|
|
|
const [timeRange, setTimeRange] = useState<string>("month");
|
|
const [categoryFilter, setCategoryFilter] = useState<string>("all");
|
|
const [statusFilter, setStatusFilter] = useState<string>("all");
|
|
|
|
// 统计数据
|
|
const stats = {
|
|
total: 1286,
|
|
inProgress: 326,
|
|
completed: 842,
|
|
delayed: 57,
|
|
weeklyNew: 93,
|
|
priorityPending: 41,
|
|
};
|
|
|
|
// 二级任务分类数据
|
|
const categoryData = [
|
|
{ name: "XK任务", count: 320, percentage: 25 },
|
|
{ name: "规划任务", count: 240, percentage: 19 },
|
|
{ name: "MNTY任务", count: 280, percentage: 22 },
|
|
{ name: "效果PG任务", count: 300, percentage: 23 },
|
|
];
|
|
|
|
// 三级任务数据
|
|
const thirdLevelData = [
|
|
{ name: "任务JC", count: 120 },
|
|
{ name: "任务调度", count: 200 },
|
|
{ name: "任务FX", count: 140 },
|
|
{ name: "行动CE", count: 100 },
|
|
{ name: "XDTY任务", count: 70 },
|
|
{ name: "方案TY任务", count: 120 },
|
|
{ name: "XXTY任务", count: 90 },
|
|
{ name: "任务PG", count: 160 },
|
|
{ name: "行动PG", count: 80 },
|
|
{ name: "信息PG", count: 130 },
|
|
{ name: "账号PG", count: 60 },
|
|
];
|
|
|
|
// 预警任务列表
|
|
const warningTasks = [
|
|
{
|
|
id: "001",
|
|
name: "五四宣传执行任务",
|
|
category: "规划任务 / 行动CE",
|
|
status: "已延期",
|
|
dueDate: "04-20",
|
|
riskLevel: "high",
|
|
},
|
|
{
|
|
id: "002",
|
|
name: "青年传播调度任务",
|
|
category: "XK任务 / 任务调度",
|
|
status: "待处理",
|
|
dueDate: "04-18",
|
|
riskLevel: "medium",
|
|
},
|
|
{
|
|
id: "003",
|
|
name: "效果复盘信息任务",
|
|
category: "效果PG / 信息PG",
|
|
status: "阻塞中",
|
|
dueDate: "04-17",
|
|
riskLevel: "high",
|
|
},
|
|
];
|
|
|
|
// 我的待办
|
|
const myTodos = {
|
|
pending: 8,
|
|
pendingConfirm: 3,
|
|
pendingReview: 2,
|
|
};
|
|
|
|
// Top 5 排行
|
|
const top5ByVolume = [
|
|
{ name: "任务PG", count: 160 },
|
|
{ name: "信息PG", count: 130 },
|
|
{ name: "任务FX", count: 120 },
|
|
{ name: "任务调度", count: 100 },
|
|
{ name: "方案TY任务", count: 90 },
|
|
];
|
|
|
|
const top5ByDelay = [
|
|
{ name: "行动CE", count: 15 },
|
|
{ name: "任务调度", count: 12 },
|
|
{ name: "信息PG", count: 10 },
|
|
{ name: "XDTY任务", count: 8 },
|
|
{ name: "账号PG", count: 6 },
|
|
];
|
|
|
|
// 完成率对比
|
|
const completionRates = [
|
|
{ name: "XK任务", rate: 72 },
|
|
{ name: "规划任务", rate: 61 },
|
|
{ name: "MNTY任务", rate: 68 },
|
|
{ name: "效果PG任务", rate: 54 },
|
|
];
|
|
|
|
return (
|
|
<div className="h-full flex flex-col">
|
|
{/* 顶部筛选区 */}
|
|
<div
|
|
className={`rounded-xl p-4 flex-shrink-0 mb-4 ${cardBg} border ${cardBorder}`}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className={`text-xl font-bold ${textPrimary}`}>任务统计总览</h1>
|
|
<p className={`text-sm ${textMuted}`}>
|
|
面向分级分类任务体系的全局监控看板
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<select
|
|
value={timeRange}
|
|
onChange={(e) => setTimeRange(e.target.value)}
|
|
className={`px-4 py-2 border rounded-lg text-sm cursor-pointer ${inputBg} ${cardBorder} ${textSecondary}`}
|
|
>
|
|
<option value="day">今日</option>
|
|
<option value="week">本周</option>
|
|
<option value="month">本月</option>
|
|
<option value="quarter">本季度</option>
|
|
<option value="custom">自定义</option>
|
|
</select>
|
|
<select
|
|
value={categoryFilter}
|
|
onChange={(e) => setCategoryFilter(e.target.value)}
|
|
className={`px-4 py-2 border rounded-lg text-sm cursor-pointer ${inputBg} ${cardBorder} ${textSecondary}`}
|
|
>
|
|
<option value="all">全部分类</option>
|
|
<option value="xk">XK任务</option>
|
|
<option value="plan">规划任务</option>
|
|
<option value="mnty">MNTY任务</option>
|
|
<option value="effect">效果PG任务</option>
|
|
</select>
|
|
<select
|
|
value={statusFilter}
|
|
onChange={(e) => setStatusFilter(e.target.value)}
|
|
className={`px-4 py-2 border rounded-lg text-sm cursor-pointer ${inputBg} ${cardBorder} ${textSecondary}`}
|
|
>
|
|
<option value="all">全部状态</option>
|
|
<option value="pending">未开始</option>
|
|
<option value="in-progress">进行中</option>
|
|
<option value="completed">已完成</option>
|
|
<option value="delayed">延期</option>
|
|
</select>
|
|
<button
|
|
className={`flex items-center gap-2 px-4 py-2 border rounded-lg text-sm transition-colors ${
|
|
isDark
|
|
? "border-blue-800/30 hover:bg-slate-700/50 text-slate-300"
|
|
: "border-red-200 hover:bg-red-50 text-red-600"
|
|
}`}
|
|
>
|
|
<Download className="w-4 h-4" />
|
|
导出统计
|
|
</button>
|
|
<button
|
|
onClick={() => onNavigate("plan-task-fx")}
|
|
className={`flex items-center gap-2 px-4 py-2 text-white rounded-lg text-sm font-medium transition-colors ${
|
|
isDark
|
|
? "bg-blue-600 hover:bg-blue-500"
|
|
: "bg-red-600 hover:bg-red-500"
|
|
}`}
|
|
>
|
|
查看全部任务
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 第一行:核心指标卡 */}
|
|
<div className="grid grid-cols-6 gap-4 mb-4 flex-shrink-0">
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<div
|
|
className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
|
isDark ? "bg-blue-500/20" : "bg-red-100"
|
|
}`}
|
|
>
|
|
<Target
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-blue-400" : "text-red-500"
|
|
}`}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm ${textSecondary}`}>任务总数</span>
|
|
</div>
|
|
<p className={`text-3xl font-bold ${textPrimary}`}>1,286</p>
|
|
<div className="flex items-center gap-1 mt-1">
|
|
<TrendingUp
|
|
className={`w-3 h-3 ${
|
|
isDark ? "text-green-400" : "text-green-500"
|
|
}`}
|
|
/>
|
|
<span
|
|
className={`text-xs ${
|
|
isDark ? "text-green-400" : "text-green-500"
|
|
}`}
|
|
>
|
|
较上周 +8%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<div
|
|
className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
|
isDark ? "bg-cyan-500/20" : "bg-red-100"
|
|
}`}
|
|
>
|
|
<Clock
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-cyan-400" : "text-red-500"
|
|
}`}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm ${textSecondary}`}>进行中</span>
|
|
</div>
|
|
<p className={`text-3xl font-bold ${textPrimary}`}>326</p>
|
|
<div className="flex items-center gap-1 mt-1">
|
|
<span className={`text-xs ${textMuted}`}>占比 25.4%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<div
|
|
className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
|
isDark ? "bg-green-500/20" : "bg-green-100"
|
|
}`}
|
|
>
|
|
<CheckCircle
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-green-400" : "text-green-500"
|
|
}`}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm ${textSecondary}`}>已完成</span>
|
|
</div>
|
|
<p className={`text-3xl font-bold ${textPrimary}`}>842</p>
|
|
<div className="flex items-center gap-1 mt-1">
|
|
<span
|
|
className={`text-xs ${
|
|
isDark ? "text-green-400" : "text-green-500"
|
|
}`}
|
|
>
|
|
完成率 65.5%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<div
|
|
className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
|
isDark ? "bg-red-500/20" : "bg-orange-100"
|
|
}`}
|
|
>
|
|
<AlertTriangle
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-red-400" : "text-orange-500"
|
|
}`}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm ${textSecondary}`}>延期任务</span>
|
|
</div>
|
|
<p
|
|
className={`text-3xl font-bold ${
|
|
isDark ? "text-red-400" : "text-orange-600"
|
|
}`}
|
|
>
|
|
57
|
|
</p>
|
|
<div className="flex items-center gap-1 mt-1">
|
|
<span
|
|
className={`text-xs ${
|
|
isDark ? "text-red-400" : "text-orange-500"
|
|
}`}
|
|
>
|
|
需重点关注
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<div
|
|
className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
|
isDark ? "bg-purple-500/20" : "bg-red-100"
|
|
}`}
|
|
>
|
|
<Plus
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-purple-400" : "text-red-500"
|
|
}`}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm ${textSecondary}`}>本周新增</span>
|
|
</div>
|
|
<p className={`text-3xl font-bold ${textPrimary}`}>93</p>
|
|
<div className="flex items-center gap-1 mt-1">
|
|
<TrendingUp
|
|
className={`w-3 h-3 ${
|
|
isDark ? "text-green-400" : "text-green-500"
|
|
}`}
|
|
/>
|
|
<span
|
|
className={`text-xs ${
|
|
isDark ? "text-green-400" : "text-green-500"
|
|
}`}
|
|
>
|
|
较上周 +12
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<div
|
|
className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
|
isDark ? "bg-yellow-500/20" : "bg-yellow-100"
|
|
}`}
|
|
>
|
|
<AlertTriangle
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-yellow-400" : "text-yellow-600"
|
|
}`}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm ${textSecondary}`}>重点待处理</span>
|
|
</div>
|
|
<p className={`text-3xl font-bold ${textPrimary}`}>41</p>
|
|
<div className="flex items-center gap-1 mt-1">
|
|
<span className={`text-xs ${textMuted}`}>高优先级 12</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 第二行:分类分布区 */}
|
|
<div className="grid grid-cols-3 gap-4 mb-4 flex-shrink-0">
|
|
{/* 左侧:二级任务分类分布 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<h3 className={`font-semibold mb-4 ${textPrimary}`}>
|
|
二级任务分类分布
|
|
</h3>
|
|
<div className="space-y-3">
|
|
{categoryData.map((item, index) => (
|
|
<div key={index} className="space-y-1">
|
|
<div className="flex items-center justify-between">
|
|
<span className={`text-sm ${textSecondary}`}>
|
|
{item.name}
|
|
</span>
|
|
<span className={`text-sm font-medium ${textPrimary}`}>
|
|
{item.count}
|
|
</span>
|
|
</div>
|
|
<div
|
|
className={`h-2 rounded-full overflow-hidden ${
|
|
isDark ? "bg-slate-700" : "bg-gray-200"
|
|
}`}
|
|
>
|
|
<div
|
|
className={`h-full rounded-full transition-all ${
|
|
index === 0
|
|
? isDark
|
|
? "bg-blue-500"
|
|
: "bg-red-500"
|
|
: index === 1
|
|
? isDark
|
|
? "bg-cyan-500"
|
|
: "bg-red-400"
|
|
: index === 2
|
|
? isDark
|
|
? "bg-purple-500"
|
|
: "bg-red-300"
|
|
: isDark
|
|
? "bg-green-500"
|
|
: "bg-red-200"
|
|
}`}
|
|
style={{ width: `${item.percentage}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 中间:三级任务分布(点击下钻) */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className={`font-semibold ${textPrimary}`}>三级任务分布</h3>
|
|
<select
|
|
className={`text-xs px-2 py-1 rounded ${inputBg} ${cardBorder} ${textMuted}`}
|
|
>
|
|
<option value="plan">规划任务</option>
|
|
<option value="xk">XK任务</option>
|
|
<option value="mnty">MNTY任务</option>
|
|
<option value="effect">效果PG任务</option>
|
|
</select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
{thirdLevelData.slice(0, 5).map((item, index) => (
|
|
<div key={index} className="flex items-center gap-2">
|
|
<span className={`text-xs w-16 ${textMuted}`}>{item.name}</span>
|
|
<div
|
|
className={`flex-1 h-4 rounded overflow-hidden ${
|
|
isDark ? "bg-slate-700" : "bg-gray-200"
|
|
}`}
|
|
>
|
|
<div
|
|
className={`h-full rounded ${
|
|
index === 0
|
|
? isDark
|
|
? "bg-blue-500"
|
|
: "bg-red-500"
|
|
: index === 1
|
|
? isDark
|
|
? "bg-cyan-500"
|
|
: "bg-red-400"
|
|
: index === 2
|
|
? isDark
|
|
? "bg-purple-500"
|
|
: "bg-red-300"
|
|
: index === 3
|
|
? isDark
|
|
? "bg-green-500"
|
|
: "bg-red-200"
|
|
: isDark
|
|
? "bg-yellow-500"
|
|
: "bg-red-100"
|
|
}`}
|
|
style={{ width: `${(item.count / 200) * 100}%` }}
|
|
/>
|
|
</div>
|
|
<span
|
|
className={`text-xs font-medium w-8 text-right ${textPrimary}`}
|
|
>
|
|
{item.count}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 右侧:三级任务数量Top5 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<h3 className={`font-semibold mb-4 ${textPrimary}`}>
|
|
三级任务数量 Top 5
|
|
</h3>
|
|
<div className="space-y-2">
|
|
{top5ByVolume.map((item, index) => (
|
|
<div key={index} className="flex items-center gap-2">
|
|
<span
|
|
className={`w-5 h-5 rounded-full flex items-center justify-center text-xs font-bold ${
|
|
index === 0
|
|
? isDark
|
|
? "bg-yellow-500/20 text-yellow-400"
|
|
: "bg-yellow-100 text-yellow-600"
|
|
: index === 1
|
|
? isDark
|
|
? "bg-slate-500/20 text-slate-400"
|
|
: "bg-gray-200 text-gray-600"
|
|
: index === 2
|
|
? isDark
|
|
? "bg-orange-500/20 text-orange-400"
|
|
: "bg-orange-100 text-orange-600"
|
|
: isDark
|
|
? "bg-slate-600/20 text-slate-500"
|
|
: "bg-gray-100 text-gray-500"
|
|
}`}
|
|
>
|
|
{index + 1}
|
|
</span>
|
|
<span className={`flex-1 text-sm ${textSecondary}`}>
|
|
{item.name}
|
|
</span>
|
|
<span className={`text-sm font-medium ${textPrimary}`}>
|
|
{item.count}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 第三行:状态与趋势分析区 */}
|
|
<div className="grid grid-cols-3 gap-4 mb-4 flex-shrink-0">
|
|
{/* 左侧:任务状态分布 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<h3 className={`font-semibold mb-4 ${textPrimary}`}>任务状态分布</h3>
|
|
<div className="space-y-3">
|
|
{categoryData.map((item, index) => (
|
|
<div key={index}>
|
|
<div className="flex items-center justify-between mb-1">
|
|
<span className={`text-xs ${textMuted}`}>{item.name}</span>
|
|
</div>
|
|
<div className="flex gap-1">
|
|
<div
|
|
className={`h-3 flex-1 rounded-l ${
|
|
isDark ? "bg-slate-700" : "bg-gray-200"
|
|
}`}
|
|
style={{ width: "30%" }}
|
|
/>
|
|
<div
|
|
className={`h-3 flex-1 ${
|
|
isDark ? "bg-blue-500" : "bg-red-400"
|
|
}`}
|
|
style={{ width: "25%" }}
|
|
/>
|
|
<div
|
|
className={`h-3 flex-1 ${
|
|
isDark ? "bg-green-500" : "bg-green-400"
|
|
}`}
|
|
style={{ width: "35%" }}
|
|
/>
|
|
<div
|
|
className={`h-3 flex-1 rounded-r ${
|
|
isDark ? "bg-red-500" : "bg-orange-400"
|
|
}`}
|
|
style={{ width: "10%" }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex items-center justify-center gap-4 mt-4 pt-4 border-t ${cardBorder}">
|
|
<div className="flex items-center gap-1">
|
|
<div
|
|
className={`w-2 h-2 rounded ${
|
|
isDark ? "bg-slate-600" : "bg-gray-400"
|
|
}`}
|
|
/>
|
|
<span className={`text-xs ${textMuted}`}>未开始</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<div
|
|
className={`w-2 h-2 rounded ${
|
|
isDark ? "bg-blue-500" : "bg-red-400"
|
|
}`}
|
|
/>
|
|
<span className={`text-xs ${textMuted}`}>进行中</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<div
|
|
className={`w-2 h-2 rounded ${
|
|
isDark ? "bg-green-500" : "bg-green-400"
|
|
}`}
|
|
/>
|
|
<span className={`text-xs ${textMuted}`}>已完成</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<div
|
|
className={`w-2 h-2 rounded ${
|
|
isDark ? "bg-red-500" : "bg-orange-400"
|
|
}`}
|
|
/>
|
|
<span className={`text-xs ${textMuted}`}>延期</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 中间:任务趋势图(简化版) */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className={`font-semibold ${textPrimary}`}>
|
|
任务趋势(近30天)
|
|
</h3>
|
|
<div className="flex gap-2">
|
|
<button
|
|
className={`px-2 py-1 rounded text-xs ${
|
|
timeRange === "7d"
|
|
? isDark
|
|
? "bg-blue-500/20 text-blue-400"
|
|
: "bg-red-100 text-red-600"
|
|
: textMuted
|
|
}`}
|
|
>
|
|
近7天
|
|
</button>
|
|
<button
|
|
className={`px-2 py-1 rounded text-xs ${
|
|
timeRange === "30d"
|
|
? isDark
|
|
? "bg-blue-500/20 text-blue-400"
|
|
: "bg-red-100 text-red-600"
|
|
: textMuted
|
|
}`}
|
|
>
|
|
近30天
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="h-32 flex items-end justify-between gap-2">
|
|
{[65, 72, 68, 75, 80, 78, 85, 82, 88, 92, 87, 95].map(
|
|
(value, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex-1 flex flex-col items-center gap-1"
|
|
>
|
|
<div
|
|
className={`w-full rounded-t transition-all ${
|
|
isDark ? "bg-blue-500/50" : "bg-red-300"
|
|
}`}
|
|
style={{ height: `${value}%` }}
|
|
/>
|
|
</div>
|
|
)
|
|
)}
|
|
</div>
|
|
<div className="flex items-center justify-center gap-4 mt-2">
|
|
<div className="flex items-center gap-1">
|
|
<div
|
|
className={`w-3 h-1 rounded ${
|
|
isDark ? "bg-blue-500" : "bg-red-500"
|
|
}`}
|
|
/>
|
|
<span className={`text-xs ${textMuted}`}>新增任务</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<div
|
|
className={`w-3 h-1 rounded ${
|
|
isDark ? "bg-green-500" : "bg-green-500"
|
|
}`}
|
|
/>
|
|
<span className={`text-xs ${textMuted}`}>完成任务</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 右侧:分类完成率对比 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<h3 className={`font-semibold mb-4 ${textPrimary}`}>
|
|
分类完成率对比
|
|
</h3>
|
|
<div className="space-y-4">
|
|
{completionRates.map((item, index) => (
|
|
<div key={index}>
|
|
<div className="flex items-center justify-between mb-1">
|
|
<span className={`text-sm ${textSecondary}`}>
|
|
{item.name}
|
|
</span>
|
|
<span
|
|
className={`text-sm font-medium ${
|
|
isDark ? "text-blue-400" : "text-red-600"
|
|
}`}
|
|
>
|
|
{item.rate}%
|
|
</span>
|
|
</div>
|
|
<div
|
|
className={`h-2 rounded-full overflow-hidden ${
|
|
isDark ? "bg-slate-700" : "bg-gray-200"
|
|
}`}
|
|
>
|
|
<div
|
|
className={`h-full rounded-full transition-all ${
|
|
isDark ? "bg-blue-500" : "bg-red-500"
|
|
}`}
|
|
style={{ width: `${item.rate}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 第四行:预警与行动区 */}
|
|
<div className="grid grid-cols-3 gap-4 flex-shrink-0">
|
|
{/* 左侧:预警任务列表 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className={`font-semibold ${textPrimary}`}>预警任务</h3>
|
|
<button
|
|
className={`text-xs ${
|
|
isDark
|
|
? "text-blue-400 hover:text-blue-300"
|
|
: "text-red-500 hover:text-red-600"
|
|
}`}
|
|
>
|
|
查看全部
|
|
</button>
|
|
</div>
|
|
<div className="space-y-3">
|
|
{warningTasks.map((task) => (
|
|
<div
|
|
key={task.id}
|
|
className={`p-3 rounded-lg border cursor-pointer transition-all hover:shadow-md ${
|
|
task.riskLevel === "high"
|
|
? isDark
|
|
? "bg-red-500/10 border-red-500/30"
|
|
: "bg-orange-50 border-orange-200"
|
|
: isDark
|
|
? "bg-yellow-500/10 border-yellow-500/30"
|
|
: "bg-yellow-50 border-yellow-200"
|
|
}`}
|
|
>
|
|
<div className="flex items-start justify-between mb-1">
|
|
<div className="flex items-center gap-2">
|
|
<AlertTriangle
|
|
className={`w-4 h-4 ${
|
|
task.riskLevel === "high"
|
|
? isDark
|
|
? "text-red-400"
|
|
: "text-orange-500"
|
|
: isDark
|
|
? "text-yellow-400"
|
|
: "text-yellow-500"
|
|
}`}
|
|
/>
|
|
<span className={`text-sm font-medium ${textPrimary}`}>
|
|
{task.name}
|
|
</span>
|
|
</div>
|
|
<span className={`text-xs ${textMuted}`}>
|
|
截止:{task.dueDate}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<span
|
|
className={`text-xs px-2 py-0.5 rounded ${
|
|
isDark
|
|
? "bg-slate-700/50 text-slate-400"
|
|
: "bg-gray-100 text-gray-500"
|
|
}`}
|
|
>
|
|
{task.category}
|
|
</span>
|
|
<span
|
|
className={`text-xs ${
|
|
task.status === "已延期"
|
|
? isDark
|
|
? "text-red-400"
|
|
: "text-orange-500"
|
|
: textMuted
|
|
}`}
|
|
>
|
|
{task.status}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 中间:重点分类排行 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<h3 className={`font-semibold mb-4 ${textPrimary}`}>重点排行</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<TrendingUp
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-blue-400" : "text-red-500"
|
|
}`}
|
|
/>
|
|
<span className={`text-sm font-medium ${textSecondary}`}>
|
|
任务量 Top 5
|
|
</span>
|
|
</div>
|
|
<div className="space-y-1">
|
|
{top5ByVolume.map((item, index) => (
|
|
<div key={index} className="flex items-center gap-2">
|
|
<span className={`w-4 text-xs ${textMuted}`}>
|
|
{index + 1}.
|
|
</span>
|
|
<span className={`flex-1 text-xs ${textSecondary}`}>
|
|
{item.name}
|
|
</span>
|
|
<span className={`text-xs font-medium ${textPrimary}`}>
|
|
{item.count}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className={`pt-3 border-t ${cardBorder}`}>
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<AlertTriangle
|
|
className={`w-4 h-4 ${
|
|
isDark ? "text-red-400" : "text-orange-500"
|
|
}`}
|
|
/>
|
|
<span className={`text-sm font-medium ${textSecondary}`}>
|
|
延期量 Top 5
|
|
</span>
|
|
</div>
|
|
<div className="space-y-1">
|
|
{top5ByDelay.map((item, index) => (
|
|
<div key={index} className="flex items-center gap-2">
|
|
<span className={`w-4 text-xs ${textMuted}`}>
|
|
{index + 1}.
|
|
</span>
|
|
<span className={`flex-1 text-xs ${textSecondary}`}>
|
|
{item.name}
|
|
</span>
|
|
<span
|
|
className={`text-xs font-medium ${
|
|
isDark ? "text-red-400" : "text-orange-500"
|
|
}`}
|
|
>
|
|
{item.count}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 右侧:我的待办/快捷入口 */}
|
|
<div className={`rounded-xl p-4 border ${cardBg} ${cardBorder}`}>
|
|
<h3 className={`font-semibold mb-4 ${textPrimary}`}>我的待办</h3>
|
|
<div className="grid grid-cols-3 gap-3 mb-4">
|
|
<div
|
|
className={`p-3 rounded-lg text-center ${
|
|
isDark ? "bg-slate-700/50" : "bg-red-50"
|
|
}`}
|
|
>
|
|
<p className={`text-2xl font-bold ${textPrimary}`}>
|
|
{myTodos.pending}
|
|
</p>
|
|
<p className={`text-xs ${textMuted}`}>待处理</p>
|
|
</div>
|
|
<div
|
|
className={`p-3 rounded-lg text-center ${
|
|
isDark ? "bg-slate-700/50" : "bg-yellow-50"
|
|
}`}
|
|
>
|
|
<p className={`text-2xl font-bold ${textPrimary}`}>
|
|
{myTodos.pendingConfirm}
|
|
</p>
|
|
<p className={`text-xs ${textMuted}`}>待确认</p>
|
|
</div>
|
|
<div
|
|
className={`p-3 rounded-lg text-center ${
|
|
isDark ? "bg-slate-700/50" : "bg-blue-50"
|
|
}`}
|
|
>
|
|
<p className={`text-2xl font-bold ${textPrimary}`}>
|
|
{myTodos.pendingReview}
|
|
</p>
|
|
<p className={`text-xs ${textMuted}`}>待复核</p>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<button
|
|
onClick={() => onNavigate("task-create")}
|
|
className={`w-full flex items-center justify-center gap-2 px-4 py-2 text-white rounded-lg text-sm font-medium transition-colors ${
|
|
isDark
|
|
? "bg-blue-600 hover:bg-blue-500"
|
|
: "bg-red-600 hover:bg-red-500"
|
|
}`}
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
新建任务
|
|
</button>
|
|
<button
|
|
onClick={() => onNavigate("plan-task-fx")}
|
|
className={`w-full flex items-center justify-center gap-2 px-4 py-2 border rounded-lg text-sm font-medium transition-colors ${
|
|
isDark
|
|
? "border-blue-800/30 hover:bg-slate-700/50 text-slate-300"
|
|
: "border-red-200 hover:bg-red-50 text-red-600"
|
|
}`}
|
|
>
|
|
<FileText className="w-4 h-4" />
|
|
进入任务中心
|
|
</button>
|
|
<button
|
|
className={`w-full flex items-center justify-center gap-2 px-4 py-2 border rounded-lg text-sm font-medium transition-colors ${
|
|
isDark
|
|
? "border-blue-800/30 hover:bg-slate-700/50 text-slate-300"
|
|
: "border-red-200 hover:bg-red-50 text-red-600"
|
|
}`}
|
|
>
|
|
<AlertTriangle className="w-4 h-4" />
|
|
查看延期任务
|
|
</button>
|
|
<button
|
|
className={`w-full flex items-center justify-center gap-2 px-4 py-2 border rounded-lg text-sm font-medium transition-colors ${
|
|
isDark
|
|
? "border-blue-800/30 hover:bg-slate-700/50 text-slate-300"
|
|
: "border-red-200 hover:bg-red-50 text-red-600"
|
|
}`}
|
|
>
|
|
<User className="w-4 h-4" />
|
|
查看我的任务
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default TaskDashboard;
|