云e办前端笔记


办公系统前端开发

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

 

1登陆页面设计

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

 

本代码主要运用了elemenuiLoading加载、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主页设计

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

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

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

 

<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创建菜单组件

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

 

8创建系统管理SysBasic的子组件及基础信息设置代码

基础信息设置 代码,采用了elementui的Tabs标签效果,内容名字是它的子组件

<template>
    <div>
        <el-tabs v-model="activeName" type="card">
            <el-tab-pane label="部门管理" name="DepMana"><DepMana></DepMana></el-tab-pane>
            <el-tab-pane label="职位管理" name="PosMana"><PosMana/></el-tab-pane>
            <el-tab-pane label="职称管理" name="JoblevelMana"><JoblevelMana/></el-tab-pane>
            <el-tab-pane label="奖惩规则" name="EcMana"><EcMana/></el-tab-pane>
            <el-tab-pane label="权限组" name="PermissMana"><PermissMana/></el-tab-pane>
        </el-tabs>
    </div>
</template>

<script>
    import DepMana from '../../components/sys/basic/DepMana'
    import EcMana from "../../components/sys/basic/EcMana";
    import JoblevelMana from "../../components/sys/basic/JoblevelMana";
    import PermissMana from "../../components/sys/basic/PermissMana";
    import PosMana from "../../components/sys/basic/PosMana";

    export default {
        name: "SysBasic",
        data(){
            return{
                activeName:'JoblevelMana'    /*默认开启PosMana*/
            }
        },
        components:{
            DepMana,
            EcMana,
            JoblevelMana,
            PermissMana,
            PosMana
        }
    }
</script>

<style scoped>

</style>

 

9基础信息设置之职位管理

采用Elementuiinput输入框、Table表格多选,含有添加、编辑、删除、批量删除事件

 

 

 

<template>
    <div>
        <div>
            <el-input suffix-icon="el-icon-plus"
                      class="addPosInput"
                      size="small"
                      placeholder="添加职位"
                      v-model="pos.name"
                      @keydown.enter.native="addPosition"
            >
            </el-input>
            <el-button type="primary" icon="el-icon-plus" size="small" @click="addPosition">添加</el-button>
        </div>

        <div>
            <el-table
                    border
                    size="small"
                    stripe
                    :data="positions"
                    @selection-change="handleSelectionChange"
                    style="width: 70%">

                <el-table-column
                    type="selection"
                        width="55">
                </el-table-column>

                <el-table-column
                        prop="id"
                        label="编号"
                        width="55">
                </el-table-column>
                <el-table-column
                        prop="name"
                        label="职位"
                        width="120">
                </el-table-column>
                <el-table-column
                        prop="createDate"
                        label="创建时间"
                        width="200">
                </el-table-column>
                <el-table-column
                        prop="enabled"
                        label="是否启用"
                        width="120">
                </el-table-column>



                <el-table-column label="操作">
                    <template scope="scope">
                        <el-button
                                size="small"
                                @click="showEditView(scope.$index, scope.row)">编辑</el-button>
                        <el-button
                                size="small"
                                type="danger"
                                @click="handleDelete(scope.$index, scope.row)">删除</el-button>
                    </template>
                </el-table-column>



            </el-table>
        </div>

        <el-button @click="deleteMany" type="danger" size="small" style="margin-top: 8px" :disabled="this.multipleSelection.length==8">批量删除</el-button>
        <el-dialog
                title="编辑职位"
                :visible.sync="dialogVisible"
                size="tiny"
               >
            <div>
                <el-tag>职位名称</el-tag>
                <el-input v-model="updatePos.name" class="updatePosInput"></el-input>
            </div>
            <span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false" size="small">取 消</el-button>
    <el-button type="primary" @click="doUpdate" size="small">确 定</el-button>
  </span>
        </el-dialog>

    </div>
</template>

<script>
    export default {
        name: "PosMana",
        data(){
            return{
                pos:{
                    name:''
                },
                positions:[],
                dialogVisible:false,
                updatePos:{
                    name:""
                },
                multipleSelection:[]

            }
        },

        mounted() {
            this.initPositions();
        },

        methods:{
            initPositions(){
                this.getRequest('/system/basic/pos/').then(resp=>{
                    if (resp){
                        this.positions = resp;
                    }
                })
            },
            doUpdate(){
               this.putRequest('/system/basic/pos/',this.updatePos).then(resp=>{
                   if (resp){
                       this.initPositions();
                       this.dialogVisible=false;
                   }
               })
            },

            addPosition(){
              if (this.pos.name){
                  this.postRequest('/system/basic/pos/',this.pos).then(resp=>{
                      if (resp){
                          this.initPositions();
                      }
                  })
              }else{
                  this.$message.error('职位名称不能为空!');
              }
            },

            showEditView(index, date) {
                Object.assign(this.updatePos,date);/*date拷贝给updatePos*/
               this.updatePos.createDate="";
              this.dialogVisible=true;
            },

            handleDelete(index, data) {
                this.$confirm('此操作将永久删除['+data.name+'], 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                   this.deleteRequest('/system/basic/pos/'+data.id).then(resp=>{
                       if (resp){
                           this.initPositions();
                       }
                   });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            },
            handleSelectionChange(val){
                this.multipleSelection=val;

            },
            deleteMany(){
                this.$confirm('此操作将永久删除['+this.multipleSelection.length+']条职位, 是否继续?', '提示',
                    {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    let ids ='?';
                    this.multipleSelection.forEach(item=>{
                        ids += 'ids=' + item.id+'&';
                    });
                    this.deleteRequest('/system/basic/pos/'+ids).then(resp=>{
                        if (resp){
                            this.initPositions();
                        }
                    });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });

            }
        }
    }
