云e办前端笔记


办公系统前端开发

本系统采用vue框架进行搭建,主要ui是Elementui,主要功能实现了一个公司的员工资料、人事管理、薪资管理、统计管理、系统管理等功能。

1登陆页面设计

如图所示,使用Elementui进行简单搭建,验证码由后端提供,主要代码如下,

本代码主要运用了elemenui的Loading加载、form验证表单

以及附属的相关事件,样式由后期调整得。

事件的主要功能:将axios从后端请求得到的token数据存入window.

SessionStorage.setItem()中,为了使axios做下一次请求时获取token认证,

登陆即完成了

<template>
    <div>
        <el-form
                v-loading="loading"
                element-loading-text="正在登录..."
                element-loading-spinner="el-icon-loading"
                element-loading-background="rgba(0, 0, 0, 0.8)"

                :rules="rules" ref="loginForm" :model="loginForm" class="loginContainer">
            <h3 class="loginTitle">系统登陆h3>
            <el-form-item prop="username">
                <el-input type="text" v-model="loginForm.username" auto-complete="false" placeholder="请输入用户名">el-input>
            el-form-item>



            <el-form-item prop="password">
                <el-input type="password" v-model="loginForm.password" auto-complete="false" placeholder="请输入密码">el-input>
            el-form-item>



            <el-form-item prop="code">
                <el-input style="width:200px ; margin-right: 5px" type="text" v-model="loginForm.code" auto-complete="false" placeholder="点击更换图片">
                el-input>
                <img :src="captchaUrl" @click="updateCaptcha" class="el-form-item__content">
            el-form-item>
            <el-checkbox v-model="checked" class="loginRemember">记住我el-checkbox>
            <el-button type="primary" style="width: 100%" @click="submitLogin">登录el-button>
        el-form>

    div>
template>

<script>

    export default {
        name: "Login",
        data(){
            return{
                captchaUrl:'/captcha?time='+new Date(),
                loginForm:{
                    username:'admin',
                    password:'123',
                    code:''
                },
                checked:true,
                loading:false,
                rules:{
                    username: [{required:true,message:'请输入用户名',trigger:'blur'}],
                    password: [{required:true,message: '请输入密码',trigger:'blur'}],
                    code: [{required:true,message:'请输入验证码',trigger:'blur'}]
                }
            }
        },
        methods: {
            updateCaptcha(){
              this.captchaUrl='/captcha?time='+new Date();
            },
            submitLogin() {
                this.$refs.loginForm.validate((valid) =>{
                    if (valid) {
                        this.loading=true;
                       this.postRequest('/login',this.loginForm).then(resp=>{
                           if (resp){
                               this.loading=false;
                               const tokenStr=resp.obj.tokenHead+resp.obj.token;
                               //存进去 跳转页面后获取
                               window.sessionStorage.setItem('tokenStr',tokenStr);
                               //跳转
                              /* this.$router.replace('/home');*/
                               /*此处解决自己输入属地址的行为*/
                               let path = this.$route.query.redirect;
                               this.$router.replace((path=='/'||path==undefined)?'/home' : path);
                           }
                           this.loading=false;
                       });
                    } else {
                        this.$message.error('请填写完整!!!');
                        return false;
                    }
                });
            }
        }
    }
script>

<style>
.el-form-item__content{
    display: flex;
    align-items: center
}
    .loginContainer{
    border-radius: 15px;
        background-clip: padding-box;/*背景裁剪到内边框*/
        margin: 180px auto;
        width: 350px;
        padding: 15px 35px 15px 35px;/*拉大间隔*/
        background: #fff;
        border: 1px solid #eaeaea;
    }
    .loginTitle{
        margin: 0px auto 40px auto;
        text-align: center;
    }
    .loginRemember{
        text-align: left;
        margin: 0px 0px 15px 0px;
    }

style>

2主页设计

如图所示,主要将上面所属的系统功能一一展示在左上角,主要运用的是

