Skip to content

Commit

Permalink
增加tag菜单功能
Browse files Browse the repository at this point in the history
  • Loading branch information
Akiraka committed Sep 5, 2024
1 parent f70c9f4 commit d352198
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
197 changes: 197 additions & 0 deletions src/layout/components/TagView/TagView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
<script setup>
import { watch, ref, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useTagViewStore } from '@/store/tagView';
import { onMounted } from 'vue';
const store = useTagViewStore();
const route = useRoute();
const router = useRouter();
const tagListRef = ref(null);
const tagMore = ref(false);
const checkTagListWidth = async () => {
await nextTick();
// 获取 tagList 宽度
const tagListWidth = tagListRef.value.offsetWidth;
// 获取 tagList 子元素实际宽度
const tagItemWidth = tagListRef.value.children[0].scrollWidth;
// 判断 tagList 宽度是否小于 tagItem 实际宽度
if (tagListWidth < tagItemWidth) {
// 设置 tagMore 为 true
tagMore.value = true;
// 计算向右偏移宽度
const rightOffset = tagListRef.value.children[0].offsetWidth - tagListRef.value.clientWidth;
tagListRef.value.children[0].style.right = rightOffset + 'px';
} else {
tagMore.value = false;
}
}
// Tag 标签上一页
const handleTagPrev = () => {
let leftOffset = tagListRef.value.clientWidth - tagListRef.value.children[0].offsetWidth;
if (leftOffset < 0) {
leftOffset = 0;
}
tagListRef.value.children[0].style.right = leftOffset + 'px';
}
// Tag 标签下一页
const handleTagNext = () => {
// 计算向右偏移宽度
const rightOffset = tagListRef.value.children[0].offsetWidth - tagListRef.value.clientWidth;
tagListRef.value.children[0].style.right = rightOffset + 'px';
}
// Tag 关闭事件
const handleTagClose = (view, index) => {
// 判断是否为当前选中
if (view.path === route.path) {
// 判断是否为第一个
if (index !== 0 ) {
router.push(store.visitedViews[index - 1].path);
} else {
router.push(store.visitedViews[index + 1].path)
}
}
store.delVisitedViews(view);
}
const handleDelTag = (v) => {
switch (v) {
case 'left':
store.delLeftVisitedViews(route);
break;
case 'right':
store.delRightVisitedViews(route);
break;
case 'all':
store.delAllVisitedViews(route);
break;
default:
console.log('参数错误')
}
}
// 监听 store 数据变化
watch(store.visitedViews, () => {
// 重新计算 tagList 宽度
checkTagListWidth();
})
// 监听路由变化
watch(() => route.path, () => {
store.addVisitedViews(route)
}, { immediate: true })
onMounted(() => {
checkTagListWidth()
})
</script>

<template>
<div class="tag-view-main">
<div
class="tag-card"
>
<div class="tag-prev" v-if="tagMore" @click="handleTagPrev">
<icon-left :size="18"/>
</div>

<div class="tag-list" ref="tagListRef">
<transition-group name="tag-fade">
<a-tag
v-for="(view, index) in store.visitedViews"
:key="view.name"
:checked="view.meta.title === route.meta.title"
:color="view.meta.title === route.meta.title ? '#5d87ff' : ''"
@close="handleTagClose(view, index)"
:closable="store.visitedViews.length > 1"
checkable
:style="{ marginRight: '5px' }"
>
<router-link :to="view.path" class="tag-link">
{{ view.title }}
</router-link>
</a-tag>
</transition-group>
<!-- <a-tag
v-for="name, index in tagListCount"
:key="index"
@close="() => tagListCount -= 1"
closable>
标题{{ name }}
</a-tag> -->

</div>

<div class="tag-next" v-if="tagMore" @click="handleTagNext">
<icon-right :size="18"/>
</div>
</div>

<!-- <div class="tag-close">-->
<!-- <a-dropdown @select="handleDelTag">-->
<!-- <a-button>-->
<!-- <template #icon>-->
<!-- <icon-down />-->
<!-- </template>-->
<!-- </a-button>-->
<!-- <template #content>-->
<!-- <a-doption value="left">关闭左侧</a-doption>-->
<!-- <a-doption value="right">关闭右侧</a-doption>-->
<!-- <a-doption value="all">关闭全部</a-doption>-->
<!-- </template>-->
<!-- </a-dropdown>-->
<!-- </div>-->
</div>
</template>

