对于element组件size属性尺寸共有 default medium small mini这四种
2-1 创建sizeSelect组件
切换element size主要通过动态修改$ELEMENT.size 为default | medium | small | mini
我们已经挂载到 vue组件实例上 可通过组件实例直接修改
// 全局配置 https://element-plus.gitee.io/#/zh-CN/component/quickstart#quan-ju-pei-zhi// 该对象目前支持 size 与 zIndex 字段。size 用于改变组件的默认尺寸 small,zIndex 设置弹框的初始 z-index(默认值:2000)。app.config.globalProperties.$ELEMENT = {size: options.size}
导入相关elemment 组件
创建sizeSelect组件
src/components/SizeSelect/index.vue
<template><div><el-dropdown trigger="click" @command="handleSize"><div><svg-icon class-name="size-icon" icon-class="size"></svg-icon></div><template #dropdown><el-dropdown-menu><el-dropdown-itemv-for="item in sizeOptions":key="item.value":command="item.value":disabled="item.value === size">{{ item.label }}</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></template><script lang="ts">import { Size } from '@/plugins/element'import {defineComponent,ref,getCurrentInstance,ComponentInternalInstance,ComponentPublicInstance,computed,nextTick} from 'vue'import { useRoute, useRouter } from 'vue-router'import { useStore } from '@/store'export default defineComponent({name: 'SizeSelect',setup() {const store = useStore()const route = useRoute()const router = useRouter()const { proxy } = getCurrentInstance() as ComponentInternalInstance// store中获取size 这里需要注意通过computed获取store状态 确保获取到正确更新const size = computed(() => store.getters.size)// element size 选项const sizeOptions = ref([{ label: 'Default', value: 'default' },{ label: 'Medium', value: 'medium' },{ label: 'Small', value: 'small' },{ label: 'Mini', value: 'mini' }])// 刷新当前路由const refreshView = () => {const { fullPath } = routenextTick(() => {// 重定向到中间页 实现vue中当前路由刷新router.replace({path: '/redirect' + fullPath})})}// command 获取点击按钮的command属性值 作为size值const handleSize = (command: Size) => {// 修改element-plus组件尺寸(proxy as ComponentPublicInstance).$ELEMENT.size = command// 更新storestore.dispatch('app/setSize', command)// 切换size需要刷新路由才能生效refreshView()proxy?.$message.success({type: 'success',message: 'Switch Size Success'})}return {sizeOptions,size,handleSize}}})</script><style lang="scss">.size-icon {font-size: 18px;}</style>
navbar导入组件并调整样式

