The global environment

  1. V14.15.4 Node. Js version
  2. Vue CLI version @vue/ CLI 4.5.13
  3. TypeScript Version 4.2.3

Project creation

  1. Execute the command
vue create project_name
Copy the code

2. Select the initial configuration itemSelect the vue3.x version

Overall project structure

Application // Application // Application // Application // Application // Application // Application // Application // Application // Application // .eslintrc.js ├─ ├─ download.config.js ├─ Package-Lock. json ├─ Public │ ├─ FavIcon ├─ SRC │ ├─ API │ ├─ ├─ Login. Currently only encapsulates the logged out │ │ └ ─ the manage. The ts / / packaging request method │ ├ ─ App. Vue │ ├ ─ assets │ │ ├ ─ SCSS / / global CSS style │ | │ └ ─ index. The vue │ │ └ ─ logo. The PNG │ ├ ─ components │ │ └ ─ NoFind. Vue/definition / 404 page │ ├ ─ main. Ts / / entrance │ ├ ─ the router configuration / / routing │ │ └ ─ but ts │ ├ ─ │ ├─ ├─ ├.sci-vue. │ ├─ sci-vue. │ ├─ sci-vue. │ ├─ sci-vue Storage. Ts / / packaging sessionStorage and localStorage, cookie store delete │ └ ─ views │ ├ ─ Home/page/content │ │ └ ─ index. The vue │ ├ ─ Layout / / project │ ├─ ├─ ├─ ├─ vue │ ├─ vue │ ├─ vue │ ├─ vue │ ├─ vue │ ├─ vueCopy the code

UI library installation and introduction

Install YARN add ant-design-vue The official website of ant-design-vue provides two import modes: on-demand load or global import. In this case, global import is used. After installation, add the following code to the main.ts file

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 引入ant-design-vue
import Antd from 'ant-design-vue'
import "ant-design-vue/dist/antd.css";

createApp(App).use(store).use(Antd).use(router).mount('#app')

Copy the code

Axios requests encapsulation

The installation

yarn add axios
Copy the code

Request to intercept

Note: The storage of tokens and vuEX storage tokens are described in the corresponding section. The request.ts file is just used to create the utils folder under the project SRC folder and create the request.ts file as follows:

import axios,{AxiosResponse, AxiosRequestConfig} from "axios"; Import {notification} from 'ant-design-vue' import store from '@/store' // obtain token import {localCache} from '@/utils/storage' import {logout} from "@/ API /login"; // Initialize const instance = axios.create({baseURL: process.env.vue_app_API_base_URL, timeout: 120 * 1000, withCredentials: true}) / / request error const err = (error: {message: string | string []. response: { data: any; status: number; }; }) => {if (error.message.includes('timeout')) {notification.error({message: 'system prompt ', description:' request timeout', duration: 4}); } if (error.response) { const data = error.response.data; const token = localCache.getCache('token') if (error.response.status == 403) { notification.error({ message: 'System warning ', description:' Failed to request resource ', duration: 4}); } if (error.response.status === 401 && ! (data.result && data.result.islogin)) {notification.error({message: 'system prompt ', description:' no access permission ', duration: 4}); If (token) {logout(); logout(); } } } return Promise.reject(error); }; / / request the instance. The interceptors. Request. Use ((config: AxiosRequestConfig) => {// Obtain system token const token:string = store.state.token; If (token) {config.headers[' x-access-token '] = token // Let each request carry a user-defined token. Modify it according to the actual situation} // Set the default parameter of the GET interface to carry a timestamp (config.method == 'get') { config.params = { _t: new Date().getTime(), ... config.params } } return config; }, err) / / interception successful request the instance. The interceptors. Response. Use ((response: AxiosResponse) = > {const config: AxiosRequestConfig = response.config || ''; const code = Number(response.data.code); / / code according to the client agreement interface before and after successful state changes the if (code = = 200 | | code = = 0) {if (config) {the console. The log (' request is successful ')} return response. The data; } else { const errorCode = [402, 403, 500]; If (errorcode.includes (response.data.code)) {notification.error({message: 'system prompt ', description:' no permission ', duration: 4}); setTimeout(() => { window.location.reload(); }, 500) } } },err) export default instance;Copy the code

VUE_APP_API_BASE_URL is used to initialize baseURL from process.env.vue_app_API_base_URL. Where. Env all environments are loaded,.env. test test environment is loaded,.env.development development environment is loaded,.env. production environment is loaded. The files are created in the root directory of the project at the same level as vue.config.js. For details about configuration items, see Vue CLI mode and Environment Variables

