manage-teacher-register.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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 './manage-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-banner.png'
  28. import { checkPhone } from '@/helpers/validate'
  29. import dayjs from 'dayjs'
  30. import { browser, getUrlCode } from '@/helpers/utils'
  31. import OUpload from '@/components/o-upload'
  32. import qs from 'query-string'
  33. export default defineComponent({
  34. name: 'companion-teacher-register',
  35. setup() {
  36. const route = useRoute()
  37. const state = reactive({
  38. showPicker: false,
  39. showSubject: false,
  40. submitStatus: false,
  41. id: route.query.id,
  42. name: route.query.name,
  43. t: route.query.t as any, // 过期时间
  44. qrCodeStatus: false, // 二维码是否失效
  45. qrCodeMessage: '',
  46. pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
  47. columns: [] as any,
  48. pickerType: null, // 下拉类型
  49. forms: {
  50. realName: '',
  51. phone: null,
  52. gender: 1,
  53. idCardNo: null,
  54. smsValidCode: '',
  55. idcardFrontImg: '',
  56. idcardBackImg: '' // 身份证反面照
  57. },
  58. btnLoading: false,
  59. checkPhone: false,
  60. checked: true,
  61. columnSubject: [] as any,
  62. countDownStatus: true, // 是否发送验证码
  63. countDownTime: 120, // 倒计时时间
  64. // countDownRef: null as any, // 倒计时实例
  65. imgCodeStatus: false,
  66. showPopup: false,
  67. code: ''
  68. })
  69. const onSubmit = async () => {
  70. if (state.qrCodeStatus) {
  71. showDialog({
  72. title: '提示',
  73. message: state.qrCodeMessage,
  74. theme: 'round-button',
  75. confirmButtonColor: '#ff8057'
  76. })
  77. return
  78. }
  79. if (!state.checked) {
  80. showToast('请阅读并同意协议')
  81. return
  82. }
  83. state.btnLoading = true
  84. try {
  85. const forms = state.forms
  86. await request.post('/api-school/open/schoolStaff/registerTeacher', {
  87. data: {
  88. ...forms,
  89. schoolId: state.id
  90. }
  91. })
  92. state.submitStatus = true
  93. } catch {
  94. //
  95. }
  96. state.btnLoading = false
  97. }
  98. const onSendCode = () => {
  99. // 发送验证码
  100. if (!checkPhone(state.forms.phone as any)) {
  101. return showToast('请输入正确的手机号码')
  102. }
  103. state.imgCodeStatus = true
  104. }
  105. const onCodeSend = () => {
  106. state.countDownStatus = false
  107. const clearTimer = setInterval(() => {
  108. state.countDownTime = state.countDownTime - 1
  109. if (state.countDownTime <= 0) {
  110. state.countDownTime = 120
  111. state.countDownStatus = true
  112. clearInterval(clearTimer)
  113. }
  114. }, 1000)
  115. }
  116. const onFinished = () => {
  117. state.countDownStatus = true
  118. // ;(this.$refs.countDownRef as any).reset()
  119. }
  120. onMounted(async () => {
  121. if (!state.id) {
  122. showToast('信息获取失败,请联系老师')
  123. }
  124. // 判断是否是微信,只能微信中打开
  125. if (!browser().weixin) {
  126. state.showPopup = true
  127. return
  128. } else {
  129. //授权
  130. const code = getUrlCode()
  131. if (!code) {
  132. const newUrl =
  133. window.location.origin +
  134. window.location.pathname +
  135. '#' +
  136. route.path +
  137. '?' +
  138. qs.stringify({
  139. ...route.query
  140. })
  141. getAppIdAndCode(newUrl)
  142. return
  143. } else {
  144. state.code = code
  145. }
  146. }
  147. // t: route.query.t, // 过期时间
  148. try {
  149. const res = await request.post('/api-school/open/schoolTeacherStudent/queryQrCodeStatus', {
  150. data: {
  151. schoolId: state.id,
  152. qrCodeEffectiveStartTime: state.t
  153. ? dayjs(Number(state.t)).format('YYYY-MM-DD HH:mm:ss')
  154. : null
  155. }
  156. })
  157. if (res.code === 999) {
  158. showDialog({
  159. title: '提示',
  160. message: res.message,
  161. theme: 'round-button',
  162. confirmButtonColor: '#FF8057'
  163. })
  164. state.qrCodeStatus = true
  165. state.qrCodeMessage = res.message
  166. }
  167. } catch (e: any) {
  168. //
  169. console.log(e)
  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. orchestraId: state.id,
  195. page: 1,
  196. rows: 50
  197. }
  198. }
  199. )
  200. const rows = data.rows || []
  201. const tempSubjects: any = []
  202. rows.forEach((item) => {
  203. tempSubjects.push({
  204. text: item.name,
  205. value: item.subjectId
  206. })
  207. })
  208. state.columnSubject = tempSubjects
  209. } catch {
  210. showDialog({
  211. message: '信息获取失败,请联系老师',
  212. theme: 'round-button',
  213. confirmButtonColor: '#FF8057'
  214. })
  215. }
  216. })
  217. const getAppIdAndCode = async (url?: string) => {
  218. try {
  219. const { data } = await request.get('/api-school/open/paramConfig/wechatAppId')
  220. // 判断是否有微信appId
  221. if (data) {
  222. closeToast()
  223. goAuth(data, url)
  224. }
  225. } catch {
  226. //
  227. }
  228. }
  229. const goAuth = (wxAppId: string, urlString?: string) => {
  230. // 用户授权
  231. console.log(urlString || window.location.href, 'urlString || window.location.href')
  232. const urlNow = encodeURIComponent(urlString || window.location.href)
  233. console.log(urlNow, 'urlNow')
  234. const scope = 'snsapi_base' //snsapi_userinfo //静默授权 用户无感知
  235. const appid = wxAppId || 'wx8654c671631cfade'
  236. const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${urlNow}&response_type=code&scope=${scope}&state=STATE&connect_redirect=1#wechat_redirect`
  237. window.location.replace(url)
  238. }
  239. const onPreview = (type: string) => {
  240. if (type === 'REGISTER') {
  241. window.open(
  242. window.location.origin + window.location.pathname + '#/preview-protocol',
  243. '_blank'
  244. )
  245. } else if (type === 'PRIVACY') {
  246. window.open(
  247. window.location.origin + window.location.pathname + '#/privacyProtocol',
  248. '_blank'
  249. )
  250. } else if (type === 'WITHDRAW') {
  251. //
  252. window.open(window.location.origin + window.location.pathname + '#/cashProtocol', '_blank')
  253. }
  254. }
  255. return () => (
  256. <div class={styles.register}>
  257. <div class={styles.title}>
  258. <p class={styles.tips}>
  259. <img src={schoolLogo} />
  260. <span>{state.name}</span>
  261. </p>
  262. </div>
  263. <Form validateFirst scrollToError onSubmit={onSubmit} ref="form" class={styles.form}>
  264. <CellGroup inset class={styles['cell-group']}>
  265. <Field
  266. label="真实姓名"
  267. v-model={state.forms.realName}
  268. rules={[{ required: true, message: '请填写真实姓名' }]}
  269. name="realName"
  270. placeholder="请填写真实姓名"
  271. maxlength="5"
  272. ></Field>
  273. <Field
  274. label="手机号码"
  275. v-model={state.forms.phone}
  276. rules={[
  277. { required: true, message: '请输入手机号码' },
  278. { pattern: state.pattern, message: '输入手机号码有误' }
  279. ]}
  280. name="phone"
  281. placeholder="请输入手机号码"
  282. maxlength={11}
  283. type="tel"
  284. ></Field>
  285. <div class={styles.phoneTips}>
  286. <Icon name="warning" size="16" />
  287. 提示:手机号码将成为您管乐团管理端登录账号
  288. </div>
  289. <Field
  290. label="身份证号码"
  291. v-model={state.forms.idCardNo}
  292. rules={[
  293. { required: true, message: '请输入身份证号' },
  294. {
  295. pattern:
  296. /^[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]$/,
  297. message: '请输入正确的身份证号'
  298. }
  299. ]}
  300. name="idCardNo"
  301. placeholder="请输入身份证号码"
  302. ></Field>
  303. <div class={[styles.fieldGroup, 'van-hairline--bottom']}>
  304. <Field
  305. label="身份证照片正面"
  306. v-model={state.forms.idcardFrontImg}
  307. readonly
  308. border={false}
  309. name="idcardFrontImg"
  310. rules={[{ required: true, message: '请上传身份证正面', trigger: 'onChange' }]}
  311. placeholder="请上传身份证正面"
  312. >
  313. {{
  314. input: () => (
  315. <OUpload
  316. style={{ width: '100%' }}
  317. tips="上传身份证正面"
  318. v-model:modelValue={state.forms.idcardFrontImg}
  319. />
  320. )
  321. }}
  322. </Field>
  323. <Field
  324. label={'上传身份证反面'}
  325. labelClass={styles.fieldTitle}
  326. v-model={state.forms.idcardBackImg}
  327. readonly
  328. border={false}
  329. name="idcardBackImg"
  330. rules={[{ required: true, message: '请上传身份证反面', trigger: 'onChange' }]}
  331. placeholder="请上传身份证反面"
  332. >
  333. {{
  334. input: () => (
  335. <OUpload
  336. style={{ width: '100%' }}
  337. tips="上传身份证反面"
  338. v-model:modelValue={state.forms.idcardBackImg}
  339. />
  340. )
  341. }}
  342. </Field>
  343. </div>
  344. <Field label="性别" name="gender" rules={[{ required: true, message: '请选择性别' }]}>
  345. {{
  346. input: () => (
  347. <RadioGroup
  348. checked-color="#FF8057"
  349. v-model={state.forms.gender}
  350. direction="horizontal"
  351. >
  352. <Tag
  353. size="large"
  354. type="primary"
  355. color={!(state.forms.gender === 1) ? '#EAEAEA' : '#FF8057'}
  356. textColor={!(state.forms.gender === 1) ? '#AAA' : '#FFF'}
  357. class={styles.radioSection}
  358. round
  359. >
  360. <Radio class={styles.radioItem} name={1}></Radio>男
  361. </Tag>
  362. <Tag
  363. size="large"
  364. type="primary"
  365. color={!(state.forms.gender === 0) ? '#EAEAEA' : '#FF8057'}
  366. textColor={!(state.forms.gender === 0) ? '#AAA' : '#FFF'}
  367. class={styles.radioSection}
  368. round
  369. >
  370. <Radio class={styles.radioItem} name={0}></Radio>女
  371. </Tag>
  372. </RadioGroup>
  373. )
  374. }}
  375. </Field>
  376. <Field
  377. label="验证码"
  378. v-model={state.forms.smsValidCode}
  379. name="smsValidCode"
  380. rules={[{ required: true, message: '请输入验证码', trigger: 'onChange' }]}
  381. placeholder="请输入验证码"
  382. maxlength={6}
  383. type="tel"
  384. >
  385. {{
  386. button: () =>
  387. state.countDownStatus ? (
  388. <Button type="primary" round size="small" color="#FF8057" onClick={onSendCode}>
  389. 发送验证码
  390. </Button>
  391. ) : (
  392. <Button
  393. type="default"
  394. round
  395. size="small"
  396. disabled
  397. style={{ minWidth: '60px' }}
  398. onClick={onSendCode}
  399. >
  400. {state.countDownTime + 's'}
  401. </Button>
  402. )
  403. }}
  404. </Field>
  405. </CellGroup>
  406. <div class={styles.protocol}>
  407. <Checkbox
  408. v-model={state.checked}
  409. icon-size="16"
  410. style="margin-right: 6px"
  411. checked-color="#FF8057"
  412. ></Checkbox>
  413. <div>
  414. <span
  415. onClick={() => {
  416. state.checked = !state.checked
  417. }}
  418. >
  419. 请认真阅读并勾选
  420. </span>
  421. <span class={styles.c} onClick={() => onPreview('REGISTER')}>
  422. 《管乐团用户注册协议》
  423. </span>
  424. <span class={styles.c} onClick={() => onPreview('PRIVACY')}>
  425. 《隐私协议》
  426. </span>
  427. <span class={styles.c} onClick={() => onPreview('WITHDRAW')}>
  428. 《共享经济平台注册经营者协议》
  429. </span>
  430. </div>
  431. </div>
  432. <Button
  433. size="large"
  434. block
  435. round
  436. class={styles['btn-submit']}
  437. color="#FF8057"
  438. loading={state.btnLoading}
  439. native-type="submit"
  440. >
  441. 完成
  442. </Button>
  443. </Form>
  444. <Popup v-model:show={state.submitStatus} round style="width: 75%" closeOnClickOverlay>
  445. <div class={styles.stautsS}>
  446. <img src={topBanner1} class={styles['submit-img']} />
  447. <div class={styles['submit-container']}>
  448. <p class={styles['submit-title']}>恭喜您已成功登记为</p>
  449. <p class={styles['submit-o']}>
  450. {state.name} <br />
  451. <span>【管理老师】</span>
  452. </p>
  453. <p class={styles['submit-tips']}>请下载管乐团管理端APP</p>
  454. <Button
  455. type="primary"
  456. color="#FF8057"
  457. block
  458. round
  459. onClick={() => {
  460. state.submitStatus = false
  461. window.location.href =
  462. window.location.origin + '/orchestra-student/#/download?type=manage'
  463. }}
  464. >
  465. 立即下载
  466. </Button>
  467. </div>
  468. </div>
  469. </Popup>
  470. {state.imgCodeStatus ? (
  471. <ImgCode
  472. v-model:value={state.imgCodeStatus}
  473. phone={state.forms.phone as any}
  474. onClose={() => {
  475. state.imgCodeStatus = false
  476. }}
  477. onSendCode={onCodeSend}
  478. />
  479. ) : null}
  480. <Popup
  481. v-model:show={state.showPopup}
  482. round
  483. style={{ width: '88%' }}
  484. closeOnClickOverlay={false}
  485. class={styles.wxPopupDialog}
  486. >
  487. <div class={styles.popupContainer}>
  488. <p class={styles.title1}>温馨提示</p>
  489. <p class={styles.popupTips}>请使用微信打开</p>
  490. </div>
  491. </Popup>
  492. </div>
  493. )
  494. }
  495. })