Let’s write a vue-naive-admin

I think we should put the tech stack in the front of the article

Vue3.2.4 + vite + Navie – UI + Spring Boot

Vue uses all puG templates and setup syntax sugar

The article introduction

When I tried the Setup syntax candy after releasing the update to Vue3.2, I fell in love with it. I always wanted to use it in normal development, but the company still used VUe2, which made me feel very helpless. So can I, on my own, build a back-end management system, so that the company can use it when writing new projects. So far today I have written the first version of my background. I called it The Righteous Indignation Backend Management System.

Background management system I think the most important is the three functions of user management, authority management, menu management (support to the secondary menu).

So I implemented these three functions

This article I wrote is just to broaden your thinking, not to teach you the technology of the article, I am not qualified. If you see a better implementation in my code, be sure to let me know in the comments.

Initialize a project using Vite

  1. Create a Vue + TS project with the name vue-naive-admin using the YARN directiveyarn create vite
  2. Install the Router for the projectyarn add vue-router@next
  3. Install vuex for the projectyarn add vuex@next
  4. Install AXIOS for the projectyarn add axios
  5. Install naive- UI for your projectyarn add navie-ui

Directory management

Tip: If you are having trouble with the following directories you can install scaffolding using YARN Add Haojiahuo and configure the command “feeldir” in package.json: “Haojiahuo feeldir” Enter YARN feeldir or NPM run feeldir to complete the initial creation. But this does not create naive directories. You still need to create your own.

Folder name role
api Holds API requests for each page
assets Static files
components component
interface All interfaces used are managed uniformly here
naive Registering naive Components
router routing
store vuex
styles Common style file
utils All the tools you use
views All pages

Write extension files for vuex, vue-Router, naive- UI

router

// src/router/index.ts

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: Array<RouteRecordRaw> = []

const router = createRouter({
  history: createWebHistory(),
  routes,
})


export default router

Copy the code

vuex

// src/store/index.ts
import { createStore } from 'vuex'


export default createStore<any> ([])Copy the code

naive

// src/naive/index.ts

import {create} from 'naive-ui'

const naive = create({
  components: [],})export default naive
Copy the code

main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import naive from './naive/index'


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

Copy the code

For common files, use vite and tsconfig to register the address abbreviation

vite.config.ts

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()], // Register the plug-in
  server: {
    open: true,},resolve: {
    alias: [{find: The '@'.replacement: path.resolve(__dirname, 'src'),}, {find: 'vv'.replacement: path.resolve(__dirname, 'src/views'),}, {find: 'api'.replacement: path.resolve(__dirname, 'src/api'),}, {find: 'cc'.replacement: path.resolve(__dirname, 'src/components'),},],},})Copy the code

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext"."useDefineForClassFields": true."module": "esnext"."moduleResolution": "node"."strict": true."jsx": "preserve"."sourceMap": true."resolveJsonModule": true."esModuleInterop": true."lib": ["esnext"."dom"]."baseUrl": ". /"."paths": {
      "@ / *": [
        "./src/*"]."cc/*": [
        "./src/components/*"]."vv/*": [
        "./src/views/*"]."api/*": [
        "./src/api/*"]}},"include": ["src/**/*.ts"."src/**/*.d.ts"."src/**/*.tsx"."src/**/*.vue"],}Copy the code

Summary of Construction Project

The construction of the project is over here, originally the construction of the project is really simple, but in order to write out the poor foundation of the students, follow the construction of a project catalog and understand, but also convenient to see the following article.

To do a project, we must first understand the requirements, and even know the requirements well. Next, we will conduct the analysis of the requirements.

Demand analysis

The login page

  1. Users can enter their accounts and passwords to log in
  2. Verify rules for user input information
  3. Save the token

Login page for your own thinking

  1. What information should I save after the user successfully logs in to facilitate my operation without disclosing the user’s information?
  2. Dynamic routing renders different side navigation bars according to different login users. How does it work with routing

User management

  1. You can select roles for users
  2. The super administrator modifies information about other users
  3. Super administrators can kick people offline
  4. The super administrator can block other users’ accounts
  5. The super administrator can delete users

Users manage their own thinking

  1. The super administrator can view information about all users
  2. When other users except the super administrator enter the user information page, we only show their account number, and can modify the information and remove the blocking function.

Role management

  1. Create roles with optional rights
  2. Modify or delete role information
  3. Access to the menu

Menu management

  1. Increase the menu
  2. Delete and modify menus

History TAB

  1. You can delete a label that displays a user’s browsing history

Designing database tables

The users table

The field name explain
user_name The user name
account_number account
password password
create_time Creation time
update_time Update time
role_id Character id
img_url The avatars
id The user id

