index.tsx 15 KB

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