home-guide.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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 { getGuidance, setGuidance } from './api';
  15. export default defineComponent({
  16. name: 'coai-guide',
  17. emits: ['close'],
  18. setup(props, { emit }) {
  19. const data = reactive({
  20. box: {
  21. height: '0px'
  22. } as any,
  23. show: false,
  24. /**
  25. *
  26. width: px2vw(840),
  27. height: px2vw(295)
  28. */
  29. steps: [
  30. {
  31. ele: '',
  32. eleRect: {} as DOMRect,
  33. img: getImage('home1.png'),
  34. handStyle: {
  35. top: '0.91rem'
  36. },
  37. imgStyle: {
  38. top: px2vw(-400 / 2),
  39. left: px2vw(-734 / 2),
  40. width: px2vw(734),
  41. height: px2vw(295)
  42. },
  43. btnsStyle: {
  44. bottom: px2vw(240),
  45. left: px2vw(-128)
  46. },
  47. boxStyle: {
  48. border: 'none',
  49. width: 0,
  50. height: 0,
  51. backgroundColor: 'transparent',
  52. position: 'fixed',
  53. top: `50%`,
  54. left: '50%'
  55. // visibility: 'hidden'
  56. },
  57. eleRectPadding: {
  58. left: 0,
  59. top: 0,
  60. width: 0,
  61. height: 0
  62. }
  63. },
  64. {
  65. ele: '',
  66. img: getImage('home2.png'),
  67. imgStyle: {
  68. top: '100%',
  69. left: px2vw(-290),
  70. width: px2vw(401),
  71. height: px2vw(227)
  72. },
  73. btnsStyle: {
  74. bottom: '30px',
  75. left: px2vw(-130)
  76. },
  77. boxStyle: {},
  78. eleRectPadding: {
  79. left: 7,
  80. top: 7,
  81. width: 14,
  82. height: 14
  83. }
  84. },
  85. {
  86. ele: '',
  87. img: getImage('home6.png'),
  88. imgStyle: {
  89. top: '100%',
  90. left: px2vw(-310),
  91. width: px2vw(477),
  92. height: px2vw(227)
  93. },
  94. btnsStyle: {
  95. bottom: '30px',
  96. left: px2vw(-150)
  97. },
  98. boxStyle: {},
  99. eleRectPadding: {
  100. left: 7,
  101. top: 7,
  102. width: 14,
  103. height: 14
  104. }
  105. },
  106. {
  107. ele: '',
  108. img: getImage('home4.png'),
  109. imgStyle: {
  110. top: '100%',
  111. left: px2vw(-310),
  112. width: px2vw(477),
  113. height: px2vw(227)
  114. },
  115. btnsStyle: {
  116. bottom: '30px',
  117. left: px2vw(-150)
  118. },
  119. boxStyle: {},
  120. eleRectPadding: {
  121. left: 7,
  122. top: 7,
  123. width: 14,
  124. height: 14
  125. }
  126. },
  127. {
  128. ele: '',
  129. img: getImage('home3.png'),
  130. handStyle: {
  131. top: '-1.39rem',
  132. left: '0.17rem',
  133. transform: 'rotate(180deg)'
  134. },
  135. imgStyle: {
  136. top: px2vw(-4),
  137. width: px2vw(454),
  138. height: px2vw(227),
  139. left: px2vw(-150)
  140. },
  141. btnsStyle: {
  142. bottom: '30px',
  143. left: px2vw(8)
  144. },
  145. boxStyle: {
  146. borderRadius: '30px'
  147. },
  148. eleRectPadding: {
  149. left: 7,
  150. top: 9,
  151. width: 14,
  152. height: 14
  153. }
  154. },
  155. {
  156. ele: '',
  157. img: getImage('home5.png'),
  158. handStyle: {
  159. top: '-1.39rem',
  160. left: '0.17rem',
  161. transform: 'rotate(180deg)'
  162. },
  163. imgStyle: {
  164. top: px2vw(-194),
  165. width: px2vw(621),
  166. height: px2vw(223),
  167. left: px2vw(-624)
  168. },
  169. btnsStyle: {
  170. top: px2vw(-42),
  171. left: px2vw(-460)
  172. },
  173. boxStyle: {},
  174. eleRectPadding: {
  175. left: 7,
  176. top: 7,
  177. width: 14,
  178. height: 14
  179. }
  180. }
  181. ],
  182. step: 0
  183. });
  184. const tipShow = ref(false);
  185. // const res = setGuidance({guideTag:'teacher-guideInfo',guideValue:'{}'})
  186. const guideInfo = ref({} as any);
  187. const getAllGuidance = async () => {
  188. try {
  189. const res = await getGuidance({ guideTag: 'teacher-guideInfo' });
  190. if (res.data) {
  191. guideInfo.value = JSON.parse(res.data?.guideValue) || null;
  192. } else {
  193. guideInfo.value = {};
  194. }
  195. if (guideInfo.value && guideInfo.value.homeGuide) {
  196. tipShow.value = false;
  197. } else {
  198. tipShow.value = true;
  199. }
  200. } catch (e) {
  201. console.log(e);
  202. }
  203. // const guideInfo = localStorage.getItem('teacher-guideInfo');
  204. };
  205. getAllGuidance();
  206. const getStepELe = () => {
  207. const ele: HTMLElement = document.getElementById(
  208. data.step === data.steps.length - 1
  209. ? 'moveNPopover'
  210. : `home-${data.step}`
  211. )!;
  212. // console.log(`coai-${data.step}`, data.steps[data.step].eleRectPadding);
  213. if (ele) {
  214. const eleRect = ele.getBoundingClientRect();
  215. const left = data.steps[data.step].eleRectPadding?.left || 0;
  216. const top = data.steps[data.step].eleRectPadding?.top || 0;
  217. const width = data.steps[data.step].eleRectPadding?.width || 0;
  218. const height = data.steps[data.step].eleRectPadding?.height || 0;
  219. data.box = {
  220. left: eleRect.x - left + 'px',
  221. top: eleRect.y - top + 'px',
  222. width: eleRect.width + width + 'px',
  223. height: eleRect.height + height + 'px'
  224. };
  225. } else {
  226. handleNext();
  227. }
  228. };
  229. const onResetGuide = async (name: string) => {
  230. try {
  231. if (name !== 'Home') return;
  232. if (!guideInfo.value) {
  233. guideInfo.value = { homeGuide: false };
  234. } else {
  235. guideInfo.value.homeGuide = false;
  236. }
  237. try {
  238. await setGuidance({
  239. guideTag: 'teacher-guideInfo',
  240. guideValue: JSON.stringify(guideInfo.value)
  241. });
  242. } catch (e) {
  243. console.log(e);
  244. }
  245. data.step = 0;
  246. getStepELe();
  247. tipShow.value = true;
  248. } catch {
  249. //
  250. }
  251. };
  252. onMounted(() => {
  253. getStepELe();
  254. window.addEventListener('resize', resetSize);
  255. eventGlobal.on('teacher-guideInfo', (name: string) => onResetGuide(name));
  256. });
  257. const resetSize = () => {
  258. getStepELe();
  259. };
  260. onUnmounted(() => {
  261. window.removeEventListener('resize', resetSize);
  262. eventGlobal.off('teacher-guideInfo', onResetGuide);
  263. });
  264. const handleNext = () => {
  265. if (data.step >= data.steps.length - 1) {
  266. endGuide();
  267. return;
  268. }
  269. data.step = data.step + 1;
  270. getStepELe();
  271. };
  272. const endGuide = async () => {
  273. // let guideInfo =
  274. // JSON.parse(localStorage.getItem('teacher-guideInfo') || '{}') || null;
  275. if (!guideInfo.value) {
  276. guideInfo.value = { homeGuide: true };
  277. } else {
  278. guideInfo.value.homeGuide = true;
  279. }
  280. try {
  281. const res = await setGuidance({
  282. guideTag: 'teacher-guideInfo',
  283. guideValue: JSON.stringify(guideInfo.value)
  284. });
  285. } catch (e) {
  286. console.log(e);
  287. }
  288. // localStorage.setItem('teacher-guideInfo', JSON.stringify(guideInfo));
  289. tipShow.value = false;
  290. // localStorage.setItem('endC')
  291. };
  292. return () => (
  293. <>
  294. {tipShow.value ? (
  295. <div
  296. v-model:show={tipShow.value}
  297. class={['n-modal-mask', 'n-modal-mask-guide']}>
  298. <div class={styles.content} onClick={() => handleNext()}>
  299. <div
  300. class={styles.backBtn}
  301. onClick={(e: Event) => {
  302. e.stopPropagation();
  303. endGuide();
  304. }}>
  305. 跳过
  306. </div>
  307. <div
  308. class={styles.box}
  309. style={{ ...data.box, ...data.steps[data.step].boxStyle }}
  310. id={`modeType-${data.step}`}>
  311. {data.steps.map((item: any, index) => (
  312. <div
  313. onClick={(e: Event) => e.stopPropagation()}
  314. class={styles.item}
  315. style={
  316. item.type == 'bottom'
  317. ? {
  318. display: index === data.step ? '' : 'none',
  319. left: `${item.eleRect?.left}px`,
  320. top: `-${item.imgStyle?.height}`
  321. }
  322. : {
  323. display: index === data.step ? '' : 'none',
  324. left: `${item.eleRect?.left}px`,
  325. top: `${data.box?.height}`
  326. }
  327. }>
  328. <img
  329. class={styles.img}
  330. style={item.imgStyle}
  331. src={item.img}
  332. />
  333. {/* <img
  334. class={styles.iconHead}
  335. style={item.handStyle}
  336. src={getImage('indexDot.png')}
  337. /> */}
  338. <div class={styles.btns} style={item.btnsStyle}>
  339. {data.step + 1 == data.steps.length ? (
  340. <>
  341. <div
  342. class={[styles.endBtn]}
  343. onClick={() => endGuide()}>
  344. 完成
  345. </div>
  346. <div
  347. class={styles.nextBtn}
  348. onClick={() => {
  349. data.step = 0;
  350. getStepELe();
  351. }}>
  352. 再看一遍
  353. </div>
  354. </>
  355. ) : (
  356. <div class={styles.btn} onClick={() => handleNext()}>
  357. {data.step !== 0
  358. ? `下一步 ${data.step}/${data.steps.length - 1}`
  359. : '开始体验'}
  360. </div>
  361. )}
  362. </div>
  363. </div>
  364. ))}
  365. </div>
  366. </div>
  367. </div>
  368. ) : null}
  369. </>
  370. );
  371. }
  372. });