This commit is contained in:
zouzhibing 2023-05-29 11:29:04 +08:00
commit dc2f38da43
8 changed files with 232 additions and 55 deletions

View File

@ -12,7 +12,7 @@ vue-element-perfect 是一个后台前端解决方案,它使用了最新的前
### git仓库(欢迎 Star⭐) ### git仓库(欢迎 Star⭐)
- Gitee —— [https://gitee.com/yuanzbz/vue-admin-perfect](https://gitee.com/yuanzbz/vue-admin-perfect) - Gitee —— [https://gitee.com/yuanzbz/vue-admin-perfect](https://gitee.com/yuanzbz/vue-admin-perfect)
- GitHub —— [https://github.com/zouzhibin/vue-admin-perfect](https://github.com/zouzhibin/vue-admin-perfect) - GitHub —— [https://github.com/zouzhibin/vue-admin-perfect](https://github.com/zouzhibin/vue-admin-perfect)
- 基础功能版本: —— [https://gitee.com/yuanzbz/vue-admin-perfect](https://gitee.com/yuanzbz/vue-admin-perfect) - 基础功能版本: —— [https://gitee.com/yuanzbz/vue-admin-simple](https://gitee.com/yuanzbz/vue-admin-simple)
## 项目功能 ## 项目功能
- 使用Vue3.0开发单文件组件采用script setup - 使用Vue3.0开发单文件组件采用script setup

38
package-lock.json generated
View File

@ -31,6 +31,8 @@
"pinia": "^2.0.21", "pinia": "^2.0.21",
"pinia-plugin-persistedstate": "^2.1.1", "pinia-plugin-persistedstate": "^2.1.1",
"print-js": "^1.6.0", "print-js": "^1.6.0",
"raf": "^3.4.1",
"resize-observer-polyfill": "^1.5.1",
"sass": "^1.54.0", "sass": "^1.54.0",
"splitpanes": "^3.1.1", "splitpanes": "^3.1.1",
"svg-sprite-loader": "^6.0.11", "svg-sprite-loader": "^6.0.11",
@ -6524,6 +6526,11 @@
"integrity": "sha512-x3nrPvG0HDSDzUiJ0WqtzhN4MD+h5B+dFJ3/qyxVuARlr4Y3aJv8gri2cZzp9Z8sGs2a+aG9gNbKngh3gme57A==", "integrity": "sha512-x3nrPvG0HDSDzUiJ0WqtzhN4MD+h5B+dFJ3/qyxVuARlr4Y3aJv8gri2cZzp9Z8sGs2a+aG9gNbKngh3gme57A==",
"dev": true "dev": true
}, },
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
@ -6883,6 +6890,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/read-pkg": { "node_modules/read-pkg": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://r2.cnpmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "resolved": "https://r2.cnpmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@ -7177,6 +7192,11 @@
"resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz", "resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz",
"integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ=="
}, },
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz",
@ -14653,6 +14673,11 @@
"integrity": "sha512-x3nrPvG0HDSDzUiJ0WqtzhN4MD+h5B+dFJ3/qyxVuARlr4Y3aJv8gri2cZzp9Z8sGs2a+aG9gNbKngh3gme57A==", "integrity": "sha512-x3nrPvG0HDSDzUiJ0WqtzhN4MD+h5B+dFJ3/qyxVuARlr4Y3aJv8gri2cZzp9Z8sGs2a+aG9gNbKngh3gme57A==",
"dev": true "dev": true
}, },
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"picocolors": { "picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
@ -14909,6 +14934,14 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true "dev": true
}, },
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"requires": {
"performance-now": "^2.1.0"
}
},
"read-pkg": { "read-pkg": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://r2.cnpmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "resolved": "https://r2.cnpmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@ -15142,6 +15175,11 @@
"resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz", "resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz",
"integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ=="
}, },
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": { "resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -27,7 +27,7 @@
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
margin-right:20px;
:deep(.el-menu-item){ :deep(.el-menu-item){
height: 100%; height: 100%;
} }

View File

