Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 

275 wiersze
5.6 KiB

  1. <template>
  2. <view class="page">
  3. <view class="logo-area">
  4. <image class="logo" src="https://cdn.csybhelp.com/images/cytx/logo.png" mode="aspectFit" />
  5. <text class="title">肠愈同行</text>
  6. <text class="subtitle">患者关爱</text>
  7. </view>
  8. <view class="btn-area">
  9. <!-- #ifdef MP-WEIXIN -->
  10. <button class="login-btn" @tap="handleWxLogin" :loading="loading">
  11. 微信一键登录
  12. </button>
  13. <!-- #endif -->
  14. <!-- #ifdef H5 -->
  15. <view class="phone-form">
  16. <view class="form-item">
  17. <input class="form-input" type="number" v-model="phone" placeholder="请输入手机号" maxlength="11" />
  18. </view>
  19. <view class="form-item code-row">
  20. <input class="form-input code-input" type="number" v-model="smsCode" placeholder="请输入验证码" maxlength="6" />
  21. <button class="sms-btn" :disabled="countdown > 0" @tap="sendSms">
  22. {{ countdown > 0 ? countdown + 's' : '获取验证码' }}
  23. </button>
  24. </view>
  25. <button class="login-btn" @tap="handlePhoneLogin" :loading="loading">
  26. 登录
  27. </button>
  28. </view>
  29. <!-- #endif -->
  30. <view class="tip">登录即表示同意
  31. <text class="link" @tap="goPrivacy">《隐私协议》</text>
  32. </view>
  33. </view>
  34. </view>
  35. </template>
  36. <script setup>
  37. import { ref, onUnmounted } from 'vue'
  38. import { post } from '@/utils/request.js'
  39. import { setToken, setUserInfo } from '@/utils/cache.js'
  40. const loading = ref(false)
  41. const phone = ref('')
  42. const smsCode = ref('')
  43. const countdown = ref(0)
  44. let timer = null
  45. onUnmounted(() => {
  46. if (timer) { clearInterval(timer); timer = null }
  47. })
  48. const loginSuccess = (res) => {
  49. setToken(res.data.token)
  50. setUserInfo(res.data.userInfo)
  51. uni.showToast({ title: '登录成功', icon: 'success' })
  52. setTimeout(() => {
  53. const pages = getCurrentPages()
  54. if (pages.length > 1) {
  55. uni.navigateBack()
  56. } else {
  57. uni.switchTab({ url: '/pages/profile/profile' })
  58. }
  59. }, 500)
  60. }
  61. // #ifdef MP-WEIXIN
  62. const handleWxLogin = async () => {
  63. if (loading.value) return
  64. loading.value = true
  65. try {
  66. const code = await new Promise((resolve, reject) => {
  67. uni.login({
  68. provider: 'weixin',
  69. success: (res) => resolve(res.code),
  70. fail: () => reject({ msg: '微信登录失败' })
  71. })
  72. })
  73. const res = await post('/api/mp/login', { code })
  74. loginSuccess(res)
  75. } catch (e) {
  76. if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
  77. } finally {
  78. loading.value = false
  79. }
  80. }
  81. // #endif
  82. // #ifdef H5
  83. const sendSms = async () => {
  84. if (countdown.value > 0) return
  85. if (!phone.value || phone.value.length !== 11) {
  86. return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
  87. }
  88. try {
  89. const res = await post('/api/mp/sendSmsCode', { mobile: phone.value, bizType: 'login' })
  90. if (res.data && res.data.code) {
  91. uni.showToast({ title: `验证码: ${res.data.code}`, icon: 'none', duration: 3000 })
  92. } else {
  93. uni.showToast({ title: '验证码已发送', icon: 'none' })
  94. }
  95. countdown.value = 60
  96. timer = setInterval(() => {
  97. countdown.value--
  98. if (countdown.value <= 0) { clearInterval(timer); timer = null }
  99. }, 1000)
  100. } catch (e) {
  101. if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
  102. }
  103. }
  104. const handlePhoneLogin = async () => {
  105. if (loading.value) return
  106. if (!phone.value || phone.value.length !== 11) {
  107. return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
  108. }
  109. if (!smsCode.value || smsCode.value.length !== 6) {
  110. return uni.showToast({ title: '请输入6位验证码', icon: 'none' })
  111. }
  112. loading.value = true
  113. try {
  114. const res = await post('/api/mp/phoneLogin', { mobile: phone.value, code: smsCode.value })
  115. loginSuccess(res)
  116. } catch (e) {
  117. if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
  118. } finally {
  119. loading.value = false
  120. }
  121. }
  122. // #endif
  123. const goPrivacy = () => {
  124. // #ifdef H5
  125. uni.navigateTo({ url: '/pages/content/content?key=privacy_policy_h5' })
  126. // #endif
  127. // #ifdef MP-WEIXIN
  128. uni.navigateTo({ url: '/pages/content/content?key=privacy_policy' })
  129. // #endif
  130. }
  131. </script>
  132. <style lang="scss" scoped>
  133. .page {
  134. min-height: 100vh;
  135. background: #fff;
  136. display: flex;
  137. flex-direction: column;
  138. align-items: center;
  139. padding-top: 25vh;
  140. /* #ifdef H5 */
  141. padding-top: 10vh;
  142. /* #endif */
  143. }
  144. .logo-area {
  145. display: flex;
  146. flex-direction: column;
  147. align-items: center;
  148. margin-bottom: 80rpx;
  149. .logo {
  150. width: 180rpx;
  151. height: 180rpx;
  152. border-radius: 24rpx;
  153. margin-bottom: 32rpx;
  154. }
  155. .title {
  156. font-size: 44rpx;
  157. font-weight: 600;
  158. color: #303133;
  159. margin-bottom: 8rpx;
  160. }
  161. .subtitle {
  162. font-size: 26rpx;
  163. color: #b0b3b8;
  164. letter-spacing: 2rpx;
  165. }
  166. }
  167. .btn-area {
  168. width: 100%;
  169. padding: 0 64rpx;
  170. box-sizing: border-box;
  171. .login-btn {
  172. width: 100%;
  173. height: 88rpx;
  174. line-height: 88rpx;
  175. background: #0F78E9;
  176. color: #fff;
  177. font-size: 32rpx;
  178. border-radius: 44rpx;
  179. border: none;
  180. &::after {
  181. border: none;
  182. }
  183. &:active {
  184. opacity: 0.85;
  185. }
  186. }
  187. .tip {
  188. text-align: center;
  189. font-size: 24rpx;
  190. color: #909399;
  191. margin-top: 32rpx;
  192. .link {
  193. color: #0F78E9;
  194. }
  195. }
  196. }
  197. .phone-form {
  198. width: 100%;
  199. .form-item {
  200. margin-bottom: 24rpx;
  201. }
  202. .form-input {
  203. width: 100%;
  204. height: 88rpx;
  205. border: 1rpx solid #dcdfe6;
  206. border-radius: 44rpx;
  207. padding: 0 36rpx;
  208. font-size: 30rpx;
  209. box-sizing: border-box;
  210. background: #f8f9fa;
  211. }
  212. .code-row {
  213. display: flex;
  214. gap: 20rpx;
  215. .code-input {
  216. flex: 2;
  217. }
  218. }
  219. .sms-btn {
  220. flex: 1;
  221. height: 88rpx;
  222. line-height: 88rpx;
  223. padding: 0;
  224. background: #0F78E9;
  225. color: #fff;
  226. border: none;
  227. border-radius: 44rpx;
  228. font-size: 26rpx;
  229. white-space: nowrap;
  230. text-align: center;
  231. &::after {
  232. border: none;
  233. }
  234. &[disabled] {
  235. background: #a0cfff;
  236. color: #fff;
  237. }
  238. }
  239. .login-btn {
  240. margin-top: 16rpx;
  241. }
  242. }
  243. </style>