Skip to content

Commit

Permalink
feat: 优化 tab 组件
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyh2001 committed Jul 27, 2023
1 parent a682f25 commit 3f93b2d
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 39 deletions.
32 changes: 29 additions & 3 deletions packages/fighting-design/tabs-item/src/tabs-item.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
<script lang="ts" setup>
import { Props } from './props'
import { TABS_PROPS_KEY } from '../../tabs/src/props'
import { inject, computed } from 'vue'
import { inject, computed, getCurrentInstance, ref, onMounted, reactive } from 'vue'
import type { ComponentInternalInstance } from 'vue'
defineOptions({ name: 'FTabsItem' })
const prop = defineProps(Props)
/** 获取当前组件实例 */
const instance = getCurrentInstance() as ComponentInternalInstance
const paneName = ref(prop.name)
/** 获取父组件注入的依赖项 */
const parentInject = inject(TABS_PROPS_KEY, null)
const pane = reactive({
paneName,
label: prop.label,
uid: instance.uid,
prop
})
/** 该组件是否显示 */
const isShow = computed(
(): boolean | null => parentInject && parentInject.activeName.value === prop.name
(): boolean | null => parentInject && parentInject.activeName.value === pane.paneName
)
/** 在组件插入及卸载时都要更新父级的 pane 列表 */
onMounted((): void => {
parentInject && parentInject.registerChild(pane)
})
// onBeforeUnmount((): void => {
// parentInject && parentInject.unRegisterChild(pane)
// })
</script>

<template>
<div v-show="isShow" class="f-tabs-item" role="tabpanel">
<div
v-show="isShow"
:class="['f-tabs-item', { 'f-tabs-item__active': isShow }]"
role="tabpanel"
>
<slot />
</div>
</template>
96 changes: 75 additions & 21 deletions packages/fighting-design/tabs/src/tabs.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script lang="ts" setup>
import { Props, TABS_PROPS_KEY } from './props'
import { provide, getCurrentInstance, computed } from 'vue'
import type { TabsItemProps, TabsItemInstance } from '../../tabs-item'
import type { ComponentInternalInstance } from 'vue'
import { provide, getCurrentInstance, ref, isVNode } from 'vue'
import { isObject, isArray } from '../../_utils'
import type { TabsItemProps } from '../../tabs-item'
import type { ComponentInternalInstance, VNode, Component } from 'vue'
defineOptions({ name: 'FTabs' })
Expand All @@ -12,41 +13,94 @@
type: [Number, String]
})
/** 获取当前组件实例 */
const activeName = ref<string | number>(0)
const childrenMap = new Map<number, any>()
const children = ref()
const flattedChildren = (children): VNode[] => {
const vNodes = isArray(children) ? children : [children]
const result: VNode[] = []
vNodes.forEach((child): void => {
if (isArray(child)) {
result.push(...flattedChildren(child))
} else if (isVNode(child) && isArray(child.children)) {
result.push(...flattedChildren(child.children))
} else {
if (isVNode(child) && child.component) {
result.push(child)
}
}
})
return result
}
const getChildrenComponent = (
root: ComponentInternalInstance,
component: string
): VNode[] => {
if (!root.subTree) return []
const flaChildren = flattedChildren(root.subTree.children)
return flaChildren.filter(
e => isObject(e.type) && (e.type as Component).name === component
)
}
const root = getCurrentInstance() as ComponentInternalInstance
const component = 'FTabsItem'
const registerChild = (child): void => {
childrenMap.set(child.uid, child)
const componentList: VNode[] = getChildrenComponent(root, component)
const componentUid: number[] = componentList
.map((item: VNode): number | null => {
return item.component ? item.component.uid : null
})
.filter(Boolean) as number[]
children.value = componentUid
.map((e: number): TabsPane | undefined => childrenMap.get(e))
.filter(Boolean)
.map((item, index) => {
item.paneName = item.paneName || index
return {
name: item.paneName || index,
label: item.prop.label
}
})
}
/** 将信息传递给子组件 */
provide(TABS_PROPS_KEY, { activeName: modelValue })
provide(TABS_PROPS_KEY, {
activeName,
registerChild
})
/**
* 切换页
*
* @param { string } name 页的名字
*/
const changeNavs = (name: TabsItemProps['name']): void => {
modelValue.value = name
activeName.value = name
console.log(name)
}
/** 获取当前组件实例 */
const instance: ComponentInternalInstance | null = getCurrentInstance()
/** 获取菜单列表 */
const navs = computed((): TabsItemProps[] => {
if (!instance || !instance.slots.default) {
return []
}
return instance.slots.default().map((e: TabsItemInstance): TabsItemProps => {
return e.props
})
})
</script>

