<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    騰訊架構師帶我寫代碼 —— vue真實企業實戰分享

    閱讀本文你會收獲些什么?

    1. 不玩虛的,真實的企業項目實戰技巧,可以直接拿過去用
    2. 真實的接口調用,實現相關功能
    3. 優秀的封裝技巧(本項目在前騰訊前端架構師指導下構建)
    4. 幫你踩坑,讓你開發更加順暢
    5. 提供脫敏化的所有代碼,讓你不會存在一知半解

    項目預覽

    • 登錄頁效果
      在這里插入圖片描述

    • 登錄時過渡效果
      在這里插入圖片描述

    • 登錄成功,跳轉頁面

    • 左側導航與右側表格效果
      在這里插入圖片描述

    • 你看到的是一個標準的后臺管理系統

    • 簡潔的頁面后面有強大的代碼支持,請繼續往下看

    項目技術棧概覽

    • 開發工具:vscode(推薦使用的前端開發工具)
    • Vue版本:V 2.6.11
    • vue-router:V 3.2.0
    • element ui版本:V 2.15.1
    • 接口調試:axios庫 V 0.21.1
    • vue-cli(腳手架)版本: V 4.5.0
    • node版本:V 13.14.0
    • node-sass:V 4.12.0
    • sass-loader:V 8.0.2
    • babel-eslint:V 10.1.0

    1. 路由配置

    router文件夾下,index.js 配置

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../views/Home.vue'
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        name: 'Home',
        component: Home,
        meta: {
          requireAuth: true
        },
        children: [
          // 學生數據
          {
            path: '/home/studentData',
            name: 'studentData',
            meta: {
              requireAuth: true
            },
            component: () => import('../views/studentData/studentData.vue')
          },
          // 老師數據
          {
            path: '/home/teacherData',
            name: 'teacherData',
            meta: {
              requireAuth: true
            },
            component: () => import('../views/teacherData/teacherData.vue')
          },
        ]
      },
      {
        path: '/login',
        name: 'login',
        component: () => import('../views/login/login.vue'),
        // 子路由
        children: [
    
        ]
      }
    ]
    // 多次點擊同一個導航時會報錯,所以添加了這段代碼
    const originalPush = VueRouter.prototype.push
    VueRouter.prototype.push = function push(location, onResolve, onReject) {
      if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
      return originalPush.call(this, location).catch(err => err)
    }
    
    const router = new VueRouter({
      routes
    })
    // 別的地方會用到 router,所以將它導出去
    export default router
    
    • 說明:因為此類項目只有中間表格部分會切換,所以路由配置采用的是:父子路由模式
    • 一個父路由:home.vue,包含:1. 左側導航,2. 頭部信息欄,3.表格部分的容器
    • view文件目錄配置如下:(如果你不太熟練,先跟著我的配置走)
      在這里插入圖片描述

    2. 登錄頁配置

    login.vue(當前是純靜態頁面,稍后添加接口與方法)

    <template>
      <div class="login-box">
        <div class="form-con">
          <h2>Vue+element ui 管理系統實戰</h2>
          <div class="label">
            <el-input placeholder="請輸入賬號" v-model="userName">
              <template slot="prepend"><i class="el-icon-user"></i></template>
            </el-input>
          </div>
          <div class="label">
            <el-input placeholder="請輸入密碼" v-model="password" show-password @keyup.enter.native="loginFn()">
              <template slot="prepend"><i class="el-icon-lock"></i></template>
            </el-input>
          </div>
          <div class="label">
            <el-button class="login-btn" type="primary" @click="loginFn()"
              >登 錄</el-button
            >
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "login",
      data() {
        return {
          userName: "",
          password: "",
          verify: "",
        };
      },
    };
    </script>
    <style lang="scss" scope>
    .login-box {
      width: 100%;
      height: 100%;
      display: flex;
      text-align: center;
      background: url("~@/assets/4.jpg") no-repeat;
      background-size: 100% 100%;
      .form-con {
        h2 {
          color: #ffffff;
          // color: $colorRed;
        }
        width: 360px;
        height: 420px;
        margin: 150px auto auto auto;
        .label {
          margin: 40px 0;
          .login-btn {
            width: 100%;
          }
        }
      }
    }
    </style>
    

    3. API層配置(接口配置)

    • 接口采用分層設計,為了方便維護,會有以下四個基礎文件(這些是推薦必須要有的,其余有需求可以自己加)

    • 其中前三個文件寫完之后,基本不用去動,一勞永逸,爽的不行
      在這里插入圖片描述

    • 思路:深度解耦高度復用

    3.1 service.js 配置

    • 此文件負責和后臺打交道,統一處理所有接口(各種攔截處理、狀態處理等)
    import axios from 'axios'
    import vue from '../main.js'
    // 從本地獲取token
    function getTokenByLocal() {
        let token = sessionStorage.getItem('token');
        return token;
    }
    
    const service = axios.create({
        baseURL: '/sys',
        // withCredentials: true,
        timeout: 5000,
    })
    
    
    // 請求攔截
    service.interceptors.request.use(
        config => {
            if (getTokenByLocal()) {
                // 在此可以設置所有接口headers頭部
                config.headers['token'] = getTokenByLocal();
            }else{
                // window.location.href="/login";
            }
            return config
        },
        error => {
            return Promise.reject(error)
        }
    )
    
    // 響應攔截
    service.interceptors.response.use(
        response => {
            let res = response.data;
            // console.log(res);
            // 狀態碼處理
            if (res.code == '200') {
                // location.href = "home/login";
            }
            // 如果為 -101 代表用戶未登錄
            if(res.code == '-101'){
                vue.$router.push('/login');
            }   
            return Promise.resolve(res);
        },
        error => {
            return Promise.reject(error)
        }
    )
    
    export default service;
    

    3.2 common.js 配置

    • 此文件只做一件事情:統一處理項目中所有接口的傳參處理
    • 一般來說,不會有太多花里胡哨的傳參
    // 將service.js引入進來
    import service from './service.js'
    
    // post請求 80% 耦合度低 復用性高
    export function requestOfPost(url, data){
        return service.post(url, data);
    }
    

    3.3 api.js 配置

    • 此文件為二次封裝,加入處理異步的 promise
    import {requestOfPost} from './common.js'
    
    export function postRequest(url, data){
        return new Promise((resolve, reject) => {
            requestOfPost(url, data).then(res => resolve(res))
            .catch(error => reject(error))
        })
    }
    
    

    3.4 url.j配置

    • 此文件統一管理所有接口路徑,不然項目里東一個西一個維護起來麻煩(架構師說的,咱也不敢反駁,仔細尋思了一下,這也是對的)
    • 如果項目里的接口超過了一百個,可以拆分成兩個
    • 切記,加注釋啊親
    const url = {
        // 登錄
        login: '/login',
        // 學生列表
        getClassmates: '/getClassmates' 
    }
    export  default url;
    

    4. 跨域處理

    • 一般來說,本地開發都需要配置接口跨域,后臺一般懶得處理(卑微前端的無力)

    • 項目最外層添加文件: vue.config.js(這個文件只能這么命名,別的名字cli 服務不會識別)
      在這里插入圖片描述

    • 配置如下

    module.exports = {
        devServer: {
            compress: false,
            open: true,
            proxy: {
                '/sys': {
                    // 代理地址
                    target: 'http://api.gebilaowang.com',
                    // websocket (一般用于即時通訊,游戲,這里不需要,所以不開)
                    ws: false, 
                    // 是否允許跨域
                    changeOrigin: true,
                    // 重寫
                    pathRewite: {
                        '/sys': '/'
                    }
                }
            }
        }
    }
    

    5. 可以愉快的調接口了

    login.vue 代碼添加

    <script>
    // 此為全局定義的過渡方法
    import { loadingShow } from "../../common/js/common.js";
    import url from "../../request/url.js";
    import { postRequest } from "../../request/api.js";
    
    export default {
      name: "login",
      props: {
        msg: String,
      },
      data() {
        return {
          userName: "",
          password: "",
          verify: "",
        };
      },
      methods: {
      	// 登錄方法
        loginFn() {
          if (!this.userName) {
            this.msgFn("warning", "請輸入賬號名");
            return;
          } else if (!this.password) {
            this.msgFn("warning", "請輸入密碼");
            return;
          } else {
            // 加載動畫
            loadingShow();
            let data = {
              userName: this.userName,
              passWord: this.password
            };
            postRequest(url.login, data).then(
              (res) => {
                // 動畫隱藏
                loadingHide();
                if (res.code == 500) {
                  this.msgFn("error", res.msg);
                  return;
                }
                // token 存入sessionStorage
                sessionStorage.setItem("token", res.token);
                // 頁面跳轉
                setTimeout(() => {
                  this.$router.push("/home/studentData");
                }, 500);
              },
              (error) => {
                console.log(error);
              }
            );
          }
        },
    
    
        // 彈窗
        msgFn(type, text) {
          this.$message({
            message: text,
            type: type,
          });
        },
      },
      created() {
      },
    };
    </script>
    

    6. main.js中 你需要的配置

    • 引入element ui中的元素并注冊
    • 一些全局的動畫配置
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    
    import {
      Button,
      Container,
      Header,
      Aside,
      Main,
      Footer,
      Input,
      Loading,
      Message,
      Menu,
      Submenu,
      MenuItem,
      MenuItemGroup,
      Dropdown,
      DropdownMenu,
      Table,
      TableColumn,
      DropdownItem,
      Form,
      FormItem,
      Select,
      Option,
      OptionGroup,
      DatePicker,
      Pagination,
      MessageBox,
      Popover,
      Tag,
      Switch,
      Dialog
    
    } from 'element-ui';
    Vue.use(global)
    Vue.use(Button)
    Vue.use(Container)
    Vue.use(Header)
    Vue.use(Aside)
    Vue.use(Main)
    Vue.use(Footer)
    Vue.use(Input)
    Vue.use(Menu)
    Vue.use(Submenu)
    Vue.use(MenuItem)
    Vue.use(MenuItemGroup)
    Vue.use(Dropdown)
    Vue.use(Table)
    Vue.use(TableColumn)
    Vue.use(DropdownMenu)
    Vue.use(DropdownItem)
    Vue.use(Form)
    Vue.use(FormItem)
    Vue.use(Select)
    Vue.use(Option)
    Vue.use(OptionGroup)
    Vue.use(DatePicker)
    Vue.use(Pagination)
    Vue.use(Popover)
    Vue.use(Dialog);
    Vue.use(Tag)
    Vue.use(Switch)
    Vue.use(Loading.directive);
    
    // 添加全局方法
    Vue.prototype.$loading = Loading.service;
    Vue.prototype.$message = Message;
    Vue.prototype.$confirm = MessageBox.confirm;
    Vue.prototype.$msgbox = MessageBox;
    Vue.config.productionTip = false;
    
    
    let vue = new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    
    export default vue;
    

    7. 配置路由攔截

    • 如果內容不多,可以放在main.js中
    • 記得要引入 router
    router.beforeEach((to, from, next) => {
      // 以token為例
      let token = sessionStorage.getItem('token');
      // 需要驗證之后才能進入 requireAuth
      if (to.meta.requireAuth) {
        if (token) {
          next();
          // 順利進入
        } else {
          // 跳入到指定頁面
          next({
            path: '/login'
          })
        }
      } else {
        // 順利進入
        next();
      }
    })
    

    8. 全局方法配置

    • 存放目錄,如下(架構師說的,我又思考了一下,放這里是有道理的)
      在這里插入圖片描述
    • 給大家一個全局方法做參考,后續的按照這個來就是(依舊很貼心)
    import vue from '../../main.js'
    
    // 遮罩層控制
    export function loadingShow(close){
        const loadingFade =  vue.$loading({
          lock: true,
          text: 'Loading',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
        })
        if(close){
            loadingFade.close();
        }
      }
    

    9. 父頁面配置(home.vue)

    • 我感覺大家可能會需要,所以代碼還是貼出來(有一種需要是作者感覺你會需要)
    <template>
      <div class="container-big">
        <el-container class="container">
          <el-aside width="220px">
            <el-menu class="menu" :default-active="index">
              <el-submenu index="1">
                <template slot="title"><i class="el-icon-s-home"></i>首頁</template>
                <el-menu-item index="1-1" @click="toRoute('/home/studentData', '學生數據')"
                  >學生數據</el-menu-item
                >
              </el-submenu>
              <el-submenu index="2">
                <template slot="title"
                  ><i class="el-icon-s-cooperation"></i>教務處 / 管理</template
                >
                <el-submenu index="2-2">
                  <template slot="title">角色管理</template>
                  <el-menu-item
                    index="2-2-1"
                    @click="toRoute('/home/teacherData', '老師管理')"
                  >
                    老師管理</el-menu-item
                  >
                  <el-menu-item
                    index="2-2-2"
                    @click="toRoute()"
                  >
                    老師審核</el-menu-item
                  >
                </el-submenu>
                <el-submenu index="2-3">
                  <template slot="title">其它管理</template>
                  <el-menu-item
                    index="2-3-1"
                    @click="
                      toRoute()
                    "
                  >
                    教務管理</el-menu-item
                  >
                  <el-menu-item
                    index="2-3-3"
                    @click="
                      toRoute(
                        '/home/shopList',
                        ' / 運營管理 / 店鋪管理 / 店鋪列表',
                        '店鋪列表',
                        '2-3-3'
                      )
                    "
                  >
                    掃廁所管理</el-menu-item
                  >
                </el-submenu>
              </el-submenu>
            </el-menu>
          </el-aside>
    
          <el-container>
            <el-header style="text-align: right; font-size: 14px">
              <div class="opration">
                <el-dropdown>
                  <i class="el-icon-setting" style="margin-right: 15px"> 操作 </i>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item @click.native="logOut()"
                      >退出登錄</el-dropdown-item
                    >
                  </el-dropdown-menu>
                </el-dropdown>
                <span>{{ userName }}</span>
              </div>
              <div class="router-con">
                <div>
                  <span>xxx公司運營管理系統</span>
                </div>
                <h2>{{pageName}}</h2>
              </div>
            </el-header>
    
            <el-main>
              <router-view></router-view>
            </el-main>
          </el-container>
        </el-container>
      </div>
    </template>
    
    <script>
    export default {
      name: "home",
      data() {
        return {
          userName: "",
          index: "1-1",
          pageName: ''
        };
      },
      created() {
        // 可以在此取權限相關數據
      },
      methods: {
        // 跳轉各個頁面
        toRoute(url, name) {
          this.$router.push(url);
          this.pageName = name;
        },
        // 退出登錄
        logOut() {
          // 跳轉至登錄頁面
          this.$router.push("/login");
          // 一般需要在此清除用戶的一些緩存數據
        },
      },
    };
    </script>
    
    <style lang="scss" scope>
    .logo {
      overflow: hidden;
      padding: 10px 0px 10px 20px;
      cursor: pointer;
      img {
        float: left;
        margin-top: 10px;
      }
      p {
        float: left;
        margin: 12px 0 0 12px;
        color: #fff;
        font-weight: 600;
        font-size: 20px;
        vertical-align: middle;
        animation: fade-in;
        animation-duration: 0.3s;
      }
    }
    .el-submenu .el-menu-item {
      text-align: left;
      height: 40px;
      line-height: 40px;
      // margin-left: 10px;
    }
    .menu {
      .el-submenu {
        .el-submenu__title {
          padding-left: 30px;
        }
        .el-menu {
          .el-submenu__title {
            padding-left: 50px !important;
          }
        }
      }
    }
    
    .el-header {
      color: #333;
      border-bottom: 1px solid #d3d3d3;
      background: #fff;
      padding: 0;
      box-shadow: darkgrey 5px 0 5px 1px; //邊框陰影
      .opration {
        height: 60px;
        line-height: 60px;
        padding: 0 20px;
        cursor: pointer;
      }
      .router-con {
        height: 60px;
        // width: 100%;
        padding: 20px;
        border-bottom: 1px solid #d3d3d3;
        text-align: left;
        div {
          margin-bottom: 10px;
        }
      }
    }
    .container-big {
      width: 100%;
      height: 100%;
    }
    .container {
      width: 100%;
      height: 100%;
      .el-main {
        margin-top: 100px;
        background: #f2f2f2;
      }
    }
    .el-aside {
      color: #333;
      height: 100%;
      background: #001529;
      overflow-y: auto;
      overflow-x: hidden;
      .el-menu {
        border: none;
        background: #001529;
    
        li {
          .el-submenu__title {
            color: #f5f5f5;
            text-align: left;
          }
          .el-submenu__title:hover {
            color: #333;
          }
          ul {
            li {
              color: #dcdcdc;
              .el-menu-item-group__title {
                // color: #dcdcdc;
              }
            }
            li.is-active {
              color: #409eff;
            }
            li:hover {
              color: #333;
            }
          }
        }
      }
    }
    </style>
    
    • 代碼中寫了詳細的注釋,在這里不多做解釋
    • 可以全部復制丟你的文件里即可看到效果(很貼心吧)

    10. FAQ:你可能會碰到的問題及解決方案

    1. 某些東西安裝出錯(國外的網絡可能不穩定,可以配置淘寶鏡像)
    2. 某些東西未生效(請注意版本問題)
    3. vue.config.js文件不生效(此文件做出了任何改變,一定要重啟服務,重新編譯)
    4. 某些組件樣式不對(查看 main.js 中的引入與注冊是否做好了)
    5. 報某些模塊找不到(查看文件存放路徑與引入路徑是否正確)
    6. 其它問題(可以直接私信我,每天我會查看幾次,看到就回復大家)
    • 為了力求大家能看懂,不會出問題。此文花了很多精力完成(抽著紅塔山,熬著夜)希望能幫到大家
    • 別忘了三連走一波啊~,點贊、評論、關注
    • 一下子看不完,可以收藏一下~
    • 再次感謝大家的支持,一定會持續輸出優質好文~
      在這里插入圖片描述
    相關推薦
    ??2020 CSDN 皮膚主題: 黑客帝國 設計師:白松林 返回首頁
    實付 9.90元
    使用余額支付
    點擊重新獲取
    掃碼支付
    錢包余額 0

    抵扣說明:

    1.余額是錢包充值的虛擬貨幣,按照1:1的比例進行支付金額的抵扣。
    2.余額無法直接購買下載,可以購買VIP、C幣套餐、付費專欄及課程。

    余額充值
    多乐彩