Skip to content

Commit

Permalink
feat: prefect icon seletor
Browse files Browse the repository at this point in the history
  • Loading branch information
chansee97 committed Apr 26, 2024
1 parent a9626d3 commit 6ea0c76
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 90 deletions.
2 changes: 2 additions & 0 deletions build/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import UnoCSS from '@unocss/vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import VueDevTools from 'vite-plugin-vue-devtools'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
Expand All @@ -21,6 +22,7 @@ export function createVitePlugins(env: ImportMetaEnv) {
// support vue
vue(),
vueJsx(),
VueDevTools(),

// support unocss
UnoCSS(),
Expand Down
7 changes: 5 additions & 2 deletions locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"cancel": "Cancel",
"confirm": "Confirm",
"close": "Closure",
"reload": "Refresh"
"reload": "Refresh",
"choose": "Choose"
},
"app": {
"loginOut": "Login out",
Expand Down Expand Up @@ -139,7 +140,9 @@
"components": {
"iconSelector": {
"inputPlaceholder": "Select target icon",
"searchPlaceholder": "Search icon"
"searchPlaceholder": "Search icon",
"clearIcon": "Clear icon",
"selectorTitle": "Icon selection"
}
}
}
7 changes: 5 additions & 2 deletions locales/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"confirm": "确认",
"cancel": "取消",
"reload": "刷新",
"close": "关闭"
"close": "关闭",
"choose": "选择"
},
"app": {
"loginOut": "退出登录",
Expand Down Expand Up @@ -74,8 +75,10 @@
},
"components": {
"iconSelector": {
"selectorTitle": "图标选择",
"inputPlaceholder": "选择目标图标",
"searchPlaceholder": "搜索图标"
"searchPlaceholder": "搜索图标",
"clearIcon": "清除图标"
}
},
"login": {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"vite": "^5.2.8",
"vite-bundle-visualizer": "^1.1.0",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "7.1.2",
"vue-tsc": "^2.0.12"
},
"simple-git-hooks": {
Expand Down
164 changes: 164 additions & 0 deletions src/components/common/IconSelect.vue
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>
35 changes: 0 additions & 35 deletions src/components/common/IconSelect/icons.ts

This file was deleted.

51 changes: 0 additions & 51 deletions src/components/common/IconSelect/index.vue

This file was deleted.

0 comments on commit 6ea0c76

Please sign in to comment.