2020-03-26 10:45:35 +08:00
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
|
using System;
|
2020-06-27 01:20:52 +08:00
|
|
|
|
using System.Collections.Generic;
|
2020-04-24 18:32:50 +08:00
|
|
|
|
using System.Globalization;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
using System.Linq;
|
2020-06-27 01:20:52 +08:00
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
2020-05-29 00:33:49 +08:00
|
|
|
|
namespace AntDesign
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public partial class InputNumber<TValue> : AntInputComponentBase<TValue>
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
|
|
|
|
private string _format;
|
|
|
|
|
protected const string PrefixCls = "ant-input-number";
|
|
|
|
|
|
|
|
|
|
[Parameter]
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public Func<TValue, string> Formatter { get; set; }
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
|
|
|
|
[Parameter]
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public Func<string, string> Parser { get; set; }
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
private TValue _step;
|
2020-04-24 18:32:50 +08:00
|
|
|
|
|
2020-03-26 10:45:35 +08:00
|
|
|
|
[Parameter]
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public TValue Step
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _step;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_step = value;
|
2020-06-27 01:20:52 +08:00
|
|
|
|
var stepStr = _step.ToString();
|
2020-03-26 10:45:35 +08:00
|
|
|
|
if (string.IsNullOrEmpty(_format))
|
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
_format = string.Join('.', stepStr.Split('.').Select(n => new string('0', n.Length)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (stepStr.IndexOf('.') > 0)
|
|
|
|
|
_decimalPlaces = stepStr.Length - stepStr.IndexOf('.') - 1;
|
|
|
|
|
else
|
|
|
|
|
_decimalPlaces = 0;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
private int? _decimalPlaces;
|
|
|
|
|
|
2020-03-26 10:45:35 +08:00
|
|
|
|
[Parameter]
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public TValue DefaultValue { get; set; }
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
|
|
|
|
[Parameter]
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public TValue Max { get; set; }
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
|
|
|
|
[Parameter]
|
2020-06-27 01:20:52 +08:00
|
|
|
|
public TValue Min { get; set; }
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
2020-04-23 17:13:56 +08:00
|
|
|
|
[Parameter]
|
|
|
|
|
public bool Disabled { get; set; }
|
2020-03-26 10:45:35 +08:00
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
private readonly bool _isNullable;
|
|
|
|
|
|
|
|
|
|
private readonly Func<TValue, TValue, TValue> _increaseFunc;
|
|
|
|
|
private readonly Func<TValue, TValue, TValue> _decreaseFunc;
|
|
|
|
|
private readonly Func<TValue, TValue, bool> _greaterThanFunc;
|
|
|
|
|
private readonly Func<TValue, string, string> _toStringFunc;
|
|
|
|
|
private readonly Func<TValue, int, TValue> _roundFunc;
|
|
|
|
|
|
|
|
|
|
private static readonly Type _surfaceType = typeof(TValue);
|
|
|
|
|
|
|
|
|
|
private static readonly Dictionary<Type, object> _defaultMaximum = new Dictionary<Type, object>()
|
|
|
|
|
{
|
|
|
|
|
{ typeof(int),int.MaxValue },
|
|
|
|
|
{ typeof(decimal),decimal.MaxValue },
|
|
|
|
|
{ typeof(double),double.PositiveInfinity },
|
|
|
|
|
{ typeof(float),float.PositiveInfinity },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static readonly Dictionary<Type, object> _defaultMinimum = new Dictionary<Type, object>()
|
|
|
|
|
{
|
|
|
|
|
{ typeof(int),int.MinValue },
|
|
|
|
|
{ typeof(decimal),decimal.MinValue },
|
|
|
|
|
{ typeof(double),double.NegativeInfinity },
|
|
|
|
|
{ typeof(float),float.NegativeInfinity},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private string _inputString;
|
|
|
|
|
private bool _focused;
|
|
|
|
|
private ElementReference _inputRef;
|
|
|
|
|
|
|
|
|
|
public InputNumber()
|
|
|
|
|
{
|
|
|
|
|
_isNullable = _surfaceType.IsGenericType && _surfaceType.GetGenericTypeDefinition() == typeof(Nullable<>);
|
|
|
|
|
|
|
|
|
|
//递增与递减
|
|
|
|
|
ParameterExpression piValue = Expression.Parameter(_surfaceType, "value");
|
|
|
|
|
ParameterExpression piStep = Expression.Parameter(_surfaceType, "step");
|
|
|
|
|
var fexpAdd = Expression.Lambda<Func<TValue, TValue, TValue>>(Expression.Add(piValue, piStep), piValue, piStep);
|
|
|
|
|
_increaseFunc = fexpAdd.Compile();
|
|
|
|
|
var fexpSubtract = Expression.Lambda<Func<TValue, TValue, TValue>>(Expression.Subtract(piValue, piStep), piValue, piStep);
|
|
|
|
|
_decreaseFunc = fexpSubtract.Compile();
|
|
|
|
|
|
|
|
|
|
//数字比较
|
|
|
|
|
ParameterExpression piLeft = Expression.Parameter(_surfaceType, "left");
|
|
|
|
|
ParameterExpression piRight = Expression.Parameter(_surfaceType, "right");
|
|
|
|
|
var fexpGreaterThan = Expression.Lambda<Func<TValue, TValue, bool>>(Expression.GreaterThan(piLeft, piRight), piLeft, piRight);
|
|
|
|
|
_greaterThanFunc = fexpGreaterThan.Compile();
|
|
|
|
|
|
|
|
|
|
//格式化
|
|
|
|
|
ParameterExpression format = Expression.Parameter(typeof(string), "format");
|
|
|
|
|
ParameterExpression value = Expression.Parameter(_surfaceType, "value");
|
|
|
|
|
Expression expValue;
|
|
|
|
|
if (_isNullable)
|
|
|
|
|
expValue = Expression.Property(value, "Value");
|
|
|
|
|
else
|
|
|
|
|
expValue = value;
|
|
|
|
|
MethodCallExpression expToString = Expression.Call(expValue, expValue.Type.GetMethod("ToString", new Type[] { typeof(string), typeof(IFormatProvider) }), format, Expression.Constant(CultureInfo.InvariantCulture));
|
|
|
|
|
var lambdaToString = Expression.Lambda<Func<TValue, string, string>>(expToString, value, format);
|
|
|
|
|
_toStringFunc = lambdaToString.Compile();
|
|
|
|
|
|
|
|
|
|
//四舍五入
|
|
|
|
|
ParameterExpression num = Expression.Parameter(_surfaceType, "num");
|
|
|
|
|
ParameterExpression decimalPlaces = Expression.Parameter(typeof(int), "decimalPlaces");
|
|
|
|
|
MethodCallExpression expRound = Expression.Call(null, typeof(InputNumberMath).GetMethod("Round", new Type[] { _surfaceType, typeof(int) }), num, decimalPlaces);
|
|
|
|
|
var lambdaRound = Expression.Lambda<Func<TValue, int, TValue>>(expRound, num, decimalPlaces);
|
|
|
|
|
_roundFunc = lambdaRound.Compile();
|
|
|
|
|
|
|
|
|
|
var underlyingType = _isNullable ? Nullable.GetUnderlyingType(_surfaceType) : _surfaceType;
|
|
|
|
|
|
|
|
|
|
if (_defaultMaximum.ContainsKey(underlyingType)) Max = (TValue)_defaultMaximum[underlyingType];
|
|
|
|
|
if (_defaultMinimum.ContainsKey(underlyingType)) Min = (TValue)_defaultMinimum[underlyingType];
|
|
|
|
|
|
|
|
|
|
_step = (TValue)Convert.ChangeType(1, underlyingType);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:45:35 +08:00
|
|
|
|
protected override void OnInitialized()
|
|
|
|
|
{
|
|
|
|
|
base.OnInitialized();
|
2020-06-27 01:20:52 +08:00
|
|
|
|
SetClass();
|
|
|
|
|
CurrentValue = DefaultValue;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetClass()
|
|
|
|
|
{
|
|
|
|
|
ClassMapper.Clear()
|
|
|
|
|
.Add(PrefixCls)
|
2020-05-20 12:57:16 +08:00
|
|
|
|
.If($"{PrefixCls}-lg", () => Size == InputSize.Large)
|
|
|
|
|
.If($"{PrefixCls}-sm", () => Size == InputSize.Small)
|
2020-06-27 01:20:52 +08:00
|
|
|
|
.If($"{PrefixCls}-focused", () => _focused)
|
2020-04-23 17:13:56 +08:00
|
|
|
|
.If($"{PrefixCls}-disabled", () => this.Disabled);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
private async Task Increase()
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
await SetFocus();
|
|
|
|
|
var num = _increaseFunc(Value, _step);
|
|
|
|
|
ChangeValue(num);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
private async Task Decrease()
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
await SetFocus();
|
|
|
|
|
var num = _decreaseFunc(Value, _step);
|
|
|
|
|
ChangeValue(num);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
private async Task SetFocus()
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
_focused = true;
|
|
|
|
|
await JsInvokeAsync(JSInteropConstants.focus, _inputRef);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnInput(ChangeEventArgs args)
|
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
_inputString = args.Value?.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnFocus()
|
|
|
|
|
{
|
|
|
|
|
_focused = true;
|
|
|
|
|
CurrentValue = Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnBlur()
|
|
|
|
|
{
|
|
|
|
|
_focused = false;
|
|
|
|
|
if (_inputString == null)
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
ChangeValue(Value);
|
|
|
|
|
return;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
2020-06-27 01:20:52 +08:00
|
|
|
|
|
|
|
|
|
_inputString = Parser != null ? Parser(_inputString) : Regex.Replace(_inputString, @"[^\+\-\d.\d]", "");
|
|
|
|
|
|
|
|
|
|
ConvertNumber(_inputString);
|
|
|
|
|
_inputString = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ConvertNumber(string inputString)
|
|
|
|
|
{
|
|
|
|
|
if (!Regex.IsMatch(inputString, @"^[+-]?\d*[.]?\d*$"))
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
return;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
if (inputString == "-" || inputString == "+")
|
|
|
|
|
{
|
|
|
|
|
inputString = "0";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TValue num;
|
|
|
|
|
if (!_isNullable)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(inputString))
|
|
|
|
|
{
|
|
|
|
|
num = default;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
num = (TValue)Convert.ChangeType(inputString, _surfaceType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-06-27 01:20:52 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(inputString))
|
|
|
|
|
{
|
|
|
|
|
num = default;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
num = (TValue)Convert.ChangeType(inputString, Nullable.GetUnderlyingType(_surfaceType));
|
|
|
|
|
}
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
2020-06-27 01:20:52 +08:00
|
|
|
|
|
|
|
|
|
ChangeValue(num);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ChangeValue(TValue value)
|
|
|
|
|
{
|
|
|
|
|
if (_greaterThanFunc(value, Max))
|
|
|
|
|
value = Max;
|
|
|
|
|
else if (_greaterThanFunc(Min, value))
|
|
|
|
|
value = Min;
|
|
|
|
|
|
|
|
|
|
CurrentValue = _decimalPlaces.HasValue ? _roundFunc(value, _decimalPlaces.Value) : value;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetIconClass(string direction)
|
|
|
|
|
{
|
2020-04-24 18:32:50 +08:00
|
|
|
|
string cls;
|
2020-03-26 10:45:35 +08:00
|
|
|
|
if (direction == "up")
|
|
|
|
|
{
|
2020-06-29 00:26:11 +08:00
|
|
|
|
cls = $"ant-input-number-handler ant-input-number-handler-up " + (!_greaterThanFunc(Max, Value) ? "ant-input-number-handler-up-disabled" : string.Empty);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-06-29 00:26:11 +08:00
|
|
|
|
cls = $"ant-input-number-handler ant-input-number-handler-down " + (!_greaterThanFunc(Value, Min) ? "ant-input-number-handler-down-disabled" : string.Empty);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cls;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
protected override string FormatValueAsString(TValue value)
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-04-23 17:13:56 +08:00
|
|
|
|
if (Formatter != null)
|
2020-03-26 10:45:35 +08:00
|
|
|
|
{
|
2020-04-23 17:13:56 +08:00
|
|
|
|
return Formatter(Value);
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 01:20:52 +08:00
|
|
|
|
if (EqualityComparer<TValue>.Default.Equals(value, default) == false)
|
|
|
|
|
return _toStringFunc(value, _format);
|
|
|
|
|
else
|
|
|
|
|
return default(TValue)?.ToString();
|
2020-03-26 10:45:35 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-23 17:13:56 +08:00
|
|
|
|
}
|