<template>
{{ activeName }}
<div role="tab" class="f-tabs">
<!-- 标签列表 -->
<div class="f-tabs__navs">
<div
v-for="(item, index) in navs"
v-for="(item, index) in children"
:key="index"
class="f-tabs__nav-item"
:class="['f-tabs__nav-item', { 'f-tabs__nav-active': item.name === activeName }]"
@click="changeNavs(item.name)"
>
{{ item.label }}
Expand Down
19 changes: 15 additions & 4 deletions packages/fighting-theme/src/radio-group.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,24 @@
// 背景状态
// 背景状态默认元素之间的间距为 0
&.f-radio-group__background {
background: #f8f9fb;
background: #ececf1;
flex-wrap: nowrap;
column-gap: var(--radio-group-column-gap, 0);
row-gap: var(--radio-group-row-gap, 0);

// 选中的
.f-radio__checked {
background-color: #fff;
.f-radio {
.f-radio__text {
color: rgb(142, 142, 160);
}

// 选中的
&.f-radio__checked {
background-color: #fff;

.f-radio__text {
color: #2d5af1;
}
}
}

// 不同尺寸
Expand Down
2 changes: 2 additions & 0 deletions packages/fighting-theme/src/tabs-item.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.f-tabs-item {
padding: 4px 8px;
box-sizing: border-box;
}
15 changes: 14 additions & 1 deletion packages/fighting-theme/src/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,23 @@
// 菜单栏
.f-tabs__navs {
display: flex;
justify-content: space-between;
border-bottom: 1px solid #ddd;
column-gap: 12px;

// 每一项
.f-tabs__nav-item {
cursor: pointer;
padding: 10px 7px;
box-sizing: border-box;
font-size: 15px;
user-select: none;
border-bottom: 2px solid transparent;

// 选中状态
&.f-tabs__nav-active {
color: #2d5af1;
border-color: #2d5af1;
}
}
}

Expand Down
17 changes: 7 additions & 10 deletions start/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
<script lang="ts" setup>
import { ref } from 'vue'
const name = ref('2')
</script>
<script lang="ts" setup></script>

<template>
{{ name }}
<f-tabs v-model="name">
<f-tabs-item name="1" label="如烟">
<f-tabs>
<f-tabs-item label="如烟">
<p>七岁的那一年,抓住那只蝉,以为能抓住夏天;</p>
<p>十七岁的那年,吻过他的脸,就以为和他能永远。</p>
</f-tabs-item>
<f-tabs-item name="2" label="盛夏光年">
<f-tabs-item label="盛夏光年">
<p>长大 难道是人必经的溃烂。</p>
</f-tabs-item>
<f-tabs-item name="3" label="我心中尚未崩坏的地方">
<f-tabs-item label="我心中尚未崩坏的地方">
<p>就算会有一天,没人与我合唱,至少在我的心中,还有个尚未崩坏的地方。</p>
</f-tabs-item>
</f-tabs>
</template>

<style lang="scss" scoped></style>

0 comments on commit 3f93b2d

Please sign in to comment.