index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import {
  2. computed,
  3. defineComponent,
  4. nextTick,
  5. onMounted,
  6. reactive,
  7. ref
  8. } from 'vue';
  9. import styles from './index.module.less';
  10. // import infoTitle from '../images/info-title.png';
  11. import {
  12. Button,
  13. CountDown,
  14. Field,
  15. Form,
  16. Picker,
  17. Popup,
  18. Radio,
  19. RadioGroup,
  20. Tag,
  21. showToast
  22. } from 'vant';
  23. import OWxTip from '@/components/m-wx-tip';
  24. import MProtocol from '@/components/m-protocol';
  25. import MImgCode from '@/components/m-img-code';
  26. import { browser, checkPhone } from '@/helpers/utils';
  27. import request from '@/helpers/request';
  28. import { useStudentRegisterStore } from '@/store/modules/student-register-store';
  29. import { setLoginInit, state } from '@/state';
  30. import iconGift from '../images/icon-gift.png';
  31. import { useRoute, useRouter } from 'vue-router';
  32. import MSticky from '@/components/m-sticky';
  33. import registerBgIcon from '../images/register-bg.png';
  34. import vipGiftIcon from '../images/vip-gift-icon.png';
  35. import agreeYes from '../images/agree-yes.png';
  36. import agreeNo from '../images/agree-no.png';
  37. import MMessageTip from '@/components/m-message-tip';
  38. const classList: any = [];
  39. for (let i = 1; i <= 40; i++) {
  40. classList.push({ text: i + '班', value: i });
  41. }
  42. export default defineComponent({
  43. name: 'register-new',
  44. emits: ['close', 'submit'],
  45. setup(props, { emit }) {
  46. const route = useRoute();
  47. const router = useRouter();
  48. const studentRegisterStore = useStudentRegisterStore();
  49. // 初始化学校编号
  50. studentRegisterStore.setShoolId(route.query.sId as any);
  51. const countDownRef = ref();
  52. const gradeList = computed(() => {
  53. let tempList: any = [];
  54. const five = [
  55. { text: '一年级', value: 1 },
  56. { text: '二年级', value: 2 },
  57. { text: '三年级', value: 3 },
  58. { text: '四年级', value: 4 },
  59. { text: '五年级', value: 5 }
  60. ];
  61. const one = [{ text: '六年级', value: 6 }];
  62. const three = [
  63. { text: '七年级', value: 7 },
  64. { text: '八年级', value: 8 },
  65. { text: '九年级', value: 9 }
  66. ];
  67. if (forms.gradeYear === 'FIVE_YEAR_SYSTEM') {
  68. tempList.push([...five]);
  69. } else if (forms.gradeYear === 'SIX_YEAR_SYSTEM') {
  70. tempList.push([...five, ...one]);
  71. } else if (forms.gradeYear === 'THREE_YEAR_SYSTEM') {
  72. tempList.push([...three]);
  73. } else if (forms.gradeYear === 'FORE_YEAR_SYSTEM') {
  74. tempList.push([...one, ...three]);
  75. } else {
  76. tempList.push([...five, ...one, ...three]);
  77. }
  78. return tempList;
  79. });
  80. const forms = reactive({
  81. countDownStatus: true,
  82. countDownTime: 1000 * 120, // 倒计时时间
  83. modelValue: false, // 是否选中协议
  84. imgCodeStatus: false,
  85. gradeNumText: '',
  86. currentClassText: '',
  87. gradeStatus: false,
  88. classStatus: false,
  89. loading: false,
  90. schoolId: route.query.sId as any,
  91. details: {} as any,
  92. gradeYear: null,
  93. schoolType: null,
  94. giftVipDay: null,
  95. showTips: false,
  96. showButton: false,
  97. showMessage: '请使用微信打开',
  98. showOtherSchool: false,
  99. showOtherMessage: ''
  100. });
  101. const isAgree = ref(false);
  102. const studentInfo = reactive({
  103. autoRegister: true,
  104. client_id: 'cooleshow-student',
  105. client_secret: 'cooleshow-student',
  106. extra: {
  107. nickname: '',
  108. currentGradeNum: '',
  109. currentClass: '',
  110. gender: 1,
  111. registerType: '', // 报名类型
  112. giftVipDay: 0, // 赠送会员天数
  113. clientType: 'TEACHER'
  114. },
  115. grant_type: 'password',
  116. loginType: 'SMS',
  117. password: '',
  118. username: ''
  119. });
  120. const onCodeSend = () => {
  121. forms.countDownStatus = false;
  122. nextTick(() => {
  123. countDownRef.value.start();
  124. });
  125. };
  126. const onSendCode = () => {
  127. // 发送验证码
  128. if (!checkPhone(studentInfo.username)) {
  129. return showToast('请输入正确的手机号码');
  130. }
  131. forms.imgCodeStatus = true;
  132. };
  133. const getUserInfos = async () => {
  134. if (
  135. studentInfo.password.length !== 6 ||
  136. !checkPhone(studentInfo.username)
  137. ) {
  138. return;
  139. }
  140. try {
  141. // 15907120131;
  142. const { data } = await request.get(
  143. `/edu-app/open/student/studentInfo?mobile=${studentInfo.username}&code=${studentInfo.password}&type=REGISTER`
  144. );
  145. if (data) {
  146. if (!studentInfo.extra.nickname) {
  147. studentInfo.extra.nickname = data.nickname;
  148. }
  149. if (!studentInfo.extra.currentGradeNum) {
  150. studentInfo.extra.currentGradeNum = data.currentGradeNum;
  151. }
  152. if (!studentInfo.extra.currentClass) {
  153. studentInfo.extra.currentClass = data.currentClass;
  154. }
  155. // if (!studentInfo.extra.gender) {
  156. studentInfo.extra.gender =
  157. studentInfo.extra.gender !== data.gender
  158. ? data.gender
  159. : studentInfo.extra.gender;
  160. // studentInfo.username = data.nickname;
  161. classList.forEach((i: any) => {
  162. if (i.value === data.currentClass) {
  163. forms.currentClassText = i.text;
  164. }
  165. });
  166. const tempGrade: any = gradeList.value[0] || [];
  167. tempGrade?.forEach((i: any) => {
  168. if (i.value === data.currentGradeNum) {
  169. forms.gradeNumText = i.text;
  170. }
  171. });
  172. }
  173. } catch {
  174. //
  175. }
  176. };
  177. const validatePhone = computed(() => {
  178. return checkPhone(studentInfo.username) ? true : false;
  179. });
  180. const onFinished = () => {
  181. forms.countDownStatus = true;
  182. countDownRef.value.reset();
  183. };
  184. const onSubmit = async (isRegister = false) => {
  185. try {
  186. if (checkForm()) return;
  187. forms.loading = true;
  188. if (!isRegister) {
  189. const checkSchool = await request.get(
  190. `/edu-app/open/student/studentSchool?mobile=${studentInfo.username}&code=${studentInfo.password}&type=REGISTER`
  191. );
  192. if (checkSchool.data?.schoolId !== forms.schoolId) {
  193. forms.showOtherSchool = true;
  194. forms.showOtherMessage = `您已绑定【${
  195. forms.details.name || ''
  196. }】,提交后将更换到【${
  197. checkSchool.data?.schoolName
  198. }】,是否确认提交?`;
  199. return;
  200. }
  201. }
  202. await request.get('/edu-app/open/student/schoolQuery', {
  203. params: {
  204. schoolId: forms.schoolId,
  205. mobile: studentInfo.username
  206. }
  207. });
  208. const { extra, ...res } = studentInfo;
  209. const result = await request.post('/edu-app/userlogin', {
  210. hideLoading: false,
  211. requestType: 'form',
  212. data: {
  213. ...res,
  214. extra: JSON.stringify({
  215. ...extra,
  216. schoolId: forms.schoolId
  217. })
  218. }
  219. });
  220. if (result.code === 5435 || result.code === 5436) {
  221. forms.showTips = true;
  222. forms.showMessage = '报名信息更新,请刷新后重新提交';
  223. forms.showButton = true;
  224. } else {
  225. setTimeout(() => {
  226. showToast('报名成功');
  227. router.push('/download');
  228. }, 100);
  229. }
  230. } catch {
  231. } finally {
  232. forms.loading = false;
  233. }
  234. };
  235. const checkForm = () => {
  236. if (!checkPhone(studentInfo.username)) {
  237. showToast('请输入正确的手机号码');
  238. return true;
  239. } else if (!studentInfo.password) {
  240. showToast('请输入验证码');
  241. return true;
  242. } else if (!studentInfo.extra.nickname) {
  243. showToast('请输入学生姓名');
  244. return true;
  245. } else if (!studentInfo.extra.currentGradeNum) {
  246. showToast('请选择所在年级');
  247. return true;
  248. } else if (!studentInfo.extra.currentClass) {
  249. showToast('请选择所在班级');
  250. return true;
  251. }
  252. return false;
  253. };
  254. const getRegisterGoods = async () => {
  255. try {
  256. const { data } = await request.get('/edu-app/open/school/detail', {
  257. params: {
  258. id: forms.schoolId
  259. },
  260. noAuthorization: true // 是否请求接口的时候添加toekn
  261. });
  262. forms.giftVipDay = data.giftVipDay;
  263. forms.schoolType = data.schoolType;
  264. forms.gradeYear = data.gradeYear;
  265. forms.details = data;
  266. studentInfo.extra.giftVipDay = data.giftVipDay;
  267. studentInfo.extra.registerType = data.registerType;
  268. if (browser().weixin) {
  269. if (data.status === 0) {
  270. forms.showTips = true;
  271. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  272. forms.showButton = false;
  273. }
  274. } else {
  275. forms.showTips = true;
  276. }
  277. } catch {}
  278. };
  279. onMounted(() => {
  280. getRegisterGoods();
  281. });
  282. return () => (
  283. <div class={styles.registerModal}>
  284. {forms.giftVipDay ? (
  285. <div class={styles.memberNumer}>
  286. <img src={iconGift} class={styles.iconGift} />
  287. <p>
  288. 现在报名立即赠送乐器AI学练工具有效期{' '}
  289. <span>{forms.giftVipDay}</span> 天
  290. </p>
  291. </div>
  292. ) : (
  293. ''
  294. )}
  295. <img class={styles.headBg} src={registerBgIcon} />
  296. <Form labelAlign="top" class={styles.registerForm}>
  297. <Field
  298. clearable={false}
  299. label="联系方式"
  300. placeholder="请输入手机号码"
  301. type="tel"
  302. autocomplete="off"
  303. class={styles.username}
  304. v-model={studentInfo.username}
  305. required
  306. input-align="right"
  307. maxlength={11}>
  308. {{
  309. label: () => (
  310. <div>
  311. 联系方式
  312. {/* (直接监护人) */}
  313. <p class={styles.tips}>(直接监护人)</p>
  314. </div>
  315. )
  316. }}
  317. </Field>
  318. <div class={[styles.fieldTipsGroup]}>
  319. <div class={[styles.fieldTips]}>
  320. 手机号是音乐数字课堂的唯一登录账户
  321. </div>
  322. </div>
  323. <Field
  324. center
  325. clearable={false}
  326. label="验证码"
  327. placeholder="请输入验证码"
  328. autocomplete="off"
  329. type="number"
  330. v-model={studentInfo.password}
  331. required
  332. input-align="right"
  333. maxlength={6}
  334. onUpdate:modelValue={(val: any) => {
  335. getUserInfos();
  336. }}>
  337. {{
  338. button: () =>
  339. forms.countDownStatus ? (
  340. <span
  341. class={[
  342. styles.codeText,
  343. !validatePhone.value ? styles.codeTextDisabled : ''
  344. ]}
  345. onClick={onSendCode}>
  346. 获取验证码
  347. </span>
  348. ) : (
  349. <CountDown
  350. ref={(el: any) => (countDownRef.value = el)}
  351. auto-start={false}
  352. class={styles.countDown}
  353. time={forms.countDownTime}
  354. onFinish={onFinished}
  355. format="ss秒后重试"
  356. />
  357. )
  358. }}
  359. </Field>
  360. <Field
  361. clearable={false}
  362. label="学生姓名"
  363. placeholder="请输入学生姓名"
  364. autocomplete="off"
  365. maxlength={14}
  366. v-model={studentInfo.extra.nickname}
  367. required
  368. input-align="right"
  369. />
  370. <Field
  371. clearable={false}
  372. label="学生性别"
  373. placeholder="请选择性别"
  374. autocomplete="off"
  375. required
  376. input-align="right"
  377. // v-model={studentInfo.extra.nickname}
  378. >
  379. {{
  380. input: () => (
  381. <RadioGroup
  382. checked-color="#ffcb75"
  383. v-model={studentInfo.extra.gender}
  384. direction="horizontal">
  385. <Tag
  386. size="large"
  387. type="primary"
  388. color={
  389. !(studentInfo.extra.gender === 1)
  390. ? '#F5F6FA'
  391. : 'linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)'
  392. }
  393. textColor={
  394. !(studentInfo.extra.gender === 1) ? '#626264' : '#fff'
  395. }
  396. class={styles.radioSection}
  397. round>
  398. <Radio class={styles.radioItem} name={1}></Radio>男
  399. </Tag>
  400. <Tag
  401. size="large"
  402. type="primary"
  403. color={
  404. !(studentInfo.extra.gender === 0)
  405. ? '#F5F6FA'
  406. : 'linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)'
  407. }
  408. textColor={
  409. !(studentInfo.extra.gender === 0) ? '#626264' : '#fff'
  410. }
  411. class={styles.radioSection}
  412. round>
  413. <Radio class={styles.radioItem} name={0}></Radio>女
  414. </Tag>
  415. </RadioGroup>
  416. )
  417. }}
  418. </Field>
  419. <Field
  420. clearable={false}
  421. label="所在年级"
  422. placeholder="请选择年级"
  423. isLink
  424. readonly
  425. clickable={false}
  426. modelValue={forms.gradeNumText}
  427. onClick={() => (forms.gradeStatus = true)}
  428. required
  429. input-align="right"
  430. />
  431. <Field
  432. clearable={false}
  433. label="所在班级"
  434. placeholder="请选择班级"
  435. isLink
  436. readonly
  437. clickable={false}
  438. modelValue={forms.currentClassText}
  439. onClick={() => (forms.classStatus = true)}
  440. required
  441. input-align="right"
  442. />
  443. {studentInfo.extra.registerType === 'GIFT_VIP_DAY' &&
  444. forms.giftVipDay &&
  445. Number(forms.giftVipDay) > 0 ? (
  446. <div class={styles.giftTips}>
  447. <img src={vipGiftIcon} />
  448. <span>
  449. 注册成功即可获得乐器AI学练工具<i>{forms.giftVipDay}</i>天有效期
  450. </span>
  451. </div>
  452. ) : null}
  453. </Form>
  454. {/* <div class={styles.agreeColumn}>
  455. <img src={isAgree.value ? agreeYes : agreeNo} onClick={() => (isAgree.value = !isAgree.value)} />
  456. <p onClick={(e: MouseEvent) => {
  457. e.stopPropagation();
  458. router.push('/preview-protocol');
  459. }}>我已阅读并同意<i>《音乐数字课堂学生端》</i>注册协议</p>
  460. </div> */}
  461. {/* <MProtocol
  462. center
  463. v-model:modelValue={forms.modelValue}
  464. prototcolType="REGISTER"
  465. /> */}
  466. <MSticky position="bottom">
  467. <Button
  468. type="primary"
  469. class={styles.submitBtn}
  470. color="linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)"
  471. block
  472. onClick={() => onSubmit()}
  473. disabled={forms.loading}
  474. loading={forms.loading}>
  475. 确认
  476. </Button>
  477. </MSticky>
  478. {forms.imgCodeStatus ? (
  479. <MImgCode
  480. v-model:value={forms.imgCodeStatus}
  481. phone={studentInfo.username}
  482. type="REGISTER"
  483. onClose={() => {
  484. forms.imgCodeStatus = false;
  485. }}
  486. onSendCode={onCodeSend}
  487. />
  488. ) : null}
  489. {/* 年级 */}
  490. <Popup
  491. v-model:show={forms.gradeStatus}
  492. position="bottom"
  493. round
  494. safeAreaInsetBottom
  495. lazyRender={false}
  496. class={'popupBottomSearch'}>
  497. <Picker
  498. showToolbar
  499. columns={gradeList.value as any}
  500. onCancel={() => (forms.gradeStatus = false)}
  501. onConfirm={(val: any) => {
  502. const selectedOption = val.selectedOptions[0];
  503. studentInfo.extra.currentGradeNum = selectedOption.value;
  504. forms.gradeNumText = selectedOption.text;
  505. forms.gradeStatus = false;
  506. }}
  507. />
  508. </Popup>
  509. {/* 班级 */}
  510. <Popup
  511. v-model:show={forms.classStatus}
  512. position="bottom"
  513. round
  514. class={'popupBottomSearch'}>
  515. <Picker
  516. showToolbar
  517. columns={classList}
  518. onCancel={() => (forms.classStatus = false)}
  519. onConfirm={(val: any) => {
  520. const selectedOption = val.selectedOptions[0];
  521. studentInfo.extra.currentClass = selectedOption.value;
  522. forms.currentClassText = selectedOption.text;
  523. forms.classStatus = false;
  524. }}
  525. />
  526. </Popup>
  527. {/* 是否在微信中打开 */}
  528. <OWxTip
  529. v-model:show={forms.showTips}
  530. message={forms.showMessage}
  531. showButton={forms.showButton}
  532. buttonText="刷新"
  533. onConfirm={async () => {
  534. forms.showTips = false;
  535. await getRegisterGoods();
  536. studentInfo.password = '';
  537. window.scrollTo({
  538. top: 0,
  539. behavior: 'smooth'
  540. });
  541. }}
  542. />
  543. <MMessageTip
  544. show={forms.showOtherSchool}
  545. messageAlign="left"
  546. message={forms.showOtherMessage}
  547. showCancelButton
  548. onCancel={() => (forms.showOtherSchool = false)}
  549. onConfirm={() => {
  550. forms.showOtherSchool = false;
  551. // 直接注册
  552. onSubmit(true);
  553. }}
  554. />
  555. </div>
  556. );
  557. }
  558. });