index.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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. },
  110. grant_type: 'password',
  111. loginType: 'SMS',
  112. password: '',
  113. username: ''
  114. });
  115. const onCodeSend = () => {
  116. forms.countDownStatus = false;
  117. nextTick(() => {
  118. countDownRef.value.start();
  119. });
  120. };
  121. const onSendCode = () => {
  122. // 发送验证码
  123. if (!checkPhone(studentInfo.username)) {
  124. return showToast('请输入正确的手机号码');
  125. }
  126. forms.imgCodeStatus = true;
  127. };
  128. const validatePhone = computed(() => {
  129. return checkPhone(studentInfo.username) ? true : false;
  130. });
  131. const onFinished = () => {
  132. forms.countDownStatus = true;
  133. countDownRef.value.reset();
  134. };
  135. const onSubmit = async () => {
  136. try {
  137. if (checkForm()) return;
  138. forms.loading = true;
  139. await request.get('/edu-app/open/student/schoolQuery', {
  140. params: {
  141. schoolId: forms.schoolId,
  142. mobile: studentInfo.username
  143. }
  144. });
  145. const { extra, ...res } = studentInfo;
  146. const result = await request.post('/edu-app/userlogin', {
  147. hideLoading: false,
  148. requestType: 'form',
  149. data: {
  150. ...res,
  151. extra: JSON.stringify({
  152. ...extra,
  153. schoolId: forms.schoolId
  154. })
  155. }
  156. });
  157. if (result.code === 5436) {
  158. forms.showTips = true;
  159. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  160. forms.showButton = false;
  161. } else if (result.code === 5435) {
  162. forms.showTips = true;
  163. forms.showMessage = '报名信息更新,请刷新后重新提交';
  164. forms.showButton = true;
  165. } else {
  166. setTimeout(() => {
  167. showToast('报名成功');
  168. router.push('/download');
  169. }, 100);
  170. }
  171. } catch {
  172. } finally {
  173. forms.loading = false;
  174. }
  175. };
  176. const checkForm = () => {
  177. if (!checkPhone(studentInfo.username)) {
  178. showToast('请输入正确的手机号码');
  179. return true;
  180. } else if (!studentInfo.password) {
  181. showToast('请输入验证码');
  182. return true;
  183. } else if (!studentInfo.extra.nickname) {
  184. showToast('请输入学生姓名');
  185. return true;
  186. } else if (!studentInfo.extra.currentGradeNum) {
  187. showToast('请选择所在年级');
  188. return true;
  189. } else if (!studentInfo.extra.currentClass) {
  190. showToast('请选择所在班级');
  191. return true;
  192. }
  193. return false;
  194. };
  195. const getRegisterGoods = async () => {
  196. try {
  197. const { data } = await request.get('/edu-app/open/school/detail', {
  198. params: {
  199. id: forms.schoolId
  200. },
  201. noAuthorization: true // 是否请求接口的时候添加toekn
  202. });
  203. forms.giftVipDay = data.giftVipDay;
  204. forms.schoolType = data.schoolType;
  205. forms.gradeYear = data.gradeYear;
  206. studentInfo.extra.giftVipDay = data.giftVipDay;
  207. studentInfo.extra.registerType = data.registerType;
  208. if (browser().weixin) {
  209. if (data.registerType !== 'GIFT_VIP_DAY' || data.status === 0) {
  210. forms.showTips = true;
  211. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  212. forms.showButton = false;
  213. }
  214. } else {
  215. forms.showTips = true;
  216. }
  217. } catch {}
  218. };
  219. onMounted(() => {
  220. getRegisterGoods();
  221. });
  222. return () => (
  223. <div class={styles.registerModal}>
  224. {forms.giftVipDay ? (
  225. <div class={styles.memberNumer}>
  226. <img src={iconGift} class={styles.iconGift} />
  227. <p>
  228. 现在报名立即赠送乐器AI学练工具有效期{' '}
  229. <span>{forms.giftVipDay}</span> 天
  230. </p>
  231. </div>
  232. ) : (
  233. ''
  234. )}
  235. <img class={styles.headBg} src={registerBgIcon} />
  236. <Form labelAlign="top" class={styles.registerForm}>
  237. <Field
  238. clearable
  239. label="联系方式"
  240. placeholder="请输入手机号码"
  241. type="tel"
  242. autocomplete="off"
  243. class={styles.username}
  244. v-model={studentInfo.username}
  245. required
  246. input-align="right"
  247. maxlength={11}>
  248. {{
  249. label: () => (
  250. <div>
  251. 联系方式
  252. {/* (直接监护人) */}
  253. <p class={styles.tips}>(直接监护人)</p>
  254. </div>
  255. )
  256. }}
  257. </Field>
  258. <div class={['van-hairline--bottom', styles.fieldTipsGroup]}>
  259. <div class={[styles.fieldTips]}>
  260. 手机号是音乐数字课堂的唯一登录账户
  261. </div>
  262. </div>
  263. <Field
  264. center
  265. clearable
  266. label="验证码"
  267. placeholder="请输入验证码"
  268. autocomplete="off"
  269. type="number"
  270. v-model={studentInfo.password}
  271. required
  272. input-align="right"
  273. maxlength={6}>
  274. {{
  275. button: () =>
  276. forms.countDownStatus ? (
  277. <span
  278. class={[
  279. styles.codeText,
  280. !validatePhone.value ? styles.codeTextDisabled : ''
  281. ]}
  282. onClick={onSendCode}>
  283. 获取验证码
  284. </span>
  285. ) : (
  286. <CountDown
  287. ref={(el: any) => (countDownRef.value = el)}
  288. auto-start={false}
  289. time={forms.countDownTime}
  290. onFinish={onFinished}
  291. format="ss秒"
  292. />
  293. )
  294. }}
  295. </Field>
  296. <Field
  297. clearable
  298. label="学生姓名"
  299. placeholder="请输入学生姓名"
  300. autocomplete="off"
  301. maxlength={14}
  302. v-model={studentInfo.extra.nickname}
  303. required
  304. input-align="right"
  305. />
  306. <Field
  307. clearable
  308. label="学生性别"
  309. placeholder="请选择性别"
  310. autocomplete="off"
  311. required
  312. input-align="right"
  313. // v-model={studentInfo.extra.nickname}
  314. >
  315. {{
  316. input: () => (
  317. <RadioGroup
  318. checked-color="#ffcb75"
  319. v-model={studentInfo.extra.gender}
  320. direction="horizontal">
  321. <Tag
  322. size="large"
  323. type="primary"
  324. color={
  325. !(studentInfo.extra.gender === 1) ? '#F5F6FA' : 'linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)'
  326. }
  327. textColor={
  328. !(studentInfo.extra.gender === 1) ? '#626264' : '#fff'
  329. }
  330. class={styles.radioSection}
  331. round>
  332. <Radio class={styles.radioItem} name={1}></Radio>男
  333. </Tag>
  334. <Tag
  335. size="large"
  336. type="primary"
  337. color={
  338. !(studentInfo.extra.gender === 0) ? '#F5F6FA' : 'linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)'
  339. }
  340. textColor={
  341. !(studentInfo.extra.gender === 0) ? '#626264' : '#fff'
  342. }
  343. class={styles.radioSection}
  344. round>
  345. <Radio class={styles.radioItem} name={0}></Radio>女
  346. </Tag>
  347. </RadioGroup>
  348. )
  349. }}
  350. </Field>
  351. <Field
  352. clearable
  353. label="所在年级"
  354. placeholder="请选择年级"
  355. isLink
  356. readonly
  357. clickable={false}
  358. modelValue={forms.gradeNumText}
  359. onClick={() => (forms.gradeStatus = true)}
  360. required
  361. input-align="right"
  362. />
  363. <Field
  364. clearable
  365. label="所在班级"
  366. placeholder="请选择班级"
  367. isLink
  368. readonly
  369. clickable={false}
  370. modelValue={forms.currentClassText}
  371. onClick={() => (forms.classStatus = true)}
  372. required
  373. input-align="right"
  374. />
  375. <div class={styles.giftTips}>
  376. <img src={vipGiftIcon} />
  377. <span>注册成功即可获得乐器AI学练工具<i>{forms.giftVipDay}</i>天有效期</span>
  378. </div>
  379. </Form>
  380. {/* <div class={styles.agreeColumn}>
  381. <img src={isAgree.value ? agreeYes : agreeNo} onClick={() => (isAgree.value = !isAgree.value)} />
  382. <p onClick={(e: MouseEvent) => {
  383. e.stopPropagation();
  384. router.push('/preview-protocol');
  385. }}>我已阅读并同意<i>《音乐数字课堂学生端》</i>注册协议</p>
  386. </div> */}
  387. {/* <MProtocol
  388. center
  389. v-model:modelValue={forms.modelValue}
  390. prototcolType="REGISTER"
  391. /> */}
  392. <MSticky position="bottom">
  393. <Button
  394. type="primary"
  395. class={styles.submitBtn}
  396. color="linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)"
  397. block
  398. onClick={onSubmit}
  399. disabled={forms.loading}
  400. loading={forms.loading}>
  401. 确认
  402. </Button>
  403. </MSticky>
  404. {forms.imgCodeStatus ? (
  405. <MImgCode
  406. v-model:value={forms.imgCodeStatus}
  407. phone={studentInfo.username}
  408. type="REGISTER"
  409. onClose={() => {
  410. forms.imgCodeStatus = false;
  411. }}
  412. onSendCode={onCodeSend}
  413. />
  414. ) : null}
  415. {/* 年级 */}
  416. <Popup
  417. v-model:show={forms.gradeStatus}
  418. position="bottom"
  419. round
  420. safeAreaInsetBottom
  421. lazyRender={false}
  422. class={'popupBottomSearch'}>
  423. <Picker
  424. showToolbar
  425. columns={gradeList.value as any}
  426. onCancel={() => (forms.gradeStatus = false)}
  427. onConfirm={(val: any) => {
  428. const selectedOption = val.selectedOptions[0];
  429. studentInfo.extra.currentGradeNum = selectedOption.value;
  430. forms.gradeNumText = selectedOption.text;
  431. forms.gradeStatus = false;
  432. }}
  433. />
  434. </Popup>
  435. {/* 班级 */}
  436. <Popup
  437. v-model:show={forms.classStatus}
  438. position="bottom"
  439. round
  440. class={'popupBottomSearch'}>
  441. <Picker
  442. showToolbar
  443. columns={classList}
  444. onCancel={() => (forms.classStatus = false)}
  445. onConfirm={(val: any) => {
  446. const selectedOption = val.selectedOptions[0];
  447. studentInfo.extra.currentClass = selectedOption.value;
  448. forms.currentClassText = selectedOption.text;
  449. forms.classStatus = false;
  450. }}
  451. />
  452. </Popup>
  453. {/* 是否在微信中打开 */}
  454. <OWxTip
  455. v-model:show={forms.showTips}
  456. message={forms.showMessage}
  457. showButton={forms.showButton}
  458. buttonText="刷新"
  459. onConfirm={async () => {
  460. forms.showTips = false;
  461. await getRegisterGoods();
  462. studentInfo.password = '';
  463. window.scrollTo({
  464. top: 0,
  465. behavior: 'smooth'
  466. });
  467. }}
  468. />
  469. </div>
  470. );
  471. }
  472. });