LeftMenuLayout.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
  2. <template>
  3. <q-drawer
  4. v-bind="$attrs"
  5. v-model="leftDrawerOpen"
  6. show-if-above
  7. no-swipe-close
  8. no-swipe-open
  9. :width="250"
  10. :mini-width="64"
  11. :breakpoint="500"
  12. :mini="miniState"
  13. :behavior="'desktop'"
  14. class="detached-container"
  15. >
  16. <div class="column full-height q-pa-sm no-wrap">
  17. <div
  18. v-if="!$q.screen.lt.sm"
  19. class="toggle-button-wrapper absolute"
  20. style="top: 50%; right: -32px; z-index: 1"
  21. >
  22. <q-btn
  23. flat
  24. round
  25. size="sm"
  26. padding="8px 8px"
  27. @click="miniState = !miniState"
  28. >
  29. <q-icon
  30. :name="miniState ? 'mdi-chevron-right' : 'mdi-chevron-left'"
  31. />
  32. <q-tooltip
  33. anchor="center right"
  34. self="center left"
  35. :offset="[10, 10]"
  36. >{{ miniState ? "Expandir menu" : "Colapsar menu" }}</q-tooltip
  37. >
  38. </q-btn>
  39. </div>
  40. <q-list class="column q-mb-md no-wrap" style="border-radius: 6px">
  41. <q-item v-ripple clickable>
  42. <div class="flex">
  43. <q-item-section avatar>
  44. <template #default>
  45. <img
  46. :src="someAvatar()"
  47. alt="avatar"
  48. style="width: 20px; height: 20px; border-radius: 50%"
  49. />
  50. </template>
  51. </q-item-section>
  52. <q-item-section>{{ user_store.user.name }}</q-item-section>
  53. </div>
  54. <q-tooltip
  55. v-if="miniState && !$q.screen.lt.sm"
  56. anchor="center right"
  57. self="center left"
  58. :offset="[10, 10]"
  59. >{{ user_store.user.name }}</q-tooltip
  60. >
  61. </q-item>
  62. </q-list>
  63. <q-list class="column no-wrap">
  64. <template v-for="menu in menus" :key="menu.name">
  65. <!-- Single Menu -->
  66. <q-item
  67. v-if="menu.type === 'single'"
  68. v-ripple
  69. clickable
  70. exact-active-class="menu-selected"
  71. exact
  72. active-class="menu-selected"
  73. :to="{ name: menu.name }"
  74. class="q-my-xs"
  75. >
  76. <q-item-section avatar>
  77. <q-icon :name="menu.icon" style="font-size: 18px" />
  78. </q-item-section>
  79. <q-item-section>{{ $t(menu.title) }}</q-item-section>
  80. <q-tooltip
  81. v-if="miniState && !$q.screen.lt.sm"
  82. anchor="center right"
  83. self="center left"
  84. :offset="[10, 10]"
  85. >{{ $t(menu.title) }}</q-tooltip
  86. >
  87. </q-item>
  88. <!-- Expansive Menu with children -->
  89. <div v-else>
  90. <template v-if="!miniState">
  91. <q-tooltip
  92. v-if="miniState && !$q.screen.lt.sm"
  93. anchor="center right"
  94. self="center left"
  95. :offset="[10, 10]"
  96. >{{ $t(menu.title) }}</q-tooltip
  97. >
  98. <q-expansion-item
  99. v-model="isExpasionItemExpanded"
  100. header-class=" menu-item--spaced"
  101. :class="{
  102. 'menu-selected':
  103. childrenAreActive(menu.children) && !isExpasionItemExpanded,
  104. }"
  105. class="menu-item--spaced"
  106. >
  107. <template #header>
  108. <q-item-section avatar>
  109. <q-icon :name="menu.icon" style="font-size: 18px" />
  110. </q-item-section>
  111. <q-item-section>{{ $t(menu.title) }}</q-item-section>
  112. </template>
  113. <div v-for="child in menu.childrens" :key="child.name">
  114. <q-item
  115. v-ripple
  116. clickable
  117. :to="{ name: child.name }"
  118. exact
  119. exact-active-class="menu-selected"
  120. class="menu-item--spaced"
  121. >
  122. <q-item-section avatar>
  123. <q-icon :name="child.icon" style="font-size: 18px" />
  124. </q-item-section>
  125. <q-item-section>{{ $t(child.title) }}</q-item-section>
  126. <q-tooltip
  127. v-if="miniState && !$q.screen.lt.sm"
  128. anchor="center right"
  129. self="center left"
  130. :offset="[10, 10]"
  131. >{{ $t(child.title) }}</q-tooltip
  132. >
  133. </q-item>
  134. </div>
  135. </q-expansion-item>
  136. </template>
  137. <template v-else>
  138. <q-item
  139. v-ripple
  140. clickable
  141. exact
  142. exact-active-class="menu-selected"
  143. class="menu-item--spaced"
  144. >
  145. <q-item-section avatar>
  146. <q-icon :name="menu.icon" style="font-size: 18px" />
  147. </q-item-section>
  148. <q-item-section>{{ $t(menu.title) }}</q-item-section>
  149. <q-tooltip
  150. v-if="miniState && !$q.screen.lt.sm"
  151. anchor="center right"
  152. self="center left"
  153. :offset="[10, 10]"
  154. >{{ $t(menu.title) }}</q-tooltip
  155. >
  156. <q-menu anchor="top right" self="top left">
  157. <q-list style="min-width: 100px">
  158. <q-item
  159. v-for="child in menu.childrens"
  160. :key="child.name"
  161. v-ripple
  162. v-close-popup
  163. clickable
  164. :to="{ name: child.name }"
  165. exact
  166. exact-active-class="menu-selected"
  167. class="menu-item--spaced"
  168. >
  169. <q-item-section avatar>
  170. <q-icon :name="child.icon" style="font-size: 18px" />
  171. </q-item-section>
  172. <q-item-section>{{ $t(child.title) }}</q-item-section>
  173. </q-item>
  174. </q-list>
  175. </q-menu>
  176. </q-item>
  177. </template>
  178. </div>
  179. </template>
  180. </q-list>
  181. <q-list class="column no-wrap overflow-hidden q-mt-auto">
  182. <q-item v-ripple clickable @click="logoutFn">
  183. <div class="flex">
  184. <q-item-section avatar>
  185. <q-icon name="logout" color="negative" style="font-size: 18px" />
  186. </q-item-section>
  187. <q-item-section>Sair</q-item-section>
  188. </div>
  189. <q-tooltip
  190. v-if="miniState && !$q.screen.lt.sm"
  191. anchor="center right"
  192. self="center left"
  193. :offset="[10, 10]"
  194. >Sair</q-tooltip
  195. >
  196. </q-item>
  197. </q-list>
  198. </div>
  199. </q-drawer>
  200. </template>
  201. <script setup>
  202. import { ref, onMounted } from "vue";
  203. import { useAuth } from "src/composables/useAuth";
  204. import { permissionStore } from "src/stores/permission";
  205. import { useRouter, useRoute } from "vue-router";
  206. import { userStore } from "src/stores/user";
  207. const { logout } = useAuth();
  208. const router = useRouter();
  209. const route = useRoute();
  210. const user_store = userStore();
  211. const leftDrawerOpen = ref(true);
  212. const miniState = ref(true);
  213. const childrenAreActive = (children) => {
  214. if (!children) return false;
  215. return children.some((child) => {
  216. return route.path.includes(child.path);
  217. });
  218. };
  219. const someAvatar = () => {
  220. return "https://cdn.quasar.dev/img/avatar4.jpg";
  221. };
  222. const isExpasionItemExpanded = ref(false);
  223. const menus = ref([
  224. {
  225. type: "single",
  226. title: "navigation.dashboard",
  227. name: "HomePage",
  228. icon: "mdi-home-variant-outline",
  229. disable: false,
  230. permission: false,
  231. permissionScope: "dashboard",
  232. },
  233. {
  234. type: "expansive",
  235. title: "navigation.registration",
  236. icon: "mdi-cog-outline",
  237. disable: false,
  238. permission: false,
  239. permissionScope: "config",
  240. childrens: [
  241. {
  242. type: "single",
  243. title: "navigation.users",
  244. name: "UsersPage",
  245. icon: "mdi-account-multiple-outline",
  246. disable: false,
  247. permission: false,
  248. permissionScope: "config.user",
  249. },
  250. ],
  251. },
  252. ]);
  253. const getMenuAccess = () => {
  254. const { getAccess } = permissionStore();
  255. menus.value = menus.value
  256. .map((menu) => {
  257. if (menu.type === "expansive") {
  258. if (getAccess(menu.permissionScope, "menu")) {
  259. menu.permission = true;
  260. }
  261. menu.childrens = menu.childrens.filter((children) => {
  262. children.permission = getAccess(children.permissionScope, "menu");
  263. return children.permission;
  264. });
  265. return menu.childrens.length > 0 ? menu : null;
  266. } else {
  267. menu.permission = getAccess(menu.permissionScope, "menu");
  268. return menu;
  269. }
  270. })
  271. .filter((menu) => menu !== null);
  272. };
  273. const logoutFn = async () => {
  274. await logout();
  275. router.push({ name: "LoginPage" });
  276. };
  277. onMounted(() => {
  278. getMenuAccess();
  279. });
  280. </script>
  281. <style lang="scss" scoped>
  282. @import "/src/css/quasar.variables.scss";
  283. .text-subtitle3 {
  284. font-size: 1.1rem !important;
  285. font-weight: 400 !important;
  286. }
  287. .menu-selected {
  288. background-color: rgba($primary, 0.1);
  289. color: $primary;
  290. }
  291. .toggle-button-wrapper {
  292. transition: transform 0.3s ease;
  293. }
  294. .toggle-button-wrapper:hover {
  295. transform: scale(1.1);
  296. }
  297. .menu-item--spaced {
  298. margin-top: 5px;
  299. margin-bottom: 5px;
  300. }
  301. </style>