layoutTop.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. import {
  2. defineComponent,
  3. ref,
  4. onMounted,
  5. nextTick,
  6. onUnmounted,
  7. reactive,
  8. computed
  9. } from 'vue';
  10. import styles from './index.module.less';
  11. import { NImage, NBadge, NPopover, NIcon, NModal, NTooltip } from 'naive-ui';
  12. import styles2 from './modals/suggestion-option.module.less';
  13. import schoolIcon from './images/schoolIcon.png';
  14. import teacherIcon from './images/teacherIcon.png';
  15. import messageIcon from './images/messageIcon.png';
  16. import closeIcon from './images/closeIcon.png';
  17. import clockIcon from './images/clockIcon.png';
  18. import schoolDot from './images/schoolDot.png';
  19. import personIcon from './images/personIcon.png';
  20. import iconAboutus from './images/icon-aboutus.png';
  21. import { useUserStore } from '@/store/modules/users';
  22. import inFront from './images/inFront.png';
  23. import inBack from './images/inBack.png';
  24. import submitBtn from './images/submitBtn.png';
  25. import sealing from './images/sealing.png';
  26. import boxBg from './images/boxBg.png';
  27. import { useRouter, useRoute } from 'vue-router';
  28. import { storeToRefs } from 'pinia';
  29. import opinionIcon from './images/opinionIcon.png';
  30. import inviteIcon from './images/invite_student_icon.png';
  31. import gnydIcon from './images/gnyd.png';
  32. import classHistoryIcon from './images/classHistoryIcon.png';
  33. import 'animate.css';
  34. import ForgotPassword from '/src/views/setting/modal/forgotPassword';
  35. import ImGroup from './imGroup';
  36. import SuggestionOption from './modals/suggestion-option';
  37. import dayjs from 'dayjs';
  38. import ClassModal from '/src/views/home/modals/class-modal';
  39. import { suggestMessageUnread } from '/src/api/user';
  40. import { eventGlobal } from '/src/utils';
  41. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  42. import { schoolDetail } from '/src/views/studentList/api';
  43. import AddStudentModel from '/src/views/studentList/modals/addStudentModel';
  44. import { modalClickMask } from '/src/state';
  45. export default defineComponent({
  46. name: 'layoutTop',
  47. setup() {
  48. const router = useRouter();
  49. const noReadCount = ref(0); // 未读数
  50. const showHeadFlag = ref(false);
  51. const showImGroup = ref(false);
  52. const showImGroupLoading = ref(true);
  53. const showSuggestionViseble = ref(false);
  54. const users = useUserStore();
  55. const showWord = ref(false);
  56. const { info } = storeToRefs(users);
  57. const userInfoStatus = ref(false);
  58. const classRecordStatus = ref(false);
  59. const prepareStore = usePrepareStore();
  60. const state = reactive({
  61. addStudentVisible: false,
  62. activeRow: {} as any
  63. });
  64. const oncheckEditStatus = (callBack: any) => {
  65. showHeadFlag.value = false;
  66. userInfoStatus.value = false;
  67. if (prepareStore.getIsEditResource) {
  68. eventGlobal.emit('pageBeforeLeave', () => callBack());
  69. } else {
  70. callBack();
  71. }
  72. };
  73. const gotoPerson = () => {
  74. userInfoStatus.value = false;
  75. router.push({ path: '/setting', query: { activeTab: 'person' } });
  76. };
  77. const gotoSchool = () => {
  78. router.push({ path: '/setting', query: { activeTab: 'school' } });
  79. };
  80. const suggestionOptionRef = ref();
  81. const resetPwd = () => {
  82. showHeadFlag.value = false;
  83. showWord.value = true;
  84. userInfoStatus.value = false;
  85. };
  86. const aboutUs = () => {
  87. router.push({ path: '/aboutUs' });
  88. };
  89. const body = document.querySelector('body');
  90. if (body) {
  91. body.className = 'myBody body';
  92. }
  93. const showOption = () => {
  94. showSuggestionViseble.value = true;
  95. if (suggestionOptionRef.value) {
  96. suggestionOptionRef.value.onReset();
  97. }
  98. console.log(suggestionOptionRef.value, 'suggestionOptionRef');
  99. };
  100. // 邀请学生二维码
  101. const showInviteQrcode = async () => {
  102. try {
  103. const { schoolInfos } = users.getUserInfo;
  104. const schoolId = schoolInfos.length > 0 ? schoolInfos[0].id : null;
  105. if (schoolId) {
  106. const { data } = await schoolDetail({ id: schoolId });
  107. state.activeRow = data;
  108. state.addStudentVisible = true;
  109. }
  110. } catch {
  111. //
  112. }
  113. };
  114. const suggestionStatus = ref(false);
  115. const getSuggestMessageUnread = async () => {
  116. try {
  117. const { data } = await suggestMessageUnread();
  118. const temp = data || [];
  119. let system: any = {};
  120. temp.forEach((item: any) => {
  121. if (item.group === 'SYSTEM') {
  122. system = item;
  123. }
  124. });
  125. if (system.number > 0) {
  126. suggestionStatus.value = system.number > 0 ? true : false;
  127. } else {
  128. suggestionStatus.value = false;
  129. }
  130. } catch {
  131. //
  132. }
  133. };
  134. onMounted(() => {
  135. window.addEventListener('message', onImMessage);
  136. showImGroupLoading.value = true;
  137. showImGroup.value = true;
  138. getSuggestMessageUnread();
  139. eventGlobal.on('onSuggestionRead', () => {
  140. if (suggestionStatus.value) {
  141. getSuggestMessageUnread();
  142. }
  143. });
  144. nextTick(() => {
  145. setTimeout(() => {
  146. showImGroup.value = false;
  147. }, 50);
  148. setTimeout(() => {
  149. showImGroupLoading.value = false;
  150. if (body) {
  151. body.className = 'myBody';
  152. }
  153. }, 1000);
  154. });
  155. });
  156. const onImMessage = (evt: MessageEvent) => {
  157. if (evt.data.api === 'onImClose') {
  158. showImGroup.value = false;
  159. } else if (evt.data.api === 'getNoReadMessageCount') {
  160. console.log(evt, 'onMessage');
  161. noReadCount.value = evt.data.count || 0;
  162. }
  163. };
  164. onUnmounted(() => {
  165. window.removeEventListener('message', onImMessage);
  166. });
  167. const imglist = [inFront, inBack, submitBtn, sealing, boxBg];
  168. const loadImg = (imgList: any) => {
  169. for (let i = 0; i < imgList.length; i++) {
  170. const img = new Image();
  171. // let currentSrc = ''
  172. img.src = imgList[i];
  173. img.onload = function (e) {
  174. // console.log('加载完毕', e, img.complete);
  175. };
  176. img.onerror = function (e) {
  177. // console.log('加载错误', e);
  178. };
  179. }
  180. };
  181. loadImg(imglist);
  182. // 功能引导
  183. const route = useRoute();
  184. const helpNoteList = reactive({
  185. baseListTab: ''
  186. });
  187. const helpNoteStatus = computed(() => {
  188. const routePath = route.path;
  189. const hidePath = [
  190. '/classDetail',
  191. '/classStudentDetail',
  192. '/notation',
  193. '/xiaoku-ai',
  194. '/studentDetail',
  195. '/classStudentRecode',
  196. '/afterWorkDetail'
  197. ];
  198. // 单独判断个人信息页面[学校设置]有引导
  199. if (route.path === '/setting') {
  200. return helpNoteList.baseListTab === 'school' ? true : false;
  201. } else {
  202. return hidePath.includes(routePath) ? false : true;
  203. }
  204. });
  205. return () => (
  206. <>
  207. <div class={styles.layoutTop}>
  208. <div class={styles.layoutLeft}>
  209. <NImage
  210. src={schoolIcon}
  211. class={styles.schoolIcon}
  212. previewDisabled></NImage>
  213. <p>
  214. {/* {info.value.schoolInfos[0].tenantName} |{' '} */}
  215. {(info.value?.schoolInfos && info.value?.schoolInfos[0].name) ||
  216. ''}
  217. </p>
  218. </div>
  219. <div class={styles.layoutRight}>
  220. <NTooltip showArrow={false}>
  221. {{
  222. trigger: () => (
  223. <div
  224. class={[
  225. styles.optons,
  226. !helpNoteStatus.value && styles.booxToolDisabled
  227. ]}
  228. id="home-1"
  229. onClick={() => {
  230. if (!helpNoteStatus.value) return;
  231. // 默认滚动到页面顶部,在显示指引
  232. document
  233. .querySelector('#WrapcoreViewWrap')
  234. ?.scrollTo(0, 0);
  235. console.log(route.name, 'guideInfo');
  236. eventGlobal.emit('teacher-guideInfo', route.name);
  237. }}>
  238. <NImage src={gnydIcon} previewDisabled></NImage>
  239. </div>
  240. ),
  241. default: '功能引导'
  242. }}
  243. </NTooltip>
  244. {/* <NTooltip showArrow={false}>
  245. {{
  246. trigger: () => (
  247. <div class={styles.optons} onClick={showInviteQrcode}>
  248. <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
  249. <NImage src={inviteIcon} previewDisabled></NImage>
  250. </NBadge>
  251. </div>
  252. ),
  253. default: '邀请学生'
  254. }}
  255. </NTooltip> */}
  256. <NPopover
  257. width={380}
  258. class={styles.popoverClassModel}
  259. placement={'bottom'}
  260. v-model:show={classRecordStatus.value}
  261. trigger="click"
  262. displayDirective="show"
  263. v-slots={{
  264. trigger: () => (
  265. <NTooltip showArrow={false}>
  266. {{
  267. trigger: () => (
  268. <div class={styles.optons}>
  269. <NImage
  270. src={classHistoryIcon}
  271. previewDisabled></NImage>
  272. </div>
  273. ),
  274. default: '上课记录'
  275. }}
  276. </NTooltip>
  277. )
  278. }}>
  279. <ClassModal
  280. onConfirm={() => {
  281. classRecordStatus.value = false;
  282. }}
  283. />
  284. </NPopover>
  285. <NTooltip showArrow={false}>
  286. {{
  287. trigger: () => (
  288. <div class={styles.optons} onClick={showOption} id="home-2">
  289. <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
  290. <NImage src={opinionIcon} previewDisabled></NImage>
  291. </NBadge>
  292. </div>
  293. ),
  294. default: '意见反馈'
  295. }}
  296. </NTooltip>
  297. {/* </div> */}
  298. <div onClick={() => (showImGroup.value = true)}>
  299. <NTooltip showArrow={false}>
  300. {{
  301. trigger: () => (
  302. <NBadge
  303. value={noReadCount.value}
  304. max={99}
  305. class={[
  306. noReadCount.value > 0 ? '' : styles.messageBadgeHide,
  307. styles.messageBadge,
  308. noReadCount.value > 0 ? '' : styles.messageBadgeNo
  309. ]}
  310. {...{ id: 'home-3' }}
  311. color={'#FF1036'}>
  312. <NImage
  313. class={[
  314. styles.messageIcon,
  315. noReadCount.value > 0 ? styles.animation : ''
  316. ]}
  317. preview-disabled
  318. src={messageIcon}></NImage>
  319. </NBadge>
  320. ),
  321. default: '聊天'
  322. }}
  323. </NTooltip>
  324. </div>
  325. <div class={styles.line}></div>
  326. <NPopover
  327. show-arrow={false}
  328. trigger="click"
  329. v-model:show={userInfoStatus.value}
  330. onUpdate:show={val => {
  331. showHeadFlag.value = val;
  332. }}
  333. class={styles.popoverHeader}
  334. placement="bottom-end"
  335. raw={true}
  336. v-slots={{
  337. trigger: () => (
  338. <div class={styles.mesgWrap} style={{ cursor: 'pointer' }}>
  339. <NImage
  340. preview-disabled
  341. class={styles.teacherIcon}
  342. src={
  343. info.value.avatar ? info.value.avatar : teacherIcon
  344. }></NImage>
  345. <NIcon
  346. class={
  347. showHeadFlag.value
  348. ? styles.rotueLeft
  349. : styles.rotueRight
  350. }>
  351. <svg
  352. xmlns="http://www.w3.org/2000/svg"
  353. viewBox="0 0 24 24">
  354. <path
  355. d="M7.38 21.01c.49.49 1.28.49 1.77 0l8.31-8.31a.996.996 0 0 0 0-1.41L9.15 2.98c-.49-.49-1.28-.49-1.77 0s-.49 1.28 0 1.77L14.62 12l-7.25 7.25c-.48.48-.48 1.28.01 1.76z"
  356. fill="currentColor"></path>
  357. </svg>
  358. </NIcon>
  359. </div>
  360. )
  361. }}>
  362. <div class={styles.propWrap}>
  363. <div class={styles.teacherInfo}>
  364. <NImage
  365. class={styles.teacherIcon}
  366. src={info.value.avatar ? info.value.avatar : teacherIcon}
  367. previewDisabled></NImage>
  368. <NTooltip class={styles.nameTool}>
  369. {{
  370. trigger: () => (
  371. <p class={styles.teacherName}>{info.value.nickname}</p>
  372. ),
  373. default: () => info.value.nickname
  374. }}
  375. </NTooltip>
  376. </div>
  377. <div class={styles.propWrapList}>
  378. <div
  379. class={styles.propWrapItem}
  380. onClick={() => oncheckEditStatus(gotoPerson)}>
  381. <NImage
  382. class={styles.smallIcon}
  383. src={personIcon}
  384. previewDisabled></NImage>
  385. <p class={styles.smallTitle}>个人信息</p>
  386. </div>
  387. {info.value.isSuperAdmin ? (
  388. <div
  389. class={styles.propWrapItem}
  390. onClick={() => {
  391. oncheckEditStatus(gotoSchool);
  392. }}>
  393. <NImage
  394. class={styles.smallIcon}
  395. src={schoolDot}
  396. previewDisabled></NImage>
  397. <p class={styles.smallTitle}>学校信息</p>
  398. </div>
  399. ) : null}
  400. <div class={styles.propWrapItem} onClick={() => resetPwd()}>
  401. <NImage
  402. class={styles.smallIcon}
  403. src={clockIcon}
  404. previewDisabled></NImage>
  405. <p class={styles.smallTitle}>修改密码</p>
  406. </div>
  407. <div
  408. class={styles.propWrapItem}
  409. onClick={() => oncheckEditStatus(aboutUs)}>
  410. <NImage
  411. class={styles.smallIcon}
  412. src={iconAboutus}
  413. previewDisabled></NImage>
  414. <p class={styles.smallTitle}>关于我们</p>
  415. </div>
  416. </div>
  417. <div
  418. class={styles.logoutInfo}
  419. onClick={() => {
  420. users.logout();
  421. router.replace('/login');
  422. }}>
  423. <div class={styles.propWrapItem}>
  424. <NImage
  425. class={styles.smallIcon}
  426. src={closeIcon}
  427. previewDisabled></NImage>
  428. <p class={styles.smallTitle}>退出登录</p>
  429. </div>
  430. </div>
  431. </div>
  432. </NPopover>
  433. <div class={styles2.isHidden}></div>
  434. </div>
  435. <NModal
  436. maskClosable={modalClickMask}
  437. class={styles.changePwdModal}
  438. v-model:show={showWord.value}
  439. preset="dialog"
  440. showIcon={false}
  441. title="修改密码">
  442. <ForgotPassword
  443. phone={info.value.phone}
  444. onClose={() => {
  445. showWord.value = false;
  446. }}
  447. />
  448. </NModal>
  449. <NModal
  450. maskClosable={modalClickMask}
  451. v-model:show={showImGroup.value}
  452. showIcon={false}
  453. class={showImGroupLoading.value ? styles.hideModal : ''}
  454. {...{ id: 'imGroupDiv' }}
  455. displayDirective="show">
  456. <ImGroup />
  457. </NModal>
  458. <NModal
  459. maskClosable={modalClickMask}
  460. class={['modalTitle', 'background', styles.suggestWrap]}
  461. v-model:show={showSuggestionViseble.value}
  462. display-directive="show"
  463. showIcon={false}>
  464. <SuggestionOption
  465. ref={suggestionOptionRef}
  466. onClose={() =>
  467. (showSuggestionViseble.value = false)
  468. }></SuggestionOption>
  469. </NModal>
  470. {/* {state.addStudentVisible ? (
  471. <div
  472. v-model:show={state.addStudentVisible}
  473. class={['n-modal-mask', styles.popBox]}>
  474. <AddStudentModel
  475. activeRow={state.activeRow}
  476. onClose={() => {
  477. state.addStudentVisible = false;
  478. }}></AddStudentModel>
  479. </div>
  480. ) : null} */}
  481. </div>
  482. </>
  483. );
  484. }
  485. });