LeftMenuLayout.vue 9.0 KB

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