feat: 增强数据导入功能与用户界面优化
- 更新 README.md,强调 Area 路径为必填项,并增加加载 Area 列表的功能,确保用户在导入时不会遗漏必要信息。 - 修改 Config.cs 和 config.json,调整 SkipExistingTitles 默认值为 false,以便用户首次导入时能看到真实冲突和报错。 - 在 MainForm.cs 中添加加载 Area 列表的按钮,优化用户交互体验。 - 更新 ExcelService.cs,增强模板导出逻辑,确保用户能够更方便地使用自定义模板。 - 调整界面布局,确保 Area 路径与迭代路径的选择更加直观,提升整体用户体验。
This commit is contained in:
@@ -21,10 +21,11 @@ namespace TfsExcelImporter
|
||||
private TextBox _txtPwd = new TextBox { UseSystemPasswordChar = true };
|
||||
|
||||
private ComboBox _cmbIteration = new ComboBox();
|
||||
private ComboBox _cmbArea = new ComboBox();
|
||||
private TextBox _txtExcel = new TextBox();
|
||||
private TextBox _txtParentPbi = new TextBox();
|
||||
private TextBox _txtAreaPath = new TextBox();
|
||||
private Button _btnLoadIterations = new Button();
|
||||
private Button _btnLoadAreas = new Button();
|
||||
private Button _btnTemplate = new Button();
|
||||
private CheckBox _chkSkip = new CheckBox();
|
||||
|
||||
@@ -337,34 +338,47 @@ namespace TfsExcelImporter
|
||||
private void BuildCardData()
|
||||
{
|
||||
_cardData = MakeCard(2, "选择迭代与数据源", true, Theme.Px(44));
|
||||
_cardData.SummaryDots = true; // 头部摘要按「圆点 + 短语」分段,四项状态一眼可辨
|
||||
_cardData.SummaryDots = true; // 头部摘要按「圆点 + 短语」分段,五项状态一眼可辨
|
||||
_cmbIteration.DropDownStyle = ComboBoxStyle.DropDown; // 允许手填完整路径
|
||||
_cmbArea.DropDownStyle = ComboBoxStyle.DropDown; // 同上:列表拿不到时仍能手填
|
||||
|
||||
_fIteration = NewField(_cmbIteration, 500, false);
|
||||
_fArea = NewField(_cmbArea, 500, true);
|
||||
_fExcel = NewField(_txtExcel, 500, true);
|
||||
_fParent = NewField(_txtParentPbi, 150, true);
|
||||
|
||||
_fIteration.SetCue("选择或手填完整迭代路径,如 Tools_Dev\\Sprint 1");
|
||||
// Area 现在是必填:留空会导致团队视图看不到新导入的工作项,所以提示里直接给写法
|
||||
_fArea.SetCue("选择或手填完整 Area 路径,如 Tools_Dev\\DataTechnologies");
|
||||
_fExcel.SetCue("选择 .xlsx 文件,或点右侧文件夹图标浏览");
|
||||
_fParent.SetCue("如 219975");
|
||||
|
||||
_btnLoadIterations = NewButton("加载迭代列表", BtnKind.Secondary, IconKind.Fetch, 130);
|
||||
_btnLoadAreas = NewButton("加载 Area 列表", BtnKind.Secondary, IconKind.Fetch, 130);
|
||||
_btnTemplate = NewButton("导出 Excel 模板", BtnKind.Secondary, IconKind.Doc, 142);
|
||||
// 行尾两个按钮宽度取齐(取两者较宽的一个):右缘本来就都由 AnchorRight 贴在 M 上,
|
||||
// 宽度一致后左缘也落在同一条竖线上,两个按钮才成为一列;同时两行 Track 给右侧留的
|
||||
// 宽度变成同一个值,上面两个输入框的右边框才对得齐。
|
||||
int wBtn = Math.Max(_btnLoadIterations.Width, _btnTemplate.Width);
|
||||
_btnLoadIterations.Width = wBtn;
|
||||
_btnLoadAreas.Width = wBtn;
|
||||
_btnTemplate.Width = wBtn;
|
||||
_chkSkip = NewCheck("跳过已存在标题", "");
|
||||
|
||||
// 三行字段对齐同一条左缘:标签列宽取最大值
|
||||
int wLbl = MaxLabelWidth("目标迭代", "Excel 文件", "父 PBI Id");
|
||||
// 四行字段对齐同一条左缘:标签列宽取最大值;必填项的红色 * 也留进列宽里,
|
||||
// 否则带 * 的行会把输入框往右推,四行左缘就不再是一条竖线。
|
||||
int wLbl = MaxLabelWidth("目标迭代", "Area 路径", "Excel 文件", "父 PBI Id")
|
||||
+ Theme.Px(2) + Theme.TextWidth("*", Theme.Body);
|
||||
int y = _cardData.HeaderH + Theme.S12;
|
||||
y = Row(_cardData, y, 52, new Lbl("目标迭代", wLbl), _fIteration);
|
||||
y = Row(_cardData, y, 52, new Lbl("目标迭代", wLbl, true), _fIteration);
|
||||
AnchorRight(_btnLoadIterations, _cardData, M, _fIteration.Top);
|
||||
Track(_fIteration, _cardData, wBtn + G + M, 0);
|
||||
y = Row(_cardData, y, 52, new Lbl("Excel 文件", wLbl), _fExcel);
|
||||
// Area 与迭代同属「写到哪儿」:紧贴迭代下方,值直接落进 System.AreaPath。
|
||||
// 行尾同样挂一个列表按钮:Area 树往往比迭代树深得多,手打路径最容易写错。
|
||||
y = Row(_cardData, y, 52, new Lbl("Area 路径", wLbl, true), _fArea);
|
||||
AnchorRight(_btnLoadAreas, _cardData, M, _fArea.Top);
|
||||
Track(_fArea, _cardData, wBtn + G + M, 0);
|
||||
y = Row(_cardData, y, 52, new Lbl("Excel 文件", wLbl, true), _fExcel);
|
||||
// 设计方案:行内操作(浏览)并入输入框尾部图标,行尾只留右对齐成一列的操作按钮
|
||||
_fExcel.AddSuffix(IconKind.Folder, BrowseExcel);
|
||||
AnchorRight(_btnTemplate, _cardData, M, _fExcel.Top);
|
||||
@@ -377,6 +391,7 @@ namespace TfsExcelImporter
|
||||
y = Row(_cardData, y, 32, _fParent.Left - M, _chkSkip);
|
||||
|
||||
_errIteration = AddErrorUnder(_cardData, _fIteration);
|
||||
_errArea = AddErrorUnder(_cardData, _fArea);
|
||||
_errExcel = AddErrorUnder(_cardData, _fExcel);
|
||||
_errParent = AddErrorUnder(_cardData, _fParent);
|
||||
_cardData.BodyH = y - _cardData.HeaderH + Theme.S8;
|
||||
@@ -554,17 +569,16 @@ namespace TfsExcelImporter
|
||||
{
|
||||
_cardAdv = MakeCard(0, "高级工具", true, Theme.Px(36));
|
||||
_cardAdv.ShowBadge = false;
|
||||
_fArea = NewField(_txtAreaPath, 420, true);
|
||||
_fArea.SetCue("如 Tools_Dev\\导入工具,留空则沿用工作项现有 Area");
|
||||
// Area 输入框已上移到第 2 步(必填),这里只剩两个批量修正动作,
|
||||
// 两者都按第 2 步填的「目标迭代 / Area 路径」筛选已有工作项。
|
||||
_btnFixArea = NewButton("按迭代修 Area", BtnKind.Secondary, IconKind.Wrench, 158);
|
||||
_btnLinkParent = NewButton("补挂父项", BtnKind.Secondary, IconKind.Tree, 120);
|
||||
|
||||
int y = _cardAdv.HeaderH + Theme.S12;
|
||||
y = Row(_cardAdv, y, 52, "Area 路径", _fArea,
|
||||
new Hint("改 Area 与补挂父项都按上面的「目标迭代」筛选已有工作项"));
|
||||
y = Row(_cardAdv, y, 34, _btnFixArea, G, _btnLinkParent, G, "已关闭的工作项会被临时重新打开,完成后自动还原状态");
|
||||
_errArea = AddErrorUnder(_cardAdv, _fArea);
|
||||
y = Row(_cardAdv, y, 34, _btnFixArea, G, _btnLinkParent, G,
|
||||
new Hint("Area 取第 2 步的「Area 路径」,按「目标迭代」筛选;已关闭的工作项会临时重新打开,完成后自动还原状态"));
|
||||
_cardAdv.BodyH = y - _cardAdv.HeaderH + Theme.S8;
|
||||
_cardAdv.SetSummary("按迭代批量修 Area / 补挂父项");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------- 摆放助手
|
||||
@@ -573,8 +587,15 @@ namespace TfsExcelImporter
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly int MinWidth; // 运行时像素(构建时已含缩放)
|
||||
/// <summary>必填项:标签后补一个红色 *,且列宽里预留它的位置。</summary>
|
||||
public readonly bool Required;
|
||||
|
||||
public Lbl(string text, int minWidth) { Text = text; MinWidth = minWidth; }
|
||||
public Lbl(string text, int minWidth, bool required = false)
|
||||
{
|
||||
Text = text;
|
||||
MinWidth = minWidth;
|
||||
Required = required;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>紧跟在字段后面的灰色说明(Caption 字号 + Ink3):非标签、不参与标签列宽计算。</summary>
|
||||
@@ -616,6 +637,21 @@ namespace TfsExcelImporter
|
||||
};
|
||||
lbl.SetBounds(x, y + (Theme.FieldH - lbl.PreferredHeight) / 2, lbl.PreferredWidth, lbl.PreferredHeight);
|
||||
card.Controls.Add(lbl);
|
||||
if (spec != null && spec.Required)
|
||||
{
|
||||
// 红色 * 紧跟标签右缘:颜色是这套界面里唯一的「必填」色板(与错误提示同色)
|
||||
var star = new Label
|
||||
{
|
||||
Text = "*",
|
||||
Font = Theme.Body,
|
||||
ForeColor = Theme.Danger,
|
||||
AutoSize = true,
|
||||
BackColor = Theme.Card
|
||||
};
|
||||
star.SetBounds(x + lbl.PreferredWidth + Theme.Px(1),
|
||||
y + (Theme.FieldH - star.PreferredHeight) / 2, star.PreferredWidth, star.PreferredHeight);
|
||||
card.Controls.Add(star);
|
||||
}
|
||||
x += Math.Max(lbl.PreferredWidth, spec != null ? spec.MinWidth : 0) + Theme.Px(6);
|
||||
lastLabel = label;
|
||||
continue;
|
||||
@@ -922,6 +958,7 @@ namespace TfsExcelImporter
|
||||
_cardData.SetSummary(string.Join(" · ", new[]
|
||||
{
|
||||
string.IsNullOrEmpty(iter) ? "迭代未选" : iter,
|
||||
string.IsNullOrWhiteSpace(_cmbArea.Text) ? "Area 未填" : "Area " + _cmbArea.Text.Trim(),
|
||||
string.IsNullOrEmpty(fileName) ? "未选 Excel" : fileName,
|
||||
_rows.Count > 0 ? _rows.Count + " 行" : "未预览",
|
||||
string.IsNullOrWhiteSpace(_txtParentPbi.Text) ? "父 PBI 未填" : "父 PBI #" + _txtParentPbi.Text.Trim()
|
||||
@@ -929,9 +966,6 @@ namespace TfsExcelImporter
|
||||
_cardPreview.SetSummary(_rows.Count > 0
|
||||
? "共 " + _rows.Count + " 行,可直接开始导入"
|
||||
: "选择 Excel 后在这里核对将要写入的内容");
|
||||
_cardAdv.SetSummary(string.IsNullOrWhiteSpace(_txtAreaPath.Text)
|
||||
? "Area 未设置"
|
||||
: "Area " + _txtAreaPath.Text.Trim());
|
||||
}
|
||||
|
||||
/// <summary>第 4 步徽标:待办 / 进行中 / 完成 / 有失败。</summary>
|
||||
@@ -992,6 +1026,7 @@ namespace TfsExcelImporter
|
||||
_btnPreview.Enabled = !busy;
|
||||
_btnTest.Enabled = !busy;
|
||||
_btnLoadIterations.Enabled = !busy;
|
||||
_btnLoadAreas.Enabled = !busy;
|
||||
_btnFixArea.Enabled = !busy;
|
||||
_btnLinkParent.Enabled = !busy;
|
||||
_fExcel.SetSuffixEnabled(!busy); // 「浏览」已并入输入框尾部,随忙等一起禁用
|
||||
|
||||
Reference in New Issue
Block a user