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

相关推荐
代码or搬砖2 小时前
Vue生命周期总结(四个阶段,八个钩子函数)
前端·javascript·vue.js
VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue超市管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
梵尔纳多2 小时前
第一个 Electron 程序
前端·javascript·electron
鹏北海-RemHusband2 小时前
记录一次微前端改造:把 10+ 个独立 Vue 项目整合到一起
前端·javascript·vue.js
程序员小寒2 小时前
前端高频面试题之Promise相关方法
前端·javascript·面试
小圣贤君2 小时前
从零到一:打造专业级小说地图设计工具的技术实践
vue.js·electron·写作·canvas·小说·小说地图
阿蒙Amon2 小时前
JavaScript学习笔记:8.日期和时间
javascript·笔记·学习
宁雨桥2 小时前
前端跨页面通信:从基础到工程化的全面指南
前端·vue.js·react.js