Login.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <template>
  2. <div class="login-page">
  3. <!-- 顶部标题 -->
  4. <div class="page-header">
  5. <div class="header-content">
  6. <div class="logo-wrapper">
  7. <div class="logo-container">
  8. <img src="@/assets/images/logo.png" alt="告白语音" class="logo-image" />
  9. </div>
  10. </div>
  11. </div>
  12. </div>
  13. <!-- 登录表单 -->
  14. <div class="login-container">
  15. <div class="login-card">
  16. <div class="login-title">手机号登录</div>
  17. <!-- 手机号输入 -->
  18. <div class="input-group">
  19. <div class="country-code">+86</div>
  20. <van-field v-model="phoneNumber" type="tel" placeholder="请输入手机号" maxlength="11" class="phone-input"
  21. :border="false" />
  22. </div>
  23. <!-- 获取验证码按钮 -->
  24. <div class="verify-btn-container">
  25. <van-button type="primary" block round :disabled="!isPhoneValid || isCountingDown" @click="getVerifyCode"
  26. class="verify-btn">
  27. {{ countDownText }}
  28. </van-button>
  29. </div>
  30. <!-- 协议条款 -->
  31. <div class="agreement-section">
  32. <div class="agreement-text">
  33. 登录即表示同意
  34. <span class="agreement-link" @click="showUserAgreement">《用户协议》</span>
  35. <span class="agreement-link" @click="showPrivacyPolicy">《隐私政策》</span>
  36. </div>
  37. </div>
  38. </div>
  39. </div>
  40. <!-- 用户协议弹框 -->
  41. <van-dialog v-model="userAgreementVisible" title="用户协议" confirm-button-text="我已阅读" :show-cancel-button="false">
  42. <div class="agreement-content">
  43. <iframe v-if="userAgreementVisible" src="https://gbyy91.com/agreement/user_agreement.html" frameborder="0"
  44. class="agreement-iframe"></iframe>
  45. </div>
  46. </van-dialog>
  47. <!-- 隐私政策弹框 -->
  48. <van-dialog v-model="privacyPolicyVisible" title="隐私政策" confirm-button-text="我已阅读" :show-cancel-button="false">
  49. <div class="agreement-content">
  50. <iframe v-if="privacyPolicyVisible" src="https://gbyy91.com/agreement/personal_info_protect.html"
  51. frameborder="0" class="agreement-iframe"></iframe>
  52. </div>
  53. </van-dialog>
  54. </div>
  55. </template>
  56. <script>
  57. import { getLoginSmsCode } from '@/api/user'
  58. export default {
  59. name: "LoginPage",
  60. data() {
  61. return {
  62. phoneNumber: "",
  63. isCountingDown: false,
  64. countDown: 60,
  65. userAgreementVisible: false,
  66. privacyPolicyVisible: false,
  67. };
  68. },
  69. computed: {
  70. // 验证手机号格式
  71. isPhoneValid() {
  72. const phoneRegex = /^1[3-9]\d{9}$/;
  73. return phoneRegex.test(this.phoneNumber);
  74. },
  75. // 倒计时文本
  76. countDownText() {
  77. if (this.isCountingDown) {
  78. return `${this.countDown}s后重新获取`;
  79. }
  80. return "获取验证码";
  81. },
  82. },
  83. methods: {
  84. // 获取验证码
  85. async getVerifyCode() {
  86. if (!this.isPhoneValid) {
  87. this.$toast("请输入正确的手机号");
  88. return;
  89. }
  90. try {
  91. this.$toast.loading({
  92. message: "发送中...",
  93. forbidClick: true,
  94. duration: 0,
  95. });
  96. // 调用发送验证码的API
  97. await getLoginSmsCode(this.phoneNumber)
  98. this.$toast.clear();
  99. this.$toast.success("验证码已发送");
  100. // 开始倒计时
  101. this.startCountDown();
  102. // 跳转到验证码页面
  103. this.$router.push({
  104. path: "/verify-code",
  105. query: {
  106. phone: this.phoneNumber,
  107. },
  108. });
  109. } catch (error) {
  110. this.$toast.clear();
  111. this.$toast.fail("发送失败,请重试");
  112. console.error("发送验证码失败:", error);
  113. }
  114. },
  115. // 开始倒计时
  116. startCountDown() {
  117. this.isCountingDown = true;
  118. this.countDown = 60;
  119. const timer = setInterval(() => {
  120. this.countDown--;
  121. if (this.countDown <= 0) {
  122. clearInterval(timer);
  123. this.isCountingDown = false;
  124. this.countDown = 60;
  125. }
  126. }, 1000);
  127. },
  128. // 显示用户协议
  129. showUserAgreement() {
  130. this.userAgreementVisible = true;
  131. },
  132. // 显示隐私政策
  133. showPrivacyPolicy() {
  134. this.privacyPolicyVisible = true;
  135. },
  136. },
  137. };
  138. </script>
  139. <style scoped>
  140. .login-page {
  141. min-height: 100vh;
  142. background-color: var(--background-color);
  143. }
  144. /* 页面头部 */
  145. .page-header {
  146. padding: 20px 16px;
  147. text-align: center;
  148. }
  149. .header-content {
  150. display: flex;
  151. justify-content: center;
  152. align-items: center;
  153. }
  154. .logo-wrapper {
  155. display: flex;
  156. justify-content: center;
  157. }
  158. .logo-container {
  159. width: 100px;
  160. height: 100px;
  161. display: flex;
  162. align-items: center;
  163. justify-content: center;
  164. }
  165. .logo-image {
  166. width: 100%;
  167. height: 100%;
  168. object-fit: contain;
  169. }
  170. /* 登录容器 */
  171. .login-container {
  172. padding: 40px 20px;
  173. }
  174. .login-card {
  175. background: var(--card-background);
  176. border-radius: 16px;
  177. padding: 32px 24px;
  178. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  179. }
  180. .login-title {
  181. font-size: 24px;
  182. font-weight: 600;
  183. color: var(--text-color);
  184. text-align: left;
  185. margin-bottom: 40px;
  186. }
  187. /* 输入组 */
  188. .input-group {
  189. display: flex;
  190. align-items: center;
  191. background: var(--input-background);
  192. border-radius: 24px;
  193. padding: 4px 20px;
  194. margin-bottom: 32px;
  195. border: 1px solid var(--border-color);
  196. }
  197. .country-code {
  198. font-size: 16px;
  199. color: var(--text-color);
  200. margin-right: 12px;
  201. font-weight: 500;
  202. }
  203. .phone-input {
  204. flex: 1;
  205. background: transparent;
  206. }
  207. .phone-input :deep(.van-field__control) {
  208. font-size: 16px;
  209. color: var(--text-color);
  210. background: transparent;
  211. }
  212. .phone-input :deep(.van-field__control::placeholder) {
  213. color: var(--text-lighter-color);
  214. }
  215. /* 验证码按钮 */
  216. .verify-btn-container {
  217. margin-bottom: 32px;
  218. }
  219. .verify-btn {
  220. height: 48px;
  221. font-size: 16px;
  222. font-weight: 500;
  223. background: var(--primary-color);
  224. border: none;
  225. }
  226. .verify-btn:disabled {
  227. background: var(--text-lighter-color);
  228. opacity: 0.6;
  229. }
  230. /* 协议部分 */
  231. .agreement-section {
  232. text-align: center;
  233. }
  234. .agreement-text {
  235. font-size: 12px;
  236. color: var(--text-lighter-color);
  237. line-height: 1.5;
  238. }
  239. .agreement-link {
  240. color: var(--primary-color);
  241. text-decoration: none;
  242. }
  243. .agreement-link:hover {
  244. text-decoration: underline;
  245. }
  246. /* 协议内容 */
  247. .agreement-content {
  248. height: 400px;
  249. overflow: hidden;
  250. }
  251. .agreement-iframe {
  252. width: 100%;
  253. height: 100%;
  254. border: none;
  255. }
  256. /* 响应式设计 */
  257. @media (max-width: 375px) {
  258. .login-container {
  259. padding: 30px 16px;
  260. }
  261. .login-card {
  262. padding: 24px 20px;
  263. }
  264. .login-title {
  265. font-size: 20px;
  266. margin-bottom: 32px;
  267. }
  268. }
  269. </style>