23-完成登录页面跳转加载路由规则

第一次登录后首页空白

解决:第一次登录,动态添加路由规则

ts 复制代码
import {
    createRouter,
    createWebHashHistory,
    type RouteRecordRaw
} from 'vue-router'
import { type App } from 'vue'
import store from '../store'
import Cookie from 'js-cookie'

const routes: RouteRecordRaw[] = [
    {
        path: '/login',
        name: 'login',
        component: () => import('../views/login/login.vue')
    },
    // {
    //     path: '/home',
    //     name: 'home',
    //     component: () => import('../views/home/home.vue')
    // },
    // 动态生成的路由配置结构需要如下
    // {
    //     path: '/pms',
    //     name: 'pms',
    //     component: () => import('../views/home/home.vue'),
    //     children: [
    //         {
    //             path: 'product',
    //             name: 'product',
    //             component: () => import('../views/pms/product.vue'),
    //         }
    //     ]
    // }
]

const router = createRouter({
    history: createWebHashHistory(),
    routes // 路由配置
})

// 根据 getters 里面的菜单生成路由规则
const genRoutes = () => {
    const menus = store.getters.getNewMenus;
    console.log('menus--->', menus)
    // const newRoutes:RouteRecordRaw[] = []
    // 循环菜单对象
    for(let key in menus) {
        const newRoute: RouteRecordRaw = {
            path: `/${menus[key].name}`,
            name: menus[key].name,
            component: () => import(`../views/home/home.vue`),
            redirect: `/${menus[key].name}/${menus[key].children[0].name}`,
            children: []
        }
        for(let i = 0; i < menus[key].children.length; i++) {
            newRoute.children?.push({
                path: menus[key].children[i].name,
                name: menus[key].children[i].name,
                component: () => import(`../views/${menus[key].name}/${menus[key].children[i].name}.vue`),
            })
        }
        // 动态添加路由规则
        router.addRoute(newRoute)
    }
    // 动态添加首页
    router.addRoute({
        path: "/",
        name: "home",
        component: () => import(`../views/home/home.vue`),
        redirect: '/index', // 访问 / 跳转到 /index 然后匹配上 index.vue
        children: [{
            path: "index",
            name: "index",
            component: () => import(`../views/index/index.vue`),
        }]
    })
    console.log('routes--->', routes)
}

// 前置导航守卫
router.beforeEach((to, from, next) => {
    // 1、token && vuex里面的 menus 为空
    const token = Cookie.get('token')
    console.log(store)
    if(token && store.state.menus.length === 0) {
        console.log('menus为空')
        // 获取用户信息
        store.dispatch('getAdminInfoApi').then(() => {
            genRoutes()
            // 路由规则没有刷新,需要再次next进入路由守卫
            next(to)
        })
    } else if(token && store.state.menus.length !== 0 && to.path === '/temp' && from.path === '/login') {
        // 第一次登录,动态添加路由规则 to.path === '/index' 要改成其他路由 /temp 避免登录进来进入 index 直接死循环
        genRoutes()
        // 路由规则没有刷新,需要再次next进入路由守卫
        next('/index')
    } else {
        next()
    }
})

export const initRouter = (app: App<Element>) => {
    app.use(router)
}
html 复制代码
<template>
    <div class="login-rule-form">
        <div class="content">
            <div class="title">商品管理系统</div>
            <el-form ref="ruleFormRef" :model="ruleForm" status-icon :rules="rules" label-width="60px">
                <el-form-item prop="username" label="账号">
                    <el-input v-model="ruleForm.username" type="text" placeholder="请输入账号"/>
                </el-form-item>
                <el-form-item prop="pwd" label="密码">
                    <el-input v-model="ruleForm.pwd" type="password" placeholder="请输入密码"/>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="loginFn()">登录</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>
</template>

<script lang='ts' setup>
import { onMounted, reactive, ref } from 'vue'
import { adminLoginApi } from '@/api/login'
import Cookie from 'js-cookie'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'

