refactor: custom icon

This commit is contained in:
张传龙 2022-08-27 11:46:34 +08:00
parent 2f1b747243
commit 0cefadc2a5
15 changed files with 1024 additions and 12 deletions

View File

@ -10,10 +10,11 @@ import IconsResolver from 'unplugin-icons/resolver'
* 图标库: https://icones.js.org/
*/
import Icons from 'unplugin-icons/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { getRootPath } from '../utils'
const customIconPath = getRootPath('src', 'assets/icons')
const customIconPath = getRootPath('src', 'assets/svg')
export default [
AutoImport({
imports: ['vue', 'vue-router'],
@ -31,4 +32,10 @@ export default [
resolvers: [NaiveUiResolver(), IconsResolver({ customCollections: ['custom'], componentPrefix: 'icon' })],
dts: false,
}),
createSvgIconsPlugin({
iconDirs: [customIconPath],
symbolId: 'icon-custom-[dir]-[name]',
inject: 'body-last',
customDomId: '__CUSTOM_SVG_ICON__',
}),
]

View File

@ -55,6 +55,7 @@
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend-plus": "^0.1.0"
},
"config": {

933
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 448 512" data-v-fba6e5d0=""><path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-92.2 312.9c-63.4 0-85.4-28.6-97.1-64.1c-16.3-51-21.5-84.3-63-84.3c-22.4 0-45.1 16.1-45.1 61.2c0 35.2 18 57.2 43.3 57.2c28.6 0 47.6-21.3 47.6-21.3l11.7 31.9s-19.8 19.4-61.2 19.4c-51.3 0-79.9-30.1-79.9-85.8c0-57.9 28.6-92 82.5-92c73.5 0 80.8 41.4 100.8 101.9c8.8 26.8 24.2 46.2 61.2 46.2c24.9 0 38.1-5.5 38.1-19.1c0-19.9-21.8-22-49.9-28.6c-30.4-7.3-42.5-23.1-42.5-48c0-40 32.3-52.4 65.2-52.4c37.4 0 60.1 13.6 63 46.6l-36.7 4.4c-1.5-15.8-11-22.4-28.6-22.4c-16.1 0-26 7.3-26 19.8c0 11 4.8 17.6 20.9 21.3c32.7 7.1 71.8 12 71.8 57.5c.1 36.7-30.7 50.6-76.1 50.6z" fill="#316c72"></path></svg>

Before

Width:  |  Height:  |  Size: 825 B

1
src/assets/svg/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 448 512"><path fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-92.2 312.9c-63.4 0-85.4-28.6-97.1-64.1c-16.3-51-21.5-84.3-63-84.3c-22.4 0-45.1 16.1-45.1 61.2c0 35.2 18 57.2 43.3 57.2c28.6 0 47.6-21.3 47.6-21.3l11.7 31.9s-19.8 19.4-61.2 19.4c-51.3 0-79.9-30.1-79.9-85.8c0-57.9 28.6-92 82.5-92c73.5 0 80.8 41.4 100.8 101.9c8.8 26.8 24.2 46.2 61.2 46.2c24.9 0 38.1-5.5 38.1-19.1c0-19.9-21.8-22-49.9-28.6c-30.4-7.3-42.5-23.1-42.5-48c0-40 32.3-52.4 65.2-52.4c37.4 0 60.1 13.6 63 46.6l-36.7 4.4c-1.5-15.8-11-22.4-28.6-22.4c-16.1 0-26 7.3-26 19.8c0 11 4.8 17.6 20.9 21.3c32.7 7.1 71.8 12 71.8 57.5c.1 36.7-30.7 50.6-76.1 50.6z"></path></svg>

After

Width:  |  Height:  |  Size: 811 B

View File

@ -0,0 +1,25 @@
<script setup>
import { renderCustomIcon } from '@/utils/icon'
const props = defineProps({
/** 图标名称(图片的文件名) */
icon: {
type: String,
required: true,
},
size: {
type: Number,
default: 14,
},
color: {
type: String,
default: undefined,
},
})
const iconCom = computed(() => renderCustomIcon(props.icon, props))
</script>
<template>
<component :is="iconCom" />
</template>

View File

@ -0,0 +1,24 @@
<script setup name="SvgIcon">
const props = defineProps({
icon: {
type: String,
required: true,
},
prefix: {
type: String,
default: 'icon-custom',
},
color: {
type: String,
default: 'currentColor',
},
})
const symbolId = computed(() => `#${props.prefix}-${props.icon}`)
</script>
<template>
<svg aria-hidden="true" width="1em" height="1em">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>

View File

@ -5,14 +5,14 @@
:key="item.path"
@click="handleBreadClick(item.path)"
>
<component :is="renderIcon(item.meta?.icon, { size: 16 })" v-if="item.meta?.icon" />
<component :is="getIcon(item.meta)" />
{{ item.meta.title }}
</n-breadcrumb-item>
</n-breadcrumb>
</template>
<script setup>
import { renderIcon } from '@/utils/icon'
import { renderCustomIcon, renderIcon } from '@/utils/icon'
const router = useRouter()
const route = useRoute()
@ -21,4 +21,10 @@ function handleBreadClick(path) {
if (path === route.path) return
router.push(path)
}
function getIcon(meta) {
if (meta?.customIcon) return renderCustomIcon(meta.customIcon, { size: 18 })
if (meta?.icon) return renderIcon(meta.icon, { size: 18 })
return null
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<router-link h-60 f-c-c to="/">
<icon-custom-logo text-36></icon-custom-logo>
<icon-custom-logo text-36 color-primary></icon-custom-logo>
<h2 v-show="!appStore.collapsed" ml-10 color-primary text-16 font-bold max-w-140 flex-shrink-0>
{{ title }}
</h2>

View File

@ -16,7 +16,7 @@ import { usePermissionStore } from '@/store/modules/permission'
import { isExternal } from '@/utils/is'
import { useAppStore } from '@/store/modules/app'
import { renderIcon } from '@/utils/icon'
import { renderCustomIcon, renderIcon } from '@/utils/icon'
const router = useRouter()
const permissionStore = usePermissionStore()
@ -43,7 +43,7 @@ function getMenuItem(route, basePath = '') {
label: (route.meta && route.meta.title) || route.name,
key: route.name,
path: resolvePath(basePath, route.path),
icon: route.meta?.icon ? renderIcon(route.meta?.icon, { size: 16 }) : renderIcon('mdi:circle-outline', { size: 8 }),
icon: getIcon(route.meta),
order: route.meta?.order || 0,
}
@ -58,9 +58,7 @@ function getMenuItem(route, basePath = '') {
label: singleRoute.meta?.title || singleRoute.name,
key: singleRoute.name,
path: resolvePath(menuItem.path, singleRoute.path),
icon: singleRoute.meta?.icon
? renderIcon(singleRoute.meta?.icon, { size: 16 })
: renderIcon('mdi:circle-outline', { size: 8 }),
icon: getIcon(singleRoute.meta),
order: menuItem.order,
}
const visibleItems = singleRoute.children ? singleRoute.children.filter((item) => item.name && !item.isHidden) : []
@ -79,6 +77,12 @@ function getMenuItem(route, basePath = '') {
return menuItem
}
function getIcon(meta) {
if (meta?.customIcon) return renderCustomIcon(meta.customIcon, { size: 18 })
if (meta?.icon) return renderIcon(meta.icon, { size: 18 })
return null
}
function handleMenuSelect(key, item) {
if (isExternal(item.path)) {
window.open(item.path)

View File

@ -2,6 +2,7 @@ import '@/styles/reset.css'
import '@/styles/variables.css'
import '@/styles/index.scss'
import 'uno.css'
import 'virtual:svg-icons-register'
import { createApp } from 'vue'
import { setupRouter } from '@/router'

View File

@ -1,7 +1,12 @@
import { h } from 'vue'
import { Icon } from '@iconify/vue'
import { NIcon } from 'naive-ui'
import SvgIcon from '@/components/custom/SvgIcon.vue'
export function renderIcon(icon, props = { size: 12 }) {
return () => h(NIcon, props, { default: () => h(Icon, { icon }) })
}
export function renderCustomIcon(icon, props = { size: 12 }) {
return () => h(NIcon, props, { default: () => h(SvgIcon, { icon }) })
}

View File

@ -17,7 +17,7 @@ export default {
component: () => import('./404.vue'),
meta: {
title: '404',
icon: 'mdi:alert-circle-outline',
icon: 'tabler:error-404',
},
},
],

View File

@ -31,6 +31,7 @@ export default {
component: () => import('./post/index.vue'),
meta: {
title: '文章列表',
icon: 'material-symbols:auto-awesome-outline-rounded',
role: ['admin'],
requireAuth: true,
},
@ -41,6 +42,7 @@ export default {
component: () => import('./post/PostCreate.vue'),
meta: {
title: '创建文章',
icon: 'material-symbols:auto-awesome-outline-rounded',
role: ['admin'],
requireAuth: true,
},

View File

@ -7,7 +7,7 @@ export default {
redirect: '/test/unocss',
meta: {
title: '基础功能测试',
icon: 'mdi:menu',
customIcon: 'logo',
order: 1,
},
children: [
@ -17,6 +17,7 @@ export default {
component: () => import('./unocss/index.vue'),
meta: {
title: '测试unocss',
icon: 'material-symbols:auto-awesome-outline-rounded',
},
},
{
@ -25,6 +26,7 @@ export default {
component: () => import('./message/index.vue'),
meta: {
title: '测试Message',
icon: 'material-symbols:auto-awesome-outline-rounded',
},
},
{
@ -33,6 +35,7 @@ export default {
component: () => import('./dialog/index.vue'),
meta: {
title: '测试Dialog',
icon: 'material-symbols:auto-awesome-outline-rounded',
},
},
{
@ -42,6 +45,7 @@ export default {
meta: {
title: '测试Keep-Alive',
keepAlive: true,
icon: 'material-symbols:auto-awesome-outline-rounded',
},
},
],