index.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <template>
  2. <div class="head-com">
  3. <div class="logo">
  4. <img src="@/assets/images/logo.png" />
  5. </div>
  6. <div class="title">{{ titleCpt }}</div>
  7. <div class="menus">
  8. <template v-for="item in menusCpt.children.filter((v) => !v.meta.root)">
  9. <div
  10. class="menu-item __hover"
  11. :class="{
  12. active:
  13. $route.matched.some((v) => v.name === item.name) ||
  14. $route.meta.root === item.name,
  15. }"
  16. @click="$router.push(item.path)"
  17. >
  18. {{ item.meta.title }}
  19. </div>
  20. </template>
  21. </div>
  22. <div class="mr-4 ml-auto">
  23. <el-button type="primary" @click="state.drawer = true"
  24. >临时字典</el-button
  25. >
  26. </div>
  27. <el-dropdown :disabled="AppStore.tenants.length < 2">
  28. <div class="__hover flex items-center gap-2 text-[#303133]">
  29. {{ AppStore.tenantInfo?.name }}
  30. <SvgIcon
  31. name="czr_arrow"
  32. :rotate="90"
  33. size="10"
  34. color="#303133"
  35. v-if="AppStore.tenants.length > 1"
  36. />
  37. </div>
  38. <template #dropdown>
  39. <el-dropdown-menu>
  40. <template
  41. v-for="item in AppStore.tenants.filter(
  42. (v) => v.id !== AppStore.tenantInfo?.id,
  43. )"
  44. >
  45. <el-dropdown-item @click="onChangeTenant(item)">{{
  46. item.name
  47. }}</el-dropdown-item>
  48. </template>
  49. </el-dropdown-menu>
  50. </template>
  51. </el-dropdown>
  52. <el-dropdown>
  53. <div
  54. class="__hover mr-4 ml-4 flex items-center gap-2 text-sm text-[#303133]"
  55. >
  56. <img src="@/assets/images/avatar-default.png" class="size-8" />
  57. {{ AppStore.userInfo?.name }}
  58. <SvgIcon name="czr_arrow" :rotate="90" size="10" color="#303133" />
  59. </div>
  60. <template #dropdown>
  61. <el-dropdown-menu>
  62. <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
  63. </el-dropdown-menu>
  64. </template>
  65. </el-dropdown>
  66. <el-drawer v-model="state.drawer" title="临时字典管理">
  67. <div class="h-full w-full overflow-y-auto">
  68. <el-collapse v-model="state.activeNames">
  69. <template v-for="[key, value] in state.dictMap">
  70. <el-collapse-item :name="key">
  71. <template #title>
  72. <el-button
  73. type="primary"
  74. size="small"
  75. @click.stop="copyDict(value.name, key)"
  76. >复制</el-button
  77. >
  78. {{ `${value.name} ${key}` }}
  79. </template>
  80. <el-descriptions :column="1" border>
  81. <template v-for="item in value.children">
  82. <el-descriptions-item>
  83. <template #label>
  84. <div @click="copy(item.dictValue)">
  85. {{ item.dictValue }}
  86. </div>
  87. </template>
  88. {{ item.dictName }}
  89. </el-descriptions-item>
  90. </template>
  91. </el-descriptions>
  92. </el-collapse-item>
  93. </template>
  94. </el-collapse>
  95. </div>
  96. </el-drawer>
  97. </div>
  98. </template>
  99. <script setup lang="ts">
  100. import { computed, getCurrentInstance, onMounted, reactive } from 'vue'
  101. import { useAppStore, useDialogStore, useDictionaryStore } from '@/stores'
  102. import { useRoute, useRouter } from 'vue-router'
  103. import { sysDict } from '@/api/modules/global/dictionary'
  104. import { copy } from '@/utils/czr-util'
  105. import { ElMessage } from 'element-plus'
  106. import { loginChangeTenant, loginLogout } from '@/api/modules/global/login'
  107. const AppStore = useAppStore()
  108. const DictionaryStore = useDictionaryStore()
  109. const DialogStore = useDialogStore()
  110. const router = useRouter()
  111. const route = useRoute()
  112. const state: any = reactive({
  113. drawer: false,
  114. dictMap: new Map(),
  115. activeNames: '',
  116. })
  117. const titleCpt = computed(() => import.meta.env.VITE_TITLE)
  118. const menusCpt: any = computed(() => {
  119. return router.getRoutes().filter((v) => v.name === 'root')[0]
  120. })
  121. const copyDict = (label, value) => {
  122. try {
  123. const key = value.replace(/_([a-z])/g, (match, letter) =>
  124. letter.toUpperCase(),
  125. )
  126. const str = `${value}: ['${key}List', '${key}Map'], // ${label}`
  127. copy(str)
  128. ElMessage.success('复制成功')
  129. } catch (e) {
  130. console.log(e)
  131. }
  132. }
  133. const onChangeTenant = (row) => {
  134. DialogStore.confirm({
  135. content: `请确认是否切换到租户${row.name}?`,
  136. onSubmit: () => {
  137. loginChangeTenant(row.id)
  138. .then(({ data }: any) => {
  139. localStorage.setItem((import.meta as any).env.VITE_TOKEN, data)
  140. window.location.reload()
  141. })
  142. .catch(() => {})
  143. .finally(() => {})
  144. },
  145. })
  146. }
  147. const logout = () => {
  148. DialogStore.confirm({
  149. content: `请确认是否退出登录?`,
  150. onSubmit: () => {
  151. loginLogout()
  152. .then(({ data }: any) => {
  153. localStorage.clear()
  154. window.location.reload()
  155. })
  156. .catch(() => {})
  157. .finally(() => {})
  158. },
  159. })
  160. }
  161. onMounted(() => {
  162. sysDict().then(({ data }: any) => {
  163. data.forEach((v) => {
  164. const d = state.dictMap.get(v.dictType)
  165. if (d) {
  166. d.children.push(v)
  167. } else {
  168. state.dictMap.set(v.dictType, { name: v.remark, children: [v] })
  169. }
  170. })
  171. })
  172. })
  173. </script>
  174. <style lang="scss" scoped>
  175. .head-com {
  176. width: 100%;
  177. height: 100%;
  178. display: flex;
  179. align-items: center;
  180. .logo {
  181. margin-left: 1.5rem;
  182. }
  183. .title {
  184. margin-left: 0.75rem;
  185. font-weight: bold;
  186. font-size: 1.38rem;
  187. background: linear-gradient(180deg, #3dc8fe 0%, #2d6bd0 46%, #6255f6 100%);
  188. color: transparent;
  189. background-clip: text;
  190. font-family: Alimama;
  191. word-break: keep-all;
  192. }
  193. .menus {
  194. margin-left: 5rem;
  195. display: flex;
  196. align-items: center;
  197. gap: 3.13rem;
  198. .menu-item {
  199. font-weight: bold;
  200. font-size: 1.13rem;
  201. color: #303133;
  202. word-break: keep-all;
  203. &.active {
  204. color: var(--czr-main-color);
  205. }
  206. }
  207. }
  208. }
  209. </style>