attent-guide.tsx 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. import { NButton } from 'naive-ui';
  2. import {
  3. defineComponent,
  4. nextTick,
  5. onMounted,
  6. onUnmounted,
  7. reactive,
  8. ref,
  9. watch
  10. } from 'vue';
  11. import styles from './index.module.less';
  12. import { getImage } from './images';
  13. import { eventGlobal, px2vw, px2vwH } from '@/utils/index';
  14. import { useRoute } from 'vue-router';
  15. import { getGuidance, setGuidance } from './api';
  16. export default defineComponent({
  17. name: 'attent-guide',
  18. emits: ['close'],
  19. setup(props, { emit }) {
  20. const route = useRoute();
  21. console.log(route.query);
  22. const data = reactive({
  23. box: {
  24. height: '0px'
  25. } as any,
  26. show: false,
  27. /**
  28. *
  29. width: px2vw(840),
  30. height: px2vw(295)
  31. */
  32. steps: [
  33. {
  34. ele: '',
  35. eleRect: {} as DOMRect,
  36. img: getImage('attent1.png'),
  37. handStyle: {
  38. top: '0.91rem'
  39. },
  40. imgStyle: {
  41. width: px2vw(647),
  42. height: px2vw(223),
  43. left: px2vw(-647),
  44. top: px2vw(-150)
  45. },
  46. btnsStyle: {
  47. bottom: px2vw(180),
  48. left: px2vw(-490)
  49. },
  50. eleRectPadding: {
  51. left: 7,
  52. top: 7,
  53. width: 14,
  54. height: 14
  55. },
  56. type: 'left'
  57. },
  58. {
  59. ele: '',
  60. img: getImage('attent2.png'),
  61. imgStyle: {
  62. left: px2vw(-290),
  63. top: px2vw(20),
  64. width: px2vw(477),
  65. height: px2vw(277)
  66. },
  67. btnsStyle: {
  68. bottom: px2vw(70),
  69. left: px2vw(-135)
  70. },
  71. boxStyle: {
  72. borderRadius: '50%'
  73. },
  74. eleRectPadding: {
  75. left: 7,
  76. top: 7,
  77. width: 14,
  78. height: 14
  79. },
  80. type: 'bottom'
  81. },
  82. {
  83. ele: '',
  84. img: getImage('attent3.png'),
  85. imgStyle: {
  86. left: px2vw(-290),
  87. top: px2vw(20),
  88. width: px2vw(382),
  89. height: px2vw(277)
  90. },
  91. btnsStyle: {
  92. bottom: px2vw(70),
  93. left: px2vw(-135)
  94. },
  95. boxStyle: {
  96. borderRadius: '50%'
  97. },
  98. eleRectPadding: {
  99. left: 7,
  100. top: 7,
  101. width: 14,
  102. height: 14
  103. },
  104. type: 'bottom'
  105. },
  106. {
  107. ele: '',
  108. img:
  109. route.query.type == 'preview'
  110. ? getImage('attent5.png')
  111. : getImage('attent4.png'),
  112. imgStyle: {
  113. top: '100%',
  114. left: px2vw(-2),
  115. width: px2vw(515),
  116. height: px2vw(227)
  117. },
  118. btnsStyle: {
  119. bottom: px2vw(30),
  120. left: px2vw(-20)
  121. },
  122. boxStyle: {
  123. borderRadius: '50px'
  124. },
  125. eleRectPadding: {
  126. left: 7,
  127. top: 7,
  128. width: 14,
  129. height: 14
  130. }
  131. }
  132. ],
  133. step: 0
  134. });
  135. const tipShow = ref(false);
  136. const guideInfo = ref({} as any);
  137. const getAllGuidance = async () => {
  138. try {
  139. const res = await getGuidance({ guideTag: 'teacher-guideInfo' });
  140. if (res.data) {
  141. guideInfo.value = JSON.parse(res.data?.guideValue) || null;
  142. } else {
  143. guideInfo.value = {};
  144. }
  145. if (guideInfo.value && guideInfo.value.attentGuide) {
  146. tipShow.value = false;
  147. } else {
  148. tipShow.value = true;
  149. }
  150. } catch (e) {
  151. console.log(e);
  152. }
  153. // const guideInfo = localStorage.getItem('teacher-guideInfo');
  154. };
  155. getAllGuidance();
  156. const getStepELe = () => {
  157. const ele: HTMLElement = document.getElementById(`attent-${data.step}`)!;
  158. if (ele) {
  159. const eleRect = ele.getBoundingClientRect();
  160. const left = data.steps[data.step].eleRectPadding?.left || 0;
  161. const top = data.steps[data.step].eleRectPadding?.top || 0;
  162. const width = data.steps[data.step].eleRectPadding?.width || 0;
  163. const height = data.steps[data.step].eleRectPadding?.height || 0;
  164. data.box = {
  165. left: eleRect.x - left + 'px',
  166. top: eleRect.y - top + 'px',
  167. width: eleRect.width + width + 'px',
  168. height: eleRect.height + height + 'px'
  169. };
  170. // console.log(`coai-${data.step}`, data.box);
  171. } else {
  172. handleNext();
  173. }
  174. };
  175. const onResetGuide = async (name: string) => {
  176. try {
  177. if (name !== 'attend-class') return;
  178. if (!guideInfo.value) {
  179. guideInfo.value = { attentGuide: false };
  180. } else {
  181. guideInfo.value.attentGuide = false;
  182. }
  183. try {
  184. await setGuidance({
  185. guideTag: 'teacher-guideInfo',
  186. guideValue: JSON.stringify(guideInfo.value)
  187. });
  188. } catch (e) {
  189. console.log(e);
  190. }
  191. data.step = 0;
  192. getStepELe();
  193. tipShow.value = true;
  194. } catch {
  195. //
  196. }
  197. };
  198. onMounted(() => {
  199. getStepELe();
  200. window.addEventListener('resize', resetSize);
  201. eventGlobal.on('teacher-guideInfo', (name: string) => onResetGuide(name));
  202. });
  203. const resetSize = () => {
  204. getStepELe();
  205. };
  206. onUnmounted(() => {
  207. window.removeEventListener('resize', resetSize);
  208. eventGlobal.off('teacher-guideInfo', onResetGuide);
  209. });
  210. const handleNext = () => {
  211. if (data.step >= 4) {
  212. endGuide();
  213. return;
  214. }
  215. data.step = data.step + 1;
  216. getStepELe();
  217. };
  218. const endGuide = async () => {
  219. if (!guideInfo.value) {
  220. guideInfo.value = { attentGuide: true };
  221. } else {
  222. guideInfo.value.attentGuide = true;
  223. }
  224. // localStorage.setItem('teacher-guideInfo', JSON.stringify(guideInfo));
  225. tipShow.value = false;
  226. try {
  227. const res = await setGuidance({
  228. guideTag: 'teacher-guideInfo',
  229. guideValue: JSON.stringify(guideInfo.value)
  230. });
  231. } catch (e) {
  232. console.log(e);
  233. }
  234. // localStorage.setItem('endC')
  235. };
  236. return () => (
  237. <>
  238. {tipShow.value ? (
  239. <div
  240. v-model:show={tipShow.value}
  241. class={['n-modal-mask', 'n-modal-mask-guide']}>
  242. <div class={styles.content} onClick={() => handleNext()}>
  243. <div
  244. class={styles.backBtn}
  245. onClick={(e: Event) => {
  246. e.stopPropagation();
  247. endGuide();
  248. }}>
  249. 跳过
  250. </div>
  251. <div
  252. class={styles.box}
  253. style={{ ...data.box, ...data.steps[data.step].boxStyle }}
  254. id={`modeType-${data.step}`}>
  255. {data.steps.map((item: any, index) => (
  256. <div
  257. onClick={(e: Event) => e.stopPropagation()}
  258. class={styles.item}
  259. style={
  260. item.type == 'bottom'
  261. ? {
  262. display: index === data.step ? '' : 'none',
  263. left: `${item.eleRect?.left}px`,
  264. top: `-${item.imgStyle?.height}`
  265. }
  266. : item.type == 'left'
  267. ? {
  268. display: index === data.step ? '' : 'none',
  269. left: `${item.eleRect?.left}px`,
  270. top: `${
  271. parseFloat(data.box?.height) / 2 -
  272. parseFloat(item.imgStyle?.height) / 20 +
  273. 14
  274. }px`
  275. }
  276. : {
  277. display: index === data.step ? '' : 'none',
  278. left: `${item.eleRect?.left}px`,
  279. top: `${data.box?.height}`
  280. }
  281. }>
  282. <img
  283. class={styles.img}
  284. style={item.imgStyle}
  285. src={item.img}
  286. />
  287. {/* <img
  288. class={styles.iconHead}
  289. style={item.handStyle}
  290. src={getImage('indexDot.png')}
  291. /> */}
  292. <div class={styles.btns} style={item.btnsStyle}>
  293. {data.step + 1 == data.steps.length ? (
  294. <>
  295. <div
  296. class={[styles.endBtn]}
  297. onClick={() => endGuide()}>
  298. 完成
  299. </div>
  300. <div
  301. class={styles.nextBtn}
  302. onClick={() => {
  303. data.step = 0;
  304. getStepELe();
  305. }}>
  306. 再看一遍
  307. </div>
  308. </>
  309. ) : (
  310. <div class={styles.btn} onClick={() => handleNext()}>
  311. 下一步 ({data.step + 1}/{data.steps.length})
  312. </div>
  313. )}
  314. </div>
  315. </div>
  316. ))}
  317. </div>
  318. </div>
  319. </div>
  320. ) : null}
  321. </>
  322. );
  323. }
  324. });