优化IconPicker组件
This commit is contained in:
parent
463088f77c
commit
5abb859559
@ -22,7 +22,7 @@
|
|||||||
v-model:show="showPicker"
|
v-model:show="showPicker"
|
||||||
:width="900"
|
:width="900"
|
||||||
:placement="'right'"
|
:placement="'right'"
|
||||||
:trap-focus="false"
|
:trap-focus="true"
|
||||||
:close-on-esc="true"
|
:close-on-esc="true"
|
||||||
>
|
>
|
||||||
<n-drawer-content :title="t('Select Icon')" :closable="true">
|
<n-drawer-content :title="t('Select Icon')" :closable="true">
|
||||||
@ -32,6 +32,7 @@
|
|||||||
<div class="search-section">
|
<div class="search-section">
|
||||||
<div class="search-controls">
|
<div class="search-controls">
|
||||||
<n-input
|
<n-input
|
||||||
|
ref="searchInputRef"
|
||||||
v-model:value="searchQuery"
|
v-model:value="searchQuery"
|
||||||
:placeholder="t('Search icon name...')"
|
:placeholder="t('Search icon name...')"
|
||||||
clearable
|
clearable
|
||||||
@ -118,8 +119,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, watch } from 'vue'
|
import { ref, computed, watch, nextTick } from 'vue'
|
||||||
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
import { NButton, NDrawer, NDrawerContent, NInput, NSelect, useMessage } from 'naive-ui'
|
import { NButton, NDrawer, NDrawerContent, NInput, NSelect, useMessage } from 'naive-ui'
|
||||||
|
import type { InputInst } from 'naive-ui'
|
||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
import { getIconLibraryConfig, getAvailableIconLibraries, DEFAULT_ICON_LIBRARY } from '@/shared/utils/icon-libraries'
|
import { getIconLibraryConfig, getAvailableIconLibraries, DEFAULT_ICON_LIBRARY } from '@/shared/utils/icon-libraries'
|
||||||
import { t } from '@/shared/i18n'
|
import { t } from '@/shared/i18n'
|
||||||
@ -153,6 +156,7 @@ const loading = ref(false)
|
|||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(200) // 每页200个图标
|
const pageSize = ref(200) // 每页200个图标
|
||||||
const hasMore = ref(true)
|
const hasMore = ref(true)
|
||||||
|
const searchInputRef = ref<InputInst | null>(null)
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const currentLibraryConfig = computed(() => {
|
const currentLibraryConfig = computed(() => {
|
||||||
@ -316,6 +320,9 @@ function selectIcon(iconName: string) {
|
|||||||
|
|
||||||
// 打开弹窗时初始化临时选择
|
// 打开弹窗时初始化临时选择
|
||||||
async function openPicker() {
|
async function openPicker() {
|
||||||
|
// 先打开 drawer,watch 会处理懒加载和聚焦
|
||||||
|
showPicker.value = true
|
||||||
|
|
||||||
// 如果已有图标名称包含前缀,提取图标名称部分
|
// 如果已有图标名称包含前缀,提取图标名称部分
|
||||||
if (selectedIcon.value && selectedIcon.value.includes(':')) {
|
if (selectedIcon.value && selectedIcon.value.includes(':')) {
|
||||||
const [prefix, iconName] = selectedIcon.value.split(':')
|
const [prefix, iconName] = selectedIcon.value.split(':')
|
||||||
@ -325,8 +332,6 @@ async function openPicker() {
|
|||||||
if (correctLibrary) {
|
if (correctLibrary) {
|
||||||
// 切换到对应的图标库
|
// 切换到对应的图标库
|
||||||
currentLibrary.value = correctLibrary.name
|
currentLibrary.value = correctLibrary.name
|
||||||
// 切换库后重新加载图标数据
|
|
||||||
await loadAllIcons()
|
|
||||||
// 设置临时选择的图标名称(不包含前缀)
|
// 设置临时选择的图标名称(不包含前缀)
|
||||||
tempSelectedIcon.value = iconName
|
tempSelectedIcon.value = iconName
|
||||||
// 自动设置搜索关键词为图标名称,显示相关搜索结果
|
// 自动设置搜索关键词为图标名称,显示相关搜索结果
|
||||||
@ -335,7 +340,6 @@ async function openPicker() {
|
|||||||
// 如果找不到对应的库,保持在All选项
|
// 如果找不到对应的库,保持在All选项
|
||||||
if (currentLibraryConfig.value.name !== 'all') {
|
if (currentLibraryConfig.value.name !== 'all') {
|
||||||
currentLibrary.value = 'all'
|
currentLibrary.value = 'all'
|
||||||
await loadAllIcons()
|
|
||||||
}
|
}
|
||||||
tempSelectedIcon.value = selectedIcon.value
|
tempSelectedIcon.value = selectedIcon.value
|
||||||
// 在All选项中,使用完整图标名称作为搜索关键词
|
// 在All选项中,使用完整图标名称作为搜索关键词
|
||||||
@ -350,7 +354,6 @@ async function openPicker() {
|
|||||||
tempSelectedIcon.value = ''
|
tempSelectedIcon.value = ''
|
||||||
searchQuery.value = ''
|
searchQuery.value = ''
|
||||||
}
|
}
|
||||||
showPicker.value = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认选择
|
// 确认选择
|
||||||
@ -385,15 +388,20 @@ function handleScroll(event: Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索防抖处理(优化性能)
|
||||||
|
const debouncedHandleSearch = useDebounceFn(() => {
|
||||||
|
currentPage.value = 1
|
||||||
|
}, 300)
|
||||||
|
|
||||||
// 处理搜索变化
|
// 处理搜索变化
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
// 搜索时重置分页
|
debouncedHandleSearch()
|
||||||
currentPage.value = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理图标库切换
|
// 处理图标库切换
|
||||||
async function handleLibraryChange(newLibrary: string) {
|
async function handleLibraryChange(newLibrary: string) {
|
||||||
currentLibrary.value = newLibrary
|
currentLibrary.value = newLibrary
|
||||||
|
// 切换库时重新加载图标
|
||||||
await loadAllIcons()
|
await loadAllIcons()
|
||||||
// 保持搜索关键词,只清空临时选择
|
// 保持搜索关键词,只清空临时选择
|
||||||
tempSelectedIcon.value = ''
|
tempSelectedIcon.value = ''
|
||||||
@ -430,9 +438,20 @@ watch(() => props.modelValue, (newValue) => {
|
|||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
// 组件挂载时加载图标
|
// 监听 drawer 打开状态,确保输入框可以获得焦点并懒加载图标
|
||||||
onMounted(() => {
|
watch(showPicker, async (isOpen) => {
|
||||||
loadAllIcons()
|
if (isOpen) {
|
||||||
|
// 懒加载:只在打开时加载图标(如果还没加载过)
|
||||||
|
if (allIcons.value.length === 0) {
|
||||||
|
await loadAllIcons()
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawer 打开后,等待 DOM 更新完成再聚焦
|
||||||
|
await nextTick()
|
||||||
|
setTimeout(() => {
|
||||||
|
searchInputRef.value?.focus()
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露给外部的方法:打开选择器
|
// 暴露给外部的方法:打开选择器
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user