refactor: pinia整合优化重构

This commit is contained in:
郝先瑞
2022-03-11 00:07:34 +08:00
parent 7bd9d70433
commit 4ae629ab65
26 changed files with 675 additions and 563 deletions

View File

@@ -1,9 +1,9 @@
<template>
<section class="app-main">
<router-view v-slot="{ Component ,route}">
<router-view v-slot="{ Component, route }">
<transition name="router-fade" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.path"/>
<component :is="Component" :key="route.path" />
</keep-alive>
</transition>
</router-view>
@@ -12,11 +12,12 @@
<script setup lang="ts">
import {computed} from "vue";
import {useTagsViewStoreHook} from '@/store/modules/tagsView'
import { computed } from "vue";
import useStore from "@/store";
const cachedViews = computed(() => useTagsViewStoreHook().cachedViews);
const { tagsView } = useStore();
const cachedViews = computed(() => tagsView.cachedViews);
</script>
<style lang="scss" scoped>
@@ -28,7 +29,7 @@ const cachedViews = computed(() => useTagsViewStoreHook().cachedViews);
overflow: hidden;
}
.fixed-header+.app-main {
.fixed-header + .app-main {
padding-top: 50px;
}
@@ -38,7 +39,7 @@ const cachedViews = computed(() => useTagsViewStoreHook().cachedViews);
min-height: calc(100vh - 84px);
}
.fixed-header+.app-main {
.fixed-header + .app-main {
padding-top: 84px;
}
}

View File

@@ -1,113 +1,124 @@
<template>
<div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container"
@toggleClick="toggleSideBar"/>
<hamburger
id="hamburger-container"
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/>
<breadcrumb id="breadcrumb-container" class="breadcrumb-container"/>
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<div class="right-menu">
<template v-if="device!=='mobile'">
<template v-if="device !== 'mobile'">
<!-- <search id="header-search" class="right-menu-item" />
<error-log class="errLog-container right-menu-item hover-effect" />-->
<screenfull id="screenfull" class="right-menu-item hover-effect"/>
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect"/>
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
<lang-select class="right-menu-item hover-effect" />
</template>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<el-dropdown
class="avatar-container right-menu-item hover-effect"
trigger="click"
>
<div class="avatar-wrapper">
<img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
<CaretBottom style="width: .6em; height: .6em;margin-left: 5px"/>
<img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" />
<CaretBottom style="width: 0.6em; height: 0.6em; margin-left: 5px" />
</div>
<template #dropdown>
<el-dropdown-menu>
<router-link to="/">
<el-dropdown-item>{{$t('navbar.dashboard')}}</el-dropdown-item>
<el-dropdown-item>{{ $t("navbar.dashboard") }}</el-dropdown-item>
</router-link>
<a target="_blank" href="https://github.com/hxrui">
<el-dropdown-item>Github</el-dropdown-item>
</a>
<a target="_blank" href="https://gitee.com/haoxr">
<el-dropdown-item>{{$t('navbar.gitee')}}</el-dropdown-item>
<el-dropdown-item>{{ $t("navbar.gitee") }}</el-dropdown-item>
</a>
<a target="_blank" href="https://www.cnblogs.com/haoxianrui/">
<el-dropdown-item>{{$t('navbar.document')}}</el-dropdown-item>
<el-dropdown-item>{{ $t("navbar.document") }}</el-dropdown-item>
</a>
<el-dropdown-item divided @click="logout">
{{$t('navbar.logout')}}
{{ $t("navbar.logout") }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<script setup lang="ts">
import {computed} from "vue"
import {useRoute, useRouter} from "vue-router"
import {ElMessageBox} from 'element-plus'
import { computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { ElMessageBox } from "element-plus";
import {useAppStoreHook} from '@/store/modules/app'
import {useUserStoreHook} from '@/store/modules/user'
import useStore from "@/store";
// 组件依赖
import Breadcrumb from '@/components/Breadcrumb/index.vue'
import Hamburger from '@/components/Hamburger/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import SizeSelect from '@/components/SizeSelect/index.vue'
import LangSelect from '@/components/LangSelect/index.vue'
import SvgIcon from '@/components/SvgIcon/index.vue'
import Breadcrumb from "@/components/Breadcrumb/index.vue";
import Hamburger from "@/components/Hamburger/index.vue";
import Screenfull from "@/components/Screenfull/index.vue";
import SizeSelect from "@/components/SizeSelect/index.vue";
import LangSelect from "@/components/LangSelect/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
// 图标依赖
import {CaretBottom} from '@element-plus/icons-vue'
import { CaretBottom } from "@element-plus/icons-vue";
const route = useRoute()
const router = useRouter()
const { app, user } = useStore();
const sidebar = computed(() => useAppStoreHook().sidebar)
const device = computed(() => useAppStoreHook().device)
const avatar = computed(() => useUserStoreHook().avatar)
const route = useRoute();
const router = useRouter();
const sidebar = computed(() => app.sidebar);
const device = computed(() => app.device);
const avatar = computed(() => user.avatar);
function toggleSideBar() {
useAppStoreHook().toggleSidebar()
app.toggleSidebar();
}
function logout() {
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
useUserStoreHook().logout().then(() => {
router.push(`/login?redirect=${route.fullPath}`)
})
})
user.logout().then(() => {
router.push(`/login?redirect=${route.fullPath}`);
});
});
}
</script>
<style lang="scss" scoped>
ul { list-style: none; margin: 0; padding: 0; }
ul {
list-style: none;
margin: 0;
padding: 0;
}
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, .025)
background: rgba(0, 0, 0, 0.025);
}
}
@@ -134,10 +145,10 @@ ul { list-style: none; margin: 0; padding: 0; }
&.hover-effect {
cursor: pointer;
transition: background .3s;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, .025)
background: rgba(0, 0, 0, 0.025);
}
}
}

