| | |
| | | <div v-if="device === 'mobile' && sidebar.opened" |
| | | class="drawer-bg" |
| | | @click="handleClickOutside" /> |
| | | <sidebar v-if="!sidebar.hide" |
| | | <div :class="{ 'fixed-header': fixedHeader }" |
| | | class="top-header"> |
| | | <navbar @setLayout="setLayout" /> |
| | | </div> |
| | | <sidebar v-if="!sidebar.hide && (!settingsStore.topNav || device === 'mobile')" |
| | | class="sidebar-container" /> |
| | | <div :class="{ hasTagsView: showTagsView, sidebarHide: sidebar.hide }" |
| | | class="main-container main-layout"> |
| | | <div :class="{ 'fixed-header': fixedHeader, 'with-tags': showTagsView }"> |
| | | <navbar @setLayout="setLayout" /> |
| | | <div v-if="showTagsView" |
| | | class="tags-view-wrapper" |
| | | :class="{ 'fixed-tags-view': fixedHeader }"> |
| | | <tags-view /> |
| | | </div> |
| | | <div class="breadcrumb-wrapper" |
| | | :class="{ 'fixed-breadcrumb': fixedHeader }"> |
| | | <hamburger v-if="!sidebar.hide && (!settingsStore.topNav || device === 'mobile')" |
| | | id="hamburger-container" |
| | | :is-active="sidebar.opened" |
| | | class="hamburger-container" |
| | | @toggleClick="toggleSideBar" /> |
| | | <breadcrumb id="breadcrumb-container" |
| | | class="breadcrumb-container" /> |
| | | </div> |
| | | <app-main /> |
| | | <settings ref="settingRef" /> |
| | |
| | | import { useRoute } from "vue-router"; |
| | | import Sidebar from "./components/Sidebar/index.vue"; |
| | | import { AppMain, Navbar, Settings, TagsView } from "./components"; |
| | | import Breadcrumb from "@/components/Breadcrumb"; |
| | | import Hamburger from "@/components/Hamburger"; |
| | | import AIChatSidebar from "@/components/AIChatSidebar/index.vue"; |
| | | import defaultSettings from "@/settings"; |
| | | |
| | |
| | | import useTagsViewStore from "@/store/modules/tagsView"; |
| | | |
| | | const settingsStore = useSettingsStore(); |
| | | const appStore = useAppStore(); |
| | | const tagsViewStore = useTagsViewStore(); |
| | | const userStore = useUserStore(); |
| | | const route = useRoute(); |
| | | const theme = computed(() => settingsStore.theme); |
| | | const sideTheme = computed(() => settingsStore.sideTheme); |
| | | const sidebar = computed(() => useAppStore().sidebar); |
| | | const device = computed(() => useAppStore().device); |
| | | const sidebar = computed(() => appStore.sidebar); |
| | | const device = computed(() => appStore.device); |
| | | const needTagsView = computed(() => settingsStore.tagsView); |
| | | const showTagsView = computed( |
| | | () => needTagsView.value && tagsViewStore.visitedViews.length > 1 |
| | | ); |
| | | const showTagsView = computed(() => needTagsView.value); |
| | | const fixedHeader = computed(() => settingsStore.fixedHeader); |
| | | const aiEnabled = computed(() => Number(userStore.aiEnabled) === 1); |
| | | const showGlobalAiChat = computed(() => { |
| | |
| | | () => device.value, |
| | | () => { |
| | | if (device.value === "mobile" && sidebar.value.opened) { |
| | | useAppStore().closeSideBar({ withoutAnimation: false }); |
| | | appStore.closeSideBar({ withoutAnimation: false }); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watchEffect(() => { |
| | | if (width.value - 1 < WIDTH) { |
| | | useAppStore().toggleDevice("mobile"); |
| | | useAppStore().closeSideBar({ withoutAnimation: true }); |
| | | appStore.toggleDevice("mobile"); |
| | | appStore.closeSideBar({ withoutAnimation: true }); |
| | | } else { |
| | | useAppStore().toggleDevice("desktop"); |
| | | appStore.toggleDevice("desktop"); |
| | | } |
| | | }); |
| | | |
| | | watchEffect(() => { |
| | | if (settingsStore.topNav && device.value !== "mobile") { |
| | | appStore.toggleSideBarHide(true); |
| | | appStore.closeSideBar({ withoutAnimation: true }); |
| | | } else { |
| | | appStore.toggleSideBarHide(false); |
| | | } |
| | | }); |
| | | |
| | | function handleClickOutside() { |
| | | useAppStore().closeSideBar({ withoutAnimation: false }); |
| | | appStore.closeSideBar({ withoutAnimation: false }); |
| | | } |
| | | |
| | | function toggleSideBar() { |
| | | appStore.toggleSideBar(); |
| | | } |
| | | |
| | | const settingRef = ref(null); |
| | |
| | | width: 100%; |
| | | top: 0; |
| | | height: 100%; |
| | | position: absolute; |
| | | z-index: 999; |
| | | position: fixed; |
| | | z-index: 1003; |
| | | } |
| | | |
| | | .top-header { |
| | | width: 100%; |
| | | z-index: 1002; |
| | | } |
| | | |
| | | .top-header.fixed-header { |
| | | position: sticky; |
| | | top: 0; |
| | | } |
| | | |
| | | .main-layout { |
| | |
| | | transition: margin-left 0.25s ease; |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 0; |
| | | } |
| | | |
| | | .fixed-header { |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: var(--layout-header-z); |
| | | .breadcrumb-wrapper { |
| | | width: 100%; |
| | | padding: 0; |
| | | background: #fff; |
| | | padding: 10px 24px; |
| | | background: transparent; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 2px; // 在 Navbar 和 TagsView 之间增加极小的空隙 |
| | | align-items: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .fixed-header.with-tags { |
| | | padding-bottom: 4px; // 底部留出一点距离,不要直接贴着主体内容 |
| | | .hamburger-container { |
| | | height: 32px; |
| | | width: 32px; |
| | | border-radius: var(--radius-sm); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: var(--text-secondary); |
| | | cursor: pointer; |
| | | transition: all 0.2s ease; |
| | | flex-shrink: 0; |
| | | |
| | | &:hover { |
| | | background: rgba(0, 0, 0, 0.05); |
| | | color: var(--el-color-primary); |
| | | } |
| | | } |
| | | |
| | | .hideSidebar .fixed-header { |
| | | .breadcrumb-container { |
| | | min-width: 0; |
| | | |
| | | :deep(.el-breadcrumb__inner) { |
| | | color: var(--text-secondary) !important; |
| | | opacity: 0.85; |
| | | |
| | | &:hover { |
| | | color: var(--el-color-primary) !important; |
| | | opacity: 1; |
| | | } |
| | | |
| | | a { |
| | | color: inherit !important; |
| | | font-weight: 500 !important; |
| | | } |
| | | } |
| | | |
| | | :deep(.no-redirect) { |
| | | color: var(--text-primary) !important; |
| | | font-weight: 600 !important; |
| | | opacity: 1; |
| | | } |
| | | |
| | | :deep(.el-breadcrumb__separator) { |
| | | color: var(--text-tertiary); |
| | | opacity: 0.5; |
| | | } |
| | | } |
| | | |
| | | .fixed-breadcrumb { |
| | | position: sticky; |
| | | top: var(--topbar-height); |
| | | z-index: calc(var(--layout-header-z) - 1); |
| | | background: var(--app-bg); |
| | | } |
| | | |
| | | .hasTagsView { |
| | | .fixed-breadcrumb { |
| | | top: calc(var(--topbar-height) + var(--tagsbar-height)); |
| | | } |
| | | } |
| | | |
| | | .tags-view-wrapper.fixed-tags-view { |
| | | position: sticky; |
| | | top: var(--topbar-height); |
| | | z-index: calc(var(--layout-header-z) - 1); |
| | | background: var(--app-bg); |
| | | } |
| | | |
| | | .hideSidebar .breadcrumb-wrapper { |
| | | width: 100%; |
| | | } |
| | | |
| | |
| | | margin-left: var(--sidebar-collapsed-width); |
| | | } |
| | | |
| | | .mobile .fixed-header { |
| | | .mobile .top-header.fixed-header { |
| | | width: 100%; |
| | | padding: 8px 10px 0; |
| | | } |
| | | |
| | | .mobile .main-layout, |