01、实现页面全局Loading进度条实现
1、第一种方式,直接找到index.html进行修改和添加
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<style>
.preloader {
background: #5838fc;
width: 100%;
height: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 9999;
}
.loaderInner {
width: 70px;
height: 60px;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
.mask {
position: absolute;
border-radius: 2px;
overflow: hidden;
-webkit-perspective: 1000;
perspective: 1000;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.plane {
background: #fff;
width: 400%;
height: 100%;
position: absolute;
-webkit-transform: translate3d(0px, 0, 0);
transform: translate3d(0px, 0, 0);
/*transition: all 0.8s ease; */
z-index: 100;
-webkit-perspective: 1000;
perspective: 1000;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.animation {
transition: all 0.3s ease;
}
#top .plane {
z-index: 2000;
-webkit-animation: trans1 1.3s ease-in infinite 0s backwards;
animation: trans1 1.3s ease-in infinite 0s backwards;
}
#middle .plane {
-webkit-transform: translate3d(0px, 0, 0);
transform: translate3d(0px, 0, 0);
background: #fff;
-webkit-animation: trans2 1.3s linear infinite 0.3s backwards;
animation: trans2 1.3s linear infinite 0.3s backwards;
}
#bottom .plane {
z-index: 2000;
-webkit-animation: trans3 1.3s ease-out infinite 0.7s backwards;
animation: trans3 1.3s ease-out infinite 0.7s backwards;
}
#top {
width: 53px;
height: 20px;
left: 20px;
-webkit-transform: skew(-15deg, 0);
transform: skew(-15deg, 0);
z-index: 100;
}
#middle {
width: 33px;
height: 20px;
left: 20px;
top: 15px;
-webkit-transform: skew(-15deg, 40deg);
transform: skew(-15deg, 40deg);
}
#bottom {
width: 53px;
height: 20px;
top: 30px;
-webkit-transform: skew(-15deg, 0);
transform: skew(-15deg, 0);
}
.preloader p {
color: #fff;
position: absolute;
left: -50px;
top: 60px;
text-align: center;
font-size: 11px;
font-weight: 600;
font-style: italic;
letter-spacing: .5px;
text-transform: uppercase;
margin: 0;
width: 200px;
}
@-webkit-keyframes trans1 {
from {
-webkit-transform: translate3d(53px, 0, 0);
transform: translate3d(53px, 0, 0);
}
to {
-webkit-transform: translate3d(-250px, 0, 0);
transform: translate3d(-250px, 0, 0);
}
}
@keyframes trans1 {
from {
-webkit-transform: translate3d(53px, 0, 0);
transform: translate3d(53px, 0, 0);
}
to {
-webkit-transform: translate3d(-250px, 0, 0);
transform: translate3d(-250px, 0, 0);
}
}
@-webkit-keyframes trans2 {
from {
-webkit-transform: translate3d(-160px, 0, 0);
transform: translate3d(-160px, 0, 0);
}
to {
-webkit-transform: translate3d(53px, 0, 0);
transform: translate3d(53px, 0, 0);
}
}
@keyframes trans2 {
from {
-webkit-transform: translate3d(-160px, 0, 0);
transform: translate3d(-160px, 0, 0);
}
to {
-webkit-transform: translate3d(53px, 0, 0);
transform: translate3d(53px, 0, 0);
}
}
@-webkit-keyframes trans3 {
from {
-webkit-transform: translate3d(53px, 0, 0);
transform: translate3d(53px, 0, 0);
}
to {
-webkit-transform: translate3d(-220px, 0, 0);
transform: translate3d(-220px, 0, 0);
}
}
@keyframes trans3 {
from {
-webkit-transform: translate3d(53px, 0, 0);
transform: translate3d(53px, 0, 0);
}
to {
-webkit-transform: translate3d(-220px, 0, 0);
transform: translate3d(-220px, 0, 0);
}
}
</style>
</head>
<body>
<div id="app">
<div class="preloader" style="/* display: none; */">
<div class="loaderInner">
<div id="top" class="mask">
<div class="plane"></div>
</div>
<div id="middle" class="mask">
<div class="plane"></div>
</div>
<div id="bottom" class="mask">
<div class="plane"></div>
</div>
<p>美好的事情即将发生,请耐心等待...</p>
</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
2:第二种方案:
https://www.npmjs.com 搜索 nprogress
安装
npm install nprogress
在main.js导入css
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from '@/App.vue'
import router from '@/router'
import store from './store'
import Loading from './plugins/PugLoading'
import 'virtual:windi.css'
import 'nprogress/nprogress.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(router)
app.use(store)
app.use(Loading)
app.use(ElementPlus)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
封装
在utils/index.js进行封装
import nprogress from 'nprogress'
export function showFullLoading() {
nprogress.start();
}
export function hideFullLoading() {
nprogress.done();
}
使用
import {
createRouter,
createWebHashHistory,
createWebHistory
} from 'vue-router'
import store from '@/store/index.js'
import { toast, showFullLoading, hideFullLoading } from '@/utils/utils'
const routes = [{
path: '/',
name: "index",
meta: { title: '首页' },
component: () =>
import ('@/views/index.vue')
},
{
path: '/about',
name: "about",
meta: { title: '关于我们' },
component: () =>
import ('@/views/about.vue')
},
{
path: '/login',
name: "login",
meta: { title: '登录' },
component: () =>
import ('@/views/login.vue')
},
{
path: '/404',
name: "404",
meta: { title: '404' },
component: () =>
import ('@/views/error-page/404.vue')
},
]
const router = createRouter({
linkActiveClass: "active",
//history: createWebHashHistory(),
history: createWebHistory(),
scrollBehavior(to, from, savedPosition) {
return { top: 0, left: 0 }
},
routes
})
// 前置防卫
router.beforeEach((to) => {
if (to.matched.length == 0) {
return { path: "/404" }
}
})
//4:判断目标路由是否是/login,如果是,则直接返回true
router.beforeEach((to, from, next) => {
showFullLoading()
// 判断是否已经登录
var isLogin = store.getters["login/isLogin"];
if (!isLogin && to.path != '/login') {
toast("请先登录", "error");
return next({ path: "/login" })
}
if (isLogin && to.path == "/login") {
toast("请不要重复登录", "error");
return next({ path: from.path ? from.path : "/" });
} else {
if (to.path != '/login') {
// 追加到到菜单
store.commit("menu/addMenu", {
title: to.meta.title,
path: to.path,
params: to.params,
active: true
});
}
return next();
}
});
// 设置标题
router.afterEach((to) => {
hideFullLoading();
document.title = to.meta.title;
})
export default router
效果
关于交互动画的使用
比如Login.vue的时候产生动画效果如下:
import { ref, reactive, onMounted } from 'vue'
import store from '@/store';
import router from '@/router';
import captchaService from '@/services/code/CaptchaService'
import { showFullLoading, hideFullLoading } from '@/utils'
// 定义类的方式封装
function UserLogin() {
//定义form表单对象,用于校验
const userFormRef = ref(null);
//定义验证码的图片地址
const captchaData = ref(null);
//2: 获取用户输入的form数据,是响应式的
const user = reactive({
username: '',
password: '',
code: '',
codeUuid: ""
})
//3: 登录提交表单
const handleLoginSubmit = () => {
// 1: 提交表单校验 校验表单输入
userFormRef.value.validate(async(valid) => {
// 如果为valid = false 还存在非法数据.
if (!valid) {
return;
}
try {
showFullLoading();
// 执行状态的异步请求
const res = await store.dispatch("user/toLogin", user);
// 跳转首页去
router.push("/")
} catch (err) {
if (err.field == "code") {
user.code = "";
document.getElementById("code").focus();
}
if (err.field == "password") {
user.password = "";
user.code = "";
document.getElementById("password").focus();
// 重新生成新的验证码
createCaptcha();
}
} finally {
hideFullLoading();
}
});
};
//4: 定义验证规则
const userLoginRules = reactive({
username: [
{ required: true, message: '请输入用户名', trigger: 'submit' },
{ min: 4, max: 20, message: '你的用户名必须是 4 to 20', trigger: 'submit' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'submit' },
{ min: 4, max: 20, message: '你的密码必须是 4 to 20', trigger: 'submit' }
],
code: [{
required: true,
message: '请输入验证码',
trigger: 'submit'
}]
});
// 5 : 生成验证码
const createCaptcha = async() => {
try {
var serverCode = await captchaService.createCaptcha();
captchaData.value = serverCode.data.img;
// 每次加载新的都把最新的uuid给用户登录对象
user.codeUuid = serverCode.data.codeToken;
} catch (err) {
}
};
// 6 : 生命周期初始化验证码
onMounted(() => {
// 如果已经登录过了。直接跳转到首页去
const isLogin = store.getters["user/isLogin"];
if (isLogin) {
router.push("/");
return;
}
// 执行创建验证码
createCaptcha();
// 定时每隔四分钟执行一次重新生成验证码
setInterval(createCaptcha, 4 * 60 * 1000);
});
// 暴露方法给页面使用
return {
userFormRef,
user,
userLoginRules,
handleLoginSubmit,
captchaData,
createCaptcha
}
}
// 5 : 导出
export default UserLogin;
按钮本身的动画效果
文档:https://element-plus.gitee.io/zh-CN/component/button.html#button-%E5%B1%9E%E6%80%A7
1: 定义
import { ref, reactive, onMounted } from 'vue'
import store from '@/store';
import router from '@/router';
import captchaService from '@/services/code/CaptchaService'
import { showFullLoading, hideFullLoading } from '@/utils'
// 定义类的方式封装
function UserLogin() {
//控制登录的按钮动画
const loading = ref(false);
//定义form表单对象,用于校验
const userFormRef = ref(null);
//定义验证码的图片地址
const captchaData = ref(null);
//2: 获取用户输入的form数据,是响应式的
const user = reactive({
username: '',
password: '',
code: '',
codeUuid: ""
})
//3: 登录提交表单
const handleLoginSubmit = () => {
// 1: 提交表单校验 校验表单输入
userFormRef.value.validate(async(valid) => {
// 如果为valid = false 还存在非法数据.
if (!valid) {
return;
}
try {
showFullLoading();
loading.value = true;
// 执行状态的异步请求
const res = await store.dispatch("user/toLogin", user);
// 跳转首页去
router.push("/")
} catch (err) {
if (err.field == "code") {
user.code = "";
document.getElementById("code").focus();
}
if (err.field == "password") {
user.password = "";
user.code = "";
document.getElementById("password").focus();
// 重新生成新的验证码
createCaptcha();
}
} finally {
loading.value = false;
hideFullLoading();
}
});
};
//4: 定义验证规则
const userLoginRules = reactive({
username: [
{ required: true, message: '请输入用户名', trigger: 'submit' },
{ min: 4, max: 20, message: '你的用户名必须是 4 to 20', trigger: 'submit' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'submit' },
{ min: 4, max: 20, message: '你的密码必须是 4 to 20', trigger: 'submit' }
],
code: [{
required: true,
message: '请输入验证码',
trigger: 'submit'
}]
});
// 5 : 生成验证码
const createCaptcha = async() => {
try {
var serverCode = await captchaService.createCaptcha();
captchaData.value = serverCode.data.img;
// 每次加载新的都把最新的uuid给用户登录对象
user.codeUuid = serverCode.data.codeToken;
} catch (err) {
}
};
// 6 : 生命周期初始化验证码
onMounted(() => {
// 如果已经登录过了。直接跳转到首页去
//const isLogin = store.getters["user/isLogin"];
//if (isLogin) {
// router.push("/");
// return;
//}
// 执行创建验证码
createCaptcha();
// 定时每隔四分钟执行一次重新生成验证码
setInterval(createCaptcha, 4 * 60 * 1000);
});
// 暴露方法给页面使用
return {
userFormRef,
user,
userLoginRules,
handleLoginSubmit,
loading,
captchaData,
createCaptcha
}
}
// 5 : 导出
export default UserLogin;
- 注意点1:响应属性记得返回,loading记得返回
// 把需要暴露的方法和相应属性全部导入
const {
userFormRef,
user,
loading, // 按钮动画
userLoginRules,
handleLoginSubmit,
createCaptcha,
captchaData
} = useLogin();
</script>
- 注意点2:响应属性记得导出时候,申明出来
使用
<el-button type="primary" class="w-[250px]" :loading="loading" @click="handleLoginSubmit">登录</el-button>
这样就实现了进度条喽