tsconfig.json

“This is the 20th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

TypeScript configuration files when compiled to JavaScript

{
  // Compile the configuration
  "compilerOptions": {
    // Object code - compiled code using esNext syntax
    "target": "esnext".// The modularity scheme that the object code needs to use -- the compiled code uses esNext modularity
    "module": "esnext".// Enable all strict modes
    "strict": true.// What should be done to preserve JSX
    "jsx": "preserve".// Auxiliary import function ---- If polyfill is used, use import instead of inserting directly into the file
    "importHelpers": true.// Parse modules as Node does
    "moduleResolution": "node".// Skip type checking for some libraries (e.g. Axios, etc.)
    // 1. Improve performance 2. Avoid collisions in multiple libraries with the same type
    "skipLibCheck": true.// The following two options are usually used together
    // The purpose is to make ESM and CJS interchangeable
    "esModuleInterop": true."allowSyntheticDefaultImports": true.// Create a mapping file (ts -> js)
    "sourceMap": true.// Whether json modules can be parsed - that is, whether json files can be imported as a separate module
    "resolveJsonModule": true.// The file path is the basic URL (that is, the file path is resolved based on the current path).
    "baseUrl": ".".// Specify the type to be used for parsing
    "types": ["webpack-env"].// Path resolution (similar to Webpack Alias) - to facilitate TS to resolve our own path alias
    "paths": {
      // Array indicates that multiple arrays can be configured, but generally one is sufficient
      "@ / *": ["src/*"]."components/*": ["src/components/*"]},You can specify which library types can be used in a project
    "lib": ["esnext"."dom"."dom.iterable"."scripthost"].// The default root path in vite is the project folder
    // The default TSC root path is the system root path
    // So we need to unify the compilation behavior of both
    "paths": {
      //./* -- The relative path is relative to the folder in which tsconfig.json is located
      "/ *": [". / *"]}},// Those files need to be parsed by TS
  "include": [
    "src/**/*.ts"."src/**/*.tsx"."src/**/*.vue"."tests/**/*.ts"."tests/**/*.tsx"].// Those files do not need to be parsed by TS
  For example, third-party libraries are introduced in a TS suffix file
  // By default, imported third parties are type checked
  // Sometimes this will cost performance, so exclude node_modules
  // But TS will still do type resolution for the type we introduced, that is, the type we used
  "exclude": ["node_modules"]}Copy the code

shims-vue.d.ts

By default, Typescript only parses TS files, not VUE SFC files, so you need to manually declare the types of SFC files

declare module '*.vue' {
  // Introduce type DefineComponent
  import { DefineComponent } from 'vue'
  
  // Introduce an instance of the Component variable whose type is DefineComponent
  const component: DefineComponent<{}, {}, any>
        
  // Export the default type
  export default component
}
Copy the code

normalize.css

By default, the interface has some default styles, such as 8px margin and padding

To prevent these default styles from impacting the project’S uI, we can use normalize.css to clear some of the browser’s default styles

The installation

> npm i normalize.css
Copy the code

main.ts

// use it globally
import 'normalize.css'
Copy the code

Use TS in Vuex

store/types.ts

export interface IRootState {
  name: string
  age: number
}
Copy the code

store/index.ts

import { createStore } from 'vuex'

import login from './login/login'

import { IRootState } from './types'

// Generics can be passed in when createStore is created - the generics correspond to the type of state
const store = createStore<IRootState>({
  state() {
    return {
      name: 'Klaus'.age: 23}},modules: {
    login
  }
})
export default store
Copy the code

store/login/login.ts

import { Module } from 'vuex'

import { ILoginState } from './types'
import { IRootState } from '.. /types'

interface IAccount {
  name: string.password: string
}

// Module is a data type that Vuex provides to represent submodules
// Two generics need to be passed -- required
// The first generic - the return type of the current module's state
// The second generic-the return value type of the root state
const loginModule: Module<ILoginState, IRootState> = {
  namespaced: true.state() {
    return {
      token: ' '}},actions: {
    async accountLoginAction({ commit }, payload: IAccount) {
      / /... This is where the asynchronous request logic for the login is placed}}}export default loginModule
Copy the code

Page login logic

