You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
5.2 KiB

  1. <script setup lang="ts">
  2. import "animate.css";
  3. // 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
  4. import "@/components/ReIcon/src/offlineIcon";
  5. import { setType } from "./types";
  6. import { emitter } from "@/utils/mitt";
  7. import { useLayout } from "./hooks/useLayout";
  8. import { useAppStoreHook } from "@/store/modules/app";
  9. import { useSettingStoreHook } from "@/store/modules/settings";
  10. import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
  11. import { h, reactive, computed, onMounted, defineComponent } from "vue";
  12. import navbar from "./components/navbar.vue";
  13. import tag from "./components/tag/index.vue";
  14. import appMain from "./components/appMain.vue";
  15. import setting from "./components/setting/index.vue";
  16. import Vertical from "./components/sidebar/vertical.vue";
  17. import Horizontal from "./components/sidebar/horizontal.vue";
  18. import backTop from "@/assets/svg/back_top.svg?component";
  19. const { isDark } = useDark();
  20. const { layout } = useLayout();
  21. const isMobile = deviceDetection();
  22. const pureSetting = useSettingStoreHook();
  23. const { $storage } = useGlobal<GlobalPropertiesApi>();
  24. const set: setType = reactive({
  25. sidebar: computed(() => {
  26. return useAppStoreHook().sidebar;
  27. }),
  28. device: computed(() => {
  29. return useAppStoreHook().device;
  30. }),
  31. fixedHeader: computed(() => {
  32. return pureSetting.fixedHeader;
  33. }),
  34. classes: computed(() => {
  35. return {
  36. hideSidebar: !set.sidebar.opened,
  37. openSidebar: set.sidebar.opened,
  38. withoutAnimation: set.sidebar.withoutAnimation,
  39. mobile: set.device === "mobile"
  40. };
  41. }),
  42. hideTabs: computed(() => {
  43. return $storage?.configure.hideTabs;
  44. })
  45. });
  46. function setTheme(layoutModel: string) {
  47. window.document.body.setAttribute("layout", layoutModel);
  48. $storage.layout = {
  49. layout: `${layoutModel}`,
  50. theme: $storage.layout?.theme,
  51. darkMode: $storage.layout?.darkMode,
  52. sidebarStatus: $storage.layout?.sidebarStatus,
  53. epThemeColor: $storage.layout?.epThemeColor
  54. };
  55. }
  56. function toggle(device: string, bool: boolean) {
  57. useAppStoreHook().toggleDevice(device);
  58. useAppStoreHook().toggleSideBar(bool, "resize");
  59. }
  60. // 判断是否可自动关闭菜单栏
  61. let isAutoCloseSidebar = true;
  62. // 监听容器
  63. emitter.on("resize", ({ detail }) => {
  64. if (isMobile) return;
  65. const { width } = detail;
  66. width <= 760 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
  67. /** width app-wrapper
  68. * 0 < width <= 760 隐藏侧边栏
  69. * 760 < width <= 990 折叠侧边栏
  70. * width > 990 展开侧边栏
  71. */
  72. if (width > 0 && width <= 760) {
  73. toggle("mobile", false);
  74. isAutoCloseSidebar = true;
  75. } else if (width > 760 && width <= 990) {
  76. if (isAutoCloseSidebar) {
  77. toggle("desktop", false);
  78. isAutoCloseSidebar = false;
  79. }
  80. } else if (width > 990) {
  81. if (!set.sidebar.isClickCollapse) {
  82. toggle("desktop", true);
  83. isAutoCloseSidebar = true;
  84. }
  85. }
  86. });
  87. onMounted(() => {
  88. if (isMobile) {
  89. toggle("mobile", false);
  90. }
  91. });
  92. const layoutHeader = defineComponent({
  93. render() {
  94. return h(
  95. "div",
  96. {
  97. class: { "fixed-header": set.fixedHeader },
  98. style: [
  99. set.hideTabs && layout.value.includes("horizontal")
  100. ? isDark.value
  101. ? "box-shadow: 0 1px 4px #0d0d0d"
  102. : "box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08)"
  103. : ""
  104. ]
  105. },
  106. {
  107. default: () => [
  108. !pureSetting.hiddenSideBar &&
  109. (layout.value.includes("vertical") || layout.value.includes("mix"))
  110. ? h(navbar)
  111. : null,
  112. !pureSetting.hiddenSideBar && layout.value.includes("horizontal")
  113. ? h(Horizontal)
  114. : null,
  115. h(tag)
  116. ]
  117. }
  118. );
  119. }
  120. });
  121. </script>
  122. <template>
  123. <div :class="['app-wrapper', set.classes]" v-resize>
  124. <div
  125. v-show="
  126. set.device === 'mobile' &&
  127. set.sidebar.opened &&
  128. layout.includes('vertical')
  129. "
  130. class="app-mask"
  131. @click="useAppStoreHook().toggleSideBar()"
  132. />
  133. <Vertical
  134. v-show="
  135. !pureSetting.hiddenSideBar &&
  136. (layout.includes('vertical') || layout.includes('mix'))
  137. "
  138. />
  139. <div
  140. :class="[
  141. 'main-container',
  142. pureSetting.hiddenSideBar ? 'main-hidden' : ''
  143. ]"
  144. >
  145. <div v-if="set.fixedHeader">
  146. <layout-header />
  147. <!-- 主体内容 -->
  148. <app-main :fixed-header="set.fixedHeader" />
  149. </div>
  150. <el-scrollbar v-else>
  151. <el-backtop
  152. title="回到顶部"
  153. target=".main-container .el-scrollbar__wrap"
  154. >
  155. <backTop />
  156. </el-backtop>
  157. <layout-header />
  158. <!-- 主体内容 -->
  159. <app-main :fixed-header="set.fixedHeader" />
  160. </el-scrollbar>
  161. </div>
  162. <!-- 系统设置 -->
  163. <setting />
  164. </div>
  165. </template>
  166. <style lang="scss" scoped>
  167. .app-wrapper {
  168. position: relative;
  169. width: 100%;
  170. height: 100%;
  171. &::after {
  172. display: table;
  173. clear: both;
  174. content: "";
  175. }
  176. &.mobile.openSidebar {
  177. position: fixed;
  178. top: 0;
  179. }
  180. }
  181. .app-mask {
  182. position: absolute;
  183. top: 0;
  184. z-index: 999;
  185. width: 100%;
  186. height: 100%;
  187. background: #000;
  188. opacity: 0.3;
  189. }
  190. .re-screen {
  191. margin-top: 12px;
  192. }
  193. </style>