(component): [tabitem] 新增 icon 属性

This commit is contained in:
sight 2022-09-17 23:50:39 +08:00
parent 993d657cf3
commit e1a41552c5
5 changed files with 64 additions and 46 deletions

View File

@ -14,10 +14,7 @@ export default (): UserConfigExport => {
}, },
], ],
}, },
plugins: [ plugins: [vue(), vueJsx()],
vue(),
vueJsx(),
],
build: { build: {
cssCodeSplit: false, cssCodeSplit: false,
outDir: "lib", outDir: "lib",

View File

@ -26,6 +26,7 @@ import {
h, h,
createTextVNode, createTextVNode,
isVNode, isVNode,
Fragment,
} from "vue"; } from "vue";
import { useResizeObserver } from "@vueuse/core"; import { useResizeObserver } from "@vueuse/core";
import { TabData, TabInjectKey } from "./interface"; import { TabData, TabInjectKey } from "./interface";
@ -44,7 +45,6 @@ export interface LayTabProps {
const slot = useSlots(); const slot = useSlots();
const childrens: Ref<VNode[]> = ref([]); const childrens: Ref<VNode[]> = ref([]);
const slotsChange = ref(true);
const tabMap = reactive(new Map<number, TabData>()); const tabMap = reactive(new Map<number, TabData>());
const setItemInstanceBySlot = function (nodeList: VNode[]) { const setItemInstanceBySlot = function (nodeList: VNode[]) {
@ -266,16 +266,27 @@ const update = () => {
} }
}; };
const renderTabChild = (child: TabData) => { const renderTabIcon = (attrs: Record<string, unknown>) => {
if (child.slots?.title) { const tab = attrs.tabData as TabData;
return () => h("span", child.slots?.title && child.slots.title()); if (typeof tab.icon === "function") {
return tab.icon();
} else if (typeof tab.icon === "string") {
return h(LayIcon, {
type: tab.icon,
style: "margin-right: 8px;",
});
} }
};
if (typeof child.title === "function") { const renderTabTitle = (attrs: Record<string, unknown>) => {
// @ts-ignore const tab = attrs.tabData as TabData;
return () => child.title(); if (tab.slots?.title) {
} else if (typeof child.title === "string") { return h(Fragment, tab.slots?.title && tab.slots.title());
return () => createTextVNode(child.title as string); }
if (typeof tab.title === "function") {
return tab.title();
} else if (typeof tab.title === "string") {
return createTextVNode(tab.title as string);
} }
}; };
@ -309,7 +320,6 @@ onMounted(() => {
}); });
provide("active", active); provide("active", active);
provide("slotsChange", slotsChange);
</script> </script>
<template> <template>
@ -339,16 +349,23 @@ provide("slotsChange", slotsChange);
:style="tabBarStyle" :style="tabBarStyle"
></div> ></div>
<li <li
v-for="(children, index) in tabItems" v-for="(child, index) in tabItems"
:key="children.id" :key="child.id"
:class="[children.id === active ? 'layui-this' : '']" :class="[child.id === active ? 'layui-this' : '']"
@click.stop="change(children.id)" @click.stop="change(child.id)"
> >
<RenderFunction :renderFunc="renderTabChild(children)" /> <span>
<RenderFunction
v-if="child['icon']"
:renderFunc="renderTabIcon"
:tabData="child"
/>
<RenderFunction :renderFunc="renderTabTitle" :tabData="child" />
</span>
<i <i
v-if="allowClose && children.closable != false" v-if="allowClose && child.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.id)" @click.stop="close(index, child.id)"
></i> ></i>
</li> </li>
</ul> </ul>

View File

@ -5,6 +5,7 @@ export const TabInjectKey = Symbol("layuiTab");
export interface TabData { export interface TabData {
id: string; id: string;
title?: string | Function; title?: string | Function;
icon?: string | Function;
closable?: string | boolean; closable?: string | boolean;
slots: Slots; slots: Slots;
} }

View File

