修复bug
This commit is contained in:
184
components/MyTabs/MyTabs.vue
Normal file
184
components/MyTabs/MyTabs.vue
Normal file
@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="my-tabs">
|
||||
<!-- 导航栏 -->
|
||||
<div class="my-tabs__nav">
|
||||
<div
|
||||
v-for="pane in panes"
|
||||
:key="pane.name"
|
||||
:class="{
|
||||
'my-tabs__item': true,
|
||||
'is-active': activeName === pane.name,
|
||||
'is-disabled': pane.disabled
|
||||
}"
|
||||
@click="handleClick(pane)"
|
||||
>
|
||||
<!-- 标签文本 -->
|
||||
<span>{{ pane.label }}</span>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<i
|
||||
v-if="pane.closable"
|
||||
class="my-tabs__close"
|
||||
@click.stop="handleClose(pane)"
|
||||
>×</i>
|
||||
</div>
|
||||
|
||||
<!-- 底部横线和滑块 -->
|
||||
<div class="my-tabs__slider-wrapper">
|
||||
<div class="my-tabs__slider-track"></div>
|
||||
<div
|
||||
class="my-tabs__slider"
|
||||
ref="slider"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区 -->
|
||||
<div class="my-tabs__content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MyTabs',
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'input'
|
||||
},
|
||||
props: {
|
||||
value: [String, Number], // 当前激活
|
||||
type: { type: String, default: '' }, // 后期可扩展 card/border-card
|
||||
closable: Boolean // 全局是否允许关闭
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
panes: [] // 缓存所有 MyTabPane 实例
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeName: {
|
||||
get() { return this.value },
|
||||
set(val) { this.$emit('input', val) }
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeName() {
|
||||
this.$nextTick(() => {
|
||||
this.updateSliderPosition();
|
||||
});
|
||||
},
|
||||
panes() {
|
||||
this.$nextTick(() => {
|
||||
this.updateSliderPosition();
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.updateSliderPosition();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
/* 收集子组件 */
|
||||
registerPane(pane) {
|
||||
this.panes.push(pane)
|
||||
// 默认选中第一个
|
||||
if (!this.activeName && this.panes.length === 1) {
|
||||
this.activeName = pane.name
|
||||
}
|
||||
},
|
||||
unregisterPane(pane) {
|
||||
const idx = this.panes.indexOf(pane)
|
||||
if (idx > -1) this.panes.splice(idx, 1)
|
||||
},
|
||||
/* 点击导航 */
|
||||
handleClick(pane) {
|
||||
if (pane.disabled) return
|
||||
this.activeName = pane.name
|
||||
this.$emit('tab-click', pane)
|
||||
},
|
||||
/* 关闭 */
|
||||
handleClose(pane) {
|
||||
this.$emit('edit', pane.name, 'remove')
|
||||
// 业务层把 v-model 绑定的数组删掉即可
|
||||
},
|
||||
/* 更新滑块位置和宽度 */
|
||||
updateSliderPosition() {
|
||||
if (!this.activeName || !this.$refs.slider) return;
|
||||
|
||||
const activeTab = this.$el.querySelector(`.my-tabs__item.is-active`);
|
||||
if (!activeTab) return;
|
||||
|
||||
const textElement = activeTab.querySelector('span');
|
||||
if (!textElement) return;
|
||||
|
||||
const { width } = textElement.getBoundingClientRect();
|
||||
const { left } = activeTab.getBoundingClientRect();
|
||||
const navLeft = this.$el.querySelector('.my-tabs__nav').getBoundingClientRect().left;
|
||||
|
||||
this.$refs.slider.style.width = `${width}px`;
|
||||
this.$refs.slider.style.left = `${left - navLeft + (activeTab.offsetWidth - width) / 2}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.my-tabs__nav {
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
.my-tabs__item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
font-size: 20px;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.my-tabs__item.is-active {
|
||||
color: #409eff;
|
||||
background: linear-gradient(90deg, $linear-gradient-start 22%, $linear-gradient-end 73%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
.my-tabs__item.is-disabled {
|
||||
color: #1e293b;
|
||||
cursor: not-allowed;
|
||||
font-family: 'Poppins-Regular', serif;
|
||||
}
|
||||
.my-tabs__close { margin-left: 6px; color: #999; }
|
||||
.my-tabs__close:hover { color: #f56c6c; }
|
||||
.my-tabs__content { padding: 15px 0; }
|
||||
|
||||
/* 底部横线和滑块样式 */
|
||||
.my-tabs__slider-wrapper {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.my-tabs__slider-track {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: #e2e8f0;
|
||||
}
|
||||
.my-tabs__slider {
|
||||
height: 4px;
|
||||
background: $header-backgroungd;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user