树节点—点击事件 — 根据节点名字,递归加载项

本文介绍了一种基于树节点的检查项加载机制,通过递归方式实现在特定节点下加载检查项,并确保所有相关子项正确展开。该机制包括根据节点名字加载检查项、递归加载一级和二级检查项,以及确保父节点展开的功能。

#region树节点 —点击事件 — 根据节点名字加载检查项

    /// <summary>
    /// 根据节点名字加载检查项
    /// </summary>
    /// <param name="PersonName">节点的名字</param>
    /// <param name="checkProjects">检查项成员链表</param>
    public void CheckEmployee(string PersonName, List<CheckProject> checkProjects)
    {
    
        _Checks = checkProjects
        TreeNode[] treeNode = treeView.Nodes.Find(PersonName, true);
        for (int i = 0; i < treeNode.Length; i++)
        {
            //在所选节点加载检查项
            LoadChecks(treeNode[i]);
            //展开检查项
            treeNode[i].ExpandAll();
            //让其父节点展开
            IsPrate(treeNode[i]);
        }
        
    }
    /// <summary>
    /// 递归 让其父节点展开
    /// </summary>
    /// <param name="tn"></param>
    public void IsPrate(TreeNode tn)
    {
        if (tn.Parent!=null)
        {
            tn.Parent.Expand();
            IsPrate(tn.Parent);
        }
        
    }
    #endregion

    #region 对选中节点进行检查项的递归加载
    /// <summary>
    /// 一级检查项的递归加载
    /// </summary>
    /// <param name="tn"></param>
    public void LoadChecks(TreeNode tn)
    {
        foreach (CheckProject checkitem in _Checks)
        {
            //一级检查项
            if (!tn.Nodes.ContainsKey(checkitem.CheckProjectName))
            {
                TreeNode tn1 = new TreeNode();
                tn1.Name = checkitem.CheckProjectName;
                tn1.Text = checkitem.CheckProjectName;

                tn.Nodes.Add(tn1);
                SecondLoadChecks(tn1);
            }
        }
    }
    /// <summary>
    /// 二级检查项递归加载
    /// </summary>
    /// <param name="tn"></param>
    public void SecondLoadChecks(TreeNode tn)
    {
        foreach (CheckProject item in _Checks)
        {
            if (item.CheckProjectName == tn.Name)
            {
                TreeNode tn1 = new TreeNode();
                tn1.Text = item.Sub_CheckProject;
                tn1.Name = item.Sub_CheckProject;
                tn.Nodes.Add(tn1);
                SecondLoadChecks(tn1);
            }
        }

    }

#endregion

