What will you gain from reading this article?
- Do not play virtual, real enterprise project actual combat skills, which can be directly used in the past
- Real interface call to realize relevant functions
- Excellent packaging skills (this project is built under the guidance of the former Tencent front-end architect)
- Help you step on the pit and make your development more smooth
- 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
- Some things are installed incorrectly (the foreign network may be unstable, and Taobao image can be configured)
- Something doesn't work (please pay attention to the version)
- vue.config.js file does not take effect (if any changes are made to this file, the service must be restarted and recompiled)
- Some components are not in the right style (check whether the import and registration in main.js are ready)
- Report that some modules cannot be found (check whether the file storage path and import path are correct)
- 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~