LeftMenuLayout.vue 11 KB

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