companion-teacher-register.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. import { areas } from '@/helpers/area'
  2. import request from '@/helpers/request'
  3. import {
  4. CellGroup,
  5. Form,
  6. Field,
  7. RadioGroup,
  8. Tag,
  9. Icon,
  10. Checkbox,
  11. Radio,
  12. Button,
  13. showToast,
  14. showDialog,
  15. showLoadingToast,
  16. closeToast,
  17. Picker,
  18. Popup,
  19. CountDown
  20. } from 'vant'
  21. import { defineComponent, onMounted, reactive } from 'vue'
  22. import { useRoute } from 'vue-router'
  23. import styles from './companion-teacher-register.module.less'
  24. import ImgCode from '@/components/o-img-code'
  25. import schoolLogo from './images/school-logo.png'
  26. import iconClose from './images/icon-close.png'
  27. import topBanner1 from './images/top-banner1.png'
  28. import { checkPhone } from '@/helpers/validate'
  29. import OUpload from '@/components/o-upload'
  30. import router from '@/router'
  31. export default defineComponent({
  32. name: 'companion-teacher-register',
  33. setup() {
  34. const route = useRoute()
  35. const state = reactive({
  36. showPicker: false,
  37. showSubject: false,
  38. submitStatus: false,
  39. showEducation: false,
  40. id: route.query.id,
  41. name: route.query.name,
  42. pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
  43. columns: [] as any,
  44. pickerType: null, // 下拉类型
  45. selectSubjects: [] as any, // 选中的声部
  46. forms: {
  47. realName: '',
  48. phone: null,
  49. gender: 1,
  50. idCardNo: null,
  51. cityCode: null,
  52. cityCodeName: '',
  53. provinceCode: null,
  54. showSubjectIds: '',
  55. subjectIds: [],
  56. smsValidCode: '',
  57. educationBackground: '', // 学历
  58. graduateSchool: null, // 毕业学校
  59. idcardFrontImg: '',
  60. idcardBackImg: '' // 身份证反面照
  61. },
  62. btnLoading: false,
  63. checkPhone: false,
  64. checked: true,
  65. columnSubject: [] as any,
  66. countDownStatus: true, // 是否发送验证码
  67. countDownTime: 120, // 倒计时时间
  68. countDownRef: null as any, // 倒计时实例
  69. imgCodeStatus: false
  70. })
  71. const onSubmit = async () => {
  72. if (!state.checked) {
  73. showToast('请阅读并同意协议')
  74. return
  75. }
  76. state.btnLoading = true
  77. try {
  78. const forms = state.forms
  79. await request.post('/api-school/open/schoolTeacherStudent/registerTeacher', {
  80. data: {
  81. ...forms,
  82. subjectIds: forms.subjectIds.join(','),
  83. schoolId: state.id
  84. }
  85. })
  86. state.submitStatus = true
  87. } catch {
  88. // showToast('保存失败,请重试')
  89. }
  90. state.btnLoading = false
  91. }
  92. const onConfirm = (val: any) => {
  93. const selectedOptions = val.selectedOptions[1]
  94. state.forms.cityCode = selectedOptions.code
  95. state.forms.cityCodeName = selectedOptions.name
  96. const selectedFirst = val.selectedOptions[0]
  97. state.forms.provinceCode = selectedFirst.code
  98. state.showPicker = false
  99. }
  100. const onSubjectRemove = (item: any, index: any) => {
  101. showDialog({
  102. title: '提示',
  103. message: '您是否删除选中的声部',
  104. confirmButtonColor: '#ff8057',
  105. showCancelButton: true
  106. }).then(() => {
  107. state.selectSubjects.splice(index, 1)
  108. const tempSubjectIds: any = []
  109. state.selectSubjects.forEach((subject: any) => {
  110. tempSubjectIds.push(subject.value)
  111. })
  112. state.forms.subjectIds = tempSubjectIds
  113. state.forms.showSubjectIds = tempSubjectIds.join(',')
  114. })
  115. }
  116. // 选择声部
  117. const onConfirmSubject = (val: any) => {
  118. const selected = val.selectedOptions[0]
  119. let isCheck = false
  120. state.selectSubjects.forEach((subject: any) => {
  121. if (subject.value === selected.value) {
  122. isCheck = true
  123. }
  124. })
  125. // 判断是否有选择一样的数据
  126. if (isCheck) {
  127. state.showSubject = false
  128. return
  129. }
  130. state.selectSubjects.push(val.selectedOptions[0])
  131. const tempSubjectIds: any = []
  132. state.selectSubjects.forEach((subject: any) => {
  133. tempSubjectIds.push(subject.value)
  134. })
  135. state.forms.subjectIds = tempSubjectIds
  136. state.forms.showSubjectIds = tempSubjectIds.join(',')
  137. state.showSubject = false
  138. }
  139. // 选择学历
  140. const onConfirmEduction = (val: any) => {
  141. const selectedOptions = val.selectedOptions[0]
  142. state.forms.educationBackground = selectedOptions.value
  143. state.showEducation = false
  144. }
  145. const onSendCode = () => {
  146. // 发送验证码
  147. if (!checkPhone(state.forms.phone as any)) {
  148. return showToast('请输入正确的手机号码')
  149. }
  150. state.imgCodeStatus = true
  151. }
  152. const onCodeSend = () => {
  153. state.countDownStatus = false
  154. const clearTimer = setInterval(() => {
  155. state.countDownTime = state.countDownTime - 1
  156. if (state.countDownTime <= 0) {
  157. state.countDownTime = 120
  158. state.countDownStatus = true
  159. clearInterval(clearTimer)
  160. }
  161. }, 1000)
  162. }
  163. const onFinished = () => {
  164. state.countDownStatus = true
  165. // ;(this.$refs.countDownRef as any).reset()
  166. }
  167. onMounted(async () => {
  168. if (!state.id) {
  169. showToast('信息获取失败,请联系老师')
  170. }
  171. try {
  172. const tempareas: any = []
  173. areas.forEach((item) => {
  174. const temp = {
  175. name: item.name,
  176. code: item.code,
  177. areas: [] as any
  178. }
  179. if (item.areas && item.areas.length > 0) {
  180. item.areas.forEach((child) => {
  181. temp.areas.push({
  182. name: child.name,
  183. code: child.code
  184. })
  185. })
  186. }
  187. tempareas.push(temp)
  188. })
  189. state.columns = tempareas || []
  190. const { data } = await request.post(
  191. '/api-school/open/orchestraSubjectConfig/pageByOrchestraId',
  192. {
  193. data: {
  194. page: 1,
  195. rows: 50
  196. }
  197. }
  198. )
  199. const rows = data.rows || []
  200. const tempSubjects: any = []
  201. rows.forEach((item: any) => {
  202. tempSubjects.push({
  203. text: item.name,
  204. value: item.subjectId
  205. })
  206. })
  207. state.columnSubject = tempSubjects
  208. } catch {
  209. showDialog({
  210. message: '信息获取失败,请联系老师',
  211. theme: 'round-button',
  212. confirmButtonColor: '#64A9FF'
  213. })
  214. }
  215. })
  216. const onPreview = () => {
  217. window.open(window.location.origin + '/#/preview-protocol', '_blank')
  218. }
  219. return () => (
  220. <div class={styles.register}>
  221. <div class={styles.title}>
  222. <p class={styles.tips}>
  223. <img src={schoolLogo} />
  224. <span>{state.name}</span>
  225. </p>
  226. </div>
  227. <Form validateFirst scrollToError onSubmit={onSubmit} ref="form" class={styles.form}>
  228. <CellGroup inset class={styles['cell-group']}>
  229. <Field
  230. required
  231. label="真实姓名"
  232. v-model={state.forms.realName}
  233. rules={[{ required: true, message: '请填写真实姓名' }]}
  234. name="realName"
  235. placeholder="请填写真实姓名"
  236. maxlength="50"
  237. ></Field>
  238. <Field
  239. required
  240. label="手机号码"
  241. v-model={state.forms.phone}
  242. rules={[
  243. { required: true, message: '请输入手机号码' },
  244. { pattern: state.pattern, message: '输入手机号码有误' }
  245. ]}
  246. name="phone"
  247. placeholder="请输入手机号码"
  248. maxlength={11}
  249. type="tel"
  250. ></Field>
  251. <Field
  252. required
  253. label="验证码"
  254. v-model={state.forms.smsValidCode}
  255. name="smsValidCode"
  256. rules={[{ required: true, message: '请输入验证码', trigger: 'onChange' }]}
  257. placeholder="请输入验证码"
  258. maxlength={6}
  259. type="tel"
  260. >
  261. {{
  262. button: () =>
  263. state.countDownStatus ? (
  264. <Button type="primary" round size="small" color="#ff8057" onClick={onSendCode}>
  265. 发送验证码
  266. </Button>
  267. ) : (
  268. <Button
  269. type="default"
  270. round
  271. size="small"
  272. disabled
  273. style={{ minWidth: '60px' }}
  274. onClick={onSendCode}
  275. >
  276. {state.countDownTime + 's'}
  277. </Button>
  278. )
  279. }}
  280. </Field>
  281. <div class={styles.phoneTips}>
  282. <Icon name="warning" size="16" />
  283. 提示:手机号码将成为您管乐团老师端登录账号
  284. </div>
  285. <Field
  286. required
  287. label="身份证号码"
  288. v-model={state.forms.idCardNo}
  289. rules={[
  290. { required: true, message: '请输入身份证号' },
  291. {
  292. pattern:
  293. /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
  294. message: '请输入正确的身份证号'
  295. }
  296. ]}
  297. name="idCardNo"
  298. maxlength={18}
  299. placeholder="请输入身份证号码"
  300. ></Field>
  301. <Field
  302. required
  303. label="性别"
  304. name="gender"
  305. rules={[{ required: true, message: '请选择性别' }]}
  306. >
  307. {{
  308. input: () => (
  309. <RadioGroup
  310. checked-color="#FF8057"
  311. v-model={state.forms.gender}
  312. direction="horizontal"
  313. >
  314. <Tag
  315. size="large"
  316. type="primary"
  317. plain={!(state.forms.gender === 1)}
  318. color="#FF8057"
  319. class={styles.radioSection}
  320. >
  321. <Radio class={styles.radioItem} name={1}></Radio>男
  322. </Tag>
  323. <Tag
  324. size="large"
  325. type="primary"
  326. plain={!(state.forms.gender === 0)}
  327. color="#FF8057"
  328. class={styles.radioSection}
  329. >
  330. <Radio class={styles.radioItem} name={0}></Radio>女
  331. </Tag>
  332. </RadioGroup>
  333. )
  334. }}
  335. </Field>
  336. <Field
  337. required
  338. label="身份证照片正面"
  339. v-model={state.forms.idcardFrontImg}
  340. readonly
  341. name="idcardFrontImg"
  342. rules={[{ required: true, message: '请上传身份证正面', trigger: 'onChange' }]}
  343. placeholder="请上传身份证正面"
  344. >
  345. {{
  346. input: () => (
  347. <OUpload
  348. style={{ width: '100%' }}
  349. tips="上传身份证正面"
  350. v-model:modelValue={state.forms.idcardFrontImg}
  351. />
  352. )
  353. }}
  354. </Field>
  355. <Field
  356. required
  357. label="身份证照片反面"
  358. v-model={state.forms.idcardBackImg}
  359. readonly
  360. name="idcardBackImg"
  361. rules={[{ required: true, message: '请上传身份证反面', trigger: 'onChange' }]}
  362. placeholder="请上传身份证反面"
  363. >
  364. {{
  365. input: () => (
  366. <OUpload
  367. style={{ width: '100%' }}
  368. tips="上传身份证反面"
  369. v-model:modelValue={state.forms.idcardBackImg}
  370. />
  371. )
  372. }}
  373. </Field>
  374. <Field
  375. required
  376. label="学历"
  377. v-model={state.forms.educationBackground}
  378. readonly
  379. name="educationBackground"
  380. onClick={() => (state.showEducation = true)}
  381. rules={[{ required: true, message: '请选择学历', trigger: 'onChange' }]}
  382. placeholder="请选择学历"
  383. >
  384. {{
  385. 'right-icon': () => (
  386. <Icon name="arrow" color={state.checkPhone ? '#aaa' : '#323233'} size="16"></Icon>
  387. )
  388. }}
  389. </Field>
  390. <Field
  391. required
  392. label="毕业学校"
  393. v-model={state.forms.graduateSchool}
  394. rules={[{ required: true, message: '请输入毕业学校' }]}
  395. name="graduateSchool"
  396. placeholder="请输入毕业学校"
  397. ></Field>
  398. <Field
  399. required
  400. label="所在城市"
  401. v-model={state.forms.cityCodeName}
  402. readonly
  403. name="cityCodeName"
  404. onClick={() => (state.showPicker = true)}
  405. rules={[{ required: true, message: '请选择所在城市', trigger: 'onChange' }]}
  406. placeholder="请选择所在城市"
  407. >
  408. {{
  409. 'right-icon': () => (
  410. <Icon name="arrow" color={state.checkPhone ? '#aaa' : '#323233'} size="16"></Icon>
  411. )
  412. }}
  413. </Field>
  414. <Field
  415. required
  416. label="声部(可多选)"
  417. v-model={state.forms.showSubjectIds}
  418. readonly
  419. name="showSubjectIds"
  420. onClick={() => (state.showSubject = true)}
  421. rules={[{ required: true, message: '请选择声部', trigger: 'onChange' }]}
  422. placeholder="请选择声部"
  423. >
  424. {{
  425. 'right-icon': () => (
  426. <Icon name="arrow" color={state.checkPhone ? '#aaa' : '#323233'} size="16"></Icon>
  427. ),
  428. input: () => (
  429. <>
  430. {state.forms.subjectIds.length <= 0 ? (
  431. <div class={styles.subjectPlaceholder} style="color:#c8c9cc">
  432. 请选择声部
  433. </div>
  434. ) : (
  435. ''
  436. )}
  437. {state.forms.subjectIds.length > 0 ? (
  438. <div>
  439. {state.selectSubjects.map((item: any, index: any) => (
  440. <Tag
  441. closeable
  442. size="medium"
  443. color="#FF8057"
  444. onClose={() => onSubjectRemove(item, index)}
  445. >
  446. {item.text}
  447. </Tag>
  448. ))}
  449. </div>
  450. ) : (
  451. ''
  452. )}
  453. </>
  454. )
  455. }}
  456. </Field>
  457. </CellGroup>
  458. <div class={styles.protocol}>
  459. <Checkbox
  460. v-model={state.checked}
  461. icon-size="16"
  462. style="margin-right: 6px"
  463. checked-color="#FF8057"
  464. ></Checkbox>
  465. <span
  466. onClick={() => {
  467. state.checked = !state.checked
  468. }}
  469. >
  470. 请认真阅读并勾选
  471. </span>
  472. <span class={styles.c} onClick={onPreview}>
  473. 《乐团伴学老师注册协议》
  474. </span>
  475. </div>
  476. <Button
  477. size="large"
  478. block
  479. round
  480. class={styles['btn-submit']}
  481. color="#FF8057"
  482. loading={state.btnLoading}
  483. native-type="submit"
  484. >
  485. 完成
  486. </Button>
  487. </Form>
  488. <Popup v-model:show={state.showPicker} position="bottom" round>
  489. <Picker
  490. showToolbar
  491. columns={state.columns}
  492. onCancel={() => (state.showPicker = false)}
  493. onConfirm={onConfirm}
  494. columnsFieldNames={{ text: 'name', value: 'code', children: 'areas' }}
  495. />
  496. </Popup>
  497. <Popup v-model:show={state.showSubject} position="bottom" round>
  498. <Picker
  499. showToolbar
  500. columns={state.columnSubject}
  501. onCancel={() => (state.showSubject = false)}
  502. onConfirm={onConfirmSubject}
  503. />
  504. </Popup>
  505. {/* 学历 */}
  506. {/* 学历分为专科、本科、硕士、博士、其他 */}
  507. <Popup v-model:show={state.showEducation} position="bottom" round>
  508. <Picker
  509. showToolbar
  510. columns={[
  511. { text: '专科', value: '专科' },
  512. { text: '本科', value: '本科' },
  513. { text: '硕士', value: '硕士' },
  514. { text: '博士', value: '博士' },
  515. { text: '其他', value: '其他' }
  516. ]}
  517. onCancel={() => (state.showEducation = false)}
  518. onConfirm={onConfirmEduction}
  519. />
  520. </Popup>
  521. <Popup v-model:show={state.submitStatus} round style="width: 75%" closeOnClickOverlay>
  522. <div class={styles.stautsS}>
  523. {/* <img
  524. class={styles['icon-close']}
  525. src={iconClose}
  526. onClick={() => {
  527. state.submitStatus = false
  528. window.location.href =
  529. window.location.origin + '/orchestra-student/#/download?type=teacher'
  530. }}
  531. /> */}
  532. <img src={topBanner1} class={styles['submit-img']} />
  533. <div class={styles['submit-container']}>
  534. <p class={styles['submit-title']}>恭喜您已成功登记为</p>
  535. <p class={styles['submit-o']}>
  536. {state.name} <br />
  537. <span>【伴学老师】</span>
  538. </p>
  539. <p class={styles['submit-tips']}>请下载管乐团老师端APP进行授课</p>
  540. <Button
  541. type="primary"
  542. color="#FF8057"
  543. block
  544. round
  545. onClick={() => {
  546. state.submitStatus = false
  547. window.location.href =
  548. window.location.origin + '/orchestra-student/#/download?type=teacher'
  549. }}
  550. >
  551. 立即下载
  552. </Button>
  553. </div>
  554. </div>
  555. </Popup>
  556. {state.imgCodeStatus ? (
  557. <ImgCode
  558. v-model:value={state.imgCodeStatus}
  559. phone={state.forms.phone as any}
  560. onClose={() => {
  561. state.imgCodeStatus = false
  562. }}
  563. onSendCode={onCodeSend}
  564. />
  565. ) : null}
  566. </div>
  567. )
  568. }
  569. })