Character sheet

The field name explain
id Character id
role_name Character name
create_time Creation time
update_time Update time
explain instructions
menu_id The id of the menu
promisition_id Authorization id

Permissions on the table

The field name explain
id Authorization id
promisition_name Permission to name
explain instructions

The menu list

The field name instructions
id Menu ids
name Menu Name Indicates the name of the router
title The menu title
component_path Component address
parent_id Id of the parent menu
path The route address is the same as the path of the router
iocn The name of the icon
redirect Redirect address
order Route display sequence

Now we can write our project

The background management system needs perfect information prompt

Naive UI provides us with two Message Notification components, Message and Notification, both of which are used to put the components to be used in their slots, for which we created a mount page to contain app.vue

<script lang="ts" setup>
import {} from "vue";
import App from "@/App.vue";
</script>

<template lang="pug">
n-notification-provider
  n-message-provider
    App
</template>

<style lang="scss" scoped></style>
Copy the code

Of course, to use n-notification-provider and n-message-provider components, we need to register in our naive files. We need to register in our naive files.

Naive tells us that we can use its useMessage and useNotification for informational prompt calls, and I guess he uses provide to assign the self-component method. So instead of repeating the message generation method, let’s save it in VUEX.

  1. Declare the vuex module in the Interface folder and create the messageModel interface file
  2. Modularize vuex to create a messageModel module
  3. Declare the methods used by messageModel in vuex
  4. Assign values to the messageModel module on the app. vue page
  5. Replace the vue entry file in main.ts

1. Declare the vuex module in the interface folder and create the messageModel interface file

// src/interface/vuex/message.ts
import { MessageProviderInst, NotificationProviderInst } from 'naive-ui'

export interface MessageModelState {
  useMessage: MessageProviderInst | null
  useNotification: NotificationProviderInst | null
}

Copy the code

Create messageModel module and 3. Declare the methods used by messageModel in vuex

// src/store/models/messageModel/index.ts

import { StoreOptions } from "vuex";
import { MessageModelState } from "@/interfaces/vuex/message";

const messageModel: StoreOptions<MessageModelState> = {
  state: {
    useMessage: null.useNotification: null,},mutations: {
    setUseMessage(state, value) {
      state.useMessage = value;
    },
    setUseNotification(state, value){ state.useNotification = value; }},actions: {}};export default messageModel

Copy the code
// src/store/models/index.ts

import { MessageModelState } from "@/interfaces/vuex/message";
import { StoreOptions } from "vuex";
import messageModel from "./messageModel";

interface StoresState extends MessageModelState {}

const stores: StoreOptions<StoresState> = {
  state: {
    ...(messageModel.state as MessageModelState),
  },
  mutations: {
    ...messageModel.mutations,
  },
  actions: {
    ...messageModel.actions,
  },
};

export default stores;

Copy the code
// src/store/index.ts

import { createStore } from 'vuex'
import stores from './models/index'

export default createStore(stores)

Copy the code

4. Assign values to the messageModel module on the app. vue page

// src/app.vue <script lang="ts" setup> import {} from 'vue' import { useMessage, UseNotification} from 'naive- UI 'import store from '@/store' useMessage()) store.commit('setUseNotification', useNotification()) </script> <template> <div class=""></div> </template> <style lang="scss" scoped></style>Copy the code

5. Replace the vue entry file in main.ts

import { createApp } from 'vue'
import mount from '@/views/mount/index.vue'- hereimport router from './router'
import store from './store'
import naive from './naive/index'


createApp(mount).use(naive).use(router).use(store).mount('#app')
Copy the code

The information prompt of the background management system completes the summary

Now we have attached the message prompt function to our project. Wherever we call the state written in VUEX, we can use the message prompt function. It’s very comfortable and saves a lot of code.

Encapsulates the request

A good request wrapper in a project can save us a lot of code work. This time, the request library we use is AXIos. Let’s wrap axiOS

What do you do before you look at the demand

  1. I want to display a success message from the back end every time the request completes, and not if it doesn’t
  2. I want the back end to know how important the information is, so the back end decides to use useMessage or useNotification
  3. 404 page jump (not done yet)
  4. Page jump at 403 (not done yet)

First of all, let’s just do a simple encapsulation, so we don’t have to spend time here

// src/utils/request/index.ts

import axios from 'axios'
import store from '@/store'

const request = axios.create({
  baseURL: ' ',
})

request.interceptors.request.use(
  (config) = > {
    return config
  },
  (err) = > {}
)

