在Vue3+ElementPlus前端中,使用watch监控对象变化,实现字典列表的级联更新处理

在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的使用,用于处理多级关联更新的情况下的功能实现。

相关推荐
伍华聪25 天前
在Vue3+ElementPlus前端中增加对@wangeditor的富文本编辑器和上传文件的处理的封装,实现系统新闻资讯的管理
sqlsugar
伍华聪1 个月前
在Vue3+ElementPlus前端中增加表格记录选择的自定义组件,通过结合Popover 弹出框和Input输入框或者按钮选择实现
sqlsugar·vue3+typescript
伍华聪1 个月前
在SqlSugar的开发框架的Vue3+ElementPlus前端中增加对报表模块的封装处理,实现常规报表的快速处理
sqlsugar
gc_22993 个月前
采用SqlSugarClient创建数据库实例引发的异步调用问题
sqlsugar·sqlsugarclient·sqlsugarscope
伍华聪4 个月前
在SqlSugar的开发框架中增加对低代码EAV模型(实体-属性-值)的WebAPI实现支持
sqlsugar·mongodb数据库·eav模型设计
三天不学习6 个月前
【并发控制、更新、版本控制】.NET开源ORM框架 SqlSugar 系列
开源·.net·orm·sqlsugar
三天不学习7 个月前
【Delete 删除数据语法合集】.NET开源ORM框架 SqlSugar 系列
后端·开源·.net·orm·微软技术·sqlsugar
三天不学习7 个月前
【update 更新数据语法合集】.NET开源ORM框架 SqlSugar 系列
数据库·后端·c#·.net·orm·sqlsugar
梁萌7 个月前
SqlSugar-文章目录
orm·sqlsugar