index.vue 5.8 KB

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