header.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <template>
  2. <div class="trademark mr-8 flex items-center text-white">
  3. <img :src="loginLogin" class="w-10 h-10 mr-4" />
  4. <div class="text-nowrap">智能客户管家</div>
  5. </div>
  6. <div class=" flex flex-row justify-start items-center text-white flex-1 parentDiv" ref="parentDiv">
  7. <div v-for="(routerItem, routerItemIdex) in routerList" @click="setCurrentRouter(routerItem)"
  8. :class="`border-b-2 border-transparent hover:border-white p-2 mr-4 cursor-pointer multipleyHeader ${activeRouter?.path === routerItem.path ? 'border-white' : ''}`"
  9. :key="routerItem.path" ref="childDivs" v-show="visibleItems.includes(routerItemIdex)">
  10. <div v-if="routerItem.children && routerItem.children.length <= 0 && routerItem?.isMenu" class="text-nowrap headerText">
  11. {{ routerItem.name }}
  12. </div>
  13. <div v-if="routerItem.children && routerItem.children.length > 0" class="flex justify-center items-center">
  14. <el-dropdown>
  15. <div class="text-white w-full h-full headerText">
  16. {{ routerItem.name }}
  17. <el-icon class="el-icon--right">
  18. <arrow-down />
  19. </el-icon>
  20. </div>
  21. <template #dropdown>
  22. <el-dropdown-menu>
  23. <el-dropdown-item v-for="child in routerItem.children" :key="child.path" @click="setCurrentRouter(child)">
  24. {{ child.name }}
  25. </el-dropdown-item>
  26. </el-dropdown-menu>
  27. </template>
  28. </el-dropdown>
  29. </div>
  30. </div>
  31. <div v-if="moreRoutes.length > 0" class="flex justify-center items-center">
  32. <el-dropdown trigger="click">
  33. <span class="text-white w-full h-full headerText">
  34. <el-icon>
  35. <MoreFilled />
  36. </el-icon>
  37. </span>
  38. <template #dropdown>
  39. <el-dropdown-menu>
  40. <el-dropdown-item v-for="(child, childIndex) in moreRoutes" :key="child.path"
  41. @click="moreSetCurrentRouter(child, childIndex)">
  42. {{ child.name }}
  43. </el-dropdown-item>
  44. </el-dropdown-menu>
  45. </template>
  46. </el-dropdown>
  47. </div>
  48. </div>
  49. <div class="flex flex-row justify-start items-center text-white header-right">
  50. <el-badge :value="numberOfLogos" :max="99" class="ml-4 cursor-pointer h-[26px]" :show-zero="false">
  51. <el-icon :size="26" @click="newsDrawer = true">
  52. <Bell />
  53. </el-icon>
  54. </el-badge>
  55. <div>
  56. <img class="w- h-8 rounded-full ml-4 cursor-pointer" :src="defaultCover" alt="" @click="drawerVis = true">
  57. </div>
  58. <el-icon :size="26" class="ml-4 cursor-pointer">
  59. <Grid />
  60. </el-icon>
  61. </div>
  62. <!-- 左侧 -->
  63. <el-drawer v-model="drawerVis" modal-class="drawerVisClass" :with-header="false">
  64. <div class="w-full h-full">
  65. <div class="bg-[#075985]">
  66. <div class="p-8">
  67. <div class="w-[100px] h-[100px] p-2 border-[1px] border-[#999] rounded-full m-auto">
  68. <img class="w-full h-full" :src="defaultCover">
  69. </div>
  70. </div>
  71. <div class="text-center text-[20px] leading-none text-white pb-3">
  72. <TextTranslation translationTypes="userName" :translationValue="userInfo.name" />
  73. </div>
  74. <div class="text-center leading-none text-slate-50 pb-3">角色:{{ userInfo.roleName }}</div>
  75. <div class="text-center leading-none text-slate-50 pb-3">公司:{{ userInfo.companyName }}</div>
  76. <div class="w-full drawerVisBtn">
  77. <div @click="logout()">退出</div>
  78. </div>
  79. </div>
  80. </div>
  81. </el-drawer>
  82. <!-- 消息 -->
  83. <el-drawer v-model="newsDrawer" modal-class="drawerVisClass" :with-header="false" size="50%">
  84. <div class="w-full h-full flex flex-row" v-loading="newsDrawerLoading">
  85. <div class="w-full h-full">
  86. <el-table :data="newsDrawerTableData" style="flex1" border>
  87. <el-table-column prop="msg" label="消息内容">
  88. <template #default="scope">
  89. <el-link type="primary" :underline="false" @click="toDetail(scope.row)">
  90. <!-- {{ scope.row.msg }} -->
  91. {{ scope.row.msgTextList[0] || '' }}
  92. <TextTranslation translationTypes="userName" :translationValue="scope.row.msgTextList[1]" v-if="(scope.row.msgTextList || []).length > 1">
  93. </TextTranslation>
  94. {{ scope.row.msgTextList[2] || '' }}
  95. </el-link>
  96. </template>
  97. </el-table-column>
  98. <el-table-column prop="checked" label="状态" width="100">
  99. <template #default="scope">
  100. <el-tag :type="scope.row.checked ? 'success' : 'danger'">
  101. {{ scope.row.checked ? '已读' : '未读' }}
  102. </el-tag>
  103. </template>
  104. </el-table-column>
  105. <el-table-column prop="time" label="时间" width="140" />
  106. </el-table>
  107. </div>
  108. <div class="messageStyle">
  109. <el-dropdown>
  110. <span class="el-dropdown-link">
  111. <el-icon class="el-icon--right">
  112. <el-icon><MoreFilled /></el-icon>
  113. </el-icon>
  114. </span>
  115. <template #dropdown>
  116. <el-dropdown-menu>
  117. <el-dropdown-item @click="oneClickRead()" :disabled="numberOfLogos <= 0">一键已读</el-dropdown-item>
  118. </el-dropdown-menu>
  119. </template>
  120. </el-dropdown>
  121. </div>
  122. </div>
  123. </el-drawer>
  124. </template>
  125. <!-- /information/list -->
  126. <script lang="ts" setup>
  127. import { onMounted, ref, watchEffect, watch } from 'vue';
  128. import { RouteRecordRaw, useRouter, useRoute } from 'vue-router';
  129. import { useStore } from "../../store/index"
  130. import { post, uploadFile } from "@/utils/request";
  131. import defaultCover from "../../assets/defaultCover.png";
  132. import { formatDate } from '@/utils/times'
  133. import loginLogin from '../../assets/login/login_logo.png'
  134. const { routers, clearStore, userInfo } = useStore()
  135. const router = useRouter();
  136. const route = useRoute()
  137. // const routerList = ref<RouteRecordRaw[]>([]);
  138. const routerList = ref<any[]>([]);
  139. const activeRouter = ref<RouteRecordRaw>();
  140. const visibleItems = ref<number[]>([]);
  141. const parentDiv = ref<HTMLElement | null>(null);
  142. const itemLastIndex = ref(0)
  143. const moreRoutes = ref<any[]>([])
  144. const newsDrawerTableData = ref<any[]>([])
  145. const drawerVis = ref(false)
  146. const newsDrawer = ref(false)
  147. const newsDrawerLoading = ref(false)
  148. const numberOfLogos = ref(0)
  149. const updateVisibleItems = () => {
  150. const parentWidth = (parentDiv.value?.offsetWidth && parentDiv.value?.offsetWidth - 150) || 10;
  151. const canvas = document.createElement('canvas');
  152. const context = canvas.getContext('2d');
  153. let textWidthList: any = [] // 所有文字的宽度
  154. let totalWidth = 0;
  155. let temporaryIndex: any = []
  156. if (context) {
  157. context.font = '16px 微软雅黑';
  158. textWidthList = routerList.value.map((item: any) => {
  159. const metrics = context.measureText(item.name);
  160. return Math.ceil(metrics.width) + 32; // 32是padding和margin的宽度
  161. })
  162. }
  163. for (let i in textWidthList) {
  164. if (totalWidth + textWidthList[i] > parentWidth) {
  165. break;
  166. }
  167. totalWidth += textWidthList[i];
  168. temporaryIndex.push(+i);
  169. }
  170. // 替换最后一个元素
  171. const lastIndex = routerList.value.findIndex(obj => obj.name === '系统设置');
  172. itemLastIndex.value = lastIndex
  173. temporaryIndex.splice(temporaryIndex.length - 1, 1, lastIndex)
  174. visibleItems.value = temporaryIndex;
  175. // 过滤出隐藏的元素
  176. let interceptIndex = Object.values(visibleItems.value).findIndex(v => +v == lastIndex)
  177. let newVisibleItems = JSON.parse(JSON.stringify(temporaryIndex)).splice(0, interceptIndex + 1)
  178. let missingIndex = findMissingNumbers(newVisibleItems)
  179. let routerLists = []
  180. for (var i in missingIndex) {
  181. routerLists.push(routerList.value[missingIndex[i]])
  182. }
  183. moreRoutes.value = routerLists
  184. // 判断当前的索引是否在隐藏元素中
  185. let currentIndex = moreRoutes.value.findIndex(obj => obj.name === activeRouter.value?.name);
  186. if (currentIndex >= 0) {
  187. replaceData(activeRouter.value, currentIndex)
  188. }
  189. };
  190. const findMissingNumbers = (arr: any) => {
  191. let missingNumbers = [];
  192. arr.sort((a: any, b: any) => a - b); // 对数组进行排序
  193. for (let i = arr[0]; i < arr[arr.length - 1]; i++) {
  194. if (!arr.includes(i)) { // 如果数组中不包含当前数字
  195. missingNumbers.push(i); // 将缺失的数字添加到结果数组中
  196. }
  197. }
  198. return missingNumbers;
  199. }
  200. const setCurrentRouter = (item: RouteRecordRaw) => {
  201. activeRouter.value = item;
  202. if (item.children && item.children.length > 0) {
  203. router.push({ path: item.children[0].path });
  204. return
  205. }
  206. router.push({ path: item.path });
  207. };
  208. const moreSetCurrentRouter = (item: RouteRecordRaw, index: any) => {
  209. activeRouter.value = item;
  210. router.push({ path: item.path });
  211. replaceData(item, index)
  212. };
  213. const replaceData = (item: any, index: any) => {
  214. let itemIndex = routerList.value.findIndex(v => v.name == item.name)
  215. let lastItem = routerList.value[visibleItems.value[visibleItems.value.length - 2]]
  216. visibleItems.value.splice(visibleItems.value.length - 2, 1, itemIndex),
  217. moreRoutes.value.splice(index, 1, lastItem)
  218. }
  219. const logout = () => {
  220. clearStore();
  221. router.push({ path: '/login' });
  222. };
  223. const getNewsDrawerTableData = () => {
  224. newsDrawerLoading.value = true
  225. post(`/information/list`, {}).then(res => {
  226. const data = res.data.map((item: any) => {
  227. return {
  228. ...item,
  229. time: formatDate(new Date(item.time))
  230. }
  231. })
  232. numberOfLogos.value = (data || []).filter((item: any) => !item.checked).length
  233. newsDrawerTableData.value = (data || []).map((item: any) => {
  234. let str = item.msg || ''
  235. let arr = str.split(/\$|=/).filter((item: any) => item && item !== 'username');
  236. return {
  237. ...item,
  238. msgTextList: arr || []
  239. }
  240. })
  241. }).catch(err => {
  242. newsDrawerTableData.value = []
  243. console.log(err)
  244. }).finally(() => {
  245. newsDrawerLoading.value = false
  246. })
  247. }
  248. const oneClickRead = () => {
  249. newsDrawerLoading.value = true
  250. post(`/information/checkAll`).then(_res => {
  251. getNewsDrawerTableData()
  252. }).catch(err => {
  253. console.log(err, '<==== 失败')
  254. }).finally(() => {
  255. newsDrawerLoading.value = false
  256. })
  257. }
  258. const toDetail = (row: any) => {
  259. console.log(row, '<=== 点击数据')
  260. const { id, type, path } = row
  261. post(`/information/check`, { id }).then(res => {
  262. console.log(res, '<=== 成功')
  263. getNewsDrawerTableData()
  264. if (path) {
  265. router.push({ path });
  266. }
  267. }).catch(err => {
  268. console.log(err, '<==== 失败')
  269. }).finally(() => {
  270. newsDrawer.value = false
  271. })
  272. }
  273. onMounted(() => {
  274. getNewsDrawerTableData()
  275. const isThereAnyDataAnalysisAvailable = routers.filter((item: any) => item.path == '/analysis') || []
  276. routerList.value = isThereAnyDataAnalysisAvailable.length > 0 ? routers : [
  277. { name: '首页', id: 99999, path: '/analysis', isMenu: true, useState: false, orderitem: 1, checked: false, icon: null, functionList: [], children: [], parentId: null },
  278. ...routers
  279. ];
  280. setTimeout(() => {
  281. console.log(routerList.value)
  282. }, 3000)
  283. activeRouter.value = routerList.value.find((item) => item.path === router.currentRoute.value.path);
  284. window.addEventListener('resize', updateVisibleItems);
  285. setTimeout(() => {
  286. updateVisibleItems();
  287. }, 500);
  288. })
  289. watchEffect(() => {
  290. watch(() => route.path, (newPath, _oldPath) => {
  291. activeRouter.value = routerList.value.find((item) => item.path === newPath);
  292. }, {
  293. immediate: false
  294. }
  295. );
  296. });
  297. </script>
  298. <style scoped lang="scss">
  299. .messageStyle {
  300. margin: 15px;
  301. margin-left: 100px;
  302. }
  303. .trademark {
  304. font-size: 20px;
  305. }
  306. .multipleyHeader {
  307. height: 96%;
  308. display: flex;
  309. align-items: center;
  310. text-wrap: nowrap;
  311. .headerText {
  312. font-size: 16px;
  313. }
  314. }
  315. .parentBox {
  316. // max-width: 80%;
  317. // min-width: 300px;
  318. flex: 1;
  319. overflow: hidden;
  320. }
  321. .header-right {
  322. width: 135px;
  323. }
  324. .parentDiv {
  325. width: 10%;
  326. }
  327. .drawerVisClass {
  328. .drawerVisBtn {
  329. margin-top: 10px;
  330. border-top: 1px solid #999;
  331. div {
  332. text-align: center;
  333. line-height: 40px;
  334. color: #fff;
  335. cursor: pointer;
  336. &:hover {
  337. background: #086597;
  338. }
  339. }
  340. }
  341. }
  342. </style>