这个问题我想了很久,怎么能优雅地实现点击左侧边栏,右边动态增减。现在有点眉目了
主要思路:分为三个部分,容器整体,容器左侧,容器右侧。左侧是按钮组,右侧是待渲染部分。
我把侧边栏单独抽出来做了个组件,不重要。然后重点是动态增减Tabs,最重要的就是用component的:is属性,根据按钮的callBack动态渲染每一个Tab的内容。
代码如下:
<div class="root-ele">
<div class="root-title">
你好, 世界!
div>
<div class="root-body">
<div class="root-body root-body-item-left">
<EventSide @openTab="toOpen">EventSide>
div>
<div class="root-body root-body-item-right">
<el-tabs
v-model="editableTabsValue"
type="card"
closable
@tab-remove="removeTab"
>
<el-tab-pane
v-for="(item, index) in allTabs.value"
:key="index"
:name="item.name"
:label="item.label"
>
<Component :is="item.name">Component>
el-tab-pane>
el-tabs>
div>
div>
div>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue'
// 样式文件
import '../RootElementStyle.css'
import MyHead from '../MyHead.vue'
import EventSide from '../../components/eventComponent/EventSide.vue'
import AllEvent from '../../components/eventComponent/AllEvent.vue'
import EventDetail from '../../components/eventComponent/EventDetail.vue'
export default defineComponent({
name: 'EventPage',
components: {
MyHead,
EventSide,
allEvent: AllEvent,
eventDetail: EventDetail,
},
setup() {
// 定义接口,用于规定数组对象的类型
interface inter{
name: string
[propName: string]: any
}
const editableTabsValue = ref('')
const allTabs = reactive({
value: [] as inter[],
})
function toOpen(item: {name: string; label: string}) {
for (let i = 0; i < allTabs.value.length; ++i) {
if (allTabs.value[i].name === item.name) {
return
}
}
allTabs.value.push(item)
editableTabsValue.value = item.name
}
function removeTab(targetName: string) {
const tabs = allTabs.value
let activeName = editableTabsValue.value
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
editableTabsValue.value = activeName
allTabs.value = tabs.filter(tab => tab.name !== targetName)
}
return {
toOpen,
editableTabsValue,
removeTab,
allTabs,
}
},
})
script>
.root-ele {
font-size: medium;
position: absolute;
height: 100%;
width: 100%;
font-family: "montserrat", sans-serif;
}
.root-title {
min-height: 7%;
max-height: 9%;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
background-color: #f4f4f5;
backdrop-filter: blur(5px);
font-size: x-large;
font-weight: lighter;
line-height: 230%;
margin-bottom: 0.5%;
color: white;
border-left: black 4px solid;
background-image: linear-gradient(125deg, #409EFF, #E6A23C, #F56C6C, #67C23A, #909399);
animation: bganimation 15s infinite;
background-size: 400%;
}
.root-body {
height: 88.5%;
overflow-y: auto;
display: flex;
border-radius: 4px;
}
.root-body .root-body-item-left {
width: 15%;
height: 100%;
background-image: linear-gradient(125deg, #DCDFE6, #E4E7ED, #EBEEF5, #F2F6FC, #C0C4CC);
animation: bganimation 15s infinite;
background-size: 400%;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
overflow-x: hidden;
}
.root-body .root-body-item-right {
display: block;
padding-left: 10px;
width: 84%;
height: 100%;
margin-left: 0.3%;
background-image: linear-gradient(125deg, #DCDFE6, #E4E7ED, #EBEEF5, #F2F6FC, #C0C4CC);
animation: bganimation 15s infinite;
background-size: 400%;
}
不废话,代码如下
<template>
<div
v-for="(item, index) in sides.sides"
:key="index"
class="event-side-item"
@click="handleOpenTab(item)"
>
{{ item.label }}
div>
template>
<script lang="ts">
import { defineComponent, reactive } from 'vue'
import eventSideConfig from './EventSideConfig'
export default defineComponent({
name: 'EventSide',
emits: ['openTab'],
setup(_, { emit }) {
const sides = reactive({
sides: eventSideConfig,
})
function handleOpenTab(item: any) {
emit('openTab', item)
}
return {
sides,
handleOpenTab,
}
},
})
script>
我把侧边栏和组件的对应关系放在EventSideConfig里面,直接读取,如果后期需要从后端动态读取,页面什么都不用改,直接赋个值就行。
定义文件如下:
const eventSideConfig = [
{
label: 'allEvent',
name: 'allEvent',
},
{
label: 'eventDetail',
name: 'eventDetail',
},
]
export default eventSideConfig
这就是我目前能想出来最优雅扩展性最强的解法。比之前那种强跳转的解法来说,更容易扩展,只要后端加载的数据格式约定好,直接就能由静态转动态。另外,没有强依赖跨组件的状态共享,耦合性低,更加安全
欢迎联系我 memeda