fix: keep admin icons local

This commit is contained in:
2026-06-12 19:42:38 +08:00
parent 8567bd6c47
commit a2787c51a1
5 changed files with 180 additions and 42 deletions
@@ -16,6 +16,43 @@ interface IconifyResponse {
const PENDING_REQUESTS: Recordable<Promise<string[]>> = {}; const PENDING_REQUESTS: Recordable<Promise<string[]>> = {};
const LOCAL_ICONS_MAP: Recordable<string[]> = {
carbon: [
'carbon:agent-detached',
'carbon:align-box-middle-right',
'carbon:align-vertical-top',
'carbon:arrange',
'carbon:arrange-horizontal',
'carbon:checkmark',
'carbon:chevron-down',
'carbon:circle-dash',
'carbon:close',
'carbon:edit',
'carbon:email',
'carbon:renew',
'carbon:reset',
'carbon:user-multiple',
],
ep: [
'ep:caret-right',
'ep:delete',
'ep:edit',
'ep:plus',
'ep:refresh',
],
lucide: [
'lucide:bot',
'lucide:check-square',
'lucide:layout-grid',
'lucide:library-big',
'lucide:list',
'lucide:megaphone',
'lucide:square-check-big',
'lucide:square-user',
'lucide:workflow',
],
};
/** /**
* 通过Iconify接口获取图标集数据。 * 通过Iconify接口获取图标集数据。
* 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。 * 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。
@@ -24,6 +61,10 @@ const PENDING_REQUESTS: Recordable<Promise<string[]>> = {};
* @returns 图标集中包含的所有图标名称 * @returns 图标集中包含的所有图标名称
*/ */
export async function fetchIconsData(prefix: string): Promise<string[]> { export async function fetchIconsData(prefix: string): Promise<string[]> {
if (LOCAL_ICONS_MAP[prefix]) {
ICONS_MAP[prefix] = LOCAL_ICONS_MAP[prefix];
return ICONS_MAP[prefix];
}
if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) { if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) {
return ICONS_MAP[prefix]; return ICONS_MAP[prefix];
} }
@@ -0,0 +1,94 @@
import type { Component, PropType } from 'vue';
import { defineComponent, h } from 'vue';
import { Icon } from '@iconify/vue';
import {
AlignEndHorizontal,
AlignHorizontalSpaceAround,
AlignStartVertical,
Bot,
BookOpen,
Check,
CheckCircle,
CheckSquare,
ChevronDown,
ChevronRight,
Circle,
Edit,
LayoutGrid,
List,
Mail,
Megaphone,
Plus,
RefreshCw,
SquareCheckBig,
Trash2,
TriangleAlert,
UserRound,
Users,
Workflow,
X,
} from './lucide';
const localIconMap: Record<string, Component> = {
'carbon:agent-detached': Bot,
'carbon:align-box-middle-right': AlignEndHorizontal,
'carbon:align-vertical-top': AlignStartVertical,
'carbon:arrange': LayoutGrid,
'carbon:arrange-horizontal': AlignHorizontalSpaceAround,
'carbon:checkmark': Check,
'carbon:checkmark-filled': CheckCircle,
'carbon:chevron-down': ChevronDown,
'carbon:circle-dash': Circle,
'carbon:close': X,
'carbon:edit': Edit,
'carbon:email': Mail,
'carbon:renew': RefreshCw,
'carbon:reset': RefreshCw,
'carbon:user-multiple': Users,
'carbon:warning-filled': TriangleAlert,
'ep:caret-right': ChevronRight,
'ep:delete': Trash2,
'ep:edit': Edit,
'ep:plus': Plus,
'ep:refresh': RefreshCw,
'ep:refresh-left': RefreshCw,
'ep:refresh-right': RefreshCw,
'lucide:bot': Bot,
'lucide:check-square': CheckSquare,
'lucide:layout-grid': LayoutGrid,
'lucide:library-big': BookOpen,
'lucide:list': List,
'lucide:megaphone': Megaphone,
'lucide:square-check-big': SquareCheckBig,
'lucide:square-user': UserRound,
'lucide:workflow': Workflow,
};
function normalizeIconName(icon: unknown) {
return typeof icon === 'string' ? icon.replace(/^i-/, '') : '';
}
const IconifyIcon = defineComponent({
inheritAttrs: false,
name: 'IconifyIcon',
props: {
icon: {
type: [String, Object, Function] as PropType<Component | string>,
default: undefined,
},
},
setup(props, { attrs }) {
return () => {
const localIcon = localIconMap[normalizeIconName(props.icon)];
if (localIcon) {
return h(localIcon, attrs);
}
return h(Icon, { ...attrs, icon: props.icon });
};
},
});
export { IconifyIcon };
+3 -6
View File
@@ -1,11 +1,8 @@
export * from './create-icon'; export * from './create-icon';
export * from './iconify-icon';
export * from './lucide'; export * from './lucide';
export type { IconifyIcon as IconifyIconStructure } from '@iconify/vue'; export type { IconifyIcon as IconifyIconStructure } from '@iconify/vue';
export { export { addCollection, addIcon, listIcons } from '@iconify/vue';
addCollection,
addIcon,
Icon as IconifyIcon,
listIcons,
} from '@iconify/vue';
@@ -3,21 +3,7 @@ import type { Component } from 'vue';
import { computed } from 'vue'; import { computed } from 'vue';
import { import { IconDefault, IconifyIcon } from '@vben-core/icons';
AlignEndHorizontal,
AlignHorizontalSpaceAround,
AlignStartVertical,
Bot,
BookOpen,
IconDefault,
IconifyIcon,
LayoutGrid,
Mail,
Megaphone,
UserRound,
Users,
Workflow,
} from '@vben-core/icons';
import { import {
isFunction, isFunction,
isHttpUrl, isHttpUrl,
@@ -31,30 +17,10 @@ const props = defineProps<{
icon?: Component | Function | string; icon?: Component | Function | string;
}>(); }>();
const localIconMap: Record<string, Component> = {
'carbon:agent-detached': Bot,
'carbon:align-box-middle-right': AlignEndHorizontal,
'carbon:align-vertical-top': AlignStartVertical,
'carbon:arrange': LayoutGrid,
'carbon:arrange-horizontal': AlignHorizontalSpaceAround,
'carbon:email': Mail,
'carbon:user-multiple': Users,
'lucide:bot': Bot,
'lucide:library-big': BookOpen,
'lucide:megaphone': Megaphone,
'lucide:square-user': UserRound,
'lucide:workflow': Workflow,
};
const isRemoteIcon = computed(() => { const isRemoteIcon = computed(() => {
return isString(props.icon) && isHttpUrl(props.icon); return isString(props.icon) && isHttpUrl(props.icon);
}); });
const localIcon = computed(() => {
if (!isString(props.icon)) return;
return localIconMap[props.icon.replace(/^i-/, '')];
});
const isComponent = computed(() => { const isComponent = computed(() => {
const { icon } = props; const { icon } = props;
return !isString(icon) && (isObject(icon) || isFunction(icon)); return !isString(icon) && (isObject(icon) || isFunction(icon));
@@ -63,7 +29,6 @@ const isComponent = computed(() => {
<template> <template>
<component :is="icon as Component" v-if="isComponent" v-bind="$attrs" /> <component :is="icon as Component" v-if="isComponent" v-bind="$attrs" />
<component :is="localIcon" v-else-if="localIcon" v-bind="$attrs" />
<img v-else-if="isRemoteIcon" :src="icon as string" v-bind="$attrs" /> <img v-else-if="isRemoteIcon" :src="icon as string" v-bind="$attrs" />
<IconifyIcon v-else-if="icon" v-bind="$attrs" :icon="icon as string" /> <IconifyIcon v-else-if="icon" v-bind="$attrs" :icon="icon as string" />
<IconDefault v-else-if="fallback" v-bind="$attrs" /> <IconDefault v-else-if="fallback" v-bind="$attrs" />
@@ -16,6 +16,43 @@ interface IconifyResponse {
const PENDING_REQUESTS: Recordable<Promise<string[]>> = {}; const PENDING_REQUESTS: Recordable<Promise<string[]>> = {};
const LOCAL_ICONS_MAP: Recordable<string[]> = {
carbon: [
'carbon:agent-detached',
'carbon:align-box-middle-right',
'carbon:align-vertical-top',
'carbon:arrange',
'carbon:arrange-horizontal',
'carbon:checkmark',
'carbon:chevron-down',
'carbon:circle-dash',
'carbon:close',
'carbon:edit',
'carbon:email',
'carbon:renew',
'carbon:reset',
'carbon:user-multiple',
],
ep: [
'ep:caret-right',
'ep:delete',
'ep:edit',
'ep:plus',
'ep:refresh',
],
lucide: [
'lucide:bot',
'lucide:check-square',
'lucide:layout-grid',
'lucide:library-big',
'lucide:list',
'lucide:megaphone',
'lucide:square-check-big',
'lucide:square-user',
'lucide:workflow',
],
};
/** /**
* 通过Iconify接口获取图标集数据。 * 通过Iconify接口获取图标集数据。
* 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。 * 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。
@@ -24,6 +61,10 @@ const PENDING_REQUESTS: Recordable<Promise<string[]>> = {};
* @returns 图标集中包含的所有图标名称 * @returns 图标集中包含的所有图标名称
*/ */
export async function fetchIconsData(prefix: string): Promise<string[]> { export async function fetchIconsData(prefix: string): Promise<string[]> {
if (LOCAL_ICONS_MAP[prefix]) {
ICONS_MAP[prefix] = LOCAL_ICONS_MAP[prefix];
return ICONS_MAP[prefix];
}
if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) { if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) {
return ICONS_MAP[prefix]; return ICONS_MAP[prefix];
} }