feat: 增加父 PBI 列表加载功能与界面优化
- 更新 README.md,明确父 PBI Id 的选择方式,增加加载 PBI 列表的功能,提升用户在导入时的便利性。 - 在 MainForm.cs 中添加加载 PBI 列表的按钮,优化父项选择逻辑,确保用户能够方便地选择合适的父项。 - 修改 MainFormUi.cs,将父 PBI Id 输入框替换为下拉框,支持从列表中选择,提升用户体验。 - 在 TfsClient.cs 中实现获取父项候选的逻辑,确保用户能够根据项目和迭代路径获取相关的工作项信息。 - 调整界面布局,确保父 PBI 选择与其他输入项一致,提升整体视觉效果。
This commit is contained in:
@@ -169,6 +169,97 @@ namespace TfsExcelImporter
|
||||
return project + "\\" + string.Join("\\", segs);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------- 父项(backlog item)
|
||||
|
||||
/// <summary>下拉用的轻量工作项快照:只要 Id / 标题 / 类型 / 状态 / 迭代。</summary>
|
||||
public class WorkItemBrief
|
||||
{
|
||||
public int Id;
|
||||
public string Title;
|
||||
public string Type;
|
||||
public string State;
|
||||
public string IterationPath;
|
||||
|
||||
/// <summary>
|
||||
/// 列表里给人看的文本:Id 打头,后面是标题;状态只在「非 New」时追加。
|
||||
/// 新建未审批的 PBI 全是 New,逐条显示纯属噪音——真正要看的是 Done/Closed 这类
|
||||
/// 挂上去也展不开的父项,让异常状态自己显形就够了。
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
var tail = string.IsNullOrWhiteSpace(State)
|
||||
|| string.Equals(State, "New", StringComparison.OrdinalIgnoreCase)
|
||||
? "" : " · " + State;
|
||||
return "#" + Id + " " + (Title ?? "") + tail;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查可当父项用的工作项(默认 Product Backlog Item)。
|
||||
/// 先按目标迭代过滤;该迭代下一条都没有时放宽到全项目——迭代里还没建 PBI 是常态,
|
||||
/// 直接返回空列表会让人以为「接口没通」。
|
||||
/// </summary>
|
||||
public async Task<List<WorkItemBrief>> GetBacklogItemsAsync(string project, string iterationPath,
|
||||
Action<string> log = null)
|
||||
{
|
||||
var scoped = await QueryWorkItemsAsync(project, iterationPath, "Product Backlog Item");
|
||||
if (scoped.Count > 0)
|
||||
{
|
||||
log?.Invoke($"父项候选: 迭代「{iterationPath}」下的 Product Backlog Item {scoped.Count} 条");
|
||||
return scoped;
|
||||
}
|
||||
|
||||
log?.Invoke("父项候选: 该迭代下没有 Product Backlog Item,放宽到全项目");
|
||||
var all = await QueryWorkItemsAsync(project, null, "Product Backlog Item");
|
||||
if (all.Count > 0)
|
||||
{
|
||||
log?.Invoke($"父项候选: 全项目 Product Backlog Item {all.Count} 条");
|
||||
return all;
|
||||
}
|
||||
|
||||
// 非敏捷模板(CMMI/Scrum 的老项目)没有 PBI 类型:退化为「除 Task 外的所有工作项」
|
||||
var fallback = await QueryWorkItemsAsync(project, iterationPath, null, excludeType: "Task");
|
||||
log?.Invoke($"父项候选: 未找到 Product Backlog Item,退回「非 Task 工作项」{fallback.Count} 条");
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WIQL 取工作项快照。workItemType 为空时按 excludeType 反向过滤(排除导入类型本身)。
|
||||
/// iterationPath 为空时不限迭代。
|
||||
/// </summary>
|
||||
private async Task<List<WorkItemBrief>> QueryWorkItemsAsync(string project, string iterationPath,
|
||||
string workItemType, string excludeType = null)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var wiql = new StringBuilder(
|
||||
"SELECT [System.Id], [System.Title], [System.WorkItemType], [System.State], [System.IterationPath] " +
|
||||
"FROM WorkItems WHERE ");
|
||||
wiql.Append($"[System.TeamProject] = '{EscapeWiql(project)}'");
|
||||
if (!string.IsNullOrWhiteSpace(iterationPath))
|
||||
wiql.Append($" AND [System.IterationPath] = '{EscapeWiql(iterationPath)}'");
|
||||
if (!string.IsNullOrWhiteSpace(workItemType))
|
||||
wiql.Append($" AND [System.WorkItemType] = '{EscapeWiql(workItemType)}'");
|
||||
else if (!string.IsNullOrWhiteSpace(excludeType))
|
||||
wiql.Append($" AND [System.WorkItemType] <> '{EscapeWiql(excludeType)}'");
|
||||
wiql.Append(" ORDER BY [System.Id]");
|
||||
|
||||
var list = new List<WorkItemBrief>();
|
||||
foreach (WorkItem wi in Store.Query(wiql.ToString()))
|
||||
{
|
||||
list.Add(new WorkItemBrief
|
||||
{
|
||||
Id = wi.Id,
|
||||
Title = wi.Title,
|
||||
Type = Convert.ToString(wi.Fields["System.WorkItemType"].Value),
|
||||
State = Convert.ToString(wi.Fields["System.State"].Value),
|
||||
IterationPath = Convert.ToString(wi.Fields["System.IterationPath"].Value)
|
||||
});
|
||||
}
|
||||
return list;
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------- 查重
|
||||
|
||||
/// <summary>查询指定迭代下同类型工作项的标题集合。</summary>
|
||||
|
||||
Reference in New Issue
Block a user