当前位置: 代码迷 >> 综合 >> 【sduoj】首页布局
  详细解决方案

【sduoj】首页布局

热度:89   发布时间:2023-11-26 10:57:19.0

2021SC@SDUSC

文章目录

  • App.vue
    • 模板<template />
    • 脚本<script />
  • 首页布局
    • Home.vue
    • ojheader
    • ojaside
    • layout

App.vue

该项目是一个由Vue.js框架所搭建的单页面应用(SPA),我们所看见的所有页面实际上均是在App.vue的基础上通过组件之间的切换完成的。以下是App.vue的源代码:

<template><div id="app"><router-view /></div>
</template><script> export default {
      created() {
      console.log("███████╗██████╗ ██╗ ██╗ ██████╗ ██╗\n██╔════╝██╔══██╗██║ ██║██╔═══██╗ ██║\n███████╗██║ ██║██║ ██║██║ ██║ ██║\n╚════██║██║ ██║██║ ██║██║ ██║██ ██║\n███████║██████╔╝╚██████╔╝╚██████╔╝╚█████╔╝\n╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ╚════╝ \n");if (localStorage.getItem("darkTheme") == null) {
      localStorage.setItem("darkTheme", false);}if (localStorage.getItem("token") != null) {
      let jwt = require("jsonwebtoken");const TOKEN = jwt.decode(localStorage.getItem("token"));let exp = TOKEN.exp * 1000;if (exp <= new Date().getTime()) {
      this.$message({
      message: "您的身份认证已过期,请重新登陆",type: "warning",duration: 1000,onClose: () => {
      localStorage.removeItem("token");this.$router.push("/login");},});} else {
      // 此处判定方式仅供参考this.$store.dispatch("setNoToken", false);if (TOKEN.ROLE.indexOf("ADMINISTRATOR") != -1) {
      this.$store.dispatch("setIsAdmin", true);} else if (TOKEN.ROLE.indexOf("ROLE_TUTOR") != -1) {
      this.$store.dispatch("setIsTeacher", true);}}}}, }; </script><style lang="less"> #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50; } </style>

模板<template />

作为整个单页应用的核心,App.vue的视图模板似乎并不复杂,只有一个<router-view />标签展示在页面当中。实际上正式这个标签,承担了展示整个项目99%的页面的功能。这是vue-router中携带的组件之一,用来渲染通过Vue路由映射过来的组件,当路由路径更改时,其内部所展示的内容也会发生更改。

脚本<script />

作为整个项目的启动页,App.vue需要完成几件事,最重要的就是对全局所需要的数据进行初始化。比如created()中的:


if (localStorage.getItem("token") != null) {
    let jwt = require("jsonwebtoken");const TOKEN = jwt.decode(localStorage.getItem("token"));let exp = TOKEN.exp * 1000;if (exp <= new Date().getTime()) {
    this.$message({
    message: "您的身份认证已过期,请重新登陆",type: "warning",duration: 1000,onClose: () => {
    localStorage.removeItem("token");this.$router.push("/login");},});} else {
    this.$store.dispatch("setNoToken", false);if (TOKEN.ROLE.indexOf("ADMINISTRATOR") != -1) {
    this.$store.dispatch("setIsAdmin", true);} else if (TOKEN.ROLE.indexOf("ROLE_TUTOR") != -1) {
    this.$store.dispatch("setIsTeacher", true);}}
}

此处是用来判断用户登录状态及身份的功能代码。首先从localStorage中取出存有用户身份信息的token,使用jsonwebtoken进行解析,判断用户token是否过期,并根据用户的不同身份向Vuex分发对应的action,以达到初始化数据的目的。

首页布局

sduoj首页主要由三部分构成:

  • ojheader:首页头部栏。根据用户登录状态呈现不同的样式,为系统提供用户登录注册以及注销等功能的入口。
  • ojaside:首页侧边栏。主要用于系统的页面导航。根据用户登录状态及通过jwt解析出的用户身份选择性展示不同的导航项。
  • layout:主窗口。用于放置<router-view />组件,根据不同的嵌套路由在主窗口中展示不同的页面组件。
    首页布局

Home.vue

首页Vue文件源代码:

<template><div class="home"><ojheader /><el-container><ojaside :pageIndex="$route.path" /><el-main><layout /></el-main></el-container></div>
</template><script> import Layout from "@/components/layout.vue"; import Ojaside from "@/components/ojaside.vue"; import Ojheader from "@/components/ojheader.vue"; export default {
      components: {
      Ojheader,Ojaside,Layout,}, }; </script>

该vue文件路径为@/pages/Home.vue,用作整个项目首页的Vue文件。结构比较简单,主要由上述三个组件构成。由于ojheaderojaside在许多页面都会重复出现,故此处将这两个组件直接放置在Home.vue中。而每个页面的页面组件则通过嵌套路由,在<layout />组件中的<router-view />中展示。
在这个vue文件中,我们将$route.path作为名为pageIndexprops传给了<ojaside />组件,是为了让侧边栏根据当前的路由状态自动更新侧边栏所选中的条目。下面让我们来依次分析三个组件的源码。

ojheader

