index.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <script setup lang="ts">
  2. import {
  3. h,
  4. reactive,
  5. computed,
  6. onMounted,
  7. defineComponent,
  8. getCurrentInstance
  9. } from "vue";
  10. import { setType } from "./types";
  11. import { routerArrays } from "./types";
  12. import { emitter } from "/@/utils/mitt";
  13. import { deviceDetection } from "@pureadmin/utils";
  14. import { useAppStoreHook } from "/@/store/modules/app";
  15. import { useMultiTagsStore } from "/@/store/modules/multiTags";
  16. import { useSettingStoreHook } from "/@/store/modules/settings";
  17. import backTop from "/@/assets/svg/back_top.svg?component";
  18. import navbar from "./components/navbar.vue";
  19. import appMain from "./components/appMain.vue";
  20. import setting from "./components/setting/index.vue";
  21. import Vertical from "./components/sidebar/vertical.vue";
  22. import Horizontal from "./components/sidebar/horizontal.vue";
  23. const isMobile = deviceDetection();
  24. const pureSetting = useSettingStoreHook();
  25. const instance = getCurrentInstance().appContext.app.config.globalProperties;
  26. // 清空缓存后从serverConfig.json读取默认配置并赋值到storage中
  27. const layout = computed(() => {
  28. // 路由
  29. if (
  30. useMultiTagsStore().multiTagsCache &&
  31. (!instance.$storage.tags || instance.$storage.tags.length === 0)
  32. ) {
  33. // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  34. instance.$storage.tags = routerArrays;
  35. }
  36. // 导航
  37. if (!instance.$storage.layout) {
  38. // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  39. instance.$storage.layout = {
  40. layout: instance.$config?.Layout ?? "vertical",
  41. theme: instance.$config?.Theme ?? "default",
  42. darkMode: instance.$config?.DarkMode ?? false,
  43. sidebarStatus: instance.$config?.SidebarStatus ?? true,
  44. epThemeColor: instance.$config?.EpThemeColor ?? "#409EFF"
  45. };
  46. }
  47. // 灰色模式、色弱模式、隐藏标签页
  48. if (!instance.$storage.configure) {
  49. // eslint-disable-next-line
  50. instance.$storage.configure = {
  51. grey: instance.$config?.Grey ?? false,
  52. weak: instance.$config?.Weak ?? false,
  53. hideTabs: instance.$config?.HideTabs ?? false,
  54. showLogo: instance.$config?.ShowLogo ?? true,
  55. showModel: instance.$config?.ShowModel ?? "smart",
  56. multiTagsCache: instance.$config?.MultiTagsCache ?? false
  57. };
  58. }
  59. return instance.$storage?.layout.layout;
  60. });
  61. const set: setType = reactive({
  62. sidebar: computed(() => {
  63. return useAppStoreHook().sidebar;
  64. }),
  65. device: computed(() => {
  66. return useAppStoreHook().device;
  67. }),
  68. fixedHeader: computed(() => {
  69. return pureSetting.fixedHeader;
  70. }),
  71. classes: computed(() => {
  72. return {
  73. hideSidebar: !set.sidebar.opened,
  74. openSidebar: set.sidebar.opened,
  75. withoutAnimation: set.sidebar.withoutAnimation,
  76. mobile: set.device === "mobile"
  77. };
  78. }),
  79. hideTabs: computed(() => {
  80. return instance.$storage?.configure.hideTabs;
  81. })
  82. });
  83. function setTheme(layoutModel: string) {
  84. window.document.body.setAttribute("layout", layoutModel);
  85. instance.$storage.layout = {
  86. layout: `${layoutModel}`,
  87. theme: instance.$storage.layout?.theme,
  88. darkMode: instance.$storage.layout?.darkMode,
  89. sidebarStatus: instance.$storage.layout?.sidebarStatus,
  90. epThemeColor: instance.$storage.layout?.epThemeColor
  91. };
  92. }
  93. function toggle(device: string, bool: boolean) {
  94. useAppStoreHook().toggleDevice(device);
  95. useAppStoreHook().toggleSideBar(bool, "resize");
  96. }
  97. // 判断是否可自动关闭菜单栏
  98. let isAutoCloseSidebar = true;
  99. // 监听容器
  100. emitter.on("resize", ({ detail }) => {
  101. if (isMobile) return;
  102. let { width } = detail;
  103. width <= 670 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
  104. /** width app-wrapper类容器宽度
  105. * 0 < width <= 760 隐藏侧边栏
  106. * 760 < width <= 990 折叠侧边栏
  107. * width > 990 展开侧边栏
  108. */
  109. if (width > 0 && width <= 760) {
  110. toggle("mobile", false);
  111. isAutoCloseSidebar = true;
  112. } else if (width > 760 && width <= 990) {
  113. if (isAutoCloseSidebar) {
  114. toggle("desktop", false);
  115. isAutoCloseSidebar = false;
  116. }
  117. } else if (width > 990) {
  118. if (!set.sidebar.isClickHamburger) {
  119. toggle("desktop", true);
  120. isAutoCloseSidebar = true;
  121. }
  122. }
  123. });
  124. onMounted(() => {
  125. if (isMobile) {
  126. toggle("mobile", false);
  127. }
  128. });
  129. function onFullScreen() {
  130. pureSetting.hiddenSideBar
  131. ? pureSetting.changeSetting({ key: "hiddenSideBar", value: false })
  132. : pureSetting.changeSetting({ key: "hiddenSideBar", value: true });
  133. }
  134. const layoutHeader = defineComponent({
  135. render() {
  136. return h(
  137. "div",
  138. {
  139. class: { "fixed-header": set.fixedHeader },
  140. style: [
  141. set.hideTabs && layout.value.includes("horizontal")
  142. ? "box-shadow: 0 1px 4px rgb(0 21 41 / 8%);"
  143. : ""
  144. ]
  145. },
  146. {
  147. default: () => [
  148. !pureSetting.hiddenSideBar &&
  149. (layout.value.includes("vertical") || layout.value.includes("mix"))
  150. ? h(navbar)
  151. : h("div"),
  152. !pureSetting.hiddenSideBar && layout.value.includes("horizontal")
  153. ? h(Horizontal)
  154. : h("div")
  155. // h(
  156. // tag,
  157. // {},
  158. // {
  159. // default: () => [
  160. // h(
  161. // "span",
  162. // { onClick: onFullScreen },
  163. // {
  164. // default: () => [
  165. // !pureSetting.hiddenSideBar ? h(fullScreen) : h(exitScreen)
  166. // ]
  167. // }
  168. // )
  169. // ]
  170. // }
  171. // )
  172. ]
  173. }
  174. );
  175. }
  176. });
  177. </script>
  178. <template>
  179. <div :class="['app-wrapper', set.classes]" v-resize>
  180. <div
  181. v-show="
  182. set.device === 'mobile' &&
  183. set.sidebar.opened &&
  184. layout.includes('vertical')
  185. "
  186. class="app-mask"
  187. @click="useAppStoreHook().toggleSideBar()"
  188. />
  189. <Vertical
  190. v-show="
  191. !pureSetting.hiddenSideBar &&
  192. (layout.includes('vertical') || layout.includes('mix'))
  193. "
  194. />
  195. <div
  196. :class="[
  197. 'main-container',
  198. pureSetting.hiddenSideBar ? 'main-hidden' : ''
  199. ]"
  200. >
  201. <div v-if="set.fixedHeader">
  202. <layout-header />
  203. <!-- 主体内容 -->
  204. <app-main :fixed-header="set.fixedHeader" />
  205. </div>
  206. <el-scrollbar v-else>
  207. <el-backtop
  208. title="回到顶部"
  209. target=".main-container .el-scrollbar__wrap"
  210. >
  211. <backTop />
  212. </el-backtop>
  213. <layout-header />
  214. <!-- 主体内容 -->
  215. <app-main :fixed-header="set.fixedHeader" />
  216. </el-scrollbar>
  217. </div>
  218. <!-- 系统设置 -->
  219. <setting />
  220. </div>
  221. </template>
  222. <style lang="scss" scoped>
  223. @mixin clearfix {
  224. &::after {
  225. content: "";
  226. display: table;
  227. clear: both;
  228. }
  229. }
  230. .app-wrapper {
  231. @include clearfix;
  232. position: relative;
  233. height: 100%;
  234. width: 100%;
  235. &.mobile.openSidebar {
  236. position: fixed;
  237. top: 0;
  238. }
  239. }
  240. .main-hidden {
  241. margin-left: 0 !important;
  242. }
  243. .app-mask {
  244. background: #000;
  245. opacity: 0.3;
  246. width: 100%;
  247. top: 0;
  248. height: 100%;
  249. position: absolute;
  250. z-index: 999;
  251. }
  252. .re-screen {
  253. margin-top: 12px;
  254. }
  255. </style>
  256. ./hooks/navbar.vue