layoutTop.tsx 17 KB

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