通用权限管理系统+vue3项目实战(一)

1.创建项目

在某个工程文件夹下创建项目

npm init vue@latest

各种工具选择都选是,并且安装环境node_modules之后,显示如图:
通用权限管理系统+vue3项目实战(一)_第1张图片

1.1 引入element-plus

在vue2的时候常用的ui框架是element-ui,在vue3时应该使用它的继承者element-plus。
同时,为保证能自动引入插件,可以使用插件unplugin-vue-components和unplugin-auto-import。
接下来下载插件。

npm install element-plus --save
npm install -D unplugin-vue-components unplugin-auto-import

在配置文件中修改成以下配置:

import Components from "unplugin-vue-components/vite"
const { ElementPlusResolver } = require("unplugin-vue-components/resolvers")
import AutoImport from 'unplugin-auto-import/vite'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),
  vueJsx(), 
  AutoImport({ resolvers: [ElementPlusResolver()] }), 
  Components({ resolvers: [ElementPlusResolver()] })],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

1.1.1 引入Element Plus图标集

由于本项目还使用了Element Plus的图标,像左侧导航的图标是根据后台数据配置确定的,因此使用什么图标是不确定的。

npm install @element-plus/icons-vue
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

1.2 安装SCSS依赖

npm install -D sass

在package.json的文件,如图所示:
通用权限管理系统+vue3项目实战(一)_第2张图片

1.3 安装vue-tools

下载地址:https://gitcode.net/mirrors/vuejs/devtools?utm_source=csdn_github_accelerator
下载解压之后,执行以下命令

# 如果没有安装yarn的话  
# 不知道自己是否安装 可以通过 yarn -v 查看一下
# 安装Vue-Devtools的依赖需要用到yarn,而不是npm,所以首先我们要安装yarn。命令行进入到解压后的Vue-Devtools目录。
npm install -g yarn   
yarn install
yarn run build:watch
yarn run dev:chrome

看到本界面,说明安装成功了。之后Ctrl+C结束命令框。
通用权限管理系统+vue3项目实战(一)_第3张图片
之后,在google浏览器开启开发者模式,加载devtools-6.5.0\packages下的shell-chrome文件夹即可。通用权限管理系统+vue3项目实战(一)_第4张图片

2. 添加页面路由

在views目录下新建Login.vue 404.vue Home.vue页面等3个简单页面

<template>
<div class='page'>
    <h2>登录页h2>
div>
template>

在router/index.ts中

import Login from '@/views/Login.vue'
import Home from '@/views/Home.vue'
import NotFound from '@/views/404.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    },
    {
      path: '/404',
      name: 'NotFound',
      component: NotFound
    },
  ]
})

运行后,如图所示:
通用权限管理系统+vue3项目实战(一)_第5张图片

2.1 改进页面路由

将router/index.ts文件修改成以下代码:

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/404',
    name: 'NotFound',
    component: NotFound
  },
]
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

3. 设计页面布局

3.1 修改App.vue

<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import zhCn from 'element-plus/dist/locale/zh-cn'
</script>
<template>
  <el-config-provider :locale="zhCn">
    <RouterView />
  </el-config-provider>
</template>

3.2 布局文件

在src/views/layout下创建index.vue文件,用于存放总体布局。

<template>
    <div class="app-wrapper">
        <page-header class="app-header"></page-header>
        <div class="app-container">
            <page-sidebar class="sidebar-container"></page-sidebar>
            <div class="main-wrapper">
                <main-content></main-content>
            </div>
        </div>
    </div>
</template>

<script>
import PageHeader from '@/views/layout/components/PageHeader.vue'
import PageSidebar from '@/views/layout/components/PageSidebar.vue'
import MainContent from '@/views/layout/components/MainContent.vue'
export default {
    name: "layout",
    components: {
        PageHeader,
        PageSidebar,
        MainContent,
    },
}
</script>

Wrapper(整体布局):包裹整个页面或页面的一部分,通常用于设置背景、边框等样式。
Container(容器):包裹页面的主要内容,通常用于设置宽度、居中等样式。
Content(内容):包裹页面的具体内容,通常用于设置字体、颜色等样式。
Main(主要内容):包裹页面的主要内容,通常用于设置页面的主要样式。

在在src/views/layout/compnents下创建各布局组件。文件,用于存放总体布局。

//PageHeader.vue
<template>
    <div class="app-wrapper">
        首栏
    </div>
