layoutTop.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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 ClassModal from '/src/views/home/modals/class-modal';
  38. import { suggestMessageUnread } from '/src/api/user';
  39. import { eventGlobal } from '/src/utils';
  40. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  41. import { schoolDetail } from '/src/views/studentList/api';
  42. // import AddStudentModel from '/src/views/studentList/modals/addStudentModel';
  43. import { modalClickMask } from '/src/state';
  44. import { teacherJobType } from '/src/utils/contants';
  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. // '/xiaoku-list',
  195. // '/studentDetail',
  196. // '/xiaoku-detail',
  197. // '/classStudentRecode',
  198. // '/afterWorkDetail'
  199. // ];
  200. // // 单独判断个人信息页面[学校设置]有引导
  201. // if (route.path === '/setting') {
  202. // return helpNoteList.baseListTab === 'school' ? true : false;
  203. // } else {
  204. // return hidePath.includes(routePath) ? false : true;
  205. // }
  206. // });
  207. return () => (
  208. <div class={styles.layoutTop}>
  209. <div class={styles.layoutLeft}>
  210. <NImage
  211. src={schoolIcon}
  212. class={styles.schoolIcon}
  213. previewDisabled></NImage>
  214. <p>
  215. {(info.value?.schoolInfos && info.value?.schoolInfos[0].name) || ''}
  216. </p>
  217. </div>
  218. <div class={styles.layoutRight}>
  219. <NTooltip showArrow={false}>
  220. {{
  221. trigger: () => (
  222. <div
  223. class={[
  224. styles.optons,
  225. // !helpNoteStatus.value && styles.booxToolDisabled
  226. ]}
  227. id="home-1"
  228. onClick={() => {
  229. // if (!helpNoteStatus.value) return;
  230. // // 默认滚动到页面顶部,在显示指引
  231. // document.querySelector('#WrapcoreViewWrap')?.scrollTo(0, 0);
  232. // console.log(route.name, 'guideInfo');
  233. // eventGlobal.emit('teacher-guideInfo', route.name);
  234. }}>
  235. <NImage src={gnydIcon} previewDisabled></NImage>
  236. </div>
  237. ),
  238. default: '功能引导'
  239. }}
  240. </NTooltip>
  241. {/* <NTooltip showArrow={false}>
  242. {{
  243. trigger: () => (
  244. <div class={styles.optons} onClick={showInviteQrcode}>
  245. <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
  246. <NImage src={inviteIcon} previewDisabled></NImage>
  247. </NBadge>
  248. </div>
  249. ),
  250. default: '邀请学生'
  251. }}
  252. </NTooltip> */}
  253. <NPopover
  254. width={380}
  255. class={styles.popoverClassModel}
  256. placement={'bottom'}
  257. v-model:show={classRecordStatus.value}
  258. trigger="click"
  259. displayDirective="show"
  260. v-slots={{
  261. trigger: () => (
  262. <NTooltip showArrow={false}>
  263. {{
  264. trigger: () => (
  265. <div class={styles.optons}>
  266. <NImage src={classHistoryIcon} previewDisabled></NImage>
  267. </div>
  268. ),
  269. default: '上课记录'
  270. }}
  271. </NTooltip>
  272. )
  273. }}>
  274. <ClassModal
  275. onConfirm={() => {
  276. classRecordStatus.value = false;
  277. }}
  278. />
  279. </NPopover>
  280. <NTooltip showArrow={false}>
  281. {{
  282. trigger: () => (
  283. <div class={styles.optons} onClick={showOption} id="home-2">
  284. <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
  285. <NImage src={opinionIcon} previewDisabled></NImage>
  286. </NBadge>
  287. </div>
  288. ),
  289. default: '意见反馈'
  290. }}
  291. </NTooltip>
  292. {/* </div> */}
  293. <div onClick={() => (showImGroup.value = true)}>
  294. <NTooltip showArrow={false}>
  295. {{
  296. trigger: () => (
  297. <NBadge
  298. value={noReadCount.value}
  299. max={99}
  300. class={[
  301. noReadCount.value > 0 ? '' : styles.messageBadgeHide,
  302. styles.messageBadge,
  303. noReadCount.value > 0 ? '' : styles.messageBadgeNo
  304. ]}
  305. {...{ id: 'home-3' }}
  306. color={'#FF1036'}>
  307. <NImage
  308. class={[
  309. styles.messageIcon,
  310. noReadCount.value > 0 ? styles.animation : ''
  311. ]}
  312. preview-disabled
  313. src={messageIcon}></NImage>
  314. </NBadge>
  315. ),
  316. default: '聊天'
  317. }}
  318. </NTooltip>
  319. </div>
  320. <div class={styles.line}></div>
  321. <NPopover
  322. show-arrow={false}
  323. trigger="click"
  324. v-model:show={userInfoStatus.value}
  325. onUpdate:show={val => {
  326. showHeadFlag.value = val;
  327. }}
  328. class={styles.popoverHeader}
  329. placement="bottom-end"
  330. raw={true}
  331. v-slots={{
  332. trigger: () => (
  333. <div class={styles.mesgWrap} style={{ cursor: 'pointer' }}>
  334. <NImage
  335. preview-disabled
  336. class={styles.teacherIcon}
  337. src={
  338. info.value.avatar ? info.value.avatar : teacherIcon
  339. }></NImage>
  340. <NIcon
  341. class={
  342. showHeadFlag.value ? styles.rotueLeft : styles.rotueRight
  343. }>
  344. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  345. <path
  346. 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"
  347. fill="currentColor"></path>
  348. </svg>
  349. </NIcon>
  350. </div>
  351. )
  352. }}>
  353. <div class={styles.propWrap}>
  354. <div class={styles.teacherInfo}>
  355. <NImage
  356. class={styles.teacherIcon}
  357. src={info.value.avatar ? info.value.avatar : teacherIcon}
  358. previewDisabled></NImage>
  359. <div class={styles.userInfos}>
  360. <NTooltip class={styles.nameTool}>
  361. {{
  362. trigger: () => (
  363. <p class={styles.teacherName}>{info.value.nickname}</p>
  364. ),
  365. default: () => info.value.nickname
  366. }}
  367. </NTooltip>
  368. {info.value.teacherJobType && (
  369. <span class={styles.roleType}>
  370. {teacherJobType[info.value.teacherJobType]}
  371. </span>
  372. )}
  373. </div>
  374. </div>
  375. <div class={styles.propWrapList}>
  376. <div
  377. class={styles.propWrapItem}
  378. onClick={() => oncheckEditStatus(gotoPerson)}>
  379. <NImage
  380. class={styles.smallIcon}
  381. src={personIcon}
  382. previewDisabled></NImage>
  383. <p class={styles.smallTitle}>个人信息</p>
  384. </div>
  385. {info.value.isSuperAdmin ||
  386. info.value.teacherJobType === 'HEADMASTER' ? (
  387. <div
  388. class={styles.propWrapItem}
  389. onClick={() => {
  390. oncheckEditStatus(gotoSchool);
  391. }}>
  392. <NImage
  393. class={styles.smallIcon}
  394. src={schoolDot}
  395. previewDisabled></NImage>
  396. <p class={styles.smallTitle}>学校信息</p>
  397. </div>
  398. ) : null}
  399. <div class={styles.propWrapItem} onClick={() => resetPwd()}>
  400. <NImage
  401. class={styles.smallIcon}
  402. src={clockIcon}
  403. previewDisabled></NImage>
  404. <p class={styles.smallTitle}>修改密码</p>
  405. </div>
  406. <div
  407. class={styles.propWrapItem}
  408. onClick={() => oncheckEditStatus(aboutUs)}>
  409. <NImage
  410. class={styles.smallIcon}
  411. src={iconAboutus}
  412. previewDisabled></NImage>
  413. <p class={styles.smallTitle}>关于我们</p>
  414. </div>
  415. </div>
  416. <div
  417. class={styles.logoutInfo}
  418. onClick={() => {
  419. users.logout();
  420. router.replace('/login');
  421. }}>
  422. <div class={styles.propWrapItem}>
  423. <NImage
  424. class={styles.smallIcon}
  425. src={closeIcon}
  426. previewDisabled></NImage>
  427. <p class={styles.smallTitle}>退出登录</p>
  428. </div>
  429. </div>
  430. </div>
  431. </NPopover>
  432. <div class={styles2.isHidden}></div>
  433. </div>
  434. <NModal
  435. maskClosable={modalClickMask}
  436. class={styles.changePwdModal}
  437. v-model:show={showWord.value}
  438. preset="dialog"
  439. showIcon={false}
  440. title="修改密码">
  441. <ForgotPassword
  442. phone={info.value.phone}
  443. onClose={() => {
  444. showWord.value = false;
  445. }}
  446. />
  447. </NModal>
  448. <NModal
  449. maskClosable={modalClickMask}
  450. v-model:show={showImGroup.value}
  451. showIcon={false}
  452. class={showImGroupLoading.value ? styles.hideModal : ''}
  453. {...{ id: 'imGroupDiv' }}
  454. displayDirective="show">
  455. <ImGroup />
  456. </NModal>
  457. <NModal
  458. maskClosable={modalClickMask}
  459. class={['modalTitle', 'background', styles.suggestWrap]}
  460. v-model:show={showSuggestionViseble.value}
  461. display-directive="show"
  462. showIcon={false}>
  463. <SuggestionOption
  464. ref={suggestionOptionRef}
  465. onClose={() =>
  466. (showSuggestionViseble.value = false)
  467. }></SuggestionOption>
  468. </NModal>
  469. </div>
  470. );
  471. }
  472. });