Browse Source

feat: integrate Iconify for enhanced icon support

- Add Iconify dependencies for various icon sets in package.json
- Update Vite configuration to include unplugin-icons for automatic icon imports
- Replace Element Plus icons with Iconify icons in layout and login views
- Enhance auto-imports to include Icon component from Iconify
- Update localization files to include new terms for improved multilingual support
yb 3 tuần trước cách đây
mục cha
commit
1c58ad8c73

+ 2 - 1
.eslintrc-auto-import.json

@@ -91,6 +91,7 @@
     "watch": true,
     "watchEffect": true,
     "watchPostEffect": true,
-    "watchSyncEffect": true
+    "watchSyncEffect": true,
+    "Icon": true
   }
 }

+ 5 - 0
package.json

@@ -45,6 +45,10 @@
   "devDependencies": {
     "@commitlint/cli": "^12.1.1",
     "@commitlint/config-conventional": "^12.1.1",
+    "@iconify-json/ep": "^1.2.3",
+    "@iconify-json/mdi": "^1.2.3",
+    "@iconify-json/ri": "^1.2.7",
+    "@iconify/vue": "^5.0.0",
     "@playwright/test": "^1.57.0",
     "@types/lodash-es": "^4.17.12",
     "@types/node": "^18.16.8",
@@ -68,6 +72,7 @@
     "prettier": "^2.8.8",
     "typescript": "~5.6.3",
     "unplugin-auto-import": "^0.19.0",
+    "unplugin-icons": "^22.5.0",
     "unplugin-vue-components": "^0.28.0",
     "vite": "5.3.1",
     "vite-plugin-vue-devtools": "^7.7.0",

+ 108 - 0
pnpm-lock.yaml

@@ -54,6 +54,18 @@ importers:
       '@commitlint/config-conventional':
         specifier: ^12.1.1
         version: 12.1.4
+      '@iconify-json/ep':
+        specifier: ^1.2.3
+        version: 1.2.3
+      '@iconify-json/mdi':
+        specifier: ^1.2.3
+        version: 1.2.3
+      '@iconify-json/ri':
+        specifier: ^1.2.7
+        version: 1.2.7
+      '@iconify/vue':
+        specifier: ^5.0.0
+        version: 5.0.0(vue@3.5.26(typescript@5.6.3))
       '@playwright/test':
         specifier: ^1.57.0
         version: 1.57.0
@@ -123,6 +135,9 @@ importers:
       unplugin-auto-import:
         specifier: ^0.19.0
         version: 0.19.0(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.6.3)))(rollup@4.55.1)
+      unplugin-icons:
+        specifier: ^22.5.0
+        version: 22.5.0(@vue/compiler-sfc@3.5.26)
       unplugin-vue-components:
         specifier: ^0.28.0
         version: 0.28.0(@babel/parser@7.28.5)(rollup@4.55.1)(vue@3.5.26(typescript@5.6.3))
@@ -141,6 +156,9 @@ importers:
 
 packages:
 
+  '@antfu/install-pkg@1.1.0':
+    resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
   '@antfu/utils@0.7.10':
     resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
 
@@ -696,6 +714,26 @@ packages:
     resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
     deprecated: Use @eslint/object-schema instead
 
+  '@iconify-json/ep@1.2.3':
+    resolution: {integrity: sha512-bESiKz5aTcbT4chTVSKjR5P+Nk81ibRtWgR7Gng5JPdF1Az+91+bIOof/OCUKThFRWqWsQ9A4XmSSrakvjuJDQ==}
+
+  '@iconify-json/mdi@1.2.3':
+    resolution: {integrity: sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==}
+
+  '@iconify-json/ri@1.2.7':
+    resolution: {integrity: sha512-j/Fkb8GlWY5y/zLj1BGxWRtDzuJFrI7562zLw+iQVEykieBgew43+r8qAvtSajvb75MfUIHjsNOYQPRD8FfLfw==}
+
+  '@iconify/types@2.0.0':
+    resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+  '@iconify/utils@3.1.0':
+    resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==}
+
+  '@iconify/vue@5.0.0':
+    resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==}
+    peerDependencies:
+      vue: '>=3'
+
   '@intlify/core-base@11.2.8':
     resolution: {integrity: sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==}
     engines: {node: '>= 16'}
@@ -2707,6 +2745,9 @@ packages:
   package-json-from-dist@1.0.1:
     resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
 
+  package-manager-detector@1.6.0:
+    resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
+
   parent-module@1.0.1:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
@@ -3280,6 +3321,29 @@ packages:
       '@vueuse/core':
         optional: true
 
