index.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <t-layout class="app-layout">
  3. <t-aside class="app-menu" width="82px">
  4. <div class="app-menu-list">
  5. <div class="app-menu-logo">
  6. <img src="@/assets/imgs/menu-logo.png" alt="logo" />
  7. </div>
  8. <div
  9. v-for="item in userStore.headerMenus"
  10. :key="item.name"
  11. :class="[
  12. 'app-menu-item',
  13. { 'is-active': userStore.curPageModule === item.name },
  14. ]"
  15. @click="moduleChange(item.name)"
  16. >
  17. <svg-icon
  18. v-if="item.meta.icon"
  19. prefix="icon-icon-fill"
  20. :name="item.meta.icon"
  21. ></svg-icon>
  22. <p>{{ item.meta.title }}</p>
  23. </div>
  24. </div>
  25. <div class="app-menu-footer"></div>
  26. </t-aside>
  27. <t-aside v-if="appStore.showSubmenu" class="app-submenu" width="232px">
  28. <left-menu class="app-submenu-body"></left-menu>
  29. <div class="app-submenu-footer">
  30. <t-dropdown
  31. :minColumnWidth="203"
  32. trigger="hover"
  33. :popupProps="{
  34. overlayInnerClassName: 'app-submenu-dropdown',
  35. }"
  36. @click="clickHandler"
  37. >
  38. <div class="app-submenu-user">
  39. <div class="app-submenu-user-content">
  40. <svg-icon name="user-circle"></svg-icon>
  41. <span class="real-name">{{ userStore.user?.realName }}</span>
  42. <span v-if="userStore.user?.mobileNumber" class="real-name">{{
  43. userStore.user?.mobileNumber
  44. }}</span>
  45. </div>
  46. <svg-icon name="more"></svg-icon>
  47. </div>
  48. <t-dropdown-menu>
  49. <t-dropdown-item :value="1">
  50. <template #prefixIcon>
  51. <svg-icon name="modify-pwd"></svg-icon>
  52. </template>
  53. 修改密码
  54. </t-dropdown-item>
  55. <t-dropdown-item :value="2">
  56. <template #prefixIcon>
  57. <svg-icon name="logout"></svg-icon>
  58. </template>
  59. 退出登录
  60. </t-dropdown-item>
  61. </t-dropdown-menu>
  62. </t-dropdown>
  63. </div>
  64. </t-aside>
  65. <t-content class="layout-content">
  66. <router-view v-slot="{ Component }">
  67. <transition name="fade-slide" mode="out-in" appear>
  68. <component :is="Component" />
  69. </transition>
  70. </router-view>
  71. </t-content>
  72. </t-layout>
  73. </template>
  74. <script setup name="Layout" lang="jsx">
  75. import { ref, onMounted, watch } from 'vue';
  76. import { useUserStore, useWorkStore, useAppStore } from '@/store';
  77. import { useRouter, useRoute } from 'vue-router';
  78. import LeftMenu from './left-menu.vue';
  79. import { moduleMap } from '@/router/asyncRoutes';
  80. import { MessagePlugin } from 'tdesign-vue-next';
  81. import useFullscreen from '@/hooks/useFullscreen';
  82. const { registFullscreenChange } = useFullscreen();
  83. const router = useRouter();
  84. const route = useRoute();
  85. const userStore = useUserStore();
  86. const workStore = useWorkStore();
  87. const appStore = useAppStore();
  88. const moduleChange = (name) => {
  89. userStore.setCurPageModule(name);
  90. const menu = userStore.moduleMenus.find((item) => !item.disabled);
  91. if (!menu) {
  92. MessagePlugin.error('权限配置错误!');
  93. return;
  94. }
  95. router.push({ name: menu.name });
  96. };
  97. const setModuleByPath = () => {
  98. let firstPath = route.path.split('/')[1];
  99. let curModuleName = moduleMap[firstPath];
  100. userStore.setCurPageModule(curModuleName);
  101. };
  102. onMounted(() => {
  103. setModuleByPath();
  104. registFullscreenChange();
  105. });
  106. const clickHandler = (data) => {
  107. if (data.value === 1) {
  108. router.push({ name: 'PasswordModify' });
  109. } else if (data.value === 2) {
  110. userStore.logout();
  111. }
  112. };
  113. watch(
  114. () => route.name,
  115. () => {
  116. if (userStore.curPageModule !== 'MyWorkbenches') return;
  117. workStore.updateWorkCounts();
  118. },
  119. { immediate: true }
  120. );
  121. </script>
  122. <style lang="less" scoped>
  123. .app-layout {
  124. background-color: #f2f3f5;
  125. height: 100%;
  126. flex-direction: row;
  127. justify-content: space-between;
  128. align-items: stretch;
  129. .layout-content {
  130. flex-grow: 2;
  131. overflow: auto;
  132. border-left: 1px solid #e5e5e5;
  133. }
  134. .app-menu {
  135. background-color: #f2f3f5;
  136. height: 100%;
  137. overflow-x: hidden;
  138. overflow-y: auto;
  139. flex-grow: 0;
  140. flex-shrink: 0;
  141. &-list {
  142. padding: 16px 8px 40px;
  143. }
  144. &-logo {
  145. height: 52px;
  146. padding: 12px 8px;
  147. }
  148. &-item {
  149. margin: 4px 0;
  150. padding: 8px 0;
  151. text-align: center;
  152. border-radius: 4px;
  153. color: #4e5969;
  154. cursor: pointer;
  155. &:hover {
  156. background-color: #e5e6eb;
  157. }
  158. &.is-active {
  159. background-color: #e5e6eb;
  160. color: var(--td-brand-color);
  161. :deep(.svg-icon use) {
  162. fill: var(--td-brand-color);
  163. }
  164. }
  165. > i {
  166. display: inline-block;
  167. vertical-align: middle;
  168. width: 20px;
  169. height: 20px;
  170. }
  171. > p {
  172. margin: 0;
  173. line-height: 18px;
  174. font-size: 12px;
  175. }
  176. .svg-icon {
  177. display: block;
  178. margin: 0 auto 3px;
  179. width: 18px;
  180. height: 18px;
  181. }
  182. }
  183. &-footer {
  184. position: absolute;
  185. height: 24px;
  186. border-radius: 3px;
  187. padding: 8px;
  188. background-color: #f2f3f5;
  189. z-index: 9;
  190. }
  191. }
  192. .app-submenu {
  193. background-color: #fff;
  194. position: relative;
  195. color: #262626;
  196. height: 100%;
  197. overflow-x: hidden;
  198. overflow-y: auto;
  199. flex-grow: 0;
  200. flex-shrink: 0;
  201. &-body {
  202. padding-bottom: 68px;
  203. height: 100%;
  204. }
  205. &-footer {
  206. position: absolute;
  207. width: 100%;
  208. bottom: 0;
  209. left: 0;
  210. padding: 16px 8px;
  211. background-color: #fff;
  212. z-index: 9;
  213. }
  214. &-user {
  215. padding: 8px 16px;
  216. display: flex;
  217. align-items: center;
  218. justify-content: space-between;
  219. border-radius: 4px;
  220. cursor: pointer;
  221. &:hover {
  222. background-color: #f2f3f5;
  223. }
  224. &-content {
  225. line-height: 20px;
  226. overflow: hidden;
  227. white-space: nowrap;
  228. text-overflow: ellipsis;
  229. flex-grow: 2;
  230. span {
  231. margin-left: 8px;
  232. }
  233. }
  234. }
  235. :deep(.t-menu__content) {
  236. display: flex;
  237. justify-content: space-between;
  238. align-items: center;
  239. flex-grow: 2;
  240. }
  241. :deep(.t-menu__count) {
  242. color: #fff;
  243. padding: 0 3px;
  244. background-color: var(--td-error-color);
  245. line-height: 20px;
  246. height: 20px;
  247. border-radius: 3px;
  248. min-width: 20px;
  249. text-align: center;
  250. font-size: 12px;
  251. }
  252. :deep(.t-menu__item .svg-icon) {
  253. margin-right: 8px;
  254. }
  255. :deep(.t-menu__item:not(.t-is-active)) {
  256. color: #262626;
  257. }
  258. :deep(.t-menu__item:not(.t-is-disabled):hover) {
  259. background-color: #e8f3ff;
  260. }
  261. :deep(.t-menu__item.t-is-active) {
  262. color: var(--td-brand-color);
  263. background-color: #e8f3ff;
  264. }
  265. :deep(.t-menu__item.t-is-active .svg-icon use) {
  266. fill: var(--td-brand-color);
  267. }
  268. :deep(.t-menu__item.t-is-disabled) {
  269. color: rgb(140, 140, 140);
  270. }
  271. }
  272. }
  273. </style>
  274. <style lang="less">
  275. .app-submenu-dropdown {
  276. .t-dropdown__item {
  277. padding-top: 8px;
  278. padding-bottom: 8px;
  279. &:hover {
  280. color: var(--td-brand-color);
  281. background-color: #e8f3ff;
  282. .svg-icon use {
  283. fill: var(--td-brand-color);
  284. }
  285. }
  286. }
  287. }
  288. </style>