Tencent architect took me to write code -- vue real enterprise actual combat sharing

What will you gain from reading this article?

  1. Do not play virtual, real enterprise project actual combat skills, which can be directly used in the past
  2. Real interface call to realize relevant functions
  3. Excellent packaging skills (this project is built under the guidance of the former Tencent front-end architect)
  4. Help you step on the pit and make your development more smooth
  5. Provide desensitization of all the code, so that you will not have a little knowledge

Project Preview

  • Landing page effect

  • Transition effect at login

  • Login succeeded, jump to the page

  • Left navigation and right table effect

  • What you see is a standard background management system

  • There is strong code support behind the concise page. Please continue to look down

Overview of project technology stack

  • Development tool: vscode (recommended front-end development tool)
  • Vue version: V 2.6.11
  • vue-router: V 3.2.0
  • element ui version: V 2.15.1
  • Interface debugging: axios library V 0.21.1
  • Vue cli (scaffold) version: V 4.5.0
  • node version: V 13.14.0
  • node-sass: V 4.12.0
  • sass-loader: V 8.0.2
  • babel-eslint: V 10.1.0

1. Routing configuration

Under router folder, index JS configuration

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: [
      // Student data
      {
        path: '/home/studentData',
        name: 'studentData',
        meta: {
          requireAuth: true
        },
        component: () => import('../views/studentData/studentData.vue')
      },
      // Teacher data
      {
        path: '/home/teacherData',
        name: 'teacherData',
        meta: {
          requireAuth: true
        },
        component: () => import('../views/teacherData/teacherData.vue')
      },
    ]
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('../views/login/login.vue'),
    // Sub route
    children: [

    ]
  }
]
// An error will be reported when clicking the same navigation multiple times, so this code is added
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 will be used in other places, so export it
export default router
  • Note: because only the middle table part of such items can be switched, the routing configuration adopts the parent-child routing mode
  • A parent route: home Vue, including: 1 Left navigation, 2 Header information bar, 3 Container for table section
  • The configuration of view file directory is as follows: (if you are not proficient, follow my configuration first)

2. Login page configuration

login.vue (currently it is a pure static page, and interfaces and methods will be added later)

<template>
  <div class="login-box">
    <div class="form-con">
      <h2>Vue+element ui Management system practice</h2>
      <div class="label">
        <el-input placeholder="Please enter the account number" v-model="userName">
          <template slot="prepend"><i class="el-icon-user"></i></template>
        </el-input>
      </div>
      <div class="label">
        <el-input placeholder="Please input a password" 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()"
          >Sign in</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 layer configuration (interface configuration)

  • The interface adopts layered design. In order to facilitate maintenance, there will be the following four basic documents (these are recommended and necessary, and the rest can be added by yourself if necessary)

  • After the first three documents are written, there is basically no need to move them. Once and for all, they are not cool

  • Idea: deep decoupling and high reuse

3.1 service.js configuration

  • This file is responsible for dealing with the background and dealing with all interfaces (various interception processing, status processing, etc.)
import axios from 'axios'
import vue from '../main.js'
// Get token from local
function getTokenByLocal() {
    let token = sessionStorage.getItem('token');
    return token;
}

const service = axios.create({
    baseURL: '/sys',
    // withCredentials: true,
    timeout: 5000,
})


// Request interception
service.interceptors.request.use(
    config => {
        if (getTokenByLocal()) {
            // All interface headers can be set here
            config.headers['token'] = getTokenByLocal();
        }else{
            // window.location.href="/login";
        }
        return config
    },
    error => {
        return Promise.reject(error)
    }
)

// Response interception
service.interceptors.response.use(
    response => {
        let res = response.data;
        // console.log(res);
        // Status code processing
        if (res.code == '200') {
            // location.href = "home/login";
        }
        // If it is - 101, it means that the user is not logged in
        if(res.code == '-101'){
            vue.$router.push('/login');
        }   
        return Promise.resolve(res);
    },
    error => {
        return Promise.reject(error)
    }
)

export default service;

3.2 common.js configuration

  • This file only does one thing: uniformly handle the parameter transfer processing of all interfaces in the project
  • Generally speaking, there won't be too many fancy passes
// Add service JS introduced
import service from './service.js'

// post request 80% low coupling and high reusability
export function requestOfPost(url, data){
    return service.post(url, data);
}

3.3 api.js configuration

  • This file is a secondary package, adding promise to handle asynchrony
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 configuration

  • This file manages all interface paths in a unified way, otherwise it will be troublesome to maintain one by one in the project (I dare not refute what the architect said, but I think it over carefully, which is also right)
  • If there are more than 100 interfaces in the project, it can be divided into two
  • Remember, add notes
const url = {
    // Sign in
    login: '/login',
    // Student list
    getClassmates: '/getClassmates' 
}
export  default url;

4. Cross domain processing

  • Generally speaking, local development needs to configure interfaces across domains, and the background is generally lazy to deal with it (the weakness of the humble front end)

  • Add files to the outermost layer of the project: Vue config. JS (this file can only be named like this, and other names will not be recognized by cli service)

  • The configuration is as follows

