You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

622 lines
13 KiB

1 month ago
  1. <template>
  2. <view class="login-registration-container">
  3. <!-- 自定义导航栏 -->
  4. <view
  5. class="custom-navbar"
  6. :style="{ paddingTop: safeAreaInsets?.top + 'px' }"
  7. >
  8. <!-- <view class="nav-left">
  9. <text class="back-btn" @click="goToIndex"></text>
  10. </view> -->
  11. <view class="nav-right">
  12. <image class="icons" src="../../../static/icons/Headset.png" alt="" />
  13. <image
  14. class="icons"
  15. @click="goToIndex"
  16. src="../../../static/icons/Frame.png"
  17. alt=""
  18. />
  19. </view>
  20. </view>
  21. <!-- Logo -->
  22. <!-- <image class="logo" src="/static/logo.png" mode="aspectFit"></image> -->
  23. <!-- 欢迎语 -->
  24. <text class="welcome-text">登录DeepChart</text>
  25. <!-- 邮箱/手机号切换 -->
  26. <view class="switch-container">
  27. <text
  28. class="switch-item"
  29. :class="{ active: switchType === 'Email' }"
  30. @click="switchEmail"
  31. >邮箱/用户名</text
  32. >
  33. <text
  34. class="switch-item"
  35. :class="{ active: switchType === 'Phone' }"
  36. @click="switchPhone"
  37. >手机号</text
  38. >
  39. </view>
  40. <!-- 输入框 -->
  41. <view class="input-container">
  42. <view v-if="switchType === 'Email'">
  43. <!-- 修改邮箱输入框容器将图标包含在内 -->
  44. <view class="input-with-icon">
  45. <image class="input-icon" src="../../../static/icons/People-safe.png" alt="" />
  46. <input
  47. class="input-field"
  48. type="text"
  49. placeholder="请输入邮箱/DeepChart ID"
  50. v-model="email"
  51. />
  52. </view>
  53. <view class="input-with-icon">
  54. <image class="input-icon" src="../../../static/icons/Unlock.png" alt="" />
  55. <input
  56. class="input-field"
  57. type="text"
  58. placeholder="请输入密码"
  59. v-model="password"
  60. />
  61. </view>
  62. </view>
  63. <view v-else class="phone-input-container">
  64. <view class="country-code-selector" @click="showCountryPicker">
  65. <image
  66. class="country-flag-img"
  67. src="../../../static/icons/Iphone.png"
  68. mode="aspectFit"
  69. ></image>
  70. <text class="country-code">{{ selectedCountry.code }}</text>
  71. <!-- <text class="arrow-down"></text> -->
  72. </view>
  73. <input
  74. class="input-field phone-input"
  75. type="number"
  76. placeholder="输入手机号"
  77. v-model="phone"
  78. @input="onPhoneInput"
  79. />
  80. <view>
  81. <button
  82. class="send-code-btn"
  83. :disabled="isCodeBtnDisabled"
  84. :class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
  85. @click="sendCode">
  86. <text class="send-code-text"
  87. :class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
  88. >{{ codeBtnText }}</text>
  89. </button>
  90. </view>
  91. </view>
  92. <view v-if="switchType === 'Phone'" class="input-with-icon">
  93. <image class="input-icon" src="../../../static/icons/Text-recognition.png" alt="" />
  94. <input
  95. class="input-field"
  96. type="text"
  97. placeholder="请输入密码"
  98. v-model="password"
  99. />
  100. </view>
  101. </view>
  102. <!-- 注册按钮 -->
  103. <button class="register-btn" @click="register">下一步</button>
  104. <!-- 或者 -->
  105. <text class="or-text">或者</text>
  106. <!-- 第三方登录 -->
  107. <view class="third-party-login">
  108. <view class="third-party-btn" @click="loginWithApple">
  109. <image
  110. class="apple-icon"
  111. src="../../../static/icons/appleIcons.png"
  112. mode="aspectFit"
  113. ></image>
  114. <text class="third-party-text">通过 Apple 继续</text>
  115. </view>
  116. <view class="third-party-btn" @click="loginWithGoogle">
  117. <image
  118. class="google-icon"
  119. src="../../../static/icons/GoogleIcons.png"
  120. mode="aspectFit"
  121. ></image>
  122. <text class="third-party-text">通过 Google 继续</text>
  123. </view>
  124. </view>
  125. <!-- 已有账号 -->
  126. <view class="existing-account">
  127. <text class="account-text">未注册账号</text>
  128. <text class="login-link" @click="goToRegistration">注册</text>
  129. </view>
  130. </view>
  131. </template>
  132. <script setup>
  133. import { ref } from "vue";
  134. // 导入完整的国家列表
  135. import countryList from "./list.js";
  136. const email = ref("");
  137. const password = ref("");
  138. const phone = ref("");
  139. const agreed = ref(false);
  140. const switchType = ref("Email"); // 默认是邮箱注册
  141. const { safeAreaInsets } = uni.getSystemInfoSync();
  142. const codeBtnText = ref("获取验证码");
  143. const isCodeBtnDisabled = ref(false); // 添加验证码按钮禁用状态
  144. // 使用从list.js导入的完整国家列表数据
  145. const countries = ref(
  146. countryList.list.map((item) => ({
  147. name: item.name,
  148. code: `+${item.tel}`,
  149. flag: item.flag,
  150. short: item.short,
  151. en: item.en,
  152. }))
  153. );
  154. // 默认选中的国家(中国)
  155. const selectedCountry = ref(
  156. countries.value.find((country) => country.short === "CN") ||
  157. countries.value[0]
  158. );
  159. // 显示国家选择器
  160. function showCountryPicker() {
  161. uni.showActionSheet({
  162. itemList: countries.value.map(
  163. (country) => `${country.name} ${country.code}`
  164. ),
  165. success: function (res) {
  166. selectedCountry.value = countries.value[res.tapIndex];
  167. },
  168. });
  169. }
  170. function goToIndex() {
  171. // 返回上一页
  172. uni.navigateTo({ url: "/pages/start/index/index" });
  173. }
  174. function switchEmail() {
  175. // 切换到邮箱注册
  176. switchType.value = "Email";
  177. }
  178. function switchPhone() {
  179. // 切换到手机注册
  180. switchType.value = "Phone";
  181. }
  182. function register() {
  183. if (switchType.value === "Email") {
  184. // 登录逻辑
  185. if (!email.value) {
  186. uni.showToast({
  187. title: "请输入邮箱地址",
  188. icon: "none",
  189. });
  190. return;
  191. }
  192. // 发送登录请求
  193. console.log("登录:", email.value);
  194. }
  195. if (switchType.value === "Phone") {
  196. // 登录逻辑
  197. if (!phone.value) {
  198. uni.showToast({
  199. title: "请输入手机号码",
  200. icon: "none",
  201. });
  202. return;
  203. }
  204. // 发送登录请求
  205. console.log("登录:", phone.value);
  206. }
  207. }
  208. function loginWithApple() {
  209. // Apple登录逻辑
  210. console.log("通过Apple登录");
  211. uni.login({
  212. provider: "apple",
  213. success: function (loginRes) {
  214. // 登录成功
  215. uni.getUserInfo({
  216. provider: "apple",
  217. success: function (info) {
  218. // 获取用户信息成功, info.authResult中保存登录认证数据
  219. console.log(info);
  220. },
  221. });
  222. },
  223. fail: function (err) {
  224. // 登录授权失败
  225. // err.code错误码参考`授权失败错误码(code)说明`
  226. console.log(err);
  227. },
  228. });
  229. }
  230. function loginWithGoogle() {
  231. // Google登录逻辑
  232. console.log("通过Google登录");
  233. uni.login({
  234. provider: "google",
  235. success: function (loginRes) {
  236. // 登录成功
  237. uni.getUserInfo({
  238. provider: "google",
  239. success: function (info) {
  240. // 获取用户信息成功, info.authResult保存用户信息
  241. console.log(info);
  242. },
  243. });
  244. },
  245. fail: function (err) {
  246. // 登录授权失败
  247. // err.code是错误码
  248. console.log(err);
  249. },
  250. });
  251. }
  252. function goToRegistration() {
  253. // 跳转到登录页
  254. uni.navigateTo({
  255. url: "/pages/start/Registration/Registration",
  256. });
  257. }
  258. function onPhoneInput(e) {
  259. // 确保只允许输入数字
  260. const value = e.detail.value;
  261. // 使用 isNaN 检查是否为有效数字
  262. if (isNaN(value)) {
  263. phone.value = "";
  264. } else {
  265. phone.value = value;
  266. }
  267. }
  268. function sendCode(){
  269. // 如果按钮已禁用,则不执行后续逻辑
  270. if (isCodeBtnDisabled.value) return;
  271. // 设置按钮为禁用状态
  272. isCodeBtnDisabled.value = true;
  273. codeBtnText.value = "重新发送"
  274. let time = 6
  275. const timer = setInterval(() => {
  276. time--
  277. codeBtnText.value = "重新发送 " + time + "s"
  278. if (time <= 0) {
  279. clearInterval(timer)
  280. codeBtnText.value = "重新发送"
  281. // 倒计时结束后启用按钮
  282. isCodeBtnDisabled.value = false;
  283. }
  284. }, 1000)
  285. return
  286. }
  287. </script>
  288. <style scoped>
  289. .login-registration-container {
  290. display: flex;
  291. flex-direction: column;
  292. align-items: center;
  293. justify-content: center;
  294. padding: 0 40rpx;
  295. height: 100vh;
  296. background-color: #ffffff;
  297. }
  298. /* 自定义导航栏样式 */
  299. .custom-navbar {
  300. position: absolute;
  301. top: 0;
  302. left: 0;
  303. /* z-index: 999; */
  304. width: 90%;
  305. height: 80rpx;
  306. display: flex;
  307. justify-content: space-between;
  308. align-items: center;
  309. padding: 10rpx 40rpx;
  310. margin-bottom: 20rpx;
  311. }
  312. .nav-left,
  313. .nav-right {
  314. flex: 1;
  315. }
  316. .nav-right {
  317. display: flex;
  318. justify-content: flex-end;
  319. }
  320. .icons {
  321. margin: 20rpx;
  322. width: 40rpx;
  323. height: 40rpx;
  324. /* margin-right: 10rpx; */
  325. }
  326. .back-btn,
  327. .headphone-btn {
  328. font-size: 36rpx;
  329. font-weight: bold;
  330. color: #333333;
  331. padding: 10rpx;
  332. }
  333. .logo {
  334. width: 120rpx;
  335. height: 120rpx;
  336. margin-bottom: 60rpx;
  337. border-radius: 20%;
  338. }
  339. .welcome-text {
  340. font-size: 48rpx;
  341. font-weight: bold;
  342. color: #333333;
  343. margin-bottom: 60rpx;
  344. /* text-align: left; */
  345. /* align-self: flex-start; */
  346. }
  347. .switch-container {
  348. display: flex;
  349. margin-bottom: 40rpx;
  350. align-self: flex-start;
  351. }
  352. .switch-item {
  353. font-size: 28rpx;
  354. color: #999999;
  355. padding: 10rpx 20rpx;
  356. position: relative;
  357. }
  358. .switch-item::after {
  359. content: "";
  360. position: absolute;
  361. bottom: 0;
  362. left: 50%;
  363. transform: translateX(-50%);
  364. width: 60%; /* 控制边框宽度 */
  365. height: 2rpx;
  366. background-color: transparent;
  367. }
  368. .switch-item.active {
  369. color: #333333;
  370. font-weight: 700;
  371. }
  372. .switch-item.active::after {
  373. content: "";
  374. position: absolute;
  375. top: 60rpx;
  376. bottom: 0;
  377. left: 50%;
  378. transform: translateX(-50%);
  379. width: 30%; /* 控制边框宽度 */
  380. height: 7rpx;
  381. background-color: #333333;
  382. }
  383. .input-container {
  384. width: 100%;
  385. margin-bottom: 40rpx;
  386. }
  387. /* 添加图标输入框样式 */
  388. .input-with-icon {
  389. display: flex;
  390. align-items: center;
  391. width: 100%;
  392. height: 80rpx;
  393. border-bottom: 2rpx solid #e5e5e5;
  394. margin-bottom: 20rpx;
  395. }
  396. .input-icon {
  397. width: 40rpx;
  398. height: 40rpx;
  399. margin: 0 20rpx;
  400. }
  401. .input-field {
  402. flex: 1;
  403. height: 80rpx;
  404. padding: 15rpx 0;
  405. font-size: 28rpx;
  406. color: #333333;
  407. border: none;
  408. background-color: transparent;
  409. }
  410. .phone-input-container {
  411. display: flex;
  412. align-items: center;
  413. width: 95.8%;
  414. height: 80rpx;
  415. /* border-radius: 20rpx; */
  416. /* border: 2rpx solid #e5e5e5; */
  417. /* background-color: #f5f5f5; */
  418. padding: 0 10rpx;
  419. border-bottom: 2rpx solid #e5e5e5;
  420. margin-bottom: 20rpx;
  421. }
  422. .country-code-selector {
  423. display: flex;
  424. align-items: center;
  425. padding: 0 10rpx;
  426. padding-bottom: 1rpx;
  427. height: 100%;
  428. /* border-right: 2rpx solid #e5e5e5; */
  429. /* background-color: #f5f5f5; */
  430. border-radius: 20rpx 0 0 20rpx;
  431. }
  432. .country-code {
  433. font-size: 28rpx;
  434. color: #333333;
  435. margin-right: 10rpx;
  436. }
  437. .country-flag-img {
  438. width: 40rpx;
  439. height: 40rpx;
  440. margin-right: 10rpx;
  441. }
  442. .arrow-down {
  443. font-size: 20rpx;
  444. color: #999999;
  445. }
  446. .phone-input {
  447. flex: 1;
  448. width: auto;
  449. height: 100%;
  450. border: none;
  451. background-color: transparent;
  452. padding: 0 20rpx;
  453. }
  454. .send-code-btn {
  455. width: 200rpx;
  456. height: 60rpx;
  457. display: inline-flex;
  458. padding: 0rpx 10rpx;
  459. justify-content: center;
  460. align-items: center;
  461. gap: 10px;
  462. border-radius: 4px;
  463. background: #000;
  464. }
  465. .send-code-btn-disabled {
  466. background: #E6E6E6; /* 禁用状态下的灰色背景 */
  467. }
  468. .send-code-btn-disabled-text {
  469. color: #999999 !important;
  470. }
  471. .send-code-text{
  472. color: #fff;
  473. font-size: 28rpx;
  474. }
  475. .agreement-container {
  476. /* display: flex; */
  477. align-items: center;
  478. margin-bottom: 40rpx;
  479. align-self: flex-start;
  480. }
  481. .checkbox {
  482. width: 10rpx;
  483. height: 10rpx;
  484. margin-right: 30rpx;
  485. /* flex: content; */
  486. }
  487. .agreement-text {
  488. margin-left: 20rpx;
  489. font-size: 24rpx;
  490. color: #666666;
  491. }
  492. .link {
  493. color: #333333;
  494. font-weight: bold;
  495. text-decoration: underline;
  496. }
  497. .register-btn {
  498. width: 100%;
  499. height: 80rpx;
  500. background-color: #000000;
  501. color: white;
  502. font-size: 32rpx;
  503. font-weight: bold;
  504. border-radius: 40rpx;
  505. margin-bottom: 40rpx;
  506. }
  507. .or-text {
  508. font-size: 24rpx;
  509. color: #999999;
  510. margin-bottom: 40rpx;
  511. }
  512. .third-party-login {
  513. width: 100%;
  514. margin-bottom: 60rpx;
  515. }
  516. .third-party-text {
  517. font-weight: bold;
  518. }
  519. .third-party-btn {
  520. width: 100%;
  521. height: 80rpx;
  522. background-color: white;
  523. border: 2rpx solid #e5e5e5;
  524. border-radius: 40rpx;
  525. display: flex;
  526. align-items: center;
  527. justify-content: center;
  528. margin-bottom: 20rpx;
  529. font-size: 28rpx;
  530. color: #333333;
  531. }
  532. .apple-icon {
  533. width: 30rpx;
  534. height: 30rpx;
  535. margin-right: 20rpx;
  536. }
  537. .google-icon {
  538. width: 30rpx;
  539. height: 30rpx;
  540. margin-right: 20rpx;
  541. }
  542. .existing-account {
  543. display: flex;
  544. align-items: center;
  545. }
  546. .account-text {
  547. font-size: 24rpx;
  548. color: #666666;
  549. }
  550. .login-link {
  551. font-size: 24rpx;
  552. font-weight: bold;
  553. color: #333333;
  554. margin-left: 10rpx;
  555. text-decoration: underline;
  556. }
  557. </style>