</template>
//PageSidebar.vue
<template>
    <div>
        侧边栏
    </div>
</template>
//MainContent.vue
<template>
    <div>
        <router-view></router-view>
    </div>
</template>

在router文件中,修改代码将布局Layout应该到界面中。我们可以在views/personal下创建个人中心页面文件index.vue,将其设置为Layout的子路由。

import Layout from '@/views/layout/index.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Layout,
    children: [
      {
        path: 'personal',
        name: 'personal',
        component: () => import('@/views/personal/index.vue')
      },
    ]
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/404',
    name: 'NotFound',
    component: NotFound
  },
]

如上所示,其实布局Layout.vue、Login登录页和404错误页都挂载到App.vue的router-view标签中。而子路由children挂载到MainContent.vue的router-view标签中,它们都应用了Layout的布局。

再根据需要调整CSS样式,访问http://localhost:5173/personal 的链接,最终设计的布局如图:
通用权限管理系统+vue3项目实战(一)_第6张图片

4.完善登录流程

登录流程分析:
(1)登录表单需要将用户信息提交到后台进行校验处理,所以这里涉及发送请求axios的操作,为便于重用代码,axios需要重新封装。
(2)在前后端分离项目中,为追求效率,可使用mockjs进行模拟数据的测试。
(3)当请求发送成功时,需要保留登录状态,登录状态将在整个项目中使用
(4)任何操作都需要在已登录状态下完成,这样才能保证用户数据或网站数据的安全
(5)如果登录状态失效,则需要更新状态并退出系统,用户需要重新登录,才可以重新执行相应操作,所以需要考虑状态管理。

4.1 封装axios

4.1.1 安装axios

先安装axios插件

npm install axios

在package.json的文件,如图所示:
通用权限管理系统+vue3项目实战(一)_第7张图片

4.1.2 设置配置文件

首先,后台的返回规则有一个通用的规则,前端的请求也有统一的规则,所以可以考虑设置一个通用的配置文件。

在src/http下创建一个config.js文件,将Axios通用配置写入文件中。

export default {
  method: 'get',
  // 基础url前缀
  baseUrl: 'http://localhost:8001',
  // 请求头信息
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  },
  // 参数
  data: {},
  // 设置超时时间
  timeout: 10000,
  // 携带凭证
  withCredentials: true,
  // 返回数据类型
  responseType: 'json'
}

4.1.3 设置拦截器

现在 统一API请求,并设置拦截器。
在src/http下创建一个request.js文件,引入Axios,再引入上一步创建的config.js文件,然后创建一个request方法,返回Promise,并导出这个方法,以便在其他文件中使用。在这个方法中通过axios.create创建一个Axios实例,代码如下:

import axios from 'axios'
import config from './config'

export default function request(options) {
    return new Promise((resolve, reject) => {
        //创建axios实例
        const instance = axios.create({
            baseURL: config.baseUrl,
            headers: config.headers,
            timeout: config.timeout,
            withCredentials: config.withCredentials
        })
        // request 请求拦截器
        instance.interceptors.request.use(
            config => {
                // todo : 请求必须携带身份信息的逻辑
                return config
            },
            error => {
                console.log(error) // 请求发生错误时,通过控制台查看报错的逻辑
                return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
            }
        )

        // response 响应拦截器
        instance.interceptors.response.use(
            response => {
                //返回响应的逻辑
                const res = response.data
				if (res.errcode !== "00000") {
					console.log("错误")
				}                
                return response.data
            },
            err => {
                //todo: 返回响应出现错误的逻辑              
                console.error(err)
                return Promise.reject(err) // 返回接口返回的错误信息
            }
        )
        // 请求处理
        instance(options).then(res => {
            resolve(res)
            return false
        }).catch(error => {
            reject(error)
        })
    })
}

分析:
(1)请求方式正确时,应该校验访问者的token信息,token信息校验正确则进入访问页面,若校验错误则重定向到登录页面。
(2)请求方式错误时,应该提示报错信息。
(3)返回正确的响应时,不做处理
(4)返回错误的响应时,应该提示报错信息。

携带的token信息跟实现机制有关,可能由cookie,localStorage,vuex等实现。

提示的报错信息跟使用的ui框架有关,应该使用ui框架提供的消息提示工具,同时在console也要提示报错。