<think>我们提供的代码中,子组件(BridgeCascader)使用了el-cascader组件,并且设置了checkStrictly为true(可以选择任意一级)。但是,在形结构中,点击父节点(公司节点)也会触发change事件,这可能不是我们期望的行为(我们期望只有选择桥梁叶子节点才触发)。 原因分析: 在Element UI的级联选择器中,即使设置了`checkStrictly: true`(即可以选择任意一级),点击非叶子节点(比如公司节点)也会触发change事件,并返回该节点的值(公司名称)。但我们的业务逻辑中,桥梁选择应该只选择具体的桥梁(叶子节点),公司节点只是作为分组。 解决方案: 我们可以通过设置`props.emitPath`为`false`(已经设置)和利用`change-on-select`(已废弃)或`expandTrigger`(控制展开方式)并不能直接解决。Element UI的Cascader组件在点击非叶子节点时,如果设置了`checkStrictly`为true,那么点击非叶子节点也会选中该节点。 但我们希望只有叶子节点才能被选中并触发change事件,可以通过以下两种方式之一: 方式一:在handleChange方法中判断选中的节点是否是叶子节点(即桥梁节点),如果不是,则不触发change事件(或者重置选择)。 方式二:在数据源中,只给叶子节点设置value(即桥梁节点有value,公司节点不设置value)。但是Element UI要求每个节点都有value,所以此方式不可行。 因此,我们采用方式一:在子组件的handleChange方法中,判断选中的节点是否是叶子节点(通过是否存在children来判断,或者通过我们自定义的数据结构)。但是注意,我们的数据中,公司节点(非叶子节点)有children,桥梁节点(叶子节点)没有children。 修改子组件的handleChange方法: 步骤: 1. 在handleChange中,我们不仅需要value,还需要知道选中的节点对象。但el-cascader在change事件中只返回value(或value数组),不返回节点对象。所以我们需要自己根据value去查找节点。 2. 我们可以通过value(桥梁编码)去查找节点,如果该节点是叶子节点(没有children),则触发change事件;否则,我们忽略这次选择(并重置选中的值)。 但是,这样会有问题:当用户点击公司节点时,我们无法直接通过value(公司名称)来获取节点对象(因为我们没有存储value到节点的映射),而且公司节点的value是公司名称,桥梁节点的value是桥梁编码。 因此,我们需要调整数据结构,在构建选项时,为每个节点添加一个type字段('company'或'bridge'),然后在handleChange中根据value判断类型(但注意,公司名称和桥梁编码可能重复,所以不可靠)。 另一种做法:在节点对象中存储一个标志,比如isLeaf。我们在构建桥梁节点时,设置isLeaf为true,公司节点不设置(或设置isLeaf为false)。然后,在handleChange中,我们根据value查找节点,并判断该节点的isLeaf属性。 具体实现: 1. 在loadBridges方法中,构建桥梁节点时添加isLeaf属性: ```javascript const bridgeOption = { value: bridge.bridgeCode, label: bridge.bridgeName, bridgeName: bridge.bridgeName, companyName: companyName, fullData: bridge, isLeaf: true // 标记为叶子节点 }; ``` 公司节点不添加isLeaf属性(或者设置为false): ```javascript const companyOption = { value: companyName, label: companyName, children: [], isLeaf: false // 非叶子节点 }; ``` 2. 在handleChange方法中,根据value查找节点,并判断是否是叶子节点: ```javascript handleChange(value) { // 根据value查找节点(注意:公司节点的value是公司名称,桥梁节点的value是桥梁编码) const node = this.findNodeByValue(value); if (node && node.isLeaf) { // 只有叶子节点才触发 this.$emit("input", value); // ... 其他逻辑(如获取运营单位等) this.$emit("change", this.getBridgeInfo(value)); } else { // 非叶子节点,重置选择(清空) this.selectedValue = ''; this.$emit("input", ''); // 可以提示用户必须选择具体桥梁 this.$message.warning('请选择具体桥梁'); } }, ``` 3. 实现findNodeByValue方法(递归查找): ```javascript findNodeByValue(value, nodes = this.bridgeOptions) { for (const node of nodes) { if (node.value === value) { return node; } if (node.children) { const found = this.findNodeByValue(value, node.children); if (found) return found; } } return null; } ``` 4. 注意:由于公司节点和桥梁节点的value在不同的层级,且值类型可能相同(虽然概率低),所以此方法是可行的。 但是,这个方案有一个问题:当数据量很大时,递归查找可能会有性能问题。不过,桥梁数据量通常不会很大。 另外,我们还需要在重置选择后,更新级联选择器的显示(清空选择)。由于我们设置了v-model(selectedValue),所以当我们将selectedValue设置为空时,级联选择器会清空。 完整修改后的子组件方法: 注意:由于我们可能同时需要桥梁节点的信息(如公司名称),所以即使判断为叶子节点,也需要通过节点获取公司名称等信息。 因此,在handleChange中,我们使用节点对象来构建bridgeInfo。 调整后的handleChange方法: ```javascript handleChange(value) { const node = this.findNodeByValue(value); if (!node) { // 没有找到节点,清空 this.selectedValue = ''; this.$emit("input", ''); return; } if (node.isLeaf) { this.selectedValue = value; this.$emit("input", value); // 构建桥梁信息 const companyName = node.companyName; // 桥梁节点存储了公司名称 const bridgeInfo = { bridgeCode: value, companyName: companyName, bridgeName: node.bridgeName }; if (this.returnFullObject) { bridgeInfo.fullData = node.fullData; } this.$emit("change", bridgeInfo); } else { // 非叶子节点,重置 this.selectedValue = ''; this.$emit("input", ''); this.$message.warning('请选择具体桥梁'); } }, ``` 这样,当用户点击公司节点(非叶子节点)时,选择会被清空,并提示用户选择具体桥梁。 注意:由于级联选择器在点击非叶子节点时,会展开子节点,同时会触发change事件(因为选中了非叶子节点),然后我们立即将其清空,所以用户会看到选择器被清空。这可能会造成一些交互上的困惑,但符合我们的业务要求(必须选择叶子节点)。 另外,我们也可以考虑在级联选择器的配置中禁止选择非叶子节点,但Element UI没有直接提供这样的选项。所以上述方法是一个可行的解决方案。 但是,还有一种更简单的方式:设置`props`的`checkStrictly`为`false`(即只能选择叶子节点),这样用户点击非叶子节点时不会选中,只会展开。但是这样用户就不能通过点击非叶子节点来选中整个分组(我们本来就不希望这样),所以这符合我们的需求。但是,注意:`checkStrictly`为`false`时,只能选择叶子节点,非叶子节点不可选。所以,我们只需要将`checkStrictly`设为`false`即可。 因此,我们可以尝试: 将`cascaderProps`中的`checkStrictly`改为`false`,这样用户只能选择叶子节点(桥梁节点),点击公司节点只会展开,不会选中,也就不会触发change事件。 修改: ```javascript cascaderProps: { value: "value", label: "label", children: "children", checkStrictly: false, // 改为false,只能选择叶子节点 emitPath: false }, ``` 这样,当用户点击公司节点(非叶子节点)时,级联选择器不会选中,也不会触发change事件。那么我们的handleChange方法就只会在选择叶子节点时触发。 所以,我们不需要前面的复杂判断,只需要修改这个配置项即可。 但是,注意:在级联选择器中,当`checkStrictly`为`false`时,我们无法选择非叶子节点(即公司节点),这正是我们想要的。而且,我们之前设置`checkStrictly`为`true`是为了可以任意选择一级,但我们现在的要求是只能选择叶子节点,所以设置为`false`是合理的。 因此,解决方案非常简单:将`checkStrictly`改为`false`。 但是,我们之前设置`checkStrictly`为`true`可能是为了能够直接选择公司(但业务上不允许),现在业务要求只能选择桥梁,所以改为false即可。 所以,请修改子组件中`cascaderProps`的`checkStrictly`为`false`。 这样,当用户点击公司节点时,不会触发change事件,只有点击桥梁节点才会触发。 因此,我们不需要修改handleChange方法,也不需要添加isLeaf标记。 总结:将`checkStrictly`设为`false`,问题解决。 注意:在Element UI文档中,`checkStrictly`为`false`时,只能选择叶子节点。 所以,请检查你的级联选择器配置,确保`checkStrictly`为`false`。 但是,我们之前的代码中已经设置`checkStrictly: true`,这是问题的根源。将其改为`false`即可。 修改后的子组件数据: ```javascript cascaderProps: { value: "value", label: "label", children: "children", checkStrictly: false, // 修改这里 emitPath: false }, ``` 这样,点击树节点(非叶子节点)就不会触发change事件了,只有选择叶子节点才会触发。 所以,请尝试这个简单的修改。 注意:如果之前设置`checkStrictly: true`是为了其他目的(比如允许选择任意一级),那么现在业务要求只能选择桥梁(叶子节点),所以应该设为false。 因此,最终解决方案:将`checkStrictly`改为`false`。</think>在 Element UI 的 Cascader 级联选择器中,点击树节点会触发 `change` 事件的原因主要有以下几点: ### 根本原因分析 1. **默认行为设计**: - Cascader 组件默认将每个节点(包括父节点)视为可选择项 - 当 `checkStrictly: true` 时,允许选择任意层级节点(包括非叶子节点) 2. **事件触发机制**: ```javascript cascaderProps: { checkStrictly: true, // 允许选择任意节点 emitPath: false // 只返回最后选中的值 } ``` - 当用户点击**任何节点**(包括公司节点)时,组件都会触发 `change` 事件 - 即使是非叶子节点(公司节点),也会被当作有效选择 ### 解决方案:限制只能选择叶子节点 #### 方法一:配置级联选择器属性(推荐) ```javascript cascaderProps: { value: "value", label: "label", children: "children", checkStrictly: true, emitPath: false, // 新增:设置只能选择叶子节点 leafOnly: true } ``` #### 方法二:在 change 事件中过滤非叶子节点 ```javascript handleChange(value) { // 查找选择的节点 const node = this.findNodeByValue(value); // 只处理叶子节点 if (node && node.isLeaf) { this.$emit("input", value); const companyName = this.bridgeUnitMap[value]; this.$emit("change", { bridgeCode: value, companyName }); } else { // 非叶子节点选择:重置选择并提示 this.selectedValue = ''; this.$message.warning('请选择具体桥梁'); } }, // 辅助方法:根据值查找节点 findNodeByValue(value, nodes = this.bridgeOptions) { for (const node of nodes) { if (node.value === value) return node; if (node.children) { const found = this.findNodeByValue(value, node.children); if (found) return found; } } return null; } ``` #### 方法三:在数据中标记叶子节点 ```javascript // 加载桥梁时标记节点类型 async loadBridges() { // ... bridges.forEach(bridge => { const bridgeOption = { value: bridge.bridgeCode, label: bridge.bridgeName, isLeaf: true // 标记为叶子节点 }; // ... }); for (const companyName in res) { const companyOption = { value: companyName, label: companyName, isLeaf: false // 标记为非叶子节点 }; // ... } } ``` ### 最佳实践建议 1. **UI 提示优化**: ```html <el-cascader :props="{ ...cascaderProps, expandTrigger: 'hover' // 鼠标悬停展开,减少误点击 }" /> ``` 2. **禁用非叶子节点选择**: ```javascript cascaderProps: { // ... checkStrictly: false // 禁止选择非叶子节点 } ``` 3. **视觉区分**: ```css /* 非叶子节点样式 */ ::v-deep .el-cascader-node:not(.is-leaf) { color: #999; cursor: default; } ``` ### 总结 点击树节点会触发 `change` 事件是因为 Cascader 组件默认将所有节点视为可选元素。通过设置 `leafOnly: true` 或在 `change` 事件中过滤非叶子节点,可以确保只有具体的桥梁节点会触发业务逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值