fix: 修改TagsView样式问题

This commit is contained in:
戴业伟 2024-02-05 11:07:48 +08:00
parent 6fff76e3f8
commit 58337f771e

View File

@ -2,8 +2,8 @@
<div
class="box-border tabs-view"
:class="{
'tabs-view-fix': state.multiTabsSetting.fixed,
'tabs-view-fixed-header': state.isMultiHeaderFixed,
'tabs-view-fix': multiTabsSetting.fixed,
'tabs-view-fixed-header': isMultiHeaderFixed,
'tabs-view-default-background': getDarkTheme === false,
'tabs-view-dark-background': getDarkTheme === true,
}"
@ -13,11 +13,11 @@
<div
ref="navWrap"
class="tabs-card"
:class="{ 'tabs-card-scrollable': state.scrollable }"
:class="{ 'tabs-card-scrollable': scrollable }"
>
<span
class="tabs-card-prev"
:class="{ 'tabs-card-prev-hide': !state.scrollable }"
:class="{ 'tabs-card-prev-hide': !scrollable }"
@click="scrollPrev"
>
<n-icon size="16" color="#515a6e">
@ -26,7 +26,7 @@
</span>
<span
class="tabs-card-next"
:class="{ 'tabs-card-next-hide': !state.scrollable }"
:class="{ 'tabs-card-next-hide': !scrollable }"
@click="scrollNext"
>
<n-icon size="16" color="#515a6e">
@ -44,7 +44,7 @@
<div
:id="`tag${element.fullPath.split('/').join('\/')}`"
class="tabs-card-scroll-item"
:class="{ 'active-item': state.activeKey === element.fullPath }"
:class="{ 'active-item': activeKey === element.fullPath }"
@click.stop="goPage(element)"
@contextmenu="handleContextMenu($event, element)"
>
@ -76,9 +76,9 @@
</n-dropdown>
</div>
<n-dropdown
:show="state.showDropdown"
:x="state.dropdownX"
:y="state.dropdownY"
:show="showDropdown"
:x="dropdownX"
:y="dropdownY"
@clickoutside="onClickOutside"
placement="bottom-start"
@select="closeHandleSelect"
@ -88,13 +88,26 @@
</div>
</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 { storage } from '@/utils';
import { StorageEnum } from '@/enums/storageEnum';
import { useTabsViewStore } from '@/store/modules/tabsView';
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
import { RouteItem } from '@/store/modules/tabsView';
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
import { useMessage } from 'naive-ui';
import Draggable from 'vuedraggable';
import { PageEnum } from '@/enums/pageEnum';
import {
@ -107,390 +120,432 @@ import {
RightOutlined,
} from '@vicons/antd';
import { renderIcon } from '@/utils';
// import elementResizeDetectorMaker from "element-resize-detector";
// import elementResizeDetectorMaker from 'element-resize-detector';
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
import { useProjectSettingStore } from '@/store/modules/projectSetting';
import { useThemeVars } from 'naive-ui';
import { useGo } from '@/hooks/web/usePage';
import { StorageEnum } from '@/enums/storageEnum';
const props = defineProps({
collapsed: {
type: Boolean,
export default defineComponent({
name: 'TabsView',
components: {
DownOutlined,
CloseOutlined,
LeftOutlined,
RightOutlined,
Draggable,
},
});
const { getDarkTheme, getAppTheme } = useDesignSetting();
const { navMode, headerSetting, menuSetting, multiTabsSetting, isMobile } =
useProjectSetting();
const settingStore = useProjectSettingStore();
props: {
collapsed: {
type: Boolean,
},
},
setup(props) {
const { getDarkTheme, getAppTheme } = useDesignSetting();
const { navMode, headerSetting, menuSetting, multiTabsSetting, isMobile } =
useProjectSetting();
const settingStore = useProjectSettingStore();
const route = useRoute();
const router = useRouter();
const tabsViewStore = useTabsViewStore();
const asyncRouteStore = useAsyncRouteStore();
const navScroll: any = ref(null);
const navWrap: any = ref(null);
const isCurrent = ref(false);
const go = useGo();
const message = useMessage();
const route = useRoute();
const router = useRouter();
const tabsViewStore = useTabsViewStore();
const asyncRouteStore = useAsyncRouteStore();
const navScroll: any = ref(null);
const navWrap: any = ref(null);
const isCurrent = ref(false);
const go = useGo();
const themeVars = useThemeVars();
const themeVars = useThemeVars();
const getCardColor = computed(() => {
return themeVars.value.cardColor;
});
const getcardcolor = computed(() => {
return themeVars.value.cardColor;
});
const getBaseColor = computed(() => {
return themeVars.value.textColor1;
});
const getbasecolor = computed(() => {
return themeVars.value.textColor1;
});
const state = reactive({
activeKey: route.fullPath,
scrollable: false,
dropdownX: 0,
dropdownY: 0,
showDropdown: false,
isMultiHeaderFixed: false,
multiTabsSetting: multiTabsSetting,
});
const state = reactive({
activeKey: route.fullPath,
scrollable: false,
dropdownX: 0,
dropdownY: 0,
showDropdown: false,
isMultiHeaderFixed: false,
multiTabsSetting: multiTabsSetting,
});
//
const getSimpleRoute = (route): RouteItem => {
const { fullPath, hash, meta, name, params, path, query } = route;
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%',
//
const getSimpleRoute = (route): RouteItem => {
const { fullPath, hash, meta, name, params, path, query } = route;
return { fullPath, hash, meta, name, params, path, query };
};
}
return {
left: lenNum,
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
};
});
//tags
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),
},
];
});
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
);
});
let cacheRoutes: RouteItem[] = [];
const simpleRoute = getSimpleRoute(route);
try {
const routesStr = storage.get(StorageEnum.ZS_TABS_ROUTES) as
| string
| null
| undefined;
cacheRoutes = routesStr ? JSON.parse(routesStr) : [simpleRoute];
} catch (e) {
cacheRoutes = [simpleRoute];
}
//
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`;
// localStorage
const routes = router.getRoutes();
cacheRoutes.forEach((cacheRoute) => {
const route = routes.find((route) => route.path === cacheRoute.path);
if (route) {
cacheRoute.meta = route.meta || cacheRoute.meta;
cacheRoute.name = (route.name || cacheRoute.name) as string;
}
});
if (isMobile.value) {
return {
left: '0px',
width: '100%',
};
}
return {
left: lenNum,
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
};
});
//
tabsViewStore.initTabs(cacheRoutes);
//tags
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),
},
];
});
//
function onScroll(e) {
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 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);
let cacheRoutes: RouteItem[] = [];
const simpleRoute = getSimpleRoute(route);
try {
const routesStr = storage.get(StorageEnum.ZS_TABS_ROUTES) as
| string
| null
| undefined;
cacheRoutes = routesStr ? JSON.parse(routesStr) : [simpleRoute];
} catch (e) {
cacheRoutes = [simpleRoute];
}
}
};
//
const tabsList: any = computed(() => tabsViewStore.tabsList);
const whiteList: string[] = [
PageEnum.BASE_LOGIN_NAME,
PageEnum.REDIRECT_NAME,
PageEnum.ERROR_PAGE_NAME,
];
// localStorage
const routes = router.getRoutes();
cacheRoutes.forEach((cacheRoute) => {
const route = routes.find((route) => route.path === cacheRoute.path);
if (route) {
cacheRoute.meta = route.meta || cacheRoute.meta;
cacheRoute.name = (route.name || cacheRoute.name) as string;
}
});
watch(
() => route.fullPath,
(to) => {
if (whiteList.includes(route.name as string)) return;
state.activeKey = to;
tabsViewStore.addTab(getSimpleRoute(route));
updateNavScroll(true);
},
{ immediate: true }
);
//
tabsViewStore.initTabs(cacheRoutes);
//
window.addEventListener('beforeunload', () => {
storage.set(StorageEnum.ZS_TABS_ROUTES, JSON.stringify(tabsList.value));
});
//
function onScroll(e) {
let scrollTop =
e.target.scrollTop ||
document.documentElement.scrollTop ||
window.pageYOffset ||
document.body.scrollTop; //
state.isMultiHeaderFixed = !!(
!headerSetting.value.fixed &&
multiTabsSetting.value.fixed &&
scrollTop >= 64
);
}
//
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();
};
window.addEventListener('scroll', onScroll, true);
//
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();
//
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);
}
}
};
//
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() {
updateNavScroll(true);
}
function onClickOutside() {
state.showDropdown = false;
}
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;
});
}
//tags
function goPage(e) {
const { fullPath } = e;
if (fullPath === route.fullPath) return;
state.activeKey = fullPath;
go(e, true);
}
function onClickOutside() {
state.showDropdown = false;
}
//tab
function closeTabItem(e) {
const { fullPath } = e;
const routeInfo = tabsList.value.find(
(item) => item.fullPath == fullPath
);
removeTab(routeInfo);
}
//tags
function goPage(e) {
const { fullPath } = e;
if (fullPath === route.fullPath) return;
state.activeKey = fullPath;
go(e, true);
}
onMounted(() => {
// onElementResize();
});
//tab
function closeTabItem(e) {
const { fullPath } = e;
const routeInfo = tabsList.value.find((item) => item.fullPath == fullPath);
removeTab(routeInfo);
}
// function onElementResize() {
// let observer;
// observer = elementResizeDetectorMaker();
// observer.listenTo(navWrap.value, handleResize);
// }
onMounted(() => {
// onElementResize();
return {
...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>
<style lang="scss" scoped>