Main logic

import { defineStore } from 'pinia'
import Cookies from 'js-cookie'
import router from '/src/router/index'
import { login, getUserInfo, getMenu } from '.. /.. /.. /api/login'
import { IAccount, ILoginType, IUserInfo } from './types'
import { IResponseType } from '.. /.. /.. /types'

interface IState {
  token: string
  userInfo: IUserInfo | null
  menus: []}export default defineStore('loginStore', {
  state: (a) :IState= > ({
    token: ' '.userInfo: null.menus: []}),actions: {
    async login(account: IAccount) {
      // Log in to obtain the token
      const { data } = await login<IAccount, IResponseType<ILoginType>>(account)
      const { token, id } = data

      this.$state.token = token
      Cookies.set('token', token)
			
      // Get user information after login
      this.fetchUserInfo(id)
      
      // Get the menu list that can be operated by the user after login
      this.fetchMenu(id)
			
      // Return to home page
      router.push('/home')},async fetchUserInfo(id: number) {
      const { data } = await getUserInfo<IResponseType<IUserInfo>>(id)
      this.$state.userInfo = data
      Cookies.set('userInfo'.JSON.stringify(data))
    },

    // There are two ways to get the dynamic menu
    // 1. Use the user ID to go to the server to get the corresponding menu - the way used here
    // 2. Define all route objects locally, obtain all accessible route objects based on the user's role, and register them dynamically
    async fetchMenu(id: number) {
      const { data } = await getMenu(id)
      this.$state.menus = data
      Cookies.set('menus'.JSON.stringify(data))
    },

    // Whether a user has logged in after refreshing is determined by the data stored in storage or cookie
    // But because the user refreshes the interface, the state in vuex or Pinia is completely empty
    // It needs to be reset
    initStore() {
      const token = Cookies.get('token')
      const userInfo = Cookies.get('userInfo')
      const menus = Cookies.get('menus')

      if (token && userInfo && menus) {
        /* eslint-disable no-param-reassign */
        this.$patch(state= > {
          state.token = token
          state.userInfo = JSON.parse(userInfo ?? '{}')
          state.menus = JSON.parse(menus ?? '{}')})}}}})Copy the code

api.ts

 // Corresponding request API
 // The API imported here is actually an instance object of AXIos
 // So the corresponding get,post and other methods call the corresponding axios methods
 // The corresponding generics are also passed to Axios
 import api from '.. /utils/api'

 Record
      
        -- Generates a new object type, key of type K, value of type T
      ,>
 // Record
      
        -- equivalent to empty object
      ,>
 export function login<T.R> (account: T) {
   return api.post<T, R>('/login', account)
 }

 export function getUserInfo<R> (id: number) {
   return api.get<R>(`/users/${id}`)}export function getMenu<R> (id: number) {
   return api.get<R>(`/role/${id}/menu`)}Copy the code

./types.ts

export interface IAccount {
  name: string
  password: string
}

export interface ILoginType {
  id: number
  name: string
  token: string
}

// The data types returned in the background are generally complex
// It is recommended to use json to ts for direct conversion.
interface Role {
  id: number
  name: string
  intro: string
  createAt: Date
  updateAt: Date
}

interface Department {
  id: number
  name: string
  parentId: number
  createAt: Date
  updateAt: Date
  leader: string
}

export interface IUserInfo {
  id: number
  name: string
  realname: string
  cellphone: number
  enable: number
  createAt: Date
  updateAt: Date
  role: Role
  department: Department
}
Copy the code

/src/type.ts

// A custom background return value type
The return value type varies from request to request, so it is up to the caller to determine the specific type
export interface IResponseType<T> {
  code: number
  data: T
}
Copy the code

main.ts

  import { createApp } from 'vue'
  import { createPinia } from 'pinia'
  import router from './router'
  import App from './App.vue'

  import { useLoginStore } from './store/index'

  import 'normalize.css'

  const app = createApp(App)
  app.use(createPinia())
  app.use(router)

  // Call store.initstore () globally
  // to ensure that every time the user refreshes the page
  // The data in vuex or Pinia is effectively initialized
  const store = useLoginStore()
  store.initStore()

  app.mount('#app')
Copy the code