Elementui的container容器布局第四个,展示的数据菜单由路由模式从

后端获取,实现自动展示相应的菜单,代码如下。

<template>
    <div>
        <el-container>

            <el-header  class="homeHeader">
                <div class="title"> 云e办div>
                    
                <el-dropdown class="userInfo" @command="commandHandler">
                      <span class="el-dropdown-link">
                          {{user.name}}<i><img :src="user.userFace"/>i>
                      span>
                    <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item command="userinf">个人中心el-dropdown-item>
                        <el-dropdown-item command="setting">设置el-dropdown-item>
                        <el-dropdown-item command="logout">注销登录el-dropdown-item>
                    el-dropdown-menu>
                el-dropdown>
            el-header>




            <el-container>




                    <el-aside width="200px">
                    <el-menu router unique-opened>
                        <el-submenu :index="index+''" v-for="(item,index) in routes" :key="index"
                         v-if="!item.hidden">
                            <template slot="title">
                                <i :class="item.iconCls" style="color: darkcyan;margin-right: 5px">i>
                                <span>{{item.name}}span>
                            template>
                                <el-menu-item v-for="(children,index) in item.children" :index="children.path">
                                    {{children.name}}
                                el-menu-item>
                        el-submenu>
                    el-menu>

                el-aside>




                <el-main>
                    <el-breadcrumb v-if="this.$router.currentRoute.path !='/home'" separator-class="el-icon-arrow-right">
                        <el-breadcrumb-item :to="{ path: '/home' }">首页el-breadcrumb-item>
                        <el-breadcrumb-item>{{this.$router.currentRoute.name}}el-breadcrumb-item>
                    el-breadcrumb>
                  <div class="homeWelcome" v-if="this.$router.currentRoute.path=='/home'">
                         欢迎来到云e办系统
                    div>
                    <router-view class="homeRouterView" />
                el-main>


            el-container>
        el-container>
    div>
template>

<script>
    export default {
        name: "Home",
        computed:{
            routes(){
                return this.$store.state.routes;
            }
        },
        data(){
          return{
              user:JSON.parse(window.sessionStorage.getItem('user'))
          }
        },
        methods:{
            commandHandler(command){
                if (command=='logout'){
                    {
                        this.$confirm('此操作将注销登录, 是否继续?', '提示', {
                            confirmButtonText: '确定',
                            cancelButtonText: '取消',
                            type: 'warning'
                        }).then(() => {
                            this.postRequest('/logout');
                            window.sessionStorage.removeItem('tokenStr');
                            window.sessionStorage.removeItem('user');
                            //清空菜单
                            this.$store.commit('initRoutes',[]);
                            this.$router.replace('/');
                        }).catch(() => {
                            this.$message({
                                type: 'info',
                                message: '已取消注销'
                            });
                        });
                    }


                }
            }
        }

    }
script>

<style>
    .homeHeader{
        background: #409eff;
        display: flex;/*文本生效*/
        align-items: center;
        justify-content: space-between;/*将最后元素末尾对齐*/
        padding: 0 15px;
        box-sizing: border-box;
    }
    .homeHeader .title{
        font-size: 30px;
        font-family: 华文楷体;
        color: white;
    }
   .homeHeader .userInfo{
        cursor:pointer;
    }
    .el-dropdown-link img{
        width: 48px;
        height: 48px;
        border-radius: 24px;
    }
    .homeWelcome{
        text-align: center;
        font-size: 30px;
        font-family: 华文楷体;
        color: #409eff;
        padding-top: 50px;
    }
    .homeRouterView{
        margin-top: 10px;
    }
style>

3Vuex及路由存储对象

安装vuex后,使用vuex构建存储对象,代码如下

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

export default new Vuex.Store({
    state:{
        routes:[]
    },

    //同步
    mutations:{
        initRoutes(state,data){
            state.routes=data;
        }
    },
    //异步
    actions:{

    }
})

4菜单初始化工具类

