更新项目配置,新增设备同步模块,优化WebSocket和Swagger配置,增强SCADA系统的免登录接口,支持数据字典项和登录日志的免登录查询与记录。调整Java编译设置,确保更好的开发体验。

This commit is contained in:
geht
2026-04-28 10:23:58 +08:00
parent bbe46dcf2d
commit 142a0bdaba
1013 changed files with 41858 additions and 28 deletions

View File

@@ -0,0 +1,300 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using PropertyMetadata = System.Windows.PropertyMetadata;
namespace YY.Admin.Core.Controls
{
//图标官网
public enum FontAwesomeStyle
{
/// <summary>
/// 常规
/// </summary>
Regular,
/// <summary>
/// 实心的
/// </summary>
Solid,
/// <summary>
/// 品牌Logo
/// </summary>
Brands
}
public class FontAwesomeIcon : TextBlock
{
#region Icon
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(nameof(Icon), typeof(string), typeof(FontAwesomeIcon),
new PropertyMetadata(null, (d, e) => ((FontAwesomeIcon)d).Text = e.NewValue?.ToString()));
public string Icon
{
get => (string)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
#endregion
#region Spin
public static readonly DependencyProperty SpinProperty =
DependencyProperty.Register(nameof(Spin), typeof(bool), typeof(FontAwesomeIcon),
new PropertyMetadata(false, (d, e) => ((FontAwesomeIcon)d).UpdateAnimation()));
public bool Spin
{
get => (bool)GetValue(SpinProperty);
set => SetValue(SpinProperty, value);
}
#endregion
#region SpinSpeed
public static readonly DependencyProperty SpinSpeedProperty =
DependencyProperty.Register(nameof(SpinSpeed), typeof(double), typeof(FontAwesomeIcon),
new PropertyMetadata(1.0, (d, e) =>
{
var fa = (FontAwesomeIcon)d;
if (fa.Spin)
fa.StartSpin();
}));
public double SpinSpeed
{
get => (double)GetValue(SpinSpeedProperty);
set => SetValue(SpinSpeedProperty, value);
}
#endregion
#region Beat
public static readonly DependencyProperty BeatProperty =
DependencyProperty.Register(nameof(Beat), typeof(bool), typeof(FontAwesomeIcon),
new PropertyMetadata(false, (d, e) => ((FontAwesomeIcon)d).UpdateAnimation()));
public bool Beat
{
get => (bool)GetValue(BeatProperty);
set => SetValue(BeatProperty, value);
}
#endregion
#region BeatScale
public static readonly DependencyProperty BeatScaleProperty =
DependencyProperty.Register(nameof(BeatScale), typeof(double), typeof(FontAwesomeIcon),
new PropertyMetadata(1.3));
public double BeatScale
{
get => (double)GetValue(BeatScaleProperty);
set => SetValue(BeatScaleProperty, value);
}
#endregion
#region BeatDuration
public static readonly DependencyProperty BeatDurationProperty =
DependencyProperty.Register(nameof(BeatDuration), typeof(double), typeof(FontAwesomeIcon),
new PropertyMetadata(0.5));
public double BeatDuration
{
get => (double)GetValue(BeatDurationProperty);
set => SetValue(BeatDurationProperty, value);
}
#endregion
#region IconFamily
public static readonly DependencyProperty IconFamilyProperty =
DependencyProperty.Register(nameof(IconFamily), typeof(FontAwesomeStyle), typeof(FontAwesomeIcon),
new PropertyMetadata(FontAwesomeStyle.Regular, OnIconFamilyChanged));
public FontAwesomeStyle IconFamily
{
get => (FontAwesomeStyle)GetValue(IconFamilyProperty);
set => SetValue(IconFamilyProperty, value);
}
private static void OnIconFamilyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fa = (FontAwesomeIcon)d;
fa.UpdateFontFamily();
}
private void UpdateFontFamily()
{
FontFamily = IconFamily switch
{
FontAwesomeStyle.Regular => (FontFamily)Application.Current.Resources["FontAwesomeRegular"],
FontAwesomeStyle.Solid => (FontFamily)Application.Current.Resources["FontAwesomeSolid"],
FontAwesomeStyle.Brands => (FontFamily)Application.Current.Resources["FontAwesomeBrands"],
_ => (FontFamily)Application.Current.Resources["FontAwesomeRegular"]
};
}
#endregion
// Transform components
private RotateTransform? _rotateTransform;
private ScaleTransform? _scaleTransform;
private TransformGroup? _transformGroup;
private bool _initialized = false;
private DoubleAnimation? _currentSpinAnimation;
public FontAwesomeIcon()
{
// 设置文本对齐方式,确保内容居中
TextAlignment = TextAlignment.Center;
VerticalAlignment = VerticalAlignment.Center;
HorizontalAlignment = HorizontalAlignment.Center;
// 设置默认字体家庭
UpdateFontFamily();
// 创建 transforms
_rotateTransform = new RotateTransform();
_scaleTransform = new ScaleTransform(1, 1);
_transformGroup = new TransformGroup();
_transformGroup.Children.Add(_scaleTransform);
_transformGroup.Children.Add(_rotateTransform);
// 使用 RenderTransform 避免布局抖动
RenderTransform = _transformGroup;
// 关键:设置变换原点为内容中心
RenderTransformOrigin = new Point(0.5, 0.5);
// 延迟初始化到 Loaded
Loaded += OnLoadedSafe;
Unloaded += OnUnloadedSafe;
SizeChanged += OnSizeChangedSafe;
}
private void OnLoadedSafe(object? sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() =>
{
if (_initialized) return;
_initialized = true;
UpdateAnimation();
}), DispatcherPriority.Loaded);
}
private void OnSizeChangedSafe(object? sender, SizeChangedEventArgs e)
{
// 确保变换原点始终居中
RenderTransformOrigin = new Point(0.5, 0.5);
}
private void OnUnloadedSafe(object? sender, RoutedEventArgs e)
{
// 停止动画,避免内存泄漏
StopSpin();
StopBeat();
}
private void UpdateAnimation()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (!_initialized)
{
return;
}
if (Spin) StartSpin(); else StopSpin();
if (Beat) StartBeat(); else StopBeat();
}
private void StartSpin()
{
if (_rotateTransform == null) return;
// 重新确认 RenderTransform 已正确赋值
if (RenderTransform != _transformGroup)
{
RenderTransform = _transformGroup;
RenderTransformOrigin = new Point(0.5, 0.5);
}
// 先取消旧动画
StopSpin();
// 方法1使用 By 动画实现无缝旋转(推荐)
var anim = new DoubleAnimation
{
By = 360, // 每次增加360度
Duration = TimeSpan.FromSeconds(Math.Max(0.01, SpinSpeed)),
RepeatBehavior = RepeatBehavior.Forever
};
// 动画在 WPF 内部直接使用 Freezable 缓存,提高性能
anim.Freeze();
_currentSpinAnimation = anim;
_rotateTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
// 方法2使用 IsCumulative 属性(备选方案)
/*
var anim = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(Math.Max(0.01, SpinSpeed))))
{
RepeatBehavior = RepeatBehavior.Forever,
IsCumulative = true // 累积值避免跳回0度
};
*/
}
private void StopSpin()
{
_rotateTransform?.BeginAnimation(RotateTransform.AngleProperty, null);
_currentSpinAnimation = null;
// 重置旋转角度
if (_rotateTransform != null)
_rotateTransform.Angle = 0;
}
private void StartBeat()
{
if (_scaleTransform == null)
{
return;
}
// 重新确认 RenderTransform 已正确赋值
if (RenderTransform != _transformGroup)
{
RenderTransform = _transformGroup;
RenderTransformOrigin = new Point(0.5, 0.5);
}
// 先停止已有缩放动画
_scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, null);
_scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null);
// 使用更平滑的缓动函数
var anim = new DoubleAnimation(1.0, BeatScale, new Duration(TimeSpan.FromSeconds(Math.Max(0.01, BeatDuration))))
{
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever,
EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseInOut }
};
_scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, anim);
_scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, anim);
}
private void StopBeat()
{
if (_scaleTransform != null)
{
_scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, null);
_scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null);
_scaleTransform.ScaleX = 1.0;
_scaleTransform.ScaleY = 1.0;
}
}
}
}

