Vue3 ElementPlus el-select 焦点事件数据不回显问题

html 复制代码
<template>
  <el-form :model="form">
    <el-form-item label="选择项">
      <el-select
        v-model="form.selectedValue"
        placeholder="请选择"
        @focus="loadOptions"
        :loading="loading"
      >
        <el-option
          v-for="item in options"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        />
      </el-select>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref, reactive } from 'vue'

const form = reactive({
  selectedValue: ''
})

const options = ref([])
const loading = ref(false)
const hasLoaded = ref(false) // 标记是否已经加载过数据

const loadOptions = async () => {
  // 如果已经加载过,则不再加载
  if (hasLoaded.value) {
    return
  }
  loading.value = true
  try {
    // 模拟异步请求
    const data = await fetchOptions()
    options.value = data
    hasLoaded.value = true // 标记已加载
  } catch (error) {
    console.error('加载选项失败', error)
  } finally {
    loading.value = false
  }
}

const fetchOptions = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { label: '选项1', value: '1' },
        { label: '选项2', value: '2' },
        { label: '选项3', value: '3' }
      ])
    }, 500)
  })
}
</script>

一般由el-select 的焦点事件处理和数据加载时机导致的

解决方案一:使用 @focus 事件加载数据,优化处理逻辑

如代码中的方案2

html 复制代码
<template>
  <el-form :model="form">
    <el-form-item label="选择项">
      <el-select
        v-model="form.selectedValue"
        placeholder="请选择"
        filterable
        @focus="handleFocus"
        @visible-change="handleVisibleChange"
        :remote="true"
        :remote-method="remoteMethod"
        :loading="loading"
      >
        <el-option
          v-for="item in options"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        />
      </el-select>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'

const form = reactive({
  selectedValue: ''
})

const options = ref([])
const loading = ref(false)
const hasLoaded = ref(false) // 标记是否已加载过数据

// 方案1:使用 visible-change 事件
const handleVisibleChange = (visible) => {
  if (visible && !hasLoaded.value) {
    loadOptions()
  }
}

// 方案2:优化 focus 事件处理
const handleFocus = () => {
  if (!hasLoaded.value) {
    loadOptions()
  }
}

const loadOptions = async () => {
  if (loading.value || hasLoaded.value) return
  
  loading.value = true
  try {
    // 模拟异步请求
    const data = await fetchOptions()
    options.value = data
    hasLoaded.value = true // 标记已加载
  } catch (error) {
    console.error('加载选项失败', error)
  } finally {
    loading.value = false
  }
}

const fetchOptions = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { label: '选项1', value: '1' },
        { label: '选项2', value: '2' },
        { label: '选项3', value: '3' }
      ])
    }, 500)
  })
}

// 远程搜索方法
const remoteMethod = (query) => {
  if (query !== '') {
    loading.value = true
    setTimeout(() => {
      loading.value = false
      options.value = [
        { label: `搜索: ${query}1`, value: 'search1' },
        { label: `搜索: ${query}2`, value: 'search2' }
      ]
    }, 200)
  } else {
    options.value = []
  }
}
</script>

解决方案二:使用 :remote="true" 和缓存机制

html 复制代码
<template>
  <el-select
    v-model="form.selectedValue"
    placeholder="请选择"
    filterable
    remote
    :remote-method="handleSearch"
    :loading="loading"
    @focus="handleSelectFocus"
    reserve-keyword
  >
    <el-option
      v-for="item in filteredOptions"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>
</template>

<script setup>
import { ref, reactive, computed } from 'vue'

const form = reactive({
  selectedValue: ''
})

const allOptions = ref([]) // 缓存所有数据
const filteredOptions = ref([])
const loading = ref(false)
const isInitialLoad = ref(true)

// 计算属性,确保选中值对应的选项存在
const computedOptions = computed(() => {
  const options = [...filteredOptions.value]
  
  // 如果当前选中的值不在选项中,添加该选项(用于显示已选中的值)
  if (form.selectedValue && !options.some(opt => opt.value === form.selectedValue)) {
    // 这里可以从缓存中查找,或者添加一个临时选项
    const cached = allOptions.value.find(opt => opt.value === form.selectedValue)
    if (cached) {
      options.unshift(cached)
    }
  }
  
  return options
})

const handleSelectFocus = async () => {
  if (isInitialLoad.value) {
    await loadInitialOptions()
    isInitialLoad.value = false
  }
}

