feat: tabs
parent
5733a2f2c9
commit
d1eb0dbd13
|
@ -5,7 +5,7 @@
|
|||
@namespace: vben;
|
||||
|
||||
// tabs
|
||||
@multiple-height: 30px;
|
||||
@multiple-height: 36px;
|
||||
|
||||
// headers
|
||||
@header-height: 48px;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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({
|
|||
|
||||
<SwitchItem title={t('layout.setting.tabs')} event={HandlerEnum.TABS_SHOW} def={unref(getShowMultipleTab)} />
|
||||
|
||||
<SwitchItem
|
||||
title={t('layout.setting.tabsIcon')}
|
||||
event={HandlerEnum.TABS_SHOW_ICON}
|
||||
def={unref(getShowMultipleTabIcon)}
|
||||
disabled={!unref(getShowMultipleTab)}
|
||||
/>
|
||||
|
||||
<SwitchItem
|
||||
title={t('layout.setting.tabsRedoBtn')}
|
||||
event={HandlerEnum.TABS_SHOW_REDO}
|
||||
|
|
|
@ -36,6 +36,7 @@ export enum HandlerEnum {
|
|||
TABS_SHOW_REDO,
|
||||
TABS_SHOW,
|
||||
TABS_SHOW_FOLD,
|
||||
TABS_SHOW_ICON,
|
||||
|
||||
LOCK_TIME,
|
||||
FULL_CONTENT,
|
||||
|
|
|
@ -149,6 +149,9 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
|
|||
case HandlerEnum.TABS_SHOW:
|
||||
return { multiTabsSetting: { show: value } }
|
||||
|
||||
case HandlerEnum.TABS_SHOW_ICON:
|
||||
return { multiTabsSetting: { showIcon: value } }
|
||||
|
||||
case HandlerEnum.TABS_SHOW_REDO:
|
||||
return { multiTabsSetting: { showRedo: value } }
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
@menu-event="handleMenuEvent"
|
||||
>
|
||||
<div :class="`${prefixCls}__info`" @contextmenu="handleContext" v-if="getIsTabs">
|
||||
<span v-if="getShowTabsIcon" :class="`${prefixCls}__prefix-icon`" @click="handleContext">
|
||||
<Icon :icon="prefixIconType" />
|
||||
</span>
|
||||
<span class="ml-1">{{ getTitle }}</span>
|
||||
</div>
|
||||
<span :class="`${prefixCls}__extra-quick`" v-else @click="handleContext">
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -85,6 +85,7 @@ export default {
|
|||
breadcrumb: '面包屑',
|
||||
breadcrumbIcon: '面包屑图标',
|
||||
tabs: '标签页',
|
||||
tabsIcon: '标签页图标',
|
||||
tabDetail: '标签详情页',
|
||||
tabsQuickBtn: '标签页快捷按钮',
|
||||
tabsRedoBtn: '标签页刷新按钮',
|
||||
|
|
|
@ -119,6 +119,8 @@ const setting: ProjectConfig = {
|
|||
cache: false,
|
||||
// 开启
|
||||
show: true,
|
||||
// 显示图标
|
||||
showIcon: true,
|
||||
// 开启快速操作
|
||||
showQuick: true,
|
||||
// 是否可以拖拽
|
||||
|
|
|
@ -39,6 +39,7 @@ export interface MenuSetting {
|
|||
export interface MultiTabsSetting {
|
||||
cache: boolean
|
||||
show: boolean
|
||||
showIcon: boolean
|
||||
showQuick: boolean
|
||||
canDrag: boolean
|
||||
showRedo: boolean
|
||||
|
|
Loading…
Reference in New Issue