对于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-item
v-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 } = route
nextTick(() => {
// 重定向到中间页 实现vue中当前路由刷新
router.replace({
path: '/redirect' + fullPath
})
})
}
// command 获取点击按钮的command属性值 作为size值
const handleSize = (command: Size) => {
// 修改element-plus组件尺寸
(proxy as ComponentPublicInstance).$ELEMENT.size = command
// 更新store
store.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中获取sidebar
const 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
}
// 定义mutations
const mutations: MutationTree<IAppState> = {
TOGGLE_SIDEBAR(state) {
state.sidebar.opened = !state.sidebar.opened
},
SET_SIZE(state, size: Size) {
state.size = size
}
}
// 定义actions
const actions: ActionTree<IAppState, IRootState> = {
toggleSidebar({ commit }) {
commit('TOGGLE_SIDEBAR')
},
setSize({ commit }, size: Size) {
commit('SET_SIZE', size)
}
}
// 定义module
const 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 } = route
router.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%B0
path: '/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)
// 缓存路由集合 暂时先是空数组 后面会放到store
const 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'
// 初始化css
import 'normalize.css/normalize.css'
// element-plus
import installElementPlus, { Size } from './plugins/element'
// 挂载到vue实例上
import { ElMessageBox, ElMessage, ElNotification } from 'element-plus'
// 全局 css
import '@/styles/index.scss'
// svg icons
import initSvgIcon from '@/icons/index'
const app = createApp(App)
// 获取store里存储的size
const size = store.state.app.size
app
.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/i18n
import 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-globalproperties
app.config.globalProperties.$message = ElMessage
app.config.globalProperties.$notify = ElNotification
app.config.globalProperties.$confirm = ElMessageBox.confirm
app.config.globalProperties.$alert = ElMessageBox.alert
app.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 { // 自定义样式404
font-size: 200px;
color: green;
}
</style>
本节参考源码
https://gitee.com/brolly/vue3-element-admin/commit/1c1d7cc4c41ea4fe0dc4cf61fed9efc5bfec9b36