(component): [tab]新增 title 插槽

This commit is contained in:
sight 2022-06-29 16:23:13 +08:00
parent 36323688bb
commit a7076b66a5
5 changed files with 161 additions and 8 deletions

View 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>

View File

@ -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>

View 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;
}

View File

@ -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>

View File

@ -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 简约模式
::: :::