Vue el-cascader级联进行懒加载功能实现

Vue el-cascader级联进行懒加载功能实现

话不多说直接看代码
动态加载
当选中某一级时,动态加载该级下的选项:
通过lazy开启动态加载,并通过lazyload来设置加载数据源的方法。 lazyload方法有两个参数,第一个参数node为当前点击的节点,第二个resolve为数据加载完成的回调(必须调用)。 为了更准确的显示节点的状态,还可以对节点数据添加是否为叶子节点的标志位 (默认字段为leaf,可通过props.leaf修改)。 否则,将以有无子节点来判断其是否为叶子节点。
示例:

<template>
  <el-cascader :props="props" />
</template>

<script lang="ts" setup>
import type { CascaderProps } from 'element-plus'

let id = 0
const props: CascaderProps = {
  lazy: true,
  lazyLoad(node, resolve) {
    const { level } = node
    setTimeout(() => {
      const nodes = Array.from({ length: level + 1 }).map((item) => ({
        value: ++id,
        label: `Option - ${id}`,
        leaf: level >= 2,
      }))
      // Invoke `resolve` callback to return the child nodes data and indicate the loading is finished.
      resolve(nodes)
    }, 1000)
  },
}
</script>

实际操作:

<template>
  <div>
    <el-cascader
      style="width: 100%; height: 30px; padding: 0px; line-height: 35px; font-size: 14px;"
      ref="cascaderRef"
      placeholder="请选择行政区域"
      v-model="Materialform.RegionalismId"
      :options="options"
      :props="props"
    ></el-cascader>
  </div>