let ruleForm = reactive({
    username: "",
    pwd: ""
})
// 自定义密码校验规则(_variable - 未使用的变量 ts不校验)
const validatePwd = (_rule: unknown, value: string | undefined, callback: (msg?: string) => void) => {
    if(!value) {
        callback('密码不能为空')
    } else {
        callback()
    }
}
// 校验规则
let rules = reactive({
    username: [
        {
            required: true,
            message: '用户名不能为空',
            trigger: 'blur'
        }
    ],
    pwd: [
        {
            required: true,
            validator: validatePwd,
            trigger: 'blur'
        }
    ]
})

// 获取el-form组件对象
let ruleFormRef = ref()
// 获取项目路由对象
let router = useRouter()
// 获取当前项目的vuex对象
let store = useStore()

onMounted(() => {
    // console.log('组件实例:', ruleFormRef.value)
    // console.log('DOM 元素:', ruleFormRef.value?.$el)
})

// 点击登录
const loginFn = () => {
    ruleFormRef.value.validate().then(() => {
        adminLoginApi({
            username: ruleForm.username,
            password: ruleForm.pwd
        }).then((res) => {
            if(res.code === 200) {
                // 储存cookie
                Cookie.set('token', res.data.token, { expires: 7 })
                ElMessage.success('登录成功')
                // 获取用户信息
                store.dispatch('getAdminInfoApi').then(res => {
                    // 跳转首页的时候先跳转一个临时路由 /temp 去生成路由规则,然后在路由守卫里在去跳转 /index
                    router.push('/temp')
                })
            } else {
                ElMessage.error('登录报错')
            }
        })
    }).catch(() => {
        console.log('校验不通过')
    })
}

</script>

<style lang='less' scoped>
.login-rule-form {
    width: 100%;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #f5f5f5;
    overflow: hidden;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    .content {
        width: 420px;
        padding: 40px;
        background-color: #fff;
        border-radius: 8px;
        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
        box-sizing: border-box;
        .title {
            font-size: 28px;
            font-weight: bold;
            text-align: center;
            margin-bottom: 30px;
        }
    }

    :deep(.el-form) {
        .el-form-item {
            margin-bottom: 20px;
            &:last-child {
                margin-bottom: 0;
            }
        }
        .el-button {
            width: 100%;
        }
    }
}
</style>
相关推荐
菜鸟茜9 小时前
Vue3 + Element Plus 省市区县级联组件封装,支持 v-model 双向绑定 + 回显,可直接复用
vue3·element-plus·组件封装·前端复用·省市区县级联
蜡台2 天前
Vue3 props ref router 数据通讯传输等使用记录
前端·javascript·vue.js·vue3·router·ref
梵得儿SHI3 天前
Vue 3 工程化实战:Axios 高阶封装与样式解决方案深度指南
前端·javascript·vue3·axios·样式解决方案·api请求管理·统一请求处理
叱咤少帅(少帅)4 天前
vue3 开源项目
vue3
儒雅的烤地瓜6 天前
Vue | 一文详解Vue3中的Setup()函数
vue.js·vue3·vue2·组合式api·setup函数·option api
Irene19918 天前
ElementPlus 与成熟后台框架对比:vue-element-plus-admin、vue-pure-admin等
前端·ui·框架·vue3
终端鹿8 天前
Vue3 与第三方组件库联动:Element Plus 按需引入与二次封装
vue3·element plus·二次封装
Grocery store owner14 天前
vue3使用wangeditor上传附件以及添加表格,可以直接复制粘贴excel内容
vue3·wangeditor
floret. 小花14 天前
Vue3 知识点总结 · 2026-03-27
前端·面试·electron·学习笔记·vue3
梵得儿SHI15 天前
Vue 3 生态工具实战:UI 组件库与表单验证完全指南
前端·ui·vue3·elementplus·表单验证·antdesignvue·veevalidate