const loadInitialOptions = async () => {
  loading.value = true
  try {
    const data = await fetchOptions()
    allOptions.value = data
    filteredOptions.value = data
  } finally {
    loading.value = false
  }
}

const handleSearch = (query) => {
  if (query) {
    loading.value = true
    setTimeout(() => {
      loading.value = false
      filteredOptions.value = allOptions.value.filter(item => 
        item.label.toLowerCase().includes(query.toLowerCase())
      )
    }, 200)
  } else {
    // 清空搜索时,显示所有选项
    filteredOptions.value = [...allOptions.value]
  }
}
</script>

解决方案三:使用 watch 监听变化并确保数据存在

html 复制代码
<template>
  <el-select
    v-model="form.selectedValue"
    placeholder="请选择"
    @focus="loadOptions"
  >
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>
</template>

<script setup>
import { ref, reactive, watch, onMounted } from 'vue'

const form = reactive({
  selectedValue: ''
})

const options = ref([])
const loaded = ref(false)

// 监听选中值的变化,确保选项存在
watch(() => form.selectedValue, (newVal) => {
  if (newVal && loaded.value) {
    ensureOptionExists(newVal)
  }
})

// 确保选中的值在选项列表中
const ensureOptionExists = (value) => {
  const exists = options.value.some(opt => opt.value === value)
  if (!exists) {
    // 可以添加一个临时选项,或者重新加载数据
    reloadOptionsIfNeeded()
  }
}

const loadOptions = async () => {
  if (loaded.value) return // 已加载则不再重复加载
  
  try {
    const data = await fetchOptions()
    options.value = data
    loaded.value = true
  } catch (error) {
    console.error('加载失败', error)
  }
}

const reloadOptionsIfNeeded = async () => {
  try {
    const data = await fetchOptions()
    options.value = data
  } catch (error) {
    console.error('重新加载失败', error)
  }
}

onMounted(() => {
  // 组件挂载时预加载数据
  loadOptions()
})
</script>

解决方案四:使用 v-if 控制重新渲染(如果以上方案都不能解决)

html 复制代码
<template>
  <el-select
    v-if="selectKey"
    :key="selectKey"
    v-model="form.selectedValue"
    placeholder="请选择"
    @focus="handleFocus"
  >
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>
</template>

<script setup>
import { ref, reactive } from 'vue'

const form = reactive({
  selectedValue: ''
})

const options = ref([])
const selectKey = ref(1) // 用于强制重新渲染的key

const handleFocus = async () => {
  await loadOptions()
  // 强制重新渲染select组件
  selectKey.value += 1
}

const loadOptions = async () => {
  if (options.value.length > 0) return
  
  const data = await fetchOptions()
  options.value = data
}
</script>
  1. 使用 @visible-change 替代 @focus - 更符合下拉框显示/隐藏的时机

  2. 添加加载状态标记 - 避免重复加载

  3. 使用 :remote="true" - Element Plus 内置的远程搜索功能更稳定

  4. 确保选中值在选项中 - 通过计算属性或监听器处理

  5. 推荐方案1

相关推荐
计算机学姐2 分钟前
基于SpringBoot的汽车租赁系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·spring·汽车·推荐算法
不一样的少年_3 分钟前
产品催: 1 天优化 Vue 官网 SEO?我用这个插件半天搞定(不重构 Nuxt)
前端·javascript·vue.js
-dcr5 分钟前
50.智能体
前端·javascript·人工智能·ai·easyui
BingoGo6 分钟前
免费可商用商业级管理后台 CatchAdmin V5 正式发布 插件化与开发效率的全面提升
vue.js·后端·php
哈__22 分钟前
React Native 鸿蒙跨平台开发:LayoutAnimation 实现鸿蒙端页面切换的淡入淡出过渡动画
javascript·react native·react.js
我要敲一万行27 分钟前
前端文件上传
前端·javascript
要加油哦~29 分钟前
算法 | 整理数据结构 | 算法题中,JS 容器的选择
前端·javascript·算法
一 乐9 小时前
婚纱摄影网站|基于ssm + vue婚纱摄影网站系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端
北辰alk10 小时前
Vue 模板引擎深度解析:基于 HTML 的声明式渲染
vue.js
北辰alk10 小时前
Vue 自定义指令完全指南:定义与应用场景详解
vue.js