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 tasks = new List(); } // 数据与状态 private List allTasks = new List(); private List filteredTasks = new List(); 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("任务管理器"); 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(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(json); if (wrapper != null && wrapper.tasks != null) { allTasks = new List(wrapper.tasks); } } } catch (Exception e) { Debug.LogError("加载任务数据失败: " + e.Message); allTasks = new List(); } } private void SaveTasks() { try { var saveData = new TaskWrapper { tasks = new List(allTasks) }; string json = JsonUtility.ToJson(saveData, true); File.WriteAllText(savePath, json); } catch (Exception e) { Debug.LogError("保存任务数据失败: " + e.Message); } } }