+  unplugin-icons@22.5.0:
+    resolution: {integrity: sha512-MBlMtT5RuMYZy4TZgqUL2OTtOdTUVsS1Mhj6G1pEzMlFJlEnq6mhUfoIt45gBWxHcsOdXJDWLg3pRZ+YmvAVWQ==}
+    peerDependencies:
+      '@svgr/core': '>=7.0.0'
+      '@svgx/core': ^1.0.1
+      '@vue/compiler-sfc': ^3.0.2 || ^2.7.0
+      svelte: ^3.0.0 || ^4.0.0 || ^5.0.0
+      vue-template-compiler: ^2.6.12
+      vue-template-es2015-compiler: ^1.9.0
+    peerDependenciesMeta:
+      '@svgr/core':
+        optional: true
+      '@svgx/core':
+        optional: true
+      '@vue/compiler-sfc':
+        optional: true
+      svelte:
+        optional: true
+      vue-template-compiler:
+        optional: true
+      vue-template-es2015-compiler:
+        optional: true
+
   unplugin-vue-components@0.28.0:
     resolution: {integrity: sha512-jiTGtJ3JsRFBjgvyilfrX7yUoGKScFgbdNw+6p6kEXU+Spf/rhxzgvdfuMcvhCcLmflB/dY3pGQshYBVGOUx7Q==}
     engines: {node: '>=14'}
@@ -3596,6 +3660,11 @@ packages:
 
 snapshots:
 
+  '@antfu/install-pkg@1.1.0':
+    dependencies:
+      package-manager-detector: 1.6.0
+      tinyexec: 1.0.2
+
   '@antfu/utils@0.7.10': {}
 
   '@babel/code-frame@7.27.1':
@@ -4079,6 +4148,31 @@ snapshots:
 
   '@humanwhocodes/object-schema@2.0.3': {}
 
+  '@iconify-json/ep@1.2.3':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/mdi@1.2.3':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/ri@1.2.7':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify/types@2.0.0': {}
+
+  '@iconify/utils@3.1.0':
+    dependencies:
+      '@antfu/install-pkg': 1.1.0
+      '@iconify/types': 2.0.0
+      mlly: 1.8.0
+
+  '@iconify/vue@5.0.0(vue@3.5.26(typescript@5.6.3))':
+    dependencies:
+      '@iconify/types': 2.0.0
+      vue: 3.5.26(typescript@5.6.3)
+
   '@intlify/core-base@11.2.8':
     dependencies:
       '@intlify/message-compiler': 11.2.8
@@ -6325,6 +6419,8 @@ snapshots:
 
   package-json-from-dist@1.0.1: {}
 
+  package-manager-detector@1.6.0: {}
+
   parent-module@1.0.1:
     dependencies:
       callsites: 3.1.0
@@ -6941,6 +7037,18 @@ snapshots:
     transitivePeerDependencies:
       - rollup
 
+  unplugin-icons@22.5.0(@vue/compiler-sfc@3.5.26):
+    dependencies:
+      '@antfu/install-pkg': 1.1.0
+      '@iconify/utils': 3.1.0
+      debug: 4.4.3
+      local-pkg: 1.1.2
+      unplugin: 2.3.11
+    optionalDependencies:
+      '@vue/compiler-sfc': 3.5.26
+    transitivePeerDependencies:
+      - supports-color
+
   unplugin-vue-components@0.28.0(@babel/parser@7.28.5)(rollup@4.55.1)(vue@3.5.26(typescript@5.6.3)):
     dependencies:
       '@antfu/utils': 0.7.10

+ 1 - 0
src/auto-imports.d.ts

