图标工具

This commit is contained in:
Gary Fu
2023-12-24 19:51:56 +08:00
parent 34d4af039e
commit 62aad55987
15 changed files with 193 additions and 91 deletions

37
package-lock.json generated
View File

@@ -18,7 +18,8 @@
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.3.13", "vue": "^3.3.13",
"vue-i18n": "^9.8.0", "vue-i18n": "^9.8.0",
"vue-router": "^4.2.5" "vue-router": "^4.2.5",
"vue-virtual-scroller": "^2.0.0-beta.8"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.6.1", "@rushstack/eslint-patch": "^1.6.1",
@@ -2463,6 +2464,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/mitt": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -3458,6 +3464,22 @@
"vue": "^3.0.0" "vue": "^3.0.0"
} }
}, },
"node_modules/vue-observe-visibility": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz",
"integrity": "sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==",
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-resize": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
"integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==",
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.2.5", "version": "4.2.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
@@ -3472,6 +3494,19 @@
"vue": "^3.2.0" "vue": "^3.2.0"
} }
}, },
"node_modules/vue-virtual-scroller": {
"version": "2.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-2.0.0-beta.8.tgz",
"integrity": "sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==",
"dependencies": {
"mitt": "^2.1.0",
"vue-observe-visibility": "^2.0.0-alpha.1",
"vue-resize": "^2.0.0-alpha.1"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@@ -19,7 +19,8 @@
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.3.13", "vue": "^3.3.13",
"vue-i18n": "^9.8.0", "vue-i18n": "^9.8.0",
"vue-router": "^4.2.5" "vue-router": "^4.2.5",
"vue-virtual-scroller": "^2.0.0-beta.8"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.6.1", "@rushstack/eslint-patch": "^1.6.1",

View File

@@ -1,5 +1,5 @@
import CommonIcon from '@/components/common-icon/index.vue' import CommonIcon from '@/components/common-icon/index.vue'
import CommonInput from '@/components/common-input/index.vue' import CommonInput from '@/components/common-form-input/index.vue'
import CommonMenu from '@/components/common-menu/index.vue' import CommonMenu from '@/components/common-menu/index.vue'
import CommonMenuItem from '@/components/common-menu-item/index.vue' import CommonMenuItem from '@/components/common-menu-item/index.vue'

View File

@@ -2,6 +2,8 @@ import { createApp } from 'vue'
import ElementPlus from 'element-plus' import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css' import 'element-plus/theme-chalk/dark/css-vars.css'
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import stores from '@/stores' import stores from '@/stores'
import icons from '@/icons' import icons from '@/icons'
import messages from '@/messages' import messages from '@/messages'
@@ -14,6 +16,7 @@ import './assets/main.css'
const app = createApp(App) const app = createApp(App)
app.use(VueVirtualScroller)
app.use(stores) app.use(stores)
app.use(router) app.use(router)
app.use(ElementPlus) app.use(ElementPlus)

View File

@@ -7,7 +7,9 @@ menu.label.roleManagement = '角色管理'
menu.label.authorityManagement = '权限管理' menu.label.authorityManagement = '权限管理'
menu.label.menuManagement = '菜单管理' menu.label.menuManagement = '菜单管理'
menu.label.toolsManagement = '工具管理' menu.label.toolsManagement = '工具管理'
menu.label.toolsIcons = '图标管理' menu.label.toolsIcons = '图标工具'
menu.label.toolsForms = '表单工具'
menu.label.toolsTables = '表格工具'
menu.label.errorPage = '错误页面' menu.label.errorPage = '错误页面'
menu.label.errorPage404 = '找不到页面' menu.label.errorPage404 = '找不到页面'
menu.label.errorPage403 = '没有权限' menu.label.errorPage403 = '没有权限'

View File

@@ -8,6 +8,8 @@ menu.label.authorityManagement = 'Authority Management'
menu.label.menuManagement = 'Menu Management' menu.label.menuManagement = 'Menu Management'
menu.label.toolsManagement = 'Tools' menu.label.toolsManagement = 'Tools'
menu.label.toolsIcons = 'Icons' menu.label.toolsIcons = 'Icons'
menu.label.toolsForms = 'Forms'
menu.label.toolsTables = 'Tables'
menu.label.errorPage = 'Error Page' menu.label.errorPage = 'Error Page'
menu.label.errorPage404 = 'Not Found' menu.label.errorPage404 = 'Not Found'
menu.label.errorPage403 = 'Access Denied' menu.label.errorPage403 = 'Access Denied'

13
src/route/ToolsRoutes.js Normal file
View File

@@ -0,0 +1,13 @@
export default [{
path: 'icons',
name: 'icons',
component: () => import('@/views/tools/Icons.vue')
}, {
path: 'forms',
name: 'forms',
component: () => import('@/views/tools/Forms.vue')
}, {
path: 'tables',
name: 'tables',
component: () => import('@/views/tools/Tables.vue')
}]

View File

@@ -1,6 +1,7 @@
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue' import HomeView from '@/views/HomeView.vue'
import AdminRoutes from '@/route/AdminRoutes' import AdminRoutes from '@/route/AdminRoutes'
import ToolsRoutes from '@/route/ToolsRoutes'
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
@@ -12,22 +13,19 @@ const router = createRouter({
children: [{ children: [{
path: 'about', path: 'about',
name: 'about', name: 'about',
component: () => import('@/views/AboutView.vue') component: () => import('@/views/account/AboutView.vue')
}, { }, {
path: 'personal', path: 'personal',
name: 'personal', name: 'personal',
component: () => import('@/views/account/PersonalInfo.vue') component: () => import('@/views/account/PersonalInfo.vue')
}, {
path: 'icons',
name: 'icons',
component: () => import('@/views/Icons.vue')
}, },
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
name: 'notFound', name: 'notFound',
component: () => import('@/views/404.vue') component: () => import('@/views/404.vue')
}, },
...AdminRoutes ...AdminRoutes,
...ToolsRoutes
] ]
} }
] ]

