From d1eb0dbd13f5f2969fdf0cf31d4239e9898dda73 Mon Sep 17 00:00:00 2001 From: xingyu Date: Fri, 12 May 2023 17:35:14 +0800 Subject: [PATCH] feat: tabs --- src/design/var/index.less | 2 +- src/hooks/setting/useMultipleTabSetting.ts | 3 + src/layouts/default/setting/SettingDrawer.tsx | 9 +- src/layouts/default/setting/enum.ts | 1 + src/layouts/default/setting/handler.ts | 3 + .../default/tabs/components/TabContent.vue | 20 +++ src/layouts/default/tabs/index.less | 124 +++++++++++++----- src/locales/lang/en/layout.ts | 1 + src/locales/lang/zh-CN/layout.ts | 1 + src/settings/projectSetting.ts | 2 + src/types/config.d.ts | 1 + 11 files changed, 132 insertions(+), 35 deletions(-) diff --git a/src/design/var/index.less b/src/design/var/index.less index 1689f76d..11a8d566 100644 --- a/src/design/var/index.less +++ b/src/design/var/index.less @@ -5,7 +5,7 @@ @namespace: vben; // tabs -@multiple-height: 30px; +@multiple-height: 36px; // headers @header-height: 48px; diff --git a/src/hooks/setting/useMultipleTabSetting.ts b/src/hooks/setting/useMultipleTabSetting.ts index 0a3225c3..65a65820 100644 --- a/src/hooks/setting/useMultipleTabSetting.ts +++ b/src/hooks/setting/useMultipleTabSetting.ts @@ -9,6 +9,8 @@ export function useMultipleTabSetting() { const getShowMultipleTab = computed(() => appStore.getMultiTabsSetting.show) + const getShowMultipleTabIcon = computed(() => appStore.getMultiTabsSetting.showIcon) + const getShowQuick = computed(() => appStore.getMultiTabsSetting.showQuick) const getShowRedo = computed(() => appStore.getMultiTabsSetting.showRedo) @@ -21,6 +23,7 @@ export function useMultipleTabSetting() { return { setMultipleTabSetting, getShowMultipleTab, + getShowMultipleTabIcon, getShowQuick, getShowRedo, getShowFold diff --git a/src/layouts/default/setting/SettingDrawer.tsx b/src/layouts/default/setting/SettingDrawer.tsx index 728432bc..a8fac932 100644 --- a/src/layouts/default/setting/SettingDrawer.tsx +++ b/src/layouts/default/setting/SettingDrawer.tsx @@ -72,7 +72,7 @@ export default defineComponent({ const { getShowHeader, getFixed: getHeaderFixed, getHeaderBgColor, getShowSearch } = useHeaderSetting() - const { getShowMultipleTab, getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting() + const { getShowMultipleTab, getShowMultipleTabIcon, getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting() const getShowMenuRef = computed(() => { return unref(getShowMenu) && !unref(getIsHorizontal) @@ -255,6 +255,13 @@ export default defineComponent({ + +
+ + + {{ getTitle }}
@@ -26,6 +29,7 @@ import { TabContentProps } from '../types' import { useDesign } from '@/hooks/web/useDesign' import { useI18n } from '@/hooks/web/useI18n' import { useTabDropdown } from '../useTabDropdown' +import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting' defineOptions({ name: 'TabContent' }) @@ -46,10 +50,26 @@ const getTitle = computed(() => { const getIsTabs = computed(() => !props.isExtra) +const prefixIconType = computed(() => { + if (props.tabItem.meta.icon) { + return props.tabItem.meta.icon + } else if (props.tabItem.path === '/dashboard/analysis') { + return 'ant-design:home-outlined' + } else { + return 'ant-design:code' + } +}) + const getTrigger = computed((): ('contextmenu' | 'click' | 'hover')[] => (unref(getIsTabs) ? ['contextmenu'] : ['click'])) const { getDropMenuList, handleMenuEvent, handleContextMenu } = useTabDropdown(props as TabContentProps, getIsTabs) +const { getShowMultipleTabIcon } = useMultipleTabSetting() + +const getShowTabsIcon = computed(() => { + return unref(getShowMultipleTabIcon) +}) + function handleContext(e) { props.tabItem && handleContextMenu(props.tabItem)(e) } diff --git a/src/layouts/default/tabs/index.less b/src/layouts/default/tabs/index.less index e6cdeb3d..82cea03b 100644 --- a/src/layouts/default/tabs/index.less +++ b/src/layouts/default/tabs/index.less @@ -3,7 +3,7 @@ html[data-theme='dark'] { .@{prefix-cls} { .ant-tabs-tab { - border-bottom: 1px solid @border-color-base; + border-bottom: none !important; } } } @@ -11,7 +11,7 @@ html[data-theme='dark'] { html[data-theme='light'] { .@{prefix-cls} { .ant-tabs-tab:not(.ant-tabs-tab-active) { - border: 1px solid #d9d9d9 !important; + border: none !important; } } } @@ -21,7 +21,7 @@ html[data-theme='light'] { height: @multiple-height + 2; line-height: @multiple-height + 2; background-color: @component-background; - border-bottom: 1px solid @border-color-base; + box-shadow: 0 1px 4px rgb(0 21 41 / 8%); .ant-tabs-small { height: @multiple-height; @@ -29,45 +29,73 @@ html[data-theme='light'] { .ant-tabs.ant-tabs-card { .ant-tabs-nav { - padding-top: 2px; height: @multiple-height; margin: 0; background-color: @component-background; border: 0; box-shadow: none; + padding-left: 10px; .ant-tabs-nav-container { height: @multiple-height; - padding-top: 2px; + margin-top: 12px; } .ant-tabs-tab { - height: calc(@multiple-height - 2px); - padding-right: 12px; - line-height: calc(@multiple-height - 2px); + height: @multiple-height; + line-height: @multiple-height; color: @text-color-base; background-color: @component-background; - transition: none; + transition: padding 0.3s; + padding: 0 24px; + margin: 0 -14px 0 0 !important; + mask: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANoAAAAkBAMAAAAdqzmBAAAAMFBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlTPQ5AAAAD3RSTlMAr3DvEM8wgCBA379gj5//tJBPAAAAnUlEQVRIx2NgAAM27fj/tAO/xBsYkIHyf9qCT8iWMf6nNQhAsk2f5rYheY7Dnua2/U+A28ZEe8v+F9Ax2v7/F4DbxkUH2wzgtvHTwbYPo7aN2jZq26hto7aN2jZq25Cy7Qvctnw62PYNbls9HWz7S8/G6//PsI6H4396gAUQy1je08W2jxDbpv6nD4gB2uWp+J9eYPsEhv/0BPS1DQBvoBLVZ3BppgAAAABJRU5ErkJggg==); + mask-size: 100% 100%; + position: relative; + z-index: 1; + + .ant-tabs-tab-btn { + color: @text-color-base; + } &:hover { - .ant-tabs-tab-remove { + z-index: 2; + padding: 0 30px 0 36px; + + .ant-tabs-tab-remove .anticon-close { opacity: 1; + + &:hover { + color: #fff; + background-color: #c0c4cc; + } } } .ant-tabs-tab-remove { - width: 8px; - height: 28px; - font-size: 12px; - color: inherit; - opacity: 0; - transition: none; - margin-left: 2px; - margin-right: -4px; + top: 4px; + left: 10px; + margin-right: -10px; + margin-left: 0; - &:hover { - svg { - width: 0.8em; + .anticon-close { + position: relative; + width: 14px; + height: 14px; + font-size: 18px; + color: inherit; + opacity: 0; + transition: opacity 0.15s; + vertical-align: middle; + line-height: 10px; + overflow: hidden; + transform-origin: 100% 50%; + border-radius: 100%; + + &:hover { + svg { + fill: #fff; + } } } } @@ -81,32 +109,51 @@ html[data-theme='light'] { svg { fill: @text-color-base; } + + &:first-child { + padding: 0 16px !important; + } + + &:active { + padding: 0 16px !important; + } } .ant-tabs-tab:not(.ant-tabs-tab-active) { &:hover { - color: @primary-color; + color: inherit; + background-color: #dee1e6; } } .ant-tabs-tab-active { position: relative; - padding-left: 18px; - background: @primary-color; + padding: 0 20px; + color: @primary-color !important; + background: #e8f4ff; border: 0; - transition: none; + font-weight: inherit; + z-index: 3; span { - color: @white !important; + color: @primary-color !important; } - .ant-tabs-tab-remove { + .ant-tabs-tab-btn { + color: @primary-color; + } + + .ant-tabs-tab-remove .anticon-close { opacity: 1; + + svg { + width: 0.6em; + } } svg { - width: 0.7em; - fill: @white; + width: inherit; + fill: @primary-color; } } } @@ -115,7 +162,7 @@ html[data-theme='light'] { padding: 0 6px; .ant-tabs-tab { - margin-right: 3px !important; + margin-right: -20px !important; } } } @@ -130,12 +177,18 @@ html[data-theme='light'] { } } + .ant-tabs-extra-content { + position: relative; + top: 0; + line-height: calc(@multiple-height - 12px) !important; + } + .ant-dropdown-trigger { display: inline-flex; } &--hide-close { - .ant-tabs-tab-remove { + .ant-tabs-tab-remove .anticon-close { opacity: 0 !important; } } @@ -173,11 +226,16 @@ html[data-theme='light'] { width: 100%; height: @multiple-height - 2; padding-left: 0; - margin-left: -10px; - font-size: 12px; + font-size: 14px; cursor: pointer; user-select: none; } + + &__prefix-icon { + & .app-iconify.anticon { + margin-right: 4px; + } + } } } diff --git a/src/locales/lang/en/layout.ts b/src/locales/lang/en/layout.ts index 272b6a20..764b28ff 100644 --- a/src/locales/lang/en/layout.ts +++ b/src/locales/lang/en/layout.ts @@ -84,6 +84,7 @@ export default { breadcrumb: 'Breadcrumbs', breadcrumbIcon: 'Breadcrumbs Icon', tabs: 'Tabs', + tabsIcon: 'Tabs Icon', tabDetail: 'Tab Detail', tabsQuickBtn: 'Tabs quick button', tabsRedoBtn: 'Tabs redo button', diff --git a/src/locales/lang/zh-CN/layout.ts b/src/locales/lang/zh-CN/layout.ts index 2fc7d62a..a2a83932 100644 --- a/src/locales/lang/zh-CN/layout.ts +++ b/src/locales/lang/zh-CN/layout.ts @@ -85,6 +85,7 @@ export default { breadcrumb: '面包屑', breadcrumbIcon: '面包屑图标', tabs: '标签页', + tabsIcon: '标签页图标', tabDetail: '标签详情页', tabsQuickBtn: '标签页快捷按钮', tabsRedoBtn: '标签页刷新按钮', diff --git a/src/settings/projectSetting.ts b/src/settings/projectSetting.ts index 3c5cdf83..56e8eb21 100644 --- a/src/settings/projectSetting.ts +++ b/src/settings/projectSetting.ts @@ -119,6 +119,8 @@ const setting: ProjectConfig = { cache: false, // 开启 show: true, + // 显示图标 + showIcon: true, // 开启快速操作 showQuick: true, // 是否可以拖拽 diff --git a/src/types/config.d.ts b/src/types/config.d.ts index 62e65c11..88b79f7f 100644 --- a/src/types/config.d.ts +++ b/src/types/config.d.ts @@ -39,6 +39,7 @@ export interface MenuSetting { export interface MultiTabsSetting { cache: boolean show: boolean + showIcon: boolean showQuick: boolean canDrag: boolean showRedo: boolean