This commit is contained in:
liangjian
2026-01-27 16:32:15 +08:00
commit 836c61ac55
303 changed files with 34442 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
<template>
<div
class="vab-layout-column"
:class="{
fixed: fixedHeader,
'no-tabs-bar': !showTabs,
}"
>
<vab-column-bar />
<div
class="vab-main"
:class="{
['vab-main-' + theme.columnStyle]: true,
'is-collapse-main': collapse,
'is-no-tabs': !showTabs,
}"
>
<div
class="vab-layout-header"
:class="{
'fixed-header': fixedHeader,
'is-no-tabs': !showTabs,
}"
>
<vab-nav />
<vab-tabs v-show="showTabs" />
</div>
<vab-app-main />
</div>
</div>
</template>
<script lang="ts" setup>
import { useSettingsStore } from '/@/store/modules/settings'
defineOptions({
name: 'VabLayoutColumn',
})
defineProps({
collapse: {
type: Boolean,
default: false,
},
fixedHeader: {
type: Boolean,
default: true,
},
showTabs: {
type: Boolean,
default: true,
},
})
const settingsStore = useSettingsStore()
const { theme } = storeToRefs(settingsStore)
</script>
<style lang="scss" scoped>
.vab-layout-column {
.vab-main {
&.is-collapse-main {
&.vab-main-horizontal,
&.vab-main-semicircle {
margin-left: calc(var(--el-left-menu-width-min) * 1.4);
:deep() {
.fixed-header {
width: calc(100% - var(--el-left-menu-width-min) * 1.4);
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,70 @@
<template>
<div
class="vab-layout-fall"
:class="{
fixed: fixedHeader,
'no-tabs-bar': !showTabs,
}"
>
<vab-fall-bar />
<div
class="vab-main"
:class="{
'is-collapse-main': collapse,
'is-no-tabs': !showTabs,
}"
>
<div
class="vab-layout-header"
:class="{
'fixed-header': fixedHeader,
'is-no-tabs': !showTabs,
}"
>
<vab-nav />
<vab-tabs v-show="showTabs" />
</div>
<vab-app-main />
</div>
</div>
</template>
<script lang="ts" setup>
defineOptions({
name: 'VabLayoutFall',
})
defineProps({
collapse: {
type: Boolean,
default: false,
},
fixedHeader: {
type: Boolean,
default: true,
},
showTabs: {
type: Boolean,
default: true,
},
})
</script>
<style lang="scss" scoped>
.vab-layout-fall {
.vab-main {
&.is-collapse-main {
&.vab-main-horizontal,
&.vab-main-semicircle {
margin-left: calc(var(--el-left-menu-width-min) * 1.4);
:deep() {
.fixed-header {
width: calc(100% - var(--el-left-menu-width-min) * 1.4);
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,61 @@
<template>
<div
class="vab-layout-vertical"
:class="{
fixed: fixedHeader,
'no-tabs-bar': !showTabs,
}"
>
<vab-side-bar />
<div v-if="device === 'mobile' && !collapse" class="vab-modal" @click="foldSideBar"></div>
<div
class="vab-main"
:class="{
'is-collapse-main': collapse,
'is-no-tabs': !showTabs,
}"
>
<div
class="vab-layout-header"
:class="{
'fixed-header': fixedHeader,
'is-no-tabs': !showTabs,
}"
>
<vab-nav />
<vab-tabs v-show="showTabs" />
</div>
<vab-app-main />
</div>
</div>
</template>
<script lang="ts" setup>
import { useSettingsStore } from '/@/store/modules/settings'
defineOptions({
name: 'VabLayoutVertical',
})
defineProps({
collapse: {
type: Boolean,
default: false,
},
fixedHeader: {
type: Boolean,
default: true,
},
showTabs: {
type: Boolean,
default: true,
},
device: {
type: String,
default: 'desktop',
},
})
const settingsStore = useSettingsStore()
const { foldSideBar } = settingsStore
</script>

176
library/layouts/index.vue Normal file
View File

@@ -0,0 +1,176 @@
<template>
<el-scrollbar ref="scrollbarRef" wrap-class="scroll-wrap">
<div class="school-apartment-box" :class="{ mobile }">
<component :is="layout" :collapse="collapse" :device="device" :fixed-header="theme.fixedHeader" :show-tabs="theme.showTabs" />
</div>
<vab-statistics />
<el-backtop target="#app .scroll-wrap" />
</el-scrollbar>
</template>
<script lang="ts" setup>
import { ElScrollbar } from 'element-plus'
import { useSettingsStore } from '/@/store/modules/settings'
import { useUserStore } from '/@/store/modules/user'
import { convertToCamelCase } from '/@/utils/convertToCamelCase'
defineOptions({
name: 'Layout',
})
interface ComponentType {
default: Component
}
const route = useRoute()
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
const userStore = useUserStore()
const { username } = storeToRefs(userStore)
const settingsStore = useSettingsStore()
const { device, collapse, theme } = storeToRefs(settingsStore)
const { toggleDevice, foldSideBar, openSideBar, updateTheme } = settingsStore
const mobile = ref(false)
let oldLayout = theme.value.layout
const visibility = useDocumentVisibility()
const imports = import.meta.glob<ComponentType>('./**/*.vue', { eager: true })
const Components: Record<string, Component> = {}
Object.getOwnPropertyNames(imports).forEach((key: any) => {
Components[key.replaceAll(/(\/|\.|index.vue)/g, '')] = imports[key].default
})
const layout = computed(() => Components[convertToCamelCase(`vab-layout-${theme.value.layout}`)])
const resizeBody = () => {
const { width } = useWindowSize()
mobile.value = width.value - 1 < 992
}
watch(mobile, (value) => {
if (value) {
oldLayout = theme.value.layout
foldSideBar()
} else openSideBar()
theme.value.layout = value ? 'vertical' : oldLayout
toggleDevice(value ? 'mobile' : 'desktop')
})
onBeforeMount(() => {
resizeBody()
window.addEventListener('resize', resizeBody)
updateTheme()
})
onBeforeUnmount(() => {
if (mobile.value) theme.value.layout = oldLayout
window.removeEventListener('resize', resizeBody)
})
watch(visibility, (current, previous) => {
if (current === 'visible' && previous === 'hidden') $baseNotify(`尊敬的${username.value},欢迎回来`, '', 'success', 'bottom-right')
})
watch(
route,
() => {
nextTick(() => {
scrollbarRef.value!.setScrollTop(0)
})
},
{ immediate: true }
)
</script>
<style lang="scss" scoped>
.el-scrollbar__view.scroll-view {
overflow: auto;
}
.school-apartment-box {
position: relative;
width: 100%;
height: 100%;
[class*='vab-layout-'] {
:deep() {
.vab-layout-header {
border-bottom: 1px solid var(--el-border-color);
&.is-no-tabs {
border-bottom: 0;
}
}
}
&.fixed {
padding-top: calc(var(--el-nav-height) + var(--el-tabs-height));
}
&.fixed.no-tabs-bar {
padding-top: var(--el-nav-height);
}
}
:deep() {
.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: calc(var(--el-z-index) - 1);
width: 100%;
}
.vab-main {
position: relative;
width: auto;
min-height: 100%;
margin-left: var(--el-left-menu-width);
&.is-collapse-main {
margin-left: var(--el-left-menu-width-min);
.fixed-header {
width: var(--el-right-content-width-min);
}
}
&:not(.is-collapse-main) {
.fixed-header {
width: calc(100% - var(--el-left-menu-width));
}
}
}
}
/* 手机端开始 */
&.mobile {
:deep() {
.vab-layout-vertical {
.el-scrollbar.vab-side-bar {
z-index: calc(var(--el-z-index) + 1);
&.is-collapse {
width: 0;
}
}
.vab-main {
.fixed-header {
width: 100%;
}
margin-left: 0;
}
}
/* 隐藏分页和页码跳转 */
.el-pager,
.el-pagination__jump {
display: none;
}
}
}
/* 手机端结束 */
}
</style>