更新项目配置,新增设备同步模块,优化WebSocket和Swagger配置,增强SCADA系统的免登录接口,支持数据字典项和登录日志的免登录查询与记录。调整Java编译设置,确保更好的开发体验。
This commit is contained in:
300
yy-admin-master/YY.Admin.Core/Controls/FontAwesomeIcon.cs
Normal file
300
yy-admin-master/YY.Admin.Core/Controls/FontAwesomeIcon.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
255
yy-admin-master/YY.Admin.Core/Controls/GridPanel.cs
Normal file
255
yy-admin-master/YY.Admin.Core/Controls/GridPanel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user