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
- Create a Vue + TS project with the name vue-naive-admin using the YARN directive
yarn create vite
- Install the Router for the project
yarn add vue-router@next
- Install vuex for the project
yarn add vuex@next
- Install AXIOS for the project
yarn add axios
- Install naive- UI for your project
yarn 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
- Users can enter their accounts and passwords to log in
- Verify rules for user input information
- Save the token
Login page for your own thinking
- What information should I save after the user successfully logs in to facilitate my operation without disclosing the user’s information?
- Dynamic routing renders different side navigation bars according to different login users. How does it work with routing
User management
- You can select roles for users
- The super administrator modifies information about other users
- Super administrators can kick people offline
- The super administrator can block other users’ accounts
- The super administrator can delete users
Users manage their own thinking
- The super administrator can view information about all users
- 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
- Create roles with optional rights
- Modify or delete role information
- Access to the menu
Menu management
- Increase the menu
- Delete and modify menus
History TAB
- 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.
- Declare the vuex module in the Interface folder and create the messageModel interface file
- Modularize vuex to create a messageModel module
- Declare the methods used by messageModel in vuex
- Assign values to the messageModel module on the app. vue page
- 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
- I want to display a success message from the back end every time the request completes, and not if it doesn’t
- I want the back end to know how important the information is, so the back end decides to use useMessage or useNotification
- 404 page jump (not done yet)
- 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
- A unified interface to write requests
- Encapsulate login request
- The interface used to write the tool
- Tool for writing route jump and button loading status
- 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.