index.tsx 17 KB

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