正确返回结果时,统一返回格式为JSON,包含3个属性:errcode、errmsg和data。code表示成功标识,为00000时表示成功,成功时通常会带回数据data,如果不是00000,则为失败,需要读取提示信息。

{
	errcode: "00000",
	errmsg: "success",
	data: {
		//....
	}
}

4.1.4 统一管理请求

为了统一管理请求,在src下创建api文件夹,用于存放各模块的远程请求方法。
例如,在api文件夹中创建login.js文件,放入以下请求。

import request from '@/http/request'

export function login(username, password) {
  return request({
    url: '/api/ums/admin/login',
    method: 'post',
    data: {
      username,
      password
    }
  })
}

该方法调用axios封装方法request来发送请求,当请求返回登录成功时,正常情况下会带回一个登录标识token,在处理请求返回时,将这个token存到本地缓存localStorage中,然后跳转到系统首页。

4.2 封装Mock.js

为了能看到登录效果,要么依赖后台功能正常返回,要么考虑前端模拟后端返回。
先安装 mockjs插件。

npm install -D  mockjs 

4.2.1 编写模拟数据

和Axios请求模拟一样,数据的模拟也区分不同模块,因此继续在src/mock下创建一个modules文件夹,用于存放不同模块的模拟函数。并在modules文件夹下创建login.js文件。

export function login(){
    return {
        url : "login",
        type: "post",
        data: {
            errcode : "00000",
            errmsg : "success",
            data : {
                token : "abc123456789",
                username : "admin"
            }
        }
    }
}

4.2.2 封装所有模块

在src目录下新建一个mock目录,创建index.js。在index.js文件中引入各模块文件

import Mock from 'mockjs'
import * as login from './modules/login.js'

// 开启/关闭模块的拦截
const openMock = true
const baseUrl = "http://localhost:8001"

//模拟所有模块
mockAll([login],openMock)
function mockAll(modules,isOpen = true){
    for (const k in modules){
        mock(modules[k],isOpen)
    }
}
function mock(mod,isOpen = true){
    if(isOpen){
        for(var key in mod){
            ((res)=>{
                let url = baseUrl
                if(!url.endsWith("/")){
                    url = url +"/"
                }
                url = url + res.url
                Mock.mock(new RegExp(url),res.type,(opts) => {
                    opts['data'] = opts.body ? JSON.parse(opts.body) :null
                    delete opts.body
                    console.log('\n')
                    console.log('%cmock拦截,请求:','color:blue',opts)
                    console.log('%cmock拦截,请求:','color:blue',res.data)
                    return res.data
                })
            })(mod[key]()|| {})
        }
    }
}

该代码统一管理所有模块的模拟数据。首先,通过openMock统一管理所有模块模拟数据的开启/关闭。
然后url必须拼接baseUrl且以“/”结尾,各模块的url不能以“/”开头。
至此,Mock.js封装完成。只需要在入口文件src/main.js中引入上面的入口文件即可,即修改main.js如下(注意加粗部分):

import './assets/main.css'
import './mock'

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

import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }

app.use(createPinia())
app.use(router)

app.mount('#app')

4.2.3 使用mockjs

通过axios访问对应的api,得到如下结果

 axios.get('http://localhost:8001/api/ums/admin/info',{
     params :{
         pageNum:1,
         pageSize:20
     }
 }).then(res => { // url即在mock.js中定义的
     console.log(res) // 打印一下响应数据
 })

通用权限管理系统+vue3项目实战(一)_第8张图片

4.引入状态管理库Pinia及管理登录状态

若未安装pinia,则先引入

npm install pinia

确保在main.js文件中应用了pinia

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

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

4.1 设置用户登录状态

在src/stores目录下新建user.ts文件,在管理用户登录状态时,需要管理用户的token、用户名、权限等。

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
    state: () => ({
       token: '',//token
       username:'', //用户名
       avatar: '', // 用户头像
       roles: []  //权限
      }),
    getters: {
      getToken: (state) => state.token,
    },
    actions: {
		login() {
		},
		logout() { },
		setAvatar(){},
		setRoles(){}
    },
  })

用户登录设计思路:
(1)用户在页面表单填写账号密码后,将数据传递给状态管理库,原因是状态管理需要保存用户信息。
(2)由状态管理访问api,接受返回的数据。

import { useUserStore } from '@/stores/user'

methods: {
    handleLogin() {
        const store = useUserStore()
        store.login()
    }
},

你可能感兴趣的:(前端技术,vue.js,javascript,ecmascript)