后端学习开发【6】——项目前端框架搭建2

243 阅读5分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

前言

在上一个文章中,已经成功初始化一个 vue 项目并且安装了一些开发必需的工具,接下来将进行代码编写了。

项目代码实现

1、添加 vue.config.js 文件

在项目的根目录下添加 vue.config.js 文件,其主要是做一些配置,比如 URL 及端口配置,打包配置等等,后期会有更多配置,现在先配置如下代码:

module.exports = {
    devServer:{
        port: 8120 //前端端口
    }
}

image.png

2、添加 request.js 文件

request.js 文件主要是用于接口请求的配置工具,我们可以在 src 目录下新建一个文件夹 utils,并在该文件夹中添加 request.js,代码如下:

import axios from 'axios'
import { Message } from 'element-ui'
import store from '../store/store'

const token =store.getters.getToken

// create an axios instance
const service = axios.create({
  baseURL: 'http://localhost:8110', // url = base url + request url
  timeout: 5000 // request timeout
})


// request interceptor
service.interceptors.request.use(
  config => {
    if (token) {
      config.headers['token'] = token
    }
    return config
  },
  
  error => {
    console.log(error)
    return Promise.reject(error)
  }
)


// response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code === 20000) {
      return response.data
    } else { 
      return Promise.reject('error')
    }
  },

  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

image.png

3、添加 vuex 仓库管理相关代码

在 src 目录下新建文件夹 store,再在该文件夹下添加 store.js 文件,代码如下:

import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex);

const store = new Vuex.Store({
    state: {//初始化数据
        token: '',
        username: ''
    },
    getters: {
        getToken: state => {return state.token},
        getUsername: state => {return state.username}
    },
    mutations: {
        setToken(state, token) {
            state.token = token
        },
        setUsername(state, username) {
            state.token = username
        },
    }
})

export default store

image.png

4、添加 router.js 文件

在 src 目录下新建文件夹 router,在该文件夹中新建 router.js 文件进行路由配置,代码如下:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Layout = () => import('@/components/Layout')
const Login = () => import('@/views/login/index')// 登录页
const Main = () => import('@/views/index')// 主页

const User = () => import('@/views/system/user')// 用户管理

// 固定的路由表
export const fixedRouter = [{
	path: '',
	component: Login,
	hidden: true
},
{
	path: '',
	component: Layout, //整体页面的布局(包含左侧菜单跟主内容区域)
	children: [{
		path: 'main',
		component: Main,
		meta: {
			title: '首页', //菜单名称
			roles: ['user', 'admin'], //当前菜单哪些角色可以看到
			icon: 'el-icon-info' //菜单左侧的icon图标
		}
	}]
},
]

// 需要权限判断展示的路由
export const permissionRouter = [{
	path: "/system",
	component: Layout,
	name: "System",
	meta: {
		title: "系统管理",
		icon: "el-icon-success",
		roles: ['admin', 'user']
	},
	children: [
		{
			path: "user",
			name: "User",
			component: User,
			meta: {
				title: "用户管理",
				icon: "el-icon-goods",
				roles: ['admin', 'user']
			},
		},
	]
}]

export default new VueRouter({
	routes: fixedRouter
})

image.png

5、添加 permission.js 文件

在 src 目录下新建 permission.js 文件,该文件是导航守卫文件,代码如下:

// 取到需要权限判断的路由表
import { permissionRouter, fixedRouter } from '@/router/router'
import router from '@/router/router'
import { Message } from 'element-ui'

var addRouFlag = false