@ -1,4 +1,4 @@
<template> <template>
<!--纵向布局--> <!--纵向布局-->
<Height/> <Height/>
<div <div
@ -16,12 +16,18 @@
:collapse-transition="false" :collapse-transition="false"
class="menu-horizontal" class="menu-horizontal"
> >
<SubMenu :menuList="permission_routes"/> <SubItem
v-for="route in permission_routes"
:key="route.path"
:item="route"
/>
</el-menu> </el-menu>
<HeaderToolRight/> <HeaderToolRight/>
</div> </div>
<TagsView v-if="themeConfig.showTag"/> <TagsView v-if="themeConfig.showTag"/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -29,7 +35,7 @@
import Height from '../../components/Header/components/Height.vue' import Height from '../../components/Header/components/Height.vue'
import HeaderToolRight from '../../components/Header/ToolRight.vue' import HeaderToolRight from '../../components/Header/ToolRight.vue'
import TagsView from '../../components/TagsView/index.vue' import TagsView from '../../components/TagsView/index.vue'
import SubMenu from '../../components/SubMenu/SubMenu.vue' import SubItem from '../../components/SubMenu/SubItem.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import {usePermissionStore} from "@/store/modules/permission" import {usePermissionStore} from "@/store/modules/permission"
const PermissionStore = usePermissionStore() const PermissionStore = usePermissionStore()
@ -50,6 +56,7 @@ const activeMenu = computed(() => {
return path return path
}) })
// //
const themeConfig = computed(() =>SettingStore.themeConfig) const themeConfig = computed(() =>SettingStore.themeConfig)
const isCollapse = computed(() =>!SettingStore.isCollapse) const isCollapse = computed(() =>!SettingStore.isCollapse)
@ -57,5 +64,6 @@ const isCollapse = computed(() =>!SettingStore.isCollapse)
<style lang="scss" scoped> <style lang="scss" scoped>
@import "./index.scss"; @import "./index.scss";
</style> </style>

View File

@ -11,7 +11,12 @@
class="el-menu-vertical-demo" class="el-menu-vertical-demo"
:collapse="isCollapse" :collapse="isCollapse"
> >
<SubMenu :menuList="permission_routes"/> <SubItem
v-for="route in permission_routes"
:key="route.path"
:item="route"
/>
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</div> </div>
@ -19,10 +24,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import Logo from './components/Logo.vue' import Logo from './components/Logo.vue'
import SubMenu from '../SubMenu/SubMenu.vue' import SubItem from '../SubMenu/SubItem.vue'
import {useSettingStore} from "@/store/modules/setting" import {useSettingStore} from "@/store/modules/setting"
import {usePermissionStore} from "@/store/modules/permission" import {usePermissionStore} from "@/store/modules/permission"
import { computed, ref, watch } from "vue"; import { computed } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
// setupstore // setupstore
@ -35,8 +40,6 @@ const isCollapse = computed(() => !SettingStore.isCollapse)
// //
const themeConfig = computed(() =>SettingStore.themeConfig ) const themeConfig = computed(() =>SettingStore.themeConfig )
const basePath = ref('/')
// //
const permission_routes = computed(() => PermissionStore.permission_routes) const permission_routes = computed(() => PermissionStore.permission_routes)

View File

@ -0,0 +1,49 @@
<template>
<component :is="type" v-bind="linkProps(to)">
<slot />
</component>
</template>
<script lang="ts">
import { isExternal } from '@/utils/validate.js'
export default {
props: {
to: {
type: String,
required: true,
},
},
computed: {
isExternal() {
return isExternal(this.to)
},
type() {
if (this.isExternal) {
return 'a'
}
return 'router-link'
},
},
methods: {
linkProps(to) {
if (this.isExternal) {
return {
href: to,
target: '_blank',
rel: 'noopener',
}
}
return {
to: to,
}
},
},
}
</script>
<style lang="scss" scoped>
a {
text-decoration: none;
}
</style>

View File

@ -0,0 +1,79 @@
<template>
<template v-if="!item.hidden">
<template v-if="!item.alwaysShow && hasOneShowingChild(item.children, item)">
<app-link v-if="onlyOneChild.meta" :to="onlyOneChild.path">
<el-menu-item :index="onlyOneChild.path">
<el-icon :size="20">
<component :is="onlyOneChild?.meta.icon"></component>
</el-icon>
<template #title>{{ onlyOneChild.meta && onlyOneChild.meta.title }}</template>
</el-menu-item>
</app-link>
</template>
<el-sub-menu :index="item.path" v-else popper-append-to-body>
<template #title>
<el-icon :size="20"> <component :is="item.meta?.icon"></component></el-icon>
<span>{{ item.meta && item.meta.title }}</span>
</template>
<sub-item
v-for="child in item.children"
:key="child.path"
:item="child"
/>
</el-sub-menu>
</template>
</template>
<script lang="ts" setup>
import { isExternal } from '@/utils/validate.js'
import AppLink from './Link.vue'
import path from 'path-browserify'
import { ref, computed } from 'vue'
const props = defineProps({
item: {
type: Object,
required: true,
},
basePath: {
type: String,
default: '',
},
})
const onlyOneChild = ref(null)
const hasOneShowingChild = (children = [], parent) => {
const showingChildren = children.filter((item) => {
//
if (item.hidden) {
return false
} else {
// 使
onlyOneChild.value = item
return true
}
})
//
if (showingChildren.length === 1) {
return true
}
//
if (showingChildren.length === 0) {
onlyOneChild.value = { ...parent, noShowingChildren: true }
return true
}
return false
}
const resolvePath = (routePath) => {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(props.basePath)) {
return props.basePath
}
return path.resolve(props.basePath, routePath)
}
</script>