<template><div><el-header style="text-align: right; background: rgba(215, 215, 215); height: 10vh; line-height: 10vh; padding-left: 0px"><div v-if="noToken"><el-button plain @click="Register">注册</el-button><el-button @click="Login" type="primary">登录</el-button></div><div v-else><el-dropdown class="drop" :show-timeout="0"><div style="margin-right: 30px" @mouseenter="nameColor='dodgerblue';" @mouseleave="nameColor='#000';"><el-avatar style="vertical-align: middle" :src="avatar"><i style="font-size: 25px; vertical-align: middle;" class="el-icon-loading"></i></el-avatar><el-link :underline="false" :style="{
     'font-size': '25px', 'font-family': 'KaiTi', 'margin-left': '5px', 'color': nameColor}">{
   { username }}</el-link></div><el-dropdown-menu><el-dropdown-item @click.native="myInfo"><i class="el-icon-user"></i>个人信息</el-dropdown-item><el-dropdown-item @click.native="Logout"><i class="el-icon-switch-button"></i>退出登录</el-dropdown-item></el-dropdown-menu></el-dropdown></div></el-header></div>
</template><script> export default {
      name: "ojheader",data() {
      return {
      noToken: this.$store.state.noToken,username: localStorage.getItem("nickname"),avatar: require("@/assets/icon/student.svg"),nameColor: "#000",isTeacher: this.$store.state.isTeacher,isAdmin: this.$store.state.isAdmin,};},methods: {
      Logout() {
      Promise.all([this.$store.dispatch("setNoToken", true),this.$store.dispatch("setIsAdmin", false),]).then(() => {
      localStorage.removeItem("token");this.$router.go(0);});},myInfo() {
      this.$router.push({
       path: "/info" });},Login() {
      this.$router.push({
       path: "/login" });},Register() {
      this.$router.push({
       path: "/register" });},}, }; </script><style> .drop:hover {
      cursor: pointer; } </style>

以上是ojheader.vue文件的源码,其路径为@/components/ojheader.vue
在视图层,使用ElementUI中的el-header作为顶栏布局容器。在容器中根据用户的登录状态动态选择渲染某个div。由于用户的登录状态不会经常性地发生改变,故此处使用v-ifv-else属性进行选择。登录后会在右侧创建一个包括用户头像及用户名的下拉列表,目前可用来进行注销登录以及查看个人信息的功能。
在上述代码中,我们看见了this.$store的字样,这是该项目中用来储存全局状态的一个仓库,使用Vuex作为全局状态管理容器,目前暂不做具体分析。我们在项目的入口文件main.js中引入了该文件,作为Vue.prototype.$store使用。
在执行注销功能的代码中,通过使用Promise.all同时向Vuex分发两条actions,在两条dispatch语句返回的Promise对象状态均变为fulfilled时,清空本地储存的token数据。

ojaside

<template><div class="ojaside"><el-menu :uniqueOpened="true" :default-active="pageIndex" :router="true" class="el-menu-vertical-demo" active-text-color="#3898FF"><el-menu-item index="/"><template #title><i class="el-icon-menu" /><span>首页</span></template></el-menu-item><el-submenu index="student" :disabled="noToken"><template #title><i class="el-icon-location" /><span>学生相关</span></template><el-menu-item index="/student/problems">题目列表</el-menu-item><el-menu-item index="/student/userList">用户组列表</el-menu-item></el-submenu><el-submenu index="teacher" v-if="!noToken&&(isTeacher||isAdmin)"><template #title><i class="el-icon-location" /><span>教师相关</span></template><el-menu-item index="/teacher/newProblem">出题</el-menu-item><el-menu-item index="/teacher/myProblems">教师查题</el-menu-item><el-menu-item index="/teacher/myProblemSets">查看题目集</el-menu-item><el-menu-item index="/teacher/userList">用户组列表</el-menu-item></el-submenu><el-submenu index="admin" v-if="!noToken&&isAdmin"><template #title><i class="el-icon-location" /><span>管理员相关</span></template><el-menu-item index="/admin/excel">学生信息上传</el-menu-item><el-menu-item index="/admin/bind">学生信息绑定</el-menu-item><el-menu-item index="/admin/role">教师权限管理</el-menu-item></el-submenu></el-menu></div>
</template><script> export default {
      name: "ojaside",props: {
      pageIndex: String,},data() {
      return {
      noToken: this.$store.state.noToken,isTeacher: this.$store.state.isTeacher,isAdmin: this.$store.state.isAdmin,};}, }; </script><style scoped> .el-menu {
      text-align: left; } .ojaside {
      position: relative;display: inline-block;width: 240px;overflow: hidden;height: 100%; } </style>

以上是@/components/ojaside.vue的代码。可以看到,在Home.vue中,我们传入了一个pageIndex,在该组件的props中进行了接收,并将其用在了el-menudefault-active属性中,用来指向当前的导航项目。
el-menu中指定了几个属性:

  • :uniqueOpened="true":保证导航页是单栏展开。
  • :router="true":通过路由模式进行跳转。

第一个属性很好理解,那么什么叫做通过路由模式进行跳转呢?其实就是使用vue-router的模式,启用该模式会在激活导航时以index作为path进行路由跳转。也就是在点击对应的el-menu-item时,会将<el-menu-item />标签中的index属性作为路由路径进行跳转。

layout

<template><div class="layout"><router-view /></div>
</template><script> export default {
      name: "layout", }; </script>

这个组件非常简单,仅仅作为一个<router-view />的容器,用以根据不同的嵌套路由渲染不同的页面组件。