|
@@ -1,7 +1,7 @@
|
|
|
<script setup lang="ts">
|
|
|
import type { VNode } from 'vue';
|
|
|
|
|
|
-import { computed, h, ref, watch, watchEffect } from 'vue';
|
|
|
+import { computed, ref, watch, watchEffect } from 'vue';
|
|
|
|
|
|
import { usePagination } from '@vben/hooks';
|
|
|
import { EmptyIcon, Grip, listIcons } from '@vben/icons';
|
|
@@ -9,6 +9,7 @@ import { $t } from '@vben/locales';
|
|
|
|
|
|
import {
|
|
|
Button,
|
|
|
+ Input,
|
|
|
Pagination,
|
|
|
PaginationEllipsis,
|
|
|
PaginationFirst,
|
|
@@ -22,11 +23,16 @@ import {
|
|
|
VbenPopover,
|
|
|
} from '@vben-core/shadcn-ui';
|
|
|
|
|
|
-import { refDebounced } from '@vueuse/core';
|
|
|
+import { refDebounced, watchDebounced } from '@vueuse/core';
|
|
|
+
|
|
|
+import { fetchIconsData } from './icons';
|
|
|
|
|
|
interface Props {
|
|
|
pageSize?: number;
|
|
|
+ /** 图标集的名字 */
|
|
|
prefix?: string;
|
|
|
+ /** 是否自动请求API以获得图标集的数据.提供prefix时有效 */
|
|
|
+ autoFetchApi?: boolean;
|
|
|
/**
|
|
|
* 图标列表
|
|
|
*/
|
|
@@ -39,16 +45,19 @@ interface Props {
|
|
|
modelValueProp?: string;
|
|
|
/** 图标样式 */
|
|
|
iconClass?: string;
|
|
|
+ type?: 'icon' | 'input';
|
|
|
}
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
prefix: 'ant-design',
|
|
|
pageSize: 36,
|
|
|
icons: () => [],
|
|
|
- inputComponent: () => h('div'),
|
|
|
iconSlot: 'default',
|
|
|
iconClass: 'size-4',
|
|
|
- modelValueProp: 'value',
|
|
|
+ autoFetchApi: true,
|
|
|
+ modelValueProp: 'modelValue',
|
|
|
+ inputComponent: undefined,
|
|
|
+ type: 'input',
|
|
|
});
|
|
|
|
|
|
const emit = defineEmits<{
|
|
@@ -62,9 +71,28 @@ const currentSelect = ref('');
|
|
|
const currentPage = ref(1);
|
|
|
const keyword = ref('');
|
|
|
const keywordDebounce = refDebounced(keyword, 300);
|
|
|
+const innerIcons = ref<string[]>([]);
|
|
|
+
|
|
|
+watchDebounced(
|
|
|
+ () => props.prefix,
|
|
|
+ async (prefix) => {
|
|
|
+ if (prefix && prefix !== 'svg' && props.autoFetchApi) {
|
|
|
+ innerIcons.value = await fetchIconsData(prefix);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true, debounce: 500, maxWait: 1000 },
|
|
|
+);
|
|
|
+
|
|
|
const currentList = computed(() => {
|
|
|
try {
|
|
|
if (props.prefix) {
|
|
|
+ if (
|
|
|
+ props.prefix !== 'svg' &&
|
|
|
+ props.autoFetchApi &&
|
|
|
+ props.icons.length === 0
|
|
|
+ ) {
|
|
|
+ return innerIcons.value;
|
|
|
+ }
|
|
|
const icons = listIcons('', props.prefix);
|
|
|
if (icons.length === 0) {
|
|
|
console.warn(`No icons found for prefix: ${props.prefix}`);
|
|
@@ -146,18 +174,61 @@ defineExpose({ toggleOpenState, open, close });
|
|
|
content-class="p-0 pt-3"
|
|
|
>
|
|
|
<template #trigger>
|
|
|
- <component
|
|
|
- :is="inputComponent"
|
|
|
- :[modelValueProp]="currentSelect"
|
|
|
- :placeholder="$t('ui.iconPicker.placeholder')"
|
|
|
- >
|
|
|
- <template #[iconSlot]>
|
|
|
- <VbenIcon :icon="currentSelect || Grip" class="size-4" />
|
|
|
- </template>
|
|
|
- </component>
|
|
|
+ <template v-if="props.type === 'input'">
|
|
|
+ <component
|
|
|
+ v-if="props.inputComponent"
|
|
|
+ :is="inputComponent"
|
|
|
+ :[modelValueProp]="currentSelect"
|
|
|
+ :placeholder="$t('ui.iconPicker.placeholder')"
|
|
|
+ role="combobox"
|
|
|
+ :aria-label="$t('ui.iconPicker.placeholder')"
|
|
|
+ aria-expanded="visible"
|
|
|
+ v-bind="$attrs"
|
|
|
+ >
|
|
|
+ <template #[iconSlot]>
|
|
|
+ <VbenIcon
|
|
|
+ :icon="currentSelect || Grip"
|
|
|
+ class="size-4"
|
|
|
+ aria-hidden="true"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </component>
|
|
|
+ <div class="relative w-full" v-else>
|
|
|
+ <Input
|
|
|
+ v-bind="$attrs"
|
|
|
+ v-model="currentSelect"
|
|
|
+ :placeholder="$t('ui.iconPicker.placeholder')"
|
|
|
+ class="h-8 w-full pr-8"
|
|
|
+ role="combobox"
|
|
|
+ :aria-label="$t('ui.iconPicker.placeholder')"
|
|
|
+ aria-expanded="visible"
|
|
|
+ />
|
|
|
+ <VbenIcon
|
|
|
+ :icon="currentSelect || Grip"
|
|
|
+ class="absolute right-1 top-1 size-6"
|
|
|
+ aria-hidden="true"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <VbenIcon
|
|
|
+ :icon="currentSelect || Grip"
|
|
|
+ v-else
|
|
|
+ class="size-4"
|
|
|
+ v-bind="$attrs"
|
|
|
+ />
|
|
|
</template>
|
|
|
<div class="mb-2 flex w-full">
|
|
|
- <component :is="inputComponent" v-bind="searchInputProps" />
|
|
|
+ <component
|
|
|
+ v-if="inputComponent"
|
|
|
+ :is="inputComponent"
|
|
|
+ v-bind="searchInputProps"
|
|
|
+ />
|
|
|
+ <Input
|
|
|
+ v-else
|
|
|
+ class="mx-2 h-8 w-full"
|
|
|
+ :placeholder="$t('ui.iconPicker.search')"
|
|
|
+ v-model="keyword"
|
|
|
+ />
|
|
|
</div>
|
|
|
|
|
|
<template v-if="paginationList.length > 0">
|