✨(component): [tab]新增 title 插槽
This commit is contained in:
parent
36323688bb
commit
a7076b66a5
28
package/component/src/component/tab/RenderTitle.vue
Normal file
28
package/component/src/component/tab/RenderTitle.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { h, PropType, defineComponent } from "vue";
|
||||
import { TabData } from "./interface";
|
||||
|
||||
export default defineComponent({
|
||||
name: "RenderTitle",
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "span",
|
||||
},
|
||||
tabItemData: {
|
||||
type: Object as PropType<TabData>,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
return props.tabItemData?.slots?.title
|
||||
? () =>
|
||||
h(
|
||||
props.tag,
|
||||
props.tabItemData?.slots?.title && props.tabItemData?.slots.title()
|
||||
)
|
||||
: () => props.tabItemData?.title;
|
||||
},
|
||||
});
|
||||
</script>
|
@ -8,6 +8,7 @@ export default {
|
||||
import "./index.less";
|
||||
import { LayIcon } from "@layui/icons-vue";
|
||||
import tabItem from "../tabItem/index.vue";
|
||||
import RenderTitle from "./renderTitle.vue";
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@ -21,8 +22,10 @@ import {
|
||||
onMounted,
|
||||
nextTick,
|
||||
CSSProperties,
|
||||
reactive,
|
||||
} from "vue";
|
||||
import { useResizeObserver } from "@vueuse/core";
|
||||
import { TabData, TabInjectKey } from "./interface";
|
||||
|
||||
export type tabPositionType = "top" | "bottom" | "left" | "right";
|
||||
|
||||
@ -40,6 +43,7 @@ const slot = useSlots();
|
||||
const slots = slot.default && slot.default();
|
||||
const childrens: Ref<VNode[]> = ref([]);
|
||||
const slotsChange = ref(true);
|
||||
const tabMap = reactive(new Map<number, TabData>());
|
||||
|
||||
const setItemInstanceBySlot = function (nodeList: VNode[]) {
|
||||
nodeList?.map((item) => {
|
||||
@ -67,6 +71,32 @@ const active = computed({
|
||||
},
|
||||
});
|
||||
|
||||
const tabItems = computed(() => {
|
||||
const tabData: TabData[] = [];
|
||||
childrens.value.forEach((item) => {
|
||||
const tab = tabMap.get(item.props?.id);
|
||||
if (tab) tabData.push(tab);
|
||||
});
|
||||
return tabData;
|
||||
});
|
||||
|
||||
const addItem = (id: number, data: any) => {
|
||||
tabMap.set(id, data);
|
||||
};
|
||||
|
||||
const removeItem = (id: number) => {
|
||||
tabMap.delete(id);
|
||||
};
|
||||
|
||||
provide(
|
||||
TabInjectKey,
|
||||
reactive({
|
||||
active: active,
|
||||
addItem,
|
||||
removeItem,
|
||||
})
|
||||
);
|
||||
|
||||
const change = function (id: any) {
|
||||
if (props.beforeLeave && props.beforeLeave(id) === false) {
|
||||
return;
|
||||
@ -294,16 +324,16 @@ provide("slotsChange", slotsChange);
|
||||
:style="tabBarStyle"
|
||||
></div>
|
||||
<li
|
||||
v-for="(children, index) in childrens"
|
||||
:key="children.props?.id"
|
||||
:class="[children.props?.id === active ? 'layui-this' : '']"
|
||||
@click.stop="change(children.props?.id)"
|
||||
v-for="(children, index) in tabItems"
|
||||
:key="children.id"
|
||||
:class="[children.id === active ? 'layui-this' : '']"
|
||||
@click.stop="change(children.id)"
|
||||
>
|
||||
{{ children.props?.title }}
|
||||
<RenderTitle :tabItemData="children"></RenderTitle>
|
||||
<i
|
||||
v-if="allowClose && children.props?.closable != false"
|
||||
v-if="allowClose && children.closable != false"
|
||||
class="layui-icon layui-icon-close layui-unselect layui-tab-close"
|
||||
@click.stop="close(index, children.props?.id)"
|
||||
@click.stop="close(index, children.id)"
|
||||
></i>
|
||||
</li>
|
||||
</ul>
|
||||
|
16
package/component/src/component/tab/interface.ts
Normal file
16
package/component/src/component/tab/interface.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Slots } from "vue";
|
||||
|
||||
export const TabInjectKey = Symbol("layuiTab");
|
||||
|
||||
export interface TabData {
|
||||
id: string;
|
||||
title?: string;
|
||||
closable?: string | boolean;
|
||||
slots: Slots;
|
||||
}
|
||||
|
||||
export interface TabsContext {
|
||||
active: string;
|
||||
addItem: (id: string, data: TabData) => void;
|
||||
removeItem: (id: string) => void;
|
||||
}
|
@ -5,7 +5,18 @@ export default {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { withDefaults, inject, Ref } from "vue";
|
||||
import {
|
||||
withDefaults,
|
||||
inject,
|
||||
Ref,
|
||||
useSlots,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
reactive,
|
||||
onBeforeUnmount,
|
||||
toRefs,
|
||||
} from "vue";
|
||||
import { TabInjectKey, TabsContext } from "../tab/interface";
|
||||
|
||||
export interface LayTabItemProps {
|
||||
id: string;
|
||||
@ -17,9 +28,29 @@ const props = withDefaults(defineProps<LayTabItemProps>(), {
|
||||
closable: true,
|
||||
});
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const slots = useSlots();
|
||||
const active = inject("active");
|
||||
const slotsChange: Ref<boolean> = inject("slotsChange") as Ref<boolean>;
|
||||
slotsChange.value = !slotsChange.value;
|
||||
const tabsCtx = inject<Partial<TabsContext>>(TabInjectKey, {});
|
||||
|
||||
const data = reactive({
|
||||
id: props.id,
|
||||
title: props.title,
|
||||
closable: props.closable,
|
||||
slots: slots,
|
||||
});
|
||||
|
||||
if (instance?.uid) {
|
||||
tabsCtx.addItem?.(props.id, data);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (instance?.uid) {
|
||||
tabsCtx.removeItem?.(props.id);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -36,6 +36,54 @@ export default {
|
||||
|
||||
:::
|
||||
|
||||
::: title 标题插槽
|
||||
:::
|
||||
|
||||
::: demo
|
||||
|
||||
<template>
|
||||
<lay-tab v-model="current11" :allow-close="true">
|
||||
<lay-tab-item title="选项一" id="1">
|
||||
<template #title>
|
||||
<lay-icon type="layui-icon-console"></lay-icon>
|
||||
<span style="margin-left:10px">选项一</span>
|
||||
</template>
|
||||
<div style="padding:20px">选项一</div>
|
||||
</lay-tab-item>
|
||||
<lay-tab-item title="选项一" id="2">
|
||||
<template #title>
|
||||
<lay-icon type="layui-icon-user"></lay-icon>
|
||||
<span style="margin-left:10px">选项一</span>
|
||||
</template>
|
||||
<div style="padding:20px">选项二</div>
|
||||
</lay-tab-item>
|
||||
<lay-tab-item title="选项一" id="3">
|
||||
<template #title>
|
||||
<lay-icon type="layui-icon-set"></lay-icon>
|
||||
<span style="margin-left:10px">选项一</span>
|
||||
</template>
|
||||
<div style="padding:20px">选项三</div>
|
||||
</lay-tab-item>
|
||||
</lay-tab>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
|
||||
const current11 = ref("1")
|
||||
|
||||
return {
|
||||
current11
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
:::
|
||||
|
||||
::: title 简约模式
|
||||
:::
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user