router.beforeEach((to, from, next) => {
  // 取到用户的角色
  let GetRole = localStorage.getItem("userRole")
  // 获取token
  let token = localStorage.getItem('token')

  // 如果登录了
  if (token) {
    // 如果用户角色不为空
    if (GetRole) {
      next() //next()方法后的代码也会执行
      // 1.如果路由表 没根据角色进行筛选,就筛选一次
      if (!addRouFlag) {
        addRouFlag = true
        // 2.根据用户的角色、和需要动态展示的路由,生成符合用户角色的路由
        var getRoutes = baseRoleGetRouters(permissionRouter, GetRole.split(","))
        // 3.利用global属性,让渲染菜单的组件sideMeuns.vue重新生成左侧菜单
        global.antRouter = fixedRouter.concat(getRoutes)
        // 4.将生成好的路由addRoutes
        router.addRoutes(fixedRouter.concat(getRoutes))
        // 5.push之后,会重新进入到beforeEach的钩子里,直接进入第一个if判断
        router.push({ path: to.path })
      }
    }else{
      Message.error('用户无权限!')
    }
  } else {
    // 用户没登录,跳转到登录页面
    if (to.path === '/') {
      next()
    } else {
      next('/')
    }
  }

})

function hasPermission(route, roles) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.indexOf(role) >= 0)
  } else {
    return true
  }
}
// 根据用户的角色取到该用户对应的路由
function baseRoleGetRouters(allRoutes, roles) {
  // allRoutes是动态路由表
  // roles是取到的用户角色,数组
  let rightRoutes = allRoutes.filter((route) => {
    // console.log(index)
    if (hasPermission(route, roles)) {
      if (route.children && route.children.length) {
        route.children = baseRoleGetRouters(route.children, roles)
      }
      return true
    }
    return false
  })
  return rightRoutes
}

image.png

6、添加 index.css 文件

在 src 目录下新建文件夹 styles,在该文件夹下新建 index.css 文件,该文将是整个项目的全局样式,代码如下:


/* =========全局======= */
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote, th, td { 
    margin:0; 
    padding:0; 
}

body { 
    font-size:14px; 
    color:#666; 
    font-family:Verdana, Microsoft YaHei, Simsun; 
    background:#fff; 
    line-height:24px; 
}

fieldset, img { 
    border:0; 
}

ol, ul { 
    list-style:none; 
}

h1, h2, h3, h4, h5, h6{ 
    font-size:100%; 
}

em { 
    font-style:normal; 
}

input, button, select, textarea { 
    outline:none; 
}

/*禁用了文本的拖拉,尤其在谷歌下*/
textarea { 
    resize:none; 
}

/*为了使文本段落左右两边对齐*/ 
p{ 
    text-align:justify; 
    text-justify:distribute;
}

/*======== Link ========*/
a { 
    color: #666; 
    text-decoration:none; 
}

a:hover { 
    color: #f60; 
    text-decoration:none; 
}

html,body,#app{
    height: 100%;
    overflow: hidden;
}

.el-header{
    padding: 0;
}

/*登录页*/
.form_wapper .el-form-item__label {
    color: #fff; 
}

/*自定义菜单宽度、背景色*/
#app .el-submenu .el-menu-item,
#app .nest-menu .el-submenu>.el-submenu__title {
  min-width: 180px !important;
  background-color: #1f2d3d !important;
}

#app .el-submenu .el-menu-item:hover,
#app .nest-menu .el-submenu>.el-submenu__title:hover {
  background-color: #001528 !important;
}

image.png

7、添加 SideMenu.vue 文件

在 src/components 目录下新建 SideMenu.vue 文件,该文件是整个项目的菜单栏组件,代码如下:

