refactor(icon): replace deprecated @iconify/iconify with @iconify/vue

- Remove @iconify/iconify (deprecated), @purge-icons/generated, vite-plugin-purge-icons
- Add @iconify/vue which uses @iconify/utils iconToSVG internally
- Rewrite Icon.vue to use @iconify/vue Icon component instead of manual DOM manipulation
- Pre-load ep/fa/fa-solid icon sets via addCollection for offline support
- Other icon sets (ion, mdi, heroicons, etc.) load from Iconify API on demand
- Remove PurgeIcons() from Vite plugin config
- Verified: all 22 icons on login page render correctly as SVGs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pull/878/head
DevDengChao 2026-03-05 12:26:12 +08:00
parent 860d2c0b29
commit 2520de56b4
6 changed files with 32 additions and 181 deletions

View File

@ -3,7 +3,6 @@ import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx' import VueJsx from '@vitejs/plugin-vue-jsx'
import progress from 'vite-plugin-progress' import progress from 'vite-plugin-progress'
import EslintPlugin from 'vite-plugin-eslint2' import EslintPlugin from 'vite-plugin-eslint2'
import PurgeIcons from 'vite-plugin-purge-icons'
import { ViteEjsPlugin } from 'vite-plugin-ejs' import { ViteEjsPlugin } from 'vite-plugin-ejs'
// @ts-ignore // @ts-ignore
import ElementPlus from 'unplugin-element-plus/vite' import ElementPlus from 'unplugin-element-plus/vite'
@ -29,7 +28,6 @@ export function createVitePlugins() {
VueJsx(), VueJsx(),
UnoCSS(), UnoCSS(),
progress(), progress(),
PurgeIcons(),
ElementPlus({}), ElementPlus({}),
AutoImport({ AutoImport({
include: [ include: [

View File

@ -105,6 +105,6 @@ const include = [
'element-plus/es/components/progress/style/css' 'element-plus/es/components/progress/style/css'
] ]
const exclude = ['@iconify/json'] const exclude: string[] = []
export { include, exclude } export { include, exclude }

View File

@ -31,7 +31,7 @@
"@element-plus/icons-vue": "2.3.2", "@element-plus/icons-vue": "2.3.2",
"@form-create/designer": "^3.4.0", "@form-create/designer": "^3.4.0",
"@form-create/element-ui": "^3.2.37", "@form-create/element-ui": "^3.2.37",
"@iconify/iconify": "^3.1.1", "@iconify/vue": "^5.0.0",
"@microsoft/fetch-event-source": "^2.0.1", "@microsoft/fetch-event-source": "^2.0.1",
"@videojs-player/vue": "^1.0.0", "@videojs-player/vue": "^1.0.0",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.2.1",
@ -90,7 +90,6 @@
"@iconify/json": "^2.2.446", "@iconify/json": "^2.2.446",
"@intlify/unplugin-vue-i18n": "^11.0.7", "@intlify/unplugin-vue-i18n": "^11.0.7",
"@playwright/test": "^1.58.2", "@playwright/test": "^1.58.2",
"@purge-icons/generated": "^0.10.0",
"@types/jsoneditor": "^9.9.6", "@types/jsoneditor": "^9.9.6",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^25.3.3", "@types/node": "^25.3.3",
@ -135,7 +134,6 @@
"vite-plugin-ejs": "^1.7.0", "vite-plugin-ejs": "^1.7.0",
"vite-plugin-eslint2": "^5.0.5", "vite-plugin-eslint2": "^5.0.5",
"vite-plugin-progress": "^0.0.7", "vite-plugin-progress": "^0.0.7",
"vite-plugin-purge-icons": "^0.10.0",
"vite-plugin-svg-icons-ng": "^1.5.2", "vite-plugin-svg-icons-ng": "^1.5.2",
"vite-plugin-top-level-await": "^1.6.0", "vite-plugin-top-level-await": "^1.6.0",
"vue-eslint-parser": "^10.4.0", "vue-eslint-parser": "^10.4.0",

View File

@ -17,9 +17,9 @@ importers:
'@form-create/element-ui': '@form-create/element-ui':
specifier: ^3.2.37 specifier: ^3.2.37
version: 3.2.37(vue@3.5.29(typescript@5.9.3)) version: 3.2.37(vue@3.5.29(typescript@5.9.3))
'@iconify/iconify': '@iconify/vue':
specifier: ^3.1.1 specifier: ^5.0.0
version: 3.1.1 version: 5.0.0(vue@3.5.29(typescript@5.9.3))
'@microsoft/fetch-event-source': '@microsoft/fetch-event-source':
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1 version: 2.0.1
@ -189,9 +189,6 @@ importers:
'@playwright/test': '@playwright/test':
specifier: ^1.58.2 specifier: ^1.58.2
version: 1.58.2 version: 1.58.2
'@purge-icons/generated':
specifier: ^0.10.0
version: 0.10.0
'@types/jsoneditor': '@types/jsoneditor':
specifier: ^9.9.6 specifier: ^9.9.6
version: 9.9.6 version: 9.9.6
@ -324,9 +321,6 @@ importers:
vite-plugin-progress: vite-plugin-progress:
specifier: ^0.0.7 specifier: ^0.0.7
version: 0.0.7(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)) version: 0.0.7(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
vite-plugin-purge-icons:
specifier: ^0.10.0
version: 0.10.0(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
vite-plugin-svg-icons-ng: vite-plugin-svg-icons-ng:
specifier: ^1.5.2 specifier: ^1.5.2
version: 1.5.2(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)) version: 1.5.2(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
@ -1483,14 +1477,6 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@iconify/iconify@2.1.2':
resolution: {integrity: sha512-QcUzFeEWkE/mW+BVtEGmcWATClcCOIJFiYUD/PiCWuTcdEA297o8D4oN6Ra44WrNOHu1wqNW4J0ioaDIiqaFOQ==}
deprecated: no longer maintained, switch to modern iconify-icon web component
'@iconify/iconify@3.1.1':
resolution: {integrity: sha512-1nemfyD/OJzh9ALepH7YfuuP8BdEB24Skhd8DXWh0hzcOxImbb1ZizSZkpCzAwSZSGcJFmscIBaBQu+yLyWaxQ==}
deprecated: no longer maintained, switch to modern iconify-icon web component
'@iconify/json@2.2.446': '@iconify/json@2.2.446':
resolution: {integrity: sha512-FrZToC6ONvIVmOkFhcNMGJYvIKJ4UPzdKE5hgAUkQijYz+ZWx74OZBHExp8osCLMEmyxrUPTFg2oynlpGWLsTw==} resolution: {integrity: sha512-FrZToC6ONvIVmOkFhcNMGJYvIKJ4UPzdKE5hgAUkQijYz+ZWx74OZBHExp8osCLMEmyxrUPTFg2oynlpGWLsTw==}
@ -1500,6 +1486,11 @@ packages:
'@iconify/utils@3.1.0': '@iconify/utils@3.1.0':
resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==}
'@iconify/vue@5.0.0':
resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==}
peerDependencies:
vue: '>=3'
'@intlify/bundle-utils@11.0.7': '@intlify/bundle-utils@11.0.7':
resolution: {integrity: sha512-fEO3CJGPymxieGh8BHox7d6stgajDQae7wgpH6YYw7WX+cdW6jTTXyljZqz7OV3JcwlS9M9UHSoO+YwiO56IhA==} resolution: {integrity: sha512-fEO3CJGPymxieGh8BHox7d6stgajDQae7wgpH6YYw7WX+cdW6jTTXyljZqz7OV3JcwlS9M9UHSoO+YwiO56IhA==}
engines: {node: '>= 20'} engines: {node: '>= 20'}
@ -1865,12 +1856,6 @@ packages:
'@polka/url@1.0.0-next.29': '@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@purge-icons/core@0.10.0':
resolution: {integrity: sha512-AtJbZv5Yy+vWX5v32DPTr+CW7AkSK8HJx52orDbrYt/9s4lGM2t4KKAmwaTQEH2HYr2HVh1mlqs54/S1s3WT1g==}
'@purge-icons/generated@0.10.0':
resolution: {integrity: sha512-I+1yN7/yDy/eZzfhAZqKF8Z6FM8D/O1vempbPrHJ0m9HlZwvf8sWXOArPJ2qRQGB6mJUVSpaXkoGBuoz1GQX5A==}
'@quansync/fs@1.0.0': '@quansync/fs@1.0.0':
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
@ -2744,9 +2729,6 @@ packages:
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
axios@0.26.1:
resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==}
axios@1.13.6: axios@1.13.6:
resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==}
@ -3067,9 +3049,6 @@ packages:
cropperjs@1.6.2: cropperjs@1.6.2:
resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==} resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==}
cross-fetch@3.2.0:
resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
cross-spawn@7.0.6: cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -4424,15 +4403,6 @@ packages:
node-fetch-native@1.6.7: node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
node-html-parser@7.0.2: node-html-parser@7.0.2:
resolution: {integrity: sha512-DxodLVh7a6JMkYzWyc8nBX9MaF4M0lLFYkJHlWOiu7+9/I6mwNK9u5TbAMC7qfqDJEPX9OIoWA2A9t4C2l1mUQ==} resolution: {integrity: sha512-DxodLVh7a6JMkYzWyc8nBX9MaF4M0lLFYkJHlWOiu7+9/I6mwNK9u5TbAMC7qfqDJEPX9OIoWA2A9t4C2l1mUQ==}
@ -4868,10 +4838,6 @@ packages:
resolution: {integrity: sha512-7H8oH5A8+L96pbBTPCt/rZrwayEhZY5/ejhdk9nRODH32H1v7+bfkaCr+kS15DcGQ7VC1HcWdQVNABFYgrMOzg==} resolution: {integrity: sha512-7H8oH5A8+L96pbBTPCt/rZrwayEhZY5/ejhdk9nRODH32H1v7+bfkaCr+kS15DcGQ7VC1HcWdQVNABFYgrMOzg==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
rollup-plugin-purge-icons@0.10.0:
resolution: {integrity: sha512-GD2ftg4L9G/sagIhtCmBn5vdyzePOisniythubpbywP0Q3ix9rZuDeFvgXTPemOsc22pvH7t22ryYQIl0rwGog==}
engines: {node: '>= 12'}
rollup@4.59.0: rollup@4.59.0:
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -5165,9 +5131,6 @@ packages:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
ts-api-utils@1.4.3: ts-api-utils@1.4.3:
resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
engines: {node: '>=16'} engines: {node: '>=16'}
@ -5387,12 +5350,6 @@ packages:
peerDependencies: peerDependencies:
vite: '>2.0.0-0' vite: '>2.0.0-0'
vite-plugin-purge-icons@0.10.0:
resolution: {integrity: sha512-4fMJKQuBu9lAPJWjqGEytRaxty1pP9bWgQLA68dwbbaCXu6NBrOUb/3kMaUc7TP09kerEk+qTriCk05OZXpjwA==}
engines: {node: '>= 12'}
peerDependencies:
vite: '>=2'
vite-plugin-svg-icons-ng@1.5.2: vite-plugin-svg-icons-ng@1.5.2:
resolution: {integrity: sha512-A68obs8XDT+q8q8dKyjrT/v0qw8h5pEBKXJ27aUXjARYeJ6MNvhIhRLLiUwnSrbn/B4TBF4UVaWRXKftAqP7+A==} resolution: {integrity: sha512-A68obs8XDT+q8q8dKyjrT/v0qw8h5pEBKXJ27aUXjARYeJ6MNvhIhRLLiUwnSrbn/B4TBF4UVaWRXKftAqP7+A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@ -5541,9 +5498,6 @@ packages:
web-storage-cache@1.1.1: web-storage-cache@1.1.1:
resolution: {integrity: sha512-D0MieGooOs8RpsrK+vnejXnvh4OOv/+lTFB35JRkJJQt+uOjPE08XpaE0QBLMTRu47B1KGT/Nq3Gbag3Orinzw==} resolution: {integrity: sha512-D0MieGooOs8RpsrK+vnejXnvh4OOv/+lTFB35JRkJJQt+uOjPE08XpaE0QBLMTRu47B1KGT/Nq3Gbag3Orinzw==}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webpack-virtual-modules@0.6.2: webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
@ -5555,9 +5509,6 @@ packages:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'} engines: {node: '>=18'}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
which-module@2.0.1: which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
@ -6938,16 +6889,6 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {} '@humanwhocodes/retry@0.4.3': {}
'@iconify/iconify@2.1.2':
dependencies:
cross-fetch: 3.2.0
transitivePeerDependencies:
- encoding
'@iconify/iconify@3.1.1':
dependencies:
'@iconify/types': 2.0.0
'@iconify/json@2.2.446': '@iconify/json@2.2.446':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
@ -6961,6 +6902,11 @@ snapshots:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
mlly: 1.8.1 mlly: 1.8.1
'@iconify/vue@5.0.0(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@iconify/types': 2.0.0
vue: 3.5.29(typescript@5.9.3)
'@intlify/bundle-utils@11.0.7(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))': '@intlify/bundle-utils@11.0.7(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))':
dependencies: dependencies:
'@intlify/message-compiler': 11.2.8 '@intlify/message-compiler': 11.2.8
@ -7266,21 +7212,6 @@ snapshots:
'@polka/url@1.0.0-next.29': {} '@polka/url@1.0.0-next.29': {}
'@purge-icons/core@0.10.0':
dependencies:
'@iconify/iconify': 2.1.2
axios: 0.26.1(debug@4.4.3)
debug: 4.4.3
fast-glob: 3.3.3
fs-extra: 10.1.0
transitivePeerDependencies:
- encoding
- supports-color
'@purge-icons/generated@0.10.0':
dependencies:
'@iconify/iconify': 3.1.1
'@quansync/fs@1.0.0': '@quansync/fs@1.0.0':
dependencies: dependencies:
quansync: 1.0.0 quansync: 1.0.0
@ -8263,15 +8194,9 @@ snapshots:
postcss: 8.5.8 postcss: 8.5.8
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
axios@0.26.1(debug@4.4.3):
dependencies:
follow-redirects: 1.15.11(debug@4.4.3)
transitivePeerDependencies:
- debug
axios@1.13.6: axios@1.13.6:
dependencies: dependencies:
follow-redirects: 1.15.11(debug@4.4.3) follow-redirects: 1.15.11
form-data: 4.0.5 form-data: 4.0.5
proxy-from-env: 1.1.0 proxy-from-env: 1.1.0
transitivePeerDependencies: transitivePeerDependencies:
@ -8626,12 +8551,6 @@ snapshots:
cropperjs@1.6.2: {} cropperjs@1.6.2: {}
cross-fetch@3.2.0:
dependencies:
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
cross-spawn@7.0.6: cross-spawn@7.0.6:
dependencies: dependencies:
path-key: 3.1.1 path-key: 3.1.1
@ -9382,9 +9301,7 @@ snapshots:
dependencies: dependencies:
tabbable: 6.4.0 tabbable: 6.4.0
follow-redirects@1.15.11(debug@4.4.3): follow-redirects@1.15.11: {}
optionalDependencies:
debug: 4.4.3
foreground-child@3.3.1: foreground-child@3.3.1:
dependencies: dependencies:
@ -10114,10 +10031,6 @@ snapshots:
node-fetch-native@1.6.7: {} node-fetch-native@1.6.7: {}
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
node-html-parser@7.0.2: node-html-parser@7.0.2:
dependencies: dependencies:
css-select: 5.2.2 css-select: 5.2.2
@ -10523,14 +10436,6 @@ snapshots:
dependencies: dependencies:
magic-string: 0.30.21 magic-string: 0.30.21
rollup-plugin-purge-icons@0.10.0:
dependencies:
'@purge-icons/core': 0.10.0
'@purge-icons/generated': 0.10.0
transitivePeerDependencies:
- encoding
- supports-color
rollup@4.59.0: rollup@4.59.0:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.8
@ -10873,8 +10778,6 @@ snapshots:
totalist@3.0.1: {} totalist@3.0.1: {}
tr46@0.0.3: {}
ts-api-utils@1.4.3(typescript@5.9.3): ts-api-utils@1.4.3(typescript@5.9.3):
dependencies: dependencies:
typescript: 5.9.3 typescript: 5.9.3
@ -11136,16 +11039,6 @@ snapshots:
rd: 2.0.1 rd: 2.0.1
vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
vite-plugin-purge-icons@0.10.0(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)):
dependencies:
'@purge-icons/core': 0.10.0
'@purge-icons/generated': 0.10.0
rollup-plugin-purge-icons: 0.10.0
vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
transitivePeerDependencies:
- encoding
- supports-color
vite-plugin-svg-icons-ng@1.5.2(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)): vite-plugin-svg-icons-ng@1.5.2(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)):
dependencies: dependencies:
fast-glob: 3.3.3 fast-glob: 3.3.3
@ -11295,8 +11188,6 @@ snapshots:
web-storage-cache@1.1.1: {} web-storage-cache@1.1.1: {}
webidl-conversions@3.0.1: {}
webpack-virtual-modules@0.6.2: {} webpack-virtual-modules@0.6.2: {}
whatwg-encoding@3.1.1: whatwg-encoding@3.1.1:
@ -11305,11 +11196,6 @@ snapshots:
whatwg-mimetype@4.0.0: {} whatwg-mimetype@4.0.0: {}
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
which-module@2.0.1: {} which-module@2.0.1: {}
which@1.3.1: which@1.3.1:

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import Iconify from '@purge-icons/generated' import { Icon as IconifyIcon } from '@iconify/vue'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'Icon' }) defineOptions({ name: 'Icon' })
@ -20,57 +20,16 @@ const props = defineProps({
svgClass: propTypes.string.def('') svgClass: propTypes.string.def('')
}) })
const elRef = ref<ElRef>(null)
const isLocal = computed(() => props.icon?.startsWith('svg-icon:')) const isLocal = computed(() => props.icon?.startsWith('svg-icon:'))
const symbolId = computed(() => { const symbolId = computed(() => {
return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon
}) })
const getIconifyStyle = computed(() => {
const { color, size } = props
return {
fontSize: `${size}px`,
height: '1em',
color
}
})
const getSvgClass = computed(() => { const getSvgClass = computed(() => {
const { svgClass } = props const { svgClass } = props
return `iconify ${svgClass}` return `iconify ${svgClass}`
}) })
const updateIcon = async (icon: string) => {
if (unref(isLocal)) return
const el = unref(elRef)
if (!el) return
await nextTick()
if (!icon) return
const svg = Iconify.renderSVG(icon, {})
if (svg) {
el.textContent = ''
el.appendChild(svg)
} else {
const span = document.createElement('span')
span.className = 'iconify'
span.dataset.icon = icon
el.textContent = ''
el.appendChild(span)
}
}
watch(
() => props.icon,
(icon: string) => {
updateIcon(icon)
}
)
</script> </script>
<template> <template>
@ -79,8 +38,11 @@ watch(
<use :xlink:href="symbolId" /> <use :xlink:href="symbolId" />
</svg> </svg>
<span v-else ref="elRef" :class="$attrs.class" :style="getIconifyStyle"> <IconifyIcon
<span :class="getSvgClass" :data-icon="symbolId"></span> v-else
</span> :icon="symbolId"
:class="getSvgClass"
:style="{ fontSize: `${size}px`, color }"
/>
</ElIcon> </ElIcon>
</template> </template>

View File

@ -1,3 +1,10 @@
import 'virtual:svg-icons-register' import 'virtual:svg-icons-register'
import '@purge-icons/generated' import { addCollection } from '@iconify/vue'
import epIcons from '@iconify/json/json/ep.json'
import faIcons from '@iconify/json/json/fa.json'
import faSolidIcons from '@iconify/json/json/fa-solid.json'
addCollection(epIcons)
addCollection(faIcons)
addCollection(faSolidIcons)