
在Vue3+ElementPlus前端中,有时候一些字典的关联显示,需要使用级联,因此一般使用watch监控对象变化,实现字典列表的级联更新。本篇随笔介绍基于实际案例来实现多级关联的处理操作,以供参考。
1、省市区的级联案例
在很多实际业务项目中,往往都可能涉及到级联显示的场景。比如有 省份 -> 城市 -> 区县 的字典数据,用户在选择“省份”时,自动更新“城市”选项;选择“城市”时,自动更新“区县”选项。基本的处理过程如下所示。
-
使用
ref
/reactive
保存选择值和字典数据。 -
使用
watch
监听上级字段变化,动态更新下级字典数据。 -
下级值要 清空/重置,避免残留无效值。
对于省市区简单例子的Vue3模板界面,如下代码所示。
<template> <div> <label>省份:</label> <select v-model="province"> <option :value="null">请选择</option> <option v-for="p in provinces" :key="p.code" :value="p.code">{{ p.name }}</option> </select> <label>城市:</label> <select v-model="city"> <option :value="null">请选择</option> <option v-for="c in cities" :key="c.code" :value="c.code">{{ c.name }}</option> </select> <label>区县:</label> <select v-model="area"> <option :value="null">请选择</option> <option v-for="a in areas" :key="a.code" :value="a.code">{{ a.name }}</option> </select> </div> </template>
在其script脚本代码的操作中,使用watch来处理界面代码逻辑如下所示。
<script setup lang="ts"> import { ref, watch } from 'vue' // 模拟字典数据 const provinces = ref([ { code: 'gd', name: '广东' }, { code: 'bj', name: '北京' } ]) const citiesDict: Record<string, { code: string; name: string }[]> = { gd: [ { code: 'gz', name: '广州' }, { code: 'sz', name: '深圳' } ], bj: [ { code: 'dc', name: '东城' }, { code: 'xc', name: '西城' } ] } const areasDict: Record<string, { code: string; name: string }[]> = { gz: [{ code: 'tianhe', name: '天河区' }, { code: 'yuexiu', name: '越秀区' }], sz: [{ code: 'nanshan', name: '南山区' }, { code: 'futian', name: '福田区' }], dc: [{ code: 'xx', name: '东城区示例' }], xc: [{ code: 'yy', name: '西城区示例' }] } // 当前选择 const province = ref<string | null>(null) const city = ref<string | null>(null) const area = ref<string | null>(null) // 下级字典 const cities = ref<{ code: string; name: string }[]>([]) const areas = ref<{ code: string; name: string }[]>([]) // 监听省份变化 → 更新城市 watch(province, (newVal) => { if (newVal) { cities.value = citiesDict[newVal] || [] } else { cities.value = [] } city.value = null // 清空下级选择 area.value = null areas.value = [] }) // 监听城市变化 → 更新区县 watch(city, (newVal) => { if (newVal) { areas.value = areasDict[newVal] || [] } else { areas.value = [] } area.value = null // 清空下级选择 }) </script>
有时候,对于字段的处理顺序,我们可能需要引入nextick来处理。
nextTick
在 Vue3 里非常适合用来 等 DOM 和响应式更新完成再执行逻辑。
在「编辑场景级联字典」这种情况里,nextTick
可以解决 字典更新和已有值赋值的时序问题。
// 监听省份变化 → 更新城市字典 watch( () => form.province, async (newVal) => { if (!newVal) { cities.value = [] form.city = null form.area = null return } cities.value = await fetchCities(newVal) // 用 nextTick 等 cities 更新后再校验 await nextTick() if (!cities.value.find(c => c.code === form.city)) { form.city = null form.area = null } }, { immediate: true } )
2、电力记录业务的处理案例
在我们的一个项目案例中,对于电力的一些级联处理,也有类似的参考价值,如对于电力记录的处理中,我们需要根据地区进行一级、二级、三级能耗的下拉列表级联更新,方便在录入的时候进行关联显示。下面是数据列表的显示部分界面截图。
在数据编辑或者新增的情况下,我们需要根据这些内容进行级联的显示处理,那么界面如下所示。
我们看到,以上几个了下拉列表的字典内容,都存在一定的级联关系,如选择区域后,需要更新一级列表、选择一级列表后,需要更新二级列表、选择二级列表后,需要更新三级列表等等。
我们使用watch来跟踪对象的变化,并及时进行字典数据的更新,如下逻辑代码所示。
const area = ref([]); // 能源区域 const level1 = ref([]); // 一级能耗计量 const level2 = ref([]); // 二级能耗计量 const level3 = ref([]); // 三级能耗计量 const initArea = () => { electrecord.GetFieldDict('area').then(data => { // console.log(data); area.value = data; }); }; // 判断下拉框的值是否有改变 watch( () => editForm.area, (newValue) => { if (!newValue) { editForm.level1 = ''; editForm.level2 = ''; editForm.level3 = ''; } if (newValue) { const whereStr = `area='${newValue}'`; electmerter.GetFieldDict('level1', whereStr).then(data => { // console.log(data); level1.value = data; }); } }, { immediate: true } ); watch( () => editForm.level1, (newValue) => { if (!newValue) { editForm.level2 = ''; editForm.level3 = ''; editForm.devicename = ''; editForm.devicecode = ''; editForm.lastnumber = 0; } if (newValue) { const whereStr = `level1='${newValue}' and area='${editForm.area}'`; electmerter.GetFieldDict('level2', whereStr).then(data => { // console.log(data); level2.value = data; }); } }, { immediate: true } ); watch( () => editForm.level2, (newValue) => { if (!newValue) { editForm.level3 = ''; editForm.devicename = ''; editForm.devicecode = ''; editForm.lastnumber = 0; } if (newValue) { const whereStr = `level2='${newValue}' and area='${editForm.area}' and level1='${editForm.level1}'`; electmerter.GetFieldDict('level3', whereStr).then(data => { // console.log(data); level3.value = data; }); } }, { immediate: true } ); watch( () => editForm.level3, (newValue) => { if (!newValue) { editForm.devicename = ''; editForm.devicecode = ''; editForm.lastnumber = 0; } }, { immediate: true } );
其中 electmerter.GetFieldDict 是ES6类中的API调用函数,主要对标后端代码里面,通用处理的获取对应表的关联字段列表。
后端通用的处理代码如下C#代码所示。
/// <summary> /// 根据字段名称,获取对应的字典列表 /// </summary> /// <param name="fieldName">字段名称</param> /// <param name="whereStr">条件字符串,如Age > 20 AND IsActive = true</param> /// <returns></returns> public virtual async Task<List<CListItem>> GetFieldDict(string fieldName, string whereStr) { var list = new List<CListItem>(); if (!fieldName.IsNullOrEmpty()) { //var sql = $"Select distinct {fieldName} from Table"; var query = this.EntityDb.AsQueryable(); if (!string.IsNullOrWhiteSpace(whereStr)) { query = query.Where(whereStr); } var fieldList = await query.Distinct().Select<string>(fieldName).ToListAsync(); if(fieldList != null && fieldList.Count >0) { var sortedList = fieldList .OrderBy(name => GetSortIndex(name)) // 主排序:数字前缀 .ThenBy(name => name) // 次排序:中文拼音顺序 .ToList(); list = sortedList.Select(s => new CListItem(s)).ToList(); } } return list; }
以上就是一些简单案例上对于watch的使用,用于处理多级关联更新的情况下的功能实现。