6-1 个人中心页
src/views/profile/index.vue
<template>
<div class="profile-container">
<el-card>
<template #header>
<div class="card-header">
<span>关于我</span>
</div>
</template>
<div class="profile" v-if="userInfo">
<div class="avatar">
<img :src="avatar" alt="" />
</div>
<h2>用户名:{{ userInfo.username }}</h2>
<h3>用户角色:{{ roleNames }}</h3>
<div v-if="userInfo.description">
<span>个人说明</span>
<p>{{ userInfo.description }}</p>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import { useStore } from '@/store'
import defaultAvatar from '@/assets/logo.png'
export default defineComponent({
name: 'Profile',
setup() {
const store = useStore()
const userInfo = computed(() => store.state.user.userInfo)
const roleNames = computed(() => store.getters.roleNames)
const avatar = computed(() => userInfo?.value?.avatar || defaultAvatar)
return {
userInfo,
avatar,
roleNames
}
}
})
</script>
<style lang="scss" scoped>
.profile-container {
width: 500px;
margin: 10px auto;
.profile {
text-align: center;
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
margin: 10px auto;
img {
width: 100%;
height: 100%;
}
}
}
}
</style>
路由注册
src/router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'
// 看作是异步获取路由
export const asyncRoutes: Array<RouteRecordRaw> = [
{
path: '/documentation',
component: Layout, // 布局组件作为一级路由
redirect: '/documentation/index',
name: 'DocumentationLayout',
children: [
{
path: 'index',
name: 'Documentation',
component: () => import(/* webpackChunkName: "documentation" */ '@/views/documentation/index.vue'),
meta: {
title: 'Documentation',
icon: 'documentation',
hidden: false, // 菜单栏不显示
// 路由是否缓存 没有这个属性或false都会缓存 true不缓存
noCache: true
}
}
]
},
{
path: '/async',
component: Layout,
redirect: '/async/index',
name: 'AsyncLayout',
children: [
{
path: 'index',
name: 'Async',
component: () => import(/* webpackChunkName: "async" */ '@/views/async.vue'),
meta: {
title: '动态路由',
icon: 'guide'
// 当guide路由激活时高亮选中的是 documentation/index菜单
// activeMenu: '/documentation/index'
}
}
]
},
{
path: '/guide',
component: Layout,
redirect: '/guide/index',
name: 'GuideLayout',
meta: {
title: 'GuideLay',
icon: 'guide'
},
children: [
{
path: 'index',
name: 'Guide',
component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),
meta: {
title: 'Guide',
icon: 'guide'
// 当guide路由激活时高亮选中的是 documentation/index菜单
// activeMenu: '/documentation/index'
}
},
{
path: 'guide2',
name: 'Guide2',
component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),
meta: {
title: 'Guide2',
icon: 'guide'
// 当guide路由激活时高亮选中的是 documentation/index菜单
// activeMenu: '/documentation/index'
}
},
{
path: 'guide3',
name: 'Guide3',
component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),
meta: {
title: 'Guide3',
icon: 'guide'
// 当guide路由激活时高亮选中的是 documentation/index菜单
// activeMenu: '/documentation/index'
}
}
]
},
{
path: '/system',
component: Layout,
redirect: '/system/user',
name: 'SystemLayout',
meta: {
title: 'System',
icon: 'lock',
alwaysShow: true // 根路由始终显示 哪怕只有一个子路由
},
children: [
{
path: 'menu',
name: 'Menu Management',
component: () => import(/* webpackChunkName: "menu" */ '@/views/system/menu/index.vue'),
meta: {
title: 'Menu Management',
hidden: false,
breadcrumb: false
}
},
{
path: 'role',
name: 'Role Management',
component: () => import(/* webpackChunkName: "role" */ '@/views/system/role/index.vue'),
meta: {
title: 'Role Management',
hidden: false
}
},
{
path: 'user',
name: 'User Management',
component: () => import(/* webpackChunkName: "user" */ '@/views/system/user/index.vue'),
meta: {
title: 'User Management'
}
}
]
},
{ // 外链路由
path: '/external-link',
component: Layout,
children: [
{
path: 'https://www.baidu.com/',
redirect: '/',
meta: {
title: 'External Link',
icon: 'link'
}
}
]
},
{ // 404一定放在要在最后面
path: '/:pathMatch(.*)*',
redirect: '/404',
meta: {
hidden: true
}
}
]
export const constantRoutes: Array<RouteRecordRaw> = [
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'DashboardLayout',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/index.vue'),
meta: {
title: 'Dashboard',
// icon: 'dashboard'
icon: 'el-icon-platform-eleme',
affix: true // 固定显示在tagsView中
}
}
]
},
{
path: '/redirect',
component: Layout,
meta: {
hidden: true
},
name: 'Redirect',
children: [
{ // 带参数的动态路由正则匹配
// https://next.router.vuejs.org/zh/guide/essentials/route-matching-syntax.html#%E5%8F%AF%E9%87%8D%E5%A4%8D%E7%9A%84%E5%8F%82%E6%95%B0
path: '/redirect/:path(.*)', // 要匹配多级路由 应该加*号
component: () => import('@/views/redirect/index.vue')
}
]
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/login/index.vue')
},
{
path: '/profile',
component: Layout,
redirect: '/profile/index',
name: 'ProfileLayout',
children: [
{
path: 'index',
name: 'Profile',
component: () => import('@/views/profile/index.vue'),
meta: {
hidden: true,
title: '个人中心'
}
}
]
},
{
path: '/401',
component: Layout,
name: '401Layout',
children: [
{
path: '',
component: () => import('@/views/error-page/401.vue'),
meta: {
title: '401',
icon: '404',
hidden: true
}
}
]
},
{
path: '/404',
component: () => import('@/views/error-page/404.vue'),
meta: {
hidden: true // 404 hidden掉
}
}
]
export const routes = [
...constantRoutes
// ...asyncRoutes
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
6-2 修改头像下拉选项
src/layout/components/avatar/index.vue
<template>
<el-dropdown
class="avatar-container">
<div class="avatar-wrapper">
<img :src="avatar" class="user-avatar">
<i class="el-icon-caret-bottom" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="username">
<span style="display: block" :style="{fontWeight: '500'}">用户名:{{username}}</span>
</el-dropdown-item>
<router-link to="/">
<el-dropdown-item>首页</el-dropdown-item>
</router-link>
<router-link to="/profile/index">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click="logout">
<span style="display: block">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script lang="ts">
import defaultAvatar from '@/assets/logo.png'
import { defineComponent, getCurrentInstance, computed } from 'vue'
import { useStore } from '@/store'
export default defineComponent({
setup() {
const store = useStore()
const { proxy } = getCurrentInstance()!
const logout = () => {
store.dispatch('user/logout').then(() => {
proxy?.$message.success('退出登录')
window.location.reload()
})
}
const userInfo = computed(() => store.state.user.userInfo)
const avatar = computed(() => userInfo.value?.avatar || defaultAvatar)
const username = computed(() => userInfo.value?.username || '')
// onMounted(() => {
// // 获取用户信息
// store.dispatch('user/getUserInfo')
// })
return {
logout,
avatar,
username
}
}
})
</script>
<style lang="scss" scoped>
.avatar-container {
margin-right: 30px;
.avatar-wrapper {
margin-top: 5px;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
cursor: pointer;
}
.el-icon-caret-bottom {
cursor: pointer;
font-size: 12px;
}
}
}
</style>