encapsulation

Create a folder API folder under the project SRC folder and create a manage.ts file with the following contents:

Import axios from '@/utils/request' /** * @desc Post request * @param URL request path * @param parameter Request parameter ** / export function postAction(url: string, parameter: any) { return axios({ url: url, method: 'post', data: Parameter})} / HTTP request * * * * @ desc @ param url request path * @ param parameter request parameters * @ param method = {post | put} * * / export function httpAction(url: string, parameter: any, method: any) { return axios({ url: url, method: method, data: /** * @desc put request * @param URL request path * @param parameter Request parameter ** / export function putAction(url: string, parameter: any) { return axios({ url: url, method: 'put', data: /** * @desc get request * @param url request * @param parameter Request ** / export function getAction(url: string, parameter: any) { return axios({ url: url, method: 'get', params: } /** * @desc delete request * @param url request * @param parameter request ** / export function deleteAction(url: string, parameter: any) { return axios({ url: url, method: 'delete', params: parameter }) }Copy the code

Vue.config.js configures the proxy

  1. Created in the project root directoryvue.config.jsConfigure the server request broker
module.exports = {
  devServer: {
    port: 3082,
    proxy: {
      "/pr-api": {
        target: "http://localhost:3085",
        ws: false,
        changeOrigin: true,
      },
    },
  },

  lintOnSave: undefined,
};
Copy the code

Unified management of project request apis

  1. In the projectsrcCreate folder under folderapiFolder and createajaxUrl.config.tsFile for unified management of the project request API. The sample is as follows
const Login = {
  login: '/sys/login'
}
export {
  Login
}
Copy the code

Log in and log out

Exit the login public method

Create the folder API folder under the project SRC folder and create the login.ts file to manage logging out.

import { createVNode } from "vue"; import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; // import { useRouter } from "vue-router"; import { postAction } from "@/api/manage"; import {cookies, localCache} from '@/utils/storage' import { Modal, Message} from 'ant-design-vue' /* exit */ export function logout() {// const router = useRouter(); Modal.confirm({title: 'Log out? 'icon: createVNode (ExclamationCircleOutlined), okText:' confirmed ', cancelText: 'cancel' onOk () {postAction ("/sys/logout ", {}), then ((res: Any) => {if (res.success) {// Clear all data cookies stored in the browser. RemoveCookie ('vuex') localCache.clearCache() // A message indicating successful exit is displayed message.success(res.message); // router.push({name: "Login"}); // Refresh the entire browser setTimeout(() => {window.location.reload(); }}}, 100)); }, onCancel() {message.info(' cancel logout '); }, class: 'test', }); }Copy the code

Cache storage read encapsulation

Note: For jS-cookie installation see section 3 of the Vuex-PersistedState usage steps. Create the utils folder under the project SRC folder and create the storage.ts file as follows:

Import Cookies from 'js-cookie' /* * localStorage encapsulation */ const localCache = {// set setCache(key: string, value: Any) {window. LocalStorage. SetItem (key, JSON stringify (value))}, / / get getCache (key: string) { const value = window.localStorage.getItem(key) if (value) { return JSON.parse(value) } }, // Delete a certain deleteCache(key: String) {window. LocalStorage. RemoveItem (key)}, / / to empty all clearCache () {window. LocalStorage. The clear ()}, } /** * sessionStorage */ const sessionStorage = {// set(key: string, value: Any) {window. The sessionStorage. SetItem (key, JSON stringify (value))}, / / remove the data get < T > (key: string) { const value = window.sessionStorage.getItem(key) if (value && value ! = "undefined" && value ! Parse (value) {return JSON. Parse (value)} return null}, string) { window.sessionStorage.removeItem(key) } } const cookies = { getCookie(key:string) { return Cookies.get(key) },  setCookie(key: string, value:any) { Cookies.set(key, value) }, removeCookie(key:string) { Cookies.remove(key); return } } export { sessionStorage, localCache, cookies }Copy the code

The login

Note: Routing and VUEX storage tokens are described in the corresponding section.

<template> < A-form layout="horizontal" :model="formState"> < A-form-item label=" account "> < A-input V-model :value="formState. Username "placeholder=" /> </a-form-item> < A-form-item label=" password "> < A-input V-model :value="formState. Password "type="password" placeholder=" placeholder" /> </a-form-item> <a-button type="primary" @click="onSubmit"> </a-button> </a-form> </template> <script lang="ts"> import {defineComponent, Reactive, UnwrapRef, toRaw } from "vue"; import { useStore } from "vuex"; import { useRouter } from "vue-router"; import { message } from "ant-design-vue"; Import {postAction} from "@/ API /manage"; Import {Login} from "@/ API/ajaxurl.config "; import {Login} from "@/ API/ajaxurl.config "; FormState {username: string; password: string | number; } export default defineComponent({ setup() { const formState: UnwrapRef<FormState> = reactive({ username: "", password: ""}); // Vuex const store = useStore(); // Vue Router const router = useRouter(); // const onSubmit = () => {let params = toRaw(formState); postAction(Login.login, params).then((res: Any) => {if (res.success) {// Set the token data to Vuex store.mit ("setToken", res.result.token); // Set user information to Vuex store.mit ("setUserInfo", res.result.userinfo); Router.push ({name: "Layout"}); } else { message.error(res.message); }}); }; return { formState, onSubmit, }; }}); </script>Copy the code

exit

<template> <div> <a-button type="primary" @click="outLogin"> </a-button> </div> </template> <script lang="ts"> import  { defineComponent } from "vue"; import { logout } from "@/api/login"; Export default defineComponent({setup() {// logout const outLogin = () => {logout(); }; return { outLogin, }; }}); </script>Copy the code

Layout Layout

Layout Layout

  1. Modify theApp.vuefile
<template> <! </template> <style lang=" SCSS "></style>Copy the code
  1. Route jump sumlayoutLayout content rendering
<template> <a-layout> <! <a-layout-header class="header"> <div class="logo" /> < A-menu theme="dark" mode="horizontal" v-model:selectedKeys="selectedKeys1" :style="{ lineHeight: '64px' }" > <a-menu-item key="1">nav 1</a-menu-item> </a-menu> </a-layout-header> <a-layout> <! <a-layout-sider width="200" style="background: #fff"> <a-menu mode="inline" v-model:selectedKeys="selectedKeys2" v-model:openKeys="openKeys" :style="{ height: '100%', borderRight: 0}" > <a-sub-menu key="sub1"> <template #title> <span> < user-menu-item </span> </template key="/home"> <router-link :to="{ path: 'home' }"> <span>home</span> </router-link> </a-menu-item> <a-menu-item key="2">option2</a-menu-item> </a-sub-menu> </a-menu> </a-layout-sider> <! <a-layout style="padding: 0 24px 24px"> "> < p style=" margin-bottom: 25px; margin-bottom: 25px; 16px 0"> <a-breadcrumb-item>Home</a-breadcrumb-item> </a-breadcrumb> <a-layout-content :style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px', }" > <! <router-view /> </a-layout-content> </a-layout> </a-layout> </a-layout> </template> <script lang="ts"> import { UserOutlined } from "@ant-design/icons-vue"; import { defineComponent, ref } from "vue"; export default defineComponent({ components: { UserOutlined, }, setup() { return { selectedKeys1: ref<string[]>(["2"]), selectedKeys2: ref<string[]>(["1"]), collapsed: ref<boolean>(false), openKeys: ref<string[]>(["sub1"]), }; }}); </script> <style> #components-layout-demo-top-side-2 .logo { float: left; width: 120px; height: 31px; margin: 16px 24px 16px 0; Background: Rgba (255, 255, 255, 0.3); } .ant-row-rtl #components-layout-demo-top-side-2 .logo { float: right; margin: 16px 0 16px 24px; } .site-layout-background { background: #fff; } </style>Copy the code

VueX shares data

Because Vuex needs to store global tokens and user information, and because Vuex runs in memory and stores data in memory, when a user refreshes a page, Memory data is reinitialized and stored persistently through sessionStorage/localStorage/cookie. Here we use two plug-ins to persist Vuex data. Vuex data persistence enabled by Vuex-PersistedState and JS-cookie is removed. Note: Ts projects using JS-cookie need to install “jS-cookie “: “^3.0.1”, and “@types/ Js-cookie “: “^2.2.7”, two dependency packages

Vuex-persistedstate Procedure

1. Install the plug-in

yarn add vuex-persistedstate -S
Copy the code

2. Use plug-ins

Export default createStore({getters, plugins: [createPersistedState()]})Copy the code

3. Install the js – cookies

Yarn add [email protected] @ types/[email protected]Copy the code

The specific use

import { createStore } from 'vuex' import createPersistedState from 'vuex-persistedstate' import Cookies from 'js-cookie' import {localCache} from '@/utils/storage' export interface UserState {count: number token: string, userInfo: Any,} // Vuex-PersistedState provides a reducer function that allows you to customize storage keys or use paths. Paths are recommended. ModuleName. XXXX, PersistData const PERSIST_PATHS = ['token', Return Vuex export default createStore({state:<UserState> {count: 0, token: '', userInfo: {}}, mutations: Count ++}, // setToken setToken(state, token) {state.token = token; SetCache ('token', token)}, // Set user information setUserInfo(state, userInfo) {state.userinfo = userInfo; Localcache. setCache('userInfo', userInfo)}}, actions: {}, modules: {}, getters: {}, Vuex data persistence plugins: [createPersistedState({/* storage default storage to localStorage storage to sessionStorage configuration storage: window.sessionStorage */ // reducer: Var var var var var var var var var var var var var var var var var var var var Paths: PERSIST_PATHS, storage: {getItem: (key) => Cookies.get(key), // Please see https://github.com/js-cookie/js-cookie#json, on how to handle JSON. setItem: (key, value) => Cookies.set(key, value, { expires: 3, secure: true }), removeItem: (key) => Cookies.remove(key), }, })] })Copy the code

VueRouter use

Nested routines are laid out by Layout

import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' const Login = () => import('@/views/Login/index.vue') const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Login', component: Login, }, { path: '/layout', name: 'Layout', Component: () => import("@/views/Layout/index.vue"), // '/home', name: 'Home', component: () => import('@/views/Home/index.vue') }, { path: '/about', name: 'About', component: () => import('@/views/About.vue') } ] }, {/ / match all path used vue2 * vue3 / : pathMatch (. *) * or / : pathMatch (. *) or / : catchAll (. *) path: '/ : pathMatch (. *) *', name: "404", Component: ()=> import("@/ Components/nofind.vue ")}] // Initialize router const router = createRouter({history: createWebHistory(process.env.BASE_URL), routes }) export default routerCopy the code

Routing to intercept

import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' import {localCache} from '@/utils/storage' // route idle load const Login = () => import('@/views/Login/index.vue') const routes: Array<RouteRecordRaw> = [] // initialize router const router = createRouter({history: CreateWebHistory (process.env.base_URL), routes}) // Set router guard route. beforeEach((to, from, next) => {// to indicates the path to be accessed, If (to.path == '/') {// The current access is the login page, Next ()} else {// Get the user's local token, jump to the login page if the token does not exist const token = localCache.getCache('token') if (! Token) {next('/')} else {// Next ()}}}) export default routerCopy the code

The overall code

import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' import {localCache} from '@/utils/storage' // route idle load const Login = () => import('@/views/Login/index.vue') const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Login', component: Login, }, { path: '/layout', name: 'Layout', component: Children: [{path: '/home', name: 'home', component: {path: '/home', name:' home', component: {path: '/home', name: 'home', component: () => import('@/views/Home/index.vue') }, { path: '/about', name: 'About', component: () => import('@/views/About.vue') } ] }, {/ / match all path used vue2 * vue3 / : pathMatch (. *) * or / : pathMatch (. *) or / : catchAll (. *) path: '/ : pathMatch (. *) *', name: "404", Component: ()=> import("@/ Components/nofind.vue ")}] // Initialize router const router = createRouter({history: CreateWebHistory (process.env.base_URL), routes}) // Set router guard route. beforeEach((to, from, next) => {// to indicates the path to be accessed, If (to.path == '/') {// The current access is the login page, Next ()} else {// Get the user's local token, jump to the login page if the token does not exist const token = localCache.getCache('token') if (! Token) {next('/')} else {// Next ()}}}) export default routerCopy the code

Configure global Styles

Combined with SCSS and vue.config.js for global style configuration, SCSS preprocessing has been installed in the initial configuration items of project creation. Create the SCSS folder under assets in the SRC folder and create the index. SCSS file. Configure SCSS by referring to project vue.config Configuration

Project vue.config configuration

Configure it in vue.config.js

module.exports = { configureWebpack: (config) => {// Production environment cancel console.log if (process.env.node_env === "production") { config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true; } else {// development environment}}, // Configure SCSS CSS: {loaderOptions: {SCSS: {prependData: '@import "@/assets/ SCSS /index.scss"; DevServer: {port: 3082, proxy: {"/pr-api": {target: "http://localhost:3085", ws: false, changeOrigin: true, }, }, }, lintOnSave: undefined, };Copy the code