feat: 添加谷歌风格的页签 (#1160)

This commit is contained in:
way 2024-06-04 13:44:47 +08:00 committed by GitHub
parent b402a8924f
commit 7a6ee58e6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 221 additions and 25 deletions

View File

@ -53,6 +53,8 @@ panel:
pureTagsStyleSmartTip: Smart tags add fun and brilliance pureTagsStyleSmartTip: Smart tags add fun and brilliance
pureTagsStyleCard: Card pureTagsStyleCard: Card
pureTagsStyleCardTip: Card tags for efficient browsing pureTagsStyleCardTip: Card tags for efficient browsing
pureTagsStyleChrome: Chrome
pureTagsStyleChromeTip: Chrome style is classic and elegant
pureInterfaceDisplay: Interface Display pureInterfaceDisplay: Interface Display
pureGreyModel: Grey Model pureGreyModel: Grey Model
pureWeakModel: Weak Model pureWeakModel: Weak Model

View File

@ -53,6 +53,8 @@ panel:
pureTagsStyleSmartTip: 灵动标签,添趣生辉 pureTagsStyleSmartTip: 灵动标签,添趣生辉
pureTagsStyleCard: 卡片 pureTagsStyleCard: 卡片
pureTagsStyleCardTip: 卡片标签,高效浏览 pureTagsStyleCardTip: 卡片标签,高效浏览
pureTagsStyleChrome: 谷歌
pureTagsStyleChromeTip: 谷歌风格,经典美观
pureInterfaceDisplay: 界面显示 pureInterfaceDisplay: 界面显示
pureGreyModel: 灰色模式 pureGreyModel: 灰色模式
pureWeakModel: 色弱模式 pureWeakModel: 色弱模式

View File

@ -2,6 +2,7 @@
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import LayFrame from "../lay-frame/index.vue"; import LayFrame from "../lay-frame/index.vue";
import LayFooter from "../lay-footer/index.vue"; import LayFooter from "../lay-footer/index.vue";
import { useTags } from "@/layout/hooks/useTag";
import { useGlobal, isNumber } from "@pureadmin/utils"; import { useGlobal, isNumber } from "@pureadmin/utils";
import BackTopIcon from "@/assets/svg/back_top.svg?component"; import BackTopIcon from "@/assets/svg/back_top.svg?component";
import { h, computed, Transition, defineComponent } from "vue"; import { h, computed, Transition, defineComponent } from "vue";
@ -12,6 +13,7 @@ const props = defineProps({
}); });
const { t } = useI18n(); const { t } = useI18n();
const { showModel } = useTags();
const { $storage, $config } = useGlobal<GlobalPropertiesApi>(); const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
const isKeepAlive = computed(() => { const isKeepAlive = computed(() => {
@ -51,9 +53,17 @@ const getMainWidth = computed(() => {
const getSectionStyle = computed(() => { const getSectionStyle = computed(() => {
return [ return [
hideTabs.value && layout ? "padding-top: 48px;" : "", hideTabs.value && layout ? "padding-top: 48px;" : "",
!hideTabs.value && layout ? "padding-top: 81px;" : "", !hideTabs.value && layout
? showModel.value == "chrome"
? "padding-top: 85px;"
: "padding-top: 81px;"
: "",
hideTabs.value && !layout.value ? "padding-top: 48px;" : "", hideTabs.value && !layout.value ? "padding-top: 48px;" : "",
!hideTabs.value && !layout.value ? "padding-top: 81px;" : "", !hideTabs.value && !layout.value
? showModel.value == "chrome"
? "padding-top: 85px;"
: "padding-top: 81px;"
: "",
props.fixedHeader props.fixedHeader
? "" ? ""
: `padding-top: 0;${ : `padding-top: 0;${

View File

@ -232,6 +232,11 @@ const markOptions = computed<Array<OptionsType>>(() => {
label: t("panel.pureTagsStyleCard"), label: t("panel.pureTagsStyleCard"),
tip: t("panel.pureTagsStyleCardTip"), tip: t("panel.pureTagsStyleCardTip"),
value: "card" value: "card"
},
{
label: t("panel.pureTagsStyleChrome"),
tip: t("panel.pureTagsStyleChromeTip"),
value: "chrome"
} }
]; ];
}); });
@ -442,7 +447,7 @@ onUnmounted(() => removeMatchMedia);
<Segmented <Segmented
resize resize
class="select-none" class="select-none"
:modelValue="markValue === 'smart' ? 0 : 1" :modelValue="markValue === 'smart' ? 0 : markValue === 'card' ? 1 : 2"
:options="markOptions" :options="markOptions"
@change="onChange" @change="onChange"
/> />

View File

@ -0,0 +1,33 @@
<template>
<svg class="w-full h-full">
<defs>
<symbol id="geometry-left" viewBox="0 0 214 36">
<path d="M17 0h197v36H0v-2c4.5 0 9-3.5 9-8V8c0-4.5 3.5-8 8-8z" />
</symbol>
<symbol id="geometry-right" viewBox="0 0 214 36">
<use xlink:href="#geometry-left" />
</symbol>
<clipPath>
<rect width="100%" height="100%" x="0" />
</clipPath>
</defs>
<svg width="51%" height="100%">
<use
xlink:href="#geometry-left"
width="214"
height="36"
fill="currentColor"
/>
</svg>
<g transform="scale(-1, 1)">
<svg width="51%" height="100%" x="-100%" y="0">
<use
xlink:href="#geometry-right"
width="214"
height="36"
fill="currentColor"
/>
</svg>
</g>
</svg>
</template>

View File

@ -41,6 +41,13 @@
padding-right: 24px; padding-right: 24px;
} }
&.chrome-item {
padding-right: 0;
padding-left: 0;
margin-right: -18px;
box-shadow: none;
}
.el-icon-close { .el-icon-close {
position: absolute; position: absolute;
top: 50%; top: 50%;
@ -76,6 +83,14 @@
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
&.chrome-scroll-container {
padding-top: 4px;
.fixed-tag {
padding: 0 !important;
}
}
.tab { .tab {
position: relative; position: relative;
float: left; float: left;
@ -89,6 +104,12 @@
&:nth-child(1) { &:nth-child(1) {
padding: 0 12px; padding: 0 12px;
} }
&.chrome-item {
&:nth-child(1) {
padding: 0;
}
}
} }
.fixed-tag { .fixed-tag {
@ -173,9 +194,29 @@
color: #fff; color: #fff;
box-shadow: 0 0 0.7px #888; box-shadow: 0 0 0.7px #888;
.chrome-tab {
z-index: 10;
}
.chrome-tab__bg {
color: var(--el-color-primary-light-9) !important;
}
.tag-title { .tag-title {
color: var(--el-color-primary) !important; color: var(--el-color-primary) !important;
} }
.chrome-close-btn {
color: var(--el-color-primary);
&:hover {
background-color: var(--el-color-primary);
}
}
.chrome-tab-divider {
opacity: 0;
}
} }
.arrow-left, .arrow-left,
@ -262,3 +303,69 @@
background: var(--el-color-primary); background: var(--el-color-primary);
animation: schedule-out-width 200ms ease-in; animation: schedule-out-width 200ms ease-in;
} }
/* 谷歌风格的页签 */
.chrome-tab {
position: relative;
display: inline-flex;
gap: 16px;
align-items: center;
justify-content: center;
padding: 0 24px;
white-space: nowrap;
cursor: pointer;
.tag-title {
padding: 0;
}
.chrome-tab-divider {
position: absolute;
right: 7px;
width: 1px;
height: 14px;
background-color: #2b2d2f;
}
&:hover {
z-index: 10;
.chrome-tab__bg {
color: #dee1e6;
}
.tag-title {
color: #1f1f1f;
}
.chrome-tab-divider {
opacity: 0;
}
}
.chrome-tab__bg {
position: absolute;
top: 0;
left: 0;
z-index: -10;
width: 100%;
height: 100%;
color: transparent;
pointer-events: none;
}
.chrome-close-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
color: #666;
border-radius: 50%;
&:hover {
color: white;
background-color: #b1b3b8;
}
}
}

View File

@ -5,6 +5,7 @@ import { RouteConfigs } from "../../types";
import { useTags } from "../../hooks/useTag"; import { useTags } from "../../hooks/useTag";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
import { onClickOutside } from "@vueuse/core"; import { onClickOutside } from "@vueuse/core";
import TagChrome from "./components/TagChrome.vue";
import { handleAliveRoute, getTopMenu } from "@/router/utils"; import { handleAliveRoute, getTopMenu } from "@/router/utils";
import { useSettingStoreHook } from "@/store/modules/settings"; import { useSettingStoreHook } from "@/store/modules/settings";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
@ -565,6 +566,7 @@ onBeforeUnmount(() => {
<div <div
ref="scrollbarDom" ref="scrollbarDom"
class="scroll-container" class="scroll-container"
:class="showModel === 'chrome' && 'chrome-scroll-container'"
@wheel.prevent="handleWheel" @wheel.prevent="handleWheel"
> >
<div ref="tabDom" class="tab select-none" :style="getTabStyle"> <div ref="tabDom" class="tab select-none" :style="getTabStyle">
@ -575,6 +577,7 @@ onBeforeUnmount(() => {
:class="[ :class="[
'scroll-item is-closable', 'scroll-item is-closable',
linkIsActive(item), linkIsActive(item),
showModel === 'chrome' && 'chrome-item',
!isAllEmpty(item?.meta?.fixedTag) && 'fixed-tag' !isAllEmpty(item?.meta?.fixedTag) && 'fixed-tag'
]" ]"
@contextmenu.prevent="openMenu(item, $event)" @contextmenu.prevent="openMenu(item, $event)"
@ -582,28 +585,46 @@ onBeforeUnmount(() => {
@mouseleave.prevent="onMouseleave(index)" @mouseleave.prevent="onMouseleave(index)"
@click="tagOnClick(item)" @click="tagOnClick(item)"
> >
<span <template v-if="showModel !== 'chrome'">
class="tag-title dark:!text-text_color_primary dark:hover:!text-primary" <span
> class="tag-title dark:!text-text_color_primary dark:hover:!text-primary"
{{ transformI18n(item.meta.title) }} >
</span> {{ transformI18n(item.meta.title) }}
<span </span>
v-if=" <span
isAllEmpty(item?.meta?.fixedTag) v-if="
? iconIsActive(item, index) || isAllEmpty(item?.meta?.fixedTag)
(index === activeIndex && index !== 0) ? iconIsActive(item, index) ||
: false (index === activeIndex && index !== 0)
" : false
class="el-icon-close" "
@click.stop="deleteMenu(item)" class="el-icon-close"
> @click.stop="deleteMenu(item)"
<IconifyIconOffline :icon="Close" /> >
</span> <IconifyIconOffline :icon="Close" />
<span </span>
v-if="showModel !== 'card'" <span
:ref="'schedule' + index" v-if="showModel !== 'card'"
:class="[scheduleIsActive(item)]" :ref="'schedule' + index"
/> :class="[scheduleIsActive(item)]"
/>
</template>
<div v-else class="chrome-tab">
<div class="chrome-tab__bg">
<TagChrome />
</div>
<span class="tag-title">
{{ transformI18n(item.meta.title) }}
</span>
<span
v-if="isAllEmpty(item?.meta?.fixedTag) ? index !== 0 : false"
class="chrome-close-btn"
@click.stop="deleteMenu(item)"
>
<IconifyIconOffline :icon="Close" />
</span>
<span class="chrome-tab-divider" />
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -115,6 +115,22 @@ html.dark {
background-color: rgb(255 255 255 / 12%); background-color: rgb(255 255 255 / 12%);
} }
} }
.chrome-tab {
.tag-title {
color: #666;
}
&:hover {
.chrome-tab__bg {
color: #333;
}
.tag-title {
color: #adadad;
}
}
}
} }
} }