diff --git a/package.json b/package.json
index b59d1c30..f152d640 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,8 @@
"test:e2e": "turbo run test:e2e",
"update:deps": "npx taze -r -w",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile",
- "catalog": "pnpx codemod pnpm/catalog"
+ "catalog": "pnpx codemod pnpm/catalog",
+ "generate-offline-icons": "node scripts/generate-offline-icons.js"
},
"devDependencies": {
"@changesets/changelog-github": "catalog:",
diff --git a/packages/icons/package.json b/packages/icons/package.json
index 9857a720..57d07eb5 100644
--- a/packages/icons/package.json
+++ b/packages/icons/package.json
@@ -55,6 +55,7 @@
"@iconify/icons-tabler": "^1.2.95",
"@iconify/icons-uiw": "^1.2.6",
"@iconify/icons-vscode-icons": "^1.2.29",
- "@iconify/icons-wpf": "^1.2.3"
+ "@iconify/icons-wpf": "^1.2.3",
+ "@iconify/json": "catalog:"
}
}
diff --git a/packages/icons/src/iconify-offline/index.ts b/packages/icons/src/iconify-offline/index.ts
index f36396d0..6f75d72d 100644
--- a/packages/icons/src/iconify-offline/index.ts
+++ b/packages/icons/src/iconify-offline/index.ts
@@ -3,7 +3,7 @@ import { createIconifyOfflineIcon } from '@vben-core/icons';
import dingdingFill from '@iconify/icons-ri/dingding-fill';
import giteeIcon from '@iconify/icons-simple-icons/gitee';
-import './menu-icons';
+import './offline-icons';
// 第三方登录相关图标
export const DingdingIcon = createIconifyOfflineIcon(
diff --git a/packages/icons/src/iconify-offline/menu-icons.ts b/packages/icons/src/iconify-offline/menu-icons.ts
deleted file mode 100644
index 85a0f285..00000000
--- a/packages/icons/src/iconify-offline/menu-icons.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { addIcon } from '@vben-core/icons';
-
-import schedule from '@iconify/icons-akar-icons/schedule';
-import settingOutline from '@iconify/icons-ant-design/setting-outlined';
-import antdTool from '@iconify/icons-ant-design/tool-outlined';
-import UserAntd from '@iconify/icons-ant-design/user-outlined';
-import Operation from '@iconify/icons-arcticons/one-hand-operation';
-import BaseLineHousesFill from '@iconify/icons-bi/houses-fill';
-import BxPackage from '@iconify/icons-bx/package';
-import modelAlt from '@iconify/icons-carbon/model-alt';
-import taskApproved from '@iconify/icons-carbon/task-approved';
-import redisWordmark from '@iconify/icons-devicon/redis-wordmark';
-import springWordmark from '@iconify/icons-devicon/spring-wordmark';
-import vscode from '@iconify/icons-devicon/vscode';
-import evergreenTree from '@iconify/icons-emojione/evergreen-tree';
-import RoleBindingOutlined from '@iconify/icons-eos-icons/role-binding-outlined';
-import SystemGroup from '@iconify/icons-eos-icons/system-group';
-import NoticePush from '@iconify/icons-fe/notice-push';
-import leave from '@iconify/icons-flat-color-icons/leave';
-import plus from '@iconify/icons-flat-color-icons/plus';
-import builDefinition from '@iconify/icons-fluent-mdl2/build-definition';
-import Dictionary from '@iconify/icons-fluent-mdl2/dictionary';
-import flow from '@iconify/icons-fluent-mdl2/flow';
-import leaveUser from '@iconify/icons-fluent-mdl2/leave-user';
-import from24 from '@iconify/icons-fluent/form-24-regular';
-import BaseLineHouse from '@iconify/icons-ic/baseline-house';
-import monitor from '@iconify/icons-ic/baseline-monitor';
-import roundLaunch from '@iconify/icons-ic/round-launch';
-import MenuSharp from '@iconify/icons-ic/sharp-menu';
-import Appointment from '@iconify/icons-icon-park-outline/appointment';
-import SettingTwo from '@iconify/icons-icon-park-twotone/setting-two';
-import boolOpenText from '@iconify/icons-lucide/book-open-text';
-import copyright from '@iconify/icons-lucide/copyright';
-import table from '@iconify/icons-lucide/table';
-import cloudDoneOutlineRounded from '@iconify/icons-material-symbols/cloud-done-outline-rounded';
-import generatingTokensOutline from '@iconify/icons-material-symbols/generating-tokens-outline';
-import LogoDevOutline from '@iconify/icons-material-symbols/logo-dev-outline';
-import expressionIcon from '@iconify/icons-material-symbols/regular-expression-rounded';
-import ccOutline from '@iconify/icons-mdi/cc-outline';
-import tools from '@iconify/icons-mdi/tools';
-import workflowOutline from '@iconify/icons-mdi/workflow-outline';
-import DepartmentLine from '@iconify/icons-mingcute/department-line';
-import profileLine from '@iconify/icons-mingcute/profile-line';
-import UserDuotone from '@iconify/icons-ph/user-duotone';
-import userList from '@iconify/icons-ph/user-list';
-import users from '@iconify/icons-ph/users-light';
-import insatnceLine from '@iconify/icons-ri/instance-line';
-import todoLine from '@iconify/icons-ri/todo-line';
-import Authy from '@iconify/icons-simple-icons/authy';
-import FolderWithFilesOutline from '@iconify/icons-solar/folder-with-files-outline';
-import monitorBoldDuotone from '@iconify/icons-solar/monitor-bold-duotone';
-import monitorCameraOutlined from '@iconify/icons-solar/monitor-camera-outline';
-import monitorPhoneOutlined from '@iconify/icons-solar/monitor-smartphone-outline';
-import InterfaceLoginDialPadFingerPasswordDialPadDotFinger from '@iconify/icons-streamline/interface-login-dial-pad-finger-password-dial-pad-dot-finger';
-import categoryPlus from '@iconify/icons-tabler/category-plus';
-import code from '@iconify/icons-tabler/code';
-
-/**
- * 这里添加菜单图标
- */
-addIcon('eos-icons:system-group', SystemGroup);
-addIcon('ph:user-duotone', UserDuotone);
-addIcon('ant-design:user-outlined', UserAntd);
-addIcon('eos-icons:role-binding-outlined', RoleBindingOutlined);
-addIcon('ic:sharp-menu', MenuSharp);
-addIcon('mingcute:department-line', DepartmentLine);
-addIcon('icon-park-outline:appointment', Appointment);
-addIcon('fluent-mdl2:dictionary', Dictionary);
-addIcon('icon-park-twotone:setting-two', SettingTwo);
-addIcon('fe:notice-push', NoticePush);
-addIcon('material-symbols:logo-dev-outline', LogoDevOutline);
-addIcon('arcticons:one-hand-operation', Operation);
-addIcon(
- 'streamline:interface-login-dial-pad-finger-password-dial-pad-dot-finger',
- InterfaceLoginDialPadFingerPasswordDialPadDotFinger,
-);
-addIcon('solar:folder-with-files-outline', FolderWithFilesOutline);
-addIcon('simple-icons:authy', Authy);
-addIcon('solar:monitor-smartphone-outline', monitorPhoneOutlined);
-addIcon('ic:baseline-house', BaseLineHouse);
-addIcon('ph:users-light', users);
-addIcon('bi:houses-fill', BaseLineHousesFill);
-addIcon('ph:user-list', userList);
-addIcon('bx:package', BxPackage);
-addIcon('solar:monitor-bold-duotone', monitorBoldDuotone);
-addIcon('solar:monitor-camera-outline', monitorCameraOutlined);
-addIcon('material-symbols:generating-tokens-outline', generatingTokensOutline);
-addIcon('devicon:redis-wordmark', redisWordmark);
-addIcon('devicon:spring-wordmark', springWordmark);
-addIcon('akar-icons:schedule', schedule);
-addIcon('mdi:tools', tools);
-addIcon('ant-design:tool-outlined', antdTool);
-addIcon('tabler:code', code);
-addIcon('flat-color-icons:plus', plus);
-addIcon('devicon:vscode', vscode);
-addIcon('lucide:table', table);
-addIcon('emojione:evergreen-tree', evergreenTree);
-addIcon('fluent-mdl2:leave-user', leaveUser);
-addIcon('mdi:workflow-outline', workflowOutline);
-addIcon('tabler:category-plus', categoryPlus);
-addIcon('carbon:model-alt', modelAlt);
-addIcon('fluent-mdl2:build-definition', builDefinition);
-addIcon('fluent-mdl2:build-definition', builDefinition);
-addIcon('icon-park-outline:monitor', monitor);
-addIcon('ri:instance-line', insatnceLine);
-addIcon('ri:todo-line', todoLine);
-addIcon('fluent:form-24-regular', from24);
-addIcon('carbon:task-approved', taskApproved);
-addIcon('ic:round-launch', roundLaunch);
-addIcon('material-symbols:cloud-done-outline-rounded', cloudDoneOutlineRounded);
-addIcon('mdi:cc-outline', ccOutline);
-addIcon('lucide:book-open-text', boolOpenText);
-addIcon('lucide:copyright', copyright);
-// 个人中心
-addIcon('mingcute:profile-line', profileLine);
-// oss配置
-addIcon('ant-design:setting-outlined', settingOutline);
-// 请假
-addIcon('flat-color-icons:leave', leave);
-// flow
-addIcon('fluent-mdl2:flow', flow);
-// 流程表达式
-addIcon('material-symbols:regular-expression-rounded', expressionIcon);
diff --git a/packages/icons/src/iconify-offline/offline-icons.ts b/packages/icons/src/iconify-offline/offline-icons.ts
new file mode 100644
index 00000000..ae00a2dd
--- /dev/null
+++ b/packages/icons/src/iconify-offline/offline-icons.ts
@@ -0,0 +1,258 @@
+// 该文件由脚本 generate-offline-icons.js 生成 ,不要手动修改
+// 该文件由脚本 generate-offline-icons.js 生成 ,不要手动修改
+// 该文件由脚本 generate-offline-icons.js 生成 ,不要手动修改
+import { addIcon } from '@vben-core/icons';
+
+addIcon('eos-icons:system-group', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('ant-design:user-outlined', {
+ body: '',
+ width: 1024,
+ height: 1024,
+});
+addIcon('eos-icons:role-binding-outlined', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('ic:sharp-menu', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('mingcute:department-line', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('icon-park-outline:appointment', {
+ body: '',
+ width: 48,
+ height: 48,
+});
+addIcon('fluent-mdl2:dictionary', {
+ body: '',
+ width: 2048,
+ height: 2048,
+});
+addIcon('ant-design:setting-outlined', {
+ body: '',
+ width: 1024,
+ height: 1024,
+});
+addIcon('fe:notice-push', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('material-symbols:logo-dev-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('solar:folder-with-files-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('solar:monitor-smartphone-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('ph:users-light', {
+ body: '',
+ width: 256,
+ height: 256,
+});
+addIcon('ph:user-list', {
+ body: '',
+ width: 256,
+ height: 256,
+});
+addIcon('bx:package', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('solar:monitor-camera-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('material-symbols:generating-tokens-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('devicon:redis-wordmark', {
+ body: '',
+ width: 128,
+ height: 128,
+});
+addIcon('devicon:spring-wordmark', {
+ body: '',
+ width: 128,
+ height: 128,
+});
+addIcon('ant-design:tool-outlined', {
+ body: '',
+ width: 1024,
+ height: 1024,
+});
+addIcon('tabler:code', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('flat-color-icons:plus', {
+ body: '',
+ width: 48,
+ height: 48,
+});
+addIcon('devicon:vscode', {
+ body: '',
+ width: 128,
+ height: 128,
+});
+addIcon('lucide:table', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('emojione:evergreen-tree', {
+ body: '',
+ width: 64,
+ height: 64,
+});
+addIcon('fluent-mdl2:leave-user', {
+ body: '',
+ width: 2048,
+ height: 2048,
+});
+addIcon('mdi:workflow-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('tabler:category-plus', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('material-symbols:regular-expression-rounded', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('fluent-mdl2:build-definition', {
+ body: '',
+ width: 2048,
+ height: 2048,
+});
+addIcon('icon-park-outline:monitor', {
+ body: '',
+ width: 48,
+ height: 48,
+});
+addIcon('fluent-mdl2:flow', {
+ body: '',
+ width: 2048,
+ height: 2048,
+});
+addIcon('flat-color-icons:leave', {
+ body: '',
+ width: 48,
+ height: 48,
+});
+addIcon('carbon:task-approved', {
+ body: '',
+ width: 32,
+ height: 32,
+});
+addIcon('ic:round-launch', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('ri:todo-line', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('material-symbols:cloud-done-outline-rounded', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('mdi:cc-outline', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('arcticons:one-hand-operation', {
+ body: '',
+ width: 48,
+ height: 48,
+});
+addIcon(
+ 'streamline:interface-login-dial-pad-finger-password-dial-pad-dot-finger',
+ {
+ body: '',
+ width: 14,
+ height: 14,
+ },
+);
+addIcon('ri:instance-line', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('skill-icons:java-light', {
+ body: '',
+ width: 256,
+ height: 256,
+});
+addIcon('tabler:file-type-xml', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('carbon:sql', {
+ body: '',
+ width: 32,
+ height: 32,
+});
+addIcon('skill-icons:typescript', {
+ body: '',
+ width: 256,
+ height: 256,
+});
+addIcon('logos:vue', {
+ body: '',
+ width: 256,
+ height: 221,
+});
+addIcon('flat-color-icons:folder', {
+ body: '',
+ width: 48,
+ height: 48,
+});
+addIcon('ep:fold', {
+ body: '',
+ width: 1024,
+ height: 1024,
+});
+addIcon('lucide:book-open-text', {
+ body: '',
+ width: 24,
+ height: 24,
+});
+addIcon('lucide:copyright', {
+ body: '',
+ width: 24,
+ height: 24,
+});
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 5cd8b0f5..faa9608b 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -24,7 +24,7 @@ catalog:
'@ctrl/tinycolor': ^4.1.0
'@eslint/js': ^9.39.1
'@faker-js/faker': ^9.9.0
- '@iconify/json': ^2.2.406
+ '@iconify/json': 2.2.431
'@iconify/tailwind': ^1.2.0
'@iconify/vue': ^5.0.0
'@intlify/core-base': ^11.1.7
diff --git a/scripts/generate-offline-icons.js b/scripts/generate-offline-icons.js
new file mode 100644
index 00000000..b9e406ca
--- /dev/null
+++ b/scripts/generate-offline-icons.js
@@ -0,0 +1,158 @@
+/* eslint-disable @typescript-eslint/no-dynamic-delete */
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const offlineIconList = [
+ 'eos-icons:system-group',
+ 'ant-design:user-outlined',
+ 'eos-icons:role-binding-outlined',
+ 'eos-icons:role-binding-outlined',
+ 'ic:sharp-menu',
+ 'mingcute:department-line',
+ 'icon-park-outline:appointment',
+ 'fluent-mdl2:dictionary',
+ 'ant-design:setting-outlined',
+ 'fe:notice-push',
+ 'material-symbols:logo-dev-outline',
+ 'solar:folder-with-files-outline',
+ 'ant-design:setting-outlined',
+ 'solar:monitor-smartphone-outline',
+ 'ph:users-light',
+ 'ph:user-list',
+ 'bx:package',
+ 'solar:monitor-camera-outline',
+ 'material-symbols:generating-tokens-outline',
+ 'devicon:redis-wordmark',
+ 'devicon:spring-wordmark',
+ 'ant-design:tool-outlined',
+ 'tabler:code',
+ 'tabler:code',
+ 'flat-color-icons:plus',
+ 'devicon:vscode',
+ 'lucide:table',
+ 'emojione:evergreen-tree',
+ 'fluent-mdl2:leave-user',
+ 'mdi:workflow-outline',
+ 'tabler:category-plus',
+ 'material-symbols:regular-expression-rounded',
+ 'fluent-mdl2:build-definition',
+ 'icon-park-outline:monitor',
+ 'fluent-mdl2:flow',
+ 'flat-color-icons:leave',
+ 'carbon:task-approved',
+ 'ic:round-launch',
+ 'ri:todo-line',
+ 'material-symbols:cloud-done-outline-rounded',
+ 'mdi:cc-outline',
+ 'arcticons:one-hand-operation',
+ 'streamline:interface-login-dial-pad-finger-password-dial-pad-dot-finger',
+ 'ri:instance-line',
+ 'skill-icons:java-light',
+ 'tabler:file-type-xml',
+ 'carbon:sql',
+ 'skill-icons:typescript',
+ 'logos:vue',
+ 'flat-color-icons:folder',
+ // 其他需要离线的
+ 'ep:fold',
+ 'lucide:book-open-text',
+ 'lucide:copyright',
+];
+
+// Deduplicate list
+const uniqueIcons = [...new Set(offlineIconList)];
+
+const outputLines = [
+ '// 该文件由脚本 generate-offline-icons.js 生成 ,不要手动修改',
+ '// 该文件由脚本 generate-offline-icons.js 生成 ,不要手动修改',
+ '// 该文件由脚本 generate-offline-icons.js 生成 ,不要手动修改',
+ "import { addIcon } from '@vben-core/icons';",
+ '',
+];
+
+const projectRoot = path.resolve(__dirname, '..');
+const nodeModules = path.join(projectRoot, 'packages/icons', 'node_modules');
+
+// Helper to find icon data
+function getIconData(prefix, name) {
+ const jsonPath = path.join(
+ nodeModules,
+ '@iconify/json/json',
+ `${prefix}.json`,
+ );
+ if (!fs.existsSync(jsonPath)) {
+ console.warn(`Warning: Icon set ${prefix} not found at ${jsonPath}`);
+ return null;
+ }
+
+ const content = fs.readFileSync(jsonPath, 'utf8');
+ const data = JSON.parse(content);
+
+ if (data.icons[name]) {
+ return {
+ ...data.icons[name],
+ width: data.icons[name].width || data.width || 24,
+ height: data.icons[name].height || data.height || 24,
+ };
+ }
+
+ if (data.aliases && data.aliases[name]) {
+ const alias = data.aliases[name];
+ const parentName = alias.parent;
+ const parentData = getIconData(prefix, parentName);
+ if (parentData) {
+ return {
+ ...parentData,
+ ...alias,
+ // Remove alias specific fields if not needed, but they overwrite parent
+ };
+ }
+ }
+
+ console.warn(`Warning: Icon ${name} not found in ${prefix}`);
+ return null;
+}
+
+uniqueIcons.forEach((iconStr) => {
+ const [prefix, ...nameParts] = iconStr.split(':');
+ const name = nameParts.join(':'); // In case name has colons, though unlikely in Iconify
+
+ if (!prefix || !name) {
+ console.warn(`Invalid icon format: ${iconStr}`);
+ return;
+ }
+
+ const iconData = getIconData(prefix, name);
+ if (iconData) {
+ // Clean up data to be minimal
+ const cleanData = {
+ body: iconData.body,
+ width: iconData.width,
+ height: iconData.height,
+ left: iconData.left,
+ top: iconData.top,
+ hFlip: iconData.hFlip,
+ vFlip: iconData.vFlip,
+ rotate: iconData.rotate,
+ };
+ // Remove undefined keys
+ Object.keys(cleanData).forEach(
+ (key) => cleanData[key] === undefined && delete cleanData[key],
+ );
+
+ outputLines.push(`addIcon('${iconStr}', ${JSON.stringify(cleanData)});`);
+ }
+});
+
+const outputPath = path.join(
+ projectRoot,
+ 'packages/icons/src/iconify-offline',
+ 'offline-icons.ts',
+);
+fs.writeFileSync(outputPath, `${outputLines.join('\n')}\n`);
+
+console.log(`Successfully generated ${outputPath}`);