@@ -7,6 +7,7 @@
 export {}
 declare global {
   const EffectScope: typeof import('vue')['EffectScope']
+  const Icon: typeof import('@iconify/vue')['Icon']
   const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
   const computed: typeof import('vue')['computed']
   const createApp: typeof import('vue')['createApp']

+ 6 - 6
src/layout/index.vue

@@ -70,14 +70,14 @@
           </el-icon>
           <el-breadcrumb separator="/">
             <el-breadcrumb-item v-for="item in breadcrumbs" :key="item.path">
-              {{ item.meta?.title }}
+              {{ t(item.meta?.title) }}
             </el-breadcrumb-item>
           </el-breadcrumb>
         </div>
         <div class="header-right">
           <el-dropdown class="lang-dropdown" @command="handleLanguage">
             <span class="lang-trigger">
-              <el-icon><Location /></el-icon>
+              <Icon icon="mdi:translate" width="18" height="18" />
               {{ currentLangLabel }}
               <el-icon><ArrowDown /></el-icon>
             </span>
@@ -158,18 +158,18 @@ import {
   VideoCameraFilled,
   Setting,
   UserFilled,
-  VideoPlay,
-  Location
+  VideoPlay
 } from '@element-plus/icons-vue'
+import { Icon } from '@iconify/vue'
 import { useAppStore } from '@/store/app'
 import { useUserStore } from '@/store/user'
 import { changePassword } from '@/api/login'
-
+import { useI18n } from 'vue-i18n'
 const route = useRoute()
 const router = useRouter()
 const appStore = useAppStore()
 const userStore = useUserStore()
-
+const { t } = useI18n()
 const sidebarOpened = computed(() => appStore.sidebarOpened)
 const userInfo = computed(() => userStore.userInfo)
 

+ 12 - 1
src/locales/en.json

@@ -1,3 +1,14 @@
 {
-  "摄像头管理系统": "Camera Management System"
+  "摄像头管理系统": "Camera Management System",
+  "仪表盘": "Dashboard",
+  "机器管理": "Machine Management",
+  "摄像头管理": "Camera Management",
+  "用户管理": "User Management",
+  "视频测试": "Video Test",
+  "Stream 测试": "Stream Test",
+  "视频管理": "Video Management",
+  "直播管理": "Live Management",
+  "Stream 配置": "Stream Configuration",
+  "审计日志": "Audit Log",
+  "观看统计": "Watching Statistics"
 }

+ 12 - 1
src/locales/zh-cn.json

@@ -1,3 +1,14 @@
 {
-  "摄像头管理系统": "摄像头管理系统"
+  "摄像头管理系统": "摄像头管理系统",
+  "仪表盘": "仪表盘",
+  "机器管理": "机器管理",
+  "摄像头管理": "摄像头管理",
+  "用户管理": "用户管理",
+  "视频测试": "视频测试",
+  "Stream 测试": "Stream 测试",
+  "视频管理": "视频管理",
+  "直播管理": "直播管理",
+  "Stream 配置": "Stream 配置",
+  "审计日志": "审计日志",
+  "观看统计": "观看统计"
 }

+ 3 - 2
src/views/login/index.vue

@@ -4,7 +4,7 @@
     <div class="login__lang">
       <el-dropdown @command="handleLanguage">
         <span class="lang-trigger">
-          <el-icon><Location /></el-icon>
+          <Icon icon="mdi:translate" width="18" height="18" />
           {{ currentLangLabel }}
           <el-icon><ArrowDown /></el-icon>
         </span>
@@ -69,7 +69,8 @@
 import { ref, reactive, onMounted, watch, computed } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
-import { User, Lock, ArrowDown, Location } from '@element-plus/icons-vue'
+import { User, Lock, ArrowDown } from '@element-plus/icons-vue'
+import { Icon } from '@iconify/vue'
 import { useUserStore } from '@/store/user'
 import { useAppStore } from '@/store/app'
 import type { LoginParams } from '@/types'

+ 25 - 3
vite.config.ts

@@ -5,6 +5,8 @@ import { resolve } from 'path'
 import AutoImport from 'unplugin-auto-import/vite'
 import Components from 'unplugin-vue-components/vite'
 import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import Icons from 'unplugin-icons/vite'
+import IconsResolver from 'unplugin-icons/resolver'
 import pkg from './package.json'
 
 export default defineConfig({
@@ -12,8 +14,20 @@ export default defineConfig({
     process.env.NODE_ENV === 'development' && vueDevTools(),
     vue(),
     AutoImport({
-      imports: ['vue', 'vue-router', 'pinia'],
-      resolvers: [ElementPlusResolver()],
+      imports: [
+        'vue',
+        'vue-router',
+        'pinia',
+        {
+          '@iconify/vue': ['Icon']
+        }
+      ],
+      resolvers: [
+        ElementPlusResolver(),
+        IconsResolver({
+          prefix: 'Icon'
+        })
+      ],
       dts: 'src/auto-imports.d.ts',
       eslintrc: {
         enabled: true,
@@ -22,8 +36,16 @@ export default defineConfig({
       }
     }),
     Components({
-      resolvers: [ElementPlusResolver()],
+      resolvers: [
+        ElementPlusResolver(),
+        IconsResolver({
+          enabledCollections: ['ep', 'mdi', 'ri']
+        })
+      ],
       dts: 'src/components.d.ts'
+    }),
+    Icons({
+      autoInstall: false
     })
   ],
   define: {