Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

231 linhas
5.2 KiB

  1. <template>
  2. <view class="page">
  3. <!-- 当前手机号 -->
  4. <view class="current-phone">
  5. <view class="label">当前绑定手机号</view>
  6. <view class="phone">{{ currentPhone ? maskedPhone : '未绑定' }}</view>
  7. </view>
  8. <!-- 绑定新手机号 -->
  9. <view class="step-card">
  10. <view class="card-title">绑定新手机号</view>
  11. <view class="form-group">
  12. <text class="form-label"><text class="required">*</text> 新手机号</text>
  13. <u-input v-model="form.mobile" type="number" placeholder="请输入新手机号" maxlength="11"
  14. border="surround" />
  15. </view>
  16. <view class="form-group">
  17. <text class="form-label"><text class="required">*</text> 验证码</text>
  18. <view class="code-row">
  19. <view class="code-input">
  20. <u-input v-model="form.code" type="number" placeholder="请输入验证码" maxlength="6"
  21. border="surround" />
  22. </view>
  23. <u-button :disabled="!!countdown" :text="countdownText"
  24. size="normal" @click="handleSendCode"
  25. color="#0E63E3"
  26. :customStyle="{ width: '240rpx', flexShrink: 0 }" />
  27. </view>
  28. </view>
  29. </view>
  30. <view class="btn-wrap">
  31. <u-button text="确认修改" :loading="submitting" @click="handleSubmit"
  32. color="#0E63E3" size="large" />
  33. </view>
  34. <!-- 温馨提示 -->
  35. <view class="tips">
  36. <view class="tips-title">温馨提示</view>
  37. <view class="tips-list">
  38. <view class="tips-item">修改手机号后,后续相关短信通知将发送到新手机号</view>
  39. </view>
  40. </view>
  41. </view>
  42. </template>
  43. <script setup>
  44. import { ref, computed } from 'vue'
  45. import { onShow } from '@dcloudio/uni-app'
  46. import { get, post } from '@/utils/request.js'
  47. import { getUserInfo, setUserInfo } from '@/utils/cache.js'
  48. const currentPhone = ref('')
  49. const form = ref({ mobile: '', code: '' })
  50. const countdown = ref(0)
  51. const submitting = ref(false)
  52. let timer = null
  53. const maskedPhone = computed(() => {
  54. if (!currentPhone.value || currentPhone.value.length !== 11) return currentPhone.value || ''
  55. return currentPhone.value.slice(0, 3) + ' **** ' + currentPhone.value.slice(-4)
  56. })
  57. const countdownText = computed(() => {
  58. return countdown.value > 0 ? `${countdown.value}s后重新获取` : '获取验证码'
  59. })
  60. onShow(() => {
  61. const info = getUserInfo()
  62. currentPhone.value = (info && info.patient && info.patient.phone) || ''
  63. })
  64. const handleSendCode = async () => {
  65. if (countdown.value > 0) return
  66. if (!form.value.mobile || !/^1[3-9]\d{9}$/.test(form.value.mobile)) {
  67. return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
  68. }
  69. if (form.value.mobile === currentPhone.value) {
  70. return uni.showToast({ title: '新手机号不能与当前手机号相同', icon: 'none' })
  71. }
  72. try {
  73. await post('/api/mp/sendSmsCode', { mobile: form.value.mobile, bizType: 'change_phone' })
  74. uni.showToast({ title: '验证码已发送', icon: 'success' })
  75. countdown.value = 60
  76. timer = setInterval(() => {
  77. countdown.value--
  78. if (countdown.value <= 0) {
  79. clearInterval(timer)
  80. timer = null
  81. }
  82. }, 1000)
  83. } catch (e) {
  84. if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
  85. }
  86. }
  87. const handleSubmit = async () => {
  88. if (!form.value.mobile || !/^1[3-9]\d{9}$/.test(form.value.mobile)) {
  89. return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
  90. }
  91. if (!form.value.code || !/^\d{6}$/.test(form.value.code)) {
  92. return uni.showToast({ title: '请输入6位验证码', icon: 'none' })
  93. }
  94. submitting.value = true
  95. try {
  96. await post('/api/mp/changePhone', { mobile: form.value.mobile, code: form.value.code })
  97. // 刷新用户信息
  98. const res = await get('/api/mp/userinfo')
  99. setUserInfo(res.data)
  100. uni.showToast({ title: '修改成功', icon: 'success' })
  101. setTimeout(() => uni.navigateBack(), 1500)
  102. } catch (e) {
  103. if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
  104. } finally {
  105. submitting.value = false
  106. }
  107. }
  108. </script>
  109. <style lang="scss" scoped>
  110. .page {
  111. min-height: 100vh;
  112. background: #f4f4f5;
  113. padding: 32rpx;
  114. padding-bottom: 60rpx;
  115. }
  116. .current-phone {
  117. background: #fff;
  118. border-radius: 8rpx;
  119. padding: 40rpx;
  120. text-align: center;
  121. border: 1rpx solid #ebeef5;
  122. margin-bottom: 32rpx;
  123. .label {
  124. font-size: 26rpx;
  125. color: #909399;
  126. margin-bottom: 16rpx;
  127. }
  128. .phone {
  129. font-size: 48rpx;
  130. font-weight: 600;
  131. color: #303133;
  132. letter-spacing: 4rpx;
  133. }
  134. }
  135. .step-card {
  136. background: #fff;
  137. border-radius: 8rpx;
  138. padding: 40rpx 32rpx;
  139. border: 1rpx solid #ebeef5;
  140. }
  141. .card-title {
  142. font-size: 32rpx;
  143. font-weight: 600;
  144. color: #303133;
  145. margin-bottom: 32rpx;
  146. }
  147. .form-group {
  148. margin-bottom: 32rpx;
  149. &:last-child {
  150. margin-bottom: 0;
  151. }
  152. }
  153. .form-label {
  154. font-size: 28rpx;
  155. color: #606266;
  156. margin-bottom: 16rpx;
  157. display: block;
  158. }
  159. .required {
  160. color: #3a85f0;
  161. }
  162. .code-row {
  163. display: flex;
  164. gap: 20rpx;
  165. align-items: stretch;
  166. .code-input {
  167. flex: 1;
  168. min-width: 0;
  169. }
  170. }
  171. .btn-wrap {
  172. padding-top: 48rpx;
  173. }
  174. .tips {
  175. margin-top: 32rpx;
  176. padding: 32rpx;
  177. background: #fff;
  178. border-radius: 8rpx;
  179. border: 1rpx solid #ebeef5;
  180. .tips-title {
  181. font-size: 28rpx;
  182. font-weight: 600;
  183. color: #303133;
  184. margin-bottom: 20rpx;
  185. }
  186. .tips-list {
  187. padding-left: 8rpx;
  188. }
  189. .tips-item {
  190. font-size: 26rpx;
  191. color: #909399;
  192. line-height: 1.8;
  193. padding-left: 20rpx;
  194. position: relative;
  195. &::before {
  196. content: '·';
  197. position: absolute;
  198. left: 0;
  199. }
  200. }
  201. }
  202. </style>