zhangwencui
2026-04-28 96aaeb495edfbc4a41dce4b7f7a416a8204a5455
src/layout/index.vue
@@ -1,131 +1,133 @@
<template>
  <div :class="classObj"
       class="app-wrapper"
       :style="{ '--current-color': theme }">
    <div v-if="device === 'mobile' && sidebar.opened"
         class="drawer-bg"
         @click="handleClickOutside" />
    <sidebar v-if="!sidebar.hide"
             class="sidebar-container" />
    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
         class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <navbar @setLayout="setLayout" />
        <tags-view v-if="needTagsView" />
      </div>
      <app-main />
      <settings ref="settingRef" />
    </div>
  </div>
</template>
<script setup>
  import { useWindowSize } from "@vueuse/core";
  import Sidebar from "./components/Sidebar/index.vue";
  import { AppMain, Navbar, Settings, TagsView } from "./components";
  import defaultSettings from "@/settings";
  import useAppStore from "@/store/modules/app";
  import useSettingsStore from "@/store/modules/settings";
  const settingsStore = useSettingsStore();
  const theme = computed(() => settingsStore.theme);
  const sideTheme = computed(() => settingsStore.sideTheme);
  const sidebar = computed(() => useAppStore().sidebar);
  const device = computed(() => useAppStore().device);
  const needTagsView = computed(() => settingsStore.tagsView);
  const fixedHeader = computed(() => settingsStore.fixedHeader);
  const classObj = computed(() => ({
    hideSidebar: !sidebar.value.opened,
    openSidebar: sidebar.value.opened,
    withoutAnimation: sidebar.value.withoutAnimation,
    mobile: device.value === "mobile",
  }));
  const { width, height } = useWindowSize();
  const WIDTH = 992; // refer to Bootstrap's responsive design
  watch(
    () => device.value,
    () => {
      if (device.value === "mobile" && sidebar.value.opened) {
        useAppStore().closeSideBar({ withoutAnimation: false });
      }
    }
  );
  watchEffect(() => {
    if (width.value - 1 < WIDTH) {
      useAppStore().toggleDevice("mobile");
      useAppStore().closeSideBar({ withoutAnimation: true });
    } else {
      useAppStore().toggleDevice("desktop");
    }
  });
  function handleClickOutside() {
    useAppStore().closeSideBar({ withoutAnimation: false });
  }
  const settingRef = ref(null);
  function setLayout() {
    settingRef.value.openSetting();
  }
</script>
<style lang="scss" scoped>
  @import "@/assets/styles/mixin.scss";
  @import "@/assets/styles/variables.module.scss";
  .app-wrapper {
    @include clearfix;
    position: relative;
    height: 100%;
    width: 100%;
    background: radial-gradient(
        circle at top,
        rgba(223, 232, 226, 0.95),
        transparent 32%
      ),
      linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
    &.mobile.openSidebar {
      position: fixed;
      top: 0;
    }
  }
  .drawer-bg {
    background: #000;
    opacity: 0.3;
    width: 100%;
    top: 0;
    height: 100%;
    position: absolute;
    z-index: 999;
  }
  .fixed-header {
    position: fixed;
    top: 0px;
    padding-top: 12px;
    right: 16px;
    z-index: 9;
    width: calc(100% - #{$base-sidebar-width} - 32px);
    transition: width 0.28s, right 0.28s;
    padding-bottom: 8px;
    background-color: #f3f6f4;
  }
  .hideSidebar .fixed-header {
    width: calc(100% - 100px);
  }
  .sidebarHide .fixed-header {
    width: calc(100% - 32px);
  }
  .mobile .fixed-header {
    width: 100%;
  }
</style>
<template>
  <div :class="classObj"
       class="app-wrapper"
       :style="{ '--current-color': theme }">
    <div v-if="device === 'mobile' && sidebar.opened"
         class="drawer-bg"
         @click="handleClickOutside" />
    <sidebar v-if="!sidebar.hide"
             class="sidebar-container" />
    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
         class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <navbar @setLayout="setLayout" />
        <tags-view v-if="needTagsView" />
      </div>
      <app-main />
      <settings ref="settingRef" />
    </div>
    <AIChatSidebar />
  </div>
</template>
<script setup>
  import { useWindowSize } from "@vueuse/core";
  import Sidebar from "./components/Sidebar/index.vue";
  import { AppMain, Navbar, Settings, TagsView } from "./components";
  import AIChatSidebar from "@/components/AIChatSidebar/index.vue";
  import defaultSettings from "@/settings";
  import useAppStore from "@/store/modules/app";
  import useSettingsStore from "@/store/modules/settings";
  const settingsStore = useSettingsStore();
  const theme = computed(() => settingsStore.theme);
  const sideTheme = computed(() => settingsStore.sideTheme);
  const sidebar = computed(() => useAppStore().sidebar);
  const device = computed(() => useAppStore().device);
  const needTagsView = computed(() => settingsStore.tagsView);
  const fixedHeader = computed(() => settingsStore.fixedHeader);
  const classObj = computed(() => ({
    hideSidebar: !sidebar.value.opened,
    openSidebar: sidebar.value.opened,
    withoutAnimation: sidebar.value.withoutAnimation,
    mobile: device.value === "mobile",
  }));
  const { width, height } = useWindowSize();
  const WIDTH = 992; // refer to Bootstrap's responsive design
  watch(
    () => device.value,
    () => {
      if (device.value === "mobile" && sidebar.value.opened) {
        useAppStore().closeSideBar({ withoutAnimation: false });
      }
    }
  );
  watchEffect(() => {
    if (width.value - 1 < WIDTH) {
      useAppStore().toggleDevice("mobile");
      useAppStore().closeSideBar({ withoutAnimation: true });
    } else {
      useAppStore().toggleDevice("desktop");
    }
  });
  function handleClickOutside() {
    useAppStore().closeSideBar({ withoutAnimation: false });
  }
  const settingRef = ref(null);
  function setLayout() {
    settingRef.value.openSetting();
  }
</script>
<style lang="scss" scoped>
  @import "@/assets/styles/mixin.scss";
  @import "@/assets/styles/variables.module.scss";
  .app-wrapper {
    @include clearfix;
    position: relative;
    height: 100%;
    width: 100%;
    background: radial-gradient(
        circle at top,
        rgba(223, 232, 226, 0.95),
        transparent 32%
      ),
      linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
    &.mobile.openSidebar {
      position: fixed;
      top: 0;
    }
  }
  .drawer-bg {
    background: #000;
    opacity: 0.3;
    width: 100%;
    top: 0;
    height: 100%;
    position: absolute;
    z-index: 999;
  }
  .fixed-header {
    position: fixed;
    top: 0px;
    padding-top: 12px;
    right: 16px;
    z-index: 9;
    width: calc(100% - #{$base-sidebar-width} - 32px);
    transition: width 0.28s, right 0.28s;
    padding-bottom: 8px;
    background-color: #f3f6f4;
  }
  .hideSidebar .fixed-header {
    width: calc(100% - 100px);
  }
  .sidebarHide .fixed-header {
    width: calc(100% - 32px);
  }
  .mobile .fixed-header {
    width: 100%;
  }
</style>