|
|
@@ -2,11 +2,25 @@ Page({
|
|
|
data: {
|
|
|
formData: {
|
|
|
occupation: '',
|
|
|
- contactName: '',
|
|
|
+ name: '',
|
|
|
+ gender: '女',
|
|
|
phone: '',
|
|
|
- organization: '',
|
|
|
- notes: ''
|
|
|
- }
|
|
|
+ code: ''
|
|
|
+ },
|
|
|
+ imgCodeInput: '',
|
|
|
+ imgCodeImage: '',
|
|
|
+ showImgCodePanel: false,
|
|
|
+ isCodeSent: false,
|
|
|
+ isSendingCode: false,
|
|
|
+ isLoadingImgCode: false,
|
|
|
+ isVerifyingImgCode: false,
|
|
|
+ isSubmitting: false,
|
|
|
+ showSubmitTip: false,
|
|
|
+ submitTipText: '',
|
|
|
+ countdown: 0,
|
|
|
+ submitApi: 'https://kt.colexiu.com/edu-app/open/student/requestTrial',
|
|
|
+ getImgCodeApi: 'https://kt.colexiu.com/edu-app/open/sendImgCode',
|
|
|
+ sendSmsApi: 'https://kt.colexiu.com/edu-app/open/sendSmsVerify'
|
|
|
},
|
|
|
|
|
|
handleInputChange: function (e) {
|
|
|
@@ -16,44 +30,398 @@ Page({
|
|
|
this.setData({ formData: next })
|
|
|
},
|
|
|
|
|
|
- handleSubmit: function () {
|
|
|
- const data = this.data.formData
|
|
|
+ handleImgCodeInput: function (e) {
|
|
|
+ const value = (e.detail.value || '').trim()
|
|
|
+ this.setData({
|
|
|
+ imgCodeInput: value
|
|
|
+ })
|
|
|
+
|
|
|
+ // 输入满足长度后自动校验,校验通过后自动发送短信
|
|
|
+ if (value.length >= 4) {
|
|
|
+ this.verifyImgCodeAndSendSms()
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- if (!data.occupation.trim()) {
|
|
|
+ handleSelectOption: function (e) {
|
|
|
+ const field = e.currentTarget.dataset.field
|
|
|
+ const value = e.currentTarget.dataset.value
|
|
|
+ const next = Object.assign({}, this.data.formData, { [field]: value })
|
|
|
+ this.setData({ formData: next })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleSendCode: function () {
|
|
|
+ const phone = (this.data.formData.phone || '').replace(/\s+/g, '')
|
|
|
+ if (!/^1[3-9]\d{9}$/.test(phone)) {
|
|
|
tt.showToast({
|
|
|
- title: '请填写职业',
|
|
|
+ title: '请先输入正确手机号',
|
|
|
icon: 'none',
|
|
|
- duration: 2000
|
|
|
+ duration: 3000
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- if (!data.contactName.trim() || !data.phone.trim()) {
|
|
|
+ if (this.data.isSendingCode || this.data.isLoadingImgCode || this.data.countdown > 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先展示图形验证码区域,再拉取图片
|
|
|
+ this.setData({
|
|
|
+ showImgCodePanel: true,
|
|
|
+ imgCodeInput: '',
|
|
|
+ imgCodeImage: '',
|
|
|
+ isLoadingImgCode: true
|
|
|
+ })
|
|
|
+ this.requestImgCode()
|
|
|
+ },
|
|
|
+
|
|
|
+ requestImgCode: function () {
|
|
|
+ const phone = (this.data.formData.phone || '').replace(/\s+/g, '')
|
|
|
+ tt.request({
|
|
|
+ url: this.data.getImgCodeApi,
|
|
|
+ method: 'GET',
|
|
|
+ data: {
|
|
|
+ phone: phone
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ if (res.statusCode !== 200) {
|
|
|
+ tt.showToast({
|
|
|
+ title: this.getResponseMessage(res, '获取图形验证码失败'),
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const base64 = this.getImgCodeBase64(res)
|
|
|
+ if (!base64) {
|
|
|
+ tt.showToast({
|
|
|
+ title: '图形验证码加载失败,请点击图片重试',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ showImgCodePanel: true,
|
|
|
+ imgCodeImage: base64.startsWith('data:image') ? base64 : `data:image/png;base64,${base64}`,
|
|
|
+ imgCodeInput: ''
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: (_err) => {
|
|
|
+ tt.showToast({
|
|
|
+ title: '获取图形验证码失败',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000
|
|
|
+ })
|
|
|
+ },
|
|
|
+ complete: () => {
|
|
|
+ this.setData({ isLoadingImgCode: false })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleRefreshImgCode: function () {
|
|
|
+ if (this.data.isLoadingImgCode || this.data.isVerifyingImgCode) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.setData({
|
|
|
+ imgCodeInput: '',
|
|
|
+ isLoadingImgCode: true
|
|
|
+ })
|
|
|
+ this.requestImgCode()
|
|
|
+ },
|
|
|
+
|
|
|
+ handleCloseImgCodeModal: function () {
|
|
|
+ this.setData({
|
|
|
+ showImgCodePanel: false,
|
|
|
+ imgCodeInput: '',
|
|
|
+ imgCodeImage: ''
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ verifyImgCodeAndSendSms: function () {
|
|
|
+ const phone = (this.data.formData.phone || '').replace(/\s+/g, '')
|
|
|
+ const imgCode = (this.data.imgCodeInput || '').trim()
|
|
|
+
|
|
|
+ if (!/^1[3-9]\d{9}$/.test(phone)) {
|
|
|
tt.showToast({
|
|
|
- title: '请完善联系人与手机号',
|
|
|
+ title: '请先输入正确手机号',
|
|
|
icon: 'none',
|
|
|
- duration: 2000
|
|
|
+ duration: 3000
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- tt.showToast({
|
|
|
- title: '提交成功,稍后联系您',
|
|
|
- icon: 'success',
|
|
|
- duration: 2000
|
|
|
+ if (imgCode.length < 4) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.data.isSendingCode || this.data.isVerifyingImgCode) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setData({ isVerifyingImgCode: true })
|
|
|
+ tt.request({
|
|
|
+ url: this.data.sendSmsApi,
|
|
|
+ method: 'POST',
|
|
|
+ header: {
|
|
|
+ 'Content-Type': 'application/x-www-form-urlencoded'
|
|
|
+ },
|
|
|
+ data: {
|
|
|
+ mobile: phone,
|
|
|
+ type: 'REGISTER',
|
|
|
+ clientId: 'BACKEND',
|
|
|
+ code: imgCode
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ const bizSuccess = !!(res && res.data && Number(res.data.code) === 200)
|
|
|
+ if (!bizSuccess) {
|
|
|
+ tt.showToast({
|
|
|
+ title: this.getResponseMessage(res, '图形验证码错误'),
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000
|
|
|
+ })
|
|
|
+ this.refreshImgCodeAfterVerifyFail()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.setData({
|
|
|
+ isCodeSent: true,
|
|
|
+ showImgCodePanel: false,
|
|
|
+ imgCodeInput: ''
|
|
|
+ })
|
|
|
+ this.startCountdown(60)
|
|
|
+ tt.showToast({
|
|
|
+ title: '验证码已发送',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: (_err) => {
|
|
|
+ tt.showToast({
|
|
|
+ title: '发送失败,请稍后重试',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000
|
|
|
+ })
|
|
|
+ this.refreshImgCodeAfterVerifyFail()
|
|
|
+ },
|
|
|
+ complete: () => {
|
|
|
+ this.setData({ isVerifyingImgCode: false })
|
|
|
+ }
|
|
|
})
|
|
|
+ },
|
|
|
|
|
|
- this.setData({
|
|
|
- formData: {
|
|
|
- occupation: '',
|
|
|
- contactName: '',
|
|
|
- phone: '',
|
|
|
- organization: '',
|
|
|
- notes: ''
|
|
|
+ startCountdown: function (seconds) {
|
|
|
+ if (this._countdownTimer) {
|
|
|
+ clearInterval(this._countdownTimer)
|
|
|
+ }
|
|
|
+ this.setData({ countdown: seconds })
|
|
|
+ this._countdownTimer = setInterval(() => {
|
|
|
+ const next = this.data.countdown - 1
|
|
|
+ if (next <= 0) {
|
|
|
+ clearInterval(this._countdownTimer)
|
|
|
+ this._countdownTimer = null
|
|
|
+ this.setData({ countdown: 0 })
|
|
|
+ return
|
|
|
}
|
|
|
+ this.setData({ countdown: next })
|
|
|
+ }, 1000)
|
|
|
+ },
|
|
|
+
|
|
|
+ handleSubmit: function () {
|
|
|
+ if (this.data.isSubmitting) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const data = this.data.formData
|
|
|
+ const occupation = (data.occupation || '').trim()
|
|
|
+ const name = (data.name || '').trim()
|
|
|
+ const gender = (data.gender || '').trim()
|
|
|
+ const phone = (data.phone || '').replace(/\s+/g, '')
|
|
|
+ const code = (data.code || '').trim()
|
|
|
+ console.log('[TRIAL_FLOW] SUBMIT_CLICK', { occupation: occupation, gender: gender })
|
|
|
+
|
|
|
+ if (!occupation) {
|
|
|
+ this.showSubmitTip('请选择职业')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!name) {
|
|
|
+ this.showSubmitTip('请输入姓名')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!gender) {
|
|
|
+ this.showSubmitTip('请选择性别')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!/^1[3-9]\d{9}$/.test(phone)) {
|
|
|
+ this.showSubmitTip('请输入正确的11位手机号')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!code) {
|
|
|
+ this.showSubmitTip('请输入短信验证码')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setData({ isSubmitting: true })
|
|
|
+ this.submitTrial({
|
|
|
+ occupation: occupation,
|
|
|
+ name: name,
|
|
|
+ gender: gender,
|
|
|
+ phone: phone,
|
|
|
+ code: code
|
|
|
})
|
|
|
},
|
|
|
|
|
|
+ submitTrial: function (params) {
|
|
|
+ tt.request({
|
|
|
+ url: this.data.submitApi,
|
|
|
+ method: 'POST',
|
|
|
+ header: {
|
|
|
+ 'Content-Type': 'application/x-www-form-urlencoded'
|
|
|
+ },
|
|
|
+ data: {
|
|
|
+ occupation: params.occupation,
|
|
|
+ name: params.name,
|
|
|
+ gender: params.gender,
|
|
|
+ phone: params.phone,
|
|
|
+ code: params.code
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ const bizSuccess = this.isApiSuccess(res)
|
|
|
+ if (!bizSuccess) {
|
|
|
+ this.showSubmitTip(this.getResponseMessage(res, '提交失败,请稍后重试'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.showSubmitTip(this.getResponseMessage(res, '提交成功'))
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ formData: {
|
|
|
+ occupation: '',
|
|
|
+ name: '',
|
|
|
+ gender: '女',
|
|
|
+ phone: '',
|
|
|
+ code: ''
|
|
|
+ },
|
|
|
+ imgCodeInput: '',
|
|
|
+ imgCodeImage: '',
|
|
|
+ showImgCodePanel: false,
|
|
|
+ isCodeSent: false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: (_err) => {
|
|
|
+ this.showSubmitTip('提交失败,请稍后重试')
|
|
|
+ },
|
|
|
+ complete: () => {
|
|
|
+ this.setData({ isSubmitting: false })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ getResponseMessage: function (res, fallback) {
|
|
|
+ if (!res || !res.data) {
|
|
|
+ return fallback
|
|
|
+ }
|
|
|
+ if (typeof res.data === 'string') {
|
|
|
+ return res.data || fallback
|
|
|
+ }
|
|
|
+ return res.data.message || res.data.msg || fallback
|
|
|
+ },
|
|
|
+
|
|
|
+ isApiSuccess: function (res) {
|
|
|
+ if (!res || res.statusCode !== 200) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ const data = res.data
|
|
|
+ if (data === undefined || data === null || data === '') {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ const failPattern = /(错误|失败|无效|过期|不存在|required|invalid|unauthorized|forbidden|denied|error|fail)/i
|
|
|
+
|
|
|
+ if (typeof data === 'string') {
|
|
|
+ return !failPattern.test(data)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof data !== 'object') {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof data.success === 'boolean') {
|
|
|
+ return data.success
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.code !== undefined && data.code !== null && data.code !== '') {
|
|
|
+ const code = String(data.code)
|
|
|
+ if (code !== '0' && code !== '200') {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.status !== undefined && data.status !== null && data.status !== '') {
|
|
|
+ const status = String(data.status)
|
|
|
+ if (status !== '0' && status !== '200' && status.toLowerCase() !== 'success') {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const msg = (data.message || data.msg || data.error || '').toString()
|
|
|
+ if (msg && failPattern.test(msg)) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return true
|
|
|
+ },
|
|
|
+
|
|
|
+ getImgCodeBase64: function (res) {
|
|
|
+ if (!res || !res.data) {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按接口约定:图片 base64 在 res.data.data
|
|
|
+ if (typeof res.data.data === 'string') {
|
|
|
+ return res.data.data.trim()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 仅保留一个最小兜底:res.data 本身是字符串
|
|
|
+ if (typeof res.data === 'string') {
|
|
|
+ return res.data.trim()
|
|
|
+ }
|
|
|
+
|
|
|
+ return ''
|
|
|
+ },
|
|
|
+
|
|
|
+ refreshImgCodeAfterVerifyFail: function () {
|
|
|
+ // 图形码失败后只刷新图片并等待用户再次输入,不触发短信发送
|
|
|
+ this.setData({
|
|
|
+ imgCodeInput: '',
|
|
|
+ isLoadingImgCode: true
|
|
|
+ })
|
|
|
+ this.requestImgCode()
|
|
|
+ },
|
|
|
+
|
|
|
+ showSubmitTip: function (text) {
|
|
|
+ if (this._submitTipTimer) {
|
|
|
+ clearTimeout(this._submitTipTimer)
|
|
|
+ }
|
|
|
+ this.setData({
|
|
|
+ showSubmitTip: true,
|
|
|
+ submitTipText: text || ''
|
|
|
+ })
|
|
|
+ this._submitTipTimer = setTimeout(() => {
|
|
|
+ this.setData({
|
|
|
+ showSubmitTip: false,
|
|
|
+ submitTipText: ''
|
|
|
+ })
|
|
|
+ this._submitTipTimer = null
|
|
|
+ }, 3000)
|
|
|
+ },
|
|
|
+
|
|
|
handleGoProduct: function () {
|
|
|
tt.redirectTo({
|
|
|
url: '/pages/product/index'
|
|
|
@@ -61,5 +429,16 @@ Page({
|
|
|
},
|
|
|
|
|
|
handleGoTrial: function () {
|
|
|
+ },
|
|
|
+
|
|
|
+ onUnload: function () {
|
|
|
+ if (this._submitTipTimer) {
|
|
|
+ clearTimeout(this._submitTipTimer)
|
|
|
+ this._submitTipTimer = null
|
|
|
+ }
|
|
|
+ if (this._countdownTimer) {
|
|
|
+ clearInterval(this._countdownTimer)
|
|
|
+ this._countdownTimer = null
|
|
|
+ }
|
|
|
}
|
|
|
})
|