index.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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 DictionaryStore.tenants.list.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 {
  96. useAppStore,
  97. useDialogStore,
  98. useDictionaryStore,
  99. useMenuStore,
  100. } from '@/stores'
  101. import { useRoute, useRouter } from 'vue-router'
  102. const AppStore = useAppStore()
  103. const DictionaryStore = useDictionaryStore()
  104. const DialogStore = useDialogStore()
  105. const router = useRouter()
  106. const route = useRoute()
  107. const { proxy } = getCurrentInstance()
  108. const state: any = reactive({
  109. drawer: false,
  110. dictMap: new Map(),
  111. activeNames: '',
  112. })
  113. const titleCpt = computed(() => import.meta.env.VITE_TITLE)
  114. const menusCpt: any = computed(
  115. () => router.options.routes.filter((v) => v.name === 'root')[0],
  116. )
  117. import { sysDict } from '@/api/modules/global/dictionary'
  118. import { copy } from '@/utils/czr-util'
  119. import { ElMessage } from 'element-plus'
  120. import { loginChangeTenant, loginLogout } from '@/api/modules/global/login'
  121. sysDict().then(({ data }: any) => {
  122. data.forEach((v) => {
  123. const d = state.dictMap.get(v.dictType)
  124. if (d) {
  125. d.children.push(v)
  126. } else {
  127. state.dictMap.set(v.dictType, { name: v.remark, children: [v] })
  128. }
  129. })
  130. })
  131. const copyDict = (label, value) => {
  132. try {
  133. const key = value.replace(/_([a-z])/g, (match, letter) =>
  134. letter.toUpperCase(),
  135. )
  136. const str = `${value}: ['${key}List', '${key}Map'], // ${label}`
  137. copy(str)
  138. ElMessage.success('复制成功')
  139. } catch (e) {
  140. console.log(e)
  141. }
  142. }
  143. const onChangeTenant = (row) => {
  144. DialogStore.confirm({
  145. content: `请确认是否切换到租户 ${row.name} ?`,
  146. onSubmit: () => {
  147. loginChangeTenant(row.id)
  148. .then(({ data }: any) => {
  149. localStorage.setItem((import.meta as any).env.VITE_TOKEN, data)
  150. window.location.reload()
  151. })
  152. .catch(() => {})
  153. .finally(() => {})
  154. },
  155. })
  156. }
  157. const logout = () => {
  158. DialogStore.confirm({
  159. content: `请确认是否退出登录?`,
  160. onSubmit: () => {
  161. loginLogout()
  162. .then(({ data }: any) => {
  163. localStorage.clear()
  164. window.location.reload()
  165. })
  166. .catch(() => {})
  167. .finally(() => {})
  168. },
  169. })
  170. }
  171. onMounted(() => {
  172. DictionaryStore.initTenants()
  173. })
  174. </script>
  175. <style lang="scss" scoped>
  176. .head-com {
  177. width: 100%;
  178. height: 100%;
  179. display: flex;
  180. align-items: center;
  181. .logo {
  182. margin-left: 1.5rem;
  183. }
  184. .title {
  185. margin-left: 0.75rem;
  186. font-weight: bold;
  187. font-size: 1.38rem;
  188. background: linear-gradient(180deg, #3dc8fe 0%, #2d6bd0 46%, #6255f6 100%);
  189. color: transparent;
  190. background-clip: text;
  191. font-family: Alimama;
  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. &.active {
  203. color: var(--czr-main-color);
  204. }
  205. }
  206. }
  207. }
  208. </style>