</template>
data(){
Materialform:{
	RegionalismId:[],
},
props: {
      value: 'value',
      label: 'label',
      expandTrigger: 'hover',
      lazy: true,
      lazyLoad: this.lazyLoad
      },
isModify: false, // 标识当前是新增还是修改操作
options: [],
created() {
this.loadInitialOptions()
},
// 加载第一级选项
loadInitialOptions() {
try {
	let that = this;
	that.options = [];
	that.http.ajax2({url: "/api/regions",
	postdata: true,
	success: function (result) {
		if (result.Status) {
		that.options = result.Data.map(x => {
		return {
			value: x.Id,
			label: x.Ext_Name,
			parentId: x.PId,
			leaf: false,
			children: []
		};
	});
}
},
type: "post",
async: false
});
return that.options;
} catch (error) {
console.error('加载初始选项失败:', error);
}
},
lazyLoad(node, resolve) {
	try {
          const { level, data } = node;
          if (level > 0) {
          const url = `/api/regions?pId=${data.value}`;
this.http.post(url, null, "加载中").then(result => {
if (result.Status) {
const children = result.Data.map(x => {
return {
        leaf: level >= 4,
        value: x.Id,
        label: x.Ext_Name,
        parentId: x.PId,
        children: []
        };
        });
// 将子节点数据添加到当前节点的 children 属性中
data.children = children;
resolve(children);
}
});
}
} catch (error) {
console.error('加载子节点数据失败:', error);
resolve([]);
}
},
// 新增操作
handleAdd() {
  this.isModify = false;
  this.selectedValues = [];
},
// 修改操作
async handleModify() {
  this.isModify = true;
  await this.loadOptionsForExistingValues();
  this.selectedValues = this.existingValues;
},
// 根据已有值加载选项
loadOptionsForExistingValues() {
let currentOptions = this.options;
for (let i = 0; i < this.Materialform.RegionalismId.split(',').length; i++) {
     const value = Number(this.Materialform.RegionalismId.split(',')[i]);
     const node = currentOptions.find(option => option.value === value);
      if (node) {
         if (i < this.Materialform.RegionalismId.split(',').length - 1) {
             if (!node.children || node.children.length === 0) {
                 this.loadChildren(node);
             }
             currentOptions = node.children;
      }
   }
  }
},
// 加载子节点
loadChildren(node) {
try {
console.log("1")
const url = `//api/regions?pId=${node.value}`;
this.http.post(url, null, "加载中").then(reslut => {
if (reslut.Status) {
let children = reslut.Data.map(x => {
    return {
            value: x.Id,
            label: x.Ext_Name,
            parentId: x.PId,
            };
})
node.children = children;
//node.Data.children = children;
//resolve(children);
   }
   })
} catch (error) {
console.error('加载子节点失败:', error);
 }
},

后台代码:

//控制器
 [HttpPost, Route("*名称*")]
 public async Task<IActionResult> *名称*(int pId = 0)
 {
     return JsonNormal(data: await Service.*名称*(pId));
 }
//写在IService
Task<WebResponseContent> *名称*(int pId = 0);
//--写在Service
/// <summary>
/// 懒加载树
/// </summary>
/// <param name="pId">父级Id</param>
/// <returns></returns>
public async Task<WebResponseContent> *名称*(int pId = 0)
{
  var list = await _repository.FindAsIQueryable(a => 根据上级ID判断).Select(a => new {字段}).ToListAsync();
  return _webResponse.OK(data: list);
}

代码解释:

  • created 钩子:在组件创建时调用 loadInitialOptions 方法,加载第一级的选项数据。
  • handleAdd 方法:处理新增操作,将 isModify 设为 false,并清空 selectedValues。
  • handleModify 方法:处理修改操作,将 isModify 设为 true,调用 loadOptionsForExistingValues 方法加载与已有值对应的选项,最后把 existingValues 赋给 selectedValues。
  • loadOptionsForExistingValues 方法:遍历 existingValues,根据每个值找到对应的节点,若该节点的子节点未加载,则调用 loadChildren 方法加载子节点。
  • lazyLoad 方法:当用户展开节点时,动态加载子节点数据。
  • loadChildren 方法:根据节点的 value 加载其子节点数据。

–那位大佬会动态选择层级,请教教我,
题目为:如果级联下级只存在一级则选择一级,如果级联下级存在四级则可以选择四级

<think>我们正在处理一个关于Vue3中使用ElementPlus的el-cascader组件实现动态加载(懒加载)数据并回显的问题。根据用户提供的引用,我们可以总结出两个关键点:1.引用[1]展示了在Vue2中使用el-cascader懒加载和回显方法,其中通过`v-model`绑定一个数组(value)来存储选中的节点路径,同时通过props配置lazyLoad函数实现懒加载。2.引用[2]则提供了一种回显的思路,即使用`placeholder`属性来显示回显的文本,但这种方法并不是直接回显选中项,而是用文本代替显示,实际选中的绑定值还是通过`v-model`。在Vue3中,ElementPlus的el-cascader组件用法类似,但需要注意组合式API的使用。我们的目标:在Vue3中实现el-cascader的动态加载(懒加载)和回显。步骤:1.设置el-cascader的props为懒加载模式,并定义lazyLoad方法。2.使用v-model绑定一个数组,该数组表示选中节点的路径(从根节点到叶子节点的value组成的数组)。3.回显问题:在动态加载的情况下,回显需要组件能够根据绑定的路径数组(v-model的值)自动加载各级节点直到叶子节点。但是,由于数据是懒加载的,初始时组件并不知道完整的节点信息,因此需要我们在初始化时主动加载路径上的节点。然而,ElementPlus的el-cascader组件在懒加载模式下,当v-model绑定的值是一个路径数组时,组件内部会自动根据这个数组去逐级加载节点,从而实现回显。但前提是:-我们必须正确配置lazyLoad函数,使得在加载每一级节点时,能够根据父节点的信息获取下一级节点。-初始时,我们设置的路径数组(v-model绑定的值)必须是有效的,即每一级的节点都能通过lazyLoad函数加载出来。因此,最佳实践如下:1.定义lazyLoad函数,该函数接收两个参数:node和resolve。node代表当前节点,resolve是加载完成后的回调函数,用于返回当前节点的子节点数组。2.在lazyLoad函数中,根据当前节点的level和value(或其他标识)去后端获取下一级节点数据,然后将获取到的节点数据通过resolve回调返回。3.回显时,直接将后端返回的路径数组(例如[省id,市id,区id])绑定到v-model即可。注意:这个数组必须是完整的路径,且每个id都必须与lazyLoad函数中加载节点时使用的id一致。但是,有时候我们可能只有叶子节点的id,而没有完整的路径。这时,我们需要在初始化时根据叶子节点id反查其所有父节点,构建完整的路径数组。这个过程通常需要后端的支持(提供一个根据子节点id获取所有父节点id列表的接口)。如果后端只能懒加载,且无法提供反查父节点路径的接口,那么回显就会变得复杂。此时,我们可以考虑:-在组件挂载时,根据叶子节点id递归加载其父节点,直到根节点,构建完整的路径数组。但这可能会增加请求次数,并且需要处理异步加载的顺序。鉴于用户没有提到这种复杂情况,我们假设后端返回的是完整的路径数组。具体代码实现Vue3+ElementPlus):1.安装ElementPlus(如果尚未安装):```bashnpminstallelement-plus--save```2.在组件中引入el-cascader和相关样式。3.编写代码:注意:以下代码使用Vue3的组合式API。</think>在 Vue3 中使用 Element Plus 的 `el-cascader` 组件实现动态加载数据和回显功能的最佳实践如下: ### 解决方案步骤 #### 1. 配置级联选择器 ```vue <template> <el-cascader v-model="selectedPath" :props="cascaderProps" clearable filterable placeholder="请选择地区" /> </template> ``` #### 2. 初始化数据与配置 ```javascript import { ref, reactive } from 'vue'; const selectedPath = ref([]); // 存储选中路径的数组 const cascaderProps = reactive({ lazy: true, async lazyLoad(node, resolve) { const { level, data } = node; const parentId = level === 0 ? null : data.value; try { // 调用API获取子级数据(示例) const children = await fetch(`/api/regions?parentId=${parentId}`) .then(res => res.json()); // 格式化节点数据 const nodes = children.map(item => ({ value: item.id, label: item.name, leaf: !item.hasChildren // 标记是否为叶子节点 })); resolve(nodes); } catch (error) { console.error('加载失败', error); resolve([]); } } }); ``` #### 3. 动态回显实现 ```javascript // 初始化回显数据 const initEcho = async (leafId) => { try { // 1. 获取完整路径(需后端支持) const path = await fetch(`/api/path/${leafId}`).then(res => res.json()); // 2. 手动加载路径节点 const loadPathNodes = async () => { const tempNodes = []; for (const id of path) { const node = await loadNodeById(id, tempNodes[tempNodes.length-1]); tempNodes.push(node); } return path; }; // 3. 设置选中路径 selectedPath.value = await loadPathNodes(); } catch (e) { console.error('回显失败', e); } }; // 辅助函数:根据ID加载节点 const loadNodeById = (id, parentNode) => { return new Promise(resolve => { cascaderProps.lazyLoad( { level: parentNode ? parentNode.level + 1 : 0, data: parentNode || { value: null } }, (nodes) => { const target = nodes.find(n => n.value === id); resolve({ ...target, level: parentNode ? parentNode.level + 1 : 0 }); } ); }); }; // 使用示例:组件挂载时回显 onMounted(() => { initEcho(1024); // 传入叶子节点ID }); ``` #### 4. 关键配置说明 | 配置项 | 说明 | |----------------|----------------------------------------------------------------------| | `lazy: true` | 启用动态加载模式 | | `lazyLoad` | 动态加载函数,接收当前节点和`resolve`回调 | | `value` 绑定 | 使用数组存储完整路径(如 `[省ID, 市ID, 区ID]`) | | 叶子节点标记 | 通过 `leaf: level >= 2` 或后端返回的 `hasChildren` 字段控制 | | 异步加载顺序 | 回显时需要按层级顺序逐级加载节点 | ### 注意事项 1. **路径接口要求**:后端需提供根据叶子节点ID查询完整路径的接口(如 `/api/path/1024` 返回 `[10, 100, 1024]`) 2. **ID一致性**:动态加载接口和路径接口使用的ID体系必须一致 3. **加载状态管理**:可添加 `loading` 状态提升用户体验 4. **错误处理**:网络请求需包含错误处理和超时机制 5. **内存优化**:大数据量时考虑虚拟滚动(Element Plus 默认支持) ### 完整示例代码 ```vue <template> <div> <el-cascader v-model="selectedPath" :props="cascaderProps" :show-all-levels="false" /> </div> </template> <script setup> import { ref, reactive, onMounted } from 'vue'; const selectedPath = ref([]); const cascaderProps = reactive({ lazy: true, async lazyLoad(node, resolve) { const { level, data } = node; const parentId = level === 0 ? 0 : data.value; // 模拟API请求 setTimeout(() => { const nodes = generateNodes(parentId, level); resolve(nodes); }, 300); } }); // 初始化回显 onMounted(async () => { const leafId = 1024; // 实际从后端获取 const fullPath = await fetchFullPath(leafId); selectedPath.value = await loadPathSequentially(fullPath); }); // 辅助函数(实际使用替换为API调用) const generateNodes = (parentId, level) => { return Array.from({ length: 5 }).map((_, i) => ({ value: parentId * 10 + (i + 1), label: `节点_${parentId}-${i + 1}`, leaf: level >= 2 })); }; const fetchFullPath = async (id) => [10, 100, 1024]; // 示例路径 </script> ``` ### 相关问题 1. 如何优化 `el-cascader` 在大数据量下的性能? 2. 动态加载模式下如何处理节点更新和缓存失效? 3. 如何实现级联选择器的搜索过滤功能? 4. 在回显过程中如何显示加载状态? 5. 如何处理跨级选择时的数据一致性? > 提示:对于复杂场景,可考虑使用 Vuex/Pinia 管理级联状态,或封装高阶组件复用加载逻辑[^1][^2]。 [^1]: Element Plus Cascader 文档: https://element-plus.org/en-US/component/cascader.html [^2]: Vue3 异步组件最佳实践: https://vuejs.org/guide/components/async.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值