index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <template>
  2. <Dropdown placement="bottomLeft" :overlayClassName="`${prefixCls}-dropdown-overlay`">
  3. <span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
  4. <img :class="`${prefixCls}__header`" :src="getAvatarUrl" />
  5. <span :class="`${prefixCls}__info hidden md:block`">
  6. <span :class="`${prefixCls}__name `" class="truncate">
  7. {{ getUserInfo.realname }}
  8. </span>
  9. </span>
  10. </span>
  11. <template #overlay>
  12. <Menu @click="handleMenuClick">
  13. <!-- <MenuItem key="doc" :text="t('layout.header.dropdownItemDoc')" icon="ion:document-text-outline" v-if="getShowDoc" /> -->
  14. <MenuDivider v-if="getShowDoc" />
  15. <MenuItem key="account" :text="t('layout.header.dropdownItemSwitchAccount')" icon="ant-design:setting-outlined" />
  16. <MenuItem key="password" :text="t('layout.header.dropdownItemSwitchPassword')" icon="ant-design:edit-outlined" />
  17. <MenuItem key="depart" :text="t('layout.header.dropdownItemSwitchDepart')" icon="ant-design:cluster-outlined" />
  18. <MenuItem key="cache" :text="t('layout.header.dropdownItemRefreshCache')" icon="ion:sync-outline" />
  19. <MenuItem v-if="sysOrgCode != 'sdmtjtgsd'" key="modalCache" :text="t('layout.header.dropdownItemSwitchModal')" icon="ion:sync-outline" />
  20. <!-- <MenuItem v-if="sysOrgCode != 'sdmtjtgsd'" key="switchTheme" :text="t('layout.header.dropdownItemSwitchTheme')" icon="ion:color-palette" /> -->
  21. <!-- <MenuItem
  22. v-if="getUseLockPage"
  23. key="lock"
  24. :text="t('layout.header.tooltipLock')"
  25. icon="ion:lock-closed-outline"
  26. />-->
  27. <MenuItem key="download" :text="t('layout.header.dropdownItemApp')" icon="ion:download-outline" />
  28. <MenuItem key="homeStyle" :text="t('layout.header.dropdownItemHomeStyle')" icon="ion:home-outline" />
  29. <MenuItem key="about" :text="t('layout.header.dropdownItemAbout')" icon="ion:information-outline" />
  30. <MenuItem key="logout" :text="t('layout.header.dropdownItemLoginOut')" icon="ion:power-outline" />
  31. </Menu>
  32. </template>
  33. </Dropdown>
  34. <LockAction v-if="lockActionVisible" ref="lockActionRef" @register="register" />
  35. <DepartSelect ref="loginSelectRef" />
  36. <ThemeSelect ref="themeSelectRef" />
  37. <HomeStyleSelect ref="homeStyleSelectRef" />
  38. <UpdatePassword v-if="passwordVisible" ref="updatePasswordRef" />
  39. </template>
  40. <script lang="ts">
  41. // components
  42. import { Dropdown, Menu, message } from 'ant-design-vue';
  43. import { defineComponent, computed, ref } from 'vue';
  44. import { SITE_URL } from '/@/settings/siteSetting';
  45. import { useUserStore } from '/@/store/modules/user';
  46. import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  47. import { useI18n } from '/@/hooks/web/useI18n';
  48. import { useDesign } from '/@/hooks/web/useDesign';
  49. import { useModal } from '/@/components/Modal';
  50. import { useMessage } from '/@/hooks/web/useMessage';
  51. import { useGo } from '/@/hooks/web/usePage';
  52. import headerImg from '/@/assets/images/header.jpg';
  53. import { propTypes } from '/@/utils/propTypes';
  54. import { openWindow } from '/@/utils';
  55. import { useGlobSetting } from '/@/hooks/setting';
  56. import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
  57. import { refreshCache, queryAllDictItems, refreshRedis } from '/@/views/system/dict/dict.api';
  58. import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  59. import { removeAuthCache, setAuthCache } from '/@/utils/auth/index';
  60. import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
  61. import { getRefPromise } from '/@/utils/index';
  62. import { get } from 'lodash-es';
  63. import { getLatestAppDownload } from '/@/api/vent';
  64. type MenuEvent = 'logout' | 'doc' | 'lock' | 'cache' | 'depart' | 'modalCache' | 'switchTheme' | 'info' | 'homeStyle' | 'download';
  65. const { createMessage, createConfirmSync } = useMessage();
  66. export default defineComponent({
  67. name: 'UserDropdown',
  68. components: {
  69. Dropdown,
  70. Menu,
  71. MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
  72. MenuDivider: Menu.Divider,
  73. LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')),
  74. DepartSelect: createAsyncComponent(() => import('./DepartSelect.vue')),
  75. ThemeSelect: createAsyncComponent(() => import('./ThemeSelect.vue')),
  76. HomeStyleSelect: createAsyncComponent(() => import('./HomeStyleSelect.vue')),
  77. UpdatePassword: createAsyncComponent(() => import('./UpdatePassword.vue')),
  78. },
  79. props: {
  80. theme: propTypes.oneOf(['dark', 'light']),
  81. },
  82. setup() {
  83. const { sysOrgCode, domainUrl } = useGlobSetting();
  84. const { prefixCls } = useDesign('header-user-dropdown');
  85. const { t } = useI18n();
  86. const { getShowDoc, getUseLockPage } = useHeaderSetting();
  87. const userStore = useUserStore();
  88. const go = useGo();
  89. const passwordVisible = ref(false);
  90. const lockActionVisible = ref(false);
  91. const lockActionRef = ref(null);
  92. const getUserInfo = computed(() => {
  93. const { realname = '', avatar, desc } = userStore.getUserInfo || {};
  94. return { realname, avatar: avatar || headerImg, desc };
  95. });
  96. const getAvatarUrl = computed(() => {
  97. let { avatar } = getUserInfo.value;
  98. if (avatar == headerImg) {
  99. return avatar;
  100. } else {
  101. return getFileAccessHttpUrl(avatar);
  102. }
  103. });
  104. const [register, { openModal }] = useModal();
  105. /**
  106. * 多部门弹窗逻辑
  107. */
  108. const loginSelectRef = ref();
  109. // update-begin--author:liaozhiyang---date:20230901---for:【QQYUN-6333】空路由问题—首次访问资源太大
  110. async function handleLock() {
  111. await getRefPromise(lockActionRef);
  112. openModal(true);
  113. }
  114. // update-end--author:liaozhiyang---date:20230901---for:【QQYUN-6333】空路由问题—首次访问资源太大
  115. // login out
  116. function handleLoginOut() {
  117. userStore.confirmLoginOut();
  118. }
  119. // open doc
  120. function openDoc() {
  121. openWindow(SITE_URL);
  122. }
  123. async function downloadApp() {
  124. const confirmed = await createConfirmSync({
  125. title: '应用下载',
  126. content: '是否下载?',
  127. });
  128. if (!confirmed) {
  129. return;
  130. }
  131. const app = await getLatestAppDownload();
  132. if (!app?.filename || !app?.downloadUrl) {
  133. createMessage.warning('未获取到应用下载路径');
  134. return;
  135. }
  136. const downloadUrl = /^https?:\/\//i.test(app.downloadUrl) ? app.downloadUrl : `${domainUrl}${app.downloadUrl}`;
  137. const link = document.createElement('a');
  138. link.href = downloadUrl;
  139. link.download = app.filename;
  140. link.style.display = 'none';
  141. document.body.appendChild(link);
  142. link.click();
  143. document.body.removeChild(link);
  144. createMessage.success('应用下载已开始,请在浏览器下载列表中查看');
  145. }
  146. // 清除缓存
  147. async function clearCache() {
  148. const result = await refreshCache();
  149. const refreshRedisResult = await refreshRedis();
  150. if (result.success && refreshRedisResult) {
  151. const res = await queryAllDictItems();
  152. removeAuthCache(DB_DICT_DATA_KEY);
  153. setAuthCache(DB_DICT_DATA_KEY, res.result);
  154. createMessage.success('刷新缓存完成!');
  155. } else {
  156. createMessage.error('刷新缓存失败!');
  157. }
  158. }
  159. function clearModalCache() {
  160. return new Promise((resolve) => {
  161. if (window['CustomDB']) {
  162. // 清除指定表的所有数据
  163. window['CustomDB'].modal
  164. .clear()
  165. .then(() => {
  166. console.log('表数据已成功清除!');
  167. resolve(null);
  168. window.location.reload();
  169. })
  170. .catch((error) => {
  171. console.error('清除数据时发生错误:', error);
  172. resolve(null);
  173. window.location.reload();
  174. });
  175. }
  176. });
  177. }
  178. // 切换部门
  179. function updateCurrentDepart() {
  180. loginSelectRef.value.show();
  181. }
  182. // 修改密码
  183. const updatePasswordRef = ref();
  184. // update-begin--author:liaozhiyang---date:20230901---for:【QQYUN-6333】空路由问题—首次访问资源太大
  185. async function updatePassword() {
  186. passwordVisible.value = true;
  187. await getRefPromise(updatePasswordRef);
  188. updatePasswordRef.value.show(userStore.getUserInfo.username);
  189. }
  190. // update-end--author:liaozhiyang---date:20230901---for:【QQYUN-6333】空路由问题—首次访问资源太大
  191. function handleMenuClick(e: { key: MenuEvent }) {
  192. switch (e.key) {
  193. case 'homeStyle':
  194. updateHomeStyle();
  195. break;
  196. case 'about':
  197. message.info(`当前版本:${get(window, '__LAST_PRODUCTION_TAG__.version', '开发模式')}`);
  198. break;
  199. case 'logout':
  200. handleLoginOut();
  201. break;
  202. case 'doc':
  203. openDoc();
  204. break;
  205. case 'lock':
  206. handleLock();
  207. break;
  208. case 'cache':
  209. clearCache();
  210. break;
  211. case 'modalCache':
  212. clearModalCache();
  213. break;
  214. case 'depart':
  215. updateCurrentDepart();
  216. break;
  217. case 'switchTheme':
  218. updateTheme();
  219. break;
  220. case 'password':
  221. updatePassword();
  222. break;
  223. case 'download':
  224. downloadApp();
  225. break;
  226. case 'account':
  227. //update-begin---author:wangshuai ---date:20221125 for:进入用户设置页面------------
  228. go(`/system/usersetting`);
  229. //update-end---author:wangshuai ---date:20221125 for:进入用户设置页面--------------
  230. break;
  231. }
  232. }
  233. /**
  234. * 切换主题弹窗
  235. */
  236. const themeSelectRef = ref();
  237. function updateTheme() {
  238. themeSelectRef.value.show();
  239. }
  240. // 首页风格弹窗
  241. const homeStyleSelectRef = ref();
  242. function updateHomeStyle() {
  243. homeStyleSelectRef.value.show();
  244. }
  245. return {
  246. prefixCls,
  247. t,
  248. getUserInfo,
  249. getAvatarUrl,
  250. handleMenuClick,
  251. getShowDoc,
  252. register,
  253. getUseLockPage,
  254. loginSelectRef,
  255. themeSelectRef,
  256. homeStyleSelectRef,
  257. updateHomeStyle,
  258. updatePasswordRef,
  259. passwordVisible,
  260. lockActionVisible,
  261. sysOrgCode,
  262. };
  263. },
  264. });
  265. </script>
  266. <style lang="less">
  267. @prefix-cls: ~'@{namespace}-header-user-dropdown';
  268. @ventSpace: zxm;
  269. .@{prefix-cls} {
  270. //height: @header-height;
  271. height: 48px;
  272. padding: 0 0 0 10px;
  273. padding-right: 10px;
  274. // overflow: hidden;
  275. font-size: 12px;
  276. cursor: pointer;
  277. align-items: center;
  278. color: @text-color-base;
  279. img {
  280. width: 24px;
  281. height: 24px;
  282. margin-right: 12px;
  283. }
  284. &__header {
  285. border-radius: 50%;
  286. }
  287. &__name {
  288. font-size: 14px;
  289. }
  290. &--dark {
  291. &:hover {
  292. background-color: @header-dark-bg-hover-color;
  293. }
  294. }
  295. &--light {
  296. &:hover {
  297. //background-color: @header-light-bg-hover-color;
  298. }
  299. .@{prefix-cls}__name {
  300. //color: @text-color-base;
  301. color: @white;
  302. }
  303. .@{prefix-cls}__desc {
  304. //color: @header-light-desc-color;
  305. color: @white;
  306. }
  307. }
  308. &-dropdown-overlay {
  309. .@{ventSpace}-dropdown-menu-item {
  310. min-width: 160px;
  311. }
  312. }
  313. }
  314. </style>