user.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <template>
  2. <div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
  3. <!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
  4. <div class="layout-navbars-breadcrumb-user-icon">
  5. <i class="iconfont icon-ziti" :title="$t('message.user.title0')"></i>
  6. </div>
  7. <template #dropdown>
  8. <el-dropdown-menu>
  9. <el-dropdown-item command="large" :disabled="state.disabledSize === 'large'">{{ $t('message.user.dropdownLarge') }}</el-dropdown-item>
  10. <el-dropdown-item command="default" :disabled="state.disabledSize === 'default'">{{ $t('message.user.dropdownDefault') }}</el-dropdown-item>
  11. <el-dropdown-item command="small" :disabled="state.disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
  12. </el-dropdown-menu>
  13. </template>
  14. </el-dropdown>
  15. <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
  16. <div class="layout-navbars-breadcrumb-user-icon">
  17. <i
  18. class="iconfont"
  19. :class="state.disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'"
  20. :title="$t('message.user.title1')"
  21. ></i>
  22. </div>
  23. <template #dropdown>
  24. <el-dropdown-menu>
  25. <el-dropdown-item command="zh-cn" :disabled="state.disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
  26. <el-dropdown-item command="en" :disabled="state.disabledI18n === 'en'">English</el-dropdown-item>
  27. <el-dropdown-item command="zh-tw" :disabled="state.disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
  28. </el-dropdown-menu>
  29. </template>
  30. </el-dropdown>
  31. <div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
  32. <el-icon :title="$t('message.user.title2')">
  33. <ele-Search />
  34. </el-icon>
  35. </div>
  36. <div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
  37. <i class="icon-skin iconfont" :title="$t('message.user.title3')"></i>
  38. </div> -->
  39. <div class="layout-navbars-breadcrumb-user-icon">
  40. <el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
  41. <template #reference>
  42. <el-badge :value="messageCenters.unread_count" :hidden="messageCenters.unread_count === 0">
  43. <el-icon :title="$t('message.user.title4')">
  44. <ele-Bell />
  45. </el-icon>
  46. </el-badge>
  47. </template>
  48. <template #default>
  49. <UserNews />
  50. </template>
  51. </el-popover>
  52. </div>
  53. <div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
  54. <i
  55. class="iconfont"
  56. :title="state.isScreenfull ? $t('message.user.title6') : $t('message.user.title5')"
  57. :class="!state.isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'"
  58. ></i>
  59. </div>
  60. <div>
  61. <!-- <span v-if="!isSocketOpen"> -->
  62. <span v-if="false">
  63. <el-popconfirm
  64. width="250"
  65. ref="onlinePopoverRef"
  66. :confirm-button-text="$t('message.user.retry')"
  67. :icon="InfoFilled"
  68. trigger="hover"
  69. icon-color="#626AEF"
  70. :title="$t('message.user.onlinePrompt')"
  71. @confirm="onlineConfirmEvent"
  72. >
  73. <template #reference>
  74. <el-badge is-dot class="item" :class="{'online-status': isSocketOpen,'online-down':!isSocketOpen}">
  75. <img :src="userInfos.avatar || headerImage" class="layout-navbars-breadcrumb-user-link-photo mr5" />
  76. </el-badge>
  77. </template>
  78. </el-popconfirm>
  79. </span>
  80. </div>
  81. <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
  82. <span class="layout-navbars-breadcrumb-user-link">
  83. <span v-if="isSocketOpen">
  84. <el-badge is-dot class="item" :class="{'online-status': isSocketOpen,'online-down':!isSocketOpen}">
  85. <img :src="userInfos.avatar || headerImage" class="layout-navbars-breadcrumb-user-link-photo mr5" />
  86. </el-badge>
  87. </span>
  88. {{ userInfos.username === '' ? 'common' : userInfos.username }}
  89. <el-icon class="el-icon--right">
  90. <ele-ArrowDown />
  91. </el-icon>
  92. </span>
  93. <template #dropdown>
  94. <el-dropdown-menu>
  95. <el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
  96. <!-- <el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item> -->
  97. <el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
  98. </el-dropdown-menu>
  99. </template>
  100. </el-dropdown>
  101. <Search ref="searchRef" />
  102. </div>
  103. </template>
  104. <script setup lang="ts" name="layoutBreadcrumbUser">
  105. import { defineAsyncComponent, ref, computed, reactive, onMounted, unref, watch } from 'vue';
  106. import { useRouter } from 'vue-router';
  107. import { ElMessageBox, ElMessage } from 'element-plus';
  108. import screenfull from 'screenfull';
  109. import { useI18n } from 'vue-i18n';
  110. import { storeToRefs } from 'pinia';
  111. import { useUserInfo } from '/@/stores/userInfo';
  112. import { useThemeConfig } from '/@/stores/themeConfig';
  113. import other from '/@/utils/other';
  114. import mittBus from '/@/utils/mitt';
  115. import { Session, Local } from '/@/utils/storage';
  116. import headerImage from '/@/assets/img/headerImage.png';
  117. import websocket from '/@/utils/websocket';
  118. import { InfoFilled } from '@element-plus/icons-vue'
  119. // 引入组件
  120. const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
  121. const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue'));
  122. // 定义变量内容
  123. const { locale, t } = useI18n();
  124. const router = useRouter();
  125. const stores = useUserInfo();
  126. const storesThemeConfig = useThemeConfig();
  127. const { userInfos } = storeToRefs(stores);
  128. const { themeConfig } = storeToRefs(storesThemeConfig);
  129. const searchRef = ref();
  130. const state = reactive({
  131. isScreenfull: false,
  132. disabledI18n: 'zh-cn',
  133. disabledSize: 'large',
  134. });
  135. // 设置分割样式
  136. const layoutUserFlexNum = computed(() => {
  137. let num: string | number = '';
  138. const { layout, isClassicSplitMenu } = themeConfig.value;
  139. const layoutArr: string[] = ['defaults', 'columns'];
  140. if (layoutArr.includes(layout) || (layout === 'classic' && !isClassicSplitMenu)) num = '1';
  141. else num = '';
  142. return num;
  143. });
  144. // 定义变量内容
  145. const { isSocketOpen } = storeToRefs(useUserInfo());
  146. // websocket状态
  147. const onlinePopoverRef = ref()
  148. const onlineConfirmEvent = () => {
  149. if (!isSocketOpen.value) {
  150. websocket.is_reonnect = true
  151. websocket.reconnect_current = 1
  152. websocket.reconnect()
  153. }
  154. // 手动隐藏弹出
  155. unref(onlinePopoverRef).popperRef?.delayHide?.()
  156. }
  157. // 全屏点击时
  158. const onScreenfullClick = () => {
  159. if (!screenfull.isEnabled) {
  160. ElMessage.warning('暂不不支持全屏');
  161. return false;
  162. }
  163. screenfull.toggle();
  164. screenfull.on('change', () => {
  165. if (screenfull.isFullscreen) state.isScreenfull = true;
  166. else state.isScreenfull = false;
  167. });
  168. };
  169. // 布局配置 icon 点击时
  170. const onLayoutSetingClick = () => {
  171. mittBus.emit('openSetingsDrawer');
  172. };
  173. // 下拉菜单点击时
  174. const onHandleCommandClick = (path: string) => {
  175. if (path === 'logOut') {
  176. ElMessageBox({
  177. closeOnClickModal: false,
  178. closeOnPressEscape: false,
  179. title: t('message.user.logOutTitle'),
  180. message: t('message.user.logOutMessage'),
  181. showCancelButton: true,
  182. confirmButtonText: t('message.user.logOutConfirm'),
  183. cancelButtonText: t('message.user.logOutCancel'),
  184. buttonSize: 'default',
  185. beforeClose: (action, instance, done) => {
  186. if (action === 'confirm') {
  187. instance.confirmButtonLoading = true;
  188. instance.confirmButtonText = t('message.user.logOutExit');
  189. setTimeout(() => {
  190. done();
  191. setTimeout(() => {
  192. instance.confirmButtonLoading = false;
  193. }, 300);
  194. }, 700);
  195. } else {
  196. done();
  197. }
  198. },
  199. })
  200. .then(async () => {
  201. // 清除缓存/token等
  202. Session.clear();
  203. // 使用 reload 时,不需要调用 resetRoute() 重置路由
  204. window.location.reload();
  205. })
  206. .catch(() => {});
  207. } else {
  208. router.push(path);
  209. }
  210. };
  211. // 菜单搜索点击
  212. const onSearchClick = () => {
  213. searchRef.value.openSearch();
  214. };
  215. // 组件大小改变
  216. const onComponentSizeChange = (size: string) => {
  217. Local.remove('themeConfig');
  218. themeConfig.value.globalComponentSize = size;
  219. Local.set('themeConfig', themeConfig.value);
  220. initI18nOrSize('globalComponentSize', 'disabledSize');
  221. window.location.reload();
  222. };
  223. // 语言切换
  224. const onLanguageChange = (lang: string) => {
  225. Local.remove('themeConfig');
  226. themeConfig.value.globalI18n = lang;
  227. Local.set('themeConfig', themeConfig.value);
  228. locale.value = lang;
  229. other.useTitle();
  230. initI18nOrSize('globalI18n', 'disabledI18n');
  231. };
  232. // 初始化组件大小/i18n
  233. const initI18nOrSize = (value: string, attr: string) => {
  234. state[attr] = Local.get('themeConfig')[value];
  235. };
  236. // 页面加载时
  237. onMounted(() => {
  238. getLastMsg()
  239. if (Local.get('themeConfig')) {
  240. initI18nOrSize('globalComponentSize', 'disabledSize');
  241. initI18nOrSize('globalI18n', 'disabledI18n');
  242. }
  243. });
  244. //消息中心的未读数量
  245. import { messageCenterStore } from '/@/stores/messageCenter';
  246. const messageCenter = messageCenterStore();
  247. //获取最新消息
  248. import { request } from "/@/utils/service";
  249. const messageCenters=ref({
  250. unread_count:0
  251. })
  252. const getLastMsg= ()=>{
  253. request({
  254. url: '/api/system/message_center/get_unread_count/',
  255. method: 'get',
  256. params: {}
  257. }).then((res:any) => {
  258. console.log("messageNumber:",res)
  259. /* messageCenter=res.data */
  260. messageCenters.value.unread_count=res.data.unread_count
  261. })
  262. }
  263. </script>
  264. <style scoped lang="scss">
  265. .layout-navbars-breadcrumb-user {
  266. display: flex;
  267. align-items: center;
  268. justify-content: flex-end;
  269. &-link {
  270. height: 100%;
  271. display: flex;
  272. align-items: center;
  273. white-space: nowrap;
  274. &-photo {
  275. width: 25px;
  276. height: 25px;
  277. border-radius: 100%;
  278. }
  279. }
  280. &-icon {
  281. padding: 0 10px;
  282. cursor: pointer;
  283. color: var(--next-bg-topBarColor);
  284. height: 50px;
  285. line-height: 50px;
  286. display: flex;
  287. align-items: center;
  288. &:hover {
  289. background: var(--next-color-user-hover);
  290. i {
  291. display: inline-block;
  292. animation: logoAnimation 0.3s ease-in-out;
  293. }
  294. }
  295. }
  296. :deep(.el-dropdown) {
  297. color: var(--next-bg-topBarColor);
  298. }
  299. :deep(.el-badge) {
  300. height: 40px;
  301. line-height: 40px;
  302. display: flex;
  303. align-items: center;
  304. }
  305. :deep(.el-badge__content.is-fixed) {
  306. top: 12px;
  307. }
  308. .online-status{
  309. cursor: pointer;
  310. :deep(.el-badge__content.is-fixed) {
  311. top: 30px;
  312. font-size: 14px;
  313. left: 5px;
  314. height: 12px;
  315. width: 12px;
  316. padding: 0;
  317. background-color: #18bc9c;
  318. }
  319. }
  320. .online-down{
  321. cursor: pointer;
  322. :deep(.el-badge__content.is-fixed) {
  323. top: 30px;
  324. font-size: 14px;
  325. left: 5px;
  326. height: 12px;
  327. width: 12px;
  328. padding: 0;
  329. background-color: #979b9c;
  330. }
  331. }
  332. }
  333. </style>