header.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  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. <div class="cursor-pointer flex items-center">
  51. <el-icon :size="24" @click="applicationMarket()">
  52. <Menu />
  53. </el-icon>
  54. <div class="ml-2">应用市场</div>
  55. </div>
  56. <el-badge :value="numberOfLogos" :max="99" class="ml-4 cursor-pointer h-[26px]" :show-zero="false">
  57. <el-icon :size="26" @click="newsDrawer = true">
  58. <Bell />
  59. </el-icon>
  60. </el-badge>
  61. <div>
  62. <img class="w- h-8 rounded-full ml-4 cursor-pointer" :src="defaultCover" alt="" @click="drawerVis = true">
  63. </div>
  64. <el-icon :size="26" class="ml-4 cursor-pointer">
  65. <Grid />
  66. </el-icon>
  67. </div>
  68. <!-- 左侧 -->
  69. <el-drawer v-model="drawerVis" modal-class="drawerVisClass" :with-header="false">
  70. <div class="w-full h-full">
  71. <div class="bg-[#075985]">
  72. <div class="p-8">
  73. <div class="w-[100px] h-[100px] p-2 border-[1px] border-[#999] rounded-full m-auto">
  74. <img class="w-full h-full" :src="defaultCover">
  75. </div>
  76. </div>
  77. <div class="text-center text-[20px] leading-none text-white pb-3">
  78. <TextTranslation translationTypes="userName" :translationValue="userInfo.name" />
  79. </div>
  80. <div class="text-center leading-none text-slate-50 pb-3">角色:{{ userInfo.roleName }}</div>
  81. <div class="text-center leading-none text-slate-50 pb-3">公司:{{ userInfo.companyName }}</div>
  82. <div class="text-center leading-none text-slate-50 pb-3"><el-link type="warning" @click="changePasswordClick()">修改密码</el-link></div>
  83. <div class="w-full drawerVisBtn" v-if="userInfo.userNameNeedTranslate != 1">
  84. <div @click="logout()">退出</div>
  85. </div>
  86. </div>
  87. </div>
  88. </el-drawer>
  89. <!-- 消息 -->
  90. <el-drawer v-model="newsDrawer" modal-class="drawerVisClass" :with-header="false" size="50%">
  91. <div class="w-full h-full flex flex-row" v-loading="newsDrawerLoading">
  92. <div class="w-full h-full">
  93. <el-table :data="newsDrawerTableData" style="flex1" border>
  94. <el-table-column prop="msg" label="消息内容">
  95. <template #default="scope">
  96. <el-link type="primary" :underline="false" @click="toDetail(scope.row)">
  97. <!-- {{ scope.row.msg }} -->
  98. {{ scope.row.msgTextList[0] || '' }}
  99. <TextTranslation translationTypes="userName" :translationValue="scope.row.msgTextList[1]" v-if="(scope.row.msgTextList || []).length > 1">
  100. </TextTranslation>
  101. {{ scope.row.msgTextList[2] || '' }}
  102. </el-link>
  103. </template>
  104. </el-table-column>
  105. <el-table-column prop="checked" label="状态" width="100">
  106. <template #default="scope">
  107. <el-tag :type="scope.row.checked ? 'success' : 'danger'">
  108. {{ scope.row.checked ? '已读' : '未读' }}
  109. </el-tag>
  110. </template>
  111. </el-table-column>
  112. <el-table-column prop="time" label="时间" width="140" />
  113. </el-table>
  114. </div>
  115. <div class="messageStyle">
  116. <el-dropdown>
  117. <span class="el-dropdown-link">
  118. <el-icon class="el-icon--right">
  119. <el-icon><MoreFilled /></el-icon>
  120. </el-icon>
  121. </span>
  122. <template #dropdown>
  123. <el-dropdown-menu>
  124. <el-dropdown-item @click="oneClickRead()" :disabled="numberOfLogos <= 0">一键已读</el-dropdown-item>
  125. </el-dropdown-menu>
  126. </template>
  127. </el-dropdown>
  128. </div>
  129. </div>
  130. </el-drawer>
  131. <!-- 修改密码 -->
  132. <el-dialog v-model="changePasswordVlsable" title="修改密码" width="500">
  133. <el-form ref="ruleFormRef" :model="changePasswordForm" class="mt-4" :rules="changePasswordRules" status-icon>
  134. <el-form-item label="原密码" label-width="90px">
  135. <el-input v-model.trim="changePasswordForm.originPassword" prop="originPassword" />
  136. </el-form-item>
  137. <el-form-item label="新密码" label-width="90px">
  138. <el-input v-model.trim="changePasswordForm.newPassword" prop="newPassword" />
  139. </el-form-item>
  140. </el-form>
  141. <template #footer>
  142. <div class="dialog-footer">
  143. <el-button @click="changePasswordVlsable = false">取消</el-button>
  144. <el-button type="primary" @click="changePassword(ruleFormRef)">
  145. 确定
  146. </el-button>
  147. </div>
  148. </template>
  149. </el-dialog>
  150. </template>
  151. <!-- /information/list -->
  152. <script lang="ts" setup>
  153. import { onMounted, ref, watchEffect, watch, reactive, inject } from 'vue';
  154. import { RouteRecordRaw, useRouter, useRoute } from 'vue-router';
  155. import { useStore } from "../../store/index"
  156. import { post, uploadFile } from "@/utils/request";
  157. import defaultCover from "../../assets/defaultCover.png";
  158. import { formatDate } from '@/utils/times'
  159. import loginLogin from '../../assets/login/login_logo.png'
  160. import type { ComponentSize, FormInstance, FormRules } from 'element-plus'
  161. const { routers, clearStore, userInfo } = useStore()
  162. const router = useRouter();
  163. const route = useRoute()
  164. // const routerList = ref<RouteRecordRaw[]>([]);
  165. const routerList = ref<any[]>([]);
  166. const activeRouter = ref<RouteRecordRaw>();
  167. const visibleItems = ref<number[]>([]);
  168. const parentDiv = ref<HTMLElement | null>(null);
  169. const itemLastIndex = ref(0)
  170. const moreRoutes = ref<any[]>([])
  171. const newsDrawerTableData = ref<any[]>([])
  172. const drawerVis = ref(false)
  173. const newsDrawer = ref(false)
  174. const newsDrawerLoading = ref(false)
  175. const numberOfLogos = ref(0)
  176. const changePasswordVlsable = ref(false)
  177. const changePasswordForm = ref({
  178. originPassword: '',
  179. newPassword: '',
  180. })
  181. interface RuleForm {
  182. originPassword: string
  183. newPassword: string
  184. }
  185. const changePasswordRules = reactive<FormRules<RuleForm>>({
  186. originPassword: [
  187. { required: true, message: '请输入原密码', trigger: 'blur' },
  188. ],
  189. newPassword: [
  190. { required: true, message: '请输入新密码', trigger: 'blur' },
  191. ],
  192. })
  193. const globalPopup = inject<GlobalPopup>('globalPopup')
  194. const ruleFormRef = ref<FormInstance>()
  195. const applicationMarket = () => {
  196. window.location.href = 'https://www.ttkuaiban.com/appMarket.html'
  197. // window.open('https://www.ttkuaiban.com/appMarket.html', '_blank');
  198. }
  199. const changePassword = (formEl: FormInstance | undefined) => {
  200. if (!formEl) return
  201. if(!changePasswordForm.value.originPassword) {
  202. globalPopup?.showWarning('请填写原密码')
  203. return
  204. }
  205. if(!changePasswordForm.value.newPassword) {
  206. globalPopup?.showWarning('请填写新密码')
  207. return
  208. }
  209. const fomrVal = {
  210. ...changePasswordForm.value,
  211. id: userInfo.id
  212. }
  213. post(`/user/editPassword`, { ...fomrVal }).then(_res => {
  214. globalPopup?.showSuccess('修改成功')
  215. setTimeout(() => {
  216. logout()
  217. }, 500)
  218. }).catch(err => {
  219. globalPopup?.showError(err.msg)
  220. })
  221. }
  222. const changePasswordClick = () => {
  223. changePasswordForm.value = {
  224. originPassword: '',
  225. newPassword: '',
  226. }
  227. changePasswordVlsable.value = true
  228. }
  229. const updateVisibleItems = () => {
  230. const parentWidth = (parentDiv.value?.offsetWidth && parentDiv.value?.offsetWidth - 150) || 10;
  231. const canvas = document.createElement('canvas');
  232. const context = canvas.getContext('2d');
  233. let textWidthList: any = [] // 所有文字的宽度
  234. let totalWidth = 0;
  235. let temporaryIndex: any = []
  236. if (context) {
  237. context.font = '16px 微软雅黑';
  238. textWidthList = routerList.value.map((item: any) => {
  239. const metrics = context.measureText(item.name);
  240. return Math.ceil(metrics.width) + 32; // 32是padding和margin的宽度
  241. })
  242. }
  243. for (let i in textWidthList) {
  244. if (totalWidth + textWidthList[i] > parentWidth) {
  245. break;
  246. }
  247. totalWidth += textWidthList[i];
  248. temporaryIndex.push(+i);
  249. }
  250. // 替换最后一个元素
  251. const lastIndex = routerList.value.findIndex(obj => obj.name === '系统设置');
  252. itemLastIndex.value = lastIndex
  253. temporaryIndex.splice(temporaryIndex.length - 1, 1, lastIndex)
  254. visibleItems.value = temporaryIndex;
  255. // 过滤出隐藏的元素
  256. let interceptIndex = Object.values(visibleItems.value).findIndex(v => +v == lastIndex)
  257. let newVisibleItems = JSON.parse(JSON.stringify(temporaryIndex)).splice(0, interceptIndex + 1)
  258. let missingIndex = findMissingNumbers(newVisibleItems)
  259. let routerLists = []
  260. for (var i in missingIndex) {
  261. routerLists.push(routerList.value[missingIndex[i]])
  262. }
  263. moreRoutes.value = routerLists
  264. // 判断当前的索引是否在隐藏元素中
  265. let currentIndex = moreRoutes.value.findIndex(obj => obj.name === activeRouter.value?.name);
  266. if (currentIndex >= 0) {
  267. replaceData(activeRouter.value, currentIndex)
  268. }
  269. };
  270. const findMissingNumbers = (arr: any) => {
  271. let missingNumbers = [];
  272. arr.sort((a: any, b: any) => a - b); // 对数组进行排序
  273. for (let i = arr[0]; i < arr[arr.length - 1]; i++) {
  274. if (!arr.includes(i)) { // 如果数组中不包含当前数字
  275. missingNumbers.push(i); // 将缺失的数字添加到结果数组中
  276. }
  277. }
  278. return missingNumbers;
  279. }
  280. const setCurrentRouter = (item: RouteRecordRaw) => {
  281. activeRouter.value = item;
  282. if (item.children && item.children.length > 0) {
  283. router.push({ path: item.children[0].path });
  284. return
  285. }
  286. router.push({ path: item.path });
  287. };
  288. const moreSetCurrentRouter = (item: RouteRecordRaw, index: any) => {
  289. activeRouter.value = item;
  290. router.push({ path: item.path });
  291. replaceData(item, index)
  292. };
  293. const replaceData = (item: any, index: any) => {
  294. let itemIndex = routerList.value.findIndex(v => v.name == item.name)
  295. let lastItem = routerList.value[visibleItems.value[visibleItems.value.length - 2]]
  296. visibleItems.value.splice(visibleItems.value.length - 2, 1, itemIndex),
  297. moreRoutes.value.splice(index, 1, lastItem)
  298. }
  299. const logout = () => {
  300. clearStore();
  301. router.push({ path: '/login' });
  302. };
  303. const getNewsDrawerTableData = () => {
  304. newsDrawerLoading.value = true
  305. post(`/information/list`, {}).then(res => {
  306. const data = res.data.map((item: any) => {
  307. return {
  308. ...item,
  309. time: formatDate(new Date(item.time))
  310. }
  311. })
  312. numberOfLogos.value = (data || []).filter((item: any) => !item.checked).length
  313. newsDrawerTableData.value = (data || []).map((item: any) => {
  314. let str = item.msg || ''
  315. let arr = str.split(/\$|=/).filter((item: any) => item && item !== 'username');
  316. return {
  317. ...item,
  318. msgTextList: arr || []
  319. }
  320. })
  321. }).catch(err => {
  322. newsDrawerTableData.value = []
  323. console.log(err)
  324. }).finally(() => {
  325. newsDrawerLoading.value = false
  326. })
  327. }
  328. const oneClickRead = () => {
  329. newsDrawerLoading.value = true
  330. post(`/information/checkAll`).then(_res => {
  331. getNewsDrawerTableData()
  332. }).catch(err => {
  333. console.log(err, '<==== 失败')
  334. }).finally(() => {
  335. newsDrawerLoading.value = false
  336. })
  337. }
  338. const toDetail = (row: any) => {
  339. console.log(row, '<=== 点击数据')
  340. const { id, type, path } = row
  341. post(`/information/check`, { id }).then(res => {
  342. console.log(res, '<=== 成功')
  343. getNewsDrawerTableData()
  344. if (path) {
  345. router.push({ path });
  346. }
  347. }).catch(err => {
  348. console.log(err, '<==== 失败')
  349. }).finally(() => {
  350. newsDrawer.value = false
  351. })
  352. }
  353. onMounted(() => {
  354. getNewsDrawerTableData()
  355. const isThereAnyDataAnalysisAvailable = routers.filter((item: any) => item.path == '/analysis') || []
  356. routerList.value = isThereAnyDataAnalysisAvailable.length > 0 ? routers : [
  357. { name: '首页', id: 99999, path: '/analysis', isMenu: true, useState: false, orderitem: 1, checked: false, icon: null, functionList: [], children: [], parentId: null },
  358. ...routers
  359. ];
  360. setTimeout(() => {
  361. console.log(routerList.value)
  362. }, 3000)
  363. activeRouter.value = routerList.value.find((item) => item.path === router.currentRoute.value.path);
  364. window.addEventListener('resize', updateVisibleItems);
  365. setTimeout(() => {
  366. updateVisibleItems();
  367. }, 500);
  368. })
  369. watchEffect(() => {
  370. watch(() => route.path, (newPath, _oldPath) => {
  371. activeRouter.value = routerList.value.find((item) => item.path === newPath);
  372. }, {
  373. immediate: false
  374. }
  375. );
  376. });
  377. </script>
  378. <style scoped lang="scss">
  379. .messageStyle {
  380. margin: 15px;
  381. margin-left: 100px;
  382. }
  383. .trademark {
  384. font-size: 20px;
  385. }
  386. .multipleyHeader {
  387. height: 96%;
  388. display: flex;
  389. align-items: center;
  390. text-wrap: nowrap;
  391. .headerText {
  392. font-size: 16px;
  393. }
  394. }
  395. .parentBox {
  396. // max-width: 80%;
  397. // min-width: 300px;
  398. flex: 1;
  399. overflow: hidden;
  400. }
  401. .header-right {
  402. width: 220px;
  403. }
  404. .parentDiv {
  405. width: 10%;
  406. }
  407. .drawerVisClass {
  408. .drawerVisBtn {
  409. margin-top: 10px;
  410. border-top: 1px solid #999;
  411. div {
  412. text-align: center;
  413. line-height: 40px;
  414. color: #fff;
  415. cursor: pointer;
  416. &:hover {
  417. background: #086597;
  418. }
  419. }
  420. }
  421. }
  422. </style>