first commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<span :class="`${prefixCls}__extra-fold`" @click="handleFold">
|
||||
<Icon :icon="getIcon" />
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, unref, computed } from 'vue';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { triggerWindowResize } from '/@/utils/event';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FoldButton',
|
||||
components: { Icon },
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('multiple-tabs-content');
|
||||
const { getShowMenu, setMenuSetting } = useMenuSetting();
|
||||
const { getShowHeader, setHeaderSetting } = useHeaderSetting();
|
||||
|
||||
const getIsUnFold = computed(() => !unref(getShowMenu) && !unref(getShowHeader));
|
||||
|
||||
const getIcon = computed(() => (unref(getIsUnFold) ? 'codicon:screen-normal' : 'codicon:screen-full'));
|
||||
|
||||
function handleFold() {
|
||||
const isUnFold = unref(getIsUnFold);
|
||||
setMenuSetting({
|
||||
show: isUnFold,
|
||||
hidden: !isUnFold,
|
||||
});
|
||||
setHeaderSetting({ show: isUnFold });
|
||||
triggerWindowResize();
|
||||
}
|
||||
|
||||
return { prefixCls, getIcon, handleFold };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<Dropdown :dropMenuList="getDropMenuList" :trigger="getTrigger" @menuEvent="handleMenuEvent" :overlayClassName="prefixCls">
|
||||
<div :class="`${prefixCls}__info`" @contextmenu="handleContext" v-if="getIsTabs">
|
||||
<!-- updateBy:sunjianlei---updateDate:2021-09-03---修改tab切换栏样式:增加前缀图标 -->
|
||||
<!-- <span v-if="showPrefixIcon" :class="`${prefixCls}__prefix-icon`" @click="handleContext">
|
||||
<Icon :icon="prefixIconType" :size="14" />
|
||||
</span> -->
|
||||
<span class="ml-1">{{ getTitle }}</span>
|
||||
</div>
|
||||
<span :class="`${prefixCls}__extra-quick`" v-else @click="handleContext">
|
||||
<Icon icon="ion:chevron-down" />
|
||||
</span>
|
||||
</Dropdown>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
import { defineComponent, computed, unref } from 'vue';
|
||||
import { Dropdown } from '/@/components/Dropdown/index';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
|
||||
import { TabContentProps } from '../types';
|
||||
|
||||
import { TabsThemeEnum } from '/@/enums/appEnum';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useTabDropdown } from '../useTabDropdown';
|
||||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
|
||||
import { useLocaleStore } from '/@/store/modules/locale';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TabContent',
|
||||
components: { Dropdown, Icon },
|
||||
props: {
|
||||
tabItem: {
|
||||
type: Object as PropType<RouteLocationNormalized>,
|
||||
default: null,
|
||||
},
|
||||
isExtra: Boolean,
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('multiple-tabs-content');
|
||||
const { t } = useI18n();
|
||||
|
||||
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
const localeStore = useLocaleStore();
|
||||
const getTitle = computed(() => {
|
||||
const { tabItem: { meta, fullPath } = {} } = props;
|
||||
let title = localeStore.getPathTitle(fullPath);
|
||||
if (title) {
|
||||
return title;
|
||||
}
|
||||
return meta && t(meta.title as string);
|
||||
});
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
|
||||
const getIsTabs = computed(() => !props.isExtra);
|
||||
|
||||
// updateBy:sunjianlei---updateDate:2021-09-03---修改tab切换栏样式:前缀图标类型
|
||||
const prefixIconType = computed(() => {
|
||||
if (props.tabItem.meta.icon) {
|
||||
return props.tabItem.meta.icon;
|
||||
} else if (props.tabItem.path === '/dashboard/analysis') {
|
||||
// 当是首页时返回 home 图标 TODO 此处可能需要动态判断首页路径
|
||||
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);
|
||||
|
||||
function handleContext(e) {
|
||||
props.tabItem && handleContextMenu(props.tabItem)(e);
|
||||
}
|
||||
|
||||
const { getTabsTheme } = useMultipleTabSetting();
|
||||
// 是否显示图标
|
||||
const showPrefixIcon = computed(() => unref(getTabsTheme) === TabsThemeEnum.SMOOTH);
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getDropMenuList,
|
||||
handleMenuEvent,
|
||||
handleContext,
|
||||
getTrigger,
|
||||
getIsTabs,
|
||||
getTitle,
|
||||
prefixIconType,
|
||||
showPrefixIcon,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-multiple-tabs-content';
|
||||
.@{prefix-cls} {
|
||||
.ant-dropdown-menu-item {
|
||||
.ant-dropdown-menu-title-content {
|
||||
.anticon {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
span:not(.anticon) {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<span :class="`${prefixCls}__extra-redo`" @click="handleRedo">
|
||||
<SvgIcon name="reload-01"></SvgIcon>
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useTabs } from '/@/hooks/web/useTabs';
|
||||
import { SvgIcon } from '/@/components/Icon/index';
|
||||
export default defineComponent({
|
||||
name: 'TabRedo',
|
||||
components: { SvgIcon },
|
||||
|
||||
setup() {
|
||||
const loading = ref(false);
|
||||
|
||||
const { prefixCls } = useDesign('multiple-tabs-content');
|
||||
const { refreshPage } = useTabs();
|
||||
|
||||
async function handleRedo() {
|
||||
loading.value = true;
|
||||
await refreshPage();
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
// Animation execution time
|
||||
}, 1200);
|
||||
}
|
||||
return { prefixCls, handleRedo, loading };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,228 @@
|
||||
@prefix-cls: ~'@{namespace}-multiple-tabs';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
.ant-tabs-tab {
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
.@{prefix-cls} {
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
border: 1px solid #e6e6e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls} {
|
||||
z-index: 10;
|
||||
height: @multiple-height + 2;
|
||||
line-height: @multiple-height + 2;
|
||||
background-color: @component-background;
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
box-shadow: 0 4px 4px rgb(0 21 41 / 8%);
|
||||
|
||||
.ant-tabs-small {
|
||||
height: calc(@multiple-height + 4px);
|
||||
}
|
||||
|
||||
.ant-tabs.ant-tabs-card {
|
||||
padding-left: 0px;
|
||||
|
||||
.ant-tabs-nav {
|
||||
height: calc(@multiple-height);
|
||||
margin: 0;
|
||||
background-color: @component-background;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
|
||||
.ant-tabs-nav-wrap {
|
||||
height: @multiple-height;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.ant-tabs-tab {
|
||||
height: calc(@multiple-height - 4px);
|
||||
padding-right: 12px;
|
||||
line-height: calc(@multiple-height - 4px);
|
||||
color: @text-color-base;
|
||||
background-color: @component-background;
|
||||
transition: none;
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: @text-color-base;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-remove {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 4px;
|
||||
|
||||
.anticon-close {
|
||||
width: 8px;
|
||||
height: 12px;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
opacity: 0;
|
||||
transition: none;
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
width: 0.8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: @text-color-base;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
position: relative;
|
||||
padding-left: 18px;
|
||||
color: @white !important;
|
||||
background: @primary-color;
|
||||
border: 1px solid transparent;
|
||||
transition: none;
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: @white;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 0.7em;
|
||||
fill: @white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-nav > div:nth-child(1) {
|
||||
padding: 0 6px;
|
||||
|
||||
.ant-tabs-tab {
|
||||
margin-right: 6px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
font-size: 12px;
|
||||
|
||||
svg {
|
||||
width: 0.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-extra-content {
|
||||
// update-begin--author:liaozhiyang---date:20241016---for:【issues/7345】标签样式切换到极简模式样式错乱
|
||||
// margin-top: 2px;
|
||||
// update-end--author:liaozhiyang---date:20241016---for:【issues/7345】标签样式切换到极简模式样式错乱
|
||||
line-height: @multiple-height !important;
|
||||
}
|
||||
|
||||
.ant-dropdown-trigger {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
&--hide-close {
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
&__extra-quick,
|
||||
&__extra-redo,
|
||||
&__extra-fold {
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: @multiple-height;
|
||||
line-height: @multiple-height;
|
||||
color: @text-color-secondary;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-left: 1px solid @border-color-base;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-base;
|
||||
}
|
||||
|
||||
span[role='img'] {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__extra-redo {
|
||||
span[role='img'] {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: @multiple-height - 2;
|
||||
padding-left: 0;
|
||||
margin-left: -10px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-dropdown-menu {
|
||||
&-title-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.@{prefix-cls} {
|
||||
&-content__info {
|
||||
width: auto;
|
||||
margin-left: 0;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item-remove {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.multiple-tabs__dropdown {
|
||||
.ant-dropdown-content {
|
||||
width: 172px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div :class="getWrapClass">
|
||||
<Tabs
|
||||
type="editable-card"
|
||||
size="small"
|
||||
:animated="false"
|
||||
:hideAdd="true"
|
||||
:tabBarGutter="3"
|
||||
:activeKey="activeKeyRef"
|
||||
@change="handleChange"
|
||||
@edit="handleEdit"
|
||||
>
|
||||
<template v-for="item in getTabsState" :key="item.query ? item.fullPath : item.path">
|
||||
<TabPane :closable="!(item && item.meta && item.meta.affix)">
|
||||
<template #tab>
|
||||
<TabContent :tabItem="item" />
|
||||
</template>
|
||||
</TabPane>
|
||||
</template>
|
||||
|
||||
<template #rightExtra v-if="getShowRedo || getShowQuick">
|
||||
<div class="rightExtra">
|
||||
<TabRedo v-if="getShowRedo" />
|
||||
<!-- <TabContent isExtra :tabItem="$route" v-if="getShowQuick" /> -->
|
||||
<!-- 列表页全屏
|
||||
<FoldButton v-if="getShowFold" />-->
|
||||
<!-- <FullscreenOutlined /> -->
|
||||
<router-link to="/ai" class="ai-icon">
|
||||
<a-tooltip title="AI助手" placement="left">
|
||||
<!-- <svg t="1706259688149" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2056" width="17" height="17">-->
|
||||
<!-- <path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#333333" p-id="19816"></path>-->
|
||||
<!-- </svg>-->
|
||||
<svg t="1737024931936" class="icon" viewBox="0 0 3011 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4248" width="256" height="256"><path d="M2028.242824 558.561882h415.021176V445.620706h-346.352941V321.415529h346.352941V210.823529l-328.463059 10.842353a2367.969882 2367.969882 0 0 0-31.021176-127.09647c341.955765-4.397176 602.352941-13.492706 781.131294-27.286588l41.441882 124.265411-320.933647 14.095059v115.772235h307.802353v124.205177h-307.802353v112.941176h375.506824v127.096471h-375.506824v113.844706c0 30.117647-5.782588 56.982588-17.468235 80.474353a114.447059 114.447059 0 0 1-50.778353 53.187764c-22.287059 11.926588-41.261176 19.154824-56.922353 21.684706-15.36 2.770824-82.522353 5.300706-201.426824 7.529412a5065.065412 5065.065412 0 0 0-31.984941-142.155294c64.632471 4.999529 114.447059 7.529412 149.624471 7.529412 44.574118 0 66.861176-23.190588 66.861176-69.632v-72.463059h-415.081411v-127.096471zM1685.624471 956.717176a1296.865882 1296.865882 0 0 0-27.286589-133.662117 2187.745882 2187.745882 0 0 0 96.978824 3.794823c18.191059 0 32.587294-4.758588 43.248941-14.155294 10.661647-9.697882 17.468235-23.491765 20.239059-41.381647 3.132235-17.889882 6.023529-66.379294 8.432941-145.408 2.590118-79.390118 3.614118-179.621647 3.373177-300.754823h-109.206589c-3.734588 212.389647-24.455529 364.604235-62.102588 456.523294-37.345882 91.919059-77.161412 156.491294-119.567059 193.837176-28.551529-27.888941-59.632941-54.392471-93.123764-79.510588-142.757647 12.830118-265.456941 25.298824-368.037648 37.165176l-15.962352-126.132705c14.095059-0.602353 28.190118-1.385412 42.345411-2.349177V88.003765h377.374118v687.043764c12.227765-1.204706 24.154353-2.349176 35.779765-3.312941 50.838588-95.653647 77.040941-244.555294 78.607058-446.58447h-85.172705V200.944941h87.04c0.301176-47.706353 0.481882-100.412235 0.481882-158.117647h125.168941c0 58.368-0.301176 111.073882-0.963765 158.117647H1957.647059l-9.878588 525.613177c-1.566118 62.464-5.481412 104.688941-11.745883 126.614588-6.324706 22.287059-16.805647 41.441882-31.563294 57.404235-14.757647 16.022588-32.768 27.105882-54.091294 33.430588-21.082353 6.264471-75.896471 10.480941-164.743529 12.649412z m-321.837177-759.567058h-140.227765V327.077647h140.227765V197.150118z m-140.227765 359.604706h140.227765V426.767059h-140.227765v129.867294z m140.227765 229.616941v-129.92753h-140.227765v140.288l140.227765-10.36047zM963.764706 114.326588V843.294118h-155.286588V114.326588H963.764706zM716.679529 843.294118H547.297882l-54.573176-167.032471H227.689412L174.140235 843.294118H5.662118l266.842353-728.96753h182.512941L716.739765 843.294118zM455.981176 556.212706L373.157647 303.585882c-5.300706-16.022588-9.035294-37.165176-11.264-63.548235h-4.216471a269.010824 269.010824 0 0 1-12.709647 61.680941L261.662118 556.212706H455.981176z" fill="#1F1F1F" p-id="4249"></path></svg>
|
||||
</a-tooltip>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
</Tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { RouteLocationNormalized, RouteMeta } from 'vue-router';
|
||||
|
||||
import { defineComponent, computed, unref, ref } from 'vue';
|
||||
|
||||
import { Tabs } from 'ant-design-vue';
|
||||
import TabContent from './components/TabContent.vue';
|
||||
import FoldButton from './components/FoldButton.vue';
|
||||
import TabRedo from './components/TabRedo.vue';
|
||||
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
|
||||
import { initAffixTabs, useTabsDrag } from './useMultipleTabs';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
|
||||
|
||||
import { REDIRECT_NAME } from '/@/router/constant';
|
||||
import { listenerRouteChange } from '/@/logics/mitt/routeChange';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
import Aide from "/@/views/dashboard/ai/components/aide/index.vue"
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MultipleTabs',
|
||||
components: {
|
||||
TabRedo,
|
||||
FoldButton,
|
||||
Tabs,
|
||||
TabPane: Tabs.TabPane,
|
||||
TabContent,
|
||||
Aide,
|
||||
},
|
||||
setup() {
|
||||
const affixTextList = initAffixTabs();
|
||||
const activeKeyRef = ref('');
|
||||
|
||||
useTabsDrag(affixTextList);
|
||||
const tabStore = useMultipleTabStore();
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
|
||||
const { prefixCls } = useDesign('multiple-tabs');
|
||||
const go = useGo();
|
||||
const { getShowQuick, getShowRedo, getShowFold, getTabsTheme } = useMultipleTabSetting();
|
||||
|
||||
const getTabsState = computed(() => {
|
||||
return tabStore.getTabList.filter((item) => !item.meta?.hideTab);
|
||||
});
|
||||
|
||||
const unClose = computed(() => unref(getTabsState).length === 1);
|
||||
|
||||
const getWrapClass = computed(() => {
|
||||
return [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--hide-close`]: unref(unClose),
|
||||
},
|
||||
`${prefixCls}--theme-${unref(getTabsTheme)}`,
|
||||
];
|
||||
});
|
||||
|
||||
listenerRouteChange((route) => {
|
||||
const { name } = route;
|
||||
if (name === REDIRECT_NAME || !route || !userStore.getToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { path, fullPath, meta = {} } = route;
|
||||
const { currentActiveMenu, hideTab } = meta as RouteMeta;
|
||||
const isHide = !hideTab ? null : currentActiveMenu;
|
||||
const p = isHide || fullPath || path;
|
||||
if (activeKeyRef.value !== p) {
|
||||
activeKeyRef.value = p as string;
|
||||
}
|
||||
|
||||
if (isHide) {
|
||||
const findParentRoute = router.getRoutes().find((item) => item.path === currentActiveMenu);
|
||||
|
||||
findParentRoute && tabStore.addTab(findParentRoute as unknown as RouteLocationNormalized);
|
||||
} else {
|
||||
tabStore.addTab(unref(route));
|
||||
}
|
||||
});
|
||||
|
||||
function handleChange(activeKey: any) {
|
||||
activeKeyRef.value = activeKey;
|
||||
go(activeKey, false);
|
||||
}
|
||||
|
||||
// Close the current tab
|
||||
function handleEdit(targetKey: string) {
|
||||
// Added operation to hide, currently only use delete operation
|
||||
if (unref(unClose)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tabStore.closeTabByKey(targetKey, router);
|
||||
}
|
||||
return {
|
||||
prefixCls,
|
||||
unClose,
|
||||
getWrapClass,
|
||||
handleEdit,
|
||||
handleChange,
|
||||
activeKeyRef,
|
||||
getTabsState,
|
||||
getShowQuick,
|
||||
getShowRedo,
|
||||
getShowFold,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import './index.less';
|
||||
@import './tabs.theme.card.less';
|
||||
@import './tabs.theme.smooth.less';
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-multiple-tabs';
|
||||
.@{prefix-cls} {
|
||||
:deep(.anticon) {
|
||||
display: inline-block;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20241016---for:【issues/7345】标签样式切换到极简模式样式错乱
|
||||
.rightExtra {
|
||||
display: flex;
|
||||
:deep(svg) {
|
||||
&:not(.icon) {
|
||||
vertical-align: -0.3em;
|
||||
}
|
||||
}
|
||||
.ai-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
width: 50px;
|
||||
padding: 0 4px;
|
||||
height: 50px;
|
||||
color: @text-color;
|
||||
text-align: center;
|
||||
border-left: 1px solid @border-color-base;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20241016---for:【issues/7345】标签样式切换到极简模式样式错乱
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,236 @@
|
||||
// tabs卡片样式
|
||||
@prefix-cls-theme-card: ~'@{prefix-cls}.@{prefix-cls}--theme-card';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls-theme-card} {
|
||||
.ant-tabs-tab {
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
.@{prefix-cls-theme-card} {
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls-theme-card} {
|
||||
@tabHeight: calc(@multiple-card-height - 10px);
|
||||
|
||||
z-index: 10;
|
||||
height: @multiple-card-height;
|
||||
line-height: @multiple-card-height;
|
||||
background-color: @component-background;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
|
||||
.ant-tabs-small {
|
||||
height: @multiple-card-height;
|
||||
}
|
||||
|
||||
.ant-tabs.ant-tabs-card {
|
||||
.ant-tabs-nav {
|
||||
height: @multiple-card-height;
|
||||
margin: 0;
|
||||
background-color: @component-background;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
padding-left: 10px;
|
||||
|
||||
.ant-tabs-nav-wrap {
|
||||
height: @tabHeight;
|
||||
margin-top: 4px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ant-tabs-tab {
|
||||
height: @tabHeight;
|
||||
line-height: @tabHeight;
|
||||
color: @text-color-base;
|
||||
background-color: @component-background;
|
||||
padding: 0 20px 0 30px;
|
||||
margin: 0 10px 0 0 !important;
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: @text-color-call-out;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
//padding: 0 36px 0 30px;
|
||||
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-remove {
|
||||
//update-begin---author:scott ---date:2023-08-28 for:【QQYUN-6374】UnoCSS替代windicss导致应用样式问题--
|
||||
/* top: 5px;*/
|
||||
//update-end---author:scott ---date::2023-08-28 for:【QQYUN-6374】UnoCSS替代windicss导致应用样式问题--
|
||||
left: 4px;
|
||||
|
||||
.anticon-close {
|
||||
position: relative;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 13px;
|
||||
color: inherit;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
top: 0;
|
||||
left: 6px;
|
||||
vertical-align: middle;
|
||||
line-height: 10px;
|
||||
overflow: hidden;
|
||||
transform-origin: 100% 50%;
|
||||
border-radius: 100%;
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: @text-color-base;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
border: none !important;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color !important;
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
position: relative;
|
||||
color: @primary-color !important;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid @primary-color !important;
|
||||
font-weight: inherit;
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 0;
|
||||
|
||||
svg {
|
||||
width: 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: inherit;
|
||||
fill: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-nav > div:nth-child(1) {
|
||||
padding: 0 6px;
|
||||
|
||||
.ant-tabs-tab {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
font-size: 12px;
|
||||
|
||||
svg {
|
||||
width: 0.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-extra-content {
|
||||
position: relative;
|
||||
top: 0;
|
||||
line-height: @multiple-card-height !important;
|
||||
}
|
||||
|
||||
.ant-dropdown-trigger {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.@{prefix-cls}--hide-close {
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls}-content {
|
||||
&__extra-quick,
|
||||
&__extra-redo,
|
||||
&__extra-fold {
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: @multiple-card-height;
|
||||
line-height: @multiple-card-height;
|
||||
color: @text-color-secondary;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-left: 1px solid @border-color-base;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-base;
|
||||
}
|
||||
|
||||
span[role='img'] {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__extra-redo {
|
||||
span[role='img'] {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: @tabHeight;
|
||||
padding-left: 0;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// tab 前缀图标样式
|
||||
&__prefix-icon {
|
||||
& .app-iconify.anticon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
// tabs圆滑样式
|
||||
@prefix-cls-theme-smooth: ~'@{prefix-cls}.@{prefix-cls}--theme-smooth';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls-theme-smooth} {
|
||||
.ant-tabs-tab {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
.@{prefix-cls-theme-smooth} {
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls-theme-smooth} {
|
||||
@tabHeight: calc(@multiple-smooth-height - 12px);
|
||||
z-index: 10;
|
||||
height: @multiple-smooth-height;
|
||||
line-height: @multiple-smooth-height;
|
||||
background-color: @component-background;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
|
||||
.ant-tabs-small {
|
||||
height: @multiple-smooth-height;
|
||||
}
|
||||
|
||||
.ant-tabs.ant-tabs-card {
|
||||
.ant-tabs-nav {
|
||||
height: @multiple-smooth-height;
|
||||
margin: 0;
|
||||
background-color: @component-background;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
padding-left: 10px;
|
||||
|
||||
.ant-tabs-nav-wrap {
|
||||
height: @tabHeight;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.ant-tabs-tab {
|
||||
height: @tabHeight;
|
||||
line-height: @tabHeight;
|
||||
color: @text-color-base;
|
||||
background-color: @component-background;
|
||||
transition: padding 0.3s;
|
||||
padding: 0 20px 0 26px;
|
||||
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 {
|
||||
z-index: 2;
|
||||
padding: 0 20px 0 26px;
|
||||
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-remove {
|
||||
top: -1px;
|
||||
left: 8px;
|
||||
|
||||
.anticon-close {
|
||||
position: relative;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 13px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: @text-color-base;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding: 0 30px 0 30px !important;
|
||||
}
|
||||
span{font-weight: 200;}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
&:hover {
|
||||
color: inherit;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab.ant-tabs-tab-active {
|
||||
position: relative;
|
||||
padding: 0 20px 0 26px;
|
||||
color: @primary-color !important;
|
||||
background: #f5f5f5;
|
||||
border: 0;
|
||||
z-index: 3;
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 1;
|
||||
|
||||
svg {
|
||||
width: 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: inherit;
|
||||
fill: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-nav > div:nth-child(1) {
|
||||
padding: 0 6px;
|
||||
|
||||
.ant-tabs-tab {
|
||||
margin-right: -15px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
.anticon-close {
|
||||
font-size: 12px;
|
||||
|
||||
svg {
|
||||
width: 0.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-extra-content {
|
||||
position: relative;
|
||||
top: 0;
|
||||
line-height: @multiple-smooth-height !important;
|
||||
}
|
||||
|
||||
.ant-dropdown-trigger {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.@{prefix-cls}--hide-close {
|
||||
.ant-tabs-tab-remove .anticon-close {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls}-content {
|
||||
&__extra-quick,
|
||||
&__extra-redo,
|
||||
&__extra-fold {
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: @multiple-smooth-height;
|
||||
line-height: @multiple-smooth-height;
|
||||
color: @text-color-secondary;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-left: 1px solid @border-color-base;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-base;
|
||||
}
|
||||
|
||||
span[role='img'] {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__extra-redo {
|
||||
span[role='img'] {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: @tabHeight;
|
||||
line-height: 32px;
|
||||
padding-left: 0;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// tab 前缀图标样式
|
||||
&__prefix-icon {
|
||||
& .app-iconify.anticon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { DropMenu } from '/@/components/Dropdown/index';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
export enum TabContentEnum {
|
||||
TAB_TYPE,
|
||||
EXTRA_TYPE,
|
||||
}
|
||||
|
||||
export type { DropMenu };
|
||||
|
||||
export interface TabContentProps {
|
||||
tabItem: RouteLocationNormalized;
|
||||
type?: TabContentEnum;
|
||||
trigger?: ('click' | 'hover' | 'contextmenu')[];
|
||||
}
|
||||
|
||||
export enum MenuEventEnum {
|
||||
REFRESH_PAGE,
|
||||
CLOSE_CURRENT,
|
||||
CLOSE_LEFT,
|
||||
CLOSE_RIGHT,
|
||||
CLOSE_OTHER,
|
||||
CLOSE_ALL,
|
||||
SCALE,
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { toRaw, ref, nextTick } from 'vue';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useSortable } from '/@/hooks/web/useSortable';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import { isNullAndUnDef } from '/@/utils/is';
|
||||
import projectSetting from '/@/settings/projectSetting';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
export function initAffixTabs(): string[] {
|
||||
const affixList = ref<RouteLocationNormalized[]>([]);
|
||||
|
||||
const tabStore = useMultipleTabStore();
|
||||
const router = useRouter();
|
||||
/**
|
||||
* @description: Filter all fixed routes
|
||||
*/
|
||||
function filterAffixTabs(routes: RouteLocationNormalized[]) {
|
||||
const tabs: RouteLocationNormalized[] = [];
|
||||
routes &&
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
tabs.push(toRaw(route));
|
||||
}
|
||||
});
|
||||
return tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Set fixed tabs
|
||||
*/
|
||||
function addAffixTabs(): void {
|
||||
const affixTabs = filterAffixTabs(router.getRoutes() as unknown as RouteLocationNormalized[]);
|
||||
affixList.value = affixTabs;
|
||||
for (const tab of affixTabs) {
|
||||
tabStore.addTab({
|
||||
meta: tab.meta,
|
||||
name: tab.name,
|
||||
path: tab.path,
|
||||
} as unknown as RouteLocationNormalized);
|
||||
}
|
||||
}
|
||||
|
||||
let isAddAffix = false;
|
||||
|
||||
if (!isAddAffix) {
|
||||
addAffixTabs();
|
||||
isAddAffix = true;
|
||||
}
|
||||
return affixList.value.map((item) => item.meta?.title).filter(Boolean) as string[];
|
||||
}
|
||||
|
||||
export function useTabsDrag(affixTextList: string[]) {
|
||||
const tabStore = useMultipleTabStore();
|
||||
const { multiTabsSetting } = projectSetting;
|
||||
const { prefixCls } = useDesign('multiple-tabs');
|
||||
nextTick(() => {
|
||||
if (!multiTabsSetting.canDrag) return;
|
||||
const el = document.querySelectorAll(`.${prefixCls} .ant-tabs-nav > div`)?.[0] as HTMLElement;
|
||||
const { initSortable } = useSortable(el, {
|
||||
filter: (e: ChangeEvent) => {
|
||||
const text = e?.target?.innerText;
|
||||
if (!text) return false;
|
||||
return affixTextList.includes(text);
|
||||
},
|
||||
onEnd: (evt) => {
|
||||
const { oldIndex, newIndex } = evt;
|
||||
|
||||
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
tabStore.sortTabs(oldIndex, newIndex);
|
||||
},
|
||||
});
|
||||
initSortable();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import type { TabContentProps } from './types';
|
||||
import type { DropMenu } from '/@/components/Dropdown';
|
||||
import type { ComputedRef } from 'vue';
|
||||
|
||||
import { computed, unref, reactive } from 'vue';
|
||||
import { MenuEventEnum } from './types';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import { RouteLocationNormalized, useRouter } from 'vue-router';
|
||||
import { useTabs } from '/@/hooks/web/useTabs';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
export function useTabDropdown(tabContentProps: TabContentProps, getIsTabs: ComputedRef<boolean>) {
|
||||
const state = reactive({
|
||||
current: null as Nullable<RouteLocationNormalized>,
|
||||
currentIndex: 0,
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const tabStore = useMultipleTabStore();
|
||||
const { currentRoute } = useRouter();
|
||||
const { refreshPage, closeAll, close, closeLeft, closeOther, closeRight } = useTabs();
|
||||
|
||||
const getTargetTab = computed((): RouteLocationNormalized => {
|
||||
return unref(getIsTabs) ? tabContentProps.tabItem : unref(currentRoute);
|
||||
});
|
||||
|
||||
/**
|
||||
* @description: drop-down list
|
||||
*/
|
||||
const getDropMenuList = computed(() => {
|
||||
if (!unref(getTargetTab)) {
|
||||
return;
|
||||
}
|
||||
const { meta } = unref(getTargetTab);
|
||||
const { path } = unref(currentRoute);
|
||||
|
||||
// Refresh button
|
||||
const curItem = state.current;
|
||||
|
||||
const isCurItem = curItem ? curItem.path === path : false;
|
||||
const index = state.currentIndex;
|
||||
const refreshDisabled = !isCurItem;
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
// Close left
|
||||
const closeLeftDisabled = () => {
|
||||
if (index === 0) {
|
||||
return true;
|
||||
} else {
|
||||
// 【TV360X-1039】当只有首页和另一个tab页时关闭左侧禁用
|
||||
const validTabList = tabStore.getTabList.filter((item) => !item?.meta?.affix);
|
||||
return validTabList[0].path === state.current?.path;
|
||||
}
|
||||
};
|
||||
// Close other
|
||||
const closeOtherDisabled = () => {
|
||||
if (tabStore.getTabList.length === 1) {
|
||||
return true;
|
||||
} else {
|
||||
// 【TV360X-1039】当只有首页和另一个tab页时关闭其它禁用
|
||||
const validTabList = tabStore.getTabList.filter((item) => !item?.meta?.affix);
|
||||
return validTabList.length == 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Close right
|
||||
const closeRightDisabled = index === tabStore.getTabList.length - 1 && tabStore.getLastDragEndIndex >= 0;
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
const dropMenuList: DropMenu[] = [
|
||||
{
|
||||
icon: 'jam:refresh-reverse',
|
||||
event: MenuEventEnum.REFRESH_PAGE,
|
||||
text: t('layout.multipleTab.reload'),
|
||||
disabled: refreshDisabled,
|
||||
divider: true,
|
||||
},
|
||||
// {
|
||||
// icon: 'ic:twotone-close',
|
||||
// event: MenuEventEnum.CLOSE_CURRENT,
|
||||
// text: t('layout.multipleTab.close'),
|
||||
// disabled: !!meta?.affix || disabled,
|
||||
// divider: true,
|
||||
// },
|
||||
{
|
||||
icon: 'mdi:arrow-left',
|
||||
event: MenuEventEnum.CLOSE_LEFT,
|
||||
text: t('layout.multipleTab.closeLeft'),
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
disabled: closeLeftDisabled(),
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
divider: false,
|
||||
},
|
||||
{
|
||||
icon: 'mdi:arrow-right',
|
||||
event: MenuEventEnum.CLOSE_RIGHT,
|
||||
text: t('layout.multipleTab.closeRight'),
|
||||
disabled: closeRightDisabled,
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
icon: 'material-symbols:arrows-outward',
|
||||
event: MenuEventEnum.CLOSE_OTHER,
|
||||
text: t('layout.multipleTab.closeOther'),
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
disabled: closeOtherDisabled(),
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
},
|
||||
// {
|
||||
// icon: 'clarity:minus-line',
|
||||
// event: MenuEventEnum.CLOSE_ALL,
|
||||
// text: t('layout.multipleTab.closeAll'),
|
||||
// disabled: disabled,
|
||||
// },
|
||||
];
|
||||
|
||||
return dropMenuList;
|
||||
});
|
||||
|
||||
function handleContextMenu(tabItem: RouteLocationNormalized) {
|
||||
return (e: Event) => {
|
||||
if (!tabItem) {
|
||||
return;
|
||||
}
|
||||
e?.preventDefault();
|
||||
const index = tabStore.getTabList.findIndex((tab) => tab.path === tabItem.path);
|
||||
state.current = tabItem;
|
||||
state.currentIndex = index;
|
||||
};
|
||||
}
|
||||
|
||||
// Handle right click event
|
||||
function handleMenuEvent(menu: DropMenu): void {
|
||||
const { event } = menu;
|
||||
switch (event) {
|
||||
case MenuEventEnum.REFRESH_PAGE:
|
||||
// refresh page
|
||||
refreshPage();
|
||||
break;
|
||||
// Close current
|
||||
case MenuEventEnum.CLOSE_CURRENT:
|
||||
close(tabContentProps.tabItem);
|
||||
break;
|
||||
// Close left
|
||||
case MenuEventEnum.CLOSE_LEFT:
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
closeLeft(state.current);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
break;
|
||||
// Close right
|
||||
case MenuEventEnum.CLOSE_RIGHT:
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
closeRight(state.current);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
break;
|
||||
// Close other
|
||||
case MenuEventEnum.CLOSE_OTHER:
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
closeOther(state.current);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
break;
|
||||
// Close all
|
||||
case MenuEventEnum.CLOSE_ALL:
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
closeAll(state.current);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { getDropMenuList, handleMenuEvent, handleContextMenu };
|
||||
}
|
||||
Reference in New Issue
Block a user