522 lines
18 KiB
C#
522 lines
18 KiB
C#
using Controls;
|
|||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.ComponentModel;
|
||
|
|
using System.Drawing;
|
||
|
|
using System.Drawing.Drawing2D;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
using System.Windows.Forms;
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
namespace JinYuan.ControlLib
|
||
|
|
{
|
||
|
|
#region 枚举定义
|
||
|
|
public enum StatusEnum
|
||
|
|
{
|
||
|
|
Init, //初始化状态 - 空白,无填充
|
||
|
|
GradeNG, // 等级查询OK状态 - 白底:黑色系渐变; 黑底:黄色系渐变?
|
||
|
|
PackNG, // 上传NG状态 - 红色系渐变
|
||
|
|
Normal // 正常状态 - 绿色系渐变
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
public partial class StatusExt : UserControl
|
||
|
|
{
|
||
|
|
// 公开属性,电芯条码用于绑定唯一标识
|
||
|
|
public string ItemId { get; set; }
|
||
|
|
// 电芯位置
|
||
|
|
public string RowId { get; set; }
|
||
|
|
|
||
|
|
|
||
|
|
#region 私有字段
|
||
|
|
private StatusEnum _currentStatus = StatusEnum.Init;
|
||
|
|
private Color _borderColor = Color.DarkGray;
|
||
|
|
private int _borderWidth = 1;
|
||
|
|
private bool _showHighlight = false;
|
||
|
|
private const int padding = 2; // 外部留白
|
||
|
|
private static readonly Color _backBlackColor = Color.FromArgb(11, 20, 36);
|
||
|
|
//private static readonly Color _backWhiteColor = Color.FromArgb(255, 255, 255);
|
||
|
|
//private Color _backColor = _backBlackColor;
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region 颜色配置
|
||
|
|
private readonly Color _whiteEdge = Color.FromArgb(200, 200, 200); // 白色边缘
|
||
|
|
private readonly Color _greenEdge = Color.FromArgb(50, 150, 50); // 绿色边缘
|
||
|
|
private readonly Color _greenMid = Color.FromArgb(140, 180, 130); // 绿色过渡
|
||
|
|
private readonly Color _grayEdge = Color.FromArgb(0, 0, 0); // 黑色边缘
|
||
|
|
private readonly Color _grayMid = Color.FromArgb(135, 135, 135); // 灰色过渡
|
||
|
|
private readonly Color _redEdge = Color.FromArgb(150, 60, 60); // 红色边缘
|
||
|
|
private readonly Color _redMid = Color.FromArgb(170, 140, 130); // 红色过渡
|
||
|
|
private readonly Color _center = Color.FromArgb(245, 245, 245); // 中心色(稍暗避免过曝)
|
||
|
|
private readonly Color _highlightStart = Color.FromArgb(120, Color.White); // 高光起始色
|
||
|
|
private readonly Color _highlightEnd = Color.FromArgb(0, Color.White); // 高光结束色
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
|
||
|
|
private bool _isClicking = false;
|
||
|
|
private DateTime _lastClickTime;
|
||
|
|
|
||
|
|
// 缓存渐变画刷,避免重复创建
|
||
|
|
private GraphicsPath _cachedPath;
|
||
|
|
private Rectangle _lastDrawRect;
|
||
|
|
private bool _clickHandled = false;
|
||
|
|
|
||
|
|
#region 构造函数
|
||
|
|
public StatusExt()
|
||
|
|
{
|
||
|
|
this.Text = string.Empty; // 清空文本
|
||
|
|
//SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
|
||
|
|
SetStyle(ControlStyles.AllPaintingInWmPaint |
|
||
|
|
ControlStyles.UserPaint |
|
||
|
|
ControlStyles.ResizeRedraw |
|
||
|
|
ControlStyles.OptimizedDoubleBuffer |
|
||
|
|
//ControlStyles.Opaque | // 避免背景重绘干扰
|
||
|
|
ControlStyles.SupportsTransparentBackColor, true);
|
||
|
|
this.DoubleBuffered = true;
|
||
|
|
this.Size = new Size(40, 40); // 确保正方形
|
||
|
|
this.MinimumSize = new Size(20, 20);
|
||
|
|
this.MaximumSize = new Size(60, 60); // 设置最大尺寸
|
||
|
|
this.BackColor = _backBlackColor;
|
||
|
|
}
|
||
|
|
|
||
|
|
//private void InitializeBrushCache()
|
||
|
|
//{
|
||
|
|
// _brushCache[StatusEnum.Init] = new SolidBrush(Color.Transparent);
|
||
|
|
// _brushCache[StatusEnum.Normal] = new LinearGradientBrush(
|
||
|
|
// new Rectangle(0, 0, 100, 100),
|
||
|
|
// Color.FromArgb(50, 150, 50),
|
||
|
|
// Color.FromArgb(140, 180, 130),
|
||
|
|
// 45f);
|
||
|
|
// _brushCache[StatusEnum.GradeNG] = new LinearGradientBrush(
|
||
|
|
// new Rectangle(0, 0, 100, 100),
|
||
|
|
// Color.FromArgb(0, 0, 0),
|
||
|
|
// Color.FromArgb(135, 135, 135),
|
||
|
|
// 45f);
|
||
|
|
// _brushCache[StatusEnum.PackNG] = new LinearGradientBrush(
|
||
|
|
// new Rectangle(0, 0, 100, 100),
|
||
|
|
// Color.FromArgb(150, 60, 60),
|
||
|
|
// Color.FromArgb(170, 140, 130),
|
||
|
|
// 45f);
|
||
|
|
//}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region 处理外部数据的方法
|
||
|
|
/// <summary>
|
||
|
|
/// 绑定数据,设置 UI 内容
|
||
|
|
/// </summary>
|
||
|
|
public void BindData(string id, string rowId, string title, StatusEnum status)
|
||
|
|
{
|
||
|
|
this.ItemId = id;
|
||
|
|
this.RowId = rowId;
|
||
|
|
this.CurrentStatus = status;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void SetStatus(StatusEnum status)
|
||
|
|
{
|
||
|
|
if (_currentStatus != status)
|
||
|
|
{
|
||
|
|
_currentStatus = status;
|
||
|
|
Invalidate(); // 状态变化时重绘
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region 属性
|
||
|
|
[Category("工业风"), Description("当前状态")]
|
||
|
|
public StatusEnum CurrentStatus
|
||
|
|
{
|
||
|
|
get => _currentStatus;
|
||
|
|
set
|
||
|
|
{
|
||
|
|
if (_currentStatus != value)
|
||
|
|
{
|
||
|
|
_currentStatus = value;
|
||
|
|
Invalidate(); // 只有变化时才重绘
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[Category("工业风"), Description("边框宽度")]
|
||
|
|
public int BorderWidth
|
||
|
|
{
|
||
|
|
get => _borderWidth;
|
||
|
|
set
|
||
|
|
{
|
||
|
|
_borderWidth = value;
|
||
|
|
Invalidate();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[Category("工业风"), Description("边框颜色")]
|
||
|
|
public Color BorderColor
|
||
|
|
{
|
||
|
|
get { return _borderColor; }
|
||
|
|
set
|
||
|
|
{
|
||
|
|
_borderColor = value;
|
||
|
|
Invalidate();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[Category("工业风"), Description("是否显示高光效果")]
|
||
|
|
public bool ShowHighlight
|
||
|
|
{
|
||
|
|
get { return _showHighlight; }
|
||
|
|
set
|
||
|
|
{
|
||
|
|
_showHighlight = value;
|
||
|
|
Invalidate();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region 重写方法
|
||
|
|
|
||
|
|
protected override void OnPaint(PaintEventArgs e)
|
||
|
|
{
|
||
|
|
base.OnPaint(e);
|
||
|
|
|
||
|
|
var g = e.Graphics;
|
||
|
|
g.SmoothingMode = SmoothingMode.AntiAlias; // 抗锯齿,圆形更平滑
|
||
|
|
g.PixelOffsetMode = PixelOffsetMode.HighQuality; // 减少模糊
|
||
|
|
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||
|
|
//g.SmoothingMode = SmoothingMode.HighSpeed; // 改用高速渲染
|
||
|
|
//g.CompositingQuality = CompositingQuality.HighSpeed;
|
||
|
|
|
||
|
|
|
||
|
|
// === 关键:有效直径要扣除两边的边框宽度 ===
|
||
|
|
int availableSize = Math.Min(Width, Height);
|
||
|
|
int drawDiameter = availableSize - _borderWidth - padding * 2; // 确保左右各留出 1~2 像素给边框
|
||
|
|
if (drawDiameter <= 0) drawDiameter = 1; // 防止负数
|
||
|
|
|
||
|
|
// 居中定位
|
||
|
|
int x = (Width - drawDiameter) / 2;
|
||
|
|
int y = (Height - drawDiameter) / 2;
|
||
|
|
var drawRect = new Rectangle(x, y, drawDiameter, drawDiameter);
|
||
|
|
|
||
|
|
//// 绘制背景圆形
|
||
|
|
//DrawBackgroundCircle(g, drawRect);
|
||
|
|
|
||
|
|
//// 绘制边框
|
||
|
|
//DrawBorder(g, drawRect);
|
||
|
|
|
||
|
|
// 只在尺寸变化时重建路径
|
||
|
|
if (_cachedPath == null || _lastDrawRect != drawRect)
|
||
|
|
{
|
||
|
|
_cachedPath?.Dispose();
|
||
|
|
_cachedPath = new GraphicsPath();
|
||
|
|
_cachedPath.AddEllipse(drawRect);
|
||
|
|
_lastDrawRect = drawRect;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 根据状态绘制渐变背景
|
||
|
|
DrawStatusBackground(g, drawRect);
|
||
|
|
// 绘制高光效果
|
||
|
|
if (_showHighlight && _currentStatus != StatusEnum.Init)
|
||
|
|
{
|
||
|
|
DrawHighlightEffect(g, drawRect);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 绘制边框
|
||
|
|
DrawBorder(g, drawRect);
|
||
|
|
|
||
|
|
//// 使用缓存画刷
|
||
|
|
//if (_brushCache.TryGetValue(_currentStatus, out var brush))
|
||
|
|
//{
|
||
|
|
// g.FillPath(brush, _cachedPath);
|
||
|
|
//}
|
||
|
|
|
||
|
|
//// 绘制边框
|
||
|
|
//using (Pen borderPen = new Pen(GetBorderColor(), _borderWidth))
|
||
|
|
//{
|
||
|
|
// borderPen.Alignment = PenAlignment.Center;
|
||
|
|
// g.DrawEllipse(borderPen, drawRect);
|
||
|
|
//}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 确保控件保持正方形
|
||
|
|
protected override void OnResize(EventArgs e)
|
||
|
|
{
|
||
|
|
base.OnResize(e);
|
||
|
|
// 强制保持正方形
|
||
|
|
int size = Math.Min(Width, Height);
|
||
|
|
this.Size = new Size(size, size);
|
||
|
|
Invalidate();
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void OnClick(EventArgs e)
|
||
|
|
{
|
||
|
|
// 防重复触发
|
||
|
|
if (_isClicking || (DateTime.Now - _lastClickTime).TotalMilliseconds < 300)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
_isClicking = true;
|
||
|
|
_lastClickTime = DateTime.Now;
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
StatusExtClicked?.Invoke(this, new StatusItemClickEventArgs(this.ItemId, this.CurrentStatus, this.RowId, this.Name));
|
||
|
|
base.OnClick(e);
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
// 使用异步延迟重置,避免阻塞UI
|
||
|
|
Task.Delay(300).ContinueWith(_ =>
|
||
|
|
{
|
||
|
|
if (!IsDisposed)
|
||
|
|
{
|
||
|
|
SafeInvoke(() => _isClicking = false);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region 私有绘制方法
|
||
|
|
//private void DrawBackgroundCircle(Graphics g, Rectangle rect)
|
||
|
|
//{
|
||
|
|
// var (edgeColor, midColor) = GetStatusColor();
|
||
|
|
|
||
|
|
// using (GraphicsPath path = CreateCirclePath(rect))
|
||
|
|
// {
|
||
|
|
// using (PathGradientBrush brush = new PathGradientBrush(path))
|
||
|
|
// {
|
||
|
|
// brush.CenterColor = _center;
|
||
|
|
// brush.SurroundColors = new Color[] { edgeColor };
|
||
|
|
// brush.FocusScales = new PointF(0.1f, 0.1f);
|
||
|
|
// g.FillPath(brush, path);
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
//}
|
||
|
|
|
||
|
|
//// 绘制高光效果
|
||
|
|
//private void DrawHighlightEffect(Graphics g, Rectangle rect)
|
||
|
|
//{
|
||
|
|
// // 高光区域(右上角的椭圆)
|
||
|
|
// int highlightSize = rect.Width / 3;
|
||
|
|
// Rectangle highlightRect = new Rectangle(
|
||
|
|
// rect.X + rect.Width / 6,
|
||
|
|
// rect.Y + rect.Height / 6,
|
||
|
|
// highlightSize,
|
||
|
|
// highlightSize
|
||
|
|
// );
|
||
|
|
|
||
|
|
// // 创建半透明高光渐变
|
||
|
|
// using (LinearGradientBrush highlightBrush = new LinearGradientBrush(
|
||
|
|
// highlightRect,
|
||
|
|
// Color.FromArgb(120, Color.White),
|
||
|
|
// Color.FromArgb(0, Color.White),
|
||
|
|
// 45f))
|
||
|
|
// {
|
||
|
|
// g.FillEllipse(highlightBrush, highlightRect);
|
||
|
|
// }
|
||
|
|
|
||
|
|
// // 中心小圆高光(增强中心亮度)
|
||
|
|
// int centerHighlightSize = rect.Width / 5;
|
||
|
|
// Rectangle centerRect = new Rectangle(
|
||
|
|
// rect.X + (rect.Width - centerHighlightSize) / 2,
|
||
|
|
// rect.Y + (rect.Height - centerHighlightSize) / 2,
|
||
|
|
// centerHighlightSize,
|
||
|
|
// centerHighlightSize
|
||
|
|
// );
|
||
|
|
|
||
|
|
// using (SolidBrush centerBrush = new SolidBrush(Color.FromArgb(80, Color.White)))
|
||
|
|
// {
|
||
|
|
// g.FillEllipse(centerBrush, centerRect);
|
||
|
|
// }
|
||
|
|
//}
|
||
|
|
|
||
|
|
//private void DrawBorder(Graphics g, Rectangle rect)
|
||
|
|
//{
|
||
|
|
// var (edgeColor, _) = GetStatusColor();
|
||
|
|
// using (Pen borderPen = new Pen(edgeColor, _borderWidth))
|
||
|
|
// {
|
||
|
|
// borderPen.Alignment = PenAlignment.Center; // 明确居中
|
||
|
|
// g.DrawEllipse(borderPen, rect);
|
||
|
|
// }
|
||
|
|
//}
|
||
|
|
|
||
|
|
private void DrawStatusBackground(Graphics g, Rectangle rect)
|
||
|
|
{
|
||
|
|
var (edgeColor, midColor) = GetStatusColor();
|
||
|
|
|
||
|
|
// 绘制渐变背景
|
||
|
|
using (PathGradientBrush brush = new PathGradientBrush(_cachedPath))
|
||
|
|
{
|
||
|
|
brush.CenterColor = _center;
|
||
|
|
brush.SurroundColors = new Color[] { edgeColor };
|
||
|
|
brush.FocusScales = new PointF(0.1f, 0.1f);
|
||
|
|
g.FillPath(brush, _cachedPath);
|
||
|
|
|
||
|
|
// 添加中间渐变层增强效果
|
||
|
|
if (_currentStatus != StatusEnum.Init)
|
||
|
|
{
|
||
|
|
using (SolidBrush midBrush = new SolidBrush(Color.FromArgb(50, midColor)))
|
||
|
|
{
|
||
|
|
int innerSize = rect.Width / 2;
|
||
|
|
Rectangle innerRect = new Rectangle(
|
||
|
|
rect.X + (rect.Width - innerSize) / 2,
|
||
|
|
rect.Y + (rect.Height - innerSize) / 2,
|
||
|
|
innerSize,
|
||
|
|
innerSize);
|
||
|
|
g.FillEllipse(midBrush, innerRect);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DrawHighlightEffect(Graphics g, Rectangle rect)
|
||
|
|
{
|
||
|
|
// 主高光区域(右上角)
|
||
|
|
int highlightSize = rect.Width / 3;
|
||
|
|
Rectangle highlightRect = new Rectangle(
|
||
|
|
rect.X + rect.Width / 6,
|
||
|
|
rect.Y + rect.Height / 6,
|
||
|
|
highlightSize,
|
||
|
|
highlightSize);
|
||
|
|
|
||
|
|
using (LinearGradientBrush highlightBrush = new LinearGradientBrush(
|
||
|
|
highlightRect,
|
||
|
|
_highlightStart,
|
||
|
|
_highlightEnd,
|
||
|
|
45f))
|
||
|
|
{
|
||
|
|
g.FillEllipse(highlightBrush, highlightRect);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 中心高光点
|
||
|
|
int centerHighlightSize = rect.Width / 5;
|
||
|
|
Rectangle centerRect = new Rectangle(
|
||
|
|
rect.X + (rect.Width - centerHighlightSize) / 2,
|
||
|
|
rect.Y + (rect.Height - centerHighlightSize) / 2,
|
||
|
|
centerHighlightSize,
|
||
|
|
centerHighlightSize);
|
||
|
|
|
||
|
|
using (SolidBrush centerBrush = new SolidBrush(Color.FromArgb(80, Color.White)))
|
||
|
|
{
|
||
|
|
g.FillEllipse(centerBrush, centerRect);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 边缘光泽效果
|
||
|
|
using (Pen glowPen = new Pen(Color.FromArgb(30, Color.White), 1))
|
||
|
|
{
|
||
|
|
g.DrawEllipse(glowPen, rect);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DrawBorder(Graphics g, Rectangle rect)
|
||
|
|
{
|
||
|
|
var (edgeColor, _) = GetStatusColor();
|
||
|
|
|
||
|
|
// 外边框
|
||
|
|
using (Pen outerPen = new Pen(Color.FromArgb(80, edgeColor), _borderWidth + 1))
|
||
|
|
{
|
||
|
|
outerPen.Alignment = PenAlignment.Inset;
|
||
|
|
g.DrawEllipse(outerPen, rect);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 内边框
|
||
|
|
using (Pen innerPen = new Pen(Color.FromArgb(150, edgeColor), _borderWidth))
|
||
|
|
{
|
||
|
|
innerPen.Alignment = PenAlignment.Inset;
|
||
|
|
Rectangle innerRect = new Rectangle(
|
||
|
|
rect.X + 1, rect.Y + 1,
|
||
|
|
rect.Width - 2, rect.Height - 2);
|
||
|
|
g.DrawEllipse(innerPen, innerRect);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private (Color, Color) GetStatusColor()
|
||
|
|
{
|
||
|
|
switch (_currentStatus)
|
||
|
|
{
|
||
|
|
case StatusEnum.PackNG:
|
||
|
|
return (_redEdge, _redMid);
|
||
|
|
case StatusEnum.GradeNG:
|
||
|
|
return (_grayEdge, _grayMid);
|
||
|
|
case StatusEnum.Normal:
|
||
|
|
return (_greenEdge, _greenMid);
|
||
|
|
default: // Init
|
||
|
|
return (_whiteEdge, Color.FromArgb(50, 50, 50));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void SafeInvoke(Action action)
|
||
|
|
{
|
||
|
|
if (IsHandleCreated && !IsDisposed)
|
||
|
|
{
|
||
|
|
if (InvokeRequired)
|
||
|
|
{
|
||
|
|
try { Invoke(action); } catch { }
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
action();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//private Color GetBorderColor()
|
||
|
|
//{
|
||
|
|
// var (edgeColor, midColor) = GetStatusColor();
|
||
|
|
|
||
|
|
// return edgeColor;
|
||
|
|
//}
|
||
|
|
|
||
|
|
//private GraphicsPath CreateCirclePath(Rectangle rect)
|
||
|
|
//{
|
||
|
|
// GraphicsPath path = new GraphicsPath();
|
||
|
|
// path.AddEllipse(rect);
|
||
|
|
// return path;
|
||
|
|
//}
|
||
|
|
|
||
|
|
|
||
|
|
//private (Color, Color) GetStatusColor()
|
||
|
|
//{
|
||
|
|
// switch (_currentStatus)
|
||
|
|
// {
|
||
|
|
// case StatusEnum.PackNG:
|
||
|
|
// return (_redEdge, _redMid);
|
||
|
|
// case StatusEnum.GradeNG:
|
||
|
|
// return (_grayEdge, _grayMid);
|
||
|
|
// case StatusEnum.Normal:
|
||
|
|
// return (_greenEdge, _greenMid);
|
||
|
|
// default:
|
||
|
|
// return (_whiteEdge, _whiteEdge);
|
||
|
|
// }
|
||
|
|
//}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region 事件
|
||
|
|
public event EventHandler<StatusItemClickEventArgs> StatusExtClicked;
|
||
|
|
|
||
|
|
// 自定义事件参数,用于传递点击的 ID 和状态
|
||
|
|
public class StatusItemClickEventArgs : EventArgs
|
||
|
|
{
|
||
|
|
public string ItemId { get; }
|
||
|
|
|
||
|
|
public string RowId { get; }
|
||
|
|
|
||
|
|
public string Name { get; }
|
||
|
|
public StatusEnum Status { get; }
|
||
|
|
|
||
|
|
public StatusItemClickEventArgs(string itemId, StatusEnum status, string rowId, string name)
|
||
|
|
{
|
||
|
|
ItemId = itemId;
|
||
|
|
Status = status;
|
||
|
|
RowId = rowId;
|
||
|
|
Name = Name;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|