<template>
  <div class="side-container">
    <!-- 遍历路由表,生成左侧菜单 -->
    <div v-for="(item, index) in menus" :key="index">
      <div v-if="!item.hidden">
        <!-- 一级菜单的情况 -->
        <template v-if="item.children && item.children.length === 1">
          <router-link :to="item.path + '/' + item.children[0].path">
            <!-- index跟浏览器地址对应,这样菜单才能显示选中状态  -->
            <el-menu-item :index="item.path + '/' + item.children[0].path">
              <template slot="title">
                <!-- 设置icon -->
                <i
                  v-if="item.children[0].meta.icon"
                  :class="item.children[0].meta.icon"
                ></i>
                <!-- 菜单名称 -->
                {{ item.children[0].meta.title }}
              </template>
            </el-menu-item>
          </router-link>
        </template>
        <!-- 一级菜单的情况 end-->
        <!-- 多级菜单 -->
        <template v-else>
          <el-submenu :index="item.path">
            <!-- 父菜单名称 -->
            <template slot="title">
              <i :class="item.meta.icon"></i>
              {{ item.meta.title }}
            </template>
            <!-- 遍历子菜单 -->
            <div
              v-for="(itemChild, indexChild) in item.children"
              :key="indexChild"
            >
              <div v-if="!itemChild.hidden">
                <!-- 二级菜单-->
                <template
                  v-if="!itemChild.children || itemChild.children.length === 0"
                >
                  <router-link :to="item.path + '/' + itemChild.path">
                    <el-menu-item :index="item.path + '/' + itemChild.path">
                      <i
                        v-if="itemChild.meta.icon"
                        :class="itemChild.meta.icon"
                      ></i>
                      {{ itemChild.meta.title }}
                    </el-menu-item>
                  </router-link>
                </template>

                <!-- 三级菜单 -->
                <template v-else> </template>
              </div>
            </div>
            <!-- 遍历子菜单 end-->
          </el-submenu>
        </template>
        <!-- 多级菜单 end-->
      </div>
    </div>
  </div>
</template>
<script>
import "@/styles/index.css";
export default {
  name: "sideMenu",
  props: {
    routes: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      menus: "",
    };
  },

  mounted() {
    this.menus = this.routes;
  },
};
</script>

image.png

8、添加 Layout.vue 文件

在 src/components 目录下新建 Layout.vue 文件,代码如下:

<template>
  <div class="app_wapper">
    <el-container>
      <!-- 左侧菜单 -->
      <el-aside class="slider_container">
        <el-menu class="sub_meuns_wapper" mode="vertical" unique-opened :default-active="$route.path" background-color="#304156" text-color="#fff" active-text-color="#409EFF">
          <!-- 菜单组件 -->
          <side-Menu :routes="getRoutes"></side-Menu>
        </el-menu>
      </el-aside>

      <!-- 右侧内容区域 -->
      <el-container class="app_content">
        <!-- header -->
        <el-header style="text-align: right; font-size: 12px">
          <i class="el-icon-setting" style="margin-right: 15px"></i>
          <el-button @click="loginOut" size="mini" plain type="primary">退出</el-button>
        </el-header>
        <!-- 内容区域 -->
        <el-main>
          <!-- 二级路由跳转 -->
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script>
// 左侧菜单组件
import SideMenu from '@/components/SideMenu'
export default {
  computed: {
    getRoutes() {
      return global.antRouter
    }

  },
  components: {
    SideMenu
  },
  data() {
    return {
    }
  },
  methods: {
    loginOut() { //退出系统
      localStorage.setItem("token", '')
      // 跳转到登录页的时候顺便刷新
      window.location.href = window.location.origin + window.location.pathname
    },
  },
}

</script>
<style scoped>
/*wapper*/
.app_wapper {
  position: relative;
  height: 100%;
  width: 100%;
}

.app_content {
  min-height: 500px;
  margin-left: 180px;
  border: 1px solid #eee;
  box-sizing: border-box;
}

.el-header {
  background-color: #B3C0D1;
  color: #333;
  line-height: 60px;
}

.app_wapper .el-aside,
.slider_container .el-menu {
  color: #333;
  transition: width .28s;
  width: 180px !important;
  height: 100%;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 1001;
  background-color: rgb(48, 65, 86);
}

</style>

image.png

9、添加 global.js 文件

在 src 目录下的 utils 文件夹中创建 global.js 文件,代码如下:

