雲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