View File

@@ -89,7 +89,7 @@ export const useBusinessMenus = () => {
}, },
{ {
index: '/admin/roles', index: '/admin/roles',
icon: 'menu', icon: 'GroupFilled',
labelKey: 'menu.label.roleManagement' labelKey: 'menu.label.roleManagement'
}, },
{ {
@@ -117,6 +117,16 @@ export const useBusinessMenus = () => {
index: '/icons', index: '/icons',
icon: 'InsertEmoticonOutlined', icon: 'InsertEmoticonOutlined',
labelKey: 'menu.label.toolsIcons' labelKey: 'menu.label.toolsIcons'
},
{
index: '/forms',
icon: 'TableRowsTwotone',
labelKey: 'menu.label.toolsForms'
},
{
index: '/tables',
icon: 'Grid',
labelKey: 'menu.label.toolsTables'
} }
] ]
}]) }])

View File

@@ -1,80 +0,0 @@
<script setup>
import { INSTALL_ICONS } from '@/icons'
import chunk from 'lodash/chunk'
import { computed, ref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { ElMessage } from 'element-plus'
const colSize = ref(8)
const showSize = ref(200)
const keyWords = ref('')
const filterIcons = computed(() => {
let icons = INSTALL_ICONS
if (keyWords.value) {
icons = icons.filter(icon => icon.toLowerCase().includes(keyWords.value.toLowerCase()))
}
return chunk(icons.slice(0, showSize.value), colSize.value)
})
const copyIcon = (icon) => {
const { copy, isSupported } = useClipboard()
if (isSupported) {
copy(icon)
ElMessage({
message: `Copied: ${icon}`,
type: 'success'
})
} else {
ElMessage({
message: `Copy Not supported: ${icon}`,
type: 'error'
})
}
}
</script>
<template>
<div>
<el-row>
<el-col>
<el-form label-width="120px">
<el-form-item label="图标名字">
<el-input
v-model="keyWords"
:placeholder="`输入关键字搜索图标,只显示前${showSize}个图标`"
/>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row
v-for="(icons, chunkIdx) in filterIcons"
:key="chunkIdx"
>
<el-col
v-for="icon in icons"
:key="icon"
:span="24/colSize"
class="text-center"
>
<a
class="el-button el-button--large is-text"
style="height:80px;"
@click="copyIcon(icon)"
>
<div>
<common-icon
size="20"
:icon="icon"
/>
</div>
</a>
</el-col>
</el-row>
</div>
</template>
<style scoped>
</style>

13
src/views/tools/Forms.vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<strong>表单测试</strong>
</div>
</template>
<style scoped>
</style>

92
src/views/tools/Icons.vue Normal file
View File

@@ -0,0 +1,92 @@
<script setup>
import { INSTALL_ICONS } from '@/icons'
import chunk from 'lodash/chunk'
import { computed, ref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { ElMessage } from 'element-plus'
const colSize = ref(8)
const keyWords = ref('')
const filterIcons = computed(() => {
let installIcons = INSTALL_ICONS
if (keyWords.value) {
installIcons = installIcons.filter(icon => icon.toLowerCase().includes(keyWords.value.toLowerCase()))
}
return chunk(installIcons, colSize.value).map((arr, idx) => {
return {
id: idx,
icons: arr
}
})
})
const copyIcon = (icon) => {
const { copy, isSupported } = useClipboard()
if (isSupported) {
copy(icon)
ElMessage({
message: `Copied: ${icon}`,
type: 'success'
})
} else {
ElMessage({
message: `Copy Not supported: ${icon}`,
type: 'error'
})
}
}
</script>
<template>
<div>
<el-row>
<el-col>
<el-form label-width="120px">
<el-form-item label="图标名字">
<el-input
v-model="keyWords"
:placeholder="`输入关键字搜索图标`"
/>
</el-form-item>
</el-form>
</el-col>
</el-row>
<RecycleScroller
v-slot="{ item }"
page-mode
class="scroller"
:items="filterIcons"
:item-size="80"
key-field="id"
>
<el-row>
<el-col
v-for="icon in item.icons"
:key="icon"
:span="24/colSize"
class="text-center"
>
<a
class="el-button el-button--large is-text"
style="height:80px;"
@click="copyIcon(icon)"
>
<div>
<common-icon
size="20"
:icon="icon"
/>
</div>
</a>
</el-col>
</el-row>
</RecycleScroller>
</div>
</template>
<style scoped>
.scroller {
height: 100%;
}
</style>

View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<strong>表格测试</strong>
</div>
</template>
<style scoped>
</style>