✨ init Project
This commit is contained in:
66
src/view/layout/aside/asideComponent/asyncSubmenu.vue
Normal file
66
src/view/layout/aside/asideComponent/asyncSubmenu.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-sub-menu
|
||||
ref="subMenu"
|
||||
:index="routerInfo.name"
|
||||
class="gva-sub-menu dark:text-slate-300 relative"
|
||||
>
|
||||
<template #title>
|
||||
<div
|
||||
v-if="!isCollapse"
|
||||
class="flex items-center"
|
||||
:style="{
|
||||
height: sideHeight
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="routerInfo.meta.icon">
|
||||
<component :is="routerInfo.meta.icon" />
|
||||
</el-icon>
|
||||
<span>{{ routerInfo.meta.title }}</span>
|
||||
</div>
|
||||
<template v-else>
|
||||
<el-icon v-if="routerInfo.meta.icon">
|
||||
<component :is="routerInfo.meta.icon" />
|
||||
</el-icon>
|
||||
<span>{{ routerInfo.meta.title }}</span>
|
||||
</template>
|
||||
</template>
|
||||
<slot />
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, computed } from 'vue'
|
||||
import { useAppStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
const appStore = useAppStore()
|
||||
const { config } = storeToRefs(appStore)
|
||||
|
||||
defineOptions({
|
||||
name: 'AsyncSubmenu'
|
||||
})
|
||||
|
||||
defineProps({
|
||||
routerInfo: {
|
||||
default: function () {
|
||||
return null
|
||||
},
|
||||
type: Object
|
||||
}
|
||||
})
|
||||
|
||||
const isCollapse = inject('isCollapse', {
|
||||
default: false
|
||||
})
|
||||
|
||||
const sideHeight = computed(() => {
|
||||
return config.value.layout_side_item_height + 'px'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.gva-sub-menu {
|
||||
.el-sub-menu__title {
|
||||
height: v-bind('sideHeight') !important;
|
||||
}
|
||||
}
|
||||
</style>
|
47
src/view/layout/aside/asideComponent/index.vue
Normal file
47
src/view/layout/aside/asideComponent/index.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<component
|
||||
:is="menuComponent"
|
||||
v-if="!routerInfo.hidden"
|
||||
:router-info="routerInfo"
|
||||
>
|
||||
<template v-if="routerInfo.children && routerInfo.children.length">
|
||||
<AsideComponent
|
||||
v-for="item in routerInfo.children"
|
||||
:key="item.name"
|
||||
:router-info="item"
|
||||
/>
|
||||
</template>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MenuItem from './menuItem.vue'
|
||||
import AsyncSubmenu from './asyncSubmenu.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'AsideComponent'
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
routerInfo: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'vertical'
|
||||
}
|
||||
})
|
||||
|
||||
const menuComponent = computed(() => {
|
||||
if (
|
||||
props.routerInfo.children &&
|
||||
props.routerInfo.children.filter((item) => !item.hidden).length
|
||||
) {
|
||||
return AsyncSubmenu
|
||||
} else {
|
||||
return MenuItem
|
||||
}
|
||||
})
|
||||
</script>
|
43
src/view/layout/aside/asideComponent/menuItem.vue
Normal file
43
src/view/layout/aside/asideComponent/menuItem.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<el-menu-item
|
||||
:index="routerInfo.name"
|
||||
class="dark:text-slate-300 overflow-hidden"
|
||||
:style="{
|
||||
height: sideHeight
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="routerInfo.meta.icon">
|
||||
<component :is="routerInfo.meta.icon" />
|
||||
</el-icon>
|
||||
<template #title>
|
||||
{{ routerInfo.meta.title }}
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useAppStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
const appStore = useAppStore()
|
||||
const { config } = storeToRefs(appStore)
|
||||
|
||||
defineOptions({
|
||||
name: 'MenuItem'
|
||||
})
|
||||
|
||||
defineProps({
|
||||
routerInfo: {
|
||||
default: function () {
|
||||
return null
|
||||
},
|
||||
type: Object
|
||||
}
|
||||
})
|
||||
|
||||
const sideHeight = computed(() => {
|
||||
return config.value.layout_side_item_height + 'px'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
146
src/view/layout/aside/combinationMode.vue
Normal file
146
src/view/layout/aside/combinationMode.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<div
|
||||
v-if="mode === 'head'"
|
||||
class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-[calc(100vw-600px)] overflow-auto"
|
||||
>
|
||||
<el-menu
|
||||
:default-active="routerStore.topActive"
|
||||
mode="horizontal"
|
||||
class="border-r-0 border-b-0 w-full flex gap-1 items-center box-border h-[calc(100%-1px)]"
|
||||
unique-opened
|
||||
@select="(index, _, ele) => selectMenuItem(index, _, ele, true)"
|
||||
>
|
||||
<template v-for="item in routerStore.topMenu">
|
||||
<aside-component
|
||||
v-if="!item.hidden"
|
||||
:key="item.name"
|
||||
:router-info="item"
|
||||
mode="horizontal"
|
||||
/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div
|
||||
v-if="mode === 'normal'"
|
||||
class="relative h-full bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 border-r shadow dark:shadow-gray-700"
|
||||
:class="isCollapse ? '' : ' px-2'"
|
||||
:style="{
|
||||
width: layoutSideWidth + 'px'
|
||||
}"
|
||||
>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
:collapse="isCollapse"
|
||||
:collapse-transition="false"
|
||||
:default-active="active"
|
||||
class="border-r-0 w-full"
|
||||
unique-opened
|
||||
@select="(index, _, ele) => selectMenuItem(index, _, ele, false)"
|
||||
>
|
||||
<template v-for="item in routerStore.leftMenu">
|
||||
<aside-component
|
||||
v-if="!item.hidden"
|
||||
:key="item.name"
|
||||
:router-info="item"
|
||||
/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
<div
|
||||
class="absolute bottom-8 right-2 w-8 h-8 bg-gray-50 dark:bg-slate-800 flex items-center justify-center rounded cursor-pointer"
|
||||
:class="isCollapse ? 'right-0 left-0 mx-auto' : 'right-2'"
|
||||
@click="toggleCollapse"
|
||||
>
|
||||
<el-icon v-if="!isCollapse">
|
||||
<DArrowLeft />
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<DArrowRight />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import AsideComponent from '@/view/layout/aside/asideComponent/index.vue'
|
||||
import { ref, provide, watchEffect, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRouterStore } from '@/pinia/modules/router'
|
||||
import { useAppStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
const appStore = useAppStore()
|
||||
const { device, config } = storeToRefs(appStore)
|
||||
|
||||
defineOptions({
|
||||
name: 'GvaAside'
|
||||
})
|
||||
|
||||
defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'normal'
|
||||
}
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const routerStore = useRouterStore()
|
||||
const isCollapse = ref(false)
|
||||
const active = ref('')
|
||||
const layoutSideWidth = computed(() => {
|
||||
if (!isCollapse.value) {
|
||||
return config.value.layout_side_width
|
||||
} else {
|
||||
return config.value.layout_side_collapsed_width
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
active.value = route.meta.activeName || route.name
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (device.value === 'mobile') {
|
||||
isCollapse.value = true
|
||||
} else {
|
||||
isCollapse.value = false
|
||||
}
|
||||
})
|
||||
|
||||
provide('isCollapse', isCollapse)
|
||||
|
||||
const selectMenuItem = (index, _, ele, top) => {
|
||||
const query = {}
|
||||
const params = {}
|
||||
routerStore.routeMap[index]?.parameters &&
|
||||
routerStore.routeMap[index]?.parameters.forEach((item) => {
|
||||
if (item.type === 'query') {
|
||||
query[item.key] = item.value
|
||||
} else {
|
||||
params[item.key] = item.value
|
||||
}
|
||||
})
|
||||
if (index === route.name) return
|
||||
if (index.indexOf('http://') > -1 || index.indexOf('https://') > -1) {
|
||||
window.open(index, '_blank')
|
||||
return
|
||||
}
|
||||
|
||||
if (!top) {
|
||||
router.push({ name: index, query, params })
|
||||
return
|
||||
}
|
||||
const leftMenu = routerStore.setLeftMenu(index)
|
||||
if (!leftMenu) {
|
||||
router.push({ name: index, query, params })
|
||||
return;
|
||||
}
|
||||
const firstMenu = leftMenu.find((item) => !item.hidden && item.path.indexOf("http://") === -1 && item.path.indexOf("https://") === -1)
|
||||
router.push({ name: firstMenu.name, query, params })
|
||||
|
||||
}
|
||||
|
||||
const toggleCollapse = () => {
|
||||
isCollapse.value = !isCollapse.value
|
||||
}
|
||||
</script>
|
106
src/view/layout/aside/headMode.vue
Normal file
106
src/view/layout/aside/headMode.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-[calc(100vw-600px)] overflow-auto"
|
||||
>
|
||||
<el-menu
|
||||
:default-active="active"
|
||||
mode="horizontal"
|
||||
class="border-r-0 w-full flex gap-1 items-center box-border h-[calc(100%-1px)]"
|
||||
unique-opened
|
||||
@select="selectMenuItem"
|
||||
>
|
||||
<template v-for="item in routerStore.asyncRouters[0].children">
|
||||
<aside-component
|
||||
v-if="!item.hidden"
|
||||
:key="item.name"
|
||||
:router-info="item"
|
||||
mode="horizontal"
|
||||
/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AsideComponent from '@/view/layout/aside/asideComponent/index.vue'
|
||||
import { ref, provide, watchEffect } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRouterStore } from '@/pinia/modules/router'
|
||||
import { useAppStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
const appStore = useAppStore()
|
||||
const { device } = storeToRefs(appStore)
|
||||
|
||||
defineOptions({
|
||||
name: 'GvaAside'
|
||||
})
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const routerStore = useRouterStore()
|
||||
const isCollapse = ref(false)
|
||||
const active = ref('')
|
||||
watchEffect(() => {
|
||||
if (route.name === 'Iframe') {
|
||||
active.value = decodeURIComponent(route.query.url)
|
||||
return
|
||||
}
|
||||
active.value = route.meta.activeName || route.name
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (device.value === 'mobile') {
|
||||
isCollapse.value = true
|
||||
} else {
|
||||
isCollapse.value = false
|
||||
}
|
||||
})
|
||||
|
||||
provide('isCollapse', isCollapse)
|
||||
|
||||
const selectMenuItem = (index) => {
|
||||
const query = {}
|
||||
const params = {}
|
||||
routerStore.routeMap[index]?.parameters &&
|
||||
routerStore.routeMap[index]?.parameters.forEach((item) => {
|
||||
if (item.type === 'query') {
|
||||
query[item.key] = item.value
|
||||
} else {
|
||||
params[item.key] = item.value
|
||||
}
|
||||
})
|
||||
if (index === route.name) return
|
||||
if (index.indexOf('http://') > -1 || index.indexOf('https://') > -1) {
|
||||
if (index === 'Iframe') {
|
||||
query.url = decodeURIComponent(index)
|
||||
router.push({
|
||||
name: 'Iframe',
|
||||
query,
|
||||
params
|
||||
})
|
||||
return
|
||||
} else {
|
||||
window.open(index, '_blank')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
router.push({ name: index, query, params })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-menu--horizontal.el-menu,
|
||||
.el-menu--horizontal > .el-menu-item.is-active {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.el-menu-item.is-active {
|
||||
background-color: var(--el-color-primary-light-8) !important;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.el-menu-item.is-active {
|
||||
background-color: var(--el-color-primary-bg) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
34
src/view/layout/aside/index.vue
Normal file
34
src/view/layout/aside/index.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<normal-mode
|
||||
v-if="
|
||||
config.side_mode === 'normal' ||
|
||||
(device === 'mobile' && config.side_mode == 'head') ||
|
||||
(device === 'mobile' && config.side_mode == 'combination')
|
||||
"
|
||||
/>
|
||||
<head-mode v-if="config.side_mode === 'head' && device !== 'mobile'" />
|
||||
<combination-mode
|
||||
v-if="config.side_mode === 'combination' && device !== 'mobile'"
|
||||
:mode="mode"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NormalMode from './normalMode.vue'
|
||||
import HeadMode from './headMode.vue'
|
||||
import CombinationMode from './combinationMode.vue'
|
||||
|
||||
defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'normal'
|
||||
}
|
||||
})
|
||||
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useAppStore } from '@/pinia'
|
||||
const appStore = useAppStore()
|
||||
const { config, device } = storeToRefs(appStore)
|
||||
</script>
|
120
src/view/layout/aside/normalMode.vue
Normal file
120
src/view/layout/aside/normalMode.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div
|
||||
class="relative h-full bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 border-r shadow dark:shadow-gray-700"
|
||||
:class="isCollapse ? '' : ' px-2'"
|
||||
:style="{
|
||||
width: layoutSideWidth + 'px'
|
||||
}"
|
||||
>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
:collapse="isCollapse"
|
||||
:collapse-transition="false"
|
||||
:default-active="active"
|
||||
class="border-r-0 w-full"
|
||||
unique-opened
|
||||
@select="selectMenuItem"
|
||||
>
|
||||
<template v-for="item in routerStore.asyncRouters[0]?.children || []">
|
||||
<aside-component
|
||||
v-if="!item.hidden"
|
||||
:key="item.name"
|
||||
:router-info="item"
|
||||
/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
<div
|
||||
class="absolute bottom-8 right-2 w-8 h-8 bg-gray-50 dark:bg-slate-800 flex items-center justify-center rounded cursor-pointer"
|
||||
:class="isCollapse ? 'right-0 left-0 mx-auto' : 'right-2'"
|
||||
@click="toggleCollapse"
|
||||
>
|
||||
<el-icon v-if="!isCollapse">
|
||||
<DArrowLeft />
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<DArrowRight />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AsideComponent from '@/view/layout/aside/asideComponent/index.vue'
|
||||
import { ref, provide, watchEffect, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRouterStore } from '@/pinia/modules/router'
|
||||
import { useAppStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
const appStore = useAppStore()
|
||||
const { device, config } = storeToRefs(appStore)
|
||||
|
||||
defineOptions({
|
||||
name: 'GvaAside'
|
||||
})
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const routerStore = useRouterStore()
|
||||
const isCollapse = ref(false)
|
||||
const active = ref('')
|
||||
const layoutSideWidth = computed(() => {
|
||||
if (!isCollapse.value) {
|
||||
return config.value.layout_side_width
|
||||
} else {
|
||||
return config.value.layout_side_collapsed_width
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
if (route.name === 'Iframe') {
|
||||
active.value = decodeURIComponent(route.query.url)
|
||||
return
|
||||
}
|
||||
active.value = route.meta.activeName || route.name
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (device.value === 'mobile') {
|
||||
isCollapse.value = true
|
||||
} else {
|
||||
isCollapse.value = false
|
||||
}
|
||||
})
|
||||
|
||||
provide('isCollapse', isCollapse)
|
||||
|
||||
const selectMenuItem = (index) => {
|
||||
const query = {}
|
||||
const params = {}
|
||||
routerStore.routeMap[index]?.parameters &&
|
||||
routerStore.routeMap[index]?.parameters.forEach((item) => {
|
||||
if (item.type === 'query') {
|
||||
query[item.key] = item.value
|
||||
} else {
|
||||
params[item.key] = item.value
|
||||
}
|
||||
})
|
||||
if (index === route.name) return
|
||||
if (index.indexOf('http://') > -1 || index.indexOf('https://') > -1) {
|
||||
if (index === 'Iframe') {
|
||||
query.url = decodeURIComponent(index)
|
||||
router.push({
|
||||
name: 'Iframe',
|
||||
query,
|
||||
params
|
||||
})
|
||||
return
|
||||
} else {
|
||||
window.open(index, '_blank')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
router.push({ name: index, query, params })
|
||||
}
|
||||
}
|
||||
|
||||
const toggleCollapse = () => {
|
||||
isCollapse.value = !isCollapse.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
Reference in New Issue
Block a user