Vue3+element动态增减el-tabs

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。Vue3+element动态增减el-tabs,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

overview

这个问题我想了很久,怎么能优雅地实现点击左侧边栏,右边动态增减。现在有点眉目了

设计

容器

容器主体

主要思路:分为三个部分,容器整体,容器左侧,容器右侧。左侧是按钮组,右侧是待渲染部分。
我把侧边栏单独抽出来做了个组件,不重要。然后重点是动态增减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">
      <!-- v-model代表当前激活哪个页面,@tab-remove代表删除某个标签的回调 -->
      <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功能动态选择要渲染的子组件 -->
          <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

over

这就是我目前能想出来最优雅扩展性最强的解法。比之前那种强跳转的解法来说,更容易扩展,只要后端加载的数据格式约定好,直接就能由静态转动态。另外,没有强依赖跨组件的状态共享,耦合性低,更加安全

欢迎联系我 memeda

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153517.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!