FM/Assets/Scripts/Editor/TaskManagerWindow.cs

690 lines
26 KiB
C#
Raw Normal View History

using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.IO;
public class TaskManagerWindow : EditorWindow
{
// 颜色定义
private static class Colors
{
public static readonly Color PriorityLow = new Color(0.6f, 0.6f, 0.6f);
public static readonly Color PriorityMedium = new Color(0.2f, 0.5f, 0.8f);
public static readonly Color PriorityHigh = new Color(0.9f, 0.5f, 0.3f);
public static readonly Color PriorityUrgent = new Color(0.9f, 0.2f, 0.2f);
public static readonly Color StatusNotStarted = new Color(0.7f, 0.7f, 0.7f);
public static readonly Color StatusInProgress = new Color(0.2f, 0.7f, 1f);
public static readonly Color StatusCompleted = new Color(0.3f, 0.8f, 0.3f);
public static readonly Color StatusPaused = new Color(0.8f, 0.8f, 0.3f);
public static readonly Color StatusAll = new Color(0.5f, 0.5f, 0.5f);
public static readonly Color CategoryDevelopment = new Color(0.4f, 0.6f, 0.8f);
public static readonly Color CategoryBugFix = new Color(0.8f, 0.4f, 0.4f);
public static readonly Color CategoryOptimization = new Color(0.4f, 0.8f, 0.4f);
public static readonly Color CategoryUI = new Color(0.8f, 0.6f, 0.8f);
public static readonly Color CategoryTesting = new Color(0.6f, 0.8f, 0.8f);
public static readonly Color CategoryDocumentation = new Color(0.8f, 0.8f, 0.4f);
public static readonly Color CategoryOther = new Color(0.7f, 0.7f, 0.7f);
public static readonly Color ProgressBar = new Color(0.2f, 0.6f, 1f);
public static readonly Color ProgressBarBg = new Color(0.8f, 0.8f, 0.8f);
public static readonly Color StatsBg = new Color(0.9f, 0.9f, 0.9f);
}
// 任务枚举定义
public enum TaskPriority { , , , }
public enum TaskStatus { , , , }
public enum TaskCategory { , BUG, , UI, , , }
// 任务数据类
[Serializable]
public class TaskItem
{
public string id;
public string title;
public string description;
public TaskCategory category;
public TaskPriority priority;
public TaskStatus status;
public string creator;
public string assignee;
public DateTime createTime;
public DateTime? dueTime;
public DateTime? completeTime;
public float progress;
}
[Serializable]
private class TaskWrapper
{
public List<TaskItem> tasks = new List<TaskItem>();
}
// 数据与状态
private List<TaskItem> allTasks = new List<TaskItem>();
private List<TaskItem> filteredTasks = new List<TaskItem>();
private Vector2 mainScrollPosition;
private string savePath;
private bool isEditing = false;
private TaskItem currentTask = null;
private string searchKeyword = "";
private TaskStatus? filterStatus = null; // null表示"全部"状态
private TaskCategory? filterCategory = null;
private DateTime tempDueTime = DateTime.Now;
private bool isDueTimeSet = false;
[MenuItem("Tool/任务管理器")]
public static void ShowWindow()
{
var window = GetWindow<TaskManagerWindow>("任务管理器");
window.minSize = new Vector2(800, 600);
}
private void OnEnable()
{
savePath = Path.Combine(Application.dataPath, "../TaskData.json");
LoadTasks();
UpdateFilteredTasks();
}
private void OnFocus()
{
UpdateFilteredTasks();
Repaint();
}
private void OnGUI()
{
DrawToolbar();
GUILayout.Space(10);
// 显示分类状态统计
if (filterCategory.HasValue)
{
DrawCategoryStatusStats(filterCategory.Value);
GUILayout.Space(10);
}
mainScrollPosition = EditorGUILayout.BeginScrollView(mainScrollPosition);
if (isEditing)
{
DrawTaskEditor();
}
else
{
DrawTaskList();
}
EditorGUILayout.EndScrollView();
}
// 绘制分类状态统计信息
private void DrawCategoryStatusStats(TaskCategory category)
{
// 计算该分类下各状态的任务数量
int total = allTasks.FindAll(t => t.category == category).Count;
int notStarted = allTasks.FindAll(t => t.category == category && t.status == TaskStatus.).Count;
int inProgress = allTasks.FindAll(t => t.category == category && t.status == TaskStatus.).Count;
int completed = allTasks.FindAll(t => t.category == category && t.status == TaskStatus.).Count;
int paused = allTasks.FindAll(t => t.category == category && t.status == TaskStatus.).Count;
// 绘制统计面板
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label($"{category} 任务状态统计 (共 {total} 个)", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
DrawStatusStat("未开始", notStarted, Colors.StatusNotStarted);
DrawStatusStat("进行中", inProgress, Colors.StatusInProgress);
DrawStatusStat("已完成", completed, Colors.StatusCompleted);
DrawStatusStat("已暂停", paused, Colors.StatusPaused);
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
// 绘制单个状态的统计项
private void DrawStatusStat(string label, int count, Color color)
{
float percentage = allTasks.Count > 0 && filterCategory.HasValue ?
(float)count / allTasks.FindAll(t => t.category == filterCategory.Value).Count * 100 : 0;
EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
EditorGUILayout.BeginHorizontal();
GUILayout.Label(label, GUILayout.Width(60));
GUILayout.Label($"{count}个 ({percentage:F1}%)", EditorStyles.miniLabel);
EditorGUILayout.EndHorizontal();
// 绘制统计进度条
Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(8));
EditorGUI.DrawRect(rect, Colors.StatsBg);
rect.width *= Mathf.Clamp01(percentage / 100f);
EditorGUI.DrawRect(rect, color);
EditorGUILayout.EndVertical();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
var newTaskStyle = new GUIStyle(EditorStyles.toolbarButton);
newTaskStyle.normal.textColor = Color.white;
newTaskStyle.normal.background = MakeColorTexture(Colors.CategoryDevelopment);
if (GUILayout.Button("新增任务", newTaskStyle))
{
StartEditingNewTask();
}
GUILayout.FlexibleSpace();
string newSearchKeyword = EditorGUILayout.TextField("", searchKeyword, EditorStyles.toolbarSearchField, GUILayout.Width(200));
if (newSearchKeyword != searchKeyword)
{
searchKeyword = newSearchKeyword;
UpdateFilteredTasks();
}
// 状态筛选下拉框(包含"全部"选项)
EditorGUILayout.LabelField("状态:", EditorStyles.label, GUILayout.Width(40));
if (GUILayout.Button(filterStatus.HasValue ? filterStatus.Value.ToString() : "全部",
EditorStyles.toolbarDropDown, GUILayout.Width(80)))
{
// 创建状态筛选菜单
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("全部"), !filterStatus.HasValue, () =>
{
filterStatus = null;
UpdateFilteredTasks();
});
foreach (TaskStatus status in Enum.GetValues(typeof(TaskStatus)))
{
bool isSelected = filterStatus.HasValue && filterStatus.Value == status;
menu.AddItem(new GUIContent(status.ToString()), isSelected, () =>
{
filterStatus = status;
UpdateFilteredTasks();
});
}
menu.ShowAsContext();
}
if (GUILayout.Button("清除筛选", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
filterStatus = null;
filterCategory = null;
searchKeyword = "";
UpdateFilteredTasks();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("分类筛选:", EditorStyles.label, GUILayout.Width(70));
foreach (TaskCategory category in Enum.GetValues(typeof(TaskCategory)))
{
bool isSelected = filterCategory.HasValue && filterCategory.Value == category;
GUIStyle buttonStyle = new GUIStyle(isSelected ? EditorStyles.toolbarButton : EditorStyles.miniButton);
buttonStyle.normal.textColor = Color.white;
buttonStyle.normal.background = MakeColorTexture(GetCategoryColor(category));
if (GUILayout.Button(category.ToString(), buttonStyle, GUILayout.Width(90)))
{
filterCategory = isSelected ? (TaskCategory?)null : category;
UpdateFilteredTasks();
}
}
EditorGUILayout.EndHorizontal();
}
private void DrawTaskList()
{
EditorGUILayout.LabelField($"任务总数: {allTasks.Count} | 显示: {filteredTasks.Count}", EditorStyles.miniLabel);
if (filteredTasks.Count == 0)
{
EditorGUILayout.LabelField("没有找到符合筛选条件的任务", EditorStyles.centeredGreyMiniLabel);
string filterInfo = "当前筛选: ";
if (filterStatus.HasValue) filterInfo += $"状态={filterStatus.Value} ";
else filterInfo += "状态=全部 ";
if (filterCategory.HasValue) filterInfo += $"分类={filterCategory.Value} ";
else filterInfo += "分类=全部 ";
if (!string.IsNullOrEmpty(searchKeyword)) filterInfo += $"关键词={searchKeyword}";
EditorGUILayout.LabelField(filterInfo, EditorStyles.miniLabel);
return;
}
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("标题", EditorStyles.boldLabel, GUILayout.Width(200));
GUILayout.Label("分类", EditorStyles.boldLabel, GUILayout.Width(100));
GUILayout.Label("优先级", EditorStyles.boldLabel, GUILayout.Width(80));
GUILayout.Label("状态", EditorStyles.boldLabel, GUILayout.Width(80));
GUILayout.Label("负责人", EditorStyles.boldLabel, GUILayout.Width(100));
GUILayout.Label("进度", EditorStyles.boldLabel, GUILayout.Width(100));
GUILayout.Label("截止日期", EditorStyles.boldLabel, GUILayout.Width(120));
EditorGUILayout.EndHorizontal();
foreach (var task in filteredTasks)
{
EditorGUILayout.BeginHorizontal();
var titleStyle = new GUIStyle(EditorStyles.label);
titleStyle.normal.textColor = GetStatusColor(task.status);
GUILayout.Label(task.title, titleStyle, GUILayout.Width(200));
var categoryStyle = new GUIStyle(EditorStyles.label);
categoryStyle.normal.textColor = Color.white;
categoryStyle.alignment = TextAnchor.MiddleCenter;
categoryStyle.normal.background = MakeColorTexture(GetCategoryColor(task.category));
categoryStyle.padding = new RectOffset(4, 4, 2, 2);
GUILayout.Label(task.category.ToString(), categoryStyle, GUILayout.Width(100));
var priorityStyle = new GUIStyle(EditorStyles.label);
priorityStyle.normal.textColor = GetPriorityColor(task.priority);
GUILayout.Label(task.priority.ToString(), priorityStyle, GUILayout.Width(80));
var statusStyle = new GUIStyle(EditorStyles.label);
statusStyle.normal.textColor = GetStatusColor(task.status);
GUILayout.Label(task.status.ToString(), statusStyle, GUILayout.Width(80));
GUILayout.Label(task.assignee, GUILayout.Width(100));
EditorGUILayout.BeginVertical();
GUILayout.Label($"{task.progress}%", GUILayout.Height(14));
DrawProgressBar(task.progress);
EditorGUILayout.EndVertical();
string dueDateText = task.dueTime.HasValue ? task.dueTime.Value.ToString("yyyy-MM-dd HH:mm") : "无";
var dueDateStyle = new GUIStyle(EditorStyles.label);
if (task.dueTime.HasValue && task.dueTime < DateTime.Now && task.status != TaskStatus.)
{
dueDateStyle.normal.textColor = Color.red;
}
GUILayout.Label(dueDateText, dueDateStyle, GUILayout.Width(120));
if (GUILayout.Button("编辑", EditorStyles.miniButton, GUILayout.Width(50)))
{
StartEditingExistingTask(task);
}
if (GUILayout.Button("删除", EditorStyles.miniButton, GUILayout.Width(50)))
{
DeleteTask(task);
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(2);
}
}
private void DeleteTask(TaskItem task)
{
if (EditorUtility.DisplayDialog("确认删除", $"确定要删除任务「{task.title}」吗?", "删除", "取消"))
{
int index = allTasks.IndexOf(task);
if (index != -1)
{
allTasks.RemoveAt(index);
SaveTasks();
UpdateFilteredTasks();
Repaint();
}
}
}
private void DrawProgressBar(float progress)
{
Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(16));
EditorGUI.DrawRect(rect, Colors.ProgressBarBg);
Color progressColor = progress >= 100 ? Colors.StatusCompleted :
progress > 50 ? new Color(0.2f, 0.7f, 0.5f) : Colors.ProgressBar;
rect.width *= Mathf.Clamp01(progress / 100f);
EditorGUI.DrawRect(rect, progressColor);
}
private void DrawTaskEditor()
{
var titleStyle = new GUIStyle(EditorStyles.boldLabel);
titleStyle.fontSize = 14;
titleStyle.normal.textColor = Colors.CategoryDevelopment;
GUILayout.Label(isEditing && currentTask.id == "" ? "新增任务" : "编辑任务", titleStyle);
GUILayout.Space(10);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
currentTask.title = EditorGUILayout.TextField("任务标题*", currentTask.title);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("任务分类:", GUILayout.Width(80));
currentTask.category = (TaskCategory)EditorGUILayout.EnumPopup(currentTask.category);
Rect colorRect = EditorGUILayout.GetControlRect(GUILayout.Width(20), GUILayout.Height(20));
EditorGUI.DrawRect(colorRect, GetCategoryColor(currentTask.category));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.Label("优先级:", GUILayout.Width(80));
currentTask.priority = (TaskPriority)EditorGUILayout.EnumPopup(currentTask.priority);
Rect priorityRect = EditorGUILayout.GetControlRect(GUILayout.Width(20), GUILayout.Height(20));
EditorGUI.DrawRect(priorityRect, GetPriorityColor(currentTask.priority));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.Label("状态:", GUILayout.Width(80));
currentTask.status = (TaskStatus)EditorGUILayout.EnumPopup(currentTask.status);
Rect statusRect = EditorGUILayout.GetControlRect(GUILayout.Width(20), GUILayout.Height(20));
EditorGUI.DrawRect(statusRect, GetStatusColor(currentTask.status));
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
currentTask.creator = EditorGUILayout.TextField("创建人", currentTask.creator);
currentTask.assignee = EditorGUILayout.TextField("负责人*", currentTask.assignee);
GUILayout.Space(10);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("创建时间:", GUILayout.Width(80));
GUILayout.Label(currentTask.createTime.ToString("yyyy-MM-dd HH:mm"), EditorStyles.label);
EditorGUILayout.EndHorizontal();
isDueTimeSet = EditorGUILayout.Toggle("设置截止时间", currentTask.dueTime.HasValue);
if (isDueTimeSet)
{
if (!currentTask.dueTime.HasValue)
{
tempDueTime = DateTime.Now;
}
else if (tempDueTime != currentTask.dueTime.Value)
{
tempDueTime = currentTask.dueTime.Value;
}
EditorGUILayout.BeginHorizontal();
GUILayout.Label("日期:", GUILayout.Width(60));
int year = EditorGUILayout.IntField(tempDueTime.Year, GUILayout.Width(60));
int month = EditorGUILayout.IntField(tempDueTime.Month, GUILayout.Width(40));
int day = EditorGUILayout.IntField(tempDueTime.Day, GUILayout.Width(40));
GUILayout.Label("时间:", GUILayout.Width(40));
int hour = EditorGUILayout.IntField(tempDueTime.Hour, GUILayout.Width(40));
int minute = EditorGUILayout.IntField(tempDueTime.Minute, GUILayout.Width(40));
EditorGUILayout.EndHorizontal();
try
{
tempDueTime = new DateTime(year, month, day, hour, minute, 0);
currentTask.dueTime = tempDueTime;
if (currentTask.dueTime < DateTime.Now && currentTask.status != TaskStatus.)
{
EditorGUILayout.LabelField("⚠️ 截止时间已过期", EditorStyles.helpBox);
}
}
catch
{
EditorGUILayout.LabelField("日期时间格式错误!", EditorStyles.label);
}
}
else
{
currentTask.dueTime = null;
}
if (currentTask.status == TaskStatus. && currentTask.completeTime.HasValue)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label("完成时间:", GUILayout.Width(80));
GUILayout.Label(currentTask.completeTime.Value.ToString("yyyy-MM-dd HH:mm"), EditorStyles.label);
EditorGUILayout.EndHorizontal();
}
GUILayout.Space(10);
currentTask.progress = EditorGUILayout.Slider("完成进度", currentTask.progress, 0, 100);
DrawProgressBar(currentTask.progress);
EditorGUILayout.LabelField("任务描述:");
currentTask.description = EditorGUILayout.TextArea(currentTask.description, GUILayout.Height(100));
GUILayout.Space(20);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
var cancelStyle = new GUIStyle(EditorStyles.miniButton);
cancelStyle.normal.background = MakeColorTexture(new Color(0.7f, 0.7f, 0.7f));
var saveStyle = new GUIStyle(EditorStyles.miniButton);
saveStyle.normal.background = MakeColorTexture(Colors.StatusCompleted);
saveStyle.normal.textColor = Color.white;
if (GUILayout.Button("取消", cancelStyle, GUILayout.Width(80)))
{
isEditing = false;
currentTask = null;
Repaint();
}
if (GUILayout.Button("保存", saveStyle, GUILayout.Width(80)))
{
SaveCurrentTask();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void SaveCurrentTask()
{
if (string.IsNullOrEmpty(currentTask.title?.Trim()))
{
EditorUtility.DisplayDialog("输入错误", "任务标题不能为空", "确定");
return;
}
if (string.IsNullOrEmpty(currentTask.assignee?.Trim()))
{
EditorUtility.DisplayDialog("输入错误", "负责人不能为空", "确定");
return;
}
if (currentTask.status == TaskStatus. && !currentTask.completeTime.HasValue)
{
currentTask.completeTime = DateTime.Now;
currentTask.progress = 100;
}
else if (currentTask.status != TaskStatus. && currentTask.progress >= 100)
{
currentTask.status = TaskStatus.;
currentTask.completeTime = DateTime.Now;
}
int existingIndex = allTasks.FindIndex(t => t.id == currentTask.id);
if (existingIndex != -1)
{
allTasks.RemoveAt(existingIndex);
}
allTasks.Add(currentTask);
SaveTasks();
UpdateFilteredTasks();
Repaint();
isEditing = false;
currentTask = null;
}
private void UpdateFilteredTasks()
{
filteredTasks = new List<TaskItem>(allTasks);
if (!string.IsNullOrEmpty(searchKeyword))
{
string keyword = searchKeyword.ToLower();
filteredTasks.RemoveAll(t =>
t.title == null || !t.title.ToLower().Contains(keyword) &&
t.description == null || !t.description.ToLower().Contains(keyword) &&
t.assignee == null || !t.assignee.ToLower().Contains(keyword)
);
}
// 状态筛选只有当filterStatus有值时才筛选
if (filterStatus.HasValue)
{
filteredTasks.RemoveAll(t => t.status != filterStatus.Value);
}
// 当filterStatus为null时即"全部"),不进行状态筛选
if (filterCategory.HasValue)
{
filteredTasks.RemoveAll(t => t.category != filterCategory.Value);
}
filteredTasks.Sort((a, b) =>
{
int priorityCompare = b.priority.CompareTo(a.priority);
if (priorityCompare != 0) return priorityCompare;
if (a.dueTime.HasValue && b.dueTime.HasValue)
{
return a.dueTime.Value.CompareTo(b.dueTime.Value);
}
return a.dueTime.HasValue ? -1 : 1;
});
}
private Color GetPriorityColor(TaskPriority priority)
{
return priority switch
{
TaskPriority. => Colors.PriorityLow,
TaskPriority. => Colors.PriorityMedium,
TaskPriority. => Colors.PriorityHigh,
TaskPriority. => Colors.PriorityUrgent,
_ => Color.black
};
}
private Color GetStatusColor(TaskStatus status)
{
return status switch
{
TaskStatus. => Colors.StatusNotStarted,
TaskStatus. => Colors.StatusInProgress,
TaskStatus. => Colors.StatusCompleted,
TaskStatus. => Colors.StatusPaused,
_ => Color.black
};
}
private Color GetCategoryColor(TaskCategory category)
{
return category switch
{
TaskCategory. => Colors.CategoryDevelopment,
TaskCategory.BUG => Colors.CategoryBugFix,
TaskCategory. => Colors.CategoryOptimization,
TaskCategory.UI => Colors.CategoryUI,
TaskCategory. => Colors.CategoryTesting,
TaskCategory. => Colors.CategoryDocumentation,
TaskCategory. => Colors.CategoryOther,
_ => Colors.CategoryOther
};
}
private Texture2D MakeColorTexture(Color color)
{
Texture2D tex = new Texture2D(1, 1);
tex.SetPixel(0, 0, color);
tex.Apply();
return tex;
}
private void StartEditingNewTask()
{
currentTask = new TaskItem
{
id = Guid.NewGuid().ToString(),
createTime = DateTime.Now,
creator = System.Environment.UserName,
progress = 0,
status = TaskStatus.,
priority = TaskPriority.
};
isEditing = true;
isDueTimeSet = false;
}
private void StartEditingExistingTask(TaskItem task)
{
currentTask = new TaskItem()
{
id = task.id,
title = task.title,
description = task.description,
category = task.category,
priority = task.priority,
status = task.status,
creator = task.creator,
assignee = task.assignee,
createTime = task.createTime,
dueTime = task.dueTime,
completeTime = task.completeTime,
progress = task.progress
};
isDueTimeSet = currentTask.dueTime.HasValue;
if (isDueTimeSet && currentTask.dueTime.HasValue)
{
tempDueTime = currentTask.dueTime.Value;
}
isEditing = true;
}
private void LoadTasks()
{
try
{
if (File.Exists(savePath))
{
string json = File.ReadAllText(savePath);
TaskWrapper wrapper = JsonUtility.FromJson<TaskWrapper>(json);
if (wrapper != null && wrapper.tasks != null)
{
allTasks = new List<TaskItem>(wrapper.tasks);
}
}
}
catch (Exception e)
{
Debug.LogError("加载任务数据失败: " + e.Message);
allTasks = new List<TaskItem>();
}
}
private void SaveTasks()
{
try
{
var saveData = new TaskWrapper { tasks = new List<TaskItem>(allTasks) };
string json = JsonUtility.ToJson(saveData, true);
File.WriteAllText(savePath, json);
}
catch (Exception e)
{
Debug.LogError("保存任务数据失败: " + e.Message);
}
}
}