因为我们刚刚使用vuex构建了路由储存对象,需要使用菜单初始化工具将菜单存入routes[],

具体工具类代码如下

import {getRequest} from './api'

export const initMenu = (router,store)=>{
    if(store.state.routes.length>0){
        return;
    }

    getRequest('/system/cfg/menu').then(data=>{
        if (data){
            let fmtRoutes = formatRoutes(data);
            router.addRoutes(fmtRoutes);
            store.commit('initRoutes',fmtRoutes);/*核心*/
        }
    })
}


export const formatRoutes = (routes)=>{
    let fmtRoutes=[];
    routes.forEach(router=>{
        let{
            path,
            component,
            name,
            iconCls,
            children,
        }= router;
        if (children&&children instanceof Array){
            //递归
            children=formatRoutes(children);
        }
        let fmRouter = {
            path:path,
            name,
            iconCls,
            children,
            component(resolve){
                if (component.startsWith('Home')){
                    require(['../views/'+component+'.vue'],resolve);
                }else if (component.startsWith('Emp')){
                    require(['../views/emp/'+component+'.vue'],resolve);
                }else if(component.startsWith('Per')){
                    require(['../views/per/'+component+'.vue'],resolve);
                }else if(component.startsWith('Sal')){
                    require(['../views/sal/'+component+'.vue'],resolve);
                }else if(component.startsWith('Sta')){
                    require(['../views/sta/'+component+'.vue'],resolve);
                }else if(component.startsWith('Sys')){
                    require(['../views/sys/'+component+'.vue'],resolve);
                }
            }
        }
        fmtRoutes.push(fmRouter);
    });
    return fmtRoutes;

}

5请求响应处理工具类

此工具类主要是过滤非法请求以及处理响应时发生的错误,将他们的信息用弹窗显示出来如图所示,具体代码如下

import axios from 'axios'
import {Message} from "element-ui";

//请求拦截器
axios.interceptors.request.use(config=>{
    if (window.sessionStorage.getItem('tokenStr')){
        //请求时自动带入token
        config.headers['Authorization'] = window.sessionStorage.getItem('tokenStr')
    }
    return config;
},error => {
   console.log(error);
})

axios.interceptors.response.use(success=>{
    //业务逻辑错误
    if (success.status||success.data.code==200){
        if (success.data.code==500||success.data.code==401||success.data.code==403){
            Message.error({message:success.data.message});
            return;
        }
        if (success.data.message){
            Message.success({message:success.data.message})
        }
    }
    return success.data;
},error=>{
    //未能调取接口错误
    if (error.response.data.code==504||error.response.data.code==404){
        Message.error({message:'服务器被吃了'})
    }else if(error.response.data.code==403){
        Message.error({message:'权限不足'})
    }else if(error.response.data.code==401){
        Message.error({message:'尚未登陆'})
        router.replace('/')
    }
    else {

        if (error.response.data.message) {
            Message.error({message: error.response.data.message});
        } else {
            Message.error({message: '未知错误'})
        }
    }
    return;
});


let base='';

//传送json请求格式的post请求
export const postRequest=(url,params)=>{
    return axios({
        method:'post',
        url:`${base}${url}`,
        data:params
    })
}

export const putRequest=(url,params)=>{
    return axios({
        method:'put',
        url:`${base}${url}`,
        data:params
    })
}

export const getRequest=(url,params)=>{
    return axios({
        method:'get',
        url:`${base}${url}`,
        data:params
    })
}



export  const deleteRequest=(url,params)=>{
    return axios({
        method:'delete',
        url:`${base}${url}`,
        data:params
    })
}

6暴露路由

使用路由模式菜单,需要将路由包装暴露出来,以便公共使用

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'


Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Login',
    component: Login,
    hidden:true
  },
]

const router = new VueRouter({
  routes
})

export default router

7创建菜单组件

我们需要将菜单组件分组创建好,然后通过工具类初始化存入路由数组,以便实现点击跳转,代码后面发布