侧边栏壁纸
  • 累计撰写 21 篇文章
  • 累计创建 11 个标签
  • 累计收到 9 条评论

Vue 实现一个简易的tabs切换

好巧
2023-05-21 / 0 评论 / 0 点赞 / 64 阅读 / 3814 字
温馨提示:
本文最后更新于 2023-10-26,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

本文示例是官方的 vite 脚手架

新建文件,例:src/components/tabs/Tabs.vue

provide 传递对象数据会失去响应式,使用 toRef 可以保持响应式

<template>
  <div class="custom-tabs">
    <ul class="tabs-nav">
      <li
        v-for="(item, index) in tabs"
        :key="index"
        class="nav-item"
        :class="`${item?.props?.name === modelValue ? 'nav-active-item' : ''}`"
        @click="handleClick(item?.props?.name, $event)"
      >
        {{ item?.props?.label }}
      </li>
    </ul>
    <div class="tabs-content">
      <slot></slot>
    </div>
  </div>
</template>

<script setup>
import { provide, toRef, useSlots, reactive, onMounted } from 'vue';

const emits = defineEmits(['update:modelValue', 'tabClick']);

const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  }
});

provide('modelValue', toRef(props, 'modelValue'));

const slots = useSlots();
const tabs = reactive([]);

const calcTabItemInstances = () => {
  if (!slots.default) {
    return;
  }

  slots.default().forEach(item => {
    tabs.push(item);
  });
};

onMounted(() => {
  calcTabItemInstances();
});

const handleClick = (name, event) => {
  if (!props.modelValue || props.modelValue === name) {
    return;
  }

  emits('update:modelValue', name);
  emits('tabClick', name, event);
};
</script>

<style scoped>
.tabs-nav {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
}

.nav-item {
  line-height: 40px;
  margin-right: 22px;
  font-size: 16px;
  color: #606266;
  cursor: pointer;
}

.nav-item:hover {
  color: #409eff;
}

.nav-active-item {
  position: relative;
  color: #409eff;
}

.nav-active-item::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 33px;
  height: 3px;
  background-color: #386fff;
  border-radius: 4px;
}

.tabs-content {
  padding-top: 22px;
}
</style>

新建文件,例:src/components/tabs/TabPane.vue

<template>
  <div v-show="name === modeleValue" class="tab-pane">
    <slot></slot>
  </div>
</template>

<script setup>
import { inject } from 'vue';

defineProps({
  label: {
    type: String,
    default: '',
    required: true
  },
  name: {
    type: String,
    default: '',
    required: true
  }
});

const modeleValue = inject('modelValue');
</script>

组件使用

<template>
  <Tabs v-model="activeName" @tab-click="handleClick">
    <TabPane label="基础信息" name="first">基础信息</TabPane>
    <TabPane label="策略信息" name="second">策略信息</TabPane>
    <TabPane label="今日概况" name="third">今日概况</TabPane>
    <TabPane label="历史概况" name="fourth">历史概况</TabPane>
  </Tabs>
</template>

<script setup>
import { ref } from 'vue';
import Tabs from './components/tabs/Tabs.vue';
import TabPane from './components/tabs/TabPane.vue';

const activeName = ref('first');

const handleClick = (tab, event) => {
  console.log(tab, event);
};
</script>

效果图

0

评论区