mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-15 01:11:52 +08:00
417 lines
12 KiB
C#
417 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Microsoft.AspNetCore.Components;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
|
|
namespace AntDesign
|
|
{
|
|
public partial class Cascader : AntInputComponentBase<string>
|
|
{
|
|
[Parameter] public bool Readonly { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// 是否支持清除
|
|
/// </summary>
|
|
[Parameter] public bool AllowClear { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// 是否显示关闭图标
|
|
/// </summary>
|
|
private bool ShowClearIcon { get; set; }
|
|
|
|
/// <summary>
|
|
/// 当此项为 true 时,点选每级菜单选项值都会发生变化
|
|
/// </summary>
|
|
[Parameter] public bool ChangeOnSelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// 默认的选中项
|
|
/// </summary>
|
|
[Parameter] public string DefaultValue { get; set; }
|
|
|
|
/// <summary>
|
|
/// 次级菜单的展开方式,可选 'click' 和 'hover'
|
|
/// </summary>
|
|
[Parameter] public string ExpandTrigger { get; set; }
|
|
|
|
/// <summary>
|
|
/// 当下拉列表为空时显示的内容
|
|
/// </summary>
|
|
[Parameter] public string NotFoundContent { get; set; } = "Not Found";
|
|
|
|
/// <summary>
|
|
/// 输入框占位文本
|
|
/// </summary>
|
|
[Parameter] public string PlaceHolder { get; set; } = "请选择";
|
|
|
|
[Parameter] public string PopupContainerSelector { get; set; } = "body";
|
|
|
|
/// <summary>
|
|
/// 在选择框中显示搜索框
|
|
/// </summary>
|
|
[Parameter] public bool ShowSearch { get; set; }
|
|
|
|
/// <summary>
|
|
/// 选择完成后的回调(参数为选中的节点集合及选中值)
|
|
/// </summary>
|
|
[Parameter] public Action<List<CascaderNode>, string, string> OnChange { get; set; }
|
|
|
|
[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);
|
|
|
|
ProcessParentAndDefault();
|
|
}
|
|
}
|
|
|
|
private List<CascaderNode> _nodelist;
|
|
|
|
/// <summary>
|
|
/// 选中节点集合(click)
|
|
/// </summary>
|
|
internal List<CascaderNode> _selectedNodes = new List<CascaderNode>();
|
|
|
|
/// <summary>
|
|
/// 选中节点集合(hover)
|
|
/// </summary>
|
|
internal List<CascaderNode> _hoverSelectedNodes = new List<CascaderNode>();
|
|
|
|
/// <summary>
|
|
/// 用于渲染下拉节点集合(一级节点除外)
|
|
/// </summary>
|
|
internal List<CascaderNode> _renderNodes = new List<CascaderNode>();
|
|
|
|
/// <summary>
|
|
/// 浮层 展开/折叠状态
|
|
/// </summary>
|
|
private bool ToggleState { get; set; }
|
|
|
|
/// <summary>
|
|
/// 鼠标是否处于 浮层 之上
|
|
/// </summary>
|
|
private bool IsOnCascader { get; set; }
|
|
|
|
/// <summary>
|
|
/// 选择节点类型
|
|
/// Click: 点击选中节点, Hover: 鼠标移入选中节点
|
|
/// </summary>
|
|
private SelectedTypeEnum SelectedType { get; set; }
|
|
|
|
private ClassMapper _inputClassMapper = new ClassMapper();
|
|
private string _displayText;
|
|
|
|
private static Hashtable _sizeMap = new Hashtable()
|
|
{
|
|
["large"] = "lg",
|
|
["small"] = "sm"
|
|
};
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
base.OnInitialized();
|
|
|
|
ClassMapper.Add("ant-cascader-picker")
|
|
.GetIf(() => $"ant-cascader-picker-{Size}", () => _sizeMap.ContainsKey(Size))
|
|
.GetIf(() => $"ant-input-{_sizeMap[Size]}", () => _sizeMap.ContainsKey(Size))
|
|
;
|
|
|
|
_inputClassMapper
|
|
.Add("ant-input")
|
|
.Add("ant-cascader-input")
|
|
.GetIf(() => $"ant-input-{_sizeMap[Size]}", () => _sizeMap.ContainsKey(Size));
|
|
}
|
|
|
|
protected override void OnValueChange(string value)
|
|
{
|
|
base.OnValueChange(value);
|
|
|
|
RefreshNodeValue(value);
|
|
}
|
|
|
|
#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();
|
|
_displayText = string.Empty;
|
|
CurrentValueAsString = string.Empty;
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options 更新后处理父节点和默认值
|
|
/// </summary>
|
|
private void ProcessParentAndDefault()
|
|
{
|
|
InitCascaderNodeState(_nodelist, null, 0);
|
|
SetDefaultValue(Value ?? DefaultValue);
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置默认选中
|
|
/// </summary>
|
|
/// <param name="defaultValue"></param>
|
|
private void SetDefaultValue(string defaultValue)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(defaultValue))
|
|
return;
|
|
|
|
_selectedNodes.Clear();
|
|
var node = GetNodeByValue(_nodelist, defaultValue);
|
|
SetSelectedNodeWithParent(node, ref _selectedNodes);
|
|
_renderNodes = _selectedNodes;
|
|
SetValue(node?.Value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置输入框选中值
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
private void SetValue(string value)
|
|
{
|
|
RefreshDisplayValue();
|
|
|
|
if (Value != value)
|
|
{
|
|
CurrentValueAsString = value;
|
|
}
|
|
}
|
|
|
|
private void RefreshDisplayValue()
|
|
{
|
|
_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;
|
|
}
|
|
}
|
|
}
|