✨(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 "./index.less";
|
||||||
import { LayIcon } from "@layui/icons-vue";
|
import { LayIcon } from "@layui/icons-vue";
|
||||||
import tabItem from "../tabItem/index.vue";
|
import tabItem from "../tabItem/index.vue";
|
||||||
|
import RenderTitle from "./renderTitle.vue";
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
computed,
|
computed,
|
||||||
@ -21,8 +22,10 @@ import {
|
|||||||
onMounted,
|
onMounted,
|
||||||
nextTick,
|
nextTick,
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
|
reactive,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { useResizeObserver } from "@vueuse/core";
|
import { useResizeObserver } from "@vueuse/core";
|
||||||
|
import { TabData, TabInjectKey } from "./interface";
|
||||||
|
|
||||||
export type tabPositionType = "top" | "bottom" | "left" | "right";
|
export type tabPositionType = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
@ -40,6 +43,7 @@ const slot = useSlots();
|
|||||||
const slots = slot.default && slot.default();
|
const slots = slot.default && slot.default();
|
||||||
const childrens: Ref<VNode[]> = ref([]);
|
const childrens: Ref<VNode[]> = ref([]);
|
||||||
const slotsChange = ref(true);
|
const slotsChange = ref(true);
|
||||||
|
const tabMap = reactive(new Map<number, TabData>());
|
||||||
|
|
||||||
const setItemInstanceBySlot = function (nodeList: VNode[]) {
|
const setItemInstanceBySlot = function (nodeList: VNode[]) {
|
||||||
nodeList?.map((item) => {
|
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) {
|
const change = function (id: any) {
|
||||||
if (props.beforeLeave && props.beforeLeave(id) === false) {
|
if (props.beforeLeave && props.beforeLeave(id) === false) {
|
||||||
return;
|
return;
|
||||||
@ -294,16 +324,16 @@ provide("slotsChange", slotsChange);
|
|||||||
:style="tabBarStyle"
|
:style="tabBarStyle"
|
||||||
></div>
|
></div>
|
||||||
<li
|
<li
|
||||||
v-for="(children, index) in childrens"
|
v-for="(children, index) in tabItems"
|
||||||
:key="children.props?.id"
|
:key="children.id"
|
||||||
:class="[children.props?.id === active ? 'layui-this' : '']"
|
:class="[children.id === active ? 'layui-this' : '']"
|
||||||
@click.stop="change(children.props?.id)"
|
@click.stop="change(children.id)"
|
||||||
>
|
>
|
||||||
{{ children.props?.title }}
|
<RenderTitle :tabItemData="children"></RenderTitle>
|
||||||
<i
|
<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"
|
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>
|
></i>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 {
|
export interface LayTabItemProps {
|
||||||
id: string;
|
id: string;
|
||||||
@ -17,9 +28,29 @@ const props = withDefaults(defineProps<LayTabItemProps>(), {
|
|||||||
closable: true,
|
closable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
const slots = useSlots();
|
||||||
const active = inject("active");
|
const active = inject("active");
|
||||||
const slotsChange: Ref<boolean> = inject("slotsChange") as Ref<boolean>;
|
const slotsChange: Ref<boolean> = inject("slotsChange") as Ref<boolean>;
|
||||||
slotsChange.value = !slotsChange.value;
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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 简约模式
|
::: title 简约模式
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user