</script>

<style>
    .addPosInput{
        width:300px;
        margin-right: 8px;
    }
    /*.posMainMain{
        margin-top: 10px;
    }*/
    .updatePosInput{
        width: 200px;
        margin-left: 8px;
    }
</style>

 

10基础信息设置之权限组

权限组 代码,采用了elementui的折叠面板手风琴模式,Tree树形控件

 

 

<template>
    <div>
        <div class="PermissManaTool">
            <el-input size="small" placeholder="请输入角色英文名" v-model="role.name">
                <template slot="prepend">ROLE_</template>
            </el-input>

            <el-input size="small" placeholder="请输入角色中文名" v-model="role.nameZh">
            </el-input>
            <el-button size="small" type="primary" icon="el-icon-plus" @click="doAddRole">添加角色</el-button>
        </div>
        <div class="permissManaMain">
            <!--折叠面板 手风琴模式 change自带事件会传:name的值
            通过 accordion 属性来设置是否以手风琴模式显示。-->
            <el-collapse     v-model="activeName" accordion @change="change">
                <el-collapse-item :title="r.nameZh" :name='r.id' v-for="(r,index) in roles" :key="index">
                    <!--卡片-->
                    <el-card class="box-card">
                        <div slot="header" class="clearfix">
                            <span>可访问资源</span>

                            <!--删除按钮-->
                            <el-button  style="float: right; padding: 3px 0;color: red" type="text" icon="el-icon-delete"
                                @click="doDeleteRole(r)"
                            />
                        </div>
                        <div>
                            <!--树形控件 defaultProps展示子类和名字   node-key="id"事件通过id获取数据-->
                            <el-tree :data="allMenus"
                                     ref="tree"
                                     :key="index" :props="defaultProps"
                                     show-checkbox
                                     :default-checked-keys="selectedMenus"
                                     node-key="id"
                            ></el-tree>
                           <!-- 样式 容器布局 向右对齐-->
                            <div style="display: flex; justify-content: flex-end">
                                <el-button size="mini">取消修改</el-button>
                                <el-button size="mini" type="primary" @click="doUpdate(r.id,index)">确认修改</el-button>
                                <!--primary为主要按钮样式-->
                            </div>
                        </div>

                    </el-card>

                </el-collapse-item>
            </el-collapse>

        </div>
    </div>
</template>

<script>
    export default {
        name: "PermissMana",
        data(){
            return{
                role:{
                    name:'',
                    nameZh:''
                },
                roles:[],
                allMenus:[],
                defaultProps: {
                    children: 'children',
                    label: 'name'
                },
                selectedMenus:[],
                activeName:-1
            }
        },
        mounted(){
            this.initRoles();
        },
        methods:{
            doAddRole(){
                if (this.role.name && this.role.nameZh){
                    this.postRequest('/system/basic/permiss/role',this.role).then(resp=>{
                       if (resp){
                           this.initRoles();
                           this.role.name='';
                           this.role.nameZh='';
                       }
                    });
                }else{
                    this.$message.error('所有字段不能为空!');
                }
            },
            doUpdate(rid,index){
              let tree = this.$refs.tree[index];
              let selectedKeys = tree.getCheckedKeys(true);/*传true只打印叶子节点*/
             let url='/system/basic/permiss/role?rid='+rid;
             let mids ='';
             selectedKeys.forEach(key=>{
                 mids += '&mids='+key;
             });
             url+=mids;


             this.putRequest(url).then(resp=>{
                 if (resp){
                     this.initRoles();
                     this.activeName=-1;
                 }
             });
            },
            doDeleteRole(role){
                this.$confirm('此操作将永久删除['+role.nameZh+'], 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    this.deleteRequest('/system/basic/permiss/role/'+role.id).then(resp=>{
                        if (resp){
                            this.initRoles();
                        }
                    });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            },



            initSelectedMenus(rid){
              this.getRequest('/system/basic/permiss/mid/'+rid).then(resp=>{
                 if (resp){
                     this.selectedMenus=resp;
                 }
              });
            },
            change(rid){
               if (rid){
                  this.initAllMenus();
                  this.initSelectedMenus(rid);
                  this.shuaxin(rid);
               }
            },

            shuaxin(rid){
                this.initAllMenus();
                this.initSelectedMenus(rid);
            },
            initAllMenus(){
                this.getRequest('/system/basic/permiss/menus').then(resp=>{
                    if (resp){
                        this.allMenus=resp;
                    }
                });
            },
            initRoles(){
                this.getRequest('/system/basic/permiss/').then(resp=>{
                    if (resp){
                        this.roles=resp;
                    }
                });
            }
        }
    }
</script>

<style >
    .PermissManaTool{
        display: flex;/*使用容器布局 下面左对齐生效*/
        justify-content: flex-start;/*左对齐*/

    }
    .PermissManaTool .el-input{
        width: 300px;
        margin-right: 6px;
    }
    .permissManaMain{
        margin-top: 10px;
        width: 700px;
    }

</style>

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM