fix: 修改TagsView样式问题
This commit is contained in:
parent
6fff76e3f8
commit
58337f771e
@ -2,8 +2,8 @@
|
|||||||
<div
|
<div
|
||||||
class="box-border tabs-view"
|
class="box-border tabs-view"
|
||||||
:class="{
|
:class="{
|
||||||
'tabs-view-fix': state.multiTabsSetting.fixed,
|
'tabs-view-fix': multiTabsSetting.fixed,
|
||||||
'tabs-view-fixed-header': state.isMultiHeaderFixed,
|
'tabs-view-fixed-header': isMultiHeaderFixed,
|
||||||
'tabs-view-default-background': getDarkTheme === false,
|
'tabs-view-default-background': getDarkTheme === false,
|
||||||
'tabs-view-dark-background': getDarkTheme === true,
|
'tabs-view-dark-background': getDarkTheme === true,
|
||||||
}"
|
}"
|
||||||
@ -13,11 +13,11 @@
|
|||||||
<div
|
<div
|
||||||
ref="navWrap"
|
ref="navWrap"
|
||||||
class="tabs-card"
|
class="tabs-card"
|
||||||
:class="{ 'tabs-card-scrollable': state.scrollable }"
|
:class="{ 'tabs-card-scrollable': scrollable }"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="tabs-card-prev"
|
class="tabs-card-prev"
|
||||||
:class="{ 'tabs-card-prev-hide': !state.scrollable }"
|
:class="{ 'tabs-card-prev-hide': !scrollable }"
|
||||||
@click="scrollPrev"
|
@click="scrollPrev"
|
||||||
>
|
>
|
||||||
<n-icon size="16" color="#515a6e">
|
<n-icon size="16" color="#515a6e">
|
||||||
@ -26,7 +26,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="tabs-card-next"
|
class="tabs-card-next"
|
||||||
:class="{ 'tabs-card-next-hide': !state.scrollable }"
|
:class="{ 'tabs-card-next-hide': !scrollable }"
|
||||||
@click="scrollNext"
|
@click="scrollNext"
|
||||||
>
|
>
|
||||||
<n-icon size="16" color="#515a6e">
|
<n-icon size="16" color="#515a6e">
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<div
|
<div
|
||||||
:id="`tag${element.fullPath.split('/').join('\/')}`"
|
:id="`tag${element.fullPath.split('/').join('\/')}`"
|
||||||
class="tabs-card-scroll-item"
|
class="tabs-card-scroll-item"
|
||||||
:class="{ 'active-item': state.activeKey === element.fullPath }"
|
:class="{ 'active-item': activeKey === element.fullPath }"
|
||||||
@click.stop="goPage(element)"
|
@click.stop="goPage(element)"
|
||||||
@contextmenu="handleContextMenu($event, element)"
|
@contextmenu="handleContextMenu($event, element)"
|
||||||
>
|
>
|
||||||
@ -76,9 +76,9 @@
|
|||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
:show="state.showDropdown"
|
:show="showDropdown"
|
||||||
:x="state.dropdownX"
|
:x="dropdownX"
|
||||||
:y="state.dropdownY"
|
:y="dropdownY"
|
||||||
@clickoutside="onClickOutside"
|
@clickoutside="onClickOutside"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
@select="closeHandleSelect"
|
@select="closeHandleSelect"
|
||||||
@ -88,13 +88,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
reactive,
|
||||||
|
computed,
|
||||||
|
ref,
|
||||||
|
toRefs,
|
||||||
|
provide,
|
||||||
|
watch,
|
||||||
|
onMounted,
|
||||||
|
nextTick,
|
||||||
|
} from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { storage } from '@/utils';
|
import { storage } from '@/utils';
|
||||||
|
import { StorageEnum } from '@/enums/storageEnum';
|
||||||
import { useTabsViewStore } from '@/store/modules/tabsView';
|
import { useTabsViewStore } from '@/store/modules/tabsView';
|
||||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
import { RouteItem } from '@/store/modules/tabsView';
|
import { RouteItem } from '@/store/modules/tabsView';
|
||||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
import { useMessage } from 'naive-ui';
|
||||||
import Draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
import {
|
import {
|
||||||
@ -107,390 +120,432 @@ import {
|
|||||||
RightOutlined,
|
RightOutlined,
|
||||||
} from '@vicons/antd';
|
} from '@vicons/antd';
|
||||||
import { renderIcon } from '@/utils';
|
import { renderIcon } from '@/utils';
|
||||||
// import elementResizeDetectorMaker from "element-resize-detector";
|
// import elementResizeDetectorMaker from 'element-resize-detector';
|
||||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
import { useThemeVars } from 'naive-ui';
|
import { useThemeVars } from 'naive-ui';
|
||||||
import { useGo } from '@/hooks/web/usePage';
|
import { useGo } from '@/hooks/web/usePage';
|
||||||
import { StorageEnum } from '@/enums/storageEnum';
|
|
||||||
const props = defineProps({
|
export default defineComponent({
|
||||||
collapsed: {
|
name: 'TabsView',
|
||||||
type: Boolean,
|
components: {
|
||||||
|
DownOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
Draggable,
|
||||||
},
|
},
|
||||||
});
|
props: {
|
||||||
const { getDarkTheme, getAppTheme } = useDesignSetting();
|
collapsed: {
|
||||||
const { navMode, headerSetting, menuSetting, multiTabsSetting, isMobile } =
|
type: Boolean,
|
||||||
useProjectSetting();
|
},
|
||||||
const settingStore = useProjectSettingStore();
|
},
|
||||||
|
setup(props) {
|
||||||
|
const { getDarkTheme, getAppTheme } = useDesignSetting();
|
||||||
|
const { navMode, headerSetting, menuSetting, multiTabsSetting, isMobile } =
|
||||||
|
useProjectSetting();
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
|
||||||
const route = useRoute();
|
const message = useMessage();
|
||||||
const router = useRouter();
|
const route = useRoute();
|
||||||
const tabsViewStore = useTabsViewStore();
|
const router = useRouter();
|
||||||
const asyncRouteStore = useAsyncRouteStore();
|
const tabsViewStore = useTabsViewStore();
|
||||||
const navScroll: any = ref(null);
|
const asyncRouteStore = useAsyncRouteStore();
|
||||||
const navWrap: any = ref(null);
|
const navScroll: any = ref(null);
|
||||||
const isCurrent = ref(false);
|
const navWrap: any = ref(null);
|
||||||
const go = useGo();
|
const isCurrent = ref(false);
|
||||||
|
const go = useGo();
|
||||||
|
|
||||||
const themeVars = useThemeVars();
|
const themeVars = useThemeVars();
|
||||||
|
|
||||||
const getCardColor = computed(() => {
|
const getcardcolor = computed(() => {
|
||||||
return themeVars.value.cardColor;
|
return themeVars.value.cardColor;
|
||||||
});
|
});
|
||||||
|
|
||||||
const getBaseColor = computed(() => {
|
const getbasecolor = computed(() => {
|
||||||
return themeVars.value.textColor1;
|
return themeVars.value.textColor1;
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
activeKey: route.fullPath,
|
activeKey: route.fullPath,
|
||||||
scrollable: false,
|
scrollable: false,
|
||||||
dropdownX: 0,
|
dropdownX: 0,
|
||||||
dropdownY: 0,
|
dropdownY: 0,
|
||||||
showDropdown: false,
|
showDropdown: false,
|
||||||
isMultiHeaderFixed: false,
|
isMultiHeaderFixed: false,
|
||||||
multiTabsSetting: multiTabsSetting,
|
multiTabsSetting: multiTabsSetting,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取简易的路由对象
|
// 获取简易的路由对象
|
||||||
const getSimpleRoute = (route): RouteItem => {
|
const getSimpleRoute = (route): RouteItem => {
|
||||||
const { fullPath, hash, meta, name, params, path, query } = route;
|
const { fullPath, hash, meta, name, params, path, query } = route;
|
||||||
return { fullPath, hash, meta, name, params, path, query };
|
return { fullPath, hash, meta, name, params, path, query };
|
||||||
};
|
|
||||||
|
|
||||||
const isMixMenuNoneSub = computed(() => {
|
|
||||||
const mixMenu = settingStore.menuSetting.mixMenu;
|
|
||||||
const currentRoute = useRoute();
|
|
||||||
if (navMode.value != 'horizontal-mix') return true;
|
|
||||||
return !(
|
|
||||||
navMode.value === 'horizontal-mix' &&
|
|
||||||
mixMenu &&
|
|
||||||
currentRoute.meta.isRoot
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
//动态组装样式 菜单缩进
|
|
||||||
const getChangeStyle = computed(() => {
|
|
||||||
const { collapsed } = props;
|
|
||||||
const { minMenuWidth, menuWidth }: any = menuSetting.value;
|
|
||||||
const { fixed }: any = multiTabsSetting.value;
|
|
||||||
let lenNum =
|
|
||||||
navMode.value === 'horizontal' || !isMixMenuNoneSub.value
|
|
||||||
? '0px'
|
|
||||||
: collapsed
|
|
||||||
? `${minMenuWidth}px`
|
|
||||||
: `${menuWidth}px`;
|
|
||||||
|
|
||||||
if (isMobile.value) {
|
|
||||||
return {
|
|
||||||
left: '0px',
|
|
||||||
width: '100%',
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
return {
|
|
||||||
left: lenNum,
|
|
||||||
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
//tags 右侧下拉菜单
|
const isMixMenuNoneSub = computed(() => {
|
||||||
const TabsMenuOptions = computed(() => {
|
const mixMenu = settingStore.menuSetting.mixMenu;
|
||||||
const isDisabled = tabsList.value.length <= 1;
|
const currentRoute = useRoute();
|
||||||
return [
|
if (navMode.value != 'horizontal-mix') return true;
|
||||||
{
|
return !(
|
||||||
label: '刷新当前',
|
navMode.value === 'horizontal-mix' &&
|
||||||
key: '1',
|
mixMenu &&
|
||||||
icon: renderIcon(ReloadOutlined),
|
currentRoute.meta.isRoot
|
||||||
},
|
);
|
||||||
{
|
});
|
||||||
label: `关闭当前`,
|
|
||||||
key: '2',
|
|
||||||
disabled: isCurrent.value || isDisabled,
|
|
||||||
icon: renderIcon(CloseOutlined),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '关闭其他',
|
|
||||||
key: '3',
|
|
||||||
disabled: isDisabled,
|
|
||||||
icon: renderIcon(ColumnWidthOutlined),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '关闭全部',
|
|
||||||
key: '4',
|
|
||||||
disabled: isDisabled,
|
|
||||||
icon: renderIcon(MinusOutlined),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
let cacheRoutes: RouteItem[] = [];
|
//动态组装样式 菜单缩进
|
||||||
const simpleRoute = getSimpleRoute(route);
|
const getChangeStyle = computed(() => {
|
||||||
try {
|
const { collapsed } = props;
|
||||||
const routesStr = storage.get(StorageEnum.ZS_TABS_ROUTES) as
|
const { minMenuWidth, menuWidth }: any = menuSetting.value;
|
||||||
| string
|
const { fixed }: any = multiTabsSetting.value;
|
||||||
| null
|
let lenNum =
|
||||||
| undefined;
|
navMode.value === 'horizontal' || !isMixMenuNoneSub.value
|
||||||
cacheRoutes = routesStr ? JSON.parse(routesStr) : [simpleRoute];
|
? '0px'
|
||||||
} catch (e) {
|
: collapsed
|
||||||
cacheRoutes = [simpleRoute];
|
? `${minMenuWidth}px`
|
||||||
}
|
: `${menuWidth}px`;
|
||||||
|
|
||||||
// 将最新的路由信息同步到 localStorage 中
|
if (isMobile.value) {
|
||||||
const routes = router.getRoutes();
|
return {
|
||||||
cacheRoutes.forEach((cacheRoute) => {
|
left: '0px',
|
||||||
const route = routes.find((route) => route.path === cacheRoute.path);
|
width: '100%',
|
||||||
if (route) {
|
};
|
||||||
cacheRoute.meta = route.meta || cacheRoute.meta;
|
}
|
||||||
cacheRoute.name = (route.name || cacheRoute.name) as string;
|
return {
|
||||||
}
|
left: lenNum,
|
||||||
});
|
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// 初始化标签页
|
//tags 右侧下拉菜单
|
||||||
tabsViewStore.initTabs(cacheRoutes);
|
const TabsMenuOptions = computed(() => {
|
||||||
|
const isDisabled = tabsList.value.length <= 1;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '刷新当前',
|
||||||
|
key: '1',
|
||||||
|
icon: renderIcon(ReloadOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `关闭当前`,
|
||||||
|
key: '2',
|
||||||
|
disabled: isCurrent.value || isDisabled,
|
||||||
|
icon: renderIcon(CloseOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关闭其他',
|
||||||
|
key: '3',
|
||||||
|
disabled: isDisabled,
|
||||||
|
icon: renderIcon(ColumnWidthOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关闭全部',
|
||||||
|
key: '4',
|
||||||
|
disabled: isDisabled,
|
||||||
|
icon: renderIcon(MinusOutlined),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
//监听滚动条
|
let cacheRoutes: RouteItem[] = [];
|
||||||
function onScroll(e) {
|
const simpleRoute = getSimpleRoute(route);
|
||||||
let scrollTop =
|
try {
|
||||||
e.target.scrollTop ||
|
const routesStr = storage.get(StorageEnum.ZS_TABS_ROUTES) as
|
||||||
document.documentElement.scrollTop ||
|
| string
|
||||||
window.pageYOffset ||
|
| null
|
||||||
document.body.scrollTop; // 滚动条偏移量
|
| undefined;
|
||||||
state.isMultiHeaderFixed = !!(
|
cacheRoutes = routesStr ? JSON.parse(routesStr) : [simpleRoute];
|
||||||
!headerSetting.value.fixed &&
|
} catch (e) {
|
||||||
multiTabsSetting.value.fixed &&
|
cacheRoutes = [simpleRoute];
|
||||||
scrollTop >= 64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('scroll', onScroll, true);
|
|
||||||
|
|
||||||
// 移除缓存组件名称
|
|
||||||
const delKeepAliveCompName = () => {
|
|
||||||
if (route.meta.keepAlive) {
|
|
||||||
const name = router.currentRoute.value.matched.find(
|
|
||||||
(item) => item.name == route.name
|
|
||||||
)?.components?.default.name;
|
|
||||||
if (name) {
|
|
||||||
asyncRouteStore.keepAliveComponents =
|
|
||||||
asyncRouteStore.keepAliveComponents.filter((item) => item != name);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 标签页列表
|
// 将最新的路由信息同步到 localStorage 中
|
||||||
const tabsList: any = computed(() => tabsViewStore.tabsList);
|
const routes = router.getRoutes();
|
||||||
const whiteList: string[] = [
|
cacheRoutes.forEach((cacheRoute) => {
|
||||||
PageEnum.BASE_LOGIN_NAME,
|
const route = routes.find((route) => route.path === cacheRoute.path);
|
||||||
PageEnum.REDIRECT_NAME,
|
if (route) {
|
||||||
PageEnum.ERROR_PAGE_NAME,
|
cacheRoute.meta = route.meta || cacheRoute.meta;
|
||||||
];
|
cacheRoute.name = (route.name || cacheRoute.name) as string;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
// 初始化标签页
|
||||||
() => route.fullPath,
|
tabsViewStore.initTabs(cacheRoutes);
|
||||||
(to) => {
|
|
||||||
if (whiteList.includes(route.name as string)) return;
|
|
||||||
state.activeKey = to;
|
|
||||||
tabsViewStore.addTab(getSimpleRoute(route));
|
|
||||||
updateNavScroll(true);
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
// 在页面关闭或刷新之前,保存数据
|
//监听滚动条
|
||||||
window.addEventListener('beforeunload', () => {
|
function onScroll(e) {
|
||||||
storage.set(StorageEnum.ZS_TABS_ROUTES, JSON.stringify(tabsList.value));
|
let scrollTop =
|
||||||
});
|
e.target.scrollTop ||
|
||||||
|
document.documentElement.scrollTop ||
|
||||||
|
window.pageYOffset ||
|
||||||
|
document.body.scrollTop; // 滚动条偏移量
|
||||||
|
state.isMultiHeaderFixed = !!(
|
||||||
|
!headerSetting.value.fixed &&
|
||||||
|
multiTabsSetting.value.fixed &&
|
||||||
|
scrollTop >= 64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭当前页面
|
window.addEventListener('scroll', onScroll, true);
|
||||||
const removeTab = (route) => {
|
|
||||||
if (tabsList.value.length === 1) {
|
|
||||||
return window['$message'].warning('这已经是最后一页,不能再关闭了!');
|
|
||||||
}
|
|
||||||
delKeepAliveCompName();
|
|
||||||
tabsViewStore.closeCurrentTab(route);
|
|
||||||
// 如果关闭的是当前页
|
|
||||||
if (state.activeKey === route.fullPath) {
|
|
||||||
const currentRoute = tabsList.value[Math.max(0, tabsList.value.length - 1)];
|
|
||||||
state.activeKey = currentRoute.fullPath;
|
|
||||||
router.push(currentRoute);
|
|
||||||
}
|
|
||||||
updateNavScroll();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 刷新页面
|
// 移除缓存组件名称
|
||||||
const reloadPage = () => {
|
const delKeepAliveCompName = () => {
|
||||||
delKeepAliveCompName();
|
if (route.meta.keepAlive) {
|
||||||
router.push({
|
const name = router.currentRoute.value.matched.find(
|
||||||
path: '/redirect' + route.fullPath,
|
(item) => item.name == route.name
|
||||||
});
|
)?.components?.default.name;
|
||||||
};
|
if (name) {
|
||||||
|
asyncRouteStore.keepAliveComponents =
|
||||||
// 注入刷新页面方法
|
asyncRouteStore.keepAliveComponents.filter((item) => item != name);
|
||||||
provide('reloadPage', reloadPage);
|
|
||||||
|
|
||||||
// 关闭左侧
|
|
||||||
const closeLeft = (route) => {
|
|
||||||
tabsViewStore.closeLeftTabs(route);
|
|
||||||
state.activeKey = route.fullPath;
|
|
||||||
router.replace(route.fullPath);
|
|
||||||
updateNavScroll();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭右侧
|
|
||||||
const closeRight = (route) => {
|
|
||||||
tabsViewStore.closeRightTabs(route);
|
|
||||||
state.activeKey = route.fullPath;
|
|
||||||
router.replace(route.fullPath);
|
|
||||||
updateNavScroll();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭其他
|
|
||||||
const closeOther = (route) => {
|
|
||||||
tabsViewStore.closeOtherTabs(route);
|
|
||||||
state.activeKey = route.fullPath;
|
|
||||||
router.replace(route.fullPath);
|
|
||||||
updateNavScroll();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭全部
|
|
||||||
const closeAll = () => {
|
|
||||||
tabsViewStore.closeAllTabs();
|
|
||||||
router.replace(PageEnum.BASE_HOME);
|
|
||||||
updateNavScroll();
|
|
||||||
};
|
|
||||||
|
|
||||||
//tab 操作
|
|
||||||
const closeHandleSelect = (key) => {
|
|
||||||
switch (key) {
|
|
||||||
//刷新
|
|
||||||
case '1':
|
|
||||||
reloadPage();
|
|
||||||
break;
|
|
||||||
//关闭
|
|
||||||
case '2':
|
|
||||||
removeTab(route);
|
|
||||||
break;
|
|
||||||
//关闭其他
|
|
||||||
case '3':
|
|
||||||
closeOther(route);
|
|
||||||
break;
|
|
||||||
//关闭所有
|
|
||||||
case '4':
|
|
||||||
closeAll();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
updateNavScroll();
|
|
||||||
state.showDropdown = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param value 要滚动到的位置
|
|
||||||
* @param amplitude 每次滚动的长度
|
|
||||||
*/
|
|
||||||
function scrollTo(value: number, amplitude: number) {
|
|
||||||
const currentScroll = navScroll.value.scrollLeft;
|
|
||||||
const scrollWidth =
|
|
||||||
(amplitude > 0 && currentScroll + amplitude >= value) ||
|
|
||||||
(amplitude < 0 && currentScroll + amplitude <= value)
|
|
||||||
? value
|
|
||||||
: currentScroll + amplitude;
|
|
||||||
navScroll.value && navScroll.value.scrollTo(scrollWidth, 0);
|
|
||||||
if (scrollWidth === value) return;
|
|
||||||
return window.requestAnimationFrame(() => scrollTo(value, amplitude));
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollPrev() {
|
|
||||||
const containerWidth = navScroll.value.offsetWidth;
|
|
||||||
const currentScroll = navScroll.value.scrollLeft;
|
|
||||||
|
|
||||||
if (!currentScroll) return;
|
|
||||||
const scrollLeft =
|
|
||||||
currentScroll > containerWidth ? currentScroll - containerWidth : 0;
|
|
||||||
scrollTo(scrollLeft, (scrollLeft - currentScroll) / 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollNext() {
|
|
||||||
const containerWidth = navScroll.value.offsetWidth;
|
|
||||||
const navWidth = navScroll.value.scrollWidth;
|
|
||||||
const currentScroll = navScroll.value.scrollLeft;
|
|
||||||
|
|
||||||
if (navWidth - currentScroll <= containerWidth) return;
|
|
||||||
const scrollLeft =
|
|
||||||
navWidth - currentScroll > containerWidth * 2
|
|
||||||
? currentScroll + containerWidth
|
|
||||||
: navWidth - containerWidth;
|
|
||||||
scrollTo(scrollLeft, (scrollLeft - currentScroll) / 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param autoScroll 是否开启自动滚动功能
|
|
||||||
*/
|
|
||||||
async function updateNavScroll(autoScroll?: boolean) {
|
|
||||||
await nextTick();
|
|
||||||
if (!navScroll.value) return;
|
|
||||||
const containerWidth = navScroll.value.offsetWidth;
|
|
||||||
const navWidth = navScroll.value.scrollWidth;
|
|
||||||
|
|
||||||
if (containerWidth < navWidth) {
|
|
||||||
state.scrollable = true;
|
|
||||||
if (autoScroll) {
|
|
||||||
let tagList =
|
|
||||||
navScroll.value.querySelectorAll('.tabs-card-scroll-item') || [];
|
|
||||||
[...tagList].forEach((tag: HTMLElement) => {
|
|
||||||
// fix SyntaxError
|
|
||||||
if (tag.id === `tag${state.activeKey.split('/').join('\/')}`) {
|
|
||||||
tag.scrollIntoView && tag.scrollIntoView();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 标签页列表
|
||||||
|
const tabsList: any = computed(() => tabsViewStore.tabsList);
|
||||||
|
const whiteList: string[] = [
|
||||||
|
PageEnum.BASE_LOGIN_NAME,
|
||||||
|
PageEnum.REDIRECT_NAME,
|
||||||
|
PageEnum.ERROR_PAGE_NAME,
|
||||||
|
];
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
(to) => {
|
||||||
|
if (whiteList.includes(route.name as string)) return;
|
||||||
|
state.activeKey = to;
|
||||||
|
tabsViewStore.addTab(getSimpleRoute(route));
|
||||||
|
updateNavScroll(true);
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 在页面关闭或刷新之前,保存数据
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
storage.set(StorageEnum.ZS_TABS_ROUTES, JSON.stringify(tabsList.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 关闭当前页面
|
||||||
|
const removeTab = (route) => {
|
||||||
|
if (tabsList.value.length === 1) {
|
||||||
|
return message.warning('这已经是最后一页,不能再关闭了!');
|
||||||
|
}
|
||||||
|
delKeepAliveCompName();
|
||||||
|
tabsViewStore.closeCurrentTab(route);
|
||||||
|
// 如果关闭的是当前页
|
||||||
|
if (state.activeKey === route.fullPath) {
|
||||||
|
const currentRoute =
|
||||||
|
tabsList.value[Math.max(0, tabsList.value.length - 1)];
|
||||||
|
state.activeKey = currentRoute.fullPath;
|
||||||
|
router.push(currentRoute);
|
||||||
|
}
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新页面
|
||||||
|
const reloadPage = () => {
|
||||||
|
delKeepAliveCompName();
|
||||||
|
router.push({
|
||||||
|
path: '/redirect' + route.fullPath,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 注入刷新页面方法
|
||||||
|
provide('reloadPage', reloadPage);
|
||||||
|
|
||||||
|
// 关闭左侧
|
||||||
|
const closeLeft = (route) => {
|
||||||
|
tabsViewStore.closeLeftTabs(route);
|
||||||
|
state.activeKey = route.fullPath;
|
||||||
|
router.replace(route.fullPath);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭右侧
|
||||||
|
const closeRight = (route) => {
|
||||||
|
tabsViewStore.closeRightTabs(route);
|
||||||
|
state.activeKey = route.fullPath;
|
||||||
|
router.replace(route.fullPath);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭其他
|
||||||
|
const closeOther = (route) => {
|
||||||
|
tabsViewStore.closeOtherTabs(route);
|
||||||
|
state.activeKey = route.fullPath;
|
||||||
|
router.replace(route.fullPath);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭全部
|
||||||
|
const closeAll = () => {
|
||||||
|
tabsViewStore.closeAllTabs();
|
||||||
|
router.replace(PageEnum.BASE_HOME);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
//tab 操作
|
||||||
|
const closeHandleSelect = (key) => {
|
||||||
|
switch (key) {
|
||||||
|
//刷新
|
||||||
|
case '1':
|
||||||
|
reloadPage();
|
||||||
|
break;
|
||||||
|
//关闭
|
||||||
|
case '2':
|
||||||
|
removeTab(route);
|
||||||
|
break;
|
||||||
|
//关闭其他
|
||||||
|
case '3':
|
||||||
|
closeOther(route);
|
||||||
|
break;
|
||||||
|
//关闭所有
|
||||||
|
case '4':
|
||||||
|
closeAll();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updateNavScroll();
|
||||||
|
state.showDropdown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value 要滚动到的位置
|
||||||
|
* @param amplitude 每次滚动的长度
|
||||||
|
*/
|
||||||
|
function scrollTo(value: number, amplitude: number) {
|
||||||
|
const currentScroll = navScroll.value.scrollLeft;
|
||||||
|
const scrollWidth =
|
||||||
|
(amplitude > 0 && currentScroll + amplitude >= value) ||
|
||||||
|
(amplitude < 0 && currentScroll + amplitude <= value)
|
||||||
|
? value
|
||||||
|
: currentScroll + amplitude;
|
||||||
|
navScroll.value && navScroll.value.scrollTo(scrollWidth, 0);
|
||||||
|
if (scrollWidth === value) return;
|
||||||
|
return window.requestAnimationFrame(() => scrollTo(value, amplitude));
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollPrev() {
|
||||||
|
const containerWidth = navScroll.value.offsetWidth;
|
||||||
|
const currentScroll = navScroll.value.scrollLeft;
|
||||||
|
|
||||||
|
if (!currentScroll) return;
|
||||||
|
const scrollLeft =
|
||||||
|
currentScroll > containerWidth ? currentScroll - containerWidth : 0;
|
||||||
|
scrollTo(scrollLeft, (scrollLeft - currentScroll) / 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollNext() {
|
||||||
|
const containerWidth = navScroll.value.offsetWidth;
|
||||||
|
const navWidth = navScroll.value.scrollWidth;
|
||||||
|
const currentScroll = navScroll.value.scrollLeft;
|
||||||
|
|
||||||
|
if (navWidth - currentScroll <= containerWidth) return;
|
||||||
|
const scrollLeft =
|
||||||
|
navWidth - currentScroll > containerWidth * 2
|
||||||
|
? currentScroll + containerWidth
|
||||||
|
: navWidth - containerWidth;
|
||||||
|
scrollTo(scrollLeft, (scrollLeft - currentScroll) / 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param autoScroll 是否开启自动滚动功能
|
||||||
|
*/
|
||||||
|
async function updateNavScroll(autoScroll?: boolean) {
|
||||||
|
await nextTick();
|
||||||
|
if (!navScroll.value) return;
|
||||||
|
const containerWidth = navScroll.value.offsetWidth;
|
||||||
|
const navWidth = navScroll.value.scrollWidth;
|
||||||
|
|
||||||
|
if (containerWidth < navWidth) {
|
||||||
|
state.scrollable = true;
|
||||||
|
if (autoScroll) {
|
||||||
|
let tagList =
|
||||||
|
navScroll.value.querySelectorAll('.tabs-card-scroll-item') || [];
|
||||||
|
[...tagList].forEach((tag: HTMLElement) => {
|
||||||
|
// fix SyntaxError
|
||||||
|
if (tag.id === `tag${state.activeKey.split('/').join('\/')}`) {
|
||||||
|
tag.scrollIntoView && tag.scrollIntoView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.scrollable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResize() {
|
||||||
|
updateNavScroll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleContextMenu(e, item) {
|
||||||
|
e.preventDefault();
|
||||||
|
isCurrent.value = PageEnum.BASE_HOME === item.path;
|
||||||
|
state.showDropdown = false;
|
||||||
|
nextTick().then(() => {
|
||||||
|
state.showDropdown = true;
|
||||||
|
state.dropdownX = e.clientX;
|
||||||
|
state.dropdownY = e.clientY;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state.scrollable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResize() {
|
function onClickOutside() {
|
||||||
updateNavScroll(true);
|
state.showDropdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleContextMenu(e, item) {
|
//tags 跳转页面
|
||||||
e.preventDefault();
|
function goPage(e) {
|
||||||
isCurrent.value = PageEnum.BASE_HOME === item.path;
|
const { fullPath } = e;
|
||||||
state.showDropdown = false;
|
if (fullPath === route.fullPath) return;
|
||||||
nextTick().then(() => {
|
state.activeKey = fullPath;
|
||||||
state.showDropdown = true;
|
go(e, true);
|
||||||
state.dropdownX = e.clientX;
|
}
|
||||||
state.dropdownY = e.clientY;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClickOutside() {
|
//删除tab
|
||||||
state.showDropdown = false;
|
function closeTabItem(e) {
|
||||||
}
|
const { fullPath } = e;
|
||||||
|
const routeInfo = tabsList.value.find(
|
||||||
|
(item) => item.fullPath == fullPath
|
||||||
|
);
|
||||||
|
removeTab(routeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
//tags 跳转页面
|
onMounted(() => {
|
||||||
function goPage(e) {
|
// onElementResize();
|
||||||
const { fullPath } = e;
|
});
|
||||||
if (fullPath === route.fullPath) return;
|
|
||||||
state.activeKey = fullPath;
|
|
||||||
go(e, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//删除tab
|
// function onElementResize() {
|
||||||
function closeTabItem(e) {
|
// let observer;
|
||||||
const { fullPath } = e;
|
// observer = elementResizeDetectorMaker();
|
||||||
const routeInfo = tabsList.value.find((item) => item.fullPath == fullPath);
|
// observer.listenTo(navWrap.value, handleResize);
|
||||||
removeTab(routeInfo);
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
return {
|
||||||
// onElementResize();
|
...toRefs(state),
|
||||||
|
navWrap,
|
||||||
|
navScroll,
|
||||||
|
route,
|
||||||
|
tabsList,
|
||||||
|
goPage,
|
||||||
|
closeTabItem,
|
||||||
|
closeLeft,
|
||||||
|
closeRight,
|
||||||
|
closeOther,
|
||||||
|
closeAll,
|
||||||
|
reloadPage,
|
||||||
|
getChangeStyle,
|
||||||
|
TabsMenuOptions,
|
||||||
|
closeHandleSelect,
|
||||||
|
scrollNext,
|
||||||
|
scrollPrev,
|
||||||
|
handleContextMenu,
|
||||||
|
onClickOutside,
|
||||||
|
getDarkTheme,
|
||||||
|
getAppTheme,
|
||||||
|
getcardcolor,
|
||||||
|
getbasecolor,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// function onElementResize() {
|
|
||||||
// let observer;
|
|
||||||
// observer = elementResizeDetectorMaker();
|
|
||||||
// observer.listenTo(navWrap.value, handleResize);
|
|
||||||
// }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user