• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

实现页面全局Loading进度条实现

武飞扬头像
龙崎流河
帮助1

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-属性

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>

这样就实现了进度条喽

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgegake
系列文章
更多 icon
同类精品
更多 icon
继续加载