global.antRouter = '' //全局的路由

image.png

10、添加 login.vue 文件

在 src/views 目录下新建文件夹 login,在该文件夹中新建 login.vue 文件,代码如下:

<template>
  <div class="login-container">
    <el-form
      ref="loginForm"
      class="login-form"
      :model="loginForm"
      :rules="rules"
    >
      <div class="login-title">Qiuの杂货铺</div>
      <el-form-item prop="username">
        <span class="svg-container">
          <i class="el-icon-user" />
        </span>
        <el-input
          v-model="loginForm.username"
          placeholder="请输入用户名"
          name="username"
          type="text"
        />
      </el-form-item>
      <el-form-item prop="password">
        <span class="svg-container">
          <i class="el-icon-lock" />
        </span>
        <el-input
          v-model="loginForm.password"
          placeholder="请输入密码"
          name="password"
          type="text"
        />
      </el-form-item>
      <el-form-item>
        <el-button
          type="primary"
          style="width: 100%"
          @click="login('loginForm')"
          >登 录</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
export default {
  name: "login",
  data() {
    return {
      loginForm: {
        username: "",
        password: "",
      },

      rules: {
        username: [
          { required: true, message: "用户名不能为空", trigger: "blur" },
        ],
        password: [
          { required: true, message: "密码不能为空", trigger: "blur" },
        ],
      },
    };
  },
  methods: {
    login(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
        }
      });
    },
  },
};
</script>
<style>
.login-container {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: url(../../assets/bg.jpeg) no-repeat left top;
  background-size: 100% 100%;
  background-attachment: fixed;
  display: flex;
  flex-flow: row nowrap;
  justify-content: center;
  align-items: center;
}
.login-form {
  width: 400px;
  height: 300px;
  background-color: rgb(255, 255, 255);
  border-radius: 20px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.login-title {
  height: 40px;
  line-height: 40px;
  font-size: 30px;
  font-weight: bold;
  margin-bottom: 30px;
  color: rgb(31, 29, 29);
}
.login-form .el-form-item__content {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
.svg-container {
  margin-right: 10px;
  font-size: 30px;
  font-weight: bold;
  color: rgb(31, 29, 29);
}
</style>

image.png

11、添加 index.vue 首页文件

在 src/views 下新建 index.vue 文件作为首页,代码如下:

<template>
  <div>我是主页面</div>
</template>
<script>
export default {
  data() {
    return {

    }

  }
}

</script>
<style scoped>
</style>

image.png

12、修改 App.vue 文件

vue 项目初始化成功后,也会在项目的 src 目录下自动生成 App.vue 文件作为前端页面的一个入口,修改如下:

<template>
  <div id="app">
    <!-- 一级路由跳转 -->
    <router-view />
  </div>
</template>
<script>
export default {
  name: 'App'
}

</script>
<style>
body{
  margin: 0;padding: 0;
}
</style>

image.png

13、编写 main.js 文件

vue 项目初始化成功后,会在项目的 src 目录下生成 main.js 文件,在该文件中,我们可以全局引入需要的资源,例如路由配置文件、接口请求配置文件等等,以方便其他组件调用,代码如下:

import Vue from 'vue'
import App from './App.vue'

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

import router from './router/router.js'//路由
import axios from '@/utils/request.js' //接口请求
import store from '@/store' //vuex仓库
import '@/utils/global'//全局
import '@/permission' // 导航守卫
import '@/styles/index.css' // 全局样式

Vue.use(ElementUI);
Vue.config.productionTip = false
Vue.prototype.axios = axios

new Vue({
  el:"#app",
  router,
  store,
  render: h => h(App),
})

image.png

运行并测试

输入命令: npm run serve 启动前端,浏览器中输入:http://localhost:8120 进行访问,结果如下图所示:

image.png 至此,整个前端的基本框架就搭建完了,下一篇我们将对接后端接口,实现简单的登录界面。