header.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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)">{{ scope.row.msg }}</el-link>
  90. </template>
  91. </el-table-column>
  92. <el-table-column prop="checked" label="状态" width="100">
  93. <template #default="scope">
  94. <el-tag :type="scope.row.checked ? 'success' : 'danger'">
  95. {{ scope.row.checked ? '已读' : '未读' }}
  96. </el-tag>
  97. </template>
  98. </el-table-column>
  99. <el-table-column prop="time" label="时间" width="140" />
  100. </el-table>
  101. </div>
  102. <div class="messageStyle">
  103. <el-dropdown>
  104. <span class="el-dropdown-link">
  105. <el-icon class="el-icon--right">
  106. <el-icon><MoreFilled /></el-icon>
  107. </el-icon>
  108. </span>
  109. <template #dropdown>
  110. <el-dropdown-menu>
  111. <el-dropdown-item @click="oneClickRead()" :disabled="numberOfLogos <= 0">一键已读</el-dropdown-item>
  112. </el-dropdown-menu>
  113. </template>
  114. </el-dropdown>
  115. </div>
  116. </div>
  117. </el-drawer>
  118. </template>
  119. <!-- /information/list -->
  120. <script lang="ts" setup>
  121. import { onMounted, ref, watchEffect, watch } from 'vue';
  122. import { RouteRecordRaw, useRouter, useRoute } from 'vue-router';
  123. import { useStore } from "../../store/index"
  124. import { post, uploadFile } from "@/utils/request";
  125. import defaultCover from "../../assets/defaultCover.png";
  126. import { formatDate } from '@/utils/times'
  127. import loginLogin from '../../assets/login/login_logo.png'
  128. const { routers, clearStore, userInfo } = useStore()
  129. const router = useRouter();
  130. const route = useRoute()
  131. // const routerList = ref<RouteRecordRaw[]>([]);
  132. const routerList = ref<any[]>([]);
  133. const activeRouter = ref<RouteRecordRaw>();
  134. const visibleItems = ref<number[]>([]);
  135. const parentDiv = ref<HTMLElement | null>(null);
  136. const itemLastIndex = ref(0)
  137. const moreRoutes = ref<any[]>([])
  138. const newsDrawerTableData = ref<any[]>([])
  139. const drawerVis = ref(false)
  140. const newsDrawer = ref(false)
  141. const newsDrawerLoading = ref(false)
  142. const numberOfLogos = ref(0)
  143. const updateVisibleItems = () => {
  144. const parentWidth = (parentDiv.value?.offsetWidth && parentDiv.value?.offsetWidth - 150) || 10;
  145. const canvas = document.createElement('canvas');
  146. const context = canvas.getContext('2d');
  147. let textWidthList: any = [] // 所有文字的宽度
  148. let totalWidth = 0;
  149. let temporaryIndex: any = []
  150. if (context) {
  151. context.font = '16px 微软雅黑';
  152. textWidthList = routerList.value.map((item: any) => {
  153. const metrics = context.measureText(item.name);
  154. return Math.ceil(metrics.width) + 32; // 32是padding和margin的宽度
  155. })
  156. }
  157. for (let i in textWidthList) {
  158. if (totalWidth + textWidthList[i] > parentWidth) {
  159. break;
  160. }
  161. totalWidth += textWidthList[i];
  162. temporaryIndex.push(+i);
  163. }
  164. // 替换最后一个元素
  165. const lastIndex = routerList.value.findIndex(obj => obj.name === '系统设置');
  166. itemLastIndex.value = lastIndex
  167. temporaryIndex.splice(temporaryIndex.length - 1, 1, lastIndex)
  168. visibleItems.value = temporaryIndex;
  169. // 过滤出隐藏的元素
  170. let interceptIndex = Object.values(visibleItems.value).findIndex(v => +v == lastIndex)
  171. let newVisibleItems = JSON.parse(JSON.stringify(temporaryIndex)).splice(0, interceptIndex + 1)
  172. let missingIndex = findMissingNumbers(newVisibleItems)
  173. let routerLists = []
  174. for (var i in missingIndex) {
  175. routerLists.push(routerList.value[missingIndex[i]])
  176. }
  177. moreRoutes.value = routerLists
  178. // 判断当前的索引是否在隐藏元素中
  179. let currentIndex = moreRoutes.value.findIndex(obj => obj.name === activeRouter.value?.name);
  180. if (currentIndex >= 0) {
  181. replaceData(activeRouter.value, currentIndex)
  182. }
  183. };
  184. const findMissingNumbers = (arr: any) => {
  185. let missingNumbers = [];
  186. arr.sort((a: any, b: any) => a - b); // 对数组进行排序
  187. for (let i = arr[0]; i < arr[arr.length - 1]; i++) {
  188. if (!arr.includes(i)) { // 如果数组中不包含当前数字
  189. missingNumbers.push(i); // 将缺失的数字添加到结果数组中
  190. }
  191. }
  192. return missingNumbers;
  193. }
  194. const setCurrentRouter = (item: RouteRecordRaw) => {
  195. activeRouter.value = item;
  196. if (item.children && item.children.length > 0) {
  197. router.push({ path: item.children[0].path });
  198. return
  199. }
  200. router.push({ path: item.path });
  201. };
  202. const moreSetCurrentRouter = (item: RouteRecordRaw, index: any) => {
  203. activeRouter.value = item;
  204. router.push({ path: item.path });
  205. replaceData(item, index)
  206. };
  207. const replaceData = (item: any, index: any) => {
  208. let itemIndex = routerList.value.findIndex(v => v.name == item.name)
  209. let lastItem = routerList.value[visibleItems.value[visibleItems.value.length - 2]]
  210. visibleItems.value.splice(visibleItems.value.length - 2, 1, itemIndex),
  211. moreRoutes.value.splice(index, 1, lastItem)
  212. }
  213. const logout = () => {
  214. clearStore();
  215. router.push({ path: '/login' });
  216. };
  217. const getNewsDrawerTableData = () => {
  218. newsDrawerLoading.value = true
  219. post(`/information/list`, {}).then(res => {
  220. const data = res.data.map((item: any) => {
  221. return {
  222. ...item,
  223. time: formatDate(new Date(item.time))
  224. }
  225. })
  226. numberOfLogos.value = (data || []).filter((item: any) => !item.checked).length
  227. newsDrawerTableData.value = data
  228. }).catch(err => {
  229. newsDrawerTableData.value = []
  230. console.log(err)
  231. }).finally(() => {
  232. newsDrawerLoading.value = false
  233. })
  234. }
  235. const oneClickRead = () => {
  236. newsDrawerLoading.value = true
  237. post(`/information/checkAll`).then(_res => {
  238. getNewsDrawerTableData()
  239. }).catch(err => {
  240. console.log(err, '<==== 失败')
  241. }).finally(() => {
  242. newsDrawerLoading.value = false
  243. })
  244. }
  245. const toDetail = (row: any) => {
  246. console.log(row, '<=== 点击数据')
  247. const { id, type, path } = row
  248. post(`/information/check`, { id }).then(res => {
  249. console.log(res, '<=== 成功')
  250. getNewsDrawerTableData()
  251. if (path) {
  252. router.push({ path });
  253. }
  254. }).catch(err => {
  255. console.log(err, '<==== 失败')
  256. }).finally(() => {
  257. newsDrawer.value = false
  258. })
  259. }
  260. onMounted(() => {
  261. getNewsDrawerTableData()
  262. routerList.value = routers;
  263. activeRouter.value = routerList.value.find((item) => item.path === router.currentRoute.value.path);
  264. window.addEventListener('resize', updateVisibleItems);
  265. setTimeout(() => {
  266. updateVisibleItems();
  267. }, 500);
  268. })
  269. watchEffect(() => {
  270. watch(() => route.path, (newPath, _oldPath) => {
  271. activeRouter.value = routerList.value.find((item) => item.path === newPath);
  272. }, {
  273. immediate: false
  274. }
  275. );
  276. });
  277. </script>
  278. <style scoped lang="scss">
  279. .messageStyle {
  280. margin: 15px;
  281. margin-left: 100px;
  282. }
  283. .trademark {
  284. font-size: 20px;
  285. }
  286. .multipleyHeader {
  287. height: 96%;
  288. display: flex;
  289. align-items: center;
  290. text-wrap: nowrap;
  291. .headerText {
  292. font-size: 16px;
  293. }
  294. }
  295. .parentBox {
  296. // max-width: 80%;
  297. // min-width: 300px;
  298. flex: 1;
  299. overflow: hidden;
  300. }
  301. .header-right {
  302. width: 135px;
  303. }
  304. .parentDiv {
  305. width: 10%;
  306. }
  307. .drawerVisClass {
  308. .drawerVisBtn {
  309. margin-top: 10px;
  310. border-top: 1px solid #999;
  311. div {
  312. text-align: center;
  313. line-height: 40px;
  314. color: #fff;
  315. cursor: pointer;
  316. &:hover {
  317. background: #086597;
  318. }
  319. }
  320. }
  321. }
  322. </style>