import type { IconifyIconStructure } from '@vben-core/icons'; import { addIcon } from '@vben-core/icons'; let loaded = false; if (!loaded) { loadSvgIcons(); loaded = true; } function parseSvg(svgData: string): IconifyIconStructure { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(svgData, 'image/svg+xml'); const svgElement = xmlDoc.documentElement; // 提取 SVG 根元素的关键样式属性 const getAttrs = (el: Element, attrs: string[]) => attrs .map((attr) => el.hasAttribute(attr) ? `${attr}="${el.getAttribute(attr)}"` : '', ) .filter(Boolean) .join(' '); const rootAttrs = getAttrs(svgElement, [ 'fill', 'stroke', 'fill-rule', 'stroke-width', ]); const svgContent = [...svgElement.childNodes] .filter((node) => node.nodeType === Node.ELEMENT_NODE) .map((node) => new XMLSerializer().serializeToString(node)) .join(''); // 若根有属性,用一个 g 标签包裹内容并继承属性 const body = rootAttrs ? `${svgContent}` : svgContent; const viewBoxValue = svgElement.getAttribute('viewBox') || ''; const [left, top, width, height] = viewBoxValue.split(' ').map((val) => { const num = Number(val); return Number.isNaN(num) ? undefined : num; }); return { body, height, left, top, width, }; } /** * 自定义的svg图片转化为组件 * @example ./svg/avatar.svg * */ async function loadSvgIcons() { const svgEagers = import.meta.glob('./icons/**', { eager: true, query: '?raw', }); await Promise.all( Object.entries(svgEagers).map((svg) => { const [key, body] = svg as [string, string | { default: string }]; // ./icons/xxxx.svg => xxxxxx const start = key.lastIndexOf('/') + 1; const end = key.lastIndexOf('.'); const iconName = key.slice(start, end); return addIcon(`svg:${iconName}`, { ...parseSvg(typeof body === 'object' ? body.default : body), }); }), ); }