细节修改
This commit is contained in:
parent
95a859ec85
commit
b6a6febec4
@ -970,9 +970,9 @@ PowerShell.exe -ExecutionPolicy Bypass -File .\run.ps1
|
||||
|
||||
| 阈值 | 默认值 | 含义 |
|
||||
| ----------------- | ------ | -------------------------- |
|
||||
| `MaxOnTimeMs` | 5.0 ms | 开启时间上限 |
|
||||
| `MaxOnTimeMs` | 50.0 ms | 开启时间上限 |
|
||||
| `MaxRiseTimeMs` | 3.0 ms | 上升时间上限 |
|
||||
| `MaxOffTimeMs` | 5.0 ms | 关断时间上限 |
|
||||
| `MaxOffTimeMs` | 50.0 ms | 关断时间上限 |
|
||||
| `MaxFallTimeMs` | 3.0 ms | 下降时间上限 |
|
||||
|
||||
> **【提示】** 阈值随项目保存(位于 `project.json` 的 `CurveThresholds` 字段),可针对不同型号被测件调整。
|
||||
@ -1734,8 +1734,8 @@ $$
|
||||
|
||||
| 编号 | 中文名 | 单位 | 定义 | 测试方法 | 判定准则 | 软件位置 |
|
||||
| ---- | ------------------ | ---- | ----------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------- | --------------- |
|
||||
| 1.1 | 开启时间 | ms | 控制信号有效 → 输出电压上升到 90% V<sub>max</sub> 的时间 | 高速采集 + 上升沿百分位检测 | ≤ MaxOnTimeMs(默认 5) | 第 10 章 |
|
||||
| 1.2 | 关闭时间 | ms | 控制信号撤销 → 输出电压下降到 10% V<sub>max</sub> 的时间 | 高速采集 + 下降沿百分位检测 | ≤ MaxOffTimeMs(默认 5) | 第 10 章 |
|
||||
| 1.1 | 开启时间 | ms | 控制信号有效 → 输出电压上升到 90% V<sub>max</sub> 的时间 | 高速采集 + 上升沿百分位检测 | ≤ MaxOnTimeMs(默认 50) | 第 10 章 |
|
||||
| 1.2 | 关闭时间 | ms | 控制信号撤销 → 输出电压下降到 10% V<sub>max</sub> 的时间 | 高速采集 + 下降沿百分位检测 | ≤ MaxOffTimeMs(默认 50) | 第 10 章 |
|
||||
| 1.3 | 控制延迟时间 | ms | 控制端电平变化 → 内部驱动信号到达 | 厂商方法;本软件等同于"开启时间"近似 | 由型号规范确定 | 第 10 章(近似) |
|
||||
| 1.4 | 释放延迟时间 | ms | 控制端撤销 → 内部驱动撤销 | 同上 | 由型号规范确定 | 第 10 章(近似) |
|
||||
| 1.5 | 负载电压上升/下降时间 | ms | 负载侧电压 10% – 90% V<sub>max</sub> 的过渡时间 | 高速采集 + 双阈值检测 | ≤ MaxRiseTimeMs / MaxFallTimeMs(默认 3)| 第 10 章 |
|
||||
|
||||
@ -43,9 +43,9 @@ public partial class CurveResultRow : ObservableObject
|
||||
/// <summary>曲线测试阈值(合格判定)。</summary>
|
||||
public sealed class CurveThresholds
|
||||
{
|
||||
public double MaxOnTimeMs { get; set; } = 5.0;
|
||||
public double MaxOnTimeMs { get; set; } = 50.0;
|
||||
public double MaxRiseTimeMs { get; set; } = 3.0;
|
||||
public double MaxOffTimeMs { get; set; } = 5.0;
|
||||
public double MaxOffTimeMs { get; set; } = 50.0;
|
||||
public double MaxFallTimeMs { get; set; } = 3.0;
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,16 @@ public sealed class ReportGenerator
|
||||
column.Item().PaddingTop(45).Element(container => CoverIdentity(container, p, reportNo));
|
||||
column.Item().PaddingTop(34).Element(container => OverallPanel(container, p));
|
||||
column.Item().ExtendVertical();
|
||||
column.Item().PaddingBottom(18).BorderTop(1).BorderColor(Line).PaddingTop(14).Table(table =>
|
||||
});
|
||||
|
||||
page.Footer().Element(container => CoverFooter(container, p));
|
||||
}
|
||||
|
||||
private static void CoverFooter(IContainer container, Project p)
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
column.Item().BorderTop(1).BorderColor(Line).PaddingTop(14).Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
@ -74,13 +83,12 @@ public sealed class ReportGenerator
|
||||
CoverSignCell(table, "审核", "");
|
||||
CoverSignCell(table, "批准", "");
|
||||
});
|
||||
});
|
||||
|
||||
page.Footer().Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text("本报告仅对所检样品及记录数据负责").FontSize(8).FontColor(Muted);
|
||||
row.ConstantItem(180).AlignRight().Text($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm}")
|
||||
.FontSize(8).FontColor(Muted);
|
||||
column.Item().PaddingTop(12).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text("本报告仅对所检样品及记录数据负责").FontSize(8).FontColor(Muted);
|
||||
row.ConstantItem(180).AlignRight().Text($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm}")
|
||||
.FontSize(8).FontColor(Muted);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -204,7 +212,7 @@ public sealed class ReportGenerator
|
||||
Section(column, section++, "开启与关断曲线", "SWITCHING CHARACTERISTICS");
|
||||
if (p.CurveResults.Count > 0)
|
||||
{
|
||||
string criteria = $"判据:开启≤{p.CurveThresholds.MaxOnTimeMs:F1}ms,上升≤{p.CurveThresholds.MaxRiseTimeMs:F1}ms,关断≤{p.CurveThresholds.MaxOffTimeMs:F1}ms,下降≤{p.CurveThresholds.MaxFallTimeMs:F1}ms";
|
||||
string criteria = $"判据:开启时间≤{p.CurveThresholds.MaxOnTimeMs:F1}ms,上升时间≤{p.CurveThresholds.MaxRiseTimeMs:F1}ms,关断时间≤{p.CurveThresholds.MaxOffTimeMs:F1}ms,下降时间≤{p.CurveThresholds.MaxFallTimeMs:F1}ms";
|
||||
column.Item().Element(x => ModuleSummary(x, criteria, p.CurveResults.Select(r => r.Status)));
|
||||
column.Item().Element(x => CurveTable(x, p));
|
||||
}
|
||||
@ -462,14 +470,14 @@ public sealed class ReportGenerator
|
||||
private static void OverallPanel(IContainer container, Project p)
|
||||
{
|
||||
container.Border(1.2f).BorderColor(StatusColor(p.OverallStatus))
|
||||
.Background(StatusBackground(p.OverallStatus)).Padding(14).Row(row =>
|
||||
.Background(StatusBackground(p.OverallStatus)).Padding(12).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().Text("综合检测结论").FontSize(9).FontColor(Muted);
|
||||
column.Item().PaddingTop(4).Text(ConclusionText(p.OverallStatus)).FontSize(9);
|
||||
});
|
||||
row.ConstantItem(110).AlignRight().AlignMiddle().Text(StatusText(p.OverallStatus))
|
||||
row.ConstantItem(72).PaddingLeft(10).AlignRight().AlignMiddle().Text(StatusText(p.OverallStatus))
|
||||
.FontSize(18).Bold().FontColor(StatusColor(p.OverallStatus));
|
||||
});
|
||||
}
|
||||
|
||||
@ -34,10 +34,11 @@ public sealed class WordReportGenerator
|
||||
Body body = main.Document.Body!;
|
||||
|
||||
string reportNo = BuildReportNumber(project);
|
||||
string? firstPageFooterId = project.Report.Cover ? AddFirstPageFooter(main) : null;
|
||||
if (project.Report.Cover)
|
||||
ComposeCover(body, project, reportNo);
|
||||
ComposeBody(body, main, project, reportNo);
|
||||
body.Append(PageSectionProperties());
|
||||
body.Append(PageSectionProperties(firstPageFooterId));
|
||||
|
||||
main.Document.Save();
|
||||
}
|
||||
@ -95,11 +96,17 @@ public sealed class WordReportGenerator
|
||||
}),
|
||||
Spacer(18),
|
||||
ConclusionBox(p),
|
||||
Spacer(52),
|
||||
SignatureTable(),
|
||||
PageBreak());
|
||||
}
|
||||
|
||||
private static string AddFirstPageFooter(MainDocumentPart main)
|
||||
{
|
||||
var footerPart = main.AddNewPart<FooterPart>();
|
||||
footerPart.Footer = new Footer(SignatureTable(), FooterParagraph());
|
||||
footerPart.Footer.Save();
|
||||
return main.GetIdOfPart(footerPart);
|
||||
}
|
||||
|
||||
private void ComposeBody(Body body, MainDocumentPart main, Project p, string reportNo)
|
||||
{
|
||||
body.Append(HeaderParagraph("固态功率控制器动态参数检测报告", reportNo));
|
||||
@ -388,10 +395,21 @@ public sealed class WordReportGenerator
|
||||
private static Paragraph FooterParagraph() =>
|
||||
Paragraph($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm} 本报告仅对所检样品及记录数据负责。", 16, Muted, JustificationValues.Center);
|
||||
|
||||
private static SectionProperties PageSectionProperties() =>
|
||||
new(
|
||||
private static SectionProperties PageSectionProperties(string? firstPageFooterId = null)
|
||||
{
|
||||
var properties = new SectionProperties();
|
||||
if (!string.IsNullOrWhiteSpace(firstPageFooterId))
|
||||
{
|
||||
properties.Append(
|
||||
new FooterReference { Type = HeaderFooterValues.First, Id = firstPageFooterId },
|
||||
new TitlePage());
|
||||
}
|
||||
|
||||
properties.Append(
|
||||
new PageSize { Width = 11906U, Height = 16838U },
|
||||
new PageMargin { Top = 900, Right = 850U, Bottom = 900, Left = 850U, Header = 450U, Footer = 450U, Gutter = 0U });
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static long CmToEmu(double cm) => (long)Math.Round(cm * 360000.0);
|
||||
|
||||
|
||||
@ -39,6 +39,15 @@ public sealed class WordReportGeneratorTests
|
||||
.ToArray();
|
||||
Assert.Contains("SimSun", fontNames);
|
||||
Assert.Equal(2, document.MainDocumentPart.ImageParts.Count());
|
||||
|
||||
var sectionProperties = document.MainDocumentPart.Document.Body!.Descendants<SectionProperties>().Single();
|
||||
var firstFooterReference = sectionProperties.Elements<FooterReference>()
|
||||
.Single(x => x.Type?.Value == HeaderFooterValues.First);
|
||||
Assert.Contains(sectionProperties.Elements<TitlePage>(), _ => true);
|
||||
|
||||
var firstFooter = (FooterPart)document.MainDocumentPart.GetPartById(firstFooterReference.Id!);
|
||||
Assert.Single(firstFooter.Footer!.Elements<Table>());
|
||||
Assert.Equal(2, firstFooter.Footer!.Descendants<TableRow>().Count());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@ -377,10 +377,35 @@ public partial class MainViewModel : ObservableObject
|
||||
_viewBeforeSettings = CurrentView;
|
||||
var vm = _sp.GetRequiredService<SettingsViewModel>();
|
||||
vm.SetCloseAction(CloseSettings);
|
||||
vm.SetProjectRefreshAction(RefreshProjectsFromSettingsAsync, CanRefreshProjectsFromSettings);
|
||||
CurrentView = vm;
|
||||
DevicePanelOpen = false;
|
||||
}
|
||||
|
||||
private bool CanRefreshProjectsFromSettings()
|
||||
{
|
||||
if (State != AppState.Testing) return true;
|
||||
|
||||
_logSink.Add(UiLogLevel.Warning, "测试中不可切换项目目录,请退出测试后再刷新工程。");
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task RefreshProjectsFromSettingsAsync(string projectsRoot)
|
||||
{
|
||||
if (!CanRefreshProjectsFromSettings()) return;
|
||||
if (string.IsNullOrWhiteSpace(projectsRoot))
|
||||
{
|
||||
_logSink.Add(UiLogLevel.Error, "项目数据路径为空,无法刷新工程。");
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(projectsRoot);
|
||||
_store.ProjectsRoot = projectsRoot;
|
||||
CurrentProject = null;
|
||||
_viewBeforeSettings = _sp.GetRequiredService<HomeViewModel>();
|
||||
await LoadProjectsAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenSettingsFromDevicePanel()
|
||||
{
|
||||
|
||||
@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
|
||||
using Microsoft.Win32;
|
||||
using SSPCTester.Devices.Config;
|
||||
using SSPCTester.Devices.Interfaces;
|
||||
using SSPCTester.Logic.Storage;
|
||||
using SSPCTester.UI.Services;
|
||||
|
||||
namespace SSPCTester.UI.ViewModels;
|
||||
@ -25,14 +26,18 @@ public partial class SettingsViewModel : ObservableObject
|
||||
private readonly StorageOptions _runtimeStorage;
|
||||
private readonly ReportOptions _runtimeReport;
|
||||
private readonly UserSettingsStore _settingsStore;
|
||||
private readonly ProjectStore _projectStore;
|
||||
private Action? _closeAction;
|
||||
private Func<bool>? _canRefreshProjects;
|
||||
private Func<string, Task>? _projectRefreshAction;
|
||||
|
||||
public SettingsViewModel(
|
||||
IOptions<DeviceOptions> devOpts,
|
||||
IOptions<StorageOptions> storeOpts,
|
||||
IOptions<ReportOptions> reportOpts,
|
||||
ISspc sspc, IPowerSupply power, ILoad load, IDaq daq, ISafetyDio safetyDio, UiLogSink log,
|
||||
UserSettingsStore settingsStore)
|
||||
UserSettingsStore settingsStore,
|
||||
ProjectStore projectStore)
|
||||
{
|
||||
_runtimeDevices = devOpts.Value;
|
||||
_runtimeStorage = storeOpts.Value;
|
||||
@ -44,6 +49,7 @@ public partial class SettingsViewModel : ObservableObject
|
||||
_safetyDio = safetyDio;
|
||||
_log = log;
|
||||
_settingsStore = settingsStore;
|
||||
_projectStore = projectStore;
|
||||
|
||||
Devices = CloneDeviceOptions(_runtimeDevices);
|
||||
Storage = CloneStorageOptions(_runtimeStorage);
|
||||
@ -141,6 +147,12 @@ public partial class SettingsViewModel : ObservableObject
|
||||
|
||||
public void SetCloseAction(Action closeAction) => _closeAction = closeAction;
|
||||
|
||||
public void SetProjectRefreshAction(Func<string, Task> refreshAction, Func<bool>? canRefresh = null)
|
||||
{
|
||||
_projectRefreshAction = refreshAction;
|
||||
_canRefreshProjects = canRefresh;
|
||||
}
|
||||
|
||||
public void NotifyDraftChanged()
|
||||
{
|
||||
ApplyDaqChannelEditorsToDraft();
|
||||
@ -154,17 +166,29 @@ public partial class SettingsViewModel : ObservableObject
|
||||
private void Close() => _closeAction?.Invoke();
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SaveSettings()
|
||||
private async Task SaveSettings() =>
|
||||
await SaveSettingsCore(logSaved: true);
|
||||
|
||||
private async Task SaveSettingsCore(bool logSaved)
|
||||
{
|
||||
ApplyDaqChannelEditorsToDraft();
|
||||
Devices.Daq.Channels = DaqChannelOptionsNormalizer.Normalize(Devices.Daq.Channels);
|
||||
ApplyToRuntime(Devices, _runtimeDevices);
|
||||
ApplyToRuntime(Storage, _runtimeStorage);
|
||||
ApplyToRuntime(Report, _runtimeReport);
|
||||
await _settingsStore.SaveAsync().ConfigureAwait(false);
|
||||
ApplyProjectsRootToStore();
|
||||
await _settingsStore.SaveAsync();
|
||||
OnPropertyChanged(nameof(HasUnsavedChanges));
|
||||
OnPropertyChanged(nameof(DaqValidationMessage));
|
||||
_log.Add(UiLogLevel.Success, "设置已保存。重新连接请单独点击“重新连接”。");
|
||||
if (logSaved)
|
||||
_log.Add(UiLogLevel.Success, "设置已保存。重新连接请单独点击“重新连接”。");
|
||||
}
|
||||
|
||||
private void ApplyProjectsRootToStore()
|
||||
{
|
||||
_projectStore.ProjectsRoot = _runtimeStorage.ProjectsRoot;
|
||||
if (!string.IsNullOrWhiteSpace(_projectStore.ProjectsRoot))
|
||||
Directory.CreateDirectory(_projectStore.ProjectsRoot);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@ -350,6 +374,42 @@ public partial class SettingsViewModel : ObservableObject
|
||||
[RelayCommand]
|
||||
private void OpenProjectsDirectory() => OpenDirectory(Storage.ProjectsRoot);
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RefreshProjects()
|
||||
{
|
||||
NotifyDraftChanged();
|
||||
if (string.IsNullOrWhiteSpace(Storage.ProjectsRoot))
|
||||
{
|
||||
_log.Add(UiLogLevel.Error, "项目数据路径为空,无法刷新工程。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_canRefreshProjects?.Invoke() == false)
|
||||
return;
|
||||
|
||||
if (_projectRefreshAction == null)
|
||||
{
|
||||
_log.Add(UiLogLevel.Warning, "刷新工程功能尚未初始化。");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await SaveSettingsCore(logSaved: false);
|
||||
await _projectRefreshAction(_projectStore.ProjectsRoot);
|
||||
await RunOnUiAsync(() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(HasUnsavedChanges));
|
||||
OnPropertyChanged(nameof(DaqValidationMessage));
|
||||
});
|
||||
_log.Add(UiLogLevel.Success, "工程列表已刷新。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Add(UiLogLevel.Error, $"刷新工程失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenReportsDirectory() => OpenDirectory(Report.SavePath);
|
||||
|
||||
|
||||
@ -37,8 +37,48 @@
|
||||
FontSize="{StaticResource FsTitle}" FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Grid.Column="2" VerticalAlignment="Center">
|
||||
<Viewbox Width="26" Height="26" Margin="0,0,6,0"
|
||||
ToolTip="{Binding SafetyDetailText}">
|
||||
<Grid Width="48" Height="48" Margin="0,0,6,0"
|
||||
ToolTip="{Binding SafetyDetailText}">
|
||||
<Ellipse x:Name="AlarmHalo" IsHitTestVisible="False"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Ellipse.Fill>
|
||||
<RadialGradientBrush Center="0.5,0.5" GradientOrigin="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
|
||||
<GradientStop Color="#66FF001A" Offset="0.0" />
|
||||
<GradientStop Color="#99FF1030" Offset="0.30" />
|
||||
<GradientStop Color="#44FF0018" Offset="0.60" />
|
||||
<GradientStop Color="#00FF0018" Offset="1.0" />
|
||||
</RadialGradientBrush>
|
||||
</Ellipse.Fill>
|
||||
<Ellipse.RenderTransform>
|
||||
<ScaleTransform ScaleX="1" ScaleY="1" />
|
||||
</Ellipse.RenderTransform>
|
||||
<Ellipse.Style>
|
||||
<Style TargetType="Ellipse">
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="AlarmHaloPulse">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
||||
From="1.0" To="0.35" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
|
||||
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
|
||||
From="0.9" To="1.12" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
|
||||
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
|
||||
From="0.9" To="1.12" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<StopStoryboard BeginStoryboardName="AlarmHaloPulse" />
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Ellipse.Style>
|
||||
</Ellipse>
|
||||
<Viewbox Width="36" Height="36" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Canvas Width="64" Height="64">
|
||||
<Canvas.Resources>
|
||||
<RadialGradientBrush x:Key="AlarmRedGlassBrush" Center="0.34,0.16" GradientOrigin="0.34,0.16" RadiusX="0.78" RadiusY="0.78">
|
||||
@ -106,33 +146,38 @@
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
<Path x:Name="AlarmSweep" Data="M17 6h8l-11 42h-8z"
|
||||
Fill="White">
|
||||
<Path.RenderTransform>
|
||||
<TranslateTransform />
|
||||
</Path.RenderTransform>
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
|
||||
<Setter Property="Opacity" Value=".65" />
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="AlarmSweepMove">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
|
||||
From="-24" To="30" Duration="0:0:0.95" RepeatBehavior="Forever" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<StopStoryboard BeginStoryboardName="AlarmSweepMove" />
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
<Canvas Width="64" Height="64" IsHitTestVisible="False">
|
||||
<Canvas.Clip>
|
||||
<PathGeometry Figures="M14 44V23C14 10.5 22.1 4 32 4s18 6.5 18 19v21z" />
|
||||
</Canvas.Clip>
|
||||
<Path x:Name="AlarmSweep" Data="M17 6h8l-11 42h-8z"
|
||||
Fill="White">
|
||||
<Path.RenderTransform>
|
||||
<TranslateTransform />
|
||||
</Path.RenderTransform>
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
|
||||
<Setter Property="Opacity" Value=".65" />
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="AlarmSweepMove">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
|
||||
From="-24" To="30" Duration="0:0:0.95" RepeatBehavior="Forever" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<StopStoryboard BeginStoryboardName="AlarmSweepMove" />
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Canvas>
|
||||
<Path Data="M25 13l7-3 3 6-8 3zM38 11c4 1 6 3 8 7l-5 2c-1-3-3-4-6-5z"
|
||||
Fill="White" Opacity=".72" />
|
||||
<Ellipse Canvas.Left="10" Canvas.Top="39" Width="44" Height="10" Fill="#771A0000" />
|
||||
@ -167,19 +212,53 @@
|
||||
<Path Data="M42 56v4M47 55v4M52 54v3" Stroke="#050505" StrokeThickness="2" StrokeStartLineCap="Round" StrokeEndLineCap="Round" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
<Button Margin="0,0,8,0" Padding="12,4"
|
||||
Command="{Binding ToggleEmergencyAlarmCommand}"
|
||||
IsEnabled="{Binding EmergencyOperationBusy, Converter={StaticResource InverseBoolConverter}}"
|
||||
ToolTip="{Binding SafetyDetailText}">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DangerButton}">
|
||||
<Style TargetType="Button" BasedOn="{StaticResource BaseButton}">
|
||||
<Setter Property="Content" Value="{Binding EmergencyButtonText}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding AlarmLampActive}" Value="False">
|
||||
<Setter Property="Background" Value="{StaticResource WarningBrush}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Background" Value="{StaticResource DangerBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource DangerBrush}" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextInverseBrush}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="EmergencyButtonChrome"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{StaticResource RadiusMd}"
|
||||
SnapsToDevicePixels="True">
|
||||
<TextBlock x:Name="EmergencyButtonText"
|
||||
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
FontFamily="{Binding FontFamily, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
FontSize="{Binding FontSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
FontWeight="{Binding FontWeight, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{TemplateBinding Padding}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="EmergencyButtonChrome" Property="Background" Value="#EAF3FC" />
|
||||
<Setter TargetName="EmergencyButtonChrome" Property="BorderBrush" Value="{StaticResource DangerBrush}" />
|
||||
<Setter Property="Foreground" Value="#000000" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="EmergencyButtonChrome" Property="Opacity" Value="0.9" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
@ -273,18 +352,6 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Button DockPanel.Dock="Bottom" Margin="12,8" Height="30" HorizontalContentAlignment="Left"
|
||||
Padding="10,0" Command="{Binding OpenProjectsFolderCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox Width="14" Height="14" Margin="0,0,6,0">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Data="{StaticResource FolderGeometry}" Fill="{StaticResource TextSecondaryBrush}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
<TextBlock Text="打开项目文件夹" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<TreeView x:Name="ProjectTree" ItemsSource="{Binding TreeRoots}"
|
||||
MouseDoubleClick="ProjectTree_MouseDoubleClick" Padding="6,6">
|
||||
<TreeView.Resources>
|
||||
|
||||
@ -277,12 +277,12 @@
|
||||
<ColumnDefinition Width="70" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="70" />
|
||||
<ColumnDefinition Width="80" />
|
||||
<ColumnDefinition Width="92" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="根目录" Style="{StaticResource FieldLabel}" />
|
||||
<TextBox Grid.Column="1" Text="{Binding Storage.ProjectsRoot, UpdateSourceTrigger=PropertyChanged}" TextChanged="DraftChanged" FontFamily="{StaticResource FontMono}" />
|
||||
<Button Grid.Column="2" Margin="8,0,0,0" Content="浏览" Command="{Binding BrowseProjectsCommand}" />
|
||||
<Button Grid.Column="3" Margin="8,0,0,0" Content="打开" Command="{Binding OpenProjectsDirectoryCommand}" />
|
||||
<Button Grid.Column="2" Margin="8,0,0,0" Content="设置" Command="{Binding BrowseProjectsCommand}" />
|
||||
<Button Grid.Column="3" Margin="8,0,0,0" Content="刷新工程" Command="{Binding RefreshProjectsCommand}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user