View File

@@ -0,0 +1,255 @@
using System.Windows;
using System.Windows.Controls;
namespace YY.Admin.Core.Controls;
public class GridPanel : Panel
{
// ------------------------------------------------------------
// 间距属性(行列间距)
// ------------------------------------------------------------
public double Gap
{
get => (double)GetValue(GapProperty);
set => SetValue(GapProperty, value);
}
public static readonly DependencyProperty GapProperty =
DependencyProperty.Register(nameof(Gap), typeof(double), typeof(GridPanel),
new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
// ------------------------------------------------------------
// 横向/纵向布局(类似 CSS flow
// ------------------------------------------------------------
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(GridPanel),
new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
// ------------------------------------------------------------
// 测量
// ------------------------------------------------------------
protected override Size MeasureOverride(Size availableSize)
{
return Orientation == Orientation.Horizontal
? MeasureHorizontal(availableSize)
: MeasureVertical(availableSize);
}
// ------------------------------------------------------------
// 布局
// ------------------------------------------------------------
protected override Size ArrangeOverride(Size finalSize)
{
return Orientation == Orientation.Horizontal
? ArrangeHorizontal(finalSize)
: ArrangeVertical(finalSize);
}
// ------------------------------------------------------------
// 横向布局(类似 CSS grid-template-columns
// ------------------------------------------------------------
private Size MeasureHorizontal(Size availableSize)
{
double panelWidth = double.IsInfinity(availableSize.Width)
? MinWidth * 4 + Gap * 3
: availableSize.Width;
// ---- 修复:不能用 MinWidth+Gap 来估算列数,应使用最小子项宽度 ----
double minChildWidth = MinWidth <= 0 ? 1 : MinWidth;
// ---- 修复:列数 = 能放下多少个最小宽度 ----
int columnCount = Math.Max(1, (int)Math.Floor((panelWidth + Gap) / (minChildWidth + Gap)));
int childCount = InternalChildren.Count;
// ---- 修复2如果列数超过子项数裁到子项数实现 auto-fit 行为) ----
if (childCount > 0 && columnCount > childCount)
{
columnCount = childCount;
}
double columnWidth = (panelWidth - Gap * (columnCount - 1)) / columnCount;
double totalHeight = 0;
double rowHeight = 0;
int col = 0;
for (int i = 0; i < childCount; i++)
{
UIElement child = InternalChildren[i];
child.Measure(new Size(columnWidth, double.PositiveInfinity));
rowHeight = Math.Max(rowHeight, child.DesiredSize.Height);
col++;
if (col >= columnCount || i == childCount - 1)
{
// ------------------------------------------------------------
// 最后一行不加Gap
// ------------------------------------------------------------
totalHeight += rowHeight;
if (i != childCount - 1) totalHeight += Gap;
col = 0;
rowHeight = 0;
}
}
return new Size(panelWidth, totalHeight);
}
private Size ArrangeHorizontal(Size finalSize)
{
double panelWidth = finalSize.Width;
// ---- 修复:与 Measure 保持一致 ----
double minChildWidth = MinWidth <= 0 ? 1 : MinWidth;
int columnCount = Math.Max(1, (int)Math.Floor((panelWidth + Gap) / (minChildWidth + Gap)));
int childCount = InternalChildren.Count;
// ---- 修复2裁到子项数以实现 auto-fit ----
if (childCount > 0 && columnCount > childCount)
{
columnCount = childCount;
}
double columnWidth = (panelWidth - Gap * (columnCount - 1)) / columnCount;
double x = 0;
double y = 0;
double rowHeight = 0;
int col = 0;
for (int i = 0; i < childCount; i++)
{
UIElement child = InternalChildren[i];
// 为了保证每列宽度一致,传入 columnWidth 并在 Arrange 时使用 columnWidth
child.Arrange(new Rect(x, y, columnWidth, child.DesiredSize.Height));
rowHeight = Math.Max(rowHeight, child.DesiredSize.Height);
col++;
if (col >= columnCount || i == childCount - 1)
{
x = 0;
// ------------------------------------------------------------
// 最后一行不加Gap
// ------------------------------------------------------------
if (i != childCount - 1) y += rowHeight + Gap;
else y += rowHeight;
col = 0;
rowHeight = 0;
}
else
{
x += columnWidth + Gap;
}
}
return finalSize;
}
// ------------------------------------------------------------
// 纵向布局(类似 CSS grid-template-rows
// ------------------------------------------------------------
private Size MeasureVertical(Size availableSize)
{
double panelHeight = double.IsInfinity(availableSize.Height)
? MinHeight * 4 + Gap * 3
: availableSize.Height;
// ---- 修复:使用最小子项高度,不能用 MinHeight+Gap ----
double minChildHeight = MinHeight <= 0 ? 1 : MinHeight;
// ---- 修复:行数 = 能放下多少个最小高度 ----
int rowCount = Math.Max(1, (int)Math.Floor((panelHeight + Gap) / (minChildHeight + Gap)));
int childCount = InternalChildren.Count;
// ---- 修复2如果行数超过子项数裁到子项数auto-fit ----
if (childCount > 0 && rowCount > childCount)
{
rowCount = childCount;
}
double rowHeight = (panelHeight - Gap * (rowCount - 1)) / rowCount;
double totalWidth = 0;
double columnWidth = 0;
int row = 0;
for (int i = 0; i < childCount; i++)
{
UIElement child = InternalChildren[i];
child.Measure(new Size(double.PositiveInfinity, rowHeight));
columnWidth = Math.Max(columnWidth, child.DesiredSize.Width);
row++;
if (row >= rowCount || i == childCount - 1)
{
// ------------------------------------------------------------
// 最后一列不加Gap
// ------------------------------------------------------------
totalWidth += columnWidth;
if (i != childCount - 1) totalWidth += Gap;
row = 0;
columnWidth = 0;
}
}
return new Size(totalWidth, panelHeight);
}
private Size ArrangeVertical(Size finalSize)
{
double panelHeight = finalSize.Height;
// ---- 修复:与 MeasureVertical 一致 ----
double minChildHeight = MinHeight <= 0 ? 1 : MinHeight;
int rowCount = Math.Max(1, (int)Math.Floor((panelHeight + Gap) / (minChildHeight + Gap)));
int childCount = InternalChildren.Count;
// ---- 修复2裁到子项数 ----
if (childCount > 0 && rowCount > childCount)
{
rowCount = childCount;
}
double rowHeight = (panelHeight - Gap * (rowCount - 1)) / rowCount;
double x = 0;
double y = 0;
double columnWidth = 0;
int row = 0;
for (int i = 0; i < childCount; i++)
{
UIElement child = InternalChildren[i];
child.Arrange(new Rect(x, y, child.DesiredSize.Width, rowHeight));
columnWidth = Math.Max(columnWidth, child.DesiredSize.Width);
row++;
if (row >= rowCount || i == childCount - 1)
{
// ------------------------------------------------------------
// 最后一列不加Gap
// ------------------------------------------------------------
x += columnWidth;
if (i != childCount - 1) x += Gap;
y = 0;
row = 0;
columnWidth = 0;
}
else
{
y += rowHeight + Gap;
}
}
return finalSize;
}
}