690 lines
26 KiB
C#
690 lines
26 KiB
C#
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|