navbar样式调整
src/layout/components/Navbar.vue
<template><div class="navbar"><hambuger @toggleClick="toggleSidebar" :is-active="sidebar.opened"/><breadcrumb /><div class="right-menu"><!-- 全屏 --><screenfull id="screefull" class="right-menu-item hover-effect" /><!-- element组件size切换 --><el-tooltip content="Global Size" effect="dark" placement="bottom"><size-select class="right-menu-item hover-effect" /></el-tooltip></div></div></template><script lang="ts">import { defineComponent, computed } from 'vue'import Breadcrumb from '@/components/Breadcrumb/index.vue'import Hambuger from '@/components/Hambuger/index.vue'import { useStore } from '@/store/index'import Screenfull from '@/components/Screenfull/index.vue'import SizeSelect from '@/components/SizeSelect/index.vue'export default defineComponent({name: 'Navbar',components: {Breadcrumb,Hambuger,Screenfull,SizeSelect},setup() {// 使用我们自定义的useStore 具备类型提示// store.state.app.sidebar 对于getters里的属性没有类型提示const store = useStore()const toggleSidebar = () => {store.dispatch('app/toggleSidebar')}// 从getters中获取sidebarconst sidebar = computed(() => store.getters.sidebar)return {toggleSidebar,sidebar}}})</script><style lang="scss">.navbar {display: flex;.right-menu {flex: 1;display: flex;align-items: center;justify-content: flex-end;padding-right: 15px;&-item {padding: 0 8px;font-size: 18px;color: #5a5e66;vertical-align: text-bottom;&.hover-effect {cursor: pointer;transition: background .3s;&:hover {background: rgba(0, 0, 0, .025);}}}}}</style>
当每次切换size时,不仅需要修改$ELEMENT.size 还要让当前路由刷新才能生效。
2-2 size属性添加到store
修改src/store/modules/app.ts
添加 state size、actions setSize、mutations SET_SIZE
import { Size } from '@/plugins/element'import { ActionTree, Module, MutationTree } from 'vuex'import { IRootState } from '../index'// 定义app里state类型export interface IAppState {sidebar: {opened: boolean},size: Size}// 定义mutationsconst mutations: MutationTree<IAppState> = {TOGGLE_SIDEBAR(state) {state.sidebar.opened = !state.sidebar.opened},SET_SIZE(state, size: Size) {state.size = size}}// 定义actionsconst actions: ActionTree<IAppState, IRootState> = {toggleSidebar({ commit }) {commit('TOGGLE_SIDEBAR')},setSize({ commit }, size: Size) {commit('SET_SIZE', size)}}// 定义moduleconst app: Module<IAppState, IRootState> = {namespaced: true,state: {sidebar: {opened: true},size: 'medium'},mutations,actions}export default app
sotre缓存中将size缓存
getters中添加size
2-3 实现当前路由刷新
解决一下两点问题
- vue中通过一个重定向中间页实现当前路由刷新 利用路由router.replace
- 实现当前路由刷新 路由不能被keep-alive缓存 目前我们是默认全部缓存
添加redirect路由
创建redirect 路由组件
src/views/redirect/index.vue
<script lang="ts">import { h } from 'vue'import { useRoute, useRouter } from 'vue-router'export default {name: 'Redirect',setup() {const route = useRoute()const router = useRouter()const { query, params } = routerouter.replace({path: '/' + params.path,query})return () => {return h('template')}}}</script>
注册路由
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',children: [{path: 'index',name: 'Documentation',component: () => import(/* webpackChunkName: "documentation" */ '@/views/documentation/index.vue'),meta: {title: 'Documentation',icon: 'documentation',hidden: false // 菜单栏不显示}}]},{path: '/guide',component: Layout,redirect: '/guide/index',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: '/system',component: Layout,redirect: '/system/user',meta: {title: 'System',icon: 'lock',alwaysShow: true // 根路由始终显示 哪怕只有一个子路由},children: [{path: 'menu',component: () => import(/* webpackChunkName: "menu" */ '@/views/system/menu.vue'),meta: {title: 'Menu Management',hidden: false,breadcrumb: false}},{path: 'role',component: () => import(/* webpackChunkName: "role" */ '@/views/system/role.vue'),meta: {title: 'Role Management',hidden: false}},{path: 'user',component: () => import(/* webpackChunkName: "user" */ '@/views/system/user.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',children: [{path: 'dashboard',name: 'Dashboard',component: () => import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/index.vue'),meta: {title: 'Dashboard',// icon: 'dashboard'icon: 'el-icon-platform-eleme'}}]},{path: '/redirect',component: Layout,meta: {hidden: true},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%B0path: '/redirect/:path(.*)', // 要匹配多级路由 应该加*号component: () => import('@/views/redirect/index.vue')}]},{path: '/401',component: Layout,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
解决keep-alive缓存问题
暂时先给keep-alive添加 include属性为空数组

修改src/layout/components/AppMain.vue
<template><div class="app-main"><!-- vue3 路由缓存 https://next.router.vuejs.org/guide/migration/index.html#router-view-keep-alive-and-transition --><router-view v-slot={Component}><transition name="fade-transform" mode="out-in"><keep-alive :include="cachedViews"><component :is="Component" :key="key" /></keep-alive></transition></router-view></div></template><script lang="ts">import { computed, defineComponent, ref } from 'vue'import { useRoute } from 'vue-router'export default defineComponent({name: 'AppMain',setup() {const route = useRoute()const key = computed(() => route.path)// 缓存路由集合 暂时先是空数组 后面会放到storeconst cachedViews = ref([])return {key,cachedViews}}})</script><style lang="scss" scoped>.app-main {/* navbar 50px */min-height: calc(100vh - 50px);}.fade-transform-enter-active,.fade-transform-leave-active {transition: all .5s;}.fade-transform-enter-from {opacity: 0;transform: translateX(-30px);}.fade-transform-leave-to {opacity: 0;transform: translateX(30px);}</style>
2-4 插件size统一从store里获取
利用vue plugin进行参数传递

修改src/main.ts
import { createApp } from 'vue'import App from './App.vue'import router from './router'import store, { key } from './store'// 初始化cssimport 'normalize.css/normalize.css'// element-plusimport installElementPlus, { Size } from './plugins/element'// 挂载到vue实例上import { ElMessageBox, ElMessage, ElNotification } from 'element-plus'// 全局 cssimport '@/styles/index.scss'// svg iconsimport initSvgIcon from '@/icons/index'const app = createApp(App)// 获取store里存储的sizeconst size = store.state.app.sizeapp.use(store, key).use(router).use(installElementPlus, {size}).use(initSvgIcon).mount('#app')/*** 相关issue问题* Why not on the d.ts use it ?(为什么不能在shims-d.ts 中设置这个?* https://github.com/vuejs/vue-next/pull/982*/// 挂载到vue实例上declare module '@vue/runtime-core' {interface ComponentCustomProperties {$message: typeof ElMessage;$notify: typeof ElNotification;$confirm: typeof ElMessageBox.confirm;$alert: typeof ElMessageBox.alert;$prompt: typeof ElMessageBox.prompt;$ELEMENT: {size: Size;};}}
修改element.ts接收plugin参数
import { App } from 'vue'import {locale,ElButton,ElMessage,ElNotification,ElMessageBox,ElMenu,ElMenuItem,ElSubmenu,ElRow,ElCol,ElBreadcrumb,ElBreadcrumbItem,ElTooltip,ElDropdown,ElDropdownMenu,ElDropdownItem} from 'element-plus'import 'element-plus/lib/theme-chalk/index.css'// Element Plus 组件内部默认使用英语// https://element-plus.gitee.io/#/zh-CN/component/i18nimport lang from 'element-plus/lib/locale/lang/zh-cn'// Element Plus 直接使用了 Day.js 项目的时间日期国际化设置, 并且会自动全局设置已经导入的 Day.js 国际化配置。import 'dayjs/locale/zh-cn'// $ELEMENT size属性类型export type Size = 'default' | 'medium' | 'small' | 'mini'interface ElementOptions {size: Size}export default (app: App, options: ElementOptions): void => {locale(lang)// 按需导入组件列表const components = [ElButton,ElMessage,ElNotification,ElMessageBox,ElMenu,ElMenuItem,ElSubmenu,ElRow,ElCol,ElBreadcrumb,ElBreadcrumbItem,ElTooltip,ElDropdown,ElDropdownMenu,ElDropdownItem]components.forEach(component => {app.component(component.name, component)})// Vue.prototype 替换为 config.globalProperties// 文档说明 https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-prototype-%E6%9B%BF%E6%8D%A2%E4%B8%BA-config-globalpropertiesapp.config.globalProperties.$message = ElMessageapp.config.globalProperties.$notify = ElNotificationapp.config.globalProperties.$confirm = ElMessageBox.confirmapp.config.globalProperties.$alert = ElMessageBox.alertapp.config.globalProperties.$prompt = ElMessageBox.prompt// 全局配置 https://element-plus.gitee.io/#/zh-CN/component/quickstart#quan-ju-pei-zhi// 该对象目前支持 size 与 zIndex 字段。size 用于改变组件的默认尺寸 small,zIndex 设置弹框的初始 z-index(默认值:2000)。app.config.globalProperties.$ELEMENT = {size: options.size}}
2-5 测试
dashboard页面导入button组件
src/views/dashboard/index.vue
<template><div><h1>Dashboard page</h1>缓存测试 <input type="text"><el-button type="primary">size改变</el-button></div></template><script>export default {name: 'Dashboard'}</script><style lang="scss">.custom-class { // 自定义样式404font-size: 200px;color: green;}</style>
本节参考源码
https://gitee.com/brolly/vue3-element-admin/commit/1c1d7cc4c41ea4fe0dc4cf61fed9efc5bfec9b36

