1.创建项目
在某个工程文件夹下创建项目
npm init vue@latest
各种工具选择都选是,并且安装环境node_modules之后,显示如图:
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的文件,如图所示:
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
},
]
})
运行后,如图所示:
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 的链接,最终设计的布局如图:
4.完善登录流程
登录流程分析:
(1)登录表单需要将用户信息提交到后台进行校验处理,所以这里涉及发送请求axios的操作,为便于重用代码,axios需要重新封装。
(2)在前后端分离项目中,为追求效率,可使用mockjs进行模拟数据的测试。
(3)当请求发送成功时,需要保留登录状态,登录状态将在整个项目中使用
(4)任何操作都需要在已登录状态下完成,这样才能保证用户数据或网站数据的安全
(5)如果登录状态失效,则需要更新状态并退出系统,用户需要重新登录,才可以重新执行相应操作,所以需要考虑状态管理。
4.1 封装axios
4.1.1 安装axios
先安装axios插件
npm install axios
在package.json的文件,如图所示:
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中,然后跳转到系统首页。
3.封装Mock.js
为了能看到登录效果,要么依赖后台功能正常返回,要么考虑前端模拟后端返回。
先安装 mockjs插件。
npm install -D mockjs
在src目录下新建一个mock目录,创建index.js。
和Axios请求模拟一样,数据的模拟也区分不同模块,因此继续在src/mock下创建一个modules文件夹,用于存放不同模块的模拟函数。并在modules文件夹下创建login.js文件。
import Mock from 'mockjs'
Mock.mock('http://localhost:8001/login',{
errcode: '00000',
errmsg: 'success',
data: {
token : 'abc123456'
}
})
export function login(){
return {
url : "login",
type: "post",
data: {
errcode : "00000",
errmsg : "success",
data : {
token : "abc123456789",
username : "admin"
}
}
}
}
在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]()|| {})
}
}
}
至此,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.引入状态管理库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')
设置用户登录状态
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
name:'',
avatar: '',
roles: []
}),
getters: {
getToken: (state) => state.token,
},
actions: {
setToken() {
},
setName(){},
setAvatar(){},
setRoles(){}
},
})
用户登录设计思路:
(1)用户在页面表单填写账号密码后,将数据传递给状态管理库,原因是状态管理需要保存用户信息。
(2)由状态管理访问api,接受返回的数据。