diff --git a/package.json b/package.json index 1ac964c9..75a3bd7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vxe-pc-ui", - "version": "4.0.84", + "version": "4.0.85", "description": "A vue based PC component library", "scripts": { "update": "npm install --legacy-peer-deps", diff --git a/packages/tabs/src/tabs.ts b/packages/tabs/src/tabs.ts index 4c7ae7c0..919363f5 100644 --- a/packages/tabs/src/tabs.ts +++ b/packages/tabs/src/tabs.ts @@ -1,7 +1,6 @@ import { defineComponent, ref, h, reactive, PropType, provide, computed, createCommentVNode, watch, nextTick, onMounted } from 'vue' import XEUtils from 'xe-utils' import { createEvent, getConfig, getIcon, permission } from '../../ui' -import VxeTabPaneComponent from './tab-pane' import { getSlotVNs } from '../../ui/src/vn' import { toCssUnit } from '../..//ui/src/dom' @@ -29,8 +28,8 @@ export default defineComponent({ 'update:modelValue', 'change', 'tab-change-fail', - 'tab-remove', - 'tab-remove-fail', + 'tab-close', + 'tab-close-fail', 'tab-click', 'tab-load' ] as VxeTabsEmits, @@ -74,18 +73,6 @@ export default defineComponent({ return staticTabs.filter(handleFilterTab) }) - const computeActiveOptionTab = computed(() => { - const { activeName } = reactData - const tabOptions = computeTabOptions.value - return tabOptions.find(item => item.name === activeName) - }) - - const computeActiveDefaultTab = computed(() => { - const { activeName } = reactData - const tabStaticOptions = computeTabStaticOptions.value - return tabStaticOptions.find(item => item.name === activeName) - }) - const computeMaps: VxeTabsPrivateComputed = { } @@ -162,7 +149,7 @@ export default defineComponent({ emit('update:modelValue', value) dispatchEvent('tab-click', { name }, evnt) if (name !== activeName) { - if (!beforeMethod || beforeMethod({ $tabs: $xeTabs, name })) { + if (!beforeMethod || beforeMethod({ $tabs: $xeTabs, name, oldName: activeName, newName: name })) { dispatchEvent('change', { value, name, oldName: activeName, newName: name }, evnt) } else { dispatchEvent('tab-change-fail', { value, name, oldName: activeName, newName: name }, evnt) @@ -173,15 +160,21 @@ export default defineComponent({ } } - const handleCloseTabEvent = (evnt: KeyboardEvent, item: VxeTabPaneDefines.TabConfig | VxeTabPaneProps) => { + const handleCloseTabEvent = (evnt: KeyboardEvent, item: VxeTabPaneDefines.TabConfig | VxeTabPaneProps, index: number, list: VxeTabsPropTypes.Options | VxeTabPaneDefines.TabConfig[]) => { + evnt.stopPropagation() + const { activeName } = reactData const beforeMethod = props.beforeCloseMethod || getConfig().tabs.beforeCloseMethod const { name } = item - const value = name - evnt.stopPropagation() - if (!beforeMethod || beforeMethod({ $tabs: $xeTabs, name })) { - dispatchEvent('tab-remove', { value, name }, evnt) + const value = activeName + let nextName = value + if (activeName === name) { + const nextItem = index < list.length - 1 ? list[index + 1] : list[index - 1] + nextName = nextItem ? nextItem.name : null + } + if (!beforeMethod || beforeMethod({ $tabs: $xeTabs, value, name, nextName })) { + dispatchEvent('tab-close', { value, name, nextName }, evnt) } else { - dispatchEvent('tab-remove-fail', { value, name }, evnt) + dispatchEvent('tab-close-fail', { value, name, nextName }, evnt) } } @@ -203,7 +196,7 @@ export default defineComponent({ h('div', { ref: refHeaderElem, class: 'vxe-tabs-header--wrapper' - }, list.map((item) => { + }, list.map((item, index) => { const { title, titleWidth, titleAlign, icon, name, slots } = item const tabSlot = slots ? slots.tab : null const itemWidth = titleWidth || allTitleWidth @@ -241,7 +234,7 @@ export default defineComponent({ ? h('div', { class: 'vxe-tabs-header--close-btn', onClick (evnt: KeyboardEvent) { - handleCloseTabEvent(evnt, item) + handleCloseTabEvent(evnt, item, index, list) } }, [ h('i', { @@ -262,37 +255,7 @@ export default defineComponent({ ]) } - const renderOptionPane = (item: VxeTabPaneProps) => { - const { initNames, activeName } = reactData - const { name, slots } = item - const defaultSlot = slots ? slots.default : null - return h(VxeTabPaneComponent, { - key: name, - ...item - }, { - default () { - return name && initNames.includes(name) - ? h('div', { - key: name, - class: ['vxe-tabs-pane--item', { - 'is--visible': activeName === name - }] - }, callSlot(defaultSlot, {})) - : createCommentVNode() - } - }) - } - - const renderOptionContent = (tabList: VxeTabsPropTypes.Options) => { - const { destroyOnClose } = props - const activeOptionTab = computeActiveOptionTab.value - if (destroyOnClose) { - return activeOptionTab ? [renderOptionPane(activeOptionTab)] : createCommentVNode() - } - return tabList.map(renderOptionPane) - } - - const renderDefaultPane = (item: VxeTabPaneDefines.TabConfig) => { + const renderTabPane = (item: VxeTabPaneProps | VxeTabPaneDefines.TabConfig) => { const { initNames, activeName } = reactData const { name, slots } = item const defaultSlot = slots ? slots.default : null @@ -302,17 +265,18 @@ export default defineComponent({ class: ['vxe-tabs-pane--item', { 'is--visible': activeName === name }] - }, callSlot(defaultSlot, {})) + }, callSlot(defaultSlot, { name })) : createCommentVNode() } - const renderDefaultContent = (staticTabList: VxeTabPaneDefines.TabConfig[]) => { + const renderTabContent = (list: VxeTabsPropTypes.Options | VxeTabPaneDefines.TabConfig[]) => { const { destroyOnClose } = props - const activeDefaultTab = computeActiveDefaultTab.value + const { activeName } = reactData + const activeDefaultTab = list.find(item => item.name === activeName) if (destroyOnClose) { - return activeDefaultTab ? [renderDefaultPane(activeDefaultTab)] : createCommentVNode() + return activeDefaultTab ? [renderTabPane(activeDefaultTab)] : createCommentVNode() } - return staticTabList.map(renderDefaultPane) + return list.map(renderTabPane) } const renderVN = () => { @@ -338,11 +302,15 @@ export default defineComponent({ renderTabHeader(defaultSlot ? tabStaticOptions : tabOptions), h('div', { class: 'vxe-tabs-pane' - }, defaultSlot ? renderDefaultContent(tabStaticOptions) : renderOptionContent(tabOptions)) + }, renderTabContent(defaultSlot ? tabStaticOptions : tabOptions)) ]) } watch(() => props.modelValue, (val) => { + const { initNames } = reactData + if (!initNames.includes(val)) { + initNames.push(val) + } reactData.activeName = val }) @@ -350,6 +318,17 @@ export default defineComponent({ updateLineStyle() }) + const optsFlag = ref(0) + watch(() => props.options ? props.options.length : -1, () => { + optsFlag.value++ + }) + watch(() => props.options, () => { + optsFlag.value++ + }) + watch(optsFlag, () => { + updateLineStyle() + }) + onMounted(() => { updateLineStyle() }) diff --git a/types/components/tabs.d.ts b/types/components/tabs.d.ts index 99a861cd..7c402352 100644 --- a/types/components/tabs.d.ts +++ b/types/components/tabs.d.ts @@ -36,10 +36,14 @@ export namespace VxeTabsPropTypes { export type BeforeChangeMethod = (params: { $tabs: VxeTabsConstructor name: VxeTabsPropTypes.ModelValue + oldName: VxeTabsPropTypes.ModelValue + newName: VxeTabsPropTypes.ModelValue }) => boolean export type BeforeCloseMethod = (params: { $tabs: VxeTabsConstructor + value: VxeTabsPropTypes.ModelValue name: VxeTabsPropTypes.ModelValue + nextName: VxeTabsPropTypes.ModelValue | null }) => boolean } @@ -81,8 +85,8 @@ export type VxeTabsEmits = [ 'update:modelValue', 'change', 'tab-change-fail', - 'tab-remove', - 'tab-remove-fail', + 'tab-close', + 'tab-close-fail', 'tab-click', 'tab-load' ] @@ -100,16 +104,20 @@ export namespace VxeTabsDefines { } export interface TabChangeFailEventParams extends TabsEventParams, ChangeParams { + value: VxeTabsPropTypes.ModelValue value: VxeTabsPropTypes.ModelValue name: VxeTabsPropTypes.ModelValue } - export interface TabRemoveEventParams extends TabsEventParams { + export interface TabCloseEventParams extends TabsEventParams { + value: VxeTabsPropTypes.ModelValue name: VxeTabsPropTypes.ModelValue + nextName: VxeTabsPropTypes.ModelValue | null } - export interface TabRemoveFailEventParams extends TabsEventParams { + export interface TabCloseFailEventParams extends TabsEventParams { name: VxeTabsPropTypes.ModelValue + nextName: VxeTabsPropTypes.ModelValue | null } export interface TabClickEventParams extends TabsEventParams { @@ -124,8 +132,8 @@ export namespace VxeTabsDefines { export type VxeTabsEventProps = { onChange?: VxeTabsEvents.Change onTabChangeFail?: VxeTabsEvents.TabChangeFail - onTabRemove?: VxeTabsEvents.TabRemove - onTabRemoveFail?: VxeTabsEvents.TabRemoveFail + onTabClose?: VxeTabsEvents.TabClose + onTabCloseFail?: VxeTabsEvents.TabCloseFail onTabClick?: VxeTabsEvents.TabClick onTabLoad?: VxeTabsEvents.TabLoad } @@ -133,8 +141,8 @@ export type VxeTabsEventProps = { export interface VxeTabsListeners { change?: VxeTabsEvents.Change tabChangeFail?: VxeTabsEvents.TabChangeFail - tabRemove?: VxeTabsEvents.TabRemove - tabRemoveFail?: VxeTabsEvents.TabRemoveFail + tabClose?: VxeTabsEvents.TabClose + tabCloseFail?: VxeTabsEvents.TabCloseFail tabClick?: VxeTabsEvents.TabClick tabLoad?: VxeTabsEvents.TabLoad } @@ -142,18 +150,29 @@ export interface VxeTabsListeners { export namespace VxeTabsEvents { export type Change = (params: VxeTabsDefines.ChangeEventParams) => void export type TabChangeFail = (params: VxeTabsDefines.TabChangeFailEventParams) => void - export type TabRemove = (params: VxeTabsDefines.TabRemoveEventParams) => void - export type TabRemoveFail = (params: VxeTabsDefines.TabRemoveFailEventParams) => void + export type TabClose = (params: VxeTabsDefines.TabCloseEventParams) => void + export type TabCloseFail = (params: VxeTabsDefines.TabCloseFailEventParams) => void export type TabClick = (params: VxeTabsDefines.TabClickEventParams) => void export type TabLoad = (params: VxeTabsDefines.TabLoadEventParams) => void } export namespace VxeTabsSlotTypes { - export interface DefaultSlotParams {} + export interface DefaultSlotParams { + name: VxeTabsPropTypes.ModelValue + } } export interface VxeTabsSlots { default: (params: VxeTabsSlotTypes.DefaultSlotParams) => any + + /** + * 自定义插槽模板 + */ + [key: string]: ((params: { + name: VxeTabsPropTypes.ModelValue + + [key: string]: any + }) => any) | undefined } export const Tabs: typeof VxeTabs