<style lang="scss" scoped>
.tag-view-main {
padding: 0 15px;
display: flex;
justify-content: space-between;
max-width: 100%;
min-width: 100%;
background-color: var(--color-bg-2);
.tag-card {
display: flex;
align-items: center;
height: 36px;
overflow: hidden;
.tag-list {
position: relative;
overflow-x: hidden;
transition: .3s ease-in-out;
}
.tag-prev, .tag-next{
width: 48px;
cursor: pointer;
text-align: center;
}
}
}
.tag-link {
color: inherit;
}
.tag-link:hover {
color: inherit;
}
.tag-fade-enter-active,
.tag-fade-leave-active {
transition: opacity 0.3s ease-in-out;
}
.tag-fade-enter-from,
.tag-fade-leave-to {
opacity: 0;
}
</style>
2 changes: 2 additions & 0 deletions src/layout/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<a-layout>
<a-layout-header>
<Navbar :collapsed="collapsed" @on-collapse="onCollapse" />
<tag-view />
</a-layout-header>
<a-layout-content class="layout-content">
<AppMain />
Expand All @@ -18,6 +19,7 @@
import { ref } from 'vue';
import { AppMain, Navbar } from './components';
import Menu from './components/Menu/Menu.vue';
import TagView from './components/TagView/TagView.vue';
const collapsed = ref(false);
const onCollapse = () => {
Expand Down
83 changes: 83 additions & 0 deletions src/store/tagView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { sessionStorage } from '@/utils/storage';

export const useTagViewStore = defineStore('tagView', () => {
const visitedViews = ref(sessionStorage.getItem('tagList') || []);
const cachedViews = ref([]);

function addVisitedViews(view) {
console.log(view.meta.title)

// Akiraka 20240228 限制标签数量
if (visitedViews.value.length >= 20) {
visitedViews.value.splice(0,1)
}

if (visitedViews.value.some(v => v.path === view.path || v.meta.title === view.meta.title)) return;

visitedViews.value.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
)
sessionStorage.setItem('tagList', visitedViews.value);
}

function delVisitedViews(view) {
for (const [i, v] of visitedViews.value.entries()) {
if (v.path === view.path) {
visitedViews.value.splice(i, 1);
break;
}
}
sessionStorage.setItem('tagList', visitedViews.value);
}

function delAllVisitedViews(view) {
visitedViews.value = visitedViews.value.filter(v => v.path === view.path);
sessionStorage.setItem('tagList', visitedViews.value);
}

function delLeftVisitedViews(view) {
for (const [i, v] of visitedViews.value.entries()) {
if (v.path === view.path) {
visitedViews.value = visitedViews.value.slice(i);
break;
}
}
sessionStorage.setItem('tagList', visitedViews.value);
}
function delRightVisitedViews(view) {
for (const [i, v] of visitedViews.value.entries()) {
if (v.path === view.path) {
visitedViews.value = visitedViews.value.slice(0, i + 1);
break;
}
}
sessionStorage.setItem('tagList', visitedViews.value);
}

function addCachedViews(view) {
if (cachedViews.value.includes(view.name)) return;
if (!view.meta.noCache) cachedViews.value.push(view.name);
}

function delCachedViews(view) {
visitedViews.value = visitedViews.value.filter(v => v !== view.name);
}

function delCachedVisitedViews() {
cachedViews.value = [];
}

return {
visitedViews,
cachedViews,
addVisitedViews,
delVisitedViews,
delAllVisitedViews,
delLeftVisitedViews,
delRightVisitedViews
}
})
30 changes: 30 additions & 0 deletions src/utils/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,34 @@ export const storage = {
removeItem(key) {
window.localStorage.removeItem(key);
}
}

export const sessionStorage = {
getKeys() {
const keys = [];
for (let i = 0; i < window.sessionStorage.length; i++) {
keys.push(window.sessionStorage.key(i));
}

return keys;
},
setItem(key, val) {
if (typeof key !== 'string') {
key = key.toString();
}

if (key === undefined || key.trim().length === 0) throw new Error('key 参数不能为空或者undefined');

window.sessionStorage.setItem(key, JSON.stringify(val));
},
getItem(key) {
const val = window.sessionStorage.getItem(key);
return JSON.parse(val);
},
clearAllKeys() {
window.sessionStorage.clear();
},
removeItem(key) {
window.sessionStorage.removeItem(key);
}
}

0 comments on commit d352198

Please sign in to comment.