@ -21,6 +21,7 @@ import { TabInjectKey, TabsContext } from "../tab/interface";
export interface LayTabItemProps { export interface LayTabItemProps {
id: string; id: string;
title?: string | Function; title?: string | Function;
icon?: string | Function;
closable?: boolean | string; closable?: boolean | string;
} }
@ -31,13 +32,12 @@ const props = withDefaults(defineProps<LayTabItemProps>(), {
const instance = getCurrentInstance(); const instance = getCurrentInstance();
const slots = useSlots(); const slots = useSlots();
const active = inject("active"); const active = inject("active");
const slotsChange: Ref<boolean> = inject("slotsChange") as Ref<boolean>;
slotsChange.value = !slotsChange.value;
const tabsCtx = inject<Partial<TabsContext>>(TabInjectKey, {}); const tabsCtx = inject<Partial<TabsContext>>(TabInjectKey, {});
const data = reactive({ const data = reactive({
id: props.id, id: props.id,
title: props.title, title: props.title,
icon: props.icon,
closable: props.closable, closable: props.closable,
slots: slots, slots: slots,
}); });

View File

@ -36,63 +36,65 @@ export default {
::: :::
::: title 标题插槽 ::: title
::: :::
::: demo 通过 `allow-close` 属性, 启用选项可关闭 ::: demo 通过 `icon` 属性, 快速设置前置图标, 也可通过 title 插槽实现
<template> <template>
<lay-tab v-model="current11" :allow-close="true"> <lay-tab v-model="current11" :allow-close="true">
<lay-tab-item id="1"> <lay-tab-item id="1" title="选项一">
<template #title>
<lay-icon type="layui-icon-console"></lay-icon>
<span style="margin-left:10px">选项一</span>
</template>
<div style="padding:20px">选项一</div> <div style="padding:20px">选项一</div>
</lay-tab-item> </lay-tab-item>
<lay-tab-item id="2"> <lay-tab-item id="2" title="选项二" icon="layui-icon-console">
<template #title>
<lay-icon type="layui-icon-user"></lay-icon>
<span style="margin-left:10px">选项二</span>
</template>
<div style="padding:20px">选项二</div> <div style="padding:20px">选项二</div>
</lay-tab-item> </lay-tab-item>
<lay-tab-item id="3"> <lay-tab-item id="3" title="选项三" :icon="renderIconFunc">
<template #title>
<lay-icon type="layui-icon-set"></lay-icon>
<span style="margin-left:10px">选项三</span>
</template>
<div style="padding:20px">选项三</div> <div style="padding:20px">选项三</div>
</lay-tab-item> </lay-tab-item>
<lay-tab-item id="4" :title="renderTitleFunc"> <lay-tab-item id="4">
<template #title>
选项四
<lay-icon type="layui-icon-set" style="margin-left:8px"></lay-icon>
</template>
<div style="padding:20px">选项四</div> <div style="padding:20px">选项四</div>
</lay-tab-item> </lay-tab-item>
<lay-tab-item id="5" :title="renderTitleFunc">
<div style="padding:20px">选项五</div>
</lay-tab-item>
</lay-tab> </lay-tab>
</template> </template>
<script> <script>
import { ref,h , resolveComponent} from 'vue' import { ref,h , Fragment,resolveComponent} from 'vue'
export default { export default {
setup() { setup() {
const LayIcon = resolveComponent("LayIcon"); const LayIcon = resolveComponent("LayIcon");
const current11 = ref("1") const current11 = ref("1")
const renderIconFunc = () => h("span", {
style: "margin-right: 8px;"
},"🚧");
const renderTitleFunc = () => [ const renderTitleFunc = () => [
h(LayIcon, h(LayIcon,
{ {
type: "layui-icon-component", type: "layui-icon-component",
style: "margin-right: 8px;",
}), }),
h("span", h("span",
{ {
style: "margin-left: 10px; color: red", style: "color: red",
}, },
"选项四") "选项五"),
] ]
return { return {
current11, current11,
renderTitleFunc renderTitleFunc,
renderIconFunc,
} }
} }
} }
@ -418,7 +420,8 @@ export default {
| 属性 | 描述 | 类型 | 默认值 | 可选值 | | 属性 | 描述 | 类型 | 默认值 | 可选值 |
| -------- | --------------------- | ---------------- | ------- | -------------- | | -------- | --------------------- | ---------------- | ------- | -------------- |
| id | 唯一标识 | `string` | - | - | | id | 唯一标识 | `string` | - | - |
| title | 头部标题,支持渲染函数 | `string` `vnode` | - | - | | title | 头部标题 | `string` `VNode` | - | - |
| icon | 前置图标| `string` `VNode`
| closable | 允许关闭 | `boolean` | `false` | `true` `false` | | closable | 允许关闭 | `boolean` | `false` | `true` `false` |
::: :::