request.interceptors.response.use(
  (response) = > {
    const messageModel = response.data['messageModel']
    const message = response.data['msg']
    if (message === ' ') return
    let messageType = response.data['code'= = =200 ? 'success' : 'error'
    if (messageModel === 'lit') {
      if (messageType === 'success') { store.state.useMessage? .success(message) }else{ store.state.useMessage? .error(message) } }else if (messageModel === 'big') {
      if (messageType === 'success') { store.state.useNotification? .success({content: 'success'.meta: message || 'Operation successful'.duration: 3000})},else{ store.state.useNotification? .error({content: 'failure'.meta: message || 'failure'.duration: 3000,})}}},(err) = >{})export default request
Copy the code

Since I’m not a professional backend, there are some features I don’t use, so I use messageModel in the parameters returned in the request to determine which popover mode to use.

Start writing the login page

So this is where we can use requests, and typically requests we have a user experience on the front end where the user hits the login button and the login button is always loading until the request is finished.

After login, we also need to jump route, so we integrate methods for this jump route and button loading

  1. A unified interface to write requests
  2. Encapsulate login request
  3. The interface used to write the tool
  4. Tool for writing route jump and button loading status
  5. Use it on the login page

1. Unified interface for write requests

// src/interface/request/index.ts

export interface ResProps<T> {
  code: string | number;
  msg: string
  data: T
}
Copy the code

2. Encapsulate the login request

// src/api/user.ts

import { ResProps } from '@/interfaces/request'
import request from '@/utils/request'
import { AxiosPromise } from 'axios'

export const userLoginApi = (data: {
  password: string
  accountNumber: string
}): AxiosPromise<ResProps<{  token: string; id: string; userName: string} > > = > {return request({
    url: 'user/login'.method: 'GET'.params: data,
  })
}

Copy the code

3. Write the interface used by the tool

// src/interface/utils/tools.ts

import { Ref } from 'vue'
import { AxiosPromise } from 'axios'
import { ResProps } from '.. /request'

export interface UseLoadingRequestParameters<T> {
  loading: Ref<boolean> state? : Ref<T> axios: AxiosPromise<ResProps<T>> }Copy the code

4. Tool for writing route jump and button loading status

// src/utils/tools/index.ts

import router from '@/router'
import { RouteLocationRaw } from 'vue-router'
import { UseLoadingRequestParameters } from '@/interfaces/utils/tools'

export const useLoadingRequest = async <T>({ state, axios, loading, }: UseLoadingRequestParameters
       ) = > {
  loading.value = true
  const res = await axios
  if (state) {
    state.value = res.data.data
  }
  loading.value = false
  
  return res
}

export const routerPush = (options: RouteLocationRaw) = > {
  router.push(options)
}

export const routerReplace = (options: RouteLocationRaw) = > {
  router.replace(options)
}

Copy the code

5. Login to the login page

<script lang="ts" setup> import { ref } from 'vue' import { routerPush, useLoadingRequest } from '@/utils/tools/index' import { userLoginApi } from '@/api/login' const formValue = ref({ AccountNumber: ", password: ",}) const rules = ref({accountNumber: {required: true, message: 'Please enter account ', trigger: 'blur',}, password: {required: true, message: 'Please enter password ', trigger: 'blur', }, }) const formRef = ref(null) as any const loginLoading = ref(false) const submitForm = async () => { formRef.value.validate(async (err: any) => { if (! err) { const res = await useLoadingRequest<{ token: string; id: string; userName: string }>({ axios: userLoginApi(formValue.value), loading: loginLoading, }) if (res.data.data.token) { window.localStorage.setItem('token', res.data.data.token) window.localStorage.setItem( 'userInfo', JSON.stringify({ userName: res.data.data.userName, id: res.data.data.id, }) ) } if (res.data.code === 200) { routerPush({ path: '/', }) } } }) } </script> <template lang="pug"> div(class="bg") div(class="center") n-card(:content-style="{'paddingBottom':  '0'}") n-form( :model="formValue" :rules="rules" label-align="left" label-placement="left" ref="formRef") N - form - item (label = "account: "Path ="accountNumber") N-input (V-model :value="formValue.accountNumber" placeholder=" input account ") n-form-item(label=" password: "Path ="password") n-input(v-model:value="formValue. Password" placeholder=" input account "type="password") n-form-item() div(style="display: flex; justify-content: space-evenly; width: 100%;" </template> <style lang=" SCSS "style lang=" SCSS" scoped> .center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -30%); width: 400px; } .bg { background-image: url('.. /.. /assets/IMG_2176.JPG'); background-size: cover; width: 100vw; height: 100vh; } </style>Copy the code

I welcome your suggestions, and I’ll update the next chapter as soon as I finish.