Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
前言
在上一个文章中,已经成功初始化一个 vue 项目并且安装了一些开发必需的工具,接下来将进行代码编写了。
项目代码实现
1、添加 vue.config.js 文件
在项目的根目录下添加 vue.config.js 文件,其主要是做一些配置,比如 URL 及端口配置,打包配置等等,后期会有更多配置,现在先配置如下代码:
module.exports = {
devServer:{
port: 8120 //前端端口
}
}
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
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
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
})
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
}
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;
}
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>
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>
9、添加 global.js 文件
在 src 目录下的 utils 文件夹中创建 global.js 文件,代码如下:
global.antRouter = '' //全局的路由
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>
11、添加 index.vue 首页文件
在 src/views 下新建 index.vue 文件作为首页,代码如下:
<template>
<div>我是主页面</div>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
<style scoped>
</style>
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>
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),
})
运行并测试
输入命令: npm run serve 启动前端,浏览器中输入:http://localhost:8120 进行访问,结果如下图所示:
至此,整个前端的基本框架就搭建完了,下一篇我们将对接后端接口,实现简单的登录界面。