View File

@@ -1,59 +1,71 @@
<template>
<div class="drawer-container">
<h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>主题颜色</span>
<div style="float: right;height: 26px;margin: -3px 8px 0 0;">
<theme-picker @change="themeChange"/>
</div>
<h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>主题颜色</span>
<div style="float: right; height: 26px; margin: -3px 8px 0 0">
<theme-picker @change="themeChange" />
</div>
</div>
<div class="drawer-item">
<span>开启 Tags-View</span>
<el-switch v-model="tagsView" class="drawer-switch"/>
</div>
<div class="drawer-item">
<span>开启 Tags-View</span>
<el-switch v-model="tagsView" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>固定 Header</span>
<el-switch v-model="fixedHeader" class="drawer-switch"/>
</div>
<div class="drawer-item">
<span>固定 Header</span>
<el-switch v-model="fixedHeader" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>侧边栏 Logo</span>
<el-switch v-model="sidebarLogo" class="drawer-switch"/>
</div>
<div class="drawer-item">
<span>侧边栏 Logo</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" />
</div>
</div>
</template>
<script setup lang="ts">
import {reactive, toRefs, watch} from "vue";
import {useSettingStoreHook} from "@/store/modules/settings";
import ThemePicker from '@/components/ThemePicker/index.vue';
import { reactive, toRefs, watch } from "vue";
import ThemePicker from "@/components/ThemePicker/index.vue";
import useStore from "@/store";
const { setting } = useStore();
const state = reactive({
fixedHeader: useSettingStoreHook().fixedHeader,
tagsView: useSettingStoreHook().tagsView,
sidebarLogo: useSettingStoreHook().sidebarLogo
})
fixedHeader: setting.fixedHeader,
tagsView: setting.tagsView,
sidebarLogo: setting.sidebarLogo,
});
const {fixedHeader, tagsView, sidebarLogo} = toRefs(state)
const { fixedHeader, tagsView, sidebarLogo } = toRefs(state);
function themeChange(val: any) {
useSettingStoreHook().changeSetting({key: 'theme', value: val})
setting.changeSetting({ key: "theme", value: val });
}
watch(() => state.fixedHeader, (value) => {
useSettingStoreHook().changeSetting({key: 'fixedHeader', value: value})
})
watch(
() => state.fixedHeader,
(value) => {
setting.changeSetting({ key: "fixedHeader", value: value });
}
);
watch(() => state.tagsView, (value) => {
useSettingStoreHook().changeSetting({key: 'tagsView', value: value})
})
watch(() => state.sidebarLogo, (value) => {
useSettingStoreHook().changeSetting({key: 'sidebarLogo', value: value})
})
watch(
() => state.tagsView,
(value) => {
setting.changeSetting({ key: "tagsView", value: value });
}
);
watch(
() => state.sidebarLogo,
(value) => {
setting.changeSetting({ key: "sidebarLogo", value: value });
}
);
</script>
<style lang="scss" scoped>
@@ -65,19 +77,19 @@ watch(() => state.sidebarLogo, (value) => {
.drawer-title {
margin-bottom: 12px;
color: rgba(0, 0, 0, .85);
color: rgba(0, 0, 0, 0.85);
font-size: 14px;
line-height: 22px;
}
.drawer-item {
color: rgba(0, 0, 0, .65);
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
padding: 12px 0;
}
.drawer-switch {
float: right
float: right;
}
.job-link {

View File

@@ -19,10 +19,13 @@
import {computed, defineComponent} from 'vue'
import { isExternal } from '@/utils/validate'
import { useRouter } from 'vue-router'
import {useAppStoreHook} from "@/store/modules/app";
const sidebar = computed(() => useAppStoreHook().sidebar);
const device = computed(() => useAppStoreHook().device);
import useStore from "@/store";
const {app}=useStore()
const sidebar = computed(() => app.sidebar);
const device = computed(() => app.device);
export default defineComponent({
props: {
@@ -35,7 +38,7 @@ export default defineComponent({
const router = useRouter()
const push = () => {
if (device.value === 'mobile' && sidebar.value.opened == true) {
useAppStoreHook().closeSideBar(false)
app.closeSideBar(false)
}
router.push(props.to).catch((err) => {
console.log(err)

View File

@@ -39,8 +39,8 @@ import {isExternal} from '@/utils/validate'
import AppLink from './Link.vue'
import {RouteRecordRaw} from "vue-router";
import SvgIcon from '@/components/SvgIcon/index.vue';
import { generateTitle } from '@/utils/i18n'
import SvgIcon from '@/components/SvgIcon/index.vue';
const props = defineProps({
item: {

View File

@@ -24,20 +24,22 @@
</template>
<script setup lang="ts">
import {computed, defineComponent} from "vue";
import {useRoute} from 'vue-router'
import SidebarItem from './SidebarItem.vue'
import Logo from './Logo.vue'
import variables from '@/styles/variables.module.scss'
import { useSettingStoreHook } from "@/store/modules/settings";
import { useAppStoreHook } from "@/store/modules/app";
import { usePermissionStoreHook } from "@/store/modules/permission";
import {useRoute} from 'vue-router'
import useStore from "@/store";
const {permission,setting,app} =useStore();
const route =useRoute()
const routes =computed(() => usePermissionStoreHook().routes)
const showLogo = computed(() => useSettingStoreHook().sidebarLogo)
const isCollapse = computed(() => !useAppStoreHook().sidebar.opened)
const routes =computed(() => permission.routes)
const showLogo = computed(() => setting.sidebarLogo)
const isCollapse = computed(() => !app.sidebar.opened)
const activeMenu = computed(() => {
const {meta, path} = route

View File

@@ -1,95 +1,119 @@
<template>
<el-scrollbar
ref="scrollContainerRef"
:vertical="false"
class="scroll-container"
@wheel.prevent="handleScroll">
<slot/>
ref="scrollContainerRef"
:vertical="false"
class="scroll-container"
@wheel.prevent="handleScroll"
>
<slot />
</el-scrollbar>
</template>
<script setup lang="ts">
import {ref, computed, onMounted, onBeforeUnmount, getCurrentInstance} from "vue";
import {useTagsViewStoreHook} from "@/store/modules/tagsView"
import {TagView} from "@/store/interface";
const emits = defineEmits()
import {
ref,
computed,
onMounted,
onBeforeUnmount,
getCurrentInstance,
} from "vue";
import { TagView } from "@/store/interface";
import useStore from "@/store";
const tagAndTagSpacing = ref(4)
const scrollContainerRef = ref(null)
const visitedViews = computed(() => useTagsViewStoreHook().visitedViews)
const emits = defineEmits();
const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref(null);
const { tagsView } = useStore();
const visitedViews = computed(() => tagsView.visitedViews);
const emitScroll = () => {
(emits as any)('scroll')
}
(emits as any)("scroll");
};
const {ctx} = getCurrentInstance() as any
const { ctx } = getCurrentInstance() as any;
const scrollWrapper = computed(() => {
return (scrollContainerRef.value as any).$refs.wrap as HTMLElement
})
return (scrollContainerRef.value as any).$refs.wrap as HTMLElement;
});
onMounted(() => {
//scrollWrapper.value.addEventListener('scroll', emitScroll, true);
})
});
onBeforeUnmount(() => {
// scrollWrapper.value.removeEventListener('scroll', emitScroll);
})
});
function handleScroll(e: WheelEvent) {
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40
scrollWrapper.value.scrollLeft = scrollWrapper.value.scrollLeft + eventDelta / 4
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
scrollWrapper.value.scrollLeft =
scrollWrapper.value.scrollLeft + eventDelta / 4;
}
function moveToTarget(currentTag:TagView) {
const $container = ctx.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth
function moveToTarget(currentTag: TagView) {
const $container = ctx.$refs.scrollContainer.$el;
const $containerWidth = $container.offsetWidth;
const $scrollWrapper = scrollWrapper.value;
let firstTag = null
let lastTag = null
let firstTag = null;
let lastTag = null;
// find first tag and last tag
if (visitedViews.value.length > 0) {
firstTag = visitedViews.value[0]
lastTag = visitedViews.value[visitedViews.value.length - 1]
firstTag = visitedViews.value[0];
lastTag = visitedViews.value[visitedViews.value.length - 1];
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
$scrollWrapper.scrollLeft = 0;
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
} else {
const tagListDom = document.getElementsByClassName('tags-view-item');
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
let prevTag = null
let nextTag = null
const tagListDom = document.getElementsByClassName("tags-view-item");
const currentIndex = visitedViews.value.findIndex(
(item) => item === currentTag
);
let prevTag = null;
let nextTag = null;
for (const k in tagListDom) {
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
if ((tagListDom[k] as any).dataset.path === visitedViews.value[currentIndex - 1].path) {
prevTag = tagListDom[k] ;
if (k !== "length" && Object.hasOwnProperty.call(tagListDom, k)) {
if (
(tagListDom[k] as any).dataset.path ===
visitedViews.value[currentIndex - 1].path
) {
prevTag = tagListDom[k];
}
if ((tagListDom[k] as any).dataset.path === visitedViews.value[currentIndex + 1].path) {
if (
(tagListDom[k] as any).dataset.path ===
visitedViews.value[currentIndex + 1].path
) {
nextTag = tagListDom[k];
}
}
}
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = (nextTag as any).offsetLeft + (nextTag as any).offsetWidth + tagAndTagSpacing.value
const afterNextTagOffsetLeft =
(nextTag as any).offsetLeft +
(nextTag as any).offsetWidth +
tagAndTagSpacing.value;
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = (prevTag as any).offsetLeft - tagAndTagSpacing.value
const beforePrevTagOffsetLeft =
(prevTag as any).offsetLeft - tagAndTagSpacing.value;
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
}
}
}
defineExpose({
moveToTarget
})
moveToTarget,
});
</script>
<style lang="scss" scoped>

View File

@@ -1,44 +1,59 @@
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
<scroll-pane
ref="scrollPaneRef"
class="tags-view-wrapper"
@scroll="handleScroll"
>
<router-link
v-for="tag in visitedViews"
:key="tag.path"
:class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
class="tags-view-item"
@click.middle="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent="openMenu(tag,$event)"
v-for="tag in visitedViews"
:key="tag.path"
:class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
class="tags-view-item"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)"
>
{{ generateTitle(tag.meta.title) }}
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)">
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;"/>
<span
v-if="!isAffix(tag)"
class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)"
>
<close
class="el-icon-close"
style="width: 1em; height: 1em; vertical-align: middle"
/>
</span>
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<ul
v-show="visible"
:style="{ left: left + 'px', top: top + 'px' }"
class="contextmenu"
>
<li @click="refreshSelectedTag(selectedTag)">
<refresh-right style="width: 1em; height: 1em;"/>
<refresh-right style="width: 1em; height: 1em" />
刷新
</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
<close style="width: 1em; height: 1em;"/>
<close style="width: 1em; height: 1em" />
关闭
</li>
<li @click="closeOtherTags">
<circle-close style="width: 1em; height: 1em;"/>
<circle-close style="width: 1em; height: 1em" />
关闭其它
</li>
<li v-if="!isFirstView()" @click="closeLeftTags">
<back style="width: 1em; height: 1em;"/>
<back style="width: 1em; height: 1em" />
关闭左侧
</li>
<li v-if="!isLastView()" @click="closeRightTags">
<right style="width: 1em; height: 1em;"/>
<right style="width: 1em; height: 1em" />
关闭右侧
</li>
<li @click="closeAllTags(selectedTag)">
<circle-close style="width: 1em; height: 1em;"/>
<circle-close style="width: 1em; height: 1em" />
关闭所有
</li>
</ul>
@@ -46,31 +61,39 @@
</template>
<script setup lang="ts" >
import {useTagsViewStoreHook} from '@/store/modules/tagsView'
import {usePermissionStoreHook} from '@/store/modules/permission'
import path from 'path-browserify'
import {
computed,
getCurrentInstance,
nextTick,
ref,
watch,
onMounted
onMounted,
} from "vue";
import {RouteRecordRaw, useRoute, useRouter} from 'vue-router'
import {TagView} from "@/store/interface";
import ScrollPane from './ScrollPane.vue'
import {Close,RefreshRight,CircleClose,Back,Right} from '@element-plus/icons-vue'
import { generateTitle } from '@/utils/i18n'
import path from "path-browserify";
const {ctx} = getCurrentInstance() as any
const router = useRouter()
import { RouteRecordRaw, useRoute, useRouter } from "vue-router";
import { TagView } from "@/store/interface";
import ScrollPane from "./ScrollPane.vue";
import {
Close,
RefreshRight,
CircleClose,
Back,
Right,
} from "@element-plus/icons-vue";
import { generateTitle } from "@/utils/i18n";
import useStore from "@/store";
const { tagsView, permission } = useStore();
const { ctx } = getCurrentInstance() as any;
const router = useRouter();
const route = useRoute();
const visitedViews = computed<any>(() => useTagsViewStoreHook().visitedViews)
const routes = computed<any>(() => usePermissionStoreHook().routes)
const visitedViews = computed<any>(() => tagsView.visitedViews);
const routes = computed<any>(() => permission.routes);
const affixTags = ref([]);
const visible = ref(false);
@@ -80,197 +103,207 @@ const left = ref(0);
const top = ref(0);
watch(route, () => {
addTags()
moveToCurrentTag()
})
addTags();
moveToCurrentTag();
});
watch(visible, (value) => {
if (value) {
document.body.addEventListener('click', closeMenu)
document.body.addEventListener("click", closeMenu);
} else {
document.body.removeEventListener('click', closeMenu)
document.body.removeEventListener("click", closeMenu);
}
})
});
function filterAffixTags(routes: RouteRecordRaw[], basePath = '/') {
let tags: TagView[] = []
function filterAffixTags(routes: RouteRecordRaw[], basePath = "/") {
let tags: TagView[] = [];
routes.forEach(route => {
routes.forEach((route) => {
if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path)
const tagPath = path.resolve(basePath, route.path);
tags.push({
fullPath: tagPath,
path: tagPath,
name: route.name,
meta: {...route.meta}
})
meta: { ...route.meta },
});
}
if (route.children) {
const childTags = filterAffixTags(route.children, route.path)
const childTags = filterAffixTags(route.children, route.path);
if (childTags.length >= 1) {
tags = tags.concat(childTags)
tags = tags.concat(childTags);
}
}
})
return tags
});
return tags;
}
function initTags() {
const res = filterAffixTags(routes.value) as []
affixTags.value = res
const res = filterAffixTags(routes.value) as [];
affixTags.value = res;
for (const tag of res) {
// Must have tag name
if ((tag as TagView).name) {
useTagsViewStoreHook().addVisitedView(tag)
tagsView.addVisitedView(tag);
}
}
}
function addTags() {
if (route.name) {
useTagsViewStoreHook().addView(route)
tagsView.addView(route);
}
return false
return false;
}
function moveToCurrentTag() {
const tags = getCurrentInstance()?.refs.tag as any[]
const tags = getCurrentInstance()?.refs.tag as any[];
nextTick(() => {
if (tags === null || tags === undefined || !Array.isArray(tags)) {
return
return;
}
for (const tag of tags) {
if ((tag.to as TagView).path === route.path) {
(scrollPaneRef.value as any).value.moveToTarget(tag)
(scrollPaneRef.value as any).value.moveToTarget(tag);
// when query is different then update
if ((tag.to as TagView).fullPath !== route.fullPath) {
useTagsViewStoreHook().updateVisitedView(route)
tagsView.updateVisitedView(route);
}
}
}
})
});
}
function isActive(tag: TagView) {
return tag.path === route.path
return tag.path === route.path;
}
function isAffix(tag: TagView) {
return tag.meta && tag.meta.affix
return tag.meta && tag.meta.affix;
}
function isFirstView() {
try {
return (selectedTag.value as TagView).fullPath === visitedViews.value[1].fullPath || (selectedTag.value as TagView).fullPath === '/index'
return (
(selectedTag.value as TagView).fullPath ===
visitedViews.value[1].fullPath ||
(selectedTag.value as TagView).fullPath === "/index"
);
} catch (err) {
return false
return false;
}
}
function isLastView() {
try {
return (selectedTag.value as TagView).fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
return (
(selectedTag.value as TagView).fullPath ===
visitedViews.value[visitedViews.value.length - 1].fullPath
);
} catch (err) {
return false
return false;
}
}
function refreshSelectedTag(view: TagView) {
useTagsViewStoreHook().delCachedView(view)
const {fullPath} = view
tagsView.delCachedView(view);
const { fullPath } = view;
nextTick(() => {
router.replace({path: '/redirect' + fullPath}).catch(err => {
console.warn(err)
})
})
router.replace({ path: "/redirect" + fullPath }).catch((err) => {
console.warn(err);
});
});
}
function toLastView(visitedViews: TagView[], view?: any) {
const latestView = visitedViews.slice(-1)[0]
const latestView = visitedViews.slice(-1)[0];
if (latestView && latestView.fullPath) {
router.push(latestView.fullPath)
router.push(latestView.fullPath);
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === 'Dashboard') {
if (view.name === "Dashboard") {
// to reload home page
router.replace({path: '/redirect' + view.fullPath})
router.replace({ path: "/redirect" + view.fullPath });
} else {
router.push('/')
router.push("/");
}
}
}
function closeSelectedTag(view: TagView) {
useTagsViewStoreHook().delView(view).then((res: any) => {
tagsView.delView(view).then((res: any) => {
if (isActive(view)) {
toLastView(res.visitedViews, view)
toLastView(res.visitedViews, view);
}
})
});
}
function closeLeftTags() {
useTagsViewStoreHook().delLeftViews(selectedTag.value).then((res: any) => {
if (!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)) {
toLastView(res.visitedViews)
tagsView.delLeftViews(selectedTag.value).then((res: any) => {
if (
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
) {
toLastView(res.visitedViews);
}
})
});
}
function closeRightTags() {
useTagsViewStoreHook().delRightViews(selectedTag.value).then((res:any) => {
if (!res.visitedViews.find((item:any) => item.fullPath === route.fullPath)) {
toLastView(res.visitedViews)
tagsView.delRightViews(selectedTag.value).then((res: any) => {
if (
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
) {
toLastView(res.visitedViews);
}
})
});
}
function closeOtherTags() {
useTagsViewStoreHook().delOtherViews(selectedTag.value).then(() => {
moveToCurrentTag()
})
tagsView.delOtherViews(selectedTag.value).then(() => {
moveToCurrentTag();
});
}
function closeAllTags(view: TagView) {
useTagsViewStoreHook().delRightViews(selectedTag.value).then((res:any) => {
if (affixTags.value.some((tag:any) => tag.path === route.path)) {
return
tagsView.delRightViews(selectedTag.value).then((res: any) => {
if (affixTags.value.some((tag: any) => tag.path === route.path)) {
return;
}
toLastView(res.visitedViews, view)
})
toLastView(res.visitedViews, view);
});
}
function openMenu(tag: TagView, e: MouseEvent) {
const menuMinWidth = 105
const offsetLeft = ctx.$el.getBoundingClientRect().left // container margin left
const offsetWidth = ctx.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const l = e.clientX - offsetLeft + 15 // 15: margin right
const menuMinWidth = 105;
const offsetLeft = ctx.$el.getBoundingClientRect().left; // container margin left
const offsetWidth = ctx.$el.offsetWidth; // container width
const maxLeft = offsetWidth - menuMinWidth; // left boundary
const l = e.clientX - offsetLeft + 15; // 15: margin right
if (l > maxLeft) {
left.value = maxLeft
left.value = maxLeft;
} else {
left.value = l
left.value = l;
}
top.value = e.clientY
visible.value = true
selectedTag.value = tag
top.value = e.clientY;
visible.value = true;
selectedTag.value = tag;
}
function closeMenu() {
visible.value = false
visible.value = false;
}
function handleScroll() {
closeMenu()
closeMenu();
}
onMounted(() => {
initTags()
addTags()
})
initTags();
addTags();
});
</script>
<style lang='scss' scoped>