index.vue 6.5 KB

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