feat:框架布局

main
向文可 2 years ago
parent 170d05b005
commit 45564d6643

@ -4,10 +4,17 @@
declare module 'vue' {
export interface GlobalComponents {
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBacktop: typeof import('element-plus/es')['ElBacktop']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']

@ -1,15 +1,16 @@
<template>
<template v-if="name">
<i v-if="isRemix" class="x-icon" :class="'x-icon-' + name" v-bind="{ ...$props, ...$attrs }"></i>
<svg v-else-if="svg" class="x-icon" aria-hidden="true">
<svg v-if="svg" class="x-icon" aria-hidden="true">
<use :href="symbolId" :fill="color" />
</svg>
<i v-else-if="isRemix" class="x-icon" :class="'x-icon-' + name" v-bind="{ ...$props, ...$attrs }"></i>
<el-icon v-else class="x-icon">
<component :is="name" v-bind="{ ...$props, ...$attrs }" />
<component :is="icons[name]" />
</el-icon>
</template>
</template>
<script>
import * as icons from '@element-plus/icons';
export default defineComponent({
name: 'XIcon',
props: {
@ -35,12 +36,13 @@
const size = computed(() =>
Number.isNaN(new Number(props.size).valueOf()) ? props.size : props.size + 'px'
);
const isRemix = computed(() => props.name.indexOf('-') !== -1 || props.isCustomSvg);
const isRemix = computed(() => !Object.keys(icons).includes(props.name));
return {
symbolId,
size,
color: props.color,
isRemix,
icons,
};
},
});

@ -0,0 +1,24 @@
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">课程管理</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">课程列表</a></el-breadcrumb-item>
<el-breadcrumb-item>添加课程</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup></script>
<style lang="less" scoped>
.el-breadcrumb {
font-size: 16px;
margin-left: @layout-space-super;
:deep(.el-breadcrumb__item) {
.el-breadcrumb__inner {
color: @layout-header-fc;
a {
color: @layout-header-fc;
}
}
}
}
</style>

@ -0,0 +1,20 @@
<template>
<div class="layout-footer">© 2020 马士兵北京教育科技有限公司</div>
</template>
<script setup>
import Breakcrumb from './breakcrumb.vue';
</script>
<style lang="less" scoped>
.layout-footer {
width: 100%;
padding: @layout-space-small 0;
display: flex;
align-items: center;
justify-content: center;
background-color: @layout-footer-bgc;
box-shadow: @layout-shadow;
color: @layout-footer-fc;
}
</style>

@ -0,0 +1,41 @@
<template>
<div class="layout-header">
<div class="header-left">
<XIcon name="menu-fold-fill" size="16" />
<LayoutBreakcrumb />
</div>
<div class="header-right">
<LayoutOperation />
<LayoutProfile />
</div>
</div>
</template>
<script setup>
import LayoutBreakcrumb from './breakcrumb.vue';
import LayoutProfile from './profile.vue';
import LayoutOperation from './operation.vue';
</script>
<style lang="less" scoped>
.layout-header {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
background-color: @layout-header-bgc;
color: @layout-header-fc;
box-shadow: @layout-shadow;
.header-left,
.header-right {
height: @layout-header-height;
padding: 0 @layout-space-large;
display: flex;
align-items: center;
}
.x-icon {
border-radius: @layout-border-radius;
cursor: pointer;
}
}
</style>

@ -12,7 +12,7 @@
<style lang="less" scoped>
.layout-logo {
width: @layout-aside-width;
height: @layout-aside-width;
height: @layout-header-height;
display: flex;
align-items: center;
justify-content: center;

@ -1,26 +1,27 @@
<template>
<el-scrollbar class="layout-main">
<router-view v-slot="{ Component }">
<transition mode="out-in" name="fade-transform">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<el-scrollbar class="layout-main" always>
<RouterView />
</el-scrollbar>
</template>
<script setup></script>
<script setup>
import RouterView from './view.vue';
</script>
<style lang="less" scoped>
.layout-main {
margin: @layout-space-large;
background-color: @color-white;
border-radius: @layout-border-radius;
box-shadow: @layout-shadow;
:deep(.el-scrollbar__view) {
width: 100%;
height: 100%;
min-height: 100%;
> * {
display: inline-block; // BFC
width: 100%;
height: 100%;
padding: @layout-space-large;
}
}
}

@ -3,7 +3,7 @@
<XIcon :name="props.menuItem.meta.icon" size="20" />
<p>{{ props.menuItem.meta.title }}</p>
</el-menu-item>
<el-sub-menu v-else>
<el-sub-menu v-else :index="props.menuItem.name">
<template #title>
<XIcon :name="props.menuItem.meta.icon" size="20" />
<p>{{ props.menuItem.meta.title }}</p>

@ -33,8 +33,8 @@
.text-overflow();
}
.el-divider {
margin: @layout-space-small 0 @layout-space-large 0;
border-color: @layout-menu-hover-fc;
margin: 0 0 @layout-space-super 0;
border-color: @color-ghost-white;
:deep(.el-divider__text) {
color: @layout-menu-active-fc;
background-color: @layout-menu-bgc;

@ -0,0 +1,38 @@
<template>
<div class="layout-operation">
<el-button type="text" @click="handleFullscreen">
<XIcon :name="isFullscreen ? 'fullscreen-exit-fill' : 'fullscreen-fill'" />
</el-button>
</div>
</template>
<script setup>
const isFullscreen = ref(!!document.fullscreenElement);
const handleFullscreen = () => {
if (isFullscreen.value) {
document.exitFullscreen();
isFullscreen.value = false;
} else {
isFullscreen.value = true;
let fn =
document.body.requestFullscreen ||
document.body.webkitRequestFullscreen ||
document.body.mozRequestFullscreen ||
document.body.msRequestFullscreen;
document.fullscreenEnabled && fn?.call(document.body);
}
};
</script>
<style lang="less" scoped>
.layout-operation {
height: @layout-header-height;
padding: 0 @layout-space-large;
display: flex;
align-items: center;
justify-content: center;
.el-avatar {
margin-right: @layout-space;
}
}
</style>

@ -0,0 +1,31 @@
<template>
<div class="layout-profile">
<el-avatar />
<el-dropdown size="middle">
<span class="el-dropdown-link">
<span>管理员</span>
<XIcon class="el-icon--right" name="ArrowDown" />
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script setup></script>
<style lang="less" scoped>
.layout-profile {
height: @layout-header-height;
display: flex;
align-items: center;
justify-content: center;
.el-avatar {
margin-right: @layout-space;
}
}
</style>

@ -0,0 +1,20 @@
<template>
<div class="layout-tabs"></div>
</template>
<script setup></script>
<style lang="less" scoped>
.layout-tabs {
width: 100%;
height: @layout-tabs-height;
border-top: 1px solid @color-ghost-white;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
box-shadow: @layout-shadow;
background-color: @layout-header-bgc;
color: @layout-header-fc;
}
</style>

@ -0,0 +1,13 @@
<template>
<router-view v-slot="{ Component }">
<transition mode="out-in" name="fade-transform">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
</template>
<script setup></script>
<style lang="less" scoped></style>

@ -8,8 +8,10 @@
<LayoutMenu />
</div>
<div class="layout-right">
<div class="layout-header"></div>
<LayoutHeader />
<LayoutTabs />
<LayoutMain />
<LayoutFooter />
</div>
</div>
</template>
@ -19,12 +21,16 @@
import LayoutLogo from './components/logo.vue';
import LayoutAside from './components/aside.vue';
import LayoutMenu from './components/menu.vue';
import LayoutHeader from './components/header.vue';
import LayoutTabs from './components/tabs.vue';
import LayoutFooter from './components/footer.vue';
</script>
<style lang="less" scoped>
.layout-container {
width: 100%;
height: 100%;
background-color: @color-white-dark;
}
.layout-default {
display: flex;
@ -35,6 +41,8 @@
}
.layout-right {
flex: 1;
display: flex;
flex-direction: column;
}
}
</style>

@ -8,6 +8,6 @@ body,
*:not([class^='el-']) {
margin: 0;
padding: 0;
transition: all 0.1s;
transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
box-sizing: border-box;
}

@ -1,6 +1,7 @@
/* color */
@color-black: #282c34;
@color-white: #fff;
@color-white-dark: #f6f8f9;
@color-primary: #409eff;
@color-primary-white: #e8f4ff;
@ -42,9 +43,16 @@
@layout-menu-active-fc: @color-white;
@layout-menu-active-bgc: lighten(@color-black, 35%);
@layout-header-height: 68px;
@layout-header-height: 60px;
@layout-header-bgc: @color-white;
@layout-header-fc: @color-black;
@layout-tabs-height: 50px;
@layout-footer-bgc: @color-white;
@layout-footer-fc: @color-black;
:export {
layoutAsideWidth: @layout-aside-width;
layoutLogoSize: calc(@layout-aside-item-size * 0.8);
layoutLogoSize: calc(@layout-header-height * 0.72);
}

@ -28,6 +28,6 @@
<style lang="less">
.container {
height: 1080px;
height: 2000px !important;
}
</style>

Loading…
Cancel
Save