Default Changelist

feature/qzw
秦泽旺 3 months ago
parent d6b3e60e2e
commit 8f2041d14a

@ -1,6 +1,8 @@
<template> <template>
<!-- 使用 `a-layout` 组件构建页面布局并根据 `device` 变量的值动态添加类名与下面的样式设置配合来实现不同设备下的布局样式适配例如可能根据是移动端桌面端等设备呈现不同的外观和布局形式 -->
<a-layout :class="['layout', device]"> <a-layout :class="['layout', device]">
<!-- SideMenu --> <!-- SideMenu -->
<!-- 当设备处于移动端通过 `isMobile()` 方法判断显示侧边栏抽屉组件`a-drawer`用于在移动端展示侧边栏菜单提供更好的交互体验和节省屏幕空间 -->
<a-drawer <a-drawer
v-if="isMobile()" v-if="isMobile()"
placement="left" placement="left"
@ -9,6 +11,7 @@
:visible="collapsed" :visible="collapsed"
@close="drawerClose" @close="drawerClose"
> >
<!-- 在抽屉内部使用 `side-menu` 组件来展示侧边栏菜单设置菜单模式为 `inline`内联模式可能表示菜单项水平排列等样式特点传入 `menus` 数据用于生成具体的菜单项同时传递主题折叠状态等相关属性用于控制菜单的外观和交互行为 -->
<side-menu <side-menu
mode="inline" mode="inline"
:menus="menus" :menus="menus"
@ -19,6 +22,7 @@
></side-menu> ></side-menu>
</a-drawer> </a-drawer>
<!-- 当设备不是移动端且满足 `isSideMenu()` 条件时推测是桌面端等支持侧边栏显示的情况显示侧边栏菜单`side-menu`组件同样传入相关属性来配置菜单的展示形式主题折叠状态等这里的折叠状态由 `collapsed` 变量控制 -->
<side-menu <side-menu
v-else-if="isSideMenu()" v-else-if="isSideMenu()"
mode="inline" mode="inline"
@ -28,8 +32,10 @@
:collapsible="true" :collapsible="true"
></side-menu> ></side-menu>
<!-- 使用另一个 `a-layout` 组件来进一步构建页面主体部分的布局通过动态绑定类名和内联样式来控制布局样式例如根据不同的 `layoutMode` `contentWidth` 来呈现不同的页面布局风格同时设置最小高度为视口高度`100vh`确保页面内容区域能占满整个屏幕高度 -->
<a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }"> <a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
<!-- layout header --> <!-- layout header -->
<!-- 使用 `global-header` 组件来展示页面头部传入如布局模式菜单数据主题折叠状态设备类型等属性通过这些属性来控制头部的显示内容和交互行为例如根据不同的布局模式显示不同的菜单样式根据折叠状态显示或隐藏某些元素等同时监听 `toggle` 事件用于处理头部相关的交互操作如展开/收起侧边栏等 -->
<global-header <global-header
:mode="layoutMode" :mode="layoutMode"
:menus="menus" :menus="menus"
@ -40,19 +46,24 @@
/> />
<!-- layout content --> <!-- layout content -->
<!-- 使用 `a-layout-content` 组件来承载页面的主体内容部分设置内容区域的高度为 `100%`占满父容器高度设置外边距以及根据 `fixedHeader` 变量的值来决定顶部的内边距若头部固定则设置顶部内边距为 `64px`否则为 `0`确保内容区域在页面中的布局位置和样式符合设计要求 -->
<a-layout-content :style="{ height: '100%', margin: '24px 24px 0', paddingTop: fixedHeader? '64px' : '0' }"> <a-layout-content :style="{ height: '100%', margin: '24px 24px 0', paddingTop: fixedHeader? '64px' : '0' }">
<!-- 当启用多标签页功能`multiTab` `true`显示 `multi-tab` 组件可能用于实现同时展示多个页面标签并可切换查看不同页面内容的功能方便用户在多个相关页面之间快速切换浏览 -->
<multi-tab v-if="multiTab"></multi-tab> <multi-tab v-if="multiTab"></multi-tab>
<!-- 使用 Vue 的过渡效果`transition`组件包裹 `route-view` 组件当路由切换时通过名为 `page-transition` 的过渡效果来实现页面切换的动画效果比如淡入淡出缩放等动画表现提升页面切换的视觉体验 -->
<transition name="page-transition"> <transition name="page-transition">
<route-view /> <route-view />
</transition> </transition>
</a-layout-content> </a-layout-content>
<!-- layout footer --> <!-- layout footer -->
<!-- 使用 `a-layout-footer` 组件来展示页面底部内部包含 `global-footer` 组件用于展示如版权信息联系方式等页面底部相关的通用内容 -->
<a-layout-footer> <a-layout-footer>
<global-footer /> <global-footer />
</a-layout-footer> </a-layout-footer>
<!-- Setting Drawer (show in development mode) --> <!-- Setting Drawer (show in development mode) -->
<!-- 当处于非生产环境`!production`显示 `setting-drawer` 组件从命名推测可能是用于在开发阶段展示一些设置选项的抽屉式组件方便开发人员进行调试配置等操作在正式上线的生产环境中则不显示该组件 -->
<setting-drawer v-if="!production"></setting-drawer> <setting-drawer v-if="!production"></setting-drawer>
</a-layout> </a-layout>
</a-layout> </a-layout>
@ -60,20 +71,26 @@
</template> </template>
<script> <script>
import { triggerWindowResizeEvent } from '../utils/util' // '../utils/util' `triggerWindowResizeEvent`
import { mapState, mapActions } from 'vuex' import { triggerWindowResizeEvent } from '../utils/util';
import { mixin, mixinDevice } from '../utils/mixin' // 'vuex' `mapState` `mapActions` `mapState` Vuex 便使`mapActions` Vuex actions mutations 便
import config from '../config/defaultSettings' import { mapState, mapActions } from 'vuex';
// '../utils/mixin' `mixin` `mixinDevice`Vue mixin使
import { mixin, mixinDevice } from '../utils/mixin';
// `config`
import config from '../config/defaultSettings';
import RouteView from './RouteView' // 使 UI
import MultiTab from '../components/MultiTab' import RouteView from './RouteView';
import SideMenu from '../components/Menu/SideMenu' import MultiTab from '../components/MultiTab';
import GlobalHeader from '../components/GlobalHeader' import SideMenu from '../components/Menu/SideMenu';
import GlobalFooter from '../components/GlobalFooter' import GlobalHeader from '../components/GlobalHeader';
import SettingDrawer from '../components/SettingDrawer' import GlobalFooter from '../components/GlobalFooter';
import SettingDrawer from '../components/SettingDrawer';
export default { export default {
name: 'BasicLayout', name: 'BasicLayout',
// `mixin` `mixinDevice` 使
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: { components: {
RouteView, RouteView,
@ -85,75 +102,92 @@ export default {
}, },
data () { data () {
return { return {
// `config` `production` 使
production: config.production, production: config.production,
// `collapsed` `false``false`
collapsed: false, collapsed: false,
// `menus`
menus: [] menus: []
} }
}, },
computed: { computed: {
...mapState({ ...mapState({
// //
// 使 `mapState` Vuex `state.permission.addRouters` `mainMenu` 便
mainMenu: state => state.permission.addRouters mainMenu: state => state.permission.addRouters
}), }),
contentPaddingLeft () { contentPaddingLeft () {
// `fixSidebar``isMobile()``contentPaddingLeft`
if (!this.fixSidebar || this.isMobile()) { if (!this.fixSidebar || this.isMobile()) {
return '0' return '0';
} }
// `sidebarOpened` `true` `256px`
if (this.sidebarOpened) { if (this.sidebarOpened) {
return '256px' return '256px';
} }
return '80px' return '80px';
} }
}, },
watch: { watch: {
sidebarOpened (val) { sidebarOpened (val) {
this.collapsed = !val // `sidebarOpened` `collapsed` `sidebarOpened` `true` `collapsed` `false`
this.collapsed =!val;
} }
}, },
created () { created () {
this.menus = this.mainMenu.find(item => item.path === '/').children // `mainMenu` Vuex `'/'` `children` `menus`
this.collapsed = !this.sidebarOpened this.menus = this.mainMenu.find(item => item.path === '/').children;
// `sidebarOpened` `collapsed`
this.collapsed =!this.sidebarOpened;
}, },
mounted () { mounted () {
const userAgent = navigator.userAgent const userAgent = navigator.userAgent;
// `userAgent` `Edge` Edge Edge
if (userAgent.indexOf('Edge') > -1) { if (userAgent.indexOf('Edge') > -1) {
this.$nextTick(() => { this.$nextTick(() => {
this.collapsed = !this.collapsed // `collapsed`
this.collapsed =!this.collapsed;
setTimeout(() => { setTimeout(() => {
this.collapsed = !this.collapsed // 16 `collapsed` Edge `collapsed`
}, 16) this.collapsed =!this.collapsed;
}) }, 16);
});
} }
}, },
methods: { methods: {
...mapActions(['setSidebar']), ...mapActions(['setSidebar']),
toggle () { toggle () {
this.collapsed = !this.collapsed // `toggle` / `collapsed` `setSidebar` `mapActions` Vuex action `collapsed` Vuex `triggerWindowResizeEvent`便
this.setSidebar(!this.collapsed) this.collapsed =!this.collapsed;
triggerWindowResizeEvent() this.setSidebar(!this.collapsed);
triggerWindowResizeEvent();
}, },
paddingCalc () { paddingCalc () {
let left = '' let left = '';
// `sidebarOpened``isMobile()``fixSidebar` `256px``80px` `0`
if (this.sidebarOpened) { if (this.sidebarOpened) {
left = this.isDesktop() ? '256px' : '80px' left = this.isDesktop()? '256px' : '80px';
} else { } else {
left = (this.isMobile() && '0') || ((this.fixSidebar && '80px') || '0') left = (this.isMobile() && '0') || ((this.fixSidebar && '80px') || '0');
} }
return left return left;
}, },
menuSelect () { menuSelect () {
// `menuSelect` `!isDesktop()``collapsed` `false`便
if (!this.isDesktop()) { if (!this.isDesktop()) {
this.collapsed = false this.collapsed = false;
} }
}, },
drawerClose () { drawerClose () {
this.collapsed = false // `drawerClose` `collapsed` `false`便
this.collapsed = false;
} }
} }
} }
</script> </script>
<style lang="less"> <style lang="less">
// '../components/global.less'
@import url('../components/global.less'); @import url('../components/global.less');
/* /*
@ -165,14 +199,17 @@ export default {
* these styles. * these styles.
*/ */
// `page-transition` enter `0`
.page-transition-enter { .page-transition-enter {
opacity: 0; opacity: 0;
} }
// `page-transition` leaveactive `0` `page-transition-enter`
.page-transition-leave-active { .page-transition-leave-active {
opacity: 0; opacity: 0;
} }
// `page-transition` enterleaveactive `.page-transition-container` `transform` `1.1`
.page-transition-enter.page-transition-container, .page-transition-enter.page-transition-container,
.page-transition-leave-active.page-transition-container { .page-transition-leave-active.page-transition-container {
-webkit-transform: scale(1.1); -webkit-transform: scale(1.1);

@ -1,11 +1,13 @@
<template> <template>
<!-- 最外层使用一个 `<div>` 元素作为容器这是一种常见的做法用于包裹内部的其他元素方便对整体内容进行布局和样式控制同时避免一些样式冲突等问题 -->
<div> <div>
<!-- 使用 `<router-view>` 组件这是 Vue Router 中的核心组件它用于根据当前的路由配置来动态渲染相应的组件内容例如当用户访问不同的路由路径时该组件所在的位置就会显示对应路由所配置的组件实现页面的动态切换和展示不同的功能页面这里它占据了整个 `<div>` 容器的空间用于展示路由对应的页面内容 -->
<router-view /> <router-view />
</div> </div>
</template> </template>
<script> <script>
// 使 ES6
// `name` `'BlankLayout'` Vue 便
export default { export default {
name: 'BlankLayout' name: 'BlankLayout'
} }

@ -1,12 +1,20 @@
<template> <template>
<!-- 根据路由元数据`$route.meta` `hiddenHeaderContent` 属性的值来动态设置外层 `<div>` 的内联样式
如果 `hiddenHeaderContent` `false`则设置顶部和左右边距为 `-24px`可能是为了让内部的 `page-header` 组件能覆盖部分外层容器的默认边距实现特定的布局效果若为 `true`则不设置任何样式`null` -->
<div :style="!$route.meta.hiddenHeaderContent? 'margin: -24px -24px 0px;' : null"> <div :style="!$route.meta.hiddenHeaderContent? 'margin: -24px -24px 0px;' : null">
<!-- pageHeader, route meta :true on hide --> <!-- pageHeader, route meta :true on hide -->
<!-- 当路由元数据中的 `hiddenHeaderContent` 属性为 `false` 即不隐藏头部内容显示 `page-header` 组件
该组件接收 `title`页面标题`logo`可能是页面 logo 图标`avatar`可能是用户头像相关等属性来展示相应的头部信息以下是其内部插槽及各部分功能的详细说明 -->
<page-header v-if="!$route.meta.hiddenHeaderContent" :title="pageTitle" :logo="logo" :avatar="avatar"> <page-header v-if="!$route.meta.hiddenHeaderContent" :title="pageTitle" :logo="logo" :avatar="avatar">
<!-- 名为 `action` 的插槽用于在 `page-header` 组件的特定位置插入自定义的操作按钮等元素具体插入的内容由使用该组件的父组件通过 `<slot>` 传递进来 -->
<slot slot="action" name="action"></slot> <slot slot="action" name="action"></slot>
<!-- 名为 `content` 的插槽用于在 `page-header` 组件中插入自定义的头部内容部分同样具体内容由父组件决定若父组件没有传递对应内容通过 `this.$slots.headerContent` 判断且存在 `description` 数据时会显示下方默认的内容结构 -->
<slot slot="content" name="headerContent"></slot> <slot slot="content" name="headerContent"></slot>
<div slot="content" v-if="!this.$slots.headerContent && description"> <div slot="content" v-if="!this.$slots.headerContent && description">
<!-- 显示一段描述信息`description`设置字体大小为 `14px`颜色为半透明黑色用于在头部提供一些辅助说明文字 -->
<p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ description }}</p> <p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ description }}</p>
<div class="link"> <div class="link">
<!-- 使用 `v-for` 指令循环遍历 `linkList` 数组用于生成一组链接元素每个链接包含一个图标通过 `a-icon` 组件展示图标类型由 `link.icon` 指定和对应的文字标题`link.title`点击链接可跳转到 `link.href` 指定的地址方便在头部展示相关的导航链接等信息 -->
<template v-for="(link, index) in linkList"> <template v-for="(link, index) in linkList">
<a :key="index" :href="link.href"> <a :key="index" :href="link.href">
<a-icon :type="link.icon" /> <a-icon :type="link.icon" />
@ -15,12 +23,14 @@
</template> </template>
</div> </div>
</div> </div>
<!-- 名为 `extra` 的插槽用于在 `page-header` 组件中插入额外的元素默认情况下包含一个用于展示图片`extraImage`的结构 `extraImage` 有值则会显示对应的图片图片会根据样式设置进行布局展示 -->
<slot slot="extra" name="extra"> <slot slot="extra" name="extra">
<div class="extra-img"> <div class="extra-img">
<img v-if="typeof extraImage!== 'undefined'" :src="extraImage"/> <img v-if="typeof extraImage!== 'undefined'" :src="extraImage"/>
</div> </div>
</slot> </slot>
<div slot="pageMenu"> <div slot="pageMenu">
<!-- `search` 属性为 `true` 显示搜索框组件`a-input-search`用于在页面头部提供搜索功能设置了宽度占位符大小以及回车键触发的按钮文本等样式和属性方便用户输入内容进行搜索操作 -->
<div class="page-menu-search" v-if="search"> <div class="page-menu-search" v-if="search">
<a-input-search <a-input-search
style="width: 80%; max-width: 522px;" style="width: 80%; max-width: 522px;"
@ -29,6 +39,7 @@
enterButton="搜索" enterButton="搜索"
/> />
</div> </div>
<!-- `tabs` 属性存在且其 `items` 属性有值时显示选项卡组件`a-tabs`用于在页面头部展示多个可切换的选项卡通过 `v-for` 循环生成每个选项卡面板`a-tab-pane`每个面板的标题由 `item.title` 指定同时绑定了选项卡切换的回调函数`@change` 事件触发 `tabs.callback`以及设置活动选项卡的 `activeKey`方便用户切换不同的页面视图等 -->
<div class="page-menu-tabs" v-if="tabs && tabs.items"> <div class="page-menu-tabs" v-if="tabs && tabs.items">
<!-- @change="callback" :activeKey="activeKey" --> <!-- @change="callback" :activeKey="activeKey" -->
<a-tabs :tabBarStyle="{margin: 0}" :activeKey="tabs.active()" @change="tabs.callback"> <a-tabs :tabBarStyle="{margin: 0}" :activeKey="tabs.active()" @change="tabs.callback">
@ -39,6 +50,7 @@
</page-header> </page-header>
<div class="content"> <div class="content">
<div class="page-header-index-wide"> <div class="page-header-index-wide">
<!-- 使用插槽`<slot>`来插入内容若启用了多标签页功能`multiTab` `true`通过 Vuex 状态获取则使用 `<keep-alive>` 组件包裹 `<router-view>`这样在切换路由时对应的页面组件会被缓存下次访问能更快地展示 `multiTab` `false`则直接使用 `<router-view>`用于根据路由配置动态展示相应的页面内容`ref="content"` 用于在组件内部通过 `this.$refs.content` 来获取该元素的引用方便后续操作 -->
<slot> <slot>
<!-- keep-alive --> <!-- keep-alive -->
<keep-alive v-if="multiTab"> <keep-alive v-if="multiTab">
@ -50,25 +62,30 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapState } from 'vuex' // 'vuex' `mapState` Vuex 便使`multiTab`
import PageHeader from '../components/PageHeader' import { mapState } from 'vuex';
// `PageHeader` 使
import PageHeader from '../components/PageHeader';
export default { export default {
name: 'PageView', name: 'PageView',
// `components` `PageHeader` 使使 `<page-header>` Vue 使
components: { components: {
PageHeader PageHeader
}, },
props: { props: {
// `avatar` `null` `avatar` `null`
avatar: { avatar: {
type: String, type: String,
default: null default: null
}, },
// `title` `true` `false`
title: { title: {
type: [String, Boolean], type: [String, Boolean],
default: true default: true
}, },
// `logo` logo `null` `avatar` logo logo
logo: { logo: {
type: String, type: String,
default: null default: null
@ -76,40 +93,52 @@ export default {
}, },
data () { data () {
return { return {
// `pageTitle` `null` `title` `page-header`
pageTitle: null, pageTitle: null,
// `description` `null` `headerContent`
description: null, description: null,
// `linkList` `href``icon``title`
linkList: [], linkList: [],
// `extraImage` `extra`
extraImage: '', extraImage: '',
// `search` `false` `true`
search: false, search: false,
// `tabs` `items`
tabs: {} tabs: {}
} }
}, },
computed: { computed: {
...mapState({ ...mapState({
// 使 `mapState` Vuex `state.app.multiTab` `multiTab` 便`<router-view>`使 `<keep-alive>`
multiTab: state => state.app.multiTab multiTab: state => state.app.multiTab
}) })
}, },
mounted () { mounted () {
this.getPageMeta() // `getPageMeta`
this.getPageMeta();
}, },
updated () { updated () {
this.getPageMeta() // `getPageMeta`
this.getPageMeta();
}, },
methods: { methods: {
getPageMeta () { getPageMeta () {
// eslint-disable-next-line // eslint-disable-next-line
this.pageTitle = (typeof(this.title) === 'string' || !this.title) ? this.title : this.$route.meta.title // `pageTitle` `title` `title` `false`使 `title` `pageTitle``this.$route.meta` `title`
this.pageTitle = (typeof(this.title) === 'string' ||!this.title)? this.title : this.$route.meta.title;
const content = this.$refs.content const content = this.$refs.content;
if (content) { if (content) {
if (content.pageMeta) { if (content.pageMeta) {
Object.assign(this, content.pageMeta) // `this.$refs.content` `pageMeta` 使 `Object.assign` `this`
Object.assign(this, content.pageMeta);
} else { } else {
this.description = content.description // `pageMeta` `content` `description``linkList``extraImage``search``tabs`
this.linkList = content.linkList this.description = content.description;
this.extraImage = content.extraImage this.linkList = content.linkList;
this.search = content.search === true this.extraImage = content.extraImage;
this.tabs = content.tabs this.search = content.search === true;
this.tabs = content.tabs;
} }
} }
} }
@ -119,23 +148,31 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.content { .content {
// `content` `24px` `0`使
margin: 24px 24px 0; margin: 24px 24px 0;
.link { .link {
// `link` `16px`使
margin-top: 16px; margin-top: 16px;
&:not(:empty) { &:not(:empty) {
// `link` `16px`使
margin-bottom: 16px; margin-bottom: 16px;
} }
a { a {
// `<a>` `32px`
margin-right: 32px; margin-right: 32px;
// `24px` `24px`
height: 24px; height: 24px;
line-height: 24px; line-height: 24px;
// 使便
display: inline-block; display: inline-block;
i { i {
// `<i>` `a-icon` `24px`使 `8px``vertical-align: middle`
font-size: 24px; font-size: 24px;
margin-right: 8px; margin-right: 8px;
vertical-align: middle; vertical-align: middle;
} }
span { span {
// `<span>` `24px`便
height: 24px; height: 24px;
line-height: 24px; line-height: 24px;
display: inline-block; display: inline-block;
@ -145,19 +182,26 @@ export default {
} }
} }
.page-menu-search { .page-menu-search {
// `page-menu-search` 使
text-align: center; text-align: center;
// `16px`
margin-bottom: 16px; margin-bottom: 16px;
} }
.page-menu-tabs { .page-menu-tabs {
// `page-menu-tabs` `48px`使
margin-top: 48px; margin-top: 48px;
} }
.extra-img { .extra-img {
// `extra-img` `-60px`
margin-top: -60px; margin-top: -60px;
//
text-align: center; text-align: center;
// `195px`
width: 195px; width: 195px;
img { img {
// `<img>` `100%`使 `extra-img`
width: 100%; width: 100%;
} }
} }

@ -1,31 +1,46 @@
<script> <script>
// 使使 ES6
export default { export default {
name: 'RouteView', name: 'RouteView',
// `props` `keepAlive`
props: { props: {
// `keepAlive` `Boolean` `true` `false`
// `true` `keepAlive` 使 `true`
keepAlive: { keepAlive: {
type: Boolean, type: Boolean,
default: true default: true
} }
}, },
data () { data () {
// `data`
return {} return {}
}, },
render () { render () {
// `this` `$route` `meta` `$store` `getters` 便使 Vuex `getters`
// `$route` `meta` `$store` Vuex `getters`
const { $route: { meta }, $store: { getters } } = this const { $route: { meta }, $store: { getters } } = this
// `<keep-alive>` `<router-view>` DOM `<keep-alive>` Vue 使
const inKeep = ( const inKeep = (
<keep-alive> <keep-alive>
<router-view /> <router-view />
</keep-alive> </keep-alive>
) )
// `<keep-alive>` `<router-view>` DOM
const notKeep = ( const notKeep = (
<router-view /> <router-view />
) )
// multiTab multiTab
// // 使 `<keep-alive>` `inKeep``notKeep`
// return meta.keepAlive ? inKeep : notKeep // `multiTab` `getters` `multiTab` `multiTab` `multiTab` `true`
// 使 `return meta.keepAlive? inKeep : notKeep` `keepAlive`
if (!getters.multiTab && meta.keepAlive === false) { if (!getters.multiTab && meta.keepAlive === false) {
return notKeep return notKeep
} }
// `<keep-alive>` `inKeep``notKeep`
// 1. `props` `keepAlive` `true`
// 2. `getters` `multiTab` `true`
// 3. `meta` `keepAlive` `true`
return this.keepAlive || getters.multiTab || meta.keepAlive? inKeep : notKeep return this.keepAlive || getters.multiTab || meta.keepAlive? inKeep : notKeep
} }
} }

@ -1,26 +1,36 @@
<template> <template>
<!-- 使用 `<div>` 元素作为整个布局的根容器并设置 `id` `userLayout`同时通过动态绑定类名`:class`添加 `user-layout-wrapper` 类以及根据 `device` 变量其值应该会根据不同设备进行变化比如区分移动端桌面端等来动态添加相应的设备相关类名用于后续样式的适配实现不同设备下的特定布局效果 -->
<div id="userLayout" :class="['user-layout-wrapper', device]"> <div id="userLayout" :class="['user-layout-wrapper', device]">
<!-- 使用 `<div>` 元素创建一个 `container` 类名的容器用于包裹页面的主要内容部分包括头部主体内容通过 `<route-view>` 展示不同路由对应的页面内容以及底部等方便对整体内容进行统一的布局和样式控制 -->
<div class="container"> <div class="container">
<!-- 创建一个 `top` 类名的 `<div>` 元素用于放置页面的顶部相关内容通常可以包含如页面标题 logo 以及一些简短的描述信息等使其在视觉上处于页面的上方位置符合常见的页面布局结构 -->
<div class="top"> <div class="top">
<!-- 使用 `<div>` 元素创建 `header` 类名的部分用于展示页面头部的具体元素例如 logo 和标题等是整个页面头部区域的核心部分可呈现出品牌标识和页面主要名称等关键信息 -->
<div class="header"> <div class="header">
<!-- 使用 `<a>` 标签创建一个链接指向根路径`/`当用户点击时会跳转到对应的页面内部包含一个 `<img>` 标签用于展示 logo 图片图片路径为相对路径 `../assets/logo.svg`并设置 `class` `logo` `alt` 属性用于图片描述在图片无法正常显示时显示 `logo` 文字提示以及一个 `<span>` 标签用于展示页面标题文字 `Online Exam`整体构成了页面头部的主要视觉元素和品牌标识展示 -->
<a href="/"> <a href="/">
<img src="../assets/logo.svg" class="logo" alt="logo"> <img src="../assets/logo.svg" class="logo" alt="logo">
<span class="title">Online Exam</span> <span class="title">Online Exam</span>
</a> </a>
</div> </div>
<!-- 使用 `<div>` 元素创建 `desc` 类名的部分用于展示一段描述性文字这里明确写出了该在线考试系统是基于 `SpringBoot + Vue` 实现的用于向用户简单介绍系统的技术实现背景等信息增强页面的可读性和对系统的初步了解 -->
<div class="desc"> <div class="desc">
基于SpringBoot+Vue实现的在线考试系统 基于SpringBoot+Vue实现的在线考试系统
</div> </div>
</div> </div>
<!-- 使用 `<route-view>` 组件这是 Vue Router 中的核心组件它会根据当前的路由配置动态渲染相应的组件内容在这里它占据了页面主体部分的空间用于展示与用户相关的不同功能页面比如登录注册等页面具体取决于路由配置实现页面的动态切换和功能展示 -->
<route-view></route-view> <route-view></route-view>
<!-- 使用 `<div>` 元素创建 `footer` 类名的部分用于展示页面底部的相关内容通常包含如版权信息友情链接联系信息等是整个页面底部区域的核心部分用于提供一些辅助性通用性的页面信息展示 -->
<div class="footer"> <div class="footer">
<!-- 使用 `<div>` 元素创建 `links` 类名的容器用于放置一组链接元素这些链接分别指向不同的外部资源如代码仓库关于作者的页面以及可以联系作者的邮箱链接等方便用户获取更多相关信息查看代码或者与作者进行沟通交流 -->
<div class="links"> <div class="links">
<a href="https://github.com/19920625lsg/spring-boot-online-exam" target="_blank">代码仓</a> <a href="https://github.com/19920625lsg/spring-boot-online-exam" target="_blank">代码仓</a>
<a href="https://19920625lsg.github.io" target="_blank">关于我</a> <a href="https://19920625lsg.github.io" target="_blank">关于我</a>
<a href="mailto:liangshanguang2@gmail.com">联系我</a> <a href="mailto:liangshanguang2@gmail.com">联系我</a>
</div> </div>
<!-- 使用 `<div>` 元素创建 `copyright` 类名的部分用于展示版权声明信息明确指出版权所有者以及版权年份等信息告知用户该页面内容的版权归属情况符合法律规范和常规的页面版权展示要求 -->
<div class="copyright"> <div class="copyright">
Copyright &copy; 2020 Liang Shan Guang Copyright &copy; 2020 Liang Shan Guang
</div> </div>
@ -28,31 +38,39 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import RouteView from './RouteView' // `./RouteView` `RouteView` `<route-view>` 使使
import { mixinDevice } from '../utils/mixin' import RouteView from './RouteView';
// `../utils/mixin` `mixinDevice`Vue `mixin` `mixinDevice` `UserLayout` 使
import { mixinDevice } from '../utils/mixin';
export default { export default {
name: 'UserLayout', name: 'UserLayout',
// `components` `RouteView` 使使 `<route-view>` Vue 使便
components: { RouteView }, components: { RouteView },
// `mixinDevice` 使
mixins: [mixinDevice], mixins: [mixinDevice],
data () { data () {
// `data`
return {} return {}
}, },
mounted () { mounted () {
document.body.classList.add('userLayout') // `mounted` `document.body` `classList` `userLayout` `body` `.userLayout` 使
document.body.classList.add('userLayout');
}, },
beforeDestroy () { beforeDestroy () {
document.body.classList.remove('userLayout') // `beforeDestroy` `document.body` `userLayout` 使
document.body.classList.remove('userLayout');
} }
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
// `id` `userLayout` `user-layout-wrapper` `100%`使
#userLayout.user-layout-wrapper { #userLayout.user-layout-wrapper {
height: 100%; height: 100%;
// `mobile` JavaScript `.container` `.main` `368px` `98%`使
&.mobile { &.mobile {
.container { .container {
.main { .main {
@ -63,25 +81,31 @@ export default {
} }
.container { .container {
// `100%`使 `100%`
width: 100%; width: 100%;
min-height: 100%; min-height: 100%;
// `#f0f2f5` `../assets/background.svg`使`no-repeat``50%` `100%`
background: #f0f2f5 url(../assets/background.svg) no-repeat 50%; background: #f0f2f5 url(../assets/background.svg) no-repeat 50%;
background-size: 100%; background-size: 100%;
// `110px` `144px`使
padding: 110px 0 144px; padding: 110px 0 144px;
position: relative; // `relative` `absolute` 便
a { a {
// `<a>`线使线
text-decoration: none; text-decoration: none;
} }
.top { .top {
text-align: center; // `center`使 `top` logo
.header { .header {
height: 44px; // `44px` `44px` `header` 便
line-height: 44px;
.badge { .badge {
// `absolute`使 `.header` `.top` `relative` 便
// `display: inline-block`使便
// `1`使`vertical-align: middle``margin-left: -12px``margin-top: -10px`使 `0.8`使
position: absolute; position: absolute;
display: inline-block; display: inline-block;
line-height: 1; line-height: 1;
@ -92,6 +116,7 @@ export default {
} }
.logo { .logo {
// `logo` `44px`使 `header` `top` `logo` `16px` `none` `logo`
height: 44px; height: 44px;
vertical-align: top; vertical-align: top;
margin-right: 16px; margin-right: 16px;
@ -99,6 +124,7 @@ export default {
} }
.title { .title {
// `33px`使`rgba(0, 0, 0,.85)`使`font-family`使 `600`使 `2px``position: relative``top: 2px`使
font-size: 33px; font-size: 33px;
color: rgba(0, 0, 0,.85); color: rgba(0, 0, 0,.85);
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif; font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
@ -109,6 +135,7 @@ export default {
} }
.desc { .desc {
// `14px`使便`rgba(0, 0, 0, 0.45)` `12px`使 `40px`使
font-size: 14px; font-size: 14px;
color: rgba(0, 0, 0, 0.45); color: rgba(0, 0, 0, 0.45);
margin-top: 12px; margin-top: 12px;
@ -117,6 +144,7 @@ export default {
} }
.main { .main {
// `260px` `368px``margin: 0 auto`使`.container`
min-width: 260px; min-width: 260px;
width: 368px; width: 368px;
margin: 0 auto; margin: 0 auto;

@ -1,7 +1,12 @@
import UserLayout from './UserLayout' // 从当前目录下相对路径为 './UserLayout' 的文件中导入 `UserLayout` 组件。从命名推测,`UserLayout` 组件可能是用于构建与用户相关页面(比如登录、注册、个人信息等页面)的布局组件,它有着特定的页面结构和样式呈现方式,方便在这些用户相关功能模块中统一页面布局风格。
import BlankLayout from './BlankLayout' import UserLayout from './UserLayout';
import BasicLayout from './BasicLayout' // 从相对路径 './BlankLayout' 的文件导入 `BlankLayout` 组件。`BlankLayout` 大概率是一种较为简洁、空白的布局组件,可能用于那些不需要过多页面装饰元素,只专注展示核心内容的页面场景,例如一些特定的功能演示页面或者临时占位页面等,提供了一个极简的页面布局框架。
import RouteView from './RouteView' import BlankLayout from './BlankLayout';
import PageView from './PageView' // 从 './BasicLayout' 文件导入 `BasicLayout` 组件。`BasicLayout` 通常作为整个应用的基础布局组件存在,它往往包含了如头部、侧边栏、主体内容区等常见的页面布局结构,为大多数页面提供一个通用的、标准的布局框架,其他页面可以基于这个框架进行具体功能模块的内容填充和样式定制。
import BasicLayout from './BasicLayout';
// 从 './RouteView' 文件导入 `RouteView` 组件。`RouteView` 组件很可能是起到衔接路由与具体页面视图之间关系的作用,它能够根据路由的切换动态地加载并展示相应的子组件内容,是实现页面根据不同路由路径呈现不同视图的关键组件之一,便于构建多页面应用的路由驱动的页面展示逻辑。
import RouteView from './RouteView';
// 从 './PageView' 文件导入 `PageView` 组件。`PageView` 组件或许是针对具体页面内容展示的一种布局包装组件,用于规范页面内容在整体布局中的呈现形式,保证不同页面在内容展示方面具有一致性的布局风格,比如统一的页面边距、内容对齐方式等,同时也方便对页面内容进行整体的样式控制和调整。
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView } // 使用 `export` 关键字将导入的这些组件(`UserLayout`、`BasicLayout`、`BlankLayout`、`RouteView`、`PageView`)进行导出,使得其他模块可以通过导入这个模块来获取这些组件,进而在整个项目中进行复用,用于构建不同功能需求的页面布局和视图展示,满足多样化的业务场景和页面设计要求。
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView };
Loading…
Cancel
Save