这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战
【千字长文,熬夜更新,原创不易,多多支持,感谢大家】
前言
小伙伴们大家好。到目前为止我们的问卷调查已经实现了配置问卷,填写问卷,保存问卷,提交问卷,提交记录以及提交结果展示等基本功能。其实到这里关于问卷调查的一些相关功能基本都已经实现的差不多了。但是为了让我们的问卷调查更加完善,看起来更像是一个系统,同时也是为了进一步学习和巩固vue3.0的相关知识,我们继续对问卷调查进行扩展,今天要扩展的是:给我们的问卷调查重新布局并添加登录功能。
重新布局
- 新增HomePage.vue
在这之前,我们的问卷调查系统并没有进行注重布局这一块,打开之后就直接展示的是问卷填写页面,然后再通过顶部的两个超链接进行页面的切换,这样看上去既不美观也不专业,今天就给我们的系统进行一下重新布局:
- 添加一个HomePage.vue组件,在该组件中进行上下和左右布局,即:首先一个大盒子被拆分成上下两部分,然后再把下面的部分再拆分成左右两个部分,我们分别把它们命名为:主盒子main-box,头部盒子header-box,左侧盒子menu-box和右侧盒子content-box
- 在header-box中添加问卷系统的标题信息,以及登录后的用户信息(后面添加)
- 在menu-box中引入elementui库中的el-menu菜单组件,将之前的两个超链接改成菜单的形式展示在页面左侧
- 在menu-box中添加一个router-view用于显示问卷内容(问卷填写,问卷详情,提交记录等)
页面的大致布局如下图所示:
下面给出给出HomePage.vue的布局代码,关于css样式大家可以自行补齐,或者有兴趣的小伙伴可以评论留言获取完整代码,样式部分就不在这里帖出了。另外关于el-menu的用法大家可以参考饿了么官网:
<template>
<div class="main-box">
<div class="header-box">
<div class="header-title">
<span>YANNIS 问 卷 调 查 管 理 系 统</span>
<div class="user">
<div>
<!--预留:用于展示登录后的用户信息-->
</div>
<div>
<!--预留按钮:用户登录后可以注销操作-->
</div>
</div>
</div>
</div>
<div class="menu-box">
<el-menu active-text-color="#ffd04b" background-color="#4a5e71" class="el-menu-vertical-demo" text-color="#fff" router default-active="/" style="height:100%">
<el-menu-item index="/">
<el-icon><question-filled /></el-icon>
<span>问卷调查</span>
</el-menu-item>
<el-menu-item index="/list">
<el-icon><list /></el-icon>
<span>提交记录</span>
</el-menu-item>
</el-menu>
</div>
<div class="content-box">
<router-view />
</div>
</div>
</template>
复制代码
import {ElMenu, ElMenuItem, ElIcon} from 'element-plus'
import {QuestionFilled, List} from '@element-plus/icons'
export default{
components:{ElMenu, ElMenuItem, ElIcon, ElButton,QuestionFilled, List,SwitchButton,UserFilled},
setup(){
}
}
复制代码
- 调整App.vue
布局页面已经完成,接下来就是把它应用到我们的问卷系统中,这里就需要修改一下我们的入口页面App.vue:
- 将原来的router-link和router-view全部删除
- 在template中使用vue自带的component组件,is的属性值指定为HomePage
- 在js代码中导入HomePage组件并注册
App.ue修改后的代码如下:
<template>
<component :is="'HomePage'" />
</template>
复制代码
import HomePage from './HomePage.vue'
export default{
components:{ HomePage }
}
复制代码
看看重新布局后的问卷调查系统看上去是不是显得更像一个系统也更专业些了?
添加登录功能
其实到这里一个问卷调查的基本功能就已经实现了,一般的问卷调查也不需要用户登录什么的。但是本系列分享不是为了做功能而分享,而是为了能够更好的学习和巩固知识,因此我们再来为这个系统添加一个强制登录的功能。下面先来简单的梳理一下登录功能要做的事情:
- 首先添加一个登录页面login.vue
- 在该页面中应该包含:用户名,密码,用户角色,登录按钮等几个元素
- 用户角色可分为:普通用户,管理员和匿名用户(允许直接匿名登录)
- 普通用户可进行问卷填写,问卷提交及问卷保存操作
- 管理员则权限最大,除了普通用户拥有的权限外还可以对问卷进行配置,以及统计问卷填写情况等功能
- 匿名用户为了让用户操作方便可以直接不输入用户名密码进行登录,权限同普通用户
- 另:关于权限功能本次分享暂不涉及,后续会视情况补齐(暂定三个角色的权限都是相同的,拥有最大权限)
- 当用户登录成功后模拟生成一个token信息,并将token,用户名和用户角色保存到vuex和本地存储localStorage中
- 当用户访问系统的其它功能时先去校验token信息是否存在(本次分享中不考虑token过期问题,只验证是否存在即可)
- 如果token存在,则正常访问
- 如果token不存在则强制跳转到登录页面进行重新登录
特别说明:
- 1.用户信息会同时保存到vux和localStorage中,因此此处涉及到一个新的知识点:vuex(需另行安装)
- 2.请求其它功能前需先进行token校验,涉及到新知识点:路由导航守卫
- 3.允许匿名登录,因此登录时需要进行角色判断,如果是匿名用户则不用强制输入用户名密码,否则需要强制输入
- 4.本次只是模拟登录功能,因此不验证用户是否真实存在,密码是否正确等
- 安装vux(已安装的可忽略此步)
- 在命令行中切换到项目的根目录下,运行:npm install vuex@next
- 在项目的src目录下添加store目录并在该目录下添加index.js文件
- 打开index.js导入vuex中的createStore方法
- 创建并导出store对象
import { createStore } from 'vuex';
export default createStore({
state:{
token: localStorage.getItem('TOKEN') : "",//保存token信息
userInfo: localStorage.getItem('USERINFO') ? JSON.parse(localStorage.getItem('USERINFO')) : {},
},
mutations:{
setToken(state, value){
state.token = value;
},
setUserInfo(state, value){
state.userInfo = value;
}
}
});
复制代码
- 添加login.vue
关于login.vue的页面设计和功能说明在上面的步骤分析中都已经介绍,这里不再多说,直接上代码,同样这里只给出HTML和JavaScript的代码,关于css样式大家可以自行补齐或者评论留言获取完整代码:
<div class="login-box">
<div class="login-form">
<div>
<div class="label"><label>用户名:</label></div>
<el-input type="text" v-model="name" />
</div>
<div>
<div class="label"><label>密 码:</label></div>
<el-input type="password" v-model="pwd" />
</div>
<div>
<div class="label"><label>角 色:</label></div>
<el-radio-group v-model="role">
<el-radio :label="0">普通用户</el-radio>
<el-radio :label="1">管理员</el-radio>
<el-radio :label="2">匿名</el-radio>
</el-radio-group>
</div>
<div>
<el-button type="primary" @click="login">登录</el-button>
</div>
</div>
</div>
复制代码
import {ElInput,ElRadio, ElRadioGroup, ElButton,ElMessage} from 'element-plus'
import constants from './constants';
import {useRouter} from 'vue-router'
import {useStore} from 'vuex'
import {reactive,toRefs} from 'vue'
export default{
components:{ElInput,ElRadio, ElRadioGroup, ElButton,ElMessage},
setup(){
const router = useRouter();
const store = useStore();
const state = reactive({name:'', role:0, pwd:''})
const login = ()=>{
if(state.role !== 2){
if(!state.name || !state.pwd){
ElMessage({
type:"error",
message:"用户名或密码不能为空"
})
return ;
}
}
localStorage.setItem("USERINFO", JSON.stringify({name:state.name || 'YANNIS', role:state.role}));
store.commit("setUserInfo", {name:state.name || 'YANNIS', role:state.role})
//模拟生成一个token信息
let token = "djsdakfjlksdfjlsdf.sdkljfsdjfoiwendcwoeifkjlsdjfokesdjhndlk.dkjvkneoidknfiejfdkjdkljdjkl"
localStorage.setItem("TOKEN", token);
store.commit("setToken", token)
router.push('/') //登录成功后调整的主页
}
return {
...toRefs(state),
login
}
}
}
复制代码
- 为login.vue配置路由并添加路由导航守卫
上面说到为了每次请求前都要进行token有效性校验,因此我们要在路由导航守卫里进行拦截
- 在导出路由对象前添加导航守卫router.beforeEach
- 在localStorage中获取到已经保存的token信息
- 如果token有效(或者本来要访问的就是登录页面)则直接跳转到用户要访问的页面(登录页面不进行拦截)
- 如果token无效并且访问的不是登录页面则进行lanq拦截并强行跳转到登录页
// router/index.js
const routes = [
//... 省略
{
path:'/login',
name:'Login',
component:()=>import(/*webpackChunkName: "Login" */ '../login.vue')
}
]
//...省略
router.beforeEach((to,from,next)=>{
const token = localStorage.getItem("TOKEN")
if(token || to.name === 'Login'){
next();
}else{
next('/login')
}
})
export default router;
复制代码
App.vue再次改造
至此登录功能也已经实现了,那么我们迫不及待的去打开登录页面尝试一下我们的新功能。然而这时你会发现:诶,登录页面没有作为一个单独的页面打开在整个浏览器中,而是嵌入到了HomePage中的content-box中了。这是因为:“我们把router-view添加到content-box中了,而通过路由访问的页面都将会展示在这个router-view中也就是会显示在content-box中”,这显然是不合理的,为了让登录页面能够作为一个单独的页面显示在整个浏览器中,我们还需要对App.vue进行二次改造:
- 在App.vue的setup方发中定义一个计算属性page
- 导入当前路由信息,根据当前的路由名称判断当前访问的是登录页面还是其它页面
- 如果是登录页面则给计算属性page赋值为登录页面对应的组件login
- 如果是非登录页面则给计算属性page赋值为布局页面对应的组件HomePage
- 修改模板中componen组件的is属性改为计算属性page
改造前
<template>
<component :is="page" />
</template>
复制代码
import HomePage from './HomePage.vue'
import login from './login.vue'
import {useRoute} from 'vue-router'
import {computed} from 'vue';
export default{
components:{HomePage,login},
setup(){
let route = useRoute();
const page = computed(()=>{
return route.name === "Login" ? "login" : "HomePage";
});
return {
page
}
}
}
复制代码
这时再次访问登录页面发现已经是我们想要的样子了
HomePage.vue再次改造
用户登录后再将用户信息展示在顶部的右侧,同时再添加一个注销按钮用于用户注销或切换登录操作
- 用户登录成功后,从vuex中取出用户信息显示在系统右上角
- 用户点击注销按钮后将localStorage中的用户信息及token信息删除,同时重新跳转到登录页面
<div class="user">
<div>
<!--预留:用于展示登录后的用户信息-->
<el-icon size="25"><user-filled /></el-icon>
<span>{{$store.state.userInfo.name}}</span>
</div>
<div>
<!--预留按钮:用户登录后可以注销操作-->
<el-button type="warning" size="mini" circle :icon="SwitchButton" @click="logout"></el-button>
</div>
</div>
复制代码
import {ElMenu, ElMenuItem, ElIcon, ElButton} from 'element-plus'
import {QuestionFilled, List,SwitchButton,UserFilled} from '@element-plus/icons'
import constants from './constants';
import {useRouter} from 'vue-router'
export default{
components:{ElMenu, ElMenuItem, ElIcon, ElButton,QuestionFilled, List,SwitchButton,UserFilled},
setup(){
const router = useRouter();
const logout = ()=>{
localStorage.removeItem(constants.Token);
localStorage.removeItem(constants.UserInfo);
router.push('/');
}
}
}
复制代码
提交记录中保存真正的用户名
之前在保存提交记录时我们是直接写死一个假的用户名,现在有了用户登录功能,我们应该把真正登录的用户信息保存进去:
- 修改Home.vue,从vuex中到useStore方法并获取vuex对象store
- 修改submit方法在store中拿到用户名并保存
// Home.vue
//...省略
import {useStore} from "vuex"
//...省略
const submit = ()=>{
//...省略
const record = {
user: store.state.userInfo.name || "Yannis",
time: new Date().toLocaleString(),
question: JSON.stringify(result),
}
//...省略
}
复制代码
总结
本次分享中为我们的问卷系统进行了重新布局并添加了登录及登出功能,在本篇中涉及到的新知识点由vuex和路由导航守卫, 另外本次分享中内容比较散,涉及到修改的模块也比较多并且所有涉及到的css样式也没有给出,如果需要源码的小伙伴可以评论留言获取。
本次分享就到这里了,喜欢的小伙伴欢迎点赞评论加关注哦!