LeftMenuLayout.vue 11 KB

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