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.

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