module.exports = {
    devServer: {
        compress: false,
        open: true,
        proxy: {
            '/sys': {
                // Agent address
                target: 'http://api.gebilaowang.com',
                // websocket (generally used for instant messaging and games. It's not needed here, so it's not open)
                ws: false, 
                // Allow cross domain
                changeOrigin: true,
                // rewrite
                pathRewite: {
                    '/sys': '/'
                }
            }
        }
    }
}

5. You can adjust the interface happily

login.vue code addition

<script>
// This is a globally defined transition method
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: {
  	// Login method
    loginFn() {
      if (!this.userName) {
        this.msgFn("warning", "Please enter the account name");
        return;
      } else if (!this.password) {
        this.msgFn("warning", "Please input a password");
        return;
      } else {
        // Load animation
        loadingShow();
        let data = {
          userName: this.userName,
          passWord: this.password
        };
        postRequest(url.login, data).then(
          (res) => {
            // Animation hiding
            loadingHide();
            if (res.code == 500) {
              this.msgFn("error", res.msg);
              return;
            }
            // token stored in sessionStorage
            sessionStorage.setItem("token", res.token);
            // Page Jump
            setTimeout(() => {
              this.$router.push("/home/studentData");
            }, 500);
          },
          (error) => {
            console.log(error);
          }
        );
      }
    },


    // Popup
    msgFn(type, text) {
      this.$message({
        message: text,
        type: type,
      });
    },
  },
  created() {
  },
};
</script>

6. main.js configuration you need

  • Introduce elements in element ui and register
  • Some global animation configurations
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);

// Add global method
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. Configure route interception

  • If you don't have much content, you can put it in main JS
  • Remember to introduce router
router.beforeEach((to, from, next) => {
  // Take token as an example
  let token = sessionStorage.getItem('token');
  // You need to verify before entering requireAuth
  if (to.meta.requireAuth) {
    if (token) {
      next();
      // Smooth entry
    } else {
      // Jump to the specified page
      next({
        path: '/login'
      })
    }
  } else {
    // Smooth entry
    next();
  }
})

8. Global method configuration

  • The storage directory is as follows (what the architect said, I thought again, and it makes sense to put it here)
  • Give you a global method for reference, and follow it up (still very considerate)
import vue from '../../main.js'

// Mask layer control
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. Parent page configuration (home.vue)

  • I feel that you may need it, so the code is still posted (there is a need that the author feels you will need it)
<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>home page</template>
            <el-menu-item index="1-1" @click="toRoute('/home/studentData', 'Student data')"
              >Student data</el-menu-item
            >
          </el-submenu>
          <el-submenu index="2">
            <template slot="title"
              ><i class="el-icon-s-cooperation"></i>dean's office / Administration</template
            >
            <el-submenu index="2-2">
              <template slot="title">Role management</template>
              <el-menu-item
                index="2-2-1"
                @click="toRoute('/home/teacherData', 'Teacher management')"
              >
                Teacher management</el-menu-item
              >
              <el-menu-item
                index="2-2-2"
                @click="toRoute()"
              >
                Teacher review</el-menu-item
              >
            </el-submenu>
            <el-submenu index="2-3">
              <template slot="title">Other management</template>
              <el-menu-item
                index="2-3-1"
                @click="
                  toRoute()
                "
              >
                Educational Administration</el-menu-item
              >
              <el-menu-item
                index="2-3-3"
                @click="
                  toRoute(
                    '/home/shopList',
                    ' / operation management / Store management / Shop list',
                    'Shop list',
                    '2-3-3'
                  )
                "
              >
                Toilet cleaning management</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"> operation </i>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item @click.native="logOut()"
                  >Log out</el-dropdown-item
                >
              </el-dropdown-menu>
            </el-dropdown>
            <span>{{ userName }}</span>
          </div>
          <div class="router-con">
            <div>
              <span>xxx Company operation management system</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() {
    // You can access permission related data here
  },
  methods: {
    // Jump to each page
    toRoute(url, name) {
      this.$router.push(url);
      this.pageName = name;
    },
    // Log out
    logOut() {
      // Jump to login page
      this.$router.push("/login");
      // It is generally necessary to clear some cached data of users here
    },
  },
};
</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; //Border shadow
  .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>
  • Detailed comments are written in the code, which will not be explained here
  • You can copy it all and lose it in your file to see the effect (very considerate)

Possible problems and solutions: FAQ

  1. Some things are installed incorrectly (the foreign network may be unstable, and Taobao image can be configured)
  2. Something doesn't work (please pay attention to the version)
  3. vue.config.js file does not take effect (if any changes are made to this file, the service must be restarted and recompiled)
  4. Some components are not in the right style (check whether the import and registration in main.js are ready)
  5. Report that some modules cannot be found (check whether the file storage path and import path are correct)
  6. Other questions (you can send me a private letter directly. I will check it several times a day and reply to you when I see it)
  • In order to make everyone understand, there will be no problem. This article took a lot of effort to finish (smoking Hongtashan and staying up all night) and I hope it can help you
  • Don't forget to walk for three times ~, like, comment and pay attention
  • You can't finish it all at once. You can collect it~
  • Thank you again for your support. We will continue to output high-quality articles~

Keywords: Javascript Vue.js

Added by mlnsharma on Tue, 08 Feb 2022 08:48:42 +0200