2020-06-02 13:34:26 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
|
using System.Linq;
|
2020-06-07 00:47:18 +08:00
|
|
|
|
using System.Collections;
|
2021-03-12 17:02:11 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2020-06-02 13:34:26 +08:00
|
|
|
|
|
|
|
|
|
namespace AntDesign
|
|
|
|
|
{
|
|
|
|
|
public partial class Cascader : AntInputComponentBase<string>
|
|
|
|
|
{
|
|
|
|
|
[Parameter] public bool Readonly { get; set; } = true;
|
|
|
|
|
|
|
|
|
|
[Parameter] public bool AllowClear { get; set; } = true;
|
|
|
|
|
|
|
|
|
|
private bool ShowClearIcon { get; set; }
|
|
|
|
|
|
|
|
|
|
[Parameter] public bool ChangeOnSelect { get; set; }
|
|
|
|
|
|
|
|
|
|
[Parameter] public string DefaultValue { get; set; }
|
|
|
|
|
|
|
|
|
|
[Parameter] public string ExpandTrigger { get; set; }
|
|
|
|
|
|
|
|
|
|
[Parameter] public string NotFoundContent { get; set; } = "Not Found";
|
|
|
|
|
|
2021-04-28 22:42:29 +08:00
|
|
|
|
[Parameter] public string PlaceHolder { get; set; } = LocaleProvider.CurrentLocale.Global.Placeholder;
|
2020-06-02 13:34:26 +08:00
|
|
|
|
|
2021-02-07 18:13:27 +08:00
|
|
|
|
[Parameter] public string PopupContainerSelector { get; set; } = "body";
|
|
|
|
|
|
2020-06-02 13:34:26 +08:00
|
|
|
|
[Parameter] public bool ShowSearch { get; set; }
|
|
|
|
|
|
2021-04-29 14:31:03 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Please use SelectedNodesChanged instead.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Obsolete("Instead use SelectedNodesChanged.")]
|
2020-06-02 13:34:26 +08:00
|
|
|
|
[Parameter] public Action<List<CascaderNode>, string, string> OnChange { get; set; }
|
|
|
|
|
|
2021-04-29 14:31:03 +08:00
|
|
|
|
[Parameter] public EventCallback<CascaderNode[]> SelectedNodesChanged { get; set; }
|
|
|
|
|
|
2020-06-02 13:34:26 +08:00
|
|
|
|
[Parameter]
|
|
|
|
|
public IReadOnlyCollection<CascaderNode> Options
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_nodelist != null)
|
|
|
|
|
return _nodelist;
|
|
|
|
|
return Array.Empty<CascaderNode>();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value == null || value.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
_nodelist = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (_nodelist == null) _nodelist = new List<CascaderNode>();
|
|
|
|
|
else if (_nodelist.Count != 0) _nodelist.Clear();
|
|
|
|
|
_nodelist.AddRange(value);
|
2021-01-16 21:54:57 +08:00
|
|
|
|
|
2021-03-23 12:59:20 +08:00
|
|
|
|
ProcessParentAndDefault();
|
2020-06-02 13:34:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<CascaderNode> _nodelist;
|
|
|
|
|
|
|
|
|
|
internal List<CascaderNode> _selectedNodes = new List<CascaderNode>();
|
|
|
|
|
|
|
|
|
|
internal List<CascaderNode> _hoverSelectedNodes = new List<CascaderNode>();
|
|
|
|
|
|
|
|
|
|
internal List<CascaderNode> _renderNodes = new List<CascaderNode>();
|
|
|
|
|
|
2021-03-12 17:02:11 +08:00
|
|
|
|
private ClassMapper _menuClassMapper = new ClassMapper();
|
|
|
|
|
private ClassMapper _inputClassMapper = new ClassMapper();
|
|
|
|
|
|
2020-06-02 13:34:26 +08:00
|
|
|
|
private bool ToggleState { get; set; }
|
|
|
|
|
|
|
|
|
|
private bool IsOnCascader { get; set; }
|
|
|
|
|
|
|
|
|
|
private SelectedTypeEnum SelectedType { get; set; }
|
|
|
|
|
|
|
|
|
|
private string _displayText;
|
2021-04-29 14:31:03 +08:00
|
|
|
|
private bool _initialized;
|
2020-06-02 13:34:26 +08:00
|
|
|
|
|
2021-03-12 17:02:11 +08:00
|
|
|
|
private static Dictionary<string, string> _sizeMap = new Dictionary<string, string>()
|
2020-06-02 13:34:26 +08:00
|
|
|
|
{
|
2021-03-23 12:59:20 +08:00
|
|
|
|
["large"] = "lg",
|
|
|
|
|
["small"] = "sm"
|
|
|
|
|
};
|
2020-06-07 00:47:18 +08:00
|
|
|
|
|
2021-03-23 12:59:20 +08:00
|
|
|
|
protected override void OnInitialized()
|
2020-06-07 00:47:18 +08:00
|
|
|
|
{
|
2021-03-23 12:59:20 +08:00
|
|
|
|
base.OnInitialized();
|
2020-06-07 00:47:18 +08:00
|
|
|
|
|
2021-03-12 17:02:11 +08:00
|
|
|
|
ClassMapper
|
|
|
|
|
.Add("ant-cascader-picker")
|
2021-03-23 12:59:20 +08:00
|
|
|
|
.GetIf(() => $"ant-cascader-picker-{Size}", () => _sizeMap.ContainsKey(Size))
|
2021-03-12 17:02:11 +08:00
|
|
|
|
.If("ant-cascader-picker-rtl", () => RTL);
|
2021-01-16 21:54:57 +08:00
|
|
|
|
|
2021-03-23 12:59:20 +08:00
|
|
|
|
_inputClassMapper
|
|
|
|
|
.Add("ant-input")
|
|
|
|
|
.Add("ant-cascader-input")
|
2021-04-28 22:42:29 +08:00
|
|
|
|
.GetIf(() => $"ant-input-{_sizeMap[Size]}", () => _sizeMap.ContainsKey(Size))
|
2021-03-12 17:02:11 +08:00
|
|
|
|
.If("ant-cascader-input-rtl", () => RTL);
|
|
|
|
|
|
|
|
|
|
_menuClassMapper
|
|
|
|
|
.Add("ant-cascader-menu")
|
|
|
|
|
.If($"ant-cascader-menu-rtl", () => RTL);
|
|
|
|
|
|
2021-04-29 14:31:03 +08:00
|
|
|
|
SetDefaultValue(Value ?? DefaultValue);
|
2020-06-02 13:34:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-11 23:03:13 +08:00
|
|
|
|
protected override void OnValueChange(string value)
|
|
|
|
|
{
|
|
|
|
|
base.OnValueChange(value);
|
|
|
|
|
|
|
|
|
|
RefreshNodeValue(value);
|
|
|
|
|
}
|
2021-01-16 21:54:57 +08:00
|
|
|
|
|
2020-06-02 13:34:26 +08:00
|
|
|
|
#region event
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 输入框单击(显示/隐藏浮层)
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void InputOnToggle()
|
|
|
|
|
{
|
|
|
|
|
SelectedType = SelectedTypeEnum.Click;
|
|
|
|
|
_hoverSelectedNodes.Clear();
|
|
|
|
|
ToggleState = !ToggleState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 输入框/浮层失去焦点(隐藏浮层)
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void CascaderOnBlur()
|
|
|
|
|
{
|
|
|
|
|
if (!IsOnCascader)
|
|
|
|
|
{
|
|
|
|
|
ToggleState = false;
|
|
|
|
|
_renderNodes = _selectedNodes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 输入框鼠标移入
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void InputOnMouseOver()
|
|
|
|
|
{
|
|
|
|
|
if (!AllowClear) return;
|
|
|
|
|
|
|
|
|
|
ShowClearIcon = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 输入框鼠标移出
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void InputOnMouseOut()
|
|
|
|
|
{
|
|
|
|
|
if (!AllowClear) return;
|
|
|
|
|
|
|
|
|
|
ShowClearIcon = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 清除已选择项
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ClearSelected()
|
|
|
|
|
{
|
|
|
|
|
_selectedNodes.Clear();
|
|
|
|
|
_hoverSelectedNodes.Clear();
|
2020-06-27 18:24:21 +08:00
|
|
|
|
_displayText = string.Empty;
|
2021-04-29 14:31:03 +08:00
|
|
|
|
SetValue(string.Empty);
|
2020-06-02 13:34:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 浮层移入
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void NodesOnMouseOver()
|
|
|
|
|
{
|
|
|
|
|
if (!AllowClear) return;
|
|
|
|
|
|
|
|
|
|
IsOnCascader = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 浮层移出
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void NodesOnMouseOut()
|
|
|
|
|
{
|
|
|
|
|
if (!AllowClear) return;
|
|
|
|
|
|
|
|
|
|
IsOnCascader = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 下拉节点单击
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="node"></param>
|
|
|
|
|
private void NodeOnClick(CascaderNode node)
|
|
|
|
|
{
|
|
|
|
|
if (node.Disabled) return;
|
|
|
|
|
|
|
|
|
|
SetSelectedNode(node, SelectedTypeEnum.Click);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 下拉节点移入
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="node"></param>
|
|
|
|
|
private void NodeOnMouseOver(CascaderNode node)
|
|
|
|
|
{
|
|
|
|
|
if (ExpandTrigger != "hover") return;
|
|
|
|
|
|
|
|
|
|
if (node.Disabled) return;
|
|
|
|
|
if (!node.HasChildren) return;
|
|
|
|
|
|
|
|
|
|
SetSelectedNode(node, SelectedTypeEnum.Hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion event
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 选中节点
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="cascaderNode"></param>
|
|
|
|
|
/// <param name="selectedType"></param>
|
|
|
|
|
internal void SetSelectedNode(CascaderNode cascaderNode, SelectedTypeEnum selectedType)
|
|
|
|
|
{
|
|
|
|
|
if (cascaderNode == null) return;
|
|
|
|
|
|
|
|
|
|
SelectedType = selectedType;
|
|
|
|
|
if (selectedType == SelectedTypeEnum.Click)
|
|
|
|
|
{
|
|
|
|
|
_selectedNodes.Clear();
|
|
|
|
|
SetSelectedNodeWithParent(cascaderNode, ref _selectedNodes);
|
|
|
|
|
_renderNodes = _selectedNodes;
|
|
|
|
|
|
|
|
|
|
if (ChangeOnSelect || !cascaderNode.HasChildren)
|
|
|
|
|
SetValue(cascaderNode.Value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_hoverSelectedNodes.Clear();
|
|
|
|
|
SetSelectedNodeWithParent(cascaderNode, ref _hoverSelectedNodes);
|
|
|
|
|
_renderNodes = _hoverSelectedNodes;
|
|
|
|
|
}
|
|
|
|
|
_renderNodes.Sort((x, y) => x.Level.CompareTo(y.Level)); //Level 升序排序
|
|
|
|
|
|
|
|
|
|
if (!cascaderNode.HasChildren)
|
|
|
|
|
{
|
|
|
|
|
ToggleState = false;
|
|
|
|
|
IsOnCascader = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设置选中所有父节点
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="node"></param>
|
|
|
|
|
/// <param name="list"></param>
|
|
|
|
|
private void SetSelectedNodeWithParent(CascaderNode node, ref List<CascaderNode> list)
|
|
|
|
|
{
|
|
|
|
|
if (node == null) return;
|
|
|
|
|
|
|
|
|
|
list.Add(node);
|
|
|
|
|
SetSelectedNodeWithParent(node.ParentNode, ref list);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-16 21:54:57 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Options 更新后处理父节点和默认值
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ProcessParentAndDefault()
|
|
|
|
|
{
|
2021-03-23 12:59:20 +08:00
|
|
|
|
InitCascaderNodeState(_nodelist, null, 0);
|
|
|
|
|
SetDefaultValue(Value ?? DefaultValue);
|
2021-01-16 21:54:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 13:34:26 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 初始化节点属性(Level, ParentNode)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="list"></param>
|
|
|
|
|
/// <param name="parentNode"></param>
|
|
|
|
|
/// <param name="level"></param>
|
|
|
|
|
private void InitCascaderNodeState(List<CascaderNode> list, CascaderNode parentNode, int level)
|
|
|
|
|
{
|
|
|
|
|
if (list == null) return;
|
|
|
|
|
|
|
|
|
|
foreach (var node in list)
|
|
|
|
|
{
|
|
|
|
|
node.Level = level;
|
|
|
|
|
node.ParentNode = parentNode;
|
|
|
|
|
|
|
|
|
|
if (node.HasChildren)
|
|
|
|
|
InitCascaderNodeState(node.Children.ToList(), node, level + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-11 23:03:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 刷新选中的内容
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
private void RefreshNodeValue(string value)
|
|
|
|
|
{
|
|
|
|
|
_selectedNodes.Clear();
|
|
|
|
|
|
|
|
|
|
var node = GetNodeByValue(_nodelist, value);
|
|
|
|
|
SetSelectedNodeWithParent(node, ref _selectedNodes);
|
|
|
|
|
_renderNodes = _selectedNodes;
|
|
|
|
|
|
|
|
|
|
RefreshDisplayValue();
|
|
|
|
|
|
|
|
|
|
OnChange?.Invoke(_selectedNodes, value, _displayText);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 13:34:26 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设置默认选中
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="defaultValue"></param>
|
|
|
|
|
private void SetDefaultValue(string defaultValue)
|
|
|
|
|
{
|
2021-04-29 14:31:03 +08:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(defaultValue))
|
|
|
|
|
{
|
|
|
|
|
_selectedNodes.Clear();
|
|
|
|
|
var node = GetNodeByValue(_nodelist, defaultValue);
|
|
|
|
|
SetSelectedNodeWithParent(node, ref _selectedNodes);
|
|
|
|
|
_renderNodes = _selectedNodes;
|
|
|
|
|
SetValue(node?.Value);
|
|
|
|
|
}
|
2020-06-02 13:34:26 +08:00
|
|
|
|
|
2021-04-29 14:31:03 +08:00
|
|
|
|
_initialized = true;
|
2020-06-02 13:34:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设置输入框选中值
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
private void SetValue(string value)
|
2020-07-11 23:03:13 +08:00
|
|
|
|
{
|
|
|
|
|
RefreshDisplayValue();
|
|
|
|
|
|
|
|
|
|
if (Value != value)
|
|
|
|
|
{
|
|
|
|
|
CurrentValueAsString = value;
|
2021-04-29 14:31:03 +08:00
|
|
|
|
|
|
|
|
|
if (_initialized && SelectedNodesChanged.HasDelegate)
|
|
|
|
|
{
|
|
|
|
|
SelectedNodesChanged.InvokeAsync(_selectedNodes.ToArray());
|
|
|
|
|
}
|
2020-07-11 23:03:13 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RefreshDisplayValue()
|
2020-06-02 13:34:26 +08:00
|
|
|
|
{
|
|
|
|
|
_selectedNodes.Sort((x, y) => x.Level.CompareTo(y.Level)); //Level 升序排序
|
|
|
|
|
_displayText = string.Empty;
|
|
|
|
|
int count = 0;
|
|
|
|
|
foreach (var node in _selectedNodes)
|
|
|
|
|
{
|
|
|
|
|
if (node == null) continue;
|
|
|
|
|
|
|
|
|
|
if (count < _selectedNodes.Count - 1)
|
|
|
|
|
_displayText += node.Label + " / ";
|
|
|
|
|
else
|
|
|
|
|
_displayText += node.Label;
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据指定值获取节点
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="list"></param>
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private CascaderNode GetNodeByValue(List<CascaderNode> list, string value)
|
|
|
|
|
{
|
|
|
|
|
if (list == null) return null;
|
|
|
|
|
CascaderNode result = null;
|
|
|
|
|
|
|
|
|
|
foreach (var node in list)
|
|
|
|
|
{
|
|
|
|
|
if (node.Value == value)
|
|
|
|
|
return node;
|
|
|
|
|
|
|
|
|
|
if (node.HasChildren)
|
|
|
|
|
{
|
|
|
|
|
var nd = GetNodeByValue(node.Children.ToList(), value);
|
|
|
|
|
if (nd != null)
|
|
|
|
|
result = nd;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|