1.路由配置
1.1路由组件的雏形
src\views\home\index.vue
(以home组件为例)
安装插件:
1.2路由配置
1.2.1路由index文件
src\router\index.ts
//通过vue-router插件实现模板路由配置
import { createRouter, createWebHashHistory } from 'vue-router'
import { constantRoute } from './router'
//创建路由器
const router = createRouter({
//路由模式hash
history: createWebHashHistory(),
routes: constantRoute,
//滚动行为
scrollBehavior() {
return {
left: 0,
top: 0,
}
},
})
export default router
1.2.2路由配置
src\router\router.ts
//对外暴露配置路由(常量路由)
export const constantRoute = [
{
//登录路由
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'login', //命名路由
},
{
//登录成功以后展示数据的路由
path: '/',
component: () => import('@/views/home/index.vue'),
name: 'layout',
},
{
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404',
},
{
//重定向
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any',
},
]
1.3路由出口
src\App.vue
2.登录模块
2.1 登录路由静态组件(布局)
src\views\login\index.vue
<template><div class="login_container"><el-row><el-col :span="12" :xs="0"></el-col><el-col :span="12" :xs="24"><el-form class="login_form"><h1>Hello</h1><h2>欢迎来到硅谷甄选</h2><el-form-item><el-input:prefix-icon="User"v-model="loginForm.username"></el-input></el-form-item><el-form-item><el-inputtype="password":prefix-icon="Lock"v-model="loginForm.password"show-password></el-input></el-form-item><el-form-item><el-button class="login_btn" type="primary" size="default">登录</el-button></el-form-item></el-form></el-col></el-row></div>
</template><script setup lang="ts">import { User, Lock } from '@element-plus/icons-vue'import { reactive } from 'vue'//收集账号与密码数据let loginForm = reactive({ username: 'admin', password: '111111' })
</script><style lang="scss" scoped>.login_container {width: 100%;height: 100vh;background: url('@/assets/images/background.jpg') no-repeat;background-size: cover;.login_form {position: relative;width: 80%;top: 30vh;background: url('@/assets/images/login_form.png') no-repeat;background-size: cover;padding: 40px;h1 {color: white;font-size: 40px;}h2 {color: white;font-size: 20px;margin: 20px 0px;}.login_btn {width: 100%;}}}
</style>
注意:
- el-col是24份的,在此左右分为了12份。我们在右边放置我们的结构。
:xs="0"
是为了响应式。el-form
下的element-plus元素都用el-form-item
包裹起来。- 通过
row
(行)和col
(列)组件,并通过 col 组件的span
属性我们就可以自由地组合布局。row
行提供gutter
属性来指定列之间的间距,其默认值为0。- 通过制定 col 组件的
offset
属性可以指定分栏偏移的栏数。- 参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xs、sm、md、lg 和 xl。
2.2 登陆业务实现
2.2.1 登录按钮绑定回调
回调应该做的事情
const login = () => {
//点击登录按钮以后干什么
//通知仓库发起请求
//请求成功->路由跳转
//请求失败->弹出登陆失败信息
}
2.2.2 仓库store初始化
- 大仓库(笔记只写一次)
安装pinia:pnpm i pinia@2.0.34
src\store\index.ts
//创建用户相关的小仓库
import { defineStore } from 'pinia'
//创建用户小仓库
const useUserStore = defineStore('User', {
//小仓库存储数据地方
state: () => {},
//处理异步|逻辑地方
actions: {},
getters: {},
})
//对外暴露小仓库
export default useUserStore
2.2.3 按钮回调
//登录按钮的回调
const login = async () => {
//按钮加载效果
loading.value = true
//点击登录按钮以后干什么
//通知仓库发起请求
//请求成功->路由跳转
//请求失败->弹出登陆失败信息
try {
//也可以书写.then语法
await useStore.userLogin(loginForm)
//编程式导航跳转到展示数据的首页
$router.push('/')
//登录成功的提示信息
ElNotification({
type: 'success',
message: '登录成功!',
})
//登录成功,加载效果也消失
loading.value = false
} catch (error) {
//登陆失败加载效果消失
loading.value = false
//登录失败的提示信息
ElNotification({
type: 'error',
message: (error as Error).message,
})
}
}
2.2.4 用户仓库
//创建用户相关的小仓库
import {defineStore} from "pinia";
// 引入接口
import {reqLogin} from "../../api/user";
//引入数据类型
import type {loginForm} from '@/api/user/type'
//创建用户小仓库
const useUserStore = defineStore('User', {
小仓库存储数据地方
state: () => {
return {
token: localStorage.getItem('TOKEN')//用户的唯一标识
}
},
//处理异步|逻辑地方
actions: {
async userLogin(data: loginForm) {
//登陆的请求
async
userLogin(data
:
loginForm
)
{
//登陆的请求
const result: any = await reqLogin(data)
if (result.code == 200) {
this.token = result.data.token
localStorage.setItem('TOKEN', result.data.token)
return 'ok'
} else {
return Promise.reject(new Error(result.data.message))
}
}
}
},
getters: {},
})
export default useUserStore
2.2.5 小结
- Element-plus中ElNotification用法(弹窗):
引入:import { ElNotification } from 'element-plus'
使用:
//登录失败的提示信息
ElNotification({
type: 'error',
message: (error as Error).message,
})
- Element-plus中el-button的loading属性。
- pinia使用actions、state的方式和vuex不同:需要引入函数和创建实例
- $router的使用:也需要引入函数和创建实例
- 在actions中使用state的token数据:this.token
- 类型定义需要注意。
- promise的使用和vue2现在看来是一样的
2.3模板封装登陆业务
2.3.1 result返回类型封装
src\api\user\type.ts
interface dataType {
token?: string
message?: string
}//登录接口返回的数据类型
export interface loginResponseData {
code: number
data: dataType
}
2.3.2 State仓库类型封装
src\store\modules\types\type.ts
//定义小仓库数据state类型
export interface UserState {
token: string | null
}
2.3.3 本地存储封装
将本地存储的方法封装到一起
src\utils\token.ts
//封装本地存储存储数据与读取数据方法
export const SET_TOKEN = (token: string) => {
localStorage.setItem('TOKEN', token)
}export const GET_TOKEN = () => {
return localStorage.getItem('TOKEN')
}
2.4 登录时间的判断
- 封装函数
src\utils\time.ts
//封装函数:获取当前时间段
export const getTime = () => {
let message = ''
//通过内置构造函数Date
const hour = new Date().getHours()
if (hour <= 9) {
message = '早上'
} else if (hour <= 14) {
message = '上午'
} else if (hour <= 18) {
message = '下午'
} else {
message = '晚上'
}
return message
}
2.5 表单校验规则
2.5.1 表单校验
- 表单绑定项
- 表单元素绑定项
Form 组件提供了表单验证的功能,只需为 rules 属性传入约定的验证规则,并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可
- 使用规则rules
//定义表单校验需要的配置对象
const rules = {
username: [
//规则对象属性:
{
required: true, // required,代表这个字段务必要校验的
min: 5, //min:文本长度至少多少位
max: 10, // max:文本长度最多多少位
message: '长度应为6-10位', // message:错误的提示信息
trigger: 'change', //trigger:触发校验表单的时机 change->文本发生变化触发校验, blur:失去焦点的时候触发校验规则
},
],
password: [
{
required: true,
min: 6,
max: 10,
message: '长度应为6-15位',
trigger: 'change',
},
],
}
- 校验规则通过后运行
const login = async () => {
//保证全部表单项校验通过
await loginForms.value.validate()
。。。。。。
}
2.5.2自定义表单校验
- 修改使用规则rules
使用自己编写的函数作为规则校验。
//定义表单校验需要的配置对象
const rules = {
username: [
//规则对象属性:
/* {
required: true, // required,代表这个字段务必要校验的
min: 5, //min:文本长度至少多少位
max: 10, // max:文本长度最多多少位
message: '长度应为6-10位', // message:错误的提示信息
trigger: 'change', //trigger:触发校验表单的时机 change->文本发生变化触发校验, blur:失去焦点的时候触发校验规则
}, */
{ trigger: 'change', validator: validatorUserName },
],
password: [
{ trigger: 'change', validator: validatorPassword },
],
}
- 自定义校验规则函数
//自定义校验规则函数
const validatorUserName = (rule: any, value: any, callback: any) => {
//rule:校验规则对象
//value:表单元素文本内容
//callback:符合条件,callback放行通过,不符合:注入错误提示信息
if (value.length >= 5) {
callback()
} else {
callback(new Error('账号长度至少5位'))
}
}const validatorPassword = (rule: any, value: any, callback: any) => {
if (value.length >= 6) {
callback()
} else {
callback(new Error('密码长度至少6位'))
}
}
3. Layout模块(主界面)
3.1 组件的静态页面
3.1.1 组件的静态页面
注意:我们将主界面单独放一个文件夹(顶替原来的home路由组件)。注意修改一下路由配置
src\layout\index.vue
<template>
<div class="layout_container">
<!-- 左侧菜单 -->
<div class="layout_slider"></div>
<!-- 顶部导航 -->
<div class="layout_tabbar"></div>
<!-- 内容展示区域 -->
<div class="layout_main">
<p style="height: 1000000px"></p>
</div>
</div>
</template><script setup lang="ts"></script>
<style lang="scss" scoped>
.layout_container {
width: 100%;
height: 100vh;
.layout_slider {
width: $base-menu-width;
height: 100vh;
background: $base-menu-background;
}
.layout_tabbar {
position: fixed;
width: calc(100% - $base-menu-width);
height: $base-tabbar-height;
background: cyan;
top: 0;
left: $base-menu-width;
}
.layout_main {
position: absolute;
width: calc(100% - $base-menu-width);
height: calc(100vh - $base-tabbar-height);
background-color: yellowgreen;
left: $base-menu-width;
top: $base-tabbar-height;
padding: 20px;
overflow: auto;
}
}
</style>
3.1.2定义部分全局变量&滚动条
scss全局变量
src\styles\variable.scss
//左侧菜单宽度
$base-menu-width :260px;
//左侧菜单背景颜色
$base-menu-background: #001529;//顶部导航的高度
$base-tabbar-height:50px;
滚动条
src\styles\index.scss
//滚动条外观设置
::-webkit-scrollbar{
width: 10px;
}::-webkit-scrollbar-track{
background: $base-menu-background;
}::-webkit-scrollbar-thumb{
width: 10px;
background-color: yellowgreen;
border-radius: 10px;
}
3.2 Logo子组件的搭建
页面左上角的这部分,我们将它做成子组件,并且封装方便维护以及修改。
3.2.1 Logo子组件
在这里我们引用了封装好的setting
src\layout\logo\index.vue
<template>
<div class="logo" v-if="setting.logoHidden">
<img :src="setting.logo" alt="" />
<p>{{ setting.title }}</p>
</div>
</template><script setup lang="ts">
//引入设置标题与logo配置文件
import setting from '@/setting'
</script><style lang="scss" scoped>
.logo {
width: 100%;
height: $base-menu-logo-height;
color: white;
display: flex;
align-items: center;
padding: 20px;
img {
width: 40px;
height: 40px;
}
p {
font-size: $base-logo-title-fontSize;
margin-left: 10px;
}
}
</style>
3.2.2 封装setting
为了方便我们以后对logo以及标题的修改。
//用于项目logo|标题配置
export default {
title: '硅谷甄选运营平台', //项目的标题
logo: '/public/logo.png', //项目logo设置
logoHidden: true, //logo组件是否隐藏
}
3.2.3 使用
在layout组件中引入并使用
3.3 左侧菜单组件
3.3.1静态页面(未封装)
主要使用到了element-plus的menu组件。附带使用了滚动组件
src\layout\index.vue
<!-- 左侧菜单 -->
<div class="layout_slider">
<Logo></Logo>
<!-- 展示菜单 -->
<!-- 滚动组件 -->
<el-scrollbar class="scrollbar">
<!-- 菜单组件 -->
<el-menu background-color="#001529" text-color="white">
<el-menu-item index="1">首页</el-menu-item>
<el-menu-item index="2">数据大屏</el-menu-item>
<!-- 折叠菜单 -->
<el-sub-menu index="3">
<template #title>
<span>权限管理</span>
</template>
<el-menu-item index="3-1">用户管理</el-menu-item>
<el-menu-item index="3-2">角色管理</el-menu-item>
<el-menu-item index="3-3">菜单管理</el-menu-item>
</el-sub-menu>
</el-menu>
</el-scrollbar>
</div>
3.3.2 递归组件生成动态菜单
在这一部分,我们要根据路由生成左侧的菜单栏
- 动态菜单子组件:src\layout\menu\index.vue
- 根据路由生成左侧的菜单栏
- 处理路由--->因为我们要根据路由以及其子路由作为我们菜单的一级|二级标题。因此我们要获取路由信息。
给路由中加入了路由元信息meta:它包含了2个属性:title以及hidden
4.仓库引入路由并对路由信息类型声明(vue-router有对应函数)
5. 父组件拿到仓库路由信息并传递给子组件
6.子组件prps接收并且处理结构
注意:
1:因为每一个项我们要判断俩次(是否要隐藏,以及子组件个数),所以在el-menu-item外面又套了一层模板
2:当子路由个数大于等于一个时,并且或许子路由还有后代路由时。这里我们使用了递归组件。递归组件需要命名(另外使用一个script标签,vue2格式)。