form.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <template>
  2. <view class="form-page">
  3. <view class="section-card">
  4. <text class="section-title">报名信息</text>
  5. <text class="card-desc">填写联系方式与准备情况,缴费后由助教分配班次</text>
  6. <view class="summary-box" v-if="courseName">
  7. <view class="summary-row">
  8. <text class="summary-label">报名课程</text>
  9. <text class="summary-value">{{ courseName }}</text>
  10. </view>
  11. </view>
  12. <view class="form-item">
  13. <text class="form-label">姓名</text>
  14. <input class="form-input" v-model="form.name" placeholder="请输入真实姓名" />
  15. </view>
  16. <view class="form-item">
  17. <text class="form-label">手机号</text>
  18. <input class="form-input" v-model="form.phone" type="number" maxlength="11" placeholder="请输入手机号" />
  19. </view>
  20. <view class="form-item switch-item">
  21. <text class="form-label">是否携带笔记本</text>
  22. <switch :checked="form.bringLaptop === 1" color="#F97316" @change="onLaptopChange" />
  23. </view>
  24. <view class="form-item">
  25. <text class="form-label">已备数据</text>
  26. <view class="check-group">
  27. <view class="check-item" @click="toggleData('wealth')">
  28. <text class="check-square" :class="{ checked: dataReadyList.indexOf('wealth') > -1 }">✓</text>
  29. <text class="check-text">券商持仓</text>
  30. </view>
  31. <view class="check-item" @click="toggleData('health')">
  32. <text class="check-square" :class="{ checked: dataReadyList.indexOf('health') > -1 }">✓</text>
  33. <text class="check-text">体检/血压报告</text>
  34. </view>
  35. <view class="check-item" @click="toggleData('growth')">
  36. <text class="check-square" :class="{ checked: dataReadyList.indexOf('growth') > -1 }">✓</text>
  37. <text class="check-text">孩子成绩/日程</text>
  38. </view>
  39. </view>
  40. </view>
  41. <view class="form-item">
  42. <text class="form-label">最想解决的问题</text>
  43. <textarea class="form-textarea" v-model="form.topProblems" placeholder="如:如何用AI管理家庭资产、孩子学习计划…" />
  44. </view>
  45. <view class="form-item" v-if="form.inviteCode">
  46. <text class="form-label">邀请码</text>
  47. <input class="form-input" v-model="form.inviteCode" disabled placeholder="微信好友邀请码" />
  48. </view>
  49. <button class="submit-btn" @click="handleSubmit" :loading="loading" :disabled="loading">提交报名</button>
  50. </view>
  51. </view>
  52. </template>
  53. <script>
  54. import { createEnrollment } from '@/utils/api.js'
  55. export default {
  56. data() {
  57. return {
  58. form: {
  59. courseId: '',
  60. name: '',
  61. phone: '',
  62. bringLaptop: 0,
  63. dataReady: '',
  64. topProblems: '',
  65. inviteCode: ''
  66. },
  67. courseName: '',
  68. dataReadyList: [],
  69. loading: false
  70. }
  71. },
  72. onLoad(options) {
  73. this.form.courseId = options && options.courseId ? options.courseId : ''
  74. this.courseName = options && options.courseName ? decodeURIComponent(options.courseName) : ''
  75. this.form.inviteCode = options && options.inviteCode ? decodeURIComponent(options.inviteCode) : ''
  76. this.form.name = options && options.name ? decodeURIComponent(options.name) : ''
  77. this.form.phone = options && options.phone ? decodeURIComponent(options.phone) : ''
  78. if (!this.form.courseId) {
  79. uni.showToast({ title: '参数错误', icon: 'none' })
  80. setTimeout(function() {
  81. uni.navigateBack({ delta: 1 })
  82. }, 1500)
  83. }
  84. },
  85. methods: {
  86. onLaptopChange(e) {
  87. this.form.bringLaptop = e.detail.value ? 1 : 0
  88. },
  89. toggleData(key) {
  90. var idx = this.dataReadyList.indexOf(key)
  91. if (idx > -1) {
  92. this.dataReadyList.splice(idx, 1)
  93. } else {
  94. this.dataReadyList.push(key)
  95. }
  96. this.form.dataReady = this.dataReadyList.join(',')
  97. },
  98. handleSubmit() {
  99. var name = (this.form.name || '').trim()
  100. var phone = (this.form.phone || '').trim()
  101. if (!name) {
  102. uni.showToast({ title: '请填写姓名', icon: 'none' })
  103. return
  104. }
  105. if (!/^1\d{10}$/.test(phone)) {
  106. uni.showToast({ title: '请填写正确的手机号', icon: 'none' })
  107. return
  108. }
  109. if (this.loading) return
  110. this.loading = true
  111. var self = this
  112. var payload = {
  113. courseId: self.form.courseId,
  114. name: name,
  115. phone: phone,
  116. bringLaptop: self.form.bringLaptop,
  117. dataReady: self.form.dataReady,
  118. topProblems: (self.form.topProblems || '').trim(),
  119. inviteCode: self.form.inviteCode
  120. }
  121. createEnrollment(payload).then(function(resp) {
  122. uni.showToast({ title: '报名成功', icon: 'success' })
  123. setTimeout(function() {
  124. var enrollmentId = resp.data && resp.data.id ? resp.data.id : ''
  125. if (enrollmentId) {
  126. // 跳转支付页(test-mode 下自动模拟支付成功)
  127. uni.redirectTo({ url: '/pages/pay/index?enrollmentId=' + enrollmentId })
  128. } else {
  129. uni.redirectTo({ url: '/pages/enroll/list' })
  130. }
  131. }, 1200)
  132. }).catch(function(err) {
  133. uni.showToast({ title: (err && err.message) || '报名失败', icon: 'none' })
  134. }).finally(function() {
  135. self.loading = false
  136. })
  137. }
  138. }
  139. }
  140. </script>
  141. <style scoped>
  142. .form-page { min-height: 100vh; background: #F5F5F5; padding: 32rpx; }
  143. .section-card { background: #FFF; border-radius: 16rpx; padding: 32rpx; }
  144. .section-title { display: block; font-size: 32rpx; font-weight: 700; color: #1E293B; margin-bottom: 8rpx; border-left: 8rpx solid #F97316; padding-left: 16rpx; }
  145. .card-desc { display: block; font-size: 26rpx; color: #64748B; margin-bottom: 32rpx; }
  146. .summary-box { background: #FFF7ED; border-radius: 8rpx; padding: 20rpx 24rpx; margin-bottom: 24rpx; border: 2rpx solid #FED7AA; }
  147. .summary-row { display: flex; align-items: center; margin-bottom: 8rpx; }
  148. .summary-row:last-child { margin-bottom: 0; }
  149. .summary-label { font-size: 24rpx; color: #64748B; width: 140rpx; flex-shrink: 0; }
  150. .summary-value { font-size: 26rpx; color: #1E293B; font-weight: 600; flex: 1; }
  151. .form-item { margin-bottom: 24rpx; }
  152. .form-label { display: block; font-size: 26rpx; color: #475569; font-weight: 500; margin-bottom: 12rpx; }
  153. .form-input { height: 72rpx; border: 2rpx solid #E2E8F0; border-radius: 8rpx; padding: 0 16rpx; font-size: 28rpx; color: #1E293B; background: #F8FAFC; }
  154. .form-textarea { width: 100%; height: 160rpx; border: 2rpx solid #E2E8F0; border-radius: 8rpx; padding: 16rpx; font-size: 28rpx; color: #1E293B; background: #F8FAFC; box-sizing: border-box; }
  155. .check-group { display: flex; flex-wrap: wrap; }
  156. .check-item { display: flex; align-items: center; width: 50%; margin-bottom: 16rpx; }
  157. .check-square { width: 36rpx; height: 36rpx; border: 2rpx solid #CBD5E1; border-radius: 6rpx; display: flex; align-items: center; justify-content: center; font-size: 24rpx; color: #FFF; margin-right: 12rpx; flex-shrink: 0; }
  158. .check-square.checked { background: #F97316; border-color: #F97316; }
  159. .check-text { font-size: 26rpx; color: #1E293B; }
  160. .switch-item { display: flex; align-items: center; justify-content: space-between; }
  161. .switch-item .form-label { margin-bottom: 0; }
  162. .submit-btn { width: 100%; height: 88rpx; line-height: 88rpx; background: #F97316; color: #FFF; font-size: 30rpx; font-weight: 600; border-radius: 44rpx; border: none; margin-top: 24rpx; }
  163. .submit-btn:active { opacity: 0.85; }
  164. </style>