-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
177 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
<script setup lang="ts"> | ||
interface Props { | ||
disabled?: boolean | ||
} | ||
const props = withDefaults(defineProps<Props>(), { | ||
disabled: false, | ||
}) | ||
interface IconList { | ||
prefix: string | ||
icons: string[] | ||
title: string | ||
total: number | ||
categories: Record<string, string[]> | ||
} | ||
const value = defineModel('value', { type: String }) | ||
// 包含的图标库系列名 | ||
const nameList = ['icon-park-outline', 'carbon'] | ||
// 获取单个图标库数据 | ||
async function fetchIconList(name: string): Promise<IconList> { | ||
return await fetch(`https://api.iconify.design/collection?prefix=${name}`).then(res => res.json()) | ||
} | ||
// 获取所有图标库数据 | ||
async function fetchIconAllList(nameList: string[]) { | ||
const namePromises = nameList.map(name => fetchIconList(name)) | ||
const targets = await Promise.all(namePromises) | ||
return targets.map((i) => { | ||
i.icons = Object.entries(i.categories).reduce((prev, next) => { | ||
const [_key, value] = next | ||
return prev.concat(value) | ||
}, [] as string[]) | ||
return i | ||
}) | ||
} | ||
const iconLists = shallowRef<IconList[]>([]) | ||
onMounted(async () => { | ||
iconLists.value = await fetchIconAllList(nameList) | ||
}) | ||
// 当前tab | ||
const currentTab = shallowRef(0) | ||
// 当前tag | ||
const currentTag = shallowRef('') | ||
// 切换tab | ||
function handleChangeTab(index: number) { | ||
currentTab.value = index | ||
currentTag.value = '' | ||
} | ||
// 搜索图标输入框值 | ||
const searchValue = ref('') | ||
// 当前页数 | ||
const currentPage = shallowRef(1) | ||
// 选择分类tag | ||
function handleSelectIconTag(icon: string) { | ||
currentTag.value = currentTag.value === icon ? '' : icon | ||
currentPage.value = 1 | ||
} | ||
// 包含当前分类或所有图标列表 | ||
const icons = computed(() => { | ||
const hasTag = !!currentTag.value | ||
if (hasTag) | ||
return iconLists.value[currentTab.value]?.categories[currentTag.value] | ||
else | ||
return iconLists.value[currentTab.value].icons | ||
}) | ||
// 符合搜索条件的图标列表 | ||
const visibleIcons = computed(() => { | ||
return icons.value | ||
?.filter(i => i.includes(searchValue.value)) | ||
?.slice((currentPage.value - 1) * 200, (currentPage.value) * 200) | ||
}) | ||
const showModal = ref(false) | ||
// 选择图标 | ||
function handleSelectIcon(icon: string) { | ||
value.value = icon | ||
showModal.value = false | ||
} | ||
// 清除图标 | ||
function clearIcon() { | ||
value.value = '' | ||
showModal.value = false | ||
} | ||
</script> | ||
|
||
<template> | ||
<n-input-group disabled> | ||
<n-button v-if="value" :disabled="props.disabled" type="primary"> | ||
<template #icon> | ||
<nova-icon :icon="value" /> | ||
</template> | ||
</n-button> | ||
<n-input :value="value" readonly :placeholder="$t('components.iconSelector.inputPlaceholder')" /> | ||
<n-button type="primary" ghost :disabled="props.disabled" @click="showModal = true"> | ||
{{ $t('common.choose') }} | ||
</n-button> | ||
</n-input-group> | ||
<n-modal | ||
v-model:show="showModal" preset="card" :title="$t('components.iconSelector.selectorTitle')" size="small" class="w-800px" :bordered="false" | ||
> | ||
<template #header-extra> | ||
<n-button type="warning" size="small" ghost @click="clearIcon"> | ||
{{ $t('components.iconSelector.clearIcon') }} | ||
</n-button> | ||
</template> | ||
|
||
<n-tabs :value="currentTab" type="line" animated placement="left" @update:value="handleChangeTab"> | ||
<n-tab-pane v-for="(list, index) in iconLists" :key="list.prefix" :name="index" :tab="list.title"> | ||
<n-flex vertical> | ||
<n-flex size="small"> | ||
<n-tag | ||
v-for="(_v, k) in list.categories" :key="k" | ||
:checked="currentTag === k" round checkable size="small" | ||
@update:checked="handleSelectIconTag(k)" | ||
> | ||
{{ k }} | ||
</n-tag> | ||
</n-flex> | ||
|
||
<n-input | ||
v-model:value="searchValue" type="text" clearable | ||
:placeholder="$t('components.iconSelector.searchPlaceholder')" | ||
/> | ||
|
||
<div class="h-410px"> | ||
<n-flex :size="2"> | ||
<n-el | ||
v-for="(icon) in visibleIcons" :key="icon" | ||
class="hover:(text-[var(--primary-color)] ring-1) ring-[var(--primary-color)] p-1 rounded flex-center" | ||
:title="`${list.prefix}:${icon}`" | ||
@click="handleSelectIcon(`${list.prefix}:${icon}`)" | ||
> | ||
<nova-icon :icon="`${list.prefix}:${icon}`" :size="24" /> | ||
</n-el> | ||
<n-empty v-if="visibleIcons.length === 0" class="w-full" /> | ||
</n-flex> | ||
</div> | ||
|
||
<n-flex justify="center"> | ||
<n-pagination | ||
v-model:page="currentPage" | ||
:item-count="icons?.length" | ||
:page-size="200" | ||
/> | ||
</n-flex> | ||
</n-flex> | ||
</n-tab-pane> | ||
</n-tabs> | ||
</n-modal> | ||
</template> |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.