index.tsx 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418
  1. import {
  2. Image,
  3. Cell,
  4. Tag,
  5. Button,
  6. Popup,
  7. showToast,
  8. Form,
  9. Field,
  10. CountDown,
  11. RadioGroup,
  12. Radio,
  13. Picker,
  14. closeToast
  15. } from 'vant';
  16. import {
  17. computed,
  18. defineComponent,
  19. nextTick,
  20. onMounted,
  21. onUnmounted,
  22. reactive,
  23. ref,
  24. watch
  25. } from 'vue';
  26. import TCPlayer from 'tcplayer.js';
  27. import 'tcplayer.js/dist/tcplayer.css';
  28. import qs from 'query-string';
  29. import {
  30. state as baseState,
  31. goWechatAuth,
  32. setLogin,
  33. setLoginInit
  34. } from '@/state';
  35. import styles from './index.module.less';
  36. import MSticky from '@/components/m-sticky';
  37. // import MVideo from '@/components/m-video';
  38. import { useRoute, useRouter } from 'vue-router';
  39. import { useStudentRegisterStore } from '@/store/modules/student-register-store';
  40. import request from '@/helpers/request';
  41. import requestStudent from './request';
  42. import { browser, checkPhone, getUrlCode, moneyFormat } from '@/helpers/utils';
  43. import deepClone from '@/helpers/deep-clone';
  44. import OWxTip from '@/components/m-wx-tip';
  45. import MDialog from '@/components/m-dialog';
  46. // import f1 from './images/new/f-1.png';
  47. // import f2 from './images/new/f-2.png';
  48. // import f3 from './images/new/f-3.png';
  49. // import iconTip2 from './images/new/icon-tip2.png';
  50. // import functionBg from './images/new/function-bg.png';
  51. // import tuangou from './images/new/tuangou.png';
  52. import icon3 from './images/new/icon-3.png';
  53. // import icon5 from './images/new/icon-7.png';
  54. // import icon6 from './images/new/icon-6.png';
  55. import giftTip from './images/new/icon-4.png';
  56. import iconGift from './images/new/icon-gift.png';
  57. import icon10 from './images/new/icon-n-10.png';
  58. import icon11 from './images/new/icon-n-11.png';
  59. import dayjs from 'dayjs';
  60. // import MMessageTip from '@/components/m-message-tip';
  61. import { CurrentTime, useCountDown, usePageVisibility } from '@vant/use';
  62. import Payment from '../adapay/payment';
  63. import QrcodePayment from './qrcode-payment';
  64. import MImgCode from '@/components/m-img-code';
  65. import { beforeSubmit } from './order-state';
  66. import { useInterval, useIntervalFn } from '@vueuse/core';
  67. import MPopup from '@/components/m-popup';
  68. import UserAuth from './component/user-auth';
  69. import MMessageTip from '@/components/m-message-tip';
  70. import SelectStudent from './modal/select-student';
  71. const classList: any = [];
  72. for (let i = 1; i <= 40; i++) {
  73. classList.push({ text: i + '班', value: i });
  74. }
  75. const GRADE_ENUM = {
  76. '1': '一年级',
  77. '2': '二年级',
  78. '3': '三年级',
  79. '4': '四年级',
  80. '5': '五年级',
  81. '6': '六年级',
  82. '7': '七年级',
  83. '8': '八年级',
  84. '9': '九年级'
  85. } as any;
  86. const getGradeList = (gradeYear: string, instrumentCode?: string) => {
  87. let tempList: any = [];
  88. const five = [
  89. { text: '一年级', value: 1, instrumentCode },
  90. { text: '二年级', value: 2, instrumentCode },
  91. { text: '三年级', value: 3, instrumentCode },
  92. { text: '四年级', value: 4, instrumentCode },
  93. { text: '五年级', value: 5, instrumentCode }
  94. ];
  95. const one = [{ text: '六年级', value: 6, instrumentCode }];
  96. const three = [
  97. { text: '七年级', value: 7, instrumentCode },
  98. { text: '八年级', value: 8, instrumentCode },
  99. { text: '九年级', value: 9, instrumentCode }
  100. ];
  101. if (gradeYear === 'FIVE_YEAR_SYSTEM') {
  102. tempList.push(...[...five]);
  103. } else if (gradeYear === 'SIX_YEAR_SYSTEM') {
  104. tempList.push(...[...five, ...one]);
  105. } else if (gradeYear === 'THREE_YEAR_SYSTEM') {
  106. tempList.push(...[...three]);
  107. } else if (gradeYear === 'FORE_YEAR_SYSTEM') {
  108. tempList.push(...[...one, ...three]);
  109. } else {
  110. tempList.push(...[...five, ...one, ...three]);
  111. }
  112. return tempList;
  113. };
  114. export default defineComponent({
  115. name: 'student-register',
  116. setup() {
  117. const route = useRoute();
  118. const pageVisibility = usePageVisibility();
  119. const studentRegisterStore = useStudentRegisterStore();
  120. const router = useRouter();
  121. // 初始化学校编号
  122. studentRegisterStore.setShoolId(route.query.sId as any);
  123. const countDownRef = ref();
  124. const mstickyRef = ref();
  125. const forms = reactive({
  126. schoolId: route.query.sId as any,
  127. paymentType: '', // 支付类型
  128. paymentChannel: '',
  129. multi_user_limit: 1, // 限制注册学生数量
  130. // popupShow: false,
  131. registerDetails: {} as any,
  132. details: [] as any,
  133. // schoolType: '', // 学校类型
  134. gradeYear: '', // 学制
  135. schoolInstrumentSetType: null as any,
  136. // bugGoods: false, // 是否购买AI
  137. isRegister: 'create' as 'create' | 'update' | '', // 是否注册学生
  138. isTipRegister: false, // 是否显示名字不一致 - 默认显示
  139. isChangeSchool: false, // 是否切换学校
  140. registerType: '', // 报名类型
  141. detailVip: {} as any,
  142. giftVipDay: 0, // 赠送天数
  143. submitLoading: false,
  144. // showMore: true,
  145. showTips: false,
  146. showButton: false,
  147. showMessage: '请使用微信扫描二维码',
  148. countDownStatus: true,
  149. countDownTime: 1000 * 120, // 倒计时时间
  150. // modelValue: false, // 是否选中协议
  151. imgCodeStatus: false,
  152. gradeNumText: '',
  153. currentClassText: '',
  154. gradeStatus: false,
  155. classStatus: false,
  156. loading: false,
  157. dialogStatus: false,
  158. dialogMessage: '',
  159. confirmButtonText: '确定',
  160. cancelButtonText: '取消',
  161. messageAlign: 'center' as 'left' | 'center' | 'right',
  162. dialogConfirmStatus: false,
  163. contract_sign: false, // 是否实名认证
  164. countDownTimePay: 60 * 1000,
  165. dialogConfig: {} as any,
  166. showSelectStudent: false, // 选择学生
  167. studentList: [], // 手机号关联学生列表
  168. studentItem: {} as any, // 选择的学生
  169. joinType: '' as 'digitalize' | 'tradition',
  170. gradeList: [] as any,
  171. classList: [] as any,
  172. saveUserId: null as any,
  173. saveId: null as any,
  174. openId: null as any,
  175. code: null as any,
  176. intervalFnRef: null as any, // 页面订时器
  177. registerExpireTime: null as any, // 结束时间
  178. instrumentCode: null as any, // 乐器编码
  179. activeOverTime: 0, // 活动结束时间
  180. activeOverStatus: true, // 活动是否结束 默认已结束
  181. gradePopupShow: false,
  182. gradePopupIndex: [] as any, // 年级下拉索引
  183. classPopupShow: false,
  184. classPopupIndex: [] as any // 班级下拉索引
  185. });
  186. const otherParams = reactive({
  187. showOtherSchool: false,
  188. showCloseButton: true, // 是否显示关闭按钮
  189. showOtherMessage: '',
  190. /** limit 超限制,change 更换学生,nickname 名称不一致 */
  191. otherType: '' as 'limit' | 'change' | 'nickname' | 'member',
  192. showCancelButton: true,
  193. cancelButtonColor: '',
  194. cancelButtonText: '取消',
  195. showConfirmButton: true,
  196. confirmButtonColor: '',
  197. confirmButtonText: '确定',
  198. messageAlign: 'left' as 'center' | 'left' | 'right'
  199. });
  200. const state = reactive({
  201. showQrcode: false,
  202. qrCodeUrl: '',
  203. pay_channel: '',
  204. orderInfo: {} as any, // 订单信息
  205. authShow: false,
  206. orderNo: null as any,
  207. config: {} as any,
  208. paymentStatus: false,
  209. orderTimer: null as any
  210. });
  211. const studentInfo = reactive({
  212. autoRegister: true,
  213. multiUser: true, // 是否为多用户
  214. client_id: 'cooleshow-student',
  215. client_secret: 'cooleshow-student',
  216. extra: {
  217. nickname: '',
  218. currentGradeNum: '' as any,
  219. currentClass: '' as any,
  220. gender: 1 as any,
  221. registerType: null as any, // 报名类型
  222. giftVipDay: 0 // 赠送会员天数
  223. },
  224. grant_type: 'password',
  225. loginType: 'SMS',
  226. password: '',
  227. username: ''
  228. });
  229. const videoForms = reactive({
  230. introductionVideo: '',
  231. introductionVideoTime: 0, // 视频总时长
  232. videoBrowsePoint: 0, // 视频最后观看点
  233. player: null as any,
  234. playerSpeed: 1,
  235. intervalFnRef: null as any,
  236. videoDetails: [] as any, // 节点列表
  237. pointVideo: {} as any, // 需要处理有效的时间段
  238. pointVideoTime: 0 // 有效时长
  239. });
  240. // 播放视频总时长
  241. const videoIntervalRef = useInterval(1000, { controls: true });
  242. videoIntervalRef.pause();
  243. // 页面定时
  244. const pageTimer = useInterval(1000, { controls: true });
  245. pageTimer.pause();
  246. /**
  247. * 格式化视屏播放有效时间 - 合并区间
  248. * @param intervals [[], []]
  249. * @example [[4, 8],[0, 4],[10, 30]]
  250. * @returns [[0, 8], [10, 30]]
  251. */
  252. const formatEffectiveTime = (intervals: any[]) => {
  253. const res: any = [];
  254. intervals.sort((a, b) => a[0] - b[0]);
  255. let prev = intervals[0];
  256. for (let i = 1; i < intervals.length; i++) {
  257. const cur = intervals[i];
  258. if (prev[1] >= cur[0]) {
  259. // 有重合
  260. prev[1] = Math.max(cur[1], prev[1]);
  261. } else {
  262. // 不重合,prev推入res数组
  263. res.push(prev);
  264. prev = cur; // 更新 prev
  265. }
  266. }
  267. res.push(prev);
  268. return res;
  269. };
  270. /**
  271. * 获取数据有效期
  272. * @param intervals [[], []]
  273. * @returns 0s
  274. */
  275. const formatTimer = (intervals: any[]) => {
  276. const afterIntervals = formatEffectiveTime(intervals);
  277. // console.log(afterIntervals, 'afterIntervals')
  278. let time = 0;
  279. afterIntervals.forEach((t: any) => {
  280. time += t[1] - t[0];
  281. });
  282. return time;
  283. };
  284. const overCountDown = useCountDown({
  285. time: forms.activeOverTime,
  286. onFinish() {
  287. forms.activeOverStatus = true;
  288. if (forms.submitLoading) return;
  289. applyOver();
  290. }
  291. });
  292. /** 报名结束提示 */
  293. const applyOver = () => {
  294. forms.showTips = true;
  295. forms.showMessage = `<p style="color: #F44541">报名已截止,感谢您的参与</p>`;
  296. forms.showButton = false;
  297. forms.intervalFnRef?.pause();
  298. };
  299. const onCodeSend = () => {
  300. forms.countDownStatus = false;
  301. nextTick(() => {
  302. countDownRef.value.start();
  303. });
  304. };
  305. const onSendCode = () => {
  306. // 发送验证码
  307. if (!checkPhone(studentInfo.username)) {
  308. return showToast('请输入正确的手机号码');
  309. }
  310. forms.imgCodeStatus = true;
  311. };
  312. const validatePhone = computed(() => {
  313. return checkPhone(studentInfo.username) ? true : false;
  314. });
  315. const onFinished = () => {
  316. forms.countDownStatus = true;
  317. countDownRef.value.reset();
  318. };
  319. const orderType = computed(() => {
  320. return state.orderInfo.orderType;
  321. });
  322. const getRegisterGoods = async () => {
  323. try {
  324. const { data } = await request.get(
  325. '/edu-app/open/userOrder/registerGoods/' + forms.schoolId,
  326. {
  327. noAuthorization: true // 是否请求接口的时候添加toekn
  328. }
  329. );
  330. // 默认选中商品
  331. studentRegisterStore.setVip(data.details || []);
  332. forms.details = deepClone(data.details || []);
  333. forms.registerDetails = data;
  334. forms.registerExpireTime = data.registerExpireTime; // '2024-03-27 17:33:52'; //
  335. if (forms.registerExpireTime) {
  336. if (dayjs(new Date()).isBefore(forms.registerExpireTime)) {
  337. // 活动没有结束
  338. forms.activeOverStatus = false;
  339. // 默认返回毫秒
  340. forms.activeOverTime = dayjs(forms.registerExpireTime).diff(
  341. dayjs(new Date())
  342. );
  343. overCountDown.reset(forms.activeOverTime);
  344. overCountDown.start();
  345. } else {
  346. applyOver();
  347. forms.activeOverStatus = true;
  348. }
  349. }
  350. if (forms.details.length > 0) {
  351. forms.detailVip = forms.details[0];
  352. // forms.giftVipDay = forms.details[0].membershipDays;
  353. }
  354. forms.giftVipDay = data.giftVipDay || 0;
  355. forms.gradeYear = data.gradeYear;
  356. forms.schoolInstrumentSetType = data.schoolInstrumentSetType;
  357. forms.registerType = data.registerType;
  358. studentInfo.extra.registerType = data.registerType;
  359. const schoolInstrumentList = data.schoolInstrumentList || [];
  360. if (data.schoolInstrumentSetType === 'SCHOOL') {
  361. const instrumentCode = schoolInstrumentList[0]?.instrumentCode;
  362. forms.gradeList = getGradeList(data.gradeYear, instrumentCode);
  363. forms.classList = classList;
  364. } else if (data.schoolInstrumentSetType === 'GRADE') {
  365. schoolInstrumentList.forEach((item: any) => {
  366. forms.gradeList.push({
  367. text: GRADE_ENUM[item.gradeNum],
  368. value: item.gradeNum,
  369. instrumentId: item.instrumentId,
  370. instrumentCode: item.instrumentCode
  371. });
  372. });
  373. forms.gradeList.sort((a: any, b: any) => a.value - b.value);
  374. forms.classList = classList;
  375. } else if (data.schoolInstrumentSetType === 'CLASS') {
  376. // 班级
  377. const tempGradeList: any[] = [];
  378. schoolInstrumentList.forEach((item: any) => {
  379. if (!tempGradeList.includes(item.gradeNum)) {
  380. tempGradeList.push(item.gradeNum);
  381. }
  382. });
  383. const lastGradeList: any[] = [];
  384. tempGradeList.forEach((temp: any) => {
  385. const list = {
  386. text: GRADE_ENUM[temp],
  387. value: temp,
  388. instrumentId: '',
  389. instrumentCode: '',
  390. instrumentName: '',
  391. classList: [] as any
  392. };
  393. schoolInstrumentList.forEach((item: any) => {
  394. if (temp === item.gradeNum) {
  395. list.instrumentId = item.instrumentId;
  396. list.instrumentCode = item.instrumentCode;
  397. list.instrumentName = item.instrumentName;
  398. list.classList.push({
  399. text: item.classNum + '班',
  400. value: item.classNum,
  401. instrumentCode: item.instrumentCode
  402. });
  403. }
  404. });
  405. // 排序班级
  406. list.classList.sort((a: any, b: any) => a.value - b.value);
  407. lastGradeList.push(list);
  408. });
  409. lastGradeList.sort((a: any, b: any) => a.value - b.value);
  410. forms.gradeList = lastGradeList;
  411. forms.classList = [];
  412. } else {
  413. forms.gradeList = getGradeList(data.gradeYear);
  414. forms.classList = classList;
  415. }
  416. if (browser().weixin) {
  417. // if (
  418. // data.schoolStatus === 0 &&
  419. // forms.schoolId == '1770035687490105346'
  420. // ) {
  421. // forms.showTips = true;
  422. // forms.showMessage = `<p style="color: #F44541">报名已截止,感谢您的参与</p>`;
  423. // forms.showButton = false;
  424. // return;
  425. // }
  426. if (data.registerType !== 'BUG_GOODS' || data.schoolStatus === 0) {
  427. forms.showTips = true;
  428. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  429. forms.showButton = false;
  430. return;
  431. }
  432. } else {
  433. forms.showTips = true;
  434. return;
  435. }
  436. // 判断是否有倒计时,倒计时是滞结束
  437. if (!forms.registerExpireTime || !forms.activeOverStatus) {
  438. pagePointInit();
  439. }
  440. } catch {}
  441. };
  442. // 计算金额
  443. const calcPrice = computed(() => {
  444. let amount: number = 0; //现价
  445. let originAmount: number = 0; // 原价
  446. const vipList: any[] = studentRegisterStore.getVip;
  447. vipList.forEach((vip: any) => {
  448. amount += Number(vip.currentPrice);
  449. originAmount += Number(vip.originalPrice);
  450. });
  451. // const goodsList: any[] = studentRegisterStore.getGoods;
  452. // goodsList.forEach((good: any) => {
  453. // amount += Number(good.price) * good.quantity;
  454. // originAmount += Number(good.originalPrice) * good.quantity;
  455. // });
  456. return {
  457. amount,
  458. originAmount
  459. };
  460. });
  461. // 格式化提示状态
  462. const changeTipStatus = (register: boolean, school: boolean) => {
  463. forms.isTipRegister = register;
  464. forms.isChangeSchool = school;
  465. };
  466. const checkForm = (status = true) => {
  467. if (!checkPhone(studentInfo.username)) {
  468. status && showToast('请输入正确的手机号码');
  469. return true;
  470. } else if (!studentInfo.password) {
  471. status && showToast('请输入验证码');
  472. return true;
  473. } else if (!studentInfo.extra.nickname) {
  474. status && showToast('请输入学生姓名');
  475. return true;
  476. } else if (![0, 1].includes(studentInfo.extra.gender)) {
  477. status && showToast('请选择性别');
  478. return true;
  479. } else if (!studentInfo.extra.currentGradeNum) {
  480. status && showToast('请选择所在年级');
  481. return true;
  482. } else if (!studentInfo.extra.currentClass) {
  483. status && showToast('请选择所在班级');
  484. return true;
  485. }
  486. return false;
  487. };
  488. //
  489. const checkSubmit = () => {
  490. const { extra } = studentInfo;
  491. // console.log(
  492. // forms.studentItem.nickname,
  493. // extra.nickname,
  494. // forms.isRegister,
  495. // forms.isTipRegister,
  496. // 'isRegister'
  497. // );
  498. if (
  499. forms.studentItem.nickname !== extra.nickname &&
  500. forms.isTipRegister
  501. ) {
  502. otherParams.showOtherMessage =
  503. '学生姓名与上次提交信息不一致,请确认修改学生信息或创建新的学生账号';
  504. otherParams.showOtherSchool = true;
  505. otherParams.showCancelButton = true;
  506. otherParams.showCloseButton = true;
  507. otherParams.cancelButtonColor =
  508. 'linear-gradient( 224deg, #3FE1E6 0%, #00CDD4 100%)';
  509. otherParams.cancelButtonText = '新建学生';
  510. otherParams.confirmButtonColor =
  511. 'linear-gradient( 305deg, #40C8FF 0%, #3192FF 100%)';
  512. otherParams.confirmButtonText = '修改信息';
  513. otherParams.otherType = 'nickname';
  514. otherParams.messageAlign = 'left';
  515. return true;
  516. }
  517. // 判断新建学员是否上限了
  518. if (
  519. forms.isRegister === 'create' &&
  520. forms.studentList.length >= forms.multi_user_limit
  521. ) {
  522. otherParams.showOtherMessage = `同一手机号最多创建${forms.multi_user_limit}个学生`;
  523. otherParams.showOtherSchool = true;
  524. otherParams.showCancelButton = false;
  525. otherParams.showCloseButton = true;
  526. otherParams.confirmButtonColor =
  527. 'linear-gradient( 305deg, #40C8FF 0%, #3192FF 100%)';
  528. otherParams.confirmButtonText = '我知道了';
  529. otherParams.otherType = 'limit';
  530. otherParams.messageAlign = 'center';
  531. return true;
  532. }
  533. // 判断是否为同一个学校
  534. if (
  535. forms.studentItem.schoolId &&
  536. forms.studentItem.schoolId !== forms.registerDetails.schoolId &&
  537. !forms.isChangeSchool &&
  538. forms.isRegister === 'update'
  539. ) {
  540. otherParams.showOtherMessage = `您已绑定<span style="color: #2B85FF">【${
  541. forms.studentItem?.schoolName || ''
  542. }】</span>,提交后将更换到
  543. <span style="color: #2B85FF">【${
  544. forms.registerDetails.schoolName || ''
  545. }】</span>
  546. ,是否确认提交?`;
  547. otherParams.showOtherSchool = true;
  548. otherParams.showCloseButton = false;
  549. otherParams.showCancelButton = true;
  550. otherParams.cancelButtonColor = '';
  551. otherParams.cancelButtonText = '取消';
  552. otherParams.confirmButtonColor = '';
  553. otherParams.confirmButtonText = '确定';
  554. otherParams.otherType = 'change';
  555. otherParams.messageAlign = 'left';
  556. return true;
  557. }
  558. return false;
  559. };
  560. /**
  561. * 登记成功之后购买
  562. */
  563. const onSubmit = async () => {
  564. forms.submitLoading = true;
  565. try {
  566. if (checkForm() || checkSubmit()) {
  567. forms.submitLoading = false;
  568. return;
  569. }
  570. const { extra, loginType, autoRegister, password, multiUser, ...res } =
  571. studentInfo;
  572. /*
  573. 新用户:
  574. autoRegister: true
  575. loginType: 'SMS'
  576. 已存在用户:
  577. autoRegister: false
  578. loginType: 'TOKEN'
  579. password: xxx
  580. */
  581. let tLoginType = loginType,
  582. tAutoRegister = autoRegister,
  583. tPassword = password,
  584. tMultiUser = multiUser;
  585. if (forms.isRegister === 'update') {
  586. tLoginType = 'TOKEN';
  587. tAutoRegister = false;
  588. tPassword = forms.studentItem.token;
  589. tMultiUser = false;
  590. }
  591. const result = await request.post('/edu-app/userlogin', {
  592. requestType: 'form',
  593. data: {
  594. loginType: tLoginType,
  595. autoRegister: tAutoRegister,
  596. password: tPassword,
  597. multiUser: tMultiUser,
  598. ...res,
  599. extra: JSON.stringify({
  600. ...extra,
  601. giftVipDay: forms.giftVipDay || 0,
  602. schoolId: forms.schoolId
  603. })
  604. }
  605. });
  606. if (result.code !== 200) {
  607. if (result.code === 5436) {
  608. forms.showTips = true;
  609. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  610. forms.showButton = false;
  611. } else if (result.code === 5435) {
  612. forms.showTips = true;
  613. forms.showMessage = result.message;
  614. forms.showButton = true;
  615. } else if (result.code === 5437) {
  616. forms.showTips = true;
  617. forms.showMessage = `<p style="color: #F44541">报名已截止,感谢您的参与</p>`; //result.message;
  618. forms.showButton = false;
  619. }
  620. } else {
  621. studentRegisterStore.setToken(
  622. result.data.token_type + ' ' + result.data.access_token
  623. );
  624. setLoginInit();
  625. let joinType = 'NOT_REGISTER';
  626. if (forms.joinType === 'digitalize') {
  627. joinType = 'SELECT_INSTRUMENT';
  628. }
  629. if (forms.joinType === 'tradition') {
  630. joinType = 'NOT_BUY_INSTRUMENT';
  631. }
  632. // 更新时间
  633. const id = await updateStat(
  634. pageTimer.counter.value,
  635. joinType,
  636. result.data.userId,
  637. forms.schoolId
  638. );
  639. forms.saveId = id;
  640. forms.saveUserId = id;
  641. pageTimer.counter.value = 0;
  642. // 获取用户信息
  643. const res = await request.get('/edu-app/user/getUserInfo', {
  644. requestType: 'form'
  645. });
  646. setLogin(res.data);
  647. await onRegisterSubmit();
  648. }
  649. } catch {
  650. // 重置信息 - 如果是新建则不提示
  651. changeTipStatus(forms.isRegister === 'create' ? false : true, false);
  652. } finally {
  653. forms.submitLoading = false;
  654. }
  655. };
  656. const updateStudentInfo = async () => {
  657. try {
  658. const { extra, username } = studentInfo;
  659. const registerResult = await request.post('/edu-app/student/register', {
  660. data: {
  661. schoolId: forms.schoolId,
  662. clientType: 'STUDENT',
  663. ...extra,
  664. giftVipFlag: forms.registerDetails.giftVipFlag || false,
  665. giftVipDay: forms.giftVipDay || 0,
  666. schoolVerify: true,
  667. firstVipDay: forms.detailVip.membershipDays || 0,
  668. mobile: username,
  669. newRegUser: forms.isRegister === 'create' ? true : false
  670. }
  671. });
  672. if (registerResult.code !== 200) {
  673. if (registerResult.code === 5436) {
  674. forms.showTips = true;
  675. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  676. forms.showButton = false;
  677. } else if (registerResult.code === 5435) {
  678. forms.showTips = true;
  679. forms.showMessage = registerResult.message;
  680. forms.showButton = true;
  681. } else if (registerResult.code === 5437) {
  682. forms.showTips = true;
  683. forms.showMessage = `<p style="color: #F44541">报名已截止,感谢您的参与</p>`; //result.message;
  684. forms.showButton = false;
  685. }
  686. return false;
  687. } else {
  688. return true;
  689. }
  690. } catch {}
  691. };
  692. // 登记成功之后购买
  693. const onRegisterSubmit = async () => {
  694. try {
  695. // 请求是否有待支付订单,如果有则自动关闭
  696. const status = await paymentOrderUnpaid();
  697. if (status) return;
  698. const schoolInfo = await request.get(
  699. '/edu-app/userPaymentOrder/registerStatus/' + forms.schoolId
  700. );
  701. const vipList = studentRegisterStore.getVip;
  702. // 传统方式
  703. if (forms.joinType === 'tradition') {
  704. const updateStatus = await updateStudentInfo();
  705. if (!updateStatus) return;
  706. setTimeout(() => {
  707. showToast('报名成功');
  708. // router.push('/download');
  709. }, 100);
  710. setTimeout(() => {
  711. if (browser().weixin) {
  712. // 关闭微信
  713. (window as any).WeixinJSBridge.call('closeWindow');
  714. }
  715. }, 1000);
  716. return;
  717. }
  718. if (schoolInfo.data.hasBuyCourse && vipList.length > 0) {
  719. // forms.dialogConfirmStatus = true;
  720. otherParams.showOtherMessage = `该学员已购买会员,是否再次购买?`;
  721. otherParams.showOtherSchool = true;
  722. otherParams.showCloseButton = false;
  723. otherParams.showCancelButton = true;
  724. otherParams.cancelButtonColor = '';
  725. otherParams.cancelButtonText = '取消';
  726. otherParams.confirmButtonColor = '';
  727. otherParams.confirmButtonText = '确定';
  728. otherParams.otherType = 'member';
  729. otherParams.messageAlign = 'center';
  730. return;
  731. }
  732. await paymentContinue();
  733. } catch {
  734. // 重置信息 - 如果是新建则不提示
  735. changeTipStatus(forms.isRegister === 'create' ? false : true, false);
  736. }
  737. };
  738. const getUserInfos = async () => {
  739. if (
  740. studentInfo.password.length !== 6 ||
  741. !checkPhone(studentInfo.username)
  742. ) {
  743. return;
  744. }
  745. try {
  746. // 15907120131;
  747. const { data } = await request.get(
  748. `/edu-app/open/student/studentInfo?mobile=${studentInfo.username}&code=${studentInfo.password}&type=REGISTER`
  749. );
  750. forms.studentList = data || [];
  751. if (forms.studentList.length > 0) {
  752. const firstStudent: any = forms.studentList[0];
  753. forms.studentItem = firstStudent;
  754. studentInfo.extra.nickname = firstStudent.nickname;
  755. const tempGrade: any = forms.gradeList || [];
  756. tempGrade?.forEach((i: any) => {
  757. if (i.value === firstStudent.currentGradeNum) {
  758. forms.instrumentCode = i.instrumentCode;
  759. forms.gradeNumText = i.text;
  760. studentInfo.extra.currentGradeNum = firstStudent.currentGradeNum;
  761. if (forms.schoolInstrumentSetType === 'CLASS') {
  762. forms.classList = i.classList;
  763. }
  764. }
  765. });
  766. forms.classList.forEach((i: any) => {
  767. if (i.value === firstStudent.currentClass) {
  768. forms.currentClassText = i.text;
  769. studentInfo.extra.currentClass = firstStudent.currentClass;
  770. }
  771. });
  772. studentInfo.extra.gender = firstStudent.gender;
  773. forms.isRegister = 'update';
  774. changeTipStatus(true, false);
  775. } else {
  776. forms.isRegister = 'create';
  777. changeTipStatus(false, false);
  778. forms.studentItem = [];
  779. }
  780. } catch {
  781. //
  782. }
  783. };
  784. // 查询未支付订单
  785. const paymentOrderUnpaid = async () => {
  786. let result = false;
  787. try {
  788. const { data } = await request.get(
  789. '/edu-app/userPaymentOrder/schoolRegisterOrder?schoolId=' +
  790. forms.schoolId
  791. );
  792. // 判断是否有待支付订单
  793. if (data && data.length > 0) {
  794. let isPadding = false; // 是否有待支付订单
  795. let paddingConfig = {} as any;
  796. let paddingData = {} as any;
  797. let isFinal = false; // 是否有完成订单
  798. let finalConfig = {} as any;
  799. data.forEach((element: any) => {
  800. // 判断是否待支付
  801. if (element.status === 'PAYING' || element.status === 'WAIT_PAY') {
  802. isPadding = true;
  803. paddingConfig = element.paymentConfig;
  804. paddingData = element;
  805. }
  806. if (
  807. element.status === 'PAID' ||
  808. element.status === 'PART_REFUNDED' ||
  809. element.status === 'REFUNDED'
  810. ) {
  811. isFinal = true;
  812. finalConfig = element.paymentConfig;
  813. }
  814. });
  815. // 判断是否有完成订单 并且选择 自备
  816. if (isFinal && forms.joinType === 'tradition') {
  817. const studentResult = await updateStudentInfo();
  818. if (!studentResult) return;
  819. setTimeout(() => {
  820. showToast('您已通过数字化方式报名成功');
  821. }, 100);
  822. return true;
  823. }
  824. // 提交报名信息时,判断该手机号是否存在待支付订单,若存则判断本次提交的报名方式,若本次提交的是团购则提示【您有待支付的报名订单,是否继续支付 重新下单/继续支付】,点击重新下单时,关闭老订单,创建新订单;若本次提交的是自备,则提示 【您有数字化方式报名的待支付订单,请关闭订单后重新报名 取消/关闭】取消则停留在当前界面,关闭则关闭订单,并停留在当前界面,用户需要再次点击报名按钮提交信息
  825. if (isPadding && forms.joinType === 'tradition') {
  826. forms.dialogStatus = true;
  827. forms.dialogMessage =
  828. '您有数字化方式报名的待支付订单,请关闭订单后重新报名';
  829. forms.cancelButtonText = '取消';
  830. forms.confirmButtonText = '关闭';
  831. forms.dialogConfig = paddingConfig;
  832. forms.messageAlign = 'left';
  833. return true;
  834. }
  835. if (isPadding && forms.joinType === 'digitalize') {
  836. // 最终确认,有待支付订单直接去支付,没有则才会创建订单
  837. // state.config = paddingConfig?.paymentConfig;
  838. // state.orderNo = paddingConfig?.orderNo;
  839. // const updateStatus = await updateStudentInfo();
  840. // if (!updateStatus) return;
  841. // await lastSubmit();
  842. // 为了处理,有待支付订单,然后后台改了金额,会导致金额不一致
  843. forms.dialogConfig = paddingConfig;
  844. const cancelStatus = await cancelPaymentOrder();
  845. if (cancelStatus) {
  846. await paymentContinue();
  847. }
  848. return true;
  849. }
  850. return false;
  851. } else {
  852. return false;
  853. }
  854. } catch {
  855. // 重置信息 - 如果是新建则不提示
  856. changeTipStatus(forms.isRegister === 'create' ? false : true, false);
  857. }
  858. return result;
  859. };
  860. // 重新下单
  861. const resetOrderPayment = async () => {
  862. try {
  863. const orderNo = forms.dialogConfig?.orderNo;
  864. if (!orderNo) return;
  865. await request.post(
  866. '/edu-app/userPaymentOrder/cancelPayment/' + orderNo
  867. );
  868. await onRegisterSubmit();
  869. } catch {
  870. //
  871. }
  872. };
  873. // 取消订单
  874. const cancelPaymentOrder = async () => {
  875. try {
  876. const orderNo = forms.dialogConfig?.orderNo;
  877. if (!orderNo) return;
  878. await request.post(
  879. '/edu-app/userPaymentOrder/cancelPayment/' + orderNo
  880. );
  881. return true;
  882. } catch {
  883. return false;
  884. }
  885. };
  886. const paymentContinue = async () => {
  887. try {
  888. const vipList = studentRegisterStore.getVip;
  889. const goodsList = studentRegisterStore.getGoods;
  890. const params: any[] = [];
  891. vipList.forEach((vip: any) => {
  892. params.push({
  893. giftVipDay: vip.membershipDays,
  894. goodsId: vip.goodsId,
  895. goodsNum: 1,
  896. goodsType: vip.goodsType,
  897. paymentCashAmount: vip.currentPrice, // 现金支付金额
  898. paymentCouponAmount: 0 // 优惠券金额
  899. });
  900. });
  901. goodsList.forEach((goods: any) => {
  902. params.push({
  903. goodsId: goods.productId,
  904. goodsNum: goods.quantity,
  905. goodsType: 'INSTRUMENTS',
  906. paymentCashAmount: goods.price, // 现金支付金额
  907. paymentCouponAmount: 0, // 优惠券金额
  908. goodsSkuId: goods.productSkuId
  909. });
  910. });
  911. // 创建订单
  912. const updateStatus = await updateStudentInfo();
  913. console.log(updateStatus, 'updateStatus');
  914. if (!updateStatus) return;
  915. const result = await request.post(
  916. '/edu-app/userPaymentOrder/executeOrder',
  917. {
  918. // hideLoading: false,
  919. data: {
  920. buryId: forms.saveUserId,
  921. registerType: forms.registerType,
  922. paymentType: forms.paymentType,
  923. bizId: forms.schoolId, // 乐团编号
  924. orderType: 'SCHOOL_REGISTER',
  925. paymentCashAmount: calcPrice.value.amount || 0,
  926. paymentCouponAmount: 0,
  927. goodsInfos: params,
  928. orderName: '学生登记',
  929. orderDesc: '学生登记'
  930. }
  931. }
  932. );
  933. if (result.code === 5436) {
  934. forms.showTips = true;
  935. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  936. forms.showButton = false;
  937. } else if (result.code === 5435) {
  938. forms.showTips = true;
  939. forms.showMessage = result.message;
  940. forms.showButton = true;
  941. } else {
  942. state.config = {
  943. ...result.data.paymentConfig,
  944. paymentType: result.data.paymentType
  945. };
  946. state.orderNo = result.data.orderNo;
  947. await lastSubmit();
  948. }
  949. } catch (e: any) {
  950. console.log(e, 'any');
  951. // 重置信息 - 如果是新建则不提示
  952. changeTipStatus(forms.isRegister === 'create' ? false : true, false);
  953. }
  954. };
  955. const lastSubmit = async () => {
  956. try {
  957. const users = baseState.user.data;
  958. // 判断是否需要实名认证, 姓名,卡号 - 参数设置可以控制
  959. if (
  960. forms.contract_sign &&
  961. (!users?.account.realName || !users?.account.idCardNo)
  962. ) {
  963. state.authShow = true;
  964. return;
  965. }
  966. const { data } = await request.post(
  967. '/edu-app/userPaymentOrder/updateReceiveAddress',
  968. {
  969. // hideLoading: false,
  970. data: {
  971. orderNo: state.orderNo,
  972. orderType: 'SCHOOL_REGISTER'
  973. }
  974. }
  975. );
  976. state.pay_channel = data.paymentChannel;
  977. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  978. router.replace({
  979. path: '/payment-result',
  980. query: {
  981. orderNo: state.orderNo
  982. }
  983. });
  984. } else {
  985. onCallback();
  986. }
  987. } catch {
  988. // 重置信息 - 如果是新建则不提示
  989. changeTipStatus(forms.isRegister === 'create' ? false : true, false);
  990. }
  991. };
  992. /**
  993. * @description 回调,判断是否有支付渠道,如果有则直接去支付
  994. * @returns void
  995. */
  996. const onCallback = () => {
  997. const pt = state.pay_channel;
  998. // 判断是否有支付方式
  999. if (pt) {
  1000. const payCode: string = beforeSubmit(state.pay_channel);
  1001. onConfirm({
  1002. payCode,
  1003. pay_channel: pt
  1004. });
  1005. } else {
  1006. if (orderType.value === 'VIP') {
  1007. state.paymentStatus = true;
  1008. } else {
  1009. // 直接去拉取微信支付
  1010. onConfirm({
  1011. payCode: 'payResult',
  1012. pay_channel: forms.paymentChannel
  1013. });
  1014. }
  1015. }
  1016. };
  1017. const onConfirm = (val: any) => {
  1018. const config: any = state.config;
  1019. state.pay_channel = val.pay_channel;
  1020. const params = qs.stringify({
  1021. pay_channel: val.pay_channel,
  1022. wxAppId: config.wxAppId,
  1023. alipayAppId: config.alipayAppId,
  1024. paymentType: forms.paymentType,
  1025. body: config.body,
  1026. price: config.price,
  1027. orderNo: config.merOrderNo,
  1028. userId: config.userId
  1029. });
  1030. // console.log(params, state.config);
  1031. // return;
  1032. if (val.payCode === 'payResult') {
  1033. window.location.href =
  1034. window.location.origin + '/classroom-app/#/payResult?' + params;
  1035. } else {
  1036. state.qrCodeUrl =
  1037. window.location.origin + '/classroom-app/#/payDefine?' + params;
  1038. state.showQrcode = true;
  1039. state.paymentStatus = false;
  1040. setTimeout(() => {
  1041. getPaymentOrderStatus();
  1042. }, 300);
  1043. }
  1044. };
  1045. // 放弃支付时,则取消订单
  1046. const onBackOut = async () => {
  1047. try {
  1048. await request.post(
  1049. '/edu-app/userPaymentOrder/cancelPayment/' + state.orderNo
  1050. );
  1051. // router.back();
  1052. } catch {
  1053. //
  1054. }
  1055. };
  1056. // 轮询查询订单状态
  1057. const getPaymentOrderStatus = async () => {
  1058. // 循环查询订单
  1059. // const orderNo = state.orderNo
  1060. const orderTimer = setInterval(async () => {
  1061. // 判断是否在当前路由,如果不是则清除定时器
  1062. if (route.name != 'student-register-form') {
  1063. clearInterval(orderTimer);
  1064. return;
  1065. }
  1066. state.orderTimer = orderTimer;
  1067. try {
  1068. const { data } = await request.post(
  1069. '/edu-app/open/userOrder/paymentStatus/' + state.orderNo,
  1070. {
  1071. hideLoading: true
  1072. }
  1073. );
  1074. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  1075. // 默认关闭支付二维码弹窗
  1076. state.showQrcode = false;
  1077. clearInterval(state.orderTimer);
  1078. setTimeout(() => {
  1079. router.replace({
  1080. path: '/payment-result',
  1081. query: {
  1082. orderNo: state.orderNo
  1083. }
  1084. });
  1085. }, 100);
  1086. }
  1087. } catch {
  1088. //
  1089. clearInterval(state.orderTimer);
  1090. }
  1091. }, 5000);
  1092. };
  1093. // 实名认证成功
  1094. const onAuthSuccess = () => {
  1095. //
  1096. state.authShow = false;
  1097. paymentContinue(); // 实名成功后自动支付
  1098. };
  1099. /**
  1100. * 页面停留时间
  1101. * @param pageBrowseTime 停留时间
  1102. * @param joinType 加入方式
  1103. * @param userId 用户编号
  1104. * @param schoolId 学校编号
  1105. */
  1106. const updateStat = async (
  1107. pageBrowseTime = 5,
  1108. joinType?: string,
  1109. userId?: string,
  1110. schoolId?: string
  1111. ) => {
  1112. try {
  1113. const videoBrowseData =
  1114. moreTime.value.length > 0 ? formatEffectiveTime(moreTime.value) : [];
  1115. const time =
  1116. videoBrowseData.length > 0 ? formatTimer(videoBrowseData) : 0;
  1117. // console.log(moreTime.value, videoBrowseData, 'video', time);
  1118. // const videoCountTime = videoIntervalRef?.counter.value
  1119. // 判断 视屏播放时间大于视屏播放有效时间则说明数据有问题,进行重置数据
  1120. const rate = Math.floor(
  1121. (time / Math.floor(videoForms.player.duration())) * 100
  1122. );
  1123. const { data } = await requestStudent.post(
  1124. '/edu-app/open/studentRegisterPointRecord/update',
  1125. {
  1126. data: {
  1127. id: forms.saveId,
  1128. useTime: pageBrowseTime, // 固定5秒
  1129. joinType,
  1130. userId,
  1131. schoolId,
  1132. videoBrowseData: JSON.stringify(videoBrowseData), // 视屏播放数据
  1133. videoBrowseDataTime: time || 0, // 有效的视频观看时长
  1134. videoBrowsePercentage: rate || 0, // 有效的视频观看时长百分比
  1135. videoBrowseTime: videoIntervalRef?.counter.value, // 视频观看时长
  1136. videoBrowsePoint: Math.floor(videoForms.player.currentTime() || 0) // 视频最后观看点 - 向下取整
  1137. }
  1138. }
  1139. );
  1140. forms.saveId = data;
  1141. return data;
  1142. } catch {
  1143. //
  1144. }
  1145. };
  1146. const getAppIdAndCode = async (url?: string) => {
  1147. try {
  1148. const { data } = await request.get(
  1149. '/edu-app/open/paramConfig/wechatAppId'
  1150. );
  1151. // 判断是否有微信appId
  1152. if (data) {
  1153. closeToast();
  1154. goWechatAuth(data, url);
  1155. }
  1156. } catch {
  1157. //
  1158. }
  1159. };
  1160. if (browser().weixin) {
  1161. //授权
  1162. const openId = sessionStorage.getItem('active-open-id');
  1163. forms.openId = openId;
  1164. const code = getUrlCode();
  1165. if (!code) {
  1166. const newUrl =
  1167. window.location.origin +
  1168. window.location.pathname +
  1169. '#' +
  1170. route.path +
  1171. '?' +
  1172. qs.stringify({
  1173. ...route.query
  1174. });
  1175. getAppIdAndCode(newUrl);
  1176. return '';
  1177. } else {
  1178. forms.code = code;
  1179. }
  1180. }
  1181. const formatTimerTo = (num: number): string => {
  1182. if (num > 9) {
  1183. return num + '';
  1184. } else {
  1185. return '0' + num;
  1186. }
  1187. };
  1188. /**
  1189. * 视屏累计时长
  1190. * 1、视屏开始播放时-开始计时
  1191. * 2、视频暂停时暂停-停止计时
  1192. * 3、视频加载时-停止计时
  1193. * 4、视频倍数播放时,时间正常计时
  1194. * 5、点击视频进度或拖动进度时,时间暂停
  1195. */
  1196. const _init = () => {
  1197. const Button = TCPlayer.getComponent('Button');
  1198. const BigPlayButton = TCPlayer.getComponent('BigPlayButton');
  1199. BigPlayButton.prototype.createEl = function () {
  1200. const el = Button.prototype.createEl.call(this);
  1201. const _html =
  1202. '<button><svg width="42px" height="42px" viewBox="0 0 42 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><circle stroke="#FFFFFF" stroke-width="1.0112392" fill-opacity="0.3" fill="#000000" stroke-linecap="square" cx="21" cy="21" r="20.5056196"></circle><g transform="translate(13.000000, 12.000000)" fill="#FFFFFF"><path d="M11.7432433,3.46351367 L18.2071084,14.4520843 C18.7734418,15.4148511 18.4520691,16.6544316 17.4893024,17.2207649 C17.1784947,17.403593 16.8244584,17.5 16.4638651,17.5 L3.53613489,17.5 C2.41915092,17.5 1.51365649,16.5945056 1.51365649,15.4775216 C1.51365649,15.1169283 1.6100635,14.762892 1.79289156,14.4520843 L8.25675667,3.46351367 C8.82309003,2.50074696 10.0626705,2.17937423 11.0254373,2.74570759 C11.3218389,2.92006148 11.5688894,3.16711204 11.7432433,3.46351367 Z" id="三角形" transform="translate(10.000000, 9.000000) rotate(-270.000000) translate(-10.000000, -9.000000) "></path></g></g></svg></button>';
  1203. el.appendChild(
  1204. TCPlayer.dom.createEl('div', {
  1205. className: 'vjs-button-icon',
  1206. innerHTML: _html
  1207. })
  1208. );
  1209. return el;
  1210. };
  1211. videoForms.player = TCPlayer('register-video', {
  1212. appID: '',
  1213. controls: true,
  1214. plugins: {}
  1215. }); // player-container-id 为播放器容器 ID,必须与 html 中一致
  1216. if (videoForms.player) {
  1217. videoForms.player.src(
  1218. videoForms.introductionVideo ||
  1219. 'https://oss.dayaedu.com/ktqy/1712800085691.mp4'
  1220. ); // url 播放地址
  1221. videoForms.player.poster(
  1222. 'https://oss.dayaedu.com/ktqy/1715770921612add9f12a.png'
  1223. );
  1224. videoForms.player.on('ready', (item: any) => {
  1225. // console.log('ready', item);
  1226. // videoForms.player.pause()
  1227. });
  1228. videoForms.player.on('loadedmetadata', () => {
  1229. console.log('loadedmetadata');
  1230. // videoForms.loading = false;
  1231. videoForms.player.currentTime(videoForms.videoBrowsePoint);
  1232. });
  1233. // 速度变化时
  1234. videoForms.player.on('ratechange', () => {
  1235. videoForms.playerSpeed =
  1236. videoForms.playerSpeed < videoForms.player.playbackRate()
  1237. ? videoForms.player.playbackRate()
  1238. : videoForms.playerSpeed;
  1239. });
  1240. videoForms.player.on('seeking', () => {
  1241. console.log('seeking');
  1242. videoIntervalRef.isActive.value && videoIntervalRef.pause();
  1243. });
  1244. // // 拖动结束时
  1245. videoForms.player.on('seeked', () => {
  1246. console.log('seeked');
  1247. videoIntervalRef.isActive.value && videoIntervalRef.pause();
  1248. });
  1249. // 正在搜索中
  1250. videoForms.player.on('waiting', () => {
  1251. // console.log('waiting pause')
  1252. videoIntervalRef.isActive.value && videoIntervalRef.pause();
  1253. });
  1254. // 如何视频在缓存不会触发
  1255. videoForms.player.on('timeupdate', () => {
  1256. // console.log('timeupdate', videoForms.player.currentTime());
  1257. // 判断视频计时器是否暂停,如果暂停则恢复
  1258. // 添加 「videoForms.player.playing」 是由会跳转到上次播放时间,会触发些方法
  1259. if (
  1260. !videoIntervalRef.isActive.value &&
  1261. videoForms.player.currentTime() > 0 &&
  1262. !videoForms.player.paused()
  1263. ) {
  1264. // console.log('timeupdate play')
  1265. videoIntervalRef.resume();
  1266. }
  1267. });
  1268. // 视屏播放时暂停
  1269. videoForms.player.on('ended', () => {
  1270. videoForms.player.pause();
  1271. });
  1272. // 开始播放
  1273. videoForms.player.on('play', () => {
  1274. console.log('play');
  1275. // 判断视频计时器是否暂停,如果暂停则恢复
  1276. videoIntervalRef.resume();
  1277. });
  1278. // 暂停播放
  1279. videoForms.player.on('pause', () => {
  1280. console.log('pause', videoIntervalRef.isActive.value);
  1281. videoIntervalRef.pause();
  1282. });
  1283. videoForms.player.on('fullscreenchange', () => {
  1284. if (videoForms.player.isFullscreen()) {
  1285. console.log('fullscreen');
  1286. const i = document.createElement('i');
  1287. i.id = 'fullscreen-back';
  1288. i.className = 'van-icon van-icon-arrow-left video-back';
  1289. i.addEventListener('click', () => {
  1290. videoForms.player.exitFullscreen();
  1291. });
  1292. document.getElementsByClassName('video-js')[0].appendChild(i);
  1293. } else {
  1294. console.log('exitfullscreen');
  1295. const i = document.getElementById('fullscreen-back');
  1296. i && i.remove();
  1297. }
  1298. });
  1299. }
  1300. };
  1301. // 保存零时时间
  1302. const moreTime: any = ref([]); // 多个观看时间段
  1303. let tempTime: any = []; // 临时存储时间
  1304. const currentTimer = useInterval(1000, { controls: true });
  1305. // 监听播放状态,
  1306. watch(
  1307. () => videoIntervalRef.isActive.value,
  1308. (newVal: boolean) => {
  1309. console.log(videoIntervalRef.isActive.value, 'videoIntervalRef');
  1310. initVideoCount(newVal);
  1311. }
  1312. );
  1313. /**
  1314. * 初始化视频时长
  1315. * @param newVal 播放状态
  1316. * @param repeat 是否为定时发送的
  1317. */
  1318. const initVideoCount = (newVal: any, repeat = false) => {
  1319. // console.log('watch', videoForms.player.currentTime())
  1320. const initTime = deepClone(tempTime);
  1321. if (repeat) {
  1322. if (tempTime.length > 0) {
  1323. // console.log('join video', tempTime, 'initTime', initTime)
  1324. tempTime[1] = Math.floor(videoForms.player.currentTime());
  1325. }
  1326. } else {
  1327. if (newVal) {
  1328. console.log(
  1329. videoForms.player.currentTime(),
  1330. 'videoForms.player.currentTime()'
  1331. );
  1332. tempTime[0] = Math.floor(videoForms.player.currentTime());
  1333. } else {
  1334. tempTime[1] = Math.floor(videoForms.player.currentTime());
  1335. }
  1336. }
  1337. console.log(
  1338. newVal,
  1339. repeat,
  1340. tempTime,
  1341. tempTime.length,
  1342. 'videoIntervalRef.isActive.value in'
  1343. );
  1344. console.log(videoForms.player.playbackRate(), 'speed');
  1345. if (tempTime.length >= 2) {
  1346. // console.log(tempTime, 'tempTime', moreTime.value)
  1347. // 处理在短时间内的时间差 【视屏拖动,点击可能会导致时间差太大】
  1348. const diffTime =
  1349. tempTime[1] -
  1350. tempTime[0] -
  1351. currentTimer.counter.value * videoForms.playerSpeed >
  1352. 2;
  1353. // console.log(diffTime, 'diffTime', currentTimer.counter.value, videoForms.playerSpeed, 'value')
  1354. // 结束时间,如果 大于开始时间则清除
  1355. if (tempTime[1] >= tempTime[0] && !diffTime)
  1356. moreTime.value.push(tempTime);
  1357. if (repeat) {
  1358. tempTime = deepClone(initTime);
  1359. } else {
  1360. tempTime = [];
  1361. currentTimer.counter.value = 0;
  1362. }
  1363. }
  1364. };
  1365. watch(pageVisibility, (value: any) => {
  1366. if (value == 'hidden') {
  1367. videoForms.player.pause();
  1368. }
  1369. });
  1370. const pagePointInit = async () => {
  1371. try {
  1372. // 判断是否获取微信code码
  1373. if (!forms.code) return;
  1374. const { data } = await request.post(
  1375. '/edu-app/open/studentRegisterPointRecord/save',
  1376. {
  1377. data: {
  1378. code: forms.code,
  1379. schoolId: forms.schoolId,
  1380. openId: forms.openId
  1381. }
  1382. }
  1383. );
  1384. forms.saveId = data.id;
  1385. forms.openId = data.openId;
  1386. moreTime.value = data.videoBrowseData
  1387. ? JSON.parse(data.videoBrowseData)
  1388. : [];
  1389. videoForms.videoBrowsePoint = data.videoBrowsePoint || 0;
  1390. if (videoForms.player) {
  1391. videoForms.player.currentTime(data.videoBrowsePoint || 0);
  1392. }
  1393. sessionStorage.setItem('active-open-id', data.openId);
  1394. // 间隔多少时间同步数据
  1395. forms.intervalFnRef = useIntervalFn(async () => {
  1396. // 页面时间恢复
  1397. pageTimer.counter.value = 0;
  1398. pageTimer.resume();
  1399. // 同步数据时先进行有效时间进行保存
  1400. initVideoCount(false, true);
  1401. await updateStat();
  1402. videoIntervalRef.counter.value = 0;
  1403. }, 5000);
  1404. } catch {}
  1405. };
  1406. /** 手机号变更时清空验证码信息,用户信息 */
  1407. const phoneChangeEmptyInfo = () => {
  1408. studentInfo.password = '';
  1409. studentInfo.extra.nickname = '';
  1410. studentInfo.extra.currentGradeNum = '';
  1411. studentInfo.extra.currentClass = '';
  1412. studentInfo.extra.gender = 1;
  1413. forms.currentClassText = '';
  1414. forms.gradeNumText = '';
  1415. forms.studentList = []; // 手机号关联学生列表
  1416. forms.studentItem = {}; // 选择的学生
  1417. forms.isRegister = 'create'; // 是否注册学生
  1418. forms.isTipRegister = false; // 是否显示名字不一致 - 默认显示
  1419. forms.isChangeSchool = false; // 是否切换学校
  1420. };
  1421. onMounted(async () => {
  1422. try {
  1423. // 获取支付类型
  1424. let expireDay = null;
  1425. const { data } = await request.get(
  1426. '/edu-app/open/paramConfig/queryByParamNameList',
  1427. {
  1428. requestType: 'form',
  1429. params: {
  1430. paramNames:
  1431. 'payment_service_provider,contract_sign,multi_user_limit,qr_code_expire_time'
  1432. }
  1433. }
  1434. );
  1435. if (data && Array.isArray(data)) {
  1436. data.forEach((item: any) => {
  1437. if (item.paramName === 'contract_sign') {
  1438. forms.contract_sign = item.paramValue === '1' ? true : false;
  1439. } else if (item.paramName === 'payment_service_provider') {
  1440. // forms.paymentType = item.paramValue || '';
  1441. const provider = JSON.parse(item.paramValue);
  1442. forms.paymentType = provider.vendor;
  1443. forms.paymentChannel = provider.channel;
  1444. } else if (item.paramName === 'multi_user_limit') {
  1445. forms.multi_user_limit = item.paramValue
  1446. ? Number(item.paramValue)
  1447. : 1;
  1448. } else if (item.paramName === 'qr_code_expire_time') {
  1449. expireDay = item.paramValue ? Number(item.paramValue) : null;
  1450. }
  1451. });
  1452. }
  1453. const createT = route.query.t;
  1454. if (createT && expireDay !== null) {
  1455. if (dayjs(Number(createT)).add(expireDay, 'day').isBefore(dayjs())) {
  1456. forms.showTips = true;
  1457. forms.showMessage = '二维码已经失效,详情请咨询学校老师';
  1458. forms.showButton = false;
  1459. }
  1460. }
  1461. // 初始化视频播放器
  1462. _init();
  1463. await getRegisterGoods();
  1464. } catch {}
  1465. });
  1466. onUnmounted(() => {
  1467. forms.intervalFnRef?.pause(); // 暂停回调
  1468. });
  1469. return () => (
  1470. <div class={styles['student-register']}>
  1471. <div class={styles.studentRegisterContainer}>
  1472. {!forms.activeOverStatus && (
  1473. <div class={styles.countdownSection}>
  1474. <div class={styles.timer}>
  1475. <img src={icon3} class={styles.timerTitle} />
  1476. <div class={styles.timerAll}>
  1477. <span>{formatTimerTo(overCountDown.current.value.days)}</span>
  1478. <span>
  1479. {formatTimerTo(overCountDown.current.value.hours)}
  1480. </span>
  1481. <span>
  1482. {formatTimerTo(overCountDown.current.value.minutes)}
  1483. </span>
  1484. <span>
  1485. {formatTimerTo(overCountDown.current.value.seconds)}
  1486. </span>
  1487. </div>
  1488. </div>
  1489. <div class={styles.timerTip}>
  1490. 为了确保您能顺利参与学习,请在规定时间内报名。
  1491. </div>
  1492. </div>
  1493. )}
  1494. <div class={[styles.studentSection, styles.studentSectionForm]}>
  1495. <div class={styles.title4}></div>
  1496. <div class={styles['video-content']}>
  1497. <video
  1498. id="register-video"
  1499. class={styles['video']}
  1500. src={'https://oss.dayaedu.com/ktqy/1712800085691.mp4'}
  1501. playsinline={true}
  1502. poster={
  1503. 'https://oss.dayaedu.com/ktqy/1715770921612add9f12a.png'
  1504. }
  1505. preload="auto"></video>
  1506. </div>
  1507. </div>
  1508. <div
  1509. class={[
  1510. styles.studentSection,
  1511. styles.studentSectionForm,
  1512. forms.giftVipDay <= 0 && styles.noSendDay
  1513. ]}
  1514. // style={{ display: 'none' }}
  1515. >
  1516. <div class={styles.title1}></div>
  1517. <Form labelAlign="left" class={styles.registerForm}>
  1518. <Field
  1519. clearable={false}
  1520. label="联系方式(直接监护人)"
  1521. placeholder="请输入手机号码"
  1522. type="tel"
  1523. required
  1524. autocomplete="off"
  1525. inputAlign="right"
  1526. class={styles.username}
  1527. v-model={studentInfo.username}
  1528. border={false}
  1529. maxlength={11}
  1530. onUpdate:modelValue={() => {
  1531. phoneChangeEmptyInfo();
  1532. }}>
  1533. {{
  1534. label: () => (
  1535. <div>
  1536. 联系方式
  1537. {/* (直接监护人) */}
  1538. <p class={styles.tips}>(直接监护人)</p>
  1539. </div>
  1540. )
  1541. }}
  1542. </Field>
  1543. <div class={['van-hairline--bottom', styles.fieldTipsGroup]}>
  1544. <div class={[styles.fieldTips]}>
  1545. 手机号是音乐数字课堂的唯一登录账户
  1546. </div>
  1547. </div>
  1548. <Field
  1549. center
  1550. clearable={false}
  1551. required
  1552. inputAlign="right"
  1553. label="验证码"
  1554. placeholder="请输入验证码"
  1555. autocomplete="off"
  1556. type="number"
  1557. v-model={studentInfo.password}
  1558. maxlength={6}
  1559. onUpdate:modelValue={(val: any) => {
  1560. getUserInfos();
  1561. }}>
  1562. {{
  1563. button: () =>
  1564. forms.countDownStatus ? (
  1565. <span
  1566. class={[
  1567. styles.codeText,
  1568. !validatePhone.value ? styles.codeTextDisabled : ''
  1569. ]}
  1570. onClick={onSendCode}>
  1571. 获取验证码
  1572. </span>
  1573. ) : (
  1574. <CountDown
  1575. ref={(el: any) => (countDownRef.value = el)}
  1576. auto-start={false}
  1577. class={styles.countDown}
  1578. time={forms.countDownTime}
  1579. onFinish={onFinished}
  1580. format="ss秒后重试"
  1581. />
  1582. )
  1583. }}
  1584. </Field>
  1585. {/* 大于等于2,则可以切换学生 */}
  1586. {/* {forms.studentList.length > 1 && (
  1587. <div
  1588. class={[
  1589. styles.selectStudentGroup,
  1590. forms.showSelectStudent && styles.selectStudentGroupChecked
  1591. ]}
  1592. onClick={() => (forms.showSelectStudent = true)}>
  1593. <i
  1594. class={[
  1595. styles.studentIcon,
  1596. !forms.studentItem.userId && styles.studentIconAdd
  1597. ]}></i>
  1598. <span>
  1599. {forms.studentItem.userId
  1600. ? forms.studentItem.nickname
  1601. : '新增学生'}
  1602. </span>
  1603. </div>
  1604. )} */}
  1605. <Field
  1606. clearable={false}
  1607. required
  1608. inputAlign="right"
  1609. label="学生姓名"
  1610. placeholder="请输入学生姓名"
  1611. autocomplete="off"
  1612. maxlength={14}
  1613. v-model={studentInfo.extra.nickname}>
  1614. {{
  1615. extra: () =>
  1616. forms.studentList.length > 1 && (
  1617. <div
  1618. class={[
  1619. styles.selectStudentGroup,
  1620. forms.showSelectStudent &&
  1621. styles.selectStudentGroupChecked
  1622. ]}
  1623. onClick={() => (forms.showSelectStudent = true)}>
  1624. <span>
  1625. {forms.studentItem.userId ? '切换' : '新增'}
  1626. </span>
  1627. </div>
  1628. )
  1629. }}
  1630. </Field>
  1631. <Field
  1632. clearable={false}
  1633. required
  1634. inputAlign="right"
  1635. label="学生性别"
  1636. placeholder="请选择性别"
  1637. autocomplete="off"
  1638. // v-model={studentInfo.extra.nickname}
  1639. >
  1640. {{
  1641. input: () => (
  1642. <RadioGroup
  1643. checked-color="linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)"
  1644. v-model={studentInfo.extra.gender}
  1645. direction="horizontal">
  1646. <Tag
  1647. size="large"
  1648. type="primary"
  1649. color={
  1650. !(studentInfo.extra.gender === 1)
  1651. ? '#F5F6FA'
  1652. : 'linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)'
  1653. }
  1654. textColor={
  1655. !(studentInfo.extra.gender === 1) ? '#626264' : '#fff'
  1656. }
  1657. class={styles.radioSection}>
  1658. <Radio class={styles.radioItem} name={1}></Radio>男
  1659. </Tag>
  1660. <Tag
  1661. size="large"
  1662. type="primary"
  1663. color={
  1664. !(studentInfo.extra.gender === 0)
  1665. ? '#F5F6FA'
  1666. : 'linear-gradient( 135deg, #31C7FF 0%, #007AFE 100%)'
  1667. }
  1668. textColor={
  1669. !(studentInfo.extra.gender === 0) ? '#626264' : '#fff'
  1670. }
  1671. class={styles.radioSection}>
  1672. <Radio class={styles.radioItem} name={0}></Radio>女
  1673. </Tag>
  1674. </RadioGroup>
  1675. )
  1676. }}
  1677. </Field>
  1678. <Field
  1679. clearable={false}
  1680. required
  1681. inputAlign="right"
  1682. label="所在年级"
  1683. placeholder="请选择年级"
  1684. isLink
  1685. readonly
  1686. clickable={false}
  1687. modelValue={forms.gradeNumText}
  1688. onClick={() => {
  1689. forms.gradePopupIndex = [studentInfo.extra.currentGradeNum];
  1690. forms.gradeStatus = true;
  1691. }}
  1692. />
  1693. <Field
  1694. clearable={false}
  1695. required
  1696. inputAlign="right"
  1697. label="所在班级"
  1698. placeholder="请选择班级"
  1699. isLink
  1700. readonly
  1701. clickable={false}
  1702. modelValue={forms.currentClassText}
  1703. onClick={() => {
  1704. if (
  1705. forms.schoolInstrumentSetType === 'CLASS' &&
  1706. forms.classList.length <= 0
  1707. ) {
  1708. showToast('请先选择年级');
  1709. return;
  1710. }
  1711. forms.classPopupIndex = [studentInfo.extra.currentClass];
  1712. forms.classStatus = true;
  1713. }}
  1714. />
  1715. {forms.giftVipDay > 0 ? (
  1716. <div class={styles.memberNumer}>
  1717. <img src={iconGift} class={styles.iconGift} />
  1718. <p>
  1719. 注册成功即可获得乐器AI学练工具
  1720. <span>{forms.giftVipDay || 0}</span>天有效期
  1721. </p>
  1722. </div>
  1723. ) : (
  1724. ''
  1725. )}
  1726. </Form>
  1727. </div>
  1728. <div class={styles.studentSection}>
  1729. <div class={styles.title2}></div>
  1730. <div class={styles.goodsGroup}>
  1731. <div
  1732. class={[
  1733. styles.goodsItem,
  1734. styles.digitalize,
  1735. forms.joinType === 'digitalize' && styles.checked
  1736. ]}
  1737. onClick={() => {
  1738. //
  1739. if (checkForm()) {
  1740. showToast('请将资料填写完整');
  1741. return;
  1742. }
  1743. forms.joinType = 'digitalize';
  1744. nextTick(() => {
  1745. mstickyRef.value?.onChnageHeight();
  1746. setTimeout(() => {
  1747. window.scrollTo(0, 1000);
  1748. }, 50);
  1749. });
  1750. }}>
  1751. <div class={styles.goodsInner}>
  1752. <i class={styles.proposalTip}></i>
  1753. 数字化方式
  1754. </div>
  1755. </div>
  1756. <div
  1757. class={[
  1758. styles.goodsItem,
  1759. styles.tradition,
  1760. forms.joinType === 'tradition' && styles.checked1
  1761. ]}
  1762. onClick={() => {
  1763. if (checkForm()) {
  1764. showToast('请将资料填写完整');
  1765. return;
  1766. }
  1767. forms.joinType = 'tradition';
  1768. nextTick(() => {
  1769. mstickyRef.value?.onChnageHeight();
  1770. setTimeout(() => {
  1771. window.scrollTo(0, 1000);
  1772. }, 50);
  1773. });
  1774. }}>
  1775. <div class={styles.goodsInner}>传统方式</div>
  1776. </div>
  1777. </div>
  1778. {forms.joinType && (
  1779. <div class={styles.goodsTypeGroup}>
  1780. {forms.joinType === 'digitalize' && (
  1781. <>
  1782. <img src={icon10} class={styles.showImg} />
  1783. {forms.detailVip.membershipDays ? (
  1784. <div class={styles.memberNumer}>
  1785. <img src={iconGift} class={styles.iconGift} />
  1786. <p>
  1787. 首次购买赠送乐器AI学练工具
  1788. <span>{forms.detailVip.membershipDays || 0}</span>
  1789. 天有效期
  1790. </p>
  1791. </div>
  1792. ) : (
  1793. ''
  1794. )}
  1795. </>
  1796. )}
  1797. {forms.joinType === 'tradition' && (
  1798. <img src={icon11} class={styles.showImg} />
  1799. )}
  1800. </div>
  1801. )}
  1802. </div>
  1803. {/* {forms.joinType === 'digitalize' && (
  1804. <div class={[styles.goodsExtra]}>
  1805. <i class={styles.iconArrow}></i>
  1806. <Cell border={false} class={styles.goodsCell}>
  1807. {{
  1808. icon: () => (
  1809. <Image
  1810. class={styles.img}
  1811. src={forms.detailVip.goodsUrl || tuangou}
  1812. />
  1813. ),
  1814. title: () => (
  1815. <div class={styles.section}>
  1816. <div class={styles.sectionContent}>
  1817. <h2>
  1818. <img src={icon5} class={styles.goodsName} />
  1819. <Tag class={styles.brandName}>
  1820. 12个月
  1821. </Tag>
  1822. </h2>
  1823. <p class={[styles.model]}>
  1824. <p>
  1825. <i></i>解决学生不会练、不知练的对错
  1826. </p>
  1827. <p>
  1828. <i></i>家长无法辅导、无需再额外请老师
  1829. </p>
  1830. </p>
  1831. <img src={icon6} class={styles.sendInstrument} />
  1832. </div>
  1833. </div>
  1834. )
  1835. }}
  1836. </Cell>
  1837. {forms.detailVip.membershipDays ? (
  1838. <div class={styles.memberNumer}>
  1839. <img src={iconGift} class={styles.iconGift} />
  1840. <p>
  1841. 首次购买赠送乐器AI学练工具
  1842. <span>{forms.detailVip.membershipDays || 0}</span>天有效期
  1843. </p>
  1844. </div>
  1845. ) : (
  1846. ''
  1847. )}
  1848. </div>
  1849. )}
  1850. {forms.joinType === 'tradition' && (
  1851. <div class={styles.goodsTradition}>
  1852. <i class={styles.iconArrow}></i>
  1853. <div class={styles.goodsTitle}></div>
  1854. <div class={styles.steps}>
  1855. <div class={styles.step}>
  1856. <span class={styles.nums}>
  1857. <span class={styles.numInner}>1</span>
  1858. </span>
  1859. <div class={styles.stepContent}>
  1860. <span>AI工具标准:</span>
  1861. 可以学练音乐教材中的乐曲,通过手机应用商店准备。
  1862. </div>
  1863. </div>
  1864. {['Panpipes', 'Ocarina', 'Tenor Recorder', 'Woodwind'].includes(
  1865. forms.instrumentCode
  1866. ) && (
  1867. <div class={styles.step}>
  1868. <span class={styles.nums}>
  1869. <span class={styles.numInner}>2</span>
  1870. </span>
  1871. <div class={styles.stepContent}>
  1872. <span>
  1873. {forms.instrumentCode === 'Panpipes' && '排箫'}
  1874. {forms.instrumentCode === 'Ocarina' && '陶笛'}
  1875. {forms.instrumentCode === 'Tenor Recorder' && '竖笛'}
  1876. {forms.instrumentCode === 'Woodwind' && '葫芦丝'}
  1877. 标准:
  1878. </span>
  1879. {forms.instrumentCode === 'Panpipes' &&
  1880. '管数不限,建议20管以上C调加嘴排箫(不需要重复更换),黑色,选择单一原调(调性多很难掌握);'}
  1881. {forms.instrumentCode === 'Ocarina' &&
  1882. 'C调、蓝色、十二孔高音、树脂或陶土均可;'}
  1883. {forms.instrumentCode === 'Tenor Recorder' &&
  1884. 'C调、木质、高音德式八孔;'}
  1885. {forms.instrumentCode === 'Woodwind' &&
  1886. 'C调、红木色、树脂或木质;'}
  1887. </div>
  1888. </div>
  1889. )}
  1890. </div>
  1891. </div>
  1892. )} */}
  1893. {forms.joinType && (
  1894. <MSticky position="bottom" ref={mstickyRef}>
  1895. <div class={styles.paymentContainer}>
  1896. {forms.joinType === 'digitalize' && (
  1897. <>
  1898. <div class={styles.payemntPrice}>
  1899. <img src={giftTip} class={styles.giftTip} />
  1900. <div>
  1901. <span class={styles.needPrice}>
  1902. <i style="font-style: normal">¥ </i>
  1903. <span>{moneyFormat(calcPrice.value.amount)}</span>
  1904. <i class={styles.unit} style="font-style: normal">
  1905. /年
  1906. </i>
  1907. </span>
  1908. {calcPrice.value.originAmount >
  1909. calcPrice.value.amount ? (
  1910. <del class={styles.allPrice}>
  1911. ¥ {moneyFormat(calcPrice.value.originAmount)}
  1912. </del>
  1913. ) : (
  1914. ''
  1915. )}
  1916. </div>
  1917. </div>
  1918. <div
  1919. class={styles.paymentBtn}
  1920. onClick={() => {
  1921. onSubmit();
  1922. }}>
  1923. <Button
  1924. round
  1925. disabled={forms.submitLoading}
  1926. loading={forms.submitLoading}>
  1927. 立即支付
  1928. </Button>
  1929. </div>
  1930. </>
  1931. )}
  1932. {forms.joinType === 'tradition' && (
  1933. <div
  1934. class={styles.traditionBtn}
  1935. onClick={() => {
  1936. onSubmit();
  1937. }}>
  1938. <Button
  1939. round
  1940. disabled={forms.submitLoading}
  1941. loading={forms.submitLoading}>
  1942. 提交报名
  1943. </Button>
  1944. </div>
  1945. )}
  1946. </div>
  1947. </MSticky>
  1948. )}
  1949. </div>
  1950. {forms.imgCodeStatus ? (
  1951. <MImgCode
  1952. v-model:value={forms.imgCodeStatus}
  1953. phone={studentInfo.username}
  1954. type="REGISTER"
  1955. onClose={() => {
  1956. forms.imgCodeStatus = false;
  1957. }}
  1958. onSendCode={onCodeSend}
  1959. />
  1960. ) : null}
  1961. {/* 年级 */}
  1962. <Popup
  1963. v-model:show={forms.gradeStatus}
  1964. position="bottom"
  1965. round
  1966. safeAreaInsetBottom
  1967. lazyRender={false}
  1968. class={'popupBottomSearch'}
  1969. onOpen={() => {
  1970. forms.gradePopupShow = true;
  1971. }}
  1972. onClosed={() => {
  1973. forms.gradePopupShow = false;
  1974. }}>
  1975. {forms.gradePopupShow && (
  1976. <Picker
  1977. showToolbar
  1978. v-model={forms.gradePopupIndex}
  1979. columns={forms.gradeList}
  1980. onCancel={() => (forms.gradeStatus = false)}
  1981. onConfirm={(val: any) => {
  1982. const selectedOption = val.selectedOptions[0];
  1983. studentInfo.extra.currentGradeNum = selectedOption.value;
  1984. forms.gradeNumText = selectedOption.text;
  1985. forms.gradeStatus = false;
  1986. if (
  1987. ['SCHOOL', 'GRADE'].includes(forms.schoolInstrumentSetType)
  1988. ) {
  1989. forms.instrumentCode = selectedOption.instrumentCode;
  1990. }
  1991. if (forms.schoolInstrumentSetType === 'CLASS') {
  1992. forms.classList = selectedOption.classList;
  1993. }
  1994. if (
  1995. ['CLASS', 'GRADE'].includes(forms.schoolInstrumentSetType)
  1996. ) {
  1997. forms.currentClassText = '';
  1998. studentInfo.extra.currentClass = '';
  1999. }
  2000. }}
  2001. />
  2002. )}
  2003. </Popup>
  2004. {/* 班级 */}
  2005. <Popup
  2006. v-model:show={forms.classStatus}
  2007. position="bottom"
  2008. round
  2009. class={'popupBottomSearch'}
  2010. onOpen={() => {
  2011. forms.classPopupShow = true;
  2012. }}
  2013. onClosed={() => {
  2014. forms.classPopupShow = false;
  2015. }}>
  2016. {forms.classPopupShow && (
  2017. <Picker
  2018. showToolbar
  2019. v-model={forms.classPopupIndex}
  2020. columns={forms.classList}
  2021. onCancel={() => (forms.classStatus = false)}
  2022. onConfirm={(val: any) => {
  2023. const selectedOption = val.selectedOptions[0];
  2024. studentInfo.extra.currentClass = selectedOption.value;
  2025. forms.currentClassText = selectedOption.text;
  2026. forms.classStatus = false;
  2027. if (['CLASS'].includes(forms.schoolInstrumentSetType)) {
  2028. forms.instrumentCode = selectedOption.instrumentCode;
  2029. }
  2030. }}
  2031. />
  2032. )}
  2033. </Popup>
  2034. {/* 已经购买过样品 */}
  2035. {/* <MDialog
  2036. title="提示"
  2037. v-model:show={forms.dialogConfirmStatus}
  2038. message={'已购买会员,是否确认购买?'}
  2039. primaryColor="#FF8057"
  2040. allowHtml={true}
  2041. confirmButtonText="确定"
  2042. showCancelButton
  2043. onConfirm={async () => {
  2044. await paymentContinue();
  2045. }}
  2046. onCancel={() => {
  2047. //取消支付,判断是否有结束时间,是否已经结束
  2048. if (forms.registerExpireTime && forms.activeOverStatus) {
  2049. applyOver();
  2050. }
  2051. }}
  2052. /> */}
  2053. <MDialog
  2054. title="提示"
  2055. v-model:show={forms.dialogStatus}
  2056. message={forms.dialogMessage}
  2057. allowHtml={true}
  2058. primaryColor="#FF8057"
  2059. showCancelButton={true}
  2060. messageAlign={forms.messageAlign}
  2061. confirmButtonText={forms.confirmButtonText}
  2062. cancelButtonText={forms.cancelButtonText}
  2063. onConfirm={async () => {
  2064. if (forms.joinType === 'tradition') {
  2065. //
  2066. await cancelPaymentOrder();
  2067. //取消支付,判断是否有结束时间,是否已经结束
  2068. if (forms.registerExpireTime && forms.activeOverStatus) {
  2069. applyOver();
  2070. }
  2071. }
  2072. if (forms.joinType === 'digitalize') {
  2073. // 继续支付
  2074. const paymentConfig = forms.dialogConfig;
  2075. state.config = paymentConfig?.paymentConfig;
  2076. state.orderNo = paymentConfig?.orderNo;
  2077. const updateStatus = await updateStudentInfo();
  2078. if (!updateStatus) return;
  2079. await lastSubmit();
  2080. }
  2081. }}
  2082. onCancel={(val: any) => {
  2083. // countDown.pause();
  2084. if (forms.joinType === 'tradition') {
  2085. forms.dialogStatus = false;
  2086. //取消支付,判断是否有结束时间,是否已经结束
  2087. if (forms.registerExpireTime && forms.activeOverStatus) {
  2088. applyOver();
  2089. }
  2090. }
  2091. if (forms.joinType === 'digitalize') {
  2092. // 重新下单 - 先关闭订单
  2093. resetOrderPayment();
  2094. }
  2095. }}
  2096. />
  2097. <Popup
  2098. show={state.paymentStatus}
  2099. closeOnClickOverlay={false}
  2100. position="bottom"
  2101. round
  2102. closeOnPopstate
  2103. safeAreaInsetBottom
  2104. style={{ minHeight: '30%' }}>
  2105. <Payment
  2106. paymentConfig={state.orderInfo}
  2107. onClose={() => (state.paymentStatus = false)}
  2108. onBackOut={onBackOut}
  2109. onConfirm={(val: any) => onConfirm(val)}
  2110. />
  2111. </Popup>
  2112. <Popup
  2113. v-model:show={state.showQrcode}
  2114. round
  2115. onClose={() => {
  2116. // 二维码关闭时清除定时器
  2117. clearInterval(state.orderTimer);
  2118. }}>
  2119. <QrcodePayment
  2120. url={state.qrCodeUrl}
  2121. pay_channel={state.pay_channel}
  2122. orderType={orderType.value}
  2123. />
  2124. </Popup>
  2125. <MPopup v-model:modelValue={state.authShow}>
  2126. <UserAuth onSuccess={onAuthSuccess} hideHeader={!browser().isApp} />
  2127. </MPopup>
  2128. {/* 是否在微信中打开 */}
  2129. <OWxTip
  2130. show={forms.showTips}
  2131. message={forms.showMessage}
  2132. showButton={forms.showButton}
  2133. buttonText="刷新"
  2134. onConfirm={() => window.location.reload()}
  2135. />
  2136. <MMessageTip
  2137. show={otherParams.showOtherSchool}
  2138. // showCloseButton={otherParams.showCloseButton}
  2139. messageAlign={otherParams.messageAlign}
  2140. message={otherParams.showOtherMessage}
  2141. showCancelButton={otherParams.showCancelButton}
  2142. cancelButtonColor={otherParams.cancelButtonColor}
  2143. cancelButtonText={otherParams.cancelButtonText}
  2144. confirmButtonColor={otherParams.confirmButtonColor}
  2145. confirmButtonText={otherParams.confirmButtonText}
  2146. onClose={() => (otherParams.showOtherSchool = false)}
  2147. onCancel={async () => {
  2148. otherParams.showOtherSchool = false;
  2149. if (otherParams.otherType === 'nickname') {
  2150. forms.isRegister = 'create'; // 新建
  2151. changeTipStatus(false, false);
  2152. onSubmit();
  2153. } else if (otherParams.otherType === 'member') {
  2154. const updateStatus = await updateStudentInfo();
  2155. if (!updateStatus) return;
  2156. //取消支付,判断是否有结束时间,是否已经结束
  2157. if (forms.registerExpireTime && forms.activeOverStatus) {
  2158. applyOver();
  2159. }
  2160. // onConfirm={async () => {
  2161. // await paymentContinue();
  2162. // }}
  2163. // onCancel={() => {
  2164. // //取消支付,判断是否有结束时间,是否已经结束
  2165. // if (forms.registerExpireTime && forms.activeOverStatus) {
  2166. // applyOver();
  2167. // }
  2168. // }}
  2169. }
  2170. }}
  2171. onConfirm={async () => {
  2172. otherParams.showOtherSchool = false;
  2173. // 名字
  2174. if (otherParams.otherType === 'nickname') {
  2175. forms.isRegister = 'update'; // 修改
  2176. changeTipStatus(false, false);
  2177. // 直接注册
  2178. onSubmit();
  2179. } else if (otherParams.otherType === 'change') {
  2180. // 学校更换
  2181. forms.isChangeSchool = true;
  2182. // 直接注册
  2183. onSubmit();
  2184. } else if (otherParams.otherType === 'limit') {
  2185. // 人数超限制
  2186. changeTipStatus(
  2187. forms.isRegister === 'create' && !forms.studentItem.userId
  2188. ? false
  2189. : true,
  2190. false
  2191. );
  2192. } else if (otherParams.otherType === 'member') {
  2193. await paymentContinue();
  2194. }
  2195. }}
  2196. />
  2197. <Popup
  2198. v-model:show={forms.showSelectStudent}
  2199. round
  2200. position="bottom"
  2201. safeAreaInsetBottom
  2202. closeable>
  2203. <SelectStudent
  2204. studentItem={forms.studentItem}
  2205. list={forms.studentList}
  2206. onClose={() => (forms.showSelectStudent = false)}
  2207. onConfirm={(val: any) => {
  2208. if (val.userId) {
  2209. forms.studentItem = val;
  2210. const firstStudent = val;
  2211. studentInfo.extra.nickname = firstStudent.nickname;
  2212. const tempGrade: any = forms.gradeList || [];
  2213. studentInfo.extra.currentGradeNum = null;
  2214. forms.gradeNumText = '';
  2215. forms.instrumentCode = '';
  2216. tempGrade?.forEach((i: any) => {
  2217. if (i.value === firstStudent.currentGradeNum) {
  2218. forms.instrumentCode = i.instrumentCode;
  2219. forms.gradeNumText = i.text;
  2220. studentInfo.extra.currentGradeNum =
  2221. firstStudent.currentGradeNum;
  2222. if (forms.schoolInstrumentSetType === 'CLASS') {
  2223. forms.classList = i.classList;
  2224. }
  2225. }
  2226. });
  2227. studentInfo.extra.currentClass = null;
  2228. forms.currentClassText = '';
  2229. forms.classList.forEach((i: any) => {
  2230. if (i.value === firstStudent.currentClass) {
  2231. forms.currentClassText = i.text;
  2232. studentInfo.extra.currentClass = firstStudent.currentClass;
  2233. }
  2234. });
  2235. studentInfo.extra.gender = firstStudent.gender;
  2236. forms.isRegister = 'update';
  2237. changeTipStatus(true, false);
  2238. forms.showSelectStudent = false;
  2239. } else {
  2240. // 判断新建学员是否上限了
  2241. if (forms.studentList.length >= forms.multi_user_limit) {
  2242. otherParams.showOtherMessage = `同一手机号最多创建${forms.multi_user_limit}个学生`;
  2243. otherParams.showOtherSchool = true;
  2244. otherParams.showCancelButton = false;
  2245. otherParams.showCloseButton = true;
  2246. otherParams.confirmButtonColor =
  2247. 'linear-gradient( 305deg, #40C8FF 0%, #3192FF 100%)';
  2248. otherParams.confirmButtonText = '我知道了';
  2249. otherParams.otherType = 'limit';
  2250. otherParams.messageAlign = 'center';
  2251. return true;
  2252. } else {
  2253. forms.studentItem = val;
  2254. forms.isRegister = 'create';
  2255. changeTipStatus(false, false);
  2256. studentInfo.extra.nickname = '';
  2257. studentInfo.extra.currentGradeNum = '';
  2258. studentInfo.extra.currentClass = '';
  2259. studentInfo.extra.gender = 1;
  2260. forms.currentClassText = '';
  2261. forms.gradeNumText = '';
  2262. forms.showSelectStudent = false;
  2263. }
  2264. }
  2265. }}
  2266. />
  2267. </Popup>
  2268. </div>
  2269. );
  2270. }
  2271. });